Android Media Framework 框架的层次:
Java层:frameworks/base/media/java/android/media/MediaPlayer.java
JNI本地调用:frameworks/base/media/jni/android_media_MediaPlayer.cpp
libmedia多媒体底层库:frameworks/base/media/libmedia/mediaplayer.cpp
libmediaplayer多媒体服务部分:frameworks/base/media/libmediaplayerservice/MediaPlayerService.cpp, StagefrightPlayer.cpp
Stagefright框架:frameworks/base/media/libstagefright/AwesomePlayer.cpp, OMX
整个Media框架的核心是AwesomePlayer,Awesomeplayer中对应的数据结构主要有DataSource, MediaExtractor, MediaSource。其中:
DataSource主要负责提供原始数据
MediaSource负责提供demux后的数据(即实际的audio 或者 video数据包)
MediaExtractor则负责中间的过程,即将从DataSource得到的原始数据解析成解码器需要的es数据,并通过MediaSource的接口输出。
Android本身支持的文件格式有限,硬件厂商有时出于运营策略也会限制部分格式支持,被限制的部分格式需要收费等,于是公司就有自己扩展支持格式的需求。
我们先看AwesomePlayer工作的3个大的步骤:
探测文件类型,根据文件类型创建对应的提取器(MediaExtractor);
进入/libstagefright/omx/SoftOMXPlugin.cpp读取kComponents数组,此为现有系统支持的解码器列表;
读取/etc/media_codec.xml文件,根据已探测的文件类型获取需要的解码器名称,再对比kComponents得到实际的解码器。
我们具体的来细看每一个步骤:
1) 探测文件类型,根据文件类型创建对应的提取器(MediaExtractor)
在AwesomePlayer的构造函数中由DataSource注册sniff探测文件类型:
AwesomePlayer::AwesomePlayer(){
...
DataSource::RegisterDefaultSniffers();
...
}
RegisterDefaultSniffers的实现:
void DataSource::RegisterDefaultSniffers() {
RegisterSniffer(SniffMPEG4);
RegisterSniffer(SniffFragmentedMP4);
RegisterSniffer(SniffMatroska);
RegisterSniffer(SniffOgg);
RegisterSniffer(SniffWAV);
RegisterSniffer(SniffFLAC);
RegisterSniffer(SniffAMR);
RegisterSniffer(SniffMPEG2TS);
RegisterSniffer(SniffMP3);
RegisterSniffer(SniffAAC);
RegisterSniffer(SniffMPEG2PS);
RegisterSniffer(SniffWVM);
char value[PROPERTY_VALUE_MAX];
if (property_get("drm.service.enabled", value, NULL)
&& (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
RegisterSniffer(SniffDRM);
}
}
// static
void DataSource::RegisterSniffer(SnifferFunc func) {
Mutex::Autolock autoLock(gSnifferMutex);
for (List<SnifferFunc>::iterator it = gSniffers.begin();
it != gSniffers.end(); ++it) {
if (*it == func) {
return;
}
}
gSniffers.push_back(func);
}
从代码可以看出RegisterDefaultSniffers的主要作用既是注册Sniffer函数将所有的sniffer函数都挂在全局链表gSniffers中。sniffer函数的主要作用就是用于探测文件的类型,每种类型的媒体文件都对应一个sniffer函数。这里从代码可以看出原生的android播放器支持的格式还比较少。
AwesomePlayer中extractor 创建流程:
在setDataSource的最后,会调用setDataSource_l(dataSource),将datasource和对应的extractor对应起来。
status_t AwesomePlayer::setDataSource_l(
const sp<DataSource> &dataSource) {
sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
if (extractor == NULL) {
return UNKNOWN_ERROR;
}
if (extractor->getDrmFlag()) {
checkDrmStatus(dataSource);
}
return setDataSource_l(extractor);
}
MediaExtractor::Create(), 这里通过MediaExtractor::Create创建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)) {
ALOGV("FAILED to autodetect media content.");
return NULL;
}
mime = tmp.string();
ALOGV("Autodetected media content as '%s' with confidence %.2f",
mime, confidence);
}
主要是将gSniffers链表中的每种格式的函数调用一遍,选取最高的confidence作为选中的文件格式。
MediaExtractor *ret = NULL;
if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
|| !strcasecmp(mime, “audio/mp4”)) {
int fragmented = 0;
if (meta != NULL && meta->findInt32(“fragmented”, &fragmented) && fragmented) {
ret = new FragmentedMP4Extractor(source);
} else {
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_WVM)) {
// Return now. WVExtractor should not have the DrmFlag set in the block below.
return new WVMExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) {
ret = new AACExtractor(source, meta);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) {
ret = new MPEG2PSExtractor(source);
}
if (ret != NULL) {
if (isDrm) {
ret->setDrmFlag(true);
} else {
ret->setDrmFlag(false);
}
}
return ret;
}
成功的通过sniff函数确定了文件的格式之后,就可以构造对应的extractor对象。
2)进入/libstagefright/omx/SoftOMXPlugin.cpp读取kComponents数组,此为现有系统支持的解码器列表
AwesomePlayer通过OMXClient::connect()得到OMX的实例,在构造OMX对象的过程中又调用了OMXMaster的构造函数创建OMXMaster的对象。
status_t OMXClient::connect() {
sp<</span>IServiceManager> sm = defaultServiceManager();
sp<</span>IBinder> binder = sm->getService(String16("media.player"));
sp<</span>IMediaPlayerService> service = interface_cast<</span>IMediaPlayerService>(binder);
CHECK(service.get() != NULL);
mOMX = service->getOMX();
CHECK(mOMX.get() != NULL);
if (!mOMX->livesLocally(NULL , getpid())) {
ALOGI("Using client-side OMX mux.");
mOMX = new MuxOMX(mOMX);
}
return OK;
}
OMX::OMX()
: mMaster(new OMXMaster),
mNodeCounter(0) {
}
在OMXMaster构造函数中有addPlugin()函数创建SoftOMXPlugin对象
OMXMaster::OMXMaster()
: mVendorLibHandle(NULL) {
addVendorPlugin();
addPlugin(new SoftOMXPlugin);
}
addPlugin()函数的实现在之前的文章中已经给出,主要是将enumerateComponents枚举出来的各种解码器名字存放在成员变量mPluginByComponentName中,类型为 KeyedVector。这样就读取了kComponents数组。
3)读取/etc/media_codec.xml文件,根据已探测的文件类型获取需要的解码器名称,再对比kComponents得到实际的解码器
AwesomePlayer构造函数结束后,在setDataSource之后会调用prepare方法,其实现中会调用initAudioDecoder和initVideoDecoder来构造解码器实例。
status_t AwesomePlayer::initVideoDecoder()
{
mVideoSource = OMXCodec::Create(
mClient.interface(),
mVideoTrack->getFormat(),
false,
mVideoTrack);
}
sp<</span>MediaSource> OMXCodec::Create(*)
{
findMatchingCodecs(
mime, createEncoder, matchComponentName, flags, &matchingCodecs);
sp<</span>OMXCodecObserver> observer = new OMXCodecObserver;
IOMX::node_id node = 0;
status_t err = omx->allocateNode(componentName, observer, &node);
sp<</span>OMXCodec> codec = new OMXCodec(
omx, node, quirks, flags,
createEncoder, mime, componentName,
source, nativeWindow);
observer->setCodec(codec);
err = codec->configureCodec(meta);
}
findMatchingCodecs()的实现:
void OMXCodec::findMatchingCodecs(
const char *mime,
bool createEncoder, const char *matchComponentName,
uint32_t flags,
Vector<CodecNameAndQuirks> *matchingCodecs) {
matchingCodecs->clear();
const MediaCodecList *list = MediaCodecList::getInstance();
if (list == NULL) {
return;
}
size_t index = 0;
for (;;) {
ssize_t matchIndex =
list->findCodecByType(mime, createEncoder, index);
if (matchIndex <</span> 0) {
break;
}
index = matchIndex + 1;
const char *componentName = list->getCodecName(matchIndex);
// If a specific codec is requested, skip the non-matching ones.
if (matchComponentName && strcmp(componentName, matchComponentName)) {
continue;
}
// When requesting software-only codecs, only push software codecs
// When requesting hardware-only codecs, only push hardware codecs
// When there is request neither for software-only nor for
// hardware-only codecs, push all codecs
if (((flags & kSoftwareCodecsOnly) && IsSoftwareCodec(componentName)) ||
((flags & kHardwareCodecsOnly) && !IsSoftwareCodec(componentName)) ||
(!(flags & (kSoftwareCodecsOnly | kHardwareCodecsOnly)))) {
ssize_t index = matchingCodecs->add();
CodecNameAndQuirks *entry = &matchingCodecs->editItemAt(index);
entry->mName = String8(componentName);
entry->mQuirks = getComponentQuirks(list, matchIndex);
ALOGV("matching '%s' quirks 0xx",
entry->mName.string(), entry->mQuirks);
}
}
if (flags & kPreferSoftwareCodecs) {
matchingCodecs->sort(CompareSoftwareCodecsFirst);
}
}
从代码可以看到主要就是从MediaCodecList找到与传入的matchComponentName对应的解码器名称,MediaCodecList就是从/etc/media_codecs.xml解析出支持的解码器名称并匹配出对应的解码器名称。
我的总结(猜想,不是很明白):
AwesomePlayer首先探测文件格式类型,由文件格式类型可以得到mime和matchComponentName,由mime创建出对应的提取器(MediaExtractor);然后读取支持的格式列表kComponents数组作为预备;最后由matchComponentName在MediaCodecList(media_codec.xml)中找到需要的解码器名称,根据这个解码器名称到kComponents中查询并创建实际的解码器。
于是,可以得出结论:
要扩展对文件格式的支持,那么这三个地方都要相应扩展:
第一,扩展相应的提取器(MediaExtractor);
第二,扩展media_codec.xml,matchComponentName在这里边找到需要的解码器名称;
第三,扩展支持的解码器列表kComponents。