Android播放器框架分析之AwesomePlayer

来源:互联网 发布:nginx windows 重启 编辑:程序博客网 时间:2024/05/16 11:57

目录:

1简介

2 AwesomePlayer概述

    2.1关键成员分析

        2.1.1 Demux相关

        2.1.2音频相关

        2.1.3视频相关

        2.1.4其他

    2.2基本播放流程

        2.2.1设置数据源URI

        2.2.2开启定时器队列,并且 Post一个AsyncPrepareEvent事件

        2.2.3 AsyncPrepare事件被触发

        2.2.4 Post第一个VideoEvent

        2.2.5 VideoEvent被触发

    2.3视频 /音频 分离

        2.3.1创建DataSource

        2.3.2根据DataSource创建MediaExtractor

        2.3.3根据MediaExtractorA/V分离

    2.4创建视频解码器

    2.5 OMXCodec::Create

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

        2.5.2 InstantiateSoftwareCodec创建软解码器

        2.5.3解码器名称的一点说明

        2.5.4创建OMXCodec

    2.6解封装,解码



1简介

Java层 要开启一个播放器进行播放,需要以下几行代码:

MediaPlayer mp = new MediaPlayer();  mp.setDisplay (...);                      // 设置播放器Suface  mp.setDataSource(PATH_TO_FILE);           //设置媒体URI  mp.prepare();                             // 初始化播放器  mp.start();                               // 开始播放

2 AwesomePlayer概述

忽略掉 JNI封装层,Stagefright AwesomePlayer开始.AwesomePlayerStagefright核心.AwesomePlayer有一些接口甚至与MediaPlayer是一一对应的,例如setDataSource, prepare.

AwesomePlayer的结构框图如下:


说明:

(1)   本文中DataSource有两个概念:

一个是上面框图中的DataSource Input(这是为了区分,我给起的名字,在代码中就叫DataSource)指的是单纯的数据输入(demux),例如:

http来说,指的是一个网络连接.

对本地播放来说指的是一个open的流媒体文件.

在后文的setDataSource中的datasource指的是从数据输入到demux输出的一个过程(即上面框图中最外层的DataSource).

这两个概念在代码中都有出现,但是代表数据输入的datasource只在setDataSource接口的内部实现中出现,对于外部看来, datasource指的就是图中外层的DataSource.

(2)   VideoTrackAudioTrack指的是Extractor(demux)的两个通道,从这里输出的分别就是单纯的解复用后的Video/Audio流了.

(3)   VideoRenderer + Surface即视频的输出.

(4)   AudioSink即音频的输出.



2.1关键成员分析

2.1.1 Demux相关

sp<MediaSource> mVideoTrack;sp<MediaSource> mAudioTrack

分别代表一个视频轨道和音频轨道,用于提取视频流和音频流(Demux后但未解码的数据). mVideoTrackmAudioTrack onPrepareAsyncEvent事件被触发时,MediaExtractor分离出来.

void AwesomePlayer::onPrepareAsyncEvent() {           status_t err = finishSetDataSource_l();   }   status_t AwesomePlayer::finishSetDataSource_l() {    sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);       return setDataSource_l(extractor);   }    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,那如何切换VideoTrackAudioTrack?


2.1.2音频相关

sp<MediaSource> mAudioSource;

mAudioSource可以认为是一个音频解码器的封装

sp<MediaPlayerBase::AudioSink> mAudioSink;

