ดูตัวอย่างวิดีโอ

วิดีโอตัวอย่างเป็นวิธีที่ยอดเยี่ยมในการกระตุ้นให้ผู้ใช้ทำ 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
        }
    }
}