/* * 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 #include #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(input.width) / output.width; if (resizeRatio > (static_cast(input.height) / output.height)) { resizeRatio = static_cast(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((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((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(input.width) / output.width; if (resizeRatio < (static_cast(input.height) / output.height)) { resizeRatio = static_cast(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(); 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(); 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(); 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(); 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 &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 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(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 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(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 &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(); 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(); 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 DvppCommon::GetInputImage() const { return inputImage_; } std::shared_ptr DvppCommon::GetDecodedImage() const { return decodedImage_; } std::shared_ptr DvppCommon::GetResizedImage() const { return resizedImage_; } std::shared_ptr DvppCommon::GetEncodedImage() const { return encodedImage_; } std::shared_ptr DvppCommon::GetCropedImage() const { return cropImage_; } DvppCommon::~DvppCommon() {}