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

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

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

  • آگهی‌ها را در پیش‌نمایش نشان ندهید. اگر آگهی‌ها را در سمت کارخواه ادغام می‌کنید، آن‌ها را در ویدیوهای پیش‌نمایش ادغام نکنید. اگر آگهی‌ها را در سمت سرور ادغام می‌کنید، ویدیوی بدون آگهی برای پیش‌نمایش‌ها ارائه دهید.
  • برای بهترین کیفیت، ویدیوهای پیش‌نمایش باید ۱۶:۹ یا ۴:۳ باشند. برای اندازه‌های توصیه‌شده ویدیوهای پیش‌نمایش، به مشخصه‌های برنامه ویدیویی مراجعه کنید.
  • وقتی ویدیو پیش‌نمایش و پوستر فیلم نسبت ابعادی متفاوتی داشته باشند، صفحه اصلی قبل‌از پخش پیش‌نمایش، اندازه نمای پوستر را به نسبت ابعادی ویدیو تغییر می‌دهد. ویدیو در قالب جعبه نامه نیست. برای مثال، اگر نسبت پوستر فیلم ASPECT_RATIO_MOVIE_POSTER (۱:۱٫۴۴۱) باشد اما نسبت ویدیو ۱۶:۹ باشد، نمای پوستر به ناحیه ۱۶:۹ تبدیل می‌شود.
  • وقتی پیش‌نمایشی ایجاد می‌کنید، محتوای آن می‌تواند به‌طور عمومی دردسترس باشد یا تحت مدیریت حقوق دیجیتال محافظت شود. در هر مورد، رویه‌های متفاوتی اعمال می‌شود. این صفحه هر دو را شرح می‌دهد.

پیش‌نمایش را در صفحه اصلی پخش می‌کند

اگر بااستفاده از هرکدام از انواع ویدیو پشتیبانی‌شده توسط ExoPlayer پیش‌نمایشی ایجاد کنید و پیش‌نمایش به‌صورت عمومی دردسترس باشد، می‌توانید پیش‌نمایش را مستقیماً در صفحه اصلی پخش کنید.

وقتی PreviewProgram می‌سازید، از setPreviewVideoUri() با نشانی وب 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));

پرداز زدن پیش‌نمایش روی سطح

اگر ویدیوتان تحت محافظت مدیریت حقوق دیجیتال است یا نوع رسانه آن توسط ExoPlayer پشتیبانی نمی‌شود، از TvInputService استفاده کنید. صفحه اصلی Android TV با فراخوانی onSetSurface()، Surface را به سرویس شما ارسال می‌کند. برنامه شما ویدیو را مستقیماً از 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/>

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

ایجاد شناسه منبع یکسان ویدیو

برای نشان دادن اینکه ویدیو پیش‌نمایش شما باید توسط برنامه‌تان ارائه شود نه صفحه اصلی Android TV، باید شناسه منبع یکسانی برای ویدیو 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/<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
        }
    }
}