mAudioSink代表一个音频输出设备.用于播放解码后的音频数据. (AudioSink is used forin-memory decode and potentially other applications where output doesn't gostraight to hardware)

AudioPlayer *mAudioPlayer;

mAudioPlayermAudioSourcemAudioSink包起来,完成一个音频播放器的功能.start, stop, pause, seek .


AudioPlayerAudioSink通过Callback建立关联.AudioSink可以输出音频时,会通过回调通知AudioPlayer填充音频数据.而此时AudioPlayer会尝试从AudioSource读取音频数据.


2.1.3视频相关

sp<MediaSource> mVideoSource

mVideoSource可以认为是一个视频解码器的封装,用于产生视频图像供AwesomeRender渲染, mVideoSource的数据源则由mVideoTrack提供.

mVideoSourceOMXCodec创建.

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

sp<AwesomeRenderer> mVideoRenderer 

负责将解码后的图片渲染输出

sp<ISurface> mISurface

供播放器渲染的画布


2.1.4其他

OMXClient mClient

OMX可以理解为一个编解码器的库,AwesomePlayer利用OMXClientOMXIL进行通信.这里OMX IL类似于一个服务端. AwesomePlayer作为一个客户端,请求OMX IL进行解码的工作.

TimedEventQueue mQueue

AwesomePlayer采用定时器队列的方式进行运作. mQueueMediaPlayer调用 prepare的时候就开始运作, (而不是MediaPlayer.start).

status_t AwesomePlayer::prepareAsync() {      if (!mQueueStarted) {          mQueue.start();          mQueueStarted = true;      }      return OK;  }  

AwesomePlayer处理了几个定时器事件,包括:

(1)onVideoEvent();

(2)onStreamDone();

(3)onBufferingUpdate();

(4)onCheckAudioStatus();

(5)onPrepareAsyncEvent();

总结:从关键的成员可以看出,AwesomePlayer拥有视频源和音频源 (VideoTrack, AudioTrack),有音视频解码器(VideoSoure, AudioSource),可以渲染图像(AwesomeRenderer) ,可以输出声音 (AudioSink),具备一个播放器完整的材料了.



2.2基本播放流程

2.2.1设置数据源URI

status_t AwesomePlayer::setDataSource(           const char *uri, const KeyedVector<String8, String8> *headers) {         mUri = uri;                // 这里只是把URL保存起来而已, 真正的工作在Prepare之后进行      return OK;   }  


2.2.2开启定时器队列,并且 Post一个AsyncPrepareEvent事件

status_t AwesomePlayer::prepareAsync() {           mQueue.start();                                               // 开启定时器队列         mAsyncPrepareEvent = new AwesomeEvent(               this, &AwesomePlayer::onPrepareAsyncEvent);           // Post AsyncPrepare 事件      mQueue.postEvent(mAsyncPrepareEvent);       return OK;   }   

Prepare之后,AwesomePlayer开始运作.


2.2.3 AsyncPrepare事件被触发

当这个事件被触发时, AwesomePlayer开始创建 VideoTrackAudioTrack ,然后创建 VideoDecoderAudioDecoder

void AwesomePlayer::onPrepareAsyncEvent() {          finishSetDataSource_l();            // a. 创建视频源和音频源        initVideoDecoder();                 // b. 创建视频解码器        initAudioDecoder();                 // c. 创建音频解码器}   

至此,播放器准备工作完成,可以开始播放了


2.2.4 Post第一个VideoEvent

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

void AwesomePlayer::postVideoEvent_l(int64_t delayUs) {       mQueue.postEventWithDelay(mVideoEvent, delayUs < 0 ? 10000 : delayUs);   }   


2.2.5 VideoEvent被触发

void AwesomePlayer::onVideoEvent() {           mVideoSource->read(&mVideoBuffer, &options);                   // 从视频解码器中读出视频图像        if (mVideoRendererIsPreview || mVideoRenderer == NULL) {       // 创建AwesomeRenderer (如果没有的话)        initRenderer_l();       }            mVideoRenderer->render(mVideoBuffer);                         // 渲染视频图像         postVideoEvent();                                             // 再次发送一个VideoEvent, 这样播放器就不停的播放了  }   

总结: SetDataSource -> Prepare -> Play-> postVieoEvent -> OnVideoEvent -> postVideoEvent-> ....onVideoEvent-> postStreamDoneEvent ->播放结束



2.3视频 /音频 分离

如前面提到的,AsyncPrepare事件被触发时, AwesomePlayer会调用finishSetDataSource_l创建 VideoTrack AudioTrack.

finishSetDataSource_l通过URI前缀判断 媒体类型,比如http, rtsp,或者本地文件等 这里的uri就是一开始 通过setDataSource设置的 根据uri创建相应的DataSource,再进一步的利用 DataSource创建MediaExtractorA/V分离

status_t AwesomePlayer::finishSetDataSource_l() {           sp<datasource> dataSource;           /// 通过URI前缀判断媒体类型, 比如 http, rtsp,或者本地文件等           /// 这里的uri就是一开始 通过setDataSource设置的           /// 根据uri 创建相应的MediaExtractor            if (!strncasecmp("http://", 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);       }  </mediaextractor></mediaextractor></datasource>

2.3.2根据DataSource创建MediaExtractor

先看看 AwesomePlayer的构造函数,里面有一行代码.

AwesomePlayer::AwesomePlayer(){       //...       DataSource::RegisterDefaultSniffers();       //...   }

RegisterDefaultSniffers注册了一些了媒体的MIME类型的探测函数.

void DataSource::RegisterDefaultSniffers() {       RegisterSniffer(SniffMPEG4);       RegisterSniffer(SniffMatroska);       RegisterSniffer(SniffOgg);       RegisterSniffer(SniffWAV);       RegisterSniffer(SniffAMR);       RegisterSniffer(SniffMPEG2TS);       RegisterSniffer(SniffMP3);   }  

这些探测用于判断媒体的MIME类型,进而决定要创建什么样的MediaExtractor.

再回到 MediaExtractor::Create, MediaExtractor对象在这里创建.下面代码有点长,其实这段代码只是根据MIME类型创建Extractor的各个分支而已.

 sp<mediaextractor> MediaExtractor::Create(            const sp<datasource> &source, const char *mime) {        sp<amessage> meta;         String8 tmp;        if (mime == NULL) {            float confidence;            if (!source->sniff(&tmp, &confidence, &meta)) {                LOGV("FAILED to autodetect media content.");                 return NULL;            }             mime = tmp.string();            LOGV("Autodetected media content as '%s' with confidence %.2f",                 mime, confidence);        }         bool isDrm = false;        // DRM MIME type syntax is "drm+type+original" where        // type is "es_based" or "container_based" and        // original is the content's cleartext MIME type        if (!strncmp(mime, "drm+", 4)) {            const char *originalMime = strchr(mime+4, '+');            if (originalMime == NULL) {                // second + not found                return NULL;            }            ++originalMime;            if (!strncmp(mime, "drm+es_based+", 13)) {                // DRMExtractor sets container metadata kKeyIsDRM to 1                return new DRMExtractor(source, originalMime);            } else if (!strncmp(mime, "drm+container_based+", 20)) {                mime = originalMime;                isDrm = true;            } else {                return NULL;            }        }         MediaExtractor *ret = NULL;        if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)                || !strcasecmp(mime, "audio/mp4")) {            ret = new MPEG4Extractor(source);        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {            ret = new MP3Extractor(source, meta);        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)                || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {            ret = new AMRExtractor(source);        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) {            ret = new FLACExtractor(source);        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) {            ret = new WAVExtractor(source);        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) {            ret = new OggExtractor(source);        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) {            ret = new MatroskaExtractor(source);        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {            ret = new MPEG2TSExtractor(source);        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_AVI)) {            ret = new AVIExtractor(source);        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WVM)) {            ret = new WVMExtractor(source);        } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) {            ret = new AACExtractor(source);        }         if (ret != NULL) {           if (isDrm) {               ret->setDrmFlag(true);           } else {               ret->setDrmFlag(false);           }        }         return ret;    }</amessage></datasource></mediaextractor>

2.3.3根据MediaExtractorA/V分离

再看看 setDataSource_l(const sp&extractor) ,这是A/V分离的最后步骤.

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 getTrack(size_t index) = 0;
获取指定的Video/Audio Track,可以看到一个Track本质上就是一个MediaSource
virtual sp getTrackMetaData ( size_t index,uint32_t flags = 0) = 0;
获取指定的TrackMetaData.AwesomePlayer, MetaData实际上就是一块可以任意信息字段的叉烧,字段类型可以是字符串或者是整形等等.这里TrackMetaData包含了TrackMIME类型.这样AwesomePlayer就可以知道这个TrackVideo还是Audio的了

总结:至此,AwesomePlayer就拥有VideoTrackAudioTrack (可能只有VideoTrack或者只有AudioTrack,例如MP3).接下来 音视频解码器VideoSource/AudioSource 将从Video/Audio Track中读取数据进行解码.



2.4创建视频解码器

VideoTrack/AudioTrack创建完毕之后,紧接着就是创建 VideoSource ( 1.2.3).看看initVideoDecoder

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.用于跟OMXIL通讯.假如最后用的是OMXCodec也不是SoftCodec的话,需要用到它.

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

c. mVideoTrack.如前面1.3.3说的.解码器会从 Video Track 中读取数据进行解码.



2.5 OMXCodec::Create
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;       }  </omxcodec></mediasource></string8></mediasource></metadata></iomx></mediasource>

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

findMatchingCodecs根据传入的MIMEkDecoderInfo中找出MIME对于的Codec (一种MIME可能对应多种Codec)

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));           }       }  </string8>

看看 kDecoderInfo里面包含了什么Codec,有点长.

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.7x30.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.7x30.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.7x30.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.divxdec" },       { MEDIA_MIMETYPE_VIDEO_MPEG2, "OMX.TCC.mpeg2dec" },       { MEDIA_MIMETYPE_VIDEO_MJPEG, "OMX.TCC.mjpegdec" },       { MEDIA_MIMETYPE_VIDEO_FLV1, "OMX.TCC.flv1dec" },   };  

可以看到MPEG4就对应了6Codec.


2.5.2 InstantiateSoftwareCodec创建软解码器

InstantiateSoftwareCodec kFactoryInfo (软解码器列表)挑挑看有没有.有的话就创建一个软解码器.看看kFactoryInfo里面有哪些软解码器

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)   };  

2.5.3解码器名称的一点说明

OMX.XXX.YYY  中间的XXX是厂商名称.OMX.TI.Video.Decoder就是TI芯片的硬视频解码器. OMX.TCC.avcdec则是TCCAVC视频解码器.没有OMX开头的,说明是软解码器.

AVC为例:

{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.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不存在.


2.5.4创建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打交道.其中关键的对象为IOMXIOMX::node_id. node_id相当于一个OMX Component的句柄.音频解码器的创建过程跟视频解码器的创建过程几乎一样,所以不分析了.


2.6解封装,解码

看回 2.2.5,VideoEvent被触发时, AwesomePlayer::onVideoEvent会被调用. onVideoEvent会尝试调用 mVideoSource.read读取视频图像,然后将视频图像交给AwesomeRenderer进行渲染.

如果采用硬解码的话 mVideoSource实际是就是一个OMXCodec对象.



阅读全文
0 0