对于 Android 11 或更高版本,您可以使用 Android Tuner 框架来提供 A/V 内容。该框架使用供应商的硬件管道,使其适用于低端和高端 SoC。该框架提供了一种安全的方式来交付受信任的执行环境 (TEE) 和安全媒体路径 (SMP) 保护的 A/V 内容,使其能够在高度受限的内容保护环境中使用。
Tuner 和 Android CAS 之间的标准化接口导致 Tuner 供应商和 CAS 供应商之间的集成更快。 Tuner 界面与MediaCodec
和AudioTrack
一起使用,为 Android TV 构建了一个统一的解决方案。 Tuner 接口支持基于主要广播标准的数字电视和模拟电视。
成分
对于 Android 11,三个组件是专门为电视平台设计的。
- Tuner HAL:框架和供应商之间的接口
- Tuner SDK API:框架和应用程序之间的接口
- 调谐器资源管理器 (TRM):协调调谐器硬件资源
对于 Android 11,以下组件已得到增强。
- CAS V2
-
TvInputService
或电视输入服务 (TIS) -
TvInputManagerService
或电视输入管理器服务 (TIMS) -
MediaCodec
或媒体编解码器 AudioTrack
或音轨MediaResourceManager
或媒体资源管理器 (MRM)
图 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、 MediaCodec
和AudioTrack
支持以下数据流模式。
- 具有清除内存缓冲区的 ES 有效负载
- 具有安全内存句柄的 ES 有效负载
- 直通
整体设计
Tuner HAL 是在 Android 框架和供应商的硬件之间定义的。
- 描述框架对供应商的期望以及供应商如何做到这一点。
- 通过
IFrontend
、IDemux
、IDescrambler
、IFilter
、IDvr
和ILnb
接口将前端、解复用器和解扰器的功能导出到框架。 - 包括将 Tuner HAL 与其他框架组件(例如
MediaCodec
和AudioTrack
)集成的功能。
创建了 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 集成。
MediaCodec
和AudioTrack
通过以下功能得到增强。
- 将安全的 A/V 内存作为内容输入。
- 配置为在隧道播放中进行硬件 A/V 同步。
- 配置了对
ES_payload
和直通模式的支持。
图 2. Tuner HAL 中的组件图
整体工作流程
下图说明了直播回放的调用顺序。
设置
图 3.直播回放的设置顺序
处理 A/V
图 4.处理直播播放的 A/V
处理加扰的内容
图 5.处理直播播放的加扰内容
处理 A/V 数据
图 6.处理 A/V 以进行直播播放
调谐器 SDK API
Tuner SDK API 处理与 Tuner JNI、Tuner HAL 和TunerResourceManager
的交互。 TIS 应用程序使用 Tuner SDK API 来访问 Tuner 资源和子组件,例如过滤器和解扰器。 Frontend 和 demux 是内部组件。
图 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
图 8. Tuner SDK API 包
Android.media.tv.tuner
Tuner 包是使用 Tuner 框架的入口点。 TIS 应用程序使用包通过指定初始设置和回调来初始化和获取资源实例。
-
tuner()
:通过指定useCase
和sessionId
参数来初始化 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.tune
、 Tuner.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()
来暂停或结束扫描。
图 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: | 强制的:DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 推荐的: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER | 根据事件和内部时间表,运行Filter.read(buffer, offset, adjustedSize) 一次或多次。数据从 HAL 的 MQ 复制到客户端缓冲区。 | 一个组装的会话包由另一个会话包填充到 FMQ。 |
isRaw: | 强制的:DemuxFilterEvent::DemuxFilterSectionEvent[n] DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 可选的: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER | for i=0; i<n; i++ 数据从 HAL 的 MQ 复制到客户端缓冲区。 | ||
TS.PES | isRaw: | 强制的:DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 推荐的: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER | 根据事件和内部时间表,运行Filter.read(buffer, offset, adjustedSize) 一次或多次。数据从 HAL 的 MQ 复制到客户端缓冲区。 | 一个组装好的 PES 包由另一个 PES 包填充到 FMQ。 |
isRaw: | 强制的:DemuxFilterEvent::DemuxFilterPesEvent[n] DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 可选的: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER | for i=0; i<n; i++ 数据从 HAL 的 MQ 复制到客户端缓冲区。 | ||
MMTP.PES | isRaw: | 强制的:DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 推荐的: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER | 根据事件和内部时间表,运行Filter.read(buffer, offset, adjustedSize) 一次或多次。数据从 HAL 的 MQ 复制到客户端缓冲区。 | 一个组装好的 MFU 包由另一个 MFU 包填充到 FMQ。 |
isRaw: | 强制的:DemuxFilterEvent::DemuxFilterPesEvent[n] DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 可选的: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER | for i=0; i<n; i++ 数据从 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: | 可选的:DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW | 客户端收到DemuxFilterStatus::DATA_READY 后可以启动MediaCodec 。客户端收到 DemuxFilterStatus::DATA_OVERFLOW Filter.flush | 不适用 |
isPassthrough: | 强制的:DemuxFilterEvent::DemuxFilterMediaEvent[n] DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW 可选的: DemuxFilterStatus::LOW_WATER DemuxFilterStatus::HIGH_WATER | 要使用MediaCodec :for i=0; i<n; i++ 要使用 AudioTrack 的直接音频:for i=0; i<n; i++ | 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++ 对于录制的内容,根据 RecordStatus::* 和内部时间表,执行以下操作之一:
| 对于索引数据:在事件有效负载中携带。 对于录制的内容: 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++ | 不适用 |
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++ 对于录制的内容,根据 RecordStatus::* 和内部时间表,执行以下操作之一:
| 对于索引数据:在事件有效负载中携带。 对于录制内容: 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: | 可选的:DemuxFilterStatus::DATA_READY DemuxFilterStatus::DATA_OVERFLOW | 过滤掉的协议子流提供过滤器链中的下一个过滤器。 | 不适用 |
isPassthrough: | 强制的: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 的示例流程
图 10.构建 PSI/SI 的流程
打开过滤器。
Filter filter = tuner.openFilter( Filter.TYPE_TS, Filter.SUBTYPE_SECTION, /* bufferSize */1000, executor, filterCallback );
配置并启动过滤器。
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();
处理
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 的示例流程
图 11.从过滤器使用 MediaEvent 的流程
- 打开、配置和启动 A/V 过滤器。
- 处理
MediaEvent
。 - 接收
MediaEvent
。 - 将线性块排队到
codec
。 - 数据消耗完毕后释放 A/V 句柄。
Android.media.tv.tuner.dvr
DvrRecorder
提供了这些录制方法。
-
configure
-
attachFilter
-
detachFilter
-
start
-
flush
-
stop
-
setFileDescriptor
-
write
DvrPlayback
提供了这些播放方法。
-
configure
-
start
-
flush
-
stop
-
setFileDescriptor
-
read
DvrSettings
用于配置DvrRecorder
和DvrPlayback
。 OnPlaybackStatusChangedListener
和OnRecordStatusChangedListener
用于报告 DVR 实例的状态。
开始记录的示例流程
图 12.开始记录的流程
打开、配置并启动
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();
接收
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. } } } };
初始化
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) , openDemux , openDescrambler , openLnb , getDemuxCaps | 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 | close 、 start 、 stop 、 configure | attach/detachFilters , flush , getQueueDesc | IDvr.hal IDvrCallback.hal |
IFilter | close 、 start 、 stop 、 configure 、 getId | flush , getQueueDesc , releaseAvHandle , setDataSource | IFilter.hal IFilterCallback.hal |
ILnb | close , setCallback | 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_1 , scan_1_1 , getStatusExt1_1 | link/unlinkCiCam | @1.1::IFrontend.hal @1.1::IFrontendCallback.hal |
IFilter | getStatusExt1_1 | configureIpCid , configureAvStreamType , getAvSharedHandle , configureMonitorEvent | @1.1::IFilter.hal @1.1::IFilterCallback.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 框架、 MediaCas
和TvInputHardwareManager
注册、请求或释放资源。
下面列出了客户端管理的接口。
-
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 以确定应用程序是前台应用程序还是后台应用程序。要创建mTvInputSessionId
、 TvInputService.onCreateSession
或TvInputService.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 资源冲突的回收机制示意图