/*
 * Copyright (c) 2020.Huawei Technologies Co., Ltd. All rights reserved.
 * 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 "DvppCommon.h"
#include <iostream>
#include <memory>

#include "Log.h"
#include "CommonDataType.h"

static auto g_resizeConfigDeleter = [](acldvppResizeConfig *const p) { acldvppDestroyResizeConfig(p); };
static auto g_picDescDeleter = [](acldvppPicDesc *const picDesc) { acldvppDestroyPicDesc(picDesc); };
static auto g_roiConfigDeleter = [](acldvppRoiConfig *const p) { acldvppDestroyRoiConfig(p); };
static auto g_jpegeConfigDeleter = [](acldvppJpegeConfig *const p) { acldvppDestroyJpegeConfig(p); };

DvppCommon::DvppCommon(aclrtStream dvppStream)
{
    dvppStream_ = dvppStream;
}

DvppCommon::DvppCommon(const VdecConfig &vdecConfig)
{
    vdecConfig_ = vdecConfig;
}

/*
 * @description: Create a channel for processing image data,
 *               the channel description is created by acldvppCreateChannelDesc
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::Init(void)
{
    dvppChannelDesc_ = acldvppCreateChannelDesc();
    if (dvppChannelDesc_ == nullptr)
    {
        return -1;
    }

    //若不调用该接口，则系统默认3种模式的通道都创建（通道数的限制，请参见总体说明。），
    //可能会占用资源，推荐用户根据实际功能指定通道模式。
    acldvppSetChannelDescMode(dvppChannelDesc_, DVPP_CHNMODE_VPC);

    APP_ERROR ret = acldvppCreateChannel(dvppChannelDesc_);
    if (ret != 0)
    {
        LogFatal << "Failed to create dvpp channel: " << GetAppErrCodeInfo(ret) << ".";
        acldvppDestroyChannelDesc(dvppChannelDesc_);
        dvppChannelDesc_ = nullptr;
        return ret;
    }

    //获取通道号，通道号会自己增加
    // uint64_t channelid = acldvppGetChannelDescChannelId(dvppChannelDesc_);
    // printf("channelid:%d\n", channelid);

    return APP_ERR_OK;
}

/*
 * @description: Create a channel for processing video data,
 *               the channel description is created by aclvdecCreateChannelDesc
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::InitVdec()
{
    isVdec_ = true;

    // create vdec channelDesc
    vdecChannelDesc_ = aclvdecCreateChannelDesc();
    if (vdecChannelDesc_ == nullptr)
    {
        LogError << "Failed to create vdec channel description.";
        return APP_ERR_ACL_FAILURE;
    }

    // channelId: 0-15
    aclError ret = aclvdecSetChannelDescChannelId(vdecChannelDesc_, vdecConfig_.channelId);
    if (ret != ACL_ERROR_NONE)
    {
        LogError << "Failed to set vdec channel id, ret = " << ret << ".";
        return APP_ERR_ACL_FAILURE;
    }

    ret = aclvdecSetChannelDescThreadId(vdecChannelDesc_, vdecConfig_.threadId);
    if (ret != ACL_ERROR_NONE)
    {
        LogError << "Failed to set thread id, ret = " << ret << ".";
        return APP_ERR_ACL_FAILURE;
    }

    // callback func
    ret = aclvdecSetChannelDescCallback(vdecChannelDesc_, vdecConfig_.callback);
    if (ret != ACL_ERROR_NONE)
    {
        LogError << "Failed to set vdec callback function, ret = " << ret << ".";
        return APP_ERR_ACL_FAILURE;
    }

    ret = aclvdecSetChannelDescEnType(vdecChannelDesc_, vdecConfig_.inFormat);
    if (ret != ACL_ERROR_NONE)
    {
        LogError << "Failed to set encoded type of input video, ret = " << ret << ".";
        return APP_ERR_ACL_FAILURE;
    }

    ret = aclvdecSetChannelDescOutPicFormat(vdecChannelDesc_, vdecConfig_.outFormat);
    if (ret != ACL_ERROR_NONE)
    {
        LogError << "Failed to set vdec output format, ret = " << ret << ".";
        return APP_ERR_ACL_FAILURE;
    }

    //设置是否实时出帧（即发送一帧解码一帧，无需依赖后续帧的传入）。
    //只支持简单参考关系的H264/H265标准码流（无长期参考帧，无B帧）。
    ret = aclvdecSetChannelDescOutMode(vdecChannelDesc_, vdecConfig_.outMode);
    if (ret != ACL_ERROR_NONE)
    {
        LogError << "Failed to set vdec output format, ret = " << ret << ".";
        return APP_ERR_ACL_FAILURE;
    }

    // create vdec channel
    ret = aclvdecCreateChannel(vdecChannelDesc_);
    if (ret != ACL_ERROR_NONE)
    {
        LogError << "Failed to create vdec channel, ret = " << ret << ".";
        return APP_ERR_ACL_FAILURE;
    }

    LogInfo << "Vdec init resource successfully.";

    return APP_ERR_OK;
}

/*
 * @description: If isVdec_ is true, destroy the channel and the channel description used by video.
 *               Otherwise destroy the channel and the channel description used by image.
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::DeInit(void)
{
    if (isVdec_)
    {
        return DestroyResource();
    }

    APP_ERROR ret = aclrtSynchronizeStream(dvppStream_); // APP_ERROR ret
    if (ret != APP_ERR_OK)
    {
        LogFatal << "Failed to synchronize stream, ret = " << ret << ".";
        return ret;
    }

    //如果在调用acldvppDestroyChannel接口销毁通道前，
    //已调用acldvppDestroyChannelDesc接口销毁了通道描述信息，那么在调用acldvppDestroyChannel接口销毁通道时会报错。
    ret = acldvppDestroyChannel(dvppChannelDesc_);
    if (ret != APP_ERR_OK)
    {
        LogFatal << "Failed to destory dvpp channel, ret = " << ret << ".";
        return ret;
    }

    //销毁通道描述信息
    ret = acldvppDestroyChannelDesc(dvppChannelDesc_);
    if (ret != APP_ERR_OK)
    {
        LogFatal << "Failed to destroy dvpp channel description, ret = " << ret << ".";
        return ret;
    }

    //释放内存
    ReleaseDvppBuffer();

    return APP_ERR_OK;
}

/*
 * @description: Destroy the channel and the channel description used by video.
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::DestroyResource()
{
    APP_ERROR ret = APP_ERR_OK;
    isVdec_ = true;
    //如果在调用aclvdecDestroyChannel接口销毁通道前，
    //已调用aclvdecDestroyChannelDesc接口销毁了通道描述信息，那么在调用aclvdecDestroyChannel接口销毁通道时会报错。
    if (vdecChannelDesc_ != nullptr)
    {
        ret = aclvdecDestroyChannel(vdecChannelDesc_);
        if (ret != APP_ERR_OK)
        {
            LogError << "Failed to destory dvpp channel, ret = " << ret;
        }
        aclvdecDestroyChannelDesc(vdecChannelDesc_);
        vdecChannelDesc_ = nullptr;
    }
    return ret;
}

/*
 * @description: Release the memory that is allocated in the interfaces which are started with "Combine"
 */
void DvppCommon::ReleaseDvppBuffer() const
{
    if (cropImage_ != nullptr)
    {
        RELEASE_DVPP_DATA(cropImage_->data);
    }
    if (resizedImage_ != nullptr)
    {
        RELEASE_DVPP_DATA(resizedImage_->data);
    }
    if (decodedImage_ != nullptr)
    {
        RELEASE_DVPP_DATA(decodedImage_->data);
    }
    if (inputImage_ != nullptr)
    {
        RELEASE_DVPP_DATA(inputImage_->data);
    }
    if (encodedImage_ != nullptr)
    {
        RELEASE_DVPP_DATA(encodedImage_->data);
    }
}

