C++程序  |  5836行  |  170.91 KB

/**
 * @copyright
 *
 *   Copyright (c) 2015, The Linux Foundation. All rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *   * Neither the name of The Linux Foundation nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE DISCLAIMED.
 *   IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
 *   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 *   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 *   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 *   DAMAGE.
 *
 * @file
 *
 *   omx_swvdec.cpp
 *
 * @brief
 *
 *   OMX software video decoder component source.
 */

#include <assert.h>
#include <fcntl.h>
#include <sys/mman.h>

#include <media/hardware/HardwareAPI.h>
#include <gralloc_priv.h>

#include "OMX_QCOMExtns.h"

#include "omx_swvdec.h"

#include "swvdec_api.h"

/**
 * ----------------
 * PUBLIC FUNCTIONS
 * ----------------
 */

/**
 * @brief Create & return component class instance.
 *
 * @retval Pointer to new component class instance.
 */
void *get_omx_component_factory_fn(void)
{
    return new omx_swvdec;
}

/**
 * @brief Component constructor.
 */
omx_swvdec::omx_swvdec():
    m_state(OMX_StateInvalid),
    m_status_flags(0),
    m_swvdec_codec(SWVDEC_CODEC_INVALID),
    m_swvdec_handle(NULL),
    m_swvdec_created(false),
    m_omx_video_codingtype(OMX_VIDEO_CodingUnused),
    m_omx_color_formattype(OMX_COLOR_FormatUnused),
    m_sync_frame_decoding_mode(false),
    m_android_native_buffers(false),
    m_meta_buffer_mode(false),
    m_port_reconfig_inprogress(false),
    m_buffer_array_ip(NULL),
    m_buffer_array_op(NULL),
    m_meta_buffer_array(NULL)
{
    // memset all member variables that are composite structures
    memset(&m_cmp,                     0, sizeof(m_cmp)); // part of base class
    memset(&m_cmp_name[0],             0, sizeof(m_cmp_name));
    memset(&m_role_name[0],            0, sizeof(m_role_name));
    memset(&m_frame_rate,              0, sizeof(m_frame_rate));
    memset(&m_frame_dimensions,        0, sizeof(m_frame_dimensions));
    memset(&m_frame_attributes,        0, sizeof(m_frame_attributes));
    memset(&m_async_thread,            0, sizeof(m_async_thread));
    memset(&m_port_ip,                 0, sizeof(m_port_ip));
    memset(&m_port_op,                 0, sizeof(m_port_op));
    memset(&m_callback,                0, sizeof(m_callback));
    memset(&m_app_data,                0, sizeof(m_app_data));
    memset(&m_prio_mgmt,               0, sizeof(m_prio_mgmt));
    memset(&m_sem_cmd,                 0, sizeof(m_sem_cmd));
    memset(&m_meta_buffer_array_mutex, 0, sizeof(m_meta_buffer_array_mutex));

    // null-terminate component name & role name strings
    m_cmp_name[0]  = '\0';
    m_role_name[0] = '\0';

    // ports are enabled & unpopulated by default
    m_port_ip.enabled     = OMX_TRUE;
    m_port_op.enabled     = OMX_TRUE;
    m_port_ip.unpopulated = OMX_TRUE;
    m_port_op.unpopulated = OMX_TRUE;
}

/**
 * @brief Component destructor.
 */
omx_swvdec::~omx_swvdec()
{
}

/**
 * @brief Initialize component.
 *
 * @param[in] cmp_name: Component name string.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::component_init(OMX_STRING cmp_name)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    OMX_SWVDEC_LOG_API("'%s', version date: %s",
                       cmp_name,
                       OMX_SWVDEC_VERSION_DATE);

    omx_swvdec_log_init();

    if (m_state != OMX_StateInvalid)
    {
        OMX_SWVDEC_LOG_ERROR("disallowed in state %s",
                             OMX_STATETYPE_STRING(m_state));
        retval = OMX_ErrorIncorrectStateOperation;
        goto component_init_exit;
    }

    // set up codec type variables based on component name string

    if (!strncmp(cmp_name,
                 "OMX.qti.video.decoder.mpeg4sw",
                 OMX_MAX_STRINGNAME_SIZE))
    {
        OMX_SWVDEC_LOG_LOW("'video_decoder.mpeg4'");

        strlcpy(m_cmp_name,               cmp_name, OMX_MAX_STRINGNAME_SIZE);
        strlcpy(m_role_name, "video_decoder.mpeg4", OMX_MAX_STRINGNAME_SIZE);

        m_swvdec_codec         = SWVDEC_CODEC_MPEG4;
        m_omx_video_codingtype = OMX_VIDEO_CodingMPEG4;
    }
    else if (!strncmp(cmp_name,
                      "OMX.qti.video.decoder.h263sw",
                      OMX_MAX_STRINGNAME_SIZE))
    {
        OMX_SWVDEC_LOG_LOW("video_decoder.h263");

        strlcpy(m_cmp_name,              cmp_name, OMX_MAX_STRINGNAME_SIZE);
        strlcpy(m_role_name, "video_decoder.h263", OMX_MAX_STRINGNAME_SIZE);

        m_swvdec_codec         = SWVDEC_CODEC_H263;
        m_omx_video_codingtype = OMX_VIDEO_CodingH263;
    }
    else if (((!strncmp(cmp_name,
                        "OMX.qti.video.decoder.divxsw",
                        OMX_MAX_STRINGNAME_SIZE))) ||
             ((!strncmp(cmp_name,
                        "OMX.qti.video.decoder.divx4sw",
                        OMX_MAX_STRINGNAME_SIZE))))
    {
        OMX_SWVDEC_LOG_LOW("video_decoder.divx");

        strlcpy(m_cmp_name,              cmp_name, OMX_MAX_STRINGNAME_SIZE);
        strlcpy(m_role_name, "video_decoder.divx", OMX_MAX_STRINGNAME_SIZE);

        m_swvdec_codec         = SWVDEC_CODEC_MPEG4;
        m_omx_video_codingtype = ((OMX_VIDEO_CODINGTYPE) QOMX_VIDEO_CodingDivx);
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("'%s': invalid component name", cmp_name);
        retval = OMX_ErrorInvalidComponentName;
        goto component_init_exit;
    }

    m_omx_color_formattype =
        (OMX_COLOR_FORMATTYPE) OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m;

    m_frame_rate.fps_numerator   = DEFAULT_FPS_NUMERATOR;
    m_frame_rate.fps_denominator = DEFAULT_FPS_DENOMINATOR;

    {
        SWVDEC_CALLBACK callback;

        SWVDEC_STATUS retval_swvdec;

        // initialize SwVdec core library

        callback.pfn_empty_buffer_done  = swvdec_empty_buffer_done_callback;
        callback.pfn_fill_buffer_done   = swvdec_fill_buffer_done_callback;
        callback.pfn_event_notification = swvdec_event_handler_callback;
        callback.p_client               = this;

        if ((retval_swvdec = swvdec_init(&m_swvdec_handle,
                                         m_swvdec_codec,
                                         &callback)) !=
            SWVDEC_STATUS_SUCCESS)
        {
            retval = retval_swvdec2omx(retval_swvdec);
            goto component_init_exit;
        }

        m_swvdec_created = true;

        // set frame dimensions for OMX component & SwVdec core
        if ((retval = set_frame_dimensions(DEFAULT_FRAME_WIDTH,
                                           DEFAULT_FRAME_HEIGHT)) !=
            OMX_ErrorNone)
        {
            goto component_init_exit;
        }

        // set frame attributes for OMX component & SwVdec core
        if ((retval = set_frame_attributes(DEFAULT_ALIGNMENT_STRIDE,
                                           DEFAULT_ALIGNMENT_SCANLINES_Y,
                                           DEFAULT_ALIGNMENT_SCANLINES_UV,
                                           DEFAULT_ALIGNMENT_SIZE)) !=
            OMX_ErrorNone)
        {
            goto component_init_exit;
        }
    }

    // get SwVdec buffer requirements for input port; set m_port_ip
    if ((retval = get_buffer_requirements_swvdec(OMX_CORE_PORT_INDEX_IP)) !=
        OMX_ErrorNone)
    {
        goto component_init_exit;
    }

    // get SwVdec buffer requirements for output port; set m_port_op
    if ((retval = get_buffer_requirements_swvdec(OMX_CORE_PORT_INDEX_OP)) !=
        OMX_ErrorNone)
    {
        goto component_init_exit;
    }

    // create callback thread's mutex & condition variable
    if ((retval = async_thread_create()) != OMX_ErrorNone)
    {
        goto component_init_exit;
    }

    // create semaphore for command processing
    if (sem_init(&m_sem_cmd, 0, 0))
    {
        OMX_SWVDEC_LOG_ERROR("failed to create command processing semaphore");
        retval = OMX_ErrorInsufficientResources;
        goto component_init_exit;
    }

    // create mutex for meta buffer info array
    if (pthread_mutex_init(&m_meta_buffer_array_mutex, NULL))
    {
        OMX_SWVDEC_LOG_ERROR("failed to create meta buffer info array mutex");
        retval = OMX_ErrorInsufficientResources;
        goto component_init_exit;
    }

    // move to 'loaded' state
    OMX_SWVDEC_LOG_HIGH("OMX_StateInvalid -> OMX_StateLoaded");
    m_state = OMX_StateLoaded;

component_init_exit:
    return retval;
}

/**
 * @brief De-initialize component.
 *
 * @param[in] cmp_handle: Component handle.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::component_deinit(OMX_HANDLETYPE cmp_handle)
{
    OMX_SWVDEC_LOG_API("");

    if (cmp_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("cmp_handle = NULL");
    }

    pthread_mutex_destroy(&m_meta_buffer_array_mutex);

    sem_destroy(&m_sem_cmd);

    async_thread_destroy();

    if (m_swvdec_created)
    {
        swvdec_deinit(m_swvdec_handle);
        m_swvdec_handle = NULL;
    }

    OMX_SWVDEC_LOG_HIGH("all done, goodbye!");

    return OMX_ErrorNone;
}

/**
 * @brief Get component version.
 *
 * @param[in]     cmp_handle:     Component handle.
 * @param[in]     cmp_name:       Component name string.
 * @param[in,out] p_cmp_version:  Pointer to component version variable.
 * @param[in,out] p_spec_version: Pointer to OMX spec version variable.
 * @param[in,out] p_cmp_UUID:     Pointer to component UUID variable.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::get_component_version(OMX_HANDLETYPE   cmp_handle,
                                                OMX_STRING       cmp_name,
                                                OMX_VERSIONTYPE *p_cmp_version,
                                                OMX_VERSIONTYPE *p_spec_version,
                                                OMX_UUIDTYPE    *p_cmp_UUID)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    (void) p_cmp_UUID;

    OMX_SWVDEC_LOG_API("");

    if (m_state == OMX_StateInvalid)
    {
        OMX_SWVDEC_LOG_ERROR("in invalid state");
        retval = OMX_ErrorInvalidState;
    }
    else if (cmp_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("cmp_handle = NULL");
        retval = OMX_ErrorInvalidComponent;
    }
    else if (strncmp(cmp_name, m_cmp_name, sizeof(m_cmp_name)))
    {
        OMX_SWVDEC_LOG_ERROR("'%s': invalid component name", cmp_name);
        retval = OMX_ErrorInvalidComponentName;
    }
    else if (p_cmp_version == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_cmp_version = NULL");
        retval = OMX_ErrorBadParameter;
    }
    else if (p_spec_version == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_spec_version = NULL");
        retval = OMX_ErrorBadParameter;
    }
    else
    {
        p_spec_version->nVersion = OMX_SPEC_VERSION;
    }

get_component_version_exit:
    return retval;
}

/**
 * @brief Send command to component.
 *
 * @param[in] cmp_handle: Component handle.
 * @param[in] cmd:        Command.
 * @param[in] param:      Command parameter.
 * @param[in] p_cmd_data: Pointer to command data.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::send_command(OMX_HANDLETYPE  cmp_handle,
                                       OMX_COMMANDTYPE cmd,
                                       OMX_U32         param,
                                       OMX_PTR         p_cmd_data)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    (void) p_cmd_data; // prevent warning for unused function argument

    if (m_state == OMX_StateInvalid)
    {
        OMX_SWVDEC_LOG_ERROR("in invalid state");
        retval = OMX_ErrorInvalidState;
        goto send_command_exit;
    }
    else if (cmp_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("cmp_handle = NULL");
        retval = OMX_ErrorInvalidComponent;
        goto send_command_exit;
    }

    switch (cmd)
    {

    case OMX_CommandStateSet:
    {
        OMX_SWVDEC_LOG_API("%s, %s",
                           OMX_COMMANDTYPE_STRING(cmd),
                           OMX_STATETYPE_STRING((OMX_STATETYPE) param));
        break;
    }

    case OMX_CommandFlush:
    case OMX_CommandPortDisable:
    case OMX_CommandPortEnable:
    {
        OMX_SWVDEC_LOG_API("%s, port index %d",
                           OMX_COMMANDTYPE_STRING(cmd),
                           param);

        if ((param != OMX_CORE_PORT_INDEX_IP) &&
            (param != OMX_CORE_PORT_INDEX_OP) &&
            (param != OMX_ALL))
        {
            OMX_SWVDEC_LOG_ERROR("port index '%d' invalid", param);
            retval = OMX_ErrorBadPortIndex;
        }
        break;
    }

    default:
    {
        OMX_SWVDEC_LOG_API("cmd %d, param %d", cmd, param);

        OMX_SWVDEC_LOG_ERROR("cmd '%d' invalid", cmd);
        retval = OMX_ErrorBadParameter;
        break;
    }

    } // switch (cmd)

    if (retval == OMX_ErrorNone)
    {
        if (cmp_handle == NULL)
        {
            OMX_SWVDEC_LOG_ERROR("cmp_handle = NULL");
            retval = OMX_ErrorInvalidComponent;
        }
        else if (m_state == OMX_StateInvalid)
        {
            OMX_SWVDEC_LOG_ERROR("in invalid state");
            retval = OMX_ErrorInvalidState;
        }
    }

    if (retval != OMX_ErrorNone)
    {
        // command not processed; return error code via event handler callback
        async_post_event(OMX_SWVDEC_EVENT_ERROR, retval, 0);
    }
    else
    {
        // post command event
        async_post_event(OMX_SWVDEC_EVENT_CMD, cmd, param);

        // wait on command semaphore
        sem_wait(&m_sem_cmd);
    }

send_command_exit:
    return retval;
}

/**
 * @brief Get a parameter from component.
 *
 * @param[in]     cmp_handle:   Component handle.
 * @param[in]     param_index:  Parameter index.
 * @param[in,out] p_param_data: Pointer to parameter data.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::get_parameter(OMX_HANDLETYPE cmp_handle,
                                        OMX_INDEXTYPE  param_index,
                                        OMX_PTR        p_param_data)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (m_state == OMX_StateInvalid)
    {
        OMX_SWVDEC_LOG_ERROR("in invalid state");
        retval = OMX_ErrorInvalidState;
    }
    else if (cmp_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("cmp_handle = NULL");
        retval = OMX_ErrorInvalidComponent;
    }
    else if (p_param_data == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_param_data = NULL");
        retval = OMX_ErrorBadParameter;
    }

    if (retval != OMX_ErrorNone)
    {
        goto get_parameter_exit;
    }

    switch (param_index)
    {

    case OMX_IndexParamAudioInit:
    {
        OMX_PORT_PARAM_TYPE *p_port_param =
            (OMX_PORT_PARAM_TYPE *) p_param_data;

        p_port_param->nPorts           = 0;
        p_port_param->nStartPortNumber = 0;

        OMX_SWVDEC_LOG_API("OMX_IndexParamAudioInit: "
                           "%d port(s), start port %d",
                           p_port_param->nPorts,
                           p_port_param->nStartPortNumber);
        break;
    }

    case OMX_IndexParamImageInit:
    {
        OMX_PORT_PARAM_TYPE *p_port_param =
            (OMX_PORT_PARAM_TYPE *) p_param_data;

        p_port_param->nPorts           = 0;
        p_port_param->nStartPortNumber = 0;

        OMX_SWVDEC_LOG_API("OMX_IndexParamImageInit: "
                           "%d port(s), start port %d",
                           p_port_param->nPorts,
                           p_port_param->nStartPortNumber);
        break;
    }

    case OMX_IndexParamVideoInit:
    {
        OMX_PORT_PARAM_TYPE *p_port_param =
            (OMX_PORT_PARAM_TYPE *) p_param_data;

        p_port_param->nPorts           = 2;
        p_port_param->nStartPortNumber = 0;

        OMX_SWVDEC_LOG_API("OMX_IndexParamVideoInit: "
                           "%d port(s), start port %d",
                           p_port_param->nPorts,
                           p_port_param->nStartPortNumber);
        break;
    }

    case OMX_IndexParamOtherInit:
    {
        OMX_PORT_PARAM_TYPE *p_port_param =
            (OMX_PORT_PARAM_TYPE *) p_param_data;

        p_port_param->nPorts           = 0;
        p_port_param->nStartPortNumber = 0;

        OMX_SWVDEC_LOG_API("OMX_IndexParamOtherInit: "
                           "%d port(s), start port %d",
                           p_port_param->nPorts,
                           p_port_param->nStartPortNumber);
        break;
    }

    case OMX_IndexConfigPriorityMgmt:
    {
        OMX_PRIORITYMGMTTYPE *p_prio_mgmt =
            (OMX_PRIORITYMGMTTYPE *) p_param_data;

        OMX_SWVDEC_LOG_API("OMX_IndexConfigPriorityMgmt");

        memcpy(p_prio_mgmt, &m_prio_mgmt, sizeof(OMX_PRIORITYMGMTTYPE));
        break;
    }

    case OMX_IndexParamStandardComponentRole:
    {
        OMX_PARAM_COMPONENTROLETYPE *p_cmp_role =
            (OMX_PARAM_COMPONENTROLETYPE *) p_param_data;

        strlcpy((char *) p_cmp_role->cRole,
                m_role_name,
                OMX_MAX_STRINGNAME_SIZE);

        OMX_SWVDEC_LOG_API("OMX_IndexParamStandardComponentRole: %s",
                           p_cmp_role->cRole);
        break;
    }

    case OMX_IndexParamPortDefinition:
    {
        OMX_PARAM_PORTDEFINITIONTYPE *p_port_def =
            (OMX_PARAM_PORTDEFINITIONTYPE *) p_param_data;

        OMX_SWVDEC_LOG_API("OMX_IndexParamPortDefinition, port index %d",
                           p_port_def->nPortIndex);

        retval = get_port_definition(p_port_def);
        break;
    }

    case OMX_IndexParamCompBufferSupplier:
    {
        OMX_PARAM_BUFFERSUPPLIERTYPE *p_buffer_supplier =
            (OMX_PARAM_BUFFERSUPPLIERTYPE *) p_param_data;

        OMX_SWVDEC_LOG_API("OMX_IndexParamCompBufferSupplier, port index %d",
                           p_buffer_supplier->nPortIndex);

        if ((p_buffer_supplier->nPortIndex == OMX_CORE_PORT_INDEX_IP) ||
            (p_buffer_supplier->nPortIndex == OMX_CORE_PORT_INDEX_OP))
        {
            p_buffer_supplier->eBufferSupplier = OMX_BufferSupplyUnspecified;
        }
        else
        {
            OMX_SWVDEC_LOG_ERROR("port index '%d' invalid",
                                 p_buffer_supplier->nPortIndex);
            retval = OMX_ErrorBadPortIndex;
        }

        break;
    }

    case OMX_IndexParamVideoPortFormat:
    {
        OMX_VIDEO_PARAM_PORTFORMATTYPE *p_port_format =
            (OMX_VIDEO_PARAM_PORTFORMATTYPE *) p_param_data;

        OMX_SWVDEC_LOG_API("OMX_IndexParamVideoPortFormat, "
                           "port index %d, index %d",
                           p_port_format->nPortIndex,
                           p_port_format->nIndex);

        retval = get_video_port_format(p_port_format);
        break;
    }

    case OMX_IndexParamVideoMpeg2:
    {
        OMX_SWVDEC_LOG_ERROR("OMX_IndexParamVideoMpeg2: unsupported");
        retval = OMX_ErrorUnsupportedIndex;
        break;
    }

    case OMX_IndexParamVideoMpeg4:
    {
        OMX_SWVDEC_LOG_API("OMX_IndexParamVideoMpeg4: unsupported");
        retval = OMX_ErrorUnsupportedIndex;
        break;
    }

    case OMX_IndexParamVideoAvc:
    {
        OMX_SWVDEC_LOG_API("OMX_IndexParamVideoAvc: unsupported");
        retval = OMX_ErrorUnsupportedIndex;
        break;
    }

    case OMX_IndexParamVideoH263:
    {
        OMX_SWVDEC_LOG_API("OMX_IndexParamVideoH263: unsupported");
        retval = OMX_ErrorUnsupportedIndex;
        break;
    }

    case OMX_IndexParamVideoProfileLevelQuerySupported:
    {
        OMX_VIDEO_PARAM_PROFILELEVELTYPE *p_profilelevel =
            (OMX_VIDEO_PARAM_PROFILELEVELTYPE *) p_param_data;

        OMX_SWVDEC_LOG_API("OMX_IndexParamVideoProfileLevelQuerySupported, "
                           "port index %d, profile index %d",
                           p_profilelevel->nPortIndex,
                           p_profilelevel->nProfileIndex);

        retval = get_supported_profilelevel(p_profilelevel);
        break;
    }

    default:
    {
        /**
         * Vendor-specific extension indices checked here since they are not
         * part of the OMX_INDEXTYPE enumerated type.
         */

        switch ((OMX_QCOM_EXTN_INDEXTYPE) param_index)
        {

        case OMX_GoogleAndroidIndexGetAndroidNativeBufferUsage:
        {
            GetAndroidNativeBufferUsageParams *p_buffer_usage =
                (GetAndroidNativeBufferUsageParams *) p_param_data;

            OMX_SWVDEC_LOG_API(
                "OMX_GoogleAndroidIndexGetAndroidNativeBufferUsage, "
                "port index %d", p_buffer_usage->nPortIndex);

            if (p_buffer_usage->nPortIndex == OMX_CORE_PORT_INDEX_OP)
            {
                p_buffer_usage->nUsage = (GRALLOC_USAGE_PRIVATE_IOMMU_HEAP |
                                          GRALLOC_USAGE_SW_READ_OFTEN |
                                          GRALLOC_USAGE_SW_WRITE_OFTEN);
            }
            else
            {
                OMX_SWVDEC_LOG_ERROR("port index '%d' invalid",
                                     p_buffer_usage->nPortIndex);
                retval = OMX_ErrorBadPortIndex;
            }
            break;
        }

        case OMX_QcomIndexFlexibleYUVDescription:
        {
            OMX_SWVDEC_LOG_API("OMX_QcomIndexFlexibleYUVDescription");

            retval = describe_color_format((DescribeColorFormatParams *)
                                           p_param_data);
            break;
        }

        default:
        {
            OMX_SWVDEC_LOG_ERROR("param index '0x%08x' invalid");
            retval = OMX_ErrorBadParameter;
            break;
        }

        } // switch ((OMX_QCOM_EXTN_INDEXTYPE) param_index)

    } // default case

    } // switch (param_index)

