Updated on 2022-03-13 GMT+08:00

Implementing the VDEC Function

For a video stream, after you create an instance by calling CreateVdecApi, you must use the same instance to call VdecCtl for video decoding, and then call DestroyVdecApi to release the instance.

During video decoding, if you need to switch from a video stream to another video stream, you must release the instance of the previous stream by calling DestroyVdecApi, and then create an instance by calling CreateVdecApi to process the new video stream.

This example reads the H.264 stream file named test_file, calls the VDEC function, and saves the decoding result to the output_dir directory.

// User-defined sub-class
class HIAI_DATA_SP_SON: public HIAI_DATA_SP {
public:
    ~ HIAI_DATA_SP_SON ()
    {
        //destruct here;
    }
    // User-defined member functions. The following is only an example.
    uint8_t GetTotalFrameNum()        
    {            
        return info_.totalFrameNum;        
    }   
	
private:        
    // User-defined member variables. The INFO structure is only an example.
    struct INFO {            
        uint8_t totalFrameNum;            
        uint8_t frameRate;        
    }info_;
};
 
IDVPPAPI * pidvppapi_vpc = NULL; 
IDVPPAPI * pidvppapi_vdec = NULL;
 
// The callback function is used as an example. The caller needs to redefine the callback function as required.
void FrameReturn(FRAME* frame, void* hiaiData)
{
    static int32_t imageCount = 0;
    imageCount++;
    HIAI_ENGINE_LOG("call save frame number:[%d], width:[%d], height:[%d]", imageCount, frame->width, frame->height);
    // The image directly output by vdec is an image of hfbc compression format, which cannot be directly displayed.
    // It is necessary to call vpc to convert hfbc to an image of uncompressed format to display.
    HIAI_ENGINE_LOG("start call vpc interface to translate hfbc.");
    IDVPPAPI* dvppHandle = nullptr;
    int32_t ret = CreateDvppApi(dvppHandle);
    if (ret != 0) {
        HIAI_ENGINE_LOG(HIAI_CREATE_DVPP_ERROR, "creat dvpp api fail.");
        return;
    }

    // Construct vpc input configuration.
    std::shared_ptr<VpcUserImageConfigure> userImage(new VpcUserImageConfigure);
    // bareDataAddr should be null which the image is hfbc.
    userImage->bareDataAddr = nullptr;
    userImage->bareDataBufferSize = 0;
    userImage->widthStride = frame->width;
    userImage->heightStride = frame->height;
    // Configuration input format
    string imageFormat(frame->image_format);
    if (frame->bitdepth == 8) {
        if (imageFormat == "nv12") {
            userImage->inputFormat = INPUT_YUV420_SEMI_PLANNER_UV;
        } else {
            userImage->inputFormat = INPUT_YUV420_SEMI_PLANNER_VU;
        }
    } else {
        if (imageFormat == "nv12") {
            userImage->inputFormat = INPUT_YUV420_SEMI_PLANNER_UV_10BIT;
        } else {
            userImage->inputFormat = INPUT_YUV420_SEMI_PLANNER_VU_10BIT;
        }
    }
    userImage->outputFormat = OUTPUT_YUV420SP_UV;
    userImage->isCompressData = true;
    // Configure hfbc input address
    VpcCompressDataConfigure* compressDataConfigure = &userImage->compressDataConfigure;
    uint64_t baseAddr = (uint64_t)frame->buffer;
    compressDataConfigure->lumaHeadAddr = baseAddr + frame->offset_head_y;
    compressDataConfigure->chromaHeadAddr = baseAddr + frame->offset_head_c;
    compressDataConfigure->lumaPayloadAddr = baseAddr + frame->offset_payload_y;
    compressDataConfigure->chromaPayloadAddr = baseAddr + frame->offset_payload_c;
    compressDataConfigure->lumaHeadStride = frame->stride_head;
    compressDataConfigure->chromaHeadStride = frame->stride_head;
    compressDataConfigure->lumaPayloadStride = frame->stride_payload;
    compressDataConfigure->chromaPayloadStride = frame->stride_payload;

    userImage->yuvSumEnable = false;
    userImage->cmdListBufferAddr = nullptr;
    userImage->cmdListBufferSize = 0;
    // Configure the roi area and output area
    std::shared_ptr<VpcUserRoiConfigure> roiConfigure(new VpcUserRoiConfigure);
    roiConfigure->next = nullptr;
    userImage->roiConfigure = roiConfigure.get();
    VpcUserRoiInputConfigure* roiInput = &roiConfigure->inputConfigure;
    roiInput->cropArea.leftOffset = 0;
    roiInput->cropArea.rightOffset = frame->width - 1;
    roiInput->cropArea.upOffset = 0;
    roiInput->cropArea.downOffset = frame->height - 1;
    VpcUserRoiOutputConfigure* roiOutput = &roiConfigure->outputConfigure;
    roiOutput->outputArea.leftOffset = 0;
    roiOutput->outputArea.rightOffset = frame->width - 1;
    roiOutput->outputArea.upOffset = 0;
    roiOutput->outputArea.downOffset = frame->height - 1;
    roiOutput->bufferSize = ALIGN_UP(frame->width, 16) * ALIGN_UP(frame->height, 2) * 3 / 2;
    roiOutput->addr = (uint8_t*)HIAI_DVPP_DMalloc(roiOutput->bufferSize);
    if (roiOutput->addr == nullptr) {
        HIAI_ENGINE_LOG(HIAI_VPC_CTL_ERROR, "can not alloc roiOutput buffer");
        DestroyDvppApi(dvppHandle);
        return;
    }
    roiOutput->widthStride = ALIGN_UP(frame->width, 16);
    roiOutput->heightStride = ALIGN_UP(frame->height, 2);

    dvppapi_ctl_msg dvppApiCtlMsg;
    dvppApiCtlMsg.in = (void*)(userImage.get());
    dvppApiCtlMsg.in_size = sizeof(VpcUserImageConfigure);
    ret = DvppCtl(dvppHandle, DVPP_CTL_VPC_PROC, &dvppApiCtlMsg);
    if (ret != 0) {
        HIAI_ENGINE_LOG(HIAI_ERROR, "call dvppctl fail");
        HIAI_DVPP_DFree(roiOutput->addr);
        roiOutput->addr = nullptr;
        DestroyDvppApi(dvppHandle);
        return;
    }
    ret = FrameReturnSaveVpcResult(frame, roiOutput->addr, imageCount);
    if (ret != 0) {
        HIAI_ENGINE_LOG(HIAI_ERROR, "save vpc result fail");
    }
    HIAI_DVPP_DFree(roiOutput->addr);
    roiOutput->addr = nullptr;
    DestroyDvppApi(dvppHandle);
    return;
}

