Предварительный просмотр видеоролика — отличный способ побудить пользователей перейти по ссылке на ваше ТВ-приложение. Превью могут варьироваться от коротких клипов до полных трейлеров к фильмам.
При создании предварительного просмотра учитывайте следующие рекомендации:
- Не показывать рекламу в предварительном просмотре. Если вы вшиваете рекламу на стороне клиента, не вставляйте ее в превью-ролики. Если вы размещаете рекламу на стороне сервера, предоставьте для предварительного просмотра видео без рекламы.
- Для наилучшего качества видео предварительного просмотра должно быть 16:9 или 4:3. Рекомендуемые размеры видео для предварительного просмотра см. в разделе «Атрибуты видеопрограммы» .
- Если видео предварительного просмотра и плакат имеют разные соотношения сторон, перед воспроизведением предварительного просмотра главный экран изменяет размер изображения постера в соответствии с соотношением сторон видео. Видео не в почтовом ящике. Например, если соотношение сторон постера —
ASPECT_RATIO_MOVIE_POSTER
(1:1,441), а соотношение видео — 16:9, вид постера преобразуется в область 16:9. - Когда вы создаете предварительный просмотр, его содержимое может быть общедоступным или защищено DRM. В каждом случае применяются разные процедуры. На этой странице описаны оба.
Воспроизведение предварительного просмотра на главном экране
Если вы создаете предварительный просмотр, используя любой из типов видео, поддерживаемых ExoPlayer , и предварительный просмотр общедоступен, вы можете воспроизвести предварительный просмотр прямо на главном экране.
При создании PreviewProgram используйте setPreviewVideoUri()
с общедоступным URL-адресом HTTPS, как показано в примере ниже. Предварительный просмотр может быть видео или аудио .
Котлин
val previewVideoUrl = Uri.parse("https://www.example.com/preview.mp4") val builder = PreviewProgram.Builder() builder.setChannelId(channelId) // ... .setPreviewVideoUri(previewVideoUrl)
Ява
Uri previewVideoUrl = Uri.parse("https://www.example.com/preview.mp4"); PreviewProgram.Builder builder = new PreviewProgram.Builder(); builder.setChannelId(channelId) // ... .setPreviewVideoUri(Uri.parse(previewVideoUrl));
Отрисовка предварительного просмотра на поверхности
Если ваше видео защищено DRM или имеет тип носителя, не поддерживаемый ExoPlayer , используйте TvInputService
. Главный экран Android TV передает Surface
в вашу службу, вызывая onSetSurface()
. Ваше приложение рисует видео прямо на этой поверхности из onTune()
.
Прямой рендеринг поверхности позволяет вашему приложению контролировать то, что и как визуализируется. Вы можете накладывать метаданные, такие как атрибуция канала.
Объявите свой TvInputService в манифесте.
Ваше приложение должно предоставлять реализацию TvInputService
, чтобы главный экран мог отображать ваш предварительный просмотр.
В декларацию службы включите фильтр намерений, который указывает TvInputService
в качестве действия, которое необходимо выполнить с намерением. Также объявите метаданные службы как отдельный ресурс XML. Объявление службы, фильтр намерений и объявление метаданных службы показаны в следующем примере:
<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>
Определите метаданные службы в отдельном XML-файле. Файл метаданных службы находится в каталоге ресурсов XML вашего приложения и должен соответствовать имени ресурса, объявленного вами в манифесте. Используя записи манифеста из предыдущего примера, вы создадите XML-файл по адресу res/xml/previewinputservice.xml
с пустым тегом tv-input
:
<?xml version="1.0" encoding="utf-8"?>
<tv-input/>
TV Input Framework должен иметь этот тег. Однако он используется только для настройки живых каналов. Поскольку вы рендерите видео, тег должен быть пустым.
Создать URI видео
Чтобы указать, что видео предварительного просмотра должно отображаться в вашем приложении, а не на главном экране Android TV, необходимо создать URI видео для PreviewProgram
. URI должен заканчиваться идентификатором, который ваше приложение использует для контента, чтобы вы могли получить контент позже в TvInputService
.
Если ваш идентификатор имеет тип Long
, используйте TvContractCompat.buildPreviewProgramUri() :
Котлин
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()
Ява
Long id = 1L; // content identifier ComponentName componentName = new ComponentName(context, PreviewVideoInputService.class); previewProgramVideoUri = TvContractCompat.buildPreviewProgramUri(id) .buildUpon() .appendQueryParameter("input", TvContractCompat.buildInputId(componentName)) .build();
Если ваш идентификатор не типа Long
, создайте URI, используя Uri.withAppendedPath()
:
Котлин
val previewProgramVideoUri = Uri.withAppendedPath(PreviewPrograms.CONTENT_URI, "content-identifier") .buildUpon() .appendQueryParameter("input", TvContractCompat.buildInputId(componentName)) .build()
Ява
previewProgramVideoUri = Uri.withAppendedPath(PreviewPrograms.CONTENT_URI, "content-identifier") .buildUpon() .appendQueryParameter("input", TvContractCompat.buildInputId(componentName)) .build();
Ваше приложение вызывает onTune(Uri videoUri)
чтобы Android TV начал предварительный просмотр видео.
Создать услугу
В следующем примере показано, как расширить TvInputService
для создания собственного PreviewInputService
. Обратите внимание, что служба использует MediaPlayer
для воспроизведения, но ваш код может использовать любой доступный видеоплеер.
Котлин
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" } }
Ява
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 } } }