get_parameter_exit:
    return retval;
}

/**
 * @brief Set a parameter to component.
 *
 * @param[in] cmp_handle:   Component handle.
 * @param[in] param_index:  Parameter index.
 * @param[in] p_param_data: Pointer to parameter data.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::set_parameter(OMX_HANDLETYPE cmp_handle,
                                        OMX_INDEXTYPE  param_index,
                                        OMX_PTR        p_param_data)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (m_state == OMX_StateInvalid)
    {
        OMX_SWVDEC_LOG_ERROR("in invalid state");
        retval = OMX_ErrorInvalidState;
    }
    else if (cmp_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("cmp_handle = NULL");
        retval = OMX_ErrorInvalidComponent;
    }
    else if (p_param_data == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_param_data = NULL");
        retval = OMX_ErrorBadParameter;
    }
    else if ((m_state != OMX_StateLoaded) &&
             (m_port_reconfig_inprogress == false))
    {
        OMX_SWVDEC_LOG_ERROR("disallowed in state %s",
                             OMX_STATETYPE_STRING(m_state));
        retval = OMX_ErrorIncorrectStateOperation;
    }

    if (retval != OMX_ErrorNone)
    {
        goto set_parameter_exit;
    }

    switch (param_index)
    {

    case OMX_IndexParamPriorityMgmt:
    {
        OMX_PRIORITYMGMTTYPE *p_prio_mgmt =
            (OMX_PRIORITYMGMTTYPE *) p_param_data;

        OMX_SWVDEC_LOG_API("OMX_IndexConfigPriorityMgmt: "
                           "group ID %d, group priority %d",
                           p_prio_mgmt->nGroupID,
                           p_prio_mgmt->nGroupPriority);

        if (m_state != OMX_StateLoaded)
        {
            OMX_SWVDEC_LOG_ERROR("'%d' state invalid; "
                                 "should be in loaded state",
                                 m_state);
            retval = OMX_ErrorIncorrectStateOperation;
        }
        else
        {
            memcpy(&m_prio_mgmt, p_prio_mgmt, sizeof(OMX_PRIORITYMGMTTYPE));
        }

        break;
    }

    case OMX_IndexParamStandardComponentRole:
    {
        OMX_PARAM_COMPONENTROLETYPE *p_cmp_role =
            (OMX_PARAM_COMPONENTROLETYPE *) p_param_data;

        OMX_SWVDEC_LOG_API("OMX_IndexParamStandardComponentRole '%s'",
                           p_cmp_role->cRole);

        if (m_state != OMX_StateLoaded)
        {
            OMX_SWVDEC_LOG_ERROR("'%d' state invalid; "
                                 "should be in loaded state",
                                 m_state);
            retval = OMX_ErrorIncorrectStateOperation;
        }
        else
        {
            if (strncmp((char *) p_cmp_role->cRole,
                        m_role_name,
                        OMX_MAX_STRINGNAME_SIZE))
            {
                OMX_SWVDEC_LOG_ERROR("'%s': invalid component role name",
                                     p_cmp_role->cRole);
                retval = OMX_ErrorBadParameter;
            }
        }

        break;
    }

    case OMX_IndexParamPortDefinition:
    {
        OMX_PARAM_PORTDEFINITIONTYPE *p_port_def =
            (OMX_PARAM_PORTDEFINITIONTYPE *) p_param_data;

        OMX_SWVDEC_LOG_API("OMX_IndexParamPortDefinition, port index %d",
                           p_port_def->nPortIndex);

        if ((m_state != OMX_StateLoaded) &&
            (((p_port_def->nPortIndex == OMX_CORE_PORT_INDEX_IP) &&
              (m_port_ip.enabled      == OMX_TRUE) &&
              (m_port_ip.populated    == OMX_TRUE)) ||
             ((p_port_def->nPortIndex == OMX_CORE_PORT_INDEX_OP) &&
              (m_port_op.enabled      == OMX_TRUE) &&
              (m_port_op.populated    == OMX_TRUE))))
        {
            OMX_SWVDEC_LOG_ERROR("OMX_IndexParamPortDefinition "
                                 "disallowed in state %s "
                                 "while port %d is enabled & populated",
                                 OMX_STATETYPE_STRING(m_state),
                                 p_port_def->nPortIndex);

            retval = OMX_ErrorIncorrectStateOperation;
        }
        else
        {
            retval = set_port_definition(p_port_def);
        }

        break;
    }

    case OMX_IndexParamCompBufferSupplier:
    {
        OMX_PARAM_BUFFERSUPPLIERTYPE *p_buffer_supplier =
            (OMX_PARAM_BUFFERSUPPLIERTYPE *) p_param_data;

        OMX_SWVDEC_LOG_API("OMX_IndexParamCompBufferSupplier: "
                           "port index %d, buffer supplier %d",
                           p_buffer_supplier->nPortIndex,
                           (int) p_buffer_supplier->eBufferSupplier);

        if ((p_buffer_supplier->nPortIndex != OMX_CORE_PORT_INDEX_IP) &&
            (p_buffer_supplier->nPortIndex != OMX_CORE_PORT_INDEX_OP))
        {
            OMX_SWVDEC_LOG_ERROR("port index '%d' invalid",
                                 p_buffer_supplier->nPortIndex);
            retval = OMX_ErrorBadPortIndex;
        }

        break;
    }

    case OMX_IndexParamVideoPortFormat:
    {
        OMX_VIDEO_PARAM_PORTFORMATTYPE *p_port_format =
            (OMX_VIDEO_PARAM_PORTFORMATTYPE *) p_param_data;

        OMX_SWVDEC_LOG_API("OMX_IndexParamVideoPortFormat, port index %d",
                           p_port_format->nPortIndex);

        if ((m_state != OMX_StateLoaded) &&
            (((p_port_format->nPortIndex == OMX_CORE_PORT_INDEX_IP) &&
              (m_port_ip.enabled         == OMX_TRUE) &&
              (m_port_ip.populated       == OMX_TRUE)) ||
             ((p_port_format->nPortIndex == OMX_CORE_PORT_INDEX_OP) &&
              (m_port_op.enabled         == OMX_TRUE) &&
              (m_port_op.populated       == OMX_TRUE))))
        {
            OMX_SWVDEC_LOG_ERROR("OMX_IndexParamVideoPortFormat "
                                 "disallowed in state %s "
                                 "while port %d is enabled & populated",
                                 OMX_STATETYPE_STRING(m_state),
                                 p_port_format->nPortIndex);

            retval = OMX_ErrorIncorrectStateOperation;
        }
        else
        {
            retval = set_video_port_format(p_port_format);
        }

        break;
    }

    case OMX_IndexParamVideoMpeg2:
    {
        OMX_SWVDEC_LOG_ERROR("OMX_IndexParamVideoMpeg2 unsupported");
        retval = OMX_ErrorUnsupportedIndex;
        break;
    }

    case OMX_IndexParamVideoMpeg4:
    {
        OMX_SWVDEC_LOG_API("OMX_IndexParamVideoMpeg4 unsupported");
        retval = OMX_ErrorUnsupportedIndex;
        break;
    }

    case OMX_IndexParamVideoAvc:
    {
        OMX_SWVDEC_LOG_API("OMX_IndexParamVideoAvc unsupported");
        retval = OMX_ErrorUnsupportedIndex;
        break;
    }

    case OMX_IndexParamVideoH263:
    {
        OMX_SWVDEC_LOG_API("OMX_IndexParamVideoH263 unsupported");
        retval = OMX_ErrorUnsupportedIndex;
        break;
    }

    default:
    {
        /**
         * Vendor-specific extension indices checked here since they are not
         * part of the OMX_INDEXTYPE enumerated type.
         */

        switch ((OMX_QCOM_EXTN_INDEXTYPE) param_index)
        {

        case OMX_QcomIndexPortDefn:
        {
            OMX_QCOM_PARAM_PORTDEFINITIONTYPE *p_port_def =
                (OMX_QCOM_PARAM_PORTDEFINITIONTYPE *) p_param_data;

            OMX_SWVDEC_LOG_API("OMX_QcomIndexPortDefn, port index %d",
                               p_port_def->nPortIndex);

            if ((m_state != OMX_StateLoaded) &&
                (((p_port_def->nPortIndex == OMX_CORE_PORT_INDEX_IP) &&
                  (m_port_ip.enabled      == OMX_TRUE) &&
                  (m_port_ip.populated    == OMX_TRUE)) ||
                 ((p_port_def->nPortIndex == OMX_CORE_PORT_INDEX_OP) &&
                  (m_port_op.enabled      == OMX_TRUE) &&
                  (m_port_op.populated    == OMX_TRUE))))
            {
                OMX_SWVDEC_LOG_ERROR("OMX_QcomIndexPortDefn "
                                     "disallowed in state %s "
                                     "while port %d is enabled & populated",
                                     OMX_STATETYPE_STRING(m_state),
                                     p_port_def->nPortIndex);

                retval = OMX_ErrorIncorrectStateOperation;
            }
            else
            {
                retval = set_port_definition_qcom(p_port_def);
            }

            break;
        }

        case OMX_QcomIndexParamVideoDivx:
        {
            OMX_SWVDEC_LOG_API("OMX_QcomIndexParamVideoDivx");
            OMX_SWVDEC_LOG_ERROR("OMX_QcomIndexParamVideoDivx unsupported");
            retval = OMX_ErrorUnsupportedIndex;
            break;
        }

        case OMX_QcomIndexParamVideoSyncFrameDecodingMode:
        {
            OMX_SWVDEC_LOG_API("OMX_QcomIndexParamVideoSyncFrameDecodingMode");
            m_sync_frame_decoding_mode = true;
            break;
        }

        case OMX_QcomIndexParamVideoDecoderPictureOrder:
        {
            QOMX_VIDEO_DECODER_PICTURE_ORDER *p_picture_order =
                (QOMX_VIDEO_DECODER_PICTURE_ORDER *) p_param_data;

            switch (p_picture_order->eOutputPictureOrder)
            {

            case QOMX_VIDEO_DISPLAY_ORDER:
            {
                OMX_SWVDEC_LOG_API(
                    "OMX_QcomIndexParamVideoDecoderPictureOrder, "
                    "QOMX_VIDEO_DISPLAY_ORDER");
                break;
            }

            case QOMX_VIDEO_DECODE_ORDER:
            {
                OMX_SWVDEC_LOG_ERROR(
                    "OMX_QcomIndexParamVideoDecoderPictureOrder, "
                    "QOMX_VIDEO_DECODE_ORDER; unsupported");

                retval = OMX_ErrorUnsupportedSetting;
                break;
            }

            default:
            {
                OMX_SWVDEC_LOG_ERROR(
                    "OMX_QcomIndexParamVideoDecoderPictureOrder, %d; invalid");

                retval = OMX_ErrorBadParameter;
                break;
            }

            }
            break;
        }

        case OMX_GoogleAndroidIndexEnableAndroidNativeBuffers:
        {
            OMX_SWVDEC_LOG_API(
                "OMX_GoogleAndroidIndexEnableAndroidNativeBuffers, %s",
                (((EnableAndroidNativeBuffersParams *) p_param_data)->enable ?
                 "enable" :
                 "disable"));

            m_android_native_buffers =
                (bool) (((EnableAndroidNativeBuffersParams *)
                         p_param_data)->enable);

            break;
        }

        case OMX_GoogleAndroidIndexUseAndroidNativeBuffer:
        {
            OMX_SWVDEC_LOG_ERROR("OMX_GoogleAndroidIndexUseAndroidNativeBuffer "
                                 "unsupported");

            retval = OMX_ErrorUnsupportedIndex;
            break;
        }

        case OMX_QcomIndexParamEnableTimeStampReorder:
        {
            OMX_SWVDEC_LOG_API(
                "OMX_QcomIndexParamEnableTimeStampReorder, %s",
                (((QOMX_INDEXTIMESTAMPREORDER *) p_param_data)->bEnable ?
                 "enable" :
                 "disable"));

            break;
        }

        case OMX_QcomIndexParamVideoMetaBufferMode:
        {
            StoreMetaDataInBuffersParams *p_meta_data =
                (StoreMetaDataInBuffersParams *) p_param_data;

            OMX_SWVDEC_LOG_API("OMX_QcomIndexParamVideoMetaBufferMode, "
                               "port index %d, %s",
                               p_meta_data->nPortIndex,
                               (p_meta_data->bStoreMetaData ?
                                "enabled" :
                                "disabled"));

            if (p_meta_data->nPortIndex == OMX_CORE_PORT_INDEX_OP)
            {
                m_meta_buffer_mode = (bool) p_meta_data->bStoreMetaData;
            }
            else
            {
                OMX_SWVDEC_LOG_ERROR("port index '%d' invalid",
                                     p_meta_data->nPortIndex);
                retval = OMX_ErrorBadPortIndex;
            }

            break;
        }

        default:
        {
            OMX_SWVDEC_LOG_ERROR("param index '0x%08x' invalid");
            retval = OMX_ErrorBadParameter;
            break;
        }

        } // switch ((OMX_QCOM_EXTN_INDEXTYPE) param_index)

        break;
    } // default case

    } // switch (param_index)

set_parameter_exit:
    return retval;
}

/**
 * @brief Get a configuration from component.
 *
 * @param[in] cmp_handle:    Component handle.
 * @param[in] config_index:  Configuration index.
 * @param[in] p_config_data: Pointer to configuration data.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::get_config(OMX_HANDLETYPE cmp_handle,
                                     OMX_INDEXTYPE  config_index,
                                     OMX_PTR        p_config_data)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (m_state == OMX_StateInvalid)
    {
        OMX_SWVDEC_LOG_ERROR("in invalid state");
        retval = OMX_ErrorInvalidState;
        goto get_config_exit;
    }
    else if (cmp_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("cmp_handle = NULL");
        retval = OMX_ErrorInvalidComponent;
        goto get_config_exit;
    }
    else if (p_config_data == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_config_data = NULL");
        retval = OMX_ErrorBadParameter;
        goto get_config_exit;
    }

    switch (config_index)
    {

    case OMX_IndexConfigCommonOutputCrop:
    {
        OMX_CONFIG_RECTTYPE *p_recttype = (OMX_CONFIG_RECTTYPE *) p_config_data;

        OMX_SWVDEC_LOG_API("OMX_IndexConfigCommonOutputCrop, port index %d",
                           p_recttype->nPortIndex);

        if (p_recttype->nPortIndex == OMX_CORE_PORT_INDEX_OP)
        {
            p_recttype->nLeft   = 0;
            p_recttype->nTop    = 0;
            p_recttype->nWidth  = m_frame_dimensions.width;
            p_recttype->nHeight = m_frame_dimensions.height;
        }
        else
        {
            OMX_SWVDEC_LOG_ERROR("port index '%d' invalid",
                                 p_recttype->nPortIndex);
            retval = OMX_ErrorBadPortIndex;
        }

        break;
    }

    default:
    {
        switch ((OMX_QCOM_EXTN_INDEXTYPE) config_index)
        {

        case OMX_QcomIndexConfigInterlaced:
        {
            OMX_QCOM_CONFIG_INTERLACETYPE *p_config_interlacetype =
                (OMX_QCOM_CONFIG_INTERLACETYPE *) p_config_data;

            OMX_SWVDEC_LOG_API("OMX_QcomIndexConfigInterlaced, "
                               "port index %d, index %d",
                               p_config_interlacetype->nPortIndex,
                               p_config_interlacetype->nIndex);

            if (p_config_interlacetype->nPortIndex == OMX_CORE_PORT_INDEX_OP)
            {
                if (p_config_interlacetype->nIndex == 0)
                {
                    p_config_interlacetype->eInterlaceType =
                        OMX_QCOM_InterlaceFrameProgressive;
                }
                else if (p_config_interlacetype->nIndex == 1)
                {
                    p_config_interlacetype->eInterlaceType =
                        OMX_QCOM_InterlaceInterleaveFrameTopFieldFirst;
                }
                else if (p_config_interlacetype->nIndex == 2)
                {
                    p_config_interlacetype->eInterlaceType =
                        OMX_QCOM_InterlaceInterleaveFrameBottomFieldFirst;
                }
                else
                {
                    OMX_SWVDEC_LOG_ERROR("index '%d' unsupported; "
                                         "no more interlaced types",
                                         p_config_interlacetype->nIndex);
                    retval = OMX_ErrorNoMore;
                }
            }
            else
            {
                OMX_SWVDEC_LOG_ERROR("port index '%d' invalid",
                                     p_config_interlacetype->nPortIndex);
                retval = OMX_ErrorBadPortIndex;
            }

            break;
        }

        case OMX_QcomIndexQueryNumberOfVideoDecInstance:
        {
            QOMX_VIDEO_QUERY_DECODER_INSTANCES *p_decoder_instances =
                (QOMX_VIDEO_QUERY_DECODER_INSTANCES *) p_config_data;

            OMX_SWVDEC_LOG_API("OMX_QcomIndexQueryNumberOfVideoDecInstance");

            p_decoder_instances->nNumOfInstances = OMX_SWVDEC_NUM_INSTANCES;
            break;
        }

        case OMX_QcomIndexConfigVideoFramePackingArrangement:
        {
            OMX_SWVDEC_LOG_API(
                "OMX_QcomIndexConfigVideoFramePackingArrangement");

            OMX_SWVDEC_LOG_ERROR(
                "OMX_QcomIndexConfigVideoFramePackingArrangement unsupported");

            retval = OMX_ErrorUnsupportedIndex;
            break;
        }

        default:
        {
            OMX_SWVDEC_LOG_ERROR("config index '%d' invalid", config_index);
            retval = OMX_ErrorBadParameter;
            break;
        }

        } // switch ((OMX_QCOM_EXTN_INDEXTYPE) config_index)

        break;
    }

    } // switch (config_index)

get_config_exit:
    return retval;
}

/**
 * @brief Set a configuration to component.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::set_config(OMX_HANDLETYPE cmp_handle,
                                     OMX_INDEXTYPE  config_index,
                                     OMX_PTR        p_config_data)
{
    (void) cmp_handle;
    (void) config_index;
    (void) p_config_data;

    OMX_SWVDEC_LOG_ERROR("not implemented");

    return OMX_ErrorNotImplemented;
}

/**
 * @brief Translate a vendor-specific extension string to a standard index type.
 *
 * @param[in]     cmp_handle:   Component handle.
 * @param[in]     param_name:   Parameter name (extension string).
 * @param[in,out] p_index_type: Pointer to extension string's index type.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::get_extension_index(OMX_HANDLETYPE cmp_handle,
                                              OMX_STRING     param_name,
                                              OMX_INDEXTYPE *p_index_type)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (m_state == OMX_StateInvalid)
    {
        OMX_SWVDEC_LOG_ERROR("in invalid state");
        retval = OMX_ErrorInvalidState;
    }
    else if (cmp_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("cmp_handle = NULL");
        retval = OMX_ErrorInvalidComponent;
    }
    else if (p_index_type == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_index_type = NULL");
        retval = OMX_ErrorBadParameter;
    }

    if (retval != OMX_ErrorNone)
        goto get_extension_index_exit;

    OMX_SWVDEC_LOG_API("'%s'", param_name);

    if (!strncmp(param_name,
                 "OMX.QCOM.index.param.video.SyncFrameDecodingMode",
                 OMX_MAX_STRINGNAME_SIZE))
    {
        *p_index_type =
            (OMX_INDEXTYPE) OMX_QcomIndexParamVideoSyncFrameDecodingMode;
    }
    else if (!strncmp(param_name,
                      "OMX.QCOM.index.param.IndexExtraData",
                      OMX_MAX_STRINGNAME_SIZE))
    {
        *p_index_type = (OMX_INDEXTYPE) OMX_QcomIndexParamIndexExtraDataType;
    }
    else if (!strncmp(param_name,
                      "OMX.google.android.index.enableAndroidNativeBuffers",
                      OMX_MAX_STRINGNAME_SIZE))
    {
        *p_index_type =
            (OMX_INDEXTYPE) OMX_GoogleAndroidIndexEnableAndroidNativeBuffers;
    }
    else if (!strncmp(param_name,
                      "OMX.google.android.index.useAndroidNativeBuffer2",
                      OMX_MAX_STRINGNAME_SIZE))
    {
        *p_index_type =
            (OMX_INDEXTYPE) OMX_GoogleAndroidIndexUseAndroidNativeBuffer2;
    }
    else if (!strncmp(param_name,
                      "OMX.google.android.index.useAndroidNativeBuffer",
                      OMX_MAX_STRINGNAME_SIZE))
    {
        *p_index_type =
            (OMX_INDEXTYPE) OMX_GoogleAndroidIndexUseAndroidNativeBuffer;
    }
    else if (!strncmp(param_name,
                      "OMX.google.android.index.getAndroidNativeBufferUsage",
                      OMX_MAX_STRINGNAME_SIZE))
    {
        *p_index_type =
            (OMX_INDEXTYPE) OMX_GoogleAndroidIndexGetAndroidNativeBufferUsage;
    }
    else if (!strncmp(param_name,
                      "OMX.google.android.index.storeMetaDataInBuffers",
                      OMX_MAX_STRINGNAME_SIZE))
    {
        *p_index_type = (OMX_INDEXTYPE) OMX_QcomIndexParamVideoMetaBufferMode;
    }
    else if (!strncmp(param_name,
                      "OMX.google.android.index.describeColorFormat",
                      OMX_MAX_STRINGNAME_SIZE))
    {
        *p_index_type = (OMX_INDEXTYPE) OMX_QcomIndexFlexibleYUVDescription;
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("'%s': not implemented", param_name);
        retval = OMX_ErrorNotImplemented;
    }

get_extension_index_exit:
    return retval;
}

/**
 * @brief Get component state.
 *
 * @param[in]     cmp_handle: Component handle.
 * @param[in,out] p_state:    Pointer to state variable.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::get_state(OMX_HANDLETYPE cmp_handle,
                                    OMX_STATETYPE *p_state)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (cmp_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("cmp_handle = NULL");
        retval = OMX_ErrorInvalidComponent;
    }
    else
    {
        OMX_SWVDEC_LOG_API("%s", OMX_STATETYPE_STRING(m_state));
        *p_state = m_state;
    }

    return retval;
}

/**
 * @brief Component tunnel request.
 *
 * @retval OMX_ErrorNotImplemented
 */
