分享

Android播放器框架分析 1

 arm_embed 2012-07-27

Android播放器框架分析 1

2887人阅读 评论(2) 收藏 举报

Author:Aya

Date:2011-08-03

 

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

Java代码
  1. MediaPlayer mp = new MediaPlayer();  
  2. mp.setDisplay (...); /// 设置播放器Suface  
  3. mp.setDataSource(PATH_TO_FILE); ///设置媒体URI  
  4. mp.prepare(); /// 初始化播放器  
  5. 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++代码
  1. status_t AwesomePlayer::prepareAsync_l() {  
  2.     if (!mQueueStarted) {  
  3.         mQueue.start();  
  4.         mQueueStarted = true;  
  5.     }  
  6.     return OK;  
  7. }  

AP处理了几个定时器事件, 包括 onVideoEvent(); onStreamDone(); onBufferingUpdate();onCheckAudioStatus() ; onPrepareAsyncEvent();

 

sp<ISurface> mISurface;

供播放器渲染的画布

 

sp<AwesomeRenderer> mVideoRenderer;

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

 

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

分别代表一个视频轨道和音频轨道,  用于提取视频帧和音频帧. mVideoTrack和mAudioTrack 在 onPrepareAsyncEvent事件被触发时, 由MediaExtractor分离出来.

C++代码
  1. void AwesomePlayer::onPrepareAsyncEvent() {   
  2.         status_t err = finishSetDataSource_l();   
  3. }  

 

C++代码
  1. status_t AwesomePlayer::finishSetDataSource_l() {   
  2.  sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);   
  3.     return setDataSource_l(extractor);   
  4. }   

 

