Saturday, August 29, 2015

TvInputService Implementation : Displaying Video from Connected HDMI Device

TvInputService Implementation : Displaying Video from Connected HDMI Device

As we know Android Tv Input Framework, a data source or Tv Input like IP-TV, Tuner, HDMI etc. is nothing else but the implementation of TvInputService. So if any of these datasource/device need KeyEvents, that should be implemented in TvInputService.

As in the post we implemented a TvInputService for HDMI input source. In order to get the video from device connected to HDMI input, we need to override following method from TvInputService correctly.

public boolean onSetSurface(Surface surface) { }

Let's see the call flow from application and implement this method.

An application uses TvView and calls it's setSurface() method to show content from selected TvInput source.

TvView.java
surfaceCreated(SurfaceHolder holder) {
   mSurface = holder.getSurface();
   setSessionSurface(mSurface);
}

private void setSessionSurface(Surface surface) {
    mSession.setSurface(surface);
}

TvInputManager.java
setSurface(Surface surface) {
    mService.setSurface(mToken, surface, mUserId);
}

TvInputManagerService.java
setSurface(IBinder sessionToken, Surface surface, int userId) {
     getSessionLocked(sessionState.hardwareSessionToken,
                 Process.SYSTEM_UID, resolvedUserId).setSurface(surface);
}

TvInputService.java
setSurface(Surface surface) {
    onSetSurface(surface);
}

Now we this need to be overriden. For HDMI-Input, or any other Hardware device, it can be done like below.

private final TvInputManager.HardwareCallback mHardwareCallback = new TvInputManager.HardwareCallback() {
    @Override
        public void onReleased() 
    {
    }
    @Override
        public void onStreamConfigChanged(TvStreamConfig[] configs)
                {
        mTvStreamConfig = configs;
                }

    };

    mHardware = mTvInputManager.acquireTvInputHardware(mDeviceId, mHardwareCallback, mTvInputInfo);

onSetSurface(Surface surface)
{
    mHardware.setSurface(mSurface, mTvStreamConfig[0]);
    return true;
}

Now this call will be directed to

TvInputManager.java
setSurface(Surface surface, TvStreamConfig config) {
      return mInterface.setSurface(surface, config);
}


TvInputHardwareManager.java
A TvInputHardwareImpl object holds only one active session. Therefore, if a client
attempts to call setSurface with different TvStreamConfig objects, the last call will prevail.

setSurface(Surface surface, TvStreamConfig config)

      if (surface == null) {
          result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
          mActiveConfig = null;
      } else {

          result = mHal.addStream(mInfo.getDeviceId(), surface, config);
}

TvInputHal.java
addStream(int deviceId, Surface surface, TvStreamConfig streamConfig) {

    if (nativeAddStream(mPtr, deviceId, streamConfig.getStreamId(), surface) == 0) {
          return SUCCESS;
}

jni/com_android_server_tv_TvInputHal.cpp
JTvInputHal::addStream(int deviceId, int streamId, const sp<Surface>& surface) {

   if (mDevice->get_stream_configurations(
           mDevice, deviceId, &numConfigs, &configs) != 0) {
       return UNKNOWN_ERROR;
   }

   if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
       ALOGE("Couldn't add stream");
       return UNKNOWN_ERROR;
   }

}

And finally in tv_input HAL implementation, here put your code for HDMI kernel driver calls to get the video stream.

tv_input_open_stream(struct tv_input_device*, int device id , tv_stream_t* stream)
{
//This is the place, the HDMI driver functions call related to opening stream should be made.
    return 0;
}

Similarly if surface is null, then close_stream will be called,

tv_input_close_stream(struct tv_input_device*, int device id , int)
{
//This is the place, the HDMI driver functions call related to closing stream should be made.
    return 0;
}

For a dummy tv_input HAL implementation, refer to post.
                     



No comments:

Post a Comment