Android4.2.2多媒体架构MediaPlay的创建过程分析(二):解析器的创建

本文均属自己阅读源码的点滴总结,转账请注明出处谢谢。

欢迎和大家交流。qq:1037701636 email: [email protected]

在上一文中,我们分析到setDataSource_pre()函数最终实际返回的是StagefrightPlayer类(class StagefrightPlayer : public MediaPlayerInterface).

1 .继续分析setDataSource 函数:

    // now set data source
    setDataSource_post(p, p->setDataSource(fd, offset, length));

实际是多态下的StagefrightPlayer的setDataSource 函数的实现:

status_t StagefrightPlayer::setDataSource(
        const char *url, const KeyedVector<String8, String8> *headers) {
    return mPlayer->setDataSource(url, headers);
}

mPlayer这个成员函数是new AwesomePlayer出来的对象,故最终是进入了stagefright中去做进一步的处理:

status_t AwesomePlayer::setDataSource(
        int fd, int64_t offset, int64_t length) {
    Mutex::Autolock autoLock(mLock);                                                                                                         ...                             .....                                                                                        return setDataSource_l(dataSource);
}
status_t AwesomePlayer::setDataSource_l(
        const sp<DataSource> &dataSource) {
    sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);//创建一个解析器MPEG4Extractor,mime = NULL

    if (extractor == NULL) {
        return UNKNOWN_ERROR;
    }

    if (extractor->getDrmFlag()) {
        checkDrmStatus(dataSource);
    }

    return setDataSource_l(extractor);
}

MediaExtractor类,可以理解为音视频数据源的解析器,我们来看其创建过程,传入是默认参数mime= NULL:

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)) {//提取mime数值
            ALOGV("FAILED to autodetect media content.");

            return NULL;
        }

        mime = tmp.string();//获取mime值
        ALOGV("Autodetected media content as ‘%s‘ with confidence %.2f",
             mime, confidence);
    }

//根据对文件解析的不同格式创建一个Extractor解析器
    MediaExtractor *ret = NULL;
    if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
            || !strcasecmp(mime, "audio/mp4")) {
        int fragmented = 0;
        if (meta != NULL && meta->findInt32("fragmented", &fragmented) && fragmented) {
            ret = new FragmentedMP4Extractor(source);
        } else {
            ret = new MPEG4Extractor(source);
        }
.................
    return ret;
}

在这里穿插解释下MIME类型的概念,谷歌来的,应该是表示各种视频格式的一个字符段:

video/x-ms-asf asf
video/mpeg mpeg mpg
video/x-msvideo avi
application/vnd.rn-realmedia rm
audio/x-pn-realaudio ram ra
audio/x-aiff aif aiff aifc
audio/mpeg mpga mp3
audio/midi mid midi
audio/wav wav
audio/x-ms-wma wma
video/x-ms-wmv wmv

2. 这里和大家简单分析source->sniff的实现过程,其目的很清楚就是获取当前视频源的MIME类型。

bool DataSource::sniff(
        String8 *mimeType, float *confidence, sp<AMessage> *meta) {
    *mimeType = "";
    *confidence = 0.0f;
    meta->clear();

    Mutex::Autolock autoLock(gSnifferMutex);
    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;
            }
        }
    }

    return *confidence > 0.0;
}

该函数中一个gSnifers的全局变量中查找注册的函数it,函数指针类型为SnifferFunc。那么这些函数是如何注册的呢,我们回到AwesomePlay的构造函数中去:

  DataSource::RegisterDefaultSniffers();
void DataSource::RegisterDefaultSniffers() {
    RegisterSniffer(SniffMPEG4);
    RegisterSniffer(SniffFragmentedMP4);
..........

    char value[PROPERTY_VALUE_MAX];
    if (property_get("drm.service.enabled", value, NULL)
            && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
        RegisterSniffer(SniffDRM);
    }
}

我们看到其注册了多个函数指针,而这都是针对不同的格式进行的注册,将他维护在这个gSniffers迭代器中:

void DataSource::RegisterSniffer(SnifferFunc func) {
    Mutex::Autolock autoLock(gSnifferMutex);

    for (List<SnifferFunc>::iterator it = gSniffers.begin();
         it != gSniffers.end(); ++it) {
        if (*it == func) {
            return;
        }
    }

    gSniffers.push_back(func);//保存函数指针
}

最终都回调到这个函数指针之中去:

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()函数逻辑比较复杂,但总体思想是读取出source的一些头信息,如果好当前的MPEG4格式所需具备的信息一样则返回。

