Android播放器框架分析 1 – Android移動開發技術文章_手機開發 Android移動開發教學課程

Author:Aya

Date:2011-08-03

the1soft
 

Java層 要開啟一個播放器進行播放, 需要以下幾行代碼:

Java代碼
MediaPlayer mp = new MediaPlayer(); 
mp.setDisplay (…); /// 設置播放器Suface 
mp.setDataSource(PATH_TO_FILE); ///設置媒體URI 
mp.prepare(); /// 初始化播放器 
mp.start(); /// 開始播放 
 MediaPlayer 的Native 層 實際上是由 stagefright 模塊 以及 OMX 模塊組成,  其中stagefright 依賴OMX進行編解碼. (據說 stagefright 和OMX 本質上是利用OpenBinder進行通訊,不過我沒研究這裡)

 

1.  AwesomePlayer

忽略掉 JNI 封裝層, Stagefright 從 AP開始. AP 是Stagefright核心類. AP有一些接口甚至與MediaPlayer 是一一對應的例如setDataSource, prepare.

1.1  關鍵成員分析

OMXClient mClient

AP利用OMXClient 跟OMX IL進行通信. 這裡OMX IL類似於一個服務端. AP 作為一個客戶端, 請求OMX IL進行解碼的工作.

TimedEventQueue mQueue

AP采用定時器隊列的方式進行運作. mQueue 在MediaPlayer調用 prepare的時候就開始運作, (而不是MediaPlayer.start).

