对于 Android 11 或更高版本,您可以使用 Android Tuner 框架来传输音视频内容。该框架使用供应商提供的硬件管道,使其既适用于低端 SoC,也适用于高端 SoC。该框架提供了一种安全的方式来传输受可信执行环境 (TEE) 和安全媒体路径 (SMP) 保护的音视频内容,使其可用于高度受限的内容保护环境。
Tuner 和 Android CAS 之间的标准化接口加快了 Tuner 供应商和 CAS 供应商之间的集成速度。Tuner 接口与 MediaCodec
和 AudioTrack
协同工作,为 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)
图 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、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 的大量录制或播放数据。
- 原生 Tuner 模块是 Tuner Java 类和 Tuner HAL 之间的桥梁。
创建 TRM 类。
- 管理有限的 Tuner 资源,例如前端、LNB、CAS 会话和来自 TV 输入 HAL 的 TV 输入设备。
- 应用规则以从应用回收不足的资源。默认规则是前台应用胜出。
Media CAS 和 CAS HAL 通过以下功能得到增强。
- 为不同的用途和算法打开 CAS 会话。
- 支持动态 CAS 系统,例如 CICAM 的移除和插入。
- 通过提供密钥令牌与 Tuner HAL 集成。
MediaCodec
和 AudioTrack
通过以下功能得到增强。
- 将安全音视频内存作为内容输入。
- 配置为在隧道式播放中执行硬件音视频同步。
- 配置为支持
ES_payload
和直通模式。
图 2. Tuner HAL 内的组件图
总体工作流程
下图说明了直播播放的调用序列。
设置
图 3. 直播播放的设置序列
处理音视频
图 4. 直播播放的音视频处理
处理加扰内容
图 5. 直播播放的加扰内容处理
处理音视频数据
图 6. 直播播放的音视频处理
Tuner SDK API
Tuner SDK API 处理与 Tuner JNI、Tuner HAL 和 TunerResourceManager
的互动。TIS 应用使用 Tuner SDK API 访问 Tuner 资源和子组件,例如过滤器和解扰器。前端和解复用器是内部组件。
图 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
图 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
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.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 没有频率列表,并且供应商 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()
来暂停或结束扫描。
图 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 |
必需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 包在 FMQ 中被另一个 PES 包填充。 |
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 包在 FMQ 中被另一个 MFU 包填充。 |
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 的 Direct Audiofor 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 中填充多路复用 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 到带有直通的 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 |
可选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 的流程
- 打开、配置和启动音视频过滤器。
- 处理
MediaEvent
。 - 接收
MediaEvent
。 - 将线性块排队到
codec
。 - 当数据被使用后,释放音视频句柄。
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); } } };
Tuner HAL
Tuner HAL 遵循 HIDL,并定义了框架和供应商硬件之间的接口。供应商使用该接口来实现 Tuner HAL,框架使用该接口与 Tuner HAL 实现进行通信。
模块
Tuner 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 |
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 |
Tuner HAL 1.1(派生自 Tuner 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 支持过滤器链接,以便过滤器可以链接到其他过滤器以实现多层处理。过滤器遵循以下规则。
- 过滤器以树状形式链接,不允许闭合路径。
- 根节点是解复用器。
- 过滤器独立运行。
- 所有过滤器都开始获取数据。
- 过滤器链接在最后一个过滤器上刷新。
下面的代码块和图 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
在 Tuner Resource Manager (TRM) 之前,在两个应用之间切换需要相同的 Tuner 硬件。TV Input Framework (TIF) 使用“先获取者胜出”机制,这意味着无论哪个应用先获取资源,该资源都将保留在该应用中。但是,此机制对于某些复杂用例可能并不理想。
TRM 作为系统服务运行,用于管理应用的 Tuner、TVInput
和 CAS 硬件资源。TRM 使用“前台应用胜出”机制,该机制根据应用的前台或后台状态以及用例类型来计算应用的优先级。TRM 根据优先级授予或撤销资源。TRM 集中管理广播、OTT 和 DVR 的 ATV 资源。
TRM 接口
TRM 在 ITunerResourceManager.aidl
中为 Tuner 框架、MediaCas
和 TvInputHardwareManager
公开了 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,以确定应用是前台应用还是后台应用。要创建 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 |
后台 |
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. 调谐器资源之间发生冲突时的回收机制图