Tuner 框架

对于 Android 11 或更高版本,您可以使用 Android Tuner 框架来传输音视频内容。该框架使用供应商提供的硬件管道,使其既适用于低端 SoC,也适用于高端 SoC。该框架提供了一种安全的方式来传输受可信执行环境 (TEE) 和安全媒体路径 (SMP) 保护的音视频内容,使其可用于高度受限的内容保护环境。

Tuner 和 Android CAS 之间的标准化接口加快了 Tuner 供应商和 CAS 供应商之间的集成速度。Tuner 接口与 MediaCodecAudioTrack 协同工作,为 Android TV 构建全面的解决方案。Tuner 接口支持基于主要广播标准的数字电视和模拟电视。

组件

对于 Android 11,有三个组件是专门为 TV 平台设计的。

  • Tuner HAL:框架和供应商之间的接口
  • Tuner SDK API:框架和应用之间的接口
  • Tuner Resource Manager (TRM):协调 Tuner 硬件资源

对于 Android 11,以下组件得到了增强。

  • CAS V2
  • TvInputService 或 TV 输入服务 (TIS)
  • TvInputManagerService 或 TV 输入管理器服务 (TIMS)
  • MediaCodec 或媒体编解码器
  • AudioTrack 或音轨
  • MediaResourceManager 或媒体资源管理器 (MRM)

Flow diagram of Tuner framework components.

图 1. Android TV 组件之间的互动

功能

前端支持以下 DTV 标准。

  • ATSC
  • ATSC3
  • DVB C/S/T
  • ISDB S/S3/T
  • 模拟

Android 12 中具有 Tuner HAL 1.1 或更高版本的前端支持以下 DTV 标准。

  • DTMB

解复用器支持以下数据流协议。

  • 传输流 (TS)
  • MPEG 媒体传输协议 (MMTP)
  • 互联网协议 (IP)
  • 类型长度值 (TLV)
  • ATSC 链路层协议 (ALP)

解扰器支持以下内容保护。

  • 安全媒体路径
  • 明文媒体路径
  • 安全本地录制
  • 安全本地播放

Tuner API 支持以下用例。

  • 扫描
  • 直播
  • 播放
  • 录制

Tuner、MediaCodecAudioTrack 支持以下数据流模式。

  • 带有明文内存缓冲区的 ES 载荷
  • 带有安全内存句柄的 ES 载荷
  • 直通

总体设计

Tuner HAL 在 Android 框架和供应商硬件之间定义。

  • 描述了框架对供应商的期望以及供应商可能执行的操作。
  • 通过 IFrontendIDemuxIDescramblerIFilterIDvrILnb 接口将前端、解复用器和解扰器的功能导出到框架。
  • 包括将 Tuner HAL 与其他框架组件(例如 MediaCodecAudioTrack)集成在一起的功能。

创建 Tuner Java 类和原生类。

  • Tuner Java API 允许应用通过公共 API 访问 Tuner HAL。
  • 原生类允许权限控制以及处理 Tuner HAL 的大量录制或播放数据。
  • 原生 Tuner 模块是 Tuner Java 类和 Tuner HAL 之间的桥梁。

创建 TRM 类。

  • 管理有限的 Tuner 资源,例如前端、LNB、CAS 会话和来自 TV 输入 HAL 的 TV 输入设备。
  • 应用规则以从应用回收不足的资源。默认规则是前台应用胜出。

Media CAS 和 CAS HAL 通过以下功能得到增强。

  • 为不同的用途和算法打开 CAS 会话。
  • 支持动态 CAS 系统,例如 CICAM 的移除和插入。
  • 通过提供密钥令牌与 Tuner HAL 集成。

MediaCodecAudioTrack 通过以下功能得到增强。

  • 将安全音视频内存作为内容输入。
  • 配置为在隧道式播放中执行硬件音视频同步。
  • 配置为支持 ES_payload 和直通模式。

Overall design of the Tuner HAL.

