https://www.2cto.com/kf/201608/542839.html

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


OpenMAX简介
android系统中的编解码器部分用的是openmax,以后会深入了解。openmax是一套标准接口,各家硬件厂商都可以遵循这个标准来做自己的实现,发挥自己芯片特性。然后提供给android系统来用。因为大部分的机顶盒芯片产品硬件的编解码是它的优势,可以把这种优势完全融入到android平台中。以后手机高清视频硬解码也会是个趋势。
第一层:OpenMaxDL(DevelopmentLayer,开发层)
第二层:OpenMaxIL(IntegrationLayer,集成层)
第三层:OpenMaxAL(ApplictionLayer,应用层)
OpenMaxIL 处在中间层的位置,OpenMAX IL作为音频,视频和图像编解码器能与多媒体编解码器交互,并以统一的行为支持组件(例如资源和皮肤)。这些编解码器或许是软硬件的混合体,对用户是的底层接口应用于嵌入式或/和移动设备。它提供了应用程序和媒体框架,透明的。本质上不存在这种标准化的接口,编解码器供应商必须写私有的或者封闭的接口,集成进移动设备。IL的主要目的是使用特征集合为编解码器提供一个系统抽象,为解决多个不同媒体系统之间轻便性的问题。
refenence2 .

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 多媒体框架服务之StagefrightPlayer和OMXCodec实现原理学习相关推荐

  1. android多媒体框架学习 详解

    原址 一:多媒体框架概述 jellybean 的多媒体跟以前的版本,通过对比没啥变化,最大的变化是google终于舍得给multimedia建个独立的git了(framework/av),等你好久了! ...

  2. android多媒体框架学习 详解 最新版本

    一:多媒体框架概述 jellybean 的多媒体跟以前的版本,通过对比没啥变化,最大的变化是google终于舍得给multimedia建个独立的git了(framework/av),等你好久了!也体现 ...

  3. Android 多媒体框架 OpenCore(PacketVideo)介绍

    OpenCore的另外一个常用的称呼是PacketVideo,它是Android的多媒体核心.PacketVideo是一家公司的名称,OpenCore是这套多媒体框架的软件层的名称.在Android的 ...

  4. Android多媒体框架(二)Codec初始化及Omx组件创建

    Codec创建流程 Android提供给应用编解码的接口为MediaCodec.我们这里从NuPlayerDecoder开始分析,一是为了衔接之前将的MediaPlayer-NuPlayer流程,二是 ...

  5. android多媒体框架之流媒体----base on jellybean(八)

    从这篇开始我们将进入流媒体的环节,流媒体在 Android中有nuplayer来实现的,在开始讲解android流媒体前,我们先来讲讲流媒体传输协议,了解了基本协议,我们在看代码的过程中,就会有事半功 ...

  6. android多媒体框架介绍(五)显示图形系统之SurfaceFlinger初步介绍

    前面介绍了比较直观的framebuffer模块(负责把有一个内存地址ADDR的内容显示到屏上),hwc模块(叠加器,负责把surfaceFlinger送来的各种输入layer叠加到显存上),接下来开始 ...

  7. android多媒体框架介绍(四)显示图形系统之hwc叠加器

    前面我们讲解了整个android图形显示系统的主要模块关系,和framebuffer,接下来我们讲解hwc叠加模块. Android7.0提供了HWC和HWC2两个版本,到了Android8.0就都默 ...

  8. Android多媒体框架之MediaMetadataRetriever

    前言 MediaMetadataRetriever是Android原生提供的获取音视频文件信息的一个类,我们可以通过这个类的相关方法获取一些基本信息,如视频时长.宽高.帧率.方向.某一帧的图片等. 初 ...

  9. Android多媒体框架图

    原址 MediaPlayer框架图 Camera框架图 SoundRecorder框架图 VideoCamera框架图 OpenCore与Skia ALSA Audio框架图 Video Overla ...

最新文章

  1. TinyML-TVM如何驯服TinyML
  2. 参数量下降85%,性能全面超越ViT:全新图像分类方法ViR
  3. 字符转换属性text-tranform改变大小写
  4. SQL基础操作_8_基础概念
  5. tomcat webapps目录文件都能删吗_详细测试实现Tomcat根域名访问的场景,看这篇文章就够了...
  6. linux menuconfig usb,[Linux]make menuconfig里面的选项很重要
  7. 产品经理常犯的七大错误
  8. SpringBoot集成Actuator监控管理
  9. 【thinkphp】ThinkPHP各个低版本(=3.2)完全配置参考手册
  10. Python基础-字典(字典常用函数/操作/字典遍历)
  11. wxWindows一些网文
  12. python列表常见的5种去重方法
  13. AtomicReference使用场景
  14. SEO入门一篇就够-SEO教程
  15. IPD数字诀(一二三四五六七八九十)
  16. 胡适致毕业生:功不唐捐
  17. 关于在vue中实现7天免登录
  18. JS轮播图(网易云轮播图)
  19. 明日服务器中断,明日之后服务器中断怎么办
  20. 36.超市微信促销活动2

热门文章

  1. Xcode - 非 App Store 下载官方 Xcode 安装包方法
  2. 股市资讯第二证券|畅通物流配送环节 保障群众生活所需
  3. 【机器学习】function regularized_cost at 0x000001E9244B5A60>报错
  4. 程序员的机器学习入门笔记(十):人脸识别核心算法PCA的前世与今生(强烈推荐)
  5. 渐变 -- QBrush
  6. onenote 使用_使用OneNote使记忆信息更容易
  7. Fast unfolding of communities in large networks 中文翻译
  8. Veritas NetBackup8.1.1汉化
  9. 最新论文笔记(+21):Privacy-Preserving Byzantine-Robust Federated Learning via Blockchain Systems/ TIFS2022
  10. 科目一科目四理论考试助手藏文驾考藏语版