353 lines
10 KiB
C++
353 lines
10 KiB
C++
|
|
#include "mpeg2StreamReader.h"
|
|
|
|
#include <fs/systemlog.h>
|
|
|
|
#include "avPacket.h"
|
|
#include "nalUnits.h"
|
|
#include "tsPacket.h"
|
|
#include "vodCoreException.h"
|
|
#include "vod_common.h"
|
|
|
|
int MPEG2StreamReader::getTSDescriptor(uint8_t* dstBuff, bool blurayMode, bool hdmvDescriptors)
|
|
{
|
|
m_sequence.video_format = 5; // Unspecified video format
|
|
try
|
|
{
|
|
for (uint8_t* nal = MPEGHeader::findNextMarker(m_buffer, m_bufEnd); nal <= m_bufEnd - 32;
|
|
nal = MPEGHeader::findNextMarker(nal + 4, m_bufEnd))
|
|
{
|
|
if (nal[3] == SEQ_START_SHORT_CODE)
|
|
{
|
|
uint8_t* nextNal = MPEGHeader::findNextMarker(nal + 4, m_bufEnd);
|
|
m_sequence.deserialize(nal + 4, nextNal - nal - 4);
|
|
m_streamAR = (VideoAspectRatio)m_sequence.aspect_ratio_info;
|
|
}
|
|
else if (nal[3] == EXT_START_SHORT_CODE)
|
|
{
|
|
BitStreamReader bitReader{};
|
|
bitReader.setBuffer(nal + 4, m_bufEnd);
|
|
int extType = bitReader.getBits(4);
|
|
if (extType == SEQUENCE_EXT)
|
|
{
|
|
m_sequence.deserializeExtension(bitReader);
|
|
}
|
|
else if (extType == SEQUENCE_DISPLAY_EXT)
|
|
{
|
|
m_sequence.deserializeDisplayExtension(bitReader);
|
|
}
|
|
}
|
|
else if (nal[3] == GOP_START_SHORT_CODE)
|
|
{
|
|
uint8_t* nextNal = MPEGHeader::findNextMarker(nal + 4, m_bufEnd);
|
|
m_gop.deserialize(nal + 4, nextNal - nal - 4);
|
|
}
|
|
}
|
|
}
|
|
catch (BitStreamException)
|
|
{
|
|
}
|
|
|
|
// HDMV registration descriptor
|
|
*dstBuff++ = (uint8_t)TSDescriptorTag::HDMV; // registration descriptor tag
|
|
*dstBuff++ = 8; // descriptor length
|
|
memcpy(dstBuff, "HDMV\xff", 5);
|
|
dstBuff += 5;
|
|
|
|
*dstBuff++ = (uint8_t)StreamType::VIDEO_MPEG2; // stream_coding_type
|
|
*dstBuff++ = (m_sequence.video_format << 4) + m_sequence.frame_rate_index;
|
|
*dstBuff++ = (m_sequence.aspect_ratio_info << 4) + 0xf;
|
|
|
|
return 10; // total descriptor length
|
|
}
|
|
|
|
CheckStreamRez MPEG2StreamReader::checkStream(uint8_t* buffer, int len)
|
|
{
|
|
CheckStreamRez rez;
|
|
uint8_t* end = buffer + len;
|
|
BitStreamReader bitReader{};
|
|
uint8_t* nextNal = 0;
|
|
bool spsFound = false;
|
|
bool gopFound = false;
|
|
bool sliceFound = false;
|
|
bool seqExtFound = false;
|
|
bool pictureFound = false;
|
|
bool pulldownFound = false;
|
|
int extType = 0;
|
|
MPEGPictureHeader frame(0);
|
|
for (uint8_t* nal = MPEGHeader::findNextMarker(buffer, end); nal <= end - 32;
|
|
nal = MPEGHeader::findNextMarker(nal + 4, end))
|
|
{
|
|
uint8_t unitType = nal[3];
|
|
try
|
|
{
|
|
if (unitType >= SLICE_MIN_START_SHORT_CODE && unitType <= SLICE_MAX_START_SHORT_CODE)
|
|
sliceFound = true;
|
|
else
|
|
switch (unitType)
|
|
{
|
|
case EXT_START_SHORT_CODE:
|
|
bitReader.setBuffer(nal + 4, end);
|
|
extType = bitReader.getBits(4);
|
|
if (extType == SEQUENCE_EXT)
|
|
{
|
|
m_sequence.deserializeExtension(bitReader);
|
|
seqExtFound = true;
|
|
}
|
|
else if (extType == PICTURE_CODING_EXT)
|
|
{
|
|
frame.deserializeCodingExtension(bitReader);
|
|
pulldownFound |= frame.repeat_first_field > 0;
|
|
}
|
|
break;
|
|
case SEQ_END_SHORT_CODE:
|
|
break;
|
|
case GOP_START_SHORT_CODE:
|
|
gopFound = true;
|
|
break;
|
|
case USER_START_SHORT_CODE:
|
|
break;
|
|
case PICTURE_START_SHORT_CODE:
|
|
if (frame.deserialize(nal + 4, end - nal - 4) == 0)
|
|
return rez;
|
|
pictureFound = true;
|
|
break;
|
|
case SEQ_START_SHORT_CODE:
|
|
nextNal = MPEGHeader::findNextMarker(nal + 4, end);
|
|
if (m_sequence.deserialize(nal + 4, nextNal - nal - 4) == 0)
|
|
return rez;
|
|
m_streamAR = (VideoAspectRatio)m_sequence.aspect_ratio_info;
|
|
spsFound = true;
|
|
break;
|
|
default:
|
|
return rez;
|
|
}
|
|
}
|
|
catch (BitStreamException)
|
|
{
|
|
// return rez;
|
|
}
|
|
}
|
|
if (spsFound && pictureFound && sliceFound)
|
|
{
|
|
rez.codecInfo = mpeg2CodecInfo;
|
|
rez.streamDescr = m_sequence.getStreamDescr();
|
|
if (pulldownFound)
|
|
rez.streamDescr += " (pulldown)";
|
|
}
|
|
return rez;
|
|
}
|
|
|
|
int MPEG2StreamReader::intDecodeNAL(uint8_t* buff)
|
|
{
|
|
try
|
|
{
|
|
int rez = 0;
|
|
uint8_t* nextNal = 0;
|
|
switch (*buff)
|
|
{
|
|
case SEQ_START_SHORT_CODE:
|
|
rez = processSeqStartCode(buff);
|
|
if (rez != 0)
|
|
return rez;
|
|
nextNal = MPEGHeader::findNextMarker(buff, m_bufEnd) + 3;
|
|
while (1)
|
|
{
|
|
if (nextNal >= m_bufEnd)
|
|
return NOT_ENOUGH_BUFFER;
|
|
switch (*nextNal)
|
|
{
|
|
case EXT_START_SHORT_CODE:
|
|
rez = processExtStartCode(nextNal);
|
|
if (rez != 0)
|
|
return rez;
|
|
break;
|
|
case GOP_START_SHORT_CODE:
|
|
m_framesAtGop = -1;
|
|
m_lastRef = -1;
|
|
break;
|
|
case PICTURE_START_SHORT_CODE:
|
|
rez = decodePicture(nextNal);
|
|
if (rez == 0)
|
|
{
|
|
m_lastDecodedPos = nextNal;
|
|
}
|
|
return rez;
|
|
}
|
|
nextNal = MPEGHeader::findNextMarker(nextNal, m_bufEnd) + 3;
|
|
}
|
|
break;
|
|
case EXT_START_SHORT_CODE:
|
|
return processExtStartCode(buff);
|
|
break;
|
|
case GOP_START_SHORT_CODE:
|
|
m_framesAtGop = -1;
|
|
m_lastRef = -1;
|
|
break;
|
|
case PICTURE_START_SHORT_CODE:
|
|
rez = decodePicture(buff);
|
|
return rez;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
catch (BitStreamException& e)
|
|
{
|
|
(void)e;
|
|
return NOT_ENOUGH_BUFFER;
|
|
}
|
|
}
|
|
|
|
int MPEG2StreamReader::processSeqStartCode(uint8_t* buff)
|
|
{
|
|
uint8_t* nextNal = MPEGHeader::findNextMarker(buff, m_bufEnd);
|
|
if (nextNal == m_bufEnd)
|
|
{
|
|
return NOT_ENOUGH_BUFFER;
|
|
}
|
|
try
|
|
{
|
|
if (m_sequence.deserialize(buff + 1, nextNal - buff - 1) == 0)
|
|
return NALUnit::UNSUPPORTED_PARAM;
|
|
m_streamAR = (VideoAspectRatio)m_sequence.aspect_ratio_info;
|
|
}
|
|
catch (BitStreamException)
|
|
{
|
|
return NOT_ENOUGH_BUFFER;
|
|
}
|
|
int oldSpsLen = 0;
|
|
updateFPS(0, buff, nextNal, oldSpsLen);
|
|
spsFound = true;
|
|
m_lastIFrame = true;
|
|
return 0;
|
|
}
|
|
|
|
int MPEG2StreamReader::processExtStartCode(uint8_t* buff)
|
|
{
|
|
BitStreamReader bitReader{};
|
|
try
|
|
{
|
|
bitReader.setBuffer(buff + 1, m_bufEnd);
|
|
int extType = bitReader.getBits(4);
|
|
if (extType == SEQUENCE_EXT)
|
|
{
|
|
m_sequence.deserializeExtension(bitReader);
|
|
m_seqExtFound = true;
|
|
}
|
|
return 0;
|
|
}
|
|
catch (BitStreamException)
|
|
{
|
|
return NOT_ENOUGH_BUFFER;
|
|
}
|
|
}
|
|
|
|
int MPEG2StreamReader::decodePicture(uint8_t* buff)
|
|
{
|
|
if (!spsFound)
|
|
return NALUnit::SPS_OR_PPS_NOT_READY;
|
|
|
|
if (!m_streamMsgPrinted)
|
|
{
|
|
LTRACE(LT_INFO, 2, "Decoding MPEG2 stream (track " << m_streamIndex << "): " << m_sequence.getStreamDescr());
|
|
m_streamMsgPrinted = true;
|
|
}
|
|
|
|
try
|
|
{
|
|
if (m_frame.deserialize(buff + 1, m_bufEnd - buff - 1) == 0)
|
|
return NALUnit::UNSUPPORTED_PARAM;
|
|
}
|
|
catch (BitStreamException)
|
|
{
|
|
return NOT_ENOUGH_BUFFER;
|
|
}
|
|
|
|
m_frame.picture_structure = 0;
|
|
int rez = findFrameExt(buff + 1);
|
|
if (rez == NOT_ENOUGH_BUFFER)
|
|
return rez;
|
|
|
|
if (m_frame.pict_type == PictureCodingType::I_FRAME)
|
|
{
|
|
m_framesAtGop = -1;
|
|
m_lastRef = -1;
|
|
}
|
|
|
|
if (m_frame.ref != m_lastRef)
|
|
{
|
|
m_framesAtGop++;
|
|
m_lastRef = m_frame.ref;
|
|
m_totalFrameNum++;
|
|
|
|
m_curDts += m_prevFrameDelay;
|
|
|
|
if (m_frame.repeat_first_field)
|
|
{
|
|
if (!m_sequence.progressive_sequence)
|
|
m_prevFrameDelay = m_pcrIncPerFrame + m_pcrIncPerField;
|
|
else if (m_frame.top_field_first)
|
|
m_prevFrameDelay = m_pcrIncPerFrame * 3;
|
|
else
|
|
m_prevFrameDelay = m_pcrIncPerFrame * 2;
|
|
}
|
|
else
|
|
m_prevFrameDelay = m_pcrIncPerFrame;
|
|
|
|
if (m_removePulldown)
|
|
{
|
|
checkPulldownSync();
|
|
m_testPulldownDts += m_prevFrameDelay;
|
|
|
|
m_prevFrameDelay = m_pcrIncPerFrame;
|
|
}
|
|
}
|
|
m_isFirstFrame = false;
|
|
int refDif = m_frame.ref - m_framesAtGop;
|
|
m_curPts = m_curDts + refDif * m_pcrIncPerFrame;
|
|
m_lastIFrame = m_frame.pict_type == PictureCodingType::I_FRAME;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int MPEG2StreamReader::findFrameExt(uint8_t* buffer)
|
|
{
|
|
for (uint8_t* nal = MPEGHeader::findNextMarker(buffer, m_bufEnd); nal < m_bufEnd - 4;
|
|
nal = MPEGHeader::findNextMarker(nal + 4, m_bufEnd))
|
|
{
|
|
if (nal[3] == EXT_START_SHORT_CODE)
|
|
{
|
|
BitStreamReader bitReader{};
|
|
try
|
|
{
|
|
bitReader.setBuffer(nal + 4, m_bufEnd);
|
|
int extType = bitReader.getBits(4);
|
|
if (extType == PICTURE_CODING_EXT)
|
|
{
|
|
m_frame.deserializeCodingExtension(bitReader);
|
|
|
|
if (m_removePulldown)
|
|
{
|
|
updateBits(bitReader, m_frame.repeat_first_field_bitpos, 1, 0);
|
|
if (m_sequence.progressive_sequence)
|
|
updateBits(bitReader, m_frame.top_field_first_bitpos, 1, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
catch (BitStreamException)
|
|
{
|
|
return NOT_ENOUGH_BUFFER;
|
|
}
|
|
}
|
|
else
|
|
return NALUnit::NOT_FOUND;
|
|
}
|
|
return NOT_ENOUGH_BUFFER;
|
|
}
|
|
|
|
void MPEG2StreamReader::updateStreamFps(void* nalUnit, uint8_t* buff, uint8_t* nextNal, int oldSpsLen)
|
|
{
|
|
m_sequence.setFrameRate(buff + 1, m_fps);
|
|
}
|