Saturday, August 15, 2015

Develop a HDMI TV Input using Android TV Framework - Part 2

Implement TvInput HAL for HDMI Input

This part deals with Android TV Input HAL implementation for HDMI. By end of this part a dummy input HAL will be implemented which can interact to any TVInput service like one of ours in part 1.
We will be able to see the interaction in logs as well as our input/session and service in "dumpsys tv_input"
If one has further lower level HDMI kernel driver implementation, it can be called here as mentioned at various placeholders and  will be able to play the videos from HDMI media player connected to Android TV, using an TV application.

As like other Android HALs, tv_input hal is located in /hardware/libhardware/modules/tv_input and default lib will be built as "tv_input.default.so",

So a basic dummy tv_input hal can be implemented like below :



typedef struct tv_input_private {
    tv_input_device_t device;

    // Callback related data
    const tv_input_callback_ops_t* callback;
    void* callback_data;
} tv_input_private_t;

static int tv_input_device_open(const struct hw_module_t* module,
        const char* name, struct hw_device_t** device);

static struct hw_module_methods_t tv_input_module_methods = {
    open: tv_input_device_open
};

tv_input_module_t HAL_MODULE_INFO_SYM = {
    common: {
        tag: HARDWARE_MODULE_TAG,
        version_major: 0,
        version_minor: 1,
        id: TV_INPUT_HARDWARE_MODULE_ID,
        name: "Sample TV input module",
        author: "The Android Open Source Project",
        methods: &tv_input_module_methods,
    }
};

#define HDMI_DEV_ID 1
#define HDMI_PORT_ID 1

void notify_hdmi_device_available()
{
    tv_input_event_t event;
    event.device_info.device_id =HDMI_DEV_ID;
    event.device_info.type = TV_INPUT_TYPE_HDMI;
    event.type = TV_INPUT_EVENT_DEVICE_AVAILABLE;
    event.device_info.audio_type = AUDIO_DEVICE_NONE;
    event.device_info.hdmi.port_id = HDMI_PORT_ID;
    callback->notify(dev, &event, data);
}

tv_stream_config_t* get_stream_configs()
{
    tv_stream_config_t* config = (tv_stream_config_t*)malloc(sizeof(config));
    config->stream_id=0;
    config->type =TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE ;
    config->max_video_width = 1280;
    config->max_video_height = 1080;
    return config;
}


static int tv_input_initialize(struct tv_input_device* dev,
        const tv_input_callback_ops_t* callback, void* data)
{
    if (dev == NULL || callback == NULL) {
        return -EINVAL;
    }
    tv_input_private_t* priv = (tv_input_private_t*)dev;
    if (priv->callback != NULL) {
        return -EEXIST;
    }
    priv->callback = callback;
    priv->callback_data = data;
    notify_hdmi_device_available(); 
    return 0;
}

static int tv_input_get_stream_configurations(
        const struct tv_input_device*, int, int*, const tv_stream_config_t**)
{
    *num_configurations = 1;
    *config = get_stream_configs();
    return 0;
}

static int tv_input_open_stream(struct tv_input_device*, int, tv_stream_t*)
{
    return 0;
}

static int tv_input_close_stream(struct tv_input_device*, int, int)
{
    return 0;
}

static int tv_input_request_capture(
        struct tv_input_device*, int, int, buffer_handle_t, uint32_t)
{
    return 0;
}

static int tv_input_cancel_capture(struct tv_input_device*, int, int, uint32_t)
{
    return 0;
}

static int tv_input_device_close(struct hw_device_t *dev)
{
    tv_input_private_t* priv = (tv_input_private_t*)dev;
    if (priv) {
        free(priv);
    }
    return 0;
}


static int tv_input_device_open(const struct hw_module_t* module,
        const char* name, struct hw_device_t** device)
{
    int status = -EINVAL;
    if (!strcmp(name, TV_INPUT_DEFAULT_DEVICE)) {
        tv_input_private_t* dev = (tv_input_private_t*)malloc(sizeof(*dev));

        /* initialize our state here */
        memset(dev, 0, sizeof(*dev));

        /* initialize the procs */
        dev->device.common.tag = HARDWARE_DEVICE_TAG;
        dev->device.common.version = TV_INPUT_DEVICE_API_VERSION_0_1;
        dev->device.common.module = const_cast<hw_module_t*>(module);
        dev->device.common.close = tv_input_device_close;

        dev->device.initialize = tv_input_initialize;
        dev->device.get_stream_configurations =
                tv_input_get_stream_configurations;
        dev->device.open_stream = tv_input_open_stream;
        dev->device.close_stream = tv_input_close_stream;
        dev->device.request_capture = tv_input_request_capture;
        dev->device.cancel_capture = tv_input_cancel_capture;

        *device = &dev->device.common;
        status = 0;
    }
    return status;
}

This HAL will interact to "TvInputManagerService", a SystemService which will call tv_input_initialize at the time of system bootup.

TvInputManagerService uses TvInputHardwareManager/TvInputHal, further jni binding, source code location is as below.

/frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java

/frameworks/base/services/core/jni/com_android_server_tv_TvInputHal.cpp

Now if HDMI source is selected from TV application, it will interact to TvInputService(implemented in part1) and TvInputService will be able to further talk to our above  tv_input hal.

Also $adb shell dumpsys tv_input shall now list our input in "inputmap" and if session is running, it will show sessionStateMap as well.


No comments:

Post a Comment