先来看看MediaExtractor所处的位置:
1. 创建流程
在GenericSource.cpp的NuPlayer::GenericSource::initFromDataSource()函数中调用了:
extractor = MediaExtractor::Create(mDataSource,
mimeType.isEmpty() ? NULL : mimeType.string());
- NuPlayer会为每个播放的文件,创建一个MediaExtractor,这个类的作用就是解析,概念上等同于demuxer或者Parser。
- MediaExtractor负责从文件中分离音视频数据,并抽象成MediaSource。
- MediaSource生产数据,送往MediaCodec。
- MediaCodec又将数据通过ACodec和OpenMAX的接口送往组件。组件解码后的数据会返回给MediaCodec.
- 之后再由MediaCodec送往Renderer模块。
sp<IMediaExtractor> MediaExtractor::Create(
const sp<DataSource> &source, const char *mime) {
ALOGV("MediaExtractor::Create %s", mime);
char value[PROPERTY_VALUE_MAX];
if (property_get("media.stagefright.extractremote", value, NULL)
&& (!strcmp("0", value) || !strcasecmp("false", value))) {
// local extractor
ALOGW("creating media extractor in calling process");
return CreateFromService(source, mime);
} else {
// Check if it's WVM, since WVMExtractor needs to be created in the media server process,
// not the extractor process.
...
}
return NULL;
}
继续调用CreateFromService函数
sp<MediaExtractor> MediaExtractor::CreateFromService(
const sp<DataSource> &source, const char *mime) {
ALOGV("MediaExtractor::CreateFromService %s", mime);
DataSource::RegisterDefaultSniffers();
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);
}
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_WVM) && getuid() == AID_MEDIA) {
// 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);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MIDI)) {
ret = new MidiExtractor(source);
}
if (ret != NULL) {
if (isDrm) {
ret->setDrmFlag(true);
} else {
ret->setDrmFlag(false);
}
}
return ret;
}
首先,这里的source是FileSource,FileSource的父类是DataSource,在setDataSource的过程中,NuPlayer::setDataSourceAsync函数会创建一个GenericSource,如下所示:
NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
sp<GenericSource> source =
new GenericSource(notify, mUIDValid, mUID);
status_t err = source->setDataSource(fd, offset, length);
if (err != OK) {
ALOGE("Failed to set data source!");
source = NULL;
}
}
而在这个类的构造函数中,有一个很重要的点:DataSource::RegisterDefaultSniffers();(GenericSource.cpp)
void DataSource::RegisterDefaultSniffers() {
Mutex::Autolock autoLock(gSnifferMutex);
if (gSniffersRegistered) {
return;
}
RegisterSniffer_l(SniffMPEG4);
RegisterSniffer_l(SniffMatroska);
RegisterSniffer_l(SniffOgg);
RegisterSniffer_l(SniffWAV);
RegisterSniffer_l(SniffFLAC);
RegisterSniffer_l(SniffAMR);
RegisterSniffer_l(SniffMPEG2TS);
RegisterSniffer_l(SniffMP3);
RegisterSniffer_l(SniffAAC);
RegisterSniffer_l(SniffMPEG2PS);
RegisterSniffer_l(SniffWVM);
RegisterSniffer_l(SniffMidi);
RegisterSniffer_l(SniffFSL);
char value[PROPERTY_VALUE_MAX];
if (property_get("drm.service.enabled", value, NULL)
&& (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
RegisterSniffer_l(SniffDRM);
}
gSniffersRegistered = true;
}
通过这个函数,注册了很多探测器函数,这些函数用来探测文件的类型
探测器函数究竟是如何探测出文件类型的,一般有三种方法:
- 读取文件名的后缀;
- 解析文件头;
- 读取一小段数据,解析这段数据
具体容器格式可以参考文章各种多媒体容器格式sniff方法总结
我们看一下MPEG4Extractor.cpp的SniffMPEG4
bool SniffMPEG4(
const sp<DataSource> &source, String8 *mimeType, float *confidence,
sp<AMessage> *meta) {
if (BetterSniffMPEG4(source, mimeType, confidence, meta)) {
return true;
}
if (LegacySniffMPEG4(source, mimeType, confidence)) {
ALOGW("Identified supported mpeg4 through LegacySniffMPEG4.");
return true;
}
return false;
}
先采用BetterSniffMPEG4方式去探测文件类型,如果没成功就采用LegacySniffMPEG4去探测。
继续回到MediaExtractor::Create函数中,它调用了source->sniff函数,这个函数如下所示:
bool DataSource::sniff(
String8 *mimeType, float *confidence, sp<AMessage> *meta) {
*mimeType = "";
*confidence = 0.0f;
meta->clear();
{
Mutex::Autolock autoLock(gSnifferMutex);
if (!gSniffersRegistered) {
return false;
}
}
ALOGV("******* In DataSource::sniff function. ************");
for (List<SnifferFunc>::iterator it = gSniffers.begin();
it != gSniffers.end(); ++it) {
String8 newMimeType;
float newConfidence;
sp<AMessage> newMeta;
if ((*it)(this, &newMimeType, &newConfidence, &newMeta)) {
if (newConfidence > *confidence) {
*mimeType = newMimeType;
*confidence = newConfidence;
*meta = newMeta;
}
}
}
ALOGV("******* At end of DataSource::sniff function. ***********");
return *confidence > 0.0;
}
从DataSource::RegisterDefaultSniffers()中注册过的sniff中,遍历执行,理论上来说,在执行到SniffMPEG4函数时,应该能够检测出来文件类型,因为这个文件就是mp4格式的。(具体内部是如何检测的,以后添加进去,同时还可以把那个检测错误的例子添加进去。)
继续回到MediaExtractor::Create函数中,从sniff函数中出来,意味着检测出媒体类型的,然后打印出:
MediaExtractor: Autodetected media content as ‘video/mp4’ with confidence 0.40
然后,会根据媒体类型来选取对应的Extractor:
if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
|| !strcasecmp(mime, "audio/mp4")) {
if(use_fsl) //源码可没有这两句,默认MPEG4Extractor
ret = new FslExtractor(source,mime);
else
ret = new MPEG4Extractor(source);
}
对于普通的平台,mp4格式的文件,就会使用MPEG4Extractor,而对于FSL平台,会提供有FSLExtractor来使用,这些Extractor以lib库的形式存在于Android系统中,这些lib库对于解析数据有硬件加速作用,但是源码是不公开的。
所以对于普通的平台,都会有加速的lib库来使用,那么就创建了MPEG4Extractor来使用,进入MPEG4Extractor的构造函数中。
MPEG4Source::MPEG4Source(
const sp<MPEG4Extractor> &owner,
const sp<MetaData> &format,
const sp<DataSource> &dataSource,
int32_t timeScale,
const sp<SampleTable> &sampleTable,
Vector<SidxEntry> &sidx,
const Trex *trex,
off64_t firstMoofOffset)
: mOwner(owner),
mFormat(format),
mDataSource(dataSource),
mTimescale(timeScale),
mSampleTable(sampleTable),
mCurrentSampleIndex(0),
mCurrentFragmentIndex(0),
mSegments(sidx),
mTrex(trex),
mFirstMoofOffset(firstMoofOffset),
mCurrentMoofOffset(firstMoofOffset),
mCurrentTime(0),
mCurrentSampleInfoAllocSize(0),
mCurrentSampleInfoSizes(NULL),
mCurrentSampleInfoOffsetsAllocSize(0),
mCurrentSampleInfoOffsets(NULL),
mIsAVC(false),
mIsHEVC(false),
mNALLengthSize(0),
mStarted(false),
mGroup(NULL),
mBuffer(NULL),
mWantsNALFragments(false),
mSrcBuffer(NULL) {
memset(&mTrackFragmentHeaderInfo, 0, sizeof(mTrackFragmentHeaderInfo));
mFormat->findInt32(kKeyCryptoMode, &mCryptoMode);
mDefaultIVSize = 0;
mFormat->findInt32(kKeyCryptoDefaultIVSize, &mDefaultIVSize);
uint32_t keytype;
const void *key;
size_t keysize;
if (mFormat->findData(kKeyCryptoKey, &keytype, &key, &keysize)) {
CHECK(keysize <= 16);
memset(mCryptoKey, 0, 16);
memcpy(mCryptoKey, key, keysize);
}
const char *mime;
bool success = mFormat->findCString(kKeyMIMEType, &mime);
CHECK(success);
mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
mIsHEVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC);
if (mIsAVC) {
uint32_t type;
const void *data;
size_t size;
CHECK(format->findData(kKeyAVCC, &type, &data, &size));
const uint8_t *ptr = (const uint8_t *)data;
CHECK(size >= 7);
CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
// The number of bytes used to encode the length of a NAL unit.
mNALLengthSize = 1 + (ptr[4] & 3);
} else if (mIsHEVC) {
uint32_t type;
const void *data;
size_t size;
CHECK(format->findData(kKeyHVCC, &type, &data, &size));
const uint8_t *ptr = (const uint8_t *)data;
CHECK(size >= 22);
CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
mNALLengthSize = 1 + (ptr[14 + 7] & 3);
}
CHECK(format->findInt32(kKeyTrackID, &mTrackId));
if (mFirstMoofOffset != 0) {
off64_t offset = mFirstMoofOffset;
parseChunk(&offset);
}
}
至此,MediaExtractor::Create函数分析完毕。