C++代码
  1. status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {   
  2.     for (size_t i = 0; i < extractor->countTracks(); ++i) {   
  3.         sp<MetaData> meta = extractor->getTrackMetaData(i);   
  4.   
  5.         const char *mime;   
  6.         CHECK(meta->findCString(kKeyMIMEType, &mime));   
  7.   
  8.         if (!haveVideo && !strncasecmp(mime, "video/", 6)) {   
  9.             setVideoSource(extractor->getTrack(i));   //
  10.             haveVideo = true;   
  11.         } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {   
  12.             setAudioSource(extractor->getTrack(i));   
  13.             haveAudio = true;   
  14.         if (haveAudio && haveVideo) {   
  15.             break;   
  16.         }   
  17.     }   
  18. }  

从上面这段代码可以看到AwesomePlayer默认采用第一个VideoTrack和第一个AudioTrack, 那如何切换VideoTrack和AudioTrack?

 

sp<MediaSource> mVideoSource;

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

mVideoSource 由OMXCodec创建.

C++代码
  1. status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {   
  2.     mVideoSource = OMXCodec::Create(   
  3.             mClient.interface(), mVideoTrack->getFormat(),   
  4.             false, // createEncoder   
  5.             mVideoTrack,   
  6.             NULL, flags);   
  7. }   

 

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++代码
  1. status_t AwesomePlayer::setDataSource_l(   
  2.         const char *uri, const KeyedVector<String8, String8> *headers) {   
  3.     /// 这里只是把URL保存起来而已, 真正的工作在Prepare之后进行   
  4.     mUri = uri;   
  5.     return OK;   
  6. }  

 

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

C++代码
  1. status_t AwesomePlayer::prepareAsync_l() {   
  2.   
  3.     /// 开启定时器队列   
  4.     mQueue.start();   
  5.   
  6.     /// Post AsyncPrepare 事件   
  7.     mAsyncPrepareEvent = new AwesomeEvent(   
  8.             this, &AwesomePlayer::onPrepareAsyncEvent);   
  9.   
  10.     mQueue.postEvent(mAsyncPrepareEvent);   
  11.     return OK;   
  12. }   

Prepare 之后,  AwesomePlayer 开始运作.

 

1.2.3 AsyncPrepare 事件被触发

当这个事件被触发时, AwesomePlayer 开始创建 VideoTrack和AudioTrack , 然后创建 VideoDecoder和AudioDecoder

C++代码
  1. void AwesomePlayer::onPrepareAsyncEvent() {   
  2.     /// a. 创建视频源和音频源   
  3.     finishSetDataSource_l();   
  4.   
  5.     /// b. 创建视频解码器   
  6.     initVideoDecoder();   
  7.        
  8.     /// c. 创建音频解码器   
  9.     initAudioDecoder();   
  10. }   

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

 

1.2.4 Post 第一个VideoEvent

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

 

C++代码
  1. void AwesomePlayer::postVideoEvent_l(int64_t delayUs) {   
  2.     mQueue.postEventWithDelay(mVideoEvent, delayUs < 0 ? 10000 : delayUs);   
  3. }   

1.2.5 VideoEvent 被触发

C++代码
  1. void AwesomePlayer::onVideoEvent() {   
  2.   
  3.     /// 从视频解码器中读出视频图像   
  4.     mVideoSource->read(&mVideoBuffer, &options);   
  5.   
  6.     /// 创建AwesomeRenderer (如果没有的话)   
  7.     if (mVideoRendererIsPreview || mVideoRenderer == NULL) {   
  8.         initRenderer_l();   
  9.     }   
  10.   
  11.     /// 渲染视频图像   
  12.      mVideoRenderer->render(mVideoBuffer);   
  13.   
  14.     /// 再次发送一个VideoEvent, 这样播放器就不停的播放了   
  15.      postVideoEvent_l();   
  16. }   

 

 总结:   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++代码
  1. status_t AwesomePlayer::finishSetDataSource_l() {   
  2.     sp<DataSource> dataSource;   
  3.     /// 通过URI前缀判断媒体类型, 比如 http, rtsp,或者本地文件等   
  4.      /// 这里的uri就是一开始 通过setDataSource设置的   
  5.      /// 根据uri 创建相应的MediaExtractor   
  6.   
  7.     if (!strncasecmp("http://", mUri.string(), 7)) {   
  8.         mConnectingDataSource = new NuHTTPDataSource;   
  9.         mConnectingDataSource->connect(mUri, &mUriHeaders);   
  10.         mCachedSource = new NuCachedSource2(mConnectingDataSource);   
  11.         dataSource = mCachedSource;   
  12.     } else if (!strncasecmp("rtsp://", mUri.string(), 7)) {   
  13.         mRTSPController->connect(mUri.string());   
  14.         sp<MediaExtractor> extractor = mRTSPController.get();   
  15.            
  16.         /// rtsp 比较特殊, MediaExtractor对象的创建不需要DataSource   
  17.         return setDataSource_l(extractor);   
  18.     } else {   
  19.         /// 本地文件   
  20.         dataSource = DataSource::CreateFromURI(mUri.string(), &mUriHeaders);   
  21.     }   
  22.   
  23.     /// 用dataSource创建一个MediaExtractor用于A/V分离        
  24.     sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);   
  25.   
  26.     return setDataSource_l(extractor);   
  27. }  

 

1.3.2 根据DataSource 创建MediaExtractor

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

C++代码
  1. AwesomePlayer::AwesomePlayer(){   
  2.     //...   
  3.     DataSource::RegisterDefaultSniffers();   
  4.     //...   
  5. }   

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

C++代码
  1. void DataSource::RegisterDefaultSniffers() {   
  2.     RegisterSniffer(SniffMPEG4);   
  3.     RegisterSniffer(SniffMatroska);   
  4.     RegisterSniffer(SniffOgg);   
  5.     RegisterSniffer(SniffWAV);   
  6.     RegisterSniffer(SniffAMR);   
  7.     RegisterSniffer(SniffMPEG2TS);   
  8.     RegisterSniffer(SniffMP3);   
  9.     RegisterSniffer(SniffTCCMedia); // TELECHIPS, SSG   
  10. }  

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

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

