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根据MediaExtractor做A/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开始.AwesomePlayer是Stagefright核心.AwesomePlayer有一些接口甚至与MediaPlayer是一一对应的,例如setDataSource, prepare.
AwesomePlayer的结构框图如下:
说明:
(1) 本文中DataSource有两个概念:
一个是上面框图中的DataSource Input(这是为了区分,我给起的名字,在代码中就叫DataSource)指的是单纯的数据输入(未demux的),例如:
对http来说,指的是一个网络连接.
对本地播放来说指的是一个open的流媒体文件.
在后文的setDataSource中的datasource指的是从数据输入到demux输出的一个过程(即上面框图中最外层的DataSource).
这两个概念在代码中都有出现,但是代表数据输入的datasource只在setDataSource接口的内部实现中出现,对于外部看来, datasource指的就是图中外层的DataSource.
(2) VideoTrack与AudioTrack指的是Extractor(即demux)的两个通道,从这里输出的分别就是单纯的解复用后的Video/Audio流了.
(3) VideoRenderer + Surface即视频的输出.
(4) AudioSink即音频的输出.
2.1关键成员分析
2.1.1 Demux相关
sp<MediaSource> mVideoTrack;sp<MediaSource> mAudioTrack
分别代表一个视频轨道和音频轨道,用于提取视频流和音频流(Demux后但未解码的数据). mVideoTrack和mAudioTrack在 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,那如何切换VideoTrack和AudioTrack?
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;
mAudioPlayer把mAudioSource和mAudioSink包起来,完成一个音频播放器的功能.如start, stop, pause, seek 等.
AudioPlayer和AudioSink通过Callback建立关联.当AudioSink可以输出音频时,会通过回调通知AudioPlayer填充音频数据.而此时AudioPlayer会尝试从AudioSource读取音频数据.
2.1.3视频相关
sp<MediaSource> mVideoSource
mVideoSource可以认为是一个视频解码器的封装,用于产生视频图像供AwesomeRender渲染, mVideoSource的数据源则由mVideoTrack提供.
mVideoSource由OMXCodec创建.
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利用OMXClient跟OMXIL进行通信.这里OMX IL类似于一个服务端. AwesomePlayer作为一个客户端,请求OMX IL进行解码的工作.
TimedEventQueue mQueue
AwesomePlayer采用定时器队列的方式进行运作. mQueue在MediaPlayer调用 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开始创建 VideoTrack和AudioTrack ,然后创建 VideoDecoder和AudioDecoder
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创建MediaExtractor做A/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根据MediaExtractor做A/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; }}
virtual size_t countTracks() = 0;
该媒体包含了几个Track?
virtual sp getTrack(size_t index) = 0;
virtual sp 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中读取数据进行解码.
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 中读取数据进行解码.
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根据传入的MIME从kDecoderInfo中找出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就对应了6种Codec.
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则是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不存在.
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打交道.其中关键的对象为IOMX和IOMX::node_id. node_id相当于一个OMX Component的句柄.音频解码器的创建过程跟视频解码器的创建过程几乎一样,所以不分析了.
2.6解封装,解码
看回 2.2.5,当VideoEvent被触发时, AwesomePlayer::onVideoEvent会被调用. onVideoEvent会尝试调用 mVideoSource.read读取视频图像,然后将视频图像交给AwesomeRenderer进行渲染.
如果采用硬解码的话 mVideoSource实际是就是一个OMXCodec对象.
- Android播放器框架分析之AwesomePlayer
- Android播放器框架分析之AwesomePlayer
- Android播放器框架分析之AwesomePlayer
- Android播放器框架分析之AwesomePlayer
- Android播放器框架分析之AwesomePlayer
- Android多媒体开发【8】-- AwesomePlayer基本框架及播放流程
- Android多媒体开发【8】-- AwesomePlayer基本框架及播放流程
- Android多媒体开发【8】-- AwesomePlayer基本框架及播放流程
- Android多媒体开发笔记-- AwesomePlayer基本框架及播放流程
- Android Multimedia框架总结(八)Stagefright框架之AwesomePlayer及数据解析器
- Android Multimedia框架总结(八)Stagefright框架之AwesomePlayer及数据解析器
- Android Multimedia框架总结(八)Stagefright框架之AwesomePlayer及数据解析器
- Android Multimedia框架总结(八)Stagefright框架之AwesomePlayer及数据解析器
- Android播放器框架分析
- Android播放器框架分析 1
- Android播放器框架分析 1 .
- Android播放器框架分析 1
- AwesomePlayer 分析
- Android 微信分享问题解决小心得(二)
- Android核心服务解析篇(二)——Android源码结构分析
- 手写Andfix热修复(Art篇)
- Android系统各个版本系统特性整理
- LinkedList源码分析
- Android播放器框架分析之AwesomePlayer
- Spring 框架
- Flume框架及应用
- Java多线程详解(二)
- C# out 参数修饰符
- 菜鸟springboot 学习之旅三
- Java多线程--Lock接口
- paint的色彩矩阵以及滤镜效果
- Android核心服务解析篇(一)——下载Android源代码