图 2. Tuner HAL 内的组件图

总体工作流程

下图说明了直播播放的调用序列。

设置

Setup sequence of live broadcast playback diagram.

图 3. 直播播放的设置序列

处理音视频

Handling A/V for live broadcast playback diagram.

图 4. 直播播放的音视频处理

处理加扰内容

Handling scrambled content for live broadcast playback diagram.

图 5. 直播播放的加扰内容处理

处理音视频数据

Process A/V data for live broadcast playback diagram.

图 6. 直播播放的音视频处理

Tuner SDK API

Tuner SDK API 处理与 Tuner JNI、Tuner HAL 和 TunerResourceManager 的互动。TIS 应用使用 Tuner SDK API 访问 Tuner 资源和子组件,例如过滤器和解扰器。前端和解复用器是内部组件。

Flow diagram of the Tuner SDK API.

图 7. 与 Tuner SDK API 的互动

版本

从 Android 12 开始,Tuner SDK API 支持 Tuner HAL 1.1 中的新功能,这是 Tuner 1.0 的向后兼容版本升级。

使用以下 API 检查正在运行的 HAL 版本。

  • android.media.tv.tuner.TunerVersionChecker.getTunerVersion()

最低要求的 HAL 版本可以在新 Android 12 API 的文档中找到。

软件包

Tuner SDK API 提供以下四个软件包。

  • android.media.tv.tuner
  • android.media.tv.tuner.frontend
  • android.media.tv.tuner.filter
  • android.media.tv.tuner.dvr

Flow diagram of the Tuner SDK API packages.

图 8. Tuner SDK API 软件包

Android.media.tv.tuner

Tuner 软件包是使用 Tuner 框架的入口点。TIS 应用使用此软件包通过指定初始设置和回调来初始化和获取资源实例。

  • tuner():通过指定 useCasesessionId 参数来初始化 Tuner 实例。
  • tune():通过指定 FrontendSetting 参数来获取前端资源并进行调谐。
  • openFilter():通过指定过滤器类型来获取过滤器实例。
  • openDvrRecorder():通过指定缓冲区大小来获取录制实例。
  • openDvrPlayback():通过指定缓冲区大小来获取播放实例。
  • openDescrambler():获取解扰器实例。
  • openLnb():获取内部 LNB 实例。
  • openLnbByName():获取外部 LNB 实例。
  • openTimeFilter():获取时间过滤器实例。

Tuner 软件包提供过滤器、DVR 和前端软件包未涵盖的功能。这些功能如下所示。

  • cancelTuning
  • scan / cancelScanning
  • getAvSyncHwId
  • getAvSyncTime
  • connectCiCam1 / disconnectCiCam
  • shareFrontendFromTuner
  • updateResourcePriority
  • setOnTuneEventListener
  • setResourceLostListener

Android.media.tv.tuner.frontend

前端软件包包括前端相关设置、信息、状态、事件和功能的集合。

FrontendSettings 由以下类针对不同的 DTV 标准派生而来。

  • AnalogFrontendSettings
  • Atsc3FrontendSettings
  • AtscFrontendSettings
  • DvbcFrontendSettings
  • DvbsFrontendSettings
  • DvbtFrontendSettings
  • Isdbs3FrontendSettings
  • IsdbsFrontendSettings
  • IsdbtFrontendSettings

从 Android 12 和 Tuner HAL 1.1 或更高版本开始,支持以下 DTV 标准。

  • DtmbFrontendSettings

FrontendCapabilities 由以下类针对不同的 DTV 标准派生而来。

  • AnalogFrontendCapabilities
  • Atsc3FrontendCapabilities
  • AtscFrontendCapabilities
  • DvbcFrontendCapabilities
  • DvbsFrontendCapabilities
  • DvbtFrontendCapabilities
  • Isdbs3FrontendCapabilities
  • IsdbsFrontendCapabilities
  • IsdbtFrontendCapabilities

