วิดีโอตัวอย่างเป็นวิธีที่ยอดเยี่ยมในการกระตุ้นให้ผู้ใช้ทำ Deep Link ไปยังแอป TV ของคุณ ตัวอย่างมีได้ตั้งแต่คลิปสั้นๆ ไปจนถึงตัวอย่างภาพยนตร์แบบเต็ม
เมื่อสร้างตัวอย่าง โปรดพิจารณาหลักเกณฑ์ต่อไปนี้
- ไม่แสดงโฆษณาในตัวอย่าง ถ้าคุณต่อโฆษณาในฝั่งไคลเอ็นต์ อย่าต่อภาพเหล่านั้นลงในวิดีโอตัวอย่าง หากคุณต้องการแสดงโฆษณาบนฝั่งเซิร์ฟเวอร์ ให้วิดีโอแบบไม่มีโฆษณาสำหรับตัวอย่าง
- เพื่อคุณภาพที่ดีที่สุด วิดีโอตัวอย่างควรเป็น 16:9 หรือ 4:3 โปรดดู แอตทริบิวต์โปรแกรมวิดีโอ สำหรับขนาดที่แนะนำของวิดีโอตัวอย่าง
- เมื่อวิดีโอตัวอย่างและภาพโปสเตอร์มีสัดส่วนภาพต่างกัน
หน้าจอหลักจะปรับขนาดมุมมองโปสเตอร์ตามอัตราส่วนของวิดีโอก่อนที่จะเล่นตัวอย่าง
วิดีโอไม่ได้มีแถบดำด้านบน-ล่างของภาพ ตัวอย่างเช่น หาก
อัตราส่วนภาพโปสเตอร์คือ
ASPECT_RATIO_MOVIE_POSTER
(1:1.441) แต่อัตราส่วนวิดีโอคือ 16:9 และมุมมองโปสเตอร์จะเปลี่ยนไปเป็น 16:9 - เมื่อคุณสร้างตัวอย่าง เนื้อหาอาจเข้าถึงได้แบบสาธารณะหรือ ได้รับการคุ้มครองภายใต้ DRM โดยแต่ละกรณีจะมีกระบวนการที่แตกต่างกัน หน้านี้ อธิบายทั้ง 2 อย่าง
เล่นตัวอย่างในหน้าจอหลัก
หากคุณสร้างตัวอย่างโดยใช้ประเภทวิดีโอใดก็ตาม รองรับโดย ExoPlayer และหน้าตัวอย่างสามารถเข้าถึงได้แบบสาธารณะ คุณสามารถเล่นตัวอย่างได้โดยตรงบนหน้าจอหลัก
เมื่อคุณสร้างโปรแกรมดูตัวอย่าง
ใช้ setPreviewVideoUri()
กับ HTTPS ที่เข้าถึงได้แบบสาธารณะ
URL ตามที่แสดงในตัวอย่างด้านล่าง ตัวอย่างอาจเป็น
video หรือ
เสียง
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));
แสดงตัวอย่างบนแพลตฟอร์ม
หากวิดีโอของคุณได้รับการป้องกันด้วย DRM หรืออยู่ในประเภทสื่อที่ระบบไม่รองรับ
ExoPlayer ให้ใช้ TvInputService
หน้าจอหลักของ Android TV จะส่ง Surface
ไปยังบริการของคุณ
โดยโทรไปที่ onSetSurface()
แอปของคุณวาดวิดีโอบนพื้นที่นี้โดยตรงจาก onTune()
การแสดงผลบนพื้นผิวโดยตรงช่วยให้แอปของคุณควบคุมสิ่งที่จะแสดงผลและวิธีแสดงผลได้ ที่แสดงผล คุณสามารถวางซ้อนข้อมูลเมตา เช่น การระบุแหล่งที่มาของช่อง
ประกาศ TvInputService ในไฟล์ Manifest
แอปของคุณต้องติดตั้งใช้งาน TvInputService
เพื่อให้หน้าจอหลัก
แสดงผลตัวอย่างได้
ในการประกาศบริการ ให้ใส่ตัวกรอง Intent ที่ระบุ
TvInputService
เป็นการดำเนินการกับ
Intent และประกาศข้อมูลเมตาของบริการเป็นทรัพยากร XML แยกต่างหากด้วย
แสดงการประกาศบริการ ตัวกรอง Intent และการประกาศข้อมูลเมตาของบริการ
ในตัวอย่างต่อไปนี้
<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
ของแอปและต้องตรงกับชื่อทรัพยากรที่คุณประกาศใน
ไฟล์ Manifest ในการใช้รายการไฟล์ Manifest จากตัวอย่างก่อนหน้านี้ คุณจะต้อง
สร้างไฟล์ XML ที่ res/xml/previewinputservice.xml
โดยที่ไม่มีข้อมูล
แท็ก tv-input
:
<?xml version="1.0" encoding="utf-8"?>
<tv-input/>
เฟรมเวิร์กอินพุตทีวีต้องมีแท็กนี้ อย่างไรก็ตาม ซึ่งใช้ในการกำหนดค่ารายการถ่ายทอดสดเท่านั้น เนื่องจากคุณกำลังแสดงผลวิดีโอ แท็กควรว่างเปล่า
สร้าง URI ของวิดีโอ
เพื่อระบุว่าแอปควรแสดงผลวิดีโอตัวอย่างแทนการแสดงผล
หน้าจอหลักของ Android TV คุณต้องสร้าง URI วิดีโอสำหรับ PreviewProgram
URI ควรลงท้ายด้วยตัวระบุที่แอปใช้สำหรับเนื้อหา เพื่อให้คุณ
สามารถเรียกดูเนื้อหาภายหลังใน TvInputService
หากตัวระบุของคุณเป็นประเภท Long
ให้ใช้
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();
หากตัวระบุของคุณไม่ใช่ประเภท Long
ให้สร้าง URI โดยใช้
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();
การโทรจากแอปของคุณ
onTune(Uri videoUri)
เพื่อให้ Android TV เริ่มเล่นวิดีโอตัวอย่าง
สร้างบริการ
ตัวอย่างต่อไปนี้แสดงวิธีขยาย TvInputService
เพื่อสร้างโฆษณาของคุณเอง
PreviewInputService
โปรดทราบว่าบริการจะใช้ MediaPlayer
ในการเล่น
แต่โค้ดสามารถใช้โปรแกรมเล่นวิดีโอใดก็ได้ที่มีอยู่
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 } } }