手順4: Fire TVのUIで再生する
ライブTVには、ユーザーが閲覧の行にあるいずれかのタイルにフォーカスしたときにプレビュー再生できる機能があります。これにより、コンテンツをすばやく簡単にプレビューできます。

プレビュー再生を統合する場合は、ライブTVアプリから提供されるSurface
の使用が可能なプレーヤー内でチャンネルコンテンツを再生する必要があります。
TvInputService.Sessionの作成
ユーザーが特定のチャンネルを選択すると、Session
(英語のみ)を作成するためにTvInputService
クラスが呼び出されます。Sessionは、コンテンツの選択、プレーヤーの準備、チャンネルコンテンツのレンダリングを行うことができる場所です。これをTvInputService
クラス(RichTvInputService
)に追加します。
次のコードは、具体的なSession
クラスの例を示しています。
private class PreviewSession extends TvInputService.Session {
PreviewSession(Context context) {
super(context);
Log.d(Utils.DEBUG_TAG, "セッションが作成されました。");
}
@Override
public boolean onTune(Uri channelUri) {
Log.d(Utils.DEBUG_TAG, "onTune " + channelUri);
...
return false;
}
@Override
public boolean onSetSurface(@Nullable Surface surface) {
Log.d(Utils.DEBUG_TAG, "onSetSurface");
...
return false;
}
}
import android.content.Context
import android.media.tv.TvInputService
import android.net.Uri
import android.util.Log
import android.view.Surface
private const val TAG = "MyTag"
private class PreviewSession(context: Context) : TvInputService.Session(context) {
init {
Log.d(TAG, "セッションが作成されました。")
}
override fun onTune(channelUri: Uri): Boolean {
Log.d(TAG, "onTune $channelUri")
return false
}
override fun onSetSurface(surface: Surface?): Boolean {
Log.d(TAG, "onSetSurface")
return false
}
override fun onSetCaptionEnabled(enabled: Boolean) {
TODO("まだ実装されていません")
}
override fun onRelease() {
TODO("まだ実装されていません")
}
override fun onSetStreamVolume(volume: Float) {
TODO("まだ実装されていません")
}
}
正しいチャンネルの特定
onTune()
コールバックでchannelId
を使用すると、ユーザーが現在選択しているチャンネルを特定できます。
次のコードは、channelUri
からチャンネルIDを取得する方法の例を示しています。
long channelId = Long.parseLong(channelUri.getLastPathSegment());
import android.net.Uri
val channelUri: Uri = TODO()
var channelId: Long? = channelUri.lastPathSegment?.toLong()
チャンネルIDは、AndroidのTVデータベースにチャンネルを挿入する際にAndroidから自動的に割り当てられるIDです。チャンネルがTVデータベースに挿入されたときにAndroidから割り当てられるチャンネルIDと、使用するチャンネルIDとの間のマップは必ず維持してください。こうすることで、チャンネルIDを使用して、いつでも正しいチャンネルコンテンツを見つけることができます。
プレビューでのチャンネルコンテンツの再生
構成可能なサーフェスでTVフィードを再生できるメディアプレーヤーを実装します。
ユーザーが閲覧中に特定のチャンネルカードにフォーカスを置くと、前の手順で作成したSessionがライブアプリで使用されます。アプリでは、TvInputService.Session
クラスの一部としてセッションを定義し、リクエストされたチャンネルへの変更やコンテンツの再生に使用します。
メディアプレーヤーの作成
メディアプレーヤーの実装にはいくつかの選択肢があります。アプリ内には、直接使用できる明確に定義されたメディアプレーヤーが必要です。使用するメディアプレーヤーについては、TVフィードを再生でき、カスタマイズされたSurface
クラスを使用するように構成できるプレーヤーであれば、厳しい要件はありません。詳細については、#onSetSurfaceを参照してください。このように、プレーヤーの実装は状況に応じて異なるため、以下の選択肢はあくまでも参考として紹介するものです。
ExoPlayer:基盤となるメディアプレーヤーの候補として適しています。
SampleTvAppのDemoPlayer:TVチャンネルの変更をサポートするメディアプレーヤーを作成するためにExoPlayerを使用している例です。
SampleTvAppのTvInputService内のDemoPlayer:カスタマイズしたメディアプレーヤーをTvInputService
で定義する方法の例です。
onSetSurface
チャンネル変更プロセス中に、AndroidのTIFフレームワークによってonSetSurface(@Nullable Surface surface)
コールバックが呼び出されます。このコールバックはTvInputService.Session
に定義されています(前の手順を参照)。開発者は、提供されたSurface
インスタンスをメディアプレーヤーに設定する必要があります。これはプレビューの再生に使用されます。
onTune
次に、onTune(Uri channelUri)
コールバックが呼び出されます。ここで正しいチャンネルを特定し(正しいチャンネルの特定を参照)、対応するチャンネルフィードを取得して、用意ができたらフィードを再生するようにメディアプレーヤーを準備します。
onTune
が再度呼び出されることはありません。2回目の再生でonTune()
を呼び出さない場合の一般的なシナリオには、次のようなものがあります。
- ユーザーがチャンネルカードにフォーカスを移した場合。
onTune()
からプレビュー再生が呼び出されます。 - ユーザーが現在のカードをクリックして全画面再生を開始した場合。
- 全画面再生がシームレスに全画面で表示された場合。この場合、別の
onTune()
は受け取りません。
チャンネル変更ステータスの通知
プレーヤーの読み込みステータスに基づいて、最新のチャンネル変更ステータスをTvInputService
に通知する必要があります。Fire TVのライブアプリは、このステータスを参照して、プレビューUIを調整します。
以下は、チャンネル変更ステータスを通知する例を示しています。この場合、ステータスは「一時的に利用不可」になります。 このコードはTvInputService.Session
に配置します。
@Override
public boolean onTune(Uri channelUri) {
Log.d(Utils.DEBUG_TAG, "onTune " + channelUri);
// ビデオが読み込まれていることをTvInputServiceに知らせます。
notifyVideoUnavailable(VIDEO_UNAVAILABLE_REASON_TUNING);
}
override fun onTune(channelUri: Uri): Boolean {
Log.d(TAG, "onTune $channelUri")
notifyVideoUnavailable(VIDEO_UNAVAILABLE_REASON_TUNING)
return true
}
次のコードは、ビデオの利用可能ステータスを通知する例を示しています。
notifyTracksChanged(getAllTracks());
String audioId = getTrackId(TvTrackInfo.TYPE_AUDIO,
mPlayer.getSelectedTrack(TvTrackInfo.TYPE_AUDIO));
String videoId = getTrackId(TvTrackInfo.TYPE_VIDEO,
mPlayer.getSelectedTrack(TvTrackInfo.TYPE_VIDEO));
String textId = getTrackId(TvTrackInfo.TYPE_SUBTITLE,
mPlayer.getSelectedTrack(TvTrackInfo.TYPE_SUBTITLE));
notifyTrackSelected(TvTrackInfo.TYPE_AUDIO, audioId);
notifyTrackSelected(TvTrackInfo.TYPE_VIDEO, videoId);
notifyTrackSelected(TvTrackInfo.TYPE_SUBTITLE, textId);
notifyVideoAvailable();
val audioId: String = getTrackId(
TvTrackInfo.TYPE_AUDIO,
mPlayer.getSelectedTrack(TvTrackInfo.TYPE_AUDIO)
)
val videoId: String = getTrackId(
TvTrackInfo.TYPE_VIDEO,
mPlayer.getSelectedTrack(TvTrackInfo.TYPE_VIDEO)
)
val textId: String = getTrackId(
TvTrackInfo.TYPE_SUBTITLE,
mPlayer.getSelectedTrack(TvTrackInfo.TYPE_SUBTITLE)
)
notifyTrackSelected(TvTrackInfo.TYPE_AUDIO, audioId)
notifyTrackSelected(TvTrackInfo.TYPE_VIDEO, videoId)
notifyTrackSelected(TvTrackInfo.TYPE_SUBTITLE, textId)
notifyVideoAvailable()
ペアレンタルコントロール
商品の要件に従って、ペアレンタルコントロール(PCON)が有効になっている場合はプレビュービデオを再生しないようにします。
次のコードは、ペアレンタルコントロールをリッスンして、ライブプレビューやネイティブの全画面再生に反映させる方法を示しています。
private TvContentRating mBlockedRating = null;
@Override
public boolean onTune(final Uri channelUri) {
...
if (mTvInputManager.isParentalControlsEnabled()) {
// サーフェスで音声や画像が再生されないようにします
mBlockedRating = < content_rating > ;
notifyContentBlocked(mBlockedRating);
} else {
// 再生が開始されます
notifyContentAllowed();
}
...
}
@Override
public void onUnblockContent(final TvContentRating unblockedRating) {
// ユーザーのPIN入力により、指定のレーティングに該当するコンテンツのブロックが
// 正常に解除されました
if (unblockedRating.unblockContent(mBlockedRating)) {
// 再生が開始されます
notifyContentAllowed();
}
}
import android.content.Context
import android.media.tv.TvContentRating
import android.media.tv.TvInputManager
import android.media.tv.TvInputService
import android.net.Uri
import android.view.Surface
private const val TAG = "MyTag"
private class PreviewSession(context: Context) : TvInputService.Session(context) {
private val tvInputManager: TvInputManager = TODO()
override fun onTune(channelUri: Uri): Boolean {
if (tvInputManager.isParentalControlsEnabled) {
// サーフェスで音声や画像が再生されないようにします
val blockedRating = getContentRating(channelUri)
notifyContentBlocked(blockedRating)
} else {
// 再生が開始されます
notifyContentAllowed()
}
return true
}
override fun onUnblockContent(unblockedRating: TvContentRating) {
// ユーザーのPIN入力により、指定のレーティングに該当するコンテンツのブロックが
// 正常に解除されました
if (unblockedRating.unblockContent(blockedRating)) { // <-- これは何か?
// 再生が開始されます
notifyContentAllowed()
}
}
}
private fun getContentRating(channelUri: Uri): TvContentRating = TODO()
アクティビティ | 必須・任意 | 備考 |
---|---|---|
mTvInputManager.isParentalControlsEnabled() |
必須 | このメソッドはPCONステータスを確認するために呼び出されます。 |
notifyContentBlocked() |
状況依存 | このメソッドはPCONによりビデオの再生がブロックされた場合に呼び出されます。 |
notifyContentAllowed() |
状況依存 | このメソッドはビデオの再生が可能な場合に呼び出されます。 |
チェックポイント: 閲覧時のプレビュー再生
- APKをビルドしてFire TVにインストールします。
- [放映中のチャンネル] 行に移動し、チャンネルカードにフォーカスを置きます。右上隅でプレビュー再生が開始されます。
- ディープリンクを使用しない場合:チャンネルカードを選択すると、全画面で再生が続行されます。
- [機能制限] メニューに移動して、機能制限を有効にします。
- [放映中のチャンネル] 行に戻り、チャンネルカードにフォーカスを置きます。プレビュー再生は開始されません。ただし、ポスターアートがある場合は閲覧画面に表示されます。
- ディープリンクを使用しない場合:チャンネルカードを選択すると、PINの入力を求めるプロンプトが表示されます。PINを入力すると、全画面再生が開始されます。
トラブルシューティング
このセクションでは、発生する可能性のある問題のトラブルシューティング手順を説明します。
- チャンネルカードにフォーカスを置いても
onTune()
コールバックがトリガーされない - 該当するチャンネルの入力IDを確認してください。
InputId
が正しくない場合、AndroidはTvInputService
を識別して呼び出すことができません。 onTune()
が呼び出されているのにプレビューの再生が開始されない-
- プレーヤーがフィードを再生できるよう正しく実装されているかどうかを確認します。
notifyVideoAvailable()
を呼び出して「準備完了」のチャンネル変更ステータスを通知していることを確認します。
- ペアレンタルコントロール(PCON)を有効にしても、閲覧セクションで引き続きプレビューが再生される
TvInputService
にペアレンタルコントロールのコードが正しく実装されていることを確認してください。PCONが有効になっている場合はプレーヤーを停止し、notifyContentBlocked()
を使用してUIに通知します。詳細については、ライブTVのリソースを参照してください。- ペアレンタルコントロール(PCON)を有効にすると、プレビュー再生だけでなくポスター画像も表示されなくなる(黒い背景のみが表示される)
- チャンネルで表示できる有効なポスターアートがあることを確認してください。現在放送中の番組の画像が表示されることが想定されています。
-
画像がある場合は、
notifyContentBlocked()
を呼び出していることを確認してください。PCONのステータスに関する通知を送信しないと、UIは更新されず、ポスター画像は使用されません。
Last updated: 2025年5月5日