/*
 * @description: Get the size of buffer used to save image for VPC according to width, height and format
 * @param  width specifies the width of the output image
 * @param  height specifies the height of the output image
 * @param  format specifies the format of the output image
 * @param: vpcSize is used to save the result size
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::GetVpcDataSize(uint32_t width, uint32_t height, acldvppPixelFormat format, uint32_t &vpcSize)
{
    // Check the invalid format of VPC function and calculate the output buffer size
    if (format != PIXEL_FORMAT_YUV_SEMIPLANAR_420 && format != PIXEL_FORMAT_YVU_SEMIPLANAR_420)
    {
        LogError << "Format[" << format << "] for VPC is not supported, just support NV12 or NV21.";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    uint32_t widthStride = DVPP_ALIGN_UP(width, VPC_WIDTH_ALIGN);
    uint32_t heightStride = DVPP_ALIGN_UP(height, VPC_HEIGHT_ALIGN);
    vpcSize = widthStride * heightStride * YUV_BGR_SIZE_CONVERT_3 / YUV_BGR_SIZE_CONVERT_2;
    return APP_ERR_OK;
}

/*
 * @description: Get the aligned width and height of the input image according to the image format
 * @param: width specifies the width before alignment
 * @param: height specifies the height before alignment
 * @param: format specifies the image format
 * @param: widthStride is used to save the width after alignment
 * @param: heightStride is used to save the height after alignment
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::GetVpcInputStrideSize(uint32_t width, uint32_t height, acldvppPixelFormat format,
                                            uint32_t &widthStride, uint32_t &heightStride)
{
    uint32_t inputWidthStride;
    // Check the invalidty of input format and calculate the input width stride
    if (format >= PIXEL_FORMAT_YUV_400 && format <= PIXEL_FORMAT_YVU_SEMIPLANAR_444)
    {
        // If format is YUV SP, keep widthStride not change.
        inputWidthStride = DVPP_ALIGN_UP(width, VPC_STRIDE_WIDTH);
    }
    else if (format >= PIXEL_FORMAT_YUYV_PACKED_422 && format <= PIXEL_FORMAT_VYUY_PACKED_422)
    {
        // If format is YUV422 packed, image size = H x W * 2;
        inputWidthStride = DVPP_ALIGN_UP(width, VPC_STRIDE_WIDTH) * YUV422_WIDTH_NU;
    }
    else if (format >= PIXEL_FORMAT_YUV_PACKED_444 && format <= PIXEL_FORMAT_BGR_888)
    {
        // If format is YUV444 packed or RGB, image size = H x W * 3;
        inputWidthStride = DVPP_ALIGN_UP(width, VPC_STRIDE_WIDTH) * YUV444_RGB_WIDTH_NU;
    }
    else if (format >= PIXEL_FORMAT_ARGB_8888 && format <= PIXEL_FORMAT_BGRA_8888)
    {
        // If format is XRGB8888, image size = H x W * 4
        inputWidthStride = DVPP_ALIGN_UP(width, VPC_STRIDE_WIDTH) * XRGB_WIDTH_NU;
    }
    else
    {
        LogError << "Input format[" << format << "] for VPC is invalid, please check it.";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    uint32_t inputHeightStride = DVPP_ALIGN_UP(height, VPC_STRIDE_HEIGHT);
    // Check the input validity width stride.
    if (inputWidthStride > MAX_RESIZE_WIDTH || inputWidthStride < MIN_RESIZE_WIDTH)
    {
        LogError << "Input width stride " << inputWidthStride << " is invalid, not in [" << MIN_RESIZE_WIDTH
                 << ", " << MAX_RESIZE_WIDTH << "].";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    // Check the input validity height stride.
    if (inputHeightStride > MAX_RESIZE_HEIGHT || inputHeightStride < MIN_RESIZE_HEIGHT)
    {
        LogError << "Input height stride " << inputHeightStride << " is invalid, not in [" << MIN_RESIZE_HEIGHT
                 << ", " << MAX_RESIZE_HEIGHT << "].";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    widthStride = inputWidthStride;
    heightStride = inputHeightStride;
    return APP_ERR_OK;
}

/*
 * @description: Get the aligned width and height of the output image according to the image format
 * @param: width specifies the width before alignment
 * @param: height specifies the height before alignment
 * @param: format specifies the image format
 * @param: widthStride is used to save the width after alignment
 * @param: heightStride is used to save the height after alignment
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::GetVpcOutputStrideSize(uint32_t width, uint32_t height, acldvppPixelFormat format,
                                             uint32_t &widthStride, uint32_t &heightStride)
{
    // Check the invalidty of output format and calculate the output width and height
    if (format != PIXEL_FORMAT_YUV_SEMIPLANAR_420 && format != PIXEL_FORMAT_YVU_SEMIPLANAR_420)
    {
        LogError << "Output format[" << format << "] for VPC is not supported, just support NV12 or NV21.";
        return APP_ERR_COMM_INVALID_PARAM;
    }

    widthStride = DVPP_ALIGN_UP(width, VPC_STRIDE_WIDTH);
    heightStride = DVPP_ALIGN_UP(height, VPC_STRIDE_HEIGHT);
    return APP_ERR_OK;
}

/*
 * @description: Set picture description information and execute resize function
 * @param: input specifies the input image information
 * @param: output specifies the output image information
 * @param: withSynchronize specifies whether to execute synchronously
 * @param: processType specifies whether to perform proportional scaling, default is non-proportional resize
 * @return: APP_ERR_OK if success, other values if failure
 * @attention: This function can be called only when the DvppCommon object is initialized with Init
 */
APP_ERROR DvppCommon::VpcResize(DvppDataInfo &input, DvppDataInfo &output, bool withSynchronize,
                                VpcProcessType processType)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_)
    {
        LogError << "VpcResize cannot be called by the DvppCommon object which is initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }

    acldvppPicDesc *inputDesc = acldvppCreatePicDesc();
    acldvppPicDesc *outputDesc = acldvppCreatePicDesc();
    resizeInputDesc_.reset(inputDesc, g_picDescDeleter);
    resizeOutputDesc_.reset(outputDesc, g_picDescDeleter);

    // Set dvpp picture descriptin info of input image
    APP_ERROR ret = SetDvppPicDescData(input, *resizeInputDesc_);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set dvpp input picture description, ret = " << ret << ".";
        return ret;
    }

    // Set dvpp picture descriptin info of output image
    ret = SetDvppPicDescData(output, *resizeOutputDesc_);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set dvpp output picture description, ret = " << ret << ".";
        return ret;
    }

    //默认缩放，非等比例
    if (processType == VPC_PT_DEFAULT)
    {
        return ResizeProcess(*resizeInputDesc_, *resizeOutputDesc_, withSynchronize);
    }

    // Get crop area according to the processType
    // When the processType is VPC_PT_FILL, the image will be cropped if the image size is different from the target resolution
    CropRoiConfig cropRoi = {0};
    GetCropRoi(input, output, processType, cropRoi);

    // The width and height of the original image will be resized by the same ratio
    // The cropped image will be pasted on the upper left corner or the middle location or the whole location according to the processType
    CropRoiConfig pasteRoi = {0};
    GetPasteRoi(input, output, processType, pasteRoi);

    //记住paste区域
    paste_up_ = pasteRoi.up;
    paste_left_ = pasteRoi.left;
    paste_right_ = pasteRoi.right;
    paste_down_ = pasteRoi.down;

    return ResizeWithPadding(*resizeInputDesc_, *resizeOutputDesc_, cropRoi, pasteRoi, withSynchronize);
}

//抠图和缩放
APP_ERROR DvppCommon::VpcCropAndPaste(const DvppCropInputInfo &input, const DvppDataInfo &output, bool withSynchronize)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_)
    {
        LogError << "VpcResize cannot be called by the DvppCommon object which is initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }

    acldvppPicDesc *inputDesc = acldvppCreatePicDesc();
    acldvppPicDesc *outputDesc = acldvppCreatePicDesc();
    resizeInputDesc_.reset(inputDesc, g_picDescDeleter); //自定义析构器
    resizeOutputDesc_.reset(outputDesc, g_picDescDeleter);

    // Set dvpp picture descriptin info of input image
    APP_ERROR ret = SetDvppPicDescData(input.dataInfo, *resizeInputDesc_);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set dvpp input picture description, ret = " << ret << ".";
        return ret;
    }

    // Set dvpp picture descriptin info of output image
    ret = SetDvppPicDescData(output, *resizeOutputDesc_);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set dvpp output picture description, ret = " << ret << ".";
        return ret;
    }

    //抠图区域
    CropRoiConfig cropRoi = input.roi;

    //贴图区域，贴在左上角，等比例  (注：获取贴图区域pasteRoi时，应该用抠图区域的宽高作等比例计算)
    CropRoiConfig pasteRoi = {0};
    DvppDataInfo cropAreaInfo;
    cropAreaInfo.width = cropRoi.right - cropRoi.left + ODD_NUM_1;
    cropAreaInfo.height = cropRoi.down - cropRoi.up + ODD_NUM_1;
    GetPasteRoi(cropAreaInfo, output, VPC_PT_PADDING, pasteRoi);

    //记住paste区域
    paste_up_ = pasteRoi.up;
    paste_left_ = pasteRoi.left;
    paste_right_ = pasteRoi.right;
    paste_down_ = pasteRoi.down;

    return ResizeWithPadding(*resizeInputDesc_, *resizeOutputDesc_, cropRoi, pasteRoi, withSynchronize);
}

