/* Copyright (c) 2015-2016, 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.
 *
 */

#define LOG_TAG "QCameraMuxer"

// System dependencies
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <utils/Errors.h>
#define STAT_H <SYSTEM_HEADER_PREFIX/stat.h>
#include STAT_H

// Camera dependencies
#include "QCameraMuxer.h"
#include "QCamera2HWI.h"

extern "C" {
#include "mm_camera_dbg.h"
}

/* Muxer implementation */
using namespace android;
namespace qcamera {

QCameraMuxer *gMuxer = NULL;

//Error Check Macros
#define CHECK_MUXER() \
    if (!gMuxer) { \
        LOGE("Error getting muxer "); \
        return; \
    } \

#define CHECK_MUXER_ERROR() \
    if (!gMuxer) { \
        LOGE("Error getting muxer "); \
        return -ENODEV; \
    } \

#define CHECK_CAMERA(pCam) \
    if (!pCam) { \
        LOGE("Error getting physical camera"); \
        return; \
    } \

#define CHECK_CAMERA_ERROR(pCam) \
    if (!pCam) { \
        LOGE("Error getting physical camera"); \
        return -ENODEV; \
    } \

#define CHECK_HWI(hwi) \
    if (!hwi) { \
        LOGE("Error !! HWI not found!!"); \
        return; \
    } \

#define CHECK_HWI_ERROR(hwi) \
    if (!hwi) { \
        LOGE("Error !! HWI not found!!"); \
        return -ENODEV; \
    } \


/*===========================================================================
 * FUNCTION         : getCameraMuxer
 *
 * DESCRIPTION     : Creates Camera Muxer if not created
 *
 * PARAMETERS:
 *   @pMuxer               : Pointer to retrieve Camera Muxer
 *   @num_of_cameras  : Number of Physical Cameras on device
 *
 * RETURN             :  NONE
 *==========================================================================*/
void QCameraMuxer::getCameraMuxer(
        QCameraMuxer** pMuxer, uint32_t num_of_cameras)
{
    *pMuxer = NULL;
    if (!gMuxer) {
        gMuxer = new QCameraMuxer(num_of_cameras);
    }
    CHECK_MUXER();
    *pMuxer = gMuxer;
    LOGH("gMuxer: %p ", gMuxer);
    return;
}

/*===========================================================================
 * FUNCTION         : QCameraMuxer
 *
 * DESCRIPTION     : QCameraMuxer Constructor
 *
 * PARAMETERS:
 *   @num_of_cameras  : Number of Physical Cameras on device
 *
 *==========================================================================*/
QCameraMuxer::QCameraMuxer(uint32_t num_of_cameras)
    : mJpegClientHandle(0),
      m_pPhyCamera(NULL),
      m_pLogicalCamera(NULL),
      m_pCallbacks(NULL),
      m_bAuxCameraExposed(FALSE),
      m_nPhyCameras(num_of_cameras),
      m_nLogicalCameras(0),
      m_MainJpegQ(releaseJpegInfo, this),
      m_AuxJpegQ(releaseJpegInfo, this),
      m_pRelCamMpoJpeg(NULL),
      m_pMpoCallbackCookie(NULL),
      m_pJpegCallbackCookie(NULL),
      m_bDumpImages(FALSE),
      m_bMpoEnabled(TRUE),
      m_bFrameSyncEnabled(FALSE),
      m_bRecordingHintInternallySet(FALSE)
{
    setupLogicalCameras();
    memset(&mJpegOps, 0, sizeof(mJpegOps));
    memset(&mJpegMpoOps, 0, sizeof(mJpegMpoOps));
    memset(&mGetMemoryCb, 0, sizeof(mGetMemoryCb));
    memset(&mDataCb, 0, sizeof(mDataCb));

    // initialize mutex for MPO composition
    pthread_mutex_init(&m_JpegLock, NULL);
    // launch MPO composition thread
    m_ComposeMpoTh.launch(composeMpoRoutine, this);

    //Check whether dual camera images need to be dumped
    char prop[PROPERTY_VALUE_MAX];
    property_get("persist.camera.dual.camera.dump", prop, "0");
    m_bDumpImages = atoi(prop);
    LOGH("dualCamera dump images:%d ", m_bDumpImages);
}

/*===========================================================================
 * FUNCTION         : ~QCameraMuxer
 *
 * DESCRIPTION     : QCameraMuxer Desctructor
 *
 *==========================================================================*/
QCameraMuxer::~QCameraMuxer() {
    if (m_pLogicalCamera) {
        delete [] m_pLogicalCamera;
        m_pLogicalCamera = NULL;
    }
    if (m_pPhyCamera) {
        delete [] m_pPhyCamera;
        m_pPhyCamera = NULL;
    }

    if (NULL != m_pRelCamMpoJpeg) {
        m_pRelCamMpoJpeg->release(m_pRelCamMpoJpeg);
        m_pRelCamMpoJpeg = NULL;
    }
    // flush Jpeg Queues
    m_MainJpegQ.flush();
    m_AuxJpegQ.flush();

    // stop and exit MPO composition thread
    m_ComposeMpoTh.sendCmd(CAMERA_CMD_TYPE_STOP_DATA_PROC, TRUE, FALSE);
    m_ComposeMpoTh.exit();

    pthread_mutex_destroy(&m_JpegLock);
}

/*===========================================================================
 * FUNCTION         : get_number_of_cameras
 *
 * DESCRIPTION     : Provide number of Logical Cameras
 *
 * RETURN             :  Number of logical Cameras
 *==========================================================================*/
int QCameraMuxer::get_number_of_cameras()
{
    return gMuxer->getNumberOfCameras();
}

/*===========================================================================
 * FUNCTION         : get_camera_info
 *
 * DESCRIPTION     : get logical camera info
 *
 * PARAMETERS:
 *   @camera_id     : Logical Camera ID
 *   @info              : Logical Main Camera Info
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              ENODEV : Camera not found
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::get_camera_info(int camera_id, struct camera_info *info)
{
    int rc = NO_ERROR;
    LOGH("E");
    cam_sync_type_t type;
    if ((camera_id < 0) || (camera_id >= gMuxer->getNumberOfCameras())) {
        LOGE("Camera id %d not found!", camera_id);
        return -ENODEV;
    }
    if(info) {
        rc = gMuxer->getCameraInfo(camera_id, info, &type);
    }
    LOGH("X, rc: %d", rc);
    return rc;
}


/*===========================================================================
 * FUNCTION         : set_callbacks
 *
 * DESCRIPTION     : Not Implemented
 *
 * PARAMETERS:
 *   @callbacks      : Camera Module Callbacks
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::set_callbacks(__unused const camera_module_callbacks_t *callbacks)
{
    // Not implemented
    return NO_ERROR;
}

/*===========================================================================
 * FUNCTION   : camera_device_open
 *
 * DESCRIPTION: static function to open a camera device by its ID
 *
 * PARAMETERS :
 *   @modue: hw module
 *   @id : camera ID
 *   @hw_device : ptr to struct storing camera hardware device info
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              BAD_VALUE : Invalid Camera ID
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::camera_device_open(
        __unused const struct hw_module_t *module, const char *id,
        struct hw_device_t **hw_device)
{
    int rc = NO_ERROR;
    LOGH("id= %d",atoi(id));
    if (!id) {
        LOGE("Invalid camera id");
        return BAD_VALUE;
    }

    rc =  gMuxer->cameraDeviceOpen(atoi(id), hw_device);
    LOGH("id= %d, rc: %d", atoi(id), rc);
    return rc;
}

/*===========================================================================
 * FUNCTION   : open_legacy
 *
 * DESCRIPTION: static function to open a camera device by its ID
 *
 * PARAMETERS :
 *   @modue: hw module
 *   @id : camera ID
 *   @halVersion: hal version
 *   @hw_device : ptr to struct storing camera hardware device info
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              BAD_VALUE : Invalid Camera ID
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::open_legacy(__unused const struct hw_module_t* module,
        const char* id, __unused uint32_t halVersion, struct hw_device_t** hw_device)
{
    int rc = NO_ERROR;
    LOGH("id= %d", atoi(id));
    if (!id) {
        LOGE("Invalid camera id");
        return BAD_VALUE;
    }

    rc =  gMuxer->cameraDeviceOpen(atoi(id), hw_device);
    LOGH("id= %d, rc: %d", atoi(id), rc);
    return rc;
}

/*===========================================================================
 * FUNCTION   : set_preview_window
 *
 * DESCRIPTION: Set Preview window for main camera
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *   @window: Preview window ops
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::set_preview_window(struct camera_device * device,
        struct preview_stream_ops *window)
{
    int rc = NO_ERROR;
    CHECK_MUXER_ERROR();
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA_ERROR(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        // Set preview window only for primary camera
        if (pCam->mode == CAM_MODE_PRIMARY) {
            QCamera2HardwareInterface *hwi = pCam->hwi;
            CHECK_HWI_ERROR(hwi);
            rc = hwi->set_preview_window(pCam->dev, window);
            if (rc != NO_ERROR) {
                LOGE("Error!! setting preview window");
                return rc;
            }
            break;
        }
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : set_callBacks
 *
 * DESCRIPTION: Set Framework callbacks to notify various frame data asynchronously
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *   @notify_cb: Notification callback
 *   @data_cb: data callback
 *   @data_cb_timestamp: data timestamp callback
 *   @get_memory: callback to obtain memory
 *   @user : userdata
 *
 * RETURN : None
 *==========================================================================*/
void QCameraMuxer::set_callBacks(struct camera_device * device,
        camera_notify_callback notify_cb,
        camera_data_callback data_cb,
        camera_data_timestamp_callback data_cb_timestamp,
        camera_request_memory get_memory,
        void *user)
{
    LOGH("E");
    CHECK_MUXER();
    int rc = NO_ERROR;
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA(cam);

    // Set callbacks to HWI
    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI(hwi);

        hwi->set_CallBacks(pCam->dev, notify_cb, data_cb, data_cb_timestamp,
                get_memory, user);

        // Set JPG callbacks
        // sending the physical camera description with the Jpeg callback
        // this will be retrieved in callbacks to get the cam instance
        // delivering JPEGs
        hwi->setJpegCallBacks(jpeg_data_callback, (void*)pCam);

        if (pCam->mode == CAM_MODE_PRIMARY) {
            rc = gMuxer->setMainJpegCallbackCookie((void*)(pCam));
            if(rc != NO_ERROR) {
                LOGW("Error setting Jpeg callback cookie");
            }
        }
    }
    // Store callback in Muxer to send data callbacks
    rc = gMuxer->setDataCallback(data_cb);
    if(rc != NO_ERROR) {
        LOGW("Error setting data callback");
    }
    // memory callback stored to allocate memory for MPO buffer
    rc = gMuxer->setMemoryCallback(get_memory);
    if(rc != NO_ERROR) {
        LOGW("Error setting memory callback");
    }
    // actual user callback cookie is saved in Muxer
    // this will be used to deliver final MPO callback to the framework
    rc = gMuxer->setMpoCallbackCookie(user);
    if(rc != NO_ERROR) {
        LOGW("Error setting mpo cookie");
    }

