テレビ入力サービスを開発する

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

TIF Companion Library を使用してテレビ入力サービスを作成する

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

プロジェクトを更新する

TIF Companion Library を使用するには、アプリの build.gradle ファイルに次の行を追加します。

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

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

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

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

サービスがテレビ入力をシステムに接続できるようにするため、マニフェストの宣言内で BIND_TV_INPUT 権限を指定します。システム サービスはバインディングを実行し、BIND_TV_INPUT 権限を付与されます。 システム テレビアプリは、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" />
    

チャンネルを定義してセットアップ アクティビティを作成する

テレビ入力サービスでは、ユーザーがシステム テレビアプリを介してアクセスするチャンネルを少なくとも 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 の詳細については、チャンネル データを利用するをご覧ください。

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

ユーザーが特定のチャンネルを選択すると、システム テレビアプリは、アプリによって作成された Session を使用し、リクエストされたチャンネルにチューニングを合わせてコンテンツを再生します。TIF Companion Library には、システムからのチャンネルおよびセッション呼び出しを処理するために拡張できるクラスがいくつか用意されています。

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;
    }
    

ユーザーがシステム テレビアプリを使用してチャンネルの 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: システムのテレビアプリに API を提供し、テレビ入力とアプリとのやり取りを管理します。

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

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

テレビ入力サービスを定義する

サービス用に、TvInputService クラスを拡張します。TvInputService 実装は、バインドされたサービスです(システム サービスがクライアントとしてこのサービスにバインドされます)。図 1 に、実装する必要があるサービス ライフサイクル メソッドを示します。

onCreate() メソッドは、HandlerThread(システム駆動のアクションを処理する UI スレッドとは別の処理スレッドを提供する)を初期化して開始します。次の例では、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 をご覧ください。

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

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

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

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

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

その他の関連情報