预览视频


预览视频是鼓励用户深入链接到电视应用的绝佳方式。 预览的范围可以从短片到完整的电影预告片。

在创建预览时,请考虑以下准则:

  • 不要在预览中显示广告。 如果您在客户端缝合广告,请不要将它们粘贴到预览视频中。 如果您在服务器端使用stich广告,请为预览提供无广告视频。
  • 为了获得最佳质量,预览视频应该是16:9或4:3。 请参阅视频节目属性以了解预览视频的建议尺寸。
  • 当预览视频和海报画面具有不同的高宽比时,主屏幕会在播放预览之前将海报视图的大小调整为视频的宽高比。 该视频不是letterboxed。 例如,如果海报艺术比例是ASPECT_RATIO_MOVIE_POSTER (1:1.441),但是视频比例是16:9,则海报视图变换为16:9的区域。
  • 当您创建预览时,其内容可以公开访问或受DRM保护。 每种情况下都适用不同的程序。 这个页面描述了两者。

在主屏幕上播放预览

如果您使用ExoPlayer支持的任何视频类型创建预览,并且预览可公开访问,则可以直接在主屏幕上播放预览。

构建PreviewProgram时,请使用setPreviewVideoUri()并使用可公开访问的HTTPS URL,如下例所示。 预览可以是视频音频 。

科特林

val previewVideoUrl = Uri.parse(“https://www.example.com/preview.mp4”)
 val builder = PreviewProgram.Builder()
 builder.setChannelId(渠道ID)
     // ...
     .setPreviewVideoUri(previewVideoUrl)

Java的

Uri previewVideoUrl = Uri.parse(“https://www.example.com/preview.mp4”);
 PreviewProgram.Builder builder = new PreviewProgram.Builder();
 builder.setChannelId(渠道ID)
     // ...
     .setPreviewVideoUri(Uri.parse(previewVideoUrl));



渲染表面上的预览

如果您的视频受DRM保护或者ExoPlayer不支持的媒体类型,请使用TvInputService Android TV主屏幕通过调用onSetSurface()Surface传递给您的服务。 您的应用程序直接在onTune()这个表面上绘制视频。

直接表面渲染可让您的应用程序控制渲染的内容以及渲染方式。 您可以覆盖元数据,例如频道归因。

在清单中声明您的TvInputService

您的应用必须提供TvInputService的实现,以便主屏幕可以呈现您的预览。在你的服务声明中,包含一个intent过滤器,它指定TvInputService为意图执行的动作。 还将服务元数据声明为单独的XML资源。 以下示例中显示了服务声明,意图过滤器和服务元数据声明:

<service android:name =“。rich.PreviewInputService”
    机器人:权限= “android.permission.BIND_TV_INPUT”>
     <! - 系统用来启动我们的帐户服务所需的过滤器。  - >
     <意图滤波器>
         <action android:name =“android.media.tv.TvInputService”/>
     </意图滤波器>
     <! - 描述此输入的XML文件。  - >
     <元数据
        机器人:名字= “android.media.tv.input”
         android:resource =“@ xml / previewinputservice”/>
 </服务>

在单独的XML文件中定义服务元数据。 服务元数据文件位于您的应用程序的XML资源目录中,并且必须与您在清单中声明的资源的名称相匹配。 使用上例中的清单条目,您可以在res/xml/previewinputservice.xml创建一个XML文件,并带有一个空的tv-input标签:

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

电视输入框架必须有这个标签。 但是,它仅用于配置直播频道。 由于您正在呈现视频,因此标记应该为空。

创建一个视频URI

为了表明您的预览视频应该由您的应用呈现,而不是Android TV主屏幕,您必须为PreviewProgram创建一个视频URI。URI应以应用程序用于内容的标识符结束,以便稍后可以在TvInputService检索内容。如果您的标识符是Long类型,请使用TvContractCompat.buildPreviewProgramUri() :


科特林

val id:Long = 1L //内容标识符
 val componentName = new ComponentName(context,PreviewVideoInputService.class)
 val previewProgramVideoUri = TvContractCompat.buildPreviewProgramUri(id)
    。建立在()
    .appendQueryParameter(“input”,TvContractCompat.buildInputId(componentName))
    。建立()

Java的

长ID = 1L;  //内容标识符
 ComponentName componentName = new ComponentName(context,PreviewVideoInputService.class);
 previewProgramVideoUri = TvContractCompat.buildPreviewProgramUri(id)
        。建立在()
        .appendQueryParameter(“input”,TvContractCompat.buildInputId(componentName))
        。建立();


如果您的标识符不是Long类型,请使用Uri.withAppendedPath()构建URI:


科特林

val previewProgramVideoUri = Uri.withAppendedPath(PreviewPrograms.CONTENT_URI,“content-identifier”)
        。建立在()
        .appendQueryParameter(“input”,TvContractCompat.buildInputId(componentName))
        。建立()

Java的

previewProgramVideoUri = Uri.withAppendedPath(PreviewPrograms.CONTENT_URI,“content-identifier”)
        。建立在()
        .appendQueryParameter(“input”,TvContractCompat.buildInputId(componentName))
        。建立();


您的应用程序调用onTune(Uri videoUri)以使Android TV启动预览视频。

创建一个服务

以下示例显示如何扩展TvInputService以创建您自己的PreviewInputService 请注意,该服务使用MediaPlayer播放,但您的代码可以使用任何可用的视频播放器。


科特林

导入android.content.Context
导入android.media.MediaPlayer
导入android.media.tv.TvInputService
导入android.net.Uri
导入android.util.Log
导入android.view.Surface
导入java.io.IOException

类PreviewVideoInputService:TvInputService(){

    重写fun onCreateSession(inputId:String):TvInputService.Session?  {
        返回PreviewSession(this)
     }

    私人内部类PreviewSession(
        内部变量上下文:上下文
     ):TvInputService.Session(context){
    
        内部var mediaPlayer:MediaPlayer?  = MediaPlayer()

        覆盖有趣的onRelease(){
            媒体播放器?.release()
             mediaPlayer = null
         }

        重写fun onTune(uri:Uri):Boolean {
             //让TvInputService知道视频正在加载。
             notifyVideoUnavailable(VIDEO_UNAVAILABLE_REASON_TUNING)
             //从TV Provider数据库获取流url
             //用于内容://android.media.tv/preview_program/ 
             val id = uri.lastPathSegment
             //在后台加载视频。
             retrieveYourVideoPreviewUrl(id){videoUri  - >
                如果(videoUri == null){
                   Log.d(TAG,“找不到视频$ id”)
                   notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN)
                 }

                尝试{
                     mPlayer.setDataSource(getApplicationContext(),videoUri)
                     mPlayer.prepare()
                     mPlayer.start()
                     notifyVideoAvailable()
                 } catch(IOException e){
                     Log.e(标签,“无法准备媒体播放器”,e)
                     notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN)
                 }
               }
          返回true
         }

        覆盖乐趣onSetSurface(surface:Surface?):Boolean {
             MEDIAPLAYER?.setSurface(表面)
            返回true
         }

        覆盖乐趣onSetStreamVolume(音量:浮动){
             //主屏幕可能会淡入淡出视频的音量。
             //你的播放器应该相应地更新。
             mediaPlayer?.setVolume(音量,音量)
         }

        在SetCaptionEnabled上重写fun(b:Boolean){
             //在此处启用/停用字幕
         }
     }

    伴侣对象{
         private const val TAG =“PreviewInputService”
     }
 }

