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 有改变,只需要更新底层的动态链接库。
### CDVDVideoCodecStageFrigh t 类
支持 StageFright 的 CDVDVideoCodec 为 CDVDVideoCodecStageFrigh t:
xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecStageFright .h:
class CDVDVideoCodecStageFrigh t : 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 CDVDVideoCodecStageFrigh t(), 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:
CDVDVideoCodecStageFrigh t::CDVDVideoCodecStageFrigh t()
: CDVDVideoCodec()
, m_convert_bitstream(false), m_converter(NULL)
, m_stf_handle(NULL)
{
if (!m_stf_dll)
m_stf_dll = new DllLibStageFrightCodec;
}
然后调用它提供的接口函数:
bool CDVDVideoCodecStageFrigh t::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 CDVDVideoCodecStageFrigh t::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