android StageFright框架解读

来源:互联网 发布:php 汉字长度 编辑:程序博客网 时间:2024/06/08 10:32

android StageFright框架解读

android多媒体框架
这里写图片描述

MediaPlayerService Stagefright OpenCore
这里写图片描述

底层openmax
这里写图片描述

1、JAVA类的路径:
frameworks/base/media/java/android/media/MediaPlayer.java
JAVA本地调用部分(JNI):
frameworks/base/media/jni/android_media_MediaPlayer.cpp
内容将被编译成为目标库libmedia_jni.so。
2、多媒体客户端部分:
一个服务端的代理,对应用层提供相关的接口,和服务端交互
frameworks/base/media/libmedia
将被编译成库libmedia.so
3、多媒体服务端部分:
//服务端库
frameworks/base/media/libmediaplayerservice
文件为mediaplayerservice.h和mediaplayerservice.cpp
将被编译成库libmediaplayerservice.so。

MediaPlayerService底层框架:
1、opencore
2、stagefright在MediaPlayerService这一层加入的(与opencore并列),android默认选择stagefright。
Stagefright在 Android中是以shared library的形式存在(libstagefright.so),AwesomePlayer可用来播放video/audio。 AwesomePlayer提供许多API,可以让上层的应用程序(Java/JNI)来调用。
二者都基于openmax框架

MediaPlayerService底层框架演变:
最早期OpenCore => StageFright => 现在NuPlayerDriver
现在Android中使用的是Stagefright + NuPlayer并存的方式:
1、Stagefright负责播放本地的媒体文件
2、NuPlayer用于播放网络流媒体文件
从Android L开始NuPlayeri渐渐开始替代了Stagefright,目前本地播放已切换到NuPlayer上了(Android N已移除了Stagefright)

Stagefight的MediaPLayer框架
这里写图片描述

MediaPlayerService基本调用流程:
1、Java层通过JNI调用Native侧的mediaplayer相关接口
2、mediaPlayerService中创建服务器端客户端,分别实例化相关播放器,把接口事件调用应 用到具体的播放器:
本地媒体调用stagefright,
流媒体调用NuPlayerDriver
3、stagefright会调用AwesomePlayer相应接口, 其实流媒体侧和本地媒体类似的,最后会调用Nuplayer.
AwesomePlayer的几个成员:
mVideoTrack(从多媒体文件中读取视频数据)
mVideoSource(解码视频)
mVideoRenderer(对解码好的视频进行格式转换,android使用的格式为RGB565)
mISurface(重绘图层)
mQueue(event事件队列)

AwesomePlyaer调用其它组件(如MPEG4Extractor)完成,参数mVideoBuffer即为解码后的帧图像,decode则是调用OMXCodec的服务接口. 也就是解码时又通过Binder做了一次跨进程通信. OMXCodec Service文件:
接口定义:
IOMX.h
客户端类:
OMXCodec.cpp
OMXClient.cpp
IOMX.cpp (BpOMX类/BnOMX类)
服务端类:
OMX.cpp
OMXNodeInstance.cpp
说明:
1、 android系统中只用openmax来做code,所以android向上抽象了一层OMXCodec,提供给上层播放器用。 播放器中音视频解码器mVideosource、mAudiosource都是OMXCodec的实例。
2、OMXCodec通过IOMX 依赖binder机制 获得 OMX服务,OMX服务 才是openmax 在android中 实现。
3、 OMX把软编解码和硬件编解码统一看作插件的形式管理起来

StageFrightPlayer.cpp

StagefrightPlayer::StagefrightPlayer()      : mPlayer(new AwesomePlayer) {      ALOGV("StagefrightPlayer");      mPlayer->setListener(this);  }  

