PNGD图片解码
JPEGE图片编码
JPEGE(JPEG Encoder)负责完成图像编码功能,将YUV格式图片编码成.jpg图片。关于JPEGE功能的详细介绍及约束请参见JPEGE功能及约束说明。
本节介绍JPEGE图片编码的接口调用流程,同时配合示例代码辅助理解该接口调用流程。
接口调用流程
开发应用时,如果涉及将YUV格式图片编码成JPEG压缩格式的图片文件,则应用程序中必须包含编码的代码逻辑,关于编码的接口调用流程,请先参见AscendCL接口调用流程了解整体流程,再查看本节中的流程说明。
图1 接口调用流程

当前系统支持将YUV格式图片编码成JPEG压缩格式的图片文件,关键接口的说明如下:
- 资源初始化:
-
调用hi_mpi_sys_init接口进行媒体数据处理系统初始化;
-
调用hi_mpi_venc_create_chn函数创建完通道;
成功创建通道之后,您可以根据实际需求设置编码的高级参数,例如场景模式、码流控制器的高级参数等,请参见hi_mpi_venc_set_jpeg_param~hi_mpi_venc_compact_jpeg_tables章节中的接口说明。
-
调用hi_mpi_venc_get_fd将通道ID转换为一个文件句柄;
-
调用hi_mpi_sys_create_epoll函数创建DVPP epoll实例;
-
调用hi_mpi_sys_ctl_epoll函数将编码通道的文件句柄添加到epoll实例中,由epoll实例处理;
select或者poll方式,不需要执行该步骤。
-
- 图片编码:
-
调用hi_mpi_venc_start_chn函数通知通道准备开始编码;
-
调用hi_mpi_dvpp_malloc接口申请存放Device上输入数据的内存。
Atlas 200/500 A2推理产品上,还支持使用aclrtMalloc接口申请内存。
调用hi_mpi_dvpp_malloc接口申请的内存为媒体数据处理的专用内存,但专用内存的地址空间有限,若关注内存规划或内存资源有限时,建议调用aclrtMalloc接口申请内存。
-
启动一个用户态线程,调用hi_mpi_sys_wait_epoll函数等待编码完成;
-
之后用户就可以调用hi_mpi_venc_send_frame函数发送待编码的码流;
-
一旦编码完成,hi_mpi_sys_wait_epoll函数或select函数或poll函数就会返回,用户就可以调用hi_mpi_venc_query_status接口查询编码状态,再调用hi_mpi_venc_get_stream函数获取编码结果;
-
用户需要注意的是,编码结果数据使用完成之后,需要及时调用hi_mpi_venc_release_stream函数释放buffer。否则会因编码buffer用完导致后续编码无法进行;
-
调用hi_mpi_dvpp_free接口释放输入内存;
Atlas 200/500 A2推理产品上,若使用aclrtMalloc接口申请内存,则需使用aclrtFree接口释放内存。
-
当用户不需发送图像到目的通道继续编码时,需要调用hi_mpi_venc_stop_chn函数通知该通道不再接收新的输入图片;
-
- 资源释放:
- 调用hi_mpi_sys_ctl_epoll函数从epoll实例中删除编码通道的文件句柄;
- 当用户完成所有编码之后,需要调用hi_mpi_venc_destroy_chn释放编码通道以及内部内存资源;
- 调用hi_mpi_sys_close_epoll函数销毁DVPP epoll实例;
- 调用hi_mpi_sys_exit接口进行媒体数据处理系统去初始化。
:::note 说明 支持DVPP内部管理输出内存,或用户自行管理输出内存两种方式:
-
不需要用户管理、由DVPP内部管理输出内存时,调用hi_mpi_venc_send_frame接口发送原始图像进行图像编码。
在调用hi_mpi_venc_create_chn接口创建通道时,必须正确设置hi_venc_chn_attr.venc_attr.buf_size参数值(参数描述请参见hi_venc_attr)。
该方式下,相比由用户管理内存,输出结果数据的JPEG头中不存在COM注释字段,数据长度会短一点,但需要用户从DVPP返回的内存中拷贝输出结果数据到指定内存。
-
由用户自行管理输出内存、管理内存的生命周期,调用hi_mpi_venc_send_jpege_frame接口发送原始图像进行图像编码。
在调用hi_mpi_venc_create_chn接口创建通道时,需将hi_venc_chn_attr.venc_attr.buf_size参数值设置为0(参数描述请参见hi_venc_attr),然后调用hi_mpi_venc_get_jpege_predicted_size接口预估输出内存大小,调用hi_mpi_dvpp_malloc/hi_mpi_dvpp_free接口申请/释放输出内存。
**该方式下,**直接在调用hi_mpi_venc_send_jpege_frame接口时,设置输出内存地址,输出结果数据直接存放到用户设置的内存中,相比由系统管理内存的方式,用户可减少一次“从DVPP返回的内存中拷贝输出结果数据到指定内存”的操作,但输出结果数据的JPEG头中可能会存在COM注释字段(字段长度范围4~19Byte),数据长度会长一点。 :::
示例代码
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br> 10<br> 11<br> 12<br> 13<br> 14<br> 15<br> 16<br> 17<br> 18<br> 19<br> 20<br> 21<br> 22<br> 23<br> 24<br> 25<br> 26<br> 27<br> 28<br> 29<br> 30<br> 31<br> 32<br> 33<br> 34<br> 35<br> 36<br> 37<br> 38<br> 39<br> 40<br> 41<br> 42<br> 43<br> 44<br> 45<br> 46<br> 47<br> 48<br> 49<br> 50<br> 51<br> 52<br> 53<br> 54<br> 55<br> 56<br> 57<br> 58<br> 59<br> 60<br> 61<br> 62<br> 63<br> 64<br> 65<br> 66<br> 67<br> 68<br> 69<br> 70<br> 71<br> 72<br> 73<br> 74<br> 75<br> 76<br> 77<br> 78<br> 79<br> 80<br> 81<br> 82<br> 83<br> 84<br> 85<br> 86<br> 87<br> 88<br> 89<br> 90<br> 91<br> 92<br> 93<br> 94<br> 95<br> 96<br> 97<br> 98<br> 99<br>100<br>101<br>102<br>103<br>104<br>105<br>106<br>107<br>108<br>109<br>110<br>111<br>112<br>113<br>114<br>115<br>116<br>117<br>118<br>119<br>120<br>121<br>122<br>123<br>124<br>125<br>126<br>127<br>128<br>129<br>130<br>131<br>132<br>133<br>134<br>135<br>136<br>137<br>138<br> | <br>// 1.AscendCL初始化<br>aclRet = aclInit(nullptr);<br>// 2.运行管理资源申请(依次申请Device、Context)<br>aclrtContext g_context;<br>aclRet = aclrtSetDevice(0);<br>aclRet = aclrtCreateContext(&g_context, 0);<br>// 获取软件栈的运行模式,不同运行模式影响后续的接口调用流程(例如是否进行数据传输等)<br>aclrtRunMode runMode;<br>aclError aclRet = aclrtGetRunMode(&runMode);<br>// 3.初始化媒体数据处理系统<br>int32_t ret = hi_mpi_sys_init();<br>// 4.创建通道<br>hi_venc_chn chn = 0;<br>hi_venc_chn_attr attr{};<br>attr.venc_attr.type = HI_PT_JPEG;<br>attr.venc_attr.profile = 0;<br>attr.venc_attr.max_pic_width = 128;<br>attr.venc_attr.max_pic_height = 128;<br>attr.venc_attr.pic_width = 128;<br>attr.venc_attr.pic_height = 128;<br>attr.venc_attr.buf_size = 2 * 1024 * 1024;<br>attr.venc_attr.is_by_frame = HI_TRUE;<br>attr.venc_attr.jpeg_attr.dcf_en = HI_FALSE;<br>attr.venc_attr.jpeg_attr.recv_mode = HI_VENC_PIC_RECV_SINGLE;<br>attr.venc_attr.jpeg_attr.mpf_cfg.large_thumbnail_num = 0;<br>ret = hi_mpi_venc_create_chn(chn, &attr);<br>// 5.通知编码器开始接收输入数据<br>hi_venc_start_param recv_param{};<br>recv_param.recv_pic_num = -1;<br>ret = hi_mpi_venc_start_chn(chn, &recv_param);<br>// 6.发送输入数据<br>// 6.1 申请输入内存<br>uint8_t* inputAddr = nullptr;<br>int32_t inputSize = 128 * 128 * 3 / 2;<br>ret = hi_mpi_dvpp_malloc(0, &inputAddr, inputSize);<br>if (runMode == ACL_HOST) {<br> void* hostInputAddr = nullptr;<br> <br> aclRet = aclrtMallocHost(&hostInputAddr, inputSize);<br> // 将输入数据读入内存中,该自定义函数JpegeReadYuvFile由用户实现<br> JpegeReadYuvFile(streamName, hostInputAddr, inputSize);<br> // 数据传输<br> aclRet = aclrtMemcpy(inputAddr, inputSize, hostInputAddr, inputSize, ACL_MEMCPY_HOST_TO_DEVICE);<br> // 完成数据传输后,需及时释放不使用的内存<br> aclrtFreeHost(hostInputAddr );<br> hostInputAddr = nullptr;<br>} else {<br> // 将输入数据读入内存中,该自定义函数JpegeReadYuvFile由用户实现<br> JpegeReadYuvFile(streamName, inputAddr, inputSize);<br>}<br>// 6.2 发送输入数据,开始编码<br>hi_video_frame_info frame{};<br>frame.mod_id = HI_ID_VENC;<br>frame.v_frame.width = 128;<br>frame.v_frame.height = 128;<br>frame.v_frame.field = HI_VIDEO_FIELD_FRAME;<br>frame.v_frame.pixel_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420;<br>frame.v_frame.video_format = HI_VIDEO_FORMAT_LINEAR;<br>frame.v_frame.compress_mode = HI_COMPRESS_MODE_NONE;<br>frame.v_frame.dynamic_range = HI_DYNAMIC_RANGE_SDR8;<br>frame.v_frame.color_gamut = HI_COLOR_GAMUT_BT709;<br>frame.v_frame.width_stride[0] = 128;<br>frame.v_frame.width_stride[1] = 128;<br>frame.v_frame.width_stride[2] = 128;<br>frame.v_frame.virt_addr[0] = inputAddr;<br>frame.v_frame.virt_addr[1] = (hi_void *)((uintptr_t)frame.v_frame.virt_addr[0] + 128 * 128);<br>frame.v_frame.frame_flag = 0;<br>frame.v_frame.time_ref = 0;<br>frame.v_frame.pts = 0;<br>ret = hi_mpi_venc_send_frame(chn, &frame, 0);<br>// 7.获取编码结果<br>// 7.1 创建EPOLL实例<br>int32_t epollFd = 0;<br>int32_t fd = hi_mpi_venc_get_fd(chn);<br>ret = hi_mpi_sys_create_epoll(10, &epollFd);<br>hi_dvpp_epoll_event event;<br>event.events = HI_DVPP_EPOLL_IN;<br>event.data = (void*)(unsigned long)(fd);<br>ret = hi_mpi_sys_ctl_epoll(epollFd, HI_DVPP_EPOLL_CTL_ADD, fd, &event);<br>int32_t eventCount = 0;<br>// 编码完成前,会超时阻塞在这里,一旦完成,才会往下执行<br>ret = hi_mpi_sys_wait_epoll(epollFd, event, 3, 1000, &eventCount);<br>// 7.2 获取编码结果<br>hi_venc_chn_status stat;<br>ret = hi_mpi_venc_query_status(chn, &stat);<br>hi_venc_stream stream;<br>stream.pack_cnt = stat.cur_packs;<br>stream.pack = new hi_venc_pack[stream.pack_cnt];<br>ret = hi_mpi_venc_get_stream(chn, &stream, 1000);<br>if (g_runMode == ACL_HOST) {<br> void* hostOutputAddr = nullptr;<br> aclRet = aclrtMallocHost(&hostOutputAddr, outputSize);<br> aclRet = aclrtMemcpy(hostOutputAddr, outputSize, stream.pack[0].addr, outputSize, ACL_MEMCPY_DEVICE_TO_HOST);<br> // ......<br> // 数据使用完成后,需及时释放不使用的内存<br> aclrtFreeHost(hostOutputAddr);<br> hostOutputAddr = nullptr;<br>} else {<br> // 可以直接使用编码输出码流数据,在stream.pack[0].addr指向的内存中<br> // ......<br>}<br>// 8.释放输入内存和输出码流<br>ret = hi_mpi_dvpp_free(inputAddr);<br>ret = hi_mpi_venc_release_stream(chn, &stream);<br>delete[] stream.pack;<br>// 9.通知编码器停止接收输入数据<br>ret = hi_mpi_venc_stop_chn(chn);<br>ret = hi_mpi_sys_ctl_epoll(epollFd, HI_DVPP_EPOLL_CTL_DEL, fd, NULL);<br>ret = hi_mpi_sys_close_epoll(epollFd);<br>// 10.销毁通道<br>ret = hi_mpi_venc_destroy_chn(chn);<br>// 11.媒体数据处理系统去初始化<br>ret = hi_mpi_sys_exit();<br>// 12.释放运行管理资源(依次释放Context、Device)<br>aclRet = aclrtDestroyContext(g_context);<br>aclRet = aclrtResetDevice(0);<br>// 13.AscendCL去初始化<br>aclRet = aclFinalize();<br>// ....<br> |
|---|
父主题: 媒体数据处理V2
PNGD图片解码
PNGD(PNG decoder)负责PNG格式图片的解码。关于PNGD功能的详细介绍及约束请参见PNGD图片解码功能。
本节介绍PNGD图片编码的接口调用流程,同时配合示例代码辅助理解该接口调用流程。
接口调用流程
开发应用时,如果涉及对PNG图片的解码,则应用程序中必须包含解码的代码逻辑,关于图片解码的接口调用流程,请先参见AscendCL接口调用流程了解整体流程,再查看本节中的流程说明。
图1 接口调用流程

