OpenMAX IL介绍与其体系

【多媒体编解码】Openmax IL (一)官方文档概述 OpenMax IL 概述PS:被博客主要是对官方文档前三章的学习于记录。 官网文档请移步: https://www.khronos.org/openmax/Openmax 框架图:openmax il API概述openmax il API 是基于组件的的多媒体API,包括core API和conponent API core : 动态的加载和卸载conponent,允许用户层和conp 阅读详情

1  OpenMAX IL介绍与其体系

这一部分的文档描述 OpenMAX IL的特性与体系。

1.1  OpenMAX IL 简述

OpenMAX IL 软件接口层定义了一套API,用于访问系统中的组件。OpenMAX IL软件层的目的:可以对系统中的组件采用不同的初始化值和不同的命令集合;同时还能提供一套统一的命令集合和方法来构建和销毁组件。

1.1.1  体系概览

考虑一个系统要实现四个多媒体处理功能模块,分别标记为 F1, F2, F3, 和F4。这四个功能模块中的任何一个都可能由不同的厂商或者同一个组织的不同部门所开发。每一个功能模块的构建和销毁都可能有不同的要求。同时,每一个模块的配置和数据传输都有不同的方式。 OpenMAX IL API可以将这些功能模块封装成一个组件,既可以每个模块一个组件或者在逻辑上相关的模块组成一个组件。OpenMAX IL  API提供一套标准的协议,可以让来自不同的厂商或者组织的组件互相交换数据,同时组件本身也可被另一个组件替换。

与OpenMAX IL API通信的实体称之为IL client, 一般是多媒体框架或者应用层。IL client可以使用 OpenMAX core来加载和卸载组件,在两个组件之间建立通信机制和访问组件的功能函数。

IL client通过IL core和组件之间进行通信。大多数情况下,IL client通过调用IL core所定义的宏来与组件通信,这些宏展开之后就直接调用一个组件的函数。例外情况是,IL client直接调用IL core的函数来加载和销毁组件,建立和撤销组件之间的隧道化连接。

组件代表着多媒体处理的一个单元或者多个单元的集合。尽管这份规范清晰的定义了OpenMAX core的功能,但是具体的功能还是由各个组件提供者负责实现。根据组件输出的参数数据类型的不同,把组件操作的数据类型分为四种:audio 数据,video数据 ,image数据,和其他数据(比如时间或者同步数据)。

通过组件句柄可以访问一个 OpenMAX 组件所提供的一系列标准方法。这些方法允许client取得和设置组件以及组件中port的配置参数,取得和设置组件的状态,给组件发送命令,接收时间通知,分配buffer,和组件的一个port建立通信,以及在两个组件port之间建立通信。

一个OpenMAX组件必须至少有一个port,以满足 OpenMAX 的一致性标准。尽管厂商可能提供一个与OpenMax兼容的,不含有任何port的组件,但是大量的一致性测试都是基于组件含有至少一个port。 OpenMAX所定义的四种类型的port对应于port可能传输的四种数据类型;audio port,video port,image port和其他port。根据port是消费buffer还是生产buffer,可以把一个port分为input类型和output类型。

在一个包含F1,F2,F3,F4四个多媒体处理功能模块的系统中,系统实现者可能会为每一个功能模块提供一个标准的OpenMax 组件,也可以简单的采取任何组合而提供一个组件。组件中的单独功能模块的区分就依赖于组件中的port。

图1-1展现了一些OpenMAX组件的实现方式:

图1-1 OpenMax组件的部分可能实现


1.1.2  关键词汇

本节讲述用于描述OpenMAX IL API的常用缩略语和定义。

1.1.2.1  缩写

表1-1列出了描述 OpenMAX IL API的常用缩写:

缩写词汇

含义

IPC 

处理器间的通信

OMX 

OpenMAX中的函数和数据结构的前缀。比如一个组件可能处于 OMX_StateExecuting的状态

表1-1  缩写词汇

1.1.2.2  关键定义

表1-2 列出了用于描述OpenMax API的关键定义:

关键词汇

含义

Accelerated  component 

某一部分功能运行在加速器上的组件。加速组件有自己的特性,比如支持多种类型的隧道化

Accelerator 

用于加快某些功能处理速度的硬件

AMR 

adaptive multimedia retrieval的缩写,3GGP consortium中的一种adaptive multi-rate编码器

Host processor 

多核心系统中用于控制多媒体加速的处理器

IL client 

调用OpenMax core或者组件方法的软件层

Main memory 

host processor和accelerator所使用的外部内存

OpenMAX  component 

包装有目标系统所要求的功能的组件

OpenMAX core 

与特定平台相关代码,用于定位和加载组件到主存中,同时core也负责从主存中卸载组件,当应用层指明这个组件不再需要的时候

Resource manager  

管理硬件资源的软件单元

RTP 

real-time protocol的简称,网络标准协议用于传输实时数据,包括音频和视频

Synchronization

同步机制

Tunnels/Tunneling 

建立和使用一种标准的数据通道,这个数据通道直接被两个组件所管理。

表1-2 关键词汇定义

1.1.3  系统组件