数据读取、解码流程:
这里写图片描述
数据由源到最终解码的流程:
DataSource(URI,FD) => MediaExtractor => mVideoTrack => mVideoSource => mVideoBuffer
DataSource(URI,FD) => MediaExtractor => mAudioTrack => mAudioSource => mAudioPlayer
1、MediaPlayerService调用Stagefright相应的接口
2、Stagefright调用AwesomePlayer相应的接口
3、AwesomePlayer调用OMXCode读取ES数据,并且进行解码的处理
4、OMXCodec调用MediaSource的read函数来获取音视频的数据
5、OMXCodec调用Android的IOMX接口,其实就是Stagefrightde中的 OMX实现
6、OMX调用OMXMaster,而OMXMaster调用OMXPluginBase的接口,这里也可以获取外部的Codec的插件,最终调用对应的解码组建来完成解码,不同解码组件不太相同,后面会做介绍
7、解码完成后,通过OMXcodec返回的裸码流数据会在Awesomeplayer中调用Render模块,实现渲染,从而给用户提供了画面

具体操作步骤:
1、设置DataSource,数据源可以两种URI和FD。
URI可以http://,rtsp://等。
FD是一个本地文件描述符,能过FD,可以找到对应的文件。
2、由DataSource生成MediaExtractor。通过sp extractor = MediaExtractor::Create(dataSource)来实现。
MediaExtractor::Create(dataSource)会根据不同的数据内容创建不同的数据读取对象。
3、通过调用setVideoSource由MediaExtractor分解生成音频数据流(mAudioTrack)和视频数据流(mVideoTrack)。
onPrepareAsyncEvent()如果DataSource是URL的话,根据地址获取数据,并开始缓冲,直到获取到mVideoTrack和mAudioTrack。
mVideoTrack和mAudioTrack通过调用initVideoDecoder()和initAudioDecoder()来生成 mVideoSource和mAudioSource这两个音视频解码器。
然后调用postBufferingEvent_l()提交事件开启缓冲。
4、数据缓冲的执行函数是onBufferingUpdate()。缓冲区有足够的数据可以播放时,调用play_l()开始播放。play_l()中关键是调用了postVideoEvent_l(),提交了 mVideoEvent。
这个事件执行时会调用函数onVideoEvent(),内部执行mVideoSource->read(&mVideoBuffer, &options)进行视频解码。
音频解码通过mAudioPlayer实现。
5、视频解码器解码后通过mVideoSource->read读取一帧帧的数据,放到mVideoBuffer中,mVideoRenderer->render(mVideoBuffer)把视频数据发送到显示模块。
当需要暂停或停止时,调用cancelPlayerEvents来提交事件用来停止解码,还可以选择是否继续缓冲数据。

