Melihat pratinjau video

Video pratinjau adalah cara tepat untuk mendorong pengguna agar melakukan deep link ke aplikasi TV Anda. Pratinjau dapat bervariasi, dari klip singkat hingga cuplikan film penuh.

Saat membuat pratinjau, pertimbangkan beberapa panduan berikut:

  • Jangan tampilkan iklan dalam pratinjau. Jika Anda merangkai iklan pada sistem klien, jangan rangkai iklan tersebut ke dalam video pratinjau. Jika merangkai iklan pada sistem server, sediakan video bebas iklan untuk pratinjau.
  • Untuk kualitas terbaik, video pratinjau harus memiliki rasio tinggi lebar 16:9 atau 4:3. Lihat Atribut program video untuk ukuran video pratinjau yang direkomendasikan.
  • Jika video pratinjau dan gambar poster memiliki rasio tinggi lebar yang berbeda, layar utama akan mengubah ukuran tampilan poster menurut rasio tinggi lebar video sebelum memutar pratinjau. Video ini tidak memiliki letterbox. Misalnya, jika rasio gambar poster adalah ASPECT_RATIO_MOVIE_POSTER (1:1.441), tetapi rasio video adalah 16:9, tampilan poster akan berubah ke kisaran 16:9.
  • Saat Anda membuat pratinjau, kontennya dapat diakses secara publik atau dilindungi berdasarkan DRM. Prosedur berbeda berlaku di setiap kasus. Halaman ini menjelaskan kedua prosedur tersebut.

Memutar pratinjau di layar utama

Jika Anda membuat pratinjau menggunakan salah satu jenis video yang didukung oleh ExoPlayer, dan pratinjau tersebut dapat diakses secara publik, Anda dapat memutar pratinjau langsung di layar utama.

Saat Anda membuat PreviewProgram, gunakan setPreviewVideoUri() dengan URL HTTPS yang dapat diakses publik seperti yang ditunjukkan dalam contoh di bawah ini. Pratinjau dapat berupa video atau audio.

Kotlin

val previewVideoUrl = Uri.parse("https://www.example.com/preview.mp4")
    val builder = PreviewProgram.Builder()
    builder.setChannelId(channelId)
        // ...
        .setPreviewVideoUri(previewVideoUrl)
    

Java

Uri previewVideoUrl = Uri.parse("https://www.example.com/preview.mp4");
    PreviewProgram.Builder builder = new PreviewProgram.Builder();
    builder.setChannelId(channelId)
        // ...
        .setPreviewVideoUri(Uri.parse(previewVideoUrl));

Merender pratinjau di permukaan

Jika video Anda dilindungi DRM atau menggunakan jenis media yang tidak didukung oleh ExoPlayer, gunakan TvInputService. Layar utama Android TV akan meneruskan Surface ke layanan Anda dengan memanggil onSetSurface(). Aplikasi Anda akan mengambil video secara langsung pada permukaan ini dari onTune().

Dengan rendering permukaan langsung, aplikasi Anda akan dapat mengontrol apa yang dirender dan bagaimana merendernya. Anda dapat menempatkan metadata seperti atribusi saluran sebagai overlay.

Mendeklarasikan TvInputService dalam manifes

Aplikasi Anda harus menyediakan implementasi TvInputService agar layar utama dapat merender pratinjau.

Dalam deklarasi layanan Anda, sertakan filter intent yang menentukan TvInputService sebagai tindakan yang akan dijalankan bersama intent. Deklarasikan juga metadata layanan sebagai resource XML yang terpisah. Deklarasi layanan, filter intent, dan deklarasi metadata layanan ditampilkan dalam contoh berikut:

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

Tentukan metadata layanan dalam file XML terpisah. File layanan metadata terletak di direktori resource XML untuk aplikasi Anda dan harus sesuai dengan nama resource yang Anda deklarasikan di manifes. Dengan menggunakan entri manifes dari contoh sebelumnya, sebaiknya buat file XML di res/xml/previewinputservice.xml, dengan tag tv-input kosong:

<?xml version="1.0" encoding="utf-8"?>
    <tv-input/>
    

Framework Input TV harus memiliki tag ini. Namun, tag ini hanya digunakan untuk mengonfigurasi live TV. Karena Anda merender video, tag ini harus kosong.

Membuat URI video

Untuk menunjukkan bahwa video pratinjau Anda akan dirender oleh aplikasi, bukan oleh layar utama Android TV, Anda harus membuat URI video untuk PreviewProgram. URI ini harus diakhiri dengan ID yang digunakan aplikasi Anda untuk konten, agar Anda dapat mengambil kembali konten tersebut nanti dalam TvInputService.

Jika ID Anda berjenis Long, gunakan TvContractCompat.buildPreviewProgramUri():

Kotlin

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()
    

Java

Long id = 1L; // content identifier
    ComponentName componentName = new ComponentName(context, PreviewVideoInputService.class);
    previewProgramVideoUri = TvContractCompat.buildPreviewProgramUri(id)
           .buildUpon()
           .appendQueryParameter("input", TvContractCompat.buildInputId(componentName))
           .build();

Jika ID Anda tidak berjenis Long, buat URI menggunakan Uri.withAppendedPath():

Kotlin

    val previewProgramVideoUri = Uri.withAppendedPath(PreviewPrograms.CONTENT_URI, "content-identifier")
           .buildUpon()
           .appendQueryParameter("input", TvContractCompat.buildInputId(componentName))
           .build()

Java

    previewProgramVideoUri = Uri.withAppendedPath(PreviewPrograms.CONTENT_URI, "content-identifier")
           .buildUpon()
           .appendQueryParameter("input", TvContractCompat.buildInputId(componentName))
           .build();

Aplikasi Anda akan memanggil onTune(Uri videoUri) untuk meminta Android TV mulai memutar pratinjau video.

Membuat layanan

Contoh berikut menunjukkan cara memperluas TvInputService untuk membuat PreviewInputService Anda sendiri. Perhatikan bahwa layanan ini menggunakan MediaPlayer untuk pemutaran, tetapi kode Anda masih dapat menggunakan semua pemutar video yang tersedia.

Kotlin

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

Java

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