OMX_ERRORTYPE omx_swvdec::component_tunnel_request(
    OMX_HANDLETYPE       cmp_handle,
    OMX_U32              port,
    OMX_HANDLETYPE       peer_component,
    OMX_U32              peer_port,
    OMX_TUNNELSETUPTYPE *p_tunnel_setup)
{
    (void) cmp_handle;
    (void) port;
    (void) peer_component;
    (void) peer_port;
    (void) p_tunnel_setup;

    OMX_SWVDEC_LOG_API("");

    OMX_SWVDEC_LOG_ERROR("not implemented");

    return OMX_ErrorNotImplemented;
}

/**
 * @brief Use buffer.
 *
 * @param[in]     cmp_handle:    Component handle.
 * @param[in,out] pp_buffer_hdr: Pointer to pointer to buffer header type
 *                               structure.
 * @param[in]     port:          Port index.
 * @param[in]     p_app_data:    Pointer to IL client app data.
 * @param[in]     bytes:         Size of buffer to be allocated in bytes.
 * @param[in]     p_buffer:      Pointer to buffer to be used.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::use_buffer(OMX_HANDLETYPE         cmp_handle,
                                     OMX_BUFFERHEADERTYPE **pp_buffer_hdr,
                                     OMX_U32                port,
                                     OMX_PTR                p_app_data,
                                     OMX_U32                bytes,
                                     OMX_U8                *p_buffer)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (m_state == OMX_StateInvalid)
    {
        OMX_SWVDEC_LOG_ERROR("in invalid state");
        retval = OMX_ErrorInvalidState;
    }
    else if (cmp_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("cmp_handle = NULL");
        retval = OMX_ErrorInvalidComponent;
    }
    else if (pp_buffer_hdr == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("pp_buffer_hdr = NULL");
        retval = OMX_ErrorBadParameter;
    }
    else
    {
        OMX_SWVDEC_LOG_API("port index %d, %p", port, p_buffer);

        if (port == OMX_CORE_PORT_INDEX_OP)
        {
            retval = buffer_use_op(pp_buffer_hdr, p_app_data, bytes, p_buffer);

            if (retval == OMX_ErrorNone)
            {
                SWVDEC_STATUS retval_swvdec;

                if ((m_status_flags & (1 << PENDING_STATE_LOADED_TO_IDLE)) &&
                    (m_port_ip.populated == OMX_TRUE) &&
                    (m_port_op.populated == OMX_TRUE))
                {
                    // start SwVdec
                    if ((retval_swvdec = swvdec_start(m_swvdec_handle)) !=
                        SWVDEC_STATUS_SUCCESS)
                    {
                        OMX_SWVDEC_LOG_ERROR("failed to start SwVdec");
                        retval = retval_swvdec2omx(retval_swvdec);
                        goto use_buffer_exit;
                    }

                    m_status_flags &= ~(1 << PENDING_STATE_LOADED_TO_IDLE);

                    async_post_event(OMX_SWVDEC_EVENT_CMD_ACK,
                                     OMX_CommandStateSet,
                                     OMX_StateIdle);
                }

                if ((m_status_flags & (1 << PENDING_PORT_ENABLE_OP)) &&
                    (m_port_op.populated == OMX_TRUE))
                {
                    if (m_port_reconfig_inprogress)
                    {
                        // start SwVdec
                        if ((retval_swvdec = swvdec_start(m_swvdec_handle)) !=
                            SWVDEC_STATUS_SUCCESS)
                        {
                            OMX_SWVDEC_LOG_ERROR("failed to start SwVdec");
                            retval = retval_swvdec2omx(retval_swvdec);
                        }
                    }

                    m_status_flags &= ~(1 << PENDING_PORT_ENABLE_OP);

                    async_post_event(OMX_SWVDEC_EVENT_CMD_ACK,
                                     OMX_CommandPortEnable,
                                     OMX_CORE_PORT_INDEX_OP);
                }
            }
        }
        else
        {
            OMX_SWVDEC_LOG_ERROR("port index '%d' invalid", port);
            retval = OMX_ErrorBadPortIndex;
        }
    }

use_buffer_exit:
    return retval;
}

/**
 * @brief Allocate new buffer & associated header.
 *
 * @param[in]     cmp_handle:    Component handle.
 * @param[in,out] pp_buffer_hdr: Pointer to pointer to buffer header type
 *                               structure.
 * @param[in]     port:          Port index.
 * @param[in]     p_app_data:    Pointer to IL client app data.
 * @param[in]     bytes:         Size of buffer to be allocated in bytes.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::allocate_buffer(OMX_HANDLETYPE         cmp_handle,
                                          OMX_BUFFERHEADERTYPE **pp_buffer_hdr,
                                          OMX_U32                port,
                                          OMX_PTR                p_app_data,
                                          OMX_U32                bytes)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (m_state == OMX_StateInvalid)
    {
        OMX_SWVDEC_LOG_ERROR("in invalid state");
        retval = OMX_ErrorInvalidState;
    }
    else if (cmp_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("cmp_handle = NULL");
        retval = OMX_ErrorInvalidComponent;
    }
    else if (pp_buffer_hdr == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("pp_buffer_hdr = NULL");
        retval = OMX_ErrorBadParameter;
    }
    else
    {
        OMX_SWVDEC_LOG_API("port index %d, %d bytes", port, bytes);

        if (port == OMX_CORE_PORT_INDEX_IP)
        {
            retval = buffer_allocate_ip(pp_buffer_hdr,
                                        p_app_data,
                                        bytes);
        }
        else if (port == OMX_CORE_PORT_INDEX_OP)
        {
            if (m_meta_buffer_mode == true)
            {
                OMX_SWVDEC_LOG_ERROR("'meta buffer mode' enabled");
                retval = OMX_ErrorBadParameter;
            }
            else if (m_android_native_buffers == true)
            {
                OMX_SWVDEC_LOG_ERROR("'android native buffers' enabled");
                retval = OMX_ErrorBadParameter;
            }
            else
            {
                retval = buffer_allocate_op(pp_buffer_hdr,
                                            p_app_data,
                                            bytes);
            }
        }
        else
        {
            OMX_SWVDEC_LOG_ERROR("port index %d invalid", port);
            retval = OMX_ErrorBadPortIndex;
        }

        if (retval == OMX_ErrorNone)
        {
            SWVDEC_STATUS retval_swvdec;

            if ((m_status_flags & (1 << PENDING_STATE_LOADED_TO_IDLE)) &&
                (m_port_ip.populated == OMX_TRUE) &&
                (m_port_op.populated == OMX_TRUE))
            {
                // start SwVdec
                if ((retval_swvdec = swvdec_start(m_swvdec_handle)) !=
                    SWVDEC_STATUS_SUCCESS)
                {
                    OMX_SWVDEC_LOG_ERROR("failed to start SwVdec");
                    retval = retval_swvdec2omx(retval_swvdec);
                    goto allocate_buffer_exit;
                }

                m_status_flags &= ~(1 << PENDING_STATE_LOADED_TO_IDLE);

                async_post_event(OMX_SWVDEC_EVENT_CMD_ACK,
                                 OMX_CommandStateSet,
                                 OMX_StateIdle);
            }

            if ((m_status_flags & (1 << PENDING_PORT_ENABLE_IP)) &&
                (m_port_ip.populated == OMX_TRUE))
            {
                m_status_flags &= ~(1 << PENDING_PORT_ENABLE_IP);

                async_post_event(OMX_SWVDEC_EVENT_CMD_ACK,
                                 OMX_CommandPortEnable,
                                 OMX_CORE_PORT_INDEX_IP);
            }

            if ((m_status_flags & (1 << PENDING_PORT_ENABLE_OP)) &&
                (m_port_op.populated == OMX_TRUE))
            {
                if (m_port_reconfig_inprogress)
                {
                    // start SwVdec
                    if ((retval_swvdec = swvdec_start(m_swvdec_handle)) !=
                        SWVDEC_STATUS_SUCCESS)
                    {
                        OMX_SWVDEC_LOG_ERROR("failed to start SwVdec");
                        retval = retval_swvdec2omx(retval_swvdec);
                    }
                }

                m_status_flags &= ~(1 << PENDING_PORT_ENABLE_OP);

                async_post_event(OMX_SWVDEC_EVENT_CMD_ACK,
                                 OMX_CommandPortEnable,
                                 OMX_CORE_PORT_INDEX_OP);
            }
        }
    }

allocate_buffer_exit:
    return retval;
}

/**
 * @brief Release buffer & associated header.
 *
 * @param[in] cmp_handle:   Component handle.
 * @param[in] port:         Port index.
 * @param[in] p_buffer_hdr: Pointer to buffer's buffer header.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::free_buffer(OMX_HANDLETYPE        cmp_handle,
                                      OMX_U32               port,
                                      OMX_BUFFERHEADERTYPE *p_buffer_hdr)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (cmp_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("cmp_handle = NULL");
        retval = OMX_ErrorInvalidComponent;
    }
    else if (p_buffer_hdr == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_buffer_hdr = NULL");
        retval = OMX_ErrorBadParameter;
    }
    else if ((port != OMX_CORE_PORT_INDEX_IP) &&
             (port != OMX_CORE_PORT_INDEX_OP))
    {
        OMX_SWVDEC_LOG_ERROR("port index '%d' invalid", port);
        retval = OMX_ErrorBadPortIndex;
    }
    else if (m_state != OMX_StateIdle)
    {
        if (m_state != OMX_StateExecuting)
        {
            OMX_SWVDEC_LOG_ERROR("disallowed in state %s",
                                 OMX_STATETYPE_STRING(m_state));
            retval = OMX_ErrorIncorrectStateOperation;
        }
        else
        {
            if (((port == OMX_CORE_PORT_INDEX_IP) && m_port_ip.enabled) ||
                ((port == OMX_CORE_PORT_INDEX_OP) && m_port_op.enabled))
            {
                OMX_SWVDEC_LOG_ERROR("port index %d not disabled", port);
                retval = OMX_ErrorBadPortIndex;
            }
        }
    }

    if (retval == OMX_ErrorNone)
    {
        OMX_SWVDEC_LOG_API("port index %d, %p", port, p_buffer_hdr);

        if (port == OMX_CORE_PORT_INDEX_IP)
        {
            retval = buffer_deallocate_ip(p_buffer_hdr);
        }
        else
        {
            retval = buffer_deallocate_op(p_buffer_hdr);
        }
    }

    if ((retval == OMX_ErrorNone) &&
        (m_status_flags & (1 << PENDING_STATE_IDLE_TO_LOADED)))
    {
        if ((m_port_ip.unpopulated == OMX_TRUE) &&
            (m_port_op.unpopulated == OMX_TRUE))
        {
            SWVDEC_STATUS retval_swvdec;

            // stop SwVdec
            if ((retval_swvdec = swvdec_stop(m_swvdec_handle)) ==
                SWVDEC_STATUS_SUCCESS)
            {
                m_status_flags &= ~(1 << PENDING_STATE_IDLE_TO_LOADED);

                async_post_event(OMX_SWVDEC_EVENT_CMD_ACK,
                                 OMX_CommandStateSet,
                                 OMX_StateLoaded);
            }
            else
            {
                OMX_SWVDEC_LOG_ERROR("failed to stop SwVdec");
                retval = retval_swvdec2omx(retval_swvdec);
            }
        }
    }

    if ((retval == OMX_ErrorNone) &&
        (m_status_flags & (1 << PENDING_PORT_DISABLE_IP)) &&
        m_port_ip.unpopulated)
    {
        m_status_flags &= ~(1 << PENDING_PORT_DISABLE_IP);

        async_post_event(OMX_SWVDEC_EVENT_CMD_ACK,
                         OMX_CommandPortDisable,
                         OMX_CORE_PORT_INDEX_IP);
    }

    if ((retval == OMX_ErrorNone) &&
        (m_status_flags & (1 << PENDING_PORT_DISABLE_OP)) &&
        m_port_op.unpopulated)
    {
        if (m_port_reconfig_inprogress)
        {
            SWVDEC_STATUS retval_swvdec;

            if ((retval_swvdec = swvdec_stop(m_swvdec_handle)) !=
                SWVDEC_STATUS_SUCCESS)
            {
                OMX_SWVDEC_LOG_ERROR("failed to stop SwVdec");
                retval = retval_swvdec2omx(retval_swvdec);
            }
        }

        m_status_flags &= ~(1 << PENDING_PORT_DISABLE_OP);

        async_post_event(OMX_SWVDEC_EVENT_CMD_ACK,
                         OMX_CommandPortDisable,
                         OMX_CORE_PORT_INDEX_OP);
    }

    return retval;
}

/**
 * @brief Send a buffer to component's input port to be emptied.
 *
 * @param[in] cmp_handle:   Component handle.
 * @param[in] p_buffer_hdr: Pointer to buffer's buffer header.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::empty_this_buffer(OMX_HANDLETYPE        cmp_handle,
                                            OMX_BUFFERHEADERTYPE *p_buffer_hdr)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    unsigned int ii;

    if (m_state == OMX_StateInvalid)
    {
        OMX_SWVDEC_LOG_ERROR("in invalid state");
        retval = OMX_ErrorInvalidState;
    }
    else if (cmp_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("cmp_handle = NULL");
        retval = OMX_ErrorInvalidComponent;
    }
    else if (p_buffer_hdr == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_buffer_hdr = NULL");
        retval = OMX_ErrorBadParameter;
    }
    else if (p_buffer_hdr->pBuffer == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_buffer_hdr->pBuffer = NULL");
        retval = OMX_ErrorBadParameter;
    }
    else if (p_buffer_hdr->pInputPortPrivate == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_buffer_hdr->pInputPortPrivate = NULL");
        retval = OMX_ErrorBadParameter;
    }
    else if (m_port_ip.enabled == OMX_FALSE)
    {
        OMX_SWVDEC_LOG_ERROR("ip port disabled");
        retval = OMX_ErrorIncorrectStateOperation;
    }
    else if (p_buffer_hdr->nInputPortIndex != OMX_CORE_PORT_INDEX_IP)
    {
        OMX_SWVDEC_LOG_ERROR("port index '%d' invalid",
                             p_buffer_hdr->nInputPortIndex);
        retval = OMX_ErrorBadPortIndex;
    }

    if (retval != OMX_ErrorNone)
    {
        goto empty_this_buffer_exit;
    }

    for (ii = 0; ii < m_port_ip.def.nBufferCountActual; ii++)
    {
        if (p_buffer_hdr == &(m_buffer_array_ip[ii].buffer_header))
        {
            OMX_SWVDEC_LOG_LOW("ip buffer %p has index %d",
                               p_buffer_hdr->pBuffer,
                               ii);
            break;
        }
    }

    if (ii == m_port_ip.def.nBufferCountActual)
    {
        OMX_SWVDEC_LOG_ERROR("ip buffer %p not found",
                             p_buffer_hdr->pBuffer);
        retval = OMX_ErrorBadParameter;
        goto empty_this_buffer_exit;
    }

    if (m_sync_frame_decoding_mode &&
        ((p_buffer_hdr->nFlags & OMX_BUFFERFLAG_CODECCONFIG) == 0))
    {
        p_buffer_hdr->nFlags |= OMX_BUFFERFLAG_EOS;
    }

    OMX_SWVDEC_LOG_API("%p: buffer %p, flags 0x%08x, filled length %d, "
                       "timestamp %lld",
                       p_buffer_hdr,
                       p_buffer_hdr->pBuffer,
                       p_buffer_hdr->nFlags,
                       p_buffer_hdr->nFilledLen,
                       p_buffer_hdr->nTimeStamp);

    async_post_event(OMX_SWVDEC_EVENT_ETB,
                     (unsigned long) p_buffer_hdr,
                     (unsigned long) ii);

empty_this_buffer_exit:
    return retval;
}

/**
 * @brief Send a buffer to component's output port to be filled.
 *
 * @param[in] cmp_handle:   Component handle.
 * @param[in] p_buffer_hdr: Pointer to buffer's buffer header.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::fill_this_buffer(OMX_HANDLETYPE        cmp_handle,
                                           OMX_BUFFERHEADERTYPE *p_buffer_hdr)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    unsigned int ii;

    SWVDEC_BUFFER *p_buffer_swvdec;

    if (m_state == OMX_StateInvalid)
    {
        OMX_SWVDEC_LOG_ERROR("in invalid state");
        retval = OMX_ErrorInvalidState;
    }
    else if (cmp_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("cmp_handle = NULL");
        retval = OMX_ErrorInvalidComponent;
    }
    else if (p_buffer_hdr == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_buffer_hdr = NULL");
        retval = OMX_ErrorBadParameter;
    }
    else if (p_buffer_hdr->pBuffer == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_buffer_hdr->pBuffer = NULL");
        retval = OMX_ErrorBadParameter;
    }
    else if (p_buffer_hdr->pOutputPortPrivate == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_buffer_hdr->pOutputPortPrivate = NULL");
        retval = OMX_ErrorBadParameter;
    }
    else if (m_port_op.enabled == OMX_FALSE)
    {
        OMX_SWVDEC_LOG_ERROR("op port disabled");
        retval = OMX_ErrorIncorrectStateOperation;
    }
    else if (p_buffer_hdr->nOutputPortIndex != OMX_CORE_PORT_INDEX_OP)
    {
        OMX_SWVDEC_LOG_ERROR("port index '%d' invalid",
                             p_buffer_hdr->nOutputPortIndex);
        retval = OMX_ErrorBadPortIndex;
    }

    if (retval != OMX_ErrorNone)
    {
        goto fill_this_buffer_exit;
    }

    OMX_SWVDEC_LOG_API("%p", p_buffer_hdr);

    for (ii = 0; ii < m_port_op.def.nBufferCountActual; ii++)
    {
        if (p_buffer_hdr == &(m_buffer_array_op[ii].buffer_header))
        {
            OMX_SWVDEC_LOG_LOW("op buffer %p has index %d",
                               p_buffer_hdr->pBuffer,
                               ii);
            break;
        }
    }

    if (ii == m_port_op.def.nBufferCountActual)
    {
        OMX_SWVDEC_LOG_ERROR("op buffer %p not found",
                             p_buffer_hdr->pBuffer);
        retval = OMX_ErrorBadParameter;
        goto fill_this_buffer_exit;
    }

    p_buffer_swvdec = &m_buffer_array_op[ii].buffer_swvdec;

    if (m_meta_buffer_mode)
    {
        struct VideoDecoderOutputMetaData *p_meta_data;

        private_handle_t *p_private_handle;

        struct vdec_bufferpayload *p_buffer_payload;

        p_meta_data =
            (struct VideoDecoderOutputMetaData *) p_buffer_hdr->pBuffer;

        p_private_handle = (private_handle_t *) (p_meta_data->pHandle);

        p_buffer_payload = &m_buffer_array_op[ii].buffer_payload;

        if (p_private_handle == NULL)
        {
            OMX_SWVDEC_LOG_ERROR(
                "p_buffer_hdr->pBuffer->pHandle = NULL");

            retval = OMX_ErrorBadParameter;
            goto fill_this_buffer_exit;
        }

        pthread_mutex_lock(&m_meta_buffer_array_mutex);

        if (m_meta_buffer_array[ii].ref_count == 0)
        {
            unsigned char *bufferaddr;

            bufferaddr = (unsigned char *) mmap(NULL,
                                                m_port_op.def.nBufferSize,
                                                PROT_READ | PROT_WRITE,
                                                MAP_SHARED,
                                                p_private_handle->fd,
                                                0);

            if (bufferaddr == MAP_FAILED)
            {
                OMX_SWVDEC_LOG_ERROR("mmap() failed for "
                                     "fd %d of size %d",
                                     p_private_handle->fd,
                                     m_port_op.def.nBufferSize);

                retval = OMX_ErrorInsufficientResources;
                goto fill_this_buffer_exit;
            }

            p_buffer_payload->bufferaddr  = bufferaddr;
            p_buffer_payload->pmem_fd     = p_private_handle->fd;
            p_buffer_payload->buffer_len  = m_port_op.def.nBufferSize;
            p_buffer_payload->mmaped_size = m_port_op.def.nBufferSize;

            p_buffer_swvdec->p_buffer      = bufferaddr;
            p_buffer_swvdec->size          = m_port_op.def.nBufferSize;
            p_buffer_swvdec->p_client_data = (void *) ((unsigned long) ii);
        }

        pthread_mutex_unlock(&m_meta_buffer_array_mutex);

        meta_buffer_ref_add(ii,
                            p_buffer_payload->pmem_fd,
                            p_buffer_payload->offset);
    }

    OMX_SWVDEC_LOG_LOW("%p: buffer %p",
                       p_buffer_hdr,
                       p_buffer_swvdec->p_buffer);

    async_post_event(OMX_SWVDEC_EVENT_FTB,
                     (unsigned long) p_buffer_hdr,
                     (unsigned long) ii);

fill_this_buffer_exit:
    return retval;
}

/**
 * @brief Set component's callback structure.
 *
 * @param[in] cmp_handle:  Component handle.
 * @param[in] p_callbacks: Pointer to callback structure.
 * @param[in] p_app_data:  Pointer to IL client app data.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::set_callbacks(OMX_HANDLETYPE    cmp_handle,
                                        OMX_CALLBACKTYPE *p_callbacks,
                                        OMX_PTR           p_app_data)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    OMX_SWVDEC_LOG_API("");

    if (m_state == OMX_StateInvalid)
    {
        OMX_SWVDEC_LOG_ERROR("in invalid state");
        retval = OMX_ErrorInvalidState;
    }
    else if (cmp_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("cmp_handle = NULL");
        retval = OMX_ErrorInvalidComponent;
    }
    else if (p_callbacks->EventHandler == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_callbacks->EventHandler = NULL");
        retval = OMX_ErrorBadParameter;
    }
    else if (p_callbacks->EmptyBufferDone == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_callbacks->EmptyBufferDone = NULL");
        retval = OMX_ErrorBadParameter;
    }
    else if (p_callbacks->FillBufferDone == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_callbacks->FillBufferDone = NULL");
        retval = OMX_ErrorBadParameter;
    }
    else
    {
        m_callback = *p_callbacks;
        m_app_data = p_app_data;
    }

    return retval;
}

/**
 * @brief Use EGL image.
 *
 * @retval OMX_ErrorNotImplemented
 */