从 Android 12 和 Tuner HAL 1.1 或更高版本开始,支持以下 DTV 标准。

  • DtmbFrontendCapabilities

FrontendInfo 检索前端的信息。FrontendStatus 检索前端的当前状态。OnTuneEventListener 侦听前端的事件。TIS 应用使用 ScanCallback 来处理来自前端的扫描消息。

频道扫描

为了设置电视,应用会扫描可能的频率,并构建一个频道阵容供用户访问。TIS 可能会使用 Tuner.tuneTuner.scan(BLIND_SCAN)Tuner.scan(AUTO_SCAN) 来完成频道扫描。

如果 TIS 具有信号的准确传输信息,例如频率、标准(例如 T/T2、S/S2)和其他必要的附加信息(例如 PLD ID),则建议使用 Tuner.tune,因为它速度更快。

当用户调用 Tuner.tune 时,会发生以下操作

  • TIS 使用 Tuner.tune 填充包含所需信息的 FrontendSettings
  • 如果信号已锁定,HAL 会报告调谐 LOCKED 消息。
  • TIS 使用 Frontend.getStatus 收集必要的信息。
  • TIS 移动到其频率列表中的下一个可用频率。

TIS 再次调用 Tuner.tune,直到所有频率都耗尽。

在调谐期间,您可以调用 stopTune()close() 来暂停或结束 Tuner.tune 调用。

Tuner.scan(AUTO_SCAN)

如果 TIS 没有足够的信息来使用 Tuner.tune,但有频率列表和标准类型(例如 DVB T/C/S),则建议使用 Tuner.scan(AUTO_SCAN)

当用户调用 Tuner.scan(AUTO_SCAN) 时,会发生以下操作

  • TIS 将 Tuner.scan(AUTO_SCAN) 与填充了频率的 FrontendSettings 一起使用。

  • 如果信号已锁定,HAL 会报告扫描 LOCKED 消息。HAL 也可能会报告其他扫描消息,以提供有关信号的其他信息。

  • TIS 使用 Frontend.getStatus 收集必要的信息。

  • TIS 调用 Tuner.scan,以便 HAL 继续处理同一频率上的下一个设置。如果 FrontendSettings 结构为空,则 HAL 使用下一个可用设置。否则,HAL 将 FrontendSettings 用于一次性扫描,并发送 END 以指示扫描操作已完成。

  • TIS 重复上述操作,直到频率上的所有设置都耗尽。

  • HAL 发送 END 以指示扫描操作已完成。

  • TIS 移动到其频率列表中的下一个可用频率。

TIS 再次调用 Tuner.scan(AUTO_SCAN),直到所有频率都耗尽。

在扫描期间,您可以调用 stopScan()close() 来暂停或结束扫描。

Tuner.scan(BLIND_SCAN)

如果 TIS 没有频率列表,并且供应商 HAL 可以搜索用户指定的前端的频率以获取前端资源,则建议使用 Tuner.scan(BLIND_SCAN)

  • TIS 使用 Tuner.scan(BLIND_SCAN)。可以在 FrontendSettings 中指定频率作为起始频率,但 TIS 会忽略 FrontendSettings 中的其他设置。
  • 如果信号已锁定,HAL 会报告扫描 LOCKED 消息。
  • TIS 使用 Frontend.getStatus 收集必要的信息。
  • TIS 再次调用 Tuner.scan 以继续扫描。(FrontendSettings 将被忽略。)
  • TIS 重复上述操作,直到频率上的所有设置都耗尽。HAL 会递增频率,而无需 TIS 执行任何操作。HAL 报告 PROGRESS

TIS 再次调用 Tuner.scan(AUTO_SCAN),直到所有频率都耗尽。HAL 报告 END 以指示扫描操作已完成。

在扫描期间,您可以调用 stopScan()close() 来暂停或结束扫描。

Flow diagram of the TIS Scan process.

图 9. TIS 扫描的流程图

