Android Media Framework 框架的層次:
- Java層:frameworks/base/media/java/android/media/MediaPlayer.java
- JNI本地調(diào)用: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中對應的數(shù)據(jù)結(jié)構(gòu)主要有DataSource, MediaExtractor, MediaSource赏表。其中:
- DataSource主要負責提供原始數(shù)據(jù)
- MediaSource負責提供demux后的數(shù)據(jù)(即實際的audio 或者 video數(shù)據(jù)包)
- MediaExtractor則負責中間的過程氮墨,即將從DataSource得到的原始數(shù)據(jù)解析成解碼器需要的es數(shù)據(jù)涣达,并通過MediaSource的接口輸出。
Android本身支持的文件格式有限册踩,硬件廠商有時出于運營策略也會限制部分格式支持泳姐,被限制的部分格式需要收費等,于是公司就有自己擴展支持格式的需求暂吉。
我們先看AwesomePlayer工作的3個大的步驟:
- 探測文件類型峻村,根據(jù)文件類型創(chuàng)建對應的提取器(MediaExtractor)鉴象;
- 進入/libstagefright/omx/SoftOMXPlugin.cpp讀取kComponents數(shù)組奸忽,此為現(xiàn)有系統(tǒng)支持的解碼器列表疗认;
- 讀取/etc/media_codec.xml文件,根據(jù)已探測的文件類型獲取需要的解碼器名稱业稼,再對比kComponents得到實際的解碼器。
我們具體的來細看每一個步驟:
1) 探測文件類型蚂蕴,根據(jù)文件類型創(chuàng)建對應的提取器(MediaExtractor)
在AwesomePlayer的構(gòu)造函數(shù)中由DataSource注冊sniff探測文件類型:
AwesomePlayer::AwesomePlayer(){
...
DataSource::RegisterDefaultSniffers();
...
}
RegisterDefaultSniffers的實現(xiàn):
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函數(shù)將所有的sniffer函數(shù)都掛在全局鏈表gSniffers中低散。sniffer函數(shù)的主要作用就是用于探測文件的類型俯邓,每種類型的媒體文件都對應一個sniffer函數(shù)。這里從代碼可以看出原生的android播放器支持的格式還比較少熔号。
AwesomePlayer中extractor 創(chuàng)建流程:
在setDataSource的最后稽鞭,會調(diào)用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創(chuàng)建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鏈表中的每種格式的函數(shù)調(diào)用一遍朦蕴,選取最高的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函數(shù)確定了文件的格式之后弟头,就可以構(gòu)造對應的extractor對象吩抓。
2)進入/libstagefright/omx/SoftOMXPlugin.cpp讀取kComponents數(shù)組,此為現(xiàn)有系統(tǒng)支持的解碼器列表
AwesomePlayer通過OMXClient::connect()得到OMX的實例赴恨,在構(gòu)造OMX對象的過程中又調(diào)用了OMXMaster的構(gòu)造函數(shù)創(chuàng)建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構(gòu)造函數(shù)中有addPlugin()函數(shù)創(chuàng)建SoftOMXPlugin對象
OMXMaster::OMXMaster()
: mVendorLibHandle(NULL) {
addVendorPlugin();
addPlugin(new SoftOMXPlugin);
}
addPlugin()函數(shù)的實現(xiàn)在之前的文章中已經(jīng)給出,主要是將enumerateComponents枚舉出來的各種解碼器名字存放在成員變量mPluginByComponentName中伦连,類型為 KeyedVector雨饺。這樣就讀取了kComponents數(shù)組。
3)讀取/etc/media_codec.xml文件惑淳,根據(jù)已探測的文件類型獲取需要的解碼器名稱额港,再對比kComponents得到實際的解碼器
AwesomePlayer構(gòu)造函數(shù)結(jié)束后,在setDataSource之后會調(diào)用prepare方法歧焦,其實現(xiàn)中會調(diào)用initAudioDecoder和initVideoDecoder來構(gòu)造解碼器實例移斩。
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()的實現(xiàn):
void OMXCodec::findMatchingCodecs(
const char *mime,
bool createEncoder, const char *matchComponentName,
uint32_t flags,
Vector<</span>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解析出支持的解碼器名稱并匹配出對應的解碼器名稱倚舀。
我的總結(jié)(猜想叹哭,不是很明白):
AwesomePlayer首先探測文件格式類型,由文件格式類型可以得到mime和matchComponentName痕貌,由mime創(chuàng)建出對應的提取器(MediaExtractor)风罩;然后讀取支持的格式列表kComponents數(shù)組作為預備;最后由matchComponentName在MediaCodecList(media_codec.xml)中找到需要的解碼器名稱舵稠,根據(jù)這個解碼器名稱到kComponents中查詢并創(chuàng)建實際的解碼器超升。
于是,可以得出結(jié)論:
要擴展對文件格式的支持哺徊,那么這三個地方都要相應擴展:
- 第一室琢,擴展相應的提取器(MediaExtractor);
- 第二落追,擴展media_codec.xml盈滴,matchComponentName在這里邊找到需要的解碼器名稱;
- 第三,擴展支持的解碼器列表kComponents巢钓。