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