/* Copyright (c) 2012-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 "QCameraChannel"

// System dependencies
#include <utils/Errors.h>

// Camera dependencies
#include "QCamera2HWI.h"

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

using namespace android;

namespace qcamera {

/*===========================================================================
 * FUNCTION   : QCameraChannel
 *
 * DESCRIPTION: constrcutor of QCameraChannel
 *
 * PARAMETERS :
 *   @cam_handle : camera handle
 *   @cam_ops    : ptr to camera ops table
 *
 * RETURN     : none
 *==========================================================================*/
QCameraChannel::QCameraChannel(uint32_t cam_handle,
                               mm_camera_ops_t *cam_ops)
{
    m_camHandle = cam_handle;
    m_camOps = cam_ops;
    m_bIsActive = false;
    m_bAllowDynBufAlloc = false;

    m_handle = 0;
}

/*===========================================================================
 * FUNCTION   : QCameraChannel
 *
 * DESCRIPTION: default constrcutor of QCameraChannel
 *
 * PARAMETERS : none
 *
 * RETURN     : none
 *==========================================================================*/
QCameraChannel::QCameraChannel()
{
    m_camHandle = 0;
    m_camOps = NULL;
    m_bIsActive = false;

    m_handle = 0;
}

/*===========================================================================
 * FUNCTION   : ~QCameraChannel
 *
 * DESCRIPTION: destructor of QCameraChannel
 *
 * PARAMETERS : none
 *
 * RETURN     : none
 *==========================================================================*/
QCameraChannel::~QCameraChannel()
{
    if (m_bIsActive) {
        stop();
    }
    for (size_t i = 0; i < mStreams.size(); i++) {
        if (mStreams[i] != NULL) {
                if (m_handle == mStreams[i]->getChannelHandle()) {
                    delete mStreams[i];
                }
        }
    }
    mStreams.clear();
    m_camOps->delete_channel(m_camHandle, m_handle);
    m_handle = 0;
}

/*===========================================================================
 * FUNCTION   : deleteChannel
 *
 * DESCRIPTION: deletes a camera channel
 *
 * PARAMETERS : none
 *
 * RETURN     : none
 *==========================================================================*/
void QCameraChannel::deleteChannel()
{
    if (m_bIsActive) {
        stop();
    }
    for (size_t i = 0; i < mStreams.size(); i++) {
        if ((mStreams[i] != NULL) && (m_handle == mStreams[i]->getChannelHandle())) {
            mStreams[i]->deleteStream();
        }
    }
    m_camOps->delete_channel(m_camHandle, m_handle);
}

/*===========================================================================
 * FUNCTION   : setStreamSyncCB
 *
 * DESCRIPTION: reg callback function to stream of stream type
 *
 * PARAMETERS :
 *    @stream_type : Stream type for which callback needs to be registered.
 *    @stream_cb   : Callback function

 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              non-zero failure code
 *==========================================================================*/
