/*
 * Copyright (c) 2020-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "camera_device.h"

#include <fcntl.h>
#include <pthread.h>
#include <string>
#include <sys/io.h>
#include <sys/prctl.h>
#include <sys/select.h>
#include <thread>
#include <unistd.h>
#include "codec_interface.h"
#include "display_layer.h"
#include "hal_camera.h"
#include "media_log.h"
#include "meta_data.h"
#include "securec.h"

#include <iostream>

using namespace OHOS;
using namespace OHOS::Media;
using namespace std;

/** Indicates that the current frame is an Instantaneous Decoder Refresh (IDR) frame. */
const int32_t KEY_IS_SYNC_FRAME = 1;
/** Indicates the frame timestamp. */
const int32_t KEY_TIME_US = 2;

const int32_t IMAGE_WIDTH = 3;       // "DATA_PIX_FORMAT"
const int32_t IMAGE_HEIGHT = 4;       // "DATA_PIX_FORMAT"
const int32_t IMAGE_SIZE = 5;       // "DATA_PIX_FORMAT"
const int32_t DELAY_TIME_ONE_FRAME = 30000;
const int32_t VIDEO_MAX_NUM = 2;        // "video max num"
const int32_t INVALID_STREAM_ID = -1;

namespace OHOS {
namespace Media {
extern Surface *g_surface;

AvCodecMime ConverFormat(ImageFormat format)
{
    if (format == FORMAT_JPEG) {
        return MEDIA_MIMETYPE_IMAGE_JPEG;
    } else if (format == FORMAT_AVC) {
        return MEDIA_MIMETYPE_VIDEO_AVC;
    } else if (format == FORMAT_HEVC) {
        return MEDIA_MIMETYPE_VIDEO_HEVC;
    } else {
        return MEDIA_MIMETYPE_INVALID;
    }
}

static int32_t SetVencSource(CODEC_HANDLETYPE codecHdl, uint32_t deviceId)
{
    Param param = {.key = KEY_DEVICE_ID, .val = (void *)&deviceId, .size = sizeof(uint32_t)};
    int32_t ret = CodecSetParameter(codecHdl, &param, 1);
    if (ret != 0) {
        MEDIA_ERR_LOG("Set enc source failed.(ret=%d)", ret);
        return ret;
    }
    return MEDIA_OK;
}

static uint32_t GetDefaultBitrate(uint32_t width, uint32_t height)
{
    uint32_t rate; /* auto calc bitrate if set 0 */
    if (width * height == 640 * 360) { /* 640,width  360,height */
        rate = 0x800; /* 2048kbps */
    } else if (width * height == 1280 * 720) { /* 1280,width  720,height */
        rate = 0x400; /* 1024kbps */
    } else if (width * height >= 2560 * 1440 && width * height <= 2716 * 1524) { /* 2560,2716 width  1440,1524,height */
        rate = 0x1800; /* 6144kbps */
    } else if (width * height == 3840 * 2160 || width * height == 4096 * 2160) { /* 3840,4096 width  2160,height */
        rate = 0xa000; /* 40960kbps */
    } else {
        rate = 0x0;
    }
    return rate;
}

static int32_t CameraCreateVideoEnc(FrameConfig &fc,
                                    StreamAttr stream,
                                    uint32_t srcDev,
                                    CODEC_HANDLETYPE *codecHdl)
{
    const uint32_t maxParamNum = 10;
    uint32_t paramIndex = 0;
    Param param[maxParamNum];

    CodecType domainKind = VIDEO_ENCODER;
    param[paramIndex].key = KEY_CODEC_TYPE;
    param[paramIndex].val = &domainKind;
    param[paramIndex].size = sizeof(CodecType);
    paramIndex++;

    AvCodecMime codecMime = ConverFormat(stream.format);
    param[paramIndex].key = KEY_MIMETYPE;
    param[paramIndex].val = &codecMime;
    param[paramIndex].size = sizeof(AvCodecMime);
    paramIndex++;

    VideoCodecRcMode rcMode = VID_CODEC_RC_CBR;
    param[paramIndex].key = KEY_VIDEO_RC_MODE;
    param[paramIndex].val = &rcMode;
    param[paramIndex].size = sizeof(VideoCodecRcMode);
    paramIndex++;

    VideoCodecGopMode gopMode = VID_CODEC_GOPMODE_NORMALP;
    param[paramIndex].key = KEY_VIDEO_GOP_MODE;
    param[paramIndex].val = &gopMode;
    param[paramIndex].size = sizeof(VideoCodecGopMode);
    paramIndex++;

    Profile profile = HEVC_MAIN_PROFILE;
    param[paramIndex].key = KEY_VIDEO_PROFILE;
    param[paramIndex].val = &profile;
    param[paramIndex].size = sizeof(Profile);
    paramIndex++;

#if (!defined(__LINUX__)) || (defined(ENABLE_PASSTHROUGH_MODE))
    uint32_t width = stream.width;
    uint32_t height = stream.height;
#else
    uint32_t width = g_surface->GetWidth();
    uint32_t height = g_surface->GetHeight();
#endif

    MEDIA_DEBUG_LOG("width=%d", width);
    param[paramIndex].key = KEY_VIDEO_WIDTH;
    param[paramIndex].val = &width;
    param[paramIndex].size = sizeof(uint32_t);
    paramIndex++;

    MEDIA_DEBUG_LOG("height=%d", height);
    param[paramIndex].key = KEY_VIDEO_HEIGHT;
    param[paramIndex].val = &height;
    param[paramIndex].size = sizeof(uint32_t);
    paramIndex++;

    uint32_t frameRate = stream.fps;
    MEDIA_DEBUG_LOG("frameRate=%u", frameRate);
    param[paramIndex].key = KEY_VIDEO_FRAME_RATE;
    param[paramIndex].val = &frameRate;
    param[paramIndex].size = sizeof(uint32_t);
    paramIndex++;

    uint32_t bitRate = GetDefaultBitrate(width, height);
    MEDIA_DEBUG_LOG("bitRate=%u kbps", bitRate);
    param[paramIndex].key = KEY_BITRATE;
    param[paramIndex].val = &bitRate;
    param[paramIndex].size = sizeof(uint32_t);
    paramIndex++;

    int32_t ret = CodecCreateByType(domainKind, codecMime, codecHdl);
    if (ret != 0) {
        MEDIA_ERR_LOG("Create video encoder failed.");
        return MEDIA_ERR;
    }

    ret = CodecSetParameter(*codecHdl, param, paramIndex);
    if (ret != 0) {
        CodecDestroy(*codecHdl);
        MEDIA_ERR_LOG("video CodecSetParameter failed.");
        return MEDIA_ERR;
    }

    ret = SetVencSource(*codecHdl, srcDev);
    if (ret != 0) {
        CodecDestroy(*codecHdl);
        return MEDIA_ERR;
    }

    return MEDIA_OK;
}

static void FillParam(Param &param, ParamKey key, uint8_t *data, uint32_t size)
{
    param.key = key;
    param.val = data;
    param.size = size;
}

static CODEC_HANDLETYPE CameraCreateJpegEncProc(FrameConfig &fc, uint32_t srcDev, AvCodecMime codecMime,
    const Param* param, uint32_t paramNum)
{
    CODEC_HANDLETYPE codecHdl = nullptr;
    if (CodecCreateByType(VIDEO_ENCODER, codecMime, &codecHdl) != 0) {
        return nullptr;
    }

    int32_t ret = CodecSetParameter(codecHdl, param, paramNum);
    if (ret != 0) {
        CodecDestroy(codecHdl);
        return nullptr;
    }

    int32_t qfactor = -1;
    fc.GetParameter(PARAM_KEY_IMAGE_ENCODE_QFACTOR, qfactor);
    if (qfactor != -1) {
        Param jpegParam = {
            .key = KEY_IMAGE_Q_FACTOR,
            .val = &qfactor,
            .size = sizeof(qfactor)
        };
        ret = CodecSetParameter(codecHdl, &jpegParam, 1);
        if (ret != 0) {
            MEDIA_ERR_LOG("CodecSetParameter set jpeg qfactor failed.(ret=%u)", ret);
        }
    }

    ret = SetVencSource(codecHdl, srcDev);
    if (ret != 0) {
        MEDIA_ERR_LOG("Set video encoder source failed.");
        CodecDestroy(codecHdl);
        return nullptr;
    }
    return codecHdl;
}

static int32_t CameraCreateJpegEnc(FrameConfig &fc, StreamAttr stream, uint32_t srcDev, CODEC_HANDLETYPE *codecHdl)
{
    uint32_t maxParamNum = 10; /* 10 maxParamNum */
    Param param[maxParamNum];
    uint32_t paramIndex = 0;

    CodecType domainKind = VIDEO_ENCODER;
    FillParam(param[paramIndex], KEY_CODEC_TYPE, reinterpret_cast<uint8_t *>(&domainKind), sizeof(CodecType));
    paramIndex++;

    AvCodecMime codecMime = ConverFormat(stream.format);
    FillParam(param[paramIndex], KEY_MIMETYPE, reinterpret_cast<uint8_t *>(&codecMime), sizeof(AvCodecMime));
    paramIndex++;

    auto surfaceList = fc.GetSurfaces();
    Surface *surface = surfaceList.front();
    uint32_t width = surface->GetWidth();
    MEDIA_DEBUG_LOG("width=%d", width);
    FillParam(param[paramIndex], KEY_VIDEO_WIDTH, reinterpret_cast<uint8_t *>(&width), sizeof(uint32_t));
    paramIndex++;

    uint32_t height = surface->GetHeight();
    MEDIA_DEBUG_LOG("height=%d", height);
    FillParam(param[paramIndex], KEY_VIDEO_HEIGHT, reinterpret_cast<uint8_t *>(&height), sizeof(uint32_t));
    paramIndex++;
    if (codecMime == MEDIA_MIMETYPE_VIDEO_HEVC) {
        VideoCodecRcMode rcMode = VID_CODEC_RC_FIXQP;
        FillParam(param[paramIndex], KEY_VIDEO_RC_MODE, reinterpret_cast<uint8_t *>(&rcMode), sizeof(VideoCodecRcMode));
        paramIndex++;

        Profile profile = HEVC_MAIN_PROFILE;
        FillParam(param[paramIndex], KEY_VIDEO_PROFILE, reinterpret_cast<uint8_t *>(&profile), sizeof(Profile));
        paramIndex++;

        uint32_t frameRate = stream.fps;
        FillParam(param[paramIndex], KEY_VIDEO_FRAME_RATE, reinterpret_cast<uint8_t *>(&frameRate), sizeof(uint32_t));
        paramIndex++;
    }
    *codecHdl = CameraCreateJpegEncProc(fc, srcDev, codecMime, param, paramIndex);
    return (*codecHdl != nullptr) ? MEDIA_OK : MEDIA_ERR;
}

static int32_t CopyCodecOutput(uint8_t *dst, uint32_t *size, CodecBuffer *buffer)
{
    if (dst == nullptr || size == nullptr || buffer == nullptr) {
        return MEDIA_ERR;
    }
    char *dstBuf = reinterpret_cast<char *>(dst);
    for (uint32_t i = 0; i < buffer->bufferCnt; i++) {
        uint32_t packSize = buffer->buffer[i].length - buffer->buffer[i].offset;
        errno_t ret = memcpy_s(dstBuf, *size, (void *)(buffer->buffer[i].buf + buffer->buffer[i].offset), packSize);
        if (ret != EOK) {
            return MEDIA_ERR;
        }
        *size -= packSize;
        dstBuf += packSize;
    }
    return MEDIA_OK;
}

static void StreamAttrInitialize(StreamAttr *streamAttr, Surface *surface,
                                 StreamType streamType, FrameConfig &fc)
{
    if (streamAttr == nullptr || surface == nullptr) {
        return;
    }
    (void)memset_s(streamAttr, sizeof(StreamAttr), 0, sizeof(StreamAttr));
    streamAttr->type = streamType;
    fc.GetParameter(CAM_IMAGE_FORMAT, streamAttr->format);
    streamAttr->width = surface->GetWidth();
    streamAttr->height = surface->GetHeight();
    fc.GetParameter(CAM_FRAME_FPS, streamAttr->fps);
    fc.GetParameter(CAM_IMAGE_INVERT_MODE, streamAttr->invertMode);
    fc.GetParameter(CAM_IMAGE_CROP_RECT, streamAttr->crop);
}

static ImageFormat Convert2HalImageFormat(uint32_t format)
{
    if (format == CAM_IMAGE_RAW12) {
        return FORMAT_RGB_BAYER_12BPP;
    }
    return FORMAT_YVU420;
}

static int32_t SurfaceSetSize(SurfaceBuffer* surfaceBuf, Surface* surface, uint32_t size)
{
#if (!defined(__LINUX__)) || (defined(ENABLE_PASSTHROUGH_MODE))
    surfaceBuf->SetSize(surface->GetSize() - size);
    if (surface->FlushBuffer(surfaceBuf) != 0) {
        MEDIA_ERR_LOG("Flush g_surface failed.");
        surface->CancelBuffer(surfaceBuf);
        return -1;
    }
#else
    surfaceBuf->SetSize(g_surface->GetSize() - size);
    if (g_surface->FlushBuffer(surfaceBuf) != 0) {
        MEDIA_ERR_LOG("Flush surface failed.");
        g_surface->CancelBuffer(surfaceBuf);
        return -1;
    }
#endif
    return 0;
}

int32_t RecordAssistant::OnVencBufferAvailble(UINTPTR userData, CodecBuffer* outBuf, int32_t *acquireFd)
{
    (void)acquireFd;
    CodecDesc* codecInfo = reinterpret_cast<CodecDesc* >(userData);
    list<Surface*> *surfaceList = &codecInfo->vencSurfaces_;
    if (surfaceList == nullptr || surfaceList->empty()) {
        MEDIA_ERR_LOG("Encoder handle is illegal.");
        return MEDIA_ERR;
    }
    int32_t ret = -1;
    for (auto &surface : *surfaceList) {
#if (!defined(__LINUX__)) || (defined(ENABLE_PASSTHROUGH_MODE))
        SurfaceBuffer *surfaceBuf = surface->RequestBuffer();
#else
        SurfaceBuffer *surfaceBuf = g_surface->RequestBuffer();
#endif
        if (surfaceBuf == nullptr) {
            MEDIA_ERR_LOG("No available buffer in surface.");
            break;
        }
#if (!defined(__LINUX__)) || (defined(ENABLE_PASSTHROUGH_MODE))
        uint32_t size = surface->GetSize();
#else
        uint32_t size = g_surface->GetSize();
#endif
        void *buf = surfaceBuf->GetVirAddr();
        if (buf == nullptr) {
            MEDIA_ERR_LOG("Invalid buffer address.");
            break;
        }
        ret = CopyCodecOutput((uint8_t*)buf, &size, outBuf);
        if (ret != MEDIA_OK) {
            MEDIA_ERR_LOG("No available outBuf in surface.");
#if (!defined(__LINUX__)) || (defined(ENABLE_PASSTHROUGH_MODE))
            surface->CancelBuffer(surfaceBuf);
#else
            g_surface->CancelBuffer(surfaceBuf);
#endif
            break;
        }
        surfaceBuf->SetInt32(KEY_IS_SYNC_FRAME, (((outBuf->flag & STREAM_FLAG_KEYFRAME) == 0) ? 0 : 1));
        surfaceBuf->SetInt64(KEY_TIME_US, outBuf->timeStamp);
        ret = SurfaceSetSize(surfaceBuf, surface, size);
        if (ret != 0) {
            break;
        }
    }
    if (CodecQueueOutput(codecInfo->vencHdl_, outBuf, 0, -1) != 0) {
        MEDIA_ERR_LOG("Codec queue output failed.");
    }
    return ret;
}

CodecCallback RecordAssistant::recordCodecCb_ = {nullptr, nullptr, RecordAssistant::OnVencBufferAvailble};

void RecordAssistant::ClearFrameConfig()
{
    for (uint32_t i = 0; i < codecInfo_.size(); i++) {
        CodecStop(codecInfo_[i].vencHdl_);
        CodecDestroy(codecInfo_[i].vencHdl_);
    }
    codecInfo_.clear();
}

int32_t RecordAssistant::SetFrameConfigEnd(int32_t result)
{
    if (result != MEDIA_OK) {
        for (uint32_t i = 0; i < codecInfo_.size(); i++) {
            CodecDestroy(codecInfo_[i].vencHdl_);
        }
        codecInfo_.clear();
        return result;
    }
    for (uint32_t i = 0; i < codecInfo_.size(); i++) {
        result = CodecSetCallback(codecInfo_[i].vencHdl_, &recordCodecCb_, reinterpret_cast<UINTPTR>(&codecInfo_[i]));
        if (result != 0) {
            MEDIA_ERR_LOG("set CodecSetCallback failed ret:%d", result);
            CodecDestroy(codecInfo_[i].vencHdl_);
            break;
        }
    }

    if (result == MEDIA_OK) {
        state_ = LOOP_READY;
    } else {
        for (uint32_t i = 0; i < codecInfo_.size(); i++) {
            CodecDestroy(codecInfo_[i].vencHdl_);
        }
        codecInfo_.clear();
    }
    return result;
}

int32_t RecordAssistant::SetFrameConfig(FrameConfig &fc, uint32_t *streamId)
{
    fc_ = &fc;
    auto surfaceList = fc.GetSurfaces();
    if (surfaceList.size() > VIDEO_MAX_NUM || surfaceList.size() == 0) {
        MEDIA_ERR_LOG("the number of surface in frame config must 1 or 2 now.\n");
        return MEDIA_ERR;
    }
    uint32_t num = 0;
    int32_t ret = MEDIA_OK;
    for (auto &surface : surfaceList) {
        CODEC_HANDLETYPE codecHdl = nullptr;
        StreamAttr stream = {};
#if (!defined(__LINUX__)) || (defined(ENABLE_PASSTHROUGH_MODE))
        StreamAttrInitialize(&stream, surface, STREAM_VIDEO, fc);
#else
        StreamAttrInitialize(&stream, g_surface, STREAM_VIDEO, fc);
#endif
        ret = HalCameraStreamCreate(cameraId_, &stream, streamId);
        if (ret != MEDIA_OK) {
            MEDIA_ERR_LOG(" creat recorder stream failed.");
            ClearFrameConfig();
            break;
        }
        streamId_ = *streamId;
        streamIdNum_[num] = *streamId;
        num++;

        StreamInfo streamInfo;
        streamInfo.type = STERAM_INFO_PRIVATE;
        fc.GetVendorParameter(streamInfo.u.data, PRIVATE_TAG_LEN);
        HalCameraStreamSetInfo(cameraId_, *streamId, &streamInfo);

        uint32_t deviceId = 0;
        HalCameraGetDeviceId(cameraId_, *streamId, &deviceId);
        ret = CameraCreateVideoEnc(fc, stream, deviceId, &codecHdl);
        if (ret != MEDIA_OK) {
            MEDIA_ERR_LOG("Cannot create suitble video encoder.");
            ClearFrameConfig();
            break;
        }
#if (!defined(__LINUX__)) || (defined(ENABLE_PASSTHROUGH_MODE))
        list<Surface*> conList({surface});
#else
        list<Surface*> conList({g_surface});
#endif
        CodecDesc info;
        info.vencHdl_ = codecHdl;
        info.vencSurfaces_ = conList;
        codecInfo_.emplace_back(info);
    }
    return SetFrameConfigEnd(ret);
}

int32_t RecordAssistant::Start(uint32_t streamId)
{
    if (state_ != LOOP_READY) {
        return MEDIA_ERR;
    }
    HalCameraStreamOn(cameraId_, streamId);
    int32_t ret = MEDIA_OK;
    int32_t i;
    for (i = 0; static_cast<uint32_t>(i) < codecInfo_.size(); i++) {
        ret = CodecStart(codecInfo_[i].vencHdl_);
        if (ret != MEDIA_OK) {
            MEDIA_ERR_LOG("Video encoder start failed.");
            ret = MEDIA_ERR;
            break;
        }
    }
    if (ret == MEDIA_ERR) {
        /* rollback */
        for (; i >= 0; i--) {
            CodecStop(codecInfo_[i].vencHdl_);
        }
        return MEDIA_ERR;
    }
    state_ = LOOP_LOOPING;
    MEDIA_INFO_LOG("Start camera recording succeed.");
    return MEDIA_OK;
}

int32_t RecordAssistant::Stop()
{
    if (state_ != LOOP_LOOPING) {
        return MEDIA_ERR;
    }
    ClearFrameConfig();
    for (uint32_t i = 0; i < VIDEO_MAX_NUM; i++) {
        if (streamIdNum_[i] != INVALID_STREAM_ID) {
            HalCameraStreamOff(cameraId_, streamIdNum_[i]);
            HalCameraStreamDestroy(cameraId_, streamIdNum_[i]);
        }
        streamIdNum_[i] = INVALID_STREAM_ID;
    }
    state_ = LOOP_STOP;
    return MEDIA_OK;
}

void* PreviewAssistant::YuvCopyProcess(void *arg)
{
    return nullptr;
}

static void GetSurfaceRect(Surface *surface, IRect *attr)
{
    attr->x = std::stoi(surface->GetUserData(string("region_position_x")));
    attr->y = std::stoi(surface->GetUserData(string("region_position_y")));
    attr->w = std::stoi(surface->GetUserData(string("region_width")));
    attr->h = std::stoi(surface->GetUserData(string("region_hegiht")));
}

int32_t PreviewAssistant::SetFrameConfig(FrameConfig &fc, uint32_t *streamId)
{
    fc_ = &fc;
    auto surfaceList = fc.GetSurfaces();
    if (surfaceList.size() != 1) {
        MEDIA_ERR_LOG("Only support one surface in frame config now.");
        return MEDIA_ERR;
    }
    Surface *surface = surfaceList.front();
    StreamAttr stream = {};
    StreamAttrInitialize(&stream, surface, STREAM_PREVIEW, fc);
    int32_t ret = HalCameraStreamCreate(cameraId_, &stream, streamId);
    if (ret != MEDIA_OK) {
        MEDIA_ERR_LOG(" creat preview stream failed.");
        return MEDIA_ERR;
    }
    StreamInfo streamInfo;
    streamInfo.type = STREAM_INFO_POS;
    streamInfo.u.pos.x = std::stoi(surface->GetUserData(string("region_position_x")));
    streamInfo.u.pos.y = std::stoi(surface->GetUserData(string("region_position_y")));

    HalCameraStreamSetInfo(cameraId_, *streamId, &streamInfo);
    streamId_ = *streamId;
    return MEDIA_OK;
}

int32_t PreviewAssistant::Start(uint32_t streamId)
{
    if (state_ == LOOP_LOOPING) {
        return MEDIA_ERR;
    }
    state_ = LOOP_LOOPING;

    int32_t retCode = pthread_create(&threadId, nullptr, YuvCopyProcess, this);
    if (retCode != 0) {
        MEDIA_ERR_LOG("fork thread YuvCopyProcess failed: %d.", retCode);
    }

    int32_t ret = HalCameraStreamOn(cameraId_, streamId);
    if (ret != MEDIA_OK) {
        MEDIA_ERR_LOG("Preview start failed of HalCameraStreamOn.(ret=%d)", ret);
        Stop();
        return MEDIA_ERR;
    }
    return MEDIA_OK;
}

int32_t PreviewAssistant::Stop()
{
    if (state_ != LOOP_LOOPING) {
        return MEDIA_ERR;
    }
    state_ = LOOP_STOP;
    pthread_join(threadId, NULL);
    HalCameraStreamOff(cameraId_, streamId_);
    HalCameraStreamDestroy(cameraId_, streamId_);
    return MEDIA_OK;
}

int32_t CaptureAssistant::SetFrameConfig(FrameConfig &fc, uint32_t *streamId)
{
    auto surfaceList = fc.GetSurfaces();
    if (surfaceList.size() != 1) {
        MEDIA_ERR_LOG("Only support one surface in frame config now.");
        return MEDIA_ERR;
    }
    if (surfaceList.empty()) {
        MEDIA_ERR_LOG("Frame config with empty surface list.");
        return MEDIA_ERR;
    }
    if (surfaceList.size() > 1) {
        MEDIA_WARNING_LOG("Capture only fullfill the first surface in frame config.");
    }
    Surface *surface = surfaceList.front();

    StreamAttr stream = {};
    StreamAttrInitialize(&stream, surface, STREAM_CAPTURE, fc);

    uint32_t deviceId = 0;
    int32_t ret = HalCameraStreamCreate(cameraId_, &stream, streamId);
    if (ret != MEDIA_OK) {
        MEDIA_ERR_LOG(" creat capture stream failed.");
        return MEDIA_ERR;
    }
    streamId_ = *streamId;
    HalCameraGetDeviceId(cameraId_, *streamId, &deviceId);
    ret = CameraCreateJpegEnc(fc, stream, deviceId, &vencHdl_);
    if (ret != MEDIA_OK) {
        MEDIA_ERR_LOG("Create capture venc failed.");
        return MEDIA_ERR;
    }

    capSurface_ = surface;
    state_ = LOOP_READY;
    return MEDIA_OK;
}

/* Block method, waiting for capture completed */
int32_t CaptureAssistant::Start(uint32_t streamId)
{
    state_ = LOOP_LOOPING;
    HalCameraStreamOn(cameraId_, streamId);
    int32_t ret = CodecStart(vencHdl_);
    if (ret != 0) {
        MEDIA_ERR_LOG("Start capture encoder failed.(ret=%d)", ret);
        state_ = LOOP_STOP;
        return MEDIA_ERR;
    }

    CodecBuffer* outInfo = (CodecBuffer*)new char[sizeof(CodecBuffer) + sizeof(CodecBufferInfo) * 3]; /* 3 buffCnt */
    if (outInfo == NULL) {
        MEDIA_ERR_LOG("malloc Dequeue buffer failed!");
        return MEDIA_ERR;
    }
    SurfaceBuffer *surfaceBuf = NULL;
    do {
        if (memset_s(outInfo, sizeof(CodecBuffer) + sizeof(CodecBufferInfo) * 0x3, 0,
            sizeof(CodecBuffer) + sizeof(CodecBufferInfo) * 3) != MEDIA_OK) { /* 3 buffCnt */
            MEDIA_ERR_LOG("memset_s failed!");
            delete(outInfo);
            return MEDIA_ERR;
        }
        outInfo->bufferCnt = 3; /* 3 buffCnt */
        ret = CodecDequeueOutput(vencHdl_, 0, nullptr, outInfo);
        if (ret != 0) {
            MEDIA_ERR_LOG("Dequeue capture frame failed.(ret=%d)", ret);
            break;
        }

        surfaceBuf = capSurface_->RequestBuffer();
        if (surfaceBuf == NULL) {
            break;
        }

        uint32_t size = capSurface_->GetSize();
        void *buf = surfaceBuf->GetVirAddr();
        if (buf == nullptr) {
            MEDIA_ERR_LOG("Invalid buffer address.");
            break;
        }
        if (CopyCodecOutput((uint8_t*)buf, &size, outInfo) != MEDIA_OK) {
            MEDIA_ERR_LOG("No available buffer in capSurface_.");
            break;
        }
        surfaceBuf->SetSize(capSurface_->GetSize() - size);
        if (capSurface_->FlushBuffer(surfaceBuf) != 0) {
            MEDIA_ERR_LOG("Flush surface buffer failed.");
            break;
        }
    } while (0);

    CodecStop(vencHdl_);
    CodecDestroy(vencHdl_);
    HalCameraStreamOff(cameraId_, streamId);
    HalCameraStreamDestroy(cameraId_, streamId);
    delete outInfo;
    outInfo = NULL;
    state_ = LOOP_STOP;

    return ret;
}

int32_t CaptureAssistant::Stop()
{
    MEDIA_DEBUG_LOG("No support method.");
    return MEDIA_OK;
}

int32_t CallbackAssistant::SetFrameConfig(FrameConfig &fc, uint32_t *streamId)
{
    fc_ = &fc;
    auto surfaceList = fc.GetSurfaces();
    if (surfaceList.size() != 1) {
        MEDIA_ERR_LOG("Only support one surface in frame config now.");
        return MEDIA_ERR;
    }
    uint32_t imageFormat = 0;
    fc.GetParameter(CAM_IMAGE_FORMAT, imageFormat);
    ImageFormat halImageFormat = Convert2HalImageFormat(imageFormat);
    MEDIA_INFO_LOG("Imageformat is %d", imageFormat);
    Surface *surface = surfaceList.front();
    StreamAttr stream = {};
    StreamAttrInitialize(&stream, surface, STREAM_CALLBACK, fc);
    stream.format = halImageFormat;
    int32_t ret = HalCameraStreamCreate(cameraId_, &stream, streamId);
    if (ret != MEDIA_OK) {
        MEDIA_ERR_LOG(" creat callback stream failed.");
        return MEDIA_ERR;
    }
    streamId_ = *streamId;
    capSurface_ = surface;
    state_ = LOOP_READY;
    return MEDIA_OK;
}

int32_t CallbackAssistant::Start(uint32_t streamId)
{
    if (state_ == LOOP_LOOPING) {
        return MEDIA_ERR;
    }
    state_ = LOOP_LOOPING;
    int32_t retCode = pthread_create(&threadId, nullptr, StreamCopyProcess, this);
    if (retCode != 0) {
        MEDIA_ERR_LOG("fork thread StreamCopyProcess failed: %d.", retCode);
    }
    HalCameraStreamOn(cameraId_, streamId);
    return MEDIA_OK;
}

void* CallbackAssistant::StreamCopyProcess(void *arg)
{
    CallbackAssistant *assistant = (CallbackAssistant *)arg;
    if (assistant == nullptr) {
        MEDIA_ERR_LOG("CallbackAssistant create failed.");
        return nullptr;
    }
    if (assistant->capSurface_ == nullptr) {
        MEDIA_ERR_LOG("capSurface_ is null.\n");
        return nullptr;
    }

    int32_t ret;
    HalBuffer streamBuffer;
    (void)memset_s(&streamBuffer, sizeof(HalBuffer), 0, sizeof(HalBuffer));
    while (assistant->state_ == LOOP_LOOPING) {
        SurfaceBuffer *surfaceBuf = assistant->capSurface_->RequestBuffer();
        if (surfaceBuf == nullptr) {
            usleep(DELAY_TIME_ONE_FRAME);
            continue;
        }

        if (streamBuffer.size != 0x0) {
            HalCameraQueueBuf(assistant->cameraId_, assistant->streamId_, &streamBuffer);
            (void)memset_s(&streamBuffer, sizeof(HalBuffer), 0, sizeof(HalBuffer));
        }
        streamBuffer.format = FORMAT_PRIVATE;
        streamBuffer.size = assistant->capSurface_->GetSize();
        if (surfaceBuf->GetVirAddr() == NULL) {
            MEDIA_ERR_LOG("Invalid buffer address.");
            break;
        }
        streamBuffer.virAddr = surfaceBuf->GetVirAddr();

        ret = HalCameraDequeueBuf(assistant->cameraId_, assistant->streamId_, &streamBuffer);
        if (ret != MEDIA_OK) {
            usleep(DELAY_TIME_ONE_FRAME);
            continue;
        }

        if (assistant->capSurface_->FlushBuffer(surfaceBuf) != 0) {
            MEDIA_ERR_LOG("Flush surface failed.");
            assistant->capSurface_->CancelBuffer(surfaceBuf);
            break;
        }
        usleep(DELAY_TIME_ONE_FRAME);
    }
    if (streamBuffer.size != 0x0) {
        HalCameraQueueBuf(assistant->cameraId_, assistant->streamId_, &streamBuffer);
    }
    MEDIA_DEBUG_LOG(" yuv thread joined \n");
    return nullptr;
}

int32_t CallbackAssistant::Stop()
{
    if (state_ != LOOP_LOOPING) {
        return MEDIA_ERR;
    }
    state_ = LOOP_STOP;
    pthread_join(threadId, NULL);
    HalCameraStreamOff(cameraId_, streamId_);
    HalCameraStreamDestroy(cameraId_, streamId_);
    return MEDIA_OK;
}

CameraDevice::CameraDevice() {}
CameraDevice::CameraDevice(uint32_t cameraId)
{
    this->cameraId = cameraId;
}

CameraDevice::~CameraDevice() {}

int32_t CameraDevice::Initialize()
{
    // Need to be Refactored when delete config file
    int32_t ret = CodecInit();
    if (ret != 0) {
        MEDIA_ERR_LOG("Codec module init failed.(ret=%d)", ret);
        return MEDIA_ERR;
    }
    MEDIA_INFO_LOG("Codec module init succeed.");
    captureAssistant_.state_ = LOOP_READY;
    previewAssistant_.state_ = LOOP_READY;
    recordAssistant_.state_ = LOOP_READY;
    callbackAssistant_.state_ = LOOP_READY;
    captureAssistant_.cameraId_ = cameraId;
    previewAssistant_.cameraId_ = cameraId;
    recordAssistant_.cameraId_ = cameraId;
    callbackAssistant_.cameraId_ = cameraId;
    return MEDIA_OK;
}

int32_t CameraDevice::UnInitialize()
{
    return MEDIA_OK;
}

int32_t CameraDevice::TriggerLoopingCapture(FrameConfig &fc, uint32_t *streamId)
{
    MEDIA_DEBUG_LOG("Camera device start looping capture.");
    DeviceAssistant *assistant = nullptr;
    int32_t fcType = fc.GetFrameConfigType();
    switch (fcType) {
        case FRAME_CONFIG_RECORD:
            assistant = &recordAssistant_;
            break;
        case FRAME_CONFIG_PREVIEW:
            assistant = &previewAssistant_;
            break;
        case FRAME_CONFIG_CAPTURE:
            assistant = &captureAssistant_;
            break;
        case FRAME_CONFIG_CALLBACK:
            assistant = &callbackAssistant_;
            break;
        default:
            break;
    }
    if (assistant == nullptr) {
        MEDIA_ERR_LOG("Invalid frame config type.(type=%d)", fcType);
        return MEDIA_ERR;
    }
    if (assistant->state_ == LOOP_IDLE || assistant->state_ == LOOP_LOOPING || assistant->state_ == LOOP_ERROR) {
        MEDIA_ERR_LOG("Device state is %d, cannot start looping capture.", assistant->state_);
        return MEDIA_ERR;
    }
    uint8_t count = 1;
    if (fcType == FRAME_CONFIG_CAPTURE) {
        auto surfaceList = fc.GetSurfaces();
        if (surfaceList.size() != 1) {
            MEDIA_ERR_LOG("Only support one surface in frame config now");
            return MEDIA_ERR;
        }
        Surface* surface = surfaceList.front();
        count = surface->GetQueueSize();
    }

    do {
        int32_t ret = assistant->SetFrameConfig(fc, streamId);
        if (ret != MEDIA_OK) {
            MEDIA_ERR_LOG("Check and set frame config failed (ret=%d)", ret);
            return MEDIA_ERR;
        }
        ret = assistant->Start(*streamId);
        if (ret != MEDIA_OK) {
            MEDIA_ERR_LOG("Start looping capture failed (ret=%d)", ret);
            return MEDIA_ERR;
        }
    } while (--count);
    return MEDIA_OK;
}

void CameraDevice::StopLoopingCapture(int32_t type)
{
    MEDIA_INFO_LOG("Stop looping capture in camera_device.cpp");

    switch (type) {
        case FRAME_CONFIG_RECORD:
            MEDIA_INFO_LOG("Stop recorder");
            recordAssistant_.Stop();;
            break;
        case FRAME_CONFIG_PREVIEW:
            MEDIA_INFO_LOG("Stop preview");
            previewAssistant_.Stop();
            break;
        case FRAME_CONFIG_CALLBACK:
            MEDIA_INFO_LOG("Stop callback");
            callbackAssistant_.Stop();
            break;
        default:
            MEDIA_INFO_LOG("Stop all");
            previewAssistant_.Stop();
            recordAssistant_.Stop();
            callbackAssistant_.Stop();
            break;
    }
}

int32_t CameraDevice::TriggerSingleCapture(FrameConfig &fc, uint32_t *streamId)
{
    return TriggerLoopingCapture(fc, streamId);
}

int32_t CameraDevice::SetCameraConfig()
{
    return MEDIA_OK;
}
} // namespace Media
} // namespace OHOS