【多媒体编解码】Android 视频解析MediaExtractor

本文详细介绍了Android中使用MediaExtractor进行多媒体数据解析的过程,特别是在播放本地视频文件时的创建流程。从MediaPlayer的setDataSource开始,经过一系列的封装和处理,最终通过MediaExtractor分离音视频轨道。文章还探讨了DataSource、MediaSource和MediaExtractor的关系,以及自定义MediaExtractor所需实现的组件和方法。
摘要由CSDN通过智能技术生成

写在前面:

学习Android多媒体的步骤:
1,Audio PCM &video YUV各种数据的处理,格式的封装与装换原理
2,多媒体的播放框架,nuplayer ,stagefright
3,音视频分离 MediaExtractor
4,音频编解码(以AAC为例)
5,视频图像编解码(以H264为例)
6,音视频同步技术

这一部分的学习之前,需要了解:
1,音视频容器的概念,参考博文:
http://blog.csdn.net/leixiaohua1020/article/details/17934487
2,不同的视频封装格式标准(这里以MP4文件分析),参考博文:
http://blog.csdn.net/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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值