图1-2描述了OpenMAX所支持的多种类型的交流机制。每一个组件可以有任意数目的port用于数据交换。只有一个output port的组件称为source component;只有一个input port的组件称为sink component;组件所有的功能运行在host  processor上的称为host component;运行在accelerator上的组件称之为 accelerator component。OpenMAX既可以直接和应用层交流,也可以整合到多媒体框架中去。

接下来描述三种通信机制。Non-tunneled communication 定义了在IL client和组件之间交换数据buffer的通信机制;Tunneling 定义了组件之间直接进行数据buffer交换的标准机制;Proprietary communication描述了两个组件之间直接进行数据交流的机制,

1.1.3.1  Component Profile

OpenMax的组件按照功能分为两类:base profile 和 interop profile。

base profile的组件应该要支持non-tunneled的通信机制。base profile可能支持proprietary communication机制。base profile组件不支持tunneled communication。

interop profile是base profile的超集。一个interop profile组件应该要支持non-tunneled和tunneled communication机制,可能支持proprietary communication机制

interop profile 和base profile最主要的不同点是前者支持tunneled communication机制。base profile的存在主要是为了简化OpenMax实现者的工作。

1.1.4  组件的状态

如图1-3 所示,一个组件可能要经历一系列的状态变化。假设每一个组件的初始状态是unloaded。应用层通过调用OpenMax core的函数来加载组件。与组件之间的直接交流可以使组件变为其他的状态。

当状态变化的时候使用了无效的数据,组件可能会进入到 invalid state。当IL cilent检测到组件处于invalid state的时候,应该对组件进行如下动作:stop, de-initialize, unload, reload。

图1-2描述了从任何状态都可以变迁为invalid state,但是要想退出 invalid state只有通过unload和reload组件。



图1-3 组件状态

当组件状态进入 IDLE state的时候可能会失败,因为在IDLE state下,必须为组件分配好所有的资源。当从LOADED到IDLE转换失败的时候,IL client可能会重试一次,或者将组件的状态变迁为WAIT FOR RESOURCES 。当组件进入WAIT FOR RESOURCES状态,它便向厂商指定的资源管理单元注册,一旦资源变为可用的时候,组件就会收到通知。资源管理单元随后将组件的状态变更为IDLE state。IL client向组件发送的命令控制着除INVALID state以外的其他状态的变化。

IDLE state状态表明组件已经拥有了所有它需要的资源,但是还没有开始处理数据。EXECUTING state表明组件准确接收buffer去处理数据,并且会调用对应的callback。当组件处于PAUSED state 状态的时候,组件保持着处理buffer数据的环境,但是没有处理数据或者交换buffer。从PAUSED状态变化为EXECUTING状态将使组件从上次暂停的地方开始处理buffer数据。从EXECUTING或者PAUSED状态变化为IDLE状态,将会导致丢失buffer处理的环境,这就意味着数据流的处理开始点需要重新指定。从IDLE变化为LOADED将会导致失去可操作的资源,比如通信buffer。

1.1.4 组件体系结构

图1-4描述了组件的体系结构。组件的入口点只有一个(通过组件的具体来访问一组标准函数),但是组件可能有多个向外的调用,这取决于组件有多少个port。每一个组件也会通过event handler来调用IL client的函数。每一个port也会发起对外部函数的调用。每一个port都有一个与之关联的指向buffer header的指针队列。命令函数所发送的命令也在组件内部对应一个命令队列。所有的参数和配置调用,都有一个特有的index和与之关联的数据结构。

图 1-4  OpenMAX IL API组件体系结构

一个port必须支持callback调用到IL client,如果是支持interop profile 的组件,同时还要支持与其他组件的port之间的交流。

1.1.6  通信行为

一旦从 OpenMAX core接收到了组件的句柄,就可以完成组件的配置工作。一旦组件的port配置完毕,每个port指定了其数据格式,并且组件进入合适的状态,就可以与组件进行数据通信调用了,这种调用是非阻塞的。数据通信特定于组件的一个port。IL client通过 OMX_EmptyThisBuffer函数来与Input port进行通信。IL client通过OMX_FillThisBuffer函数来与output port进行通信。在相同的上下文实现中,OMX_EmptyBufferDone或者 OMX_FillBufferDone callback将会在IL client 调用函数返回之前被调用。图1-5显示了相同上下文和不同上下文两种情况下对上述过程的不同实现。注意,IL client不要臆断组件的callback/return的时序。

图 1-5不同的上下文与相同上下文中不同的操作

和组件的数据通信一般直接发生在组件指定的port。每一个port都有一个它应该分配或者使用的组件自定义的最小的buffer数目。一个port将一个buffer header与一个buffer关联。一个buffer header有成员指向了buffer中的数据,并且提供与buffer中内容相关联的元数据。每一个组件port都应该有能力分配它自己的buffer,或者使用预先分配的buffer。

1.1.7  Tunneled Buffer的分配和共享

这节描述tunneling component buffer的分配和buffer共享。对于一个tunnel,一个port提供buffer,它称为supplier port,然后把这些buffer传递给另一个port,这个port称为non-supplier port。在最简单的情况下,supplier 负责分配buffer,然而,复杂点的情况下, tunneling component可以选择复用另外一个组件port的buffer,以避免内存的拷贝和优化内存的使用。这种情况下称之为buffer分享。

