Un video di anteprima è un ottimo modo per incoraggiare gli utenti a utilizzare i deep link alla tua app TV. Le anteprime possono variare da brevi clip a trailer di film interi.
Quando crei un'anteprima, tieni presente queste linee guida:
- Non mostrare annunci in un'anteprima. Se unisci gli annunci lato client, non unirli ai video di anteprima. Se unisci gli annunci lato server, fornisci un video senza annunci per le anteprime.
- Per una qualità ottimale, i video di anteprima devono essere in formato 16:9 o 4:3. Per le dimensioni consigliate dei video di anteprima, consulta Attributi del programma video.
- Quando il video di anteprima e la locandina hanno proporzioni diverse, la schermata Home ridimensiona la visualizzazione del poster in base alle proporzioni del video prima di riprodurre l'anteprima. Il video non è in formato letterbox. Ad esempio, se le proporzioni della locandina sono
ASPECT_RATIO_MOVIE_POSTER(1:1,441), ma le proporzioni del video sono 16:9, la visualizzazione della locandina si trasforma in una regione 16:9. - Quando crei un'anteprima, i suoi contenuti possono essere accessibili pubblicamente o protetti da DRM. In ogni caso si applicano procedure diverse. Questa pagina descrive entrambe le procedure.
Riprodurre l'anteprima nella schermata Home
Se crei un'anteprima utilizzando uno dei tipi di video supportati da ExoPlayer e l'anteprima è accessibile pubblicamente, puoi riprodurla direttamente nella schermata Home.
Quando crei un oggetto PreviewProgram, utilizza setPreviewVideoUri() con un URL HTTPS accessibile pubblicamente
, come mostrato nell'esempio seguente. L'anteprima può essere
un video o un 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));
Eseguire il rendering dell'anteprima su una superficie
Se il video è protetto da DRM o in un tipo di media non supportato da
ExoPlayer, utilizza un oggetto TvInputService. La schermata Home di Android TV passa un
Surface al tuo servizio chiamando onSetSurface(). La tua app disegna
video direttamente su questa superficie da onTune().
Il rendering diretto della superficie consente alla tua app di controllare ciò che viene sottoposto a rendering e come viene eseguito il rendering. Puoi sovrapporre i metadati, ad esempio l'attribuzione del canale.
Dichiarare TvInputService nel manifest
La tua app deve fornire un'implementazione di TvInputService in modo che la schermata
Home possa eseguire il rendering dell'anteprima.
Nella dichiarazione del servizio, includi un filtro per intent che specifichi
TvInputService come azione da eseguire con l'intent. Dichiara anche i metadati del servizio come risorsa XML separata. La dichiarazione del servizio, il filtro per intent e la dichiarazione dei metadati del servizio sono mostrati nell'esempio seguente:
<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 della tua app e deve corrispondere al nome della risorsa dichiarata nel manifest. Utilizzando le voci del manifest dell'esempio precedente, creerai un file XML in res/xml/previewinputservice.xml, con un tag tv-input vuoto:
<?xml version="1.0" encoding="utf-8"?>
<tv-input/>
TV Input Framework deve avere questo tag. Tuttavia, viene utilizzato solo per configurare i canali live. Poiché stai eseguendo il rendering di un video, il tag deve essere vuoto.
Creare un URI video
Per indicare che il video di anteprima deve essere sottoposto a rendering dalla tua app anziché dalla schermata Home di Android TV, devi creare un URI video per un oggetto PreviewProgram.
L'URI deve terminare con l'identificatore utilizzato dalla tua app per i contenuti, in modo da poter recuperare i contenuti in un secondo momento in TvInputService.
Se l'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();
La tua app chiama
onTune(Uri videoUri)
per fare in modo che Android TV avvii il video di anteprima.
Creare un servizio
L'esempio seguente mostra come estendere TvInputService per creare il tuo PreviewInputService. Tieni presente che il servizio utilizza un oggetto 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/<id>
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/<id>
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
}
}
}