C++代碼
status_t AwesomePlayer::prepareAsync_l() { 
    if (!mQueueStarted) { 
        mQueue.start(); 
        mQueueStarted = true; 
    } 
    return OK; 

AP處理瞭幾個定時器事件, 包括 onVideoEvent(); onStreamDone(); onBufferingUpdate();onCheckAudioStatus() ; onPrepareAsyncEvent();

 

sp<ISurface> mISurface;

供播放器渲染的畫佈

 

sp<AwesomeRenderer> mVideoRenderer;

負責將解碼後的圖片渲染輸出

 

sp<MediaSource> mVideoTrack;  sp<MediaSource> mAudioTrack;

分別代表一個視頻軌道和音頻軌道,  用於提取視頻幀和音頻幀. mVideoTrack和mAudioTrack 在 onPrepareAsyncEvent事件被觸發時, 由MediaExtractor分離出來.

C++代碼
void AwesomePlayer::onPrepareAsyncEvent() {  
        status_t err = finishSetDataSource_l();  

 

C++代碼
status_t AwesomePlayer::finishSetDataSource_l() {  
 sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);  
    return setDataSource_l(extractor);  
}  
 

C++代碼
status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {  
    for (size_t i = 0; i < extractor->countTracks(); ++i) {  
        sp<MetaData> meta = extractor->getTrackMetaData(i);  
 
        const char *mime;  
        CHECK(meta->findCString(kKeyMIMEType, &mime));  
 
        if (!haveVideo && !strncasecmp(mime, "video/", 6)) {  
            setVideoSource(extractor->getTrack(i));   //
            haveVideo = true;  
        } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {  
            setAudioSource(extractor->getTrack(i));  
            haveAudio = true;  
        if (haveAudio && haveVideo) {  
            break;  
        }  
    }  

從上面這段代碼可以看到AwesomePlayer默認采用第一個VideoTrack和第一個AudioTrack, 那如何切換VideoTrack和AudioTrack?

 

sp<MediaSource> mVideoSource;

mVideoSource 可以認為是一個視頻解碼器的封裝, 用於產生視頻圖像供AwesomeRender渲染,  mVideoSource的數據源則由mVideoTrack提供.

mVideoSource 由OMXCodec創建.

C++代碼
status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {  
    mVideoSource = OMXCodec::Create(  
            mClient.interface(), mVideoTrack->getFormat(),  
            false, // createEncoder  
            mVideoTrack,  
            NULL, flags);  
}  
 

sp<MediaSource> mAudioSource; sp<MediaPlayerBase::AudioSink> mAudioSink; AudioPlayer *mAudioPlayer;

mAudioSource 也可以認為是一個音頻解碼器的封裝

mAudioSink 代表一個音頻輸出設備. 用於播放解碼後的音頻數據. (AudioSink is used for in-memory decode and potentially other applications where output doesn't go straight to hardware)

mAudioPlayer 把mAudioSource和mAudioSink 包起來,完成一個音頻播放器的功能. 如start, stop, pause, seek 等.

AudioPlayer和 AudioSink通過Callback建立關聯. 當AudioSink可以輸出音頻時,會通過回調通知AudioPlayer填充音頻數據. 而此時AudioPlayer 會嘗試從AudioSource 讀取音頻數據.

 

總結: 從關鍵的成員可以看出, AwesomePlayer 擁有視頻源和音頻源 (VideoTrack, AudioTrack), 有音視頻解碼器(VideoSoure, AudioSource), 可以渲染圖像 (AwesomeRenderer) , 可以輸出聲音 (AudioSink), 具備一個播放器完整的材料瞭.

1.2 基本播放流程

1.2.1 設置數據源URI

C++代碼
status_t AwesomePlayer::setDataSource_l(  
        const char *uri, const KeyedVector<String8, String8> *headers) {  
    /// 這裡隻是把URL保存起來而已, 真正的工作在Prepare之後進行  
    mUri = uri;  
    return OK;  

 

1.2.2 開啟定時器隊列,並且 Post一個AsyncPrepareEvent 事件

C++代碼
status_t AwesomePlayer::prepareAsync_l() {  
 
    /// 開啟定時器隊列  
    mQueue.start();  
 
    /// Post AsyncPrepare 事件  
    mAsyncPrepareEvent = new AwesomeEvent(  
            this, &AwesomePlayer::onPrepareAsyncEvent);  
 
    mQueue.postEvent(mAsyncPrepareEvent);  
    return OK;  
}  
Prepare 之後,  AwesomePlayer 開始運作.

 

1.2.3 AsyncPrepare 事件被觸發

當這個事件被觸發時, AwesomePlayer 開始創建 VideoTrack和AudioTrack , 然後創建 VideoDecoder和AudioDecoder

C++代碼
void AwesomePlayer::onPrepareAsyncEvent() {  
    /// a. 創建視頻源和音頻源  
    finishSetDataSource_l();  
 
    /// b. 創建視頻解碼器  
    initVideoDecoder();  
      
    /// c. 創建音頻解碼器  
    initAudioDecoder();  
}  
至此,播放器準備工作完成, 可以開始播放瞭

 

1.2.4 Post 第一個VideoEvent

AwesomePlayer::play() 調用 -> AwesomePlayer::play_l() 調用 -> AwesomePlayer::postVideoEvent_l(int64_t delayUs)

 

C++代碼
void AwesomePlayer::postVideoEvent_l(int64_t delayUs) {  
    mQueue.postEventWithDelay(mVideoEvent, delayUs < 0 ? 10000 : delayUs);  
}  
1.2.5 VideoEvent 被觸發

C++代碼
void AwesomePlayer::onVideoEvent() {  
 
    /// 從視頻解碼器中讀出視頻圖像  
    mVideoSource->read(&mVideoBuffer, &options);  
 
    /// 創建AwesomeRenderer (如果沒有的話)  
    if (mVideoRendererIsPreview || mVideoRenderer == NULL) {  
        initRenderer_l();  
    }  
 
    /// 渲染視頻圖像  
     mVideoRenderer->render(mVideoBuffer);  
 
    /// 再次發送一個VideoEvent, 這樣播放器就不停的播放瞭  
     postVideoEvent_l();  
}  
 

 總結:   SetDataSource -> Prepare -> Play -> postVieoEvent -> OnVideoEvent -> postVideoEvent-> …. onVideoEvent-> postStreamDoneEvent -> 播放結束

1.3  視頻 / 音頻 分離

1.3.1 創建DataSource

如前面提到的, 當AsyncPrepare 事件被觸發時, AwesomePlayer會調用 finishSetDataSource_l 創建 VideoTrack 和 AudioTrack.

finishSetDataSource_l  通過URI前綴判斷媒體類型, 比如 http, rtsp,或者本地文件等  
這裡的uri就是一開始 通過setDataSource設置的  
根據uri 創建相應的DataSource, 再進一步的利用 DataSource 創建MediaExtractor做A/V分離

C++代碼
status_t AwesomePlayer::finishSetDataSource_l() {  
    sp<DataSource> dataSource;  
    /// 通過URI前綴判斷媒體類型, 比如 http, rtsp,或者本地文件等  
     /// 這裡的uri就是一開始 通過setDataSource設置的  
     /// 根據uri 創建相應的MediaExtractor  
 
    if (!strncasecmp("https://", mUri.string(), 7)) {  
        mConnectingDataSource = new NuHTTPDataSource;  
        mConnectingDataSource->connect(mUri, &mUriHeaders);  
        mCachedSource = new NuCachedSource2(mConnectingDataSource);  
        dataSource = mCachedSource;  
    } else if (!strncasecmp("rtsp://", mUri.string(), 7)) {  
        mRTSPController->connect(mUri.string());  
        sp<MediaExtractor> extractor = mRTSPController.get();  
          
        /// rtsp 比較特殊, MediaExtractor對象的創建不需要DataSource  
        return setDataSource_l(extractor);  
    } else {  
        /// 本地文件  
        dataSource = DataSource::CreateFromURI(mUri.string(), &mUriHeaders);  
    }  
 
    /// 用dataSource創建一個MediaExtractor用於A/V分離       
    sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);  
 
    return setDataSource_l(extractor);  

 

1.3.2 根據DataSource 創建MediaExtractor

先看看 AwesomePlayer 的構造函數,裡面有一行代碼.

C++代碼
AwesomePlayer::AwesomePlayer(){  
    //…  
    DataSource::RegisterDefaultSniffers();  
    //…  
}  
RegisterDefaultSniffers 註冊瞭一些瞭媒體的MIME類型的探測函數.

C++代碼
void DataSource::RegisterDefaultSniffers() {  
    RegisterSniffer(SniffMPEG4);  
    RegisterSniffer(SniffMatroska);  
    RegisterSniffer(SniffOgg);  
    RegisterSniffer(SniffWAV);  
    RegisterSniffer(SniffAMR);  
    RegisterSniffer(SniffMPEG2TS);  
    RegisterSniffer(SniffMP3);  
    RegisterSniffer(SniffTCCMedia); // TELECHIPS, SSG  

這些探測用於判斷媒體的MIME類型, 進而決定要創建什麼樣的MediaExtractor.

再回到 MediaExtractor::Create, MediaExtractor對象在這裡創建. 下面代碼有點長, 其實這段代碼隻是根據MIME類型創建Extractor的各個分支而已.

TCC在這個函數添加瞭一點代碼, 用於創建自己的Extractor 對象.

C++代碼
sp<MediaExtractor> MediaExtractor::Create(  
        const sp<DataSource> &source, const char *mime) {  
    sp<AMessage> meta;  
 
    String8 tmp;  
    if (mime == NULL) {  
        float confidence;  
        ///調用之前註冊的MIME探測函數判斷MIME類型  
        if (!source->sniff(&tmp, &confidence, &meta)) {  
            return NULL;  
        }  
 
        mime = tmp.string();  
    }  
 
    if ((!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)  
            || !strcasecmp(mime, "audio/mp4"))  
            // TELECHIPS, SSG, only use MPEG4Extractor for HTTP source  
            && (source->flags() & DataSource::kIsCachingDataSource)) {  
        return new MPEG4Extractor(source);  
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {  
        return new MP3Extractor(source, meta);  
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)  
            || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {  
        return new AMRExtractor(source);  
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) {  
        return new WAVExtractor(source);  
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) {  
        return new OggExtractor(source);  
    // TELECHIPS, SSG  
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WEBM)) {  
        return new MatroskaExtractor(source);  
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {  
        return new MPEG2TSExtractor(source);  
    }  
    // TELECHIPS, SSG  
    else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_FLAC)  
            || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_APE)  
            || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MP2)  
            || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_RA)  
            || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WMA)  
            || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_AAC)  
            || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_AVI)  
            || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)  
            || !strcasecmp(mime, "audio/mp4")  
            || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WMV)  
            || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_ASF)  
            || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)  
            || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPG)  
            || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MOV)  
            || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_FLV)  
            || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_DIVX)  
            || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_RM)  
            || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_TS)) {  
        ///ITCCMediaExtractor是TCC專有MediaExtractor  
        ///可惜他沒有公開代碼, 所以A/V分離失敗的時候  
         ///並不知道失敗的原因  
        return new ITCCMediaExtractor(source);  
    }  
 
    return NULL;  

 (可以看到幾乎所有封裝格式都支持. 那為什麼播放 Asf +H264的文件會失敗呢? 隻能懷疑ITCCMediaExtractor實現的有問題瞭)

 

 1.3.3 根據MediaExtractor 做A/V分離

