動画をプレビューする

プレビュー動画は、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
        }
    }
}