/*
 * @description: Set image description information
 * @param: dataInfo specifies the image information
 * @param: picsDesc specifies the picture description information to be set
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::SetDvppPicDescData(const DvppDataInfo &dataInfo, acldvppPicDesc &picDesc) const
{
    APP_ERROR ret = acldvppSetPicDescData(&picDesc, dataInfo.data);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set data for dvpp picture description, ret = " << ret << ".";
        return ret;
    }
    ret = acldvppSetPicDescSize(&picDesc, dataInfo.dataSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set size for dvpp picture description, ret = " << ret << ".";
        return ret;
    }
    ret = acldvppSetPicDescFormat(&picDesc, dataInfo.format);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set format for dvpp picture description, ret = " << ret << ".";
        return ret;
    }
    ret = acldvppSetPicDescWidth(&picDesc, dataInfo.width);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set width for dvpp picture description, ret = " << ret << ".";
        return ret;
    }
    ret = acldvppSetPicDescHeight(&picDesc, dataInfo.height);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set height for dvpp picture description, ret = " << ret << ".";
        return ret;
    }
    ret = acldvppSetPicDescWidthStride(&picDesc, dataInfo.widthStride);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set aligned width for dvpp picture description, ret = " << ret << ".";
        return ret;
    }
    ret = acldvppSetPicDescHeightStride(&picDesc, dataInfo.heightStride);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set aligned height for dvpp picture description, ret = " << ret << ".";
        return ret;
    }

    return APP_ERR_OK;
}

/*
 * @description: Check whether the image format and zoom ratio meet the requirements
 * @param: input specifies the input image information
 * @param: output specifies the output image information
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::CheckResizeParams(const DvppDataInfo &input, const DvppDataInfo &output) const
{
    if (output.format != PIXEL_FORMAT_YUV_SEMIPLANAR_420 && output.format != PIXEL_FORMAT_YVU_SEMIPLANAR_420)
    {
        LogError << "Output format[" << output.format << "] for VPC is not supported, just support NV12 or NV21.";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    //针对缩放功能，贴图/抠图的宽高缩放比例范围：[1/32, 16]。
    if (((float)output.height / input.height) < MIN_RESIZE_SCALE ||
        ((float)output.height / input.height) > MAX_RESIZE_SCALE)
    {
        LogError << "Resize scale should be in range [1/32, 16], which is " << (output.height / input.height) << ".";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    if (((float)output.width / input.width) < MIN_RESIZE_SCALE ||
        ((float)output.width / input.width) > MAX_RESIZE_SCALE)
    {
        LogError << "Resize scale should be in range [1/32, 16], which is " << (output.width / input.width) << ".";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    return APP_ERR_OK;
}

/*
 * @description: Scale the input image to the size specified by the output image and
 *               saves the result to the output image (non-proportionate scaling)
 * @param: inputDesc specifies the description information of the input image
 * @param: outputDesc specifies the description information of the output image
 * @param: withSynchronize specifies whether to execute synchronously
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::ResizeProcess(acldvppPicDesc &inputDesc, acldvppPicDesc &outputDesc, bool withSynchronize)
{
    acldvppResizeConfig *resizeConfig = acldvppCreateResizeConfig();
    if (resizeConfig == nullptr)
    {
        LogError << "Failed to create dvpp resize config.";
        return APP_ERR_COMM_INVALID_POINTER;
    }

    resizeConfig_.reset(resizeConfig, g_resizeConfigDeleter);
    APP_ERROR ret = acldvppVpcResizeAsync(dvppChannelDesc_, &inputDesc, &outputDesc, resizeConfig_.get(), dvppStream_);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to resize asynchronously, ret = " << ret << ".";
        return ret;
    }

    if (withSynchronize)
    {
        ret = aclrtSynchronizeStream(dvppStream_);
        if (ret != APP_ERR_OK)
        {
            LogError << "Failed to synchronize stream, ret = " << ret << ".";
            return ret;
        }
    }
    return APP_ERR_OK;
}

/*
 * @description: Crop the image from the input image based on the specified area and
 *               paste the cropped image to the specified position of the target image
 *               as the output image
 * @param: inputDesc specifies the description information of the input image
 * @param: outputDesc specifies the description information of the output image
 * @param: cropRoi specifies the cropped area
 * @param: pasteRoi specifies the pasting area
 * @param: withSynchronize specifies whether to execute synchronously
 * @return: APP_ERR_OK if success, other values if failure
 * @attention: If the width and height of the crop area are different from those of the
 *             paste area, the image is scaled again
 */
APP_ERROR DvppCommon::ResizeWithPadding(acldvppPicDesc &inputDesc, acldvppPicDesc &outputDesc, CropRoiConfig &cropRoi,
                                        CropRoiConfig &pasteRoi, bool withSynchronize)
{
    acldvppRoiConfig *cropRoiCfg = acldvppCreateRoiConfig(cropRoi.left, cropRoi.right, cropRoi.up, cropRoi.down);
    if (cropRoiCfg == nullptr)
    {
        LogError << "Failed to create dvpp roi config for corp area.";
        return APP_ERR_COMM_FAILURE;
    }
    cropAreaConfig_.reset(cropRoiCfg, g_roiConfigDeleter);

    acldvppRoiConfig *pastRoiCfg = acldvppCreateRoiConfig(pasteRoi.left, pasteRoi.right, pasteRoi.up, pasteRoi.down);
    if (pastRoiCfg == nullptr)
    {
        LogError << "Failed to create dvpp roi config for paster area.";
        return APP_ERR_COMM_FAILURE;
    }
    pasteAreaConfig_.reset(pastRoiCfg, g_roiConfigDeleter);

    APP_ERROR ret = acldvppVpcCropAndPasteAsync(dvppChannelDesc_, &inputDesc, &outputDesc, cropAreaConfig_.get(),
                                                pasteAreaConfig_.get(), dvppStream_);
    if (ret != APP_ERR_OK)
    {
        // release resource.
        LogError << "Failed to crop and paste asynchronously, ret = " << ret << ".";
        return ret;
    }
    if (withSynchronize)
    {
        ret = aclrtSynchronizeStream(dvppStream_);
        if (ret != APP_ERR_OK)
        {
            LogError << "Failed tp synchronize stream, ret = " << ret << ".";
            return ret;
        }
    }
    return APP_ERR_OK;
}

/*
 * @description: Get crop area
 * @param: input specifies the input image information
 * @param: output specifies the output image information
 * @param: processType specifies whether to perform proportional scaling
 * @param: cropRoi is used to save the info of the crop roi area
 * @return: APP_ERR_OK if success, other values if failure
 */
void DvppCommon::GetCropRoi(const DvppDataInfo &input, const DvppDataInfo &output, VpcProcessType processType,
                            CropRoiConfig &cropRoi) const
{
    // When processType is not VPC_PT_FILL, crop area is the whole input image
    if (processType != VPC_PT_FILL)
    {
        cropRoi.right = CONVERT_TO_ODD(input.width - ODD_NUM_1);
        cropRoi.down = CONVERT_TO_ODD(input.height - ODD_NUM_1);
        return;
    }

    bool widthRatioSmaller = true;
    // The scaling ratio is based on the smaller ratio to ensure the smallest edge to fill the targe edge
    float resizeRatio = static_cast<float>(input.width) / output.width;
    if (resizeRatio > (static_cast<float>(input.height) / output.height))
    {
        resizeRatio = static_cast<float>(input.height) / output.height;
        widthRatioSmaller = false;
    }

    const int halfValue = 2;
    // The left and up must be even, right and down must be odd which is required by acl
    if (widthRatioSmaller)
    {
        cropRoi.left = 0;
        cropRoi.right = CONVERT_TO_ODD(input.width - ODD_NUM_1);
        cropRoi.up = CONVERT_TO_EVEN(static_cast<uint32_t>((input.height - output.height * resizeRatio) / halfValue));
        cropRoi.down = CONVERT_TO_ODD(input.height - cropRoi.up - ODD_NUM_1);
        return;
    }

    cropRoi.up = 0;
    cropRoi.down = CONVERT_TO_ODD(input.height - ODD_NUM_1);
    cropRoi.left = CONVERT_TO_EVEN(static_cast<uint32_t>((input.width - output.width * resizeRatio) / halfValue));
    cropRoi.right = CONVERT_TO_ODD(input.width - cropRoi.left - ODD_NUM_1);
    return;
}

/*
 * @description: Get paste area
 * @param: input specifies the input image information
 * @param: output specifies the output image information
 * @param: processType specifies whether to perform proportional scaling
 * @param: pasteRio is used to save the info of the paste area
 * @return: APP_ERR_OK if success, other values if failure
 */
