写在前面:
学习Android多媒体的步骤:
1,Audio PCM &video YUV各种数据的处理,格式的封装与装换原理
2,多媒体的播放框架,nuplayer ,stagefright
3,音视频分离 MediaExtractor
4,音频编解码(以AAC为例)
5,视频图像编解码(以H264为例)
6,音视频同步技术
这一部分的学习之前,需要了解:
1,音视频容器的概念,参考博文:
http://blog.youkuaiyun.com/leixiaohua1020/article/details/17934487
2,不同的视频封装格式标准(这里以MP4文件分析),参考博文:
http://blog.youkuaiyun.com/chenchong_219/article/details/44263691
3,openmax IL框架
https://www.khronos.org/openmaxil
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 process
RegisterSniffer_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::gSniffers
String8 newMimeType;
float newConfidence;
sp<AMessage> newMeta;
if ((*it)(this, &newMimeType, &newConfidence, &newMeta)) {
//执行每一个已注册的sniffXXX函数,比较所有返回true的sniffXXX函数中间,将confidence最大的那个的相关赋值,返回
if (newConfidence > *confidence) {
*mimeT