Wyświetlanie podglądu filmów

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