void DvppCommon::GetPasteRoi(const DvppDataInfo &input, const DvppDataInfo &output, VpcProcessType processType,
                             CropRoiConfig &pasteRoi) const
{
    if (processType == VPC_PT_FILL)
    {
        pasteRoi.right = CONVERT_TO_ODD(output.width - ODD_NUM_1);
        pasteRoi.down = CONVERT_TO_ODD(output.height - ODD_NUM_1);
        return;
    }

    bool widthRatioLarger = true;
    // The scaling ratio is based on the larger ratio to ensure the largest edge to fill the targe edge
    float resizeRatio = static_cast<float>(input.width) / output.width;
    if (resizeRatio < (static_cast<float>(input.height) / output.height))
    {
        resizeRatio = static_cast<float>(input.height) / output.height;
        widthRatioLarger = false;
    }

    // Left and up is 0 when the roi paste on the upper left corner
    if (processType == VPC_PT_PADDING)
    {
        pasteRoi.right = (input.width / resizeRatio) - ODD_NUM_1;
        pasteRoi.down = (input.height / resizeRatio) - ODD_NUM_1;
        pasteRoi.right = CONVERT_TO_ODD(pasteRoi.right);
        pasteRoi.down = CONVERT_TO_ODD(pasteRoi.down);
        return;
    }

    const int halfValue = 2;
    // Left and up is 0 when the roi paste on the middler location
    if (widthRatioLarger)
    {
        pasteRoi.left = 0;
        pasteRoi.right = output.width - ODD_NUM_1;
        pasteRoi.up = (output.height - (input.height / resizeRatio)) / halfValue;
        pasteRoi.down = output.height - pasteRoi.up - ODD_NUM_1;
    }
    else
    {
        pasteRoi.up = 0;
        pasteRoi.down = output.height - ODD_NUM_1;
        pasteRoi.left = (output.width - (input.width / resizeRatio)) / halfValue;
        pasteRoi.right = output.width - pasteRoi.left - ODD_NUM_1;
    }

    // The left must be even and align to 16, up must be even, right and down must be odd which is required by acl
    pasteRoi.left = DVPP_ALIGN_UP(CONVERT_TO_EVEN(pasteRoi.left), VPC_WIDTH_ALIGN);
    pasteRoi.right = CONVERT_TO_ODD(pasteRoi.right);
    pasteRoi.up = CONVERT_TO_EVEN(pasteRoi.up);
    pasteRoi.down = CONVERT_TO_ODD(pasteRoi.down);
    return;
}

/*
 * @description: Resize the image specified by input and save the result to member variable resizedImage_
 * @param: input specifies the input image information
 * @param: output specifies the output image information
 * @param: withSynchronize specifies whether to execute synchronously
 * @param: processType specifies whether to perform proportional scaling, default is non-proportional resize
 * @return: APP_ERR_OK if success, other values if failure
 * @attention: This function can be called only when the DvppCommon object is initialized with Init
 */
APP_ERROR DvppCommon::CombineResizeProcess(DvppDataInfo &input, DvppDataInfo &output, bool withSynchronize,
                                           VpcProcessType processType)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_)
    {
        LogError << "CombineResizeProcess cannot be called by the DvppCommon object which is initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }

    //检查缩放比例是否合适
    APP_ERROR ret = CheckResizeParams(input, output);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }

    //在外部设置对齐后宽高
    // Get widthStride and heightStride for input and output image according to the format
    // ret = GetVpcInputStrideSize(input.width, input.height, input.format, input.widthStride,
    //                             input.heightStride);
    // if (ret != APP_ERR_OK)
    // {
    //     return ret;
    // }

    resizedImage_ = std::make_shared<DvppDataInfo>();
    resizedImage_->width = output.width;
    resizedImage_->height = output.height;
    resizedImage_->format = output.format;
    ret = GetVpcOutputStrideSize(output.width, output.height, output.format, resizedImage_->widthStride,
                                 resizedImage_->heightStride);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }
    // Get output buffer size for resize output
    ret = GetVpcDataSize(output.width, output.height, output.format, resizedImage_->dataSize);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }
    // Malloc buffer for output of resize module
    // Need to pay attention to release of the buffer
    ret = acldvppMalloc((void **)(&(resizedImage_->data)), resizedImage_->dataSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to malloc " << resizedImage_->dataSize << " bytes on dvpp for resize, ret = " << ret << ".";
        return ret;
    }
    aclrtMemset(resizedImage_->data, resizedImage_->dataSize, YUV_GREYER_VALUE, resizedImage_->dataSize);
    resizedImage_->frameId = input.frameId;

    ret = VpcResize(input, *resizedImage_, withSynchronize, processType);
    if (ret != APP_ERR_OK)
    {
        // Release the output buffer when resize failed, otherwise release it after use
        RELEASE_DVPP_DATA(resizedImage_->data);
    }
    return ret;
}

//等比例缩放，不使用成员变量
APP_ERROR DvppCommon::CombineResizeProcess2(DvppDataInfo &input, DvppDataInfo &output, bool withSynchronize,
                                            VpcProcessType processType)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_)
    {
        LogError << "CombineResizeProcess cannot be called by the DvppCommon object which is initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }

    //检查缩放比例
    APP_ERROR ret = CheckResizeParams(input, output);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }

    ret = GetVpcOutputStrideSize(output.width, output.height, output.format, output.widthStride,
                                 output.heightStride);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }

    // Get output buffer size for resize output
    ret = GetVpcDataSize(output.width, output.height, output.format, output.dataSize);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }
    // Malloc buffer for output of resize module
    // Need to pay attention to release of the buffer
    ret = acldvppMalloc((void **)(&(output.data)), output.dataSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to malloc " << output.dataSize << " bytes on dvpp for resize, ret = " << ret << ".";
        return ret;
    }
    aclrtMemset(output.data, output.dataSize, YUV_GREYER_VALUE, output.dataSize);

    ret = VpcResize(input, output, withSynchronize, processType);
    if (ret != APP_ERR_OK)
    {
        // Release the output buffer when resize failed, otherwise release it after use
        RELEASE_DVPP_DATA(output.data);
    }
    return ret;
}

//抠图+缩放
APP_ERROR DvppCommon::CombineCropProcess2(DvppCropInputInfo &input, DvppDataInfo &output, bool withSynchronize)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_)
    {
        LogError << "CombineCropProcess2 cannot be called by the DvppCommon object which is initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }

    //检查抠图区域
    APP_ERROR ret = CheckCropParams(input);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }

    //检查缩放比例
    DvppDataInfo box_step1;
    box_step1.width = input.roi.right - input.roi.left + ODD_NUM_1;
    box_step1.height = input.roi.down - input.roi.up + ODD_NUM_1;
    ret = CheckResizeParams(box_step1, output);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }

    //得到输出对齐宽高
    ret = GetVpcOutputStrideSize(output.width, output.height, output.format, output.widthStride,
                                 output.heightStride);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }

    // Get output buffer size for resize output
    ret = GetVpcDataSize(output.width, output.height, output.format, output.dataSize);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }
    // Malloc buffer for output of resize module
    // Need to pay attention to release of the buffer
    ret = acldvppMalloc((void **)(&(output.data)), output.dataSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to malloc " << output.dataSize << " bytes on dvpp for resize, ret = " << ret << ".";
        return ret;
    }
    aclrtMemset(output.data, output.dataSize, YUV_GREYER_VALUE, output.dataSize);

    //抠图和缩放
    ret = VpcCropAndPaste(input, output, withSynchronize);
    if (ret != APP_ERR_OK)
    {
        // Release the output buffer when resize failed, otherwise release it after use
        RELEASE_DVPP_DATA(output.data);
    }
    return ret;
}

/*
 * @description: Set picture description information and execute crop function
 * @param: cropInput specifies the input image information and cropping area
 * @param: output specifies the output image information
 * @param: withSynchronize specifies whether to execute synchronously
 * @return: APP_ERR_OK if success, other values if failure
 * @attention: This function can be called only when the DvppCommon object is initialized with Init
 */
APP_ERROR DvppCommon::VpcCrop(const DvppCropInputInfo &cropInput, const DvppDataInfo &output, bool withSynchronize)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_)
    {
        LogError << "VpcCrop cannot be called by the DvppCommon object which is initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }

    acldvppPicDesc *inputDesc = acldvppCreatePicDesc();
    acldvppPicDesc *outputDesc = acldvppCreatePicDesc();
    cropInputDesc_.reset(inputDesc, g_picDescDeleter);
    cropOutputDesc_.reset(outputDesc, g_picDescDeleter);

    // Set dvpp picture descriptin info of input image
    APP_ERROR ret = SetDvppPicDescData(cropInput.dataInfo, *cropInputDesc_);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }
    // Set dvpp picture descriptin info of output image
    ret = SetDvppPicDescData(output, *cropOutputDesc_);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }
    return CropProcess(*cropInputDesc_, *cropOutputDesc_, cropInput.roi, withSynchronize);
}

