多媒体隧道

news2024/11/22 21:13:49

多媒体隧道使压缩的视频数据能够通过硬件视频解码器直接传送到显示器,而无需通过应用程序代码或 Android 框架代码进行处理。 Android 堆栈下方的设备特定代码通过将视频帧呈现时间戳与以下类型的内部时钟之一进行比较来确定将哪些视频帧发送到显示器以及何时发送它们:

  • 对于 Android 5 或更高版本中的点播视频播放, AudioTrack时钟与应用传入的音频演示时间戳同步

  • 对于 Android 11 或更高版本的直播播放,由调谐器驱动的节目参考时钟 (PCR) 或系统时钟 (STC)

背景

Android 上的传统视频播放会在压缩视频帧被解码时通知应用程序。然后,应用程序将解码的视频帧发布到显示器,以与相应的音频帧在相同的系统时钟时间进行渲染,检索历史AudioTimestamps实例以计算正确的时间。

由于隧道式视频播放绕过了应用程序代码并减少了作用于视频的进程数量,因此它可以根据 OEM 实现提供更高效的视频渲染。它还可以通过避免由 Android 请求渲染视频的时间和真正的硬件 vsync 的时间之间的潜在偏差引入的时间问题,为所选时钟(PRC、STC 或音频)提供更准确的视频节奏和同步。但是,隧道也可以减少对 GPU 效果的支持,例如画中画 (PiP) 窗口中的模糊或圆角,因为缓冲区绕过 Android 图形堆栈。

下图显示了隧道如何简化视频播放过程。

传统模式与隧道模式的比较

图 1.传统和隧道视频播放过程的比较

对于应用程序开发人员

由于大多数应用程序开发人员都集成了用于播放实现的库,因此在大多数情况下,实施只需要重新配置该库以进行隧道播放。对于隧道视频播放器的低级实现,请使用以下说明。

对于 Android 5 或更高版本中的点播视频播放:

  1. 创建一个SurfaceView实例。

  2. 创建一个audioSessionId实例。

  3. 使用步骤 2 中创建的audioSessionId实例创建AudioTrackMediaCodec实例。

  4. 使用音频数据中第一个音频帧的呈现时间戳将音频数据排队到AudioTrack 。

对于 Android 11 或更高版本的直播播放:

  1. 创建一个SurfaceView实例。

  2. Tuner获取一个avSyncHwId实例。

  3. 使用在步骤 2 中创建的avSyncHwId实例创建AudioTrackMediaCodec实例。

API 调用流程显示在以下代码片段中:

aab.setContentType(AudioAttributes.CONTENT_TYPE_MOVIE);

// configure for audio clock sync
aab.setFlag(AudioAttributes.FLAG_HW_AV_SYNC);
// or, for tuner clock sync (Android 11 or higher)
new tunerConfig = TunerConfiguration(0, avSyncId);
aab.setTunerConfiguration(tunerConfig);

if (codecName == null) {
  return FAILURE;
}

// configure for audio clock sync
mf.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, audioSessionId);
// or, for tuner clock sync (Android 11 or higher)
mf.setInteger(MediaFormat.KEY_HARDWARE_AV_SYNC_ID, avSyncId);

点播视频播放的行为

因为隧道式点播视频播放隐式绑定到AudioTrack播放,所以隧道式视频播放的行为可能取决于音频播放的行为。

  • 在大多数设备上,默认情况下,在音频播放开始之前不会渲染视频帧。但是,应用程序可能需要在开始音频播放之前渲染视频帧,例如,在搜索时向用户显示当前视频位置。

    • 要表示第一个排队的视频帧应在解码后立即渲染,请将PARAMETER_KEY_TUNNEL_PEEK参数设置为1 。当压缩视频帧在队列中重新排序时(例如存在B 帧时),这意味着第一个显示的视频帧应该始终是 I 帧。

    • 如果您不希望在音频播放开始之前渲染第一个排队的视频帧,请将此参数设置为0 。

    • 如果未设置此参数,OEM 将确定设备的行为。

  • 当音频数据未提供给AudioTrack并且缓冲区为空(音频欠载)时,视频播放会停止,直到写入更多音频数据,因为音频时钟不再前进。

  • 在播放期间,应用程序无法纠正的不连续性可能会出现在音频演示时间戳中。发生这种情况时,OEM 会通过停止当前视频帧来纠正负间隙,并通过丢弃视频帧或插入静音音频帧(取决于 OEM 实施)来纠正正间隙。对于插入的无声音频帧, AudioTimestamp帧位置不会增加。

