プレビュー動画は、TV アプリへのディープリンクをユーザーに促すのに効果的な方法です。 プレビューには、短いクリップから完全な映画の予告編まで、さまざまなものがあります。
プレビューを作成する際は、次のガイドラインに沿って進めてください。
- プレビュー内に広告を表示しないでください。クライアントサイドで広告を合成する場合 プレビュー動画につなぎ合わせないでください。サーバーサイドで広告をつなぎ合わせると プレビュー用に広告なしの動画を提供する
- 最高の品質を実現するため、プレビュー動画は 16:9 または 4:3 にします。詳しくは、 動画プログラムの属性 をご覧ください。
- プレビュー動画とポスターアートのアスペクト比が異なる場合、
プレビューを再生する前に、ホーム画面でポスタービューのサイズを動画のアスペクト比に変更します。
動画はレターボックス化されません。たとえば
ポスターアートの比率は
ASPECT_RATIO_MOVIE_POSTER
(1:1.441) 動画の比率が 16:9 の場合、ポスタービューは 16:9 の領域に変換されます。 - プレビューを作成すると、そのコンテンツは一般公開されるか、 保護されています。それぞれの場合で異なる手順が適用されます。このページ どちらも説明しました。
ホーム画面でプレビューを再生する
いずれかの動画タイプを使用してプレビューを作成する場合 ExoPlayer がサポート プレビューが一般公開されている場合は、ホーム画面で直接プレビューを再生できます。
PreviewProgram をビルドする場合
一般公開されている HTTPS で setPreviewVideoUri()
を使用する
次のような URL が生成されます。プレビューは次のいずれかです。
動画または
audio。
val previewVideoUrl = Uri.parse("https://www.example.com/preview.mp4") val builder = PreviewProgram.Builder() builder.setChannelId(channelId) // ... .setPreviewVideoUri(previewVideoUrl)
Uri previewVideoUrl = Uri.parse("https://www.example.com/preview.mp4"); PreviewProgram.Builder builder = new PreviewProgram.Builder(); builder.setChannelId(channelId) // ... .setPreviewVideoUri(Uri.parse(previewVideoUrl));
プレビューをサーフェス上にレンダリングする
動画が DRM で保護されたものである場合や、
ExoPlayer には、TvInputService
を使用します。
Android TV のホーム画面が Surface
をサービスに渡します。
onSetSurface()
を呼び出します。アプリは、onTune()
から直接このサーフェスに動画を描画します。
ダイレクト サーフェス レンダリングを使用すると、レンダリングされる内容と方法をアプリで制御できます。 表示されます。チャンネル属性などのメタデータをオーバーレイできます。
マニフェスト内で TvInputService を宣言する
アプリで TvInputService
の実装を提供する必要があります。
プレビューをレンダリングできます
サービスの宣言に、指定するインテント フィルタを含めます。
TvInputService
で実行するアクションとして、
使用します。また、サービス メタデータを個別の XML リソースとして宣言します。「
サービス宣言、インテント フィルタ、サービス メタデータの宣言が表示されています
使用します。
<service android:name=".rich.PreviewInputService" 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. --> <meta-data android:name="android.media.tv.input" android:resource="@xml/previewinputservice" /> </service>
サービス メタデータを別の XML ファイルで定義します。
サービス メタデータ ファイルは XML リソース ディレクトリにある
で宣言したリソースの名前と一致する必要があります。
使用します。前の例のマニフェスト エントリを使用して、次の操作を行います。
res/xml/previewinputservice.xml
に、空のファイルを含む XML ファイルを
tv-input
タグ:
<?xml version="1.0" encoding="utf-8"?>
<tv-input/>
テレビ入力フレームワークには、このタグが必要です。ただし、 ライブ チャンネルの設定にのみ使用されます。ここでは動画をレンダリングするため タグは空にする必要があります。
動画 URI を作成する
プレビュー動画をアプリの
PreviewProgram
の動画 URI を作成する必要があります。
URI の末尾は、アプリがコンテンツに使用する識別子で終わる必要があります。
後で TvInputService
でコンテンツを取得できます。
識別子のタイプが Long
の場合は、次のコマンドを使用します。
TvContractCompat.buildPreviewProgramUri():
val id: Long = 1L // content identifier val componentName = new ComponentName(context, PreviewVideoInputService.class) val previewProgramVideoUri = TvContractCompat.buildPreviewProgramUri(id) .buildUpon() .appendQueryParameter("input", TvContractCompat.buildInputId(componentName)) .build()
Long id = 1L; // content identifier ComponentName componentName = new ComponentName(context, PreviewVideoInputService.class); previewProgramVideoUri = TvContractCompat.buildPreviewProgramUri(id) .buildUpon() .appendQueryParameter("input", TvContractCompat.buildInputId(componentName)) .build();
識別子が Long
型でない場合は、次のコマンドを使用して URI を作成します。
Uri.withAppendedPath()
:
val previewProgramVideoUri = Uri.withAppendedPath(PreviewPrograms.CONTENT_URI, "content-identifier") .buildUpon() .appendQueryParameter("input", TvContractCompat.buildInputId(componentName)) .build()
previewProgramVideoUri = Uri.withAppendedPath(PreviewPrograms.CONTENT_URI, "content-identifier") .buildUpon() .appendQueryParameter("input", TvContractCompat.buildInputId(componentName)) .build();
アプリからの呼び出し
onTune(Uri videoUri)
Android TV でプレビュー動画を開始します。
サービスを作成する
次の例は、TvInputService
を拡張して独自のものを作成する方法を示しています。
PreviewInputService
。なお、サービスは再生に MediaPlayer
を使用しますが、
使用可能な動画プレーヤーであればどれでも
コードで使用できます
import android.content.Context import android.media.MediaPlayer import android.media.tv.TvInputService import android.net.Uri import android.util.Log import android.view.Surface import java.io.IOException class PreviewVideoInputService : TvInputService() { override fun onCreateSession(inputId: String): TvInputService.Session? { return PreviewSession(this) } private inner class PreviewSession( internal var context: Context ) : TvInputService.Session(context) { internal var mediaPlayer: MediaPlayer? = MediaPlayer() override fun onRelease() { mediaPlayer?.release() mediaPlayer = null } override fun onTune(uri: Uri): Boolean { // Let the TvInputService know that the video is being loaded. notifyVideoUnavailable(VIDEO_UNAVAILABLE_REASON_TUNING) // Fetch the stream url from the TV Provider database // for content://android.media.tv/preview_program/val id = uri.lastPathSegment // Load your video in the background. retrieveYourVideoPreviewUrl(id) { videoUri -> if (videoUri == null) { Log.d(TAG, "Could not find video $id") notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN) } try { mPlayer.setDataSource(getApplicationContext(), videoUri) mPlayer.prepare() mPlayer.start() notifyVideoAvailable() } catch (IOException e) { Log.e(TAG, "Could not prepare media player", e) notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN) } } return true } override fun onSetSurface(surface: Surface?): Boolean { mediaPlayer?.setSurface(surface) return true } override fun onSetStreamVolume(volume: Float) { // The home screen may fade in and out the video's volume. // Your player should be updated accordingly. mediaPlayer?.setVolume(volume, volume) } override fun onSetCaptionEnabled(b: Boolean) { // enable/disable captions here } } companion object { private const val TAG = "PreviewInputService" } }
import android.content.Context; import android.media.MediaPlayer; import android.media.tv.TvInputService; import android.net.Uri; import android.support.annotation.Nullable; import android.util.Log; import android.view.Surface; import java.io.IOException; public class PreviewVideoInputService extends TvInputService { private static final String TAG = "PreviewVideoInputService"; @Nullable @Override public Session onCreateSession(String inputId) { return new PreviewSession(this); } private class PreviewSession extends TvInputService.Session { private MediaPlayer mPlayer; PreviewSession(Context context) { super(context); mPlayer = new MediaPlayer(); } @Override public boolean onTune(Uri channelUri) { // Let the TvInputService know that the video is being loaded. notifyVideoUnavailable(VIDEO_UNAVAILABLE_REASON_TUNING); // Fetch the stream url from the TV Provider database // for content://android.media.tv/preview_program/String id = uri.getLastPathSegment(); // Load your video in the background. retrieveYourVideoPreviewUrl(id, new MyCallback() { public void callback(Uri videoUri) { if (videoUri == null) { Log.d(TAG, "Could not find video" + id); notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN); } try { mPlayer.setDataSource(getApplicationContext(), videoUri); mPlayer.prepare(); mPlayer.start(); notifyVideoAvailable(); } catch (IOException e) { Log.e(TAG, "Could not prepare media player", e); notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN); } } }); return true; } @Override public boolean onSetSurface(@Nullable Surface surface) { if (mPlayer != null) { mPlayer.setSurface(surface); } return true; } @Override public void onRelease() { if (mPlayer != null) { mPlayer.release(); } mPlayer = null; } @Override public void onSetStreamVolume(float volume) { if (mPlayer != null) { // The home screen may fade in and out the video's volume. // Your player should be updated accordingly. mPlayer.setVolume(volume, volume); } } @Override public void onSetCaptionEnabled(boolean enabled) { // enable/disable captions here } } }