/*
 * @description: Check whether the size of the cropped data and the cropped area meet the requirements
 * @param: input specifies the image information and the information about the area to be cropped
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::CheckCropParams(const DvppCropInputInfo &input) const
{
    APP_ERROR ret;

    //不检查数据大小是否合适
    // uint32_t payloadSize;
    // ret = GetVpcDataSize(input.dataInfo.widthStride, input.dataInfo.heightStride, PIXEL_FORMAT_YUV_SEMIPLANAR_420,
    //                      payloadSize);
    // if (ret != APP_ERR_OK)
    // {
    //     return ret;
    // }
    // if (payloadSize != input.dataInfo.dataSize)
    // {
    //     LogError << "Input data size: " << payloadSize << " to crop does not match input yuv image size: "
    //              << input.dataInfo.dataSize << ".";
    //     return APP_ERR_COMM_INVALID_PARAM;
    // }

    if ((!CHECK_EVEN(input.roi.left)) || (!CHECK_EVEN(input.roi.up)) || (!CHECK_ODD(input.roi.right)) ||
        (!CHECK_ODD(input.roi.down)))
    {
        LogError << "Crop area left and top(" << input.roi.left << ", " << input.roi.up
                 << ") must be even, right bottom(" << input.roi.right << "," << input.roi.down << ") must be odd.";
        return APP_ERR_COMM_INVALID_PARAM;
    }

    // Calculate crop width and height according to the input location
    uint32_t cropWidth = input.roi.right - input.roi.left + ODD_NUM_1;
    uint32_t cropHeight = input.roi.down - input.roi.up + ODD_NUM_1;
    if ((cropWidth < MIN_CROP_WIDTH) || (cropHeight < MIN_CROP_HEIGHT))
    {
        LogError << "Crop area width:" << cropWidth << " need to be larger than 10 and height:" << cropHeight
                 << " need to be larger than 6.";
        return APP_ERR_COMM_INVALID_PARAM;
    }

    if ((input.roi.left + cropWidth > input.dataInfo.width) || (input.roi.up + cropHeight > input.dataInfo.height))
    {
        LogError << "Target rectangle start location(" << input.roi.left << "," << input.roi.up << ") with size("
                 << cropWidth << "," << cropHeight << ") is out of the input image(" << input.dataInfo.width << ","
                 << input.dataInfo.height << ") to be cropped.";
        return APP_ERR_COMM_INVALID_PARAM;
    }

    return APP_ERR_OK;
}

/*
 * @description: It is used to crop an input image based on a specified region and
 *               store the cropped image to the output memory as an output image
 * @param: inputDesc specifies the description information of the input image
 * @param: outputDesc specifies the description information of the output image
 * @param: CropRoiConfig specifies the cropped area
 * @param: withSynchronize specifies whether to execute synchronously
 * @return: APP_ERR_OK if success, other values if failure
 * @attention: if the region of the output image is inconsistent with the crop area, the image is scaled again
 */
APP_ERROR DvppCommon::CropProcess(acldvppPicDesc &inputDesc, acldvppPicDesc &outputDesc,
                                  const CropRoiConfig &cropArea, bool withSynchronize)
{
    uint32_t leftOffset = CONVERT_TO_EVEN(cropArea.left);
    uint32_t rightOffset = CONVERT_TO_ODD(cropArea.right);
    uint32_t upOffset = CONVERT_TO_EVEN(cropArea.up);
    uint32_t downOffset = CONVERT_TO_ODD(cropArea.down);

    auto cropRioCfg = acldvppCreateRoiConfig(leftOffset, rightOffset, upOffset, downOffset);
    if (cropRioCfg == nullptr)
    {
        LogError << "DvppCommon: create dvpp vpc resize failed.";
        return APP_ERR_DVPP_RESIZE_FAIL;
    }
    cropRoiConfig_.reset(cropRioCfg, g_roiConfigDeleter);

    APP_ERROR ret = acldvppVpcCropAsync(dvppChannelDesc_, &inputDesc, &outputDesc, cropRoiConfig_.get(), dvppStream_);
    if (ret != APP_ERR_OK)
    {
        // release resource.
        LogError << "Failed to crop, ret = " << ret << ".";
        return ret;
    }
    if (withSynchronize)
    {
        ret = aclrtSynchronizeStream(dvppStream_);
        if (ret != APP_ERR_OK)
        {
            LogError << "Failed to synchronize stream, ret = " << ret << ".";
            return ret;
        }
    }
    return APP_ERR_OK;
}

/*
 * @description: Crop the image specified by the input parameter and saves the result to member variable cropImage_
 * @param: input specifies the input image information and cropping area
 * @param: output specifies the output image information
 * @param: withSynchronize specifies whether to execute synchronously
 * @return: APP_ERR_OK if success, other values if failure
 * @attention: This function can be called only when the DvppCommon object is initialized with Init
 */
APP_ERROR DvppCommon::CombineCropProcess(DvppCropInputInfo &input, DvppDataInfo &output, bool withSynchronize)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_)
    {
        LogError << "CombineCropProcess cannot be called by the DvppCommon object which is initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }

    // Get widthStride and heightStride for input and output image according to the format
    //外部设置输入的对齐宽高
    // APP_ERROR ret = GetVpcInputStrideSize(input.dataInfo.width, input.dataInfo.height, input.dataInfo.format,
    //                                       input.dataInfo.widthStride, input.dataInfo.heightStride);
    // if (ret != APP_ERR_OK)
    // {
    //     return ret;
    // }

    APP_ERROR ret = CheckCropParams(input);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }
    cropImage_ = std::make_shared<DvppDataInfo>();
    cropImage_->width = output.width;
    cropImage_->height = output.height;
    cropImage_->format = output.format;
    ret = GetVpcOutputStrideSize(output.width, output.height, output.format, cropImage_->widthStride,
                                 cropImage_->heightStride);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }
    // Get output buffer size for resize output
    ret = GetVpcDataSize(output.width, output.height, output.format, cropImage_->dataSize);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }

    // Malloc buffer for output of resize module
    // Need to pay attention to release of the buffer
    ret = acldvppMalloc((void **)(&(cropImage_->data)), cropImage_->dataSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to malloc " << cropImage_->dataSize << " bytes on dvpp for resize, ret = " << ret << ".";
        return ret;
    }
    cropImage_->frameId = input.dataInfo.frameId;
    ret = VpcCrop(input, *cropImage_, withSynchronize);
    if (ret != APP_ERR_OK)
    {
        // Release the output buffer when resize failed, otherwise release it after use
        RELEASE_DVPP_DATA(cropImage_->data);
    }
    return ret;
}

/*
 * @description: Set the description of the output image and decode
 * @param: input specifies the input image information
 * @param: output specifies the output image information
 * @param: withSynchronize specifies whether to execute synchronously
 * @return: APP_ERR_OK if success, other values if failure
 * @attention: This function can be called only when the DvppCommon object is initialized with Init
 */
APP_ERROR DvppCommon::JpegDecode(DvppDataInfo &input, DvppDataInfo &output, bool withSynchronize)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_)
    {
        LogError << "JpegDecode cannot be called by the DvppCommon object which is initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }

    acldvppPicDesc *outputDesc = acldvppCreatePicDesc();
    decodeOutputDesc_.reset(outputDesc, g_picDescDeleter);

    APP_ERROR ret = acldvppSetPicDescData(decodeOutputDesc_.get(), output.data);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set desc data, ret = " << ret << ".";
        return ret;
    }
    ret = acldvppSetPicDescFormat(decodeOutputDesc_.get(), output.format);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set desc format, ret = " << ret << ".";
        return ret;
    }
    ret = acldvppSetPicDescSize(decodeOutputDesc_.get(), output.dataSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set desc size, ret = " << ret << ".";
        return ret;
    }

    ret = acldvppJpegDecodeAsync(dvppChannelDesc_, input.data, input.dataSize, decodeOutputDesc_.get(), dvppStream_);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to decode jpeg, ret = " << ret << ".";
        return ret;
    }
    if (withSynchronize)
    {
        ret = aclrtSynchronizeStream(dvppStream_);
        if (ret != APP_ERR_OK)
        {
            LogError << "Failed to synchronize stream, ret = " << ret << ".";
            return APP_ERR_DVPP_JPEG_DECODE_FAIL;
        }
    }

    ret = acldvppGetPicDescRetCode(decodeOutputDesc_.get());
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to get image info, ret = " << ret << ".";
        return ret;
    }
    output.width = acldvppGetPicDescWidth(decodeOutputDesc_.get());
    output.height = acldvppGetPicDescHeight(decodeOutputDesc_.get());
    output.widthStride = acldvppGetPicDescWidthStride(decodeOutputDesc_.get());
    output.heightStride = acldvppGetPicDescHeightStride(decodeOutputDesc_.get());
    output.dataSize = acldvppGetPicDescSize(decodeOutputDesc_.get());

    return APP_ERR_OK;
}

