Android源码分析:StageFright多媒体框架

本文链接地址: http://www.redwolf-blog.com/?p=991

StageFright in Android2.3.4

在函数sp MediaPlayerService::Client::createPlayer(player_type playerType)中会去创建某个类型的playerStagefrightPlayer就是其中选项之一。

 

StagefrightPlayer是一个wrapper,它使用AwesomePlayer进行实际的操作。

在进行播放操作时,AwesomePlayer::play_l()中创建AudioPlayer音频播放器。

 

AwesomeEvent:该类的功能就是利用其fire成员函数完成在某一个时刻去调用AwesomePlayer的一个成员函数。

 

事件驱动机制

Alooper (见目录frameworks/base/media/libstagefright/foundation/下),它是一个looper循环,在start后它启动一个线程不断去检查mEventQueue队列上是否有event事件以及是否达到执行时间:若有,则调用:

gLooperRoster.deliverMessage(event.mMessage);

分发事件消息,从而最终调用:

handler->onMessageReceived(msg);

去处理消息。可以通过:

void ALooper::post(const sp &msg, int64_t delayUs)

向队列mEventQueue上添加消息并唤醒等待的线程去执行上述检查。

 

event中包含消息,该消息由Amessage表示。

消息处理是AHandler子类完成的,子类实现其纯虚函数onMessageReceived用于处理队列上的某个时刻的Amessage消息。

 

ESDS ?

 

SourceExtractor/Writer

 

MediaSource容器格式,子类包括:

AudioSource依赖于AudioRecord

CameraSource使用C++实现的native Camera,与CameraService交互,用于录像

ThreadedSource依赖于MediaSource

JPEGSource 依赖于DataSource

ShoutcastSource 依赖于HTTPStream

SineSource sin(x)运算得到的数据

 

DataSource的子类包括:

FileSource:文件源,用于读取来自文件的多媒体数据

ThrottledSource 用于对DataSource进行带宽限制

NuHTTPDataSource

NuCachedSource2依赖于 DataSource

OggSource

WAVSource

FMRadioDataSource

 

 

 

Extractor用于分离多媒体数据流中的音频和视频数据,其基类是MediaExtractor,目前Android系统中的子类包括:

AMRExtractor

OggExtractor

PCMExtractor

WAVExtractor

MatroskaExtractor

 

Writer子类用于将数据输出,最常见的输出是输出到文件中。基类是MediaWriterAndroid提供的子类包括:

AMRWriter

MPEG4Writer

MPEG2TSWriter

 

播放流程:

Java API中,当调用完MediaPlayersetDataSourceprepare函数后,就可以再调用play进行播放了。这三个API最终会调用到StagefrightPlayer/AwesomePlayer同名函数。

setDataSource

setDataSource比较简单,其作用就是指定播放数据源。数据源可以来自SDCARD上的文件,也可以来自RTSPHTTPLIVE服务器上的流。

 

其最终调用是将上层传递来的参数转化为DataSource,再由DataSource得到MediaExtractor(见行344):

 

再根据MediaExtractor获得TrackMetaData(行385),再由MetaData得到mime(行388)信息,然后判断是否有音频数据和视频数据(行390393),从而指定source: setVideoSource/setAudioSource(见行391394),为videoaudio播放做准备。代码截图如下:

 

 

Prepare

prepare函数是进行播放前的准备。在AwesomePlayer中的调用顺序如下:

AwesomePlayer::prepare() ->

AwesomePlayer::prepare_l()->

AwesomePlayer::prepareAsync_l() ->

AwesomePlayer::onPrepareAsyncEvent()

 

其中prepareAsync_l() 会创建一个Event并送到队列上去,然后立即返回到prepare_l(),而prepare_l()则在一个锁上等待,待真正的prepare执行完后被唤醒返回。通过发送event的方式实现了异步调用,Event的处理函数是onPrepareAsyncEvent()。函数prepareAsync_l()的代码截图如下,创建event并发送到队列见行1490~1493

 

 

onPrepareAsyncEvent()中完成三个最重要的任务就是:调用finishSetDataSource_l完成对setDataSource后的后续处理工作(见行1752)、初始化AudioVideoDecoder(见行1761和行1770)。

 