再看看 setDataSource_l(const sp<MediaExtractor> &extractor) , 這是A/V分離的最後步驟.

C++代碼
status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {  
 
    /// 從全部的Track中選取一個Video Track和一個AudioTrack  
    for (size_t i = 0; i < extractor->countTracks(); ++i) {  
        sp<MetaData> meta = extractor->getTrackMetaData(i);  
 
        const char *mime;  
        CHECK(meta->findCString(kKeyMIMEType, &mime));  
 
        if (!haveVideo && !strncasecmp(mime, "video/", 6)) {  
            setVideoSource(extractor->getTrack(i));  
            haveVideo = true;  
        } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {  
            setAudioSource(extractor->getTrack(i));  
            haveAudio = true;  
        }  
 
        if (haveAudio && haveVideo) {  
            break;  
        }  
    }  
 
    /// Flags 標志這個媒體的一些屬性:  
     /// CAN_SEEK_BACKWARD 是否能後退10秒  
    /// CAN_SEEK_FORWARD 是否能前進10秒  
    /// CAN_SEEK 能否Seek?  
    /// CAN_PAUSE 能否暫停  
    mExtractorFlags = extractor->flags();  
    return OK;  
}  
從這個函數可以看到MediaExtractor 需要實現的基本比較重要的接口 (這個幾個接口都是純虛函數, 可見Extractor的子類是一定要搞定它們的)

