1. 框架结构

1.1StageFright和openCore和NuPlayer的关系


上图可知,stagefright是在MediaPlayerService这一层加入的,和opencZ喎�"/kf/ware/vc/" target="_blank" class="keylink">vcmXKx7KiwdC1xKOs1NrRodPDb3BlbmNvcmW7ucrHc3RhZ2VmcmlnaHS1xLT6wuvH0Lu7yc/SsrfHs6PI3dLXoaM8L3A+DQo8cD5BbmRyb2lkyc+1xE1lZGlhUGxheWVysqW3xbXXsuO/8rzc0tG+rb6twPrBy7bgtM6x5Lavo6y009fu1OfPyLXET3BlbkNvcmW1vbrzwLS1xFN0YWdlRnJpZ2h01Nm1vc/W1Nq1xE51UGxheWVyRHJpdmVyo6zU2rmk1/e/qsq8vdO0pUFuZHJvaWS1xMqxuvLS0b6t0saz/cHLT3BlbkNvcmXL+dLUttRPcGVuQ29yZbXEwcu94r32vfbNo8H01NrM/cu1uf2jrNXi0Km/8rzc1NrR3b34uf2zzNbQ0ruw47a8ysfPyMG91ta/8rzcsqK05qOsyLu689TZ1NrEs7j2sOaxvtbQvavG5NLGs/2jrNTnz8hBbmRyb2lk1tDKudPDtcTKx1N0YWdlZnJpZ2h0ICsgTnVQbGF5ZXKyorTmtcS3vcq9o6zG5NbQx7DV37i61PCypbfFsb612LXEw73M5c7EvP6jrLrz1d/Tw9PasqW3xc34wufB98O9zOXOxLz+o6y1q8rH1Nq688C0tcRBbmRyb2lkIEy/qsq8TnVQbGF5ZXJpvaW9pb+qyrzM5rT6wctTdGFnZWZyaWdodKOsxL/HsLG+tdiypbfF0tG+rcfQu7u1vU51UGxheWVyyc/By6Os1NpBbmRyb2lkIE4gQU9QUyDUtLT6wuvW0Mn11sHSxrP9wctTdGFnZWZyaWdodKGjPC9wPg0KPGgyIGlkPQ=="12-openmax">1.2 OpenMAX

1.3 StageFright

基于Stagefight的MediaPLayer框架的结构:

stageFright is a player .

上图可以看出播放过程主要涉及3个进程: app端进程,媒体框架服务(stageFright),OMX服务.实际使用还经常用到一个专门做跨进程内存共享管理的进程(MemoryDeal).注意后面会经常讲到”客户端”,有时并不是指app端,要区分开来.
- 应用层和framework层?使用到MediaPlayer的应用很多,最常见的就是Music和Video,如果要了解这些应用的实现可以看下AOSP代码中的packages/apps,这些代码中用到了frameworks/base/media/所提供的MediaPlayer接口,这些接口都十分简单,我们只需要知道这些接口的具体功能就可以开发出一款功能较为齐全的Music Player.
- Native Media Player 层:
应用层的native实现.通过binder机制和Service交互.

Media Player Service 部分:
从Native层发出的IPC请求将会由Media Player Service 部分进行处理.MediaPlayerService是在frameworks/av/media/mediaserver/main_mediaserver.cpp的main方法中初始化的,在main方法中还启动了多个Android系统服务比如AudioFlinger, CameraService等,实例化Media Player Service 子系统的工作包括MediaPlayerService对象的创建,以及内置底层Media PLayer播放框架工厂的注册,一旦 MediaPlayerService 服务启动,MediaPlayerService将会接受Native MediaPlayer 层的IPC请求,并且为每个操作media内容的请求实例化一个MediaPlayerService::Client对象, Client有一个createPlayer 的方法可以使用特定的工厂类为某个特定的类型创建一个本地media player,后续的发向native层的请求都会交给刚刚提到的native 层的 media palyer来处理,这里的media player指的是StagefrightPlayer或者Nuplayerdriver.但是我们这里先不讨论Nuplayerdriver。