finishSetDataSource_l主要是根据不同的源uri字符串创建不同的源。它判断的几种字符串是“http://”“httplive://”、 “rtsp://gtalk/”“rtsp://”,然后根据它们创建不同的数据源实例。AudioVideo初始化主要是根据上面第一步调用setDataSource后,得到的mVideoTrackmAudioTrack作为source传递给OMXCodec,让OMX解码后的数据又作为AudioPlayer的源和Video的源。Video播放是通过不断发送mVideoEvent方式的让lopper循环不断去读取解码后的video数据,然后送往VideoRenderer进行渲染输出,参见后面详述的onVideoEventVideoRenderer介绍。

 

当完成解码器的初始化后,跟据源情况开始缓冲(行1779)或告诉prepare完成(行1781),唤醒在锁上等待的prepare_l()函数。

 

 

postBufferingEvent_l用于发送eventmBufferingEventPending,该event的处理函数是onBufferingUpdate,它用于向UI通知缓冲进度。

 

Play

play_l中,首先检查在initAudioDecoderOMXCodec返回的mAudioSource是否具备,在其不为空(785) 的情况下去创建AudioPlayer:若AudioPlayer已创建则直接resume(行818),否则再检查AudioSink是否提供(行787),然后去创建AudioPlayer(行788),并调用start(行793)开始工作,若出错则需销毁已创建的AudioPlayer(行797)。

 

接着一个主要的动作就是发送mVideoEvent(见行828)开始video的播放:

 

mVideoEvent的处理函数是onVideoEvent。在onVideoEvent中,它会从OMX中已经解码过的Video数据源中读取数据放入到mVideoBuffer缓冲区,然后根据时间差,要么调用mVideoRendererrender直接渲染缓冲区,如果过早则等待下一个片刻去渲染,如果过晚则丢弃该缓冲区中的视频桢。

下面的截图代码主要功能是完成读取解码后的Video数据。见行1233,从OMXCodec中读取解码后的Video数据到缓冲区mVideoBuffer中。

 

接着会获取该视频数据的播放时间戳,见下图行1283

 

并以第一桢到达的系统时间与视频时间戳的差值(见行1305),供后面播放视频桢参考使用。若视频数据到达时间较晚,大于40ms,则丢弃该视频数据,见行1329~1334;若提前10ms到达,则退出等待下一次onVideoEvent再进行处理。

 

 

当满足条件继续往下执行时,则判断mVideoRenderer是否已经初始化;若没有,则调用initRenderer_l进行初始化(见行1350);接着,则使用mVideoRenderer进行视频桢的渲染输出(见行1362);最后,则是释放上一次的缓冲区mLastVideoBuffer,本次的缓冲区被视作上一次的缓冲区,接着发送event(行1372)进行下一次读取视频数据并播放循环。

 

 

当播放完毕或出错时,会导致mStreamDoneEvent的发送,从而onStreamDone被调用。onStreamDone会通知上层播放结束,若标志设置为loop,则跳到初始位置重新循环播放;若有视频源,则还要调用postVideoEvent_l()开启视频播放。

VideoRenderer

AndroidStagefright定义了一个android::VideoRenderer(参见头文件frameworks/base/include/media/stagefright/VideoRenderer.h),作为一个抽象类,它定义了一个统一的Video渲染输出接口即它的render成员函数。各硬件平台厂家需继承该类,提供一个子类去实现针对自己平台的render函数,然后这些平台厂家的VideoRenderer代码放置在libstagefrighthw.so库中,由AwesomeLocalRenderer加载。如QualcommTI的实现在下面这些文件中:

hardware/qcom/media/libstagefrighthw/stagefright_surface_output_msm7x30.cpp

hardware/msm7k/libstagefrighthw/stagefright_surface_output_msm72xx.cpp

hardware/ti/omap3/libstagefrighthw/stagefright_overlay_output.cpp

其它平台厂家如ST-Ericsson的实现则放在:

v

BINDER

endor/st-ericsson/hardware/libstagefrighthw/STEVideoRenderer.cpp

 

 

MediaPlayer

MediaServer

 

 

 

 

Libstagefright.so

 

 

 

 

 

 

 

 

 

 

