Xem trước video

Video xem trước là một cách hay để khuyến khích người dùng đường liên kết sâu đến ứng dụng dành cho TV. Bản xem trước có thể bao gồm các đoạn video ngắn cho đến đoạn giới thiệu phim đầy đủ.

Khi bạn tạo bản xem trước, hãy xem xét các nguyên tắc sau:

  • Không hiển thị quảng cáo trong bản xem trước. Nếu bạn ghép quảng cáo ở phía máy khách, đừng ghép chúng vào video xem trước. Nếu bạn thâm nhập quảng cáo trên phía máy chủ, cung cấp video không có quảng cáo cho khán giả xem trước.
  • Để có chất lượng tốt nhất, video xem trước nên có tỷ lệ 16:9 hoặc 4:3. Xem Thuộc tính chương trình video cho kích thước đề xuất của video xem trước.
  • Khi video xem trước và ảnh áp phích có tỷ lệ khung hình khác nhau, màn hình chính sẽ đổi kích thước chế độ xem áp phích thành tỷ lệ khung hình của video trước khi phát bản xem trước. Video không có hiệu ứng hòm thư. Ví dụ: nếu tỷ lệ ảnh áp phích là ASPECT_RATIO_MOVIE_POSTER (1:1,441) nhưng tỷ lệ video là 16:9, chế độ xem áp phích chuyển thành vùng 16:9.
  • Khi bạn tạo một bản xem trước, nội dung của bản xem trước đó có thể truy cập công khai hoặc được bảo vệ theo DRM. Mỗi trường hợp sẽ có những quy trình khác nhau. Trang này mô tả cả hai.

Phát bản xem trước trên màn hình chính

Nếu bạn tạo bản xem trước bằng một loại video bất kỳ được hỗ trợ bởi ExoPlayer và bản xem trước đều có thể truy cập công khai, bạn có thể phát bản xem trước ngay trên màn hình chính.

Khi bạn tạo một PreviewProgram sử dụng setPreviewVideoUri() với HTTPS có thể truy cập công khai như trong ví dụ dưới đây. Bản xem trước có thể là video hoặc âm thanh.

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));

Kết xuất bản xem trước trên một nền tảng

Nếu video của bạn được bảo vệ bằng DRM hoặc thuộc một loại nội dung nghe nhìn không được ExoPlayer, sử dụng TvInputService. Màn hình chính của Android TV chuyển một Surface đến dịch vụ của bạn bằng cách gọi onSetSurface(). Ứng dụng của bạn vẽ video trực tiếp trên nền tảng này từ onTune().

Tính năng kết xuất giao diện trực tiếp cho phép ứng dụng kiểm soát nội dung và cách hiển thị nội dung đó kết xuất. Bạn có thể phủ siêu dữ liệu như thông tin ghi nhận sự đóng góp cho kênh.

Khai báo TvInputService trong tệp kê khai

Ứng dụng của bạn phải cung cấp một phương thức triển khai TvInputService để màn hình chính có thể hiển thị bản xem trước của bạn.

Trong phần khai báo dịch vụ, hãy thêm một bộ lọc ý định chỉ định TvInputService làm hành động để thực hiện với ý định. Ngoài ra, hãy khai báo siêu dữ liệu dịch vụ dưới dạng một tài nguyên XML riêng. Chiến lược phát hành đĩa đơn Nội dung khai báo dịch vụ, bộ lọc ý định và khai báo siêu dữ liệu dịch vụ trong ví dụ sau:

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

Xác định siêu dữ liệu dịch vụ trong một tệp XML riêng. Tệp siêu dữ liệu dịch vụ nằm trong thư mục tài nguyên XML cho ứng dụng của bạn và phải khớp với tên của tài nguyên mà bạn đã khai báo trong tệp kê khai. Bằng cách sử dụng các mục nhập tệp kê khai từ ví dụ trước, bạn sẽ tạo một tệp XML tại res/xml/previewinputservice.xml, có một tệp trống Thẻ tv-input:

<?xml version="1.0" encoding="utf-8"?>
<tv-input/>

Khung đầu vào TV phải có thẻ này. Tuy nhiên, nó chỉ được dùng để định cấu hình kênh trực tiếp. Vì bạn đang hiển thị một video, thẻ phải trống.

Tạo URI video

Để cho biết rằng ứng dụng của bạn nên hiển thị video xem trước thay vì màn hình chính của Android TV, bạn phải tạo URI video cho PreviewProgram. URI phải kết thúc bằng giá trị nhận dạng mà ứng dụng của bạn sử dụng cho nội dung, do đó, bạn có thể truy xuất nội dung sau này trong TvInputService.

Nếu giá trị nhận dạng của bạn là loại Long, hãy sử dụng 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();

Nếu giá trị nhận dạng của bạn không thuộc loại Long, hãy tạo URI bằng cách sử dụng 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();

Ứng dụng của bạn gọi điện onTune(Uri videoUri) để Android TV bắt đầu phát video xem trước.

Tạo dịch vụ

Ví dụ sau đây cho thấy cách mở rộng TvInputService để tạo kiểu khớp riêng của bạn PreviewInputService Lưu ý rằng dịch vụ này sử dụng MediaPlayer để phát, nhưng mã của bạn có thể sử dụng bất kỳ trình phát video nào có sẵn.

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