调谐器框架

对于 Android 11 或更高版本,您可以使用 Android Tuner 框架来提供 A/V 内容。该框架使用供应商的硬件管道,使其适用于低端和高端 SoC。该框架提供了一种安全的方式来交付受信任的执行环境 (TEE) 和安全媒体路径 (SMP) 保护的 A/V 内容,使其能够在高度受限的内容保护环境中使用。

Tuner 和 Android CAS 之间的标准化接口导致 Tuner 供应商和 CAS 供应商之间的集成更快。 Tuner 界面与MediaCodecAudioTrack一起使用,为 Android TV 构建了一个统一的解决方案。 Tuner 接口支持基于主要广播标准的数字电视和模拟电视。

成分

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

  • Tuner HAL:框架和供应商之间的接口
  • Tuner SDK API:框架和应用程序之间的接口
  • 调谐器资源管理器 (TRM):协调调谐器硬件资源

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

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

Tuner 框架组件的流程图。

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

特征

前端支持以下 DTV 标准。

  • 空中交通管制委员会
  • ATSC3
  • DVB C/S/T
  • ISDB S/S3/T
  • 模拟

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

  • DTMB

Demux 支持以下流协议。

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

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

  • 安全的媒体路径
  • 清除媒体路径
  • 安全的本地记录
  • 安全的本地播放

调谐器 API 支持以下用例。

  • 扫描
  • 居住
  • 回放
  • 记录

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

  • 具有清除内存缓冲区的 ES 有效负载
  • 具有安全内存句柄的 ES 有效负载
  • 直通

整体设计

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

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

创建了 Tuner Java 类和本地类。

  • Tuner Java API 允许应用通过公共 API 访问 Tuner HAL。
  • 本机类允许使用 Tuner HAL 对大量录制或播放数据进行权限控制和处理。
  • Native Tuner 模块是 Tuner Java 类和 Tuner HAL 之间的桥梁。

创建了一个 TRM 类。

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

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

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

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

  • 将安全的 A/V 内存作为内容输入。
  • 配置为在隧道播放中进行硬件 A/V 同步。
  • 配置了对ES_payload和直通模式的支持。

Tuner HAL 的整体设计。

图 2. Tuner HAL 中的组件图

整体工作流程

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

设置

直播回放图设置顺序。

图 3.直播回放的设置顺序

处理 A/V

处理直播播放图的A/V。

图 4.处理直播播放的 A/V

处理加扰的内容

处理直播播放图的加扰内容。

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

处理 A/V 数据

处理直播回放图的A/V数据。

图 6.处理 A/V 以进行直播播放

调谐器 SDK API

Tuner SDK API 处理与 Tuner JNI、Tuner HAL 和TunerResourceManager的交互。 TIS 应用程序使用 Tuner SDK API 来访问 Tuner 资源和子组件,例如过滤器和解扰器。 Frontend 和 demux 是内部组件。

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()

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

套餐

Tuner SDK API 提供以下四个包。

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

Tuner SDK API 包的流程图。

图 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 CiCam
  • shareFrontendFromTuner
  • updateResourcePriority
  • setOnTuneEventListener
  • setResourceLostListener

Android.media.tv.tuner.frontend

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

课程

FrontendSettings由以下类派生用于不同的 DTV 标准。

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

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

  • DtmbFrontendSettings

FrontendCapabilities由以下类派生用于不同的 DTV 标准。

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

从带有 Tuner HAL 1.1 或更高版本的 Android 12 开始,支持以下 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 没有频率列表,并且 Vendor HAL 可以搜索用户指定前端的频率以获取前端资源,则建议使用Tuner.scan(BLIND_SCAN)

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

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

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

TIS 扫描过程的流程图。

图 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

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

  • 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 包由另一个 PES 包填充到 FMQ。
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 包由另一个 MFU 包填充到 FMQ。
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的直接音频:
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 中填充的 Muxed 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 to IP.IP with passthrough,则录制的流有一个 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 复制到客户端缓冲区。
下载包由另一个IP下载包填入FMQ。
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 负载包由另一个 IP 负载包填充到 FMQ。
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 的示例流程

使用过滤器构建 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 的示例流程

使用过滤器中的 MediaEvent 的示例流程。

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

  1. 打开、配置和启动 A/V 过滤器。
  2. 处理MediaEvent
  3. 接收MediaEvent
  4. 将线性块排队到codec
  5. 数据消耗完毕后释放 A/V 句柄。

Android.media.tv.tuner.dvr

DvrRecorder提供了这些录制方法。

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

DvrPlayback提供了这些播放方法。

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

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

开始记录的示例流程

开始记录的示例流程。

图 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);
          }
        }
      };
    

调谐器 HAL

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

模块

调谐器 HAL 1.0

模块基本控制特定于模块的控件HAL 文件
ITuner不适用frontend(open, getIds, getInfo)openDemuxopenDescrambleropenLnbgetDemuxCaps ITuner.hal
IFrontend setCallback , getStatus , close tune , stopTune , scan , stopScan , setLnb IFrontend.hal
IFrontendCallback.hal
IDemux close setFrontendDataSource , openFilter , openDvr , getAvSyncHwId , getAvSyncTime , connect / disconnectCiCam CiCam IDemux.hal
IDvr closestartstopconfigure attach/detachFiltersflushgetQueueDesc IDvr.hal
IDvrCallback.hal
IFilter closestartstopconfiguregetId flushgetQueueDescreleaseAvHandlesetDataSource IFilter.hal
IFilterCallback.hal
ILnb closesetCallback setVoltage , setTone , setSatellitePosition , sendDiseqcMessage ILnb.hal
ILnbCallback.hal
IDescrambler close setDemuxSource , setKeyToken , addPid , removePid IDescrambler.hal

调谐器 HAL 1.1(源自调谐器 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

Tuner HAL 模块之间交互的流程图。

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

过滤器联动

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

  • 过滤器以树的形式链接,不允许关闭路径。
  • 根节点是 demux。
  • 过滤器独立运行。
  • 所有过滤器开始获取数据。
  • 过滤器连杆冲洗最后一个过滤器。

下面的代码块和图 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>)
}

过滤器联动示例图。

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

调谐器资源管理器

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

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

TRM接口

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

下面列出了客户端管理的接口。

  • 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
BACKGROUND 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 值会降低客户端的优先级值。

回收机制

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

回收机制流程示意图。

图 15. Tuner 资源冲突的回收机制示意图