6 月 3 日の「#Android11: The Beta Launch Show」にぜひご参加ください。

TV 入力サービスの開発

TV 入力サービスは、メディア ストリーム ソースを表し、メディア コンテンツをチャンネルおよび番組としてリニアなテレビ放送形式で公開することを可能にします。TV 入力サービスを使用すると、保護者による使用制限、番組ガイド情報、コンテンツのレーティングを提供できます。TV 入力サービスは、Android システムの TV アプリで動作します。このアプリは、テレビのチャンネル コンテンツを最終的に制御および公開します。システム TV アプリはデバイス専用に開発されており、サードパーティ アプリで変更することはできません。TV 入力フレームワーク(TIF)アーキテクチャとそのコンポーネントの詳細については、TV 入力フレームワークをご覧ください。

TIF コンパニオン ライブラリを使用して TV 入力サービスを作成する

TIF コンパニオン ライブラリは、一般的な TV 入力サービス機能の拡張可能な実装を提供するフレームワークです。TIF コンパニオン ライブラリを使用すると、Android TV のおすすめの方法に沿った独自の TV 入力サービスを短時間で簡単に作成できます。

プロジェクトを更新する

TIF コンパニオン ライブラリを使用するには、アプリの build.gradle ファイルに次の行を追加します。

    compile 'com.google.android.libraries.tv:companionlibrary:0.2'
    

現在、TIF コンパニオン ライブラリは Android フレームワークに含まれていません。Android SDK ではなく jcenter を介して、Gradle 依存関係として配布されています。jcenter をチェックして、tif-companion ライブラリの最新バージョンを見つけてください。

マニフェストで TV 入力サービスを宣言する

アプリでは、システムがアプリへのアクセスに使用する TvInputService 互換サービスを提供する必要があります。TIF コンパニオン ライブラリには、カスタマイズ可能な TvInputService のデフォルト実装を提供する BaseTvInputService クラスが用意されています。BaseTvInputService のサブクラスを作成し、マニフェストでそのサブクラスをサービスとして宣言します。

サービスが TV 入力をシステムに接続できるようにするため、マニフェストの宣言内で BIND_TV_INPUT 権限を指定します。システム サービスはバインディングを実行し、BIND_TV_INPUT 権限を付与されます。システム TV アプリは、TvInputManager インターフェースを介して TV 入力サービスにリクエストを送信します。

サービス宣言に、インテントで実行するアクションとして 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 入力の初期構成とチャンネル スキャンを表すセットアップ インターフェースが含まれている必要があります。メタデータ ファイルには、ユーザーがコンテンツを録画できるかどうかを示すフラグも含める必要があります。アプリでのコンテンツの録画をサポートする方法については、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 入力サービスでは、ユーザーがシステム 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 コンパニオン ライブラリには、requestImmediateSync() のオーバーロード メソッドが追加で用意されており、同期するチャンネル データの対象時間をミリ秒単位で指定できます。デフォルトのメソッドでは、1 時間分のチャンネル データが同期されます。

TIF コンパニオン ライブラリには、setUpPeriodicSync() のオーバーロード メソッドも追加で用意されており、同期するチャンネル データの対象時間に加えて、定期的に同期を実行する頻度を指定できます。デフォルトのメソッドでは、12 時間おきに 48 時間分のチャンネル データが同期されます。

チャンネル データと EPG の詳細については、チャンネル データの操作をご覧ください。

チューニング リクエストとメディア再生を処理する

ユーザーが特定のチャンネルを選択すると、システム 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 アプリを使用してチャンネルの 1 つを視聴し始めると、システムはセッションの onPlayChannel() メソッドを呼び出します。番組の再生が始まる前に特別なチャンネルの初期化を行う必要がある場合は、このメソッドをオーバーライドします。

次に、システムは現在スケジュールされている番組を取得し、セッションの onPlayProgram() メソッドを呼び出して、番組情報と開始時刻をミリ秒単位で指定します。その後、TvPlayer インターフェースを使用して、番組の再生を開始します。

メディア プレーヤーのコードでは、特定の再生イベントを処理するために、TvPlayer を実装する必要があります。TvPlayer クラスは、BaseTvInputService 実装の複雑さを増すことなく、タイムシフト制御などの機能を処理します。

セッションの getTvPlayer() メソッドで、TvPlayer を実装するメディア プレーヤーを返します。TV 入力サービスのサンプルアプリは、ExoPlayer を使用するメディア プレーヤーを実装します。

TV 入力フレームワークを使用して TV 入力サービスを作成する

TV 入力サービスが TIF コンパニオン ライブラリを使用できない場合は、次のコンポーネントを実装する必要があります。

  • TvInputService: TV 入力に長時間のバックグラウンド可用性を提供します。
  • TvInputService.Session: TV 入力状態を維持し、ホスティング アプリと通信します。
  • TvContract: TV 入力で利用可能なチャンネルと番組について記述します。
  • TvContract.Channels: TV チャンネルに関する情報を表します。
  • TvContract.Programs: 番組タイトルや開始時刻などのデータを含むテレビ番組について記述します。
  • TvTrackInfo: 音声、動画、または字幕トラックを表します。
  • TvContentRating: コンテンツのレーティングについて記述し、カスタムのコンテンツ レーティング スキームを使用できるようにします。
  • TvInputManager: システム TV アプリに API を提供し、TV 入力およびアプリとのやり取りを管理します。

また、次の処理も行う必要があります。

  1. マニフェストで TV 入力サービスを宣言するで説明したように、マニフェストで TV 入力サービスを宣言する。
  2. サービス メタデータ ファイルを作成する。
  3. チャンネルと番組の情報を作成して登録する。
  4. セットアップ アクティビティを作成する。

TV 入力サービスを定義する

サービス用に、TvInputService クラスを拡張します。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 のライフサイクル

ブロックされたコンテンツの操作と保護者による使用制限の適用の詳細については、コンテンツを制御するをご覧ください。TV 入力サービスで処理するシステム駆動型のアクションについては、TvInputManager をご覧ください。

TvInputServiceTvInputService.Session を作成します。このセッションは、Handler.Callback を実装してプレーヤーの状態の変更を処理します。onSetSurface() を使用して、TvInputService.Session は動画コンテンツに Surface を設定します。Surface による動画のレンダリング操作の詳細については、プレーヤーとサーフェスを統合するをご覧ください。

TvInputService.Session は、ユーザーがチャンネルを選択すると onTune() イベントを処理し、コンテンツとコンテンツ メタデータの変更のためにシステム TV アプリに通知します。そうした notify() メソッドについては、このトレーニングのコンテンツを制御するトラック選択を処理するで詳しく説明されています。

セットアップ アクティビティを定義する

システム TV アプリは、TV 入力用に定義したセットアップ アクティビティで動作します。セットアップ アクティビティは必須であり、このアクティビティでシステム データベースに少なくとも 1 つのチャンネル レコードを提供する必要があります。システム TV アプリは、TV 入力用のチャンネルが見つからない場合、セットアップ アクティビティを呼び出します。

次のレッスンであるチャンネル データの作成と更新で示すように、セットアップ アクティビティは、システム TV アプリに対して、TV 入力を通じて利用可能になったチャンネルについて記述します。

その他のリファレンス