virtual size_t countTracks() = 0; /// 該媒體包含瞭幾個Track?

virtual sp<MediaSource> getTrack(size_t index) = 0;  /// 獲取指定的Video/Audio Track,  可以看到一個Track本質上就是一個MediaSource.

virtual sp<MetaData> getTrackMetaData ( size_t index, uint32_t flags = 0) = 0; ///獲取指定的Track的MetaData. 在AwesomePlayer 中, MetaData 實際上就是一塊可以任意信息字段的叉燒, 字段類型可以是字符串或者是整形等等.這裡Track的MetaData包含瞭Track的MIME類型. 這樣AwesomePlayer就可以知道這個Track是Video 還是Audio的瞭.

總結: 至此, AwesomePlayer 就擁有VideoTrack 和AudioTrack瞭 (可能隻有VideoTrack或者隻有AudioTrack, 例如MP3).  接下來 音視頻解碼器 VideoSource/AudioSource 將從Video/Audio Track 中讀取數據進行解碼.

 

1.4  創建視頻解碼器

VideoTrack/AudioTrack 創建完畢之後, 緊接著就是創建 VideoSource瞭 (見 1.2.3). 看看initVideoDecoder

C++代碼
status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {  
    mVideoSource = OMXCodec::Create(  
            mClient.interface(), mVideoTrack->getFormat(),  
            false, // createEncoder  
            mVideoTrack,  
            NULL, flags);  
    /// …  
    return mVideoSource != NULL ? OK : UNKNOWN_ERROR;  
}  
VideoSource 是由 OMXCodec::Create 創建的. 從OMXCodec::Create的參數可以看出創建一個視頻解碼器需要什麼材料:

a. OMXClient. 用於跟OMX IL 通訊. 假如最後用的是OMXCodec 也不是SoftCodec的話, 需要用到它.

b. mVideoTrack->getFormat ().  getFormat返回包含該video track格式信息的MetaData.

c. mVideoTrack. 如前面1.3.3 說的. 解碼器會從 Video Track 中讀取數據進行解碼.

 

1.4.1 OMXCodec::Create

C++代碼
sp<MediaSource> OMXCodec::Create(  
        const sp<IOMX> &omx,  
        const sp<MetaData> &meta, bool createEncoder,  
        const sp<MediaSource> &source,  
        const char *matchComponentName,  
        uint32_t flags) {  
 
    /// 獲取MIME類型  
    const char *mime;  
    bool success = meta->findCString(kKeyMIMEType, &mime);  
 
    /// 根據MIME找出可能匹配的Codec  
    Vector<String8> matchingCodecs;  
    findMatchingCodecs(  
            mime, createEncoder, matchComponentName, flags, &matchingCodecs);  
 
    IOMX::node_id node = 0;  
 
    /// 對每一種可能匹配的Codec, 嘗試申請Codec  
    const char *componentName;  
    for (size_t i = 0; i < matchingCodecs.size(); ++i) {  
        componentName = matchingCodecs[i].string();  
 
        /// 嘗試申請軟Codec  
        sp<MediaSource> softwareCodec = createEncoder?  
            InstantiateSoftwareEncoder(componentName, source, meta):  
            InstantiateSoftwareCodec(componentName, source);  
 
        if (softwareCodec != NULL) {  
            return softwareCodec;  
        }  
 
 
        /// 嘗試申請OMXCodec  
        status_t err = omx->allocateNode(componentName, observer, &node);  
        if (err == OK) {  
            sp<OMXCodec> codec = new OMXCodec(  
                    omx, node, quirks,  
                    createEncoder, mime, componentName,  
                    source);  
              
            /// 配置申請出來的OMXCodec  
            err = codec->configureCodec(meta, flags);  
            if (err == OK) {  
                return codec;  
            }  
        }  
    }  
 
    return NULL;  

 

1.4.2 OMXCodec::findMatchingCodecs 找出可能匹配的Codec

findMatchingCodecs 根據傳入的MIME 從kDecoderInfo 中找出MIME對於的Codec名 (一種MIME可能對應多種Codec)

C++代碼
void   
OMXCodec::findMatchingCodecs(  
        const char *mime,  
        bool createEncoder, const char *matchComponentName,  
        uint32_t flags,  
        Vector<String8> *matchingCodecs) {  
 
    for (int index = 0;; ++index) {  
        const char *componentName;  
 
    componentName = GetCodec(  
                    kDecoderInfo,  
                    sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]),  
                    mime, index);  
 
 
        matchingCodecs->push(String8(componentName));  
    }  

看看 kDecoderInfo 裡面包含瞭什麼Codec吧, 有點長.

