上一篇文章分析了LAV Filter 中的LAV Video的两个主要的类:CLAVVideo和CDecodeThread。文章:LAV Filter 源代码分析 3: LAV Video (1)
在这里继续上篇文章的内容。文章中提到LAVVideo主要通过CDecodeThread这个类进行解码线程的管理,其中有一个关键的管理函数:ThreadProc(),包含了对解码线程的各种操作。函数如下所示:
//包含了对进程的各种操作 DWORD CDecodeThread::ThreadProc() { HRESULT hr; DWORD cmd; BOOL bEOS = FALSE; BOOL bReinit = FALSE; SetThreadName(-1, "LAVVideo Decode Thread"); HANDLE hWaitEvents[2] = { GetRequestHandle(), m_evInput }; //不停转圈,永不休止 while(1) { if (!bEOS && !bReinit) { // Wait for either an input sample, or an request WaitForMultipleObjects(2, hWaitEvents, FALSE, INFINITE); } //根据操作命令的不同 if (CheckRequest(&cmd)) { switch (cmd) { //创建解码器 case CMD_CREATE_DECODER: { CAutoLock lock(&m_ThreadCritSec); //创建 hr = CreateDecoderInternal(m_ThreadCallContext.pmt, m_ThreadCallContext.codec); Reply(hr); m_ThreadCallContext.pmt = NULL; } break; case CMD_CLOSE_DECODER: { //关闭 ClearQueues(); SAFE_DELETE(m_pDecoder); Reply(S_OK); } break; case CMD_FLUSH: { //清楚 ClearQueues(); m_pDecoder->Flush(); Reply(S_OK); } break; case CMD_EOS: { bEOS = TRUE; m_evEOSDone.Reset(); Reply(S_OK); } break; case CMD_EXIT: { //退出 Reply(S_OK); return 0; } break; case CMD_INIT_ALLOCATOR: { CAutoLock lock(&m_ThreadCritSec); hr = m_pDecoder->InitAllocator(m_ThreadCallContext.allocator); Reply(hr); m_ThreadCallContext.allocator = NULL; } break; case CMD_POST_CONNECT: { CAutoLock lock(&m_ThreadCritSec); hr = PostConnectInternal(m_ThreadCallContext.pin); Reply(hr); m_ThreadCallContext.pin = NULL; } break; case CMD_REINIT: { //重启 CMediaType &mt = m_pLAVVideo->GetInputMediaType(); CreateDecoderInternal(&mt, m_Codec); m_TempSample[1] = m_NextSample; m_NextSample = m_FailedSample; m_FailedSample = NULL; bReinit = TRUE; m_evEOSDone.Reset(); Reply(S_OK); m_bDecoderNeedsReInit = FALSE; } break; default: ASSERT(0); } } if (m_bDecoderNeedsReInit) { m_evInput.Reset(); continue; } if (bReinit && !m_NextSample) { if (m_TempSample[0]) { m_NextSample = m_TempSample[0]; m_TempSample[0] = NULL; } else if (m_TempSample[1]) { m_NextSample = m_TempSample[1]; m_TempSample[1] = NULL; } else { bReinit = FALSE; m_evEOSDone.Set(); m_evSample.Set(); continue; } } //获得一份数据 IMediaSample *pSample = GetSample(); if (!pSample) { // Process the EOS now that the sample queue is empty if (bEOS) { bEOS = FALSE; m_pDecoder->EndOfStream(); m_evEOSDone.Set(); m_evSample.Set(); } continue; } //解码 DecodeInternal(pSample); // Release the sample //释放 SafeRelease(&pSample); // Indicates we're done decoding this sample m_evDecodeDone.Set(); // Set the Sample Event to unblock any waiting threads m_evSample.Set(); } return 0; }
该函数中,DecodeInternal(pSample)为实际上真正具有解码功能的函数,来看看它的源代码吧:
STDMETHODIMP CDecodeThread::DecodeInternal(IMediaSample *pSample) { HRESULT hr = S_OK; if (!m_pDecoder) return E_UNEXPECTED; //调用接口进行解码 hr = m_pDecoder->Decode(pSample); // If a hardware decoder indicates a hard failure, we switch back to software // This is used to indicate incompatible media if (FAILED(hr) && m_bHWDecoder) { DbgLog((LOG_TRACE, 10, L"::Receive(): Hardware decoder indicates failure, switching back to software")); m_bHWDecoderFailed = TRUE; // Store the failed sample for re-try in a moment m_FailedSample = pSample; m_FailedSample->AddRef(); // Schedule a re-init when the main thread goes there the next time m_bDecoderNeedsReInit = TRUE; // Make room in the sample buffer, to ensure the main thread can get in m_TempSample[0] = GetSample(); } return S_OK; }
该函数比较简短,从源代码中可以看出,调用了m_pDecoder的Decode()方法。其中m_pDecoder为ILAVDecoder类型的指针,而ILAVDecoder是一个接口,并不包含实际的方法,如下所示。注意,从程序注释中可以看出,每一个解码器都需要实现该接口规定的函数。
/** * Decoder interface * * Every decoder needs to implement this to interface with the LAV Video core */ //接口 interface ILAVDecoder { /** * Virtual destructor */ virtual ~ILAVDecoder(void) {}; /** * Initialize interfaces with the LAV Video core * This function should also be used to create all interfaces with external DLLs * * @param pSettings reference to the settings interface * @param pCallback reference to the callback interface * @return S_OK on success, error code if this decoder is lacking an external support dll */ STDMETHOD(InitInterfaces)(ILAVVideoSettings *pSettings, ILAVVideoCallback *pCallback) PURE; /** * Check if the decoder is functional */ STDMETHOD(Check)() PURE; /** * Initialize the codec to decode a stream specified by codec and pmt. * * @param codec Codec Id * @param pmt DirectShow Media Type * @return S_OK on success, an error code otherwise */ STDMETHOD(InitDecoder)(AVCodecID codec, const CMediaType *pmt) PURE; /** * Decode a frame. * * @param pSample Media Sample to decode * @return S_OK if decoding was successfull, S_FALSE if no frame could be extracted, an error code if the decoder is not compatible with the bitstream * * Note: When returning an actual error code, the filter will switch to the fallback software decoder! This should only be used for catastrophic failures, * like trying to decode a unsupported format on a hardware decoder. */ STDMETHOD(Decode)(IMediaSample *pSample) PURE; /** * Flush the decoder after a seek. * The decoder should discard any remaining data. * * @return unused */ STDMETHOD(Flush)() PURE; /** * End of Stream * The decoder is asked to output any buffered frames for immediate delivery * * @return unused */ STDMETHOD(EndOfStream)() PURE; /** * Query the decoder for the current pixel format * Mostly used by the media type creation logic before playback starts * * @return the pixel format used in the decoding process */ STDMETHOD(GetPixelFormat)(LAVPixelFormat *pPix, int *pBpp) PURE; /** * Get the frame duration. * * This function is not mandatory, and if you cannot provide any specific duration, return 0. */ STDMETHOD_(REFERENCE_TIME, GetFrameDuration)() PURE; /** * Query whether the format can potentially be interlaced. * This function should return false if the format can 100% not be interlaced, and true if it can be interlaced (but also progressive). */ STDMETHOD_(BOOL, IsInterlaced)() PURE; /** * Allows the decoder to handle an allocator. * Used by DXVA2 decoding */ STDMETHOD(InitAllocator)(IMemAllocator **ppAlloc) PURE; /** * Function called after connection is established, with the pin as argument */ STDMETHOD(PostConnect)(IPin *pPin) PURE; /** * Get the number of sample buffers optimal for this decoder */ STDMETHOD_(long, GetBufferCount)() PURE; /** * Get the name of the decoder */ STDMETHOD_(const WCHAR*, GetDecoderName)() PURE; /** * Get whether the decoder outputs thread-safe buffers */ STDMETHOD(HasThreadSafeBuffers)() PURE; /** * Get whether the decoder should sync to the main thread */ STDMETHOD(SyncToProcessThread)() PURE; };
下面来看看封装libavcodec库的类吧,该类的定义位于decoders文件夹下,名为avcodec.h,如图所示:
该类名字叫CDecAvcodec,其继承了CDecBase。而CDecBase继承了ILAVDecoder。
/* 雷霄骅 * 中国传媒大学/数字电视技术 * leixiaohua1020@126.com * */ /* * Copyright (C) 2010-2013 Hendrik Leppkes * http://www.1f0.de * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #include "DecBase.h" #include "H264RandomAccess.h" #include <map> #define AVCODEC_MAX_THREADS 16 typedef struct { REFERENCE_TIME rtStart; REFERENCE_TIME rtStop; } TimingCache; //解码器(AVCODEC)(其实还有WMV9,CUVID等) class CDecAvcodec : public CDecBase { public: CDecAvcodec(void); virtual ~CDecAvcodec(void); // ILAVDecoder STDMETHODIMP InitDecoder(AVCodecID codec, const CMediaType *pmt); //解码 STDMETHODIMP Decode(const BYTE *buffer, int buflen, REFERENCE_TIME rtStart, REFERENCE_TIME rtStop, BOOL bSyncPoint, BOOL bDiscontinuity); STDMETHODIMP Flush(); STDMETHODIMP EndOfStream(); STDMETHODIMP GetPixelFormat(LAVPixelFormat *pPix, int *pBpp); STDMETHODIMP_(REFERENCE_TIME) GetFrameDuration(); STDMETHODIMP_(BOOL) IsInterlaced(); STDMETHODIMP_(const WCHAR*) GetDecoderName() { return L"avcodec"; } STDMETHODIMP HasThreadSafeBuffers() { return S_OK; } STDMETHODIMP SyncToProcessThread() { return m_pAVCtx && m_pAVCtx->thread_count > 1 ? S_OK : S_FALSE; } // CDecBase STDMETHODIMP Init(); protected: virtual HRESULT AdditionaDecoderInit() { return S_FALSE; } virtual HRESULT PostDecode() { return S_FALSE; } virtual HRESULT HandleDXVA2Frame(LAVFrame *pFrame) { return S_FALSE; } //销毁解码器,各种Free STDMETHODIMP DestroyDecoder(); private: STDMETHODIMP ConvertPixFmt(AVFrame *pFrame, LAVFrame *pOutFrame); protected: AVCodecContext *m_pAVCtx; AVFrame *m_pFrame; AVCodecID m_nCodecId; BOOL m_bDXVA; private: AVCodec *m_pAVCodec; AVCodecParserContext *m_pParser; BYTE *m_pFFBuffer; BYTE *m_pFFBuffer2; int m_nFFBufferSize; int m_nFFBufferSize2; SwsContext *m_pSwsContext; CH264RandomAccess m_h264RandomAccess; BOOL m_bNoBufferConsumption; BOOL m_bHasPalette; // Timing settings BOOL m_bFFReordering; BOOL m_bCalculateStopTime; BOOL m_bRVDropBFrameTimings; BOOL m_bInputPadded; BOOL m_bBFrameDelay; TimingCache m_tcBFrameDelay[2]; int m_nBFramePos; TimingCache m_tcThreadBuffer[AVCODEC_MAX_THREADS]; int m_CurrentThread; REFERENCE_TIME m_rtStartCache; BOOL m_bResumeAtKeyFrame; BOOL m_bWaitingForKeyFrame; int m_iInterlaced; };
从CDecAvcodec类的定义可以看出,包含了各种功能的函数。首先我们看看初始化函数Init()
// ILAVDecoder STDMETHODIMP CDecAvcodec::Init() { #ifdef DEBUG DbgSetModuleLevel (LOG_CUSTOM1, DWORD_MAX); // FFMPEG messages use custom1 av_log_set_callback(lavf_log_callback); #else av_log_set_callback(NULL); #endif //注册 avcodec_register_all(); return S_OK; }
可见其调用了ffmpeg的API函数avcodec_register_all()进行了解码器的注册。
我们再来看看其解码函数Decode():
//解码 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; //初始化Packet 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) { //设置AVPacket中的数据 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; }
终于,我们从这个函数中看到了很多的ffmpeg的API,结构体,以及变量。比如解码视频的函数avcodec_decode_video2()。
解码器初始化函数:InitDecoder()
//创建解码器 STDMETHODIMP CDecAvcodec::InitDecoder(AVCodecID codec, const CMediaType *pmt) { //要是有,先销毁 DestroyDecoder(); DbgLog((LOG_TRACE, 10, L"Initializing ffmpeg for codec %S", avcodec_get_name(codec))); BITMAPINFOHEADER *pBMI = NULL; videoFormatTypeHandler((const BYTE *)pmt->Format(), pmt->FormatType(), &pBMI); //查找解码器 m_pAVCodec = avcodec_find_decoder(codec); CheckPointer(m_pAVCodec, VFW_E_UNSUPPORTED_VIDEO); //初始化上下文环境 m_pAVCtx = avcodec_alloc_context3(m_pAVCodec); CheckPointer(m_pAVCtx, E_POINTER); if(codec == AV_CODEC_ID_MPEG1VIDEO || codec == AV_CODEC_ID_MPEG2VIDEO || pmt->subtype == FOURCCMap(MKTAG('H','2','6','4')) || pmt->subtype == FOURCCMap(MKTAG('h','2','6','4'))) { m_pParser = av_parser_init(codec); } DWORD dwDecFlags = m_pCallback->GetDecodeFlags(); LONG biRealWidth = pBMI->biWidth, biRealHeight = pBMI->biHeight; if (pmt->formattype == FORMAT_VideoInfo || pmt->formattype == FORMAT_MPEGVideo) { VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)pmt->Format(); if (vih->rcTarget.right != 0 && vih->rcTarget.bottom != 0) { biRealWidth = vih->rcTarget.right; biRealHeight = vih->rcTarget.bottom; } } else if (pmt->formattype == FORMAT_VideoInfo2 || pmt->formattype == FORMAT_MPEG2Video) { VIDEOINFOHEADER2 *vih2 = (VIDEOINFOHEADER2 *)pmt->Format(); if (vih2->rcTarget.right != 0 && vih2->rcTarget.bottom != 0) { biRealWidth = vih2->rcTarget.right; biRealHeight = vih2->rcTarget.bottom; } } //各种赋值 m_pAVCtx->codec_id = codec; m_pAVCtx->codec_tag = pBMI->biCompression; m_pAVCtx->coded_width = pBMI->biWidth; m_pAVCtx->coded_height = abs(pBMI->biHeight); m_pAVCtx->bits_per_coded_sample = pBMI->biBitCount; m_pAVCtx->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK; m_pAVCtx->err_recognition = AV_EF_CAREFUL; m_pAVCtx->workaround_bugs = FF_BUG_AUTODETECT; m_pAVCtx->refcounted_frames = 1; if (codec == AV_CODEC_ID_H264) m_pAVCtx->flags2 |= CODEC_FLAG2_SHOW_ALL; // Setup threading int thread_type = getThreadFlags(codec); if (thread_type) { // Thread Count. 0 = auto detect int thread_count = m_pSettings->GetNumThreads(); if (thread_count == 0) { thread_count = av_cpu_count() * 3 / 2; } m_pAVCtx->thread_count = max(1, min(thread_count, AVCODEC_MAX_THREADS)); m_pAVCtx->thread_type = thread_type; } else { m_pAVCtx->thread_count = 1; } if (dwDecFlags & LAV_VIDEO_DEC_FLAG_NO_MT) { m_pAVCtx->thread_count = 1; } //初始化AVFrame m_pFrame = av_frame_alloc(); CheckPointer(m_pFrame, E_POINTER); m_h264RandomAccess.SetAVCNALSize(0); // Process Extradata //处理ExtraData BYTE *extra = NULL; size_t extralen = 0; getExtraData(*pmt, NULL, &extralen); BOOL bH264avc = FALSE; if (extralen > 0) { DbgLog((LOG_TRACE, 10, L"-> Processing extradata of %d bytes", extralen)); // Reconstruct AVC1 extradata format if (pmt->formattype == FORMAT_MPEG2Video && (m_pAVCtx->codec_tag == MAKEFOURCC('a','v','c','1') || m_pAVCtx->codec_tag == MAKEFOURCC('A','V','C','1') || m_pAVCtx->codec_tag == MAKEFOURCC('C','C','V','1'))) { MPEG2VIDEOINFO *mp2vi = (MPEG2VIDEOINFO *)pmt->Format(); extralen += 7; extra = (uint8_t *)av_mallocz(extralen + FF_INPUT_BUFFER_PADDING_SIZE); extra[0] = 1; extra[1] = (BYTE)mp2vi->dwProfile; extra[2] = 0; extra[3] = (BYTE)mp2vi->dwLevel; extra[4] = (BYTE)(mp2vi->dwFlags ? mp2vi->dwFlags : 4) - 1; // Actually copy the metadata into our new buffer size_t actual_len; getExtraData(*pmt, extra+6, &actual_len); // Count the number of SPS/PPS in them and set the length // We'll put them all into one block and add a second block with 0 elements afterwards // The parsing logic does not care what type they are, it just expects 2 blocks. BYTE *p = extra+6, *end = extra+6+actual_len; BOOL bSPS = FALSE, bPPS = FALSE; int count = 0; while (p+1 < end) { unsigned len = (((unsigned)p[0] << 8) | p[1]) + 2; if (p + len > end) { break; } if ((p[2] & 0x1F) == 7) bSPS = TRUE; if ((p[2] & 0x1F) == 8) bPPS = TRUE; count++; p += len; } extra[5] = count; extra[extralen-1] = 0; bH264avc = TRUE; m_h264RandomAccess.SetAVCNALSize(mp2vi->dwFlags); } else if (pmt->subtype == MEDIASUBTYPE_LAV_RAWVIDEO) { if (extralen < sizeof(m_pAVCtx->pix_fmt)) { DbgLog((LOG_TRACE, 10, L"-> LAV RAW Video extradata is missing..")); } else { extra = (uint8_t *)av_mallocz(extralen + FF_INPUT_BUFFER_PADDING_SIZE); getExtraData(*pmt, extra, NULL); m_pAVCtx->pix_fmt = *(AVPixelFormat *)extra; extralen -= sizeof(AVPixelFormat); memmove(extra, extra+sizeof(AVPixelFormat), extralen); } } else { // Just copy extradata for other formats extra = (uint8_t *)av_mallocz(extralen + FF_INPUT_BUFFER_PADDING_SIZE); getExtraData(*pmt, extra, NULL); } // Hack to discard invalid MP4 metadata with AnnexB style video if (codec == AV_CODEC_ID_H264 && !bH264avc && extra[0] == 1) { av_freep(&extra); extralen = 0; } m_pAVCtx->extradata = extra; m_pAVCtx->extradata_size = (int)extralen; } else { if (codec == AV_CODEC_ID_VP6 || codec == AV_CODEC_ID_VP6A || codec == AV_CODEC_ID_VP6F) { int cropH = pBMI->biWidth - biRealWidth; int cropV = pBMI->biHeight - biRealHeight; if (cropH >= 0 && cropH <= 0x0f && cropV >= 0 && cropV <= 0x0f) { m_pAVCtx->extradata = (uint8_t *)av_mallocz(1 + FF_INPUT_BUFFER_PADDING_SIZE); m_pAVCtx->extradata_size = 1; m_pAVCtx->extradata[0] = (cropH << 4) | cropV; } } } m_h264RandomAccess.flush(m_pAVCtx->thread_count); m_CurrentThread = 0; m_rtStartCache = AV_NOPTS_VALUE; LAVPinInfo lavPinInfo = {0}; BOOL bLAVInfoValid = SUCCEEDED(m_pCallback->GetLAVPinInfo(lavPinInfo)); m_bInputPadded = dwDecFlags & LAV_VIDEO_DEC_FLAG_LAVSPLITTER; // Setup codec-specific timing logic BOOL bVC1IsPTS = (codec == AV_CODEC_ID_VC1 && !(dwDecFlags & LAV_VIDEO_DEC_FLAG_VC1_DTS)); // Use ffmpegs logic to reorder timestamps // This is required for H264 content (except AVI), and generally all codecs that use frame threading // VC-1 is also a special case. Its required for splitters that deliver PTS timestamps (see bVC1IsPTS above) m_bFFReordering = ( codec == AV_CODEC_ID_H264 && !(dwDecFlags & LAV_VIDEO_DEC_FLAG_H264_AVI)) || codec == AV_CODEC_ID_VP8 || codec == AV_CODEC_ID_VP3 || codec == AV_CODEC_ID_THEORA || codec == AV_CODEC_ID_HUFFYUV || codec == AV_CODEC_ID_FFVHUFF || codec == AV_CODEC_ID_MPEG2VIDEO || codec == AV_CODEC_ID_MPEG1VIDEO || codec == AV_CODEC_ID_DIRAC || codec == AV_CODEC_ID_UTVIDEO || codec == AV_CODEC_ID_DNXHD || codec == AV_CODEC_ID_JPEG2000 || (codec == AV_CODEC_ID_MPEG4 && pmt->formattype == FORMAT_MPEG2Video) || bVC1IsPTS; // Stop time is unreliable, drop it and calculate it m_bCalculateStopTime = (codec == AV_CODEC_ID_H264 || codec == AV_CODEC_ID_DIRAC || (codec == AV_CODEC_ID_MPEG4 && pmt->formattype == FORMAT_MPEG2Video) || bVC1IsPTS); // Real Video content has some odd timestamps // LAV Splitter does them allright with RV30/RV40, everything else screws them up m_bRVDropBFrameTimings = (codec == AV_CODEC_ID_RV10 || codec == AV_CODEC_ID_RV20 || ((codec == AV_CODEC_ID_RV30 || codec == AV_CODEC_ID_RV40) && (!(dwDecFlags & LAV_VIDEO_DEC_FLAG_LAVSPLITTER) || (bLAVInfoValid && (lavPinInfo.flags & LAV_STREAM_FLAG_RV34_MKV))))); // Enable B-Frame delay handling m_bBFrameDelay = !m_bFFReordering && !m_bRVDropBFrameTimings; m_bWaitingForKeyFrame = TRUE; m_bResumeAtKeyFrame = codec == AV_CODEC_ID_MPEG2VIDEO || codec == AV_CODEC_ID_VC1 || codec == AV_CODEC_ID_RV30 || codec == AV_CODEC_ID_RV40 || codec == AV_CODEC_ID_VP3 || codec == AV_CODEC_ID_THEORA || codec == AV_CODEC_ID_MPEG4; m_bNoBufferConsumption = codec == AV_CODEC_ID_MJPEGB || codec == AV_CODEC_ID_LOCO || codec == AV_CODEC_ID_JPEG2000; m_bHasPalette = m_pAVCtx->bits_per_coded_sample <= 8 && m_pAVCtx->extradata_size && !(dwDecFlags & LAV_VIDEO_DEC_FLAG_LAVSPLITTER) && (codec == AV_CODEC_ID_MSVIDEO1 || codec == AV_CODEC_ID_MSRLE || codec == AV_CODEC_ID_CINEPAK || codec == AV_CODEC_ID_8BPS || codec == AV_CODEC_ID_QPEG || codec == AV_CODEC_ID_QTRLE || codec == AV_CODEC_ID_TSCC); if (FAILED(AdditionaDecoderInit())) { return E_FAIL; } if (bLAVInfoValid) { // Setting has_b_frames to a proper value will ensure smoother decoding of H264 if (lavPinInfo.has_b_frames >= 0) { DbgLog((LOG_TRACE, 10, L"-> Setting has_b_frames to %d", lavPinInfo.has_b_frames)); m_pAVCtx->has_b_frames = lavPinInfo.has_b_frames; } } // Open the decoder //打开解码器 int ret = avcodec_open2(m_pAVCtx, m_pAVCodec, NULL); if (ret >= 0) { DbgLog((LOG_TRACE, 10, L"-> ffmpeg codec opened successfully (ret: %d)", ret)); m_nCodecId = codec; } else { DbgLog((LOG_TRACE, 10, L"-> ffmpeg codec failed to open (ret: %d)", ret)); DestroyDecoder(); return VFW_E_UNSUPPORTED_VIDEO; } m_iInterlaced = 0; for (int i = 0; i < countof(ff_interlace_capable); i++) { if (codec == ff_interlace_capable[i]) { m_iInterlaced = -1; break; } } // Detect chroma and interlaced if (m_pAVCtx->extradata && m_pAVCtx->extradata_size) { if (codec == AV_CODEC_ID_MPEG2VIDEO) { CMPEG2HeaderParser mpeg2Parser(extra, extralen); if (mpeg2Parser.hdr.valid) { if (mpeg2Parser.hdr.chroma < 2) { m_pAVCtx->pix_fmt = AV_PIX_FMT_YUV420P; } else if (mpeg2Parser.hdr.chroma == 2) { m_pAVCtx->pix_fmt = AV_PIX_FMT_YUV422P; } m_iInterlaced = mpeg2Parser.hdr.interlaced; } } else if (codec == AV_CODEC_ID_H264) { CH264SequenceParser h264parser; if (bH264avc) h264parser.ParseNALs(extra+6, extralen-6, 2); else h264parser.ParseNALs(extra, extralen, 0); if (h264parser.sps.valid) m_iInterlaced = h264parser.sps.interlaced; } else if (codec == AV_CODEC_ID_VC1) { CVC1HeaderParser vc1parser(extra, extralen); if (vc1parser.hdr.valid) m_iInterlaced = (vc1parser.hdr.interlaced ? -1 : 0); } } if (codec == AV_CODEC_ID_DNXHD) m_pAVCtx->pix_fmt = AV_PIX_FMT_YUV422P10; else if (codec == AV_CODEC_ID_FRAPS) m_pAVCtx->pix_fmt = AV_PIX_FMT_BGR24; if (bLAVInfoValid && codec != AV_CODEC_ID_FRAPS && m_pAVCtx->pix_fmt != AV_PIX_FMT_DXVA2_VLD) m_pAVCtx->pix_fmt = lavPinInfo.pix_fmt; DbgLog((LOG_TRACE, 10, L"AVCodec init successfull. interlaced: %d", m_iInterlaced)); return S_OK; }
解码器销毁函数:DestroyDecoder()
//销毁解码器,各种Free STDMETHODIMP CDecAvcodec::DestroyDecoder() { DbgLog((LOG_TRACE, 10, L"Shutting down ffmpeg...")); m_pAVCodec = NULL; if (m_pParser) { av_parser_close(m_pParser); m_pParser = NULL; } if (m_pAVCtx) { avcodec_close(m_pAVCtx); av_freep(&m_pAVCtx->extradata); av_freep(&m_pAVCtx); } av_frame_free(&m_pFrame); av_freep(&m_pFFBuffer); m_nFFBufferSize = 0; av_freep(&m_pFFBuffer2); m_nFFBufferSize2 = 0; if (m_pSwsContext) { sws_freeContext(m_pSwsContext); m_pSwsContext = NULL; } m_nCodecId = AV_CODEC_ID_NONE; return S_OK; }
相关推荐
LAV Filter 源代码分析 4: LAV Video (2) 382 9.3 MPlayer 408 9.3.1 Mplayer 支持的格式 408 9.3.2 Mplayer 中头文件的功能分析 408 9.3.3 MPlayer.main 主流程简要说明 408 9.3.4 Mplayer 源码分析 409 第十章 ...
LAV Filter 源代码分析 4: LAV Video (2) 400 9.3 MPlayer 427 9.3.1 Mplayer支持的格式 427 9.3.2 Mplayer 中头文件的功能分析 427 9.3.3 MPlayer.main 主流程简要说明 428 9.3.4 Mplayer源码分析 429 第十章 ...
【LAVFilters源码】是针对视频开发领域的重要组件,其源代码可用于使用Visual Studio 2010进行编译。LAVFilters是一款开源的、高质量的多媒体滤镜套件,专为处理音视频解码任务而设计。它在视频播放、处理和分析中...
4. **添加Source Filter**: 使用`CoCreateInstance`函数创建并添加视频源过滤器,如File Source(GraphEdit中的"CLSID_VideoCaptureDevice"),用于读取视频文件。 5. **添加Lav Filter**: 接下来,你需要手动添加...
1. LAVVideo.ax:这是视频解码滤镜,能够解析多种视频编码格式,如H.264、VP9、MPEG-2、MPEG-4等,并将解码后的数据传递给播放器进行渲染。 2. LAVSplitter.ax:此文件是分离器滤镜,负责将复杂的多媒体文件拆分...
2. **LAV Video Splitter**:这个组件处理视频流,分离不同的视频轨道并准备它们进行解码。它能够处理H.264、VP9、AV1等多种视频编码格式,以及各种容器格式,如MKV、MP4、TS等。 3. **LAV Audio Decoder** 和 **...
1. **环境准备**:确保你已经安装了Visual Studio,这是开发MFC应用的标准工具,同时你也需要下载LAV Filter的源代码或预编译版本。 2. **新建MFC项目**:在Visual Studio中,选择“文件”->“新建”->“项目”,...
2. 构建过滤图:创建源滤镜(如File Source Filter)来读取媒体文件,接着添加解码滤镜进行解码,最后添加渲染滤镜(如Video Render Filter或Audio Render Filter)将解码后的数据输出到屏幕或扬声器。 3. 连接滤镜...
在播放视频时,通常需要以下几种过滤器:源过滤器(Source Filter)来获取媒体数据,解码过滤器(Decoder Filter)将压缩的媒体数据转化为原始格式,以及渲染过滤器(Renderer Filter)将数据呈现到屏幕上。...
在Windows上,可以通过预编译的二进制包进行安装,而在Linux或MacOS等其他系统上,通常需要通过包管理器或者源代码编译来安装。 **步骤1:引入FFmpeg到Qt项目** 要在Qt项目中使用FFmpeg,首先需要将FFmpeg的头文件...
在VB6中,你可以使用` FilterGraph `对象来创建和控制这个图,添加源过滤器(如文件播放器)、解码过滤器和渲染过滤器。 3. **Video Renderers**: 渲染过滤器是将解码后的视频帧显示到屏幕的关键。对于高清视频,...
5. **解码**:找到合适的解码器后,创建解码上下文并使用`avcodec_decode_video2()`或`avcodec_decode_audio4()`解码数据。 6. **处理解码后的数据**:解码后的数据可能需要进一步处理,如显示视频帧或播放音频。 ...
首先,为了在C++项目中使用FFmpeg,我们需要下载并编译FFmpeg源代码,将其库文件链接到我们的项目中。通常,这涉及到配置编译选项,比如设置-L(库路径)和-l(链接库)标志。确保链接了必要的库,例如`-lavformat`...
LIBS += -lGstreamer-1.0 -lavformat -lavcodec -lavutil -lavfilter ``` 接着,我们可以编写代码实现视频播放功能。创建一个`QMediaPlayer`实例,设置其视频输出为`QVideoWidget`,然后加载播放地址: ```cpp ...
- 源过滤器(Source Filter):提供媒体流,例如文件源过滤器可以从媒体文件中读取数据。 - 解码过滤器(Decoder Filter):将编码的数据转换为原始的音频或视频流。 - 渲染过滤器(Renderer Filter):将媒体流...