    LOGH("X");

}

/*===========================================================================
 * FUNCTION   : enable_msg_type
 *
 * DESCRIPTION: Enable msg_type to send callbacks
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *   @msg_type: callback Message type to be enabled
 *
 * RETURN : None
 *==========================================================================*/
void QCameraMuxer::enable_msg_type(struct camera_device * device, int32_t msg_type)
{
    LOGH("E");
    CHECK_MUXER();
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA(pCam);
        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI(hwi);
        hwi->enable_msg_type(pCam->dev, msg_type);
    }
    LOGH("X");
}

/*===========================================================================
 * FUNCTION   : disable_msg_type
 *
 * DESCRIPTION: disable msg_type to send callbacks
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *   @msg_type: callback Message type to be disabled
 *
 * RETURN : None
 *==========================================================================*/
void QCameraMuxer::disable_msg_type(struct camera_device * device, int32_t msg_type)
{
    LOGH("E");
    CHECK_MUXER();
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA(pCam);
        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI(hwi);
        hwi->disable_msg_type(pCam->dev, msg_type);
    }
    LOGH("X");
}

/*===========================================================================
 * FUNCTION   : msg_type_enabled
 *
 * DESCRIPTION: Check if message type enabled
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *   @msg_type: message type
 *
 * RETURN : true/false
 *==========================================================================*/
int QCameraMuxer::msg_type_enabled(struct camera_device * device, int32_t msg_type)
{
    LOGH("E");
    CHECK_MUXER_ERROR();
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA_ERROR(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        if (pCam->mode == CAM_MODE_PRIMARY) {
            return hwi->msg_type_enabled(pCam->dev, msg_type);
        }
    }
    LOGH("X");
    return false;
}

/*===========================================================================
 * FUNCTION   : start_preview
 *
 * DESCRIPTION: Starts logical camera preview
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::start_preview(struct camera_device * device)
{
    LOGH("E");
    CHECK_MUXER_ERROR();
    int rc = NO_ERROR;
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA_ERROR(cam);

    // prepare preview first for all cameras
    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        rc = hwi->prepare_preview(pCam->dev);
        if (rc != NO_ERROR) {
            LOGE("Error preparing preview !! ");
            return rc;
        }
    }

    if (cam->numCameras > 1) {
        uint sessionId = 0;
        // Set up sync for camera sessions
        for (uint32_t i = 0; i < cam->numCameras; i++) {
            pCam = gMuxer->getPhysicalCamera(cam, i);
            CHECK_CAMERA_ERROR(pCam);

            QCamera2HardwareInterface *hwi = pCam->hwi;
            CHECK_HWI_ERROR(hwi);

            if(pCam->mode == CAM_MODE_PRIMARY) {
                // bundle primary cam with all aux cameras
                for (uint32_t j = 0; j < cam->numCameras; j++) {
                    if (j == cam->nPrimaryPhyCamIndex) {
                        continue;
                    }
                    sessionId = cam->sId[j];
                    LOGH("Related cam id: %d, server id: %d sync ON"
                            " related session_id %d",
                            cam->pId[i], cam->sId[i], sessionId);
                    rc = hwi->bundleRelatedCameras(true, sessionId);
                    if (rc != NO_ERROR) {
                        LOGE("Error Bundling physical cameras !! ");
                        return rc;
                    }
                }
            }

            if (pCam->mode == CAM_MODE_SECONDARY) {
                // bundle all aux cam with primary cams
                sessionId = cam->sId[cam->nPrimaryPhyCamIndex];
                LOGH("Related cam id: %d, server id: %d sync ON"
                        " related session_id %d",
                        cam->pId[i], cam->sId[i], sessionId);
                rc = hwi->bundleRelatedCameras(true, sessionId);
                if (rc != NO_ERROR) {
                    LOGE("Error Bundling physical cameras !! ");
                    return rc;
                }
            }
        }

        // Remember Sync is ON
        cam->bSyncOn = true;
    }
    // Start Preview for all cameras
    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);
        rc = hwi->start_preview(pCam->dev);
        if (rc != NO_ERROR) {
            LOGE("Error starting preview !! ");
            return rc;
        }
    }
    LOGH("X");
    return rc;
}

/*===========================================================================
 * FUNCTION   : stop_preview
 *
 * DESCRIPTION: Stops logical camera preview
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *
 * RETURN     : None
 *==========================================================================*/
void QCameraMuxer::stop_preview(struct camera_device * device)
{
    LOGH("E");
    CHECK_MUXER();
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI(hwi);

        QCamera2HardwareInterface::stop_preview(pCam->dev);
    }

    //Flush JPEG Queues. Nodes in Main and Aux JPEGQ are not valid after preview stopped.
    gMuxer->m_MainJpegQ.flush();
    gMuxer->m_AuxJpegQ.flush();
    LOGH(" X");
}

/*===========================================================================
 * FUNCTION   : preview_enabled
 *
 * DESCRIPTION: Checks preview enabled
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *
 * RETURN     : true/false
 *==========================================================================*/
int QCameraMuxer::preview_enabled(struct camera_device * device)
{
    LOGH("E");
    CHECK_MUXER_ERROR();
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA_ERROR(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        if (pCam->mode == CAM_MODE_PRIMARY) {
            return hwi->preview_enabled(pCam->dev);
        }
    }
    LOGH("X");
    return false;
}

/*===========================================================================
 * FUNCTION   : store_meta_data_in_buffers
 *
 * DESCRIPTION: Stores metadata in buffers
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *   @enable: Enable/disable metadata
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::store_meta_data_in_buffers(struct camera_device * device, int enable)
{
    LOGH("E");
    CHECK_MUXER_ERROR();
    int rc = NO_ERROR;
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA_ERROR(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        rc = hwi->store_meta_data_in_buffers(pCam->dev, enable);
        if (rc != NO_ERROR) {
            LOGE("Error storing metat data !! ");
            return rc;
        }
    }
    LOGH("X");
    return rc;
}

/*===========================================================================
 * FUNCTION   : start_recording
 *
 * DESCRIPTION: Starts recording on camcorder
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::start_recording(struct camera_device * device)
{
    LOGH("E");
    CHECK_MUXER_ERROR();
    int rc = NO_ERROR;
    bool previewRestartNeeded = false;
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA_ERROR(cam);

    // In cases where recording hint is not set, hwi->start_recording will
    // internally restart the preview.
    // To take the preview restart control in muxer,
    // 1. call pre_start_recording first
    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        rc = hwi->pre_start_recording(pCam->dev);
        if (rc != NO_ERROR) {
            LOGE("Error preparing recording start!! ");
            return rc;
        }
    }

    // 2. Check if preview restart is needed. Check all cameras.
    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        if (hwi->isPreviewRestartNeeded()) {
            previewRestartNeeded = hwi->isPreviewRestartNeeded();
            break;
        }
    }

    if (previewRestartNeeded) {
        // 3. if preview restart needed. stop the preview first
        for (uint32_t i = 0; i < cam->numCameras; i++) {
            pCam = gMuxer->getPhysicalCamera(cam, i);
            CHECK_CAMERA_ERROR(pCam);

            QCamera2HardwareInterface *hwi = pCam->hwi;
            CHECK_HWI_ERROR(hwi);

            rc = hwi->restart_stop_preview(pCam->dev);
            if (rc != NO_ERROR) {
                LOGE("Error in restart stop preview!! ");
                return rc;
            }
        }

        //4. Update the recording hint value to TRUE
        for (uint32_t i = 0; i < cam->numCameras; i++) {
            pCam = gMuxer->getPhysicalCamera(cam, i);
            CHECK_CAMERA_ERROR(pCam);

            QCamera2HardwareInterface *hwi = pCam->hwi;
            CHECK_HWI_ERROR(hwi);

            rc = hwi->setRecordingHintValue(TRUE);
            if (rc != NO_ERROR) {
                LOGE("Error in setting recording hint value!! ");
                return rc;
            }
            gMuxer->m_bRecordingHintInternallySet = TRUE;
        }

        // 5. start the preview
        for (uint32_t i = 0; i < cam->numCameras; i++) {
            pCam = gMuxer->getPhysicalCamera(cam, i);
            CHECK_CAMERA_ERROR(pCam);

            QCamera2HardwareInterface *hwi = pCam->hwi;
            CHECK_HWI_ERROR(hwi);

            rc = hwi->restart_start_preview(pCam->dev);
            if (rc != NO_ERROR) {
                LOGE("Error in restart start preview!! ");
                return rc;
            }
        }
    }

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        if (pCam->mode == CAM_MODE_PRIMARY) {
            rc = hwi->start_recording(pCam->dev);
            if (rc != NO_ERROR) {
                LOGE("Error starting recording!! ");
            }
            break;
        }
    }
    LOGH("X");
    return rc;
}

/*===========================================================================
 * FUNCTION   : stop_recording
 *
 * DESCRIPTION: Stops recording on camcorder
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *
 * RETURN     : None
 *==========================================================================*/
