TV 输入服务代表媒体流来源,并允许您以 以频道和节目的形式呈现线性广播电视节目。借助 TV 输入服务,您可以 家长控制、收视指南信息和内容分级。TV 输入服务的工作原理 Android 系统 TV 应用。此应用最终会控制和呈现频道内容 。系统 TV 应用是专为设备开发的,不可更改 由第三方应用开发。如需详细了解 TV 输入框架 (TIF) 请参阅 <ph type="x-smartling-placeholder"></ph> TV 输入框架。
使用 TIF 随播内容库创建 TV 输入服务
TIF 随播内容库框架提供可扩展的 常见 TV 输入服务功能的实现。供原始设备制造商 (OEM) 用来构建 渠道。
更新项目
TIF 随播内容库可供 OEM 在 androidtv-sample-inputs 存储库有关如何在应用中添加库的示例,请参阅该代码库。
在清单中声明 TV 输入服务
您的应用必须提供与 TvInputService
兼容的
服务。TIF
随播内容库提供 BaseTvInputService
类,
提供 TvInputService
的默认实现
进行自定义创建 BaseTvInputService
的子类。
并在清单中将该子类声明为服务。
在清单声明中,指定
BIND_TV_INPUT
权限,以允许
服务将电视输入连接到系统。系统服务
执行绑定,并且
BIND_TV_INPUT
权限。
系统 TV 应用向 TV 输入服务发送请求
通过 TvInputManager
接口实现。
在您的服务声明中,请包括一个 intent 过滤器,用于指定
TvInputService
作为要通过
intent。另外,请将服务元数据声明为单独的 XML 资源。通过
服务声明、intent 过滤器和服务元数据声明
如下例中所示:
<service android:name=".rich.RichTvInputService" android:label="@string/rich_input_label" android:permission="android.permission.BIND_TV_INPUT"> <!-- Required filter used by the system to launch our account service. --> <intent-filter> <action android:name="android.media.tv.TvInputService" /> </intent-filter> <!-- An XML file which describes this input. This provides pointers to the RichTvInputSetupActivity to the system/TV app. --> <meta-data android:name="android.media.tv.input" android:resource="@xml/richtvinputservice" /> </service>
在单独的 XML 文件中定义服务元数据。服务 元数据 XML 文件必须包含一个描述电视输入源的 初始配置和信道扫描。元数据文件还应包含 标记指明用户能否录制内容。有关 有关如何支持在应用中录制内容的信息,请参阅 支持内容录制。
服务元数据文件位于 XML 资源目录中
并且必须与您在
清单。使用上一个示例中的清单条目,
使用以下代码创建 XML 文件:res/xml/richtvinputservice.xml
以下内容:
<?xml version="1.0" encoding="utf-8"?> <tv-input xmlns:android="http://schemas.android.com/apk/res/android" android:canRecord="true" android:setupActivity="com.example.android.sampletvinput.rich.RichTvInputSetupActivity" />
定义频道并创建设置 Activity
您的 TV 输入服务必须至少定义一个供用户使用的频道 通过系统 TV 应用访问。你应该注册自己的频道 存储在系统数据库中,并提供一个设置 activity,供系统用来 在无法为您的应用找到渠道时调用。
首先,让您的应用可以在 Electronic 收视指南 (EPG),其数据包含可观看的频道和节目 。如需让您的应用能够执行这些操作,并与 设备重启后电子节目单,将以下元素添加到应用清单中:
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED "/>
添加以下元素以确保您的应用显示在 Google Play 商店作为一款在 Android TV 中提供内容频道的应用:
<uses-feature android:name="android.software.live_tv" android:required="true" />
接下来,创建一个扩展 EpgSyncJobService
的类
类。这个抽象类让你可以轻松创建
在系统数据库中创建并更新频道。
在您的子类中,创建并返回
getChannels()
。如果您的频道来自 XMLTV 文件
请使用 XmlTvParser
类。否则,生成
使用 Channel.Builder
类以编程方式创建频道。
对于每个通道,系统会调用 getProgramsForChannel()
需要在给定时间段内可以观看的节目列表时
频道。返回 Program
对象的列表,以获取
。使用 XmlTvParser
类从
XMLTV 文件,也可以使用
Program.Builder
类。
对于每个 Program
对象,使用
InternalProviderData
对象来设置计划信息,例如
节目的视频类型。如果您参加的计划数量有限
让频道循环重复,请使用
InternalProviderData.setRepeatable()
方法,值为
true
。
实现该作业服务后,将其添加到您的应用清单中:
<service android:name=".sync.SampleJobService" android:permission="android.permission.BIND_JOB_SERVICE" android:exported="true" />
最后,创建设置 Activity。您的设置 activity 应该提供了一种 以同步频道和节目数据。要实现这一点,一种方法是让用户 通过 Activity 中的界面进行操作。您也可以让应用自动执行此操作 当 activity 启动时。当设置 activity 需要同步频道和 程序信息,应用应启动作业服务:
Kotlin
val inputId = getActivity().intent.getStringExtra(TvInputInfo.EXTRA_INPUT_ID) EpgSyncJobService.cancelAllSyncRequests(getActivity()) EpgSyncJobService.requestImmediateSync( getActivity(), inputId, ComponentName(getActivity(), SampleJobService::class.java) )
Java
String inputId = getActivity().getIntent().getStringExtra(TvInputInfo.EXTRA_INPUT_ID); EpgSyncJobService.cancelAllSyncRequests(getActivity()); EpgSyncJobService.requestImmediateSync(getActivity(), inputId, new ComponentName(getActivity(), SampleJobService.class));
使用 requestImmediateSync()
方法进行同步
作业服务。用户必须等待同步完成,因此您应该
请保持相对较短的请求期限。
使用 setUpPeriodicSync()
方法获取作业服务
定期在后台同步频道和节目数据:
Kotlin
EpgSyncJobService.setUpPeriodicSync( context, inputId, ComponentName(context, SampleJobService::class.java) )
Java
EpgSyncJobService.setUpPeriodicSync(context, inputId, new ComponentName(context, SampleJobService.class));
TIF 随播内容库提供另一种过载方法,
requestImmediateSync()
,用于指定
同步的频道数据(以毫秒为单位)。默认方法会同步一个小时的
价值的渠道。
TIF 随播内容库还提供另一种过载方法,
setUpPeriodicSync()
,用于指定
要同步的频道数据,以及定期同步的执行频率。通过
默认方法每 12 小时同步 48 小时的频道数据。
如需详细了解频道数据和电子节目单,请参阅 使用频道数据。
处理微调请求和媒体播放
当用户选择特定频道时,系统 TV 应用会使用
Session
(由您的应用创建),用于调到所请求的频道
和播放内容。TIF 随播内容库提供
类,您可以扩展这些类来处理来自系统的频道和会话调用。
BaseTvInputService
子类创建的会话可处理
调参请求。替换
onCreateSession()
方法,创建从
BaseTvInputService.Session
类,并调用
super.sessionCreated()
。在以下
例如,onCreateSession()
会返回
RichTvInputSessionImpl
对象,用于扩展
BaseTvInputService.Session
:
Kotlin
override fun onCreateSession(inputId: String): Session = RichTvInputSessionImpl(this, inputId).apply { setOverlayViewEnabled(true) }
Java
@Override public final Session onCreateSession(String inputId) { RichTvInputSessionImpl session = new RichTvInputSessionImpl(this, inputId); session.setOverlayViewEnabled(true); return session; }
当用户使用系统 TV 应用开始观看您的某个频道时,
系统会调用您会话的 onPlayChannel()
方法。替换
如果您需要在
开始播放。
然后,系统获取当前安排的节目,并调用您的
会话的 onPlayProgram()
方法,以指定程序
信息和开始时间(以毫秒为单位)。使用
TvPlayer
接口,用于开始播放节目。
您的媒体播放器代码应实现 TvPlayer
来处理
特定播放事件TvPlayer
类负责处理功能
例如时移控制,同时又不会增加复杂性
BaseTvInputService
实现。
在会话的 getTvPlayer()
方法中,返回
实现 TvPlayer
的媒体播放器。通过
<ph type="x-smartling-placeholder"></ph>
TV Input Service 示例应用实现了一个媒体播放器,可使用
ExoPlayer。
使用 TV 输入框架创建 TV 输入服务
如果您的 TV 输入服务无法使用 TIF 随播内容库,则需要 来实现以下组件:
TvInputService
为实例提供长时间运行的后台可用性, 电视输入设备TvInputService.Session
可保持 TV 输入状态,并与用于通信的 使用托管应用TvContract
描述电视可用的频道和节目 输入TvContract.Channels
代表有关 TV 频道的信息TvContract.Programs
描述电视节目,其中包含节目等数据 标题和开始时间TvTrackInfo
代表音频、视频或字幕轨道TvContentRating
描述内容分级,允许自定义内容 评分方案TvInputManager
为系统 TV 应用提供 API,并管理 与 TV 输入源和应用交互
您还需要执行以下操作:
- 在清单中声明您的 TV 输入服务,如下所示: 如在 清单。
- 创建服务元数据文件。
- 创建并注册您的频道和节目信息。
- 创建您的设置 Activity。
定义您的 TV 输入服务
对于您的服务,您可以扩展 TvInputService
类。答
TvInputService
实现是
绑定服务,系统服务
是与其绑定的客户端服务生命周期方法
如图 1 所示。
onCreate()
方法会初始化并启动
HandlerThread
,它提供了一个与界面线程分开的进程线程,
处理系统驱动的操作。在以下示例中,onCreate()
方法初始化 CaptioningManager
并准备处理
ACTION_BLOCKED_RATINGS_CHANGED
和 ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED
操作。这些
操作说明了用户更改家长控制设置时触发的系统 intent,以及
屏蔽的评分列表发生了更改。
Kotlin
override fun onCreate() { super.onCreate() handlerThread = HandlerThread(javaClass.simpleName).apply { start() } dbHandler = Handler(handlerThread.looper) handler = Handler() captioningManager = getSystemService(Context.CAPTIONING_SERVICE) as CaptioningManager setTheme(android.R.style.Theme_Holo_Light_NoActionBar) sessions = mutableListOf<BaseTvInputSessionImpl>() val intentFilter = IntentFilter().apply { addAction(TvInputManager.ACTION_BLOCKED_RATINGS_CHANGED) addAction(TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED) } registerReceiver(broadcastReceiver, intentFilter) }
Java
@Override public void onCreate() { super.onCreate(); handlerThread = new HandlerThread(getClass() .getSimpleName()); handlerThread.start(); dbHandler = new Handler(handlerThread.getLooper()); handler = new Handler(); captioningManager = (CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE); setTheme(android.R.style.Theme_Holo_Light_NoActionBar); sessions = new ArrayList<BaseTvInputSessionImpl>(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(TvInputManager .ACTION_BLOCKED_RATINGS_CHANGED); intentFilter.addAction(TvInputManager .ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED); registerReceiver(broadcastReceiver, intentFilter); }
![](https://developer.android.google.cn/static/images/tv/tvinput-life.png?authuser=9&hl=zh-cn)
图 1. TvInputService 生命周期。
请参阅
控制内容,详细了解如何处理被屏蔽的内容以及如何提供
家长控制请参阅TvInputManager
更多
TV 输入服务中可能需要处理的所有对象。
TvInputService
会创建一个
实现了 Handler.Callback
的 TvInputService.Session
处理播放器状态变化。包含
onSetSurface()
,
TvInputService.Session
会将 Surface
设为
视频内容。请参阅将播放器与 Surface 集成
详细了解如何使用 Surface
呈现视频。
TvInputService.Session
负责处理
onTune()
事件,并将内容和内容变化告知系统 TV 应用
内容元数据。如需了解这些 notify()
方法,请参阅
<ph type="x-smartling-placeholder"></ph>
控制内容和处理轨道选择
详细介绍。
定义您的设置 Activity
系统 TV 应用使用您为 TV 输入定义的设置 Activity。通过 必须执行设置活动,并且必须为系统数据库提供至少一个频道记录。通过 系统 TV 应用在无法为 TV 输入找到频道时调用设置 activity。
设置 activity 向系统 TV 应用描述通过 TV 提供的频道 如下一课中所述,创建 和更新频道数据。