分析文件:

StageFright主要是对AwesomePlayer的封装.AwesomePlayer是事件驱动的播放器.本文主要分析它对视频流的处理.重点在AwesomePlayer::onVideoEvent函数.其中从流中read packet,parse,decode .都在mVideoSource->read(&mVideoBuffer, &options);函数完成.该函数在OMXCodec.cpp实现.其中read(extract)和parse 在AwesomePlyaer调用其它组件(如MPEG4Extractor)完成,参数mVideoBuffer即为解码后的帧图像,decode则是调用OMXCodec的服务接口.也就是解码时又通过Binder做了一次跨进程通信.关于OMXCodec Service的一些文件:
- 接口定义:
IOMX.h
- 客户端类:
OMXCodec.cpp
OMXClient.cpp
IOMX.cpp (BpOMX类/BnOMX类)
- 服务端类:
OMX.cpp
OMXNodeInstance.cpp

示例函数
fillOutpuBuffer. 见源码书签和注释.

以上类是通过Binder机制实现的.因为这我第一个学习的android Framework.下面先简单介绍Binder机制:

2. Binder机制简单介绍

binder是android 系统下的一种IPC机制。是进程间交互的一种方式。在开发android应用时,脑袋一定要一直保持C/S结构的思想。
android应用的开发说白了就是通过android提供的一系列的服务来完成自己的目的,咱们刚才也的那个播放器的apk也是需要android提供的播放器的服务来完成的。
apk是一个独立的进程,android的系统服务也是很多个独立的进程。binder的功能就是把client 和 service 连接起来。

2.1 入门实例

Binder简单实例 - lansehai的专栏 - 博客频道 - CSDN.NET

2.2 总结

a. IInterface 的继承类里声明将要跨进程调用的函数.声明为纯虚函数. (如IOMX.h中IOMX类).
a. 如果服务端和客户端的创建都在同一个进程中,interface_cast会直接获得xx的Bn实例,就是相当于直接声明了一个xx类型。
OMXClient#getOMX()
c. BnBinder是Service端接口.binder是代理(BpBinder).Service实现业务功能.客户端需要实现发送功能.

2.3 Imemory

调用OMXCodec Component需要进程间共享内存.android在实现进程共享内存时使用Binder和匿名共享内存实现了一套共享内存机制.

MemoryHeapBase和MemoryBase.前者是匿名共享内存类,以页为单位.后者是基于MemoryHeapBase的匿名共享内存,使用偏移值表示.ta解决多个clinets一个Service 内存共享问题.

重要函数总结:

getMemory
成员函数getMemory用来获取内部的MemoryHeapBase对象的IMemoryHeap接口.如果成员变量mHeap的值为NULL,就表示这个BpMemory对象尚未建立好匿名共享内存,于是,就会通过一个Binder进程间调用去Server端请求匿名共享内存信息,在这些信息中,最重要的就是这个Server端的MemoryHeapBase对象的引用heap了,通过这个引用可以在Client端进程中创建一个BpMemoryHeap远程接口,最后将这个BpMemoryHeap远程接口保存在成员变量mHeap中,同时,从Server端获得的信息还包括这块匿名共享内存在整个匿名共享内存中的偏移位置以及大小。这样,这个BpMemory对象中的匿名共享内存就准备就绪了。

pointer()
成员函数pointer()用来获取内部所维护的匿名共享内存的基地址;

size()
成员函数size()用来获取内部所维护的匿名共享内存的大小; offset()
用来获取内部所维护的这部分匿名共享内存在整个匿名共享内存中的偏移量。

使用实例:

在BpSharedBuffer类的成员函数transact中,向Server端发出了一个请求代码为GET_BUFFER的Binder进程间调用请求,请求Server端返回一个匿名共享内存对象的远程接口IMemory,它实际指向的是一个BpMemory对象,获得了这个对象之后,就将它返回给调用者;
在BnSharedBuffer类的成员函数onTransact中,当它接收到从Client端发送过来的代码为GET_BUFFER的Binder进程间调用请求后,便调用其子类的getBuffer成员函数来获一个匿名共享内存对象接口IMemory,它实际指向的是一个MemoryBase对象,获得了这个对象之后,就把它返回给Client端。
(详细示例见链接最后一节)

2.4 IOMX

IOMX定义了OMXCodec的接口.如fillBuffer,emptyBuffer.

3. StageFright底层具体实现

说白了既然StageFright是个播放器,那么它至少有4大部分:source、demux、decoder、output。
1.source:数据源,数据的来源不一定都是本地file,也有可能是网路上的各种协议例如:http、rtsp、HLS等。
2.demux解复用:视频文件一般情况下都是把音视频的ES流交织的通过某种规则放在一起。这种规则就是容器规则。现在有很多不同的容器格式。如ts、mp4、flv、mkv、avi、rmvb等等。demux的功能就是把音视频的ES流从容器中剥离出来,然后分别送到不同的解码器中。
3.decoder解码:解码器–播放器的核心模块。分为音频和视频解码器。
4.output输出:输出部分分为音频和视频输出。解码后的音频(pcm)和视频(yuv)的原始数据需要得到音视频的output模块的支持才能真正的让人的感官系统(眼和耳)辨识到。
所以,播放器大致分成上述4部分。怎么抽象的实现这4大部分、以及找到一种合理的方式将这几部分组织并运动起来。是每个播放器不同的实现方式而已。

AwesomePlayer是实现播放的底层操作者,它在StagefrightPlayer初始化的时候被创建,它负责将对应的音频视频和对应的解码器对应起来。这里涉及到了MediaExtractor,它会从媒体文件中抽取到有效的头信息。并返回对应的引用。在准备播放的时候AwesomePlayer通过OMXCodec来根据媒体文件类型创建解码器,解码器是驻留在OMX子系统上(OMX是OpenMAX在Android上面的实现),这些解码器主要用于处理内存缓冲,转化成原始数据格式,这部分的实现代码主要在frameworks/av/media/libstagefright/omx 以及frameworks/av/media/libstagefright/codecs 目录下, Stagefright Media Player和 OMX部件(Component)是通过IPC方式交互的.
AwesomePlayer最终会处理应用层发出的播放,暂停,停止等请求,这些请求往往和媒体类型有关联对于音频文件.AwesomePlayer 将会创建一个AudioPlayer来对文件进行处理,比如当前文件只有音频部分需要播放,这时候AwesomePlayer将会调用AudioPlayer::start()进行播放,一旦用户提交了其他新的请求AudioPlayer会使用MediaSource对象来和底层的OMX子系统进行交互。

3.1 StageFright和OMXCodec通信

StageFright数据处理流程

重点在read函数.从流中read packet,parse,decode .都在mVideoSource->read(&mVideoBuffer, &options);函数完成.

3.2底层进程通信对缓存的管理:

read函数将数据读到缓存并处理后,如何传到OMXCodec Servcie?
read循环最终调用了:
OMXCodec::drainInputBuffer(BufferInfo *info),最终通过emptyBuffer传递缓存.

?

1

2

3

4

5

<code><code><code><code><code><code><code>status_t emptyBuffer(

            node_id node,

            buffer_id buffer,

            OMX_U32 range_offset, OMX_U32 range_length,

            OMX_U32 flags, OMX_TICKS timestamp)</code></code></code></code></code></code></code>

通过分别分析客户端和服务端的emptyBuffer函数得到以下信息:
服务端:
a. 约定buffer_id 为
OMX_BUFFERHEADERTYPE *指针.
客户端
如何对应客户端读出的缓存给服务端?

?

1

2

3