void QCameraMuxer::stop_recording(struct camera_device * device)
{

    int rc = NO_ERROR;
    LOGH("E");

    CHECK_MUXER();
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI(hwi);

        if (pCam->mode == CAM_MODE_PRIMARY) {
            QCamera2HardwareInterface::stop_recording(pCam->dev);
            break;
        }
    }

    // If recording hint is set internally to TRUE,
    // we need to set it to FALSE.
    // preview restart is needed in between
    if (gMuxer->m_bRecordingHintInternallySet) {
        // stop the preview first
        for (uint32_t i = 0; i < cam->numCameras; i++) {
            pCam = gMuxer->getPhysicalCamera(cam, i);
            CHECK_CAMERA(pCam);

            QCamera2HardwareInterface *hwi = pCam->hwi;
            CHECK_HWI(hwi);

            rc = hwi->restart_stop_preview(pCam->dev);
            if (rc != NO_ERROR) {
                LOGE("Error in restart stop preview!! ");
                return;
            }
        }

        // Update the recording hint value to FALSE
        for (uint32_t i = 0; i < cam->numCameras; i++) {
            pCam = gMuxer->getPhysicalCamera(cam, i);
            CHECK_CAMERA(pCam);

            QCamera2HardwareInterface *hwi = pCam->hwi;
            CHECK_HWI(hwi);

            rc = hwi->setRecordingHintValue(FALSE);
            if (rc != NO_ERROR) {
                LOGE("Error in setting recording hint value!! ");
                return;
            }
            gMuxer->m_bRecordingHintInternallySet = FALSE;
        }

        // start the preview
        for (uint32_t i = 0; i < cam->numCameras; i++) {
            pCam = gMuxer->getPhysicalCamera(cam, i);
            CHECK_CAMERA(pCam);

            QCamera2HardwareInterface *hwi = pCam->hwi;
            CHECK_HWI(hwi);

            rc = hwi->restart_start_preview(pCam->dev);
            if (rc != NO_ERROR) {
                LOGE("Error in restart start preview!! ");
                return;
            }
        }
    }
    LOGH("X");
}

/*===========================================================================
 * FUNCTION   : recording_enabled
 *
 * DESCRIPTION: Checks for recording enabled
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *
 * RETURN     : true/false
 *==========================================================================*/
int QCameraMuxer::recording_enabled(struct camera_device * device)
{
    LOGH("E");
    CHECK_MUXER_ERROR();
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA_ERROR(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        if (pCam->mode == CAM_MODE_PRIMARY) {
            return hwi->recording_enabled(pCam->dev);
        }
    }
    LOGH("X");
    return false;
}

/*===========================================================================
 * FUNCTION   : release_recording_frame
 *
 * DESCRIPTION: Release the recording frame
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *   @opaque: Frame to be released
 *
  * RETURN     : None
 *==========================================================================*/
void QCameraMuxer::release_recording_frame(struct camera_device * device,
                const void *opaque)
{
    LOGH("E");
    CHECK_MUXER();
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI(hwi);

        if (pCam->mode == CAM_MODE_PRIMARY) {
            QCamera2HardwareInterface::release_recording_frame(pCam->dev, opaque);
            break;
        }
    }
    LOGH("X");
}

/*===========================================================================
 * FUNCTION   : auto_focus
 *
 * DESCRIPTION: Performs auto focus on camera
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::auto_focus(struct camera_device * device)
{
    LOGH("E");
    CHECK_MUXER_ERROR();
    int rc = NO_ERROR;
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA_ERROR(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);
        // Call auto focus on main camera
        if (pCam->mode == CAM_MODE_PRIMARY) {
            rc = QCamera2HardwareInterface::auto_focus(pCam->dev);
            if (rc != NO_ERROR) {
                LOGE("Error auto focusing !! ");
                return rc;
            }
            break;
        }
    }
    LOGH("X");
    return rc;
}

/*===========================================================================
 * FUNCTION   : cancel_auto_focus
 *
 * DESCRIPTION: Cancels auto focus
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::cancel_auto_focus(struct camera_device * device)
{
    LOGH("E");
    CHECK_MUXER_ERROR();
    int rc = NO_ERROR;
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA_ERROR(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);
        // Cancel auto focus on primary camera
        if (pCam->mode == CAM_MODE_PRIMARY) {
            rc = QCamera2HardwareInterface::cancel_auto_focus(pCam->dev);
            if (rc != NO_ERROR) {
                LOGE("Error cancelling auto focus !! ");
                return rc;
            }
            break;
        }
    }
    LOGH("X");
    return rc;
}

/*===========================================================================
 * FUNCTION   : take_picture
 *
 * DESCRIPTION: Take snapshots on device
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::take_picture(struct camera_device * device)
{
    LOGH("E");
    CHECK_MUXER_ERROR();
    int rc = NO_ERROR;
    bool previewRestartNeeded = false;
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA_ERROR(cam);

    char prop[PROPERTY_VALUE_MAX];
    property_get("persist.camera.dual.camera.mpo", prop, "1");
    gMuxer->m_bMpoEnabled = atoi(prop);
    // If only one Physical Camera included in Logical, disable MPO
    int numOfAcitvePhyCam = 0;
    gMuxer->getActiveNumOfPhyCam(cam, numOfAcitvePhyCam);
    if (gMuxer->m_bMpoEnabled && numOfAcitvePhyCam <= 1) {
        gMuxer->m_bMpoEnabled = 0;
    }
    LOGH("dualCamera MPO Enabled:%d ", gMuxer->m_bMpoEnabled);

    if (!gMuxer->mJpegClientHandle) {
        // set up jpeg handles
        pCam = gMuxer->getPhysicalCamera(cam, 0);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        rc = hwi->getJpegHandleInfo(&gMuxer->mJpegOps, &gMuxer->mJpegMpoOps,
                &gMuxer->mJpegClientHandle);
        if (rc != NO_ERROR) {
            LOGE("Error retrieving jpeg handle!");
            return rc;
        }

        for (uint32_t i = 1; i < cam->numCameras; i++) {
            pCam = gMuxer->getPhysicalCamera(cam, i);
            CHECK_CAMERA_ERROR(pCam);

            QCamera2HardwareInterface *hwi = pCam->hwi;
            CHECK_HWI_ERROR(hwi);

            rc = hwi->setJpegHandleInfo(&gMuxer->mJpegOps, &gMuxer->mJpegMpoOps,
                    gMuxer->mJpegClientHandle);
            if (rc != NO_ERROR) {
                LOGE("Error setting jpeg handle %d!", i);
                return rc;
            }
        }
    }

    // prepare snapshot for main camera
    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        if (pCam->mode == CAM_MODE_PRIMARY) {
            rc = hwi->prepare_snapshot(pCam->dev);
            if (rc != NO_ERROR) {
                LOGE("Error preparing for snapshot !! ");
                return rc;
            }
        }
        // set Mpo composition for each session
        rc = hwi->setMpoComposition(gMuxer->m_bMpoEnabled);
        //disable MPO if AOST features are enabled
        if (rc != NO_ERROR) {
            gMuxer->m_bMpoEnabled = 0;
            rc = NO_ERROR;
        }
    }

    // initialize Jpeg Queues
    gMuxer->m_MainJpegQ.init();
    gMuxer->m_AuxJpegQ.init();
    gMuxer->m_ComposeMpoTh.sendCmd(
            CAMERA_CMD_TYPE_START_DATA_PROC, FALSE, FALSE);

    // In cases where recording hint is set, preview is running,
    // hwi->take_picture will internally restart the preview.
    // To take the preview restart control in muxer,
    // 1. call pre_take_picture first
    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        // no need to call pre_take_pic on Aux if not MPO (for AOST,liveshot...etc.)
        if ( (gMuxer->m_bMpoEnabled == 1) || (pCam->mode == CAM_MODE_PRIMARY) ) {
            rc = hwi->pre_take_picture(pCam->dev);
            if (rc != NO_ERROR) {
                LOGE("Error preparing take_picture!! ");
                return rc;
            }
        }
    }

    // 2. Check if preview restart is needed. Check all cameras.
    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        if (hwi->isPreviewRestartNeeded()) {
            previewRestartNeeded = hwi->isPreviewRestartNeeded();
            break;
        }
    }

    if (previewRestartNeeded) {
        // 3. if preview restart needed. stop the preview first
        for (uint32_t i = 0; i < cam->numCameras; i++) {
            pCam = gMuxer->getPhysicalCamera(cam, i);
            CHECK_CAMERA_ERROR(pCam);

            QCamera2HardwareInterface *hwi = pCam->hwi;
            CHECK_HWI_ERROR(hwi);

            rc = hwi->restart_stop_preview(pCam->dev);
            if (rc != NO_ERROR) {
                LOGE("Error in restart stop preview!! ");
                return rc;
            }
        }

        //4. Update the recording hint value to FALSE
        for (uint32_t i = 0; i < cam->numCameras; i++) {
            pCam = gMuxer->getPhysicalCamera(cam, i);
            CHECK_CAMERA_ERROR(pCam);

            QCamera2HardwareInterface *hwi = pCam->hwi;
            CHECK_HWI_ERROR(hwi);

            rc = hwi->setRecordingHintValue(FALSE);
            if (rc != NO_ERROR) {
                LOGE("Error in setting recording hint value!! ");
                return rc;
            }
        }

        // 5. start the preview
        for (uint32_t i = 0; i < cam->numCameras; i++) {
            pCam = gMuxer->getPhysicalCamera(cam, i);
            CHECK_CAMERA_ERROR(pCam);

            QCamera2HardwareInterface *hwi = pCam->hwi;
            CHECK_HWI_ERROR(hwi);

            rc = hwi->restart_start_preview(pCam->dev);
            if (rc != NO_ERROR) {
                LOGE("Error in restart start preview!! ");
                return rc;
            }
        }
    }

    // As frame sync for dual cameras is enabled, the take picture call
    // for secondary camera is handled only till HAL level to init corresponding
    // pproc channel and update statemachine.
    // This call is forwarded to mm-camera-intf only for primary camera
    // Primary camera should receive the take picture call after all secondary
    // camera statemachines are updated
    for (int32_t i = cam->numCameras-1 ; i >= 0; i--) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        // no need to call take_pic on Aux if not MPO (for AOST)
        if ( (gMuxer->m_bMpoEnabled == 1) || (pCam->mode == CAM_MODE_PRIMARY) ) {
            rc = QCamera2HardwareInterface::take_picture(pCam->dev);
            if (rc != NO_ERROR) {
                LOGE("Error taking picture !! ");
                return rc;
            }
        }
    }
    LOGH("X");
    return rc;
}

/*===========================================================================
 * FUNCTION   : cancel_picture
 *
 * DESCRIPTION: Cancel the take picture call
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::cancel_picture(struct camera_device * device)
{
    LOGH("E");
    CHECK_MUXER_ERROR();
    int rc = NO_ERROR;
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA_ERROR(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        rc = QCamera2HardwareInterface::cancel_picture(pCam->dev);
        if (rc != NO_ERROR) {
            LOGE("Error cancelling picture !! ");
            return rc;
        }
    }
    gMuxer->m_ComposeMpoTh.sendCmd(CAMERA_CMD_TYPE_STOP_DATA_PROC, FALSE, FALSE);
    // flush Jpeg Queues
    gMuxer->m_MainJpegQ.flush();
    gMuxer->m_AuxJpegQ.flush();

    LOGH("X");
    return rc;
}

/*===========================================================================
 * FUNCTION   : set_parameters
 *
 * DESCRIPTION: Sets the parameters on camera
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *   @parms : Parameters to be set on camera
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::set_parameters(struct camera_device * device,
        const char *parms)

{
    LOGH("E");
    CHECK_MUXER_ERROR();
    int rc = NO_ERROR;
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    bool needRestart = false;
    CHECK_CAMERA_ERROR(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        rc = QCamera2HardwareInterface::set_parameters(pCam->dev, parms);
        if (rc != NO_ERROR) {
            LOGE("Error setting parameters !! ");
            return rc;
        }

        needRestart |= hwi->getNeedRestart();
    }

    if (needRestart) {
        for (uint32_t i = 0; i < cam->numCameras; i++) {
            pCam = gMuxer->getPhysicalCamera(cam, i);
            CHECK_CAMERA_ERROR(pCam);

            QCamera2HardwareInterface *hwi = pCam->hwi;
            CHECK_HWI_ERROR(hwi);

            LOGD("stopping preview for cam %d", i);
            rc = QCamera2HardwareInterface::stop_after_set_params(pCam->dev);
            if (rc != NO_ERROR) {
                LOGE("Error stopping camera rc=%d!! ", rc);
                return rc;
            }
        }
    }

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        LOGD("commiting parameters for cam %d", i);
        rc = QCamera2HardwareInterface::commit_params(pCam->dev);
        if (rc != NO_ERROR) {
            LOGE("Error committing parameters rc=%d!! ", rc);
            return rc;
        }
    }

    if (needRestart) {
        for (uint32_t i = 0; i < cam->numCameras; i++) {
            pCam = gMuxer->getPhysicalCamera(cam, i);
            CHECK_CAMERA_ERROR(pCam);

            QCamera2HardwareInterface *hwi = pCam->hwi;
            CHECK_HWI_ERROR(hwi);

            LOGD("restarting preview for cam %d", i);
            rc = QCamera2HardwareInterface::restart_after_set_params(pCam->dev);
            if (rc != NO_ERROR) {
                LOGE("Error restarting camera rc=%d!! ", rc);
                return rc;
            }
        }
    }

    LOGH(" X");
    return rc;
}

/*===========================================================================
 * FUNCTION   : get_parameters
 *
 * DESCRIPTION: Gets the parameters on camera
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *
 * RETURN     : Parameter string or NULL
 *==========================================================================*/