/*
 * @description: Get the aligned width and height of the image after decoding
 * @param: width specifies the width before alignment
 * @param: height specifies the height before alignment
 * @param: widthStride is used to save the width after alignment
 * @param: heightStride is used to save the height after alignment
 * @return: APP_ERR_OK if success, other values if failure
 * @attention: This function is used for obtaining jpeg decode stride in Ascend 310
 */
void DvppCommon::GetJpegDecodeStrideSize(uint32_t width, uint32_t height, uint32_t &widthStride, uint32_t &heightStride)
{
    widthStride = DVPP_ALIGN_UP(width, JPEGD_STRIDE_WIDTH);
    heightStride = DVPP_ALIGN_UP(height, JPEGD_STRIDE_HEIGHT);
}

/*
 * @description: Get picture width and height and number of channels from image data
 * @param: data specifies the memory to store the image data
 * @param: dataSize specifies the size of the image data
 * @param: width is used to save the image width
 * @param: height is used to save the image height
 * @param: components is used to save the number of channels
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::GetJpegImageInfo(const void *data, uint32_t dataSize, uint32_t &width, uint32_t &height,
                                       int32_t &components)
{
    uint32_t widthTmp;
    uint32_t heightTmp;
    int32_t componentsTmp;
    APP_ERROR ret = acldvppJpegGetImageInfo(data, dataSize, &widthTmp, &heightTmp, &componentsTmp);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to get image info of jpeg, ret = " << ret << ".";
        return ret;
    }
    if (widthTmp > MAX_JPEGD_WIDTH || widthTmp < MIN_JPEGD_WIDTH)
    {
        LogError << "Input width is invalid, not in [" << MIN_JPEGD_WIDTH << ", " << MAX_JPEGD_WIDTH << "].";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    if (heightTmp > MAX_JPEGD_HEIGHT || heightTmp < MIN_JPEGD_HEIGHT)
    {
        LogError << "Input height is invalid, not in [" << MIN_JPEGD_HEIGHT << ", " << MAX_JPEGD_HEIGHT << "].";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    width = widthTmp;
    height = heightTmp;
    components = componentsTmp;
    return APP_ERR_OK;
}

/*
 * @description: Get the size of the buffer for storing decoded images based on the image data, size, and format
 * @param: data specifies the memory to store the image data
 * @param: dataSize specifies the size of the image data
 * @param: format specifies the image format
 * @param: decSize is used to store the result size
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::GetJpegDecodeDataSize(const void *data, uint32_t dataSize, acldvppPixelFormat format,
                                            uint32_t &decSize)
{
    uint32_t outputSize;
    APP_ERROR ret = acldvppJpegPredictDecSize(data, dataSize, format, &outputSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to predict decode size of jpeg image, ret = " << ret << ".";
        return ret;
    }
    decSize = outputSize;
    return APP_ERR_OK;
}

/*
 * @description: Decode the image specified by imageInfo and save the result to member variable decodedImage_
 * @param: imageInfo specifies image information
 * @param: format specifies the image format
 * @param: withSynchronize specifies whether to execute synchronously
 * @return: APP_ERR_OK if success, other values if failure
 * @attention: This function can be called only when the DvppCommon object is initialized with Init
 */
APP_ERROR DvppCommon::CombineJpegdProcess(const RawData &imageInfo, acldvppPixelFormat format, bool withSynchronize, bool bReleaseInput/*=false*/)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_)
    {
        LogError << "CombineJpegdProcess cannot be called by the DvppCommon object which is initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }

    int32_t components;
    inputImage_ = std::make_shared<DvppDataInfo>();
    inputImage_->format = format;
    APP_ERROR ret = GetJpegImageInfo(imageInfo.data.get(), imageInfo.lenOfByte, inputImage_->width, inputImage_->height,
                                     components);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to get input image info, ret = " << ret << ".";
        return ret;
    }

    // Get the buffer size of decode output according to the input data and output format
    uint32_t outBuffSize;
    ret = GetJpegDecodeDataSize(imageInfo.data.get(), imageInfo.lenOfByte, format, outBuffSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to get size of decode output buffer, ret = " << ret << ".";
        return ret;
    }

    // In TransferImageH2D function, device buffer will be alloced to store the input image
    // Need to pay attention to release of the buffer
    ret = TransferImageH2D(imageInfo, inputImage_);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }

    decodedImage_ = std::make_shared<DvppDataInfo>();
    decodedImage_->format = format;
    decodedImage_->dataSize = outBuffSize;
    // Malloc dvpp buffer to store the output data after decoding
    // Need to pay attention to release of the buffer
    ret = acldvppMalloc((void **)&decodedImage_->data, decodedImage_->dataSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to malloc memory on dvpp, ret = " << ret << ".";
        RELEASE_DVPP_DATA(inputImage_->data);
        return ret;
    }

    ret = JpegDecode(*inputImage_, *decodedImage_, withSynchronize);
    if (ret != APP_ERR_OK)
    {
        // Release the output buffer when decode failed, otherwise release it after use
        RELEASE_DVPP_DATA(inputImage_->data);
        //inputImage_->data = nullptr;
        RELEASE_DVPP_DATA(decodedImage_->data);
        //decodedImage_->data = nullptr;
        return ret;
    }
    
    //释放图片解码时内部申请的dvpp输入内存。当参数bReleaseInput没传或传false时由调用者释放(兼顾历史调用点)
    if(bReleaseInput)
    {
        RELEASE_DVPP_DATA(inputImage_->data);
    }

    return APP_ERR_OK;
}

/*
 * @description: Transfer data from host to device
 * @param: imageInfo specifies the image data on the host
 * @param: jpegInput is used to save the buffer and its size which is allocate on the device
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::TransferImageH2D(const RawData &imageInfo, const std::shared_ptr<DvppDataInfo> &jpegInput) const
{
    // Check image buffer size validity
    if (imageInfo.lenOfByte <= 0)
    {
        LogError << "The input buffer size on host should not be empty.";
        return APP_ERR_COMM_INVALID_PARAM;
    }

    uint8_t *inDevBuff = nullptr;
    APP_ERROR ret = acldvppMalloc((void **)&inDevBuff, imageInfo.lenOfByte);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to malloc " << imageInfo.lenOfByte << " bytes on dvpp, ret = " << ret << ".";
        return ret;
    }

    // Copy the image data from host to device
    ret = aclrtMemcpyAsync(inDevBuff, imageInfo.lenOfByte, imageInfo.data.get(), imageInfo.lenOfByte,
                           ACL_MEMCPY_HOST_TO_DEVICE, dvppStream_);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to copy " << imageInfo.lenOfByte << " bytes from host to device, ret = " << ret << ".";
        RELEASE_DVPP_DATA(inDevBuff);
        return ret;
    }
    // Attention: We must call the aclrtSynchronizeStream to ensure the task of memory replication has been completed
    // after calling aclrtMemcpyAsync
    ret = aclrtSynchronizeStream(dvppStream_);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to synchronize stream, ret = " << ret << ".";
        RELEASE_DVPP_DATA(inDevBuff);
        return ret;
    }
    jpegInput->data = inDevBuff;
    jpegInput->dataSize = imageInfo.lenOfByte;
    return APP_ERR_OK;
}

/*
 * @description: Create and set the description of a video stream
 * @param: data specifies the information about the video stream
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::CreateStreamDesc(std::shared_ptr<DvppDataInfo> data)
{
    // Malloc input device memory which need to be released in vdec callback function
    void *modelInBuff = nullptr;
    APP_ERROR ret = acldvppMalloc(&modelInBuff, data->dataSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to malloc dvpp data with " << data->dataSize << " bytes, ret = " << ret << ".";
        return APP_ERR_ACL_BAD_ALLOC;
    }
    // copy input to device memory
    ret = aclrtMemcpy(modelInBuff, data->dataSize, static_cast<uint8_t *>(data->data), data->dataSize,
                      ACL_MEMCPY_HOST_TO_DEVICE);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to copy memory with " << data->dataSize << " bytes from host to device, ret = "
                 << ret << ".";
        acldvppFree(modelInBuff);
        modelInBuff = nullptr;
        return APP_ERR_ACL_FAILURE;
    }
    // Create input stream desc which need to be destoryed in vdec callback function
    streamInputDesc_ = acldvppCreateStreamDesc();
    if (streamInputDesc_ == nullptr)
    {
        LogError << "Failed to create input stream description.";
        return APP_ERR_ACL_FAILURE;
    }
    ret = acldvppSetStreamDescData(streamInputDesc_, modelInBuff);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set data for stream desdescription, ret = " << ret << ".";
        return ret;
    }
    // set size for dvpp stream desc
    ret = acldvppSetStreamDescSize(streamInputDesc_, data->dataSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set data size for stream desdescription,  ret = " << ret << ".";
        return ret;
    }

    ret = acldvppSetStreamDescTimestamp(streamInputDesc_, data->timestamp);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set data Timestamp for stream desdescription,  ret = " << ret << ".";
        return ret;
    }
    return APP_ERR_OK;
}

/*
 * @description: Decode the video based on the video stream specified by data and user-defined data,
 *               and outputs the image of each frame
 * @param: data specifies the information about the video stream
 * @param: userdata is specified for user-defined data
 * @return: APP_ERR_OK if success, other values if failure
 * @attention: This function can be called only when the DvppCommon object is initialized with InitVdec
 */
