首页 > 程序开发 > 移动开发 > Android > 正文

Android播放器框架分析 1
2012-01-11 09:22:05 我来说两句
收藏 我要投稿 [字体: 小 大]

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

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.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.

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


更多相关文章

  1. android 类似QQ 换皮肤 实现思路 apk资源共享
  2. Android(安卓)Studio配置SVN
  3. Android基础入门教程——2.2.1 LinearLayout(线性布局)
  4. Android(安卓)之 自动提示功能(AutoCompleteTextView)的使用
  5. 【Android(安卓)Audio】Android(安卓)Audio System 之二:AudioFli
  6. android 透明度
  7. Android(安卓)对话框【Dialog】去除白色边框代码
  8. android NDK/JNI-实例开发流程
  9. Android(安卓)桌面组件widget

随机推荐

  1. 八大数据类型
  2. js 事件与 fetch api 简单总结
  3. js语法:数据类型、函数,作用域/作用域链、
  4. 【前端】js如何获取tree型数组的最大深度
  5. 【前端】js中a||b,a&&b 这种表示啥意思?
  6. 【前端】html5 video标签 自适应
  7. 【前端】手机号码输入框添加 空格
  8. 【前端】多页面有重复的html代码,怎么解决
  9. 【前端】在electron使用ckplayer播放rtmp
  10. 【前端】ios下获得焦点之后,页面布局上移