最终返回一个属于该source的MIEM类型,如这里假设的是MEDIA_MIMETYPE_CONTAINER_MPEG4的格式。

3.解析器的创建

经历过上述的过程,继续回到sp<MediaExtractor> MediaExtractor::Create()函数中去,根据提取到的MEME的类型,做如下操作。

  ret = new MPEG4Extractor(source);

创建好上述的解析器后,我们回到AwesomePlayer::setDataSource_l()中,继续执行setDataSource_l(extractor),对新建的这个解析器做处理,其实质是显示音视频A/V的分离。

setVideoSource(extractor->getTrack(i));//设置视频源mVideoTrack ;

setAudioSource(extractor->getTrack(i));//设置音频源mAudioTrack;

mVideoTrack和mAudioTrack的做为创建的AwesomePlay的成员函数,其类型为MPEG4Source,继承了MediaSource。

sp<MediaSource> MPEG4Extractor::getTrack(size_t index) {
    status_t err;
    if ((err = readMetaData()) != OK) {
        return NULL;
    }

    Track *track = mFirstTrack;
    while (index > 0) {
        if (track == NULL) {
            return NULL;
        }

        track = track->next;
        --index;
    }

    if (track == NULL) {
        return NULL;
    }

    return new MPEG4Source(
            track->meta, mDataSource, track->timescale, track->sampleTable);
}

到此为止就是讲视频源进行了A\V的分离,其过程是通过Stagefrightplay多媒体框架——>Awesomeplay——>MPEG4Extractor——>MPEG4Source.这几个过程。

4. 准备好解码器

在完成APP侧的setDataSource后,就进入prepare操作。在MPS侧由如下函数来实现:

status_t MediaPlayerService::Client::prepareAsync()
{
    ALOGV("[%d] prepareAsync", mConnId);
    sp<MediaPlayerBase> p = getPlayer();//stragefrightplay类
    if (p == 0) return UNKNOWN_ERROR;
    status_t ret; 

    ret = p->prepareAsync();

#if CALLBACK_ANTAGONIZER
    ALOGD("start Antagonizer");
    if (ret == NO_ERROR) mAntagonizer->start();
#endif
    return ret;
}

getPlayer获取之前创建的播放器StagefrightPlayer这个对象,继续执行:

status_t StagefrightPlayer::prepareAsync() {
    return mPlayer->prepareAsync();
}

mPlayer即为Awesomeplayer,实际的实现如下:

status_t AwesomePlayer::prepareAsync_l() {
    if (mFlags & PREPARING) {
        return UNKNOWN_ERROR;  // async prepare already pending
    }

    if (!mQueueStarted) {
        mQueue.start();
        mQueueStarted = true;
    }

    modifyFlags(PREPARING, SET);
    mAsyncPrepareEvent = new AwesomeEvent(
            this, &AwesomePlayer::onPrepareAsyncEvent);//onPrepareAsyncEvent回调函数,事件处理

    mQueue.postEvent(mAsyncPrepareEvent);//传入类对象AwesomeEvent,mAsyncPrepareEvent

    return OK;
}

在这里将回答在(一)中提到的事件注册与处理的这个过程。

4.1 AwesomePlayer中的Event处理机制。

a. 首先这里需要来看mQueue,他是TimedEventQueue类的对象,称为时间事件队列。首次调用是,需要进行start。

void TimedEventQueue::start() {
    if (mRunning) {
        return;
    }

    mStopped = false;

    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    pthread_create(&mThread, &attr, ThreadWrapper, this);//创建一个进程

    pthread_attr_destroy(&attr);

    mRunning = true;
}

显而易见的是,这个e事件处理机制脱离了Awesomeplayer,独立创建了一个线程ThradWrapper,其内部调用thredEntry来处理

void TimedEventQueue::threadEntry() {
    prctl(PR_SET_NAME, (unsigned long)"TimedEventQueue", 0, 0, 0);

    for (;;) {
        int64_t now_us = 0;
        sp<Event> event;
            while (mQueue.empty()) {
                mQueueNotEmptyCondition.wait(mLock);
            }
.......
}

该函数就是在不断等待着有事件需要处理,类似于一个Queue里面非空则一直阻塞,等待signal唤醒。

b.我们来看AwesomeEvent类继承了TimedEventQueue的Event内部类。其构造函数表明,将一个函数指针作为一个参数维护在mMethod。可以猜测到这个函数将会作为事件发生时的处理函数。那么这个过程如何触发呢?

struct AwesomeEvent : public TimedEventQueue::Event {
    AwesomeEvent(
            AwesomePlayer *player,
            void (AwesomePlayer::*method)())
        : mPlayer(player),
          mMethod(method) {
    }

protected:
    virtual ~AwesomeEvent() {}

    virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
        (mPlayer->*mMethod)();//调用最终的注册的处理函数
    }

private:
    AwesomePlayer *mPlayer;
    void (AwesomePlayer::*mMethod)();

    AwesomeEvent(const AwesomeEvent &);
    AwesomeEvent &operator=(const AwesomeEvent &);
};