对于设备制造商

配置

OEM 应该创建一个单独的视频解码器来支持隧道视频播放。此解码器应在media_codecs.xml文件中宣传它能够进行隧道播放:

<Feature name="tunneled-playback" required="true"/>

当隧道MediaCodec实例配置了音频会话 ID 时,它会向AudioFlinger查询此HW_AV_SYNC ID:

if (entry.getKey().equals(MediaFormat.KEY_AUDIO_SESSION_ID)) {
    int sessionId = 0;
    try {
        sessionId = (Integer)entry.getValue();
    }
    catch (Exception e) {
        throw new IllegalArgumentException("Wrong Session ID Parameter!");
    }
    keys[i] = "audio-hw-sync";
    values[i] = AudioSystem.getAudioHwSyncForSession(sessionId);
}

在此查询期间, AudioFlinger从主音频设备检索HW_AV_SYNC ID,并在内部将其与音频会话 ID 关联:

audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
char *reply = dev->get_parameters(dev, AUDIO_PARAMETER_HW_AV_SYNC);
AudioParameter param = AudioParameter(String8(reply));
int hwAVSyncId;
param.getInt(String8(AUDIO_PARAMETER_HW_AV_SYNC), hwAVSyncId);

如果已经创建了AudioTrack实例,则会将HW_AV_SYNC ID 传递给具有相同音频会话 ID 的输出流。如果尚未创建,则在AudioTrack创建期间将HW_AV_SYNC ID 传递给输出流。这是由播放线程完成的:

mOutput->stream->common.set_parameters(&mOutput->stream->common, AUDIO_PARAMETER_STREAM_HW_AV_SYNC, hwAVSyncId);

HW_AV_SYNC ID,无论是对应于音频输出流还是Tuner配置,都被传递到 OMX 或 Codec2 组件中,以便 OEM 代码可以将编解码器与相应的音频输出流或 Tuner 流相关联。

在组件配置期间,OMX 或 Codec2 组件应返回一个边带句柄,该句柄可用于将编解码器与 Hardware Composer (HWC) 层相关联。当应用程序将表面与MediaCodec相关联时,此边带句柄通过SurfaceFlinger向下传递给 HWC,它将层配置为边带层。

err = native_window_set_sideband_stream(nativeWindow.get(), sidebandHandle);
if (err != OK) {
  ALOGE("native_window_set_sideband_stream(%p) failed! (err %d).", sidebandHandle, err);
  return err;
}

HWC 负责在适当的时间从编解码器输出接收新的图像缓冲区,同步到相关的音频输出流或调谐器程序参考时钟,将缓冲区与其他层的当前内容合成,并显示结果图像。这与正常的准备和设置周期无关。只有当其他层发生变化或边带层的属性(例如位置或大小)发生变化时,才会调用准备和设置。

OMX

隧道解码器组件应支持以下内容:

  • 设置OMX.google.android.index.configureVideoTunnelMode扩展参数,该参数使用ConfigureVideoTunnelModeParams结构传入与音频输出设备关联的HW_AV_SYNC ID。

  • 配置OMX_IndexConfigAndroidTunnelPeek参数,该参数告诉编解码器渲染或不渲染第一个解码的视频帧,无论音频播放是否已开始。

  • 当第一个隧道视频帧已解码并准备好呈现时,发送OMX_EventOnFirstTunnelFrameReady事件。