OMX_ERRORTYPE omx_swvdec::use_EGL_image(OMX_HANDLETYPE         cmp_handle,
                                        OMX_BUFFERHEADERTYPE **pp_buffer_hdr,
                                        OMX_U32                port,
                                        OMX_PTR                p_app_data,
                                        void                  *egl_image)
{
    (void) cmp_handle;
    (void) pp_buffer_hdr;
    (void) port;
    (void) p_app_data;
    (void) egl_image;

    OMX_SWVDEC_LOG_API("");

    OMX_SWVDEC_LOG_ERROR("not implemented");

    return OMX_ErrorNotImplemented;
}

/**
 * @brief Enumerate component role.
 *
 * @param[in]     cmp_handle: Component handle.
 * @param[in,out] p_role:     Pointer to component role string.
 * @param[in]     index:      Role index being queried.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::component_role_enum(OMX_HANDLETYPE cmp_handle,
                                              OMX_U8        *p_role,
                                              OMX_U32        index)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (m_state == OMX_StateInvalid)
    {
        OMX_SWVDEC_LOG_ERROR("in invalid state");
        retval = OMX_ErrorInvalidState;
    }
    else if (cmp_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("cmp_handle = NULL");
        retval = OMX_ErrorInvalidComponent;
    }
    else if (index > 0)
    {
        OMX_SWVDEC_LOG_HIGH("index '%d' unsupported; no more roles", index);
        retval = OMX_ErrorNoMore;
    }
    else
    {
        memcpy(p_role, m_role_name, OMX_MAX_STRINGNAME_SIZE);
        OMX_SWVDEC_LOG_API("index '%d': '%s'", index, p_role);
    }

    return retval;
}

/**
 * -------------------------
 * SwVdec callback functions
 * -------------------------
 */

/**
 * @brief SwVdec empty buffer done callback.
 *
 * @param[in] swvdec_handle:   SwVdec handle.
 * @param[in] p_buffer_ip:     Pointer to input buffer structure.
 * @param[in] p_client_handle: Pointer to SwVdec's client handle.
 *
 * @retval SWVDEC_STATUS_SUCCESS
 * @retval SWVDEC_STATUS_NULL_POINTER
 * @retval SWVDEC_STATUS_INVALID_PARAMETERS
 */
SWVDEC_STATUS omx_swvdec::swvdec_empty_buffer_done_callback(
    SWVDEC_HANDLE  swvdec_handle,
    SWVDEC_BUFFER *p_buffer_ip,
    void          *p_client_handle)
{
    SWVDEC_STATUS retval = SWVDEC_STATUS_SUCCESS;

    if (p_buffer_ip == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_buffer_ip = NULL");
        retval = SWVDEC_STATUS_NULL_POINTER;
    }
    else if (p_client_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_client_handle = NULL");
        retval = SWVDEC_STATUS_NULL_POINTER;
    }
    else
    {
        omx_swvdec *p_omx_swvdec = (omx_swvdec *) p_client_handle;

        if (swvdec_handle != p_omx_swvdec->m_swvdec_handle)
        {
            OMX_SWVDEC_LOG_ERROR("invalid SwVdec handle");
            retval = SWVDEC_STATUS_INVALID_PARAMETERS;
        }
        else
        {
            p_omx_swvdec->swvdec_empty_buffer_done(p_buffer_ip);
        }
    }

    return retval;
}

/**
 * @brief SwVdec fill buffer done callback.
 *
 * @param[in] swvdec_handle:   SwVdec handle.
 * @param[in] p_buffer_op:     Pointer to output buffer structure.
 * @param[in] p_client_handle: Pointer to SwVdec's client handle.
 *
 * @retval SWVDEC_STATUS_SUCCESS
 * @retval SWVDEC_STATUS_NULL_POINTER
 * @retval SWVDEC_STATUS_INVALID_PARAMETERS
 */
SWVDEC_STATUS omx_swvdec::swvdec_fill_buffer_done_callback(
    SWVDEC_HANDLE  swvdec_handle,
    SWVDEC_BUFFER *p_buffer_op,
    void          *p_client_handle)
{
    SWVDEC_STATUS retval = SWVDEC_STATUS_SUCCESS;

    if (p_buffer_op == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_buffer_op = NULL");
        retval = SWVDEC_STATUS_NULL_POINTER;
    }
    else if (p_client_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_client_handle = NULL");
        retval = SWVDEC_STATUS_NULL_POINTER;
    }
    else
    {
        omx_swvdec *p_omx_swvdec = (omx_swvdec *) p_client_handle;

        if (swvdec_handle != p_omx_swvdec->m_swvdec_handle)
        {
            OMX_SWVDEC_LOG_ERROR("invalid SwVdec handle");
            retval = SWVDEC_STATUS_INVALID_PARAMETERS;
        }
        else
        {
            p_omx_swvdec->swvdec_fill_buffer_done(p_buffer_op);
        }
    }

    return retval;
}

/**
 * @brief SwVdec event handler callback.
 *
 * @param[in] swvdec_handle:   SwVdec handle.
 * @param[in] event:           Event.
 * @param[in] p_data:          Pointer to event-specific data.
 * @param[in] p_client_handle: Pointer to SwVdec's client handle.
 *
 * @retval SWVDEC_STATUS_SUCCESS
 * @retval SWVDEC_STATUS_NULL_POINTER
 * @retval SWVDEC_STATUS_INVALID_PARAMETERS
 */
SWVDEC_STATUS omx_swvdec::swvdec_event_handler_callback(
    SWVDEC_HANDLE swvdec_handle,
    SWVDEC_EVENT  event,
    void         *p_data,
    void         *p_client_handle)
{
    SWVDEC_STATUS retval = SWVDEC_STATUS_SUCCESS;

    if ((event == SWVDEC_EVENT_RELEASE_REFERENCE) && (p_data == NULL))
    {
        OMX_SWVDEC_LOG_ERROR("p_data = NULL");
        retval = SWVDEC_STATUS_NULL_POINTER;
    }
    else if (p_client_handle == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_client_handle = NULL");
        retval = SWVDEC_STATUS_NULL_POINTER;
    }
    else
    {
        omx_swvdec *p_omx_swvdec = (omx_swvdec *) p_client_handle;

        if (swvdec_handle != p_omx_swvdec->m_swvdec_handle)
        {
            OMX_SWVDEC_LOG_ERROR("invalid SwVdec handle");
            retval = SWVDEC_STATUS_INVALID_PARAMETERS;
        }
        else
        {
            p_omx_swvdec->swvdec_event_handler(event, p_data);
        }
    }

    return retval;
}

/**
 * -----------------
 * PRIVATE FUNCTIONS
 * -----------------
 */

/**
 * @brief Set frame dimensions for OMX component & SwVdec core.
 *
 * @param[in] width:  Frame width.
 * @param[in] height: Frame height.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::set_frame_dimensions(unsigned int width,
                                               unsigned int height)
{
    OMX_ERRORTYPE retval;

    m_frame_dimensions.width  = width;
    m_frame_dimensions.height = height;

    OMX_SWVDEC_LOG_HIGH("%d x %d",
                        m_frame_dimensions.width,
                        m_frame_dimensions.height);

    retval = set_frame_dimensions_swvdec();

    return retval;
}

/**
 * @brief Set frame attributes for OMX component & SwVdec core, based on
 *        frame dimensions & alignment factors.
 *
 * @param[in] alignment_stride:       Frame stride alignment factor.
 * @param[in] alignment_scanlines_y:  Frame   luma scanlines alignment factor.
 * @param[in] alignment_scanlines_uv: Frame chroma scanlines alignment factor.
 * @param[in] alignment_size:         Frame size alignment factor.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::set_frame_attributes(
    unsigned int alignment_stride,
    unsigned int alignment_scanlines_y,
    unsigned int alignment_scanlines_uv,
    unsigned int alignment_size)
{
    OMX_ERRORTYPE retval;

    unsigned int width  = m_frame_dimensions.width;
    unsigned int height = m_frame_dimensions.height;

    unsigned int scanlines_uv;

    unsigned int plane_size_y;
    unsigned int plane_size_uv;

    m_frame_attributes.stride    = ALIGN(width,  alignment_stride);
    m_frame_attributes.scanlines = ALIGN(height, alignment_scanlines_y);

    scanlines_uv = ALIGN(height / 2, alignment_scanlines_uv);

    plane_size_y  = m_frame_attributes.stride * m_frame_attributes.scanlines;
    plane_size_uv = m_frame_attributes.stride * scanlines_uv;

    m_frame_attributes.size =
        ALIGN(plane_size_y + plane_size_uv, alignment_size);

    OMX_SWVDEC_LOG_HIGH("stride %d, scanlines %d, size %d",
                        m_frame_attributes.stride,
                        m_frame_attributes.scanlines,
                        m_frame_attributes.size);

    retval = set_frame_attributes_swvdec();

    return retval;
}

/**
 * @brief Get video port format for input or output port.
 *
 * @param[in,out] p_port_format: Pointer to video port format type.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::get_video_port_format(
    OMX_VIDEO_PARAM_PORTFORMATTYPE *p_port_format)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (p_port_format->nPortIndex == OMX_CORE_PORT_INDEX_IP)
    {
        if (p_port_format->nIndex == 0)
        {
            p_port_format->eColorFormat       = OMX_COLOR_FormatUnused;
            p_port_format->eCompressionFormat = m_omx_video_codingtype;

            OMX_SWVDEC_LOG_HIGH("color format 0x%08x, "
                                "compression format 0x%08x",
                                p_port_format->eColorFormat,
                                p_port_format->eCompressionFormat);
        }
        else
        {
            OMX_SWVDEC_LOG_HIGH("index '%d' unsupported; "
                                "no more compression formats",
                                p_port_format->nIndex);
            retval = OMX_ErrorNoMore;
        }
    }
    else if (p_port_format->nPortIndex == OMX_CORE_PORT_INDEX_OP)
    {
        if (p_port_format->nIndex == 0)
        {
            p_port_format->eColorFormat =
                OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m;
            p_port_format->eCompressionFormat = OMX_VIDEO_CodingUnused;

            OMX_SWVDEC_LOG_HIGH("color format 0x%08x, "
                                "compression format 0x%08x",
                                p_port_format->eColorFormat,
                                p_port_format->eCompressionFormat);
        }
        else if (p_port_format->nIndex == 1)
        {
            p_port_format->eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
            p_port_format->eCompressionFormat = OMX_VIDEO_CodingUnused;

            OMX_SWVDEC_LOG_HIGH("color format 0x%08x, "
                                "compression format 0x%08x",
                                p_port_format->eColorFormat,
                                p_port_format->eCompressionFormat);
        }
        // Here, add additional supported color formats as necessary.
        else
        {
            OMX_SWVDEC_LOG_HIGH("index '%d' unsupported; no more color formats",
                                p_port_format->nIndex);
            retval = OMX_ErrorNoMore;
        }
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("port index '%d' invalid",
                             p_port_format->nPortIndex);
        retval = OMX_ErrorBadPortIndex;
    }

    return retval;
}

/**
 * @brief Set video port format for input or output port.
 *
 * @param[in] p_port_format: Pointer to video port format type.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::set_video_port_format(
    OMX_VIDEO_PARAM_PORTFORMATTYPE *p_port_format)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (p_port_format->nPortIndex == OMX_CORE_PORT_INDEX_IP)
    {
        // do nothing
        OMX_SWVDEC_LOG_HIGH("OMX_IndexParamVideoPortFormat, port index 0; "
                            "doing nothing");
    }
    else if (p_port_format->nPortIndex == OMX_CORE_PORT_INDEX_OP)
    {
        /**
         * If color format specified by IL client is different from default,
         * set alignment factors for new color format and call
         * set_frame_attributes().
         */
        switch (p_port_format->eColorFormat)
        {

        case OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m:
        {
            // do nothing; this is the default color format
            OMX_SWVDEC_LOG_HIGH(
                "'0x%08x': OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m",
                p_port_format->eColorFormat);
            break;
        }

        case OMX_COLOR_FormatYUV420SemiPlanar:
        {
            OMX_SWVDEC_LOG_HIGH("'0x%08x': OMX_COLOR_FormatYUV420SemiPlanar",
                                p_port_format->eColorFormat);

            retval = set_frame_attributes(16,    //       stride alignment
                                          1,     //  Y scanlines alignment
                                          1,     // UV scanlines alignment
                                          4096); //         size alignment
            break;
        }

        default:
        {
            OMX_SWVDEC_LOG_ERROR("color format '0x%08x' invalid or unsupported",
                                 p_port_format->eColorFormat);
            retval = OMX_ErrorBadParameter;
            break;
        }

        }
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("port index '%d' invalid",
                             p_port_format->nPortIndex);
        retval = OMX_ErrorBadPortIndex;
    }

set_video_port_format_exit:
    return retval;
}

/**
 * @brief Get port definition for input or output port.
 *
 * @param[in,out] p_port_def: Pointer to port definition type.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::get_port_definition(
    OMX_PARAM_PORTDEFINITIONTYPE *p_port_def)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    p_port_def->eDomain = OMX_PortDomainVideo;

    if (p_port_def->nPortIndex == OMX_CORE_PORT_INDEX_IP)
    {
        if ((retval = get_buffer_requirements_swvdec(OMX_CORE_PORT_INDEX_IP)) !=
            OMX_ErrorNone)
        {
            goto get_port_definition_exit;
        }

        p_port_def->eDir               = OMX_DirInput;
        p_port_def->nBufferCountActual = m_port_ip.def.nBufferCountActual;
        p_port_def->nBufferCountMin    = m_port_ip.def.nBufferCountMin;
        p_port_def->nBufferSize        = m_port_ip.def.nBufferSize;
        p_port_def->bEnabled           = m_port_ip.enabled;
        p_port_def->bPopulated         = m_port_ip.populated;

        OMX_SWVDEC_LOG_HIGH("port index %d: "
                            "count actual %d, count min %d, size %d",
                            p_port_def->nPortIndex,
                            p_port_def->nBufferCountActual,
                            p_port_def->nBufferCountMin,
                            p_port_def->nBufferSize);

        // frame dimensions & attributes don't apply to input port

        p_port_def->format.video.eColorFormat       = OMX_COLOR_FormatUnused;
        p_port_def->format.video.eCompressionFormat = m_omx_video_codingtype;
    }
    else if (p_port_def->nPortIndex == OMX_CORE_PORT_INDEX_OP)
    {
        unsigned int frame_width  = m_frame_dimensions.width;
        unsigned int frame_height = m_frame_dimensions.height;

        if ((retval = get_frame_dimensions_swvdec()) != OMX_ErrorNone)
        {
            goto get_port_definition_exit;
        }

        p_port_def->format.video.nFrameWidth  = m_frame_dimensions.width;
        p_port_def->format.video.nFrameHeight = m_frame_dimensions.height;

        // if frame dimensions have changed, update frame attributes
        if ((frame_width  != m_frame_dimensions.width) ||
            (frame_height != m_frame_dimensions.height))
        {
            if ((retval = set_frame_attributes(DEFAULT_ALIGNMENT_STRIDE,
                                               DEFAULT_ALIGNMENT_SCANLINES_Y,
                                               DEFAULT_ALIGNMENT_SCANLINES_UV,
                                               DEFAULT_ALIGNMENT_SIZE)) !=
                OMX_ErrorNone)
            {
                goto get_port_definition_exit;
            }
        }

        if ((retval = get_frame_attributes_swvdec()) != OMX_ErrorNone)
        {
            goto get_port_definition_exit;
        }

        p_port_def->format.video.nStride      = m_frame_attributes.stride;
        p_port_def->format.video.nSliceHeight = m_frame_attributes.scanlines;

        OMX_SWVDEC_LOG_HIGH("port index %d: "
                            "%d x %d, stride %d, sliceheight %d",
                            p_port_def->nPortIndex,
                            p_port_def->format.video.nFrameWidth,
                            p_port_def->format.video.nFrameHeight,
                            p_port_def->format.video.nStride,
                            p_port_def->format.video.nSliceHeight);

        /**
         * Query to SwVdec core for buffer requirements is not allowed in
         * executing state since it will overwrite the component's buffer
         * requirements updated via the most recent set_parameter().
         *
         * Buffer requirements communicated to component via set_parameter() are
         * not propagated to SwVdec core.
         *
         * The only execption is if port reconfiguration is in progress, in
         * which case the query to SwVdec core is required since buffer
         * requirements can change based on new dimensions.
         */
        if ((m_state != OMX_StateExecuting) || m_port_reconfig_inprogress)
        {
            if ((retval =
                 get_buffer_requirements_swvdec(OMX_CORE_PORT_INDEX_OP)) !=
                OMX_ErrorNone)
            {
                goto get_port_definition_exit;
            }
        }

        p_port_def->eDir               = OMX_DirOutput;
        p_port_def->nBufferCountActual = m_port_op.def.nBufferCountActual;
        p_port_def->nBufferCountMin    = m_port_op.def.nBufferCountMin;
        p_port_def->nBufferSize        = m_port_op.def.nBufferSize;
        p_port_def->bEnabled           = m_port_op.enabled;
        p_port_def->bPopulated         = m_port_op.populated;

        OMX_SWVDEC_LOG_HIGH("port index %d: "
                            "count actual %d, count min %d, size %d",
                            p_port_def->nPortIndex,
                            p_port_def->nBufferCountActual,
                            p_port_def->nBufferCountMin,
                            p_port_def->nBufferSize);

        p_port_def->format.video.eColorFormat       = m_omx_color_formattype;
        p_port_def->format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("port index '%d' invalid", p_port_def->nPortIndex);
        retval = OMX_ErrorBadPortIndex;
    }

get_port_definition_exit:
    return retval;
}

