本文链接地址: http://www.redwolf-blog.com/?p=991
StageFright in Android2.3.4
在函数sp
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
向队列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子类用于将数据输出,最常见的输出是输出到文件中。基类是MediaWriter,Android提供的子类包括:
AMRWriter
MPEG4Writer
MPEG2TSWriter
播放流程:
在Java API中,当调用完MediaPlayer的setDataSource和prepare函数后,就可以再调用play进行播放了。这三个API最终会调用到StagefrightPlayer/AwesomePlayer同名函数。
setDataSource
setDataSource比较简单,其作用就是指定播放数据源。数据源可以来自SDCARD上的文件,也可以来自RTSP或HTTPLIVE服务器上的流。
其最终调用是将上层传递来的参数转化为DataSource,再由DataSource得到MediaExtractor(见行344):
再根据MediaExtractor获得Track的MetaData(行385),再由MetaData得到mime(行388)信息,然后判断是否有音频数据和视频数据(行390、393),从而指定source: setVideoSource/setAudioSource(见行391、394),为video和audio播放做准备。代码截图如下:
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)、初始化Audio和Video的Decoder(见行1761和行1770)。
finishSetDataSource_l主要是根据不同的源uri字符串创建不同的源。它判断的几种字符串是“http://”、“httplive://”、 “rtsp://gtalk/”和“rtsp://”,然后根据它们创建不同的数据源实例。Audio和Video初始化主要是根据上面第一步调用setDataSource后,得到的mVideoTrack和mAudioTrack作为source传递给OMXCodec,让OMX解码后的数据又作为AudioPlayer的源和Video的源。Video播放是通过不断发送mVideoEvent方式的让lopper循环不断去读取解码后的video数据,然后送往VideoRenderer进行渲染输出,参见后面详述的onVideoEvent和VideoRenderer介绍。
当完成解码器的初始化后,跟据源情况开始缓冲(行1779)或告诉prepare完成(行1781),唤醒在锁上等待的prepare_l()函数。
postBufferingEvent_l用于发送event:mBufferingEventPending,该event的处理函数是onBufferingUpdate,它用于向UI通知缓冲进度。
Play
在play_l中,首先检查在initAudioDecoder中OMXCodec返回的mAudioSource是否具备,在其不为空(行785) 的情况下去创建AudioPlayer:若AudioPlayer已创建则直接resume(行818),否则再检查AudioSink是否提供(行787),然后去创建AudioPlayer(行788),并调用start(行793)开始工作,若出错则需销毁已创建的AudioPlayer(行797)。
接着一个主要的动作就是发送mVideoEvent(见行828)开始video的播放:
mVideoEvent的处理函数是onVideoEvent。在onVideoEvent中,它会从OMX中已经解码过的Video数据源中读取数据放入到mVideoBuffer缓冲区,然后根据时间差,要么调用mVideoRenderer的render直接渲染缓冲区,如果过早则等待下一个片刻去渲染,如果过晚则丢弃该缓冲区中的视频桢。
下面的截图代码主要功能是完成读取解码后的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
Android为Stagefright定义了一个android::VideoRenderer(参见头文件frameworks/base/include/media/stagefright/VideoRenderer.h),作为一个抽象类,它定义了一个统一的Video渲染输出接口即它的render成员函数。各硬件平台厂家需继承该类,提供一个子类去实现针对自己平台的render函数,然后这些平台厂家的VideoRenderer代码放置在libstagefrighthw.so库中,由AwesomeLocalRenderer加载。如Qualcomm和TI的实现在下面这些文件中:
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已经将它们封装为两个不同的类:AwesomeLocalRenderer和AwesomeRemoteRenderer。在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″去获取不支持旋转功能的函数符号。在上面都未成功的情况下,则创建一个SoftwareRenderer供AwesomeLocalRenderer使用。
init函数代码截图如下:
AwesomeRemoteRenderer:它与本地Rednerer一样,只不过是多了一层:它要通过Binder与远端进行交互。具体是:通过IOMXRenderer中定义的render接口,从而调用MediaServer侧的OMXRenderer::render(IOMX::buffer_id buffer) (参见OMX.cpp),亦即通过Binder与对侧的OMXRenderer进行交互。OMXRenderer实际与各平台厂家的使用的是VideoRenderer,后者是在OMX::createRenderer中打开libstagefrighthw.so库,解析出函数符号后进行创建所得的,与同上面AwesomeLocalRenderer的init函数。
AudioPlayer
AudioPlayer用于播放音频,由AwesomePlayer使用。AudioPlayer将已经解码的音频数据(PCM格式)送往Android中的AudioFlinger,实现声音播放功能。其数据流图如下:
其中Source往往来自于OMXCodec::Create返回的已经解码的PCM MediaSource。编码的音频数据在使用OpenOMX解码后输出时,在stagefright中同样被视作一个source,因此它可以被指定为AudioPlayer的MediaSource。
AudioPlayer中有两个比较重要的函数,第一个就是start函数,第二个是回调函数所使用的fillBuffer函数。
start函数检查上层调用者是否给指定了mAudioSink,若开发者指定了自己的AudioSink(MediaPlayerBase的嵌套类,如在MediaPlayerService这有个嵌套类AudioOutput和AudioCache是其子类),则使用它进行输出(见行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即是其中之一。
在AudioPlayer的fillBuffer中,它会调用mSource的read将解码出的数据读到缓冲区mInputBuffer中,后又将其copy到AudioTrack提供的缓冲区中。
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
Android对Recorder使用,统一使用MediaRecorderClient,由它作为Recorder的代表。在MediaRecorderClient中,将使用不同的多媒体框架的Recorder,比如OpenCore的PVMediaRecoder或StagefrightRecorder。在MediaPlayerService的createMediaRecorder函数中,它创建一个MediaRecorderClient的实例,后者的构造函数将根据设置选择创建一个Recorder(比如StagefrightRecorder)。
在StagefrightRecorder中,比较重要的是start函数,它是recording的起点,是录制各种格式的入口点。它的代码很简单,在Switch分支中,调用不同的Recording函数:
对于AAC的录制,startAACRecording会给予未实现的提示而返回。现在看一下mpeg4的录制过程,见函数startMPEG4Recording:
它最先是创建audioEncoder和videoEncoder,并将它们指定为writer的source;接着为该输出的文件格式容器添加meta数据;在指定listener后,就可以调用writer的start进行录制了。创建audioEncoder和videoEncoder的函数如下:
setupAudioEncoder:用于创建audioEncoder,并将其指定为writer的源。目前Android中只支持AMR(包括窄带NB和宽带WB)和AAC(实际上AAC录音未实现)两种格式。
createAudioSource:创建音频源,主要做下面3部分工作:它首先创建一个AudioSource对象;接着为其指定meta数据,诸如MIME类型、声道、Bitrate和Timescale等;最后,调用OMXCodec::Create返回一个audioEncoder,这个audioEncoder被当作writer的源(在setupAudioEncoder中指定为writer的源)。
setupVideoEncoder:类似于音频,它用于创建videoEncoder。它首先创建一个CameraSource的对象作为源,在配置完其meta数据后,使用OMXCodec::Create创建一个videoEncoder。
行1001调用setupCameraSource配置完Camera后,接着在1004创建一个CameraSource的实例;接着配置meta信息。从代码看,目前Android只支持h263、h264和mpeg4的视频编码录制。
在下面的行1057处使用OMXCodec创建了一个videoEncoder,并通过修改实参所指区域将其返回给调用者。
StagefrightMediaScanner
StagefrightMetadataRetriever