Un video di anteprima è un ottimo modo per incoraggiare gli utenti a inserire link diretti alla tua app TV. Le anteprime possono spaziare da brevi clip a trailer di film completi.
Quando crei un'anteprima, tieni presente le seguenti linee guida:
- Non mostrare gli annunci in un'anteprima. Se cuci gli annunci sul lato client, non unirli nei video di anteprima. Se stichi gli annunci sul lato server, offrire un video senza annunci per le anteprime.
- Per una qualità ottimale, i video di anteprima devono essere in formato 16:9 o 4:3. Consulta Attributi dei programmi video per conoscere le dimensioni consigliate dei video di anteprima.
- Quando il video di anteprima e la locandina hanno proporzioni diverse,
nella schermata Home la visualizzazione poster viene ridimensionata alle proporzioni del video prima di riprodurre l'anteprima.
Il video non è letterbox. Ad esempio, se
la proporzione della locandina è
ASPECT_RATIO_MOVIE_POSTER
(1:1,441) ma le proporzioni del video sono 16:9, la visualizzazione poster si trasforma in una regione 16:9. - Quando crei un'anteprima, i relativi contenuti possono essere accessibili pubblicamente oppure protette da DRM. A ogni caso si applicano procedure diverse. Questa pagina entrambi.
Riproduci l'anteprima sulla schermata Home
Se crei un'anteprima utilizzando uno qualsiasi dei tipi di video supportato da ExoPlayer e l'anteprima è accessibile pubblicamente, puoi riprodurla direttamente sulla schermata Home.
Quando crei un Programma Anteprima
utilizza setPreviewVideoUri()
con un HTTPS accessibile pubblicamente
URL come mostrato nell'esempio riportato di seguito. L'anteprima può essere
video o
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));
Visualizza l'anteprima su una piattaforma
Se il tuo video è protetto da DRM o in un tipo multimediale non supportato da
ExoPlayer, utilizza un TvInputService
.
La schermata Home di Android TV trasmette un Surface
al tuo servizio
chiamando il numero onSetSurface()
. La tua app disegna video direttamente su questa piattaforma da onTune()
.
Il rendering diretto della superficie consente all'app di controllare cosa viene visualizzato e come eseguire il rendering. Puoi sovrapporre metadati come l'attribuzione del canale.
Dichiara il tuo TvInputService nel manifest
La tua app deve fornire un'implementazione di TvInputService
per consentire il rendering dell'anteprima nella schermata Home.
Nella dichiarazione del servizio, includi un filtro per intent che specifichi
TvInputService
come azione da eseguire con
l'intento. Dichiara anche i metadati del servizio come risorsa XML separata. La
vengono mostrate la dichiarazione del servizio, il filtro per intent e la dichiarazione dei metadati del servizio
nel seguente esempio:
<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>
Definisci i metadati del servizio in un file XML separato.
Il file dei metadati del servizio si trova nella directory delle risorse XML
per la tua app e deve corrispondere al nome della risorsa che hai dichiarato
del file manifest. Utilizzando le voci manifest dell'esempio precedente,
crea un file XML all'indirizzo res/xml/previewinputservice.xml
, con un
Tag tv-input
:
<?xml version="1.0" encoding="utf-8"?>
<tv-input/>
TV Input Framework deve avere questo tag. Tuttavia, viene utilizzato solo per configurare i canali dal vivo. Dal momento che stai eseguendo il rendering di un video, il tag deve essere vuoto.
Crea un URI del video
Per indicare che il video di anteprima dovrebbe essere visualizzato dall'app anziché
nella schermata Home di Android TV, devi creare un URI video per un PreviewProgram
.
L'URI deve terminare con l'identificatore utilizzato dalla tua app per i contenuti, quindi
può recuperare i contenuti in un secondo momento in TvInputService
.
Se il tuo identificatore è di tipo Long
, utilizza
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();
Se l'identificatore non è di tipo Long
, crea l'URI utilizzando
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();
Chiamate dalla tua app
onTune(Uri videoUri)
per avviare la riproduzione del video di anteprima su Android TV.
Creare un servizio
L'esempio seguente mostra come estendere TvInputService
per crearne una personalizzata
PreviewInputService
. Tieni presente che il servizio utilizza MediaPlayer
per la riproduzione,
ma il tuo codice può utilizzare qualsiasi video player disponibile.
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 } } }