c.  mQueue.postEvent(mAsyncPrepareEvent);//传入类对象AwesomeEvent,mAsyncPrepareEvent来实现事件的唤醒与处理

TimedEventQueue::event_id TimedEventQueue::postTimedEvent(
        const sp<Event> &event, int64_t realtime_us) {
    Mutex::Autolock autoLock(mLock);

    event->setEventID(mNextEventID++);

    List<QueueItem>::iterator it = mQueue.begin();
    while (it != mQueue.end() && realtime_us >= (*it).realtime_us) {
        ++it;
    }

    QueueItem item;
    item.event = event;
    item.realtime_us = realtime_us;

    if (it == mQueue.begin()) {
        mQueueHeadChangedCondition.signal();
    }

    mQueue.insert(it, item);//在mQueue中插入event

    mQueueNotEmptyCondition.signal();//发出信号触发事件

    return event->eventID();
}

将当前的Event对象插入打Awesomeplayer的mQueue队列中,然后发出signal,唤醒threadEntry线程,让线程去处理当前的事件。

5.真正进入解码器创建的世界

void AwesomePlayer::onPrepareAsyncEvent() { //基于队列和事件机制调用
    Mutex::Autolock autoLock(mLock);

    if (mFlags & PREPARE_CANCELLED) {
        ALOGI("prepare was cancelled before doing anything");
        abortPrepare(UNKNOWN_ERROR);
        return;
    }

    if (mUri.size() > 0) {
        status_t err = finishSetDataSource_l();

        if (err != OK) {
            abortPrepare(err);
            return;
        }
    }

    if (mVideoTrack != NULL && mVideoSource == NULL) {
        status_t err = initVideoDecoder();//初始化视频解码器

        if (err != OK) {
            abortPrepare(err);
            return;
        }
    }

    if (mAudioTrack != NULL && mAudioSource == NULL) {
        status_t err = initAudioDecoder();//初始化饮品解码器

        if (err != OK) {
            abortPrepare(err);
            return;
        }
    }

    modifyFlags(PREPARING_CONNECTED, SET);

    if (isStreamingHTTP()) {
        postBufferingEvent_l();
    } else {
        finishAsyncPrepare_l();//完成异步的prepare
    }
}

对于音视频解码器的创建和调用这里不在做分析,考虑到他是一块独特的模块,将在另一文中进行分析,自己也有很多内容需要进一步消化。

Android4.2.2多媒体架构MediaPlay的创建过程分析(二):解析器的创建

时间: 2024-11-05 23:33:40

Android4.2.2多媒体架构MediaPlay的创建过程分析(二):解析器的创建的相关文章

Android4.2.2多媒体架构MediaPlay的创建过程分析(一)

本文均属自己阅读源码的点滴总结,转账请注明出处谢谢. 欢迎和大家交流.qq:1037701636 email:[email protected] Android源码版本Version:4.2.2; 硬件平台 全志A31 前沿:回首往事,记得2012年的时候,那时还年少不知,就研究过android的多媒体框架,那时还是2.3的源码,看过stagefright的源码,记得当时是特别的痛苦.而今,再次看起这个多媒体模块的代码,突然间觉得豁然开朗,模块间的层次清晰,有据可依,遇到的疑问往往都能迎刃而解.

PowerShell创建虚拟机 (二、实现批量创建)

上一篇,我们大概了解到了如何用PowerShell脚本(以下简称PS或PS脚本)创建一台虚拟机,以及自定义虚拟机相关的配置,这一篇,我们一起探讨一下如何实现批量创建虚拟机. 批量创建,顾名思义,就是让脚本运行一次,创建多台虚拟机,最简单的实现方法呢,就是循环,下面我们一起来看一段代码: for($i=1;$i -le10;$i++) { $i } 这是PS脚本中最简单的循环之一,运行之后,效果如下图: 这个PS脚本的意思呢,是从1循环到10,一共10次,每次干的事情呢,就是将变量$i的结果输出到

Android4.2.2下Stagefright多媒体架构中的A31的OMX插件和Codec组件