任何两个port之间的一个tunnel,代表这两个port之间的依赖关系。buffer分享机制,扩展了这种依赖,以便所有分享同一buffer集的port可以形成一条隐式的依赖链。准确的说,这条依赖链上的一个port分配buffer,其他的port共享这些buffer。

buffer共享在一个组件内实现,然后把这种共享关系传递到另外一个组件。一个non-supplier port不会知道supplier 组件的提供的buffer是自己分配的还是复用的此组件其他port上的buffer。更进一步说,supplier组件也不知道是否non-supplier组件会复用它所提供的buffer。

严格来说,一个组件仅有义务服从它所需的外部语义,并且可以实现这些语义背后的buffer共享。更具体的,外部语义要求一个组件做以下事情:

• 在它的所有supplier port上提供buffer。

• 准确的传达port 上 buffer的要求。

• 使用OMX_EmptyThisBuffer调用将一个buffer从output port传递到input port

• 使用OMX_FillThisBuffer函数调用将一个buffer从input port返回到output buffer。

如果一个组件选择共享buffer,那么它可能需要执行以下操作来满足这些要求:

• 在supplier port上提供复用的buffer。

• 当在port上传达buffer的要求时,准确的记下需要分享buffer 的port的数目

• 在内部使用OMX_EmptyThisBuffer函数调用将一个buffer从一个input port传递到output port,对应的会有一个 OMX_EmptyBufferDone 回调。

1.1.7.1  相关条款

这一节描述在tunneled buffer分配和共享中用到的相关条款。图1-6展示了总的概念图:


图1-6 buffer分配和共享关系的例子

一对tunneling port,主动发起UseBuffer调用的是supplier port。一个 buffer supplier port 并不一定要分配buffer,它可能复用同一个组件内其他port的buffer。图1-6中 port a,c是 supplier port。接收相邻port 的UseBuffer函数调用的port,称之为 non-supplier port。图中b,d是 non-supplier port。

一个port的tunneling port是与之相邻的,共享同一条tunnel的port。port b是port a的tunnelping port。同样的,port a也是port b的tunneling port。

一个allocator port 是一个自己分配buffer的 supplier port。图中只有port a是allocator port。

同一个组件中复用其他port的buffer的称之为 sharing port 。port c是一个sharing port。

至少使用了一条tunnel的组件称之为tunneling component。

一个port的 buffer requirement包括需要的buffer的数目和每一个buffer的size。一个port通过在它的 tunneled port上调用 OMX_GetParameter,可以获得一个 OMX_PORTDEFINITIONTYPE数据结构,这个数据结构表示了buffer的requirement。注意一个port也可以从共享它buffer的port获得buffer requirement而不用调用OMX_GetParameter,因为这两个port可以位于同一个组件内。

1.1.7.2  IL Client Component Setup

为了构建一个tunneling component,IL client 需要遵循以下步骤:

1.加载所有tunneling component,并且在这些组件之间建立tunnel;

2.给所有的组件发送命令,以便组件的状态从loaded state 变为idle state;

如果IL client不按照上述步骤执行,组件可能由于彼此之间的依赖关系而导致永远都不会进入到idle 状态。

1.1.7.3  共享buffer组件从Loaded转变为Idle State

在 OMX_SetupTunnel被调用的时候,将会确认一条tunnel的两个port中哪一个(input或者output)是buffer supplier。因此当一个组件接收命令从loaded转换为idle的时候,它应该清楚的知道它所有的组件的角色:supplier或者 non-supplier 。

当接收命令从loaded转换为idle,组件按顺序执行以下操作:

1.组件确定采取拿一种方式实现buffer共享,遵循以下规则:

a) 组件可以复用buffer采取如下两种方式:复用一个input的buffer到一个或者多个output port上;或者复用一个output的buffer到一个input上。

b) 只有 supplier port才可以复用另一个port的buffer。

c) 当一个组件中有多个output port分享buffer的时候,port的buffer属性为只读。

如图1-7 所示:

图1-7 允许的buffer分享关系

2.组件确定supplier port,以及哪些supplier port是allocator port。只有当一个supplier port不复用同一个组件的non-supplier port的buffer的时候,它就是 allocator port。图1-8中,箭头背向的port是supplier port,箭头指向的port为non-supplier port。一个port指出来的箭头表示共享关系。带有盒子的port代表 allocator port。


3.一个组件按照以下操作给它的每一个allocator port分配buffer:

a) allocator port确定 buffer requirement 根据每一个复用buffer的port。参见下面的A

b) allocator port通过调用OMX_GetParameter来确认每一个tunneled port的buffer requirement。参见条款B

c) allocator port根据自己的buffer requirement,tunneled port的buffer requirement,以及sharing port的buffer requirement来确定应该分配buffer的最大数目。

d) allocator port告知与之tunneling的non-supplier port的实际buffer数目。通过调用 OMX_SetParameter函数,参数OMX_IndexParamPortDefinition,以此来设置nBufferCountActual的合适参数。见条款E

e) allocator port 和每一个与之关联的sharing port共享它的buffer。见条款D