APP_ERROR DvppCommon::CombineVdecProcess(std::shared_ptr<DvppDataInfo> data, void *userData)
{
    // Return special error code when the DvppCommon object is not initialized with InitVdec
    if (!isVdec_)
    {
        LogError << "CombineVdecProcess cannot be called by the DvppCommon object which is not initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }

    // create stream desc
    APP_ERROR ret = CreateStreamDesc(data);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }

    uint32_t dataSize;
    ret = GetVideoDecodeDataSize(vdecConfig_.inputWidth, vdecConfig_.inputHeight, vdecConfig_.outFormat, dataSize);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }

    void *picOutBufferDev = nullptr;
    // picOutBufferDev need to be destoryed in vdec callback function
    ret = acldvppMalloc(&picOutBufferDev, dataSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to malloc memory with " << dataSize << " bytes, ret = " << ret << ".";
        return APP_ERR_ACL_BAD_ALLOC;
    }

    // picOutputDesc_ will be destoryed in vdec callback function
    picOutputDesc_ = acldvppCreatePicDesc();
    if (picOutputDesc_ == NULL)
    {
        return APP_ERR_ACL_BAD_ALLOC;
    }

    DvppDataInfo dataInfo;
    dataInfo.width = vdecConfig_.inputWidth;
    dataInfo.height = vdecConfig_.inputHeight;
    dataInfo.format = vdecConfig_.outFormat;
    ret = GetVideoDecodeStrideSize(dataInfo.width, dataInfo.height, dataInfo.format, dataInfo.widthStride, dataInfo.heightStride);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to GetVideoDecodeStrideSize, ret = " << ret << ".";
        return ret;
    }
    dataInfo.dataSize = dataSize;
    dataInfo.data = static_cast<uint8_t *>(picOutBufferDev);

    ret = SetDvppPicDescData(dataInfo, *picOutputDesc_);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }

    // send frame，发送到通道中
    ret = aclvdecSendFrame(vdecChannelDesc_, streamInputDesc_, picOutputDesc_, nullptr, userData);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to send frame, ret = " << ret << ".";
        return APP_ERR_ACL_FAILURE;
    }

    return APP_ERR_OK;
}

/*
 * @description: Send eos frame when video stream ends
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::VdecSendEosFrame() const
{
    // create input stream desc
    acldvppStreamDesc *eosStreamDesc = acldvppCreateStreamDesc();
    if (eosStreamDesc == nullptr)
    {
        LogError << "Fail to create dvpp stream desc for eos.";
        return ACL_ERROR_FAILURE;
    }

    // set eos for eos stream desc
    APP_ERROR ret = acldvppSetStreamDescEos(eosStreamDesc, true);
    if (ret != ACL_ERROR_NONE)
    {
        LogError << "Fail to set eos for stream desc, ret = " << ret << ".";
        acldvppDestroyStreamDesc(eosStreamDesc);
        return ret;
    }

    // send eos and synchronize
    ret = aclvdecSendFrame(vdecChannelDesc_, eosStreamDesc, nullptr, nullptr, nullptr);
    if (ret != ACL_ERROR_NONE)
    {
        LogError << "Fail to send eos, ret = " << ret << ".";
        acldvppDestroyStreamDesc(eosStreamDesc);
        return ret;
    }

    // destroy input stream desc
    ret = acldvppDestroyStreamDesc(eosStreamDesc);
    if (ret != ACL_ERROR_NONE)
    {
        LogError << "Fail to destory dvpp stream desc for eos, ret = " << ret << ".";
        return ret;
    }
    return ret;
}

/*
 * @description: Get the aligned width and height of the output image after video decoding
 * @param: width specifies the width before alignment
 * @param: height specifies the height before alignment
 * @param: format specifies the format of the output image
 * @param: widthStride is used to save the width after alignment
 * @param: heightStride is used to save the height after alignment
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::GetVideoDecodeStrideSize(uint32_t width, uint32_t height, acldvppPixelFormat format,
                                               uint32_t &widthStride, uint32_t &heightStride)
{
    // Check the invalidty of output format and calculate the output width and height
    if (format != PIXEL_FORMAT_YUV_SEMIPLANAR_420 && format != PIXEL_FORMAT_YVU_SEMIPLANAR_420)
    {
        LogError << "Input format[" << format << "] for VPC is not supported, just support NV12 or NV21.";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    widthStride = (width < VDEC_SPECIAL_WIDTH) ? VDEC_SPECIAL_STRIDE : DVPP_ALIGN_UP(width, VDEC_STRIDE_WIDTH);
    heightStride = DVPP_ALIGN_UP(height, VDEC_STRIDE_HEIGHT);
    return APP_ERR_OK;
}

/*
 * @description: Get the buffer size for storing results after video decoding
 * @param  width specifies the width of the output image after video decoding
 * @param  height specifies the height of the output image after video decoding
 * @param  format specifies the format of the output image
 * @param: vpcSize is used to save the result size
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::GetVideoDecodeDataSize(uint32_t width, uint32_t height, acldvppPixelFormat format,
                                             uint32_t &vdecSize)
{
    // Check the invalid format of vdec output and calculate the output buffer size
    if (format != PIXEL_FORMAT_YUV_SEMIPLANAR_420 && format != PIXEL_FORMAT_YVU_SEMIPLANAR_420)
    {
        LogError << "Format[" << format << "] for VPC is not supported, just support NV12 or NV21.";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    uint32_t widthStride = DVPP_ALIGN_UP(width, VDEC_STRIDE_WIDTH);
    uint32_t heightStride = DVPP_ALIGN_UP(height, VDEC_STRIDE_HEIGHT);
    vdecSize = widthStride * heightStride * YUV_BGR_SIZE_CONVERT_3 / YUV_BGR_SIZE_CONVERT_2;
    return APP_ERR_OK;
}

/*
 * @description: Encode a YUV image into a JPG image
 * @param: input specifies the input image information
 * @param: output specifies the output image information
 * @param: jpegeConfig specifies the encoding configuration data
 * @param: withSynchronize specifies whether to execute synchronously
 * @return: APP_ERR_OK if success, other values if failure
 * @attention: This function can be called only when the DvppCommon object is initialized with Init
 */
APP_ERROR DvppCommon::JpegEncode(DvppDataInfo &input, DvppDataInfo &output, acldvppJpegeConfig *jpegeConfig,
                                 bool withSynchronize) const
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_)
    {
        LogError << "JpegEncode cannot be called by the DvppCommon object which is initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }

    APP_ERROR ret = SetDvppPicDescData(input, *encodeInputDesc_);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }

    ret = acldvppJpegEncodeAsync(dvppChannelDesc_, encodeInputDesc_.get(), output.data, &output.dataSize, jpegeConfig,
                                 dvppStream_);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to encode image, ret = " << ret << ".";
        return ret;
    }
    if (withSynchronize)
    {
        ret = aclrtSynchronizeStream(dvppStream_);
        if (ret != APP_ERR_OK)
        {
            LogError << "Failed to aclrtSynchronizeStream, ret = " << ret << ".";
            return APP_ERR_DVPP_JPEG_ENCODE_FAIL;
        }
    }
    //LogInfo << "Encode successfully.";
    return APP_ERR_OK;
}

