XBMC 使用 Android StageFright 硬件解碼

轉載自:http://blog.csdn.net/mirkerson/article/details/40184803
XBMC 在 Android 平臺上,除了可以通過 Java MediaCodec API 使用硬件解碼功能,還可以直接調用 Android 媒體框架 StageFright 提供的 C++ API 訪問硬件解碼器。

StageFright 是 Android 在 4.0 (level 14) 上的媒體框架(http://source.android.com/devices/media.html),支持 OpenMAX 標準。在該媒體框架下,硬件廠商採用 OMX plugin 方式封裝 libstagefrighthw.so 私有庫,爲系統提供硬件編解碼器。Android 的 MediaPlayer 通過調用 StageFright API 爲 App 提供編解碼功能。

雖然 StageFright API 屬於非正式的 API,爲了較好的視頻播放效果,XBMC 也直接調用 Stagefright API,進行硬件解碼。

## XBMC 調用 StageFright API

XBMC 使用 StageFright 分兩步:首先封裝一個通用類,供程序上層使用統一接口調用;再將與 Android 底層
相關的 StageFright 代碼封裝到一個單獨的動態鏈接庫,並提供自己的 API。隨着 Android 平臺的升級,如果 StageFright API 有改變,只需要更新底層的動態鏈接庫。

### CDVDVideoCodecStageFright 類

支持 StageFright 的 CDVDVideoCodec 爲 CDVDVideoCodecStageFright:

    xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecStageFright.h:
    
    class CDVDVideoCodecStageFright : public CDVDVideoCodec

播放視頻時,XBMC 根據系統和軟件設置,決定是否使用 Codec Factory 創建 StageFright 解碼器

    xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp:
    
    CDVDVideoCodec* CDVDFactoryCodec::CreateVideoCodec(CDVDStreamInfo &hint, unsigned int surfaces, const std::vector& formats)
    {
      CDVDVideoCodec* pCodec = NULL;

    #if defined(HAS_LIBSTAGEFRIGHT)
      hwSupport += "libstagefright:yes ";
    #elif defined(_LINUX)
      hwSupport += "libstagefright:no ";
    #endif

    #if defined(HAS_LIBSTAGEFRIGHT)
      if (!hint.software && CSettings::Get().GetBool("videoplayer.usestagefright"))
        {
            switch(hint.codec)
            {
              case CODEC_ID_H264:
              case CODEC_ID_MPEG4:
              case CODEC_ID_MPEG2VIDEO:
              case CODEC_ID_VC1:
              case CODEC_ID_WMV3:
              case CODEC_ID_VP3:
              case CODEC_ID_VP6:
              case CODEC_ID_VP6F:
              case CODEC_ID_VP8:
                if ( (pCodec = OpenCodec(new CDVDVideoCodecStageFright(), hint, options)) ) return pCodec;
                break;
              default:
                break;
            }
        }
    #endif
    }

    CDVDVideoCodec* CDVDFactoryCodec::OpenCodec(CDVDVideoCodec* pCodec, CDVDStreamInfo &hints, CDVDCodecOptions &options )
    {
        if( pCodec->Open( hints, options ) )
        {
          CLog::Log(LOGDEBUG, "FactoryCodec - Video: %s - Opened", pCodec->GetName());
          return pCodec;
        }
      return NULL;
    }

在 DVDVideoCodecStageFright 中,加載動態鏈接庫 libXBMCvcodec_stagefrightICS-arm.so

    xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecStagefright.cpp:

    CDVDVideoCodecStageFright::CDVDVideoCodecStageFright()
      : CDVDVideoCodec()
      , m_convert_bitstream(false),  m_converter(NULL)
      , m_stf_handle(NULL)
    {
      if (!m_stf_dll)
        m_stf_dll = new DllLibStageFrightCodec;
    }

然後調用它提供的接口函數:

    bool CDVDVideoCodecStageFright::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
    {
        m_stf_handle = m_stf_dll->create_stf(&g_application, &CApplicationMessenger::Get(), &g_Windowing, &g_advancedSettings);

        if (!m_stf_dll->stf_Open(m_stf_handle, hints))
        {
          Dispose();
          return false;
        }
        return true;
    }

    int CDVDVideoCodecStageFright::Decode(uint8_t *pData, int iSize, double dts, double pts)
    {
      rtn = m_stf_dll->stf_Decode(m_stf_handle, demuxer_content, demuxer_bytes, dts, pts);
      return rtn;
    }
    
### 封裝 StageFright 的動態鏈接庫 libXBMCvcodec_stagefrightICS-arm.so

在它的 Makefile 中,可以看到它依賴 libstagefright.so

    LIBS += -landroid -lEGL -lGLESv2 -L${prefix}/opt/android-libs -lstdc++ -lutils -lcutils -lstagefright -lbinder -lui -lgui

該動態鏈接庫中定義了一套自己的接口函數來使用解碼器:

    xbmc/cores/dvdplayer/DVDCodecs/Video/libstagefrightICS/StageFrightInterfac.cpp

    extern "C"
    {
      void* create_stf(CApplication* application, CApplicationMessenger* applicationMessenger, CWinSystemEGL* windowing, CAdvancedSettings* advsettings);
      void destroy_stf(void*);

      bool stf_Open(void*, CDVDStreamInfo &hints);
      void stf_Dispose(void*);
      int  stf_Decode(void*, uint8_t *pData, int iSize, double dts, double pts);
      void stf_Reset(void*);
      bool stf_GetPicture(void*, DVDVideoPicture *pDvdVideoPicture);
      bool stf_ClearPicture(void*, DVDVideoPicture* pDvdVideoPicture);
      void stf_SetDropState(void*, bool bDrop);
      void stf_SetSpeed(void*, int iSpeed);

      void stf_LockBuffer(void*, EGLImageKHR eglimg);
      void stf_ReleaseBuffer(void*, EGLImageKHR eglimg);
    }

在接口函數實現代碼中,解碼功能又通過 CStageFrightVideo 對象完成。

    xbmc/cores/dvdplayer/DVDCodecs/Video/libstagefrightICS/StageFrightInterfac.cpp

    void* create_stf(CApplication* application, CApplicationMessenger* applicationMessenger, CWinSystemEGL* windowing, CAdvancedSettings* advsettings)
    {
      return (void*)new CStageFrightVideo(application, applicationMessenger, windowing, advsettings);
    }

    bool stf_Open(void* stf, CDVDStreamInfo &hints)
    {
      return ((CStageFrightVideo*)stf)->Open(hints);
    }

    int  stf_Decode(void* stf, uint8_t *pData, int iSize, double dts, double pts)
    {
      return ((CStageFrightVideo*)stf)->Decode(pData, iSize, dts, pts);
    }

最後,所有直接調用 Stagefright API 的操作都被封裝到 CStageFrightVideo 類中。

    xbmc/cores/dvdplayer/DVDCodecs/Video/libstagefrightICS/StageFrightVideo.h

    class CStageFrightVideo
    {
    public:
      CStageFrightVideo(CApplication* application, CApplicationMessenger* applicationMessenger, CWinSystemEGL* windowing, CAdvancedSettings* advsettings);
      virtual ~CStageFrightVideo();

      bool Open(CDVDStreamInfo &hints);
      void Dispose(void);
      int  Decode(uint8_t *pData, int iSize, double dts, double pts);
      void Reset(void);
      bool GetPicture(DVDVideoPicture *pDvdVideoPicture);
      bool ClearPicture(DVDVideoPicture* pDvdVideoPicture);
      void SetDropState(bool bDrop);
      virtual void SetSpeed(int iSpeed);

      void LockBuffer(EGLImageKHR eglimg);
      void ReleaseBuffer(EGLImageKHR eglimg);

    private:
      CStageFrightVideoPrivate* p;
    };

通過 StageFright API 使用解碼器也分爲三個步驟:

1. 創建解碼器

主要調用 StageFright 中 OMXClient 的 connect() 和 OMXCodec 的 Create() 函數,同時創建解碼線程。

    xbmc/cores/dvdplayer/DVDCodecs/Video/libstagefrightICS/StageFrightVideo.cpp

    CStageFrightVideo::CStageFrightVideo(CApplication* application, CApplicationMessenger* applicationMessenger, CWinSystemEGL* windowing, CAdvancedSettings* advsettings)
    {
      p = new CStageFrightVideoPrivate;
      ...
    }

    bool CStageFrightVideo::Open(CDVDStreamInfo &hints)
    {
      p->source    = new CStageFrightMediaSource(p, p->meta);
      p->client    = new OMXClient;

      if (p->client->connect() !=  OK)
      {
        return false;
      }

      p->decoder  = OMXCodec::Create(p->client->interface(), p->meta,
                                             false, p->source, NULL,
                                             OMXCodec::kHardwareCodecsOnly | (p->quirks & QuirkSWRender ? OMXCodec::kClientNeedsFramebuffer : 0),
                                             p->mVideoNativeWindow
                                             );

      if (!(p->decoder != NULL && p->decoder->start() ==  OK))
      {
        return false;
      }

      p->decode_thread = new CStageFrightDecodeThread(p);
      p->decode_thread->Create(true );
    }

2. 將編碼的數據傳給解碼器

編碼數據由 Decode() 函數複製到 in_queue 中的空閒緩存

    int  CStageFrightVideo::Decode(uint8_t *pData, int iSize, double dts, double pts)
    {
      Frame *frame;
      int demuxer_bytes = iSize;
      uint8_t *demuxer_content = pData;
      int ret = 0;

      if (demuxer_content)
      {
        frame = (Frame*)malloc(sizeof(Frame));
        frame->status  = OK;
        frame->medbuf = p->getBuffer(demuxer_bytes);
        fast_memcpy(frame->medbuf->data(), demuxer_content, demuxer_bytes);

        p->in_mutex.lock();
        p->in_queue.push_back(frame);
        p->in_condition.notify();
        p->in_mutex.unlock();
      }
    }

創建解碼器時傳入的 p->source 對象,其類 CStageFrightMediaSource 提供的 read() 函數,負責將 in_queue 隊列中的緩存取出交給 OMXCodec 解碼器處理。

    class CStageFrightMediaSource : public MediaSource
    {
      virtual status_t read(MediaBuffer **buffer,
                            const MediaSource::ReadOptions *options)
      {
        Frame *frame;
        status_t ret;
        *buffer = NULL;

        std::list
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章