f) 每一个已经分配的buffer,allocator port都要调用OMX_UseBuffer来传递buffer到tunneling port。见条款C

一个组件应该满足以下条款:

A. 确定一个sharing port的requirement的时候,它应该首先在它的tunneled port上调用OMX_GetParameter查询requirement,然后返回它自己的requirements和查询所得的requirements的最大值。

B. 当 non-supplier port 接收 OMX_GetParameter调用,查询它的buffer requirements的时候,non-supplier port应该首先查询所有复用它的buffer的port的requirements,然后返回自己的requirements和查询到的requirements中的最大值。

C. 当一个 non-supplier port接收到它的tunneled port所发起的OMX_UseBuffer调用时,non-supplier port 应该给和自己所在组件的复用它buffer的port共享这个buffer。

D. 在同一个组件上,A和B共享一块buffer,当B复用这块buffer的时候,它应该首先调用 OMX_UseBuffer,把这块buffer传递给它的 tunneled port。

E. 当一个non-supplier port接收到来自它的tunneled port发起的针对OMX_IndexParamPortDefinition 的OMX_SetParameter 调用时,它应该把参数nBufferCountActual传递给任何复用它buffer的port。同样的,每一个 supplier port通过这种方式接收到 nBufferCountActual的时候,应该通过执行OMX_SetParameter (OMX_IndexParamPortDefinition)调用把 nBufferCount传递给它的 tunneled port。通过这种方式,依赖链中实际使用的buffer的数目得以传递下去。

当所有enable的port都得到了他们所需要的buffer的时候,组件可以从loaded状态变为idle状态

1.1.7.4  使用共享内存的协议

当一个input port通过 OMX_EmptyThisBuffer接收一个共享buffer的时候,这个input port可能和一个output port通过以下机制共享这块buffer:

•  在input port 调用 OMX_EmptyBufferDone 作为对它的tunneling port的回复之前,output port在它的tunneling port上调用 OMX_EmptyThisBuffer。

•  input port直到所有和它共享buffer的output已经分享了这块buffer之后才调用 OMX_EmptyBufferDone

1.1.7.5  不共享buffer组件从Loaded转变为Idle状态

如果组件不共享内存,比起共享组件的实现,将会减少很多步骤和所遵循的条款。非共享内存组件从loaded转变为idle,按照以下步骤:

1.确定buffer共享实现的方式。在这种case下,不用进行这一步。

2.确定哪一些supplier port是 allocator port。全部都是。

3.按照如下步骤为每一个 allocator port分配内存:

a. 因为没有共享内存,所以不用询问sharing port对buffer的要求;

b. allocator port 通过调用OMX_GetParameter来取得tunneled port对buffer的要求;

c. 根据allocator和与之关联的tunneling port的buffer requirement的最大值,来分配内存;

d. 因为没有内存共享,所以不用向sharing port传递buffer.

e. 对每一块分配的内存,allocator port 在tunneling port上调用 OMX_UseBuffer。

1.1.8  port重连

port重连允许一个tunneled 组件被另一个tunneled组件替换,而不用破环其他的组件。在图1-10,组件B1被组件B2替换。为了达到这点,组件A的output port和组件B的input port首先要通过port 禁止命令设置为不可用。一旦分配的buffer被返回给它们的拥有者并被释放,组件A的output port就可以连接到组件B2。接下来组件B1的output port和组件C的input port也要采取类似的方式禁用。当所有的分配的buffer都返回给它们的拥有者然后被释放,组件C的input port可以连接到组件B2的output port。所有的port可以被允许使用通过发送使能命令。

图1-10 port重连

在某些情况下,比如音频,当重连一个组件到另一个组件的时候,可能希望新组件的数据采取fade-in的方式,而原始的组件的数据采取fade-out的方式(即淡入淡出)。图1-11 说明了这是如何做到的。第一步,组件A发送数据到组件B1,组件B1发送数据到组件C;组件A和组件C都有一个额外的port处于禁用状态。第二步,IL client首先为A和B2建立tunnel,然后为B2和C建立tunnel。然后使能两条tunnel上的所有port。假设组件C是audio组件,那么C就可以将组件B1和B2的数据采用变化的增益参数进行混合。在第三步,从组件A和C连接B1的port被禁用。

图1-11 重连组件

图1-11 重连组件

1.1.9  命令队列和buffer刷新

组件内独立的命令队列可以使组件刷新那些还没有处理的buffer,把它们归还给IL client,当采取 non-tunneled通信机制的时候,或者归还给与之关联的tunneled port,当采取的是tunneled通信机制。图1-12 假设组件有一个port使用了IL client分配的buffer。在这个例子中,client发送了5块buffer给组件,在发送刷新命令之前。当组件处理刷新命令的时候,会按照原始的顺序把未处理的buffer返回给client,然后触发它的 event handler 发送event通知client。在接收到刷新命令之前,已经处理了两块buffer。连同为处理的三块buffer,组件会按照接收顺序,全部返还给client。

图1-12 刷新buffer

1.1.10  标记Buffer

IL client也可以通过标记buffer触发产生一个事件。在一个buffer的buffer header中可以标记这块buffer。在OpenMax的组件链中,标记从一个input port传递到一个output port。标记允许一个组件发送一个事件给IL client,当这个组件遇到一个标记buffer。图1-13 解释了这个过程:

图1-13 标记buffer

IL client发送一条命令标记一个buffer。组件的output要发送的下一个buffer被标记,图中为B1。组件B处理B1的数据,然后把结果填入到B2,同时还带上标记。当组件C通过它的input port接收到B2,组件不会触发它的event handler,直到它已经处理完毕这块buffer。

1.1.11  事件和callback

组件发送给IL client 6种事件:

• 错误事件

• 命令处理完成通知事件

• 标记buffer事件

• port设置变化通知事件

• buffer flag 事件,当遇到流的结尾

• 资源请求事件,当组件等待它所需要的资源的时候

1.1.12  Buffer Payload

port的配置定义了port上传输的buffer的数据格式,但是配置并没有说明数据在buffer中是如何存放的。一般来说有三种case来描述一块buffer如何被数据填充,这三种case都有各自的好处。在三种case中,buffer中有效数据的范围和起点由buffer header中的pBuffer,nOffset,和nFilledLength三个参数来决定。pBuffer指向buffer中有效数据的起始点。nOffset表示buffer 起始点到有效数据起始点的偏移量。nFilledLength表明有效数据的长度。因此buffer中有效数据位于 pBuffer +nOffset 到 pBuffer + nOffset + nFilledLength的范围内。下面的case,描述了在解码或者编码中,buffer中的压缩数据流进或者流出一个组件。在所有的case下,buffer只是为数据的传递提供了一种机制,而对buffer中的内容没有特殊的要求。对于数据的要求定义在port的配置中。buffer中的阴影部分表示数据,空白部分表示没有数据。case 1:buffer被部分或者全部填充。在这种case下,buffer中含有压缩数据帧,使用f1到fn来标记。

case 1为播放解码数据提供了便利。一个buffer可以含有多个帧,减少了为了达到解码所要求的数据量而导致的buffer传递的次数。然后,这种情况下,需要decoder自己去buffer中解析帧数据。同时组件需要有一个帧buffer,这个buffer中存放解析数据或者存放一部分的帧数据,因为完整的帧数据可能存在于下一个buffer中。

case 2 每一个buffer中填充的是完整的帧。

case 2和case 1不同,因为case 1可能只含有一帧的部分数据。共同点都是需要解码器从buffer中分析出一块帧数据。

case 3 每一块buffer都只含有一帧完整的数据。


case 3的好处是不需要解码器解析帧数据了,解析帧数据的工作放在了 source component。但是每次只能传输一帧数据也可能会对性能造成很大的冲击。

作为基本的要求,解码器或者编码器都应该支持case 1.

1.1.13   Buffer Flags和时间戳

Buffer flags 表示着buffer数据中的某些属性。时间戳描述着buffer中的数据在显示时候的时间信息。一旦一个buffer的时间戳被 clock component组件所确定,任何的组件都不能为了速率控制或者同步而修改这个值。

Buffer中的元数据(即标志和时间戳),适用于buffer中的第一个新的逻辑单元。因此,对于一个buffer中含有多个逻辑单元,buffer的元数据适用于buffer起始边界以后的逻辑单元。除非另有说明,否则当一个组件接收到标记了flag或者时间戳的逻辑输入单元,它应该将这些元数据复制到逻辑输出单元中去。

1.1.14  同步

通过clock component的同步port可以到达同步的目的。clock component和它的port被划分在 其他组件中。clock component内部含有一个根据audio或者video的参考时钟来记录流媒体播放位置的多媒体时钟。clock component可以通过同步port传输带有时间信息的buffer到client 组件。client组件可能给某个操作打上时间戳(例如一个视频帧的呈现),通过请求clock component发送时间戳信息,来匹配媒体时钟。图1-14 展示了这一过程:

1.1.15 速率控制

clock component通过提供一系列的配置来改变媒体时钟,以此实现所有的速率控制。IL client可以通过改变媒体时钟的比例系数(有效的改变媒体时钟变化的方向和速度),来实现播放,快进,暂停,后退,以及慢放。IL client也可以通过这些配置来启动或者暂停媒体时钟,以此来改变媒体时钟的状态。clock component通过在同步port上发送带有新的比例系数和媒体时钟状态的媒体时钟变化通知,来告知所有的client组件,媒体时钟的比例系数和状态发生了变化。尽管一个组件可能并不会以改变buffer的时间戳来应对媒体时钟比例系数的变化,但是它可能改变它相应的处理。

1.1.16 组件注册

静态链接或者动态加载。

1.1.17  资源管理

这一节描述OpenMax IL 中用于资源管理的API。

1.1.17.1  资源管理的必要

当一个组件因为缺少资源而不允许变迁为idle状态时,IL client没有必要知道缺少的资源,也没有必要知道是哪一个组件在使用这些资源。

OpenMAX的一个目标是通过提供IL 层,是IL 层以上的软件层与硬件无关。通过指定下面这些关于资源管理的条款,以使OpenMax达到与硬件无关。

• 一个IL client没有必要知道IL 实现的细节或者IL 组件在使用哪一种资源

• 如果发生资源冲突,IL client能够依赖组件的一致行为而不用管IL的实现和具体的硬件平台

