پیش نمایش ویدیوها

یک ویدیوی پیش‌نمایش، راهی عالی برای تشویق کاربران به ورود به اپلیکیشن تلویزیونی شماست. پیش‌نمایش‌ها می‌توانند از کلیپ‌های کوتاه گرفته تا تریلرهای کامل فیلم را شامل شوند.

هنگام ایجاد پیش‌نمایش، این دستورالعمل‌ها را در نظر بگیرید:

  • تبلیغات را در پیش‌نمایش نمایش ندهید. اگر تبلیغات را در سمت کلاینت به هم می‌چسبانید، آنها را در ویدیوهای پیش‌نمایش قرار ندهید. اگر تبلیغات را در سمت سرور به هم می‌چسبانید، یک ویدیوی بدون تبلیغ برای پیش‌نمایش‌ها ارائه دهید.
  • برای بهترین کیفیت، ویدیوهای پیش‌نمایش باید ۱۶:۹ یا ۴:۳ باشند. برای اندازه‌های پیشنهادی ویدیوهای پیش‌نمایش، به ویژگی‌های برنامه ویدیو مراجعه کنید.
  • وقتی ویدیوی پیش‌نمایش و پوستر هنری نسبت ابعاد متفاوتی داشته باشند، صفحه اصلی قبل از پخش پیش‌نمایش، اندازه نمای پوستر را به نسبت ابعاد ویدیو تغییر می‌دهد. ویدیو به صورت Letterbox نمایش داده نمی‌شود. برای مثال، اگر نسبت تصویر پوستر 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 استفاده کنید. صفحه اصلی تلویزیون اندروید با فراخوانی onSetSurface() یک Surface به سرویس شما ارسال می‌کند. برنامه شما ویدیو را مستقیماً از onTune() روی این سطح ترسیم می‌کند.

رندرینگ مستقیم سطحی به برنامه شما اجازه می‌دهد تا آنچه رندر می‌شود و نحوه رندر شدن آن را کنترل کند. می‌توانید فراداده‌هایی مانند انتساب کانال را روی هم قرار دهید.

سرویس TvInputService خود را در مانیفست تعریف کنید.

برنامه شما باید پیاده‌سازی TvInputService را ارائه دهد تا صفحه اصلی بتواند پیش‌نمایش شما را رندر کند.

در تعریف سرویس خود، یک فیلتر intent قرار دهید که TvInputService به عنوان عملی که باید با intent انجام شود، مشخص کند. همچنین metadata سرویس را به عنوان یک منبع XML جداگانه تعریف کنید. تعریف سرویس، فیلتر intent و تعریف metadata سرویس در مثال زیر نشان داده شده است:

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

چارچوب ورودی تلویزیون باید این برچسب را داشته باشد. با این حال، فقط برای پیکربندی کانال‌های زنده استفاده می‌شود. از آنجایی که شما در حال رندر کردن یک ویدیو هستید، این برچسب باید خالی باشد.

ایجاد یک URL ویدیویی

برای اینکه مشخص کنید ویدیوی پیش‌نمایش شما باید توسط برنامه‌تان به جای صفحه اصلی تلویزیون اندروید رندر شود، باید یک 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) را فراخوانی می‌کند تا تلویزیون اندروید پیش‌نمایش ویدیو را شروع کند.

ایجاد یک سرویس

مثال زیر نحوه‌ی بسط 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/<id>
            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/<id>
            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
        }
    }
}