/**
 * @brief Set port definition for input or output port.
 *
 * @param[in] p_port_def: Pointer to port definition type.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::set_port_definition(
    OMX_PARAM_PORTDEFINITIONTYPE *p_port_def)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    OMX_SWVDEC_LOG_HIGH("port index %d: "
                        "count actual %d, count min %d, size %d",
                        p_port_def->nPortIndex,
                        p_port_def->nBufferCountActual,
                        p_port_def->nBufferCountMin,
                        p_port_def->nBufferSize);

    if (p_port_def->nPortIndex == OMX_CORE_PORT_INDEX_IP)
    {
        m_port_ip.def.nBufferCountActual = p_port_def->nBufferCountActual;
        m_port_ip.def.nBufferCountMin    = p_port_def->nBufferCountMin;
        m_port_ip.def.nBufferSize        = p_port_def->nBufferSize;
    }
    else if (p_port_def->nPortIndex == OMX_CORE_PORT_INDEX_OP)
    {
        OMX_SWVDEC_LOG_HIGH("port index %d: %d x %d",
                            p_port_def->nPortIndex,
                            p_port_def->format.video.nFrameWidth,
                            p_port_def->format.video.nFrameHeight);

        m_port_op.def.nBufferCountActual = p_port_def->nBufferCountActual;
        m_port_op.def.nBufferCountMin    = p_port_def->nBufferCountMin;
        m_port_op.def.nBufferSize        = p_port_def->nBufferSize;

        retval = set_frame_dimensions(p_port_def->format.video.nFrameWidth,
                                      p_port_def->format.video.nFrameHeight);
        if (retval != OMX_ErrorNone)
            goto set_port_definition_exit;

        m_frame_attributes.stride    = p_port_def->format.video.nStride;
        m_frame_attributes.scanlines = p_port_def->format.video.nSliceHeight;
        m_frame_attributes.size      = p_port_def->nBufferSize;

        retval = set_frame_attributes(DEFAULT_ALIGNMENT_STRIDE,
                                      DEFAULT_ALIGNMENT_SCANLINES_Y,
                                      DEFAULT_ALIGNMENT_SCANLINES_UV,
                                      DEFAULT_ALIGNMENT_SIZE);
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("port index '%d' invalid", p_port_def->nPortIndex);
        retval = OMX_ErrorBadPortIndex;
    }

set_port_definition_exit:
    return retval;
}

/**
 * @brief Get supported profile & level.
 *
 * The supported profiles & levels are not queried from SwVdec core, but
 * hard-coded. This should ideally be replaced with a query to SwVdec core.
 *
 * @param[in,out] p_profilelevel: Pointer to video profile & level type.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::get_supported_profilelevel(
    OMX_VIDEO_PARAM_PROFILELEVELTYPE *p_profilelevel)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (p_profilelevel == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_profilelevel = NULL");
        retval = OMX_ErrorBadParameter;
        goto get_supported_profilelevel_exit;
    }

    if (p_profilelevel->nPortIndex != OMX_CORE_PORT_INDEX_IP)
    {
        OMX_SWVDEC_LOG_ERROR("port index '%d' invalid",
                             p_profilelevel->nPortIndex);
        retval = OMX_ErrorBadPortIndex;
        goto get_supported_profilelevel_exit;
    }

    if (m_omx_video_codingtype == OMX_VIDEO_CodingH263)
    {
        if (p_profilelevel->nProfileIndex == 0)
        {
            p_profilelevel->eProfile = OMX_VIDEO_H263ProfileBaseline;
            p_profilelevel->eLevel   = OMX_VIDEO_H263Level70;
            OMX_SWVDEC_LOG_HIGH("H.263 baseline profile, level 70");
        }
        else
        {
            OMX_SWVDEC_LOG_HIGH("profile index '%d' unsupported; "
                                "no more profiles",
                                p_profilelevel->nProfileIndex);
            retval = OMX_ErrorNoMore;
        }
    }
    else if ((m_omx_video_codingtype == OMX_VIDEO_CodingMPEG4) ||
             (m_omx_video_codingtype ==
              ((OMX_VIDEO_CODINGTYPE) QOMX_VIDEO_CodingDivx)))
    {
        if (p_profilelevel->nProfileIndex == 0)
        {
            p_profilelevel->eProfile = OMX_VIDEO_MPEG4ProfileSimple;
            p_profilelevel->eLevel   = OMX_VIDEO_MPEG4Level5;
            OMX_SWVDEC_LOG_HIGH("MPEG-4 simple profile, level 5");
        }
        else if (p_profilelevel->nProfileIndex == 1)
        {
            p_profilelevel->eProfile = OMX_VIDEO_MPEG4ProfileAdvancedSimple;
            p_profilelevel->eLevel   = OMX_VIDEO_MPEG4Level5;
            OMX_SWVDEC_LOG_HIGH("MPEG-4 advanced simple profile, level 5");
        }
        else
        {
            OMX_SWVDEC_LOG_HIGH("profile index '%d' unsupported; "
                                "no more profiles",
                                p_profilelevel->nProfileIndex);
            retval = OMX_ErrorNoMore;
        }
    }
    else
    {
        assert(0);
        retval = OMX_ErrorUndefined;
    }

get_supported_profilelevel_exit:
    return retval;
}

/**
 * @brief Describe color format.
 *
 * @param[in,out] p_params: Pointer to 'DescribeColorFormatParams' structure.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::describe_color_format(
    DescribeColorFormatParams *p_params)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (p_params == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_params = NULL");
        retval = OMX_ErrorBadParameter;
    }
    else
    {
        MediaImage *p_img = &p_params->sMediaImage;

        switch (p_params->eColorFormat)
        {

        case QOMX_COLOR_FORMATYUV420PackedSemiPlanar32m:
        {
            size_t stride, scanlines;

            p_img->mType = MediaImage::MEDIA_IMAGE_TYPE_YUV;
            p_img->mNumPlanes = 3;

            p_img->mWidth  = p_params->nFrameWidth;
            p_img->mHeight = p_params->nFrameHeight;

            stride    = ALIGN(p_img->mWidth,  DEFAULT_ALIGNMENT_STRIDE);
            scanlines = ALIGN(p_img->mHeight, DEFAULT_ALIGNMENT_SCANLINES_Y);

            p_img->mBitDepth = 8;

            // plane 0 (Y)
            p_img->mPlane[MediaImage::Y].mOffset = 0;
            p_img->mPlane[MediaImage::Y].mColInc = 1;
            p_img->mPlane[MediaImage::Y].mRowInc = stride;
            p_img->mPlane[MediaImage::Y].mHorizSubsampling = 1;
            p_img->mPlane[MediaImage::Y].mVertSubsampling  = 1;

            // plane 1 (U)
            p_img->mPlane[MediaImage::Y].mOffset = stride * scanlines;
            p_img->mPlane[MediaImage::Y].mColInc = 2;
            p_img->mPlane[MediaImage::Y].mRowInc = stride;
            p_img->mPlane[MediaImage::Y].mHorizSubsampling = 2;
            p_img->mPlane[MediaImage::Y].mVertSubsampling  = 2;

            // plane 2 (V)
            p_img->mPlane[MediaImage::Y].mOffset = stride * scanlines + 1;
            p_img->mPlane[MediaImage::Y].mColInc = 2;
            p_img->mPlane[MediaImage::Y].mRowInc = stride;
            p_img->mPlane[MediaImage::Y].mHorizSubsampling = 2;
            p_img->mPlane[MediaImage::Y].mVertSubsampling  = 2;

            break;
        }

        case OMX_COLOR_FormatYUV420SemiPlanar:
        {
            size_t stride, scanlines;

            p_img->mType = MediaImage::MEDIA_IMAGE_TYPE_YUV;
            p_img->mNumPlanes = 3;

            p_img->mWidth  = p_params->nFrameWidth;
            p_img->mHeight = p_params->nFrameHeight;

            stride    = ALIGN(p_img->mWidth,  16);
            scanlines = ALIGN(p_img->mHeight,  1);

            p_img->mBitDepth = 8;

            // plane 0 (Y)
            p_img->mPlane[MediaImage::Y].mOffset = 0;
            p_img->mPlane[MediaImage::Y].mColInc = 1;
            p_img->mPlane[MediaImage::Y].mRowInc = stride;
            p_img->mPlane[MediaImage::Y].mHorizSubsampling = 1;
            p_img->mPlane[MediaImage::Y].mVertSubsampling  = 1;

            // plane 1 (U)
            p_img->mPlane[MediaImage::Y].mOffset = stride * scanlines;
            p_img->mPlane[MediaImage::Y].mColInc = 2;
            p_img->mPlane[MediaImage::Y].mRowInc = stride;
            p_img->mPlane[MediaImage::Y].mHorizSubsampling = 2;
            p_img->mPlane[MediaImage::Y].mVertSubsampling  = 2;

            // plane 2 (V)
            p_img->mPlane[MediaImage::Y].mOffset = stride * scanlines + 1;
            p_img->mPlane[MediaImage::Y].mColInc = 2;
            p_img->mPlane[MediaImage::Y].mRowInc = stride;
            p_img->mPlane[MediaImage::Y].mHorizSubsampling = 2;
            p_img->mPlane[MediaImage::Y].mVertSubsampling  = 2;

            break;
        }

        default:
        {
            OMX_SWVDEC_LOG_ERROR("color format '0x%08x' invalid/unsupported",
                                 p_params->eColorFormat);

            p_img->mType = MediaImage::MEDIA_IMAGE_TYPE_UNKNOWN;

            retval = OMX_ErrorBadParameter;
            break;
        }

        } // switch (p_params->eColorFormat)
    }

    return retval;
}

/**
 * @brief Set QTI vendor-specific port definition for input or output port.
 *
 * @param[in] p_port_def: Pointer to QTI vendor-specific port definition type.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::set_port_definition_qcom(
    OMX_QCOM_PARAM_PORTDEFINITIONTYPE *p_port_def)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (p_port_def->nPortIndex == OMX_CORE_PORT_INDEX_IP)
    {
        switch (p_port_def->nFramePackingFormat)
        {

        case OMX_QCOM_FramePacking_Arbitrary:
        {
            OMX_SWVDEC_LOG_ERROR("OMX_QCOM_FramePacking_Arbitrary unsupported");
            retval = OMX_ErrorUnsupportedSetting;
            break;
        }

        case OMX_QCOM_FramePacking_OnlyOneCompleteFrame:
        {
            OMX_SWVDEC_LOG_HIGH(
                "OMX_QCOM_FramePacking_OnlyOneCompleteFrame");
            break;
        }

        default:
        {
            OMX_SWVDEC_LOG_ERROR(
                "frame packing format '%d' unsupported");
            retval = OMX_ErrorUnsupportedSetting;
            break;
        }

        }
    }
    else if (p_port_def->nPortIndex == OMX_CORE_PORT_INDEX_OP)
    {
        OMX_SWVDEC_LOG_HIGH("nMemRegion %d, nCacheAttr %d",
                            p_port_def->nMemRegion,
                            p_port_def->nCacheAttr);
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("port index '%d' invalid",
                             p_port_def->nPortIndex);
        retval = OMX_ErrorBadPortIndex;
    }

    return retval;
}

/**
 * @brief Set SwVdec frame dimensions based on OMX component frame dimensions.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::set_frame_dimensions_swvdec()
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    SWVDEC_PROPERTY property;

    SWVDEC_STATUS retval_swvdec;

    property.id = SWVDEC_PROPERTY_ID_FRAME_DIMENSIONS;

    property.info.frame_dimensions.width  = m_frame_dimensions.width;
    property.info.frame_dimensions.height = m_frame_dimensions.height;

    if ((retval_swvdec = swvdec_setproperty(m_swvdec_handle, &property)) !=
        SWVDEC_STATUS_SUCCESS)
    {
        retval = retval_swvdec2omx(retval_swvdec);
    }

    return retval;
}

/**
 * @brief Set SwVdec frame attributes based on OMX component frame attributes.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::set_frame_attributes_swvdec()
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    SWVDEC_FRAME_ATTRIBUTES *p_frame_attributes;

    SWVDEC_PROPERTY property;

    SWVDEC_STATUS retval_swvdec;

    p_frame_attributes = &property.info.frame_attributes;

    property.id = SWVDEC_PROPERTY_ID_FRAME_ATTRIBUTES;

    p_frame_attributes->color_format = SWVDEC_COLOR_FORMAT_SEMIPLANAR_NV12;

    p_frame_attributes->semiplanar.stride    = m_frame_attributes.stride;
    p_frame_attributes->semiplanar.offset_uv = (m_frame_attributes.stride *
                                                m_frame_attributes.scanlines);

    p_frame_attributes->size = m_frame_attributes.size;

    if ((retval_swvdec = swvdec_setproperty(m_swvdec_handle, &property)) !=
        SWVDEC_STATUS_SUCCESS)
    {
        retval = retval_swvdec2omx(retval_swvdec);
    }

    return retval;
}

/**
 * @brief Get SwVdec frame dimensions and set OMX component frame dimensions.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::get_frame_dimensions_swvdec()
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    SWVDEC_PROPERTY property;

    SWVDEC_STATUS retval_swvdec;

    property.id = SWVDEC_PROPERTY_ID_FRAME_DIMENSIONS;

    if ((retval_swvdec = swvdec_getproperty(m_swvdec_handle, &property)) !=
        SWVDEC_STATUS_SUCCESS)
    {
        retval = retval_swvdec2omx(retval_swvdec);
    }
    else
    {
        m_frame_dimensions.width  = property.info.frame_dimensions.width;
        m_frame_dimensions.height = property.info.frame_dimensions.height;
    }

    return retval;
}

/**
 * @brief Get SwVdec frame attributes and set OMX component frame attributes.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::get_frame_attributes_swvdec()
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    SWVDEC_PROPERTY property;

    SWVDEC_STATUS retval_swvdec;

    property.id = SWVDEC_PROPERTY_ID_FRAME_ATTRIBUTES;

    if ((retval_swvdec = swvdec_getproperty(m_swvdec_handle, &property)) !=
        SWVDEC_STATUS_SUCCESS)
    {
        retval = retval_swvdec2omx(retval_swvdec);
    }
    else
    {
        m_frame_attributes.stride =
            property.info.frame_attributes.semiplanar.stride;

        m_frame_attributes.scanlines =
            (property.info.frame_attributes.semiplanar.offset_uv /
             property.info.frame_attributes.semiplanar.stride);

        m_frame_attributes.size = property.info.frame_attributes.size;
    }

    return retval;
}

/**
 * @brief Get SwVdec buffer requirements; set input or output port definitions.
 *
 * @param[in] port_index: Port index.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::get_buffer_requirements_swvdec(
    unsigned int port_index)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    SWVDEC_PROPERTY property;

    SWVDEC_STATUS retval_swvdec;

    SWVDEC_BUFFER_REQ *p_buffer_req;

    if (port_index == OMX_CORE_PORT_INDEX_IP)
    {
        property.id = SWVDEC_PROPERTY_ID_BUFFER_REQ_IP;

        p_buffer_req = &property.info.buffer_req_ip;

        if ((retval_swvdec = swvdec_getproperty(m_swvdec_handle, &property)) !=
            SWVDEC_STATUS_SUCCESS)
        {
            retval = retval_swvdec2omx(retval_swvdec);
            goto get_buffer_requirements_swvdec_exit;
        }

        m_port_ip.def.nBufferSize        = p_buffer_req->size;
        m_port_ip.def.nBufferCountMin    = p_buffer_req->mincount;
        m_port_ip.def.nBufferCountActual = MAX(p_buffer_req->mincount,
                                               OMX_SWVDEC_IP_BUFFER_COUNT);
        m_port_ip.def.nBufferAlignment   = p_buffer_req->alignment;

        OMX_SWVDEC_LOG_HIGH("ip port: %d bytes x %d, %d-byte aligned",
                            m_port_ip.def.nBufferSize,
                            m_port_ip.def.nBufferCountActual,
                            m_port_ip.def.nBufferAlignment);
    }
    else if (port_index == OMX_CORE_PORT_INDEX_OP)
    {
        property.id = SWVDEC_PROPERTY_ID_BUFFER_REQ_OP;

        p_buffer_req = &property.info.buffer_req_op;

        if ((retval_swvdec = swvdec_getproperty(m_swvdec_handle, &property)) !=
            SWVDEC_STATUS_SUCCESS)
        {
            retval = retval_swvdec2omx(retval_swvdec);
            goto get_buffer_requirements_swvdec_exit;
        }

        if (m_sync_frame_decoding_mode)
        {
            // only 1 output buffer for sync frame decoding mode
            p_buffer_req->mincount = 1;
        }

        m_port_op.def.nBufferSize        = p_buffer_req->size;
        m_port_op.def.nBufferCountMin    = p_buffer_req->mincount;
        m_port_op.def.nBufferCountActual = p_buffer_req->mincount;
        m_port_op.def.nBufferAlignment   = p_buffer_req->alignment;

        OMX_SWVDEC_LOG_HIGH("op port: %d bytes x %d, %d-byte aligned",
                            m_port_op.def.nBufferSize,
                            m_port_op.def.nBufferCountActual,
                            m_port_op.def.nBufferAlignment);
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("port index '%d' invalid", port_index);
        retval = OMX_ErrorBadPortIndex;
    }

get_buffer_requirements_swvdec_exit:
    return retval;
}

/**
 * @brief Allocate input buffer, and input buffer info array if ncessary.
 *
 * @param[in,out] pp_buffer_hdr: Pointer to pointer to buffer header type
 *                               structure.
 * @param[in]     p_app_data:    Pointer to IL client app data.
 * @param[in]     size:          Size of buffer to be allocated in bytes.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::buffer_allocate_ip(
    OMX_BUFFERHEADERTYPE **pp_buffer_hdr,
    OMX_PTR                p_app_data,
    OMX_U32                size)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    unsigned int ii;

    if (size != m_port_ip.def.nBufferSize)
    {
        OMX_SWVDEC_LOG_ERROR("requested size (%d bytes) not equal to "
                             "configured size (%d bytes)",
                             size,
                             m_port_ip.def.nBufferSize);
        retval = OMX_ErrorBadParameter;
        goto buffer_allocate_ip_exit;
    }

    if (m_buffer_array_ip == NULL)
    {
        // input buffer info array not allocated; allocate here

        OMX_SWVDEC_LOG_HIGH("allocating buffer info array, %d element%s",
                            m_port_ip.def.nBufferCountActual,
                            (m_port_ip.def.nBufferCountActual > 1) ? "s" : "");

        if ((retval = buffer_allocate_ip_info_array()) != OMX_ErrorNone)
        {
            goto buffer_allocate_ip_exit;
        }
    }

    for (ii = 0; ii < m_port_ip.def.nBufferCountActual; ii++)
    {
        if (m_buffer_array_ip[ii].buffer_populated == false)
        {
            OMX_SWVDEC_LOG_LOW("buffer %d not populated", ii);
            break;
        }
    }

    if (ii < m_port_ip.def.nBufferCountActual)
    {
        int pmem_fd = -1;

        unsigned char *bufferaddr;

        OMX_SWVDEC_LOG_HIGH("ip buffer %d: %d bytes being allocated",
                            ii,
                            size);

        m_buffer_array_ip[ii].ion_info.ion_fd_device =
            ion_memory_alloc_map(&m_buffer_array_ip[ii].ion_info.ion_alloc_data,
                                 &m_buffer_array_ip[ii].ion_info.ion_fd_data,
                                 size,
                                 m_port_ip.def.nBufferAlignment);

        if (m_buffer_array_ip[ii].ion_info.ion_fd_device < 0)
        {
            retval = OMX_ErrorInsufficientResources;
            goto buffer_allocate_ip_exit;
        }

        pmem_fd = m_buffer_array_ip[ii].ion_info.ion_fd_data.fd;

        bufferaddr = (unsigned char *) mmap(NULL,
                                            size,
                                            PROT_READ | PROT_WRITE,
                                            MAP_SHARED,
                                            pmem_fd,
                                            0);

        if (bufferaddr == MAP_FAILED)
        {
            OMX_SWVDEC_LOG_ERROR("mmap() failed for fd %d of size %d",
                                 pmem_fd,
                                 size);
            close(pmem_fd);
            ion_memory_free(&m_buffer_array_ip[ii].ion_info);
            retval = OMX_ErrorInsufficientResources;
            goto buffer_allocate_ip_exit;
        }

        *pp_buffer_hdr = &m_buffer_array_ip[ii].buffer_header;

        m_buffer_array_ip[ii].buffer_payload.bufferaddr  = bufferaddr;
        m_buffer_array_ip[ii].buffer_payload.pmem_fd     = pmem_fd;
        m_buffer_array_ip[ii].buffer_payload.buffer_len  = size;
        m_buffer_array_ip[ii].buffer_payload.mmaped_size = size;
        m_buffer_array_ip[ii].buffer_payload.offset      = 0;

        m_buffer_array_ip[ii].buffer_swvdec.p_buffer      = bufferaddr;
        m_buffer_array_ip[ii].buffer_swvdec.size          = size;
        m_buffer_array_ip[ii].buffer_swvdec.p_client_data =
            (void *) ((unsigned long) ii);

        m_buffer_array_ip[ii].buffer_populated = true;

        OMX_SWVDEC_LOG_HIGH("ip buffer %d: %p, %d bytes",
                            ii,
                            bufferaddr,
                            size);

        (*pp_buffer_hdr)->pBuffer           = (OMX_U8 *) bufferaddr;
        (*pp_buffer_hdr)->nSize             = sizeof(OMX_BUFFERHEADERTYPE);
        (*pp_buffer_hdr)->nVersion.nVersion = OMX_SPEC_VERSION;
        (*pp_buffer_hdr)->nAllocLen         = size;
        (*pp_buffer_hdr)->pAppPrivate       = p_app_data;
        (*pp_buffer_hdr)->nInputPortIndex   = OMX_CORE_PORT_INDEX_IP;
        (*pp_buffer_hdr)->pInputPortPrivate =
            (void *) &(m_buffer_array_ip[ii].buffer_payload);

        m_port_ip.populated   = port_ip_populated();
        m_port_ip.unpopulated = OMX_FALSE;
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("all %d ip buffers allocated",
                             m_port_ip.def.nBufferCountActual);
        retval = OMX_ErrorInsufficientResources;
    }

buffer_allocate_ip_exit:
    return retval;
}

/**
 * @brief Allocate output buffer, and output buffer info array if necessary.
 *
 * @param[in,out] pp_buffer_hdr: Pointer to pointer to buffer header type
 *                               structure.
 * @param[in]     p_app_data:    Pointer to IL client app data.
 * @param[in]     size:          Size of buffer to be allocated in bytes.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::buffer_allocate_op(
    OMX_BUFFERHEADERTYPE **pp_buffer_hdr,
    OMX_PTR                p_app_data,
    OMX_U32                size)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    unsigned int ii;

    if (size != m_port_op.def.nBufferSize)
    {
        OMX_SWVDEC_LOG_ERROR("requested size (%d bytes) not equal to "
                             "configured size (%d bytes)",
                             size,
                             m_port_op.def.nBufferSize);
        retval = OMX_ErrorBadParameter;
        goto buffer_allocate_op_exit;
    }

    if (m_buffer_array_op == NULL)
    {
        // output buffer info array not allocated; allocate here

        OMX_SWVDEC_LOG_HIGH("allocating buffer info array, %d element%s",
                            m_port_op.def.nBufferCountActual,
                            (m_port_op.def.nBufferCountActual > 1) ? "s" : "");

        if ((retval = buffer_allocate_op_info_array()) != OMX_ErrorNone)
        {
            goto buffer_allocate_op_exit;
        }
    }

    for (ii = 0; ii < m_port_op.def.nBufferCountActual; ii++)
    {
        if (m_buffer_array_op[ii].buffer_populated == false)
        {
            OMX_SWVDEC_LOG_LOW("buffer %d not populated", ii);
            break;
        }
    }

    if (ii < m_port_op.def.nBufferCountActual)
    {
        int pmem_fd = -1;

        unsigned char *bufferaddr;

        OMX_SWVDEC_LOG_HIGH("op buffer %d: %d bytes being allocated",
                            ii,
                            size);

        m_buffer_array_op[ii].ion_info.ion_fd_device =
            ion_memory_alloc_map(&m_buffer_array_op[ii].ion_info.ion_alloc_data,
                                 &m_buffer_array_op[ii].ion_info.ion_fd_data,
                                 size,
                                 m_port_op.def.nBufferAlignment);

        if (m_buffer_array_op[ii].ion_info.ion_fd_device < 0)
        {
            retval = OMX_ErrorInsufficientResources;
            goto buffer_allocate_op_exit;
        }

        pmem_fd = m_buffer_array_op[ii].ion_info.ion_fd_data.fd;

        bufferaddr = (unsigned char *) mmap(NULL,
                                            size,
                                            PROT_READ | PROT_WRITE,
                                            MAP_SHARED,
                                            pmem_fd,
                                            0);

        if (bufferaddr == MAP_FAILED)
        {
            OMX_SWVDEC_LOG_ERROR("mmap() failed for fd %d of size %d",
                                 pmem_fd,
                                 size);
            close(pmem_fd);
            ion_memory_free(&m_buffer_array_op[ii].ion_info);
            retval = OMX_ErrorInsufficientResources;
            goto buffer_allocate_op_exit;
        }

        *pp_buffer_hdr = &m_buffer_array_op[ii].buffer_header;

        m_buffer_array_op[ii].buffer_payload.bufferaddr  = bufferaddr;
        m_buffer_array_op[ii].buffer_payload.pmem_fd     = pmem_fd;
        m_buffer_array_op[ii].buffer_payload.buffer_len  = size;
        m_buffer_array_op[ii].buffer_payload.mmaped_size = size;
        m_buffer_array_op[ii].buffer_payload.offset      = 0;

        m_buffer_array_op[ii].buffer_swvdec.p_buffer      = bufferaddr;
        m_buffer_array_op[ii].buffer_swvdec.size          = size;
        m_buffer_array_op[ii].buffer_swvdec.p_client_data =
            (void *) ((unsigned long) ii);

        m_buffer_array_op[ii].buffer_populated = true;

        OMX_SWVDEC_LOG_HIGH("op buffer %d: %p, %d bytes",
                            ii,
                            bufferaddr,
                            size);

        (*pp_buffer_hdr)->pBuffer            = (OMX_U8 *) bufferaddr;
        (*pp_buffer_hdr)->nSize              = sizeof(OMX_BUFFERHEADERTYPE);
        (*pp_buffer_hdr)->nVersion.nVersion  = OMX_SPEC_VERSION;
        (*pp_buffer_hdr)->nAllocLen          = size;
        (*pp_buffer_hdr)->pAppPrivate        = p_app_data;
        (*pp_buffer_hdr)->nOutputPortIndex   = OMX_CORE_PORT_INDEX_OP;
        (*pp_buffer_hdr)->pOutputPortPrivate =
            (void *) &(m_buffer_array_op[ii].buffer_payload);

        m_port_op.populated   = port_op_populated();
        m_port_op.unpopulated = OMX_FALSE;
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("all %d op buffers allocated",
                             m_port_op.def.nBufferCountActual);
        retval = OMX_ErrorInsufficientResources;
    }

buffer_allocate_op_exit:
    return retval;
}

/**
 * @brief Allocate input buffer info array.
 */