• 一个IL client不应该和硬件厂商的资源管理器直接交互,有以下两个原因: 

• 和硬件无关原则相违反

• 和硬件资源管理器的具体工作大多数都是由IL client来处理,这会影响IL client在多种多媒体平台上的表现

虽然资源管理在 OpenMAX IL API 1.0版本中解决,但是作为资源管理的“钩子”已经以行为准则,组件的优先级,资源管理相关的组件状态而落实。这些钩子将会使在以后的版本中全面解决资源管理奠定基础。

在继续之前,资源管理和政策的定义会使下面的讨论收益:

•  Resource management负责组件访问有限的资源。一个资源管理者要知道,当前有多少具体的资源可用,当前使用资源的组件,当前的组件使用了多少资源。一个资源管理器应该制定策略,当资源发生冲突的时候,哪一个组件可以优先使用。

• Policy 负责管理组件链。根据 resource managemen提供的信息,或者系统配置,或者应用层的请求,或者其他的因素,策略管理者应该能够决定哪一个组件链可以运行或者恢复运行。

1.1.17.2  体系假设

下面有两种关于 OpenMAX IL层的体系假设:

假设1:在应用层和 OpenMAX IL之间存在一个framework层,包含有策略管理者。

假设2:一个系统可能有多个硬件平台供OpenMAX 组件使用,这些硬件平台由硬件厂商指定的资源管理者管理。


1.1.17.3  组件优先级

每一个组件有一个由IL client设置的优先级(OMX_U32整形)。具体的优先级的范围可以留给平台去实现,但是优先级的目的是很重要的,并且能够在不同的IL 实现上表现一致。组件的优先级按照数值从高到低越来越重要,0表示最高优先级。当两个组件拥有相同的优先级,最近被拥有过资源的组件被认为具有较高的优先级。

1.1.17.4  行为规范

在IL 层定义了如下行为规范:

• 只有当一个组件进入idle状态没有足够的资源或者一个组件不能释放占用的资源,将会产生OMX_ErrorInsufficientResources错误。

• 当组件进入idle状态的时候,它并不知道已经有人抢占了资源,或者低优先级的组件需要释放资源。

• 一个组件已经占据了的资源被抢占,当它从Executing或者Paused进入Idle的时候,要发送OMX_ErrorResourcesPreempted 给IL client,或者当它从Idle进入Loaded的时候,要发送 OMX_ErrorResourcesLost给IL client。

• 如果IL client想知道什么时候与组件相关的流可以启动或者恢复,它就应该请求得到一个通知,当组件的资源变为可用的时候。当把组件的状态置为OMX_StateWaitForResources state,当资源可用,client就会收到通知。

1.1.17.5  硬件厂商相关的资源管理

为了实现上述规范,需要一个位于IL层之下与硬件厂商相关的资源管理者执行下面的功能:

• 实现管理等待队列

• 记录可利用的资源

• 记录使用资源的组件,以及他们使用的是什么资源

• 当一个高优先级的组件需要资源的时候通知一个或者多个组件释放他们所占用的资源

OpenMax多媒体引擎 摘自《Android系统级深入开发—移植与调试》 18.1  OpenMax系统结构和移植内容 OpenMax是一个多媒体应用程序的框架标准。其中,OpenMax IL(集成层)技术规格定义了媒体组件接口,以便在嵌入式器件的流媒体框架中快速集成加速编解码器。 在Android中,OpenMax IL层,通常可以用于多媒体引擎的插件,Android的多媒体引擎OpenCore和StageFri 阅读详情

相关推荐

Unity 机械臂控制(二)——从碰撞检测到姿态解算:实现精准抓取

本文详细介绍了Unity中机械臂控制的实现方法,从碰撞检测到姿态解算,涵盖精准抓取的全流程。重点讲解了碰撞检测的分层处理、状态机设计、逆向运动学(IK)算法以及抓取稳定性优化等关键技术,并提供了实用的调试技巧和性能优化建议,帮助开发者实现高效的机械臂控制。

weixin_28693447的博客 306

OpenMAX/IL: OMX IL 学习笔记【1】- 结构框架

一、OpenMax简介 OpenMAX(Open Media Acceleration的缩写,开放多媒体加速层)是一个多媒体应用程序的标准。由NVIDIA公司和Khronos™在2006年推出。 它是无授权费的、跨平台的C语言程序接口序列,这些接口对音频、视频、静态图片的常用操作进行封装。 它包括三层,自上而下分别是应用层(AL)、集成层(IL)和开发层(DL)。 OpenMAX AL:Application Layer,应用和多媒体中间层的标准接口,使得应用在多媒体接口上具有了可移植性。 OpenMAX

houxiaoni01的博客 4811

2023-12-26~2025-07-04备份

一、海康视觉与汇川机器人 服务器-客户端通信设置IP在同一网段名称IP端口类型海康相机工控机7930服务器机器人3000客户端海康VM服务器设置1通信管理 增添TCP服务端,设置本机IP:192.168.0.20,端口号79302 协议解析 通信双方约定通信格式(设置收发数据格式,分隔符)机器人客户端设置(不同类型机器人设置有差别)1 通讯服务设置 设置连接服务器IP、服务端端口号、机器人端口号,确保连接正常2 协议解析 使用指令代码构建通讯解析。

