这篇文章主要分析如何切换到Tv Input,并显示其内容。App一般通过TvView来跟TvInputService建立联系,通过调用TvView的方法来选择Tv Input,显示内容。

源码路径:frameworks/base/media/java/android/media/tv

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;                }

先调用onCreateSession(inputId)创建一个Session,然后通知Session创建成功,即调用cb.onSessionCreated,最终会调到TvView.MySessionCallback.onSessionCreated,

   public void onSessionCreated(Session session) {           ...            mSession = session;            if (session != null) {                synchronized (sMainTvViewLock) {                    if (hasWindowFocus() && TvView.this == sMainTvView.get()) {                        mSession.setMain();          ---<1>                    }                }                // mSurface may not be ready yet as soon as starting an application.                // In the case, we don't send Session.setSurface(null) unnecessarily.                // setSessionSurface will be called in surfaceCreated.                if (mSurface != null) {                    setSessionSurface(mSurface);      ---<2>                    if (mSurfaceChanged) {                        dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);                    }                }                createSessionOverlayView();                if (mCaptionEnabled != CAPTION_DEFAULT) {                    mSession.setCaptionEnabled(mCaptionEnabled == CAPTION_ENABLED);                }                mSession.tune(mChannelUri, mTuneParams);    ---<3>                if (mHasStreamVolume) {                    mSession.setStreamVolume(mStreamVolume);                }             ...            }  
  • <1> 设置当前的Session为主Session,setMain最终会调到TvInputService.Session.onSetMain,如果TV Input Service是管理HDMI CEC逻辑设备,需要实现onSetMain这个方法来选择对应的HDMI CEC逻辑设备作为源设备,主Session对应的Tv Input为当前活动的源设备(Active Source)。

  • <2> 为当前的Input Session设置Surface,Tv Input会在这个surface上渲染视频,setSessionSurface会最终调到TvInputService.Session.setSurface,接着会调到TvInputHardwareManager.setSurface,最后调用到TvInputHal.addOrupdateStream让HAL来做相关的配置。这一步是关键,Tv Input端口的选择以及视频通道的配置都是这一部完成的。

  • <3> 调谐到对应的频道,主要是指模拟电视或者数字电视。

备注:

文章来源:http://www.07net01.com/program/2016/02/1301393.html

更多相关文章

  1. 学习Android(安卓)--从现在开始
  2. 使用Lint 和 Annotations来提升代码质量
  3. Android(安卓)databinding(详解三)--自定义属性使用
  4. Android之XUtils的框架总结
  5. android 莫名出现Conversion to Dalvik format failed: Unable t
  6. Android发送短信时 短信内容超长处理
  7. 【Android(安卓)界面效果34】Android里Service的bindService()和
  8. android电话流程(打电话,接电话,挂断电话)
  9. Android(安卓)Studio使用Volley

随机推荐

  1. 说说在 Android 中如何压缩图片
  2. 杂谈汇总(更新...)
  3. Android 下的视频播放器faplayer 编译
  4. IoT项目:Arduino使用Parse.com的Temboo向A
  5. Android(安卓)Studio Generate Signed AP
  6. 怎样隐藏Android软件盘
  7. java.lang.RuntimeException: setAudioSo
  8. Android(安卓)ANR 分析解决方法
  9. android 操作sdcard中的多媒体文件(一)——
  10. 解决更新并使用最新ADT20不能创建android