char* QCameraMuxer::get_parameters(struct camera_device * device)
{
    LOGH("E");

    if (!gMuxer)
        return NULL;

    char* ret = NULL;
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    if (!cam) {
        LOGE("Error getting logical camera");
        return NULL;
    }

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        if (!pCam) {
            LOGE("Error getting physical camera");
            return NULL;
        }
        QCamera2HardwareInterface *hwi = pCam->hwi;
        if (!hwi) {
            LOGE("Allocation of hardware interface failed");
            return NULL;
        }
        if (pCam->mode == CAM_MODE_PRIMARY) {
            // Get only primary camera parameters
            ret = QCamera2HardwareInterface::get_parameters(pCam->dev);
            break;
        }
    }

    LOGH("X");
    return ret;
}

/*===========================================================================
 * FUNCTION   : put_parameters
 *
 * DESCRIPTION: Puts parameters on camera
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *   @parm : parameters
 *
 * RETURN     : None
 *==========================================================================*/
void QCameraMuxer::put_parameters(struct camera_device * device, char *parm)
{
    LOGH("E");
    CHECK_MUXER();
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI(hwi);

        if (pCam->mode == CAM_MODE_PRIMARY) {
            // Parameters are not used in HWI and hence freed
            QCamera2HardwareInterface::put_parameters(pCam->dev, parm);
            break;
        }
    }
    LOGH("X");
}

/*===========================================================================
 * FUNCTION   : send_command
 *
 * DESCRIPTION: Send command to camera
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *   @cmd : Command
 *   @arg1/arg2 : command arguments
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::send_command(struct camera_device * device,
        int32_t cmd, int32_t arg1, int32_t arg2)
{
    LOGH("E");
    CHECK_MUXER_ERROR();
    int rc = NO_ERROR;
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA_ERROR(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        rc = QCamera2HardwareInterface::send_command(pCam->dev, cmd, arg1, arg2);
        if (rc != NO_ERROR) {
            LOGE("Error sending command !! ");
            return rc;
        }
    }

        switch (cmd) {
#ifndef VANILLA_HAL
        case CAMERA_CMD_LONGSHOT_ON:
            for (uint32_t i = 0; i < cam->numCameras; i++) {
                pCam = gMuxer->getPhysicalCamera(cam, i);
                CHECK_CAMERA_ERROR(pCam);

                QCamera2HardwareInterface *hwi = pCam->hwi;
                CHECK_HWI_ERROR(hwi);

                rc = QCamera2HardwareInterface::send_command_restart(pCam->dev,
                        cmd, arg1, arg2);
                if (rc != NO_ERROR) {
                    LOGE("Error sending command restart !! ");
                    return rc;
                }
            }
        break;
        case CAMERA_CMD_LONGSHOT_OFF:
            gMuxer->m_ComposeMpoTh.sendCmd(CAMERA_CMD_TYPE_STOP_DATA_PROC,
                    FALSE, FALSE);
            // flush Jpeg Queues
            gMuxer->m_MainJpegQ.flush();
            gMuxer->m_AuxJpegQ.flush();
        break;
#endif
        default:
            // do nothing
            rc = NO_ERROR;
        break;
        }

    LOGH("X");
    return rc;
}

/*===========================================================================
 * FUNCTION   : release
 *
 * DESCRIPTION: Release the camera
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *
 * RETURN     : None
 *==========================================================================*/
void QCameraMuxer::release(struct camera_device * device)
{
    LOGH("E");
    CHECK_MUXER();
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI(hwi);

        QCamera2HardwareInterface::release(pCam->dev);
    }
    LOGH("X");
}

/*===========================================================================
 * FUNCTION   : dump
 *
 * DESCRIPTION: Dump the camera info
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *   @fd : fd
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::dump(struct camera_device * device, int fd)
{
    LOGH("E");
    CHECK_MUXER_ERROR();
    int rc = NO_ERROR;
    qcamera_physical_descriptor_t *pCam = NULL;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(device);
    CHECK_CAMERA_ERROR(cam);

    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        QCamera2HardwareInterface *hwi = pCam->hwi;
        CHECK_HWI_ERROR(hwi);

        rc = QCamera2HardwareInterface::dump(pCam->dev, fd);
        if (rc != NO_ERROR) {
            LOGE("Error dumping");
            return rc;
        }
    }
    LOGH("X");
    return rc;
}

/*===========================================================================
 * FUNCTION   : close_camera_device
 *
 * DESCRIPTION: Close the camera
 *
 * PARAMETERS :
 *   @hw_dev : camera hardware device info
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::close_camera_device(hw_device_t *hw_dev)
{
    LOGH("E");
    CHECK_MUXER_ERROR();
    int rc = NO_ERROR;
    qcamera_physical_descriptor_t *pCam = NULL;
    camera_device_t *cam_dev = (camera_device_t*)hw_dev;
    qcamera_logical_descriptor_t *cam = gMuxer->getLogicalCamera(cam_dev);
    CHECK_CAMERA_ERROR(cam);

    // Unlink camera sessions
    if (cam->bSyncOn) {
        if (cam->numCameras > 1) {
            uint sessionId = 0;
            // unbundle primary camera with all aux cameras
            for (uint32_t i = 0; i < cam->numCameras; i++) {
                pCam = gMuxer->getPhysicalCamera(cam, i);
                CHECK_CAMERA_ERROR(pCam);

                QCamera2HardwareInterface *hwi = pCam->hwi;
                CHECK_HWI_ERROR(hwi);

                if(pCam->mode == CAM_MODE_PRIMARY) {
                    // bundle primary cam with all aux cameras
                    for (uint32_t j = 0; j < cam->numCameras; j++) {
                        if (j == cam->nPrimaryPhyCamIndex) {
                            continue;
                        }
                        sessionId = cam->sId[j];
                        LOGH("Related cam id: %d, server id: %d sync OFF"
                                " related session_id %d",
                                cam->pId[i], cam->sId[i], sessionId);
                        rc = hwi->bundleRelatedCameras(false, sessionId);
                        if (rc != NO_ERROR) {
                            LOGE("Error Bundling physical cameras !! ");
                            break;
                        }
                    }
                }

                if (pCam->mode == CAM_MODE_SECONDARY) {
                    // unbundle all aux cam with primary cams
                    sessionId = cam->sId[cam->nPrimaryPhyCamIndex];
                    LOGH("Related cam id: %d, server id: %d sync OFF"
                            " related session_id %d",
                            cam->pId[i], cam->sId[i], sessionId);
                    rc = hwi->bundleRelatedCameras(false, sessionId);
                    if (rc != NO_ERROR) {
                        LOGE("Error Bundling physical cameras !! ");
                        break;
                    }
                }
            }
        }
        cam->bSyncOn = false;
    }

    // Attempt to close all cameras regardless of unbundle results
    for (uint32_t i = 0; i < cam->numCameras; i++) {
        pCam = gMuxer->getPhysicalCamera(cam, i);
        CHECK_CAMERA_ERROR(pCam);

        hw_device_t *dev = (hw_device_t*)(pCam->dev);
        LOGH("hw device %x, hw %x", dev, pCam->hwi);

        rc = QCamera2HardwareInterface::close_camera_device(dev);
        if (rc != NO_ERROR) {
            LOGE("Error closing camera");
        }
        pCam->hwi = NULL;
        pCam->dev = NULL;
    }

    // Reset JPEG client handle
    gMuxer->setJpegHandle(0);
    LOGH("X, rc: %d", rc);
    return rc;
}

/*===========================================================================
 * FUNCTION         : setupLogicalCameras
 *
 * DESCRIPTION     : Creates Camera Muxer if not created
 *
 * RETURN     :
 *              NO_ERROR  : success
 *              other: non-zero failure code
 *==========================================================================*/
