テレビ入力サービスは、メディア ストリーム ソースを表し、メディア コンテンツをチャンネルおよび番組としてリニアなテレビ放送形式で公開することを可能にします。テレビ入力サービスを使用すると、保護者による使用制限、番組ガイド情報、コンテンツのレーティングを提供できます。テレビ入力サービスは、Android システムのテレビアプリで動作します。このアプリは、テレビのチャンネル コンテンツを最終的に制御および公開します。システム TV アプリはデバイス専用に開発されており、サードパーティ製アプリで変更することはできません。テレビ入力フレームワーク(TIF) アーキテクチャとそのコンポーネントの詳細については、 テレビ入力フレームワークをご覧ください。
TIF Companion Library を使用してテレビ入力サービスを作成する
TIF Companion Library は、一般的なテレビ入力サービス機能の拡張可能な実装を提供するフレームワークです。Android 5.0(API レベル 21)から Android 7.1(API レベル 25)までのチャンネルを構築するために使用することを想定しています。
プロジェクトを更新する
TIF Companion Library は、 androidtv-sample-inputs リポジトリで OEM がレガシー用途に使用できます。ライブラリをアプリに含める方法の例については、そのリポジトリをご覧ください。
マニフェストでテレビ入力サービスを宣言する
アプリでは、システムがアプリへのアクセスに使用する TvInputService 互換サービスを提供する必要があります。TIF
Companion Library には、カスタマイズ可能な TvInputService
のデフォルト実装を提供する BaseTvInputService クラスが用意されています。BaseTvInputService のサブクラスを作成し、マニフェストでそのサブクラスをサービスとして宣言します。
サービスがテレビ入力をシステムに接続できるようにするため、マニフェストの宣言内で BIND_TV_INPUT 権限を指定します。システム サービスはバインディングを実行し、BIND_TV_INPUT 権限を付与されます。
システム TV アプリは、TvInputManager インターフェースを介してテレビ入力サービスにリクエストを送信します。
サービス宣言内に、インテントを使用して実行するアクションとして TvInputService を指定するインテント フィルタを追加します。また、サービス メタデータを個別の XML リソースとして宣言します。サービス宣言、インテント フィルタ、サービス メタデータ宣言の例を以下に示します。
<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 ファイルには、TV 入力の初期構成とチャンネル スキャンを表すセットアップ インターフェースが含まれている必要があります。メタデータ ファイルには、ユーザーがコンテンツを録画できるかどうかを示すフラグも含める必要があります。アプリでコンテンツの録画をサポートする方法について詳しくは、コンテンツの録画をサポートするをご覧ください。
サービス メタデータ ファイルはアプリの 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" />
チャンネルを定義してセットアップ アクティビティを作成する
テレビ入力サービスでは、ユーザーがシステム TV アプリを介してアクセスするチャンネルを少なくとも 1 つ定義する必要があります。システム データベースにチャンネルを登録し、アプリのチャンネルが見つからないときにシステムが呼び出すセットアップ アクティビティを設定します。
まず、アプリがシステムの電子番組ガイド(EPG)の読み取りと書き込みを行えるようにします。EPG のデータには、ユーザーが視聴できるチャンネルと番組が含まれています。アプリがこのアクションを実行してデバイスの再起動後に EPG と同期できるようにするには、アプリ マニフェストに次の要素を追加します。
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED "/>
次の要素を追加し、Android TV でコンテンツ チャンネルを提供するアプリとして Google Play ストアに表示されるようにします。
<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 オブジェクトを使用して、番組の動画タイプなどの番組情報を設定します。番組の数が限られていて、チャンネル内で繰り返し放送したい場合は、番組に関する情報を設定する際に true の値を指定した InternalProviderData.setRepeatable メソッドを使用します。
ジョブサービスを実装したら、アプリ マニフェストに追加します。
<service android:name=".sync.SampleJobService" android:permission="android.permission.BIND_JOB_SERVICE" android:exported="true" />
最後に、セットアップ アクティビティを作成します。セットアップ アクティビティでは、チャンネルと番組のデータを同期する方法を提供する必要があります。1 つの方法として、ユーザーがアクティビティの UI を使用して同期する方法があります。アクティビティの開始時にアプリが自動的に同期する方法もあります。セットアップ アクティビティでチャンネルと番組の情報を同期するには、アプリがジョブサービスを開始する必要があります。
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 Companion Library には、requestImmediateSync のオーバーロード メソッドが追加で用意されており、同期するチャンネル データの対象時間をミリ秒単位で指定できます。デフォルトのメソッドでは、1 時間分のチャンネル データが同期されます。
TIF Companion Library には、setUpPeriodicSync のオーバーロード メソッドも追加で用意されており、同期するチャンネル データの対象時間に加えて、定期的に同期を実行する頻度を指定できます。デフォルトのメソッドでは、12 時間おきに 48 時間分のチャンネル データが同期されます。
チャンネル データと EPG の詳細については、 チャンネル データを利用するをご覧ください。
チューニング リクエストとメディア再生を処理する
ユーザーが特定のチャンネルを選択すると、システム TV アプリは、アプリによって作成された Session を使用し、リクエストされたチャンネルにチューニングを合わせてコンテンツを再生します。TIF Companion Library には、システムからのチャンネルおよびセッション呼び出しを処理するために拡張できるクラスがいくつか用意されています。
BaseTvInputService サブクラスで、チューニング リクエストを処理するセッションを作成します。onCreateSession メソッドをオーバーライドして、BaseTvInputService.Session クラスから拡張されたセッションを作成し、新しいセッションで super.sessionCreated を呼び出します。次の例では、onCreateSession は BaseTvInputService.Session を拡張する RichTvInputSessionImpl オブジェクトを返します。
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 アプリを使用してチャンネルの 1 つを視聴し始めると、システムはセッションの onPlayChannel メソッドを呼び出します。番組の再生が始まる前に特別なチャンネルの初期化を行う必要がある場合は、このメソッドをオーバーライドします。
次に、システムは現在スケジュールされている番組を取得し、セッションの onPlayProgram メソッドを呼び出して、番組情報と開始時刻をミリ秒単位で指定します。TvPlayer インターフェースを使用して番組の再生を開始します。
プレーヤー コードでは、特定の再生イベントを処理するために、TvPlayer を実装する必要があります。TvPlayer クラスは、BaseTvInputService 実装の複雑さを増すことなく、タイムシフト制御などの機能を処理します。
セッションの getTvPlayer メソッドで、
TvPlayer を実装するメディア プレーヤーを返します。
テレビ入力サービスのサンプルアプリは、
ExoPlayerを使用するメディア プレーヤーを実装します。
TV 入力フレームワークを使用して TV 入力サービスを作成する
テレビ入力サービスが TIF Companion Library を使用できない場合は、次のコンポーネントを実装する必要があります。
TvInputService:長時間かつバックグラウンドで使用可能な テレビ入力を提供します。TvInputService.Sessionはテレビ入力状態を維持し、ホスティング アプリと通信しますTvContract:テレビ入力で利用可能なチャンネルと番組について記述します。TvContract.Channels: テレビ チャンネルに関する情報を表します。TvContract.Programs:番組タイトルや開始時刻などのデータを含むテレビ番組について記述します。TvTrackInfo: 音声、動画、または字幕トラックを表します。TvContentRatingはコンテンツのレーティングについて記述し、カスタムのコンテンツ レーティング スキームを使用できるようにします。TvInputManager:システム TV アプリに API を提供し、TV 入力およびアプリとのやり取りを管理します。
また、次の処理も行う必要があります。
- マニフェストでテレビ入力サービスを宣言する で説明したように、マニフェストでテレビ入力サービスを宣言する。
- サービス メタデータ ファイルを作成する。
- チャンネルと番組の情報を作成して登録する。
- セットアップ アクティビティを作成する。
テレビ入力サービスを定義する
サービス用に、TvInputService クラスを拡張します。A
TvInputService 実装は、
バインドされたサービスです(システム サービス
がクライアントとしてこのサービスにバインドされます)。図 1 に、実装する必要があるサービス ライフサイクル メソッドを示します。
onCreate メソッドは、(システム駆動のアクションを処理する UI スレッドとは別の処理スレッドを提供する)HandlerThread を初期化して開始します。次の例では、onCreate メソッドは CaptioningManager を初期化し、ACTION_BLOCKED_RATINGS_CHANGED アクションと ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED アクションを処理する準備をします。これらのアクションは、ユーザーが保護者による使用制限の設定を変更したときと、ブロックされたレーティングのリストに変更があったときに起動されるシステム インテントについて記述します。
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); }
図 1.TvInputService のライフサイクル
ブロックされたコンテンツの操作と
保護者による使用制限の適用の詳細については、
コンテンツを制御するをご覧ください。テレビ入力サービスで処理するシステム駆動型のアクションについては、TvInputManager をご覧ください。
TvInputService は
TvInputService.Session を作成します。このセッションは、Handler.Callback
を実装してプレーヤーの状態の変更を処理します。
onSetSurface
を使用して、TvInputService.Session は動画コンテンツに Surface を設定します。プレーヤーを Surface と統合する
を使用して動画をレンダリングする方法の詳細については、Surfaceもご覧ください。
TvInputService.Session は、ユーザーがチャンネルを選択すると onTune イベントを処理し、コンテンツとコンテンツ メタデータの変更のためにシステム TV アプリに通知します。そうした notify メソッドについては、このトレーニングの
コンテンツを制御するとトラック選択を処理する
で詳しく説明されています。
セットアップ アクティビティを定義する
システム TV アプリは、デベロッパーが TV 入力用に定義したセットアップ アクティビティで動作します。セットアップ アクティビティは必須であり、このアクティビティでシステム データベースに少なくとも 1 つのチャンネル レコードを提供する必要があります。システムの TV アプリは、テレビ入力用のチャンネルが見つからない場合、セットアップ Activity を呼び出します。
次のレッスンであるチャンネル データの作成と更新で示すように、セットアップ アクティビティは、システムのテレビアプリに対して、テレビ 入力を通じて利用可能になったチャンネルについて記述します。