Prévisualiser des vidéos

Un aperçu vidéo est un excellent moyen d'encourager les utilisateurs à créer des liens profonds vers votre application TV. Il peut s'agir de courts extraits ou de bandes-annonces complètes de films.

Lorsque vous créez un aperçu, tenez compte des points suivants:

  • Ne pas diffuser d'annonces dans l'aperçu Si vous assemblez des annonces côté client, ne les assemblez pas dans des vidéos d'aperçu. Si vous insérez des annonces côté serveur, fournissez une vidéo sans publicité pour les aperçus.
  • Pour une qualité optimale, les aperçus vidéo doivent être au format 16:9 ou 4:3. Consultez la section 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, l'écran d'accueil redimensionne la vue poster au format de la vidéo avant de lire l'aperçu. La vidéo n'est pas au format letterbox. Par exemple, si le format de l'affiche est de ASPECT_RATIO_MOVIE_POSTER (1:1,441), mais que le format de la vidéo est de 16:9, la vue poster se transforme en une région 16:9.
  • Lorsque vous créez un aperçu, son contenu peut être accessible publiquement ou protégé par DRM. Différentes procédures 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 compatibles avec ExoPlayer et que l'aperçu est accessible au public, vous pouvez le lire directement sur l'écran d'accueil.

Lorsque vous créez un PreviewProgram, utilisez setPreviewVideoUri() avec une URL HTTPS accessible publiquement, comme illustré dans l'exemple ci-dessous. L'aperçu peut être en vidéo ou en 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 si le type de contenu multimédia n'est pas compatible avec ExoPlayer, utilisez un TvInputService. L'écran d'accueil d'Android TV transmet un Surface à votre service en appelant onSetSurface(). Votre application dessine la vidéo directement sur cette surface à partir de onTune().

Le rendu de surface direct permet à votre application de contrôler le rendu et le mode de rendu. Vous pouvez superposer des métadonnées telles que l'attribution de chaîne.

Déclarer votre TvInputService dans le fichier manifeste

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

Dans votre déclaration de service, incluez un filtre d'intent qui spécifie TvInputService comme action à effectuer avec l'intent. Déclarez également les métadonnées du service en tant que ressource XML distincte. La déclaration de service, le filtre d'intent et la déclaration de métadonnées du service sont illustré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 de votre application et doit correspondre au nom de la ressource que vous avez déclarée dans le fichier manifeste. En utilisant les entrées du fichier manifeste de l'exemple précédent, vous devez créer un fichier XML à l'emplacement res/xml/previewinputservice.xml, avec une balise tv-input vide:

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

Le framework d'entrée TV doit comporter ce tag. Cependant, il n'est utilisé que pour configurer les canaux en direct. Puisque vous affichez une vidéo, la balise doit être vide.

Créer un URI vidéo

Pour indiquer que votre 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 élément PreviewProgram. L'URI doit se terminer par l'identifiant que votre application utilise pour le contenu, afin que vous puissiez le récupérer 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 à l'aide de 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 votre propre PreviewInputService. Notez que le service utilise un MediaPlayer pour la lecture, mais que 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
        }
    }
}