LAV Filter 源代码分析 4: LAV Video (2)

来源:互联网 发布:telnet端口23在哪 编辑:程序博客网 时间:2024/05/19 19:44

上一篇文章分析了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 16typedef 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()

// ILAVDecoderSTDMETHODIMP 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()

//销毁解码器,各种FreeSTDMETHODIMP 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;}



原创粉丝点击