TCC在这个函数添加了一点代码, 用于创建自己的Extractor 对象.

C++代码
  1. sp<MediaExtractor> MediaExtractor::Create(   
  2.         const sp<DataSource> &source, const char *mime) {   
  3.     sp<AMessage> meta;   
  4.   
  5.     String8 tmp;   
  6.     if (mime == NULL) {   
  7.         float confidence;   
  8.         ///调用之前注册的MIME探测函数判断MIME类型   
  9.         if (!source->sniff(&tmp, &confidence, &meta)) {   
  10.             return NULL;   
  11.         }   
  12.   
  13.         mime = tmp.string();   
  14.     }   
  15.   
  16.     if ((!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)   
  17.             || !strcasecmp(mime, "audio/mp4"))   
  18.             // TELECHIPS, SSG, only use MPEG4Extractor for HTTP source   
  19.             && (source->flags() & DataSource::kIsCachingDataSource)) {   
  20.         return new MPEG4Extractor(source);   
  21.     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {   
  22.         return new MP3Extractor(source, meta);   
  23.     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)   
  24.             || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {   
  25.         return new AMRExtractor(source);   
  26.     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) {   
  27.         return new WAVExtractor(source);   
  28.     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) {   
  29.         return new OggExtractor(source);   
  30.     // TELECHIPS, SSG   
  31.     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WEBM)) {   
  32.         return new MatroskaExtractor(source);   
  33.     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {   
  34.         return new MPEG2TSExtractor(source);   
  35.     }   
  36.     // TELECHIPS, SSG   
  37.     else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_FLAC)   
  38.             || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_APE)   
  39.             || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MP2)   
  40.             || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_RA)   
  41.             || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WMA)   
  42.             || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_AAC)   
  43.             || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_AVI)   
  44.             || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)   
  45.             || !strcasecmp(mime, "audio/mp4")   
  46.             || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WMV)   
  47.             || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_ASF)   
  48.             || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)   
  49.             || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPG)   
  50.             || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MOV)   
  51.             || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_FLV)   
  52.             || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_DIVX)   
  53.             || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_RM)   
  54.             || !strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_TS)) {   
  55.         ///ITCCMediaExtractor是TCC专有MediaExtractor   
  56.         ///可惜他没有公开代码, 所以A/V分离失败的时候   
  57.          ///并不知道失败的原因   
  58.         return new ITCCMediaExtractor(source);   
  59.     }   
  60.   
  61.     return NULL;   
  62. }  

 (可以看到几乎所有封装格式都支持. 那为什么播放 Asf +H264的文件会失败呢? 只能怀疑ITCCMediaExtractor实现的有问题了)

 

 1.3.3 根据MediaExtractor 做A/V分离

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

C++代码
  1. status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {   
  2.   
  3.     /// 从全部的Track中选取一个Video Track和一个AudioTrack   
  4.     for (size_t i = 0; i < extractor->countTracks(); ++i) {   
  5.         sp<MetaData> meta = extractor->getTrackMetaData(i);   
  6.   
  7.         const char *mime;   
  8.         CHECK(meta->findCString(kKeyMIMEType, &mime));   
  9.   
  10.         if (!haveVideo && !strncasecmp(mime, "video/", 6)) {   
  11.             setVideoSource(extractor->getTrack(i));   
  12.             haveVideo = true;   
  13.         } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {   
  14.             setAudioSource(extractor->getTrack(i));   
  15.             haveAudio = true;   
  16.         }   
  17.   
  18.         if (haveAudio && haveVideo) {   
  19.             break;   
  20.         }   
  21.     }   
  22.   
  23.     /// Flags 标志这个媒体的一些属性:   
  24.      /// CAN_SEEK_BACKWARD 是否能后退10秒   
  25.     /// CAN_SEEK_FORWARD 是否能前进10秒   
  26.     /// CAN_SEEK 能否Seek?   
  27.     /// CAN_PAUSE 能否暂停   
  28.     mExtractorFlags = extractor->flags();   
  29.     return OK;   
  30. }   

