XBMC 使用 Android StageFright 硬件解码

本文介绍XBMC如何利用Android平台的StageFright框架实现硬件解码,包括使用StageFright API的具体步骤和涉及的关键类及函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值