Java的

import android.content.Context;
 import android.media.MediaPlayer;
 import android.media.tv.TvInputService;
导入android.net.Uri;
导入android.support.annotation.Nullable;
导入android.util.Log;
导入android.view.Surface;
 import java.io.IOException;

公共类PreviewVideoInputService继承TvInputService {
     private static final String TAG =“PreviewVideoInputService”;

     @Nullable
     @覆盖
     public Session onCreateSession(String inputId){
        返回新的PreviewSession(this);
     }

    私人类PreviewSession继承TvInputService.Session {

        私人MediaPlayer mPlayer;

         PreviewSession(上下文上下文){
            超级(上下文);
             mPlayer = new MediaPlayer();
         }

         @覆盖
         public boolean onTune(Uri channelUri){
             //让TvInputService知道视频正在加载。
             notifyVideoUnavailable(VIDEO_UNAVAILABLE_REASON_TUNING);
             //从TV Provider数据库获取流url
             //用于内容://android.media.tv/preview_program/ 
             String id = uri.getLastPathSegment();
             //在后台加载视频。
             retrieveYourVideoPreviewUrl(id,new MyCallback(){
               public void callback(Uri videoUri){
                如果(videoUri == null){
                   Log.d(TAG,“找不到视频”+ id);
                   notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
                 }

                尝试{
                     mPlayer.setDataSource(getApplicationContext(),videoUri);
                     mPlayer.prepare();
                     mPlayer.start();
                     notifyVideoAvailable();
                 } catch(IOException e){
                     Log.e(标签,“无法准备媒体播放器”,e);
                     notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
                 }
               }
             });
            返回true;
         }

         @覆盖
         public boolean onSetSurface(@Nullable Surface surface){
             if(mPlayer!= null){
                 mPlayer.setSurface(表面);
             }
            返回true;
         }

         @覆盖
         public void onRelease(){
             if(mPlayer!= null){
                 mPlayer.release();
             }
             mPlayer = null;
         }

         @覆盖
         public void onSetStreamVolume(float volume){
             if(mPlayer!= null){
                 //主屏幕可能会淡入淡出视频的音量。
                 //你的播放器应该相应地更新。
                 mPlayer.setVolume(volume,volume);
             }
         }

         @覆盖
         public void onSetCaptionEnabled(boolean enabled){
             //在此处启用/停用字幕
         }
     }
 }