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

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

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

  • تبلیغات را در پیش نمایش نشان ندهید. اگر تبلیغات را در سمت مشتری قرار می دهید، آنها را در ویدیوهای پیش نمایش قرار ندهید. اگر تبلیغات را در سمت سرور قرار می دهید، یک ویدیوی بدون آگهی برای پیش نمایش ارائه دهید.
  • برای بهترین کیفیت، پیش نمایش فیلم ها باید 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 با فراخوانی 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/>

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