AOSP 实现通过OMXNodeInstance在ACodec中配置隧道模式,如下代码片段所示:

OMX_INDEXTYPE index;
OMX_STRING name = const_cast<OMX_STRING>(
        "OMX.google.android.index.configureVideoTunnelMode");

OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);

ConfigureVideoTunnelModeParams tunnelParams;
InitOMXParams(&tunnelParams);
tunnelParams.nPortIndex = portIndex;
tunnelParams.bTunneled = tunneled;
tunnelParams.nAudioHwSync = audioHwSync;
err = OMX_SetParameter(mHandle, index, &tunnelParams);
err = OMX_GetParameter(mHandle, index, &tunnelParams);
sidebandHandle = (native_handle_t*)tunnelParams.pSidebandWindow;

如果组件支持这种配置,它应该为这个编解码器分配一个边带句柄,并通过pSidebandWindow成员将它传回,以便 HWC 可以识别关联的编解码器。如果组件不支持此配置,则应将bTunneled设置为OMX_FALSE 。

编解码器2

在 Android 11 或更高版本中, Codec2支持隧道播放。解码器组件应支持以下内容:

  • 配置C2PortTunneledModeTuning ,它配置隧道模式并传入从音频输出设备或调谐器配置检索到的HW_AV_SYNC 。

  • 查询C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE ,为 HWC 分配和检索边带句柄。

  • 在连接到C2_PARAMKEY_TUNNEL_HOLD_RENDER时处理C2Work ,它指示编解码器解码并发出工作完成信号,但在 1) 编解码器稍后被指示渲染它或 2) 音频播放开始之前不渲染输出缓冲区。

  • 处理C2_PARAMKEY_TUNNEL_START_RENDER ,它指示编解码器立即渲染标有C2_PARAMKEY_TUNNEL_HOLD_RENDER的帧,即使音频播放尚未开始。

  • 保留debug.stagefright.ccodec_delayed_params未配置(推荐)。如果您确实配置它,请设置为false 。

    注意:不要将debug.stagefright.ccodec_delayed_params设置为true ,因为这会导致延迟将C2_PARAMKEY_TUNNEL_START_RENDER发送到编解码器。

AOSP 实现通过C2PortTunnelModeTuning在CCodec中配置隧道模式,如下代码片段所示:

if (msg->findInt32("audio-hw-sync", &tunneledPlayback->m.syncId[0])) {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::AUDIO_HW_SYNC;
} else if (msg->findInt32("hw-av-sync-id", &tunneledPlayback->m.syncId[0])) {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::HW_AV_SYNC;
} else {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::REALTIME;
    tunneledPlayback->setFlexCount(0);
}
c2_status_t c2err = comp->config({ tunneledPlayback.get() }, C2_MAY_BLOCK,
        failures);
std::vector<std::unique_ptr<C2Param>> params;
c2err = comp->query({}, {C2PortTunnelHandleTuning::output::PARAM_TYPE},
        C2_DONT_BLOCK, &params);
if (c2err == C2_OK && params.size() == 1u) {
    C2PortTunnelHandleTuning::output *videoTunnelSideband =
            C2PortTunnelHandleTuning::output::From(params[0].get());
    return OK;
}

如果组件支持这种配置,它应该为这个编解码器分配一个边带句柄,并通过C2PortTunnelHandlingTuning将其传回,以便 HWC 可以识别关联的编解码器。

音频 HAL

对于点播视频播放,音频 HAL 接收与大端格式的音频数据内联的音频演示时间戳,该时间戳位于应用程序写入的每个音频数据块开头的标头内:

struct TunnelModeSyncHeader {
  // The 32-bit data to identify the sync header (0x55550002)
  int32 syncWord;
  // The size of the audio data following the sync header before the next sync
  // header might be found.
  int32 sizeInBytes;
  // The presentation timestamp of the first audio sample following the sync
  // header.
  int64 presentationTimestamp;
  // The number of bytes to skip after the beginning of the sync header to find the
  // first audio sample (20 bytes for compressed audio, or larger for PCM, aligned
  // to the channel count and sample size).
  int32 offset;
}

为了让 HWC 与相应的音频帧同步渲染视频帧,音频 HAL 应解析同步标头并使用演示时间戳将播放时钟与音频渲染重新同步。要在播放压缩音频时重新同步,音频 HAL 可能需要解析压缩音频数据中的元数据以确定其播放持续时间。

暂停支持

Android 5 或更低版本不包含暂停支持。您只能通过 A/V 饥饿来暂停隧道播放,但如果视频的内部缓冲区很大(例如,OMX 组件中有一秒钟的数据),它会使暂停看起来没有响应。

在 Android 5.1 或更高版本中, AudioFlinger支持直接(隧道)音频输出的暂停和恢复。如果 HAL 实现了暂停和恢复,则将跟踪暂停和恢复转发给 HAL。

通过在播放线程中执行 HAL 调用(与卸载相同)来遵守暂停、刷新、恢复调用顺序。

实施建议

音频 HAL

对于 Android 11,来自 PCR 或 STC 的 HW 同步 ID 可用于 A/V 同步,因此支持仅视频流。

对于 Android 10 或更低版本,支持隧道视频播放的设备应在其audio_policy.conf文件中至少具有一个带有FLAG_HW_AV_SYNCAUDIO_OUTPUT_FLAG_DIRECT标志的音频输出流配置文件。这些标志用于从音频时钟设置系统时钟。

OMX

设备制造商应该有一个单独的 OMX 组件用于隧道视频播放(制造商可以有额外的 OMX 组件用于其他类型的音频和视频播放,例如安全播放)。隧道组件应该:

  • 在其输出端口上指定 0 个缓冲区( nBufferCountMin 、 nBufferCountActual )。

  • 实现OMX.google.android.index.prepareForAdaptivePlayback setParameter扩展。

  • media_codecs.xml文件中指定其功能并声明隧道播放功能。它还应阐明对帧大小、对齐方式或比特率的任何限制。一个例子如下所示:

    <MediaCodec name="OMX.OEM_NAME.VIDEO.DECODER.AVC.tunneled"
    type="video/avc" >
        <Feature name="adaptive-playback" />
        <Feature name="tunneled-playback" required=”true” />
        <Limit name="size" min="32x32" max="3840x2160" />
        <Limit name="alignment" value="2x2" />
        <Limit name="bitrate" range="1-20000000" />
            ...
    </MediaCodec>
    

如果使用相同的 OMX 组件来支持隧道和非隧道解码,它应该将隧道播放功能保留为非必需。隧道和非隧道解码器都具有相同的能力限制。一个例子如下所示:

<MediaCodec name="OMX._OEM\_NAME_.VIDEO.DECODER.AVC" type="video/avc" >
    <Feature name="adaptive-playback" />
    <Feature name="tunneled-playback" />
    <Limit name="size" min="32x32" max="3840x2160" />
    <Limit name="alignment" value="2x2" />
    <Limit name="bitrate" range="1-20000000" />
        ...
</MediaCodec>

硬件作曲家 (HWC)

当显示器上有一个隧道层(具有HWC_SIDEBAND compositionType的层)时,该层的sidebandStream是 OMX 视频组件分配的边带句柄。

HWC 将解码的视频帧(来自隧道 OMX 组件)同步到关联的音轨(使用audio-hw-sync ID)。当一个新的视频帧变为当前时,HWC 将它与在最后一次准备或设置调用期间接收到的所有层的当前内容合成,并显示结果图像。只有当其他层发生变化或边带层的属性(例如位置或大小)发生变化时,才会调用准备或设置。