int QCameraMuxer::setupLogicalCameras()
{
    int rc = NO_ERROR;
    char prop[PROPERTY_VALUE_MAX];
    int i = 0;
    int primaryType = CAM_TYPE_MAIN;

    LOGH("[%d] E: rc = %d", rc);
    // Signifies whether AUX camera has to be exposed as physical camera
    property_get("persist.camera.aux.camera", prop, "0");
    m_bAuxCameraExposed = atoi(prop);

    // Signifies whether AUX camera needs to be swapped
    property_get("persist.camera.auxcamera.swap", prop, "0");
    int swapAux = atoi(prop);
    if (swapAux != 0) {
        primaryType = CAM_TYPE_AUX;
    }

    // Check for number of camera present on device
    if (!m_nPhyCameras || (m_nPhyCameras > MM_CAMERA_MAX_NUM_SENSORS)) {
        LOGE("Error!! Invalid number of cameras: %d",
                 m_nPhyCameras);
        return BAD_VALUE;
    }

    m_pPhyCamera = new qcamera_physical_descriptor_t[m_nPhyCameras];
    if (!m_pPhyCamera) {
        LOGE("Error allocating camera info buffer!!");
        return NO_MEMORY;
    }
    memset(m_pPhyCamera, 0x00,
            (m_nPhyCameras * sizeof(qcamera_physical_descriptor_t)));
    uint32_t cameraId = 0;
    m_nLogicalCameras = 0;

    // Enumerate physical cameras and logical
    for (i = 0; i < m_nPhyCameras ; i++, cameraId++) {
        camera_info *info = &m_pPhyCamera[i].cam_info;
        rc = QCamera2HardwareInterface::getCapabilities(cameraId,
                info, &m_pPhyCamera[i].type);
        m_pPhyCamera[i].id = cameraId;
        m_pPhyCamera[i].device_version = CAMERA_DEVICE_API_VERSION_1_0;
        m_pPhyCamera[i].mode = CAM_MODE_PRIMARY;

        if (!m_bAuxCameraExposed && (m_pPhyCamera[i].type != primaryType)) {
            m_pPhyCamera[i].mode = CAM_MODE_SECONDARY;
            LOGH("Camera ID: %d, Aux Camera, type: %d, facing: %d",
 cameraId, m_pPhyCamera[i].type,
                    m_pPhyCamera[i].cam_info.facing);
        }
        else {
            m_nLogicalCameras++;
            LOGH("Camera ID: %d, Main Camera, type: %d, facing: %d",
 cameraId, m_pPhyCamera[i].type,
                    m_pPhyCamera[i].cam_info.facing);
        }
    }

    if (!m_nLogicalCameras) {
        // No Main camera detected, return from here
        LOGE("Error !!!! detecting main camera!!");
        delete [] m_pPhyCamera;
        m_pPhyCamera = NULL;
        return -ENODEV;
    }
    // Allocate Logical Camera descriptors
    m_pLogicalCamera = new qcamera_logical_descriptor_t[m_nLogicalCameras];
    if (!m_pLogicalCamera) {
        LOGE("Error !!!! allocating camera info buffer!!");
        delete [] m_pPhyCamera;
        m_pPhyCamera = NULL;
        return  NO_MEMORY;
    }
    memset(m_pLogicalCamera, 0x00,
            (m_nLogicalCameras * sizeof(qcamera_logical_descriptor_t)));
    // Assign MAIN cameras for each logical camera
    int index = 0;
    for (i = 0; i < m_nPhyCameras ; i++) {
        if (m_pPhyCamera[i].mode == CAM_MODE_PRIMARY) {
            m_pLogicalCamera[index].nPrimaryPhyCamIndex = 0;
            m_pLogicalCamera[index].id = index;
            m_pLogicalCamera[index].device_version = CAMERA_DEVICE_API_VERSION_1_0;
            m_pLogicalCamera[index].pId[0] = i;
            m_pLogicalCamera[index].type[0] = CAM_TYPE_MAIN;
            m_pLogicalCamera[index].mode[0] = CAM_MODE_PRIMARY;
            m_pLogicalCamera[index].facing = m_pPhyCamera[i].cam_info.facing;
            m_pLogicalCamera[index].numCameras++;
            LOGH("Logical Main Camera ID: %d, facing: %d,"
                    "Phy Id: %d type: %d mode: %d",
 m_pLogicalCamera[index].id,
                    m_pLogicalCamera[index].facing,
                    m_pLogicalCamera[index].pId[0],
                    m_pLogicalCamera[index].type[0],
                    m_pLogicalCamera[index].mode[0]);

            index++;
        }
    }
    //Now assign AUX cameras to logical camera
    for (i = 0; i < m_nPhyCameras ; i++) {
        if (m_pPhyCamera[i].mode == CAM_MODE_SECONDARY) {
            for (int j = 0; j < m_nLogicalCameras; j++) {
                int n = m_pLogicalCamera[j].numCameras;
                ///@note n can only be 1 at this point
                if ((n < MAX_NUM_CAMERA_PER_BUNDLE) &&
                        (m_pLogicalCamera[j].facing ==
                        m_pPhyCamera[i].cam_info.facing)) {
                    m_pLogicalCamera[j].pId[n] = i;
                    m_pLogicalCamera[j].type[n] = CAM_TYPE_AUX;
                    m_pLogicalCamera[j].mode[n] = CAM_MODE_SECONDARY;
                    m_pLogicalCamera[j].numCameras++;
                    LOGH("Aux %d for Logical Camera ID: %d,"
                        "aux phy id:%d, type: %d mode: %d",
 n, j, m_pLogicalCamera[j].pId[n],
                        m_pLogicalCamera[j].type[n], m_pLogicalCamera[j].mode[n]);
                }
            }
        }
    }
    //Print logical and physical camera tables
    for (i = 0; i < m_nLogicalCameras ; i++) {
        for (uint8_t j = 0; j < m_pLogicalCamera[i].numCameras; j++) {
            LOGH("Logical Camera ID: %d, index: %d, "
                    "facing: %d, Phy Id: %d type: %d mode: %d",
 i, j, m_pLogicalCamera[i].facing,
                    m_pLogicalCamera[i].pId[j], m_pLogicalCamera[i].type[j],
                    m_pLogicalCamera[i].mode[j]);
        }
    }
    LOGH("[%d] X: rc = %d", rc);
    return rc;
}

/*===========================================================================
 * FUNCTION   : getNumberOfCameras
 *
 * DESCRIPTION: query number of logical cameras detected
 *
 * RETURN     : number of cameras detected
 *==========================================================================*/
int QCameraMuxer::getNumberOfCameras()
{
    return m_nLogicalCameras;
}

