Visualizza l'anteprima dei video

Un video di anteprima è un ottimo modo per incoraggiare gli utenti a utilizzare link diretti alla tua app TV. Le anteprime possono variare da brevi clip a trailer di film completi.

Quando crei un'anteprima, tieni presente queste linee guida:

  • Non mostrare gli annunci in anteprima. Se unisci annunci sul lato client, non farlo in video di anteprima. Se aggiungi annunci sul 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. Consulta la sezione Attributi dei programmi video per conoscere le dimensioni consigliate dei video di anteprima.
  • Quando il video di anteprima e la locandina hanno proporzioni diverse, la schermata Home ridimensiona la visualizzazione poster in base alle proporzioni del video prima di riprodurre l'anteprima. Il video non è letterbox. Ad esempio, se il rapporto della locandina è ASPECT_RATIO_MOVIE_POSTER (1:1,441), ma quello del video è 16:9, la visualizzazione poster si trasforma in un'area geografica 16:9.
  • Quando crei un'anteprima, i relativi contenuti possono essere accessibili pubblicamente o protetti con DRM. Si applicano procedure diverse per ciascun caso. Questa pagina descrive entrambi.

Riproduci 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 PreviewProgram, utilizza setPreviewVideoUri() con un URL HTTPS accessibile pubblicamente, come mostrato nell'esempio seguente. 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));

Eseguire il rendering dell'anteprima su una superficie

Se il video è protetto da DRM o in un tipo di supporto non supportato da ExoPlayer, utilizza un TvInputService. La schermata Home di Android TV trasmette un messaggio Surface al tuo servizio chiamando il numero onSetSurface(). L'app realizza video direttamente su questa piattaforma a partire da onTune().

Il rendering diretto della superficie consente all'app di controllare gli elementi sottoposti a rendering e la modalità di rendering. Puoi sovrapporre metadati come l'attribuzione del canale.

Dichiara il tuo TvInputService nel file 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. Inoltre, dichiara i metadati del servizio come risorsa XML separata. La dichiarazione del servizio, il filtro di intent e la dichiarazione dei metadati di 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 di metadati del servizio si trova nella directory delle risorse XML dell'app e deve corrispondere al nome della risorsa dichiarata nel file manifest. Utilizzando le voci 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 Dirette TV. Dal momento che stai eseguendo il rendering di un video, il tag dovrebbe essere vuoto.

Crea un URI video

Per indicare che il rendering del video di anteprima deve essere eseguito dalla tua app anziché dalla schermata Home di Android TV, devi creare un URI video per un elemento PreviewProgram. L'URI deve terminare con l'identificatore utilizzato dall'app per i contenuti, in modo che tu possa 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();

L'app chiama onTune(Uri videoUri) per fare in modo che Android TV avvii il video di anteprima.

Crea un servizio

L'esempio seguente mostra come estendere TvInputService per creare la tua PreviewInputService. Tieni presente che il servizio utilizza un MediaPlayer per la riproduzione, ma con il tuo codice puoi usare 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
        }
    }
}