int32_t QCameraChannel::setStreamSyncCB (cam_stream_type_t stream_type,
        stream_cb_routine stream_cb)
{
    int32_t rc = UNKNOWN_ERROR;
    for (size_t i = 0; i < mStreams.size(); i++) {
        if ((mStreams[i] != NULL) && (stream_type == mStreams[i]->getMyType())) {
            rc = mStreams[i]->setSyncDataCB(stream_cb);
            break;
        }
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : init
 *
 * DESCRIPTION: initialization of channel
 *
 * PARAMETERS :
 *   @attr    : channel bundle attribute setting
 *   @dataCB  : data notify callback
 *   @userData: user data ptr
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraChannel::init(mm_camera_channel_attr_t *attr,
                             mm_camera_buf_notify_t dataCB,
                             void *userData)
{
    m_handle = m_camOps->add_channel(m_camHandle,
                                      attr,
                                      dataCB,
                                      userData);
    if (m_handle == 0) {
        LOGE("Add channel failed");
        return UNKNOWN_ERROR;
    }
    return NO_ERROR;
}

/*===========================================================================
 * FUNCTION   : addStream
 *
 * DESCRIPTION: add a stream into channel
 *
 * PARAMETERS :
 *   @allocator      : stream related buffer allocator
 *   @streamInfoBuf  : ptr to buf that contains stream info
 *   @miscBuf        : ptr to buf that contains misc buffers
 *   @minStreamBufNum: number of stream buffers needed
 *   @paddingInfo    : padding information
 *   @stream_cb      : stream data notify callback
 *   @userdata       : user data ptr
 *   @bDynAllocBuf   : flag indicating if allow allocate buffers in 2 steps
 *   @online_rotation: rotation applied online
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraChannel::addStream(QCameraAllocator &allocator,
        QCameraHeapMemory *streamInfoBuf, QCameraHeapMemory *miscBuf,
        uint8_t minStreamBufNum, cam_padding_info_t *paddingInfo,
        stream_cb_routine stream_cb, void *userdata, bool bDynAllocBuf,
        bool bDeffAlloc, cam_rotation_t online_rotation)
{
    int32_t rc = NO_ERROR;
    if (mStreams.size() >= MAX_STREAM_NUM_IN_BUNDLE) {
        LOGE("stream number (%zu) exceeds max limit (%d)",
               mStreams.size(), MAX_STREAM_NUM_IN_BUNDLE);
        if (streamInfoBuf != NULL) {
            streamInfoBuf->deallocate();
            delete streamInfoBuf;
            streamInfoBuf = NULL;
        }
        return BAD_VALUE;
    }
    QCameraStream *pStream = new QCameraStream(allocator,
            m_camHandle, m_handle, m_camOps, paddingInfo, bDeffAlloc,
            online_rotation);
    if (pStream == NULL) {
        LOGE("No mem for Stream");
        if (streamInfoBuf != NULL) {
            streamInfoBuf->deallocate();
            delete streamInfoBuf;
            streamInfoBuf = NULL;
        }
        return NO_MEMORY;
    }

    rc = pStream->init(streamInfoBuf, miscBuf, minStreamBufNum,
                       stream_cb, userdata, bDynAllocBuf);
    if (rc == 0) {
        mStreams.add(pStream);
    } else {
        delete pStream;
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : linkStream
 *
 * DESCRIPTION: link a stream into channel
 *
 * PARAMETERS :
 *   @ch      : Channel which the stream belongs to
 *   @stream  : Stream which needs to be linked
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraChannel::linkStream(QCameraChannel *ch, QCameraStream *stream)
{
    int32_t rc = NO_ERROR;

    if ((0 == m_handle) || (NULL == ch) || (NULL == stream)) {
        return NO_INIT;
    }

    int32_t handle = m_camOps->link_stream(m_camHandle,
            ch->getMyHandle(),
            stream->getMyHandle(),
            m_handle);
    if (0 == handle) {
        LOGE("Linking of stream failed");
        rc = INVALID_OPERATION;
    } else {
        mStreams.add(stream);
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : start
 *
 * DESCRIPTION: start channel, which will start all streams belong to this channel
 *
 * PARAMETERS : None
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraChannel::start()
{
    int32_t rc = NO_ERROR;

    if(m_bIsActive) {
        LOGW("Attempt to start active channel");
        return rc;
    }
    if (mStreams.size() > 1) {
        // there is more than one stream in the channel
        // we need to notify mctl that all streams in this channel need to be bundled
        cam_bundle_config_t bundleInfo;
        memset(&bundleInfo, 0, sizeof(bundleInfo));
        rc = m_camOps->get_bundle_info(m_camHandle, m_handle, &bundleInfo);
        if (rc != NO_ERROR) {
            LOGE("get_bundle_info failed");
            return rc;
        }
        if (bundleInfo.num_of_streams > 1) {
            for (int i = 0; i < bundleInfo.num_of_streams; i++) {
                QCameraStream *pStream = getStreamByServerID(bundleInfo.stream_ids[i]);
                if (pStream != NULL) {
                    if ((pStream->isTypeOf(CAM_STREAM_TYPE_METADATA))
                            || (pStream->isTypeOf(CAM_STREAM_TYPE_OFFLINE_PROC))) {
                        // Skip metadata for reprocess now because PP module cannot handle meta data
                        // May need furthur discussion if Imaginglib need meta data
                        continue;
                    }

                    cam_stream_parm_buffer_t param;
                    memset(&param, 0, sizeof(cam_stream_parm_buffer_t));
                    param.type = CAM_STREAM_PARAM_TYPE_SET_BUNDLE_INFO;
                    param.bundleInfo = bundleInfo;
                    rc = pStream->setParameter(param);
                    if (rc != NO_ERROR) {
                        LOGE("stream setParameter for set bundle failed");
                        return rc;
                    }
                }
            }
        }
    }

    for (size_t i = 0; i < mStreams.size(); i++) {
        if ((mStreams[i] != NULL) &&
                (m_handle == mStreams[i]->getChannelHandle())) {
            mStreams[i]->start();
        }
    }
    rc = m_camOps->start_channel(m_camHandle, m_handle);

    if (rc != NO_ERROR) {
        for (size_t i = 0; i < mStreams.size(); i++) {
            if ((mStreams[i] != NULL) &&
                    (m_handle == mStreams[i]->getChannelHandle())) {
                mStreams[i]->stop();
            }
        }
    } else {
        m_bIsActive = true;
        for (size_t i = 0; i < mStreams.size(); i++) {
            if (mStreams[i] != NULL) {
                mStreams[i]->cond_signal();
            }
        }
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : stop
 *
 * DESCRIPTION: stop a channel, which will stop all streams belong to this channel
 *
 * PARAMETERS : none
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraChannel::stop()
{
    int32_t rc = NO_ERROR;
    size_t i = 0;

    if (!m_bIsActive) {
        return NO_INIT;
    }

    while(i < mStreams.size()) {
        if (mStreams[i] != NULL) {
            if (m_handle == mStreams[i]->getChannelHandle()) {
                mStreams[i]->stop();
                i++;
            } else {
                // Remove linked stream from stream list
                mStreams.removeAt(i);
            }
        }
    }

    rc = m_camOps->stop_channel(m_camHandle, m_handle);

    m_bIsActive = false;
    return rc;
}

/*===========================================================================
 * FUNCTION   : bufDone
 *
 * DESCRIPTION: return a stream buf back to kernel
 *
 * PARAMETERS :
 *   @recvd_frame  : stream buf frame to be returned
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraChannel::bufDone(mm_camera_super_buf_t *recvd_frame)
{
    int32_t rc = NO_ERROR;
    for (uint32_t i = 0; i < recvd_frame->num_bufs; i++) {
        if (recvd_frame->bufs[i] != NULL) {
            for (size_t j = 0; j < mStreams.size(); j++) {
                if (mStreams[j] != NULL &&
                        mStreams[j]->getMyHandle() == recvd_frame->bufs[i]->stream_id) {
                    rc = mStreams[j]->bufDone(recvd_frame->bufs[i]->buf_idx);
                    break; // break loop j
                }
            }
        }
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : bufDone
 *
 * DESCRIPTION: return specified buffer from super buffer to kernel
 *
 * PARAMETERS :
 *   @recvd_frame  : stream buf frame to be returned
 *   @stream_id      : stream ID of the buffer to be released
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraChannel::bufDone(mm_camera_super_buf_t *recvd_frame, uint32_t stream_id)
{
    int32_t rc = NO_ERROR;
    int32_t index;
    for (int32_t i = 0; i < (int32_t)recvd_frame->num_bufs; i++) {
        index = -1;
        if ((recvd_frame->bufs[i] != NULL) &&
                (recvd_frame->bufs[i]->stream_id == stream_id)) {
            for (size_t j = 0; j < mStreams.size(); j++) {
                if ((mStreams[j] != NULL) &&
                        (mStreams[j]->getMyHandle() == stream_id)) {
                    rc = mStreams[j]->bufDone(recvd_frame->bufs[i]->buf_idx);
                    index = i;
                    break; // break loop j
                }
            }
            if ((index >= 0) && (index < (int32_t)recvd_frame->num_bufs)) {
                for (int32_t j = index; j < (int32_t)(recvd_frame->num_bufs - 1); j++) {
                    recvd_frame->bufs[j] = recvd_frame->bufs[j + 1];
                }
                recvd_frame->num_bufs--;
                i--;
            }
        }
    }

    return rc;
}

/*===========================================================================
 * FUNCTION   : processZoomDone
 *
 * DESCRIPTION: process zoom done event
 *
 * PARAMETERS :
 *   @previewWindoe : ptr to preview window ops table, needed to set preview
 *                    crop information
 *   @crop_info     : crop info as a result of zoom operation
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraChannel::processZoomDone(preview_stream_ops_t *previewWindow,
                                        cam_crop_data_t &crop_info)
{
    int32_t rc = NO_ERROR;
    for (size_t i = 0; i < mStreams.size(); i++) {
        if ((mStreams[i] != NULL) &&
                (m_handle == mStreams[i]->getChannelHandle())) {
            rc = mStreams[i]->processZoomDone(previewWindow, crop_info);
        }
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : getStreamByHandle
 *
 * DESCRIPTION: return stream object by stream handle
 *
 * PARAMETERS :
 *   @streamHandle : stream handle
 *
 * RETURN     : stream object. NULL if not found
 *==========================================================================*/
QCameraStream *QCameraChannel::getStreamByHandle(uint32_t streamHandle)
{
    for (size_t i = 0; i < mStreams.size(); i++) {
        if (mStreams[i] != NULL && mStreams[i]->getMyHandle() == streamHandle) {
            return mStreams[i];
        }
    }
    return NULL;
}

/*===========================================================================
 * FUNCTION   : getStreamByServerID
 *
 * DESCRIPTION: return stream object by stream server ID from daemon
 *
 * PARAMETERS :
 *   @serverID : stream server ID
 *
 * RETURN     : stream object. NULL if not found
 *==========================================================================*/
QCameraStream *QCameraChannel::getStreamByServerID(uint32_t serverID)
{
    for (size_t i = 0; i < mStreams.size(); i++) {
        if (mStreams[i] != NULL && mStreams[i]->getMyServerID() == serverID) {
            return mStreams[i];
        }
    }
    return NULL;
}

/*===========================================================================
 * FUNCTION   : getStreamByIndex
 *
 * DESCRIPTION: return stream object by index of streams in the channel
 *
 * PARAMETERS :
 *   @index : index of stream in the channel
 *
 * RETURN     : stream object. NULL if not found
 *==========================================================================*/
QCameraStream *QCameraChannel::getStreamByIndex(uint32_t index)
{
    if (index >= MAX_STREAM_NUM_IN_BUNDLE) {
        return NULL;
    }

    if (index < mStreams.size()) {
        return mStreams[index];
    }
    return NULL;
}

/*===========================================================================
 * FUNCTION   : UpdateStreamBasedParameters
 *
 * DESCRIPTION: update any stream based settings from parameters
 *
 * PARAMETERS :
 *   @param   : reference to parameters object
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraChannel::UpdateStreamBasedParameters(QCameraParametersIntf &param)
{
    int32_t rc = NO_ERROR;
    if (param.isPreviewFlipChanged()) {
        // try to find preview stream
        for (size_t i = 0; i < mStreams.size(); i++) {
            if ((mStreams[i] != NULL) &&
                    (m_handle == mStreams[i]->getChannelHandle()) &&
                    (mStreams[i]->isTypeOf(CAM_STREAM_TYPE_PREVIEW) ||
                    (mStreams[i]->isOrignalTypeOf(CAM_STREAM_TYPE_PREVIEW))) ) {
                cam_stream_parm_buffer_t param_buf;
                memset(&param_buf, 0, sizeof(cam_stream_parm_buffer_t));
                param_buf.type = CAM_STREAM_PARAM_TYPE_SET_FLIP;
                param_buf.flipInfo.flip_mask =
                        (uint32_t)param.getFlipMode(CAM_STREAM_TYPE_PREVIEW);
                rc = mStreams[i]->setParameter(param_buf);
                if (rc != NO_ERROR) {
                    LOGW("set preview stream flip failed");
                }
            }
        }
    }
    if (param.isVideoFlipChanged()) {
        // try to find video stream
        for (size_t i = 0; i < mStreams.size(); i++) {
            if ((mStreams[i] != NULL) &&
                    (m_handle == mStreams[i]->getChannelHandle()) &&
                    (mStreams[i]->isTypeOf(CAM_STREAM_TYPE_VIDEO) ||
                    (mStreams[i]->isOrignalTypeOf(CAM_STREAM_TYPE_VIDEO))) ) {
                cam_stream_parm_buffer_t param_buf;
                memset(&param_buf, 0, sizeof(cam_stream_parm_buffer_t));
                param_buf.type = CAM_STREAM_PARAM_TYPE_SET_FLIP;
                param_buf.flipInfo.flip_mask =
                        (uint32_t)param.getFlipMode(CAM_STREAM_TYPE_VIDEO);
                rc = mStreams[i]->setParameter(param_buf);
                if (rc != NO_ERROR) {
                    LOGW("set video stream flip failed");
                }
            }
        }
    }
    if (param.isSnapshotFlipChanged()) {
        // try to find snapshot/postview stream
        for (size_t i = 0; i < mStreams.size(); i++) {
            if (mStreams[i] != NULL &&
                    (m_handle == mStreams[i]->getChannelHandle()) &&
                    (mStreams[i]->isTypeOf(CAM_STREAM_TYPE_SNAPSHOT) ||
                     mStreams[i]->isOrignalTypeOf(CAM_STREAM_TYPE_SNAPSHOT) ||
                     mStreams[i]->isTypeOf(CAM_STREAM_TYPE_POSTVIEW) ||
                 mStreams[i]->isOrignalTypeOf(CAM_STREAM_TYPE_POSTVIEW) ) ) {
                cam_stream_parm_buffer_t param_buf;
                memset(&param_buf, 0, sizeof(cam_stream_parm_buffer_t));
                param_buf.type = CAM_STREAM_PARAM_TYPE_SET_FLIP;
                param_buf.flipInfo.flip_mask =
                        (uint32_t)param.getFlipMode(CAM_STREAM_TYPE_SNAPSHOT);
                rc = mStreams[i]->setParameter(param_buf);
                if (rc != NO_ERROR) {
                    LOGW("set snapshot stream flip failed");
                }
            }
        }
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : QCameraPicChannel
 *
 * DESCRIPTION: constructor of QCameraPicChannel
 *
 * PARAMETERS :
 *   @cam_handle : camera handle
 *   @cam_ops    : ptr to camera ops table
 *
 * RETURN     : none
 *==========================================================================*/
QCameraPicChannel::QCameraPicChannel(uint32_t cam_handle,
                                     mm_camera_ops_t *cam_ops) :
    QCameraChannel(cam_handle, cam_ops)
{
    m_bAllowDynBufAlloc = true;
}

/*===========================================================================
 * FUNCTION   : QCameraPicChannel
 *
 * DESCRIPTION: default constructor of QCameraPicChannel
 *
 * PARAMETERS : none
 *
 * RETURN     : none
 *==========================================================================*/
QCameraPicChannel::QCameraPicChannel()
{
    m_bAllowDynBufAlloc = true;
}

/*===========================================================================
 * FUNCTION   : ~QCameraPicChannel
 *
 * DESCRIPTION: destructor of QCameraPicChannel
 *
 * PARAMETERS : none
 *
 * RETURN     : none
 *==========================================================================*/
QCameraPicChannel::~QCameraPicChannel()
{
}

/*===========================================================================
 * FUNCTION   : takePicture
 *
 * DESCRIPTION: send request for queued snapshot frames
 *
 * PARAMETERS :
 *   @buf : request buf info
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraPicChannel::takePicture (mm_camera_req_buf_t *buf)
{
    int32_t rc = m_camOps->request_super_buf(m_camHandle, m_handle, buf);
    return rc;
}

/*===========================================================================
 * FUNCTION   : cancelPicture
 *
 * DESCRIPTION: cancel request for queued snapshot frames
 *
 * PARAMETERS : none
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraPicChannel::cancelPicture()
{
    int32_t rc = m_camOps->cancel_super_buf_request(m_camHandle, m_handle);
    return rc;
}

/*===========================================================================
 * FUNCTION   : stopAdvancedCapture
 *
 * DESCRIPTION: stop advanced capture based on advanced capture type.
 *
 * PARAMETERS :
 *   @type : advanced capture type.
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraPicChannel::stopAdvancedCapture(mm_camera_advanced_capture_t type)
{
    int32_t rc = m_camOps->process_advanced_capture(m_camHandle,
            m_handle, type, 0, NULL);
    return rc;
}

/*===========================================================================
 * FUNCTION   : startAdvancedCapture
 *
 * DESCRIPTION: start advanced capture based on advanced capture type.
 *
 * PARAMETERS :
 *   @type : advanced capture type.
 *   @config: advance capture config
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraPicChannel::startAdvancedCapture(mm_camera_advanced_capture_t type,
        cam_capture_frame_config_t *config)
{
    int32_t rc = NO_ERROR;

    rc = m_camOps->process_advanced_capture(m_camHandle, m_handle, type,
            1, config);
    return rc;
}

/*===========================================================================
* FUNCTION   : flushSuperbuffer
 *
 * DESCRIPTION: flush the all superbuffer frames.
 *
 * PARAMETERS :
 *   @frame_idx : frame index of focused frame
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraPicChannel::flushSuperbuffer(uint32_t frame_idx)
{
    int32_t rc = m_camOps->flush_super_buf_queue(m_camHandle, m_handle, frame_idx);
    return rc;
}

/*===========================================================================
 * FUNCTION   : QCameraVideoChannel
 *
 * DESCRIPTION: constructor of QCameraVideoChannel
 *
 * PARAMETERS :
 *   @cam_handle : camera handle
 *   @cam_ops    : ptr to camera ops table
 *
 * RETURN     : none
 *==========================================================================*/
QCameraVideoChannel::QCameraVideoChannel(uint32_t cam_handle,
                                         mm_camera_ops_t *cam_ops) :
    QCameraChannel(cam_handle, cam_ops)
{
}

/*===========================================================================
 * FUNCTION   : QCameraVideoChannel
 *
 * DESCRIPTION: default constructor of QCameraVideoChannel
 *
 * PARAMETERS : none
 *
 * RETURN     : none
 *==========================================================================*/
QCameraVideoChannel::QCameraVideoChannel()
{
}

/*===========================================================================
 * FUNCTION   : ~QCameraVideoChannel
 *
 * DESCRIPTION: destructor of QCameraVideoChannel
 *
 * PARAMETERS : none
 *
 * RETURN     : none
 *==========================================================================*/
QCameraVideoChannel::~QCameraVideoChannel()
{
}

/*===========================================================================
 * FUNCTION   : takePicture
 *
 * DESCRIPTION: send request for queued snapshot frames
 *
 * PARAMETERS :
 *   @mm_camera_req_buf_t : request buf info
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraVideoChannel::takePicture(mm_camera_req_buf_t *buf)
{
    int32_t rc = m_camOps->request_super_buf(m_camHandle, m_handle, buf);
    return rc;
}

/*===========================================================================
 * FUNCTION   : cancelPicture
 *
 * DESCRIPTION: cancel request for queued snapshot frames
 *
 * PARAMETERS : none
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraVideoChannel::cancelPicture()
{
    int32_t rc = m_camOps->cancel_super_buf_request(m_camHandle, m_handle);
    return rc;
}

/*===========================================================================
 * FUNCTION   : releaseFrame
 *
 * DESCRIPTION: return video frame from app
 *
 * PARAMETERS :
 *   @opaque     : ptr to video frame to be returned
 *   @isMetaData : if frame is a metadata or real frame
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraVideoChannel::releaseFrame(const void * opaque, bool isMetaData)
{
    QCameraStream *pVideoStream = NULL;
    for (size_t i = 0; i < mStreams.size(); i++) {
        if (mStreams[i] != NULL && mStreams[i]->isTypeOf(CAM_STREAM_TYPE_VIDEO)) {
            pVideoStream = mStreams[i];
            break;
        }
    }

    if (NULL == pVideoStream) {
        LOGE("No video stream in the channel");
        return BAD_VALUE;
    }

    int32_t rc = pVideoStream->bufDone(opaque, isMetaData);
    return rc;
}

/*===========================================================================
 * FUNCTION   : QCameraReprocessChannel
 *
 * DESCRIPTION: constructor of QCameraReprocessChannel
 *
 * PARAMETERS :
 *   @cam_handle : camera handle
 *   @cam_ops    : ptr to camera ops table
 *
 * RETURN     : none
 *==========================================================================*/
QCameraReprocessChannel::QCameraReprocessChannel(uint32_t cam_handle,
                                                 mm_camera_ops_t *cam_ops) :
    QCameraChannel(cam_handle, cam_ops),
    m_pSrcChannel(NULL),
    mPassCount(0)
{
    memset(mSrcStreamHandles, 0, sizeof(mSrcStreamHandles));
}

/*===========================================================================
 * FUNCTION   : QCameraReprocessChannel
 *
 * DESCRIPTION: default constructor of QCameraReprocessChannel
 *
 * PARAMETERS : none
 *
 * RETURN     : none
 *==========================================================================*/
QCameraReprocessChannel::QCameraReprocessChannel() :
    m_pSrcChannel(NULL),
    mPassCount(0)
{
}

/*===========================================================================
 * FUNCTION   : ~QCameraReprocessChannel
 *
 * DESCRIPTION: destructor of QCameraReprocessChannel
 *
 * PARAMETERS : none
 *
 * RETURN     : none
 *==========================================================================*/
QCameraReprocessChannel::~QCameraReprocessChannel()
{
}

/*===========================================================================
 * FUNCTION   : addReprocStreamsFromSource
 *
 * DESCRIPTION: add reprocess streams from input source channel
 *
 * PARAMETERS :
 *   @allocator      : stream related buffer allocator
 *   @featureConfig  : pp feature configuration
 *   @pSrcChannel    : ptr to input source channel that needs reprocess
 *   @minStreamBufNum: number of stream buffers needed
 *   @burstNum       : number of burst captures needed
 *   @paddingInfo    : padding information
 *   @param          : reference to parameters
 *   @contStream     : continous streaming mode or burst
 *   @offline        : configure for offline reprocessing
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraReprocessChannel::addReprocStreamsFromSource(
        QCameraAllocator& allocator, cam_pp_feature_config_t &featureConfig,
        QCameraChannel *pSrcChannel, uint8_t minStreamBufNum, uint8_t burstNum,
        cam_padding_info_t *paddingInfo, QCameraParametersIntf &param, bool contStream,
        bool offline)
{
    int32_t rc = 0;
    QCameraStream *pStream = NULL;
    QCameraHeapMemory *pStreamInfoBuf = NULL;
    QCameraHeapMemory *pMiscBuf = NULL;
    cam_stream_info_t *streamInfo = NULL;
    cam_padding_info_t padding;

    memset(mSrcStreamHandles, 0, sizeof(mSrcStreamHandles));
    if (NULL == paddingInfo) {
        return BAD_VALUE;
    }
    padding = *paddingInfo;
    //Use maximum padding so that the buffer
    //can be rotated
    padding.width_padding = MAX(padding.width_padding, padding.height_padding);
    padding.height_padding = padding.width_padding;
    padding.offset_info.offset_x = 0;
    padding.offset_info.offset_y = 0;

    LOGD("num of src stream = %d", pSrcChannel->getNumOfStreams());

    for (uint32_t i = 0; i < pSrcChannel->getNumOfStreams(); i++) {
        cam_pp_feature_config_t pp_featuremask = featureConfig;
        pStream = pSrcChannel->getStreamByIndex(i);
        if (pStream != NULL) {
            if (param.getofflineRAW() && !((pStream->isTypeOf(CAM_STREAM_TYPE_RAW))
                    || (pStream->isTypeOf(CAM_STREAM_TYPE_POSTVIEW))
                    || (pStream->isTypeOf(CAM_STREAM_TYPE_METADATA))
                    || (pStream->isOrignalTypeOf(CAM_STREAM_TYPE_RAW)))) {
                //Skip all the stream other than RAW and POSTVIEW incase of offline of RAW
                continue;
            }

            if (pStream->isTypeOf(CAM_STREAM_TYPE_RAW)
                    && (!param.getofflineRAW())) {
                // Skip raw for reprocess now because PP module cannot handle
                // meta data&raw. May need furthur discussion if Imaginglib need meta data
                continue;
            }

            if (((pStream->isTypeOf(CAM_STREAM_TYPE_METADATA))
                    && !(param.getManualCaptureMode() >=
                    CAM_MANUAL_CAPTURE_TYPE_3))
                    || (pStream->isTypeOf(CAM_STREAM_TYPE_ANALYSIS))) {
                // Skip metadata
                continue;
            }

            if (pStream->isTypeOf(CAM_STREAM_TYPE_PREVIEW) ||
                    pStream->isTypeOf(CAM_STREAM_TYPE_POSTVIEW) ||
                    pStream->isOrignalTypeOf(CAM_STREAM_TYPE_PREVIEW) ||
                    pStream->isOrignalTypeOf(CAM_STREAM_TYPE_POSTVIEW)) {
                cam_feature_mask_t feature_mask = featureConfig.feature_mask;

                // skip thumbnail reprocessing if not needed
                if (!param.needThumbnailReprocess(&feature_mask)) {
                    continue;
                }
                // CAC, SHARPNESS, FLIP and WNR would have been already applied -
                // on preview/postview stream in realtime.
                // So, need not apply again.
                feature_mask &= ~(CAM_QCOM_FEATURE_DENOISE2D |
                        CAM_QCOM_FEATURE_CAC |
                        CAM_QCOM_FEATURE_SHARPNESS |
                        CAM_QCOM_FEATURE_FLIP |
                        CAM_QCOM_FEATURE_RAW_PROCESSING);
                if (!feature_mask) {
                    // Skip thumbnail stream reprocessing since no other
                    //reprocessing is enabled.
                    continue;
                }
            }

            if (pStream->isTypeOf(CAM_STREAM_TYPE_METADATA)) {
                pp_featuremask.feature_mask = 0;
                pp_featuremask.feature_mask |= CAM_QCOM_FEATURE_METADATA_PROCESSING;
            }

            pStreamInfoBuf = allocator.allocateStreamInfoBuf(CAM_STREAM_TYPE_OFFLINE_PROC);
            if (pStreamInfoBuf == NULL) {
                LOGE("no mem for stream info buf");
                rc = NO_MEMORY;
                break;
            }

            streamInfo = (cam_stream_info_t *)pStreamInfoBuf->getPtr(0);
            memset(streamInfo, 0, sizeof(cam_stream_info_t));
            streamInfo->stream_type = CAM_STREAM_TYPE_OFFLINE_PROC;
            // Enable CPP high performance mode to put it in turbo frequency mode for
            // burst/longshot/HDR snapshot cases
            streamInfo->perf_mode = CAM_PERF_HIGH_PERFORMANCE;
            if (param.getofflineRAW() && pStream->isTypeOf(CAM_STREAM_TYPE_RAW)) {
                streamInfo->fmt = CAM_FORMAT_YUV_420_NV21;
            } else {
                rc = pStream->getFormat(streamInfo->fmt);
            }

            if (pStream->isTypeOf(CAM_STREAM_TYPE_POSTVIEW) ||
                    pStream->isTypeOf(CAM_STREAM_TYPE_PREVIEW)) {
                param.getThumbnailSize(&(streamInfo->dim.width), &(streamInfo->dim.height));
            } else {
                if ((param.isPostProcScaling()) &&
                        (pp_featuremask.feature_mask & CAM_QCOM_FEATURE_SCALE)) {
                    rc = param.getStreamDimension(CAM_STREAM_TYPE_OFFLINE_PROC,
                            streamInfo->dim);
                } else if ((param.getofflineRAW()) &&
                        (pStream->isTypeOf(CAM_STREAM_TYPE_RAW))) {
                    param.getStreamDimension(CAM_STREAM_TYPE_SNAPSHOT,streamInfo->dim);
                } else {
                    rc = pStream->getFrameDimension(streamInfo->dim);
                }
            }

            if ( contStream ) {
                streamInfo->streaming_mode = CAM_STREAMING_MODE_CONTINUOUS;
                streamInfo->num_of_burst = 0;
            } else {
                streamInfo->streaming_mode = CAM_STREAMING_MODE_BURST;
                streamInfo->num_of_burst = burstNum;
            }
            streamInfo->num_bufs = minStreamBufNum;

            cam_stream_reproc_config_t rp_cfg;
            memset(&rp_cfg, 0, sizeof(cam_stream_reproc_config_t));
            if (offline) {
                cam_frame_len_offset_t offset;
                memset(&offset, 0, sizeof(cam_frame_len_offset_t));

                rp_cfg.pp_type = CAM_OFFLINE_REPROCESS_TYPE;
                pStream->getFormat(rp_cfg.offline.input_fmt);
                pStream->getFrameDimension(rp_cfg.offline.input_dim);
                pStream->getFrameOffset(offset);
                rp_cfg.offline.input_buf_planes.plane_info = offset;
                rp_cfg.offline.input_type = pStream->getMyOriginalType();
                //For input metadata + input buffer
                rp_cfg.offline.num_of_bufs = 2;
            } else {
                rp_cfg.pp_type = CAM_ONLINE_REPROCESS_TYPE;
                rp_cfg.online.input_stream_id = pStream->getMyServerID();
                rp_cfg.online.input_stream_type = pStream->getMyOriginalType();
            }
            param.getStreamRotation(streamInfo->stream_type,
                    streamInfo->pp_config, streamInfo->dim);
            streamInfo->reprocess_config = rp_cfg;
            streamInfo->reprocess_config.pp_feature_config = pp_featuremask;

            if (!(pStream->isTypeOf(CAM_STREAM_TYPE_SNAPSHOT)
                || pStream->isOrignalTypeOf(CAM_STREAM_TYPE_SNAPSHOT)
                || pStream->isTypeOf(CAM_STREAM_TYPE_RAW)
                || pStream->isOrignalTypeOf(CAM_STREAM_TYPE_RAW))) {
                // CAC, SHARPNESS, FLIP and WNR would have been already applied -
                // on preview/postview stream in realtime. Need not apply again.
                streamInfo->reprocess_config.pp_feature_config.feature_mask &=
                        ~CAM_QCOM_FEATURE_CAC;
                streamInfo->reprocess_config.pp_feature_config.feature_mask &=
                        ~CAM_QCOM_FEATURE_SHARPNESS;
                streamInfo->reprocess_config.pp_feature_config.feature_mask &=
                        ~CAM_QCOM_FEATURE_FLIP;
                //Don't do WNR for thumbnail
                streamInfo->reprocess_config.pp_feature_config.feature_mask &=
                        ~CAM_QCOM_FEATURE_DENOISE2D;
                streamInfo->reprocess_config.pp_feature_config.feature_mask &=
                        ~CAM_QCOM_FEATURE_CDS;
                streamInfo->reprocess_config.pp_feature_config.feature_mask &=
                        ~CAM_QCOM_FEATURE_DSDN;
                //No need of RAW processing for other than RAW streams
                streamInfo->reprocess_config.pp_feature_config.feature_mask &=
                        ~CAM_QCOM_FEATURE_RAW_PROCESSING;

                if (param.isHDREnabled()
                  && !param.isHDRThumbnailProcessNeeded()){
                    streamInfo->reprocess_config.pp_feature_config.feature_mask
                      &= ~CAM_QCOM_FEATURE_HDR;
                }
            }

            cam_stream_type_t type = CAM_STREAM_TYPE_DEFAULT;
            if (offline) {
                type = streamInfo->reprocess_config.offline.input_type;
            } else {
                type = streamInfo->reprocess_config.online.input_stream_type;
            }
            if (type == CAM_STREAM_TYPE_SNAPSHOT) {
                int flipMode = param.getFlipMode(type);
                if (flipMode > 0) {
                    streamInfo->reprocess_config.pp_feature_config.feature_mask |=
                            CAM_QCOM_FEATURE_FLIP;
                    streamInfo->reprocess_config.pp_feature_config.flip = (uint32_t)flipMode;
                }
            }

            if ((streamInfo->reprocess_config.pp_feature_config.feature_mask
                    & CAM_QCOM_FEATURE_SCALE)
                    && param.isReprocScaleEnabled()
                    && param.isUnderReprocScaling()) {
                //we only Scale Snapshot frame
                if (pStream->isTypeOf(CAM_STREAM_TYPE_SNAPSHOT)) {
                    streamInfo->dim.width =
                            streamInfo->reprocess_config.pp_feature_config.scale_param.output_width;
                    streamInfo->dim.height =
                            streamInfo->reprocess_config.pp_feature_config.scale_param.output_height;
                }
                LOGH("stream width=%d, height=%d.",
                         streamInfo->dim.width, streamInfo->dim.height);
            }

            // save source stream handler
            mSrcStreamHandles[mStreams.size()] = pStream->getMyHandle();

            pMiscBuf = allocator.allocateMiscBuf(streamInfo);

            LOGH("Configure Reprocessing: stream = %d, res = %dX%d, fmt = %d, type = %d",
                    pStream->getMyOriginalType(), streamInfo->dim.width,
                    streamInfo->dim.height, streamInfo->fmt, type);

            // add reprocess stream
            if (streamInfo->reprocess_config.pp_feature_config.feature_mask
                    & CAM_QCOM_FEATURE_ROTATION) {
                rc = addStream(allocator, pStreamInfoBuf, pMiscBuf,
                        minStreamBufNum, &padding, NULL, NULL, false, false,
                        streamInfo->reprocess_config.pp_feature_config.rotation);
            } else {
                rc = addStream(allocator, pStreamInfoBuf, pMiscBuf,
                        minStreamBufNum, &padding, NULL, NULL, false, false);
            }
            if (rc != NO_ERROR) {
                LOGE("add reprocess stream failed, ret = %d", rc);
                break;
            }
        }
    }

    if (rc == NO_ERROR) {
        m_pSrcChannel = pSrcChannel;
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : getStreamBySrouceHandle
 *
 * DESCRIPTION: find reprocess stream by its source stream handle
 *
 * PARAMETERS :
 *   @srcHandle : source stream handle
 *
 * RETURN     : ptr to reprocess stream if found. NULL if not found
 *==========================================================================*/
QCameraStream * QCameraReprocessChannel::getStreamBySrouceHandle(uint32_t srcHandle)
{
    QCameraStream *pStream = NULL;

    for (size_t i = 0; i < mStreams.size(); i++) {
        if (mSrcStreamHandles[i] == srcHandle) {
            pStream = mStreams[i];
            break;
        }
    }

    return pStream;
}

/*===========================================================================
 * FUNCTION   : stop
 *
 * DESCRIPTION: stop channel and unmap offline buffers
 *
 * PARAMETERS : none
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraReprocessChannel::stop()
{
    int32_t rc = QCameraChannel::stop();

    if (!mOfflineBuffers.empty()) {
        QCameraStream *stream = NULL;
        List<OfflineBuffer>::iterator it = mOfflineBuffers.begin();
        int error = NO_ERROR;
        for( ; it != mOfflineBuffers.end(); it++) {
            stream = (*it).stream;
            if (NULL != stream) {
                error = stream->unmapBuf((*it).type,
                                         (*it).index,
                                         -1);
                if (NO_ERROR != error) {
                    LOGE("Error during offline buffer unmap %d",
                           error);
                }
            }
        }
        mOfflineBuffers.clear();
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : doReprocessOffline
 *
 * DESCRIPTION: request to do offline reprocess on the frame
 *
 * PARAMETERS :
 *   @frame   : frame to be performed a reprocess
 *   @meta_buf : Metadata buffer for reprocessing
 *   @pStream  : Actual reprocess stream
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraReprocessChannel::doReprocessOffline(mm_camera_buf_def_t *frame,
        mm_camera_buf_def_t *meta_buf, QCameraStream *pStream)
{
    int32_t rc = 0;
    OfflineBuffer mappedBuffer;
    uint32_t buf_index = 0;
    uint32_t meta_buf_index = 0;

    if ((frame == NULL) || (meta_buf == NULL)) {
        LOGE("Invalid Input Paramters");
        return INVALID_OPERATION;
    }

    if (pStream == NULL) {
        pStream = getStreamBySrouceHandle(frame->stream_id);
        if (pStream == NULL) {
            LOGE("Input validation failed.");
            return INVALID_OPERATION;
        }
    }

    if (!mOfflineBuffers.empty()) {
        List<OfflineBuffer>::iterator it = mOfflineBuffers.begin();
        for( ; it != mOfflineBuffers.end(); it++) {
            buf_index = (buf_index < ((*it).index)) ? ((*it).index) : buf_index;
        }
        buf_index += 1;
    }

    meta_buf_index = buf_index;
    if (meta_buf != NULL) {
        rc = pStream->mapBuf(CAM_MAPPING_BUF_TYPE_OFFLINE_META_BUF,
                meta_buf_index,
                -1,
                meta_buf->fd,
                meta_buf->buffer,
                meta_buf->frame_len);
        if (NO_ERROR != rc ) {
            LOGE("Error during metadata buffer mapping");
            rc = -1;
            return rc;
        }

        mappedBuffer.index = meta_buf_index;
        mappedBuffer.stream = pStream;
        mappedBuffer.type = CAM_MAPPING_BUF_TYPE_OFFLINE_META_BUF;
        mOfflineBuffers.push_back(mappedBuffer);
        buf_index += 1;
    }

    rc = pStream->mapBuf(CAM_MAPPING_BUF_TYPE_OFFLINE_INPUT_BUF,
             buf_index,
             -1,
             frame->fd,
             frame->buffer,
             frame->frame_len);
    if (NO_ERROR != rc ) {
        LOGE("Error during reprocess input buffer mapping");
        rc = -1;
        return rc;
    }
    mappedBuffer.index = buf_index;
    mappedBuffer.stream = pStream;
    mappedBuffer.type = CAM_MAPPING_BUF_TYPE_OFFLINE_INPUT_BUF;
    mOfflineBuffers.push_back(mappedBuffer);

    cam_stream_parm_buffer_t param;
    memset(&param, 0, sizeof(cam_stream_parm_buffer_t));

    param.type = CAM_STREAM_PARAM_TYPE_DO_REPROCESS;
    param.reprocess.buf_index = buf_index;
    param.reprocess.frame_idx = frame->frame_idx;

    if (meta_buf != NULL) {
        param.reprocess.meta_present = 1;
        param.reprocess.meta_buf_index = meta_buf_index;
    }

    LOGI("Offline reprocessing id = %d buf Id = %d meta index = %d type = %d",
             param.reprocess.frame_idx, param.reprocess.buf_index,
            param.reprocess.meta_buf_index, pStream->getMyOriginalType());

    rc = pStream->setParameter(param);
    if (rc != NO_ERROR) {
        LOGE("stream setParameter for reprocess failed");
        return rc;
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : doReprocessOffline
 *
 * DESCRIPTION: request to do offline reprocess on the frame
 *
 * PARAMETERS :
 *   @frame   : frame to be performed a reprocess
 *   @meta_buf : Metadata buffer for reprocessing
 *   @mParameter : camera parameters
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraReprocessChannel::doReprocessOffline(mm_camera_super_buf_t *frame,
        mm_camera_buf_def_t *meta_buf, QCameraParametersIntf &mParameter)
{
    int32_t rc = 0;
    QCameraStream *pStream = NULL;

    if (mStreams.size() < 1) {
        LOGE("No reprocess streams");
        return -1;
    }
    if (m_pSrcChannel == NULL) {
        LOGE("No source channel for reprocess");
        return -1;
    }

    if (frame == NULL) {
        LOGE("Invalid source frame");
        return BAD_VALUE;
    }

    for (uint32_t i = 0; i < frame->num_bufs; i++) {
        pStream = getStreamBySrouceHandle(frame->bufs[i]->stream_id);
        if ((pStream != NULL) &&
                (m_handle == pStream->getChannelHandle())) {
            if (mParameter.getofflineRAW() &&
                    !((pStream->isOrignalTypeOf(CAM_STREAM_TYPE_RAW))
                    || (pStream->isOrignalTypeOf(CAM_STREAM_TYPE_METADATA)))) {
                continue;
            }

            if ((pStream->isOrignalTypeOf(CAM_STREAM_TYPE_METADATA)
                     && (mParameter.getManualCaptureMode()
                     < CAM_MANUAL_CAPTURE_TYPE_3))
                     || (pStream->isTypeOf(CAM_STREAM_TYPE_ANALYSIS))) {
                // Skip metadata for reprocess now because PP module cannot handle meta data
                // May need furthur discussion if Imaginglib need meta data
                continue;
            }

            // Update Metadata
            if (meta_buf != NULL) {
                uint32_t stream_id = frame->bufs[i]->stream_id;
                QCameraStream *srcStream =
                        m_pSrcChannel->getStreamByHandle(stream_id);
                metadata_buffer_t *pMetaData =
                        (metadata_buffer_t *)meta_buf->buffer;
                if ((NULL != pMetaData) && (NULL != srcStream)) {
                    IF_META_AVAILABLE(cam_crop_data_t, crop,
                            CAM_INTF_META_CROP_DATA, pMetaData) {
                        if (MAX_NUM_STREAMS > crop->num_of_streams) {
                            for (int j = 0; j < MAX_NUM_STREAMS; j++) {
                                if (crop->crop_info[j].stream_id ==
                                            srcStream->getMyServerID()) {
                                    // Store crop/roi information for offline reprocess
                                    // in the reprocess stream slot
                                    crop->crop_info[crop->num_of_streams].crop =
                                            crop->crop_info[j].crop;
                                    crop->crop_info[crop->num_of_streams].roi_map =
                                            crop->crop_info[j].roi_map;
                                    for (uint8_t k = 0; k < mStreams.size(); k++) {
                                        if (srcStream->getMyType() ==
                                                mStreams[k]->getMyOriginalType()) {
                                            crop->crop_info[crop->num_of_streams].stream_id =
                                                    mStreams[k]->getMyServerID();
                                            break;
                                        }
                                    }
                                    crop->num_of_streams++;
                                    break;
                                }
                            }
                        } else {
                            LOGE("No space to add reprocess stream crop/roi information");
                        }
                    }
                }
            }

            rc = doReprocessOffline (frame->bufs[i], meta_buf, pStream);
        }
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : doReprocess
 *
 * DESCRIPTION: request to do a reprocess on the frame
 *
 * PARAMETERS :
 *   @frame   : frame to be performed a reprocess
 *   @mParameter : camera parameters
 *   @pMetaStream: Metadata stream handle
 *   @meta_buf_index : Metadata buffer index
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraReprocessChannel::doReprocess(mm_camera_super_buf_t *frame,
        QCameraParametersIntf &mParameter, QCameraStream *pMetaStream,
        uint8_t meta_buf_index)
{
    int32_t rc = 0;
    if (mStreams.size() < 1) {
        LOGE("No reprocess streams");
        return -1;
    }
    if (m_pSrcChannel == NULL) {
        LOGE("No source channel for reprocess");
        return -1;
    }

    if (pMetaStream == NULL) {
        LOGW("Null Metadata buffer for processing");
    }

    for (uint32_t i = 0; i < frame->num_bufs; i++) {
        QCameraStream *pStream = getStreamBySrouceHandle(frame->bufs[i]->stream_id);
        if ((pStream != NULL) && (m_handle == pStream->getChannelHandle())) {
            if (mParameter.getofflineRAW() && !((pStream->isOrignalTypeOf(CAM_STREAM_TYPE_RAW))
                    || (pStream->isOrignalTypeOf(CAM_STREAM_TYPE_POSTVIEW))
                    || (pStream->isOrignalTypeOf(CAM_STREAM_TYPE_METADATA)))) {
                //Skip all the stream other than RAW and POSTVIEW incase of offline of RAW
                continue;
            }
            if ((pStream->isOrignalTypeOf(CAM_STREAM_TYPE_METADATA)
                     && (mParameter.getManualCaptureMode()
                     < CAM_MANUAL_CAPTURE_TYPE_3))
                     || (pStream->isTypeOf(CAM_STREAM_TYPE_ANALYSIS))) {
                // Skip metadata for reprocess now because PP module cannot handle meta data
                // May need furthur discussion if Imaginglib need meta data
                continue;
            }

            cam_stream_parm_buffer_t param;
            memset(&param, 0, sizeof(cam_stream_parm_buffer_t));
            param.type = CAM_STREAM_PARAM_TYPE_DO_REPROCESS;
            param.reprocess.buf_index = frame->bufs[i]->buf_idx;
            param.reprocess.frame_idx = frame->bufs[i]->frame_idx;
            if (pMetaStream != NULL) {
                // we have meta data frame bundled, sent together with reprocess frame
                param.reprocess.meta_present = 1;
                param.reprocess.meta_stream_handle = pMetaStream->getMyServerID();
                param.reprocess.meta_buf_index = meta_buf_index;
            }

            LOGI("Online reprocessing id = %d buf Id = %d meta index = %d type = %d",
                     param.reprocess.frame_idx, param.reprocess.buf_index,
                    param.reprocess.meta_buf_index, pStream->getMyOriginalType());

            rc = pStream->setParameter(param);
            if (rc != NO_ERROR) {
                LOGE("stream setParameter for reprocess failed");
                break;
            }
        }
    }
    return rc;
}

/*===========================================================================
 * FUNCTION   : doReprocess
 *
 * DESCRIPTION: request to do a reprocess on the frame
 *
 * PARAMETERS :
 *   @buf_fd     : fd to the input buffer that needs reprocess
 *   @buffer     : buffer pointer of actual buffer
 *   @buf_lenght : length of the input buffer
 *   @ret_val    : result of reprocess.
 *                 Example: Could be faceID in case of register face image.
 *
 * RETURN     : int32_t type of status
 *              NO_ERROR  -- success
 *              none-zero failure code
 *==========================================================================*/
int32_t QCameraReprocessChannel::doReprocess(int buf_fd, void *buffer,
        size_t buf_length, int32_t &ret_val)
{
    int32_t rc = 0;
    if (mStreams.size() < 1) {
        LOGE("No reprocess streams");
        return -1;
    }

    uint32_t buf_idx = 0;
    for (size_t i = 0; i < mStreams.size(); i++) {
        if ((mStreams[i] != NULL) &&
                (m_handle != mStreams[i]->getChannelHandle())) {
            continue;
        }
        rc = mStreams[i]->mapBuf(CAM_MAPPING_BUF_TYPE_OFFLINE_INPUT_BUF,
                                 buf_idx, -1,
                                 buf_fd, buffer, buf_length);

        if (rc == NO_ERROR) {
            cam_stream_parm_buffer_t param;
            memset(&param, 0, sizeof(cam_stream_parm_buffer_t));
            param.type = CAM_STREAM_PARAM_TYPE_DO_REPROCESS;
            param.reprocess.buf_index = buf_idx;
            rc = mStreams[i]->setParameter(param);
            if (rc == NO_ERROR) {
                ret_val = param.reprocess.ret_val;
            }
            mStreams[i]->unmapBuf(CAM_MAPPING_BUF_TYPE_OFFLINE_INPUT_BUF,
                                  buf_idx, -1);
        }
    }
    return rc;
}

}; // namespace qcamera