Android TV Input Framework(TIF)--3 显示Tv Input内容

本文深入探讨Android TV Input Framework中如何通过TvView显示Tv Input内容。通过TvView与TvInputService交互,利用tune方法选择并显示TV输入。详细介绍了tune方法的流程,包括创建Session、设置Surface、调谐频道等关键步骤,揭示了TV Input服务在显示内容过程中的工作原理。

上一篇文章我们分析了TvInputManagerService如何构建Tv Input list,这篇文章主要分析如何切换到Tv Input,并显示其内容。App一般通过TvView来跟TvInputService建立联系,通过调用TvView的方法来选择Tv Input,显示内容。我们看一下 Android开发文档对TvView的描述:

TvView类概述:
显示TV内容。TvView类为应用程序提供了一个高级接口,以显示实现TvInputService的各种TV信号源的节目。(注意系统上可用的TV Inputs列表可以通过调用TvInputManager.getTvInputList()来获取。
一旦应用程序提供了特定TV通道的URL给tune(String, Uri)方法,它负责下层的服务绑定(和解除绑定如果当前的TvView已经绑定到一个服务)并且根据需要自动的分配或者释放资源。除了一些控制内容如何呈现的基本方法,它也提供了一个给已经连接的TvInputService分发输入事件的办法,以允许为Tv Input客制化按键动作。


Tv Input的选择和显示是通过TvView的tune方法来做的,我们看一下tune的实现:

    /**
     * Tunes to a given channel.
     *
     * @param inputId The ID of TV input which will play the given channel.
     * @param channelUri The URI of a channel.
     * @param params Extra parameters which might be handled with the tune event.
     * @hide
     */
    @SystemApi
    public void tune(String inputId, Uri channelUri, Bundle params) {
        ...
        if (mSessionCallback != null && mSessionCallback.mInputId.equals(inputId)) {
            if (mSession != null) {
                mSession.tune(channelUri, params);
            } else {
                // Session is not created yet. Replace the channel which will be set once the
                // session is made.
                mSessionCallback.mChannelUri = channelUri;
                mSessionCallback.mTuneParams = params;
            }
        } else {
            resetInternal();
            // When createSession() is called multiple times before the callback is called,
            // only the callback of the last createSession() call will be actually called back.
            // The previous callbacks will be ignored. For the logic, mSessionCallback
            // is newly assigned for every createSession request and compared with
            // MySessionCreateCallback.this.
            mSessionCallback = new MySessionCallback(inputId, channelUri, params);
            if (mTvInputManager != null) {
                mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
            }
        }
    }

tune方法有三个参数,我们只关注第一个参数inputId,每个Tv Input都有一个唯一的inputId和TvInputInfo实例,inputId由TvInputInfo.getId()获取。第一次调用tune的时候,mSessionCallback为null,所以首先执行mSessionCallback = new MySessionCallback(inputId, channelUri, params)创建一个SessionCallback实例,然后通过mTvInputManager.createSession(inputId, mSessionCallback, mHandler)来跟TvInputService之间创建一个会话,当会话创建成功以后,mSessionCallback被回调回来,createSession的具体细节我们忽略掉,最终会调用到TvInputService的createSession,

          public void createSession(InputChannel channel, ITvInputSessionCallback cb,
                    String inputId) {
                if (channel == null) {
                    Log.w(TAG, "Creating session without input channel");
                }
                if (cb == null) {
                    return;
                }
                SomeArgs args = SomeArgs.obtain();
                args.arg1 = channel;
                args.arg2 = cb;
                args.arg3 = inputId;
                mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args).sendToTarget();
            }

接着ServiceHandler.handleMessage

    public final void handleMessage(Message msg) {
            switch (msg.what) {
                case DO_CREATE_SESSION: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    InputChannel channel = (InputChannel) args.arg1;
                    ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2;
                    String inputId = (String) args.arg3;
                    args.recycle();
                    Session sessionImpl = onCreateSession(inputId);
                    ...
  else {
                        SomeArgs someArgs = SomeArgs.obtain();
                        someArgs.arg1 = sessionImpl;
                        someArgs.arg2 = stub;
                        someArgs.arg3 = cb;
                        someArgs.arg4 = null;
                        mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED,
                                someArgs).sendToTarget();
                    }
                    return;
                }
                case DO_NOTIFY_SESSION_CREATED: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    Session sessionImpl = (Session) args.arg1;
                    ITvInputSession stub = (ITvInputSession) args.arg2;
                    ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg3;
                    IBinder hardwareSessionToken = (IBinder) args.arg4;
                    try {
                        cb.onSessionCreated(stub, hardwareSessionToken);
                    } catch (RemoteException e) {
                        Log.e(TAG, "error in onSessionCreated");
                    }
                    if (sessionImpl != null) {
                        sessionImpl.initialize(cb);
                    }
                    args.recycle();
                    return
