调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
// 1.获取软件栈的运行模式,不同运行模式影响后续的接口调用流程(例如是否进行数据传输等) aclrtRunMode runMode; aclError aclRet = aclrtGetRunMode(&runMode); // 2.AscendCL初始化 aclRet = aclInit(nullptr); // 3.运行管理资源申请(依次申请Device、Context) aclrtContext g_context; aclRet = aclrtSetDevice(0); aclRet = aclrtCreateContext(&g_context, 0); // 4.初始化媒体数据处理系统 int32_t ret = hi_mpi_sys_init(); // 5.创建通道 hi_vdec_chn chnId; hi_vdec_chn_attr chnAttr; hi_pic_buf_attr buf_attr{1920, 1080, 0, 8, HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420, HI_COMPRESS_MODE_NONE}; chnAttr.type = HI_PT_H264; chnAttr.mode = HI_VDEC_SEND_MODE_FRAME; chnAttr.pic_width = 1920; chnAttr.pic_height = 1080; chnAttr.stream_buf_size = 1920 * 1080 * 3 / 2; chnAttr.frame_buf_cnt = 16; chnAttr.frame_buf_size = hi_vdec_get_pic_buf_size(HI_PT_H264, &buf_attr); chnAttr.video_attr.ref_frame_num = 12; chnAttr.video_attr.temporal_mvp_en = HI_TRUE; chnAttr.video_attr.tmv_buf_size = hi_vdec_get_tmv_buf_size(HI_PT_H264, 1920, 1080); ret = hi_mpi_vdec_create_chn(chnId, &chnAttr); // 6.设置通道属性 hi_vdec_chn_param chnParam; ret = hi_mpi_vdec_get_chn_param(chnId, &chnParam); chnParam.video_param.dec_mode = HI_VIDEO_DEC_MODE_IPB; chnParam.video_param.compress_mode = HI_COMPRESS_MODE_HFBC; chnParam.video_param.video_format = HI_VIDEO_FORMAT_TILE_64x16; chnParam.display_frame_num = 3; chnParam.video_param.out_order = HI_VIDEO_OUT_ORDER_DISPLAY; ret = hi_mpi_vdec_set_chn_param(chnId, &chnParam); // 7.解码器启动接收码流 ret = hi_mpi_vdec_start_recv_stream(chnId); // 8.发送码流 // 8.1 申请输入内存 uint8_t* inputAddr = nullptr; // inputsize表示每一帧码流占用的内存大小,此处以1024 Byte为例,用户需根据实际情况计算内存大小 int32_t inputSize = 1024; ret = hi_mpi_dvpp_malloc(0, &inputAddr, inputSize); // 如果运行模式为ACL_HOST,则需要申请Host内存,将输入码流数据读入Host内存,再通过aclrtMemcpy接口将Host的码流数据传输到Device,数据传输完成后,需及时释放Host内存;否则直接将输入图片数据读入Device内存 if (runMode == ACL_HOST) { void* hostInputAddr = nullptr; // 申请Host内存 aclRet = aclrtMallocHost(&hostInputAddr, inputSize); // 将输入码流读入内存中,该自定义函数ReadStreamFile由用户实现 ReadStreamFile(streamName, hostInputAddr, inputSize); // 数据传输 aclRet = aclrtMemcpy(inputAddr, inputSize, hostInputAddr, inputSize, ACL_MEMCPY_HOST_TO_DEVICE); // 完成数据传输后,需及时释放不使用的内存 aclrtFreeHost(hostInputAddr ); hostInputAddr = nullptr; } else { // 将输入码流读入内存中,该自定义函数ReadStreamFile由用户实现 ReadStreamFile(streamName, inputAddr, inputSize); } // 8.2 申请输出内存 uint8_t* outputAddr = nullptr; // 输出图片数据占用的内存大小与输出图片格式有关 int32_t outputSize = 1920 * 1080 * 3 / 2; ret = hi_mpi_dvpp_malloc(0, &outputAddr, outputSize); // 8.3 构造存放一帧输入码流信息的结构体 hi_vdec_stream stream; stream.addr = inputAddr; stream.len = inputSize; stream.end_of_frame = HI_TRUE; stream.end_of_stream = HI_FALSE; stream.need_display = HI_TRUE; // 8.4 构造存放一帧输出结果信息的结构体 hi_vdec_pic_info outPicInfo; outPicInfo.width = 1920; outPicInfo.height = 1080; outPicInfo.width_stride = 1920; outPicInfo.height_stride = 1080; outPicInfo.pixel_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420; outPicInfo.vir_addr = outputAddr; outPicInfo.buffer_size = outputSize; // 8.5 发送一帧码流 ret = hi_mpi_vdec_send_stream(chnId, &stream, &outPicInfo, 0); // 9.接收解码结果 // 9.1 获取解码结果 hi_video_frame_info frame; hi_vdec_stream stream; hi_vdec_supplement_info stSupplement; ret = hi_mpi_vdec_get_frame(chnId, &frame, &stSupplement, &stream, 0); if (ret == HI_SUCCESS) { decResult = frame.v_frame.frame_flag; if (decResult == 0) { // 0: Decode success printf("[%s][%d] Chn %u GetFrame Success, Decode Success \n",__FUNCTION__, __LINE__, chnId); } elseif (decResult == 1) { // 1: Decode fail printf("[%s][%d] Chn %u GetFrame Success, Decode Fail \n",__FUNCTION__, __LINE__, chnId); } elseif (decResult == 2) { // 2:This result is returned for the second field of printf("[%s][%d] Chn %u GetFrame Success, No Picture \n", __FUNCTION__, __LINE__, chnId); } elseif (decResult == 3) { // 3: Reference frame number set error printf("[%s][%d] Chn %u GetFrame Success, RefFrame Num Error \n",__FUNCTION__, __LINE__, chnId); } elseif (decResult == 4) { // 4: Reference frame size set error printf("[%s][%d] Chn %u GetFrame Success, RefFrame Size Error \n",__FUNCTION__, __LINE__, chnId); } } // 9.2 如果运行模式为ACL_HOST,且Host上需要展示VDEC输出的图片数据,则需要申请Host内存,通过aclrtMemcpy接口将Device的输出图片数据传输到Host if (g_runMode == ACL_HOST) { void* hostOutputAddr = nullptr; aclRet = aclrtMallocHost(&hostOutputAddr, outputSize); aclRet = aclrtMemcpy(hostOutputAddr, outputSize, frame.v_frame.virt_addr[0], outputSize, ACL_MEMCPY_DEVICE_TO_HOST); // ...... // 数据使用完成后,需及时释放不需要使用的内存 aclrtFreeHost(hostOutputAddr); hostOutputAddr = nullptr; } else { // 可以直接使用VDEC的输出图片数据,在frame.v_frame.virt_addr[0]指向的内存中 // TODO:推理相关的代码逻辑 } // 9.3 释放输入、输出内存 ret = hi_mpi_dvpp_free(frame.v_frame.virt_addr[0]); ret = hi_mpi_dvpp_free(stream.addr); // 9.4 释放资源 ret = hi_mpi_vdec_release_frame(chnId, &frame); // 10.解码器停止接收码流 ret = hi_mpi_vdec_stop_recv_stream(chnId); // 11.销毁通道 ret = hi_mpi_vdec_destroy_chn(chnId); // 12. 媒体数据处理系统去初始化 ret = hi_mpi_sys_exit(); // 13. 释放运行管理资源(依次释放Context、Device) aclRet = aclrtDestroyContext(g_context); aclRet = aclrtResetDevice(0); // 14.AscendCL去初始化 aclRet = aclFinalize(); // ....