OMX_ERRORTYPE omx_swvdec::buffer_allocate_ip_info_array()
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    unsigned int ii;

    OMX_BUFFERHEADERTYPE *p_buffer_hdr;

    if (m_buffer_array_ip != NULL)
    {
        OMX_SWVDEC_LOG_ERROR("buffer info array already allocated");
        retval = OMX_ErrorInsufficientResources;
        goto buffer_allocate_ip_hdr_exit;
    }

    // input buffer info array not allocated; allocate here

    OMX_SWVDEC_LOG_HIGH("allocating buffer info array, %d element%s",
                        m_port_ip.def.nBufferCountActual,
                        (m_port_ip.def.nBufferCountActual > 1) ? "s" : "");

    m_buffer_array_ip =
        (OMX_SWVDEC_BUFFER_INFO *) calloc(sizeof(OMX_SWVDEC_BUFFER_INFO),
                                          m_port_ip.def.nBufferCountActual);

    if (m_buffer_array_ip == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("failed to allocate buffer info array; "
                             "%d element%s, %d bytes requested",
                             m_port_ip.def.nBufferCountActual,
                             (m_port_ip.def.nBufferCountActual > 1) ? "s" : "",
                             sizeof(OMX_SWVDEC_BUFFER_INFO) *
                             m_port_ip.def.nBufferCountActual);
        retval = OMX_ErrorInsufficientResources;
        goto buffer_allocate_ip_hdr_exit;
    }

    for (ii = 0; ii < m_port_ip.def.nBufferCountActual; ii++)
    {
        p_buffer_hdr = &m_buffer_array_ip[ii].buffer_header;

        // reset file descriptors

        m_buffer_array_ip[ii].buffer_payload.pmem_fd = -1;
        m_buffer_array_ip[ii].ion_info.ion_fd_device = -1;

        m_buffer_array_ip[ii].buffer_swvdec.p_client_data =
            (void *) ((unsigned long) ii);

        p_buffer_hdr->nSize              = sizeof(OMX_BUFFERHEADERTYPE);
        p_buffer_hdr->nVersion.nVersion  = OMX_SPEC_VERSION;
        p_buffer_hdr->nOutputPortIndex   = OMX_CORE_PORT_INDEX_IP;
        p_buffer_hdr->pOutputPortPrivate =
            (void *) &(m_buffer_array_ip[ii].buffer_payload);
    }

buffer_allocate_ip_hdr_exit:
    return retval;
}

/**
 * @brief Allocate output buffer info array.
 */
OMX_ERRORTYPE omx_swvdec::buffer_allocate_op_info_array()
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    unsigned int ii;

    OMX_BUFFERHEADERTYPE *p_buffer_hdr;

    if (m_buffer_array_op != NULL)
    {
        OMX_SWVDEC_LOG_ERROR("buffer info array already allocated");
        retval = OMX_ErrorInsufficientResources;
        goto buffer_allocate_op_hdr_exit;
    }

    // output buffer info array not allocated; allocate here

    OMX_SWVDEC_LOG_HIGH("allocating buffer info array, %d element%s",
                        m_port_op.def.nBufferCountActual,
                        (m_port_op.def.nBufferCountActual > 1) ? "s" : "");

    m_buffer_array_op =
        (OMX_SWVDEC_BUFFER_INFO *) calloc(sizeof(OMX_SWVDEC_BUFFER_INFO),
                                          m_port_op.def.nBufferCountActual);

    if (m_buffer_array_op == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("failed to allocate buffer info array; "
                             "%d element%s, %d bytes requested",
                             m_port_op.def.nBufferCountActual,
                             (m_port_op.def.nBufferCountActual > 1) ? "s" : "",
                             sizeof(OMX_SWVDEC_BUFFER_INFO) *
                             m_port_op.def.nBufferCountActual);
        retval = OMX_ErrorInsufficientResources;
        goto buffer_allocate_op_hdr_exit;
    }

    for (ii = 0; ii < m_port_op.def.nBufferCountActual; ii++)
    {
        p_buffer_hdr = &m_buffer_array_op[ii].buffer_header;

        // reset file descriptors

        m_buffer_array_op[ii].buffer_payload.pmem_fd = -1;
        m_buffer_array_op[ii].ion_info.ion_fd_device = -1;

        m_buffer_array_op[ii].buffer_swvdec.p_client_data =
            (void *) ((unsigned long) ii);

        p_buffer_hdr->nSize              = sizeof(OMX_BUFFERHEADERTYPE);
        p_buffer_hdr->nVersion.nVersion  = OMX_SPEC_VERSION;
        p_buffer_hdr->nOutputPortIndex   = OMX_CORE_PORT_INDEX_OP;
        p_buffer_hdr->pOutputPortPrivate =
            (void *) &(m_buffer_array_op[ii].buffer_payload);
    }

buffer_allocate_op_hdr_exit:
    return retval;
}

/**
 * @brief Use buffer allocated by IL client; allocate output buffer info array
 *        if necessary.
 *
 * @param[in,out] pp_buffer_hdr: Pointer to pointer to buffer header type
 *                               structure.
 * @param[in]     p_app_data:    Pointer to IL client app data.
 * @param[in]     size:          Size of buffer to be allocated in bytes.
 * @param[in]     p_buffer:      Pointer to buffer to be used.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::buffer_use_op(
    OMX_BUFFERHEADERTYPE **pp_buffer_hdr,
    OMX_PTR                p_app_data,
    OMX_U32                size,
    OMX_U8                *p_buffer)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    unsigned int ii;

    (void) size;

    if (m_buffer_array_op == NULL)
    {
        // output buffer info array not allocated; allocate here

        OMX_SWVDEC_LOG_HIGH("allocating buffer info array, %d element%s",
                            m_port_op.def.nBufferCountActual,
                            (m_port_op.def.nBufferCountActual > 1) ? "s" : "");

        if ((retval = buffer_allocate_op_info_array()) != OMX_ErrorNone)
        {
            goto buffer_use_op_exit;
        }
    }

    if (m_meta_buffer_mode && (m_meta_buffer_array == NULL))
    {
        // meta buffer info array not allocated; allocate here

        OMX_SWVDEC_LOG_HIGH("allocating meta buffer info array, %d element%s",
                            m_port_op.def.nBufferCountActual,
                            (m_port_op.def.nBufferCountActual > 1) ? "s" : "");

        if ((retval = meta_buffer_array_allocate()) != OMX_ErrorNone)
        {
            goto buffer_use_op_exit;
        }
    }

    for (ii = 0; ii < m_port_op.def.nBufferCountActual; ii++)
    {
        if (m_buffer_array_op[ii].buffer_populated == false)
        {
            OMX_SWVDEC_LOG_LOW("buffer %d not populated", ii);
            break;
        }
    }

    if (ii < m_port_op.def.nBufferCountActual)
    {
        struct vdec_bufferpayload *p_buffer_payload;

        SWVDEC_BUFFER *p_buffer_swvdec;

        *pp_buffer_hdr   = &m_buffer_array_op[ii].buffer_header;
        p_buffer_payload = &m_buffer_array_op[ii].buffer_payload;
        p_buffer_swvdec  = &m_buffer_array_op[ii].buffer_swvdec;

        if (m_meta_buffer_mode)
        {
            p_buffer_swvdec->size          = m_port_op.def.nBufferSize;
            p_buffer_swvdec->p_client_data = (void *) ((unsigned long) ii);

            m_buffer_array_op[ii].buffer_populated = true;

            (*pp_buffer_hdr)->pBuffer     = p_buffer;
            (*pp_buffer_hdr)->pAppPrivate = p_app_data;
            (*pp_buffer_hdr)->nAllocLen   =
                sizeof(struct VideoDecoderOutputMetaData);

            OMX_SWVDEC_LOG_HIGH("op buffer %d: %p (meta buffer)",
                                ii,
                                *pp_buffer_hdr);

            m_port_op.populated   = port_op_populated();
            m_port_op.unpopulated = OMX_FALSE;
        }
        else if (m_android_native_buffers)
        {
            private_handle_t *p_handle;

            OMX_U8 *p_buffer_mapped;

            p_handle = (private_handle_t *) p_buffer;

            if (((OMX_U32) p_handle->size) < m_port_op.def.nBufferSize)
            {
                OMX_SWVDEC_LOG_ERROR("requested size (%d bytes) not equal to "
                                     "configured size (%d bytes)",
                                     p_handle->size,
                                     m_port_op.def.nBufferSize);

                retval = OMX_ErrorBadParameter;
                goto buffer_use_op_exit;
            }

            m_port_op.def.nBufferSize = p_handle->size;

            p_buffer_mapped = (OMX_U8 *) mmap(NULL,
                                              p_handle->size,
                                              PROT_READ | PROT_WRITE,
                                              MAP_SHARED,
                                              p_handle->fd,
                                              0);

            if (p_buffer_mapped == MAP_FAILED)
            {
                OMX_SWVDEC_LOG_ERROR("mmap() failed for fd %d of size %d",
                                     p_handle->fd,
                                     p_handle->size);

                retval = OMX_ErrorInsufficientResources;
                goto buffer_use_op_exit;
            }

            p_buffer_payload->bufferaddr  = p_buffer_mapped;
            p_buffer_payload->pmem_fd     = p_handle->fd;
            p_buffer_payload->buffer_len  = p_handle->size;
            p_buffer_payload->mmaped_size = p_handle->size;
            p_buffer_payload->offset      = 0;

            p_buffer_swvdec->p_buffer      = p_buffer_mapped;
            p_buffer_swvdec->size          = m_port_op.def.nBufferSize;
            p_buffer_swvdec->p_client_data = (void *) ((unsigned long) ii);

            m_buffer_array_op[ii].buffer_populated = true;

            (*pp_buffer_hdr)->pBuffer     = (m_android_native_buffers ?
                                             ((OMX_U8 *) p_handle) :
                                             p_buffer_mapped);
            (*pp_buffer_hdr)->pAppPrivate = p_app_data;
            (*pp_buffer_hdr)->nAllocLen   = m_port_op.def.nBufferSize;

            m_buffer_array_op[ii].ion_info.ion_fd_data.fd = p_handle->fd;

            OMX_SWVDEC_LOG_HIGH("op buffer %d: %p",
                                ii,
                                *pp_buffer_hdr);

            m_port_op.populated   = port_op_populated();
            m_port_op.unpopulated = OMX_FALSE;
        }
        else
        {
            OMX_SWVDEC_LOG_ERROR("neither 'meta buffer mode' nor "
                                 "'android native buffers' enabled");
            retval = OMX_ErrorBadParameter;
        }
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("all %d op buffers populated",
                             m_port_op.def.nBufferCountActual);
        retval = OMX_ErrorInsufficientResources;
    }

buffer_use_op_exit:
    return retval;
}

/**
 * @brief De-allocate input buffer.
 *
 * @param[in] p_buffer_hdr: Pointer to buffer header structure.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::buffer_deallocate_ip(
    OMX_BUFFERHEADERTYPE *p_buffer_hdr)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    unsigned int ii;

    if (p_buffer_hdr == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_buffer_hdr = NULL");
        retval = OMX_ErrorBadParameter;
        goto buffer_deallocate_ip_exit;
    }
    else if (m_buffer_array_ip == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("ip buffer array not allocated");
        retval = OMX_ErrorBadParameter;
        goto buffer_deallocate_ip_exit;
    }

    for (ii = 0; ii < m_port_ip.def.nBufferCountActual; ii++)
    {
        if (p_buffer_hdr == &(m_buffer_array_ip[ii].buffer_header))
        {
            OMX_SWVDEC_LOG_LOW("%p has index %d",
                               p_buffer_hdr->pBuffer,
                               ii);
            break;
        }
    }

    if (ii < m_port_ip.def.nBufferCountActual)
    {
        if (m_buffer_array_ip[ii].buffer_payload.pmem_fd > 0)
        {
            m_buffer_array_ip[ii].buffer_populated = false;

            m_port_ip.populated = OMX_FALSE;

            munmap(m_buffer_array_ip[ii].buffer_payload.bufferaddr,
                   m_buffer_array_ip[ii].buffer_payload.mmaped_size);

            close(m_buffer_array_ip[ii].buffer_payload.pmem_fd);
            m_buffer_array_ip[ii].buffer_payload.pmem_fd = -1;

            ion_memory_free(&m_buffer_array_ip[ii].ion_info);

            // check if all buffers are unpopulated
            for (ii = 0; ii < m_port_ip.def.nBufferCountActual; ii++)
            {
                if (m_buffer_array_ip[ii].buffer_populated)
                    break;
            }

            // if no buffers are populated, de-allocate input buffer info array
            if (ii == m_port_ip.def.nBufferCountActual)
            {
                buffer_deallocate_ip_info_array();
                m_port_ip.unpopulated = OMX_TRUE;
            }
        }
        else
        {
            OMX_SWVDEC_LOG_ERROR("%p: pmem_fd %d",
                                 p_buffer_hdr->pBuffer,
                                 m_buffer_array_ip[ii].buffer_payload.pmem_fd);
        }
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("%p not found", p_buffer_hdr->pBuffer);
        retval = OMX_ErrorBadParameter;
    }

buffer_deallocate_ip_exit:
    return retval;
}

/**
 * @brief De-allocate output buffer.
 *
 * @param[in] p_buffer_hdr: Pointer to buffer header structure.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::buffer_deallocate_op(
    OMX_BUFFERHEADERTYPE *p_buffer_hdr)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    unsigned int ii;

    if (p_buffer_hdr == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_buffer_hdr = NULL");
        retval = OMX_ErrorBadParameter;
        goto buffer_deallocate_op_exit;
    }
    else if (m_buffer_array_op == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("op buffer array not allocated");
        retval = OMX_ErrorBadParameter;
        goto buffer_deallocate_op_exit;
    }

    for (ii = 0; ii < m_port_op.def.nBufferCountActual; ii++)
    {
        if (p_buffer_hdr == &(m_buffer_array_op[ii].buffer_header))
        {
            OMX_SWVDEC_LOG_LOW("%p has index %d",
                               p_buffer_hdr->pBuffer,
                               ii);
            break;
        }
    }

    if (ii < m_port_op.def.nBufferCountActual)
    {
        assert(m_buffer_array_op[ii].buffer_payload.pmem_fd > 0);

        if (m_meta_buffer_mode)
        {
            // do nothing; munmap() & close() done in FBD or RR
        }
        else if (m_android_native_buffers)
        {
            munmap(m_buffer_array_op[ii].buffer_payload.bufferaddr,
                   m_buffer_array_op[ii].buffer_payload.mmaped_size);
            m_buffer_array_op[ii].buffer_payload.pmem_fd = -1;
        }
        else if (m_sync_frame_decoding_mode)
        {
            munmap(m_buffer_array_op[ii].buffer_payload.bufferaddr,
                   m_buffer_array_op[ii].buffer_payload.mmaped_size);
            close(m_buffer_array_op[ii].buffer_payload.pmem_fd);
            m_buffer_array_op[ii].buffer_payload.pmem_fd = -1;
            ion_memory_free(&m_buffer_array_op[ii].ion_info);
        }
        else
        {
            assert(0);
        }

        m_buffer_array_op[ii].buffer_populated = false;

        m_port_op.populated = OMX_FALSE;

        // check if all buffers are unpopulated
        for (ii = 0; ii < m_port_op.def.nBufferCountActual; ii++)
        {
            if (m_buffer_array_op[ii].buffer_populated)
                break;
        }

        // if no buffers are populated, de-allocate output buffer info array
        if (ii == m_port_op.def.nBufferCountActual)
        {
            buffer_deallocate_op_info_array();
            m_port_op.unpopulated = OMX_TRUE;

            if (m_meta_buffer_mode)
            {
                meta_buffer_array_deallocate();
            }
        }
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("%p not found", p_buffer_hdr->pBuffer);
        retval = OMX_ErrorBadParameter;
    }

buffer_deallocate_op_exit:
    return retval;
}

/**
 * @brief De-allocate input buffer info array.
 */
void omx_swvdec::buffer_deallocate_ip_info_array()
{
    assert(m_buffer_array_ip != NULL);

    free(m_buffer_array_ip);
    m_buffer_array_ip = NULL;
}

/**
 * @brief De-allocate output buffer info array.
 */
void omx_swvdec::buffer_deallocate_op_info_array()
{
    assert(m_buffer_array_op != NULL);

    free(m_buffer_array_op);
    m_buffer_array_op = NULL;
}

/**
 * @brief Allocate meta buffer info array.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::meta_buffer_array_allocate()
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    m_meta_buffer_array = ((OMX_SWVDEC_META_BUFFER_INFO *)
                           calloc(sizeof(OMX_SWVDEC_META_BUFFER_INFO),
                                  m_port_op.def.nBufferCountActual));

    if (m_meta_buffer_array == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("failed to allocate meta_buffer info array; "
                             "%d element%s, %d bytes requested",
                             m_port_op.def.nBufferCountActual,
                             (m_port_op.def.nBufferCountActual > 1) ? "s" : "",
                             sizeof(OMX_SWVDEC_META_BUFFER_INFO) *
                             m_port_op.def.nBufferCountActual);

        retval = OMX_ErrorInsufficientResources;
    }

    return retval;
}

/**
 * @brief De-allocate meta buffer info array.
 */
void omx_swvdec::meta_buffer_array_deallocate()
{
    assert(m_meta_buffer_array != NULL);

    free(m_meta_buffer_array);
    m_meta_buffer_array = NULL;
}

/**
 * @brief Add meta buffer reference.
 *
 * @param[in] index:
 * @param[in] fd:
 * @param[in] offset:
 */
void omx_swvdec::meta_buffer_ref_add(unsigned int index,
                                     unsigned int fd,
                                     unsigned int offset)
{
    pthread_mutex_lock(&m_meta_buffer_array_mutex);

    if ((m_meta_buffer_array[index].dup_fd != 0) &&
        (m_meta_buffer_array[index].fd     != fd) &&
        (m_meta_buffer_array[index].offset != offset))
    {
        OMX_SWVDEC_LOG_LOW("index %d taken by fd %d, offset %d",
                           index,
                           m_meta_buffer_array[index].fd,
                           m_meta_buffer_array[index].offset);
    }
    else
    {
        if (m_meta_buffer_array[index].dup_fd == 0)
        {
            m_meta_buffer_array[index].fd     = fd;
            m_meta_buffer_array[index].dup_fd = dup(fd);
            m_meta_buffer_array[index].offset = offset;
        }

        m_meta_buffer_array[index].ref_count++;
    }

    pthread_mutex_unlock(&m_meta_buffer_array_mutex);
}

/**
 * @brief Remove meta buffer reference.
 *
 * @param[in] fd:
 * @param[in] offset:
 */
void omx_swvdec::meta_buffer_ref_remove(unsigned int fd,
                                        unsigned int offset)
{
    unsigned int ii;

    pthread_mutex_lock(&m_meta_buffer_array_mutex);

    for (ii = 0; ii < m_port_op.def.nBufferCountActual; ii++)
    {
        if ((m_meta_buffer_array[ii].fd     == fd) &&
            (m_meta_buffer_array[ii].offset == offset))
        {
            m_meta_buffer_array[ii].ref_count--;

            if (m_meta_buffer_array[ii].ref_count == 0)
            {
                close(m_meta_buffer_array[ii].dup_fd);

                m_meta_buffer_array[ii].fd = 0;
                m_meta_buffer_array[ii].dup_fd = 0;
                m_meta_buffer_array[ii].offset = 0;

                munmap(m_buffer_array_op[ii].buffer_payload.bufferaddr,
                       m_buffer_array_op[ii].buffer_payload.mmaped_size);

                m_buffer_array_op[ii].buffer_payload.bufferaddr  = NULL;
                m_buffer_array_op[ii].buffer_payload.offset      = 0;
                m_buffer_array_op[ii].buffer_payload.mmaped_size = 0;

                m_buffer_array_op[ii].buffer_swvdec.p_buffer = NULL;
                m_buffer_array_op[ii].buffer_swvdec.size     = 0;
            }

            break;
        }
    }

    assert(ii < m_port_op.def.nBufferCountActual);

    pthread_mutex_unlock(&m_meta_buffer_array_mutex);
}

/**
 * @brief Check if ip port is populated, i.e., if all ip buffers are populated.
 *
 * @retval  true
 * @retval false
 */
OMX_BOOL omx_swvdec::port_ip_populated()
{
    OMX_BOOL retval = OMX_FALSE;

    if (m_buffer_array_ip != NULL)
    {
        unsigned int ii;

        for (ii = 0; ii < m_port_ip.def.nBufferCountActual; ii++)
        {
            if (m_buffer_array_ip[ii].buffer_populated == false)
            {
                break;
            }
        }

        if (ii == m_port_ip.def.nBufferCountActual)
        {
            retval = OMX_TRUE;
        }
    }

    return retval;
}

/**
 * @brief Check if op port is populated, i.e., if all op buffers are populated.
 *
 * @retval  true
 * @retval false
 */
OMX_BOOL omx_swvdec::port_op_populated()
{
    OMX_BOOL retval = OMX_FALSE;

    if (m_buffer_array_op != NULL)
    {
        unsigned int ii;

        for (ii = 0; ii < m_port_op.def.nBufferCountActual; ii++)
        {
            if (m_buffer_array_op[ii].buffer_populated == false)
            {
                break;
            }
        }

        if (ii == m_port_op.def.nBufferCountActual)
        {
            retval = OMX_TRUE;
        }
    }

    return retval;
}

