Um vídeo de prévia é uma ótima maneira de incentivar os usuários a criar um link direto para seu app de TV. As prévias podem variar de clipes curtos a trailers de filmes completos.
Ao criar uma prévia, considere estas diretrizes:
- Não mostre anúncios em uma prévia. Se você agrupar anúncios no lado do cliente, não os agrupe em vídeos de prévia. Se você agrupar anúncios no lado do servidor, fornecem um vídeo sem anúncios para prévias.
- Para atingir a melhor qualidade, os vídeos de prévia devem ter a proporção 16:9 ou 4:3. Consulte Atributos do programa de vídeo para os tamanhos recomendados de vídeos de prévia.
- Quando o vídeo de prévia e a arte do pôster têm proporções diferentes, o
a tela inicial redimensiona a visualização do pôster para a proporção do vídeo antes de exibir a prévia.
O vídeo não tem efeito letterbox. Por exemplo, se
a proporção da arte do pôster é
ASPECT_RATIO_MOVIE_POSTER
(1:1,441) mas a proporção do vídeo for 16:9, a visualização do pôster será transformada em uma região de 16:9. - Quando você cria uma prévia, o conteúdo dela pode ser acessado publicamente ou protegidos por DRM. Diferentes procedimentos são usados em cada caso. Esta página descreve ambos.
Exibir a prévia na tela inicial
Se você criar uma prévia usando qualquer um dos tipos de vídeo compatível com o ExoPlayer e a visualização for acessível ao público, será possível reproduzi-la diretamente na tela inicial.
Ao criar um PreviewProgram
use setPreviewVideoUri()
com um HTTPS de acesso público
como mostrado no exemplo abaixo. A visualização pode ser
video ou
áudio.
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));
Renderizar a prévia em uma superfície
Se o vídeo for protegido por DRM ou estiver em um tipo de mídia incompatível com o
ExoPlayer, use um TvInputService
.
A tela inicial do Android TV transmite um Surface
para o serviço.
chame onSetSurface()
. O app desenha o vídeo diretamente nessa superfície usando o onTune()
.
A renderização direta de superfície permite que o app controle o que é renderizado e como é renderizado. Você pode sobrepor metadados, como atribuição de canais.
Declarar seu TvInputService no manifesto
Seu app precisa oferecer uma implementação de TvInputService
para que a tela inicial possa renderizar sua visualização.
Na declaração do serviço, inclua um filtro de intent que especifique
TvInputService
como a ação a ser realizada com o
intenção. Além disso, declare os metadados de serviço com um recurso XML separado. A
declaração de serviço, filtro de intent e declaração de metadados de serviço são mostrados
no exemplo a seguir:
<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>
Defina os metadados de serviço em um arquivo XML separado.
O arquivo de metadados de serviço está localizado no diretório de recursos XML.
para seu app e precisa corresponder ao nome do recurso declarado na
manifesto do aplicativo. Usando as entradas de manifesto do exemplo anterior, você usaria
crie um arquivo XML em res/xml/previewinputservice.xml
, com uma
Tag tv-input
:
<?xml version="1.0" encoding="utf-8"?>
<tv-input/>
O TV Input Framework precisa ter essa tag, No entanto, ela é usada apenas para configurar canais ao vivo. Como você está renderizando um vídeo, a tag deve estar vazia.
Criar um URI de vídeo
Para indicar que o vídeo de prévia será renderizado pelo app em vez de
na tela inicial do Android TV, crie um URI de vídeo para uma PreviewProgram
.
O URI precisa terminar com o identificador que o app usa para o conteúdo. Assim, você
pode recuperar o conteúdo mais tarde no TvInputService
.
Se o identificador for do tipo Long
, use
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 o identificador não for do tipo Long
, crie o URI usando:
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();
Seu app chama
onTune(Uri videoUri)
para fazer o Android TV iniciar o vídeo de prévia.
Criar um serviço
O exemplo a seguir mostra como estender TvInputService
para criar o seu próprio
PreviewInputService
. O serviço usa um MediaPlayer
para reprodução,
mas seu código pode usar qualquer player de vídeo disponível.
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 } } }