对于 Android 11 或更高版本,您可以使用 Android Tuner 框架来传送 A/V 内容。该框架使用供应商的硬件管道,使其适用于低端和高端 SoC。该框架提供了一种安全的方式来交付受可信执行环境 (TEE) 和安全媒体路径 (SMP) 保护的 A/V 内容,使其可以在高度受限的内容保护环境中使用。
Tuner 和 Android CAS 之间的标准化接口使 Tuner 供应商和 CAS 供应商之间的集成更快。 Tuner 界面与MediaCodec
和AudioTrack
配合使用,为 Android TV 构建一个统一的解决方案。调谐器接口支持基于主要广播标准的数字电视和模拟电视。
成分
对于 Android 11,三个组件是专门为 TV 平台设计的。
- Tuner HAL:框架和供应商之间的接口
- Tuner SDK API:框架和应用程序之间的接口
- 调谐器资源管理器 (TRM):协调调谐器硬件资源
对于 Android 11,以下组件已得到增强。
- CAS V2
-
TvInputService
或电视输入服务 (TIS) -
TvInputManagerService
或电视输入管理器服务 (TIMS) -
MediaCodec
或媒体编解码器 AudioTrack
或音轨MediaResourceManager
或媒体资源管理器 (MRM)
图 1. Android TV 组件之间的交互
特征
前端支持以下 DTV 标准。
- ATSC
- 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。
- Native 类允许使用 Tuner HAL 进行权限控制和处理大量记录或播放数据。
- Native Tuner 模块是 Tuner Java 类和 Tuner HAL 之间的桥梁。
TRM 类已创建。
- 管理有限的调谐器资源,例如前端、LNB、CAS 会话和电视输入 HAL 中的电视输入设备。
- 应用规则从应用程序回收不足的资源。默认规则是前台获胜。
Media CAS 和 CAS HAL 通过以下功能得到增强。
- 打开不同用途和算法的 CAS 会话。
- 支持动态 CAS 系统,例如 CICAM 删除和插入。
- 通过提供密钥令牌与 Tuner HAL 集成。
MediaCodec
和AudioTrack
通过以下功能得到增强。
- 采用安全的 A/V 内存作为内容输入。
- 配置为在隧道播放中进行硬件 A/V 同步。
- 配置了对
ES_payload
和 passthrough 模式的支持。
图 2. Tuner HAL 内的组件图
整体工作流程
下图说明了直播回放的调用顺序。
设置
图 3.直播回放设置顺序
处理音频/视频
图 4.处理直播播放的 A/V
处理乱码内容
图 5.处理直播播放的加扰内容
处理 A/V 数据
图 6.处理直播回放的 A/V
调谐器 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()
:获取时间过滤器实例。
调谐器包提供了过滤器、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
从具有 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
调用。
调谐器.扫描(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()
来暂停或结束扫描。
调谐器.扫描(BLIND_SCAN)
如果TIS没有频率列表,并且Vendor 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
从具有 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 header 过滤掉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 的混合 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复制到客户端缓冲区。 | 下载包由另一个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); } } };
调谐器哈尔
Tuner HAL 遵循 HIDL,定义框架和供应商硬件之间的接口。供应商使用该接口来实现 Tuner HAL,框架使用它与 Tuner HAL 实现进行通信。
模块
调谐器 HAL 1.0
模块 | 基本控制 | 模块特定的控件 | 哈尔文件 |
---|---|---|---|
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)
模块 | 基本控制 | 模块特定的控件 | 哈尔文件 |
---|---|---|---|
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 (TRM) 出现之前,两个应用程序之间的切换需要相同的 Tuner 硬件。 TV 输入框架 (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 值会降低客户端的优先级值。
回收机制
下图展示了发生资源冲突时如何回收和分配资源。
图 15. Tuner 资源之间冲突的回收机制图