/*
 * @description: Get the aligned width, height, and data size of the input image
 * @param: inputImage specifies the input image information
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::GetJpegEncodeStrideSize(std::shared_ptr<DvppDataInfo> &inputImage)
{
    uint32_t inputWidth = inputImage->width;
    uint32_t inputHeight = inputImage->height;
    acldvppPixelFormat format = inputImage->format;
    uint32_t widthStride;
    uint32_t heightStride;
    uint32_t encodedBufferSize;
    // Align up the input width and height and calculate buffer size of encoded input file
    if (format == PIXEL_FORMAT_YUV_SEMIPLANAR_420 || format == PIXEL_FORMAT_YVU_SEMIPLANAR_420)
    {
        widthStride = DVPP_ALIGN_UP(inputWidth, JPEGE_STRIDE_WIDTH);
        heightStride = DVPP_ALIGN_UP(inputHeight, JPEGE_STRIDE_HEIGHT);
        encodedBufferSize = widthStride * heightStride * YUV_BYTES_NU / YUV_BYTES_DE;
    }
    else if (format == PIXEL_FORMAT_YUYV_PACKED_422 || format == PIXEL_FORMAT_UYVY_PACKED_422 ||
             format == PIXEL_FORMAT_YVYU_PACKED_422 || format == PIXEL_FORMAT_VYUY_PACKED_422)
    {
        widthStride = DVPP_ALIGN_UP(inputWidth * YUV422_WIDTH_NU, JPEGE_STRIDE_WIDTH);
        heightStride = DVPP_ALIGN_UP(inputHeight, JPEGE_STRIDE_HEIGHT);
        encodedBufferSize = widthStride * heightStride;
    }
    else
    {
        return APP_ERR_COMM_INVALID_PARAM;
    }
    if (encodedBufferSize == 0)
    {
        LogError << "Input host buffer size is empty.";
        return APP_ERR_COMM_INVALID_PARAM;
    }
    inputImage->widthStride = widthStride;
    inputImage->heightStride = heightStride;
    inputImage->dataSize = encodedBufferSize;
    return APP_ERR_OK;
}

/*
 * @description: Estimate the size of the output memory required by image encoding according to
 *               the input image description and image encoding configuration data
 * @param: input specifies specifies the input image information
 * @param: jpegeConfig specifies the encoding configuration data
 * @param: encSize is used to save the result size
 * @return: APP_ERR_OK if success, other values if failure
 * @attention: This function can be called only when the DvppCommon object is initialized with Init
 */
APP_ERROR DvppCommon::GetJpegEncodeDataSize(DvppDataInfo &input, acldvppJpegeConfig *jpegeConfig, uint32_t &encSize)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_)
    {
        LogError << "GetJpegEncodeDataSize cannot be called by the DvppCommon object which is initialized with "
                 << "InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }

    acldvppPicDesc *inputDesc = acldvppCreatePicDesc();
    encodeInputDesc_.reset(inputDesc, g_picDescDeleter);

    APP_ERROR ret = SetDvppPicDescData(input, *encodeInputDesc_);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }

    uint32_t outputSize;
    ret = acldvppJpegPredictEncSize(encodeInputDesc_.get(), jpegeConfig, &outputSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to predict encode size of jpeg image, ret = " << ret << ".";
        return ret;
    }
    encSize = outputSize;
    return APP_ERR_OK;
}

/*
 * @description: Set the encoding configuration data
 * @param: level specifies the encode quality range
 * @param: jpegeConfig specifies the encoding configuration data
 * @return: APP_ERR_OK if success, other values if failure
 */
APP_ERROR DvppCommon::SetEncodeLevel(uint32_t level, acldvppJpegeConfig &jpegeConfig)
{
    // Set the encoding quality
    // The coding quality range [0, 100]
    // The level 0 coding quality is similar to the level 100
    // The smaller the value in [1, 100], the worse the quality of the output picture
    auto ret = (APP_ERROR)acldvppSetJpegeConfigLevel(&jpegeConfig, level);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }
    return APP_ERR_OK;
}

/*
 * @description: Encode the image specified by imageInfo and save the result to member variable encodedImage_
 * @param: imageInfo specifies image information
 * @param: width specifies the width of the input image
 * @param: height specifies the height of the input image
 * @param: format specifies the format of the input image
 * @param: withSynchronize specifies whether to execute synchronously
 * @return: APP_ERR_OK if success, other values if failure
 * @attention: This function can be called only when the DvppCommon object is initialized with Init
 */
APP_ERROR DvppCommon::CombineJpegeProcess(const RawData &imageInfo, uint32_t width, uint32_t height,
                                          acldvppPixelFormat format, bool withSynchronize)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_)
    {
        LogError << "CombineJpegeProcess cannot be called by the DvppCommon object which is initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }
    inputImage_ = std::make_shared<DvppDataInfo>();
    inputImage_->format = format;
    inputImage_->width = width;
    inputImage_->height = height;
    // In TransferImageH2D function, device buffer will be alloced to store the input image
    // Need to pay attention to release of the buffer
    APP_ERROR ret = TransferImageH2D(imageInfo, inputImage_);
    if (ret != APP_ERR_OK)
    {
        return ret;
    }
    // Get stride size of encoded image
    ret = GetJpegEncodeStrideSize(inputImage_);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to get encode stride size of input image file, ret = " << ret << ".";
        return ret;
    }

    auto jpegeConfig = acldvppCreateJpegeConfig();
    jpegeConfig_.reset(jpegeConfig, g_jpegeConfigDeleter);

    uint32_t encodeLevel = 100;
    ret = SetEncodeLevel(encodeLevel, *jpegeConfig_);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set encode level, ret = " << ret << ".";
        return ret;
    }

    // Get the buffer size of encode output according to the input data and jpeg encode config
    uint32_t encodeOutBufferSize;
    ret = GetJpegEncodeDataSize(*inputImage_, jpegeConfig_.get(), encodeOutBufferSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to get size of encode output buffer, ret = " << ret << ".";
        return ret;
    }

    encodedImage_ = std::make_shared<DvppDataInfo>();
    encodedImage_->dataSize = encodeOutBufferSize;
    // Malloc dvpp buffer to store the output data after decoding
    // Need to pay attention to release of the buffer
    ret = acldvppMalloc((void **)&encodedImage_->data, encodedImage_->dataSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to malloc memory on dvpp, ret = " << ret << ".";
        acldvppFree(inputImage_->data);
        return ret;
    }

    // Encode input image
    ret = JpegEncode(*inputImage_, *encodedImage_, jpegeConfig_.get(), withSynchronize);
    if (ret != APP_ERR_OK)
    {
        // Release the output buffer when decode failed, otherwise release it after use
        acldvppFree(inputImage_->data);
        acldvppFree(encodedImage_->data);
        return ret;
    }
    return APP_ERR_OK;
}

//jpg编码，不使用成员变量
APP_ERROR DvppCommon::CombineJpegeProcess2(DvppDataInfo &input, DvppDataInfo &output, bool withSynchronize)
{
    // Return special error code when the DvppCommon object is initialized with InitVdec
    if (isVdec_)
    {
        LogError << "CombineJpegeProcess2 cannot be called by the DvppCommon object which is initialized with InitVdec.";
        return APP_ERR_DVPP_OBJ_FUNC_MISMATCH;
    }

    auto jpegeConfig = acldvppCreateJpegeConfig();
    jpegeConfig_.reset(jpegeConfig, g_jpegeConfigDeleter);

    uint32_t encodeLevel = 25; //编码质量设为25，防止图片太大，sftp上传太慢
    APP_ERROR ret = SetEncodeLevel(encodeLevel, *jpegeConfig_);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to set encode level, ret = " << ret << ".";
        return ret;
    }

    // Get the buffer size of encode output according to the input data and jpeg encode config
    uint32_t encodeOutBufferSize;
    ret = GetJpegEncodeDataSize(input, jpegeConfig_.get(), encodeOutBufferSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to get size of encode output buffer, ret = " << ret << ".";
        return ret;
    }

    //创建输出内存
    output.dataSize = encodeOutBufferSize;

    // Malloc dvpp buffer to store the output data after decoding
    // Need to pay attention to release of the buffer
    ret = acldvppMalloc((void **)(&output.data), output.dataSize);
    if (ret != APP_ERR_OK)
    {
        LogError << "Failed to malloc memory on dvpp, ret = " << ret << ".";
        return ret;
    }

    // Encode input image
    ret = JpegEncode(input, output, jpegeConfig_.get(), withSynchronize);
    if (ret != APP_ERR_OK)
    {
        // Release the output buffer when resize failed, otherwise release it after use
        RELEASE_DVPP_DATA(output.data);
        return ret;
    }

    return APP_ERR_OK;
}

std::shared_ptr<DvppDataInfo> DvppCommon::GetInputImage() const
{
    return inputImage_;
}

std::shared_ptr<DvppDataInfo> DvppCommon::GetDecodedImage() const
{
    return decodedImage_;
}

std::shared_ptr<DvppDataInfo> DvppCommon::GetResizedImage() const
{
    return resizedImage_;
}

std::shared_ptr<DvppDataInfo> DvppCommon::GetEncodedImage() const
{
    return encodedImage_;
}

std::shared_ptr<DvppDataInfo> DvppCommon::GetCropedImage() const
{
    return cropImage_;
}

DvppCommon::~DvppCommon() {}