青铜小码农 3126

OpenMax IL层设计分析总结

文章目录一、OpenMax的设计理念与特性点设计理念---媒体框架的抽象/可移植性/异步处理/组件组合设计特性---组件化API/方便新增解码器/方便扩展/支持动态链接/可配置二、OpenMax的设计点分析2.1 【兼容性】版本兼容性设计---组件版本号\指针函数\入参void指针2.2 【扩展性】新增组件设计---动态库新增组件2.3 【扩展性】新增组件新增私有功能 设计---扩展参数设置类型枚举2.4 【性能】异步处理性能设计---配置异步通知 / buffer轮转回调通知2.5 【编解码领域】组件间t

runafterhit的博客 1114

TSmaster CANFD配置避坑指南:为什么你的PCAN盒不支持最新版本?

本文针对TSmaster配置PCAN硬件进行CANFD通信时的常见问题,提供了详细的避坑指南。重点解析了CANFD配置中TSEG1等关键参数的理解误区,并深入探讨了TSmaster新版本与PCAN硬件驱动之间的兼容性变化,帮助工程师快速定位并解决连接失败或无法发送CANFD报文等问题。

alpha的博客 543

12V转5V稳压电路详细分析(7805三端稳压,LM2596稳压)

7805线性三端稳压 DCDC的意思是直流变到直流(不同直流电源值的转换),只要符合这个定义都可以叫DCDC转换器,或称之为开关电源或开关调整器。 7805属于常用的线性三端稳压器,输入电压范围不大于35V,输入输出压差2V,最大输出电流1.5A,下图为原理图,其原理简单,使 用方便,深受很多用户的喜爱。但是其不足之处就是效率低,特别是压差越大时其效率越低。比如这里12V转5V的效率为 5V/12V≈41.7%。 如上电路是最经典的7805稳压5V电路,电路中有两个电容,电容的作用如下: ...

a2001444的博客 9万+

OpenMax IL开发指引

前言: OpenMax IL参考网站:OpenMAX Overview - The Khronos Group Inc OpenMax IL源码下载网站:Bellagio OpenMAX Integration Layer

intel1985的专栏 1126

时序约束实战:从setup/hold到时钟路径约束的深度解析

本文深度解析数字设计中的时序约束实战,重点探讨如何从setup/hold检查的核心原理出发,理解并应用时钟路径约束。文章通过剖析Path Requirement的计算逻辑,结合同频、倍频及异步时钟域等典型场景,提供了从解读时序报告到编写、调试SDC约束的系统方法,帮助工程师有效应对复杂SoC设计中的时序收敛挑战。

weixin_29291185的博客 105

OpenMAX/IL: OMX IL 学习笔记【2】- 组件

OpenMAX的重点组成部分就是组件,OpenMAX通过将meida流过程中的各个模块抽象化为组件来进行耦合,在OpenMAX标准下,数据流通过组件来进行传递、处理、显示。在该篇文章里,不需要了解细致的组件内部实现机理,也不需要知道各种方法的代码实现形式(如遇少量代码形式的解析说明可暂时略过,只需要知道该段代码要完成的工作是什么即可),通过阅读该文章,需要知道的是组件是什么?它有什么用处?内部的主要组成结构是怎样的?更加细节性的介绍放到后面的文章里面介绍。 OpenMAX IL spec手册下载:ht.

houxiaoni01的博客 1833

自动驾驶仿真(六)—— SIL软件在环仿真测试

自动驾驶仿真六—— SIL软件在环仿真测试1. 在环仿真测试2. SIL软件在环仿真测试2.1 模型配置参数2.2 S function生成实现2.3 结果对比3. 参考学习的书目教材 博主前面的博文已经简要介绍了Carsim、Prescan 与 Simulink 在 “V”型开发流程的一些应用,现今成熟的汽车控制系统软件都是以(Model Based Design,MBD)来开发,它以图形化的方式表达复杂的逻辑、代码可读性、可移植性、开发调试便利程度都大大增强,同时利用成熟的代码生成工具链,也避免了手工代

zhudj08的博客 8654

静态时序分析(STA)基本概念

静态时序分析中常见概念(fasle Path,multicycle path等)

m0_61544122的博客 2256

OpenMax总结(二)OpenMax IL层结构

更详细内容见官方spechttps://www.khronos.org/openmax/【OpenMax IL层结构】图中的虚线中的内容是OpenMax IL层的内容,其主要实现了OpenMax IL中的各个组件(Component)。对下层,OpenMax IL可以调用OpenMax DL层的接口,也可以直接调用各种Codec实现。对上层,OpenMax IL可以给OpenMax AL 层等框架...

先行 “1” 步 2371

【微服务】Spring Aop原理深入解析

spring原理深度解析

congge 1万+

OpenMax总结(三)OpenMAX IL Control API

【OpenMAX IL Control API】OpenMax IL层的接口定义由若干个头文件组成,这也是实现它需要实现的内容,它们的基本描述如下所示。OMX_Types.h:OpenMax Il的数据类型定义OMX_Core.h:OpenMax IL核心的APIOMX_Component.h:OpenMax IL 组件相关的 APIOMX_Audio.h:音频相关的常量和数据结构OMX_IVCo...