/*
*Error reporting callback function. If you do not need error information, you do not need to define this function.
*/
void ErrReport(VDECERR* vdecErr)
{
    if (vdecErr != nullptr) {
        HIAI_ENGINE_LOG(HIAI_CREATE_DVPP_ERROR, "vdec error code is %d, channelId = %u\n", vdecErr->errType, vdecErr->channelId);
    }
}

// Main entry of the test function
void TEST_VDEC()
{
    char outputDir[20] = {0};
    int32_t safeFuncRet = memset_s(outputDir, sizeof(outputDir), 0, 20);
    if (safeFuncRet != EOK) {
        HIAI_ENGINE_LOG(HIAI_ERROR, "memset_s fail");
        return;
    }
    safeFuncRet = strncpy_s(outputDir, sizeof(outputDir), "output_dir", strlen("output_dir"));
    if (safeFuncRet != EOK) {
        HIAI_ENGINE_LOG(HIAI_ERROR, "strncpy_s fail");
        return;
    }
    if (access(outputDir, F_OK) == -1) {
        if (mkdir(outputDir, 0700) < 0) { // directory authority: 0700
            HIAI_ENGINE_LOG(HIAI_VDEC_CTL_ERROR, "create dir failed.");
            return;
        }
    } else if (access(outputDir, R_OK | W_OK | X_OK) == -1) {
        HIAI_ENGINE_LOG(HIAI_VDEC_CTL_ERROR, "output directory authority is not correct, use 700 instead.");
        return;
    }
    IDVPPAPI *pidvppapi = nullptr;
    int32_t ret = CreateVdecApi(pidvppapi, 0);
    if (ret != 0) {
        HIAI_ENGINE_LOG(HIAI_CREATE_DVPP_ERROR, "create dvpp api fail.");
        return;
    }
    if (pidvppapi != nullptr) {
        FILE* fp = fopen(g_inFileName, "rb");
        if (fp == nullptr) {
            HIAI_ENGINE_LOG(HIAI_OPEN_FILE_ERROR, "open file: %s failed.", g_inFileName);
            DestroyVdecApi(pidvppapi, 0);
            return;
        }
        fseek(fp, 0L, SEEK_END);
        int file_size = ftell(fp);
        fseek(fp, 0L, SEEK_SET);
        int rest_len = file_size;
        int len = file_size;

        vdec_in_msg vdec_msg;
        vdec_msg.call_back = FrameReturn;
        vdec_msg.err_report = ErrReport; // can be unassigned, if user does not need error info
        vdec_msg.hiai_data = nullptr;
        int32_t safeFuncRet = 0;
        if (g_format == 0) {
            safeFuncRet = memcpy_s(vdec_msg.video_format, sizeof(vdec_msg.video_format), "h264", 4);
            if (safeFuncRet != EOK) {
                HIAI_ENGINE_LOG(HIAI_ERROR, "memcpy_s fail");
                DestroyVdecApi(pidvppapi, 0);
                fclose(fp);
                fp = nullptr;
                return;
            }
        } else {
            safeFuncRet = memcpy_s(vdec_msg.video_format, sizeof(vdec_msg.video_format), "h265", 4);
            if (safeFuncRet != EOK) {
                HIAI_ENGINE_LOG(HIAI_ERROR, "memcpy_s fail");
                DestroyVdecApi(pidvppapi, 0);
                fclose(fp);
                fp = nullptr;
                return;
            }
        }
        vdec_msg.in_buffer = (char*)malloc(len);
        if (vdec_msg.in_buffer == nullptr) {
            HIAI_ENGINE_LOG(HIAI_ERROR, "malloc fail");
            fclose(fp);
            fp = nullptr;
            DestroyVdecApi(pidvppapi, 0);
            return;
        }

        dvppapi_ctl_msg dvppApiCtlMsg;
        dvppApiCtlMsg.in = (void*)(&vdec_msg);
        dvppApiCtlMsg.in_size = sizeof(vdec_in_msg);

        while (rest_len > 0) {
            int read_len = fread(vdec_msg.in_buffer, 1, len, fp);
            HIAI_ENGINE_LOG("rest_len is %d,read len is %d.", rest_len, read_len);
            vdec_msg.in_buffer_size = read_len;
            fclose(fp);
            fp = nullptr;
            if (VdecCtl(pidvppapi, DVPP_CTL_VDEC_PROC, &dvppApiCtlMsg, 0) != 0) {
                HIAI_ENGINE_LOG(HIAI_VDEC_CTL_ERROR, "call dvppctl process faild!");
                free(vdec_msg.in_buffer);
                vdec_msg.in_buffer = nullptr;
                DestroyVdecApi(pidvppapi, 0);
                return;
            }
            HIAI_ENGINE_LOG(HIAI_OK, "call vdec process success!");
            rest_len = rest_len - len;
        }
        free(vdec_msg.in_buffer);
        vdec_msg.in_buffer = nullptr;
    } else {
        HIAI_ENGINE_LOG(HIAI_VDEC_CTL_ERROR, "pidvppapi is null!");
    }
    DestroyVdecApi(pidvppapi, 0);
    return;
}