步骤解读:
1、由数据源DataSource生成MediaExtractor。
通过MediaExtractor::Create(dataSource)来实现。Create方法通过两步来生成相应的 MediaExtractor(MediaExtractor.cpp):

    通过dataSource->sniff来探测数据类型    生成相应的Extractor:    if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)            || !strcasecmp(mime, "audio/mp4")) {        return new MPEG4Extractor(source);    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {        return new MP3Extractor(source, meta);    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)            || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {        return new AMRExtractor(source);    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) {        return new WAVExtractor(source);    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) {        return new OggExtractor(source);    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) {        return new MatroskaExtractor(source);    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {        return new MPEG2TSExtractor(source);    }

2、音视频轨道分离,生成mVideoTrack和mAudioTrack两个MediaSource。代码如下(AwesomePlayer.cpp):

if (!haveVideo && !strncasecmp(mime, "video/", 6)) {    setVideoSource(extractor->getTrack(i));    haveVideo = true;} else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {    setAudioSource(extractor->getTrack(i));    haveAudio = true;}

得到的这两个MediaSource,只具有parser功能,没有decode功能。还需要对这两个MediaSource做进一步的包装,获取了两个MediaSource(具有parse和decode功能):

mVideoSource = OMXCodec::Create(            mClient.interface(), mVideoTrack->getFormat(),            false, // createEncoder            mVideoTrack,            NULL, flags);mAudioSource = OMXCodec::Create(                mClient.interface(), mAudioTrack->getFormat(),                false, // createEncoder                mAudioTrack);

3、StageFright的Decode
这里写图片描述
从流中read packet,parse,decode .都在mVideoSource->read(&mVideoBuffer, &options)函数完成

当调用MediaSource.start()方法后,它的内部就会开始从数据源获取数据并解析,等到缓冲区满后便停止。在AwesomePlayer里就可以调用MediaSource的read方法读取解码后的数据。
a: 对于mVideoSource来说,读取的数据:
mVideoSource->read(&mVideoBuffer, &options)交给显示模块进行渲染,mVideoRenderer->render(mVideoBuffer);
b: 对mAudioSource来说,用mAudioPlayer对mAudioSource进行封装,然后由mAudioPlayer负责读取数据和播放控制。

经过“数据流的封装”得到的两个MediaSource,其实是两个OMXCodec。AwesomePlayer和mAudioPlayer都是从MediaSource中得到数据进行播放。AwesomePlayer得到的是最终需要渲染的原始视频数据, 而mAudioPlayer读取的是最终需要播放的原始音频数据。也就是说,从OMXCodec中读到的数据已经是原始数据了。
(1)、
OMXCodec是怎么把数据源经过parse、decode两步以后转化成原始数据的。从OMXCodec::Create这个构造方法开始,它的参数:
IOMX &omx //指的是一个OMXNodeInstance对象的实例。
MetaData &meta //由MediaSource.getFormat获取得到。这个对象的主要成员就是一个KeyedVector<uint32_t, typed_data> mItems,里面存放了一些代表MediaSource格式信息的名值对。
bool createEncoder //指明OMXCodec是编码还是解码。
MediaSource &source //一个MediaExtractor。
char *matchComponentName //指定一种Codec用于生成这个OMXCodec。

a: findMatchingCodecs寻找对应的Codec,找到以后为当前IOMX分配节点并注册事件监听器:omx->allocateNode(componentName, observer, &node)。
b: 把IOMX封装进一个OMXCodec:

sp<OMXCodec> codec = new OMXCodec(                    omx, node, quirks,                    createEncoder, mime, componentName,                    source);

AwesomePlayer中得到这个OMXCodec后,首先调用mVideoSource->start()进行初始化。 OMXCodec初始化主要是做两件事:
a: 向OpenMAX发送开始命令。mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle)
b: 调用allocateBuffers()分配两个缓冲区,存放在Vector mPortBuffers[2]中,分别用于输入和输出。
(2)、
AwesomePlayer开始播放后,通过mVideoSource->read(&mVideoBuffer, &options)读取数据,实际调用OMXCodec.read来读取数据。而OMXCodec.read主要分两步来实现数据的读取:
a:通过调用drainInputBuffers()对mPortBuffers[kPortIndexInput]进行填充,这一步完成 parse。由OpenMAX从数据源把demux后的数据读取到输入缓冲区,作为OpenMAX的输入。
b:通过fillOutputBuffers()对mPortBuffers[kPortIndexOutput]进行填充,这一步完成 decode。由OpenMAX对输入缓冲区中的数据进行解码,然后把解码后可以显示的视频数据输出到输出缓冲区。
(3)、
AwesomePlayer通过mVideoRenderer->render(mVideoBuffer)对经过parse和decode 处理的数据进行渲染。一个mVideoRenderer其实就是一个包装了IOMXRenderer的AwesomeRemoteRenderer:

mVideoRenderer = new AwesomeRemoteRenderer(                mClient.interface()->createRenderer(                        mISurface, component,                        (OMX_COLOR_FORMATTYPE)format,                        decodedWidth, decodedHeight,                                    mVideoWidth, mVideoHeight,                        rotationDegrees));
原创粉丝点击