C++代碼
static const CodecInfo kDecoderInfo[] = {  
    { MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" },  
//    { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" },  
    { MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder" },  
//    { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.PV.mp3dec" },  
//    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.decode" },  
    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBDecoder" },  
//    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.PV.amrdec" },  
    { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.decode" },  
    { MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBDecoder" },  
//    { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.PV.amrdec" },  
    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },  
    { MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" },  
//    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacdec" },  
    { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "G711Decoder" },  
    { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "G711Decoder" },  
    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7×30.video.decoder.mpeg4" },  
    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" },  
    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },  
    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.SEC.MPEG4.Decoder" },  
    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TCC.mpeg4dec" },  
    { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" },  
//    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4dec" },  
    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7×30.video.decoder.h263" },  
    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" },  
    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.SEC.H263.Decoder" },  
    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TCC.h263dec" },  
    { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" },  
//    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263dec" },  
    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7×30.video.decoder.avc" },  
    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" },  
    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },  
    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.SEC.AVC.Decoder" },  
    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TCC.avcdec" },  
    { MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" },  
//    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcdec" },  
    { MEDIA_MIMETYPE_AUDIO_VORBIS, "VorbisDecoder" },  
    { MEDIA_MIMETYPE_VIDEO_VPX, "VPXDecoder" },  
 
    // TELECHIPS, SSG  
    { MEDIA_MIMETYPE_AUDIO_MPEG_TCC, "OMX.TCC.mp3dec" },  
    { MEDIA_MIMETYPE_AUDIO_AAC_TCC, "OMX.TCC.aacdec" },  
    { MEDIA_MIMETYPE_AUDIO_VORBIS_TCC, "OMX.TCC.vorbisdec" },  
    { MEDIA_MIMETYPE_AUDIO_WMA, "OMX.TCC.wmadec" },  
    { MEDIA_MIMETYPE_AUDIO_AC3, "OMX.TCC.ac3dec" },  
    { MEDIA_MIMETYPE_AUDIO_RA, "OMX.TCC.radec" },  
    { MEDIA_MIMETYPE_AUDIO_FLAC, "OMX.TCC.flacdec" },  
    { MEDIA_MIMETYPE_AUDIO_APE, "OMX.TCC.apedec" },  
    { MEDIA_MIMETYPE_AUDIO_MP2, "OMX.TCC.mp2dec" },  
    { MEDIA_MIMETYPE_AUDIO_PCM, "OMX.TCC.pcmdec" },  
    { MEDIA_MIMETYPE_AUDIO_DTS, "OMX.TCC.dtsdec" },  
 
    { MEDIA_MIMETYPE_VIDEO_VC1, "OMX.TCC.wmvdec" },  
    { MEDIA_MIMETYPE_VIDEO_WMV12, "OMX.TCC.wmv12dec" },  
    { MEDIA_MIMETYPE_VIDEO_RV, "OMX.TCC.rvdec" },  
    { MEDIA_MIMETYPE_VIDEO_DIVX, "OMX.TCC.pxdec" },  
    { MEDIA_MIMETYPE_VIDEO_MPEG2, "OMX.TCC.mpeg2dec" },  
    { MEDIA_MIMETYPE_VIDEO_MJPEG, "OMX.TCC.mjpegdec" },  
    { MEDIA_MIMETYPE_VIDEO_FLV1, "OMX.TCC.flv1dec" },  
}; 
可以看到MPEG4就對應瞭6種Codec.

1.4.3 InstantiateSoftwareCodec 創建軟編碼器

InstantiateSoftwareCodec  從 kFactoryInfo (軟編碼器列表) 挑挑看有沒有. 有的話就創建一個軟編碼器. 看看kFactoryInfo 裡面有哪些軟編碼器

C++代碼
static const FactoryInfo kFactoryInfo[] = {  
    FACTORY_REF(MP3Decoder)  
    FACTORY_REF(AMRNBDecoder)  
    FACTORY_REF(AMRWBDecoder)  
    FACTORY_REF(AACDecoder)  
    FACTORY_REF(AVCDecoder)  
    FACTORY_REF(G711Decoder)  
    FACTORY_REF(M4vH263Decoder)  
    FACTORY_REF(VorbisDecoder)  
    FACTORY_REF(VPXDecoder)  
}; 
 

1.4.4 編碼器名稱的一點說明

OMX.XXX.YYY

中間的XXX是廠商名稱. 如OMX.TI.Video.Decoder 就是TI 芯片的硬視頻解碼器. 而 OMX.TCC.avcdec 則是TCC的AVC 視頻解碼器.  沒有OMX開頭的,說明是軟解碼器.

以AVC為例:

    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7×30.video.decoder.avc" },
    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" },
    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },
    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.SEC.AVC.Decoder" },
    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TCC.avcdec" },
    { MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" },

可以看到軟解碼器被放到最後. 這樣的話後面嘗試申請解碼器的時候便會優先申請硬Codec. 除非硬Codec不存在.

 

1.4.5 創建OMXCodec

申請OMXCodec比較簡單, 調用IOMX::allocateNode 申請即可. 編碼器的名稱例如 OMX.TCC.avcdec 即是OMX 組件(Component)的名稱

IOMX::node_id node = 0;

omx->allocateNode(componentName, observer, &node); /// 這個時候就已經是和OMX IL 層進行通訊瞭, 雖然是進程間通訊. 但是封裝成這個樣子,我們也看不出來瞭, 和本地調用一樣.

sp<OMXCodec> codec = new OMXCodec(
                    omx, node, quirks,
                    createEncoder, mime, componentName,
                    source);

codec->configureCodec(meta, flags); /// codec 創建出來後, 要配置一下codec.

如果進去看看configureCodec的代碼, 可以看到實際上是調用 IOMX::setParameter, 和IOMX::setConfig. 同樣,也是屬於IPC, 因為是和OMX IL 通訊.

總結:  理想的情況下,  調用OMXCodec::Create 應該返回一個OMXCodec 對象而不是軟解對象.  Android 默認的策略也是優先創建硬解碼器.  至此AwesomePlayer 通過OMXCodec 進而跟OML IL 打交道.  其中關鍵的對象為IOMX和IOMX::node_id. node_id 相當於一個OMX Component的句柄. 音頻解碼器的創建過程跟視頻解碼器的創建過程幾乎一樣, 所以不分析瞭.

 

1.5 解封裝, 解碼

看回 1.2.5,  當Video Event 被觸發時, AwesomePlayer::onVideoEvent 會被調用. onVideoEvent 會嘗試調用 mVideoSource.read 讀取視頻圖像,然後將視頻圖像交給AwesomeRenderer進行渲染.

如果采用硬解碼的話 mVideoSource 實際是就是一個OMXCodec 對象.  下一篇分析下 OMXCodec.read 幹瞭什麼事.

摘自 最大的敵人是自己

發佈留言