翻译:openmax文档
设计哲学
如前所述,OpenMAX IL API 的核心重点是媒体组件的可移植性。现有设备和媒体实现解决方案的多样性要求 OpenMAX IL 将媒体软件栈的更高层次作为关键初始用户。对于许多操作系统而言,这意味着现有的媒体框架或某种形式的多媒体中间件。
另一个关键目标是 OpenMAX AL API,它为 OpenMAX IL 提供了一个标准化的更高应用级接口伴侣。OpenMAX AL 的设计是为了与 OpenMAX IL 实现兼容。
因此,OpenMAX IL API 的大部分内容都满足了多媒体中间件的需求,允许该层尽可能轻量级。其结果是创建了一个可以轻松地插入大多数操作系统和多媒体中间件解决方案软件栈的接口。
API 的设计还力求兼容尽可能多的系统架构。所得到的设计采用了高度异步通信,允许在另一个线程、多个处理元素或专用硬件上进行处理。此外,通过隧道技术使硬件加速组件能够相互直接通信,为实施架构提供了更大的灵活性和效率。
1.2.3 软件环境
在某些系统中,已经存在用户级别的媒体框架。在那些没有这种多媒体中间件的系统中,OpenMAX AL 可以填补这一空白。OpenMAX IL API 被设计为能够轻松地适应这一层之下,接口之间的开销极少或没有。在大多数情况下,原生的媒体框架可以被一个仅负责转换 API 的薄层所替代。同样地,鉴于这两个 API 的协同设计,OpenMAX IL 可以更加无缝地融入 OpenMAX AL 的实现中。图 1-1 展示了 OpenMAX IL API 的软件环境。
利益相关者
参与多媒体解决方案生产的公司种类繁多,它们构成了几个类别的利益相关者,每个类别都对 IL API 持有自己的兴趣。这些利益相关者可能包括:
1.2.4.1 硅供应商(Silicon Vendors, SV)
硅供应商负责提供一套具有代表性的、针对其平台特制的 OpenMAX IL 组件。这些组件应当能够充分展示其平台的能力。硅供应商不仅需要提供基础的媒体处理单元(如编解码器、音频处理器等),还可能包括与这些硬件紧密集成的软件接口和驱动程序。
1.2.4.2 独立软件供应商(Independent Software Vendors, ISV)
独立软件供应商预计会提供额外的、具有差异化的 OpenMAX IL 组件,这些组件可能针对某个特定的硅供应商平台,也可能具有更广泛的适用性。ISV 的产品往往专注于特定的应用领域或功能,如高级视频编辑、游戏引擎优化等,它们通过 OpenMAX IL API 与硬件和操作系统进行交互,以实现高效、流畅的多媒体体验。
1.2.4.3 操作系统供应商(Operating System Vendors, OSV)
操作系统供应商负责提供软件多媒体框架和标准的参考 OpenMAX IL 组件,这些组件能够集成硅供应商和 ISV 提供的组件。OSV 的职责还包括对标准参考 OpenMAX IL 组件进行一致性测试,以确保它们在不同平台上的兼容性和性能表现。此外,OSV 还可能提供必要的开发工具、文档和支持,以帮助开发者更轻松地使用 OpenMAX IL API。
1.2.4.4 原始设备制造商(Original Equipment Manufacturers, OEM)
原始设备制造商负责将 SV、ISV 和 OSV 提供的 OpenMAX IL 组件集成到其特定的产品架构中,以生产出集成了 OpenMAX IL 的多媒体设备。OEM 可能会根据市场需求和自身产品特点对集成过程进行修改和优化。此外,OEM 还可能自行开发和集成专有的 OpenMAX IL 组件,以增强产品的差异化竞争力。OEM 的角色在于将各种技术组件整合成一个完整的产品,并确保其符合市场标准和用户期望。
1.2.5 接口概述
OpenMAX IL API 是一个基于组件的媒体 API,它主要由两个主要部分组成:核心 API 和组件 API。这种设计使得 OpenMAX IL 能够灵活地集成和处理各种多媒体数据,同时保持高度的可扩展性和可定制性。
1.2.5.1 核心(Core)
OpenMAX IL 的核心部分负责动态地加载和卸载组件,以及促进组件之间的通信。一旦组件被加载,API 允许用户直接与组件进行通信,从而消除了高层命令可能带来的开销。此外,核心还允许用户在两个组件之间建立通信隧道。一旦通信隧道建立,核心 API 将不再参与通信过程,数据将直接在组件之间流动。这种设计确保了高效的数据传输和组件间的无缝协作。
1.2.5.2 组件(Components)
在 OpenMAX 集成层中,组件代表了功能的独立单元。组件可以是数据源、数据接收器、编解码器、过滤器、分割器、混音器或任何其他数据操作符。组件的具体实现可以依赖于硬件、软件编解码器、其他处理器或它们的组合。例如,一个视频编解码器组件可能是一个硬件加速的视频处理单元,而一个音频过滤器组件可能是一个软件实现的音频效果器。通过组合不同的组件,OpenMAX IL 能够构建出复杂的多媒体处理流水线,以满足各种应用场景的需求。
总的来说,OpenMAX IL API 的接口设计既灵活又高效,它允许开发者根据具体需求选择和配置合适的组件,并通过核心 API 实现组件之间的通信和数据交换。这种设计思想使得 OpenMAX IL 成为了一个强大的多媒体处理平台,能够支持从简单的音频播放到复杂的视频编辑等多种应用场景。
在 OpenMAX IL API 中,组件的单个参数可以通过一系列相关联的数据结构、枚举和接口进行设置或检索。这些参数包括与组件操作相关的数据(如编解码器选项)或组件的实际执行状态。通过这些参数,开发者可以精确地控制组件的行为,以实现所需的多媒体处理效果。
对于缓冲区状态、错误以及其他时间敏感的数据,OpenMAX IL API 通过一组回调函数将这些信息传递给应用程序。这些回调函数通过标准的参数设置机制进行配置,它们允许 API 暴露出系统架构的异步特性。通过这种方式,开发者可以在不阻塞主程序执行的情况下,及时响应和处理这些重要的状态和数据变化。
组件之间的数据通信是通过称为端口的接口进行的。端口不仅代表了组件与数据流之间的连接,还代表了维持这种连接所需的缓冲区。用户可以通过输入端口向组件发送数据,或通过输出端口从组件接收数据。类似地,通过将一个组件的输出端口连接到另一个组件的相似格式输入端口,可以建立两个组件之间的通信隧道。这种灵活的数据交换机制使得开发者能够构建出复杂而高效的多媒体处理流程。
综上所述,OpenMAX IL API 通过其丰富的参数设置、回调机制和端口接口,为开发者提供了强大的多媒体处理能力。无论是对组件行为的精确控制,还是对异步事件的及时响应,OpenMAX IL 都能够满足多媒体应用开发中的多样化需求。
2 OpenMAX IL 介绍与架构
本节文档将详细介绍 OpenMAX IL 的特性和架构。
2.1 OpenMAX IL 描述
OpenMAX IL 层是一个 API,它定义了一个软件接口,用于为系统中的软件组件提供一个访问层。该软件接口的目的是将具有不同初始化和命令方法的组件封装起来,并提供一个具有标准化命令集和组件构造与销毁方法的软件层。这样,无论组件来自不同的供应商还是组织内部的不同团队,都可以通过 OpenMAX IL API 进行统一管理和交互。
2.1.1 架构概述
考虑一个系统需要实现四个多媒体处理功能 F1、F2、F3 和 F4。这些功能可能来自不同的供应商,也可能是组织内部不同团队开发的。每个功能在设置和拆除时可能有不同的要求,配置和数据传输的方法也可能不同。OpenMAX IL API 提供了一种将这些功能(单独或按逻辑分组)封装成组件的手段。API 包括一个标准协议,使得来自不同供应商/团队的兼容组件能够相互交换数据,并可以互换使用。
OpenMAX IL API 与一个称为 IL 客户端的高层实体接口,该实体通常是过滤器图多媒体框架、OpenMAX AL 或应用程序的功能部分。IL 客户端与称为核心的集中式 IL 实体交互。IL 客户端使用 OpenMAX IL 核心来加载和卸载组件、设置两个 OpenMAX IL 组件之间的直接通信,并访问组件的方法。
IL 客户端始终通过 IL 核心与组件进行通信。在大多数情况下,这种通信等同于调用 IL 核心的宏之一,这些宏直接转换为对组件方法之一的调用。例外情况(即 IL 客户端调用实际的核心函数)包括组件的创建和销毁、查询已安装组件及其支持的角色,以及通过隧道连接两个组件。
组件体现了媒体处理功能。虽然本规范明确定义了 OpenMAX IL 核心的功能,但组件提供者定义了给定组件的功能。组件根据它们导出的参数结构处理四种类型的数据:音频、视频、图像和其他(例如,用于同步的时间数据)。
OpenMAX IL组件通过其组件句柄提供对一组标准组件功能的访问。这些功能允许客户端获取和设置组件及端口配置参数,获取和设置组件的状态,向组件发送命令,接收事件通知,分配缓冲区,与单个组件端口建立通信,以及在两个组件端口之间建立通信。
每个OpenMAX IL组件都必须至少有一个端口来声明其符合OpenMAX IL标准。尽管供应商可能提供一个没有端口的OpenMAX IL兼容组件,但符合性测试的大部分内容都依赖于至少一个符合标准的端口。OpenMAX IL中定义的四种类型的端口对应于端口可能传输的数据类型:音频、视频和图像数据端口,以及其他端口。每个端口根据其是消耗还是产生缓冲区被定义为输入或输出端口。
在一个包含四个多媒体处理功能F1、F2、F3和F4的系统中,系统实现者可能会为每个功能提供一个标准的OpenMAX IL接口。实现者同样可以轻松地选择这些功能的任何组合。这些功能的划分是基于端口的。图2-1展示了一个提供这些功能的OpenMAX IL实现的几种可能的分区。
可以与一个单一的组件端口建立通信,以及在两个组件端口之间建立通信。每个OpenMAX IL组件都必须至少有一个端口来声明其符合OpenMAX IL标准。尽管供应商可能提供一个没有端口的OpenMAX IL兼容组件,但大部分符合性测试都依赖于至少一个符合标准的端口。OpenMAX IL中定义的四种类型的端口对应于端口可能传输的数据类型:音频、视频和图像数据端口,以及其他端口。每个端口根据其是消耗还是产生缓冲区被定义为输入或输出端口。
在一个包含四个多媒体处理功能F1、F2、F3和F4的系统中,系统实现者可能会为每个功能提供一个标准的OpenMAX IL接口。实现者同样可以轻松地选择这些功能的任何组合。这些功能的划分是基于端口的。图2-1展示了一个提供这些功能的OpenMAX IL实现的几种可能的分区。
2.1.3 系统组件
图2-2展示了使用OpenMAX IL实现的各种通信类型。每个组件可以有任意数量的端口用于数据通信。具有单个输出端口的组件被称为源组件。具有单个输入端口的组件被称为接收组件。完全在主机处理器上运行的组件被称为主机组件。在松散耦合的加速器上运行的组件被称为加速器组件。OpenMAX IL可以直接与应用程序集成,也可以与多媒体框架组件集成,以实现异构实现。
描述了三种通信类型。非隧道通信定义了一种机制,用于在IL客户端和组件之间交换数据缓冲区。隧道化定义了一种标准机制,允许组件以标准方式直接相互交换数据缓冲区。专有通信描述了一种专有机制,用于两个组件之间的直接数据通信,并且当发出隧道化请求时(前提是这两个组件都具备此能力),可以作为隧道化的一种替代方案使用。
2.1.3.1 组件规范
OpenMAX IL组件功能被分为两个规范:基础规范(base profile)和互操作性规范(interop profile)。
基础规范应支持非隧道通信。基础规范组件可能支持专有通信。但基础规范组件不支持隧道通信。
互操作性规范是基础规范的超集。互操作性规范组件应支持非隧道通信和隧道通信。互操作性规范组件可能支持专有通信。
互操作性规范与基础规范之间的主要区别在于,互操作性规范组件支持隧道通信。基础规范的存在是为了通过简化实现来降低OpenMAX IL实现者的采用门槛。基础规范组件不需要实现隧道通信。
2.1.4 组件状态
每个OpenMAX IL组件都会经历一系列状态转换,如图2-3所示。每个组件最初都被视为未加载状态。通过调用OpenMAX IL核心,可以加载组件。然后,通过直接与组件通信,可以实现所有其他状态转换。
当使用无效数据进行状态转换时,组件会进入无效状态。例如,如果回调指针没有设置为有效位置,组件可能会超时并警告IL客户端出现错误。当IL客户端检测到无效状态时,应停止、取消初始化、卸载并重新加载组件。尽管从图2-3中可以看出,任何状态都可以进入无效状态,但退出无效状态的唯一方法是卸载并重新加载组件。
通常,组件在IDLE(空闲)状态时,应拥有其所有操作资源。然而,在过渡到IDLE状态时,如果资源分配参数未知,则存在例外情况。例如,一个解码视频的组件在检查数据流之前,不知道需要多少参考帧,但组件又不能在过渡到IDLE状态之前检查数据流。在这些情况下,组件可能会推迟资源分配,直到它知道分配参数为止。如果动态分配失败,组件将自行挂起。因此,我们经常区分那些在“前期”分配的资源(例如在过渡到IDLE时)和后来通过调用分配的资源,前者称为静态资源,后者称为动态资源。
过渡到IDLE状态可能会失败,因为该状态需要分配所有操作静态资源。当从LOADED(已加载)状态过渡到IDLE状态失败时,IL客户端可以重试,或者选择将组件置于WAIT FOR RESOURCES(等待资源)状态。在进入WAIT FOR RESOURCES状态时,组件会与特定于供应商的资源管理器注册,以便在资源可用时收到通知。随后,组件将过渡到IDLE状态。IL客户端发送的命令控制除INVALID(无效)状态以外的所有其他状态转换。
IDLE状态表示组件已拥有其所需的所有静态资源,但当前并未处理数据。EXECUTING(执行中)状态表示组件正在等待接收要处理的缓冲区,并将按照第3节中的规定进行必要的回调。PAUSED(暂停)状态保持组件中缓冲区执行的上下文,但不处理数据或交换缓冲区。从PAUSED状态过渡到EXECUTING状态可以恢复组件停止处理缓冲区的地方。从EXECUTING或PAUSED状态过渡到IDLE状态将导致处理缓冲区的上下文丢失,这要求重新引入流的开始。从IDLE状态过渡到LOADED状态将导致丢失操作资源,如通信缓冲区。
2.1.5 组件架构
图2-4展示了组件的架构。请注意,组件只有一个入口点(通过其句柄指向一组标准函数),但根据组件拥有多少个端口,可能会有多个可能的传出调用。每个组件都会调用指定的IL客户端事件处理器。每个端口还会调用(或回调)指定的外部函数。每个端口还关联有一个指向缓冲区头的指针队列。这些缓冲区头指向实际的缓冲区。命令函数也有一个命令队列。所有参数或配置调用都在特定的索引上执行,并包括与该参数或配置相关联的结构,如图2-4所示。
一个端口应支持对IL客户端的回调,并且,如果它是互操作配置文件组件的一部分,则应支持与其他组件上端口的通信。
2.1.6 通信行为
组件的配置可以在从OpenMAX IL核心接收到组件的句柄后完成。与组件的数据通信调用是非阻塞的,并且一旦端口数量被配置、每个端口被配置为特定的数据格式、且组件被置于适当的状态后,这些调用就会被启用。数据通信特定于组件的某个端口。输入端口总是由IL客户端通过OMX_EmptyThisBuffer调用(有关更多信息,请参见第3.2.2.17节)。输出端口总是由IL客户端通过OMX_FillThisBuffer调用(有关更多信息,请参见第3.2.2.18节)。在上下文内实现中,在返回之前会进行EmptyBufferDone或FillBufferDone的回调。图2-5展示了上下文内与上下文外实现的预期行为。请注意,IL客户端不应对返回/回调序列做出假设,以便能够在上下文内和上下文外的OpenMAX IL组件之间进行异构集成。
与组件的数据通信始终指向组件的特定端口。每个端口都有一个组件定义的最小缓冲区数量,该数量应被分配或使用。端口将缓冲区头与每个缓冲区相关联。缓冲区头引用缓冲区中的数据,并提供与缓冲区内容相关联的元数据。每个组件端口都应能够分配自己的缓冲区或使用预分配的缓冲区;这两种选择之一通常会比另一种更高效。
2.1.7.1 IL客户端组件设置
为了设置隧道组件,IL客户端应该按照以下顺序执行以下设置操作:
- 加载所有隧道组件,并在这些组件上设置隧道。
- 命令所有隧道组件从加载状态过渡到空闲状态。
注意:如果IL客户端在某些组件共享缓冲区时不按此方式操作,由于组件之间可能存在的依赖关系,隧道组件可能永远不会过渡到空闲状态。
2.1.7.2 组件从加载状态过渡到空闲状态
当被命令从加载状态过渡到空闲状态时,非共享组件的每个提供者端口会执行以下操作:
- 通过OMX_GetParameter调用确定其隧道端口的缓冲区需求。
- 根据其自身的最大需求和隧道端口的需求分配缓冲区。
- 对其隧道端口调用OMX_UseBuffer。
端口重新连接
端口重新连接允许将一个隧道组件替换为另一个隧道组件,而无需拆除周围的组件。在图2-7中,组件B1将被组件B2替换。为此,应首先使用端口禁用命令禁用组件A的输出端口和组件B的输入端口。一旦所有已分配的缓冲区都返回到其合法所有者并被释放,组件A的输出端口可以连接到组件B2。类似地,应对组件B1的输出端口和组件C的输入端口发出端口禁用命令。在所有已分配的缓冲区都返回到其所有者并被释放后,组件C的输入端口可以连接到组件B2的输出端口。然后,可以对所有端口发出启用命令。有关端口禁用和启用的更多信息,请参阅第3.4.4节“端口禁用和启用”。
在某些情况下,比如音频处理,可能希望将一个组件重新连接到另一个组件,并在为新组件渐入数据的同时为原始组件渐出数据。图2-8展示了这是如何工作的。在步骤1中,组件A向组件B1发送数据,然后组件B1将数据发送给组件C。组件A和C都有一个额外的被禁用的端口。在步骤2中,IL客户端首先在组件A和B2之间建立隧道,然后在B2和C之间建立隧道,并启用这两个隧道中的所有端口。假设这些是音频组件,组件C可能能够以不同的增益混合来自组件B1和B2的数据。在步骤3中,将组件A和C连接到组件B1的端口被禁用,组件B1的资源可能会被释放。
2.1.9 队列和刷新
一个独立的命令队列允许组件在使用非隧道通信时刷新尚未处理的缓冲区,并将这些缓冲区返回给IL客户端,或者在使用隧道通信时返回给隧道输出端口。例如,假设一个组件有一个使用IL客户端分配的缓冲区的输出端口。在这个例子中,客户端在向组件发送刷新命令之前,先向组件发送了一系列五个缓冲区。在处理刷新命令时,组件返回每个未处理的缓冲区,并触发其事件处理程序以通知IL客户端。在刷新命令得到处理之前,已经处理了两个缓冲区。组件返回剩余的三个未填充的缓冲区并生成一个事件。IL客户端应在尝试反初始化组件之前等待该事件。
2.1.10 标记缓冲区
IL客户端还可以在遇到标记的缓冲区时触发事件生成。缓冲区可以在其缓冲区头中被标记。标记在OpenMAX IL组件链中从输入缓冲区内部传输到输出缓冲区。这个标记允许组件在遇到标记的缓冲区时向IL客户端发送事件。图2-9展示了这是如何工作的。
IL客户端发送一个命令来标记一个缓冲区。组件的输出端口发送的下一个缓冲区被标记为B1。组件B处理B1缓冲区,并将结果以及标记一起放在缓冲区B2中。当组件C通过其输入端口接收到标记的缓冲区B2时,组件在处理完缓冲区之前不会触发其事件处理程序。
2.1.11 事件和回调
组件向IL客户端发送六种类型的事件:
• 错误事件:被枚举,并可能随时发生。
• 命令完成通知事件:在命令成功执行后触发。
• 标记缓冲区事件:当组件检测到标记的缓冲区时触发。
• 端口设置更改通知事件:当组件更改其端口设置时生成。
• 缓冲区标志事件:在遇到流结束时触发。
• 资源获取事件:当组件获得它一直在等待的资源时生成。
端口在缓冲区可用时或需要缓冲区时执行缓冲区处理回调。
2.1.12 缓冲区有效载荷
端口配置用于确定和定义在组件端口上传输的数据的格式,但配置并不定义这些数据在缓冲区中的存在方式。
通常有三种情况描述缓冲区如何被数据填充。每种情况都有其自身的优势。
在所有情况下,缓冲区中有效数据的范围和位置都由缓冲区头的pBuffer、nOffset和nFilledLen参数定义。pBuffer参数指向缓冲区的起始位置。nOffset参数表示缓冲区起始位置与有效数据起始位置之间的字节数。nFilledLen参数指定缓冲区中连续有效数据的字节数。因此,缓冲区中的有效数据位于pBuffer + nOffset到pBuffer + nOffset + nFilledLen的范围内。
以下情况代表在解码或编码时,被传输到组件内或从组件内传输出的缓冲区中的压缩数据。在所有情况下,缓冲区仅作为数据的传输机制,对内容没有特定要求。内容的要求由端口配置参数定义。
缓冲区中的阴影部分表示数据,白色部分表示没有数据。
情况1:每个缓冲区被全部或部分填充。在包含压缩数据帧的缓冲区的情况下,数据帧由f1到fn表示。
情况1在解码以进行播放时提供了一个好处。缓冲区可以容纳多个帧,并减少为解码一定量的数据所需的缓冲事务数量。然而,这种情况可能要求解码器在解码帧时解析数据。此外,它还可能要求解码器组件具有一个帧构建缓冲区,用于放置解析后的数据或保存将在下一个缓冲区中完成的部分帧。
情况2:每个缓冲区仅填充完整的压缩数据帧。
情况2与情况1不同,因为它要求先对压缩数据进行解析,以确保只有完整的帧被放入缓冲区。此外,情况2也可能要求解码器组件在解码前对数据进行解析。这种情况可能不需要情况1中解析帧所需的额外工作缓冲区。
情况3:每个缓冲区仅填充一个压缩数据帧。
情况3的优点是解码组件不需要解析数据,解析工作将在源组件中完成。然而,这种方法在数据传输方面造成了瓶颈,数据传输将仅限于每次传输一个帧。根据实现方式,每个帧一次事务可能对性能的影响大于从缓冲区中解析帧的影响。
至少,解码器或编码器组件需要支持情况1。根据定义,如果编解码器组件能够支持情况1,那么它也可以支持情况2和情况3,但前提是压缩格式允许字节对齐的帧边界。例如,在将自适应多速率(AMR)编解码器配置为RTP有效载荷格式、带宽高效模式时,采用情况2或情况3进行操作可能没有意义。该格式定义的非字节对齐帧将不符合这些情况下定义的字节对齐帧边界。
在向解码器输入或从编码器输出时填充压缩数据的缓冲区时,如果帧不是字节对齐的,则可能会出现仅将填充限制为完整帧的问题。除了格式规范中定义的任何填充之外,还需要添加额外的填充。然后需要移除这些填充,因为数据不能直接附加。这将需要了解标准规范之外的填充位。同样,如果这种填充不符合端口配置的标准规范,则无法始终将完整帧放入缓冲区。在任何情况下,都需要具体了解如何处理这种情况,并且不同组件之间的处理方法可能不同。
为了互操作性,虽然对于未压缩数据格式,至少会在缓冲区中传递一个完整的数据单元,但不应假设或要求缓冲区中传递的内容是任何数量的完整帧。每个缓冲区中传递的压缩数据格式的内容量没有限制。
2.1.13 缓冲区标志和时间戳
缓冲区标志将某些属性(例如,数据流的末尾)与缓冲区中包含的数据相关联。缓冲区时间戳将微秒级的呈现时间与缓冲区中用于控制数据渲染时间的数据相关联。一旦时间戳与缓冲区相关联,任何组件都不应更改时间戳以进行速率控制或同步,这些操作在时钟组件中实现。
缓冲区元数据(即标志和时间戳)适用于缓冲区中的[第一个]新逻辑单元。因此,如果缓冲区中存在多个逻辑单元,则元数据适用于在缓冲区中首先出现的起始边界的逻辑单元。[缓冲区中的后续逻辑单元没有明确的标志或时间戳。如果每个逻辑单元都需要明确的标志和时间戳,则每个缓冲区应包含一个或更少的逻辑单元]。除非另有说明(例如,在标志定义中),否则接收带有标志或时间戳的逻辑输入单元的组件应将元数据复制到输入贡献的所有逻辑输出单元中。
2.1.14 同步
通过使用时钟组件上的同步(sync)端口来实现同步。这些端口和时钟组件在“其他”域内定义,并使用与数据端口相同的协议和调用进行操作。时钟组件维护一个媒体时钟,该时钟根据音频和视频参考时钟跟踪媒体流中的位置。时钟组件通过同步端口向客户端组件传输包含时间信息(由媒体时间更新表示,并包含媒体时钟的当前位置、比例和状态)的缓冲区。客户端组件可以通过请求时钟组件在媒体时钟与之匹配时发送该时间戳,来将操作的执行(例如,视频帧的呈现)定时到时间戳。在这种情况下,当客户端组件通过其同步端口接收到请求的满足时,它会执行该操作。图2-13说明了在组件示例配置中时间和数据缓冲区的流动。
2.1.15 速率控制
时钟组件还通过公开一系列用于控制其媒体时钟的配置来实现所有速率控制。IL客户端可以更改媒体时钟的缩放因子(有效地改变媒体时钟的前进速率和方向),以实现播放、快进、倒带、暂停和慢动作等特殊模式。IL客户端还可以通过使用这些配置来改变媒体时钟的状态来启动和停止时钟。时钟组件通过在所有同步端口上发送包含新缩放比例或状态的媒体时间更新,来使其所有客户端组件了解媒体时钟缩放比例和状态的变化。尽管组件可能不会在缩放比例变化时更改缓冲区时间戳,但它可以相应地更改其处理。例如,在特殊模式下,音频组件可能会调整音频的缩放比例和音调,或者完全停止传输输出。
2.1.16 组件注册
组件如何与核心进行注册通常是特定于核心的。然而,如果核心支持与组件的静态链接,那么它将支持第3节中描述的标准编译时组件注册方案。因此,供应商可以提供适合与所有支持它的核心进行静态链接的组件;这是通过将组件信息放入与组件和核心链接的数据结构中来实现的。
组件可以使用此机制进行静态注册,但其代码的大部分可以动态加载。
组件提供一个接口来检索其支持的标准组件角色。核心可以利用此接口将角色相关信息暴露给IL客户端。
2.1.17 资源管理
本节讨论资源管理在OpenMAX IL API中的作用。
2.1.17.1 资源管理的必要性
当由于资源不足而导致组件无法进入空闲状态时,IL客户端不知道受限资源是什么,也不知道哪些组件正在使用该资源。因此,IL客户端无法解决资源冲突。这些情况促使了IL资源管理的必要性。
OpenMAX IL的一个目标是通过IL层为其上层提供硬件独立性。关于资源管理,可以通过指定以下要求来实现硬件独立性的目标:
• IL客户端(例如,通常是软件平台一部分的多媒体插件)不需要了解IL实现的细节或IL组件正在使用哪种资源。
• 在出现资源冲突时,IL客户端应该能够依赖跨IL实现和硬件平台的一致组件行为。
• 出于两个原因,IL客户端不应该直接与硬件供应商特定的资源管理器接口交互:
-
此方法违反了硬件独立性的目标。
-
此方法给IL客户端增加了大量重新工作,这会影响IL客户端在多个硬件平台上的可重用性。
尽管OpenMAX IL API版本1.1并未完全解决资源管理问题,但已经以行为规则、组件优先级和与资源管理相关的组件状态的形式设置了资源管理的“钩子”。这些“钩子”为OpenMAX IL API后续版本中的全面资源管理奠定了基础。
在进一步讨论之前,为了后续讨论的需要,我们定义了资源管理和策略这两个术语:
• 资源管理负责管理组件对有限资源的访问。资源管理器将了解有多少特定资源可用,哪些组件当前正在使用该资源,以及这些组件正在使用多少资源。基于资源冲突和可用性,资源管理器将向策略推荐哪些组件应该被抢占或恢复。
• 策略负责管理组件链或流。策略用于根据包括资源、系统配置和其他因素在内的信息来确定流是否可以运行。
2.1.17.2 示例架构
图2-15展示了一个基于OpenMAX IL的示例系统的高级架构图。在此示例中,应用程序和IL层之间存在一个具有策略管理器的多媒体框架。此示例系统还具有多个硬件平台,这些平台由不同的OpenMAX IL组件使用,并由多个硬件供应商特定的资源管理器管理。但是,该系统同样适用于单个集中式资源管理器。
此示例架构被用作以下关于组件优先级、行为规则和特定于硬件的资源管理器的讨论的背景。但需要注意的是,此讨论适用于任何基于OpenMAX IL的架构。
为确保在资源冲突情况下组件行为的一致性,需要对组件优先级和行为规则进行统一定义。
2.1.17.3 组件优先级
每个IL组件都有一个由IL客户端设置的优先级值(OMX_U32整数)。优先级采用降序排列,其中0表示最高优先级。同时,还适用以下打破平局规则:当比较具有相同优先级的组件时,最近获取资源的组件应被视为比拥有资源时间更长的组件具有更高的优先级。
此外,IL客户端还可以为IL组件分配组优先级。任何共享相同组ID的组件都将保持相同的组优先级。
2.1.17.4 行为规则
在IL层定义了以下行为:
• 当资源不足且无法通过抢占优先级较低的组件来释放足够资源时,尝试进入空闲状态的组件将收到OMX_ErrorInsufficientResources错误。
• 当组件尝试进入空闲状态而其所需资源需要通过抢占优先级较低的组件来释放时,该组件并不知道抢占正在发生。
• 当拥有需要被抢占资源的组件从执行或暂停状态转移到空闲状态时,它将向IL客户端发送OMX_ErrorResourcesPreempted错误。一旦资源被释放,该组件在从空闲状态转移到加载状态时,将向IL客户端发送OMX_ErrorResourcesLost错误。
• 在IL客户端希望知道与组件关联的流何时可以恢复或开始时,IL客户端应请求在资源可用时获得通知。这通过将组件置于OMX_StateWaitForResources状态来实现。当资源变得可用时,组件会自动进入空闲状态。当客户端收到组件处于空闲状态的通知时,它可以尝试将该链中的其余组件也移动到空闲状态。这种自动转移到空闲状态的行为确保了当多个IL客户端等待同一资源时,一旦资源可用,IL客户端就可以立即恢复或启动流。如果组件只是自动转移到加载状态,那么另一个IL客户端可能会先抢占该资源。
这些行为规则仅旨在涵盖IL客户端与IL组件之间的交互。
2.1.17.5 特定于硬件供应商的资源管理器
为实现上述行为规则,可能存在特定于硬件供应商的资源管理器,并执行以下功能:
• 实现和管理等待队列。
• 跟踪可用资源。
• 跟踪每个拥有资源的组件以及它们正在使用哪些资源。
• 当更高优先级的组件请求资源时,通知一个或多个组件需要放弃其资源。
• 当资源可用时,通知正在等待该资源的最高优先级组件。
组件与特定于硬件供应商的资源管理器之间的实际交互是供应商特定的,且不在本文档的讨论范围之内。第3节提供了与优先级和资源管理相关的参数结构和用例的更多详细信息。
2.1.17.6 组件挂起
当组件缺乏足够资源来处理数据时,它可能会选择挂起自身,以实现更优化的动态资源管理。组件挂起解决了两个用例:
- 组件丢失了关键资源,且该资源丢失可能是暂时性的。
- 关键资源的动态分配失败。
在没有挂起能力的情况下,组件对抢占和资源丢失的唯一可能反应是通过过渡到空闲状态然后加载状态来取消初始化。这种取消初始化会导致数据流的状态丢失,因为缓冲区必须返回给其分配器。挂起允许组件保留其状态,以便在延迟一段时间后,在挂起点恢复处理。
挂起是组件在空闲或暂停状态下的一种属性。具体而言,当组件丢失了一个或多个阻止其处理数据的资源时,它会被“挂起”。这意味着组件不能同时被挂起并处于执行状态(因为“执行”意味着组件将在数据可用时处理或输出数据)。因此,组件可以在通常持有一些资源但不寻求处理数据的任何时候被挂起,即在空闲或暂停状态下。
组件挂起不需要新的组件状态,但增加了一个新的由组件发起的状态转换,即从执行状态转换到暂停状态,这是执行状态的组件在挂起时对自身执行的操作。IL客户端可以对挂起的组件执行任何正常的状态转换,但以下情况除外:客户端无法将挂起的组件转换为执行状态。任何尝试这样做的操作都会失败并返回OMX_ErrorComponentSuspended错误。
2.1.18 内容管道
IL组件可以利用内容管道来从源或目标同步拉取或推送内容(例如文件流),从而抽象化源或目标平台的实现细节(例如本地文件、远程文件、广播等)。内容管道是一个对象,它通过实现内容管道结构中定义的数据访问抽象接口来提供内容访问。
内容管道接口包括用于常规内容操作的功能,包括:
• 打开、关闭和创建内容
• 在内容中定位到特定位置
• 获取内容中的位置
• 从当前位置读取数据
• 向当前位置写入数据
此外,这个内容管道接口还包括用于适应可能异步向远程位置流式传输数据或从远程位置流式传输数据的内容管道实现的功能。在这种情况下,管道可能不会立即准备好提供数据(在读取时)或接受数据(在写入时)。此外,此类管道可能维护自己的数据缓存。这些功能支持:
• 检查管道中是否有可用的字节(无论是传入还是传出),以验证管道客户端是否可以执行后续的读取或写入操作。
• 通过管道提供的数据缓冲区读取或写入数据,以避免在管道缓冲区和客户端缓冲区之间进行不必要的内存复制。
利用内容管道的组件(例如容器分离器或多路复用器)通过OMX_GetContentPipe函数从IL Core获取管道。或者,IL客户端可以通过OMX_IndexParamCustomContentPipe配置提供自定义内容管道(例如,如果客户端自己实现了内容管道)。IL客户端通过OMX_IndexParamContentURI参数将目标内容指定为URI。
2.1.20 视频解码器错误映射
视频解码器组件具有在解码流时通知IL客户端遇到的任何宏块(MB)错误的能力。客户端可以通过一个专用参数随时查询组件,以获取其遇到的MB错误的映射图。
此功能的一个潜在用途是视频电话用例,其中连接一端的视频终端为远程视频终端生成编码的比特流。在传输过程中,编码的比特流可能会损坏,从而导致远程终端在接收和解码时出现MB错误。一个能够与两端通信的应用程序可以在解码终端提取MB错误映射图,并将其传输到编码终端,允许其在后续编码帧中使用内部宏块刷新错误的宏块。
2.1.21 缓冲区负载附加信息
根据缓冲区负载类型和组件要求,可能需要将额外的支持信息附加到缓冲区的末尾,以便在下一个组件中进一步处理缓冲区负载内容。
例如,视频去块算法需要宏块级别的量化信息,以便对视频内容进行去块处理。
缓冲区负载附加信息的存在应通过缓冲区头结构中的“额外数据”缓冲区标志来标识,该缓冲区头结构在3.1.2.7节——OMX_BUFFERHEADERTYPE中描述。
此额外的缓冲区负载信息适用于缓冲区中的第一个新逻辑单元。因此,如果缓冲区中存在多个逻辑单元,“额外数据”标志适用于在缓冲区中首先出现起始边界的逻辑单元。缓冲区中的后续逻辑单元没有明确的“额外数据”。如果每个逻辑单元都需要明确的“额外数据”,则每个缓冲区中应包含一个或更少的逻辑单元。
2.1.21.1 缓冲区数据格式
当存在额外数据时,数据类型和大小等数据属性由紧随缓冲区负载之后、实际数据之前的相应数据结构来标识。可以将多种类型的额外数据作为一系列块对(支持数据结构和实际数据)附加到正常负载的末尾。为了终止这一系列额外数据部分,应在缓冲区中包含一个进一步的数据结构,以指示这是结束项。有关更多详细信息,请参阅第4.2.33节。
2.2 字节序
OpenMAX IL API数据结构的实现中使用的字节序应遵守IL客户端运行所在平台的字节序。这一要求包括IL客户端使用的接口以及组件之间的接口(例如,仅在两个隧道组件之间执行的函数)。OpenMAX IL实现负责支持此要求时固有的任何字节序转换;对于IL客户端和使用与IL客户端相同字节序的组件而言,任何此类转换都是透明的。
3 OpenMAX 集成层控制API
OpenMAX 集成层API允许集成层客户端控制音频、视频和图像领域的多媒体组件。此外,还包含了一个“其他”领域,以提供额外的功能,如音视频(A/V)同步。OpenMAX 集成层API的用户通常是多媒体框架。在本文档的其余部分中,OpenMAX 集成层API的用户将被称为IL客户端。
OpenMAX 集成层API在一组头文件中定义,具体为:
- OMX_Types.h:OpenMAX IL中使用的数据类型
- OMX_Core.h:OpenMAX IL核心API
- OMX_Component.h:OpenMAX IL组件API
- OMX_Audio.h:OpenMAX IL音频领域数据结构
- OMX_IVCommon.h:OpenMAX IL图像和视频领域共用的结构
- OMX_Video.h:OpenMAX IL视频领域数据结构
- OMX_Image.h:OpenMAX IL图像领域数据结构
- OMX_Other.h:OpenMAX IL其他领域数据结构(包括A/V同步)
- OMX_Index.h:所有OpenMAX IL定义的数据结构的索引
- OMX_ContentPipe.h:内容管道定义
本节描述如何配置OpenMAX IL核心和OpenMAX IL组件以进行操作。
首先,介绍OpenMAX IL数据类型。接下来,描述OpenMAX IL核心的方法。组件实现的方法将在3.2.3节中讨论。最后,3.4节展示了包括组件初始化、正常数据流、数据隧道设置以及存在数据隧道时的数据流在内的几个有意义操作的调用序列。这些序列图旨在描述IL客户端、IL核心和OpenMAX IL组件之间的动态交互。
在记录函数时,对于函数参数使用以下约定:
<param_name> [in]
指定输入参数,该参数由函数调用者设置并由函数实现读取。<param_name> [out]
指定输出参数,该参数由函数实现设置并传回给调用者。当函数返回时,调用者可以读取作为引用传递的参数的新值。-
•
<param_name> [inout]
指定一个输入/输出参数,该参数可以由函数调用者设置。函数实现可以在将其返回给函数调用者之前修改该参数。这种参数分类也可以在OpenMAX IL头文件中找到,其中定义了空宏OMX_IN、OMX_OUT和OMX_INOUT。OMX_IN对应于函数参数
<param_name> [in]
。OMX_OUT对应于函数参数<param_name> [out]
,而OMX_INOUT对应于函数参数<param_name> [inout]
。3.1 OpenMAX IL 类型
3.1.1 枚举
在OMX_Core.h中定义了五个32位整数枚举:
• OMX_ERRORTYPE 是由OpenMAX 集成层API中定义的每个函数返回的(见3.1.1.3节)。
• OMX_COMMANDTYPE 包括IL客户端可以向OpenMAX IL组件发送的所有可能命令(见3.1.1.1节)。
• OMX_EVENTTYPE 包括可以在OpenMAX IL组件内部生成并通过回调函数传递给IL客户端的事件(见3.1.1.4节)。
• OMX_BUFFERSUPPLIERTYPE 包括在隧道端口情况下缓冲区提供者的所有可能性。有关此枚举类型使用的描述,请参见3.1.1.5节。
• OMX_STATETYPE,在3.1.1.2节中描述。
表3-2 描述了每个命令要使用的参数。
表3-2: 命令语法
命令代码 | 参数 |
---|---|
OMX_CommandStateSet | nParam: OMX_STATETYPE - 要转换到的状态(从NULL开始) |
OMX_CommandFlush | nParam: OMX_U32 - 目标端口ID(NULL) |
OMX_CommandPortDisable | nParam: OMX_U32 - 目标端口ID(NULL) |
OMX_CommandPortEnable | nParam: OMX_U32 - 目标端口ID(NULL) |
OMX_CommandMarkBuffer | nParam1: OMX_U32 - 目标端口ID <br> nParam2: OMX_MARKTYPE* - 标记数据和目标组件 |
3.1.1.2 OMX_STATETYPE
OMX_STATETYPE 用于指定组件的状态,如加载(Loaded)、空闲(Idle)、执行(Executing)等。
图3-1 展示了由于IL客户端调用OMX_SendCommand(OMX_StateSet, <state>)而导致的状态转换,其中组件的新状态作为参数传递。
在图3-1中,被“<”和“>”括号包围的转换名称表示该转换不是由IL客户端发送的命令触发的,而是组件内部事件的结果。
OpenMAX 组件状态
- 初始状态:组件刚被创建时的状态。
- 最终状态:组件被销毁时的状态。
- OMX_FreeHandle():释放组件句柄的函数,可能导致组件进入无效状态(OMX_StateInvalid)或由于内部错误。
- OMX_GetHandle():获取组件句柄的函数,成功时组件进入等待资源状态(OMX_StateWaitForResources)。
- OMX_StateWaitForResources:等待必要资源可用的状态。
- OMX_StateLoaded:资源已加载,但组件尚未准备执行。
- OMX_StateIdle:组件已准备好执行,但尚未开始执行。
- OMX_StateExecuting:组件正在执行。
- OMX_StatePause:组件暂停执行。
图3-1. OpenMAX IL 组件状态转换
此图展示了组件在不同状态之间的转换,包括由于资源可用、资源丢失、组件暂停、组件恢复等事件引起的状态变化。
本节描述组件状态。IL客户端通过OMX_SendCommand函数和OMX_CommandStateSet命令来命令组件改变状态。
表3-3 表示OpenMAX IL组件的状态。
字段名称 | 描述 | 静态资源已分配 | 缓冲区位置 |
---|---|---|---|
OMX_StateInvalid | 组件已损坏或遇到无法恢复的错误。 | 不适用 | 不适用 |
OMX_StateLoaded | 组件已被加载,但尚未分配任何资源。 | 不适用 | 不适用 |
OMX_StateIdle | 组件已拥有所有资源,但尚未传输任何缓冲区或开始处理数据。 | 是 | 仅供应商 |
OMX_StateExecuting | 组件正在传输缓冲区并处理数据(如果数据可用)。 | 是 | 供应商或非供应商 |
OMX_StatePause | 组件的数据处理已被暂停,但可以从暂停点恢复。 | 是 | 供应商或非供应商 |
OMX_StateWaitForResources | 组件正在等待某个资源变得可用。 | 不适用 | 不适用 |
表3-3: OpenMAX IL 组件状态
- OMX_StateInvalid:组件因某种原因已损坏或遇到无法恢复的错误。
- OMX_StateLoaded:组件已加载到系统中,但尚未分配任何执行所需的资源。
- OMX_StateIdle:组件已准备好进行数据处理,但尚未开始执行。此时,所有必要的资源都已分配,但还没有开始处理数据或传输数据缓冲区。如果组件是数据的提供者,它将在此状态下准备数据以供将来处理。
- OMX_StateExecuting:组件正在处理数据。它可能正在传输数据缓冲区并对其进行处理(如果数据可用)。此状态下,组件可以是数据的提供者也可以是非提供者。
- OMX_StatePause:组件的数据处理被暂停。可以在某个点暂停处理,并在将来某个时刻从该点恢复处理。此状态下,组件同样可以是数据的提供者或非提供者。
- OMX_StateWaitForResources:组件正在等待某些外部资源变得可用,以便继续执行。在此状态下,组件不会执行任何数据处理或数据传输操作。
3.1.1.2.1 OMX_StateLoaded
组件在通过OMX_GetHandle调用创建后,但在其资源分配之前,处于OMX_StateLoaded状态。在此状态下,IL客户端可以通过OMX_SetParameter修改组件的参数,使用OMX_SetupTunnel在组件的端口上设置数据隧道,或者将组件转换到OMX_StateIdle状态或OMX_StateWaitForResources状态。
如果组件在尝试转换到OMX_StateIdle状态时未能获取其所有静态资源,IL客户端可以选择将当前处于OMX_StateLoaded状态的组件转换到OMX_StateWaitForResources状态。
3.1.1.2.1.1 OMX_StateLoaded到OMX_StateIdle
如果IL客户端请求从OMX_StateLoaded状态转换到OMX_StateIdle状态,组件应在完成转换之前获取其所有静态资源,包括所有已启用端口的缓冲区。组件不会为任何禁用的端口获取缓冲区。此外,在转换完成之前,缓冲区提供者(在未进行隧道传输时始终是IL客户端)应确保非提供者拥有其所有缓冲区。
对于与IL客户端连接的端口,IL客户端可以自行分配缓冲区,然后通过对该端口的OMX_UseBuffer调用将它们传递给端口,或者它可以指示端口通过对该端口的OMX_AllocateBuffer调用来执行分配。对于每个端口,IL客户端应仅使用OMX_UseBuffer或OMX_AllocateBuffer中的一种。
当端口进行隧道传输时,提供者端口要么自行分配缓冲区,要么(如果端口实现缓冲区共享)则重用同一组件上某个端口的缓冲区。然后,隧道传输的提供者端口通过对非提供者端口的OMX_UseBuffer调用来将缓冲区传递给非提供者。
端口上使用的缓冲区数量在其端口定义中指定(请参阅OMX_IndexParamPortDefinition),该数量默认为最小值(在同一结构中指定),但可以在OMX_UseBuffer和OMX_AllocateBuffer调用序列之前通过调用OMX_SetParameter由提供者进行修改。
3.1.1.2.2 OMX_StateIdle
在OMX_StateIdle状态下,组件已准备好使用,这意味着所有必要的静态资源都已正确分配。然而,提供者保留其所有缓冲区,并且没有发生缓冲区交换或处理。因此,如果此状态是从OMX_StateExecuting或OMX_StatePause状态进入的,则组件应将其正在处理的所有缓冲区返回给各自的提供者。IL客户端可以将组件转换到除OMX_StateInvalid和OMX_StateWaitForResources状态之外的任何状态。
3.1.1.2.2.1 OMX_StateIdle到OMX_StateLoaded
在从OMX_StateIdle转换到OMX_StateLoaded时,每个缓冲区提供者应对非提供者端口上的每个缓冲区调用OMX_FreeBuffer。如果提供者分配了缓冲区,则应在调用OMX_FreeBuffer之前释放该缓冲区。如果非提供者端口分配了缓冲区,则应在收到OMX_FreeBuffer调用时释放该缓冲区。此外,非提供者端口在收到OMX_FreeBuffer调用时始终应释放缓冲区头。当所有缓冲区都从组件中移除后,状态转换完成;组件通过回调事件通知发起OMX_SendCommand调用的完成。
3.1.1.2.2.2 OMX_StateIdle到OMX_StateExecuting
当组件被挂起时,此转换是不允许的。如果IL客户端请求从OMX_StateIdle状态转换到OMX_StateExecuting状态,并且组件未被挂起,则组件应开始传输和处理数据。如果组件被挂起时客户端请求此转换,则组件将失败调用并返回OMX_ErrorComponentSuspended错误。对于与IL客户端通信的端口,IL客户端将通过OMX_EmptyThisBuffer和OMX_FillThisBuffer启动缓冲区传输。在隧道端口中,任何同时作为提供者的输入端口应通过OMX_FillThisBuffer将其空缓冲区传输到隧道输出端口。
3.1.1.2.3 OMX_StateExecuting
在此状态下,OpenMAX IL组件正在传输和处理数据缓冲区;因此,组件不能在此状态下被挂起。组件应在其输入端口上接受OMX_EmptyThisBuffer调用,并在其输出端口上接受OMX_FillThisBuffer调用。与IL客户端通信的任何端口都应调用EmptyBufferDone和FillBufferDone回调函数,以分别将空或满的缓冲区返回给IL客户端。任何隧道端口都应在其对应的隧道端口上调用OMX_FillThisBuffer或OMX_EmptyThisBuffer,以分别将空或满的缓冲区返回给其隧道端口。IL客户端可以将处于OMX_StateExecuting状态的组件转换到OMX_StateIdle状态或OMX_StatePause状态。
3.1.1.2.3.1 OMX_StateExecuting到OMX_StateIdle
如果IL客户端请求从OMX_StateExecuting状态转换到OMX_StateIdle状态,则组件应在完成转换之前将所有缓冲区返回给各自的提供者,并接收属于其提供者端口的所有缓冲区。与IL客户端通信的任何端口都应通过EmptyBufferDone和FillBufferDone回调函数返回其持有的任何缓冲区,这些回调函数分别由输入和输出端口使用。任何非提供者端口都应使用OMX_EmptyThisBuffer或OMX_FillThisBuffer将其持有的所有缓冲区返回给其正在与之建立隧道的输入端口或输出端口。同样,任何提供者隧道端口都应等待其所有缓冲区从隧道端口返回。
3.1.1.2.3.2 OMX_StateExecuting到OMX_StatePause
从OMX_StateExecuting状态转换到OMX_StatePause状态的情况有三种:
• 当客户端明确请求转换时
• 当组件丢失执行所需的资源,但如果在以后重新获得资源,则可以从资源丢失点恢复时。在这种情况下,组件应自动执行转换,并发出带有OMX_ErrorResourcesSuspended错误的错误事件。
• 当组件尝试分配动态资源失败时。在这种情况下,组件应自动执行转换,并发出带有OMX_ErrorDynamicResourcesUnavailable错误的错误事件。
3.1.1.2.4 OMX_StatePause
在此状态下,OpenMAX IL组件不传输或处理数据,但缓冲区不一定会返回给其提供者。从OMX_StatePause状态,可以通过转换到OMX_StateExecuting来恢复执行,最好是不丢失数据。然而,如果客户端在组件被挂起时请求此转换,则组件应失败调用并返回OMX_ErrorResourcesSuspended错误。组件仍然可以在其输入端接受数据缓冲区,但这些缓冲区将仅被排队而不进一步处理。IL客户端可以将处于OMX_StatePause状态的组件转换到OMX_StateIdle或OMX_StateExecuting。在从OMX_StatePause到OMX_StateIdle的转换中,组件应以与3.1.1.2.3.1节中描述的OMX_StateExecuting到OMX_StateIdle转换相同的方式将所有缓冲区返回给各自的提供者。
3.1.1.2.5 OMX_StateWaitForResources
在此状态下,组件正在等待其所需的一个或多个资源变得可用。此状态与资源管理相关。假设平台上存在一个或多个特定于硬件的资源管理器来处理可用资源。OpenMAX IL组件和资源管理器之间的交互不在本规范的范围内。
如果处于OMX_StateLoaded状态的组件因缓冲区以外的资源不足而无法进入OMX_StateIdle状态,并且IL客户端希望在所需资源可用时收到通知,则IL客户端可以将组件置于OMX_StateWaitForResources状态。IL客户端可以通过将组件从OMX_StateWaitForResources状态转换到OMX_StateLoaded状态来命令组件停止等待资源。如果处于OMX_StateWaitForResources状态的组件获得了其正在等待的所有资源,则应启动到OMX_StateIdle状态的转换。
3.1.1.2.5.1 OMX_StateWaitForResources到OMX_StateIdle
当组件从OMX_StateWaitForResources状态启动到OMX_StateIdle状态的转换时,它应通过OMX_EventResourcesAcquired事件向IL客户端通知此转换的开始。当IL客户端接收到OMX_EventResourcesAcquired事件时,它应以从OMX_StateLoaded到OMX_StateIdle的转换方式调用OMX_UseBuffer和OMX_AllocateBuffer。同样,组件在获得包括缓冲区在内的所有静态资源之前,无法完成到OMX_StateIdle状态的转换。
3.1.1.2.6 OMX_StateInvalid
在此状态下,组件已发生内部损坏或无法恢复的错误。当检测到此类情况时,组件会自行转换到OMX_StateInvalid状态,并通过生成值为OMX_ErrorInvalidState的OMX_EventError事件来通知IL客户端。当IL客户端接收到指示转换到OMX_StateInvalid的OMX_EventError时,它应释放与该组件关联的所有资源,并最终调用OMX_FreeHandle来释放与组件关联的句柄。
处于OMX_StateInvalid状态的组件应对其上的每个调用都失败,并返回OMX_ErrorInvalidState错误消息,除了OMX_GetState、OMX_FreeBuffer或OMX_ComponentDeinit之外。IL客户端还可以通过OMX_SendCommand显式命令转换到OMX_StateInvalid状态。组件可以在任何状态和OMX_StateInvalid状态之间进行转换。
3.1.2.9.2 EmptyBufferDone
一个组件使用EmptyBufferDone回调函数将从输入端口接收的缓冲区传回给IL客户端。组件设置缓冲区头的nOffset和nFilledLen值,以反映其消耗的缓冲区部分;例如,如果缓冲区被完全消耗,nFilledLen将被设置为等于0x0。
除了促进执行中的组件与IL客户端之间的正常数据流外,组件还在以下情况下使用EmptyBufferDone函数将输入缓冲区返回给IL客户端:
• IL客户端命令从OMX_StateExecuting或OMX_StatePause状态过渡到OMX_StateIdle状态或OMX_StateInvalid状态。
• IL客户端刷新或禁用一个端口。
EmptyBufferDone
调用是一个阻塞调用,它应该在五毫秒内返回。因此,IL客户端可以选择在此调用期间不填充缓冲区,而是将它们排队以便在此调用之外进行处理。
EmptyBufferDone
调用的定义如下:
c复制代码
OMX_ERRORTYPE(* OMX_CALLBACKTYPE::EmptyBufferDone)( |
OMX_OUT OMX_HANDLETYPE hComponent, |
OMX_OUT OMX_PTR pAppData, |
OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) |
参数说明:
hComponent [out]
:调用此函数的组件的句柄。pAppData [out]
:指向IL客户端定义的数据的指针。pBuffer [out]
:指向已消耗或返回的OMX_BUFFERHEADERTYPE
结构的指针。
FillBufferDone
是一个组件用来将从输出端口接收的缓冲区传回给IL客户端的回调函数。组件设置缓冲区头的nOffset
和nFilledLen
以反映其填充的缓冲区部分;例如,如果缓冲区不包含任何数据,则nFilledLen
等于0x0。
除了促进执行中的组件与IL客户端之间的正常数据流外,组件还在以下情况下使用此函数将输出缓冲区返回给IL客户端:
- IL客户端命令从
OMX_StateExecuting
或OMX_StatePause
状态过渡到OMX_StateIdle
或OMX_StateInvalid
状态。 - IL客户端刷新或禁用一个端口。
FillBufferDone
调用是一个阻塞调用,它应该在五毫秒内返回。IL客户端可以选择在此调用期间不清空缓冲区,而是将它们排队以便在此调用之外进行消费。
FillBufferDone
的定义如下:
c复制代码
OMX_ERRORTYPE(* OMX_CALLBACKTYPE::FillBufferDone)( |
OMX_OUT OMX_HANDLETYPE hComponent, |
OMX_OUT OMX_PTR pAppData, |
OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) |
参数说明:
hComponent [out]
:要访问的组件的句柄。此句柄是通过调用GetHandle
函数返回的组件句柄。pAppData [out]
:指向IL客户端定义的数据的指针。pBuffer [out]
:指向已填充或返回的OMX_BUFFERHEADERTYPE
结构的指针。
3.2 OpenMAX IL 核心方法/宏
OpenMAX IL 核心为希望使用OpenMAX IL组件的IL客户端实现了主要接口。为了提高效率,OpenMAX IL 定义了一组OpenMAX IL 核心宏,这些宏与大多数OpenMAX IL 组件方法一一对应。
根据函数的不同,一些宏和方法建议函数在5毫秒或20毫秒内返回。标准机构认为,对于可能不需要缓冲区处理的命令,5毫秒的超时是一个合理的响应时间。标准机构将20毫秒的超时确定为可能需要完成缓冲区处理的命令的合理响应时间;这里的假设是,最长的缓冲区处理时间将少于30毫秒,这对应于每秒30帧的视频。这些超时时间主要是为了通过一致性测试,使组件集成商能够很好地了解组件的响应延迟。
这些宏包括以下内容:
• 获取组件信息(版本、功能)。
• 在初始化时设置/获取组件参数。
• 在运行时设置/获取组件参数。
• 分配/释放缓冲区。
• 将装满数据的缓冲区发送到OpenMAX IL 组件端口。
• 将空缓冲区发送到OpenMAX IL 组件端口。
• 向组件发送命令。
• 获取组件的实际状态。
• 获取对OpenMAX IL 组件专有参数的引用。
OpenMAX IL Core 还实现了以下方法:
• 初始化/去初始化整个OpenMAX IL Core。
• 获取OpenMAX IL 组件句柄。
• 释放OpenMAX IL 组件句柄。
• 在运行时检测平台上所有可用的OpenMAX IL 组件。
• 在OpenMAX IL 组件之间设置数据隧道。
• 获取内容管道。
• 查询已安装的标准组件实现的信息。
当为方法的执行指定时间限制时,这并不是对组件符合标准的硬性限制,但如果未遵守该限制,则应在与组件相关的描述文档中注明。
3.2.1 函数的返回码
表3-9列出了每个函数所有可能的返回错误码。严重错误表示组件无法从中恢复的错误。当发生严重错误时,组件应过渡到OMX_StateInvalid状态。除最后两列外,所有列均对应于从对组件的调用中返回的错误。最右边的两列表示由于内部错误而异步发送的错误。
3.2.2.5 OMX_CommandPortDisable
OMX_CommandPortDisable
命令用于禁用端口。nParam
指定要禁用的端口的索引。如果 nParam
的值为 OMX_ALL
,则组件应禁用所有端口。禁用的端口没有缓冲区,并且不会通过隧道连接到 IL 客户端或其他端口。在从 OMX_StateLoaded
或 OMX_StateWaitForResources
状态转换到 OMX_StateIdle
状态时,禁用的端口不会分配缓冲区。IL 客户端可以通过 OMX_SetParameter
更改禁用端口的参数,或在其上设置隧道,而不管组件的状态如何。因此,OMX_CommandPortDisable
命令与 OMX_CommandPortEnable
命令合作,对于端口的动态重新配置或重新隧道化非常有用。
当端口接收到 OMX_CommandPortDisable
时,它应立即在其端口定义结构中清除 bEnabled
。如果 IL 客户端正在禁用的是非供应商端口,则它应通过 OMX_EmptyThisBuffer/OMX_FillThisBuffer
(如果隧道化)或 EmptyBufferDone/FillBufferDone
(如果未隧道化)将持有的任何缓冲区返回给供应商端口。然后,IL 客户端应等待供应商端口通过 OMX_FreeBuffer
释放缓冲区,然后再完成禁用命令。如果 IL 客户端正在禁用的是已分配缓冲区的供应商端口,则它应等待非供应商端口通过 OMX_EmptyThisBuffer
或 OMX_FillThisBuffer
返回所有缓冲区。然后,IL 客户端应通过 OMX_FreeBuffer
释放缓冲区,然后再完成禁用命令。
对于组件成功禁用的每个端口,组件应发送一个 OMX_EventCmdComplete
事件,其中 nData1
指示 OMX_CommandPortDisable
,nData2
为单个端口索引,即使使用 OMX_ALL
值作为 nParam
导致端口被禁用也是如此。如果禁用操作失败,组件应通过 OMX_EventError
事件通知 IL 客户端错误。
3.2.2.6 OMX_CommandPortEnable
OMX_CommandPortEnable
命令用于启用端口。nParam
指定要启用的端口的索引。如果 nParam
的值为 OMX_ALL
,则组件应启用所有端口。启用的端口应遵守组件状态的所有要求。因此,端口应:
- 如果组件处于
OMX_StateLoaded
状态或OMX_StateWaitForResources
状态,则不分配缓冲区;否则,在所有其他状态下分配缓冲区。 - 在从
OMX_StateLoaded
状态或OMX_WaitForResources
状态转换到OMX_StateIdle
状态时分配缓冲区。 - 在
OMX_StateExecuting
状态下传输缓冲区以促进数据流。 - 在除
OMX_StateLoaded
之外的所有状态下,不允许通过OMX_SetParameter
修改其参数。
OMX_CommandPortEnable
命令与 OMX_CommandPortDisable
命令合作,对于端口的动态重新配置或重新隧道化非常有用。
当端口接收到 OMX_CommandPortEnable
时,它应立即在其端口定义结构中设置 bEnabled
。如果 IL 客户端在除 OMX_StateLoaded
或 OMX_StateWaitForResources
之外的任何状态下启用端口,则该端口应通过从 OMX_StateLoaded
到 OMX_StateIdle
的转换中使用的相同调用序列来分配其缓冲区。如果 IL 客户端在组件处于 OMX_StateExecuting
状态时启用端口,则该端口应开始传输缓冲区。
对于组件成功启用的每个端口,组件应发送一个 OMX_EventCmdComplete
事件,其中 nData1
指示 OMX_CommandPortEnable
,nData2
为单个端口索引,即使使用 OMX_ALL
值作为 nParam
导致启用操作也是如此。如果端口启用操作失败,组件应通过 OMX_EventError
事件通知 IL 客户端错误。
3.2.2.7 OMX_CommandMarkBuffer
OMX_CommandMarkBuffer
命令指示给定端口标记缓冲区。nParam
持有将执行标记的端口的索引。OMX_SendCommand
的 pCmdData
参数指向一个 OMX_MARKTYPE
结构。此结构的 pMarkTargetComponent
字段持有指向在处理标记缓冲区后将发送事件的组件的指针。此结构的 pMarkData
字段持有指向与标记相关联的应用程序特定数据的指针,以便在标记事件发生时唯一地标识该标记给应用程序(称为标记数据)。
当被指示标记缓冲区时,组件将在接收到标记命令后,将其接收到的下一个输入缓冲区标记为标记。例外情况是源组件,它将标记其输出缓冲区队列中下一个添加的缓冲区。对于非源组件,nParam
中的端口索引值持有将标记其下一个缓冲区的输入端口的索引。对于源组件,nParam
中的端口索引值持有将标记其下一个缓冲区的输出端口的索引。
在以下情况下,多个标记可能会竞争同一个缓冲区:
- 组件接收到两个或更多标记命令,且没有中间缓冲区。
- 两个或更多带有标记的输入缓冲区共同贡献到一个输出缓冲区(例如,在混音器中)。
- 组件接收到一个标记命令,但下一个缓冲区已经被标记。
如果多个标记竞争应用于同一个缓冲区,则组件使用接收到的第一个标记来标记缓冲区,并按组件接收它们的顺序将剩余的标记应用于后续缓冲区。如果没有后续缓冲区,组件可能会在一个或多个空缓冲区上发送剩余的标记。
对于组件成功标记缓冲区的每个端口,组件应发送一个 OMX_EventCmdComplete
事件,其中 nData1
指示 OMX_CommandMarkBuffer
,nData2
为单个端口索引。如果标记操作失败,组件应通过 OMX_EventError
事件通知 IL 客户端错误。
缓冲区头包含 pMarkTargetComponent
和 pMarkData
字段,其含义与 OMX_MARKTYPE
中的相同。组件通过将从标记命令中复制的 pMarkTargetComponent
和 pMarkData
字段复制到缓冲区头来标记缓冲区。这两个字段默认值为 NULL(即在缓冲区被标记之前)。组件根据为缓冲区标志和时间戳建立的缓冲区元数据规则,将标记字段从输入缓冲区传播到输出缓冲区。目标组件不传播标记,而是将两个字段都清除为 NULL。
当组件接收到缓冲区时,它会将自己的指针与 pMarkTargetComponent
进行比较。如果指针匹配,组件应在缓冲区离开组件或在不离开组件的情况下完全处理后,立即发送一个包含 pMarkData
作为参数的标记事件。
OMX_MARKTYPE
定义如下:
c复制代码
typedef struct OMX_MARKTYPE { |
OMX_HANDLETYPE hMarkTargetComponent; |
OMX_PTR pMarkData; |
} OMX_MARKTYPE; |
参数描述如下:
hMarkTargetComponent
:指向将在处理标记缓冲区后发送事件的组件的句柄。pMarkData
:指向与标记相关联的应用程序特定数据的指针,用于在标记事件发生时唯一地标识该标记给应用程序。
3.2.2.14 OMX_UseBufferOMX_UseBuffer
宏请求组件使用已由 IL 客户端分配或已由隧道组件提供的缓冲区。OMX_UseBuffer
的实现应分配缓冲区头,用给定的输入参数填充它,并通过 ppBufferHdr
输出参数返回。
在填充缓冲区头结构内的字段时,组件需要正确初始化 pInputPortIndex
和 pOutputPortIndex
。同时,它们还需要使用 pAppPrivate
函数参数来初始化 pAppPrivate
字段。当在输出端口或输入端口上调用时,pAppPrivate
参数也应用于初始化 pInputPortPrivate
或 pOutputPortPrivate
字段。
OMX_UseBuffer
宏应在以下条件下执行:
• 当组件处于 OMX_StateLoaded
状态并已发送状态转换请求到 OMX_StateIdle
时
• 当组件处于 OMX_StateWaitForResources
状态,所需资源已可用,并且组件准备进入 OMX_StateIdle
状态时
• 当组件处于 OMX_StateExecuting
、OMX_StatePause
或 OMX_StateIdle
状态时,在禁用的端口上
这是一个阻塞调用。组件应在 20 毫秒内从该调用返回。
3.2.2.15 OMX_AllocateBufferOMX_AllocateBuffer
宏将请求组件分配一个新的缓冲区和缓冲区头。组件将分配缓冲区和缓冲区头,并返回一个指向缓冲区头的指针。
在填充缓冲区头结构内的字段时,组件需要正确初始化 pInputPortIndex
和 pOutputPortIndex
。同时,它们还需要使用 pAppPrivate
函数参数来初始化 pAppPrivate
字段。当分别在输出端口或输入端口上调用时,pAppPrivate
参数也应用于初始化 pInputPortPrivate
或 pOutputPortPrivate
字段。
这是一个阻塞调用,应在以下条件下执行:
• 当组件处于 OMX_StateLoaded
状态并已发送状态转换请求到 OMX_StateIdle
时。
• 当组件处于 OMX_StateWaitForResources
状态,所需资源已可用,并且组件准备进入 OMX_StateIdle
状态时。
• 当组件处于 OMX_StateExecuting
、OMX_StatePause
或 OMX_StateIdle
状态时,在禁用的端口上。
OMX_AllocateBuffer
宏仅在特定端口上分配缓冲区,以便与 IL 客户端进行通信。此宏不能用于为隧道端口分配缓冲区。在端口配置为隧道之前分配的缓冲区将导致组件对端口的 OMX_SetupTunnel
调用失败。
组件应在此调用后五毫秒内返回。
3.2.2.16 OMX_FreeBufferOMX_FreeBuffer
宏将从组件中释放一个缓冲区和缓冲区头。如果组件仅分配了缓冲区头,则它应仅释放缓冲区头。如果组件同时分配了缓冲区和缓冲区头,则它应同时释放两者。因此,组件应跟踪其分配的哪些缓冲区,以便能够执行相应的释放操作。
此调用应在以下条件下执行:
• 当组件处于 OMX_StateIdle
状态,且 IL 客户端已发送状态转换请求到 OMX_StateLoaded
(例如,在停止组件期间)时。
• 当组件处于 OMX_StateExecuting
、OMX_StatePause
或 OMX_StateIdle
状态时,在禁用的端口上。
此调用可以在任何时间进行,但如果未按上述描述执行,则可能导致端口发送 OMX_ErrorPortUnpopulated
事件错误。
当进行隧道传输以从供应商端口正在与之进行隧道传输的端口释放缓冲区头时,此调用由缓冲区供应商端口发起。
这是一个阻塞调用。组件应在 20 毫秒内从此调用返回。
3.2.2.17 OMX_EmptyThisBufferOMX_EmptyThisBuffer
宏将已填充的缓冲区发送到组件的输入端口。当缓冲区包含数据时,缓冲区头的 nFilledLen
字段的值将不为零。如果缓冲区不包含数据,则 nFilledLen
的值为 0x0
。当组件处于或正在向 OMX_StateExecuting
状态转换,或在 OMX_StatePause
状态时,会调用 OMX_EmptyThisBuffer
宏来传递包含数据的缓冲区。
当端口未进行隧道传输时,发送到 OMX_EmptyThisBuffer
的缓冲区在清空后,将通过 EmptyBufferDone
回调返回给 IL 客户端。
当端口进行隧道传输时,只要组件处于 OMX_StateExecuting
状态,发送到 OMX_EmptyThisBuffer
的缓冲区在清空后将被发送到隧道端口。每当隧道端口被刷新或禁用时,这些缓冲区将使用 OMX_EmptyThisBuffer
返回给提供它们的输入端口。此外,当调用 OMX_FillThisBuffer
的组件从 OMX_StateExecuting
状态或 OMX_StatePaused
状态转换到 OMX_StateIdle
状态时,这些缓冲区也会返回给提供它们的输入端口。
这是一个非阻塞调用,因为组件会将缓冲区排队并立即返回。缓冲区将在适当的时间之后被清空。如果缓冲区头中的 nInputPortIndex
参数未指定有效的输入端口,则组件将返回 OMX_ErrorBadPortIndex
。组件应在此调用后五毫秒内返回。
3.2.2.18 OMX_FillThisBufferOMX_FillThisBuffer
宏会将一个空缓冲区发送到组件的输出端口。当组件处于或正在向 OMX_StateExecuting
状态转换,或处于 OMX_StatePaused
状态时,会调用 OMX_FillThisBuffer
宏来传递不包含数据的缓冲区。
当端口未进行隧道传输时,发送到 OMX_FillThisBuffer
的缓冲区在填充完成后,将通过 FillBufferDone
回调返回给 IL 客户端。
当端口进行隧道传输时,只要组件处于 OMX_StateExecuting
状态,发送到 OMX_FillThisBuffer
的缓冲区在填充完成后将被发送到隧道端口。每当隧道端口被刷新或禁用时,这些缓冲区将使用 OMX_FillThisBuffer
返回给提供它们的输出端口。此外,当调用 OMX_FillThisBuffer
的组件从 OMX_StateExecuting
状态或 OMX_StatePaused
状态转换到 OMX_StateIdle
状态时,这些缓冲区也会返回给提供它们的输出端口。
这是一个非阻塞调用,因为组件会将缓冲区排队并立即返回。缓冲区将在适当的时间之后被填充。如果缓冲区头中的 nOutputPortIndex
参数未指定有效的输出端口,则组件将返回 OMX_ErrorBadPortIndex
。组件应在此调用后五毫秒内返回。
3.2.2.19 OMX_UseEGLImageOMX_UseEGLImage
允许 OMX IL 组件使用通过 EGL 分配的图像作为缓冲区。EGLImage 旨在在基于 EGL 的渲染接口(如 OpenGL ES 和 OpenVG)之间共享数据。EGLImage 的格式对 EGL 客户端来说是不透明的,因此通过此宏分配的任何内存都不能直接被 IL 客户端访问。
组件应提供此接口的方法,但可以选择不实现它,通过返回 OMX_ErrorNotImplemented
来表示。组件应检查提供给方法的 EGLImage,并确定 EGLImage 是否与端口配置兼容。
OMX_UseEGLImage
宏请求组件使用 EGL 提供的 EGLImage,而不是使用 OMX_UseBuffer
方法。OMX_UseEGLImage
的实现应分配缓冲区头,用给定的输入参数填充它,并通过 ppBufferHdr
输出参数传回。pBufferHdr
参数的 pBuffer
字段应为 0x0
,因为 EGLImage 的格式对 IL 客户端来说是不透明的。OMX_UseEGLImage
宏应在以下条件下执行:
• 当组件处于 OMX_StateLoaded
状态,并已发送状态转换请求到 OMX_StateIdle
时。
• 当组件处于 OMX_StateWaitForResources
状态,所需资源可用,且组件准备进入 OMX_StateIdle
状态时。
• 在组件处于 OMX_StateExecuting
、OMX_StatePause
或 OMX_StateIdle
状态时,对禁用的端口进行操作。
这是一个阻塞调用。组件应在此调用后 20 毫秒内返回。