先行 “1” 步 979

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

OpenMax IL: component 概述

OpenMax IL 有四个部分组成:          客户端(Client):OpenMax IL的调用者  组件(Component):OpenMax IL的单元,每一个组件实现一种功能  端口(Port):组件的输入输出接口  隧道化(Tunneled):让两个组件直接连接的方式 OpenMax IL 中重要的组成部分是component,component是OpenMa

太妃糖的专栏 6263

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

18. Android MultiMedia框架完全解析 - OpenMAX介绍

OpenMAX IL API通过C语言致力于打造可移植媒体组件的阵列平台。这些组件可以是来源(source)、汇出(sink)、编解码器(codec)、过滤器(filter)、分离器(splitter)、混频器(mixers),或任何其他操作。 OpenMAX IL API允许用户加载,控制,连接和卸载各个组件。Android主要的多媒体引擎StageFright是通过IBinder使用Open...

吾皇的博客 3898

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

gst-openmax介绍

【引言】             对于gstreamer多媒体框架在硬件平台上面的适配,目前有两种方法,一种是直接将硬件平台的编解码能力直接编写为gstreamer插件,这也是前期选择的方法;另一种是使用gst-openmax抽象层,使得实现了Openmax-IL接口的硬件平台可以直接使用gstreamer框架(因为众多的android设备都实现了openmax-IL接口,所以gst-openm

xiaohuan_study的专栏 6448

OpenMAX IL多媒体硬件加速接口实战解析

多媒体硬件加速在现代计算设备中扮演着越来越重要的角色,尤其在嵌入式Linux和Android平台上,视频编解码性能直接依赖于底层SoC的硬件能力。然而,不同芯片厂商的私有SDK接口各异,导致软件移植成本高昂。OpenMAX IL作为一套开放的多媒体组件接口标准,通过状态机、端口和缓冲区管理等机制,为上层提供了统一的硬件加速抽象层。它让GStreamer、Android MediaCodec等框架能够以一致的方式调用硬件编解码器,解决跨平台复用难题。从视频硬解码到图像处理,OpenMAX IL广泛应用于车载系

weixin_34279184的博客 379

编解码抽象层OpenMax简介

一、OpenMax相关概念 OpenMax存在背景—为多媒体硬解方案提供标准提升移植效率 随着消费者对视频、音频、语音和 3D 等应用程序在智能手机、音频和视频媒体播放器和游戏机等各种平台上的改进功能的需求不断增长,多媒体硬件平台出现了各种解决方案加速多媒体应用程序。(如具:有特定多媒体扩展功能的通用处理器、低级硬件加速器,包括 DSP 在内的多处理器架构、专用硬件视频解码器) 所有这些架构变体的主要挑战之一是开发高效的代码。尽管通常会提供编译器,但很少能从高级编程语言中挖掘整个架构的全部潜力。结果是应用程

runafterhit的博客 4154

OpenMax IL: component 基础知识

OpenMax IL: component 基础知识 OpenMax IL 有四个部分组成:         1.客户端(Client):OpenMax IL的调用者 2.组件(Component):OpenMax IL的单元,每一个组件实现一种功能 3.端口(Port):组件的输入输出接口 4.隧道化(Tunneled):让两个组件直接连接的方式 OpenMax IL 中重要的组

tigerly的专栏 681

OpenMAX介绍(总括)

一、OpenMax简介     OpenMAX是一个多媒体应用程序的标准。由NVIDIA公司和Khronos™在2006年推出。     它是无授权费的、跨平台的C语言程序接口序列,这些接口对音频、视频、静态图片的常用操作进行封装。     它包括三层,分别是应用层(AI)、集成层(IL)和开发层(DL)。其中IL层已经成为了事实上的多媒体框架标准。嵌入式处理器或者多媒体编解码模块的硬件生产

qq69696698的专栏 1万+

OpenMAX/IL: OMX IL 学习笔记【1】- 接口与头文件

1.  OpenMAX IL 层的接口定义是由若干个头文件的形式给出的,在头文件中定义了一些结构体和需要开发者实现的接口函数,包括: OMX_Types.h:OpenMax Il的数据类型定义OMX_Core.h:OpenMax IL核心的APIOMX_Component.h:OpenMax IL 组件相关的 APIOMX_Audio.h:音频相关的常量和数据结构OMX_IVCommon

Blue_XX的专栏 3623

OpenMax框架

OpenMAX open media acceleration:开放多媒体加速器。跨平台的软件抽象层,用来处理多媒体。 注意:这是一个标准,而不是具体的实现。 openmax建立了三个层次上的标准。 AL层:应用层。完全免费,跨平台的开放标准。提供创建,控制player和recorder对象;连接player,recoreder与配置输入和输出对象(content reader/writer,audio inputs/outputs,display windows) IL层:集成层。IL层的API定

ambitions666的博客 1141
上一篇: Android MediaPlayer 框架UML图
下一篇: MediaProvider 数据库Audio相关表格对应关系
Sailingthink
博客等级 码龄14年 31粉丝 26原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值