<think>我们结束之前关于AudioTrack和MediaCodec的讨论,现在转向Android TV Input FrameworkTV Input)的开发指南和实现。 TV Input Framework (TIF)Android系统上用于支持电视信号(如HDMI、调谐器、网络流等)的框架。它允许开发者创建TV输入服务(TV Input Service, TIS),这些服务可以接收电视信号并将其内容呈现给系统的电视应用(如Live Channels)。 ### TV Input 开发核心步骤 #### 1. **创建TV输入服务(TV Input Service)** - 继承自`TvInputService`类,并在AndroidManifest.xml中声明。 - 必须实现`onCreateSession`方法,返回一个`TvInputService.Session`实例。 ```java public class MyTvInputService extends TvInputService { @Override public Session onCreateSession(String inputId) { return new MySession(this); } } ``` #### 2. **实现会话(Session)** - `TvInputService.Session`负责处理与TV输入相关的操作,如调谐到频道、播放控制等。 - 关键方法: - `onSetSurface`:设置用于视频渲染的Surface。 - `onTune`:调谐到指定频道(URI)。 - `onRelease`:释放资。 ```java private class MySession extends TvInputService.Session { private Surface mSurface; private Context mContext; public MySession(Context context) { super(context); mContext = context; } @Override public void onSetSurface(Surface surface) { mSurface = surface; // 设置视频输出Surface } @Override public boolean onTune(Uri channelUri) { // 调谐到channelUri对应的频道 return true; // 返回true表示成功 } @Override public void onRelease() { // 释放资 } } ``` #### 3. **处理频道信息** - 使用`TvContract`类管理频道和节目信息。 - 创建`ContentProvider`来存储频道数据(可选,系统电视应用会通过`TvContract`访问)。 #### 4. **视频播放** - 在Session中,通常使用`MediaPlayer`或`ExoPlayer`播放视频流。 - 将播放器的视频输出绑定到`onSetSurface`提供的Surface。 ```java private MediaPlayer mMediaPlayer; @Override public void onSetSurface(Surface surface) { mSurface = surface; if (mMediaPlayer != null) { mMediaPlayer.setSurface(surface); } } @Override public boolean onTune(Uri channelUri) { if (mMediaPlayer != null) { mMediaPlayer.release(); } mMediaPlayer = new MediaPlayer(); mMediaPlayer.setSurface(mSurface); try { mMediaPlayer.setDataSource(mContext, channelUri); mMediaPlayer.prepareAsync(); mMediaPlayer.setOnPreparedListener(MediaPlayer::start); } catch (IOException e) { e.printStackTrace(); return false; } return true; } ``` #### 5. **在AndroidManifest.xml中声明服务** ```xml <service android:name=".MyTvInputService" android:permission="android.permission.BIND_TV_INPUT"> <intent-filter> <action android:name="android.media.tv.TvInputService" /> </intent-filter> <meta-data android:name="android.media.tv.input" android:resource="@xml/tv_input" /> </service> ``` #### 6. **创建TV输入描述文件(tv_input.xml)** - 在`res/xml/`目录下创建`tv_input.xml`文件,描述TV输入的基本信息。 ```xml <tv-input xmlns:android="http://schemas.android.com/apk/res/android" android:canRecord="true" android:setupActivity="com.example.MySetupActivity" /> ``` ### 高级功能 #### 时移播放(Time-Shifting) - 实现`Session`的`onTimeShiftGetCurrentPosition`和`onTimeShiftGetStartPosition`等方法。 - 需要支持播放控制(暂停、快进、快退等)。 #### 录制(Recording) - 实现`TvInputService.RecordingSession`。 - 重写`onStartRecording`、`onStopRecording`等方法。 ### 调试与测试 - 使用`adb shell dumpsys tv_input`查看已注册的TV输入服务状态。 - 通过系统电视应用(如Live Channels)测试输入。 ### 注意事项 1. **权限**:需要声明`TV_INPUT_HARDWARE`权限(仅系统应用可用)或作为普通应用提供网络流输入。 2. **性能**:视频播放必须满足实时性要求,避免卡顿。 3. **兼容性**:不同Android TV版本可能有差异,需测试目标版本。 ### 示例项目 Google官方示例:[androidtv-sample-inputs](https://github.com/android/tv-samples) ### §§相关问题§§ 1. 如何为TV输入服务添加电子节目指南(EPG)? 2. TV输入服务如何支持DRM保护的内容3. 如何实现TV输入服务的时移播放功能? 4. TV输入服务如何进行音视频同步? 5. 如何处理TV输入服务中的遥控器按键事件?</think>## Android TV Input Framework 开发指南 TV Input Framework (TIF)Android TV 的核心组件,用于管理各种电视信号(如 HDMI、调谐器、网络流媒体等)。以下是关键实现指南: ### 一、核心组件 1. **TV Input Service** 继承 `TvInputService` 的基础服务,负责信号管理: ```java public class MyTvInputService extends TvInputService { @Override public Session onCreateSession(String inputId) { return new MySession(this); } } ``` 2. **TV Input Session** 处理具体播放逻辑的会话类: ```java private class MySession extends TvInputService.Session { private Surface mSurface; public MySession(Context context) { super(context); } @Override public boolean onSetSurface(Surface surface) { mSurface = surface; // 设置视频渲染表面 return true; } } ``` ### 二、播放流程实现 1. **视频渲染** 使用 `MediaPlayer` 或 `ExoPlayer` 绑定到 Surface: ```java @Override public boolean onTune(Uri channelUri) { mMediaPlayer = new MediaPlayer(); mMediaPlayer.setSurface(mSurface); mMediaPlayer.setDataSource(this, channelUri); mMediaPlayer.prepareAsync(); return true; } ``` 2. **EPG 电子节目指南** 通过 `TvContract` 添加节目信息: ```java ContentValues values = new ContentValues(); values.put(TvContract.Programs.COLUMN_TITLE, "News"); values.put(TvContract.Programs.COLUMN_START_TIME_UTC, startTime); getContentResolver().insert(TvContract.Programs.CONTENT_URI, values); ``` ### 三、输入声明 在 `AndroidManifest.xml` 中注册: ```xml <service android:name=".MyTvInputService" android:permission="android.permission.BIND_TV_INPUT"> <intent-filter> <action android:name="android.media.tv.TvInputService" /> </intent-filter> <meta-data android:name="android.media.tv.input" android:resource="@xml/tv_input" /> </service> ``` 创建 `res/xml/tv_input.xml`: ```xml <tv-input xmlns:android="http://schemas.android.com/apk/res/android" android:canRecord="true" android:setupActivity="com.example.SetupActivity" /> ``` ### 四、高级功能实现 1. **时移播放** 重写会话的时移方法: ```java @Override public void onTimeShiftPlay() { mMediaPlayer.start(); // 恢复播放 } @Override public void onTimeShiftPause() { mMediaPlayer.pause(); // 暂停播放 } ``` 2. **DRM 内容保护** 使用 `MediaDrm` 处理加密内容: ```java MediaDrm drm = new MediaDrm(UUID.fromString(drmScheme)); drm.provideKeyResponse(sessionId, keyResponse); ``` ### 五、调试技巧 1. 使用 TV 模拟器: ```bash adb shell am start -n \ com.android.tv/com.android.tv.tuner.tvinput.TvInputSetupActivity \ --es input_id "com.example/.MyTvInputService" ``` 2. 查看输入状态: ```bash adb shell dumpsys tv_input ``` ### 六、性能优化 1. **启动时间优化** 延迟加载非核心资,确保 `onCreateSession()` 在 $t \leq 200ms$ 内完成 2. **内存管理** 使用 `SurfaceHolder.Callback` 监听 Surface 生命周期: ```java surfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceDestroyed(SurfaceHolder holder) { releasePlayer(); // 及时释放资 } }); ``` > 参考:[Android TV Input Framework 官方文档](https://developer.android.com/tv/tif)[^1] --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值