Cómo obtener vistas previas de videos

Un video de vista previa es una excelente manera de alentar a los usuarios a acceder mediante un vínculo directo a tu app para TV. Las vistas previas pueden ser desde clips cortos hasta avances completos de películas.

Cuando crees una vista previa, ten en cuenta los siguientes lineamientos:

  • No muestres anuncios en una vista previa. Si unes anuncios del lado del cliente, no las unas en videos de vista previa. Si agregas anuncios del servidor, proporcionar un video sin anuncios para las vistas previas.
  • Para obtener la mejor calidad, los videos de vista previa deberían ser de 16:9 o 4:3. Consulta Atributos de programación de video para los tamaños recomendados de los videos de vista previa.
  • Cuando el video de vista previa y el afiche tienen diferentes relaciones de aspecto, el la pantalla principal cambia el tamaño de la vista de póster a la relación de aspecto del video antes de reproducir la vista previa. El video no tiene formato letterbox. Por ejemplo, la proporción de afiches de arte es ASPECT_RATIO_MOVIE_POSTER (1:1.441) pero la relación del video es de 16:9, la vista del póster se transforma a una región de 16:9.
  • Cuando creas una vista previa, el contenido puede ser de acceso público o están protegidos por DRM. En cada caso, se aplican diferentes procedimientos. Esta página describe ambos.

Reproduce la vista previa en la pantalla principal

Si creas una vista previa con cualquiera de los tipos de video compatible con ExoPlayer y la vista previa es de acceso público, puedes reproducirla directamente en la pantalla de inicio.

Cuando compilas un PreviewProgram. usa setPreviewVideoUri() con un protocolo HTTPS de acceso público como se muestra en el siguiente ejemplo. La vista previa puede ser de 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));

Procesa la vista previa en una superficie

Si tu video está protegido por DRM o se encuentra en un tipo de archivo multimedia no compatible con ExoPlayer, usa un TvInputService. La pantalla principal de Android TV pasa un Surface a tu servicio. llamando a onSetSurface(). Tu app obtiene los videos directamente en esta superficie desde onTune().

La renderización en superficie directa permite que tu app controle qué se renderiza y cómo se hace. se renderizan. Puedes superponer metadatos, como una atribución de canal.

Declara tu TvInputService en el manifiesto

Tu app debe proporcionar una implementación de TvInputService para que la pantalla principal renderice la vista previa.

En tu declaración de servicio, incluye un filtro de intents que especifique TvInputService como la acción que se debe realizar con el . También declara los metadatos del servicio como recurso XML independiente. El Se muestran la declaración del servicio, el filtro de intents y la declaración de metadatos del servicio. en el siguiente ejemplo:

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

Define los metadatos del servicio en un archivo XML independiente. El archivo de metadatos del servicio se encuentra en el directorio de recursos XML. para tu app y debe coincidir con el nombre del recurso que declaraste en el . Con las entradas del manifiesto del ejemplo anterior, deberías crea un archivo en formato XML en res/xml/previewinputservice.xml, con una Etiqueta tv-input:

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

El marco de trabajo de entrada de TV debe tener esta etiqueta. Sin embargo, solo se usa para configurar canales en vivo. Como estás renderizando un video, la etiqueta debe estar vacía.

Crea un URI de video

Para indicar que la app debería renderizar el video de vista previa en lugar de hacerlo la pantalla principal de Android TV, debes crear un URI de video para un PreviewProgram. El URI debe terminar con el identificador que usa tu app para el contenido. puede recuperar el contenido más tarde en el TvInputService.

Si tu identificador es del tipo Long, usa 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 tu identificador no es del tipo Long, compila el URI con 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();

Llamadas de tu app onTune(Uri videoUri) para que Android TV inicie la vista previa del video.

Crea un servicio

En el siguiente ejemplo, se muestra cómo extender TvInputService para crear uno propio PreviewInputService Ten en cuenta que el servicio usa un MediaPlayer para la reproducción. pero tu código puede usar cualquier reproductor de video 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
        }
    }
}