<code><code><code><code><code><code><code><code>    err = mOMX->emptyBuffer(

            mNode, info->mBuffer, 0, offset,

            flags, timestampUs);</code></code></code></code></code></code></code></code>

mBuffer即为BufferId.实质为指针.服务端转为OMX_BUFFERHEADERTYPE *类型后,调用CopyToOMX(header),调用OpenMAX接口,将输入buffer拷贝到Codec硬件内存.

解码后调用fillOutputBuffer将缓存从Codec拷贝回来,同样是跨进程调用,实现函数是fillBuffer.服务端fillBuffer实现把缓存拷贝到对应的buffer_id.

如何初始化缓存?

![(“>http://img1.ph.126.net/gasrK49-HwQvjRERL5s2-g==/6599299576238819530.png)~~
客户端
allocateBuffersOnPort函数初始化缓存池.这个函数比较重要,直接画图来看他的调用和IPC通信:

AwesomePlayerAwesomePlayerOMXCodecOMXCodecMemoryDealerMemoryDealerOMXClient.cppOMXClient.cppIOMX.cppIOMX.cppOMX.cppOMX.cppOMXNodeInstance.cppOMXNodeInstance.cpp媒体框架进程allocateBuffersOnPortnew MemoryDealer();然后调用MemoryDealer::allocate函数分配得到一个Imemory指针spmOMX->allocateBuffer[backup]这里还封装了一次,只有使用remote Codec时才跨进程调用上一步中的判断也是使用远程Codec时调用allocateBufferBackupgetOMX(node)->allocateBufferBackupallocateBufferbackup 这里就是跨进程调用了.IOMX.cpp里BpIOMX::allocateBufferbackup 使用writeInt32等完成发送,Service端BnBinder::OnTransact也在这里实现接收.其中最特别的又是writeStrongBinder,直接把Imemory对象发送出去,服务端得到一个Imemory的客户端BpBinder,以便可以访问共享内存.allocateBufferbackup继续封装

note right of OMXNodeInstance.cpp:调用Codec接口的地方,也就是调用芯片厂商提供的接口.
例如OMX_AllocateBuffer分配得到.OMX_BUFFERHEADERTYPE 指针.再通过reply写回客户端,也就是客户端可以得到一个OMX_BUFFERHEADERTYPE 的指针.OMX_BUFFERHEADERTYPE的一个重要成员BufferMeta将Imemory的共享内存封装进来.最后函数返回客户端一个buffer_id,实际就是一个在服务端保存的OMX_BUFFERHEADERTYPE指针.然后把这个指针绑定到对应的portIndex, MMediaPlayer使用时再绑定到对应的MediaInfo上.
这样下次OMXCodec调用drainInputBuffer这样的函数时,就能通过MediaInfo在OMXCodec Component 找到对应bufferId ,进而通过emptyBuffer这样的跨进程调用通知服务端(Component),服务端有bufferId后,转为BufferHeader指针后遍知道该使用那块共享内存了.

再补充几个其中的几个信息:

通过MemoryDealer分配匿名共享内存. 通过mOMX->allocateBufferWithBackup(mNode, portIndex, mem, &buffer);分配指定大小共享缓存.并绑定到buffer_id.(以下带mOMX 字样的都是IPC通信 ,对应的服务端函数在OMXNodeInstance.cpp).也就是客户端的每个bufferInfo保存了一个Service对应的OMX_BUFFERHEADERTYPE对象指针. 将客户端的共享缓存地址通过mem->pointer();赋值给info::mData. 注意MediaBuffer的初始化:info.mMediaBuffer = new MediaBuffer(info.mData, info.mSize);MediaBuffer::mData 即为info.mData.也就是MediaSource,例如MPEG4Extractor 的read /parse 操作也是直接对共享缓存操作. OMXCodec::allocateBuffersOnPort为每个buffer分配缓存和index.同时保存缓存信息到服务端:mOMX->storeMetaDataInBuffers. 以及把缓存队列传到MediaSource(此例是MPEG4Exctractor):mSource->setBuffers(buffers) 服务端分配缓存见OMXNodeInstance::allocateBufferbackup.服务端的共享内存指针保存在BufferMeta buffer_meta.例如emptyBuffer调用CopyTOOMX可以把芯片缓存拷贝到共享内存缓存.

顺便学习以下如何写Binder机制中客户端的发送函数,一个BpBinder函数是长这样的:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

<code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code>virtual status_t allocateBufferWithBackup() {

        Parcel data, reply;

        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());

        data.writeIntPtr((intptr_t)node);

        data.writeInt32(port_index);

        data.writeStrongBinder(params->asBinder());

        remote()->transact(ALLOC_BUFFER_WITH_BACKUP, data, &reply);

        status_t err = reply.readInt32();

//...

        *buffer = (void*)reply.readIntPtr();

        return err;

    }</code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code>

通过IMemory的学习可知,IOMX客户端通过MemoryDealer构建MemoryBase对象是Imemory的远程接口实现.而OMX Compoent的接收onTransact中,读取的是一个匿名共享内存对象的远程接口Imemory,它实质指向一个BpMemory对象.接收完通过interface_cast(data.readStrongBinder())转为Imemory调用.

//todo
- node_id 是啥? 怎么区分remoteOMX & localOMX?
OMXCodec.cpp不是直接调用OMXCodec的客户端接口.中间还封装了一个类OMXClient.cpp.当判断使用remoteOMX时才调用remote接口.即BpOMX.而BpOMX在IOMX.cpp实现.node_id是区分local/remote的关键.猜测noteId是标记媒体框架服务(MediaPlayer)的客户端程序.

Given a node_id and the calling process’ pid, returns true iff the implementation of the OMX interface lives in the same process.
猜测是如果同进程调用的话不再重新映射共享内存?

OMXObserver是干么用的?
如何读取packet,Source Input?
err = mSource->read(&srcBuffer, &options); 保存在 MediaBuffer::mData.

MediaCodec和OMXCodec的关系.

其实在openmax接口设计中,他不光能用来当编解码。通过他的组件可以组成一个完整的播放器,包括sourc、demux、decode、output。
1. android系统中只用openmax来做code,所以android向上抽象了一层OMXCodec,提供给上层播放器用。
播放器中音视频解码器mVideosource、mAudiosource都是OMXCodec的实例。

2. OMXCodec通过IOMX 依赖binder机制 获得 OMX服务,OMX服务 才是openmax 在android中 实现。

3. OMX把软编解码和硬件编解码统一看作插件的形式管理起来。

由文中可知,MediaPlayer(stageFright),MediaCodec 都是调用OMXCodec,stageFright和MediaCodec是平行关系,无相互调用. OMXCodec和ACodec都是更底层的东西.

OMXCodec和ACocdec

NuPlayer调用ACodec(支持网络流).

android中stagefright和OMXCodec原理分析相关推荐

  1. 深入解析Android中View的工作原理

    Android中的任何一个布局.任何一个控件其实都是直接或间接继承自View实现的,当然也包括我们在平时开发中所写的各种炫酷的自定义控件了,所以学习View的工作原理对于我们来说显得格外重要,本篇博客 ...

  2. android中倒计时控件CountDownTimer分析

    android中倒计时控件CountDownTimer分析1 示例代码 new CountDownTimer(10000, 1000) {public void onTick(long millisU ...

  3. android scroller,深入理解Android中Scroller的滚动原理

    View的平滑滚动效果 什么是实现View的平滑滚动效果呢,举个简单的例子,一个View从在我们指定的时间内从一个位置滚动到另外一个位置,我们利用Scroller类可以实现匀速滚动,可以先加速后减速, ...

  4. Android中app卡顿原因分析示例

    http://www.cnblogs.com/zhucai/p/weibo-graphics-performance-analyse.html 朱才 专注于Android图形动画 MIUI工程师 博客 ...

  5. JDK 中的 BIO 实现原理分析(二十)

    今天对JDK 中的 BIO 实现原理进行分析 一.简介 对比 Linux 上网络编程,我们会发现 JDK Socket 的编程逻辑是一模一样的.实际上也是这样,JDK 网络编程也没有做很多事,主要还是 ...

  6. Android 7.0 分屏原理分析

    在以往的Android系统上,所有Activity都是全屏的,如果不设置透明效果,一次只能看到一个Activity界面. 但是从Android N(7.0)版本开始,系统支持了多窗口功能.在有了多窗口 ...

  7. 浅析 Android 中 Binder 的上层原理

    纸上得来终觉浅,绝知此事要躬行 Binder 一直是我心里的一个坎儿,因为不管是 Android 中的哪个组件,都总是会或多或少的涉及 Binder.对于 Binder 是 Android 为了提升其 ...

  8. android viewgroup 事件,android中viewgroup的事件传递分析

    在上一篇中我们分析了从view的dispatchTouchEvent到onTouchListener的onTouch回调到onTouchEvent到onClickLisener的onClickandr ...

  9. Android中启动Activity(startActivity)流程图分析

    在上篇博文< Android中ActivityManagerService与应用程序(客户端)通信模型分析>中,我们从宏观架构上掌握 ActivityManagerService与应用程序 ...

  10. Android中图像变换Matrix的原理应用

    第一部分 Matrix的数学原理 在Android中,如果你用Matrix进行过图像处理,那么一定知道Matrix这个类.Android中的Matrix是一个3 x 3的矩阵,其内容如下: Matri ...

最新文章

  1. Exchange系列课程之三--群集环境中安装Exchange Server 2003
  2. 史上最全 yum 入门使用教程和常见错误解决办法
  3. 信息学奥赛一本通(1163:阿克曼(Ackmann)函数)
  4. (88)Verilog HDL系统函数和任务:$fdisplay
  5. (转)淘淘商城系列——zookeeper单机版安装
  6. 网管囧事系列二:又要杀毒,又要防***,网管怎样三头六臂?
  7. 安卓版App开发心得
  8. cnvd与cnnvd区别_漏洞编码CVE/CAN/BUGTRAQ/CNCVE/CNVD/CNNVD都指什么?
  9. 当不知轴承型号时如何寻找轴承故障频率_在工业自动化应用场景中的机械故障类型以及诊断方法...
  10. 慧荣SM2246XT主控的固态硬盘修复开卡不识别怎么短接方法
  11. 2021鹏业安装算量软件常见问题整理(三)
  12. 对比苏州和杭州这两座城市,你会发现?
  13. 直播改革:关闭10家平台监管3万主播
  14. DPDK ring简单说明
  15. nodejs项目(基于Express)——为上传的图片贴上国旗图标(使用gm)并返回图片位置
  16. 解一元二次方程——Java
  17. 计算机本体应用,本体评估方法研究综述
  18. git仓库报错【 ! [rejected] master -> master (non-fast-forward)error: failed to push some refs to】
  19. AAA认证 设置域 PPP认证 AAA默认域认证
  20. 洛谷 P1618 三连击(升级版)

热门文章

  1. 如何正确做笔记?符号笔记法、康奈尔笔记法总结!
  2. 超级APP势不可挡--20190706
  3. 英文顺序:第一~第N
  4. springboot建筑造价师资格考试应试网站设计与实现毕业设计源码260839
  5. android 插桩工具,自插桩测试示例  |  Android 开源项目  |  Android Open Source Project...
  6. java汉字转拼音maven_java汉字转拼音pinyin4j功能实现示例
  7. jpa方言的yml格式
  8. 即时通讯创业必读:解密微信的产品定位、创新思维、设计法则等
  9. 印象笔记粘贴HTML,VScode下MarkDown如何连接印象笔记
  10. 谷歌地图地名显示繁体字_谷歌地图怎么把字去掉