justdan96 / tsMuxer

tsMuxer is a transport stream muxer for remuxing/muxing elementary streams, EVO/VOB/MPG, MKV/MKA, MP4/MOV, TS, M2TS to TS to M2TS. Supported video codecs H.264/AVC, H.265/HEVC, VC-1, MPEG2. Supported audio codecs AAC, AC3 / E-AC3(DD+), DTS/ DTS-HD.
Apache License 2.0
849 stars 144 forks source link

Flush too early causing extra frame to be created #629

Open moveman opened 2 years ago

moveman commented 2 years ago

The following code in MPEGStreamReader::readPacket() assumed at the end of the H264 stream, there is a NAL unit (such as filler data) after the last picture:

  uint8_t* nextNal = NALUnit::findNALWithStartCode((std::min)(m_curPos + 3, m_bufEnd), m_bufEnd, m_longCodesAllowed);
    if (nextNal == m_bufEnd)
    {
        storeBufferRest();
        return NEED_MORE_DATA;
    }

If there are no such filler data NAL unit, it will return NEED_MORE_DATA.

This in turn caused m_codecInfo[minDtsIndex].m_lastAVRez to be changed:

                if (m_codecInfo[minDtsIndex].lastReadRez != BufferedFileReader::DATA_EOF2)
                {
                    int res = m_codecInfo[minDtsIndex].m_streamReader->readPacket(avPacket);
                    m_codecInfo[minDtsIndex].m_lastAVRez = res;
                }

metaDemuxer has already had m_isEOF set to 1 because all bytes of the H264 stream has been read. Having m_lastAVRez set to NEED_MORE_DATA will cause StreamInfo::read() to return DATA_EOF2:

    if (m_lastAVRez != 0)
    {
        if (m_isEOF)
        {
            m_blockSize = 0;
            return BufferedFileReader::DATA_EOF2;
        }
        m_lastAVRez = 0;

This will cause MetaDemuxer to flush packet METADemuxer::readPacket():

                if (m_codecInfo[minDtsIndex].lastReadRez != BufferedFileReader::DATA_EOF2)
                {
                    int res = m_codecInfo[minDtsIndex].m_streamReader->readPacket(avPacket);
                    m_codecInfo[minDtsIndex].m_lastAVRez = res;
                }
                else
                {
                    // flush single stream
                    m_codecInfo[minDtsIndex].m_streamReader->flushPacket(avPacket);
                    m_codecInfo[minDtsIndex].m_flushed = true;
                }

When flushPacket is called, PTS changed:

// MPEGStreamReader::flushPacket():
    avPacket.pts = m_curPts + m_timeOffset;
    avPacket.dts = m_curDts + m_timeOffset - m_pcrIncPerFrame * getFrameDepth();  // shift dts back

When PTS changes, PES packet will be flushed in TSMuxer::muxPacket():

    if (avPacket.dts != m_streamInfo[tsIndex].m_dts || avPacket.pts != m_streamInfo[tsIndex].m_pts ||
        tsIndex != m_lastTSIndex || avPacket.flags & AVPacket::FORCE_NEW_FRAME)
    {
        writePESPacket();
        newPES = true;
    }

Since the last picture has not been muxed yet, another PES will be created. Outcome will be like this: For the last picture, two PES will be created. One contains the NAL units enclosed by the red circle; another contains the NAL units enclosed by the gray circle: image

Workaround to this problem seems to be always adding filler data to the end of the H264 stream.

jcdr428 commented 2 years ago

@moveman can you please copy your .meta file ? I am not clear on the issue, is it the last frame that is copied twice, or is it simply the AUD nal which is wrongfully added ?

moveman commented 2 years ago

@jcdr428 this is the .meta file:

MUXOPT --no-pcr-on-video-pid --cbr --bitrate=5606.25 --vbv-len=3000 --start-time=62950
V_MPEG4/ISO/AVC, "/mnt/job/77c0fa26-cd8d-4611-919b-171777c52eda/1video.h264"
A_AAC, "/mnt/job/77c0fa26-cd8d-4611-919b-171777c52eda/1_0.aac", timeshift=0ms, , lang=eng

The last frame is not copied twice. AUD nal is wrongly added to the last frame. In addition, this last frame has two PTS instead of one PTS.