Android.media.tv.tuner.filter

过滤器软件包是过滤器操作以及配置、设置、回调和事件的集合。该软件包包括以下操作。请参阅 Android 源代码以获取完整的操作列表。

  • configure()
  • start()
  • stop()
  • flush()
  • read()

请参阅 Android 源代码以获取完整列表。

FilterConfiguration 由以下类派生而来。这些配置用于主过滤器类型,它们指定过滤器使用哪种协议来提取数据。

  • AlpFilterConfiguration
  • IpFilterConfiguration
  • MmtpFilterConfiguration
  • TlvFilterConfiguration
  • TsFilterConfiguration

这些设置由以下类派生而来。这些设置用于过滤器子类型,它们指定过滤器可以排除哪些类型的数据。

  • SectionSettings
  • AvSettings
  • PesSettings
  • RecordSettings
  • DownloadSettings

FilterEvent 由以下类派生而来,用于报告不同类型数据的事件。

  • SectionEvent
  • MediaEvent
  • PesEvent
  • TsRecordEvent
  • MmtpRecordEvent
  • TemiEvent
  • DownloadEvent
  • IpPayloadEvent

从 Android 12 和 Tuner HAL 1.1 或更高版本开始,支持以下事件。

  • IpCidChangeEvent
  • RestartEvent
  • ScramblingStatusEvent
来自过滤器的事件和数据格式
过滤器类型 标志 事件 数据操作 数据格式
TS.SECTION
MMTP.SECTION
IP.SECTION
TLV.SECTION
ALP.SECTION
isRaw
true
必需
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

推荐
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
根据事件和内部计划,运行
Filter.read(buffer, offset, adjustedSize) 一次或多次。

数据从 HAL 的 MQ 复制到客户端缓冲区。
一个组装好的会话包在 FMQ 中被另一个会话包填充。
isRaw
false
必需
DemuxFilterEvent::DemuxFilterSectionEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

可选
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++
Filter.read(buffer, offset, DemuxFilterSectionEven[i].size)


数据从 HAL 的 MQ 复制到客户端缓冲区。
TS.PES isRaw
true
必需
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

推荐
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
根据事件和内部计划,运行
Filter.read(buffer, offset, adjustedSize) 一次或多次。

数据从 HAL 的 MQ 复制到客户端缓冲区。
一个组装好的 PES 包在 FMQ 中被另一个 PES 包填充。
isRaw
false
必需
DemuxFilterEvent::DemuxFilterPesEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

可选
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++
Filter.read(buffer, offset, DemuxFilterPesEven[i].size)


数据从 HAL 的 MQ 复制到客户端缓冲区。
MMTP.PES isRaw
true
必需
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

推荐
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
根据事件和内部计划,运行
Filter.read(buffer, offset, adjustedSize) 一次或多次。

数据从 HAL 的 MQ 复制到客户端缓冲区。
一个组装好的 MFU 包在 FMQ 中被另一个 MFU 包填充。
isRaw
false
必需
DemuxFilterEvent::DemuxFilterPesEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

可选
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++
Filter.read(buffer, offset, DemuxFilterPesEven[i].size)


数据从 HAL 的 MQ 复制到客户端缓冲区。
TS.TS
不适用 必需
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

推荐
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
根据事件和内部计划,运行
Filter.read(buffer, offset, adjustedSize) 一次或多次。

数据从 HAL 的 MQ 复制到客户端缓冲区。
过滤掉带有 ts 标头的 ts
在 FMQ 中填充。
TS.Audio
TS.Video
MMTP.Audio
MMTP.Video
isPassthrough
true
可选
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
客户端可以在收到 DemuxFilterStatus::DATA_READY 后启动 MediaCodec
客户端可以在收到 DemuxFilterStatus::DATA_OVERFLOW 后调用 Filter.flush
不适用
isPassthrough
false
必需
DemuxFilterEvent::DemuxFilterMediaEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

