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