Android Studio V3.12环境下TV开发教程
(转自Android官网https://developer.android.com/training/tv/start)
文章源自:光谷佳武 https://blog.csdn.net/jiawuhan/article/details/80647861
预览视频
预览视频是鼓励用户深入链接到电视应用的绝佳方式。 预览的范围可以从短片到完整的电影预告片。
在创建预览时,请考虑以下准则:
- 不要在预览中显示广告。 如果您在客户端缝合广告,请不要将它们粘贴到预览视频中。 如果您在服务器端使用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){ //在此处启用/停用字幕 } } }