可选
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
要使用 MediaCodec
for i=0; i<n; i++
linearblock = MediaEvent[i].getLinearBlock();
codec.startQueueLinearBlock(linearblock)
linearblock.recycle()


要使用 AudioTrack 的 Direct Audio
for i=0; i<n; i++
audioHandle = MediaEvent[i].getAudioHandle();
audiotrack.write(encapsulated(audiohandle))
ION 内存中的 ES 或部分 ES 数据。
TS.PCR
IP.NTP
ALP.PTP
不适用 必需:不适用
可选:不适用
不适用 不适用
TS.RECORD 不适用 必需
DemuxFilterEvent::DemuxFilterTsRecordEvent[n]
RecordStatus::DATA_READY
RecordStatus::DATA_OVERFLOW
RecordStatus::LOW_WATER
RecordStatus::HIGH_WATER

可选
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
对于索引数据
for i=0; i<n; i++
DemuxFilterTsRecordEvent[i];


对于录制内容,根据 RecordStatus::* 和内部计划,执行以下操作之一
  • 运行 DvrRecord.write(adustedSize) 一次或多次以写入存储空间。
    数据从 HAL 的 MQ 传输到存储空间。
  • 运行 DvrRecord.write(buffer, adustedSize) 一次或多次以写入缓冲区。
    数据从 HAL 的 MQ 复制到客户端缓冲区。
对于索引数据:在事件载荷中携带。

对于录制内容:在 FMQ 中填充多路复用 TS 流。
TS.TEMI 不适用 必需
DemuxFilterEvent::DemuxFilterTemiEvent[n]

可选
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++
DemuxFilterTemiEvent[i];
不适用
MMTP.MMTP 不适用 必需
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

推荐
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
根据事件和内部计划,运行
Filter.read(buffer, offset, adjustedSize) 一次或多次。

数据从 HAL 的 MQ 复制到客户端缓冲区。
过滤掉带有 mmtp 标头的 mmtp
在 FMQ 中填充。
MMTP.RECORD 不适用 必需
DemuxFilterEvent::DemuxFilterMmtpRecordEvent[n]
RecordStatus::DATA_READY
RecordStatus::DATA_OVERFLOW
RecordStatus::LOW_WATER
RecordStatus::HIGH_WATER

可选
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
对于索引数据: for i=0; i<n; i++
DemuxFilterMmtpRecordEvent[i];


对于录制内容,根据 RecordStatus::* 和内部计划,执行以下操作之一
  • 运行 DvrRecord.write(adjustedSize) 一次或多次以写入存储空间。
    数据从 HAL 的 MQ 传输到存储空间。
  • 运行 DvrRecord.write(buffer, adjustedSize) 一次或多次以写入缓冲区。
    数据从 HAL 的 MQ 复制到客户端缓冲区。
对于索引数据:在事件载荷中携带。

对于录制内容:在 FMQ 中填充多路复用录制流。

如果用于录制的过滤器源是 TLV.TLV 到带有直通的 IP.IP,则录制的流具有 TLV 和 IP 标头。
MMTP.DOWNLOAD 不适用 必需
DemuxFilterEvent::DemuxFilterDownloadEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

可选
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++ Filter.read(buffer, offset, DemuxFilterDownloadEvent[i].size)

数据从 HAL 的 MQ 复制到客户端缓冲区。
下载包在 FMQ 中被另一个 IP 下载包填充。
IP.IP_PAYLOAD 不适用 必需
DemuxFilterEvent::DemuxFilterIpPayloadEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

可选
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++ Filter.read(buffer, offset, DemuxFilterIpPayloadEvent[i].size)

数据从 HAL 的 MQ 复制到客户端缓冲区。
IP 载荷包在 FMQ 中被另一个 IP 载荷包填充。
IP.IP
TLV.TLV
ALP.ALP
isPassthrough
true
可选
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
过滤掉的协议子流馈送到过滤器链中的下一个过滤器。 不适用
isPassthrough
false
必需
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