当前系统支持解码PNG图片,关键接口的说明如下:
-
调用hi_mpi_sys_init接口进行媒体数据处理系统初始化。
-
调用hi_mpi_pngd_create_chn接口创建通道。
-
调用hi_mpi_dvpp_malloc接口申请Device上的内存,存放输入或输出数据。
Atlas 200/500 A2推理产品上,还支持使用aclrtMalloc接口申请内存。
调用hi_mpi_dvpp_malloc接口申请的内存为媒体数据处理的专用内存,但专用内存的地址空间有限,若关注内存规划或内存资源有限时,建议调用aclrtMalloc接口申请内存。
-
调用hi_mpi_pngd_send_stream接口发送解码码流,hi_mpi_pngd_send_stream接口是异步接口,调用该接口仅表示任务下发成功,还需要调hi_mpi_pngd_get_image_data接口获取解码结果数据。
-
调用hi_mpi_dvpp_free接口释放输入、输出内存。
Atlas 200/500 A2推理产品上,若使用aclrtMalloc接口申请内存,则需使用aclrtFree接口释放内存。
-
调用hi_mpi_pngd_destroy_chn接口销毁通道。
-
调用hi_mpi_sys_exit接口进行媒体数据处理系统去初始化。
示例代码
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br> 10<br> 11<br> 12<br> 13<br> 14<br> 15<br> 16<br> 17<br> 18<br> 19<br> 20<br> 21<br> 22<br> 23<br> 24<br> 25<br> 26<br> 27<br> 28<br> 29<br> 30<br> 31<br> 32<br> 33<br> 34<br> 35<br> 36<br> 37<br> 38<br> 39<br> 40<br> 41<br> 42<br> 43<br> 44<br> 45<br> 46<br> 47<br> 48<br> 49<br> 50<br> 51<br> 52<br> 53<br> 54<br> 55<br> 56<br> 57<br> 58<br> 59<br> 60<br> 61<br> 62<br> 63<br> 64<br> 65<br> 66<br> 67<br> 68<br> 69<br> 70<br> 71<br> 72<br> 73<br> 74<br> 75<br> 76<br> 77<br> 78<br> 79<br> 80<br> 81<br> 82<br> 83<br> 84<br> 85<br> 86<br> 87<br> 88<br> 89<br> 90<br> 91<br> 92<br> 93<br> 94<br> 95<br> 96<br> 97<br> 98<br> 99<br>100<br>101<br>102<br>103<br>104<br>105<br>106<br>107<br>108<br>109<br>110<br>111<br>112<br>113<br>114<br>115<br>116<br>117<br>118<br>119<br>120<br>121<br> | <br>// 1.获取软件栈的运行模式,不同运行模式影响后续的接口调用流程(例如是否进行数据传输等)<br>aclrtRunMode runMode;<br>aclError aclRet = aclrtGetRunMode(&runMode);<br>// 2.AscendCL初始化<br>aclRet = aclInit(nullptr);<br>// 3.运行管理资源申请(依次申请Device、Context)<br>aclrtContext g_context;<br>aclRet = aclrtSetDevice(0);<br>aclRet = aclrtCreateContext(&g_context, 0);<br>// 4.初始化媒体数据处理系统<br>int32_t ret = hi_mpi_sys_init();<br>// 5.创建通道<br>hi_pngd_chn chnId;<br>hi_pngd_chn_attr chnAttr; // hi_pngd_chn_attr都是保留参数,无需设置<br>ret = hi_mpi_pngd_create_chn(chnId, &chnAttr);<br>// 6.发送码流<br>// 6.1 申请输入内存<br>uint8_t* inputAddr = nullptr;<br>// inputsize表示输入图片占用的内存大小,此处以1024 byte为例,用户需根据实际情况计算内存大小<br>int32_t inputSize = 1024;<br>ret = hi_mpi_dvpp_malloc(0, &inputAddr, inputSize);<br>if (runMode == ACL_HOST) {<br> void* hostInputAddr = nullptr;<br> <br> aclRet = aclrtMallocHost(&hostInputAddr, inputSize);<br> // 将输入图片读入内存中,该自定义函数ReadStreamFile由用户实现<br> ReadStreamFile(fileName, hostInputAddr, inputSize);<br> // 数据传输<br> aclRet = aclrtMemcpy(inputAddr, inputSize, hostInputAddr, inputSize, ACL_MEMCPY_HOST_TO_DEVICE); <br>} else {<br> // 将输入图片读入内存中,该自定义函数ReadStreamFile由用户实现<br> ReadStreamFile(fileName, inputAddr, inputSize);<br>}<br>// 6.2 构造存放输入图片信息的结构体<br>hi_img_stream stStream{};<br>hi_img_info stImgInfo{};<br>stStream.pts = 0;<br>if (g_runMode == ACL_HOST) {<br> stStream.addr = (uint8_t *)hostInputAddr;<br>} else {<br> stStream.addr = (uint8_t *)inputAddr;<br>}<br>stStream.len = inputSize;<br>stStream.type = HI_PT_PNG;<br>ret = hi_mpi_png_get_image_info(&stStream, &stImgInfo);<br>if (g_runMode == ACL_HOST) {<br> // 如果不使用Host上的数据,需及时释放<br> aclrtFreeHost(hostInputAddr);<br> hostInputAddr = nullptr;<br>}<br>stStream.addr = (uint8_t *)inputAddr;<br>// 6.3 构造存放输出图片信息的结构体,并申请输出内存<br>hi_pic_info outPicInfo{};<br>void *outBuffer = nullptr;<br>outPicInfo.picture_width = stImgInfo.width;<br>outPicInfo.picture_height = stImgInfo.height;<br>outPicInfo.picture_width_stride = stImgInfo.width_stride;<br>outPicInfo.picture_height_stride = stImgInfo.height_stride;<br>outPicInfo.picture_buffer_size = stImgInfo.img_buf_size;<br>outPicInfo.picture_format = HI_PIXEL_FORMAT_UNKNOWN;<br>ret = hi_mpi_dvpp_malloc(0, &outBuffer, outPicInfo.buffer_size);<br>outPicInfo.picture_address = (uint64_t)outBuffer;<br>// 6.4 发送需解码的输入图片<br>ret = hi_mpi_pngd_send_stream(chnId, &stream, &outPicInfo, 0);<br>// 7.接收解码结果<br>// 7.1 获取解码结果<br>hi_pic_info picInfo;<br>hi_img_stream stream;<br>ret = hi_mpi_pngd_get_image_data(chnId, &picInfo, &stream, 0);<br>if (ret == HI_SUCCESS) { // Decode success<br> printf("[%s][%d] Chn %u GetFrame Success, Decode Success \n",__FUNCTION__, __LINE__, chnId);<br>} else if (ret == HI_ERR_PNGD_BUF_EMPTY){ // Decoding<br> printf("[%s][%d] Chn %u Decoding, try again \n",__FUNCTION__, __LINE__, chnId);<br>} else { // Decode fail<br> printf("[%s][%d] Chn %u GetFrame Success, Decode Fail \n",__FUNCTION__, __LINE__, chnId);<br>}<br>if (g_runMode == ACL_HOST) {<br> void* hostOutputAddr = nullptr;<br> aclRet = aclrtMallocHost(&hostOutputAddr, outputSize);<br> aclRet = aclrtMemcpy(hostOutputAddr, outputSize, frame.v_frame.virt_addr[0], outputSize, ACL_MEMCPY_DEVICE_TO_HOST);<br> // ......<br> // 数据使用完成后,及时释放不使用的内存<br> aclrtFreeHost(hostOutputAddr);<br> hostOutputAddr = nullptr;<br>} else {<br> // 可以直接使用PNGD的输出图片数据,在outputPic.picture_address指向的内存中<br> // ......<br>}<br>// 7.3 释放输入、输出内存<br>ret = hi_mpi_dvpp_free(frame.v_frame.virt_addr[0]);<br>ret = hi_mpi_dvpp_free(stream.addr);<br>// 8.销毁通道<br>ret = hi_mpi_pngd_destroy_chn(chnId);<br>// 9. 媒体数据处理系统去初始化<br>ret = hi_mpi_sys_exit();<br>// 10. 释放运行管理资源(依次释放Context、Device)<br>aclRet = aclrtDestroyContext(g_context);<br>aclRet = aclrtResetDevice(0);<br>// 11.AscendCL去初始化<br>aclRet = aclFinalize();<br>// ....<br> |
|---|
父主题: 媒体数据处理V2
VDEC视频解码
VDEC(Video Decoder)负责将H264/H265格式的视频码流解码为YUV/RGB格式的图片。关于VDEC功能的详细介绍及约束请参见VDEC功能及约束说明。
本节介绍VDEC视频编码的接口调用流程,同时配合示例代码辅助理解该接口调用流程。
接口调用流程
开发应用时,如果涉及视频解码,则应用程序中必须包含解码的代码逻辑,关于视频解码的接口调用流程,请先参见AscendCL接口调用流程了解整体流程,再查看本节中的流程说明。
图1 接口调用的流程