下图表示 HWC 与硬件(或内核或驱动程序)同步器一起工作,以基于音频 (7c) 将视频帧 (7b) 与最新组合 (7a) 组合以在正确的时间显示。

HWC 基于音频组合视频帧

图 2. HWC 硬件(或内核或驱动程序)同步器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1019608.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

郑州大学图书馆许少辉《乡村振兴战略下传统村落文化旅游设计》中文文献——2023学生开学季辉少许

郑州大学图书馆许少辉《乡村振兴战略下传统村落文化旅游设计》中文文献——2023学生开学季辉少许

MySQL 几种导数据的方法与遇到的问题

零、说在前面 MySQL导数据通常使用第三方工具和MySQL自身的工具&#xff0c;本文分别就这两类方法分别介绍。 一、第三方工具之 Navicat 1.1、Navicat的“数据传输”工具 打开Navicat&#xff0c;点击“工具”标签&#xff0c;找到“数据传输”&#xff0c;即可看到操作界面。…

SQL Server 日期范围按每月一行拆分

要将 SQL Server 中的日期范围按每月一行拆分&#xff0c;可以使用一个表值函数&#xff08;Table-Valued Function&#xff09;来生成日期范围内的月份&#xff0c;并将其与其他数据连接&#xff0c;以创建包含每月一行的结果集。 以下是一个示例&#xff0c;说明如何实现这一…

怎么用外网访问自己的网站?快解析内网端口映射来实现

想要访问服务器上的网站需要直接或间接访问服务器IP地址&#xff0c;但是如果服务器没有公网IP地址&#xff0c;那么就需要借助外网进行访问。当我们需要远程访问内网的Web服务器时&#xff0c;我们需要使用一些技术来实现此目的。这就需要通过使用类似快解析内网端口映射方式进…

使用HTTP爬虫ip中的常见误区与解决方法

在使用HTTP爬虫进行网页抓取时&#xff0c;涉及到IP地址的处理&#xff0c;可能会存在一些常见的误区。以下是一些常见误区及解决方法&#xff1a; 1.使用个人IP进行大规模爬取&#xff1a;如果你使用个人住宅IP进行大规模爬取&#xff0c;可能会被目标网站视为恶意攻击&#x…

解决Pycharm使用Conda激活环境失败的问题

Q:公司电脑终端使用powershell来激活conda环境时报错? 同时手动打开powershell报"profile.ps1” 无法被加载的错误 A: 1,手动打开powershell&#xff0c;设置管理员打开 2,打开powershell 打开 PowerShell 终端&#xff0c;并输入以下命令&#xff1a;Get-ExecutionPo…

Spring AOP以及统一处理

一.Spring AOP 1.什么是Spring AOP AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff1a;面向切面编程&#xff0c;它是一种思想&#xff0c;它是对某一类事情的集中处理。 2.AOP的作用 想象一个场景&#xff0c;我们在做后台系统时&#xff0c;除了登录…

Java基于SpringBoot的漫画网站,附源码,教程

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 今天为大家带来的是 基于SpringBootVue的漫画之家系统&#xff0c;亲测可用&#xff0c;可以作为课程设计&…

搜索关键词标红组件

搜索关键词标红功能 今年做的一个需求&#xff0c;先看效果图。 先讲一下实现方式&#xff0c;前端输入关键词查询&#xff0c;后端返回html模板&#xff0c;前端通过v-html渲染 查到的数据是分页式&#xff0c;v-html的样式需要使用/deep/声明 下面是组件代码 <template…

现货黄金与黄金一样吗?

在众多的黄金投资方式中&#xff0c;现货黄金可以说是集各家所长于一身的一种&#xff0c;它既承载了实物黄金的特性&#xff0c;能够发挥黄金抗通胀、避风险的重要作用&#xff0c;也拥有纸黄金无需交收的优点&#xff0c;更具有黄金期货可以双向交易、带有资金杠杆的高收益特…

