多媒体编解码】Android 视频解析MediaExtractor"/>
【多媒体编解码】Android 视频解析MediaExtractor
写在前面:
学习Android多媒体的步骤:
1,Audio PCM &video YUV各种数据的处理,格式的封装与装换原理
2,多媒体的播放框架,nuplayer ,stagefright
3,音视频分离 MediaExtractor
4,音频编解码(以AAC为例)
5,视频图像编解码(以H264为例)
6,音视频同步技术
这一部分的学习之前,需要了解:
1,音视频容器的概念,参考博文:
2,不同的视频封装格式标准(这里以MP4文件分析),参考博文:
3,openmax IL框架
4,查看视频文件工具:
ultraedit 一个文本编辑器
Elecard Video Format Analyzer视频格式分析器,可以看到视频每个box的各个元素的说明,偏移值,大小等信息。通过某些具体的box可以查询到视频的格式信息。
=============以下是正文部分====================
以播放本地视频文件为例,创建MediaExtractor的流程,序列图如下:
序列图说明(以下标号代表序列图中的交互序列编号):
交互1,nuplayer::setDataSourceAsync
从MediaPlayer setDataSource开始,实质是调用
setDataSourceAsync(int fd, int64_t offset, int64_t length),不同的播放方式,参数不一样。
主要工作是:
交互2~4 :创建一个GenericSource,同时将获取的参数通过GenericSource::setDataSource传递
交互5: 发送消息kWhatSetDataSource给 nuplayer(AHandler)处理事件。主要是
将获得的nuplayer::Source(GenericSource)赋值给snuplayer::mSource
发送消息给NuPlayerDriver,告诉上层setDataSource完成,提示上层可以开始下一步指令。见交互6:driver->notifySetDataSourceCompleted
交互8:Nuplayer::prepareAsync
上层得到设置谁完成的消息之后,调用这个函数开始下一步的指令,主要工作是:
交互 9, :发送消息kWhatPrepare给Nuplayer(AHandler)
交互10 :nuplayer收到消息后,操作mSource (也是一个AHandler),在这个离职中间,实质是调用NuPlayer::GenericSource::prepareAsync(),主要工作是:
给Souece创建一个ALooper,用来循环接收处理AMessage
发送消息kWhatPrepareAsync给Source(AHandler)开始异步准备- 交互12: 接受到消息后,调用GenericSource::onPrepareAsync(),主要的工作是:
根据条件,实例化NuPlayer::GenericSource::mDataSource,一个具体的DataSource的派生类,本例是 FileSource。
根据mDataSource,创建一个MediaExtractor,GenericSource::initFromDataSource;具体流程,就是交互13~20,这个流程比较繁琐,但是只需要关注17,18,20
交互13~17:这里才是重点
交互13:GenericSource::initFromDataSource
后面还将具体分析这个函数的其他重要工作
1,根据sniff创建指定的mediaExtractor,创建同时读取数据,创建metaData,解析“track”并且分离
2,根据track,初始化mVideoTrack和mAudioTrack,加入 mSources
3,从metaData获取
kKeyDuration
kKeyBitRate交互16:sp MediaExtractor::CreateFromService
主要工作是遍历所有注册的Extractor,分别去读取文件头,根据条件判断具体选用哪个Extractor,以及初始化minetype,具体看下面:
交互17:DataSource::RegisterDefaultSniffers()
// The sniffer can optionally fill in "meta" with an AMessage containing// a dictionary of values that helps the corresponding extractor initialize// its state without duplicating effort already exerted by the sniffer.typedef bool (*SnifferFunc)(const sp<DataSource> &source, String8 *mimeType,float *confidence, sp<AMessage> *meta);// static
void DataSource::RegisterSniffer_l(SnifferFunc func) {for (List<SnifferFunc>::iterator it = gSniffers.begin();it != gSniffers.end(); ++it) {if (*it == func) {return;}}gSniffers.push_back(func);
}
// static
void DataSource::RegisterDefaultSniffers() {Mutex::Autolock autoLock(gSnifferMutex);if (gSniffersRegistered) {return;}/*实质就是将左右的extractor注册并且保存在DataSource::gSniffers(Vector)中间可见,如果需要自定义一个IMediaExtrector的派生类,则必须实现这个方法,这个方法具体什么作用,看下面分析*/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);+if (getuid() == AID_MEDIA) {// WVM only in the media server processRegisterSniffer_l(SniffWVM);}RegisterSniffer_l(SniffMidi);//RegisterSniffer_l(AVUtils::get()->getExtendedSniffer());char value[PROPERTY_VALUE_MAX];if (property_get("drm.service.enabled", value, NULL)&& (!strcmp(value, "1") || !strcasecmp(value, "true"))) {RegisterSniffer_l(SniffDRM);}gSniffersRegistered = true;
}
- 交互18:DataSource::sniff:
主要作用是遍历DataSource::gSniffers,按序执行每个Extractor的SniffXXX函数,给mineType,confidence和meta赋值
bool DataSource::sniff(String8 *mimeType, float *confidence, sp<AMessage> *meta) {*mimeType = "";*confidence = 0.0f;meta->clear();int count =0;{Mutex::Autolock autoLock(gSnifferMutex);if (!gSniffersRegistered) {return false;}}for (List<SnifferFunc>::iterator it = gSniffers.begin();it != gSniffers.end(); ++it) {//遍历DataSource::gSniffersString8 newMimeType;float newConfidence;sp<AMessage> newMeta;if ((*it)(this, &newMimeType, &newConfidence, &newMeta)) {//执行每一个已注册的sniffXXX函数,比较所有返回true的sniffXXX函数中间,将confidence最大的那个的相关赋值,返回if (newConfidence > *confidence) {*mimeType
更多推荐
【多媒体编解码】Android 视频解析MediaExtractor
发布评论