/**
 * @brief Flush input, output, or both input & output ports.
 *
 * @param[in] port_index: Index of port to flush.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::flush(unsigned int port_index)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (((port_index == OMX_CORE_PORT_INDEX_IP) &&
         m_port_ip.flush_inprogress) ||
        ((port_index == OMX_CORE_PORT_INDEX_OP) &&
         m_port_op.flush_inprogress) ||
        ((port_index == OMX_ALL) &&
         m_port_ip.flush_inprogress &&
         m_port_op.flush_inprogress))
    {
        OMX_SWVDEC_LOG_HIGH("flush port %d already in progress", port_index);
    }
    else
    {
        SWVDEC_FLUSH_TYPE swvdec_flush_type;

        SWVDEC_STATUS retval_swvdec;

        if (port_index == OMX_CORE_PORT_INDEX_IP)
        {
            m_port_ip.flush_inprogress = OMX_TRUE;

            // no separate SwVdec flush type for input
        }
        else if (port_index == OMX_CORE_PORT_INDEX_OP)
        {
            m_port_op.flush_inprogress = OMX_TRUE;

            swvdec_flush_type = (m_port_ip.flush_inprogress ?
                                 SWVDEC_FLUSH_TYPE_ALL :
                                 SWVDEC_FLUSH_TYPE_OP);

            if ((retval_swvdec = swvdec_flush(m_swvdec_handle,
                                              swvdec_flush_type)) !=
                SWVDEC_STATUS_SUCCESS)
            {
                retval = retval_swvdec2omx(retval_swvdec);
            }
        }
        else if (port_index == OMX_ALL)
        {
            m_port_ip.flush_inprogress = OMX_TRUE;
            m_port_op.flush_inprogress = OMX_TRUE;

            swvdec_flush_type = SWVDEC_FLUSH_TYPE_ALL;

            if ((retval_swvdec = swvdec_flush(m_swvdec_handle,
                                              swvdec_flush_type)) !=
                SWVDEC_STATUS_SUCCESS)
            {
                retval = retval_swvdec2omx(retval_swvdec);
            }
        }
        else
        {
            assert(0);
        }
    }

    return retval;
}

/**
 * @brief Allocate & map ION memory.
 */
int omx_swvdec::ion_memory_alloc_map(struct ion_allocation_data *p_alloc_data,
                                     struct ion_fd_data         *p_fd_data,
                                     OMX_U32                     size,
                                     OMX_U32                     alignment)
{
    int fd = -EINVAL;
    int rc = -EINVAL;

    if ((p_alloc_data == NULL) || (p_fd_data == NULL) || (size == 0))
    {
        OMX_SWVDEC_LOG_ERROR("invalid arguments");
        goto ion_memory_alloc_map_exit;
    }

    if ((fd = open("/dev/ion", O_RDONLY)) < 0)
    {
        OMX_SWVDEC_LOG_ERROR("failed to open ion device; fd = %d", fd);
        goto ion_memory_alloc_map_exit;
    }

    p_alloc_data->len   = size;
    p_alloc_data->align = (alignment < 4096) ? 4096 : alignment;
    p_alloc_data->heap_id_mask = ION_HEAP(ION_IOMMU_HEAP_ID);
    p_alloc_data->flags = 0;

    OMX_SWVDEC_LOG_LOW("heap_id_mask 0x%08x, len %d, align %d",
                       p_alloc_data->heap_id_mask,
                       p_alloc_data->len,
                       p_alloc_data->align);

    rc = ioctl(fd, ION_IOC_ALLOC, p_alloc_data);

    if (rc || (p_alloc_data->handle == 0))
    {
        OMX_SWVDEC_LOG_ERROR("ioctl() for allocation failed");
        close(fd);
        fd = -ENOMEM;
        goto ion_memory_alloc_map_exit;
    }

    p_fd_data->handle = p_alloc_data->handle;

    if (ioctl(fd, ION_IOC_MAP, p_fd_data))
    {
        struct vdec_ion ion_buf_info;

        OMX_SWVDEC_LOG_ERROR("ioctl() for mapping failed");

        ion_buf_info.ion_alloc_data = *p_alloc_data;
        ion_buf_info.ion_fd_device  = fd;
        ion_buf_info.ion_fd_data    = *p_fd_data;

        ion_memory_free(&ion_buf_info);

        p_fd_data->fd = -1;
        close(fd);
        fd = -ENOMEM;
        goto ion_memory_alloc_map_exit;
    }

ion_memory_alloc_map_exit:
    return fd;
}

/**
 * @brief Free ION memory.
 */
void omx_swvdec::ion_memory_free(struct vdec_ion *p_ion_buf_info)
{
    if (p_ion_buf_info == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_ion_buf_info = NULL");
        goto ion_memory_free_exit;
    }

    if (ioctl(p_ion_buf_info->ion_fd_device,
              ION_IOC_FREE,
              &p_ion_buf_info->ion_alloc_data.handle))
    {
        OMX_SWVDEC_LOG_ERROR("ioctl() for freeing failed");
    }

    close(p_ion_buf_info->ion_fd_device);

    p_ion_buf_info->ion_fd_device         = -1;
    p_ion_buf_info->ion_alloc_data.handle =  0;
    p_ion_buf_info->ion_fd_data.fd        = -1;

ion_memory_free_exit:
    return;
}

/**
 * ----------------------------
 * component callback functions
 * ----------------------------
 */

/**
 * @brief Empty buffer done callback.
 *
 * @param[in] p_buffer_ip: Pointer to input buffer structure.
 */
void omx_swvdec::swvdec_empty_buffer_done(SWVDEC_BUFFER *p_buffer_ip)
{
    unsigned long index = (unsigned long) p_buffer_ip->p_client_data;

    async_post_event(OMX_SWVDEC_EVENT_EBD,
                     (unsigned long) &m_buffer_array_ip[index].buffer_header,
                     index);
}

/**
 * @brief Fill buffer done callback.
 *
 * @param[in] p_buffer_op: Pointer to output buffer structure.
 */
void omx_swvdec::swvdec_fill_buffer_done(SWVDEC_BUFFER *p_buffer_op)
{
    unsigned long index = (unsigned long) p_buffer_op->p_client_data;

    OMX_BUFFERHEADERTYPE *p_buffer_hdr;

    if (index < ((unsigned long) m_port_op.def.nBufferCountActual))
    {
        p_buffer_hdr = &m_buffer_array_op[index].buffer_header;

        p_buffer_hdr->nFlags     = p_buffer_op->flags;
        p_buffer_hdr->nTimeStamp = p_buffer_op->timestamp;
        p_buffer_hdr->nFilledLen = ((m_meta_buffer_mode &&
                                     p_buffer_op->filled_length) ?
                                    p_buffer_hdr->nAllocLen :
                                    p_buffer_op->filled_length);
    }

    async_post_event(OMX_SWVDEC_EVENT_FBD,
                     (unsigned long) &m_buffer_array_op[index].buffer_header,
                     index);
}

/**
 * @brief Event handler callback.
 *
 * @param[in] event:  Event.
 * @param[in] p_data: Pointer to event-specific data.
 */
void omx_swvdec::swvdec_event_handler(SWVDEC_EVENT event, void *p_data)
{
    switch (event)
    {

    case SWVDEC_EVENT_FLUSH_ALL_DONE:
    {
        async_post_event(OMX_SWVDEC_EVENT_FLUSH_PORT_IP, 0, 0);
        async_post_event(OMX_SWVDEC_EVENT_FLUSH_PORT_OP, 0, 0);
        break;
    }

    case SWVDEC_EVENT_FLUSH_OP_DONE:
    {
        async_post_event(OMX_SWVDEC_EVENT_FLUSH_PORT_OP, 0, 0);
        break;
    }

    case SWVDEC_EVENT_RELEASE_REFERENCE:
    {
        SWVDEC_BUFFER *p_buffer_op = (SWVDEC_BUFFER *) p_data;

        unsigned long index = (unsigned long) p_buffer_op->p_client_data;

        OMX_SWVDEC_LOG_LOW("release reference: %p", p_buffer_op->p_buffer);

        assert(index < ((unsigned long) m_port_op.def.nBufferCountActual));

        if (m_meta_buffer_mode)
        {
            meta_buffer_ref_remove(
                m_buffer_array_op[index].buffer_payload.pmem_fd,
                m_buffer_array_op[index].buffer_payload.offset);
        }

        break;
    }

    case SWVDEC_EVENT_RECONFIG_REQUIRED:
    {
        async_post_event(OMX_SWVDEC_EVENT_PORT_RECONFIG, 0, 0);
        break;
    }

    case SWVDEC_EVENT_FATAL_ERROR:
    default:
    {
        async_post_event(OMX_SWVDEC_EVENT_ERROR, OMX_ErrorHardware, 0);
        break;
    }

    }
}

/**
 * @brief Translate SwVdec status return value to OMX error type return value.
 *
 * @param[in] retval_swvdec: SwVdec status return value.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::retval_swvdec2omx(SWVDEC_STATUS retval_swvdec)
{
    OMX_ERRORTYPE retval_omx;

    switch (retval_swvdec)
    {

    SWVDEC_STATUS_SUCCESS:
        retval_omx = OMX_ErrorNone;
        break;

    SWVDEC_STATUS_FAILURE:
        retval_omx = OMX_ErrorUndefined;
        break;

    SWVDEC_STATUS_NULL_POINTER:
    SWVDEC_STATUS_INVALID_PARAMETERS:
        retval_omx = OMX_ErrorBadParameter;
        break;

    SWVDEC_STATUS_INVALID_STATE:
        retval_omx = OMX_ErrorInvalidState;
        break;

    SWVDEC_STATUS_INSUFFICIENT_RESOURCES:
        retval_omx = OMX_ErrorInsufficientResources;
        break;

    SWVDEC_STATUS_UNSUPPORTED:
        retval_omx = OMX_ErrorUnsupportedSetting;
        break;

    SWVDEC_STATUS_NOT_IMPLEMENTED:
        retval_omx = OMX_ErrorNotImplemented;
        break;

    default:
        retval_omx = OMX_ErrorUndefined;
        break;

    }

    return retval_omx;
}

/**
 * @brief Create asynchronous thread.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::async_thread_create()
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    pthread_attr_t thread_attributes;

    if (sem_init(&m_async_thread.sem_thread_created, 0, 0))
    {
        OMX_SWVDEC_LOG_ERROR("failed to create async thread created semaphore");
        retval = OMX_ErrorInsufficientResources;
    }
    else if (sem_init(&m_async_thread.sem_event, 0, 0))
    {
        OMX_SWVDEC_LOG_ERROR("failed to create async thread event semaphore");
        retval = OMX_ErrorInsufficientResources;
    }
    else if (pthread_attr_init(&thread_attributes))
    {
        OMX_SWVDEC_LOG_ERROR("failed to create thread attributes object");
        retval = OMX_ErrorInsufficientResources;
    }
    else if (pthread_attr_setdetachstate(&thread_attributes,
                                         PTHREAD_CREATE_JOINABLE))
    {
        OMX_SWVDEC_LOG_ERROR("failed to set detach state attribute");
        retval = OMX_ErrorInsufficientResources;

        pthread_attr_destroy(&thread_attributes);
    }
    else
    {
        m_async_thread.created = false;
        m_async_thread.exit    = false;

        if (pthread_create(&m_async_thread.handle,
                           &thread_attributes,
                           (void *(*)(void *)) async_thread,
                           this))
        {
            OMX_SWVDEC_LOG_ERROR("failed to create async thread");
            retval = OMX_ErrorInsufficientResources;

            pthread_attr_destroy(&thread_attributes);
        }
        else
        {
            if (pthread_setname_np(m_async_thread.handle, "swvdec_async"))
            {
                // don't return error
                OMX_SWVDEC_LOG_ERROR("failed to set async thread name");
            }

            sem_wait(&m_async_thread.sem_thread_created);
            m_async_thread.created = true;
        }
    }

    return retval;
}

/**
 * @brief Destroy asynchronous thread.
 */
void omx_swvdec::async_thread_destroy()
{
    if (m_async_thread.created)
    {
        m_async_thread.exit = true;
        sem_post(&m_async_thread.sem_event);

        pthread_join(m_async_thread.handle, NULL);
        m_async_thread.created = false;
    }

    m_async_thread.exit = false;

    sem_destroy(&m_async_thread.sem_event);
    sem_destroy(&m_async_thread.sem_thread_created);
}

/**
 * @brief Post event to appropriate queue.
 *
 * @param[in] event_id:     Event ID.
 * @param[in] event_param1: Event parameter 1.
 * @param[in] event_param2: Event parameter 2.
 *
 * @retval  true if post event successful
 * @retval false if post event unsuccessful
 */
bool omx_swvdec::async_post_event(unsigned long event_id,
                                  unsigned long event_param1,
                                  unsigned long event_param2)
{
    OMX_SWVDEC_EVENT_INFO event_info;

    bool retval = true;

    event_info.event_id     = event_id;
    event_info.event_param1 = event_param1;
    event_info.event_param2 = event_param2;

    switch (event_id)
    {

    case OMX_SWVDEC_EVENT_ETB:
    case OMX_SWVDEC_EVENT_EBD:
    {
        retval = m_queue_port_ip.push(&event_info);
        break;
    }

    case OMX_SWVDEC_EVENT_FTB:
    case OMX_SWVDEC_EVENT_FBD:
    {
        retval = m_queue_port_op.push(&event_info);
        break;
    }

    default:
    {
        retval = m_queue_command.push(&event_info);
        break;
    }

    }

    if (retval == true)
    {
        sem_post(&m_async_thread.sem_event);
    }

    return retval;
}

/**
 * @brief Asynchronous thread.
 *
 * @param[in] p_cmp: Pointer to OMX SwVdec component class.
 */
void omx_swvdec::async_thread(void *p_cmp)
{
    if (p_cmp == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_cmp = NULL");
    }
    else
    {
        omx_swvdec *p_omx_swvdec = (omx_swvdec *) p_cmp;

        ASYNC_THREAD *p_async_thread = &p_omx_swvdec->m_async_thread;

        OMX_SWVDEC_LOG_HIGH("created");

        sem_post(&p_async_thread->sem_thread_created);

        while (p_async_thread->exit == false)
        {
            sem_wait(&p_async_thread->sem_event);

            if (p_async_thread->exit == true)
            {
                break;
            }

            p_omx_swvdec->async_process_event(p_cmp);
        }
    }

    OMX_SWVDEC_LOG_HIGH("exiting");
}

/**
 * @brief Process event.
 *
 * @param[in] p_cmp: Pointer to OMX SwVdec component class.
 */
void omx_swvdec::async_process_event(void *p_cmp)
{
    omx_swvdec *p_omx_swvdec;

    OMX_SWVDEC_EVENT_INFO event_info;

    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (p_cmp == NULL)
    {
        OMX_SWVDEC_LOG_ERROR("p_cmp = NULL");
        goto async_process_event_exit;
    }

    p_omx_swvdec = (omx_swvdec *) p_cmp;

    // NOTE: queues popped in order of priority; do not change!

    if ((p_omx_swvdec->m_queue_command.pop(&event_info) == false) &&
        (p_omx_swvdec->m_queue_port_op.pop(&event_info) == false) &&
        (p_omx_swvdec->m_queue_port_ip.pop(&event_info) == false))
    {
        OMX_SWVDEC_LOG_LOW("no event popped");
        goto async_process_event_exit;
    }

    switch (event_info.event_id)
    {

    case OMX_SWVDEC_EVENT_CMD:
    {
        OMX_COMMANDTYPE cmd   = (OMX_COMMANDTYPE) event_info.event_param1;
        OMX_U32         param = (OMX_U32)         event_info.event_param2;

        retval = p_omx_swvdec->async_process_event_cmd(cmd, param);
        break;
    }

    case OMX_SWVDEC_EVENT_CMD_ACK:
    {
        OMX_COMMANDTYPE cmd   = (OMX_COMMANDTYPE) event_info.event_param1;
        OMX_U32         param = (OMX_U32)         event_info.event_param2;

        retval = p_omx_swvdec->async_process_event_cmd_ack(cmd, param);
        break;
    }

    case OMX_SWVDEC_EVENT_ERROR:
    {
        OMX_ERRORTYPE error_code = (OMX_ERRORTYPE) event_info.event_param1;

        retval = p_omx_swvdec->async_process_event_error(error_code);
        break;
    }

    case OMX_SWVDEC_EVENT_ETB:
    {
        OMX_BUFFERHEADERTYPE *p_buffer_hdr =
            (OMX_BUFFERHEADERTYPE *) event_info.event_param1;

        unsigned int index = event_info.event_param2;

        retval = p_omx_swvdec->async_process_event_etb(p_buffer_hdr, index);
        break;
    }

    case OMX_SWVDEC_EVENT_FTB:
    {
        OMX_BUFFERHEADERTYPE *p_buffer_hdr =
            (OMX_BUFFERHEADERTYPE *) event_info.event_param1;

        unsigned int index = event_info.event_param2;

        retval = p_omx_swvdec->async_process_event_ftb(p_buffer_hdr, index);
        break;
    }

    case OMX_SWVDEC_EVENT_EBD:
    {
        OMX_BUFFERHEADERTYPE *p_buffer_hdr =
            (OMX_BUFFERHEADERTYPE *) event_info.event_param1;

        unsigned int index = event_info.event_param2;

        retval = p_omx_swvdec->async_process_event_ebd(p_buffer_hdr, index);
        break;
    }

    case OMX_SWVDEC_EVENT_FBD:
    {
        OMX_BUFFERHEADERTYPE *p_buffer_hdr =
            (OMX_BUFFERHEADERTYPE *) event_info.event_param1;

        unsigned int index = event_info.event_param2;

        retval = p_omx_swvdec->async_process_event_fbd(p_buffer_hdr, index);
        break;
    }

    case OMX_SWVDEC_EVENT_EOS:
    {
        retval = p_omx_swvdec->async_process_event_eos();
        break;
    }

    case OMX_SWVDEC_EVENT_FLUSH_PORT_IP:
    {
        retval = p_omx_swvdec->async_process_event_flush_port_ip();
        break;
    }

    case OMX_SWVDEC_EVENT_FLUSH_PORT_OP:
    {
        retval = p_omx_swvdec->async_process_event_flush_port_op();
        break;
    }

    case OMX_SWVDEC_EVENT_PORT_RECONFIG:
    {
        retval = p_omx_swvdec->async_process_event_port_reconfig();
        break;
    }

    default:
    {
        assert(0);
        retval = OMX_ErrorUndefined;
        break;
    }

    }

    if (retval != OMX_ErrorNone)
    {
        p_omx_swvdec->async_post_event(OMX_SWVDEC_EVENT_ERROR, retval, 0);
    }

async_process_event_exit:
    return;
}

/**
 * @brief Process command event.
 *
 * @param[in] cmd:   Command.
 * @param[in] param: Command parameter.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::async_process_event_cmd(OMX_COMMANDTYPE cmd,
                                                  OMX_U32         param)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    bool cmd_ack = false; // set to 'true' if command is to be acknowledged

    SWVDEC_STATUS retval_swvdec;

    switch (cmd)
    {

    case OMX_CommandStateSet:
    {
        retval = async_process_event_cmd_state_set(&cmd_ack,
                                                   (OMX_STATETYPE) param);
        break;
    }

    case OMX_CommandFlush:
    {
        retval = async_process_event_cmd_flush((unsigned int) param);
        break;
    }

    case OMX_CommandPortDisable:
    {
        retval = async_process_event_cmd_port_disable(&cmd_ack,
                                                      (unsigned int) param);
        break;
    }

    case OMX_CommandPortEnable:
    {
        retval = async_process_event_cmd_port_enable(&cmd_ack,
                                                     (unsigned int) param);
        break;
    }

    default:
    {
        OMX_SWVDEC_LOG_ERROR("cmd '%d' invalid", (int) cmd);

        retval = OMX_ErrorBadParameter;
        break;
    }

    } // switch (cmd)

    // post appropriate event

    if (retval != OMX_ErrorNone)
    {
        async_post_event(OMX_SWVDEC_EVENT_ERROR, retval, 0);
    }
    else if (cmd_ack)
    {
        async_post_event(OMX_SWVDEC_EVENT_CMD_ACK, cmd, param);
    }

    // post to command semaphore
    sem_post(&m_sem_cmd);

    return retval;
}

/**
 * @brief Process command acknowledgement event.
 *
 * @param[in] cmd:   Command.
 * @param[in] param: Command parameter.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::async_process_event_cmd_ack(OMX_COMMANDTYPE cmd,
                                                      OMX_U32         param)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    switch (cmd)
    {

    case OMX_CommandStateSet:
    {
        m_state = (OMX_STATETYPE) param;

        OMX_SWVDEC_LOG_CALLBACK("EventHandler(): OMX_EventCmdComplete, "
                                "OMX_CommandStateSet, %s",
                                OMX_STATETYPE_STRING(m_state));

        m_callback.EventHandler(&m_cmp,
                                m_app_data,
                                OMX_EventCmdComplete,
                                OMX_CommandStateSet,
                                (OMX_U32) m_state,
                                NULL);
        break;
    }

    case OMX_CommandFlush:
    case OMX_CommandPortEnable:
    case OMX_CommandPortDisable:
    {
        if ((cmd == OMX_CommandPortEnable) && m_port_reconfig_inprogress)
        {
            m_port_reconfig_inprogress = false;
        }

        OMX_SWVDEC_LOG_CALLBACK("EventHandler(): OMX_EventCmdComplete, "
                                "%s, port index %d",
                                OMX_COMMANDTYPE_STRING(cmd),
                                param);

        m_callback.EventHandler(&m_cmp,
                                m_app_data,
                                OMX_EventCmdComplete,
                                cmd,
                                param,
                                NULL);
        break;
    }

    default:
    {
        OMX_SWVDEC_LOG_ERROR("cmd '%d' invalid", (int) cmd);
        retval = OMX_ErrorBadParameter;
        break;
    }

    } // switch (cmd)

    return retval;
}

/**
 * @brief Process error event.
 *
 * @param[in] error_code: Error code.
 *
 * @retval OMX_ErrorNone
 */
OMX_ERRORTYPE omx_swvdec::async_process_event_error(OMX_ERRORTYPE error_code)
{
    if (error_code == OMX_ErrorInvalidState)
    {
        m_state = OMX_StateInvalid;
    }

    OMX_SWVDEC_LOG_CALLBACK("EventHandler(): OMX_EventError, 0x%08x",
                            error_code);

    m_callback.EventHandler(&m_cmp,
                            m_app_data,
                            OMX_EventError,
                            (OMX_U32) error_code,
                            0,
                            NULL);

    return OMX_ErrorNone;
}