推荐
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
根据事件和内部计划,运行
Filter.read(buffer, offset, adjustedSize) 一次或多次。

数据从 HAL 的 MQ 复制到客户端缓冲区。
过滤掉的带有协议标头的协议子流在 FMQ 中填充。
IP.PAYLOAD_THROUGH
TLV.PAYLOAD_THROUGH
ALP.PAYLOAD_THROUGH
不适用 可选
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
过滤掉的协议载荷馈送到过滤器链中的下一个过滤器。 不适用
使用过滤器构建 PSI/SI 的示例流程

Example flow for using filter to build PSI/SI.

图 10. 构建 PSI/SI 的流程

  1. 打开过滤器。

    Filter filter = tuner.openFilter(
      Filter.TYPE_TS,
      Filter.SUBTYPE_SECTION,
      /* bufferSize */1000,
      executor,
      filterCallback
    );
    
  2. 配置并启动过滤器。

    Settings settings = SectionSettingsWithTableInfo
        .builder(Filter.TYPE_TS)
        .setTableId(2)
        .setVersion(1)
        .setCrcEnabled(true)
        .setRaw(false)
        .setRepeat(false)
        .build();
      FilterConfiguration config = TsFilterConfiguration
        .builder()
        .setTpid(10)
        .setSettings(settings)
        .build();
      filter.configure(config);
      filter.start();
    
  3. 处理 SectionEvent

    FilterCallback filterCallback = new FilterCallback() {
      @Override
      public void onFilterEvent(Filter filter, FilterEvent[] events) {
        for (FilterEvent event : events) {
          if (event instanceof SectionEvent) {
            SectionEvent sectionEvent = (SectionEvent) event;
            int tableId = sectionEvent.getTableId();
            int version = sectionEvent.getVersion();
            int dataLength = sectionEvent.getDataLength();
            int sectionNumber = sectionEvent.getSectionNumber();
            filter.read(buffer, 0, dataLength); }
          }
        }
    };
    
使用来自过滤器的 MediaEvent 的示例流程

Example flow to use MediaEvent from filter.

图 11. 使用来自过滤器的 MediaEvent 的流程

  1. 打开、配置和启动音视频过滤器。
  2. 处理 MediaEvent
  3. 接收 MediaEvent
  4. 将线性块排队到 codec
  5. 当数据被使用后,释放音视频句柄。

Android.media.tv.tuner.dvr

DvrRecorder 提供以下用于录制的方法。

  • configure
  • attachFilter
  • detachFilter
  • start
  • flush
  • stop
  • setFileDescriptor
  • write

DvrPlayback 提供以下用于播放的方法。

  • configure
  • start
  • flush
  • stop
  • setFileDescriptor
  • read

DvrSettings 用于配置 DvrRecorderDvrPlaybackOnPlaybackStatusChangedListenerOnRecordStatusChangedListener 用于报告 DVR 实例的状态。

启动录制的示例流程

Example flow to start a record.