当前系统支持解码H264/H265的视频码流,关键接口的说明如下:
-
调用hi_mpi_sys_init接口进行媒体数据处理系统初始化。
-
调用hi_vdec_get_pic_buf_size接口获取解码所需的帧存大小、调用hi_vdec_get_tmv_buf_size接口获取矢量预测缓冲区的大小,在创建通道时需要使用这些数据。
-
调用hi_mpi_vdec_create_chn接口创建通道。
-
调用hi_mpi_dvpp_malloc接口申请Device上的内存,存放输入或输出数据。
Atlas 200/500 A2推理产品上,还支持使用aclrtMalloc接口申请内存。
调用hi_mpi_dvpp_malloc接口申请的内存为媒体数据处理的专用内存,但专用内存的地址空间有限,若关注内存规划或内存资源有限时,建议调用aclrtMalloc接口申请内存。
-
解码前,需调用hi_mpi_vdec_start_recv_stream接口通知解码器启动接收码流,再调用hi_mpi_vdec_send_stream接口发送解码码流,hi_mpi_vdec_send_stream接口是异步接口,调用该接口仅表示任务下发成功,还需要调hi_mpi_vdec_get_frame接口获取解码结果数据,成功获取解码数据后,可以调用hi_mpi_vdec_release_frame接口释放帧相关的资源。解码结束后,需调用hi_mpi_vdec_stop_recv_stream接口通知解码器停止接收码流。
-
调用hi_mpi_dvpp_free接口释放输入、输出内存。
Atlas 200/500 A2推理产品上,若使用aclrtMalloc接口申请内存,则需使用aclrtFree接口释放内存。
-
调用hi_mpi_vdec_destroy_chn接口销毁通道。
-
调用hi_mpi_sys_exit接口进行媒体数据处理系统去初始化。
示例代码
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br> 10<br> 11<br> 12<br> 13<br> 14<br> 15<br> 16<br> 17<br> 18<br> 19<br> 20<br> 21<br> 22<br> 23<br> 24<br> 25<br> 26<br> 27<br> 28<br> 29<br> 30<br> 31<br> 32<br> 33<br> 34<br> 35<br> 36<br> 37<br> 38<br> 39<br> 40<br> 41<br> 42<br> 43<br> 44<br> 45<br> 46<br> 47<br> 48<br> 49<br> 50<br> 51<br> 52<br> 53<br> 54<br> 55<br> 56<br> 57<br> 58<br> 59<br> 60<br> 61<br> 62<br> 63<br> 64<br> 65<br> 66<br> 67<br> 68<br> 69<br> 70<br> 71<br> 72<br> 73<br> 74<br> 75<br> 76<br> 77<br> 78<br> 79<br> 80<br> 81<br> 82<br> 83<br> 84<br> 85<br> 86<br> 87<br> 88<br> 89<br> 90<br> 91<br> 92<br> 93<br> 94<br> 95<br> 96<br> 97<br> 98<br> 99<br>100<br>101<br>102<br>103<br>104<br>105<br>106<br>107<br>108<br>109<br>110<br>111<br>112<br>113<br>114<br>115<br>116<br>117<br>118<br>119<br>120<br>121<br>122<br>123<br>124<br>125<br>126<br>127<br>128<br>129<br>130<br>131<br>132<br>133<br>134<br>135<br>136<br>137<br>138<br>139<br>140<br>141<br>142<br>143<br>144<br>145<br>146<br>147<br>148<br>149<br>150<br>151<br>152<br>153<br>154<br>155<br>156<br>157<br>158<br> | <br>// 1.获取软件栈的运行模式,不同运行模式影响后续的接口调用流程(例如是否进行数据传输等)<br>aclrtRunMode runMode;<br>aclError aclRet = aclrtGetRunMode(&runMode);<br>// 2.AscendCL初始化<br>aclRet = aclInit(nullptr);<br>// 3.运行管理资源申请(依次申请Device、Context)<br>aclrtContext g_context;<br>aclRet = aclrtSetDevice(0);<br>aclRet = aclrtCreateContext(&g_context, 0);<br>// 4.初始化媒体数据处理系统<br>int32_t ret = hi_mpi_sys_init();<br>// 5.创建通道<br>hi_vdec_chn chnId;<br>hi_vdec_chn_attr chnAttr;<br>hi_pic_buf_attr buf_attr{1920, 1080, 0, 8, HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420, HI_COMPRESS_MODE_NONE};<br>chnAttr.type = HI_PT_H264;<br>chnAttr.mode = HI_VDEC_SEND_MODE_FRAME;<br>chnAttr.pic_width = 1920;<br>chnAttr.pic_height = 1080;<br>chnAttr.stream_buf_size = 1920 * 1080 * 3 / 2;<br>chnAttr.frame_buf_cnt = 16;<br>chnAttr.frame_buf_size = hi_vdec_get_pic_buf_size(HI_PT_H264, &buf_attr);<br>chnAttr.video_attr.ref_frame_num = 12;<br>chnAttr.video_attr.temporal_mvp_en = HI_TRUE;<br>chnAttr.video_attr.tmv_buf_size = hi_vdec_get_tmv_buf_size(HI_PT_H264, 1920, 1080);<br>ret = hi_mpi_vdec_create_chn(chnId, &chnAttr);<br>// 6.设置通道属性<br>hi_vdec_chn_param chnParam;<br>ret = hi_mpi_vdec_get_chn_param(chnId, &chnParam);<br>chnParam.video_param.dec_mode = HI_VIDEO_DEC_MODE_IPB; <br>chnParam.video_param.compress_mode = HI_COMPRESS_MODE_HFBC; <br>chnParam.video_param.video_format = HI_VIDEO_FORMAT_TILE_64x16; <br>chnParam.display_frame_num = 3;<br>chnParam.video_param.out_order = HI_VIDEO_OUT_ORDER_DISPLAY;<br>ret = hi_mpi_vdec_set_chn_param(chnId, &chnParam);<br>// 7.解码器启动接收码流<br>ret = hi_mpi_vdec_start_recv_stream(chnId);<br>// 8.发送码流<br>// 8.1 申请输入内存<br>uint8_t* inputAddr = nullptr;<br>// inputsize表示每一帧码流占用的内存大小,此处以1024 Byte为例,用户需根据实际情况计算内存大小<br>int32_t inputSize = 1024;<br>ret = hi_mpi_dvpp_malloc(0, &inputAddr, inputSize);<br>// 如果运行模式为ACL_HOST,则需要申请Host内存,将输入码流数据读入Host内存,再通过aclrtMemcpy接口将Host的码流数据传输到Device,数据传输完成后,需及时释放Host内存;否则直接将输入图片数据读入Device内存<br>if (runMode == ACL_HOST) {<br> void* hostInputAddr = nullptr;<br> // 申请Host内存<br> aclRet = aclrtMallocHost(&hostInputAddr, inputSize);<br> // 将输入码流读入内存中,该自定义函数ReadStreamFile由用户实现<br> ReadStreamFile(streamName, hostInputAddr, inputSize);<br> // 数据传输<br> aclRet = aclrtMemcpy(inputAddr, inputSize, hostInputAddr, inputSize, ACL_MEMCPY_HOST_TO_DEVICE);<br> // 完成数据传输后,需及时释放不使用的内存<br> aclrtFreeHost(hostInputAddr );<br> hostInputAddr = nullptr;<br>} else {<br> // 将输入码流读入内存中,该自定义函数ReadStreamFile由用户实现<br> ReadStreamFile(streamName, inputAddr, inputSize);<br>}<br>// 8.2 申请输出内存<br>uint8_t* outputAddr = nullptr;<br>// 输出图片数据占用的内存大小与输出图片格式有关<br>int32_t outputSize = 1920 * 1080 * 3 / 2;<br>ret = hi_mpi_dvpp_malloc(0, &outputAddr, outputSize);<br>// 8.3 构造存放一帧输入码流信息的结构体<br>hi_vdec_stream stream;<br>stream.addr = inputAddr;<br>stream.len = inputSize;<br>stream.end_of_frame = HI_TRUE;<br>stream.end_of_stream = HI_FALSE;<br>stream.need_display = HI_TRUE;<br>// 8.4 构造存放一帧输出结果信息的结构体<br>hi_vdec_pic_info outPicInfo;<br>outPicInfo.width = 1920;<br>outPicInfo.height = 1080;<br>outPicInfo.width_stride = 1920;<br>outPicInfo.height_stride = 1080;<br>outPicInfo.pixel_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420;<br>outPicInfo.vir_addr = outputAddr;<br>outPicInfo.buffer_size = outputSize;<br>// 8.5 发送一帧码流<br>ret = hi_mpi_vdec_send_stream(chnId, &stream, &outPicInfo, 0);<br>// 9.接收解码结果<br>// 9.1 获取解码结果<br>hi_video_frame_info frame; <br>hi_vdec_stream stream;<br>hi_vdec_supplement_info stSupplement;<br>ret = hi_mpi_vdec_get_frame(chnId, &frame, &stSupplement, &stream, 0);<br>if (ret == HI_SUCCESS) {<br> decResult = frame.v_frame.frame_flag;<br> if (decResult == 0) { // 0: Decode success<br> printf("[%s][%d] Chn %u GetFrame Success, Decode Success \n",__FUNCTION__, __LINE__, chnId);<br> } elseif (decResult == 1) { // 1: Decode fail<br> printf("[%s][%d] Chn %u GetFrame Success, Decode Fail \n",__FUNCTION__, __LINE__, chnId);<br> } elseif (decResult == 2) { // 2:This result is returned for the second field of<br> printf("[%s][%d] Chn %u GetFrame Success, No Picture \n", __FUNCTION__, __LINE__, chnId);<br> } elseif (decResult == 3) { // 3: Reference frame number set error<br> printf("[%s][%d] Chn %u GetFrame Success, RefFrame Num Error \n",__FUNCTION__, __LINE__, chnId);<br> } elseif (decResult == 4) { // 4: Reference frame size set error<br> printf("[%s][%d] Chn %u GetFrame Success, RefFrame Size Error \n",__FUNCTION__, __LINE__, chnId);<br> }<br>}<br>// 9.2 如果运行模式为ACL_HOST,且Host上需要展示VDEC输出的图片数据,则需要申请Host内存,通过aclrtMemcpy接口将Device的输出图片数据传输到Host<br>if (g_runMode == ACL_HOST) {<br> void* hostOutputAddr = nullptr;<br> aclRet = aclrtMallocHost(&hostOutputAddr, outputSize);<br> aclRet = aclrtMemcpy(hostOutputAddr, outputSize, frame.v_frame.virt_addr[0], outputSize, ACL_MEMCPY_DEVICE_TO_HOST);<br> // ......<br> // 数据使用完成后,需及时释放不需要使用的内存<br> aclrtFreeHost(hostOutputAddr);<br> hostOutputAddr = nullptr;<br>} else {<br> // 可以直接使用VDEC的输出图片数据,在frame.v_frame.virt_addr[0]指向的内存中<br> // TODO:推理相关的代码逻辑<br>}<br>// 9.3 释放输入、输出内存<br>ret = hi_mpi_dvpp_free(frame.v_frame.virt_addr[0]);<br>ret = hi_mpi_dvpp_free(stream.addr);<br>// 9.4 释放资源<br>ret = hi_mpi_vdec_release_frame(chnId, &frame);<br>// 10.解码器停止接收码流<br>ret = hi_mpi_vdec_stop_recv_stream(chnId);<br>// 11.销毁通道<br>ret = hi_mpi_vdec_destroy_chn(chnId);<br>// 12. 媒体数据处理系统去初始化<br>ret = hi_mpi_sys_exit();<br>// 13. 释放运行管理资源(依次释放Context、Device)<br>aclRet = aclrtDestroyContext(g_context);<br>aclRet = aclrtResetDevice(0);<br>// 14.AscendCL去初始化<br>aclRet = aclFinalize();<br>// ....<br> |
|---|
父主题: 媒体数据处理V2
VENC视频编码
VENC(Video Encoder)将YUV420SP格式的图片编码成H264/H265格式的视频码流。关于VENC功能的详细介绍及约束请参见VENC功能及约束说明。
本节介绍VENC视频编码的接口调用流程,同时配合示例代码辅助理解该接口调用流程。在实现VENC视频编码功能时,可在创建通道时设置基本参数、或调用对应的set接口设置高级参数,优化视频编码质量,请参见优化视频编码质量。
接口调用流程
开发应用时,如果涉及视频编码,则应用程序中必须包含编码的代码逻辑,关于编码的接口调用流程,请先参见AscendCL接口调用流程了解整体流程,再查看本节中的流程说明。
图1 接口调用流程

