一文搞懂Codec2框架解析
1 前言–Codec2.0是什么
在Android Q之前,Android的兩套多媒體框架分別為MediaPlayer與MediaCodec,后者只負(fù)責(zé)解碼與渲染工作,解封裝工作由MediaExtractor代勞,MediaCodec經(jīng)由ACodec層調(diào)用第三方編解碼標(biāo)準(zhǔn)接口OpenMAX IL,實現(xiàn)硬件編解碼。芯片廠商只需要支持上Khronos 制定的OpenMAX接口,就可以實現(xiàn)MediaCodec的硬件編解碼。谷歌在Android Q上推出了Codec2.0,指在于取代ACodec與OpenMAX,它可以看作是一套新的對接MediaCodec的中間件,往上對接MediaCodec Native層,往下提供新的API標(biāo)準(zhǔn)供編解碼使用,相當(dāng)于ACodec 2.0。
2 Codec2.0框架
Codec2.0的代碼目錄位于/frameworks/av/media/codec2。目錄結(jié)構(gòu)如下:
codec2 |--components #具體編解碼組件與組件接口層 | |--base/SimpleC2Component.cpp | |--base/SimpleC2Interface.cpp | |--avc/C2SoftAvcDec.cpp |--core #存在核心的頭文件,譬如Buffer定義、Component定義、Config定義、Param定義 |--docs #暫時存放doxygen配置文件與腳本 |--faultinjection |--hidl #與hidl調(diào)用相關(guān)的實現(xiàn) |--client/client.cpp |--1.0/utils/Component.cpp |--1.0/utils/ComponentInterface.cpp |--1.0/utils/ComponentStore.cpp |--1.0/utils/Configurable.cpp |--1.0/utils/include/codec2/hidl/1.0/Component.h |--1.0/utils/include/codec2/hidl/1.0/Configurable.h |--1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h |--sfplugin #頂層接口與實現(xiàn)層 | |--CCodec.cpp | |--CCodec.h | |--CBufferChannel.cpp | |--CBufferChannel.h |--tests |--vndk #基礎(chǔ)的util實現(xiàn) | |--C2Store.cpp
sfplugin/CCodec.cpp是頂層實現(xiàn),它提供的接口為MediaCodec Native層所調(diào)用,與libstagefright/ACodec接口一致,都繼承于CodecBase,如下所示:
virtual std::shared_ptr<BufferChannelBase> getBufferChannel() override; virtual void initiateAllocateComponent(const sp<AMessage> &msg) override; virtual void initiateConfigureComponent(const sp<AMessage> &msg) override; virtual void initiateCreateInputSurface() override; virtual void initiateSetInputSurface(const sp<PersistentSurface> &surface) override; virtual void initiateStart() override; virtual void initiateShutdown(bool keepComponentAllocated = false) override; virtual status_t setSurface(const sp<Surface> &surface) override; virtual void signalFlush() override; virtual void signalResume() override; virtual void signalSetParameters(const sp<AMessage> ¶ms) override; virtual void signalEndOfInputStream() override; virtual void signalRequestIDRFrame() override; void onWorkDone(std::list<std::unique_ptr<C2Work>> &workItems); void onInputBufferDone(uint64_t frameIndex, size_t arrayIndex);
CCodec類中最重要的成員對象包括mChannel、mClient、mClientListener。mChannel是CCodecBufferChannel類,主要負(fù)責(zé)buffer的傳遞。mClient是Codec2Client類,提供了Codec 2.0的最精要的接口,它包括了四個子類,Listener、Configurable、Interface以及Component。Client.h頭文件對此有一段簡要的描述,可翻閱之。
Listener用于input buffer、output buffer以及error的回調(diào)。Interface提供配置與參數(shù)的交互接口,在component與CCodec之間。Component則是具體decoder/encoder component的代表。Interface與Component都是經(jīng)由ComponentStore創(chuàng)建而來,ComponentStore可以看作是對接Codec2Client的組件,該組件可以由不同的插件實現(xiàn),原生實現(xiàn)的是C2PlatformComponentStore,廠商可以通過實現(xiàn)自己的Store插件對接到ComponentStore,則完成了硬件編解碼在Codec 2.0的對接。
3 流程解析
CCodec類的對象關(guān)系如下圖所示:
Codec2Client的成員Component通過C2PlatformComponent而創(chuàng)建,C2ComponentStore是接口類。而在ClientListener這條通路上,是一條回調(diào)通路,從底往上回調(diào),分別經(jīng)過SimpleC2Component、Component::Listener、HidlListener以及ClientListener,到達(dá)CCodec,再回調(diào)到MediaCodec。
3.1 初始化流程
CCodec的初始化接口為initiateAllocateComponent,調(diào)用到內(nèi)部函數(shù)allocate,allocate做了許多工作,首先是調(diào)用到Codec2Client的接口CreateFromService,嘗試創(chuàng)建了一個服務(wù)名為default的Codec2Client客戶端(服務(wù)名為default的Codec2Client是廠商的Codec2Client),否則則創(chuàng)建服務(wù)名為software的Codec2Client,這是谷歌的原生Codec2Client,即,基于C2PlatformComponentStore的codec 2插件。如果能夠創(chuàng)建default Codec2Client,則會調(diào)用SetPreferredCodec2ComponentStore,將廠商的ComponentStore設(shè)置為默認(rèn)的codec 2插件。這樣子,codec2.0就不會走谷歌原生的軟編解碼器,而會走芯片廠商提供的編解碼器,通常是硬編硬解。
3.2 啟動流程
mChannel是MCodecBufferChannel類,它的start接口實現(xiàn)稍微復(fù)雜,主要是獲取AllocatorStore,再為input buffer與output buffer創(chuàng)建BlockPool,完成之后通過CCodec::mCallback回調(diào)告訴MediaCodec。接下來,初始化input buffer,開始調(diào)用queue接口送數(shù)據(jù)進(jìn)編解碼組件,原生組件為SimpleC2Component,具體可以送到C2SoftAvcDec,也可以送到C2SoftHevcDec,等等。
3.3 Input Buffer的回調(diào)
當(dāng)input buffer數(shù)據(jù)被消耗以后,onInputBuffersReleased通過IPC被調(diào)用,HidlListener繼而開始回調(diào)onInputBufferDone,Codec2Client是個接口類,實現(xiàn)類為CCodec::ClientListener,因而回調(diào)到了CCodec::ClientListener,往后通過CCodec,CCodecBufferChannel,CCodecBufferChannel在完成onInputBufferReleased與expireComponentBuffer之后,調(diào)用feedInputBufferAvailable繼續(xù)送空閑的Input Buffer給編解碼組件。
//client.cpp virtual Return<void> onInputBuffersReleased( const hidl_vec<InputBuffer>& inputBuffers) override { std::shared_ptr<Listener> listener = base.lock(); if (!listener) { LOG(DEBUG) << "onInputBuffersReleased -- listener died."; return Void(); } for (const InputBuffer& inputBuffer : inputBuffers) { LOG(VERBOSE) << "onInputBuffersReleased --" " received death notification of" " input buffer:" " frameIndex = " << inputBuffer.frameIndex << ", bufferIndex = " << inputBuffer.arrayIndex << "."; listener->onInputBufferDone( inputBuffer.frameIndex, inputBuffer.arrayIndex); } return Void(); }
onInputBuffersReleased究竟是怎么被觸發(fā)的,目前仍未追蹤到,在client.h中,有一段對Input Buffer管理的描述,說明了onInputBuffersReleased是一個IPC call。如下所示:
* InputBufferManager holds a collection of records representing tracked buffers * and their callback listeners. Conceptually, one record is a triple (listener, * frameIndex, bufferIndex) where * * - (frameIndex, bufferIndex) is a pair of indices used to identify the buffer. * - listener is of type IComponentListener. Its onInputBuffersReleased() * function will be called after the associated buffer dies. The argument of * onInputBuffersReleased() is a list of InputBuffer objects, each of which * has the following members: * * uint64_t frameIndex * uint32_t arrayIndex * * When a tracked buffer associated to the triple (listener, frameIndex, * bufferIndex) goes out of scope, listener->onInputBuffersReleased() will be * called with an InputBuffer object whose members are set as follows: * * inputBuffer.frameIndex = frameIndex * inputBuffer.arrayIndex = bufferIndex
3.4 Output Buffer的回調(diào)
這一條路就有點長了,難點在于Codec2Client::Listener與IComponentListener是接口類,分別由CCodec::ClientListener與Codec2Client::Component::HidlListener實現(xiàn),這會讓不熟悉C++的人一時半會摸不著頭腦。從這一條通路可以看出不同模塊的層次,HidleListener連接溝通了SimpleC2Component與Codec2Client,而Codec2Client是CCodec所調(diào)用的對象,CCodec將Buffer的管理都將由CodecBufferChannel打理,而CodecBufferChannel直接反饋于MediaCodec。
我們來看一下這條回調(diào)路上幾個類的關(guān)系。譬如,Component::Listener回調(diào)的時候,調(diào)用的是IComponentListener的接口,而IComponentListener實際由Codec2Client::Component::HidlListener繼承實現(xiàn),所以,實際上是調(diào)用到了HidlListener,故而用實線表示,虛函數(shù)的調(diào)用用虛線表示。
4 總結(jié)
在CCodec的幾個接口中,初始化、啟動、參數(shù)與配置交互、回調(diào)交互是比較復(fù)雜的流程,對于參數(shù)與配置交互,在OMX中是采用SetParameter、SetConfig、GetParameter、GetConfig來實現(xiàn)的,而在Codec2中,由ComponentInterface、C2Param一起完成,這塊留作下次研究。我們從頂至下,先明確頂層CCodec的接口,通過幾個接口的流程追蹤,梳理出各個類的關(guān)系,也了解了數(shù)據(jù)的回調(diào)流向,如此一來,后續(xù)分析代碼就有了框架層的認(rèn)識,不會陷入細(xì)節(jié)繞得團(tuán)團(tuán)轉(zhuǎn)。
到此這篇關(guān)于一文搞懂Codec2框架解析的文章就介紹到這了,更多相關(guān)Codec2框架解析內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VC實現(xiàn)動態(tài)菜單的創(chuàng)建方法
這篇文章主要介紹了VC實現(xiàn)動態(tài)菜單的創(chuàng)建方法,需要的朋友可以參考下2014-07-07一文帶你掌握C++中的移動語義和完美轉(zhuǎn)發(fā)
這篇文章主要為大家詳細(xì)介紹了C++中的移動語義和完美轉(zhuǎn)發(fā)的相關(guān)知識,文中的示例代碼講解詳細(xì),對我們深入掌握C++有一定的幫助,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12