AwesomePlayer中,针对不同情况,对平台厂家的VideoRenderer有两种不同的使用方法。Stagefright已经将它们封装为两个不同的类:AwesomeLocalRendererAwesomeRemoteRenderer。在AwesomePlayer::initRenderer_l()中,根据不同的解码组件(Decoder Component)名称(见行968)使用这两个Renderer:

 

当解码组件是以OMX开头时,则使用AwesomeRemoteRenderer,否则使用AwesomeLocalRenderer。解码组建信息来自文件OMXCodec.cpp中的kDecoderInfo[] 中的codec信息(TODO: make sure)。

 

AwesomeLocalRenderer:在其init函数(见下面的截图)中,打开libstagefrighthw.so库,优先解析函数符号:“_Z26createRendererWithRotationRKN7android2spINS_8″,获取创建平台厂家自己的VideoRenderer子类的具有支持旋转功能的函数符号,若成功,则调用该函数创建一个VideoRenderer子类对象,供AwesomeLocalRenderer使用。若未成功,则解析“_Z14createRendererRKN7android2spINS_8ISurfaceEEEPKc20″去获取不支持旋转功能的函数符号。在上面都未成功的情况下,则创建一个SoftwareRendererAwesomeLocalRenderer使用。

init函数代码截图如下:

 

 

 

AwesomeRemoteRenderer:它与本地Rednerer一样,只不过是多了一层:它要通过Binder与远端进行交互。具体是:通过IOMXRenderer中定义的render接口,从而调用MediaServer侧的OMXRenderer::render(IOMX::buffer_id buffer) (参见OMX.cpp),亦即通过Binder与对侧的OMXRenderer进行交互。OMXRenderer实际与各平台厂家的使用的是VideoRenderer,后者是在OMX::createRenderer中打开libstagefrighthw.so库,解析出函数符号后进行创建所得的,与同上面AwesomeLocalRendererinit函数。

 

AudioPlayer

AudioPlayer用于播放音频,由AwesomePlayer使用。AudioPlayer将已经解码的音频数据(PCM格式)送往Android中的AudioFlinger,实现声音播放功能。其数据流图如下:

 

 

 

其中Source往往来自于OMXCodec::Create返回的已经解码的PCM MediaSource。编码的音频数据在使用OpenOMX解码后输出时,在stagefright中同样被视作一个source,因此它可以被指定为AudioPlayerMediaSource

AudioPlayer中有两个比较重要的函数,第一个就是start函数,第二个是回调函数所使用的fillBuffer函数。

start函数检查上层调用者是否给指定了mAudioSink,若开发者指定了自己的AudioSinkMediaPlayerBase的嵌套类,如在MediaPlayerService这有个嵌套类AudioOutputAudioCache是其子类),则使用它进行输出(见行112);否则,则创建一个Android中的AudioTrack的实例作为AudioSink输出(见行134)。

 

在打开mAudioSink时,指定了回调函数AudioPlayer::AudioSinkCallback。在创建AudioTrack时,指定了回调函数AudioCallback。二者最终均调用的是fillBuffer

AudioTrack将要调用的回调函数是:

 

它只处理EVENT_MORE_DATA(见273),其它Event直接返回。

 

 

AudioTrack中的processAudioBuffer函数中,当需要上游的数据进行处理时,它就使用EVENT_MORE_DATA(见行1022)调用回调函数,让AudioPlayer将数据填充到其缓冲区中。processAudioBuffer函数代码片段如下:

 

其中mCbf就是回调函数,它是一个指向AudioPlaye中的静态函数:

AudioCallback(int event, void *user, void *info) 的函数指针。

AudioTrack中,当指定了回调函数时,会创建一个AudioTrackThread线程,该线程将不断地执行processAudioBuffer,调用回调函数处理各种event事件,上述的EVENT_MORE_DATA即是其中之一。

 

AudioPlayerfillBuffer中,它会调用mSourceread将解码出的数据读到缓冲区mInputBuffer中,后又将其copyAudioTrack提供的缓冲区中。

OMX

OMXClient

用于获取从MediaPlayerService中获取IOMX(见行38),见connect函数代码(行31~42):

 

MediaPlayerService(文件MediaPlayerService.cpp)中的实现如下(行291):

 

 

 

OMX

