先来看看MediaExtractor所处的位置:
(一)创建流程
在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模块。
函数如下所示(MediaExtractor.cpp):
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)) {//此时的source是FileSource
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;
}
}
bool use_fsl = false;
int value;
MediaExtractor *ret = NULL;
value = property_get_int32("media.fsl_codec.flag", 0);
if(value & 0x01)
use_fsl = true;
if(isDrm)
use_fsl = false;
if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
|| !strcasecmp(mime, "audio/mp4")) {
if(use_fsl)
ret = new FslExtractor(source,mime);
else
ret = new MPEG4Extractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
if(use_fsl)
ret = new FslExtractor(source,mime);
else
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)) {
if(use_fsl)
ret = new FslExtractor(source,mime);
else
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)) {
if(use_fsl)
ret = new FslExtractor(source,mime);
else
ret = new MatroskaExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
if(use_fsl)
ret = new FslExtractor(source,mime);
else
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)) {
if(use_fsl)
ret = new FslExtractor(source,mime);
else
ret = new AACExtractor(source, meta);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) {
if(use_fsl)
ret = new FslExtractor(source,mime);
else
ret = new MPEG2PSExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MIDI)) {
ret = new MidiExtractor(source);
} else if(!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_AVI)){
ret = new FslExtractor(source,mime);
} else if(!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_FLV)){
ret = new FslExtractor(source,mime);
} else if(!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_ASF)){
ret = new FslExtractor(source,mime);
} else if(!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_RMVB)){
ret = new FslExtractor(source,mime);
} else if(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_APE)) {
ret = new FslExtractor(source,mime);
}
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;
}
通过这个函数,注册了很多探测器函数,这些函数用来探测文件的类型 (探测器函数究竟是如何探测出文件类型的,一般有三种方法:1) 读取文件名的后缀;2) 解析文件头;3) 读取一小段数据,解析这段数据)。
继续回到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)
ret = new FslExtractor(source,mime);
else
ret = new MPEG4Extractor(source);
}
对于普通的平台,mp4格式的文件,就会使用MPEG4Extractor,而对于FSL平台,会提供有FSLExtractor来使用,这些Extractor以lib库的形式存在于Android系统中,这些lib库对于解析数据有硬件加速作用,但是源码是不公开的。
所以对于普通的文件,都会有加速的lib库来使用,那么就创建了FslExtractor来使用,进入FslExtractor的构造函数中(FslExtractor.cpp):
FslExtractor::FslExtractor(const sp<DataSource> &source,const char *mime)
: mDataSource(source),
mReader(new FslDataSourceReader(mDataSource)),
mMime(strdup(mime)),
bInit(false),
mFileMetaData(new MetaData)
{
memset(&mLibName,0,255);
mLibHandle = NULL;
IParser = NULL;
parserHandle = NULL;
mFileMetaData->setCString(kKeyMIMEType, mime);
currentVideoTs = 0;
currentAudioTs = 0;
mVideoActived = false;
ALOGV("FslExtractor::FslExtractor mime=%s",mMime);
ALOGD("FslExtractor::FslExtractor mime=%s",mMime);
}
创建了一个FslDataSourceReader来读取文件。
至此,MediaExtractor::Create函数分析完毕。
(二)FslExtractor类的解读:
class FslExtractor : public MediaExtractor
class MediaExtractor : public RefBase
class MediaExtractor : public RefBase {
public:
static sp<MediaExtractor> Create(
const sp<DataSource> &source, const char *mime = NULL);
virtual size_t countTracks() = 0;
virtual sp<MediaSource> getTrack(size_t index) = 0;
enum GetTrackMetaDataFlags {
kIncludeExtensiveMetaData = 1
};
virtual sp<MetaData> getTrackMetaData(
size_t index, uint32_t flags = 0) = 0;
// Return container specific meta-data. The default implementation
// returns an empty metadata object.
virtual sp<MetaData> getMetaData();
enum Flags {
CAN_SEEK_BACKWARD = 1, // the "seek 10secs back button"
CAN_SEEK_FORWARD = 2, // the "seek 10secs forward button"
CAN_PAUSE = 4,
CAN_SEEK = 8, // the "seek bar"
};
// If subclasses do _not_ override this, the default is
// CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE
virtual uint32_t flags() const;
// for DRM
void setDrmFlag(bool flag) {
mIsDrm = flag;
};
bool getDrmFlag() {
return mIsDrm;
}
virtual char* getDrmTrackInfo(size_t trackID, int *len) {
return NULL;
}
virtual void setUID(uid_t uid) {
}
protected:
MediaExtractor() : mIsDrm(false) {}
virtual ~MediaExtractor() {}
private:
bool mIsDrm;
MediaExtractor(const MediaExtractor &);
MediaExtractor &operator=(const MediaExtractor &);
};
这个类是个基类,定义了一些简单的函数。
下面是FslExtractor类的实现:
class FslExtractor : public MediaExtractor {
public:
// Extractor assumes ownership of "source".
FslExtractor(const sp<DataSource> &source,const char *mime);//构造函数
virtual size_t countTracks(); //统计媒体文件中的Track数目
virtual sp<MediaSource> getTrack(size_t index); //获取Track
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);//获取track元数据
virtual sp<MetaData> getMetaData();
virtual uint32_t flags() const;
status_t Init(); //初始化函数
status_t ActiveTrack(uint32 index);
status_t DisableTrack(uint32 index);
status_t HandleSeekOperation(uint32_t index,int64_t * ts, uint32_t flag);
status_t GetNextSample(uint32_t index,bool is_sync);
status_t CheckInterleaveEos(uint32_t index);
status_t ClearTrackSource(uint32_t index);
protected:
virtual ~FslExtractor();
private:
sp<DataSource> mDataSource;
FslDataSourceReader *mReader;
char *mMime;
bool bInit;
char mLibName[255];
void *mLibHandle;
FslParserInterface * IParser; //FslParserInterface类中包含了一个lib库向外提供的函数接口
FslFileStream fileOps;//对于文件的操作函数集合
ParserMemoryOps memOps;//对于内存的操作函数集合
ParserOutputBufferOps outputBufferOps;//对于outputbuffer的操作函数集合
uint32_t mReadMode;
uint32_t mNumTracks;
bool bSeekable;
uint64_t mMovieDuration;
struct TrackInfo {
uint32_t mTrackNum;
sp<FslMediaSource> mSource;
sp<MetaData> mMeta;
const FslExtractor *mExtractor;
bool bCodecInfoSent;
bool bPartial;
sp<ABuffer> buffer;
int64_t outTs = 0;
int32_t syncFrame = 0;
uint32_t max_input_size;
uint32_t type;
bool bIsNeedConvert;
};
Vector<TrackInfo> mTracks;
sp<MetaData> mFileMetaData;
FslParserHandle parserHandle;
Mutex mLock;
int64_t currentVideoTs;
int64_t currentAudioTs;
bool mVideoActived;
bool isLiveStreaming() const;
status_t GetLibraryName();
status_t CreateParserInterface();
status_t ParseFromParser();
status_t ParseMetaData();
status_t ParseMediaFormat();
status_t ParseVideo(uint32 index, uint32 type,uint32 subtype);
status_t ParseAudio(uint32 index, uint32 type,uint32 subtype);
status_t ParseText(uint32 index, uint32 type,uint32 subtype);
int bytesForSize(size_t size);
void storeSize(uint8_t *data, size_t &idx, size_t size);
void addESDSFromCodecPrivate(
const sp<MetaData> &meta,
bool isAudio, const void *priv, size_t privSize);
status_t addVorbisCodecInfo(
const sp<MetaData> &meta,
const void *_codecPrivate, size_t codecPrivateSize);
bool isTrackModeParser();
status_t convertPCMData(sp<ABuffer> inBuffer, sp<ABuffer> outBuffer, int32_t bitPerSample);
FslExtractor(const FslExtractor &);
FslExtractor &operator=(const FslExtractor &);
};
(三)解析文件流程
在NuPlayer::GenericSource::initFromDataSource()函数中,创建好Extractor后,随后就调用mFileMeta = extractor->getMetaData();来解析文件,来看看其内部实现(FslExtractor.cpp):
sp<MetaData> FslExtractor::getMetaData()
{
if(!bInit){
status_t ret = OK;
ret = Init();
if(ret != OK)
return NULL;
}
return mFileMetaData;
}
跳转到Init函数中去执行了:
status_t FslExtractor::Init()
{
status_t ret = OK;
if(mReader == NULL)
return UNKNOWN_ERROR;
ALOGD("FslExtractor::Init BEGIN");
memset (&fileOps, 0, sizeof(FslFileStream));
fileOps.Open = appFileOpen;
fileOps.Read= appReadFile;
fileOps.Seek = appSeekFile;
fileOps.Tell = appGetCurrentFilePos;
fileOps.Size= appFileSize;
fileOps.Close = appFileClose;
fileOps.CheckAvailableBytes = appCheckAvailableBytes;
fileOps.GetFlag = appGetFlag;
memset (&memOps, 0, sizeof(ParserMemoryOps));
memOps.Calloc = appCalloc;
memOps.Malloc = appMalloc;
memOps.Free= appFree;
memOps.ReAlloc= appReAlloc;
outputBufferOps.RequestBuffer = appRequestBuffer;
outputBufferOps.ReleaseBuffer = appReleaseBuffer;
ret = CreateParserInterface();
if(ret != OK){
ALOGE("FslExtractor create parser failed");
return ret;
}
ret = ParseFromParser();
ALOGD("FslExtractor::Init ret=%d",ret);
if(ret == OK)
bInit = true;
return ret;
}
这个Init函数中,初始化了fileOps,memOps和outputBufferOps,分别为它们初始化了对应的函数指针,然后就进入CreateParserInterface函数了,这个函数如下所示:
status_t FslExtractor::CreateParserInterface()
{
status_t ret = OK;
int32 err = PARSER_SUCCESS;
tFslParserQueryInterface myQueryInterface;
ret = GetLibraryName();
if(ret != OK)
return ret;
do{
mLibHandle = dlopen(mLibName, RTLD_NOW);
if (mLibHandle == NULL){
ret = UNKNOWN_ERROR;
break;
}
ALOGD("load parser name %s",mLibName);
myQueryInterface = (tFslParserQueryInterface)dlsym(mLibHandle, "FslParserQueryInterface");
if(myQueryInterface == NULL){
ret = UNKNOWN_ERROR;
break;
}
IParser = new FslParserInterface;
if(IParser == NULL){
ret = UNKNOWN_ERROR;
break;
}
err = myQueryInterface(PARSER_API_GET_VERSION_INFO, (void **)&IParser->getVersionInfo);
if(err)
break;
if(!IParser->getVersionInfo){
err = PARSER_ERR_INVALID_API;
break;
}
// create & delete
err = myQueryInterface(PARSER_API_CREATE_PARSER, (void **)&IParser->createParser);
if(err)
break;
if(!IParser->createParser){
err = PARSER_ERR_INVALID_API;
break;
}
}
这个函数中重要的部分我都用红色标出了,首先通过GetLibraryName函数获取到所需的lib库的名字,对于MP4格式的文件,所需的lib库的名字就为lib_mp4_parser_arm11_elinux.3.0.so,
status_t FslExtractor::GetLibraryName()
{
const char * name = NULL;
for (size_t i = 0; i < sizeof(mime_table) / sizeof(mime_table[0]); i++) {
if (!strcmp((const char *)mMime, mime_table[i].mime)) {
name = mime_table[i].name;
break;
}
}
if(name == NULL)
return NAME_NOT_FOUND;
strcpy(mLibName, "lib_");
strcat(mLibName,name);
strcat(mLibName,"_parser_arm11_elinux.3.0.so");
ALOGD("GetLibraryName %s",mLibName);
return OK;
}
然后使用dlopen函数来打开动态库,把Handler保存在mLibHandle中,这个mLibHandle就交给dlsym系统调用来使用了。有关dlopen和dlsym调用的知识,可以查看:《ShareLibarayMgr.cpp的解析》
然后tFslParserQueryInterface是一个函数指针:
typedef int32 (*tFslParserQueryInterface)(uint32 id, void ** func);
首先声明一个tFslParserQueryInterface类型的变量myQueryInterface,这样的话,myQueryInterface也就是一个函数指针了,然后通过强制类型转换,把dlsym(mLibHandle, "FslParserQueryInterface")也转换成一个tFslParserQueryInterface类型的函数指针,最终通过myQueryInterface(PARSER_API_GET_VERSION_INFO, (void **)&IParser->getVersionInfo);就把一个enum值与lib提供出来的接口函数统一起来。从而每个lib提供出来的函数接口就保存在FslParserInterface * IParser中了,以后如果想要使用lib库提供的函数,就只需要使用IParser中的函数接口就行,如IParser->getVersionInfo(), IParser->createParser2()等等。
下面继续回到FslExtractor::Init()函数中去,下一个调用的函数就是ParseFromParser();
status_t FslExtractor::ParseFromParser()
{
int32 err = (int32)PARSER_SUCCESS;
uint32 flag = FLAG_H264_NO_CONVERT | FLAG_OUTPUT_PTS | FLAG_ID3_FORMAT_NON_UTF8;
uint32 trackCnt = 0;
bool bLive = mReader->isLiveStreaming();
ALOGI("Core parser %s \n", IParser->getVersionInfo());
if(IParser->createParser2){
if(bLive){
flag |= FILE_FLAG_NON_SEEKABLE;
flag |= FILE_FLAG_READ_IN_SEQUENCE;
}
err = IParser->createParser2(flag,
&fileOps,
&memOps,
&outputBufferOps,
(void *)mReader,
&parserHandle);
ALOGD("createParser2 flag=%x,err=%d\n",flag,err);
}else{
err = IParser->createParser(bLive,
&fileOps,
&memOps,
&outputBufferOps,
(void *)mReader,
&parserHandle);
ALOGD("createParser flag=%x,err=%d\n",flag,err);
}
if(PARSER_SUCCESS != err)
{
ALOGE("fail to create the parser: %d\n", err);
return UNKNOWN_ERROR;
}
if(mReader->isStreaming() || !strcasecmp(mMime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)
|| !strcasecmp(mMime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS))
mReadMode = PARSER_READ_MODE_FILE_BASED;
else
mReadMode = PARSER_READ_MODE_TRACK_BASED;
err = IParser->setReadMode(parserHandle, mReadMode);
if(PARSER_SUCCESS != err)
{
ALOGW("fail to set read mode to track mode\n");
mReadMode = PARSER_READ_MODE_FILE_BASED;
err = IParser->setReadMode(parserHandle, mReadMode);
if(PARSER_SUCCESS != err)
{
ALOGE("fail to set read mode to file mode\n");
return UNKNOWN_ERROR;
}
}
if ((NULL == IParser->getNextSample && PARSER_READ_MODE_TRACK_BASED == mReadMode)
|| (NULL == IParser->getFileNextSample && PARSER_READ_MODE_FILE_BASED == mReadMode)){
ALOGE("get next sample did not exist");
return UNKNOWN_ERROR;
}
err = IParser->getNumTracks(parserHandle, &trackCnt);
if(err)
return UNKNOWN_ERROR;
mNumTracks = trackCnt;
if(IParser->initializeIndex){
err = IParser->initializeIndex(parserHandle);
}
ALOGI("mReadMode=%d,mNumTracks=%u",mReadMode,mNumTracks);
err = IParser->isSeekable(parserHandle,(bool *)&bSeekable);
if(err)
return UNKNOWN_ERROR;
ALOGI("bSeekable %d", bSeekable);
err = IParser->getMovieDuration(parserHandle, (uint64 *)&mMovieDuration);
if(err)
return UNKNOWN_ERROR;
err = ParseMetaData();
if(err)
return UNKNOWN_ERROR;
err = ParseMediaFormat();
if(err)
return UNKNOWN_ERROR;
return OK;
}
首先通过IParser->getVersionInfo()获取当前使用的lib库的版本号并打印出来,然后通过IParser->createParser2来创建Parser,然后设置ReadMode,对于实时直播视频等等,它的ReadMode是PARSER_READ_MODE_FILE_BASED,对于普通文件,ReadMode是PARSER_READ_MODE_TRACK_BASED,然后把ReadMode通过IParser->setReadMode函数设置到lib库里面,这些都是lib库需要的一些参数,具体的实现在其内部。
之后判断IParser->getNextSample是否存在,IParser->getNextSample对应PARSER_READ_MODE_TRACK_BASED, 而IParser->getFileNextSample 对应PARSER_READ_MODE_FILE_BASED。这两个函数都是必须提供好的,如果没有这两个函数的话,就无法继续运行了。
然后就是通过IParser->getNumTracks来获取这个媒体文件的track数目,并通过IParser->isSeekable来查询文件是否可以执行seek操作。
然后通过ParseMetaData函数,来获取一些medatada数据,metadata数据包括title,artist,album等等,在系统中显示的这些信息就是从这里获取到的。
之后就是ParseMediaFormat函数了,在这个函数内部会去使用IParser->getNumPrograms, IParser->getProgramTracks,IParser->getTrackType等等操作,获取文件中的track内容和信息。然后根据解析出来的TrackTypes执行ParseVideo,ParseAudio和ParseText函数。
在FslExtractor::ParseVideo函数内部,分别依次调用IParser->getTrackDuration,IParser->getDecoderSpecificInfo,IParser->getBitRate,IParser->getVideoFrameWidth,IParser->getVideoFrameHeight,IParser->getVideoFrameRate等等lib库里面的函数来获取这些信息,然后打印出:
FslExtractor: ParseVideo width=1024,height=768,fps=20,rotate=0
同时呢,解析出来这些数据后,当然是把他们放在metadata数据里面比较合适啊,于是通过meta->setData把这些数据都放到metadata中。
同时,在FslExtractor中有个Vector<TrackInfo> mTracks,这里面保存着这些Track的信息,先声明一个TrackInfo结构体,然后将里面的成员变量一一赋值,最后添加到Vector中,最后打印出这句话:
FslExtractor: add video track index=0,source index=0,mime=video/avc
至此,ParseFromParser()函数算是分析完了,我们继续回到FslExtractor::Init()函数中,打印出这句话:FslExtractor: FslExtractor::Init ret=0