/*===========================================================================
 * FUNCTION   : getCameraInfo
 *
 * DESCRIPTION: query camera information with its ID
 *
 * PARAMETERS :
 *   @camera_id : camera ID
 *   @info      : ptr to camera info struct
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int QCameraMuxer::getCameraInfo(int camera_id,
        struct camera_info *info, __unused cam_sync_type_t *p_cam_type)
{
    int rc = NO_ERROR;
    LOGH("E, camera_id = %d", camera_id);
    cam_sync_type_t cam_type = CAM_TYPE_MAIN;

    if (!m_nLogicalCameras || (camera_id >= m_nLogicalCameras) ||
            !info || (camera_id < 0)) {
        LOGE("m_nLogicalCameras: %d, camera id: %d",
                m_nLogicalCameras, camera_id);
        return -ENODEV;
    }

    if (!m_pLogicalCamera || !m_pPhyCamera) {
        LOGE("Error! Cameras not initialized!");
        return NO_INIT;
    }
    uint32_t phy_id =
            m_pLogicalCamera[camera_id].pId[
            m_pLogicalCamera[camera_id].nPrimaryPhyCamIndex];
    rc = QCamera2HardwareInterface::getCapabilities(phy_id, info, &cam_type);
    LOGH("X");
    return rc;
}

/*===========================================================================
 * FUNCTION   : setCallbacks
 *
 * DESCRIPTION: set callback functions to send asynchronous notifications to
 *              frameworks.
 *
 * PARAMETERS :
 *   @callbacks : callback function pointer
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraMuxer::setCallbacks(const camera_module_callbacks_t *callbacks)
{
    if(callbacks) {
        m_pCallbacks = callbacks;
        return NO_ERROR;
    } else {
        return BAD_TYPE;
    }
}

/*===========================================================================
 * FUNCTION   : setDataCallback
 *
 * DESCRIPTION: set data callback function for snapshots
 *
 * PARAMETERS :
 *   @data_cb : callback function pointer
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraMuxer::setDataCallback(camera_data_callback data_cb)
{
    if(data_cb) {
        mDataCb = data_cb;
        return NO_ERROR;
    } else {
        return BAD_TYPE;
    }
}

/*===========================================================================
 * FUNCTION   : setMemoryCallback
 *
 * DESCRIPTION: set get memory callback for memory allocations
 *
 * PARAMETERS :
 *   @get_memory : callback function pointer
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraMuxer::setMemoryCallback(camera_request_memory get_memory)
{
    if(get_memory) {
        mGetMemoryCb = get_memory;
        return NO_ERROR;
    } else {
        return BAD_TYPE;
    }
}

/*===========================================================================
 * FUNCTION   : setMpoCallbackCookie
 *
 * DESCRIPTION: set mpo callback cookie. will be used for sending final MPO callbacks
 *                     to framework
 *
 * PARAMETERS :
 *   @mpoCbCookie : callback function pointer
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraMuxer::setMpoCallbackCookie(void* mpoCbCookie)
{
    if(mpoCbCookie) {
        m_pMpoCallbackCookie = mpoCbCookie;
        return NO_ERROR;
    } else {
        return BAD_TYPE;
    }
}

/*===========================================================================
 * FUNCTION   : getMpoCallbackCookie
 *
 * DESCRIPTION: gets the mpo callback cookie. will be used for sending final MPO callbacks
 *                     to framework
 *
 * PARAMETERS :none
 *
 * RETURN     :void ptr to the mpo callback cookie
 *==========================================================================*/
void* QCameraMuxer::getMpoCallbackCookie(void)
{
    return m_pMpoCallbackCookie;
}

/*===========================================================================
 * FUNCTION   : setMainJpegCallbackCookie
 *
 * DESCRIPTION: set jpeg callback cookie.
 *                     set to phy cam instance of the primary related cam instance
 *
 * PARAMETERS :
 *   @jpegCbCookie : ptr to jpeg cookie
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraMuxer::setMainJpegCallbackCookie(void* jpegCbCookie)
{
    if(jpegCbCookie) {
        m_pJpegCallbackCookie = jpegCbCookie;
        return NO_ERROR;
    } else {
        return BAD_TYPE;
    }
}

/*===========================================================================
 * FUNCTION   : getMainJpegCallbackCookie
 *
 * DESCRIPTION: gets the jpeg callback cookie for primary related cam instance
 *                     set to phy cam instance of the primary related cam instance
 *
 * PARAMETERS :none
 *
 * RETURN     :void ptr to the jpeg callback cookie
 *==========================================================================*/
void* QCameraMuxer::getMainJpegCallbackCookie(void)
{
    return m_pJpegCallbackCookie;
}

/*===========================================================================
 * FUNCTION   : cameraDeviceOpen
 *
 * DESCRIPTION: open a camera device with its ID
 *
 * PARAMETERS :
 *   @camera_id : camera ID
 *   @hw_device : ptr to struct storing camera hardware device info
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int QCameraMuxer::cameraDeviceOpen(int camera_id,
        struct hw_device_t **hw_device)
{
    int rc = NO_ERROR;
    uint32_t phyId = 0;
    qcamera_logical_descriptor_t *cam = NULL;

    if (camera_id < 0 || camera_id >= m_nLogicalCameras) {
        LOGE("Camera id %d not found!", camera_id);
        return -ENODEV;
    }

    if ( NULL == m_pLogicalCamera) {
        LOGE("Hal descriptor table is not initialized!");
        return NO_INIT;
    }

    char prop[PROPERTY_VALUE_MAX];
    property_get("persist.camera.dc.frame.sync", prop, "1");
    m_bFrameSyncEnabled = atoi(prop);

    // Get logical camera
    cam = &m_pLogicalCamera[camera_id];

    if (m_pLogicalCamera[camera_id].device_version ==
            CAMERA_DEVICE_API_VERSION_1_0) {
        // HW Dev Holders
        hw_device_t *hw_dev[cam->numCameras];

        if (m_pPhyCamera[cam->pId[0]].type != CAM_TYPE_MAIN) {
            LOGE("Physical camera at index 0 is not main!");
            return UNKNOWN_ERROR;
        }

        // Open all physical cameras
        for (uint32_t i = 0; i < cam->numCameras; i++) {
            phyId = cam->pId[i];
            QCamera2HardwareInterface *hw =
                    new QCamera2HardwareInterface((uint32_t)phyId);
            if (!hw) {
                LOGE("Allocation of hardware interface failed");
                return NO_MEMORY;
            }
            hw_dev[i] = NULL;

            // Make Camera HWI aware of its mode
            cam_sync_related_sensors_event_info_t info;
            info.sync_control = CAM_SYNC_RELATED_SENSORS_ON;
            info.mode = m_pPhyCamera[phyId].mode;
            info.type = m_pPhyCamera[phyId].type;
            info.is_frame_sync_enabled = m_bFrameSyncEnabled;
            rc = hw->setRelatedCamSyncInfo(&info);
            if (rc != NO_ERROR) {
                LOGE("setRelatedCamSyncInfo failed %d", rc);
                delete hw;
                return rc;
            }

            rc = hw->openCamera(&hw_dev[i]);
            if (rc != NO_ERROR) {
                delete hw;
                return rc;
            }
            hw->getCameraSessionId(&m_pPhyCamera[phyId].camera_server_id);
            m_pPhyCamera[phyId].dev = reinterpret_cast<camera_device_t*>(hw_dev[i]);
            m_pPhyCamera[phyId].hwi = hw;
            cam->sId[i] = m_pPhyCamera[phyId].camera_server_id;
            LOGH("camera id %d server id : %d hw device %x, hw %x",
                     phyId, cam->sId[i], hw_dev[i], hw);
        }
    } else {
        LOGE("Device version for camera id %d invalid %d",
                 camera_id, m_pLogicalCamera[camera_id].device_version);
        return BAD_VALUE;
    }

    cam->dev.common.tag = HARDWARE_DEVICE_TAG;
    cam->dev.common.version = HARDWARE_DEVICE_API_VERSION(1, 0);
    cam->dev.common.close = close_camera_device;
    cam->dev.ops = &mCameraMuxerOps;
    cam->dev.priv = (void*)cam;
    *hw_device = &cam->dev.common;
    return rc;
}


/*===========================================================================
 * FUNCTION   : getLogicalCamera
 *
 * DESCRIPTION: Get logical camera descriptor
 *
 * PARAMETERS :
 *   @device : camera hardware device info
 *
 * RETURN     : logical camera descriptor or NULL
 *==========================================================================*/
qcamera_logical_descriptor_t* QCameraMuxer::getLogicalCamera(
        struct camera_device * device)
{
    if(device && device->priv){
        return (qcamera_logical_descriptor_t*)(device->priv);
    }
    return NULL;
}

/*===========================================================================
 * FUNCTION   : getPhysicalCamera
 *
 * DESCRIPTION: Get physical camera descriptor
 *
 * PARAMETERS :
 *   @log_cam : Logical camera descriptor
 *   @index : physical camera index
 *
 * RETURN     : physical camera descriptor or NULL
 *==========================================================================*/
qcamera_physical_descriptor_t* QCameraMuxer::getPhysicalCamera(
        qcamera_logical_descriptor_t* log_cam, uint32_t index)
{
    if(!log_cam){
        return NULL;
    }
    return &m_pPhyCamera[log_cam->pId[index]];
}

/*===========================================================================
 * FUNCTION   : getActiveNumOfPhyCam
 *
 * DESCRIPTION: Get active physical camera number in Logical Camera
 *
 * PARAMETERS :
 *   @log_cam :   Logical camera descriptor
 *   @numOfAcitvePhyCam :  number of active physical camera in Logical Camera.
 *
 * RETURN     :
 *                NO_ERROR  : success
 *                ENODEV : Camera not found
 *                other: non-zero failure code
 *==========================================================================*/
int32_t QCameraMuxer::getActiveNumOfPhyCam(
        qcamera_logical_descriptor_t* log_cam, int& numOfAcitvePhyCam)
{
    CHECK_CAMERA_ERROR(log_cam);

    numOfAcitvePhyCam = log_cam->numCameras;
    return NO_ERROR;
}


/*===========================================================================
 * FUNCTION   : sendEvtNotify
 *
 * DESCRIPTION: send event notify to HWI for error callbacks
 *
 * PARAMETERS :
 *   @msg_type: msg type to be sent
 *   @ext1    : optional extension1
 *   @ext2    : optional extension2
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraMuxer::sendEvtNotify(int32_t msg_type, int32_t ext1,
        int32_t ext2)
{
    LOGH("E");

    CHECK_MUXER_ERROR();

    qcamera_physical_descriptor_t *pCam = NULL;
    pCam = (qcamera_physical_descriptor_t*)(gMuxer->getMainJpegCallbackCookie());

    CHECK_CAMERA_ERROR(pCam);

    QCamera2HardwareInterface *hwi = pCam->hwi;
    CHECK_HWI_ERROR(hwi);

    LOGH("X");
    return pCam->hwi->sendEvtNotify(msg_type, ext1, ext2);
}

/*===========================================================================
 * FUNCTION   : composeMpo
 *
 * DESCRIPTION: Composition of the 2 MPOs
 *
 * PARAMETERS : none
 *   @main_Jpeg: pointer to info to Main Jpeg
 *   @aux_Jpeg : pointer to info to Aux JPEG
 *
  * RETURN : none
 *==========================================================================*/