当前系统支持H264/H265格式的视频码流,关键接口的说明如下:
- 资源初始化:
-
用hi_mpi_sys_init接口进行媒体数据处理系统初始化;
-
调用hi_mpi_venc_create_chn函数创建完通道;
成功创建通道之后,您可以根据实际需求设置编码的高级参数,例如场景模式、码流控制器的高级参数等,请参见hi_mpi_venc_set_jpeg_param~hi_mpi_venc_compact_jpeg_tables章节中的接口说明。
-
调用hi_mpi_venc_get_fd将通道ID转换为一个文件句柄;
-
调用hi_mpi_sys_create_epoll函数创建DVPP epoll实例;
-
调用hi_mpi_sys_ctl_epoll函数将编码通道的文件句柄添加到epoll实例中,由epoll实例处理;
select或者poll方式,不需要执行该步骤。
-
- 视频编码:
-
调用hi_mpi_venc_start_chn函数通知通道准备开始编码;
-
调用hi_mpi_dvpp_malloc接口申请存放Device上输入数据的内存。
Atlas 200/500 A2推理产品上,还支持使用aclrtMalloc接口申请内存。
调用hi_mpi_dvpp_malloc接口申请的内存为媒体数据处理的专用内存,但专用内存的地址空间有限,若关注内存规划或内存资源有限时,建议调用aclrtMalloc接口申请内存。
-
启动一个用户态线程,调用hi_mpi_sys_wait_epoll函数等待编码完成;
-
之后用户就可以调用hi_mpi_venc_send_frame函数发送待编码的码流;
-
一旦编码完成,hi_mpi_sys_wait_epoll函数或select函数或poll函数就会返回,用户就可以调用hi_mpi_venc_query_status接口查询编码状态,再调用hi_mpi_venc_get_stream函数获取编码结果;
-
用户需要注意的是,编码结果数据使用完成之后,需要及时调用hi_mpi_venc_release_stream函数释放buffer。否则会因编码buffer用完导致后续编码无法进行;
-
调用hi_mpi_dvpp_free接口释放输入内存;
Atlas 200/500 A2推理产品上,若使用aclrtMalloc接口申请内存,则需使用aclrtFree接口释放内存。
-
当用户不需发送图像到目的通道继续编码时,需要调用hi_mpi_venc_stop_chn函数通知该通道不再接收新的输入图片;
-
- 资源释放:
- 调用hi_mpi_sys_ctl_epoll函数从epoll实例中删除编码通道的文件句柄;
- 当用户完成所有编码之后,需要调用hi_mpi_venc_destroy_chn释放编码通道以及内部内存资源;
- 调用hi_mpi_sys_close_epoll函数销毁DVPP epoll实例;
- 调用hi_mpi_sys_exit接口进行媒体数据处理系统去初始化。
优化视频编码质量
在实现VENC视频编码功能时,可在创建通道时设置基本参数、或调用对应的set接口设置高级参数,优化视频编码质量,以下调整手段可以叠加使用,效果是叠加的,例如:
- H264视频数据获取场景,分辨率720P,gop = 60,帧率30fps,码率1M需要提升编码质量,可以使用如下优化手段组合:CBR模式、HI_VENC_SCENE_0、stats_time等于2、profile等于2、关闭宏块级码控。
- H265电影场景,分辨率1080P,gop=30,帧率25fps,码率2M需要提升编码质量,可以使用如下优化手段组合:CBR模式、HI_VENC_SCENE_1、stats_time等于1、关闭宏块级码控。
当前支持以下方式优化视频编码质量:
-
设置基本参数,优化视频编码质量
不同分辨率的视频,其编码质量与视频的帧率、GOP(Group of pictures)、码率有关,在调用hi_mpi_venc_create_chn接口创建通道时,可设置编码的等级、设置H.264/H.265协议编码场景下CBR/VBR/AVBR/CVBR/QVBR模式的帧率、GOP、码率等参数,来调整视频编码质量:
- 编码等级,通过hi_venc_chn_attr.venc_attr结构内的profile参数来设置;
- 帧率,通过hi_venc_chn_attr.rc_attr结构体内的src_frame_rate输入帧率参数、dst_frame_rate输出帧率参数来设置;
- GOP,通过hi_venc_chn_attr.rc_attr结构体内的gop参数来设置;
- 码率,通过hi_venc_chn_attr.rc_attr结构体内的bit_rate或max_bit_rate或target_bit_rate参数来设置。
表1 典型场景下帧率、GOP、码率的取值
画质/分辨率 帧率 GOP 码率(Mbps) 4K 3840*2160/4096*2160 25或30 建议GOP为帧率的整数倍,例如帧率为25时,GOP建议25或50。 - 视频数据获取场景 H264/H265码流,码率取值8 12。12。 H264码流,不涉及。
- 秀场/主播/短视频场景 H265码流,码率取值6
- 游戏视频场景 H264/H265码流,码率取值10~16。2K 2560*1440 25或30 建议GOP为帧率的整数倍,例如帧率为25时,GOP建议25或50。 - 视频数据获取场景 H264/H265码流,码率取值6 10。8。 H264码流,不涉及。
- 秀场/主播/短视频场景 H265码流,码率取值4.8
- 游戏视频场景 H264/H265码流,码率取值6~10。1080P(蓝光) 1920*1080 25或30 建议GOP为帧率的整数倍,例如帧率为25时,GOP建议25或50。 - 视频数据获取场景 H265码流,码率取值1 4。 H264码流,码率取值26。
- 秀场/主播/短视频场景 H265码流,码率取值1.43.6。 H264码流,码率取值24.8。
- 游戏视频场景 H264/H265码流,码率取值3~6。720P(高清) 1280*720 25或30 建议GOP为帧率的整数倍,例如帧率为25时,GOP建议25或50。 - 视频数据获取场景 H265码流,码率取值0.8 2。 H264码流,码率取值13。
- 秀场/主播/短视频场景 H265码流,码率取值12。 H264码流,码率取值13。
- 游戏视频场景 H264/H265码流,码率取值2~4。480P/D1_N(标清) 854*480/720*480 25或30 建议GOP为帧率的整数倍,例如帧率为25时,GOP建议25或50。 - 视频数据获取场景 H265码流,码率取值0.3 0.7。 H264码流,码率取值0.61.4。
- 秀场/主播/短视频场景 H265码流,码率取值0.250.6。 H264码流,码率取值0.30.7。
- 游戏视频场景 不涉及。576P/D1(标清) 720*576 25或30 建议GOP为帧率的整数倍,例如帧率为25时,GOP建议25或50。 - 视频数据获取场景 H265码流,码率取值0.3 0.7。 H264码流,码率取值0.61.4。
- 秀场/主播/短视频场景 H265码流,码率取值0.250.6。 H264码流,码率取值0.30.7。
- 游戏视频场景 不涉及。270P(流畅) 480*270 25或30 建议GOP为帧率的整数倍,例如帧率为25时,GOP建议25或50。 - 视频数据获取场景 不涉及。
- 秀场/主播/短视频场景 H265码流,码率取值0.2。 H264码流,码率取值0.3。
- 游戏视频场景 不涉及。CIF P/N 352*288/320*240 25或30 建议GOP为帧率的整数倍,例如帧率为25时,GOP建议25或50。 - 视频数据获取场景 H264/H265码流,码率取值0.25。
- 秀场/主播/短视频场景 不涉及。
- 游戏视频场景 不涉及。 -
设置高级参数,调整视频编码细节
您可以调用接口设置码控模式、宏块级码率控制参数、编码场景模式等,来调整视频编码的细节,进一步改善编码质量。
表2 高级配置项列表
配置项 接口 参数名 说明 码控模式 hi_mpi_venc_create_chn hi_venc_chn_attr.rc_attr结构体内的rc_mode参数 追求码率平稳或追求PSNR大且码率符合目标值,配置为CBR; 追求节省码率,对主观编码质量有一定要求,配置为VBR; 追求节省码率,对主观编码质量有一定要求,且场景中有较多静止画面,配置为AVBR; 追求PSNR且对码率上浮没有严格要求,配置为QVBR; 追求节省码率,对主观编码质量有一定要求,且可以根据带宽、存储空间要求进行更多调整,配置为CVBR; 码率控制模型统计时间 hi_mpi_venc_create_chn hi_venc_chn_attr.rc_attr内各模式属性值结构体内的stats_time参数 关注长期码率稳定,短期波动不在意的可以设置大一些,例:DVR存盘。设大可以提高重编码判决的门槛,重编码次数会减少,但是码率波动会加大。 宏块级码率控制参数 hi_mpi_venc_set_rc_param hi_venc_rc_param结构内的threshold_i、threshold_p、threshold_b、direction、row_qp_delta参数 如果图像内容复杂、细节较多或用户关注PSNR等客观指标时,需关闭宏块级码率控制。 第一帧的起始Qp值 hi_mpi_venc_create_chn hi_venc_rc_param结构内的first_frame_start_qp参数 典型场景下,用户配置的码率小于表1中给的参考值,且编码后的视频第一帧明显模糊,则建议配置first_frame_start_qp参数,参数值取[min_i_qp, max_i_qp]的中间值,例如,[min_i_qp, max_i_qp]为[30, 40],则first_frame_start_qp参数配置为35,同时将max_reencode_times参数配置为0,会获得较好的编码质量。 编码场景模式 hi_mpi_venc_set_scene_mode hi_venc_scene_mode 安防场景配置为HI_VENC_SCENE_0;自动驾驶、直播、游戏、动画、电影配置为HI_VENC_SCENE_1。
示例代码
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br> 10<br> 11<br> 12<br> 13<br> 14<br> 15<br> 16<br> 17<br> 18<br> 19<br> 20<br> 21<br> 22<br> 23<br> 24<br> 25<br> 26<br> 27<br> 28<br> 29<br> 30<br> 31<br> 32<br> 33<br> 34<br> 35<br> 36<br> 37<br> 38<br> 39<br> 40<br> 41<br> 42<br> 43<br> 44<br> 45<br> 46<br> 47<br> 48<br> 49<br> 50<br> 51<br> 52<br> 53<br> 54<br> 55<br> 56<br> 57<br> 58<br> 59<br> 60<br> 61<br> 62<br> 63<br> 64<br> 65<br> 66<br> 67<br> 68<br> 69<br> 70<br> 71<br> 72<br> 73<br> 74<br> 75<br> 76<br> 77<br> 78<br> 79<br> 80<br> 81<br> 82<br> 83<br> 84<br> 85<br> 86<br> 87<br> 88<br> 89<br> 90<br> 91<br> 92<br> 93<br> 94<br> 95<br> 96<br> 97<br> 98<br> 99<br>100<br>101<br>102<br>103<br>104<br>105<br>106<br>107<br>108<br>109<br>110<br>111<br>112<br>113<br>114<br>115<br>116<br>117<br>118<br>119<br>120<br>121<br>122<br>123<br>124<br>125<br>126<br>127<br>128<br>129<br>130<br>131<br>132<br>133<br>134<br>135<br>136<br>137<br>138<br>139<br>140<br>141<br>142<br>143<br>144<br>145<br>146<br> | <br>// 1.获取软件栈的运行模式,不同运行模式影响后续的接口调用流程(例如是否进行数据传输等)<br>aclrtRunMode runMode;<br>aclError aclRet = aclrtGetRunMode(&runMode);<br>// 2.AscendCL初始化<br>aclRet = aclInit(nullptr);<br>// 3.运行管理资源申请(依次申请Device、Context)<br>aclrtContext g_context;<br>aclRet = aclrtSetDevice(0);<br>aclRet = aclrtCreateContext(&g_context, 0);<br>// 4.初始化媒体数据处理系统<br>int32_t ret = hi_mpi_sys_init();<br>// 5.创建通道<br>hi_venc_chn chn = 0;<br>hi_venc_chn_attr attr{};<br>attr.venc_attr.type = HI_PT_H265;<br>attr.venc_attr.profile = 0;<br>attr.venc_attr.max_pic_width = 128;<br>attr.venc_attr.max_pic_height = 128;<br>attr.venc_attr.pic_width = 128;<br>attr.venc_attr.pic_height = 128;<br>attr.venc_attr.buf_size = 2 * 1024 * 1024;<br>attr.venc_attr.is_by_frame = HI_TRUE;<br>attr.rc_attr.rc_mode = HI_VENC_RC_MODE_H265_VBR;<br>attr.rc_attr.h265_vbr.gop = 30;<br>attr.rc_attr.h265_vbr.stats_time = 1;<br>attr.rc_attr.h265_vbr.src_frame_rate = 30;<br>attr.rc_attr.h265_vbr.dst_frame_rate = 30;<br>attr.rc_attr.h265_vbr.max_bit_rate = 4000;<br>attr.gop_attr.gop_mode = HI_VENC_GOP_MODE_NORMAL_P;<br>attr.gop_attr.normal_p.ip_qp_delta = 3;<br>ret = hi_mpi_venc_create_chn(chn, &attr);<br>// 6.通知编码器启动接收输入数据<br>hi_venc_start_param recv_param{};<br>recv_param.recv_pic_num = -1;<br>ret = hi_mpi_venc_start_chn(chn, &recv_param);<br>// 7.发送输入数据<br>// 7.1 申请输入内存<br>uint8_t* inputAddr = nullptr;<br>int32_t inputSize = 128 * 128 * 3 / 2;<br>ret = hi_mpi_dvpp_malloc(0, &inputAddr, inputSize);<br>// 如果运行模式为ACL_HOST,则需要申请Host内存,将输入数据读入Host内存,再通过aclrtMemcpy接口将Host的数据传输到Device,数据传输完成后,需及时释放Host内存;否则直接将输入数据读入Device内存<br>if (runMode == ACL_HOST) {<br> void* hostInputAddr = nullptr;<br> // 申请Host内存<br> aclRet = aclrtMallocHost(&hostInputAddr, inputSize);<br> // 将输入数据读入内存中,该自定义函数VencReadYuvFile由用户实现<br> VencReadYuvFile(streamName, hostInputAddr, inputSize);<br> // 数据传输<br> aclRet = aclrtMemcpy(inputAddr, inputSize, hostInputAddr, inputSize, ACL_MEMCPY_HOST_TO_DEVICE);<br> // 完成数据传输后,需及时释放不使用的内存<br> aclrtFreeHost(hostInputAddr );<br> hostInputAddr = nullptr;<br>} else {<br> // 将输入数据读入内存中,该自定义函数VencReadYuvFile由用户实现<br> VencReadYuvFile(streamName, inputAddr, inputSize);<br>}<br>// 7.2 发送输入数据,开始编码<br>hi_video_frame_info frame{};<br>frame.mod_id = HI_ID_VENC;<br>frame.v_frame.width = 128;<br>frame.v_frame.height = 128;<br>frame.v_frame.field = HI_VIDEO_FIELD_FRAME;<br>frame.v_frame.pixel_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420;<br>frame.v_frame.video_format = HI_VIDEO_FORMAT_LINEAR;<br>frame.v_frame.compress_mode = HI_COMPRESS_MODE_NONE;<br>frame.v_frame.dynamic_range = HI_DYNAMIC_RANGE_SDR8;<br>frame.v_frame.color_gamut = HI_COLOR_GAMUT_BT709;<br>frame.v_frame.width_stride[0] = 128;<br>frame.v_frame.width_stride[1] = 128;<br>frame.v_frame.width_stride[2] = 128;<br>frame.v_frame.virt_addr[0] = inputAddr;<br>frame.v_frame.virt_addr[1] = (hi_void *)((uintptr_t)frame.v_frame.virt_addr[0] + 128 * 128);<br>frame.v_frame.frame_flag = 0;<br>frame.v_frame.time_ref = 0;<br>frame.v_frame.pts = 0;<br>ret = hi_mpi_venc_send_frame(chn, &frame, 0);<br>// 8.获取编码结果<br>// 8.1 创建EPOLL实例<br>int32_t epollFd = 0;<br>int32_t fd = hi_mpi_venc_get_fd(chn);<br>ret = hi_mpi_sys_create_epoll(10, &epollFd);<br>hi_dvpp_epoll_event event;<br>event.events = HI_DVPP_EPOLL_IN;<br>event.data = (void*)(unsigned long)(fd);<br>ret = hi_mpi_sys_ctl_epoll(epollFd, HI_DVPP_EPOLL_CTL_ADD, fd, &event);<br>int32_t eventCount = 0;<br>// 编码完成前,会超时阻塞在这里,一旦完成,才会往下执行<br>ret = hi_mpi_sys_wait_epoll(epollFd, event, 3, 1000, &eventCount);<br>// 8.2 获取编码结果<br>hi_venc_chn_status stat;<br>ret = hi_mpi_venc_query_status(chn, &stat);<br>hi_venc_stream stream;<br>stream.pack_cnt = stat.cur_packs;<br>stream.pack = new hi_venc_pack[stream.pack_cnt];<br>ret = hi_mpi_venc_get_stream(chn, &stream, 1000);<br>// 8.3 如果运行模式为ACL_HOST,且Host上需要使用编码输出的码流,则需要申请Host内存,通过aclrtMemcpy接口将Device的输出码流传输到Host<br>if (g_runMode == ACL_HOST) {<br> void* hostOutputAddr = nullptr;<br> aclRet = aclrtMallocHost(&hostOutputAddr, outputSize);<br> aclRet = aclrtMemcpy(hostOutputAddr, outputSize, stream.pack[0].addr, outputSize, ACL_MEMCPY_DEVICE_TO_HOST);<br> // ......<br> // 数据使用完成后,需及时释放不使用的内存<br> aclrtFreeHost(hostOutputAddr);<br> hostOutputAddr = nullptr;<br>} else {<br> // 可以直接使用编码输出码流数据,在stream.pack[0].addr指向的内存中<br> // TODO: 推理相关的代码逻辑<br>}<br>// 9.释放输入内存和输出码流<br>ret = hi_mpi_dvpp_free(inputAddr);<br>ret = hi_mpi_venc_release_stream(chn, &stream);<br>delete[] stream.pack;<br>// 10.通知编码器停止接收输入数据<br>ret = hi_mpi_venc_stop_chn(chn);<br>ret = hi_mpi_sys_ctl_epoll(epollFd, HI_DVPP_EPOLL_CTL_DEL, fd, NULL);<br>ret = hi_mpi_sys_close_epoll(epollFd);<br>// 11.销毁通道<br>ret = hi_mpi_venc_destroy_chn(chn);<br>// 12.媒体数据处理系统去初始化<br>ret = hi_mpi_sys_exit();<br>// 13.释放运行管理资源(依次释放Context、Device)<br>aclRet = aclrtDestroyContext(g_context);<br>aclRet = aclrtResetDevice(0);<br>// 14.AscendCL去初始化<br>aclRet = aclFinalize();<br>// ....<br> |
|---|
父主题: 媒体数据处理V2
在线提单