Prévisualiser des vidéos

Un aperçu vidéo est un excellent moyen d'encourager les utilisateurs à ajouter un lien profond vers votre application TV. Il peut s'agir d'extraits courts ou de bandes-annonces de film complètes.

Lorsque vous créez un aperçu, tenez compte des consignes suivantes:

  • Ne pas diffuser d'annonces dans un aperçu Si vous assemblez les annonces côté client, ne les assemblez pas dans des aperçus vidéo. Si vous assemblez les annonces côté serveur, des vidéos sans publicité pour les aperçus.
  • Pour une qualité optimale, les aperçus vidéo doivent être au format 16:9 ou 4:3. Voir Attributs des programmes vidéo pour connaître les tailles recommandées pour les aperçus vidéo.
  • Lorsque l'aperçu vidéo et l'affiche ont des formats différents, écran d'accueil redimensionne l'affichage de l'affiche au format de la vidéo avant la lecture de l'aperçu. La vidéo n'est pas au format letterbox. Par exemple, si le format de l'affiche est ASPECT_RATIO_MOVIE_POSTER (1:1.441) mais que la vidéo est en 16:9, la vue poster se transforme en zone 16:9.
  • Lorsque vous créez un aperçu, son contenu peut être accessible au public ou protégées par la DRM. Des procédures différentes s'appliquent dans chaque cas. Cette page décrit les deux.

Lire l'aperçu sur l'écran d'accueil

Si vous créez un aperçu à l'aide de l'un des types de vidéo compatible avec ExoPlayer et que l'aperçu est accessible publiquement, vous pouvez le lire directement sur l'écran d'accueil.

Lorsque vous créez un PreviewProgram utilisez setPreviewVideoUri() avec un protocole HTTPS comme indiqué dans l'exemple ci-dessous. L'aperçu peut être : video ou 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));

Afficher l'aperçu sur une surface

Si votre vidéo est protégée par DRM ou utilise un type de support non compatible avec ExoPlayer, utilisez un TvInputService. L'écran d'accueil d'Android TV transmet un Surface à votre service en appelant onSetSurface(). Votre application dessine des vidéos directement sur cette surface à partir de onTune().

Le rendu direct de la surface permet à votre application de contrôler ce qui est affiché et comment il est affiché rendu. Vous pouvez superposer des métadonnées telles que l'attribution de la chaîne.

Déclarer TvInputService dans le fichier manifeste

Votre application doit fournir une implémentation de TvInputService pour que votre aperçu s'affiche sur l'écran d'accueil.

Dans votre déclaration de service, incluez un filtre d'intent qui spécifie TvInputService comme action à effectuer avec la l'intention. Déclarez également les métadonnées du service en tant que ressource XML distincte. La la déclaration de service, le filtre d'intent et la déclaration de métadonnées du service sont affichés dans l'exemple suivant:

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

Définissez les métadonnées du service dans un fichier XML distinct. Le fichier de métadonnées du service se trouve dans le répertoire de ressources XML. pour votre application et doit correspondre au nom de la ressource déclarée dans le fichier manifeste. En utilisant les entrées du fichier manifeste de l'exemple précédent, vous devez créez un fichier XML à l'emplacement res/xml/previewinputservice.xml, avec un fichier Balise tv-input:

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

Le framework d'entrée TV doit comporter cette balise. Toutefois, il n'est utilisé que pour configurer les chaînes en direct. Puisque vous effectuez le rendu d'une vidéo, le tag doit être vide.

Créer un URI vidéo

Pour indiquer que l'aperçu vidéo doit être affiché par votre application plutôt que par l'écran d'accueil d'Android TV, vous devez créer un URI vidéo pour un PreviewProgram. L'URI doit se terminer par l'identifiant utilisé par votre application pour le contenu. peut récupérer le contenu ultérieurement dans TvInputService.

Si votre identifiant est de type Long, utilisez 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();

Si votre identifiant n'est pas de type Long, créez l'URI en utilisant 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();

Votre application appelle onTune(Uri videoUri) pour qu'Android TV lance l'aperçu vidéo.

Créer un service

L'exemple suivant montre comment étendre TvInputService pour créer le vôtre PreviewInputService Notez que le service utilise un MediaPlayer pour la lecture, mais votre code peut utiliser n'importe quel lecteur vidéo disponible.

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