Film z podglądem to świetny sposób na zachęcenie użytkowników do skorzystania z precyzyjnego linku do aplikacji na telewizory. Mogą to być zarówno krótkie klipy, jak i pełne zwiastuny filmów.
Tworząc podgląd, weź pod uwagę te wskazówki:
- Nie wyświetlaj reklam na podglądzie. Jeśli połączysz reklamy po stronie klienta, nie łącz ich w filmy z podglądem. Jeśli umieścisz reklamy po stronie serwera, możesz udostępnić podgląd filmu bez reklam.
- Aby uzyskać najlepszą jakość, filmy z podglądem powinny mieć proporcje 16:9 lub 4:3. Zobacz Atrybuty programu wideo w przypadku zalecanych rozmiarów filmów w podglądzie.
- Jeśli film z podglądem i plakat mają różne formaty obrazu,
przed odtworzeniem podglądu na ekranie głównym zmienia się rozmiar widoku plakatu na proporcje filmu.
Film nie ma czarnych pasów. Na przykład, jeśli
współczynnik proporcji plakatu jest
ASPECT_RATIO_MOVIE_POSTER
(1:1,441) ale proporcje to 16:9, widok plakatu zmienia się na 16:9. - Gdy utworzysz podgląd, jego treść może być dostępna publicznie lub są chronione przez DRM. W każdym przypadku obowiązują inne procedury. Ta strona i je opisuje.
Odtwórz podgląd na ekranie głównym
Jeśli utworzysz podgląd, używając dowolnego z typów wideo obsługiwane przez ExoPlayer a podgląd jest publicznie dostępny, możesz go odtworzyć bezpośrednio na ekranie głównym.
Podczas tworzenia programu podglądu
użyj setPreviewVideoUri()
z publicznie dostępnym protokołem HTTPS
zgodnie z przykładem poniżej. Podgląd można wybrać na 2 sposoby:
video lub
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));
Renderuj podgląd na powierzchni
Jeśli film jest chroniony przez DRM lub zawiera multimedia, które nie są obsługiwane przez
ExoPlayer, użyj TvInputService
.
Ekran główny Androida TV przekazuje Surface
do Twojej usługi
, dzwoniąc pod numer onSetSurface()
. Aplikacja pobiera film bezpośrednio na tej platformie z aplikacji onTune()
.
Renderowanie bezpośrednio na powierzchni pozwala aplikacji kontrolować, co i w jaki sposób jest renderowane. wyrenderowano. Możesz nałożyć na film metadane, np. źródło kanału.
Zadeklaruj usługę TvInputService w pliku manifestu
Aplikacja musi udostępniać implementację interfejsu TvInputService
aby podgląd mógł być renderowany na ekranie głównym.
W deklaracji dotyczącej usługi umieść filtr intencji, który określa
TvInputService
jako działanie do wykonania z
intencji. Zadeklaruj także metadane usługi jako oddzielny zasób XML.
wyświetlana jest deklaracja dotycząca usługi, filtr intencji i deklaracja metadanych usługi
w tym przykładzie:
<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>
Zdefiniuj metadane usługi w osobnym pliku XML.
Plik metadanych usługi znajduje się w katalogu zasobów XML
dla Twojej aplikacji i musi być zgodna z nazwą zasobu zadeklarowanego w
pliku manifestu. Korzystając z wpisów w pliku manifestu z poprzedniego przykładu:
utwórz plik XML pod adresem res/xml/previewinputservice.xml
z pustym
Tag tv-input
:
<?xml version="1.0" encoding="utf-8"?>
<tv-input/>
Platforma do wprowadzania danych na telewizorze musi zawierać ten tag. Pamiętaj jednak: i służy wyłącznie do konfigurowania kanałów na żywo. Renderujesz film, tag powinien być pusty.
Tworzenie identyfikatora URI filmu
Aby wskazać, że film z podglądem powinien być renderowany przez aplikację, a nie przez aplikację
ekranu głównego Androida TV, musisz utworzyć identyfikator URI filmu dla PreviewProgram
.
Identyfikator URI powinien kończyć się identyfikatorem używanym przez aplikację dla treści, więc
może pobrać treść później w TvInputService
.
Jeśli Twój identyfikator to Long
, użyj
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();
Jeśli identyfikator jest innego typu niż Long
, utwórz identyfikator URI za pomocą
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();
Połączenia z aplikacji
onTune(Uri videoUri)
aby Android TV uruchomił film z podglądem.
Utwórz usługę
Ten przykład pokazuje, jak rozszerzyć TvInputService
, aby móc utworzyć własne
PreviewInputService
Pamiętaj, że usługa używa do odtwarzania plików MediaPlayer
,
ale w kodzie może być używany dowolny dostępny odtwarzacz.
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 } } }