从这个函数可以看到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++代码
  1. status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {   
  2.     mVideoSource = OMXCodec::Create(   
  3.             mClient.interface(), mVideoTrack->getFormat(),   
  4.             false, // createEncoder   
  5.             mVideoTrack,   
  6.             NULL, flags);   
  7.     /// ...   
  8.     return mVideoSource != NULL ? OK : UNKNOWN_ERROR;   
  9. }   

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++代码
  1. sp<MediaSource> OMXCodec::Create(   
  2.         const sp<IOMX> &omx,   
  3.         const sp<MetaData> &meta, bool createEncoder,   
  4.         const sp<MediaSource> &source,   
  5.         const char *matchComponentName,   
  6.         uint32_t flags) {   
  7.   
  8.     /// 获取MIME类型   
  9.     const char *mime;   
  10.     bool success = meta->findCString(kKeyMIMEType, &mime);   
  11.   
  12.     /// 根据MIME找出可能匹配的Codec   
  13.     Vector<String8> matchingCodecs;   
  14.     findMatchingCodecs(   
  15.             mime, createEncoder, matchComponentName, flags, &matchingCodecs);   
  16.   
  17.     IOMX::node_id node = 0;   
  18.   
  19.     /// 对每一种可能匹配的Codec, 尝试申请Codec   
  20.     const char *componentName;   
  21.     for (size_t i = 0; i < matchingCodecs.size(); ++i) {   
  22.         componentName = matchingCodecs[i].string();   
  23.   
  24.         /// 尝试申请软Codec   
  25.         sp<MediaSource> softwareCodec = createEncoder?   
  26.             InstantiateSoftwareEncoder(componentName, source, meta):   
  27.             InstantiateSoftwareCodec(componentName, source);   
  28.   
  29.         if (softwareCodec != NULL) {   
  30.             return softwareCodec;   
  31.         }   
  32.   
  33.   
  34.         /// 尝试申请OMXCodec   
  35.         status_t err = omx->allocateNode(componentName, observer, &node);   
  36.         if (err == OK) {   
  37.             sp<OMXCodec> codec = new OMXCodec(   
  38.                     omx, node, quirks,   
  39.                     createEncoder, mime, componentName,   
  40.                     source);   
  41.                
  42.             /// 配置申请出来的OMXCodec   
  43.             err = codec->configureCodec(meta, flags);   
  44.             if (err == OK) {   
  45.                 return codec;   
  46.             }   
  47.         }   
  48.     }   
  49.   
  50.     return NULL;   
  51. }  

 

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

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

C++代码
  1. void    
  2. OMXCodec::findMatchingCodecs(   
  3.         const char *mime,   
  4.         bool createEncoder, const char *matchComponentName,   
  5.         uint32_t flags,   
  6.         Vector<String8> *matchingCodecs) {   
  7.   
  8.     for (int index = 0;; ++index) {   
  9.         const char *componentName;   
  10.   
  11.     componentName = GetCodec(   
  12.                     kDecoderInfo,   
  13.                     sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]),   
  14.                     mime, index);   
  15.   
  16.   
  17.         matchingCodecs->push(String8(componentName));   
  18.     }   
  19. }  

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