python学习之【包和内置模块】

前言 接上篇文章 python学习之【模块】&#xff0c;这篇文章接着学习python中的包。 python中的包 包是一种用“点式模块名”构造Python模块命名空间的方法。在包中存放着一些功能相近的模块。 包的创建和导入 包的创建 我们可以在pytarm中创建一个package文件&#xff…

【Leetcode热题】打卡day1——10

目录 1、两数之和 - 哈希表 1、两数之和 - 哈希表 1. 两数之和 思路&#xff1a; 建立map&#xff0c;mp[nums[i]]i 存储值所对应的下标 顺序遍历每一个元素&#xff0c;先查找mp中是否存在与nums[i]匹配的值&#xff08;target-nums[i]&#xff09; 如果存在&#xff0c;则返…

如何使用Vcluster实现Kubernetes中的多租户

Kubernetes彻底改变了组织部署和管理容器化应用程序的方式&#xff0c;使跨集群编排和扩展应用程序变得更加容易。然而&#xff0c;在共享的Kubernetes集群上运行多个异构工作负载会带来资源争用、安全风险、缺乏定制和复杂管理等挑战。 以下几种方法可以在Kubernetes中实现隔离…

《golang设计模式》第二部分·结构型模式-05-门面模式Facade)

文章目录 1. 概述1.1 角色1.2 类图 2. 代码示例2.1 设计2.2 代码2.2 类图 1. 概述 门面&#xff08;Facade&#xff09;向客户端提供使用子系统的统一接口&#xff0c;用于简化客户端使用子系统的操作。 1.1 角色 门面角色&#xff08;Facade&#xff09; 客户端可以调用的接…

数字孪生和元宇宙:打造未来的数字边界

数字孪生和元宇宙是近两年来被热议的两个概念&#xff0c;但由于技术的交叉两者也极易被混淆。本文希望带大家深入探讨一下这两者之间的关系&#xff0c;以及它们如何一起构建了数字时代的新格局。 1. 数字孪生的本质 数字孪生是一种虚拟模型&#xff0c;它通过数字手段对现实…

在微信公众号怎么实现投票活动

微信公众号实现投票活动的方法和步骤 一、投票活动的优势 通过投票活动&#xff0c;微信公众号可以实现用户参与、增加互动、了解用户需求等功能&#xff0c;同时也可以提升品牌知名度和用户粘性。以下是一些投票活动的优势&#xff1a; 增加用户参与度&#xff1a;通过投票活…

Seata四种事务模式AT、TCC、SAGA 、 XA详解

文章目录 一、 GlobalTransactional一、AT模式1、原理2 、优缺点3、实现 二、TCC模式1、原理2 、优缺点3、实现 三、SAGA模式1 、原理2 、优缺点3 、实现 四、XA模式1 、原理2 、优缺点3、 实现 五、 四种模式对比 我们知道Seata是一款开源的分布式事务解决方案&#xff0c;致力…

【LeetCode75】第五十八题 组合总和Ⅲ

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目让我们找出长度为k并且总和为n的所有组合&#xff0c;我们能用的元素只有1~9。 那么这种要求排列组合的题&#xff0c;基本是是离不…

2023客服管理者面临的挑战

客服管理者在当今的数字化时代也面临着许多挑战。以下是一些主要的挑战&#xff1a; 同行业竞争加剧&#xff1a;客服行业面临着来自同行业的竞争压力。为了获得竞争优势&#xff0c;企业需要不断提高自身的产品和服务质量&#xff0c;同时还需要不断降低成本、提高效率。然而…

go调用so库

来源&#xff1a;微信公众号「编程学习基地」 文章目录 go调用so库生成so库调用so库 go调用so库 生成so库 load_so.h #ifndef _LOAD_SO_H #define _LOAD_SO_Hint do_test_so_func(int a,int b);#endif load_so.c #include "load_so.h"int do_test_so_func(int …