StageFright框架流程解读
1、 StageFright介绍
Android froyo版本号多媒体引擎做了变动,新加入�了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)来调用。
2、 StageFright数据流封装
2.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.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;
}
2.3》得到的这两个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);
当调用MediaSource.start()方法后,它的内部就会開始从数据源获取数据并解析,等到缓冲区满后便停止。在AwesomePlayer里就能够调用MediaSource的read方法读取解码后的数据。
对于mVideoSource来说,读取的数据:mVideoSource->read(&mVideoBuffer, &options)交给显示模块进行渲染,mVideoRenderer->render(mVideoBuffer);
对mAudioSource来说,用mAudioPlayer对mAudioSource进行封装,然后由mAudioPlayer负责读取数据和播放控制。
3、 StageFright的Decode
经过“数据流的封装”得到的两个MediaSource,事实上是两个OMXCodec。AwesomePlayer和mAudioPlayer都是从MediaSource中得到数据进行播放。AwesomePlayer得到的是终于须要渲染的原始视频数据,而mAudioPlayer读取的是终于须要播放的原始音频数据。也就是说,从OMXCodec中读到的数据已经是原始数据了。
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。
先使用findMatchingCodecs寻找相应的Codec,找到以后为当前IOMX分配节点并注冊事件监听器:omx->allocateNode(componentName, observer, &node)。最后,把IOMX封装进一个OMXCodec:
sp<OMXCodec> codec = new OMXCodec(
omx, node, quirks,
createEncoder, mime, componentName,
source);
这样就得到了OMXCodec。
AwesomePlayer中得到这个OMXCodec后,首先调用mVideoSource->start()进行初始化。 OMXCodec初始化主要是做两件事:
向OpenMAX发送開始命令。mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle)
调用allocateBuffers()分配两个缓冲区,存放在Vector<BufferInfo> mPortBuffers[2]中,分别用于输入和输出。
AwesomePlayer開始播放后,通过mVideoSource->read(&mVideoBuffer, &options)读取数据。mVideoSource->read(&mVideoBuffer, &options)详细是调用OMXCodec.read来读取数据。而OMXCodec.read主要分两步来实现数据的读取:
通过调用drainInputBuffers()对mPortBuffers[kPortIndexInput]进行填充,这一步完毕 parse。由OpenMAX从数据源把demux后的数据读取到输入缓冲区,作为OpenMAX的输入。
通过fillOutputBuffers()对mPortBuffers[kPortIndexOutput]进行填充,这一步完毕 decode。由OpenMAX对输入缓冲区中的数据进行解码,然后把解码后能够显示的视频数据输出到输出缓冲区。
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));
4、 StageFright处理流程
Audioplayer 为AwesomePlayer的成员,audioplayer通过callback来驱动数据的获取,awesomeplayer则是通过 videoevent来驱动。二者有个共性,就是数据的获取都抽象成mSource->Read()来完毕,且read内部把parser和dec 绑在一起。Stagefright AV同步部分,audio全然是callback驱动数据流,video部分在onVideoEvent里会获取audio的时间戳,是传统的AV时间戳做同步。
4.1》AwesomePlayer的Video主要有下面几个成员:
mVideoSource(解码视频)
mVideoTrack(从多媒体文件里读取视频数据)
mVideoRenderer(对解码好的视频进行格式转换,android使用的格式为RGB565)
mISurface(重画图层)
mQueue(event事件队列)
4.2》stagefright执行时的Audio部分抽象流程例如以下:
设置mUri的路径
启动mQueue,创建一个线程来执行 threadEntry(命名为TimedEventQueue,这个线程就是event调度器)
打开mUri所指定的文件的头部,则会依据类型选择不同的分离器(如MPEG4Extractor)
使用 MPEG4Extractor对MP4进行音视频轨道的分离,并返回MPEG4Source类型的视频轨道给mVideoTrack
依据 mVideoTrack中的编码类型来选择解码器,avc的编码类型会选择AVCDecoder,并返回给mVideoSource,并设置mVideoSource中的mSource为mVideoTrack
插入onVideoEvent到Queue中,開始解码播放
通过mVideoSource对象来读取解析好的视频buffer
假设解析好的buffer还没到AV时间戳同步的时刻,则推迟到下一轮操作
mVideoRenderer为空,则进行初始化(假设不使用 OMX会将mVideoRenderer设置为AwesomeLocalRenderer)
通过mVideoRenderer对象将解析好的视频buffer转换成RGB565格式,并发给display模块进行图像绘制
将onVideoEvent又一次插入event调度器来循环
4.3》数据由源到终于解码后的流程例如以下:
URI,FD
|
DataSource
|
MediaExtractor
|
mVideoTrack mAudioTrack//音视频数据流
|
mVideoSource mAudioSource//音视频解码器
| |
mVideoBuffer mAudioPlayer
说明:
设置DataSource,数据源能够两种URI和FD。URI能够http://,rtsp://等。FD是一个本地文件描写叙述符,能过FD,能够找到相应的文件。
由DataSource生成MediaExtractor。通过sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);来实现。 MediaExtractor::Create(dataSource)会依据不同的数据内容创建不同的数据读取对象。
通过调用setVideoSource由MediaExtractor分解生成音频数据流(mAudioTrack)和视频数据流(mVideoTrack)。
onPrepareAsyncEvent()假设DataSource是URL的话,依据地址获取数据,并開始缓冲,直到获取到mVideoTrack和mAudioTrack。mVideoTrack和mAudioTrack通过调用initVideoDecoder()和initAudioDecoder()来生成 mVideoSource和mAudioSource这两个音视频解码器。然后调用postBufferingEvent_l()提交事件开启缓冲。
数据缓冲的运行函数是onBufferingUpdate()。缓冲区有足够的数据能够播放时,调用play_l()開始播放。play_l()中关键是调用了postVideoEvent_l(),提交了 mVideoEvent。这个事件运行时会调用函数onVideoEvent()。这个函数通过调用 mVideoSource->read(&mVideoBuffer, &options)进行视频解码。音频解码通过mAudioPlayer实现。
视频解码器解码后通过mVideoSource->read读取一帧帧的数据,放到mVideoBuffer中,最后通过 mVideoRenderer->render(mVideoBuffer)把视频数据发送到显示模块。当须要暂停或停止时,调用cancelPlayerEvents来提交事件用来停止解码,还能够选择是否继续缓冲数据。
5、 代码标记Log
根据第4》项StageFright描写叙述的Vide视频播放流程,作Log标记跟踪视频DATA获取、CODEC过程。从AwesomePlayer.cpp中方法着手,过程例如以下:
在改动的/mydroid/frameworks/base/media/libstagefrigh/下,用mm编译,并调试直到生成对应的.so文件。注:同意单模块编译时,需事先在/mydroid下同意. ./build/envsetup.sh文件。
在/mydroid/文件夹下make进行总体编译,生成system.img文件。说明:先单模块编译,后再总体编译的优点是,能够缩短调试编译的时间。
将system.img文件copy到/android-sdk-linux/platforms/android-8/下。注意:事先备份原有的system.img。
带sdcard启动模拟器,在/android-sdk-linux/tools/下执行./adb shell文件,再执行logcat
打开Gallery选择视频文件执行,并同步查看log。
反馈结果例如以下:
I/ActivityManager( 61): Starting: Intent { act=android.intent.action.VIEW dat=content://media/external/video/media/5 typ=video/mp4 cmp=com.cooliris.media/.MovieView } from pid 327
I/RenderView( 327): OnPause RenderView com.cooliris.media.RenderView@4054a3b0
E/AwesomePlayer( 34): beginning AwesomePlayer... by jay remarked...
E/AwesomePlayer( 34): returning AwesomeEvent...by jay remarked...
E/AwesomePlayer( 34): returning AwesomeEvent...by jay remarked...
E/AwesomePlayer( 34): returning AwesomeEvent...by jay remarked...
E/AwesomePlayer( 34): returning AwesomeEvent...by jay remarked...
E/AwesomePlayer( 34): ending AwesomePlayer... by jay remarked...
E/AwesomePlayer( 34): setting video source now... by jay remarked...
E/AwesomePlayer( 34): setting Video Type... by jay remarked...
E/AwesomePlayer( 34): returning AwesomeEvent...by jay remarked...
E/AwesomePlayer( 34): beginning initVideoDecoder by jay remarked...
D/MediaPlayer( 327): getMetadata
I/ActivityManager( 61): Displayed com.cooliris.media/.MovieView: +1s761ms
E/AwesomePlayer( 34): beginning AwesomeLocalRenderer init ...by jay remarked...
E/AwesomePlayer( 34): returning open(libstagefrighthw.so) correctly by jay remarked...
E/MemoryHeapBase( 34): error opening /dev/pmem_adsp: No such file or directory
I/SoftwareRenderer( 34): Creating physical memory heap failed, reverting to regular heap.
E/AwesomePlayer( 34): ending AwesomeLocalRenderer init close ...by jay remarked...
E/AwesomePlayer( 34): returning AwesomeLocalRenderer...by jay remarked...
I/CacheService( 327): Starting CacheService
StageFright框架流程解读的更多相关文章
- Android Multimedia框架总结(九)Stagefright框架之数据处理及到OMXCodec过程
转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼:http://blog.csdn.net/hejjunlin/article/details/52532085 不知不觉到第九篇了,感觉还有 ...
- Android Multimedia框架总结(八)Stagefright框架之AwesomePlayer及数据解析器
转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼:http://blog.csdn.net/hejjunlin/article/details/52503057 前言:前面一篇分析了medi ...
- CI框架源码阅读笔记1 - 环境准备、基本术语和框架流程
最开始使用CI框架的时候,就打算写一个CI源码阅读的笔记系列,可惜虎头蛇尾,一直没有行动.最近项目少,总算是有了一些时间去写一些东西.于是准备将之前的一些笔记和经验记录下来,一方面权作备忘,另一方面时 ...
- Android Multimedia框架总结(十)Stagefright框架之音视频输出过程
转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52560012 前言:上篇文中最后 ...
- stagefright框架(一)Video Playback的流程
在Android上,預設的多媒體框架(multimedia framework)是OpenCORE. OpenCORE的優點是兼顧了跨平台的移植性,而且已經過多方驗證,所以相對來說較為穩定:但是其缺點 ...
- stagefright框架(六)-Audio Playback的流程
到目前为止,我们都只着重在video处理的部分,对于audio却只字未提.这篇文章将会开始audio处理的流程. Stagefright中关于audio的部分是交由AudioPlayer来处理,它是在 ...
- stagefright框架(四)-Video Buffer传输流程
這篇文章將介紹Stagefright中是如何和OMX video decoder传送buffer. (1) OMXCodec會在一開始的時候透過read函式來傳送未解碼的data給decoder,並且 ...
- AI移动自动化测试框架设计(解读)
声明:原文出自"前端之巅"微信公众号"爱奇艺基于AI的移动端自动化测试框架的设计"一文,作者:何梁伟,爱奇艺Android架构师.文章提供了一种基于AI算法的自 ...
- SSH框架流程详解
解图: 由图可见,有三个框架{ ①. Struts_2 ②. Spring ③. Hibernate } 框架 作用 本质 同等于 Struts_2 实现MVC / 控制.跳转 过滤器(Filter) ...
随机推荐
- poj3321Apple Tree(树状数组)
http://poj.org/problem?id=3321 刚一看题以为要建一颗树 看了下讨论说dfs 这里dfs遍历时设的标号很好 一个low一个high 包含了以这一节点为根节点的子树结点的所有 ...
- [HDU 4828] Grids
Grids Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)Total Subm ...
- Office启动加载vs。。。项
PowerPoint: 选项->加载项->Chinese Translation Addin->管理[COM加载项]转到->取消Chinese Translation Addi ...
- oracle 分组排序
SELECT * FROM (SELECT A.*, RANK() OVER(PARTITION BY A.DR_ATP_ID, A.AT_CODE ORDER BY A.KEY_CODE) RANK ...
- UVA 1515 Pool construction 水塘(最大流,经典)
题意: 给一个h*w的矩阵,每个格子中是'#'和'.'两个符号之一,分别代表草和洞.现在要将洞给围起来(将草和洞分离),每条边需花费b元(即将一个洞包起来需要4边,将2个连续的洞包起来需要6边,省了2 ...
- android:installLocation简析
在Froyo(android 2.2,API Level:8)中引入了android:installLocation.通过设置该属性可以使得开发者以及用户决定程序的安装位置. android:inst ...
- 初次接触Object对象变量
Object 变量存储为 32 位(4 个字节)的地址形式,其为对象的引用.利用 Set 语句,声明为 Object 的变量可以赋值为任何对象的引用. 第一次注意到还有个数据类型,帮助文件里只有上面这 ...
- FZU 2105-Digits Count(线段树延时标记)
题意: 每次操作区间每个数进行一种(&或|.或^ )给定的一个数,到sum时统计给定区间的和. 分析: 这个题让我觉得我的思维很不活跃,对懒惰标记理解,还远远不够,通过这道题我对懒惰标记加深了 ...
- 设计模式_Memento_备忘录模式
形象例子: 同时跟几个MM聊天时,一定要记清楚刚才跟MM说了些什么话,不然MM发现了会不高兴的哦,幸亏我有个备忘录,刚才与哪个MM说了什么话我都拷贝一份放到备忘录里面保存,这样可以随时察看以前的记录啦 ...
- PHP常用的基本文件和目录操作总结
来源:http://www.ido321.com/835.html 一.基本文件的操作 文件的基本操作有:文件判断.目录判断.文件大小.读写性判断.存在性判断及文件时间等 1: <?php 2: ...