預覽影片是鼓勵使用者深層連結至 TV 應用程式的好方法。 試聽片段包括短片、完整電影預告片等。
建立預覽畫面時,請遵守下列規範:
- 不要在預覽畫面中顯示廣告。如果是在客戶端拼接廣告 不要將這些圖片拼接成預覽影片。如果是在伺服器端插入廣告 提供無廣告預覽影片。
- 為取得最佳品質,預覽影片的格式應為 16:9 或 4:3。詳情請見 影片節目屬性 建議大小。
- 如果預覽影片和海報圖片的長寬比不同,
主畫面會在播放預覽前,將海報畫面調整為影片的長寬比。
影片並未加上黑邊。舉例來說
海報圖片比例為
ASPECT_RATIO_MOVIE_POSTER
(1:1.441) 但影片比例為 16:9,海報畫面會轉換成 16:9 的區域。 - 建立預覽時,其內容可以公開存取,也可以 受 DRM 保護。這兩種情況適用的程序不同。這個頁面 都描述兩者。
在主畫面上播放預覽畫面
如果您使用任何影片類型建立預覽 ExoPlayer 支援 而且預覽畫面可供公開存取,你可以直接在主畫面上播放預覽畫面。
建立 PreviewProgram
透過可公開存取的 HTTPS 使用 setPreviewVideoUri()
網址,如以下範例所示。您可以選擇
video 或
audio。
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 保護,或使用不受 DRM 保護的媒體類型
ExoPlayer,請使用 TvInputService
。
Android TV 主畫面會將 Surface
傳遞至你的服務
呼叫 onSetSurface()
。應用程式會從 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 資源目錄中
,而且必須與您在
資訊清單。使用上述範例的資訊清單項目,您會執行下列動作
在 res/xml/previewinputservice.xml
中建立 XML 檔案
tv-input
標記:
<?xml version="1.0" encoding="utf-8"?>
<tv-input/>
電視輸入架構必須含有這個代碼。不過 只用於設定直播管道您正在算繪影片 代碼必須留空
建立影片 URI
表示預覽影片應由應用程式顯示,而非
Android TV 主畫面,您必須為 PreviewProgram
建立影片 URI。
URI 的結尾必須是應用程式用於內容的 ID,因此您
之後就能在 TvInputService
中擷取內容。
如果 ID 類型為 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();
如果 ID 並非 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 播放預覽影片。
建立 Service
以下範例說明如何擴充 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 } } }