int main(int argc, char *argv[]) { if(argc < 2) { printf("Usage: ./get_file_duration inp.ts\n"); exit(1); } int vid_pid; int pmt_pid_num; uint64_t first_pts; uint64_t last_pts; int total_duration = 0; tsdemux_pmt_info pmt_info; GetPmtInfo(argv[1], &pmt_pid_num, &pmt_info, 32); for(int i = 0; i < pmt_info.num_pids; i++) { if(strncmp(pmt_info.strm_info[i].media_type.c_str(), "video", strlen("video")) == 0) { vid_pid = pmt_info.strm_info[i].pid_num; break; } } first_pts = GetFirstPts(argv[1], vid_pid); last_pts = GetLastPts(argv[1], vid_pid); total_duration = (last_pts - first_pts)/90; total_duration += GetFrameDuration(argv[1], vid_pid); printf("Duration=%d\n", total_duration); }
MediaTime M2VParser::CountBFrames(){ //We count after the first chunk. MediaTime count = 0; if(m_eos) return 0; if(notReachedFirstGOP) return 0; for(size_t i = 1; i < chunks.size(); i++){ MPEGChunk* c = chunks[i]; if(c->GetType() == MPEG_VIDEO_PICTURE_START_CODE){ MPEG2PictureHeader h; ParsePictureHeader(c, h); if(h.frameType == MPEG2_B_FRAME){ count += GetFrameDuration(h); }else{ return count; } } } return -1; }
/* Decode data from the current packet. Return -1 on error, 0 if the packet is finished, * and 1 if we have a frame (we may have more data in the packet). */ int MovieDecoder_FFMpeg::DecodePacket( float fTargetTime ) { if( m_iEOF == 0 && m_iCurrentPacketOffset == -1 ) return 0; /* no packet */ while( m_iEOF == 1 || (m_iEOF == 0 && m_iCurrentPacketOffset < m_Packet.size) ) { /* If we have no data on the first frame, just return EOF; passing an empty packet * to avcodec_decode_video in this case is crashing it. However, passing an empty * packet is normal with B-frames, to flush. This may be unnecessary in newer * versions of avcodec, but I'm waiting until a new stable release to upgrade. */ if( m_Packet.size == 0 && m_iFrameNumber == -1 ) return 0; /* eof */ bool bSkipThisFrame = fTargetTime != -1 && GetTimestamp() + GetFrameDuration() < fTargetTime && (m_pStream->codec->frame_number % 2) == 0; int iGotFrame; /* Hack: we need to send size = 0 to flush frames at the end, but we have * to give it a buffer to read from since it tries to read anyway. */ m_Packet.data = m_Packet.size ? m_Packet.data : NULL; int len = avcodec::avcodec_decode_video2( m_pStream->codec, m_Frame, &iGotFrame, &m_Packet ); if( len < 0 ) { LOG->Warn("avcodec_decode_video2: %i", len); return -1; // XXX } m_iCurrentPacketOffset += len; if( !iGotFrame ) { if( m_iEOF == 1 ) m_iEOF = 2; continue; } if( m_Frame->pkt_dts != AV_NOPTS_VALUE ) { m_fTimestamp = (float) (m_Frame->pkt_dts * av_q2d(m_pStream->time_base)); } else { /* If the timestamp is zero, this frame is to be played at the * time of the last frame plus the length of the last frame. */ m_fTimestamp += m_fLastFrameDelay; } /* Length of this frame: */ m_fLastFrameDelay = (float) av_q2d(m_pStream->time_base); m_fLastFrameDelay += m_Frame->repeat_pict * (m_fLastFrameDelay * 0.5f); ++m_iFrameNumber; if( m_iFrameNumber == 0 ) { /* Some videos start with a timestamp other than 0. I think this is used * when audio starts before the video. We don't want to honor that, since * the DShow renderer doesn't and we don't want to break sync compatibility. */ const float expect = 0; const float actual = m_fTimestamp; if( actual - expect > 0 ) { LOG->Trace("Expect %f, got %f -> %f", expect, actual, actual - expect ); m_fTimestampOffset = actual - expect; } } if( bSkipThisFrame ) continue; return 1; } return 0; /* packet done */ }
//Reads frames until it can timestamp them, usually reads until it has //an I, P, B+, P...this way it can stamp them all and set references. //NOTE: references aren't yet used by our system but they are kept up //with in this plugin. int32_t M2VParser::FillQueues(){ if(chunks.empty()){ return -1; } bool done = false; while(!done){ MediaTime myTime = currentStampingTime; MPEGChunk* chunk = chunks.front(); while (chunk->GetType() != MPEG_VIDEO_PICTURE_START_CODE) { if (chunk->GetType() == MPEG_VIDEO_GOP_START_CODE) { if (gopChunk) delete gopChunk; gopChunk = chunk; } else if (chunk->GetType() == MPEG_VIDEO_SEQUENCE_START_CODE) { if (seqHdrChunk) delete seqHdrChunk; ParseSequenceHeader(chunk, m_seqHdr); seqHdrChunk = chunk; } chunks.erase(chunks.begin()); if (chunks.empty()) return -1; chunk = chunks.front(); } MPEG2PictureHeader picHdr; ParsePictureHeader(chunk, picHdr); MediaTime bcount; if(myTime == nextSkip){ myTime+=nextSkipDuration; currentStampingTime=myTime; } switch(picHdr.frameType){ case MPEG2_I_FRAME: bcount = CountBFrames(); if(bcount > 0){ //..BBIBB.. myTime += bcount; nextSkip = myTime; nextSkipDuration = GetFrameDuration(picHdr); }else{ //IPBB.. if(bcount == -1 && !m_eos) return -1; currentStampingTime += GetFrameDuration(picHdr);; } ShoveRef(myTime); QueueFrame(chunk, myTime, picHdr); notReachedFirstGOP = false; break; case MPEG2_P_FRAME: bcount = CountBFrames(); if(firstRef == -1) break; if(bcount > 0){ //..PBB.. myTime += bcount; nextSkip = myTime; nextSkipDuration = GetFrameDuration(picHdr); }else{ //..PPBB.. if(bcount == -1 && !m_eos) return -1; currentStampingTime+=GetFrameDuration(picHdr); } ShoveRef(myTime); QueueFrame(chunk, myTime, picHdr); break; default: //B-frames if(firstRef == -1 || secondRef == -1) break; QueueFrame(chunk, myTime, picHdr); currentStampingTime+=GetFrameDuration(picHdr); } chunks.erase(chunks.begin()); delete chunk; if (chunks.empty()) return -1; } return 0; }
int32_t M2VParser::QueueFrame(MPEGChunk* chunk, MediaTime timecode, MPEG2PictureHeader picHdr){ MPEGFrame* outBuf; bool bCopy = true; binary* pData = chunk->GetPointer(); uint32_t dataLen = chunk->GetSize(); if ((seqHdrChunk && keepSeqHdrsInBitstream && (MPEG2_I_FRAME == picHdr.frameType)) || gopChunk) { uint32_t pos = 0; bCopy = false; dataLen += (seqHdrChunk && keepSeqHdrsInBitstream ? seqHdrChunk->GetSize() : 0) + (gopChunk ? gopChunk->GetSize() : 0); pData = (binary *)safemalloc(dataLen); if (seqHdrChunk && keepSeqHdrsInBitstream && (MPEG2_I_FRAME == picHdr.frameType)) { memcpy(pData, seqHdrChunk->GetPointer(), seqHdrChunk->GetSize()); pos += seqHdrChunk->GetSize(); delete seqHdrChunk; seqHdrChunk = NULL; } if (gopChunk) { memcpy(pData + pos, gopChunk->GetPointer(), gopChunk->GetSize()); pos += gopChunk->GetSize(); delete gopChunk; gopChunk = NULL; } memcpy(pData + pos, chunk->GetPointer(), chunk->GetSize()); } MediaTime duration = GetFrameDuration(picHdr); outBuf = new MPEGFrame(pData, dataLen, bCopy); if (seqHdrChunk && !keepSeqHdrsInBitstream && (MPEG2_I_FRAME == picHdr.frameType)) { outBuf->seqHdrData = (binary *)safemalloc(seqHdrChunk->GetSize()); outBuf->seqHdrDataSize = seqHdrChunk->GetSize(); memcpy(outBuf->seqHdrData, seqHdrChunk->GetPointer(), outBuf->seqHdrDataSize); delete seqHdrChunk; seqHdrChunk = NULL; } if(picHdr.frameType == MPEG2_I_FRAME){ outBuf->frameType = 'I'; }else if(picHdr.frameType == MPEG2_P_FRAME){ outBuf->frameType = 'P'; }else{ outBuf->frameType = 'B'; } outBuf->timecode = (MediaTime)(timecode * (1000000000/(m_seqHdr.frameRate*2))); outBuf->duration = (MediaTime)(duration * (1000000000/(m_seqHdr.frameRate*2))); if(outBuf->frameType == 'P'){ outBuf->firstRef = (MediaTime)(firstRef * (1000000000/(m_seqHdr.frameRate*2))); }else if(outBuf->frameType == 'B'){ outBuf->firstRef = (MediaTime)(firstRef * (1000000000/(m_seqHdr.frameRate*2))); outBuf->secondRef = (MediaTime)(secondRef * (1000000000/(m_seqHdr.frameRate*2))); } outBuf->rff = (picHdr.repeatFirstField != 0); outBuf->tff = (picHdr.topFieldFirst != 0); outBuf->progressive = (picHdr.progressive != 0); outBuf->pictureStructure = (uint8_t) picHdr.pictureStructure; buffers.push(outBuf); return 0; }
STDMETHODIMP CDecAvcodec::Decode(const BYTE *buffer, int buflen, REFERENCE_TIME rtStartIn, REFERENCE_TIME rtStopIn, BOOL bSyncPoint, BOOL bDiscontinuity) { int got_picture = 0; int used_bytes = 0; BOOL bParserFrame = FALSE; BOOL bFlush = (buffer == NULL); BOOL bEndOfSequence = FALSE; AVPacket avpkt; av_init_packet(&avpkt); if (m_pAVCtx->active_thread_type & FF_THREAD_FRAME) { if (!m_bFFReordering) { m_tcThreadBuffer[m_CurrentThread].rtStart = rtStartIn; m_tcThreadBuffer[m_CurrentThread].rtStop = rtStopIn; } m_CurrentThread = (m_CurrentThread + 1) % m_pAVCtx->thread_count; } else if (m_bBFrameDelay) { m_tcBFrameDelay[m_nBFramePos].rtStart = rtStartIn; m_tcBFrameDelay[m_nBFramePos].rtStop = rtStopIn; m_nBFramePos = !m_nBFramePos; } uint8_t *pDataBuffer = NULL; if (!bFlush && buflen > 0) { if (!m_bInputPadded && (!(m_pAVCtx->active_thread_type & FF_THREAD_FRAME) || m_pParser)) { // Copy bitstream into temporary buffer to ensure overread protection // Verify buffer size if (buflen > m_nFFBufferSize) { m_nFFBufferSize = buflen; m_pFFBuffer = (BYTE *)av_realloc_f(m_pFFBuffer, m_nFFBufferSize + FF_INPUT_BUFFER_PADDING_SIZE, 1); if (!m_pFFBuffer) { m_nFFBufferSize = 0; return E_OUTOFMEMORY; } } memcpy(m_pFFBuffer, buffer, buflen); memset(m_pFFBuffer+buflen, 0, FF_INPUT_BUFFER_PADDING_SIZE); pDataBuffer = m_pFFBuffer; } else { pDataBuffer = (uint8_t *)buffer; } if (m_nCodecId == AV_CODEC_ID_H264) { BOOL bRecovered = m_h264RandomAccess.searchRecoveryPoint(pDataBuffer, buflen); if (!bRecovered) { return S_OK; } } else if (m_nCodecId == AV_CODEC_ID_VP8 && m_bWaitingForKeyFrame) { if (!(pDataBuffer[0] & 1)) { DbgLog((LOG_TRACE, 10, L"::Decode(): Found VP8 key-frame, resuming decoding")); m_bWaitingForKeyFrame = FALSE; } else { return S_OK; } } } while (buflen > 0 || bFlush) { REFERENCE_TIME rtStart = rtStartIn, rtStop = rtStopIn; if (!bFlush) { avpkt.data = pDataBuffer; avpkt.size = buflen; avpkt.pts = rtStartIn; if (rtStartIn != AV_NOPTS_VALUE && rtStopIn != AV_NOPTS_VALUE) avpkt.duration = (int)(rtStopIn - rtStartIn); else avpkt.duration = 0; avpkt.flags = AV_PKT_FLAG_KEY; if (m_bHasPalette) { m_bHasPalette = FALSE; uint32_t *pal = (uint32_t *)av_packet_new_side_data(&avpkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); int pal_size = FFMIN((1 << m_pAVCtx->bits_per_coded_sample) << 2, m_pAVCtx->extradata_size); uint8_t *pal_src = m_pAVCtx->extradata + m_pAVCtx->extradata_size - pal_size; for (int i = 0; i < pal_size/4; i++) pal[i] = 0xFF<<24 | AV_RL32(pal_src+4*i); } } else { avpkt.data = NULL; avpkt.size = 0; } // Parse the data if a parser is present // This is mandatory for MPEG-1/2 if (m_pParser) { BYTE *pOut = NULL; int pOut_size = 0; used_bytes = av_parser_parse2(m_pParser, m_pAVCtx, &pOut, &pOut_size, avpkt.data, avpkt.size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); if (used_bytes == 0 && pOut_size == 0 && !bFlush) { DbgLog((LOG_TRACE, 50, L"::Decode() - could not process buffer, starving?")); break; } // Update start time cache // If more data was read then output, update the cache (incomplete frame) // If output is bigger, a frame was completed, update the actual rtStart with the cached value, and then overwrite the cache if (used_bytes > pOut_size) { if (rtStartIn != AV_NOPTS_VALUE) m_rtStartCache = rtStartIn; } else if (used_bytes == pOut_size || ((used_bytes + 9) == pOut_size)) { // Why +9 above? // Well, apparently there are some broken MKV muxers that like to mux the MPEG-2 PICTURE_START_CODE block (which is 9 bytes) in the package with the previous frame // This would cause the frame timestamps to be delayed by one frame exactly, and cause timestamp reordering to go wrong. // So instead of failing on those samples, lets just assume that 9 bytes are that case exactly. m_rtStartCache = rtStartIn = AV_NOPTS_VALUE; } else if (pOut_size > used_bytes) { rtStart = m_rtStartCache; m_rtStartCache = rtStartIn; // The value was used once, don't use it for multiple frames, that ends up in weird timings rtStartIn = AV_NOPTS_VALUE; } bParserFrame = (pOut_size > 0); if (pOut_size > 0 || bFlush) { if (pOut && pOut_size > 0) { if (pOut_size > m_nFFBufferSize2) { m_nFFBufferSize2 = pOut_size; m_pFFBuffer2 = (BYTE *)av_realloc_f(m_pFFBuffer2, m_nFFBufferSize2 + FF_INPUT_BUFFER_PADDING_SIZE, 1); if (!m_pFFBuffer2) { m_nFFBufferSize2 = 0; return E_OUTOFMEMORY; } } memcpy(m_pFFBuffer2, pOut, pOut_size); memset(m_pFFBuffer2+pOut_size, 0, FF_INPUT_BUFFER_PADDING_SIZE); avpkt.data = m_pFFBuffer2; avpkt.size = pOut_size; avpkt.pts = rtStart; avpkt.duration = 0; const uint8_t *eosmarker = CheckForEndOfSequence(m_nCodecId, avpkt.data, avpkt.size, &m_MpegParserState); if (eosmarker) { bEndOfSequence = TRUE; } } else { avpkt.data = NULL; avpkt.size = 0; } int ret2 = avcodec_decode_video2 (m_pAVCtx, m_pFrame, &got_picture, &avpkt); if (ret2 < 0) { DbgLog((LOG_TRACE, 50, L"::Decode() - decoding failed despite successfull parsing")); got_picture = 0; } } else { got_picture = 0; } } else { used_bytes = avcodec_decode_video2 (m_pAVCtx, m_pFrame, &got_picture, &avpkt); } if (FAILED(PostDecode())) { av_frame_unref(m_pFrame); return E_FAIL; } // Decoding of this frame failed ... oh well! if (used_bytes < 0) { av_frame_unref(m_pFrame); return S_OK; } // When Frame Threading, we won't know how much data has been consumed, so it by default eats everything. // In addition, if no data got consumed, and no picture was extracted, the frame probably isn't all that useufl. // The MJPEB decoder is somewhat buggy and doesn't let us know how much data was consumed really... if ((!m_pParser && (m_pAVCtx->active_thread_type & FF_THREAD_FRAME || (!got_picture && used_bytes == 0))) || m_bNoBufferConsumption || bFlush) { buflen = 0; } else { buflen -= used_bytes; pDataBuffer += used_bytes; } // Judge frame usability // This determines if a frame is artifact free and can be delivered // For H264 this does some wicked magic hidden away in the H264RandomAccess class // MPEG-2 and VC-1 just wait for a keyframe.. if (m_nCodecId == AV_CODEC_ID_H264 && (bParserFrame || !m_pParser || got_picture)) { m_h264RandomAccess.judgeFrameUsability(m_pFrame, &got_picture); } else if (m_bResumeAtKeyFrame) { if (m_bWaitingForKeyFrame && got_picture) { if (m_pFrame->key_frame) { DbgLog((LOG_TRACE, 50, L"::Decode() - Found Key-Frame, resuming decoding at %I64d", m_pFrame->pkt_pts)); m_bWaitingForKeyFrame = FALSE; } else { got_picture = 0; } } } // Handle B-frame delay for frame threading codecs if ((m_pAVCtx->active_thread_type & FF_THREAD_FRAME) && m_bBFrameDelay) { m_tcBFrameDelay[m_nBFramePos] = m_tcThreadBuffer[m_CurrentThread]; m_nBFramePos = !m_nBFramePos; } if (!got_picture || !m_pFrame->data[0]) { if (!avpkt.size) bFlush = FALSE; // End flushing, no more frames av_frame_unref(m_pFrame); continue; } /////////////////////////////////////////////////////////////////////////////////////////////// // Determine the proper timestamps for the frame, based on different possible flags. /////////////////////////////////////////////////////////////////////////////////////////////// if (m_bFFReordering) { rtStart = m_pFrame->pkt_pts; if (m_pFrame->pkt_duration) rtStop = m_pFrame->pkt_pts + m_pFrame->pkt_duration; else rtStop = AV_NOPTS_VALUE; } else if (m_bBFrameDelay && m_pAVCtx->has_b_frames) { rtStart = m_tcBFrameDelay[m_nBFramePos].rtStart; rtStop = m_tcBFrameDelay[m_nBFramePos].rtStop; } else if (m_pAVCtx->active_thread_type & FF_THREAD_FRAME) { unsigned index = m_CurrentThread; rtStart = m_tcThreadBuffer[index].rtStart; rtStop = m_tcThreadBuffer[index].rtStop; } if (m_bRVDropBFrameTimings && m_pFrame->pict_type == AV_PICTURE_TYPE_B) { rtStart = AV_NOPTS_VALUE; } if (m_bCalculateStopTime) rtStop = AV_NOPTS_VALUE; /////////////////////////////////////////////////////////////////////////////////////////////// // All required values collected, deliver the frame /////////////////////////////////////////////////////////////////////////////////////////////// LAVFrame *pOutFrame = NULL; AllocateFrame(&pOutFrame); AVRational display_aspect_ratio; int64_t num = (int64_t)m_pFrame->sample_aspect_ratio.num * m_pFrame->width; int64_t den = (int64_t)m_pFrame->sample_aspect_ratio.den * m_pFrame->height; av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den, num, den, 1 << 30); pOutFrame->width = m_pFrame->width; pOutFrame->height = m_pFrame->height; pOutFrame->aspect_ratio = display_aspect_ratio; pOutFrame->repeat = m_pFrame->repeat_pict; pOutFrame->key_frame = m_pFrame->key_frame; pOutFrame->frame_type = av_get_picture_type_char(m_pFrame->pict_type); pOutFrame->ext_format = GetDXVA2ExtendedFlags(m_pAVCtx, m_pFrame); if (m_pFrame->interlaced_frame || (!m_pAVCtx->progressive_sequence && (m_nCodecId == AV_CODEC_ID_H264 || m_nCodecId == AV_CODEC_ID_MPEG2VIDEO))) m_iInterlaced = 1; else if (m_pAVCtx->progressive_sequence) m_iInterlaced = 0; pOutFrame->interlaced = (m_pFrame->interlaced_frame || (m_iInterlaced == 1 && m_pSettings->GetDeinterlacingMode() == DeintMode_Aggressive) || m_pSettings->GetDeinterlacingMode() == DeintMode_Force) && !(m_pSettings->GetDeinterlacingMode() == DeintMode_Disable); LAVDeintFieldOrder fo = m_pSettings->GetDeintFieldOrder(); pOutFrame->tff = (fo == DeintFieldOrder_Auto) ? m_pFrame->top_field_first : (fo == DeintFieldOrder_TopFieldFirst); pOutFrame->rtStart = rtStart; pOutFrame->rtStop = rtStop; PixelFormatMapping map = getPixFmtMapping((AVPixelFormat)m_pFrame->format); pOutFrame->format = map.lavpixfmt; pOutFrame->bpp = map.bpp; if (m_nCodecId == AV_CODEC_ID_MPEG2VIDEO || m_nCodecId == AV_CODEC_ID_MPEG1VIDEO) pOutFrame->avgFrameDuration = GetFrameDuration(); if (map.conversion) { ConvertPixFmt(m_pFrame, pOutFrame); } else { for (int i = 0; i < 4; i++) { pOutFrame->data[i] = m_pFrame->data[i]; pOutFrame->stride[i] = m_pFrame->linesize[i]; } pOutFrame->priv_data = av_frame_alloc(); av_frame_ref((AVFrame *)pOutFrame->priv_data, m_pFrame); pOutFrame->destruct = lav_avframe_free; } if (bEndOfSequence) pOutFrame->flags |= LAV_FRAME_FLAG_END_OF_SEQUENCE; if (pOutFrame->format == LAVPixFmt_DXVA2) { pOutFrame->data[0] = m_pFrame->data[4]; HandleDXVA2Frame(pOutFrame); } else { Deliver(pOutFrame); } if (bEndOfSequence) { bEndOfSequence = FALSE; if (pOutFrame->format == LAVPixFmt_DXVA2) { HandleDXVA2Frame(m_pCallback->GetFlushFrame()); } else { Deliver(m_pCallback->GetFlushFrame()); } } if (bFlush) { m_CurrentThread = (m_CurrentThread + 1) % m_pAVCtx->thread_count; } av_frame_unref(m_pFrame); } return S_OK; }
int32_t M2VParser::PrepareFrame(MPEGChunk* chunk, MediaTime timecode, MPEG2PictureHeader picHdr){ MPEGFrame* outBuf; bool bCopy = true; binary* pData = chunk->GetPointer(); uint32_t dataLen = chunk->GetSize(); if ((seqHdrChunk && keepSeqHdrsInBitstream && (MPEG2_I_FRAME == picHdr.frameType)) || gopChunk) { uint32_t pos = 0; bCopy = false; dataLen += (seqHdrChunk && keepSeqHdrsInBitstream ? seqHdrChunk->GetSize() : 0) + (gopChunk ? gopChunk->GetSize() : 0); pData = (binary *)safemalloc(dataLen); if (seqHdrChunk && keepSeqHdrsInBitstream && (MPEG2_I_FRAME == picHdr.frameType)) { memcpy(pData, seqHdrChunk->GetPointer(), seqHdrChunk->GetSize()); pos += seqHdrChunk->GetSize(); delete seqHdrChunk; seqHdrChunk = NULL; } if (gopChunk) { memcpy(pData + pos, gopChunk->GetPointer(), gopChunk->GetSize()); pos += gopChunk->GetSize(); delete gopChunk; gopChunk = NULL; } memcpy(pData + pos, chunk->GetPointer(), chunk->GetSize()); } outBuf = new MPEGFrame(pData, dataLen, bCopy); if (seqHdrChunk && !keepSeqHdrsInBitstream && (MPEG2_I_FRAME == picHdr.frameType)) { outBuf->seqHdrData = (binary *)safemalloc(seqHdrChunk->GetSize()); outBuf->seqHdrDataSize = seqHdrChunk->GetSize(); memcpy(outBuf->seqHdrData, seqHdrChunk->GetPointer(), outBuf->seqHdrDataSize); delete seqHdrChunk; seqHdrChunk = NULL; } if(picHdr.frameType == MPEG2_I_FRAME){ outBuf->frameType = 'I'; }else if(picHdr.frameType == MPEG2_P_FRAME){ outBuf->frameType = 'P'; }else{ outBuf->frameType = 'B'; } outBuf->timecode = timecode; // Still the sequence number outBuf->invisible = invisible; outBuf->duration = GetFrameDuration(picHdr); outBuf->rff = (picHdr.repeatFirstField != 0); outBuf->tff = (picHdr.topFieldFirst != 0); outBuf->progressive = (picHdr.progressive != 0); outBuf->pictureStructure = (uint8_t) picHdr.pictureStructure; OrderFrame(outBuf); return 0; }
STDMETHODIMP CDecAvcodec::Decode(const BYTE *buffer, int buflen, REFERENCE_TIME rtStartIn, REFERENCE_TIME rtStopIn, BOOL bSyncPoint, BOOL bDiscontinuity) { CheckPointer(m_pAVCtx, E_UNEXPECTED); int got_picture = 0; int used_bytes = 0; BOOL bFlush = (buffer == nullptr); BOOL bEndOfSequence = FALSE; AVPacket avpkt; av_init_packet(&avpkt); if (m_pAVCtx->active_thread_type & FF_THREAD_FRAME) { if (!m_bFFReordering) { m_tcThreadBuffer[m_CurrentThread].rtStart = rtStartIn; m_tcThreadBuffer[m_CurrentThread].rtStop = rtStopIn; } m_CurrentThread = (m_CurrentThread + 1) % m_pAVCtx->thread_count; } else if (m_bBFrameDelay) { m_tcBFrameDelay[m_nBFramePos].rtStart = rtStartIn; m_tcBFrameDelay[m_nBFramePos].rtStop = rtStopIn; m_nBFramePos = !m_nBFramePos; } uint8_t *pDataBuffer = nullptr; if (!bFlush && buflen > 0) { if (!m_bInputPadded && (!(m_pAVCtx->active_thread_type & FF_THREAD_FRAME) || m_pParser)) { // Copy bitstream into temporary buffer to ensure overread protection // Verify buffer size if (buflen > m_nFFBufferSize) { m_nFFBufferSize = buflen; m_pFFBuffer = (BYTE *)av_realloc_f(m_pFFBuffer, m_nFFBufferSize + FF_INPUT_BUFFER_PADDING_SIZE, 1); if (!m_pFFBuffer) { m_nFFBufferSize = 0; return E_OUTOFMEMORY; } } memcpy(m_pFFBuffer, buffer, buflen); memset(m_pFFBuffer+buflen, 0, FF_INPUT_BUFFER_PADDING_SIZE); pDataBuffer = m_pFFBuffer; } else { pDataBuffer = (uint8_t *)buffer; } if (m_nCodecId == AV_CODEC_ID_VP8 && m_bWaitingForKeyFrame) { if (!(pDataBuffer[0] & 1)) { DbgLog((LOG_TRACE, 10, L"::Decode(): Found VP8 key-frame, resuming decoding")); m_bWaitingForKeyFrame = FALSE; } else { return S_OK; } } } while (buflen > 0 || bFlush) { REFERENCE_TIME rtStart = rtStartIn, rtStop = rtStopIn; if (!bFlush) { avpkt.data = pDataBuffer; avpkt.size = buflen; avpkt.pts = rtStartIn; if (rtStartIn != AV_NOPTS_VALUE && rtStopIn != AV_NOPTS_VALUE) avpkt.duration = (int)(rtStopIn - rtStartIn); else avpkt.duration = 0; avpkt.flags = AV_PKT_FLAG_KEY; if (m_bHasPalette) { m_bHasPalette = FALSE; uint32_t *pal = (uint32_t *)av_packet_new_side_data(&avpkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); int pal_size = FFMIN((1 << m_pAVCtx->bits_per_coded_sample) << 2, m_pAVCtx->extradata_size); uint8_t *pal_src = m_pAVCtx->extradata + m_pAVCtx->extradata_size - pal_size; for (int i = 0; i < pal_size/4; i++) pal[i] = 0xFF<<24 | AV_RL32(pal_src+4*i); } } else { avpkt.data = nullptr; avpkt.size = 0; } // Parse the data if a parser is present // This is mandatory for MPEG-1/2 if (m_pParser) { BYTE *pOut = nullptr; int pOut_size = 0; used_bytes = av_parser_parse2(m_pParser, m_pAVCtx, &pOut, &pOut_size, avpkt.data, avpkt.size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); if (used_bytes == 0 && pOut_size == 0 && !bFlush) { DbgLog((LOG_TRACE, 50, L"::Decode() - could not process buffer, starving?")); break; } else if (used_bytes > 0) { buflen -= used_bytes; pDataBuffer += used_bytes; } // Update start time cache // If more data was read then output, update the cache (incomplete frame) // If output is bigger, a frame was completed, update the actual rtStart with the cached value, and then overwrite the cache if (used_bytes > pOut_size) { if (rtStartIn != AV_NOPTS_VALUE) m_rtStartCache = rtStartIn; } else if (used_bytes == pOut_size || ((used_bytes + 9) == pOut_size)) { // Why +9 above? // Well, apparently there are some broken MKV muxers that like to mux the MPEG-2 PICTURE_START_CODE block (which is 9 bytes) in the package with the previous frame // This would cause the frame timestamps to be delayed by one frame exactly, and cause timestamp reordering to go wrong. // So instead of failing on those samples, lets just assume that 9 bytes are that case exactly. m_rtStartCache = rtStartIn = AV_NOPTS_VALUE; } else if (pOut_size > used_bytes) { rtStart = m_rtStartCache; m_rtStartCache = rtStartIn; // The value was used once, don't use it for multiple frames, that ends up in weird timings rtStartIn = AV_NOPTS_VALUE; } if (pOut_size > 0 || bFlush) { if (pOut && pOut_size > 0) { if (pOut_size > m_nFFBufferSize2) { m_nFFBufferSize2 = pOut_size; m_pFFBuffer2 = (BYTE *)av_realloc_f(m_pFFBuffer2, m_nFFBufferSize2 + FF_INPUT_BUFFER_PADDING_SIZE, 1); if (!m_pFFBuffer2) { m_nFFBufferSize2 = 0; return E_OUTOFMEMORY; } } memcpy(m_pFFBuffer2, pOut, pOut_size); memset(m_pFFBuffer2+pOut_size, 0, FF_INPUT_BUFFER_PADDING_SIZE); avpkt.data = m_pFFBuffer2; avpkt.size = pOut_size; avpkt.pts = rtStart; avpkt.duration = 0; const uint8_t *eosmarker = CheckForEndOfSequence(m_nCodecId, avpkt.data, avpkt.size, &m_MpegParserState); if (eosmarker) { bEndOfSequence = TRUE; } } else { avpkt.data = nullptr; avpkt.size = 0; } int ret2 = avcodec_decode_video2 (m_pAVCtx, m_pFrame, &got_picture, &avpkt); if (ret2 < 0) { DbgLog((LOG_TRACE, 50, L"::Decode() - decoding failed despite successfull parsing")); got_picture = 0; } } else { got_picture = 0; } } else { used_bytes = avcodec_decode_video2 (m_pAVCtx, m_pFrame, &got_picture, &avpkt); buflen = 0; } if (FAILED(PostDecode())) { av_frame_unref(m_pFrame); return E_FAIL; } // Decoding of this frame failed ... oh well! if (used_bytes < 0) { av_frame_unref(m_pFrame); return S_OK; } // Judge frame usability // This determines if a frame is artifact free and can be delivered. if (m_bResumeAtKeyFrame) { if (m_bWaitingForKeyFrame && got_picture) { if (m_pFrame->key_frame) { DbgLog((LOG_TRACE, 50, L"::Decode() - Found Key-Frame, resuming decoding at %I64d", m_pFrame->pkt_pts)); m_bWaitingForKeyFrame = FALSE; } else { got_picture = 0; } } } // Handle B-frame delay for frame threading codecs if ((m_pAVCtx->active_thread_type & FF_THREAD_FRAME) && m_bBFrameDelay) { m_tcBFrameDelay[m_nBFramePos] = m_tcThreadBuffer[m_CurrentThread]; m_nBFramePos = !m_nBFramePos; } if (!got_picture || !m_pFrame->data[0]) { if (!avpkt.size) bFlush = FALSE; // End flushing, no more frames av_frame_unref(m_pFrame); continue; } /////////////////////////////////////////////////////////////////////////////////////////////// // Determine the proper timestamps for the frame, based on different possible flags. /////////////////////////////////////////////////////////////////////////////////////////////// if (m_bFFReordering) { rtStart = m_pFrame->pkt_pts; if (m_pFrame->pkt_duration) rtStop = m_pFrame->pkt_pts + m_pFrame->pkt_duration; else rtStop = AV_NOPTS_VALUE; } else if (m_bBFrameDelay && m_pAVCtx->has_b_frames) { rtStart = m_tcBFrameDelay[m_nBFramePos].rtStart; rtStop = m_tcBFrameDelay[m_nBFramePos].rtStop; } else if (m_pAVCtx->active_thread_type & FF_THREAD_FRAME) { unsigned index = m_CurrentThread; rtStart = m_tcThreadBuffer[index].rtStart; rtStop = m_tcThreadBuffer[index].rtStop; } if (m_bRVDropBFrameTimings && m_pFrame->pict_type == AV_PICTURE_TYPE_B) { rtStart = AV_NOPTS_VALUE; } if (m_bCalculateStopTime) rtStop = AV_NOPTS_VALUE; /////////////////////////////////////////////////////////////////////////////////////////////// // All required values collected, deliver the frame /////////////////////////////////////////////////////////////////////////////////////////////// LAVFrame *pOutFrame = nullptr; AllocateFrame(&pOutFrame); AVRational display_aspect_ratio; int64_t num = (int64_t)m_pFrame->sample_aspect_ratio.num * m_pFrame->width; int64_t den = (int64_t)m_pFrame->sample_aspect_ratio.den * m_pFrame->height; av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den, num, den, INT_MAX); pOutFrame->width = m_pFrame->width; pOutFrame->height = m_pFrame->height; pOutFrame->aspect_ratio = display_aspect_ratio; pOutFrame->repeat = m_pFrame->repeat_pict; pOutFrame->key_frame = m_pFrame->key_frame; pOutFrame->frame_type = av_get_picture_type_char(m_pFrame->pict_type); pOutFrame->ext_format = GetDXVA2ExtendedFlags(m_pAVCtx, m_pFrame); if (m_pFrame->interlaced_frame || (!m_pAVCtx->progressive_sequence && (m_nCodecId == AV_CODEC_ID_H264 || m_nCodecId == AV_CODEC_ID_MPEG2VIDEO))) m_iInterlaced = 1; else if (m_pAVCtx->progressive_sequence) m_iInterlaced = 0; if ((m_nCodecId == AV_CODEC_ID_H264 || m_nCodecId == AV_CODEC_ID_MPEG2VIDEO) && m_pFrame->repeat_pict) m_nSoftTelecine = 2; else if (m_nSoftTelecine > 0) m_nSoftTelecine--; // Don't apply aggressive deinterlacing to content that looks soft-telecined, as it would destroy the content bool bAggressiveFlag = (m_iInterlaced == 1 && m_pSettings->GetDeinterlacingMode() == DeintMode_Aggressive) && !m_nSoftTelecine; pOutFrame->interlaced = (m_pFrame->interlaced_frame || bAggressiveFlag || m_pSettings->GetDeinterlacingMode() == DeintMode_Force) && !(m_pSettings->GetDeinterlacingMode() == DeintMode_Disable); LAVDeintFieldOrder fo = m_pSettings->GetDeintFieldOrder(); pOutFrame->tff = (fo == DeintFieldOrder_Auto) ? m_pFrame->top_field_first : (fo == DeintFieldOrder_TopFieldFirst); pOutFrame->rtStart = rtStart; pOutFrame->rtStop = rtStop; PixelFormatMapping map = getPixFmtMapping((AVPixelFormat)m_pFrame->format); pOutFrame->format = map.lavpixfmt; pOutFrame->bpp = map.bpp; if (m_nCodecId == AV_CODEC_ID_MPEG2VIDEO || m_nCodecId == AV_CODEC_ID_MPEG1VIDEO) pOutFrame->avgFrameDuration = GetFrameDuration(); AVFrameSideData * sdHDR = av_frame_get_side_data(m_pFrame, AV_FRAME_DATA_HDR_MASTERING_INFO); if (sdHDR) { if (sdHDR->size == 24) { MediaSideDataHDR * hdr = (MediaSideDataHDR *)AddLAVFrameSideData(pOutFrame, IID_MediaSideDataHDR, sizeof(MediaSideDataHDR)); if (hdr) { CByteParser hdrParser(sdHDR->data, sdHDR->size); for (int i = 0; i < 3; i++) { hdr->display_primaries_x[i] = hdrParser.BitRead(16) * 0.00002; hdr->display_primaries_y[i] = hdrParser.BitRead(16) * 0.00002; } hdr->white_point_x = hdrParser.BitRead(16) * 0.00002; hdr->white_point_y = hdrParser.BitRead(16) * 0.00002; hdr->max_display_mastering_luminance = hdrParser.BitRead(32) * 0.0001; hdr->min_display_mastering_luminance = hdrParser.BitRead(32) * 0.0001; } } else { DbgLog((LOG_TRACE, 10, L"::Decode(): Found HDR data of an unexpected size (%d)", sdHDR->size)); } } if (map.conversion) { ConvertPixFmt(m_pFrame, pOutFrame); } else { AVFrame *pFrameRef = av_frame_alloc(); av_frame_ref(pFrameRef, m_pFrame); for (int i = 0; i < 4; i++) { pOutFrame->data[i] = pFrameRef->data[i]; pOutFrame->stride[i] = pFrameRef->linesize[i]; } pOutFrame->priv_data = pFrameRef; pOutFrame->destruct = lav_avframe_free; // Check alignment on rawvideo, which can be off depending on the source file if (m_nCodecId == AV_CODEC_ID_RAWVIDEO) { for (int i = 0; i < 4; i++) { if ((intptr_t)pOutFrame->data[i] % 16u || pOutFrame->stride[i] % 16u) { // copy the frame, its not aligned properly and would crash later CopyLAVFrameInPlace(pOutFrame); break; } } } } if (bEndOfSequence) pOutFrame->flags |= LAV_FRAME_FLAG_END_OF_SEQUENCE; if (pOutFrame->format == LAVPixFmt_DXVA2) { pOutFrame->data[0] = m_pFrame->data[4]; HandleDXVA2Frame(pOutFrame); } else { Deliver(pOutFrame); } if (bEndOfSequence) { bEndOfSequence = FALSE; if (pOutFrame->format == LAVPixFmt_DXVA2) { HandleDXVA2Frame(m_pCallback->GetFlushFrame()); } else { Deliver(m_pCallback->GetFlushFrame()); } } if (bFlush) { m_CurrentThread = (m_CurrentThread + 1) % m_pAVCtx->thread_count; } av_frame_unref(m_pFrame); } return S_OK; }
/* Decode data from the current packet. Return -1 on error, 0 if the packet is finished, * and 1 if we have a frame (we may have more data in the packet). */ int MovieDecoder_FFMpeg::DecodePacket( float fTargetTime ) { if( m_iEOF == 0 && m_iCurrentPacketOffset == -1 ) return 0; /* no packet */ while( m_iEOF == 1 || (m_iEOF == 0 && m_iCurrentPacketOffset < m_Packet.size) ) { if( m_bGetNextTimestamp ) { if (m_Packet.dts != int64_t(AV_NOPTS_VALUE)) { m_fPTS = float( m_Packet.dts * av_q2d(m_pStream->time_base) ); /* dts is the timestamp of the first frame in this packet. Only use it once; * if we get more than one frame from the same packet (eg. f;lushing the last * frame), extrapolate. */ m_Packet.dts = int64_t(AV_NOPTS_VALUE); } else m_fPTS = -1; m_bGetNextTimestamp = false; } /* If we have no data on the first frame, just return EOF; passing an empty packet * to avcodec_decode_video in this case is crashing it. However, passing an empty * packet is normal with B-frames, to flush. This may be unnecessary in newer * versions of avcodec, but I'm waiting until a new stable release to upgrade. */ if( m_Packet.size == 0 && m_iFrameNumber == -1 ) return 0; /* eof */ bool bSkipThisFrame = fTargetTime != -1 && GetTimestamp() + GetFrameDuration() <= fTargetTime && (m_pStream->codec->frame_number % 2) == 0; int iGotFrame; CHECKPOINT; /* Hack: we need to send size = 0 to flush frames at the end, but we have * to give it a buffer to read from since it tries to read anyway. */ static uint8_t dummy[FF_INPUT_BUFFER_PADDING_SIZE] = { 0 }; int len = avcodec::avcodec_decode_video( m_pStream->codec, &m_Frame, &iGotFrame, m_Packet.size? m_Packet.data:dummy, m_Packet.size ); CHECKPOINT; if( len < 0 ) { LOG->Warn("avcodec_decode_video: %i", len); return -1; // XXX } m_iCurrentPacketOffset += len; if( !iGotFrame ) { if( m_iEOF == 1 ) m_iEOF = 2; continue; } m_bGetNextTimestamp = true; if( m_fPTS != -1 ) { m_fTimestamp = m_fPTS; } else { /* If the timestamp is zero, this frame is to be played at the * time of the last frame plus the length of the last frame. */ m_fTimestamp += m_fLastFrameDelay; } /* Length of this frame: */ m_fLastFrameDelay = (float)m_pStream->codec->time_base.num / m_pStream->codec->time_base.den; m_fLastFrameDelay += m_Frame.repeat_pict * (m_fLastFrameDelay * 0.5f); ++m_iFrameNumber; if( m_Frame.pict_type == FF_B_TYPE ) m_bHadBframes = true; if( m_iFrameNumber == 0 ) { /* Some videos start with a timestamp other than 0. I think this is used * when audio starts before the video. We don't want to honor that, since * the DShow renderer doesn't and we don't want to break sync compatibility. */ const float expect = 0; const float actual = m_fTimestamp; if( actual - expect > 0 ) { LOG->Trace("Expect %f, got %f -> %f", expect, actual, actual - expect ); m_fTimestampOffset = actual - expect; } } if( bSkipThisFrame ) continue; return 1; } return 0; /* packet done */ }