void QCameraMuxer::composeMpo(cam_compose_jpeg_info_t* main_Jpeg,
        cam_compose_jpeg_info_t* aux_Jpeg)
{
    LOGH("E Main Jpeg %p Aux Jpeg %p", main_Jpeg, aux_Jpeg);

    CHECK_MUXER();
    if(main_Jpeg == NULL || aux_Jpeg == NULL) {
        LOGE("input buffers invalid, ret = NO_MEMORY");
        gMuxer->sendEvtNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
        return;
    }

    pthread_mutex_lock(&m_JpegLock);

    m_pRelCamMpoJpeg = mGetMemoryCb(-1, main_Jpeg->buffer->size +
            aux_Jpeg->buffer->size, 1, m_pMpoCallbackCookie);
    if (NULL == m_pRelCamMpoJpeg) {
        LOGE("getMemory for mpo, ret = NO_MEMORY");
        gMuxer->sendEvtNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
        pthread_mutex_unlock(&m_JpegLock);
        return;
    }

    // fill all structures to send for composition
    mm_jpeg_mpo_info_t mpo_compose_info;
    mpo_compose_info.num_of_images = 2;
    mpo_compose_info.primary_image.buf_filled_len = main_Jpeg->buffer->size;
    mpo_compose_info.primary_image.buf_vaddr =
            (uint8_t*)(main_Jpeg->buffer->data);
    mpo_compose_info.aux_images[0].buf_filled_len = aux_Jpeg->buffer->size;
    mpo_compose_info.aux_images[0].buf_vaddr =
            (uint8_t*)(aux_Jpeg->buffer->data);
    mpo_compose_info.output_buff.buf_vaddr =
            (uint8_t*)m_pRelCamMpoJpeg->data;
    mpo_compose_info.output_buff.buf_filled_len = 0;
    mpo_compose_info.output_buff_size = main_Jpeg->buffer->size +
            aux_Jpeg->buffer->size;

    LOGD("MPO buffer size %d\n"
            "expected size %d, mpo_compose_info.output_buff_size %d",
             m_pRelCamMpoJpeg->size,
            main_Jpeg->buffer->size + aux_Jpeg->buffer->size,
            mpo_compose_info.output_buff_size);

    LOGD("MPO primary buffer filled lengths\n"
            "mpo_compose_info.primary_image.buf_filled_len %d\n"
            "mpo_compose_info.primary_image.buf_vaddr %p",
            mpo_compose_info.primary_image.buf_filled_len,
            mpo_compose_info.primary_image.buf_vaddr);

    LOGD("MPO aux buffer filled lengths\n"
            "mpo_compose_info.aux_images[0].buf_filled_len %d"
            "mpo_compose_info.aux_images[0].buf_vaddr %p",
            mpo_compose_info.aux_images[0].buf_filled_len,
            mpo_compose_info.aux_images[0].buf_vaddr);

    if(m_bDumpImages) {
        LOGD("Dumping Main Image for MPO");
        char buf_main[QCAMERA_MAX_FILEPATH_LENGTH];
        memset(buf_main, 0, sizeof(buf_main));
        snprintf(buf_main, sizeof(buf_main),
                QCAMERA_DUMP_FRM_LOCATION "Main.jpg");

        int file_fd_main = open(buf_main, O_RDWR | O_CREAT, 0777);
        if (file_fd_main >= 0) {
            ssize_t written_len = write(file_fd_main,
                    mpo_compose_info.primary_image.buf_vaddr,
                    mpo_compose_info.primary_image.buf_filled_len);
            fchmod(file_fd_main, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
            LOGD("written number of bytes for main Image %zd\n",
                     written_len);
            close(file_fd_main);
        }

        LOGD("Dumping Aux Image for MPO");
        char buf_aux[QCAMERA_MAX_FILEPATH_LENGTH];
        memset(buf_aux, 0, sizeof(buf_aux));
        snprintf(buf_aux, sizeof(buf_aux),
                QCAMERA_DUMP_FRM_LOCATION "Aux.jpg");

        int file_fd_aux = open(buf_aux, O_RDWR | O_CREAT, 0777);
        if (file_fd_aux >= 0) {
            ssize_t written_len = write(file_fd_aux,
                    mpo_compose_info.aux_images[0].buf_vaddr,
                    mpo_compose_info.aux_images[0].buf_filled_len);
            fchmod(file_fd_aux, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
            LOGD("written number of bytes for Aux Image %zd\n",
                     written_len);
            close(file_fd_aux);
        }
    }

    int32_t rc = mJpegMpoOps.compose_mpo(&mpo_compose_info);
    LOGD("Compose mpo returned %d", rc);

    if(rc != NO_ERROR) {
        LOGE("ComposeMpo failed, ret = %d", rc);
        gMuxer->sendEvtNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
        pthread_mutex_unlock(&m_JpegLock);
        return;
    }

    if(m_bDumpImages) {
        char buf_mpo[QCAMERA_MAX_FILEPATH_LENGTH];
        memset(buf_mpo, 0, sizeof(buf_mpo));
        snprintf(buf_mpo, sizeof(buf_mpo),
                QCAMERA_DUMP_FRM_LOCATION "Composed.MPO");

        int file_fd_mpo = open(buf_mpo, O_RDWR | O_CREAT, 0777);
        if (file_fd_mpo >= 0) {
            ssize_t written_len = write(file_fd_mpo,
                    m_pRelCamMpoJpeg->data,
                    m_pRelCamMpoJpeg->size);
            fchmod(file_fd_mpo, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
            LOGD("written number of bytes for MPO Image %zd\n",
                     written_len);
            close(file_fd_mpo);
        }
    }

    mDataCb(main_Jpeg->msg_type,
            m_pRelCamMpoJpeg,
            main_Jpeg->index,
            main_Jpeg->metadata,
            m_pMpoCallbackCookie);

    if (NULL != m_pRelCamMpoJpeg) {
        m_pRelCamMpoJpeg->release(m_pRelCamMpoJpeg);
        m_pRelCamMpoJpeg = NULL;
    }

    pthread_mutex_unlock(&m_JpegLock);
    LOGH("X");
    return;
}

/*===========================================================================
 * FUNCTION   : matchFrameId
 *
 * DESCRIPTION: function to match frame ids within queue nodes
 *
 * PARAMETERS :
 *   @data: pointer to queue node to be matched for condition
 *   @user_data: caller can add more info here
 *   @match_data : value to be matched against
 *
 * RETURN     : true or false based on whether match was successful or not
 *==========================================================================*/
bool QCameraMuxer::matchFrameId(void *data, __unused void *user_data,
        void *match_data)
{
    LOGH("E");

    if (!data || !match_data) {
        return false;
    }

    cam_compose_jpeg_info_t * node = (cam_compose_jpeg_info_t *) data;
    uint32_t frame_idx = *((uint32_t *) match_data);
    LOGH("X");
    return node->frame_idx == frame_idx;
}

/*===========================================================================
 * FUNCTION   : findPreviousJpegs
 *
 * DESCRIPTION: Finds Jpegs in the queue with index less than delivered one
 *
 * PARAMETERS :
 *   @data: pointer to queue node to be matched for condition
 *   @user_data: caller can add more info here
 *   @match_data : value to be matched against
 *
 * RETURN     : true or false based on whether match was successful or not
 *==========================================================================*/
bool QCameraMuxer::findPreviousJpegs(void *data, __unused void *user_data,
        void *match_data)
{
    LOGH("E");

    if (!data || !match_data) {
        return false;
    }
    cam_compose_jpeg_info_t * node = (cam_compose_jpeg_info_t *) data;
    uint32_t frame_idx = *((uint32_t *) match_data);
    LOGH("X");
    return node->frame_idx < frame_idx;
}

/*===========================================================================
 * FUNCTION   : releaseJpegInfo
 *
 * DESCRIPTION: callback function for the release of individual nodes
 *                     in the JPEG queues.
 *
 * PARAMETERS :
 *   @data      : ptr to the data to be released
 *   @user_data : caller can add more info here
 *
 * RETURN     : None
 *==========================================================================*/
void QCameraMuxer::releaseJpegInfo(void *data, __unused void *user_data)
{
    LOGH("E");

    cam_compose_jpeg_info_t *jpegInfo = (cam_compose_jpeg_info_t *)data;
    if(jpegInfo && jpegInfo->release_cb) {
        if (jpegInfo->release_data != NULL) {
            jpegInfo->release_cb(jpegInfo->release_data,
                    jpegInfo->release_cookie,
                    NO_ERROR);
        }
    }
    LOGH("X");
}

/*===========================================================================
 * FUNCTION   : composeMpoRoutine
 *
 * DESCRIPTION: specialized thread for MPO composition
 *
 * PARAMETERS :
 *   @data   : pointer to the thread owner
 *
 * RETURN     : void* to thread
 *==========================================================================*/
void* QCameraMuxer::composeMpoRoutine(__unused void *data)
{
    LOGH("E");
    if (!gMuxer) {
        LOGE("Error getting muxer ");
        return NULL;
    }

    int running = 1;
    int ret;
    uint8_t is_active = FALSE;
    QCameraCmdThread *cmdThread = &gMuxer->m_ComposeMpoTh;
    cmdThread->setName("CAM_ComposeMpo");

    do {
        do {
            ret = cam_sem_wait(&cmdThread->cmd_sem);
            if (ret != 0 && errno != EINVAL) {
                LOGE("cam_sem_wait error (%s)", strerror(errno));
                return NULL;
            }
        } while (ret != 0);

        // we got notified about new cmd avail in cmd queue
        camera_cmd_type_t cmd = cmdThread->getCmd();
        switch (cmd) {
        case CAMERA_CMD_TYPE_START_DATA_PROC:
            {
                LOGH("start ComposeMpo processing");
                is_active = TRUE;

                // signal cmd is completed
                cam_sem_post(&cmdThread->sync_sem);
            }
            break;
        case CAMERA_CMD_TYPE_STOP_DATA_PROC:
            {
                LOGH("stop ComposeMpo processing");
                is_active = FALSE;

                // signal cmd is completed
                cam_sem_post(&cmdThread->sync_sem);
            }
            break;
        case CAMERA_CMD_TYPE_DO_NEXT_JOB:
            {
                if (is_active == TRUE) {
                    LOGH("Mpo Composition Requested");
                    cam_compose_jpeg_info_t *main_jpeg_node = NULL;
                    cam_compose_jpeg_info_t *aux_jpeg_node = NULL;
                    bool foundMatch = false;
                    while (!gMuxer->m_MainJpegQ.isEmpty() &&
                            !gMuxer->m_AuxJpegQ.isEmpty()) {
                        main_jpeg_node = (cam_compose_jpeg_info_t *)
                                gMuxer->m_MainJpegQ.dequeue();
                        if (main_jpeg_node != NULL) {
                            LOGD("main_jpeg_node found frame idx %d"
                                    "ptr %p buffer_ptr %p buffer_size %d",
                                     main_jpeg_node->frame_idx,
                                    main_jpeg_node,
                                    main_jpeg_node->buffer->data,
                                    main_jpeg_node->buffer->size);
                            // find matching aux node in Aux Jpeg Queue
                            aux_jpeg_node =
                                    (cam_compose_jpeg_info_t *) gMuxer->
                                    m_AuxJpegQ.dequeue();
                            if (aux_jpeg_node != NULL) {
                                LOGD("aux_jpeg_node found frame idx %d"
                                        "ptr %p buffer_ptr %p buffer_size %d",
                                         aux_jpeg_node->frame_idx,
                                        aux_jpeg_node,
                                        aux_jpeg_node->buffer->data,
                                        aux_jpeg_node->buffer->size);
                                foundMatch = true;
                                // start MPO composition
                                gMuxer->composeMpo(main_jpeg_node,
                                        aux_jpeg_node);
                            }
                        }
                        if (main_jpeg_node != NULL) {
                            if ( main_jpeg_node->release_cb ) {
                                main_jpeg_node->release_cb(
                                        main_jpeg_node->release_data,
                                        main_jpeg_node->release_cookie,
                                        NO_ERROR);
                            }
                            free(main_jpeg_node);
                            main_jpeg_node = NULL;
                        } else {
                            LOGH("Mpo Match not found");
                        }
                        if (aux_jpeg_node != NULL) {
                            if (aux_jpeg_node->release_cb) {
                                aux_jpeg_node->release_cb(
                                        aux_jpeg_node->release_data,
                                        aux_jpeg_node->release_cookie,
                                        NO_ERROR);
                            }
                            free(aux_jpeg_node);
                            aux_jpeg_node = NULL;
                        } else {
                            LOGH("Mpo Match not found");
                        }
                    }
                }
            break;
            }
        case CAMERA_CMD_TYPE_EXIT:
            LOGH("ComposeMpo thread exit");
            running = 0;
            break;
        default:
            break;
        }
    } while (running);
    LOGH("X");
    return NULL;
}

/*===========================================================================
 * FUNCTION   : jpeg_data_callback
 *
 * DESCRIPTION: JPEG data callback for snapshot
 *
 * PARAMETERS :
 *   @msg_type : callback msg type
 *   @data : data ptr of the buffer
 *   @index : index of the frame
 *   @metadata : metadata associated with the buffer
 *   @user : callback cookie returned back to the user
 *   @frame_idx : frame index for matching frames
 *   @release_cb : callback function for releasing the data memory
 *   @release_cookie : cookie for the release callback function
 *   @release_data :pointer indicating what needs to be released
 *
 * RETURN : none
 *==========================================================================*/
void QCameraMuxer::jpeg_data_callback(int32_t msg_type,
           const camera_memory_t *data, unsigned int index,
           camera_frame_metadata_t *metadata, void *user,
           uint32_t frame_idx, camera_release_callback release_cb,
           void *release_cookie, void *release_data)
{
    LOGH("E");
    CHECK_MUXER();

    if(data != NULL) {
        LOGH("jpeg received: data %p size %d data ptr %p frameIdx %d",
                 data, data->size, data->data, frame_idx);
        int rc = gMuxer->storeJpeg(((qcamera_physical_descriptor_t*)(user))->type,
                msg_type, data, index, metadata, user, frame_idx, release_cb,
                release_cookie, release_data);
        if(rc != NO_ERROR) {
            gMuxer->sendEvtNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
        }
    } else {
        gMuxer->sendEvtNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
    }
    LOGH("X");
    return;
}

/*===========================================================================
 * FUNCTION   : storeJpeg
 *
 * DESCRIPTION: Stores jpegs from multiple related cam instances into a common Queue
 *
 * PARAMETERS :
 *   @cam_type : indicates whether main or aux camera sent the Jpeg callback
 *   @msg_type : callback msg type
 *   @data : data ptr of the buffer
 *   @index : index of the frame
 *   @metadata : metadata associated with the buffer
 *   @user : callback cookie returned back to the user
 *   @frame_idx : frame index for matching frames
 *   @release_cb : callback function for releasing the data memory
 *   @release_cookie : cookie for the release callback function
 *   @release_data :pointer indicating what needs to be released
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraMuxer::storeJpeg(cam_sync_type_t cam_type,
        int32_t msg_type, const camera_memory_t *data, unsigned int index,
        camera_frame_metadata_t *metadata, void *user,uint32_t frame_idx,
        camera_release_callback release_cb, void *release_cookie,
        void *release_data)
{
    LOGH("E jpeg received: data %p size %d data ptr %p frameIdx %d",
             data, data->size, data->data, frame_idx);

    CHECK_MUXER_ERROR();

    if (!m_bMpoEnabled) {
        if (cam_type == CAM_TYPE_MAIN) {
            // send data callback only incase of main camera
            // aux image is ignored and released back
            mDataCb(msg_type,
                    data,
                    index,
                    metadata,
                    m_pMpoCallbackCookie);
        }
        if (release_cb) {
            release_cb(release_data, release_cookie, NO_ERROR);
        }
        LOGH("X");
        return NO_ERROR;
    }

    cam_compose_jpeg_info_t* pJpegFrame =
            (cam_compose_jpeg_info_t*)malloc(sizeof(cam_compose_jpeg_info_t));
    if (!pJpegFrame) {
        LOGE("Allocation failed for MPO nodes");
        return NO_MEMORY;
    }
    memset(pJpegFrame, 0, sizeof(*pJpegFrame));

    pJpegFrame->msg_type = msg_type;
    pJpegFrame->buffer = const_cast<camera_memory_t*>(data);
    pJpegFrame->index = index;
    pJpegFrame->metadata = metadata;
    pJpegFrame->user = user;
    pJpegFrame->valid = true;
    pJpegFrame->frame_idx = frame_idx;
    pJpegFrame->release_cb = release_cb;
    pJpegFrame->release_cookie = release_cookie;
    pJpegFrame->release_data = release_data;
    if(cam_type == CAM_TYPE_MAIN) {
        if (m_MainJpegQ.enqueue((void *)pJpegFrame)) {
            LOGD("Main FrameIdx %d", pJpegFrame->frame_idx);
            if (m_MainJpegQ.getCurrentSize() > 0) {
                LOGD("Trigger Compose");
                m_ComposeMpoTh.sendCmd(CAMERA_CMD_TYPE_DO_NEXT_JOB, FALSE, FALSE);
            }
        } else {
            LOGE("Enqueue Failed for Main Jpeg Q");
            if ( pJpegFrame->release_cb ) {
                // release other buffer also here
                pJpegFrame->release_cb(
                        pJpegFrame->release_data,
                        pJpegFrame->release_cookie,
                        NO_ERROR);
            }
            free(pJpegFrame);
            pJpegFrame = NULL;
            return NO_MEMORY;
        }

    } else {
        if (m_AuxJpegQ.enqueue((void *)pJpegFrame)) {
            LOGD("Aux FrameIdx %d", pJpegFrame->frame_idx);
            if (m_AuxJpegQ.getCurrentSize() > 0) {
                LOGD("Trigger Compose");
                m_ComposeMpoTh.sendCmd(CAMERA_CMD_TYPE_DO_NEXT_JOB, FALSE, FALSE);
            }
        } else {
            LOGE("Enqueue Failed for Aux Jpeg Q");
            if ( pJpegFrame->release_cb ) {
                // release other buffer also here
                pJpegFrame->release_cb(
                        pJpegFrame->release_data,
                        pJpegFrame->release_cookie,
                        NO_ERROR);
            }
            free(pJpegFrame);
            pJpegFrame = NULL;
            return NO_MEMORY;
        }
    }
    LOGH("X");

    return NO_ERROR;
}


// Muxer Ops
camera_device_ops_t QCameraMuxer::mCameraMuxerOps = {
    .set_preview_window =        QCameraMuxer::set_preview_window,
    .set_callbacks =             QCameraMuxer::set_callBacks,
    .enable_msg_type =           QCameraMuxer::enable_msg_type,
    .disable_msg_type =          QCameraMuxer::disable_msg_type,
    .msg_type_enabled =          QCameraMuxer::msg_type_enabled,

    .start_preview =             QCameraMuxer::start_preview,
    .stop_preview =              QCameraMuxer::stop_preview,
    .preview_enabled =           QCameraMuxer::preview_enabled,
    .store_meta_data_in_buffers= QCameraMuxer::store_meta_data_in_buffers,

    .start_recording =           QCameraMuxer::start_recording,
    .stop_recording =            QCameraMuxer::stop_recording,
    .recording_enabled =         QCameraMuxer::recording_enabled,
    .release_recording_frame =   QCameraMuxer::release_recording_frame,

    .auto_focus =                QCameraMuxer::auto_focus,
    .cancel_auto_focus =         QCameraMuxer::cancel_auto_focus,

    .take_picture =              QCameraMuxer::take_picture,
    .cancel_picture =            QCameraMuxer::cancel_picture,

    .set_parameters =            QCameraMuxer::set_parameters,
    .get_parameters =            QCameraMuxer::get_parameters,
    .put_parameters =            QCameraMuxer::put_parameters,
    .send_command =              QCameraMuxer::send_command,

    .release =                   QCameraMuxer::release,
    .dump =                      QCameraMuxer::dump,
};


}; // namespace android