图 12. 启动录制的流程

  1. 打开、配置和启动 DvrRecorder

    DvrRecorder recorder = openDvrRecorder(/* bufferSize */ 1000, executor, listener);
    DvrSettings dvrSettings = DvrSettings
    .builder()
    .setDataFormat(DvrSettings.DATA_FORMAT_TS)
    .setLowThreshold(100)
    .setHighThreshold(900)
    .setPacketSize(188)
    .build();
    recorder.configure(dvrSettings);
    recorder.attachFilter(filter);
    recorder.setFileDescriptor(fd);
    recorder.start();
    
  2. 接收 RecordEvent 并检索索引信息。

    FilterCallback filterCallback = new FilterCallback() {
      @Override
      public void onFilterEvent(Filter filter, FilterEvent[] events) {
        for (FilterEvent event : events) {
          if (event instanceof TsRecordEvent) {
            TsRecordEvent recordEvent = (TsRecordEvent) event;
            int tsMask = recordEvent.getTsIndexMask();
            int scMask = recordEvent.getScIndexMask();
            int packetId = recordEvent.getPacketId();
            long dataLength = recordEvent.getDataLength();
            // handle the masks etc. }
          }
        }
    };
    
  3. 初始化 OnRecordStatusChangedListener 并存储录制数据。

      OnRecordStatusChangedListener listener = new OnRecordStatusChangedListener() {
        @Override
        public void onRecordStatusChanged(int status) {
          // a customized way to consume data efficiently by using status as a hint.
          if (status == Filter.STATUS_DATA_READY) {
            recorder.write(size);
          }
        }
      };
    

Tuner HAL

Tuner HAL 遵循 HIDL,并定义了框架和供应商硬件之间的接口。供应商使用该接口来实现 Tuner HAL,框架使用该接口与 Tuner HAL 实现进行通信。

模块

Tuner HAL 1.0

模块 基本控件 模块特定控件 HAL 文件
ITuner 不适用 frontend(open, getIds, getInfo)openDemuxopenDescrambleropenLnbgetDemuxCaps ITuner.hal
IFrontend setCallbackgetStatusclosetunestopTunescanstopScansetLnb IFrontend.hal
IFrontendCallback.hal
IDemux close setFrontendDataSourceopenFilteropenDvrgetAvSyncHwIdgetAvSyncTimeconnect / disconnectCiCam IDemux.hal
IDvr closestartstopconfigure attach/detachFiltersflushgetQueueDesc IDvr.hal
IDvrCallback.hal
IFilter closestartstopconfiguregetId flushgetQueueDescreleaseAvHandlesetDataSource IFilter.hal
IFilterCallback.hal
ILnb closesetCallback setVoltagesetTonesetSatellitePositionsendDiseqcMessage ILnb.hal
ILnbCallback.hal
IDescrambler close setDemuxSourcesetKeyTokenaddPidremovePid IDescrambler.hal

Tuner HAL 1.1(派生自 Tuner HAL 1.0)

模块 基本控件 模块特定控件 HAL 文件
ITuner 不适用 getFrontendDtmbCapabilities @1.1::ITuner.hal
IFrontend tune_1_1scan_1_1getStatusExt1_1 link/unlinkCiCam @1.1::IFrontend.hal
@1.1::IFrontendCallback.hal
IFilter getStatusExt1_1 configureIpCidconfigureAvStreamTypegetAvSharedHandleconfigureMonitorEvent @1.1::IFilter.hal
@1.1::IFilterCallback.hal

Flow diagram of interactions between the modules of the Tuner HAL.

图 13. Tuner HAL 模块之间互动的图

过滤器链接

Tuner HAL 支持过滤器链接,以便过滤器可以链接到其他过滤器以实现多层处理。过滤器遵循以下规则。

  • 过滤器以树状形式链接,不允许闭合路径。
  • 根节点是解复用器。
  • 过滤器独立运行。
  • 所有过滤器都开始获取数据。
  • 过滤器链接在最后一个过滤器上刷新。

下面的代码块和图 14 说明了多层过滤的示例。

demuxCaps = ITuner.getDemuxCap;
If (demuxCaps[IP][MMTP] == true) {
        ipFilter = ITuner.openFilter(<IP, ..>)
        mmtpFilter1 = ITuner.openFilter(<MMTP ..>)
        mmtpFilter2 = ITuner.openFilter(<MMTP ..>)
        mmtpFilter1.setDataSource(<ipFilter>)
        mmtpFilter2.setDataSource(<ipFilter>)
}

Diagram of filter linkage example.

图 14. 多层过滤器链接的流程图

Tuner Resource Manager

在 Tuner Resource Manager (TRM) 之前,在两个应用之间切换需要相同的 Tuner 硬件。TV Input Framework (TIF) 使用“先获取者胜出”机制,这意味着无论哪个应用先获取资源,该资源都将保留在该应用中。但是,此机制对于某些复杂用例可能并不理想。

TRM 作为系统服务运行,用于管理应用的 Tuner、TVInput 和 CAS 硬件资源。TRM 使用“前台应用胜出”机制,该机制根据应用的前台或后台状态以及用例类型来计算应用的优先级。TRM 根据优先级授予或撤销资源。TRM 集中管理广播、OTT 和 DVR 的 ATV 资源。

TRM 接口

TRM 在 ITunerResourceManager.aidl 中为 Tuner 框架、MediaCasTvInputHardwareManager 公开了 AIDL 接口,用于注册、请求或释放资源。

用于客户端管理的接口如下所示。

  • registerClientProfile(in ResourceClientProfile profile, IResourcesReclaimListener listener, out int[] clientId)
  • unregisterClientProfile(in int clientId)

用于请求和释放资源的接口如下所示。

  • requestFrontend(TunerFrontendRequest request, int[] frontendHandle) / releaseFrontend
  • requestDemux(TunerDemuxRequest request, int[] demuxHandle) / releaseDemux
  • requestDescrambler(TunerDescramblerRequest request, int[] descramblerHandle) / releaseDescrambler
  • requestCasSession(CasSessionRequest request, int[] casSessionHandle) / releaseCasSession
  • requestLnb(TunerLnbRequest request, int[] lnbHandle) / releaseLnb

客户端和请求类如下所示。

  • ResourceClientProfile
  • ResourcesReclaimListener
  • TunerFrontendRequest
  • TunerDemuxRequest
  • TunerDescramblerRequest
  • CasSessionRequest
  • TunerLnbRequest

客户端优先级

TRM 通过使用客户端个人资料中的参数以及配置文件中的优先级值来计算客户端的优先级。优先级也可能会被客户端的任意优先级值更新。

客户端个人资料中的参数

TRM 从 mTvInputSessionId 检索进程 ID,以确定应用是前台应用还是后台应用。要创建 mTvInputSessionIdTvInputService.onCreateSessionTvInputService.onCreateRecordingSession 会初始化 TIS 会话。

mUseCase 指示会话的用例。预定义的用例如下所示。

TvInputService.PriorityHintUseCaseType  {
  PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK
  PRIORITY_HINT_USE_CASE_TYPE_LIVE
  PRIORITY_HINT_USE_CASE_TYPE_RECORD,
  PRIORITY_HINT_USE_CASE_TYPE_SCAN,
  PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND
}

配置文件

默认配置文件

下面的默认配置文件为预定义的用例提供优先级值。用户可以使用自定义配置文件更改这些值。

用例 前台 后台
LIVE 490 400
PLAYBACK 480 300
RECORD 600 500
SCAN 450 200
后台 180 100
自定义配置文件

供应商可以自定义配置文件 /vendor/etc/tunerResourceManagerUseCaseConfig.xml。此文件用于添加、移除或更新用例类型和用例优先级值。自定义文件可以使用 platform/hardware/interfaces/tv/tuner/1.0/config/tunerResourceManagerUseCaseConfigSample.xml 作为模板。

例如,新的供应商用例是 VENDOR_USE_CASE__[A-Z0-9]+, [0 - 1000]。格式应遵循 platform/hardware/interfaces/tv/tuner/1.0/config/tunerResourceManagerUseCaseConfig.xsd

任意优先级值和 nice 值

TRM 提供 updateClientPriority,供客户端更新任意优先级值和 nice 值。任意优先级值会覆盖根据用例类型和会话 ID 计算出的优先级值。

nice 值表示当客户端与其他客户端发生冲突时,客户端行为的宽松程度。nice 值会在客户端的优先级值与发起挑战的客户端进行比较之前降低客户端的优先级值。

回收机制

下图显示了当资源冲突发生时,资源是如何回收和分配的。

Diagram of reclaim mechanism process.

图 15. 调谐器资源之间发生冲突时的回收机制图