C++代码
  1. static const CodecInfo kDecoderInfo[] = {   
  2.     { MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" },   
  3. //    { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" },   
  4.     { MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder" },   
  5. //    { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.PV.mp3dec" },   
  6. //    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.decode" },   
  7.     { MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBDecoder" },   
  8. //    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.PV.amrdec" },   
  9.     { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.decode" },   
  10.     { MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBDecoder" },   
  11. //    { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.PV.amrdec" },   
  12.     { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },   
  13.     { MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" },   
  14. //    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacdec" },   
  15.     { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "G711Decoder" },   
  16.     { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "G711Decoder" },   
  17.     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.decoder.mpeg4" },   
  18.     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" },   
  19.     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },   
  20.     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.SEC.MPEG4.Decoder" },   
  21.     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TCC.mpeg4dec" },   
  22.     { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" },   
  23. //    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4dec" },   
  24.     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.decoder.h263" },   
  25.     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" },   
  26.     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.SEC.H263.Decoder" },   
  27.     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TCC.h263dec" },   
  28.     { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" },   
  29. //    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263dec" },   
  30.     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.decoder.avc" },   
  31.     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" },   
  32.     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },   
  33.     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.SEC.AVC.Decoder" },   
  34.     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TCC.avcdec" },   
  35.     { MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" },   
  36. //    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcdec" },   
  37.     { MEDIA_MIMETYPE_AUDIO_VORBIS, "VorbisDecoder" },   
  38.     { MEDIA_MIMETYPE_VIDEO_VPX, "VPXDecoder" },   
  39.   
  40.     // TELECHIPS, SSG   
  41.     { MEDIA_MIMETYPE_AUDIO_MPEG_TCC, "OMX.TCC.mp3dec" },   
  42.     { MEDIA_MIMETYPE_AUDIO_AAC_TCC, "OMX.TCC.aacdec" },   
  43.     { MEDIA_MIMETYPE_AUDIO_VORBIS_TCC, "OMX.TCC.vorbisdec" },   
  44.     { MEDIA_MIMETYPE_AUDIO_WMA, "OMX.TCC.wmadec" },   
  45.     { MEDIA_MIMETYPE_AUDIO_AC3, "OMX.TCC.ac3dec" },   
  46.     { MEDIA_MIMETYPE_AUDIO_RA, "OMX.TCC.radec" },   
  47.     { MEDIA_MIMETYPE_AUDIO_FLAC, "OMX.TCC.flacdec" },   
  48.     { MEDIA_MIMETYPE_AUDIO_APE, "OMX.TCC.apedec" },   
  49.     { MEDIA_MIMETYPE_AUDIO_MP2, "OMX.TCC.mp2dec" },   
  50.     { MEDIA_MIMETYPE_AUDIO_PCM, "OMX.TCC.pcmdec" },   
  51.     { MEDIA_MIMETYPE_AUDIO_DTS, "OMX.TCC.dtsdec" },   
  52.   
  53.     { MEDIA_MIMETYPE_VIDEO_VC1, "OMX.TCC.wmvdec" },   
  54.     { MEDIA_MIMETYPE_VIDEO_WMV12, "OMX.TCC.wmv12dec" },   
  55.     { MEDIA_MIMETYPE_VIDEO_RV, "OMX.TCC.rvdec" },   
  56.     { MEDIA_MIMETYPE_VIDEO_DIVX, "OMX.TCC.divxdec" },   
  57.     { MEDIA_MIMETYPE_VIDEO_MPEG2, "OMX.TCC.mpeg2dec" },   
  58.     { MEDIA_MIMETYPE_VIDEO_MJPEG, "OMX.TCC.mjpegdec" },   
  59.     { MEDIA_MIMETYPE_VIDEO_FLV1, "OMX.TCC.flv1dec" },   
  60. };  

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

1.4.3 InstantiateSoftwareCodec 创建软编码器

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

C++代码
  1. static const FactoryInfo kFactoryInfo[] = {   
  2.     FACTORY_REF(MP3Decoder)   
  3.     FACTORY_REF(AMRNBDecoder)   
  4.     FACTORY_REF(AMRWBDecoder)   
  5.     FACTORY_REF(AACDecoder)   
  6.     FACTORY_REF(AVCDecoder)   
  7.     FACTORY_REF(G711Decoder)   
  8.     FACTORY_REF(M4vH263Decoder)   
  9.     FACTORY_REF(VorbisDecoder)   
  10.     FACTORY_REF(VPXDecoder)   
  11. };  

 

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.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不存在.

 

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 干了什么事.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多