LAV Video 是使用很广泛的DirectShow Filter。它封装了FFMPEG中的libavcodec,支持十分广泛的视频格式的解码。在这里对其源代码进行详细的分析。
LAV Video 工程代码的结构如下图所示
直接看LAV Video最主要的类CLAVVideo吧,它的定义位于LAVVideo.h中。
LAVVideo.h
/* 雷霄骅 * 中国传媒大学/数字电视技术 * [email protected] * */ /* * 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 "decoders/ILAVDecoder.h" #include "DecodeThread.h" #include "ILAVPinInfo.h" #include "LAVPixFmtConverter.h" #include "LAVVideoSettings.h" #include "H264RandomAccess.h" #include "FloatingAverage.h" #include "ISpecifyPropertyPages2.h" #include "SynchronizedQueue.h" #include "subtitles/LAVSubtitleConsumer.h" #include "subtitles/LAVVideoSubtitleInputPin.h" #include "BaseTrayIcon.h" #define LAVC_VIDEO_REGISTRY_KEY L"Software\\LAV\\Video" #define LAVC_VIDEO_REGISTRY_KEY_FORMATS L"Software\\LAV\\Video\\Formats" #define LAVC_VIDEO_REGISTRY_KEY_OUTPUT L"Software\\LAV\\Video\\Output" #define LAVC_VIDEO_REGISTRY_KEY_HWACCEL L"Software\\LAV\\Video\\HWAccel" #define LAVC_VIDEO_LOG_FILE L"LAVVideo.txt" #define DEBUG_FRAME_TIMINGS 0 #define DEBUG_PIXELCONV_TIMINGS 0 #define LAV_MT_FILTER_QUEUE_SIZE 4 typedef struct { REFERENCE_TIME rtStart; REFERENCE_TIME rtStop; } TimingCache; //解码核心类 //Transform Filter [uuid("EE30215D-164F-4A92-A4EB-9D4C13390F9F")] class CLAVVideo : public CTransformFilter, public ISpecifyPropertyPages2, public ILAVVideoSettings, public ILAVVideoStatus, public ILAVVideoCallback { public: CLAVVideo(LPUNKNOWN pUnk, HRESULT* phr); ~CLAVVideo(); static void CALLBACK StaticInit(BOOL bLoading, const CLSID *clsid); // IUnknown // 查找接口必须实现 DECLARE_IUNKNOWN; STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv); // ISpecifyPropertyPages2 // 属性页 // 获取或者创建 STDMETHODIMP GetPages(CAUUID *pPages); STDMETHODIMP CreatePage(const GUID& guid, IPropertyPage** ppPage); // ILAVVideoSettings STDMETHODIMP SetRuntimeConfig(BOOL bRuntimeConfig); STDMETHODIMP SetFormatConfiguration(LAVVideoCodec vCodec, BOOL bEnabled); STDMETHODIMP_(BOOL) GetFormatConfiguration(LAVVideoCodec vCodec); STDMETHODIMP SetNumThreads(DWORD dwNum); STDMETHODIMP_(DWORD) GetNumThreads(); STDMETHODIMP SetStreamAR(DWORD bStreamAR); STDMETHODIMP_(DWORD) GetStreamAR(); STDMETHODIMP SetPixelFormat(LAVOutPixFmts pixFmt, BOOL bEnabled); STDMETHODIMP_(BOOL) GetPixelFormat(LAVOutPixFmts pixFmt); STDMETHODIMP SetRGBOutputRange(DWORD dwRange); STDMETHODIMP_(DWORD) GetRGBOutputRange(); STDMETHODIMP SetDeintFieldOrder(LAVDeintFieldOrder fieldOrder); STDMETHODIMP_(LAVDeintFieldOrder) GetDeintFieldOrder(); STDMETHODIMP SetDeintForce(BOOL bForce); STDMETHODIMP_(BOOL) GetDeintForce(); STDMETHODIMP SetDeintAggressive(BOOL bAggressive); STDMETHODIMP_(BOOL) GetDeintAggressive(); STDMETHODIMP_(DWORD) CheckHWAccelSupport(LAVHWAccel hwAccel); STDMETHODIMP SetHWAccel(LAVHWAccel hwAccel); STDMETHODIMP_(LAVHWAccel) GetHWAccel(); STDMETHODIMP SetHWAccelCodec(LAVVideoHWCodec hwAccelCodec, BOOL bEnabled); STDMETHODIMP_(BOOL) GetHWAccelCodec(LAVVideoHWCodec hwAccelCodec); STDMETHODIMP SetHWAccelDeintMode(LAVHWDeintModes deintMode); STDMETHODIMP_(LAVHWDeintModes) GetHWAccelDeintMode(); STDMETHODIMP SetHWAccelDeintOutput(LAVDeintOutput deintOutput); STDMETHODIMP_(LAVDeintOutput) GetHWAccelDeintOutput(); STDMETHODIMP SetHWAccelDeintHQ(BOOL bHQ); STDMETHODIMP_(BOOL) GetHWAccelDeintHQ(); STDMETHODIMP SetSWDeintMode(LAVSWDeintModes deintMode); STDMETHODIMP_(LAVSWDeintModes) GetSWDeintMode(); STDMETHODIMP SetSWDeintOutput(LAVDeintOutput deintOutput); STDMETHODIMP_(LAVDeintOutput) GetSWDeintOutput(); STDMETHODIMP SetDeintTreatAsProgressive(BOOL bEnabled); STDMETHODIMP_(BOOL) GetDeintTreatAsProgressive(); STDMETHODIMP SetDitherMode(LAVDitherMode ditherMode); STDMETHODIMP_(LAVDitherMode) GetDitherMode(); STDMETHODIMP SetUseMSWMV9Decoder(BOOL bEnabled); STDMETHODIMP_(BOOL) GetUseMSWMV9Decoder(); STDMETHODIMP SetDVDVideoSupport(BOOL bEnabled); STDMETHODIMP_(BOOL) GetDVDVideoSupport(); STDMETHODIMP SetHWAccelResolutionFlags(DWORD dwResFlags); STDMETHODIMP_(DWORD) GetHWAccelResolutionFlags(); STDMETHODIMP SetTrayIcon(BOOL bEnabled); STDMETHODIMP_(BOOL) GetTrayIcon(); STDMETHODIMP SetDeinterlacingMode(LAVDeintMode deintMode); STDMETHODIMP_(LAVDeintMode) GetDeinterlacingMode(); // ILAVVideoStatus STDMETHODIMP_(const WCHAR *) GetActiveDecoderName() { return m_Decoder.GetDecoderName(); } // CTransformFilter // 核心的 HRESULT CheckInputType(const CMediaType* mtIn); HRESULT CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut); HRESULT DecideBufferSize(IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES *pprop); HRESULT GetMediaType(int iPosition, CMediaType *pMediaType); HRESULT SetMediaType(PIN_DIRECTION dir, const CMediaType *pmt); HRESULT EndOfStream(); HRESULT BeginFlush(); HRESULT EndFlush(); HRESULT NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate); //处理的核心 //核心一般才有IMediaSample HRESULT Receive(IMediaSample *pIn); HRESULT CheckConnect(PIN_DIRECTION dir, IPin *pPin); HRESULT BreakConnect(PIN_DIRECTION dir); HRESULT CompleteConnect(PIN_DIRECTION dir, IPin *pReceivePin); int GetPinCount(); CBasePin* GetPin(int n); STDMETHODIMP JoinFilterGraph(IFilterGraph * pGraph, LPCWSTR pName); // ILAVVideoCallback STDMETHODIMP AllocateFrame(LAVFrame **ppFrame); STDMETHODIMP ReleaseFrame(LAVFrame **ppFrame); STDMETHODIMP Deliver(LAVFrame *pFrame); STDMETHODIMP_(LPWSTR) GetFileExtension(); STDMETHODIMP_(BOOL) FilterInGraph(PIN_DIRECTION dir, const GUID &clsid) { if (dir == PINDIR_INPUT) return FilterInGraphSafe(m_pInput, clsid); else return FilterInGraphSafe(m_pOutput, clsid); } STDMETHODIMP_(DWORD) GetDecodeFlags() { return m_dwDecodeFlags; } STDMETHODIMP_(CMediaType&) GetInputMediaType() { return m_pInput->CurrentMediaType(); } STDMETHODIMP GetLAVPinInfo(LAVPinInfo &info) { if (m_LAVPinInfoValid) { info = m_LAVPinInfo; return S_OK; } return E_FAIL; } STDMETHODIMP_(CBasePin*) GetOutputPin() { return m_pOutput; } STDMETHODIMP_(CMediaType&) GetOutputMediaType() { return m_pOutput->CurrentMediaType(); } STDMETHODIMP DVDStripPacket(BYTE*& p, long& len) { static_cast<CDeCSSTransformInputPin*>(m_pInput)->StripPacket(p, len); return S_OK; } STDMETHODIMP_(LAVFrame*) GetFlushFrame(); STDMETHODIMP ReleaseAllDXVAResources() { ReleaseLastSequenceFrame(); return S_OK; } public: // Pin Configuration const static AMOVIESETUP_MEDIATYPE sudPinTypesIn[]; const static int sudPinTypesInCount; const static AMOVIESETUP_MEDIATYPE sudPinTypesOut[]; const static int sudPinTypesOutCount; private: HRESULT LoadDefaults(); HRESULT ReadSettings(HKEY rootKey); HRESULT LoadSettings(); HRESULT SaveSettings(); HRESULT CreateTrayIcon(); HRESULT CreateDecoder(const CMediaType *pmt); HRESULT GetDeliveryBuffer(IMediaSample** ppOut, int width, int height, AVRational ar, DXVA2_ExtendedFormat dxvaExtFormat, REFERENCE_TIME avgFrameDuration); HRESULT ReconnectOutput(int width, int height, AVRational ar, DXVA2_ExtendedFormat dxvaExtFlags, REFERENCE_TIME avgFrameDuration, BOOL bDXVA = FALSE); HRESULT SetFrameFlags(IMediaSample* pMS, LAVFrame *pFrame); HRESULT NegotiatePixelFormat(CMediaType &mt, int width, int height); BOOL IsInterlaced(); HRESULT Filter(LAVFrame *pFrame); HRESULT DeliverToRenderer(LAVFrame *pFrame); HRESULT PerformFlush(); HRESULT ReleaseLastSequenceFrame(); HRESULT GetD3DBuffer(LAVFrame *pFrame); HRESULT RedrawStillImage(); HRESULT SetInDVDMenu(bool menu) { m_bInDVDMenu = menu; return S_OK; } enum {CNTRL_EXIT, CNTRL_REDRAW}; HRESULT ControlCmd(DWORD cmd) { return m_ControlThread->CallWorker(cmd); } private: friend class CVideoOutputPin; friend class CDecodeThread; friend class CLAVControlThread; friend class CLAVSubtitleProvider; friend class CLAVSubtitleConsumer; //解码线程 CDecodeThread m_Decoder; CAMThread *m_ControlThread; REFERENCE_TIME m_rtPrevStart; REFERENCE_TIME m_rtPrevStop; BOOL m_bForceInputAR; BOOL m_bSendMediaType; BOOL m_bFlushing; HRESULT m_hrDeliver; CLAVPixFmtConverter m_PixFmtConverter; std::wstring m_strExtension; DWORD m_bDXVAExtFormatSupport; DWORD m_bMadVR; DWORD m_bOverlayMixer; DWORD m_dwDecodeFlags; BOOL m_bInDVDMenu; AVFilterGraph *m_pFilterGraph; AVFilterContext *m_pFilterBufferSrc; AVFilterContext *m_pFilterBufferSink; LAVPixelFormat m_filterPixFmt; int m_filterWidth; int m_filterHeight; LAVFrame m_FilterPrevFrame; BOOL m_LAVPinInfoValid; LAVPinInfo m_LAVPinInfo; CLAVVideoSubtitleInputPin *m_pSubtitleInput; CLAVSubtitleConsumer *m_SubtitleConsumer; LAVFrame *m_pLastSequenceFrame; AM_SimpleRateChange m_DVDRate; BOOL m_bRuntimeConfig; struct VideoSettings { BOOL TrayIcon; DWORD StreamAR; DWORD NumThreads; BOOL bFormats[Codec_VideoNB]; BOOL bMSWMV9DMO; BOOL bPixFmts[LAVOutPixFmt_NB]; DWORD RGBRange; DWORD HWAccel; BOOL bHWFormats[HWCodec_NB]; DWORD HWAccelResFlags; DWORD HWDeintMode; DWORD HWDeintOutput; BOOL HWDeintHQ; DWORD DeintFieldOrder; LAVDeintMode DeintMode; DWORD SWDeintMode; DWORD SWDeintOutput; DWORD DitherMode; BOOL bDVDVideo; } m_settings; CBaseTrayIcon *m_pTrayIcon; #ifdef DEBUG FloatingAverage<double> m_pixFmtTimingAvg; #endif };
该类中包含了解码线程类:CDecodeThread m_Decoder;,这里封装了解码功能。
同时该类中包含了函数Receive(IMediaSample *pIn);,是发挥解码功能的函数,其中pIn是输入的解码前的视频压缩编码数据。
下面来看看Receive()函数:
//处理的核心 //核心一般才有IMediaSample HRESULT CLAVVideo::Receive(IMediaSample *pIn) { CAutoLock cAutoLock(&m_csReceive); HRESULT hr = S_OK; AM_SAMPLE2_PROPERTIES const *pProps = m_pInput->SampleProps(); if(pProps->dwStreamId != AM_STREAM_MEDIA) { return m_pOutput->Deliver(pIn); } AM_MEDIA_TYPE *pmt = NULL; //获取媒体类型等等 if (SUCCEEDED(pIn->GetMediaType(&pmt)) && pmt) { CMediaType mt = *pmt; DeleteMediaType(pmt); if (mt != m_pInput->CurrentMediaType() || !(m_dwDecodeFlags & LAV_VIDEO_DEC_FLAG_DVD)) { DbgLog((LOG_TRACE, 10, L"::Receive(): Input sample contained media type, dynamic format change...")); m_Decoder.EndOfStream(); hr = m_pInput->SetMediaType(&mt); if (FAILED(hr)) { DbgLog((LOG_ERROR, 10, L"::Receive(): Setting new media type failed...")); return hr; } } } m_hrDeliver = S_OK; // Skip over empty packets if (pIn->GetActualDataLength() == 0) { return S_OK; } //解码 hr = m_Decoder.Decode(pIn); if (FAILED(hr)) return hr; if (FAILED(m_hrDeliver)) return m_hrDeliver; return S_OK; }
由代码我们可以看出,实际发挥出解码功能的函数是hr = m_Decoder.Decode(pIn);。
下面我们来看看CDecodeThread类的Decode()方法:
//解码线程的解码函数 STDMETHODIMP CDecodeThread::Decode(IMediaSample *pSample) { CAutoLock decoderLock(this); if (!CAMThread::ThreadExists()) return E_UNEXPECTED; // Wait until the queue is empty while(HasSample()) Sleep(1); // Re-init the decoder, if requested // Doing this inside the worker thread alone causes problems // when switching from non-sync to sync, so ensure we're in sync. if (m_bDecoderNeedsReInit) { CAMThread::CallWorker(CMD_REINIT); while (!m_evEOSDone.Check()) { m_evSample.Wait(); ProcessOutput(); } } m_evDeliver.Reset(); m_evSample.Reset(); m_evDecodeDone.Reset(); pSample->AddRef(); // Send data to worker thread, and wake it (if it was waiting) PutSample(pSample); // If we don't have thread safe buffers, we need to synchronize // with the worker thread and deliver them when they are available // and then let it know that we did so if (m_bSyncToProcess) { while (!m_evDecodeDone.Check()) { m_evSample.Wait(); ProcessOutput(); } } ProcessOutput(); return S_OK; }
DecodeThread.h
/* 雷霄骅 * 中国传媒大学/数字电视技术 * [email protected] * */ /* * 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 "decoders/ILAVDecoder.h" #include "SynchronizedQueue.h" class CLAVVideo; class CDecodeThread : public ILAVVideoCallback, protected CAMThread, protected CCritSec { public: CDecodeThread(CLAVVideo *pLAVVideo); ~CDecodeThread(); // Parts of ILAVDecoder STDMETHODIMP_(const WCHAR*) GetDecoderName() { return m_pDecoder ? m_pDecoder->GetDecoderName() : NULL; } STDMETHODIMP_(long) GetBufferCount() { return m_pDecoder ? m_pDecoder->GetBufferCount() : 4; } STDMETHODIMP_(BOOL) IsInterlaced() { return m_pDecoder ? m_pDecoder->IsInterlaced() : TRUE; } STDMETHODIMP GetPixelFormat(LAVPixelFormat *pPix, int *pBpp) { ASSERT(m_pDecoder); return m_pDecoder->GetPixelFormat(pPix, pBpp); } STDMETHODIMP_(REFERENCE_TIME) GetFrameDuration() { ASSERT(m_pDecoder); return m_pDecoder->GetFrameDuration(); } STDMETHODIMP HasThreadSafeBuffers() { return m_pDecoder ? m_pDecoder->HasThreadSafeBuffers() : S_FALSE; } STDMETHODIMP CreateDecoder(const CMediaType *pmt, AVCodecID codec); STDMETHODIMP Close(); //解码线程的解码函数 STDMETHODIMP Decode(IMediaSample *pSample); STDMETHODIMP Flush(); STDMETHODIMP EndOfStream(); STDMETHODIMP InitAllocator(IMemAllocator **ppAlloc); STDMETHODIMP PostConnect(IPin *pPin); STDMETHODIMP_(BOOL) IsHWDecoderActive() { return m_bHWDecoder; } // ILAVVideoCallback STDMETHODIMP AllocateFrame(LAVFrame **ppFrame); STDMETHODIMP ReleaseFrame(LAVFrame **ppFrame); STDMETHODIMP Deliver(LAVFrame *pFrame); STDMETHODIMP_(LPWSTR) GetFileExtension(); STDMETHODIMP_(BOOL) FilterInGraph(PIN_DIRECTION dir, const GUID &clsid); STDMETHODIMP_(DWORD) GetDecodeFlags(); STDMETHODIMP_(CMediaType&) GetInputMediaType(); STDMETHODIMP GetLAVPinInfo(LAVPinInfo &info); STDMETHODIMP_(CBasePin*) GetOutputPin(); STDMETHODIMP_(CMediaType&) GetOutputMediaType(); STDMETHODIMP DVDStripPacket(BYTE*& p, long& len); STDMETHODIMP_(LAVFrame*) GetFlushFrame(); STDMETHODIMP ReleaseAllDXVAResources(); protected: //包含了对进程的各种操作,重要 DWORD ThreadProc(); private: STDMETHODIMP CreateDecoderInternal(const CMediaType *pmt, AVCodecID codec); STDMETHODIMP PostConnectInternal(IPin *pPin); STDMETHODIMP DecodeInternal(IMediaSample *pSample); STDMETHODIMP ClearQueues(); STDMETHODIMP ProcessOutput(); bool HasSample(); void PutSample(IMediaSample *pSample); IMediaSample* GetSample(); void ReleaseSample(); bool CheckForEndOfSequence(IMediaSample *pSample); private: //各种对进程进行的操作 enum {CMD_CREATE_DECODER, CMD_CLOSE_DECODER, CMD_FLUSH, CMD_EOS, CMD_EXIT, CMD_INIT_ALLOCATOR, CMD_POST_CONNECT, CMD_REINIT}; //注意DecodeThread像是一个处于中间位置的东西 //连接了Filter核心类CLAVVideo和解码器的接口ILAVDecoder CLAVVideo *m_pLAVVideo; ILAVDecoder *m_pDecoder; AVCodecID m_Codec; BOOL m_bHWDecoder; BOOL m_bHWDecoderFailed; BOOL m_bSyncToProcess; BOOL m_bDecoderNeedsReInit; CAMEvent m_evInput; CAMEvent m_evDeliver; CAMEvent m_evSample; CAMEvent m_evDecodeDone; CAMEvent m_evEOSDone; CCritSec m_ThreadCritSec; struct { const CMediaType *pmt; AVCodecID codec; IMemAllocator **allocator; IPin *pin; } m_ThreadCallContext; CSynchronizedQueue<LAVFrame *> m_Output; CCritSec m_SampleCritSec; IMediaSample *m_NextSample; IMediaSample *m_TempSample[2]; IMediaSample *m_FailedSample; std::wstring m_processName; };
其中第一个指针变量就是这个工程中最核心的类CLAVVideo,而第二个指针变量则是解码器的接口。通过这个接口就可以调用具体解码器的相应方法了。(注:在源代码中发现,解码器不光包含libavcodec,也可以是wmv9等等,换句话说,是可以扩展其他种类的解码器的。不过就目前的情况来看,lavvideo似乎不如ffdshow支持的解码器种类多)
该类里面还有一个函数:
ThreadProc()
该函数中包含了对线程的各种操作,其中包含调用了ILAVDecoder接口的各种方法:
//包含了对进程的各种操作 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; }