本文均属自己阅读源码的点滴总结,转账请注明出处谢谢. 欢迎和大家交流.qq:1037701636 email: [email protected] 在前面的博文中提到,AwesomePlayer::onPrepareAsyncEvent()开始进行Codec解码器组件的获取以及创建,这里和大家分享. 1.以解码器实例作为切入点 status_t AwesomePlayer::initVideoDecoder(uint32_t flags) { ATRACE_CALL(); ...... ALOG

MySQL主从多种架构部署及常见错误问题解析

本文的主要内容有mysql复制原理,mysql一主多从.双主架构的示例解读,以及mysql在主从复制架构实践中的常见错误问题和解决方法. 一 mysql复制原理 1 原理解读 mysql的复制(replication)是异步复制,即从一个mysql实列或端口(Master)复制到另一个mysql实列的或端口(slave):复制操作由3个进程完成,其中2个(SQL进程和I/O进程)在Slave上,另一个在Master上:要实现复制,必须打开Master端的二进制日志(log-bin),log-bi

步步深入:MySQL架构总览-&gt;查询执行流程-&gt;SQL解析顺序

前言: 一直是想知道一条SQL语句是怎么被执行的,它执行的顺序是怎样的,然后查看总结各方资料,就有了下面这一篇博文了. 本文将从MySQL总体架构--->查询执行流程--->语句执行顺序来探讨一下其中的知识. 一.MySQL架构总览: 架构最好看图,再配上必要的说明文字. 下图根据参考书籍中一图为原本,再在其上添加上了自己的理解. 从上图中我们可以看到,整个架构分为两层,上层是MySQLD的被称为的‘SQL Layer’,下层是各种各样对上提供接口的存储引擎,被称为‘Storage Engin

MySQL架构总览-&gt;查询执行流程-&gt;SQL解析顺序

前言: 一直是想知道一条SQL语句是怎么被执行的,它执行的顺序是怎样的,然后查看总结各方资料,就有了下面这一篇博文了. 本文将从MySQL总体架构--->查询执行流程--->语句执行顺序来探讨一下其中的知识. 一.MySQL架构总览: 架构最好看图,再配上必要的说明文字. 下图根据参考书籍中一图为原本,再在其上添加上了自己的理解. 从上图中我们可以看到,整个架构分为两层,上层是MySQLD的被称为的'SQL Layer',下层是各种各样对上提供接口的存储引擎,被称为'Storage Engin

Docker源码分析(二):Docker Client创建与命令执行

1. 前言 如今,Docker作为业界领先的轻量级虚拟化容器管理引擎,给全球开发者提供了一种新颖.便捷的软件集成测试与部署之道.在团队开发软件时,Docker可以提供可复用的运行环境.灵活的资源配置.便捷的集成测试方法以及一键式的部署方式.可以说,Docker的优势在简化持续集成.运维部署方面体现得淋漓尽致,它完全让开发者从持续集成.运维部署方面中解放出来,把精力真正地倾注在开发上. 然而,把Docker的功能发挥到极致,并非一件易事.在深刻理解Docker架构的情况下,熟练掌握Docker C

Android4.4深入浅出之SurfaceFlinger框架-渲染一个surface(二)

SurfaceFlinger自启动之后,主要有三种类型线程参与工作: 1.binder线程,负责监控binder设备完成与客户端的交接 2.控制台事件监控线程,负责监控硬件帧缓冲区的睡眠/唤醒状态切换事件. 3.UI渲染线程,负责渲染UI. 一 UI渲染线程 UI渲染线程平时是处于休眠状态,一旦binder线程监测到有其他进程发过来的请求渲染UI的消息就会唤醒UI渲染线程,另一方面一旦SurfaceFlinger服务的控制台事件监控线程发现硬件帧缓冲区即将要进入睡眠或者唤醒状态时,它就会往Sur

DNS服务器介绍(一)——创建DNS正反解析区域

背景介绍 DNS服务作为互联网上一个基础服务承担着将用户请求的名称转换成对应的IP或将IP转换为名称的功能.DNS实际上是将互联网上所有主机的FQDN以"."分割成若干个区域,每一个区域都有特定的主机来进行管理.以正向解析为例:当用户发起对www.contoso.com名称的解析请求时,本地DNS服务器会先查询缓存内是否有该名称的IP,如果没有此时就分为两种情况: 当客户端向本地的DNS服务器发起请求时(1),如果本地DNS服务器不允许递归查询,他会立即向客户端反馈找不到该名称对应的I