OMX类(参见代码frameworks/base/media/libstagefright/omx/omx.cpp)继承自BnOMX,运行在mediaplayer进程中,执行对侧来自IOMX的调用请求。OMX类根据node_id查找到对应的OMXNodeInstance,将调用请求转到OMXNodeInstance,后者将调用OpenMAX IL接口,参见:

frameworks/base/media/libstagefright/omx/OMXNodeInstance.cpp

Android的代码目录frameworks/base/include/media/stagefright/openmax下面给出了OpenMAX IL的头文件,诸如OMX_Core.h。在OMX_Core.h中声明了OMX_Init/OMX_Deinit/OMX_GetHandle/OMX_UseBuffer等函数或宏,它们是OpenMAX IL 接口API的一部分。在OMXNodeInstance.cpp中就调用到OpenMAX IL部分接口函数。各个平台厂家可以遵循OpenMAX IL规范给出自己的OpenMAX IL实现,比如TI在下面的文件中:

hardware/ti/omap3/omx/system/src/openmax_il/omx_core/src/OMX_Core.c

给出了其部分API实现。ST-Ericsson的部分API实现位于:

vendor/st-ericsson/multimedia/linux/bellagio_omxcore/libomxil-bellagio/src/omxcore.c

 

A/V同步

libstagefright中定义了时钟源TimeSource这个类,其成员函数getRealTimeUs用于返回自播放开始的时长。SystemTimeSource直接是来自系统时间,getRealTimeUs返回值就是调用时刻系统时钟与创建SystemTimeSource实例时系统时钟的差值。

AudioPlayer也继承自TimeSource,因此也是一个时钟源。它的播放时钟依赖于来自于已播放的桢数除以采样率,然后再减去延迟,延迟往往由AudioSink(或AudioTrack)提供。视频播放会依赖AudioPlayer的时钟。当从meta中得到视频桢时间戳信息时,然后以时钟源作为基准进行同步,当晚于40ms时则丢弃,当早于10ms则等待。

 

 

 

StagefrightRecorder

 

AndroidRecorder使用,统一使用MediaRecorderClient,由它作为Recorder的代表。在MediaRecorderClient中,将使用不同的多媒体框架的Recorder,比如OpenCorePVMediaRecoderStagefrightRecorder。在MediaPlayerServicecreateMediaRecorder函数中,它创建一个MediaRecorderClient的实例,后者的构造函数将根据设置选择创建一个Recorder(比如StagefrightRecorder)。

StagefrightRecorder中,比较重要的是start函数,它是recording的起点,是录制各种格式的入口点。它的代码很简单,在Switch分支中,调用不同的Recording函数:

 

 

对于AAC的录制,startAACRecording会给予未实现的提示而返回。现在看一下mpeg4的录制过程,见函数startMPEG4Recording

它最先是创建audioEncodervideoEncoder,并将它们指定为writersource;接着为该输出的文件格式容器添加meta数据;在指定listener后,就可以调用writerstart进行录制了。创建audioEncodervideoEncoder的函数如下:

setupAudioEncoder:用于创建audioEncoder,并将其指定为writer的源。目前Android中只支持AMR(包括窄带NB和宽带WB)和AAC(实际上AAC录音未实现)两种格式。

 

 

createAudioSource:创建音频源,主要做下面3部分工作:它首先创建一个AudioSource对象;接着为其指定meta数据,诸如MIME类型、声道、BitrateTimescale等;最后,调用OMXCodec::Create返回一个audioEncoder,这个audioEncoder被当作writer的源(在setupAudioEncoder中指定为writer的源)。

 

setupVideoEncoder:类似于音频,它用于创建videoEncoder。它首先创建一个CameraSource的对象作为源,在配置完其meta数据后,使用OMXCodec::Create创建一个videoEncoder

1001调用setupCameraSource配置完Camera后,接着在1004创建一个CameraSource的实例;接着配置meta信息。从代码看,目前Android只支持h263h264mpeg4的视频编码录制。

 

在下面的行1057处使用OMXCodec创建了一个videoEncoder,并通过修改实参所指区域将其返回给调用者。

 

 

 

 

StagefrightMediaScanner

 

StagefrightMetadataRetriever


你可能感兴趣的:(Android)