/**
 * @brief Process OMX_CommandStateSet.
 *
 * @param[in,out] p_cmd_ack: Pointer to 'command acknowledge' boolean variable.
 * @param[in]     state_new: New state to which transition is requested.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::async_process_event_cmd_state_set(
    bool         *p_cmd_ack,
    OMX_STATETYPE state_new)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    SWVDEC_STATUS retval_swvdec;

    OMX_SWVDEC_LOG_HIGH("'%s-to-%s' requested",
                        OMX_STATETYPE_STRING(m_state),
                        OMX_STATETYPE_STRING(state_new));

    /**
     * Only the following state transitions are allowed via CommandStateSet:
     *
     * LOADED -> IDLE -> EXECUTING
     * LOADED <- IDLE <- EXECUTING
     */

    if (m_state == OMX_StateInvalid)
    {
        OMX_SWVDEC_LOG_ERROR("in state %s", OMX_STATETYPE_STRING(m_state));
        retval = OMX_ErrorInvalidState;
    }
    else if (state_new == OMX_StateInvalid)
    {
        OMX_SWVDEC_LOG_ERROR("requested transition to state %s",
                             OMX_STATETYPE_STRING(state_new));
        retval = OMX_ErrorInvalidState;
    }
    else if ((m_state   == OMX_StateLoaded) &&
             (state_new == OMX_StateIdle))
    {
        if ((m_port_ip.populated == OMX_TRUE) &&
            (m_port_op.populated == OMX_TRUE))
        {
            // start SwVdec
            if ((retval_swvdec = swvdec_start(m_swvdec_handle)) ==
                SWVDEC_STATUS_SUCCESS)
            {
                *p_cmd_ack = true;
            }
            else
            {
                OMX_SWVDEC_LOG_ERROR("failed to start SwVdec");
                retval = retval_swvdec2omx(retval_swvdec);
            }
        }
        else
        {
            m_status_flags |= (1 << PENDING_STATE_LOADED_TO_IDLE);

            OMX_SWVDEC_LOG_LOW("'loaded-to-idle' pending");
        }
    }
    else if ((m_state   == OMX_StateIdle) &&
             (state_new == OMX_StateExecuting))
    {
        *p_cmd_ack = true;
    }
    else if ((m_state   == OMX_StateExecuting) &&
             (state_new == OMX_StateIdle))
    {
        m_status_flags |= (1 << PENDING_STATE_EXECUTING_TO_IDLE);

        OMX_SWVDEC_LOG_LOW("'executing-to-idle' pending");

        retval = flush(OMX_ALL);
    }
    else if ((m_state   == OMX_StateIdle) &&
             (state_new == OMX_StateLoaded))
    {
        if ((m_port_ip.unpopulated == OMX_TRUE) &&
            (m_port_op.unpopulated == OMX_TRUE))
        {
            // stop SwVdec
            if ((retval_swvdec = swvdec_stop(m_swvdec_handle)) ==
                SWVDEC_STATUS_SUCCESS)
            {
                *p_cmd_ack = true;
            }
            else
            {
                OMX_SWVDEC_LOG_ERROR("failed to stop SwVdec");
                retval = retval_swvdec2omx(retval_swvdec);
            }
        }
        else
        {
            m_status_flags |= (1 << PENDING_STATE_IDLE_TO_LOADED);

            OMX_SWVDEC_LOG_LOW("'idle-to-loaded' pending");
        }
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("state transition '%s -> %s' illegal",
                             OMX_STATETYPE_STRING(m_state),
                             OMX_STATETYPE_STRING(state_new));

        retval = ((state_new == m_state) ?
                  OMX_ErrorSameState :
                  OMX_ErrorIncorrectStateTransition);
    }

    return retval;
}

/**
 * @brief Process OMX_CommandFlush.
 *
 * @param[in] port_index: Index of port to flush.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::async_process_event_cmd_flush(unsigned int port_index)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    OMX_SWVDEC_LOG_HIGH("flush port %d requested", port_index);

    if (port_index == OMX_CORE_PORT_INDEX_IP)
    {
        m_status_flags |= (1 << PENDING_PORT_FLUSH_IP);

        OMX_SWVDEC_LOG_LOW("ip port flush pending");
    }
    else if (port_index == OMX_CORE_PORT_INDEX_OP)
    {
        m_status_flags |= (1 << PENDING_PORT_FLUSH_OP);

        OMX_SWVDEC_LOG_LOW("op port flush pending");
    }
    else if (port_index == OMX_ALL)
    {
        m_status_flags |= (1 << PENDING_PORT_FLUSH_IP);
        m_status_flags |= (1 << PENDING_PORT_FLUSH_OP);

        OMX_SWVDEC_LOG_LOW("ip & op ports flush pending");
    }

    retval = flush(port_index);

    return retval;
}

/**
 * @brief Process OMX_CommandPortDisable.
 *
 * @param[in,out] p_cmd_ack:  Pointer to 'command acknowledge' boolean variable.
 * @param[in]     port_index: Index of port to disable.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::async_process_event_cmd_port_disable(
    bool         *p_cmd_ack,
    unsigned int  port_index)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    OMX_SWVDEC_LOG_HIGH("disable port %d requested", port_index);

    if (port_index == OMX_CORE_PORT_INDEX_IP)
    {
        if (m_port_ip.enabled == OMX_FALSE)
        {
            OMX_SWVDEC_LOG_ERROR("ip port already disabled");
            retval = OMX_ErrorBadPortIndex;
        }
        else
        {
            m_port_ip.enabled = OMX_FALSE;

            if (m_port_ip.unpopulated)
            {
                *p_cmd_ack = true;
            }
            else
            {
                m_status_flags |= (1 << PENDING_PORT_DISABLE_IP);

                OMX_SWVDEC_LOG_LOW("ip port disable pending");

                if (m_port_ip.num_pending_buffers)
                {
                    retval = flush(port_index);
                }
            }
        }
    }
    else if (port_index == OMX_CORE_PORT_INDEX_OP)
    {
        if (m_port_op.enabled == OMX_FALSE)
        {
            OMX_SWVDEC_LOG_ERROR("op port already disabled");
            retval = OMX_ErrorBadPortIndex;
        }
        else
        {
            m_port_op.enabled = OMX_FALSE;

            if (m_port_op.unpopulated)
            {
                *p_cmd_ack = true;
            }
            else
            {
                m_status_flags |= (1 << PENDING_PORT_DISABLE_OP);

                OMX_SWVDEC_LOG_LOW("op port disable pending");

                if (m_port_op.num_pending_buffers)
                {
                    retval = flush(port_index);
                }
            }
        }
    }
    else if (port_index == OMX_ALL)
    {
        if (m_port_ip.enabled == OMX_FALSE)
        {
            OMX_SWVDEC_LOG_ERROR("ip port already disabled");
            retval = OMX_ErrorBadPortIndex;
        }
        else if (m_port_op.enabled == OMX_FALSE)
        {
            OMX_SWVDEC_LOG_ERROR("op port already disabled");
            retval = OMX_ErrorBadPortIndex;
        }
        else
        {
            if (m_port_ip.unpopulated && m_port_op.unpopulated)
            {
                *p_cmd_ack = true;
            }
            else
            {
                m_port_ip.enabled = OMX_FALSE;
                m_port_op.enabled = OMX_FALSE;

                if (m_port_ip.unpopulated == OMX_FALSE)
                {
                    m_status_flags |= (1 << PENDING_PORT_DISABLE_IP);

                    OMX_SWVDEC_LOG_LOW("ip port disable pending");

                    if (m_port_ip.num_pending_buffers)
                    {
                        retval = flush(port_index);
                    }
                }

                if ((retval == OMX_ErrorNone) &&
                    (m_port_op.unpopulated == OMX_FALSE))
                {
                    m_status_flags |= (1 << PENDING_PORT_DISABLE_OP);

                    OMX_SWVDEC_LOG_LOW("op port disable pending");

                    if (m_port_op.num_pending_buffers)
                    {
                        retval = flush(port_index);
                    }
                }
            }
        }
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("port index '%d' invalid");
        retval = OMX_ErrorBadPortIndex;
    }

    return retval;
}

/**
 * @brief Process OMX_CommandPortEnable.
 *
 * @param[in,out] p_cmd_ack:  Pointer to 'command acknowledge' boolean variable.
 * @param[in]     port_index: Index of port to enable.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::async_process_event_cmd_port_enable(
    bool         *p_cmd_ack,
    unsigned int  port_index)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    OMX_SWVDEC_LOG_HIGH("enable port %d requested", port_index);

    if (port_index == OMX_CORE_PORT_INDEX_IP)
    {
        if (m_port_ip.enabled)
        {
            OMX_SWVDEC_LOG_ERROR("ip port already enabled");
            retval = OMX_ErrorBadPortIndex;
        }
        else
        {
            m_port_ip.enabled = OMX_TRUE;

            if (m_port_ip.populated)
            {
                *p_cmd_ack = true;
            }
            else
            {
                m_status_flags |= (1 << PENDING_PORT_ENABLE_IP);

                OMX_SWVDEC_LOG_LOW("ip port enable pending");
            }
        }
    }
    else if (port_index == OMX_CORE_PORT_INDEX_OP)
    {
        if (m_port_op.enabled)
        {
            OMX_SWVDEC_LOG_ERROR("op port already enabled");
            retval = OMX_ErrorBadPortIndex;
        }
        else
        {
            m_port_op.enabled = OMX_TRUE;

            if (m_port_op.populated)
            {
                *p_cmd_ack = true;
            }
            else
            {
                m_status_flags |= (1 << PENDING_PORT_ENABLE_OP);

                OMX_SWVDEC_LOG_LOW("op port enable pending");
            }
        }
    }
    else if (port_index == OMX_ALL)
    {
        if (m_port_ip.enabled)
        {
            OMX_SWVDEC_LOG_ERROR("ip port already enabled");
            retval = OMX_ErrorBadPortIndex;
        }
        else if (m_port_op.enabled)
        {
            OMX_SWVDEC_LOG_ERROR("op port already enabled");
            retval = OMX_ErrorBadPortIndex;
        }
        else
        {
            m_port_ip.enabled = OMX_TRUE;
            m_port_op.enabled = OMX_TRUE;

            if (m_port_ip.populated && m_port_op.populated)
            {
                *p_cmd_ack = true;
            }
            else if (m_port_ip.populated == false)
            {
                m_status_flags |= (1 << PENDING_PORT_ENABLE_IP);

                OMX_SWVDEC_LOG_LOW("ip port enable pending");
            }
            else if (m_port_op.populated == false)
            {
                m_status_flags |= (1 << PENDING_PORT_ENABLE_OP);

                OMX_SWVDEC_LOG_LOW("op port enable pending");
            }
        }
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("port index '%d' invalid");
        retval = OMX_ErrorBadPortIndex;
    }

    return retval;
}

/**
 * @brief Process ETB event.
 *
 * @param[in] p_buffer_hdr: Pointer to buffer header.
 * @param[in] index:        Index of buffer in input buffer info array.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::async_process_event_etb(
    OMX_BUFFERHEADERTYPE *p_buffer_hdr,
    unsigned int          index)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    m_port_ip.num_pending_buffers++;

    if ((p_buffer_hdr->nFilledLen == 0) &&
        ((p_buffer_hdr->nFlags & OMX_BUFFERFLAG_EOS) == 0))
    {
        OMX_SWVDEC_LOG_HIGH("returning %p, buffer %p; "
                            "zero length & no EOS flag",
                            p_buffer_hdr,
                            p_buffer_hdr->pBuffer);

        async_post_event(OMX_SWVDEC_EVENT_EBD,
                         (unsigned long) p_buffer_hdr,
                         (unsigned long) index);
    }
    else if (m_port_ip.flush_inprogress)
    {
        OMX_SWVDEC_LOG_HIGH("returning %p, buffer %p; "
                            "ip port flush in progress",
                            p_buffer_hdr,
                            p_buffer_hdr->pBuffer);

        async_post_event(OMX_SWVDEC_EVENT_EBD,
                         (unsigned long) p_buffer_hdr,
                         (unsigned long) index);
    }
    else
    {
        SWVDEC_STATUS retval_swvdec;

        SWVDEC_BUFFER *p_buffer_swvdec =
            &(m_buffer_array_ip[index].buffer_swvdec);

        if (p_buffer_hdr->nFilledLen &&
            ((p_buffer_hdr->nFlags & OMX_BUFFERFLAG_CODECCONFIG) == 0))
        {
            m_ts_list.push(p_buffer_hdr->nTimeStamp);
        }

        assert(p_buffer_swvdec->p_buffer == p_buffer_hdr->pBuffer);

        p_buffer_swvdec->flags         = p_buffer_hdr->nFlags;
        p_buffer_swvdec->timestamp     = p_buffer_hdr->nTimeStamp;
        p_buffer_swvdec->filled_length = p_buffer_hdr->nFilledLen;

        m_diag.dump_ip(p_buffer_swvdec->p_buffer,
                       p_buffer_swvdec->filled_length);

        retval_swvdec = swvdec_emptythisbuffer(m_swvdec_handle,
                                               p_buffer_swvdec);

        if (retval_swvdec != SWVDEC_STATUS_SUCCESS)
        {
            retval = retval_swvdec2omx(retval_swvdec);
        }
    }

    return retval;
}

/**
 * @brief Process FTB event.
 *
 * @param[in] p_buffer_hdr: Pointer to buffer header.
 * @param[in] index:        Index of buffer in output buffer info array.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::async_process_event_ftb(
    OMX_BUFFERHEADERTYPE *p_buffer_hdr,
    unsigned int          index)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    m_port_op.num_pending_buffers++;

    if (m_port_op.flush_inprogress)
    {
        OMX_SWVDEC_LOG_HIGH("returning %p, buffer %p; "
                            "op port flush in progress",
                            p_buffer_hdr,
                            m_buffer_array_op[index].buffer_swvdec.p_buffer);

        async_post_event(OMX_SWVDEC_EVENT_FBD,
                         (unsigned long) p_buffer_hdr,
                         (unsigned long) index);
    }
    else
    {
        SWVDEC_STATUS retval_swvdec;

        SWVDEC_BUFFER *p_buffer_swvdec =
            &(m_buffer_array_op[index].buffer_swvdec);

        retval_swvdec = swvdec_fillthisbuffer(m_swvdec_handle, p_buffer_swvdec);

        if (retval_swvdec != SWVDEC_STATUS_SUCCESS)
        {
            retval = retval_swvdec2omx(retval_swvdec);
        }
    }

    return retval;
}

/**
 * @brief Process EBD event.
 *
 * @param[in] p_buffer_hdr: Pointer to buffer header.
 * @param[in] index:        Index of buffer in output buffer info array.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::async_process_event_ebd(
    OMX_BUFFERHEADERTYPE *p_buffer_hdr,
    unsigned int          index)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (index < m_port_ip.def.nBufferCountActual)
    {
        m_port_ip.num_pending_buffers--;

        // should ideally be set in swvdec_empty_buffer_done()
        p_buffer_hdr->nFilledLen = 0;

        OMX_SWVDEC_LOG_CALLBACK(
            "EmptyBufferDone(): %p, buffer %p",
            p_buffer_hdr,
            m_buffer_array_ip[index].buffer_swvdec.p_buffer);

        m_callback.EmptyBufferDone(&m_cmp, m_app_data, p_buffer_hdr);
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("buffer index '%d' invalid", index);
        retval = OMX_ErrorBadParameter;
    }

    return retval;
}

/**
 * @brief Process FBD event.
 *
 * @param[in] p_buffer_hdr: Pointer to buffer header.
 * @param[in] index:        Index of buffer in output buffer info array.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::async_process_event_fbd(
    OMX_BUFFERHEADERTYPE *p_buffer_hdr,
    unsigned int          index)
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (index < m_port_op.def.nBufferCountActual)
    {
        OMX_U8 *p_buffer;

        p_buffer = m_buffer_array_op[index].buffer_swvdec.p_buffer;

        m_port_op.num_pending_buffers--;

        if (m_port_op.flush_inprogress)
        {
            p_buffer_hdr->nFilledLen = 0;
            p_buffer_hdr->nTimeStamp = 0;
            p_buffer_hdr->nFlags    &= ~OMX_BUFFERFLAG_DATACORRUPT;
        }

        if (p_buffer_hdr->nFilledLen)
        {
            if (m_sync_frame_decoding_mode)
            {
                p_buffer_hdr->nTimeStamp = 0;
            }
            else
            {
                if (m_ts_list.pop(&p_buffer_hdr->nTimeStamp) == false)
                {
                    OMX_SWVDEC_LOG_ERROR("failed to pop timestamp from list");
                }
            }

            m_diag.dump_op(p_buffer,
                           m_frame_dimensions.width,
                           m_frame_dimensions.height,
                           m_frame_attributes.stride,
                           m_frame_attributes.scanlines);
        }

        if (p_buffer_hdr->nFlags & OMX_BUFFERFLAG_EOS)
        {
            async_post_event(OMX_SWVDEC_EVENT_EOS, 0, 0);

            m_ts_list.reset();
        }

        if (m_meta_buffer_mode &&
            ((p_buffer_hdr->nFlags & OMX_BUFFERFLAG_READONLY)) == 0)
        {
            meta_buffer_ref_remove(
                m_buffer_array_op[index].buffer_payload.pmem_fd,
                m_buffer_array_op[index].buffer_payload.offset);
        }

        OMX_SWVDEC_LOG_CALLBACK(
            "FillBufferDone(): %p, buffer %p, "
            "flags 0x%08x, timestamp %lld",
            p_buffer_hdr,
            p_buffer,
            p_buffer_hdr->nFlags,
            p_buffer_hdr->nTimeStamp);

        m_callback.FillBufferDone(&m_cmp, m_app_data, p_buffer_hdr);
    }
    else
    {
        OMX_SWVDEC_LOG_ERROR("buffer index '%d' invalid", index);
        retval = OMX_ErrorBadParameter;
    }

async_process_event_fbd_exit:
    return retval;
}

/**
 * @brief Process EOS event.
 *
 * @retval OMX_ErrorNone
 */
OMX_ERRORTYPE omx_swvdec::async_process_event_eos()
{
    OMX_SWVDEC_LOG_CALLBACK("EventHandler(): "
                            "OMX_EventBufferFlag, port %d, EOS",
                            OMX_CORE_PORT_INDEX_OP);

    m_callback.EventHandler(&m_cmp,
                            m_app_data,
                            OMX_EventBufferFlag,
                            OMX_CORE_PORT_INDEX_OP,
                            OMX_BUFFERFLAG_EOS,
                            NULL);

    return OMX_ErrorNone;
}

/**
 * @brief Process input port flush event.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::async_process_event_flush_port_ip()
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    OMX_SWVDEC_EVENT_INFO event_info;

    OMX_BUFFERHEADERTYPE *p_buffer_hdr;

    unsigned int index;

    while (m_queue_port_ip.pop(&event_info))
    {
        switch (event_info.event_id)
        {

        case OMX_SWVDEC_EVENT_ETB:
        {
            p_buffer_hdr = (OMX_BUFFERHEADERTYPE *) event_info.event_param1;
            index = event_info.event_param2;

            // compensate decrement in async_process_event_ebd()
            m_port_ip.num_pending_buffers++;

            retval = async_process_event_ebd(p_buffer_hdr, index);
            break;
        }

        case OMX_SWVDEC_EVENT_EBD:
        {
            p_buffer_hdr = (OMX_BUFFERHEADERTYPE *) event_info.event_param1;
            index = event_info.event_param2;

            retval = async_process_event_ebd(p_buffer_hdr, index);
            break;
        }

        default:
        {
            assert(0);
            break;
        }

        }
    }

    assert(m_port_ip.num_pending_buffers == 0);

    if ((retval == OMX_ErrorNone) &&
        (m_status_flags & (1 << PENDING_PORT_FLUSH_IP)))
    {
        m_status_flags &= ~(1 << PENDING_PORT_FLUSH_IP);

        async_post_event(OMX_SWVDEC_EVENT_CMD_ACK,
                         OMX_CommandFlush,
                         OMX_CORE_PORT_INDEX_IP);
    }

    m_port_ip.flush_inprogress = OMX_FALSE;

    return retval;
}

/**
 * @brief Process output port flush event.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::async_process_event_flush_port_op()
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    OMX_SWVDEC_EVENT_INFO event_info;

    OMX_BUFFERHEADERTYPE *p_buffer_hdr;

    unsigned int index;

    while (m_queue_port_op.pop(&event_info))
    {
        switch (event_info.event_id)
        {

        case OMX_SWVDEC_EVENT_FTB:
        {
            p_buffer_hdr = (OMX_BUFFERHEADERTYPE *) event_info.event_param1;
            index = event_info.event_param2;

            // compensate decrement in async_process_event_fbd()
            m_port_op.num_pending_buffers++;

            retval = async_process_event_fbd(p_buffer_hdr, index);
            break;
        }

        case OMX_SWVDEC_EVENT_FBD:
        {
            p_buffer_hdr = (OMX_BUFFERHEADERTYPE *) event_info.event_param1;
            index = event_info.event_param2;

            retval = async_process_event_fbd(p_buffer_hdr, index);
            break;
        }

        default:
        {
            assert(0);
            break;
        }

        }
    }

    assert(m_port_op.num_pending_buffers == 0);

    if ((retval == OMX_ErrorNone) &&
        (m_status_flags & (1 << PENDING_PORT_FLUSH_OP)))
    {
        m_status_flags &= ~(1 << PENDING_PORT_FLUSH_OP);

        async_post_event(OMX_SWVDEC_EVENT_CMD_ACK,
                         OMX_CommandFlush,
                         OMX_CORE_PORT_INDEX_OP);
    }

    if ((retval == OMX_ErrorNone) &&
        (m_status_flags & (1 << PENDING_STATE_EXECUTING_TO_IDLE)))
    {
        m_status_flags &= ~(1 << PENDING_STATE_EXECUTING_TO_IDLE);

        async_post_event(OMX_SWVDEC_EVENT_CMD_ACK,
                         OMX_CommandStateSet,
                         OMX_StateIdle);
    }

    if (m_port_reconfig_inprogress == false)
    {
        m_ts_list.reset();
    }

    m_port_op.flush_inprogress = OMX_FALSE;

    return retval;
}

/**
 * @brief Process port reconfiguration event.
 *
 * @retval OMX_ERRORTYPE
 */
OMX_ERRORTYPE omx_swvdec::async_process_event_port_reconfig()
{
    OMX_ERRORTYPE retval = OMX_ErrorNone;

    if (m_port_reconfig_inprogress)
    {
        OMX_SWVDEC_LOG_ERROR("port reconfiguration in progress");
        retval = OMX_ErrorIncorrectStateOperation;
    }
    else
    {
        m_port_reconfig_inprogress = true;

        OMX_SWVDEC_LOG_CALLBACK("EventHandler(): "
                                "OMX_EventPortSettingsChanged, port %d",
                                OMX_CORE_PORT_INDEX_OP);

        m_callback.EventHandler(&m_cmp,
                                m_app_data,
                                OMX_EventPortSettingsChanged,
                                OMX_CORE_PORT_INDEX_OP,
                                0,
                                NULL);
    }

    return retval;
}