Video pratinjau adalah cara efektif untuk mendorong pengguna agar melakukan deep link ke aplikasi TV Anda. Pratinjau dapat berkisar dari klip pendek hingga cuplikan film lengkap.
Saat membuat pratinjau, pertimbangkan beberapa panduan berikut:
- Jangan tampilkan iklan dalam pratinjau. Jika Anda merangkai iklan pada sisi klien, jangan gabungkan menjadi video pratinjau. Jika Anda menyimpan iklan pada sisi server, menyediakan video bebas iklan untuk pratinjau.
- Untuk kualitas terbaik, video pratinjau harus memiliki rasio lebar tinggi 16:9 atau 4:3. Lihat Atribut program video untuk ukuran video pratinjau yang direkomendasikan.
- Jika video pratinjau dan gambar poster memiliki rasio aspek yang berbeda,
layar utama mengubah ukuran tampilan poster ke rasio aspek video sebelum memutar pratinjau.
Video ini tidak memiliki letterbox. Misalnya, jika
rasio gambar poster adalah
ASPECT_RATIO_MOVIE_POSTER
(1:1,441) tetapi rasio videonya adalah 16:9, tampilan poster akan berubah ke wilayah 16:9. - Saat Anda membuat pratinjau, kontennya dapat diakses secara publik atau dilindungi berdasarkan DRM. Prosedur berbeda berlaku di setiap kasus. Halaman ini menjelaskan keduanya.
Memutar pratinjau di layar utama
Jika Anda membuat pratinjau menggunakan salah satu jenis video didukung oleh ExoPlayer dan pratinjau dapat diakses secara publik, Anda dapat memutar pratinjau langsung di layar utama.
Saat Anda membangun PreviewProgram
gunakan setPreviewVideoUri()
dengan HTTPS yang dapat diakses secara 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, menggunakan 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 dapat mengontrol apa yang dirender dan bagaimana tampilannya dirender. Anda dapat menempatkan metadata seperti atribusi saluran sebagai overlay.
Mendeklarasikan TvInputService dalam manifes
Aplikasi Anda harus menyediakan implementasi TvInputService
sehingga layar utama bisa merender pratinjau Anda.
Dalam deklarasi layanan Anda, sertakan filter intent yang menentukan
TvInputService
sebagai tindakan yang akan dilakukan dengan
intent. Deklarasikan juga metadata layanan sebagai resource XML yang terpisah. Tujuan
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 metadata layanan berada di direktori resource XML
untuk aplikasi Anda dan harus cocok dengan nama resource yang Anda deklarasikan dalam
manifes. Dengan menggunakan entri manifes dari contoh sebelumnya, Anda akan
buat file XML di res/xml/previewinputservice.xml
, dengan kolom kosong
Tag tv-input
:
<?xml version="1.0" encoding="utf-8"?>
<tv-input/>
Framework Input TV harus memiliki tag ini. Namun, ini hanya digunakan untuk mengonfigurasi saluran live. Karena Anda merender video, tag harus kosong.
Membuat URI video
Untuk menunjukkan bahwa video pratinjau Anda seharusnya dirender oleh aplikasi, bukan
layar utama Android TV, Anda harus membuat URI video untuk PreviewProgram
.
URI harus diakhiri dengan ID yang digunakan aplikasi Anda untuk konten, sehingga Anda
dapat mengambil kontennya nanti dalam TvInputService
.
Jika ID Anda adalah jenis 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();
Panggilan aplikasi Anda
onTune(Uri videoUri)
agar Android TV memulai video pratinjau.
Membuat layanan
Contoh berikut menunjukkan cara memperluas TvInputService
untuk membuat kueri Anda sendiri
PreviewInputService
. Perhatikan bahwa layanan ini menggunakan MediaPlayer
untuk pemutaran,
tetapi kode Anda dapat menggunakan
pemutar video apa pun 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 } } }