您的位置:首页 > 运维架构 > 网站架构

Android StagFright架构分析

2013-10-23 11:33 197 查看
第一章stagefright整体分析
从Android2.0开始,新添加了stagefright框架,并且默认情况android选择stagefright,并没有完全抛弃 opencore,主要是做了一个OMX层,仅仅是对 opencore的omx-component部分做了引用。stagefright是在MediaPlayerService这一层加入的,和 opencore是并列的。Stagefright在 Android中是以shared library的形式存在(libstagefright.so),其中的module
--AwesomePlayer可用来播放video/audio。AwesomePlayer提供许多API,可以让上层的应用程序(Java/JNI)来调用。

第二章stagefright 数据流封装
2.1由数据源DataSource生成MediaExtractor

Awesomeplayer通过setDataSource()接口函数来设置数据源,并完成数据源的封装。最后得到两个数据源流,分别是mAudioTrack,mVideoTrack。如图2-1所示。

图2-1 音视频数据源设置
图MediaExtractor::Create(dataSource)通过两步来生成相应的MediaExtractor(MediaExtractor.cpp):

(1).source->sniff()这个是探测当前这个数据源格式是否支持。当然这个探测的前提条件是DataSource.cpp文件里面,早就注册好的一些音视频文件格式。

 参考DataSource.cpp里面的代码:

 voidDataSource::RegisterDefaultSniffers() {

   RegisterSniffer(SniffMPEG4); //mp4文件格式, 解析文件MPEG4Extractor.cpp

   RegisterSniffer(SniffMatroska); //解析文件,MatroskaExtractor.cpp

   RegisterSniffer(SniffOgg);   //ogg文件格式, 解析文件
OggExtractor.cpp

   RegisterSniffer(SniffWAV);   //wav文件格式, 解析文件WAVExtractor.cpp

   RegisterSniffer(SniffAMR);   //amr文件格式, 解析文件
AMRExtractor.cpp

   RegisterSniffer(SniffMPEG2TS); //ts流文件,解析文件MPEG2TSExtractor.cpp

   RegisterSniffer(SniffMP3); //mp3文件格式, 解析文件MP3Extractor.cpp

   RegisterSniffer(SniffAAC);    //aac文件格式, 解析文件
AACExtractor.cpp



所以一个音视频多媒体文件格式要被支持,需要在DataSource.cpp文件里面先定义好一个多媒体文件检测的函数,当判断是否支持的时候就会调用对应的检测函数,例如像SniffMP3()函数,就是在MP3Extractor.cpp 文件中被定义。

 (2). 生成相应的Extractor:

    if (!strcasecmp(mime,MEDIA_MIMETYPE_CONTAINER_MPEG4)

           || !strcasecmp(mime, "audio/mp4")) {

        return newMPEG4Extractor(source);

    } else if (!strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_MPEG)) {

        return newMP3Extractor(source, meta);

    } else if (!strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_AMR_NB)

           || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {

        return newAMRExtractor(source);

    } else if (!strcasecmp(mime,MEDIA_MIMETYPE_CONTAINER_WAV)) {

        return newWAVExtractor(source);

    } else if (!strcasecmp(mime,MEDIA_MIMETYPE_CONTAINER_OGG)) {

        return newOggExtractor(source);

    } else if (!strcasecmp(mime,MEDIA_MIMETYPE_CONTAINER_MATROSKA)) {

        return newMatroskaExtractor(source);

    } else if (!strcasecmp(mime,MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {

        return newMPEG2TSExtractor(source);

    }

创建了这个类对象后,当播放影视频数据解码就是通过这个类对象来读取原来的音视频的数据源,其中读数据源的函数就在对应文件XXXExtractor.cpp文件里,这个文件主要是包含多媒体格式的解析功能。

 

从上面分析得出,一个多媒体格式在数据源方面要做到一下这3个步骤:

(1).要有一个解析文件格式文件,XXXExtractor.cpp文件。

(2).把这个多媒体文件格式检测函数SniffXXX()注册到DataSource.cpp文件中。

(3).在MediaExtractor.cpp文件的Create()函数后面,添加一个匹配的多媒体类。

 

2.2 原始音视频数据流如何被decoder封装调用

在上一节主要介绍数据源的获取,又是如何被decoder调用的。在上一节中,到最后主要就是得到两个类对象,mAudioTrack,mVideoTrack。

在Awesomeplayer.cpp文件中,可以找到这样2个函数,initVideoDecoder() initAudioDecoder(), 在这两个函数中,分别可以找到如下2个函数,创建decoder。

mVideoSource = OMXCodec::Create(

           mClient.interface(), mVideoTrack->getFormat(),

           false, // createEncoder

           mVideoTrack,

           NULL, flags);

mAudioSource = OMXCodec::Create(

               mClient.interface(), mAudioTrack->getFormat(),

               false, // createEncoder

               mAudioTrack);

其实在创建的时候,这两个类对象就被传递进去,当真正启动播放的时候,deocder只要用这个两个类去read数据,就能得到对应的au
4000
dio、video原始数据流。

 

分析stagefright自带的mp3解码过程中数据流被调用过程

图2-2 音频decoder初始化过程
图2-2是libstagefright自带的mp3解码器初始化过程,其中红色的字体就是mp3数据源被初始化的过程,最红被会保存在mp3decoder类里面一个mSource对象里面。

在mp3播放过程中,当audio需要pcm数据的时候,就会调用mp3decoder.cpp文件read函数,如下图2-3所示。

图2-3 mp3播放过程中请求数据
第三章stagefright audioplayer分析
3.1 audio player分析   

创建的audiocodec过程参考图2-2所示。

mAudioSource =OMXCodec::Create(

               mClient.interface(),

               mAudioTrack->getFormat(),

               false, // createEncoder

               mAudioTrack);

这里有一个比较重要的变量函数,mAudioSource,其实就是对应一个audio的codec类。例如软件mp3,这个类就是对应了mp3decoder.cpp文件里面的类对象。AwesomePlayer中得到这个OMXCodec后,调用mAudioSource ->start()进行OMXCodec初始化。

图3-1 awesomeplayer 创建audioplayer过程
等到上层调用play函数的时候,在awesomeplayer会创建一个audioplayer类,这里需要注意图3-1中,红色部分的mAudioSink这个变量,是pcm数据消费端的一个类对象。其实就是一个MediaPlayerService::AudioOutput()这个类对象。为什么需要在这里特别说明一下这个变量,因为当开始播放音乐的时候,会调用下面这个类函数。

status_t MediaPlayerService::AudioOutput::open(

        uint32_tsampleRate, int channelCount, int format, int bufferCount,

        AudioCallback cb, void *cookie,LatencyCallback latencyCb);

这个函数的参数AudioCallback cb就是请求pcm数据的callback函数。

上面这点明白了之后,我们再来看一下audioplayer里面的start()函数。

status_t AudioPlayer::start(bool sourceAlreadyStarted) {



    status_t err = mAudioSink->open(

               mSampleRate, numChannels, AudioSystem::PCM_16_BIT,

               DEFAULT_AUDIOSINK_BUFFERCOUNT,

                &AudioPlayer::AudioSinkCallback, this,

               &AudioPlayer::LatencyCallback);



}

这个函数,就是MediaPlayerService::AudioOutput::open()这个函数。所以我们这里的焦点就看到上面红色标注的函数AudioPlayer::AudioSinkCallback这个callback函数的功能,就是填充pcm数据到buffer里面。我们来分析一下这个函数的具体流程。

图3-2 AudioSinkCallback分析
通过这个函数的流程,就可以把第二章的内容串联起来了。图3-3是一个以MP3软解码为示例的一个数据流程图。

图3-3 MP3软解码数据流程图
 

从图3-3中,我们可以知道,Audioplayer为AwesomePlayer的成员,audioplayer通过callback来驱动数据的获取,数据的获取都抽象成mSource->Read()来完成,且codec内部的read函数把parser和dec 绑在一起。

 

第四章stagefright 处理流程
Audioplayer 为AwesomePlayer的成员,audioplayer通过callback来驱动数据的获取,awesomeplayer则是通过 videoevent来驱动。二者有个共性,就是数据的获取都抽象成mSource->Read()来完成,且read内部把parser和dec 绑在一起。Stagefright
AV同步部分,audio完全是callback驱动数据流,video部分在onVideoEvent里会获取audio的时间戳,是传统的AV时间戳 做同步。

4.1 audio video 音视频同步处理

    在stagefright中,audio的输出是通过callback函数来启动,video则根据audio的timestamp来做同步。

    当callback函数被调用时,audioplayer读取解码后的pcm数据时候,会取得两个时间值

   CHECK(mInputBuffer->meta_data()->findInt64( kKeyTime,&mPositionTimeMediaUs));

    mPositionTimeRealUs =((mNumFramesPlayed +size_done / mFrameSize) * 1000000)/ mSampleRate;

 其中mPositionTimeMediaUs就是时间标签timestamp,mPositionTimeRealUs则是播放此资料的实际时间,是根据比特率和帧数算出来的。

   Video则是根据audio的时间标签来作出差值计算的,在awesomeplayer.cpp文件的onVideoEvent()函数中。

void AwesomePlayer::onVideoEvent()

{  

..

  mVideoSource->read(&mVideoBuffer, ...);

  mVideoBuffer->meta_data()->findInt64(kKeyTime,&timeUs);

  mAudioPlayer->getMediaTimeMapping(&realTimeUs,&mediaTimeUs);

  mTimeSourceDeltaUs = realTimeUs - mediaTimeUs;

  nowUs = ts->getRealTimeUs() - mTimeSourceDeltaUs;

  latenessUs = nowUs - timeUs;

  ...

    if (latenessUs > 40000) {

        // We're more than40ms late.

        LOGV("we'relate by %lld us (%.2fsecs)", latenessUs, latenessUs / 1E6);

        mVideoBuffer->release();

        mVideoBuffer =NULL;

       postVideoEvent_l();

        return; }

    if (latenessUs <-10000) {

        // We're more than10ms early.

       postVideoEvent_l(10000);

        return;   }  }

用getMediaTimeMapping()获取了audio的实际播放时间和媒体时间。根据这两个时间,计算一个是视频和audio之间的一个时间差值latenessUs,然后根据这个值,进行视频跳转处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: