功能支持度说明
VDEC视频解码
VDEC(Video Decoder)负责将H264/H265格式的视频码流解码为YUV/RGB格式的图片。关于VDEC功能的详细介绍及约束请参见功能及约束说明。
本节介绍VDEC视频编码的接口调用流程,同时配合示例代码辅助理解该接口调用流程。
接口调用流程
开发应用时,如果涉及视频解码,则应用程序中必须包含视频解码的代码逻辑,关于视频解码的接口调用流程,请先参见pyACL接口调用流程了解整体流程,再查看本节中的流程说明。
图1 视频解码流程

实现视频的解码,关键接口的说明如下:
- 调用acl.media.vdec_create_channel接口创建视频解码处理的通道。
- 创建视频解码处理通道前,需先执行以下操作:
-
调用acl.media.vdec_create_channel_desc接口创建通道描述信息。
-
调用acl.media.vdec_set_channel_desc系列接口设置通道描述信息的属性,包括解码通道号、线程、回调函数、视频编码协议等,其中:
-
回调函数需由用户提前创建,用于在视频解码后,获取解码数据,并及时释放相关资源,回调函数的原型前参见函数:vdec_set_channel_desc_callback。
在回调函数内,用户需调用acl.media.dvpp_get_pic_desc_ret_code接口获取“ret_code”返回码判断是否解码成功,“ret_code”为“0”表示解码成功,为“1”表示解码失败。如果解码失败,需要根据日志中的返回码判断具体的问题,返回码请参见返回码说明。
解码结束后,建议用户在回调函数内及时释放VDEC的输入码流内存、输出图片内存以及相应的视频码流描述信息、图片描述信息。
-
线程需由用户提前创建,并自定义线程函数,在线程函数内调用acl.rt.process_report接口,等待指定时间后,触发1.b.i中的回调函数。
:::note 说明 如果不调用acl.media.vdec_set_channel_desc_out_pic_format接口设置输出格式,则默认使用“YUV420SP NV12”。 :::
-
-
- acl.media.vdec_create_channel接口内部封装了如下接口,无需用户单独调用:
- acl.rt.create_stream接口:显式创建Stream,VDEC内部使用。
- acl.rt.subscribe_report接口:指定处理Stream上回调函数的线程,回调函数和线程是由用户调用acl.media.vdec_set_channel_desc系列接口时指定的。
- 创建视频解码处理通道前,需先执行以下操作:
- 调用acl.media.vdec_send_frame接口将视频码流解码成YUV420SP格式的图片。
-
视频解码前,需先执行以下操作:
- 调用acl.media.dvpp_create_stream_desc接口创建输入视频码流描述信息,并调用acl.media.dvpp_set_stream_desc系列接口设置输入视频的内存地址、内存大小、码流格式等属性。
- 调用acl.media.dvpp_create_pic_desc接口创建输出图片描述信息,并调用acl.media.dvpp_set_pic_desc系列接口设置输出图片的内存地址、内存大小、图片格式等属性。
-
视频解码时:
- acl.media.vdec_send_frame接口内部封装了acl.rt.launch_callback接口,用于在Stream的任务队列中增加一个需要执行的回调函数。用户无需单独调用acl.rt.launch_callback接口。
-
视频解码后,视频解码的结果数据通过回调函数获取:
获取解码数据前,先获取“ret_code”的值,判断解码是否成功,0表示解码成功,1表示解码失败。如果解码失败,需要根据日志中的返回码判断具体的问题,返回码请参见返回码说明。
如果用户需要获取解码的帧序号,则可以在acl.media.vdec_send_frame接口的“user_data”参数处定义,然后解码的帧序号可以通过“user_data”参数传递给VDEC的回调函数,用于确定回调函数中处理的是第几帧数据。
如果不想获取某一帧的解码结果, 可以调用acl.media.vdec_send_skipped_frame接口, 将待解码的码流(输入内存)传到解码器进行解码,此时,解码结果最终不会输出,解码完成的回调函数中返回的“dvpp_pic_desc”为0。
-
- 调用acl.media.vdec_destroy_channel接口销毁视频处理的通道。
- 系统会等待已发送帧解码完成且用户的回调函数处理完成后再销毁通道。
- acl.media.vdec_destroy_channel接口内部封装了如下接口,无需用户单独调用。
- acl.rt.unsubscribe_report接口:取消线程注册(Stream上的回调函数不再由指定线程处理)。
- acl.rt.destroy_stream接口:销毁Stream。
- 销毁通道后,需调用acl.media.vdec_destroy_channel_desc接口销毁通道描述信息。
- 销毁通道描述信息后,用户才可以销毁1.b.ii中创建的线程。
示例代码
您可以从样例介绍中获取完整样例代码。
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝运行,仅供参考。
<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> | python<br>import acl<br># ......<br># 1.pyACL初始化。<br>ret = acl.init()<br># 2.运行管理资源申请,包括Device、Context、Stream。<br>ret = acl.rt.set_device(self.device_id)<br>self.context, ret = acl.rt.create_context(self.device_id)<br>self.stream, ret = acl.rt.create_stream()<br># 3.创建回调函数。<br>def _callback(self, input_stream_desc, output_pic_desc, user_data):<br> # 输入码流描述。<br> if input_stream_desc:<br> ret = acl.media.dvpp_destroy_stream_desc(input_stream_desc)<br> if ret != 0:<br> print("acl.media.dvpp_destroy_stream_desc failed")<br> # 输出图片描述。<br> if output_pic_desc:<br> vdec_out_buffer = acl.media.dvpp_get_pic_desc_data(output_pic_desc)<br> ret_code = acl.media.dvpp_get_pic_desc_ret_code(output_pic_desc)<br> data_size = acl.media.dvpp_get_pic_desc_size(output_pic_desc)<br> self.images_buffer.append(dict({"buffer": vdec_out_buffer,<br> "size": data_size}))<br> ret = acl.media.dvpp_destroy_pic_desc(output_pic_desc)<br> if ret != 0:<br> print("acl.media.dvpp_destroy_pic_desc failed")<br> self.output_count += 1<br> print("[Vdec] [_callback] _callback exit success")<br> # 在此样例中,释放vdec_out_buffer在vdec流程之后处理。<br># 4.创建回调处理线程,并提交注册由此线程执行回调函数。<br>timeout = 100<br>cb_thread_id, ret = acl.util.start_thread(self._thread_func, [timeout])<br>acl.rt.subscribe_report(cb_thread_id, self.stream)<br># 5.创建视频码流处理通道时的通道描述信息,设置视频处理通道描述信息的属性,其中线程、callback回调函数需要用户提前创建。<br>def init_resource(self, cb_thread_id):<br> print("[Vdec] class Vdec init resource stage:")<br> self.vdec_channel_desc = acl.media.vdec_create_channel_desc()<br> acl.media.vdec_set_channel_desc_channel_id(self.vdec_channel_desc, self._channel_id)<br> acl.media.vdec_set_channel_desc_thread_id(self.vdec_channel_desc, cb_thread_id) <br> acl.media.vdec_set_channel_desc_callback(self.vdec_channel_desc, self._callback)<br> acl.media.vdec_set_channel_desc_entype(self.vdec_channel_desc, self._en_type)<br> acl.media.vdec_set_channel_desc_out_pic_format(self.vdec_channel_desc, self._format)<br> acl.media.vdec_create_channel(self.vdec_channel_desc)<br> print("[Vdec] class Vdec init resource stage success")<br># 6.创建视频码流处理的通道。<br>ret = acl.media.vdec_create_channel(self.vdec_channel_desc)<br># ......<br># 7.申请Device内存dataDev,存放视频解码的输入视频数据。<br># 将通过acl.rt.memcpy接口将Host的图片数据传输到Device,数据传输完成后,需及时调用acl.rt.free_host接口释放Host内存。<br># 输出图片尺寸对齐方式设置。<br>output_pic_size = (self.input_width * self.input_height * 3) // 2<br>img = np.fromfile(img_path, dtype=self.dtype)<br>input_stream_size = img.size<br>bytes_data = img.tobytes()<br>img_ptr = acl.util.bytes_to_ptr(bytes_data)<br>self.input_stream_mem, ret = acl.media.dvpp_malloc(input_stream_size)<br>ret = acl.rt.memcpy(self.input_stream_mem,<br> input_stream_size,<br> img_ptr,<br> input_stream_size,<br> ACL_MEMCPY_HOST_TO_DEVICE)<br># 8.循环10次执行视频解码,输出10张YUV420SP NV12格式的图片。<br>def forward(self, output_pic_size, input_stream_size):<br> self.frame_config = acl.media.vdec_create_frame_config()<br> for i in range(self.rest_len):<br> print("[Vdec] forward index:{}".format(i))<br> # 8.1 创建输入视频码流描述信息,设置码流信息的属性。<br> self._set_input(input_stream_size)<br> # 8.2 创建输出图片描述信息,设置图片描述信息的属性。<br> self._set_pic_output(output_pic_size)<br> # 8.3 执行视频码流解码,解码每帧数据后,系统自动调用callback回调函数将解码后的数据写入文件,再及时释放相关资源。<br> ret = acl.media.vdec_send_frame(self.vdec_channel_desc,<br> self.dvpp_stream_desc,<br> self.dvpp_pic_desc,<br> self.frame_config,<br> None)<br> check_ret("acl.media.vdec_send_frame", ret)<br> print('[Vdec] vdec_send_frame stage success')<br># 9.释放图片处理通道、图片描述信息。<br>ret = acl.media.vdec_destroy_channel(self.vdec_channel_desc)<br>acl.media.vdec_destroy_channel_desc(self.vdec_channel_desc)<br># 10. 释放运行管理资源。<br>ret = acl.rt.destroy_dtream(self.stream)<br>ret = acl.rt.destroy_context(self.context)<br>ret = acl.rt.reset_device(self.device_id)<br>ret = acl.finalize()<br># 11.pyACL去初始化。<br># ....<br> |
|---|
返回码说明
表1 返回码列表
| 返回码 | 含义 | 可能原因及解决方法 |
|---|---|---|
| AICPU_DVPP_KERNEL_STATE_SUCCESS = 0 | 解码成功。 | - |
| AICPU_DVPP_KERNEL_STATE_FAILED = 1 | 其它错误。 | - |
| AICPU_DVPP_KERNEL_STATE_DVPP_ERROR = 2 | AscendCL内部调用其它模块的接口失败。 | - |
| AICPU_DVPP_KERNEL_STATE_PARAM_INVALID = 3 | 参数校验失败。 | 请检查接口的参数是否符合接口要求。 |
| AICPU_DVPP_KERNEL_STATE_OUTPUT_SIZE_INVALID = 4 | 输出内存大小校验失败。 | 请检查输出内存大小是否符合接口要求。 |
| AICPU_DVPP_KERNEL_STATE_INTERNAL_ERROR = 5 | 系统内部错误。 | - |
| AICPU_DVPP_KERNEL_STATE_QUEUE_FULL = 6 | 系统内部队列满。 | - |
| AICPU_DVPP_KERNEL_STATE_QUEUE_EMPTY = 7 | 系统内部队列空。 | - |
| AICPU_DVPP_KERNEL_STATE_QUEUE_NOT_EXIST = 8 | 系统内部队列不存在。 | - |
| AICPU_DVPP_KERNEL_STATE_GET_CONTEX_FAILED = 9 | 获取系统内部上下文失败。 | - |
| AICPU_DVPP_KERNEL_STATE_SUBMIT_EVENT_FAILED = 10 | 提交系统内部事件失败。 | - |
| AICPU_DVPP_KERNEL_STATE_MEMORY_FAILED = 11 | 系统内部申请内存失败。 | 请检查系统是否有可用内存。 |
| AICPU_DVPP_KERNEL_STATE_SEND_NOTIFY_FAILED = 12 | 发送系统内部通知失败。 | - |
| AICPU_DVPP_KERNEL_STATE_VPC_OPERATE_FAILED = 13 | 系统内部接口处理失败。 | - |
| AICPU_DVPP_KERNEL_STATE_CHANNEL_ABNORMAL = 14 | 当前通道异常。 | - |
| ERR_INVALID_STATE = 0x10001 | VDEC解码器状态异常错误。 | - |
| ERR_HARDWARE = 0x10002 | 硬件错误,包含解码器启动、执行、停止等异常。 | - |
| ERR_SCD_CUT_FAIL = 0x10003 | 将视频码流分解成多帧图片异常。 | 请检查输入的视频流数据是否正确。 |
| ERR_VDM_DECODE_FAIL = 0x10004 | 解码某一帧异常。 | 请检查输入的视频流数据是否正确。 |
| ERR_ALLOC_MEM_FAIL = 0x10005 | 内部申请内存失败。 | 请检查系统是否有可用内存,若忽略错误,则可能导致用户持续送入码流却无任何解码结果输出。 |
| ERR_ALLOC_DYNAMIC_MEM_FAIL = 0x10006 | 包括输入视频分辨率超范围、内部动态申请内存失败等异常。 | 请检查输入视频流的分辨率、系统是否有可用内存,若忽略错误,则可能导致用户持续送入码流却无任何解码结果输出。 |
| ERR_ALLOC_IN_OR_OUT_PORT_MEM_FAIL = 0x10007 | 系统内部申请VDEC的输入、输出buffer异常。 | 请检查系统是否有可用内存,若忽略错误,则可能导致用户持续送入码流却无任何解码结果输出。 |
| ERR_BITSTREAM = 0x10008 | 码流错误(如语法解析失败、重发eos或首帧即发送eos)。 | 请检查输入的视频流数据是否正确。 |
| ERR_VIDEO_FORMAT = 0x10009 | 输入视频格式错误。 | 请检查输入视频的格式是否为h264或h265。 |
| ERR_IMAGE_FORMAT = 0x1000a | 输出格式配置错误。 | 请检查输出图像的格式是否为nv12或nv21。 |
| ERR_CALLBACK = 0x1000b | 回调函数为空。 | 请检查配置的回调函数是否为空。 |
| ERR_INPUT_BUFFER = 0x1000c | 输入内存为空。 | 请检输入内存是否为空。 |
| ERR_INBUF_SIZE = 0x1000d | 输入内存大小<=0。 | 请检查输入内存大小是否小于等于0。 |
| ERR_THREAD_CREATE_FBD_FAIL = 0x1000e | 系统内部将解码结果通过回调函数返回给用户的线程异常。 | 请检查系统中资源(例如:线程、内存等)是否可用。 |
| ERR_CREATE_INSTANCE_FAIL = 0x1000f | 创建解码实例失败。 | - |
| ERR_INIT_DECODER_FAIL = 0x10010 | 初始化解码器失败,例如解码实例个数超出范围(最大16)。 | - |
| ERR_GET_CHANNEL_HANDLE_FAIL = 0x10011 | 系统内部获取某路视频流的解码句柄失败。 | - |
| ERR_COMPONENT_SET_FAIL = 0x10012 | 系统内部设置解码实例异常。 | 请检查解码的入参值是否正确,例如输入视频格式video_format、输出帧格式image_format等。 |
| ERR_COMPARE_NAME_FAIL = 0x10013 | 系统内部设置解码实例名称异常。 | 请检查解码的入参值是否正确,例如输入视频格式video_format、输出帧格式image_format等。 |
| ERR_OTHER = 0x10014 | 其它错误。 | - |
| ERR_DECODE_NOPIC = 0x20000 | 隔行码流场景下使用,隔行码流每帧发送两场,解码时其中一块无图像输出,属于正常现象,会返回该错误码;隔行码流的解码输出数据都在奇数场对应的输出buffer中。 | - |
| 0x20001 | 参考帧个数设置错误。 | 请检查码流实际参考帧个数与用户设置的参考帧个数是否一致。 |
| 0x20002 | VDEC解码帧存大小设置错误。 | 请检查输入码流实际宽、高与用户设置的宽、高是否一致。 |
| 0xA0058001 | 无效的Device ID。 | 请检查Device ID。 |
| 0xA0058002 | 无效的channel ID。 | 请检查传入接口的通道号是否正确,或者检查通道总数是否达到上限。 |
| 0xA0058003 | 参数不合法,例如不合法的枚举值。 | 请根据日志检查出错的参数。 |
| 0xA0058004 | 资源已存在。 | 请检查是否重复创建通道。 |
| 0xA0058005 | 通道资源不存在。 | 请检查是否使用了不存在的通道号或通道句柄。 |
| 0xA0058006 | 函数参数中有空指针。 | 请根据日志检查接口的入参。 |
| 0xA0058007 | 使能系统、Device或通道前未配置对应的参数。 | 请根据日志检查接口的入参。 |
| 0xA0058008 | 不支持的参数或者功能。 | 请根据日志检查接口的入参。 |
| 0xA0058009 | 该操作不允许,如试图修改静态配置参数。 | - |
| 0xA005800C | 分配内存失败,如系统内存不足。 | 请检查系统是否有可用内存,若忽略错误,则可能导致用户持续送入码流却无任何解码结果输出。 |
| 0xA005800D | 分配缓存失败,如申请的数据缓冲区太大。 | - |
| 0xA005800E | 缓冲区中无数据。 | 系统未完成解码,缓冲区中无解码结果数据,需等待缓冲区中有数据后,再尝试获取数据。 |
| 0xA005800F | 缓冲区中数据满。 | 用户发送输入码流数据的速度太快,导致输入缓冲区数据满,请尝试降低发送输入码流数据的速度,或者在创建通道时将缓冲区大小设置为较大值。 |
| 0xA0058010 | 系统没有初始化或者相关依赖的模块没有加载。 | 请检查是否调用系统初始化接口。 |
| 0xA0058011 | 地址错误。 | - |
| 0xA0058012 | 系统忙。 | 请检查VDEC解码总路数是否达到上限。 |
| 0xA0058013 | 缓存小于实际需要的大小。 | - |
| 0xA0058014 | 硬件或软件处理超时。 | - |
| 0xA0058015 | 内部系统错误。 | - |
| 0xA005803F | 最大的返回码,该模块的错误码必须小于该值。 | - |
父主题: 媒体数据处理V1
VENC视频编码
VENC(Video Encoder)将YUV420SP格式的图片编码成H264/H265格式的视频码流。关于VENC功能的详细介绍及约束请参见功能及约束说明。
本节介绍VENC视频编码的接口调用流程,同时配合示例代码辅助理解该接口调用流程。
接口调用流程
开发应用时,如果涉及视频编码,则应用程序中必须包含视频编码的代码逻辑,关于视频编码的接口调用流程,请先参见pyACL接口调用流程了解整体流程,再查看本节中的流程说明。
图1 视频编码流程

实现视频的编码,关键接口的说明如下:
- 调用acl.media.venc_create_channel接口创建视频编码处理的通道。
- 创建视频编码处理通道前,需先执行以下操作:
-
调用acl.media.venc_create_channel_desc接口创建通道描述信息。
-
调用acl.media.venc_set_channel_desc_param接口设置通道描述信息的属性,包括线程、回调函数、视频编码协议、输入图片格式等,其中:
-
回调函数需由用户提前创建,用于在视频编码后,获取编码数据,并及时释放相关资源,回调函数的原型前参见函数:venc_set_channel_desc_callback。
视频编码结束后,建议用户在回调函数内及时释放输入图片内存、以及相应的图片描述信息。视频编码的输出内存由系统管理,不由用户管理,因此无需用户释放。
-
线程需由用户提前创建,并自定义线程函数,在线程函数内调用acl.rt.process_report接口,等待指定时间后,触发1.b.i中的回调函数。
:::note 说明 推荐使用acl.media.venc_set_channel_desc_param接口设置通道描述信息的属性,通过枚举值来选择通过该接口设置某一个属性的值。
但为兼容旧版本,也可以调用acl.media.venc.set_channel_desc系列接口设置通道描述信息的属性,每个属性的设置对应一个set接口。 :::
-
-
- acl.media.venc_create_channel接口内部封装了如下接口,无需用户单独调用:
- acl.rt.create_stream接口:显式创建Stream,VENC内部使用。
- acl.rt.subscribe_report接口:指定处理Stream上回调函数的线程,回调函数和线程是由用户调用acl.media.venc_set_channel_desc_param接口时指定的。
- 创建视频编码处理通道前,需先执行以下操作:
- 调用acl.media.venc_send_frame接口将YUV420SP格式的图片编码成H264/H265格式的视频码流。
- 视频编码前,需先执行以下操作:
- 调用acl.media.dvpp_create_pic_desc接口创建输入图片描述信息,并调用acl.media.dvpp_set_pic_desc系列接口设置输入图片的内存地址、内存大小、图片格式等属性。
- 调用acl.media.venc_create_frame_config接口创建单帧编码配置数据,并调用acl.media.venc_set_frame_config系列接口设置是否强制重新开始I帧间隔、是否结束帧。
- 视频编码时,acl.media.venc_send_frame接口内部封装了acl.rt.launch_callback接口,用于在Stream的任务队列中增加一个需要执行的回调函数。用户无需单独调用acl.rt.launch_callback接口。
- 视频编码后,视频编码的结果数据通过回调函数获取。
- 视频编码前,需先执行以下操作:
- 调用acl.media.venc_destroy_channel接口销毁视频处理的通道。
- 系统会等待已发送帧编码完成且用户的回调函数处理完成后再销毁通道。
- acl.media.venc_destroy_channel接口内部封装了如下接口,无需用户单独调用。
- acl.rt.unsubscribe_report接口:取消线程注册(Stream上的回调函数不再由指定线程处理)。
- acl.rt.destroy_stream接口:销毁Stream。
- 销毁通道后,需调用acl.media.venc_destroy_channel_desc接口销毁通道描述信息。
- 销毁通道描述信息后,用户才可以销毁1.b.ii中创建的线程。
示例代码
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝运行,仅供参考。
<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> | python<br>import acl<br># ......<br># 1.资源初始化: 创建 AclVenc 对象进行初始化。<br># 1.1 pyACL初始化。<br>ret = acl.init()<br># 1.2 运行管理资源申请,包括Device、Context。<br>device_id = 0<br>ret = acl.rt.set_device(device_id)<br>self.context, ret = acl.rt.create_context(device_id)<br># 调用acl.rt.get_run_mode接口获取软件栈的运行模式,根据运行模式来判断后续的内存申请接口调用逻辑。<br>runMode, ret = acl.rt.get_run_mode()<br># 2.创建执行回调函数的线程及线程函数。<br>def cb_thread_func(self, args_list):<br> ctx = args_list[0]<br> timeout = args_list[1]<br> print("[info] thread args_list = ", self.ctx, timeout, self.g_callbackRunFlag, "\n")<br> ret = acl.rt.set_context(ctx)<br> assert ret == 0<br> while self.g_callbackRunFlag is True:<br> print("[info] thread g_callbackRunFlag = ", self.g_callbackRunFlag, "\n")<br> ret = acl.rt.process_report(timeout)<br> print("[info] acl.rt.process_report =", ret)<br>timeout = 1000<br>g_callbackRunFlag = True<br>self.cb_thread_id, ret = acl.util.start_thread(self.cb_thread_func, [self.ctx, timeout])<br># 3.创建回调函数。<br># 3.1 获取流数据输出。<br>def get_stream_data(self, stream_desc):<br> stream_data = acl.media.dvpp_get_stream_desc_data(stream_desc)<br> assert stream_data is not None<br> stream_data_size = acl.media.dvpp_get_stream_desc_size(stream_desc)<br> print("[info] stream_data size", stream_data_size)<br> # stream memcpy d2h<br> np_data = np.zeros(stream_data_size, dtype=np.byte)<br> <br> bytes_data = np_data.tobytes()<br> np_data_ptr = acl.util.bytes_to_ptr(bytes_data)<br> ret = acl.rt.memcpy(np_data_ptr, stream_data_size,<br> stream_data, stream_data_size,<br> memcpy_kind.get("ACL_MEMCPY_DEVICE_TO_HOST"))<br> assert ret == 0<br> return np_data<br># 3.2 回调函数将视频流保存文件。<br>def callback_func(self, input_pic_desc, output_stream_desc, user_data):<br> # 获取视频编码结果数据, 转换成numpy对象。<br> output_numpy = self.get_stream_data(output_stream_desc)<br> with open('./data/output.h265', 'ab') as f:<br> f.write(output_numpy)<br> print("[INFO] [callback_func] stream size =", acl.media.dvpp_get_stream_desc_size(output_stream_desc))<br> <br># 4.创建视频编码处理通道时的通道描述信息。<br>self.venc_channel_desc = acl.media.venc_create_channel_desc()<br># 5.设置通道描述信息的属性,其中线程、callback回调函数需要用户提前创建。<br># vencChannelDesc_是aclvdecChannelDesc类型。<br>venc_format = 1<br>venc_entype = 0<br>input_width = 1280<br>input_height = 720<br>ret = acl.media.venc_set_channel_desc_thread_id(self.venc_channel_desc, self.cb_thread_id)<br>ret = acl.media.venc_set_channel_desc_callback(self.venc_channel_desc, self.callback_func)<br>ret = acl.media.venc_set_channel_desc_entype(self.venc_channel_desc, venc_entype)<br>ret = acl.media.venc_set_channel_desc_pic_format(self.venc_channel_desc, venc_format)<br>ret = acl.media.venc_set_channel_desc_key_frame_interval(self.venc_channel_desc, 16)<br>ret = acl.media.venc_set_channel_desc_pic_height(self.venc_channel_desc, input_height)<br>ret = acl.media.venc_set_channel_desc_pic_width(self.venc_channel_desc, input_width )<br># 6.创建视频码流处理的通道、单帧编码配置数据。<br>ret = acl.media.venc_create_channel(self.venc_channel_desc)<br># vencFrameConfig_是aclvencFrameConfig类型。<br>frame_config = acl.media.venc_create_frame_config()<br># 7.申请Device内存dataDev,存放视频编码的输入数据。<br># 7.1 读入图片数据。<br>output_pic_size = (input_width * input_height * 3) // 2<br>print("[INFO] output_pic_size:", output_pic_size, " load vdec file:", venc_file_path)<br>file_context = np.fromfile(venc_file_path, dtype=np.byte)<br>file_size = file_context.size<br>bytes_data = file_context.tobytes()<br>file_mem = acl.util.bytes_to_ptr(bytes_data)<br>input_size = file_size<br># 如果调用acl.rt.get_run_mode接口获取软件栈的运行模式为ACL_HOST,则需要通过acl.rt.memcpy接口将Host的图片数据传输到Device,数据传输完成后,需及时调用acl.rt.free_host接口释放Host内存。<br># 如果调用acl.rt.get_run_mode接口获取软件栈的运行模式为ACL_DEVICE,则直接申请Device内存,存放输入图片数据。<br># 此处是运行模式为 ACL_HOST。<br>input_mem, ret = acl.media.dvpp_malloc(input_size)<br>assert ret == 0<br>ret = acl.rt.memcpy(input_mem, input_size, file_mem,<br> file_size, memcpy_kind.get("ACL_MEMCPY_HOST_TO_DEVICE"))<br>assert ret == 0<br># 8.执行视频编码。<br>def venc_set_frame_config(self, frame_confg, eos, iframe):<br> ret = acl.media.venc_set_frame_config_eos(frame_confg, eos)<br> assert ret == 0<br> ret = acl.media.venc_set_frame_config_force_i_frame(frame_confg, iframe)<br> assert ret == 0<br>def venc_process(self, venc_channel_desc, input_mem, input_size, frame_config):<br> # 设置为非结束帧。<br> self.venc_set_frame_config(frame_config, 0, 0)<br> print("[INFO] set frame config")<br> self.test_get_frame_config(frame_config)<br> # set picture description<br> dvpp_pic_desc = acl.media.dvpp_create_pic_desc()<br> assert dvpp_pic_desc is not None<br> ret = acl.media.dvpp_set_pic_desc_data(dvpp_pic_desc, input_mem)<br> assert ret == 0<br> ret = acl.media.dvpp_set_pic_desc_size(dvpp_pic_desc, input_size)<br> assert ret == 0<br> print("[INFO] set pic desc")<br> # 发送1张图片到编码器进行编码。<br> venc_cnt = 16<br> while venc_cnt:<br> ret = acl.media.venc_send_frame(venc_channel_desc, dvpp_pic_desc, 0, frame_config, 0)<br> assert ret == 0<br> print("[INFO] venc send frame")<br> venc_cnt -= 1<br> # 结束视频编码时可以发送eos为1的空图片,表示当前编码结束。<br> self.venc_set_frame_config(frame_config, 1, 0)<br> ret = acl.media.venc_send_frame(venc_channel_desc, 0, 0, frame_config, 0)<br> assert ret == 0<br># 9.释放资源。<br>ret = acl.media.dvpp_free(input_mem)<br>ret = acl.media.venc_destroy_frame_config(frame_config)<br>assert ret == 0<br>print("[INFO] free resources")<br>g_callbackRunFlag = False<br>ret = acl.util.stop_thread(self.cb_thread_id)<br>print("[INFO] thread join")<br># 10. 释放运行管理资源。<br>ret = acl.rt.destroy_stream(self.stream)<br>ret = acl.rt.destroy_context(self.context)<br>ret = acl.rt.reset_device(self.device_id);<br>ret = acl.finalize()<br># 11.pyACL去初始化。<br># ....<br> |
|---|
如果调用acl.media.venc_set_channel_desc_param接口设置通道描述信息的属性,调用acl.media.venc_get_channel_desc_param接口获取通道描述信息中的属性值,示例代码如下:
<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> | <br>ACL_VENC_THREAD_ID_UINT64 = 0 #回调线程ID。<br>ACL_VENC_CALLBACK_PTR = 1 #回调函数。<br>ACL_VENC_PIXEL_FORMAT_UINT32 = 2 #输入图像格式。<br>ACL_VENC_ENCODE_TYPE_UINT32 = 3 #视频编码协议。<br>ACL_VENC_PIC_WIDTH_UINT32 = 4 #输入图片宽度。<br>ACL_VENC_PIC_HEIGHT_UINT32 = 5 #输入图片高度。<br>ACL_VENC_KEY_FRAME_INTERVAL_UINT32 = 6 #关键帧间隔。<br>ACL_VENC_BUF_ADDR_PTR = 7 #编码输出缓存地址。<br>ACL_VENC_BUF_SIZE_UINT32 = 8 #编码输出缓存大小。<br>ACL_VENC_RC_MODE_UINT32 = 9 #码率控制模式。<br>ACL_VENC_SRC_RATE_UINT32 = 10 #输入码流帧率。<br>ACL_VENC_MAX_BITRATE_UINT32 = 11 #输出码率。<br>ACL_VENC_MAX_IP_PROP_UINT32 = 12 #一个GOP内单个I帧bit数和单个P帧bit数的比例。<br># 设置回调函数。<br>ret = acl.media.venc_set_channel_desc_param(self.venc_channel_desc, ACL_VENC_CALLBACK_PTR, self.callback_func)<br># 获取回调函数。<br>get_cb_func, ret = acl.media.venc_get_channel_desc_param(self.venc_channel_desc, ACL_VENC_CALLBACK_PTR)<br>PIXEL_FORMAT_YUV_SEMIPLANAR_420 = 1<br># 设置输入图片格式。<br>ret = acl.media.venc_set_channel_desc_param(self.venc_channel_desc, ACL_VENC_PIXEL_FORMAT_UINT32, PIXEL_FORMAT_YUV_SEMIPLANAR_420)<br># 获取输入图片格式。<br>get_pic_format, ret = acl.media.venc_get_channel_desc_param(self.venc_channel_desc, ACL_VENC_PIXEL_FORMAT_UINT32)<br>width = 128<br># 设置图片宽度。<br>ret = acl.media.venc_set_channel_desc_param(self.venc_channel_desc, ACL_VENC_PIC_WIDTH_UINT32, width)<br># 获取图片宽度。<br>get_width, ret = acl.media.venc_get_channel_desc_param(self.venc_channel_desc, ACL_VENC_PIC_WIDTH_UINT32)<br>height = 128<br># 设置图片高度。<br>ret = acl.media.venc_set_channel_desc_param(self.venc_channel_desc, ACL_VENC_PIC_HEIGHT_UINT32, height)<br># 获取图片高度。<br>get_height, ret = acl.media.venc_get_channel_desc_param(self.venc_channel_desc, ACL_VENC_PIC_HEIGHT_UINT32)<br> |
|---|
父主题: 媒体数据处理V1
功能支持度说明
昇腾AI处理器对媒体数据处理V2版本各功能的支持度如下表所示。
| 昇腾AI处理器 | VPC | JPEGD | JPEGE | PNGD | VDEC | VENC |
|---|---|---|---|---|---|---|
| Atlas 200/500 A2推理产品 | √ | √ | √ | √ | √ | √ |
父主题: 媒体数据处理V2
VPC图片处理典型功能
VPC(Vision Preprocessing Core)负责图像处理功能,支持对图片做抠图、缩放、格式转换等操作。关于VPC功能的详细介绍请参见VPC功能,关于VPC功能对输入、输出的约束要求,请参见约束说明。
本节以抠图、缩放为例说明VPC图像处理时的接口调用流程,同时配合以下典型功能的示例代码辅助理解该接口调用流程:
典型功能接口调用流程(以缩放为例)
开发应用时,如果涉及抠图、缩放等图片处理,则应用程序中必须包含图片处理的代码逻辑,关于图片处理的接口调用流程,请先参见pyACL接口调用流程了解整体流程,再查看本节中的流程说明。
图1 接口调用流程(以缩放为例)

当前系统支持对输入图片做抠图、缩放等处理,关键接口的说明如下:
-
调用acl.himpi.sys_init接口进行媒体数据处理系统初始化。
-
调用acl.himpi.vpc_create_chn接口创建通道。
-
调用acl.himpi.dvpp_malloc接口申请Device上的内存,存放输入或输出数据。
-
执行抠图、缩放等,此步骤以缩放为例说明,其它功能(例如格式转换、金字塔等)请参见VPC功能下的接口说明。
调用acl.himpi.vpc_resize接口,按指定区域从输入图片中抠图,抠出的图作为输出图片。acl.himpi.vpc_resize接口是异步接口,调用该接口成功仅表示任务下发成功,还需要调用acl.himpi.vpc_get_process_result接口等待任务完成。
- 可以跟acl.himpi.vpc_resize接口在同一个线程中调用acl.himpi.vpc_get_process_result接口,也可以新起一个线程调用acl.himpi.vpc_get_process_result接口,后者多线程并行,提高效率,但用户需自行实现线程间同步。
- 在实现抠图、缩放等功能时,通过将输入图片和输出图片的格式设置成不同的,达到转换图片格式的目的。
-
调用acl.himpi.dvpp_free接口释放输入、输出内存。
-
调用acl.himpi.vpc_destroy_chn接口销毁通道。
-
调用acl.himpi.sys_exit接口进行媒体数据处理系统去初始化。
图片缩放示例代码
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝运行,仅供参考。
## 1.获取软件栈的运行模式,不同运行模式影响后续的接口调用流程(例如是否进行数据传输等)。
run_mode, ret = acl.rt.get_run_mode()
## 2.pyACL 初始化。
ret = acl.init()
## 3.运行管理资源申请(依次申请Device、Context)。
ret = acl.rt.set_device(0)
context, ret = acl.rt.create_context(0)
## 4.初始化媒体数据处理系统。
ret = acl.himpi.sys_init()
## 5.创建通道。
channel_id = 0
chn_attr = {'attr': 0, 'pic_width': 0, 'pic_height': 0}
ret = acl.himpi.vpc_create_chn(channel_id, chn_attr)
## 6.执行缩放。
## 6.1 构造存放输入图片信息的字典。
input_pic = {'picture_width': 1920,
'picture_height': 1080,
'picture_width_stride': 1920,
'picture_height_stride': 1080,
'picture_format': HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420}
input_pic["picture_buffer_size"] = input_pic["picture_width_stride"] * input_pic["picture_height_stride"] * 3 // 2
picture_address, ret = acl.himpi.dvpp_malloc(0, input_pic["picture_buffer_size"])
input_pic["picture_address"] = picture_address
## 如果运行模式为ACL_HOST,则需要申请Host内存,将输入图片数据读入Host内存,再通过acl.rt.memcpy接口将Host的图片数据传输到Device,数据传输完成后,需及时释放Host内存;否则直接将输入图片数据读入Device内存。
## 直接将输入图片数据读入Device内存。
if run_mode == ACL_HOST:
# 将输入图片读入内存中。
vpc_file = np.fromfile(vpc_file_path, dtype=np.byte)
vpc_file_size = vpc_file.itemsize * vpc_file.size
bytes_data = vpc_file.tobytes()
vpc_file_ptr = acl.util.bytes_to_ptr(bytes_data)
# 数据传输。
ret = acl.rt.memcpy(input_pic["picture_address"], input_pic["picture_buffer_size"],
vpc_file_ptr, vpc_file_size, ACL_MEMCPY_HOST_TO_DEVICE)
else:
# 将输入图片读入内存中。
vpc_file = np.fromfile(vpc_file_path, dtype=np.byte)
vpc_file_size = vpc_file.itemsize * vpc_file.size
bytes_data = vpc_file.tobytes()
vpc_file_ptr = acl.util.bytes_to_ptr(bytes_data)
# 数据传输。
ret = acl.rt.memcpy(input_pic["picture_address"], input_pic["picture_buffer_size"],
vpc_file_ptr, vpc_file_size, ACL_MEMCPY_DEVICE_TO_DEVICE)
## 6.3 构造存放输出图片信息的字典。
output_pic = {'picture_width': 960,
'picture_height': 540,
'picture_width_stride': 960,
'picture_height_stride': 540,
'picture_format': HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420}
output_pic["picture_buffer_size"] = output_pic["picture_width_stride"] * output_pic["picture_height_stride"] * 3 // 2
picture_address, ret = acl.himpi.dvpp_malloc(0, output_pic["picture_buffer_size"])
output_pic["picture_address"] = picture_address
## 初始化内存。
ret = acl.rt.memset(output_pic["picture_address"], output_pic["picture_buffer_size"], 0, output_pic["picture_buffer_size"])
## 6.4 调用缩放接口。
testid, ret = acl.himpi.vpc_resize(channel_id, input_pic, output_pic, 0, 0, 0, -1)
## 6.5 等待任务处理结束,任务处理结束后,输出图片数据在outputPic.picture_address指向的内存中。
ret = acl.himpi.vpc_get_process_result(channel_id, task_id, -1)
## 6.6 如果运行模式为ACL_HOST,且Host上需要展示VPC输出的图片数据,则需要申请Host内存,通过acl.rt.memcpy接口将Device的输出图片数据传输到Host;如果Host上不需要展示VPC输出的图片数据,则VPC的输出图片数据可以直接作为模型推理的输入。
## 6.6 VPC的输出图片数据可以直接作为模型推理的输入。
if run_mode == ACL_HOST:
np_output = np.zeros(output_pic.get('picture_buffer_size'), dtype=np.byte)
bytes_data = np_output.tobytes()
np_output_ptr = acl.util.bytes_to_ptr(bytes_data)
ret = acl.rt.memcpy(np_output_ptr, output_pic.get('picture_buffer_size'), output_pic.get("picture_address"),
output_pic.get('picture_buffer_size'), ACL_MEMCPY_DEVICE_TO_HOST)
# ......
else
# 可以直接使用VPC的输出图片数据,在outputPic.picture_address指向的内存中。
# ......
}
## 6.7 释放输入、输出内存。
ret = acl.himpi.dvpp_free(input_pic['picture_address'])
ret = acl.himpi.dvpp_free(output_pic['picture_address'])
## 7.销毁通道。
ret = acl.himpi.vpc_destroy_chn(channel_id)
## 8. 媒体数据处理系统去初始化。
ret = acl.himpi.sys_exit()
## 9. 释放运行管理资源(依次释放Context、Device)。
ret = acl.rt.destroy_context(context)
ret = acl.rt.reset_device(0)
## 10.pyACL去初始化。
ret = acl.finalize()
抠图示例代码
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝运行,仅供参考。
## 1.获取软件栈的运行模式,不同运行模式影响后续的接口调用流程(例如是否进行数据传输等)。
run_mode, ret = acl.rt.get_run_mode()
## 2.pyACL 初始化。
ret = acl.init()
## 3.运行管理资源申请(依次申请Device、Context)。
ret = acl.rt.set_device(0)
context, ret = acl.rt.create_context(0)
## 4.初始化媒体数据处理系统。
ret = acl.himpi.sys_init()
## 5.创建通道。
channel_id = 0
chn_attr = {'attr': 0, 'pic_width': 0, 'pic_height': 0}
ret = acl.himpi.vpc_create_chn(channel_id, chn_attr)
## 6.执行抠图。
## 6.1 构造存放输入图片信息的字典。
input_pic = {'picture_width': 1920,
'picture_height': 1080,
'picture_width_stride': 1920,
'picture_height_stride': 1080,
'picture_format': HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420}
input_pic["picture_buffer_size"] = input_pic["picture_width_stride"] * input_pic["picture_height_stride"] * 3 // 2
picture_address, ret = acl.himpi.dvpp_malloc(0, input_pic["picture_buffer_size"])
input_pic["picture_address"] = picture_address
## 如果运行模式为ACL_HOST,则需要申请Host内存,将输入图片数据读入Host内存,再通过acl.rt.memcpy接口将Host的图片数据传输到Device,数据传输完成后,需及时释放Host内存;否则直接将输入图片数据读入Device内存。
## 直接将输入图片数据读入Device内存。
if run_mode == ACL_HOST:
# 将输入图片读入内存中。
vpc_file = np.fromfile(vpc_file_path, dtype=np.byte)
vpc_file_size = vpc_file.itemsize * vpc_file.size
bytes_data = vpc_file.tobytes()
vpc_file_ptr = acl.util.bytes_to_ptr(bytes_data)
# 数据传输。
ret = acl.rt.memcpy(input_pic["picture_address"], input_pic["picture_buffer_size"],
vpc_file_ptr, vpc_file_size, ACL_MEMCPY_HOST_TO_DEVICE)
else:
# 将输入图片读入内存中。
vpc_file = np.fromfile(vpc_file_path, dtype=np.byte)
vpc_file_size = vpc_file.itemsize * vpc_file.size
bytes_data = vpc_file.tobytes()
vpc_file_ptr = acl.util.bytes_to_ptr(bytes_data)
# 数据传输。
ret = acl.rt.memcpy(input_addr, input_size, vpc_file_ptr, vpc_file_size, ACL_MEMCPY_DEVICE_TO_DEVICE)
## 6.3 构造存放输出图片信息的字典。
multithreading_count = 1
crop_region_infos = []
for i in range(multithreading_count):
output_pic = {'picture_width': 960,
'picture_height': 540,
'picture_width_stride': 960,
'picture_height_stride': 540,
'picture_format': HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420}
output_pic["picture_buffer_size"] = output_pic["picture_width_stride"] * output_pic["picture_height_stride"] * 3 // 2
picture_address, ret = acl.himpi.dvpp_malloc(0, output_pic["picture_buffer_size"])
output_pic["picture_address"] = picture_address
ret = acl.rt.memset(output_pic["picture_address"], output_pic["picture_buffer_size"], 0, output_pic["picture_buffer_size"])
crop_region = {'top_offset': 0, 'left_offset': 0, 'crop_width': 960, 'crop_height': 540}
crop_region_info = {'dest_pic_info': output_pic, 'crop_region': crop_region}
crop_region_infos.append(crop_region_info)
## 6.4 调用抠图接口。
task_id, ret = acl.himpi.vpc_crop(channel_id, input_pic, crop_region_infos, 1, -1)
## 6.5 等待任务处理结束,任务处理结束后,输出图片数据在outputPic.picture_address指向的内存中。
ret = acl.himpi.vpc_get_process_result(channel_id, task_id, -1)
## 6.6 如果运行模式为ACL_HOST,且Host上需要展示VPC输出的图片数据,则需要申请Host内存,通过acl.rt.memcpy接口将Device的输出图片数据传输到Host;如果Host上不需要展示VPC输出的图片数据,则VPC的输出图片数据可以直接作为模型推理的输入。
## 6.6 VPC的输出图片数据可以直接作为模型推理的输入。
if run_mode == ACL_HOST:
np_output = np.zeros(output_pic.get('picture_buffer_size'), dtype=np.byte)
bytes_data = np_output.tobytes()
np_output_ptr = acl.util.bytes_to_ptr(bytes_data)
ret = acl.rt.memcpy(np_output_ptr, output_pic.get('picture_buffer_size'), output_pic.get("picture_address"),
output_pic.get('picture_buffer_size'), ACL_MEMCPY_DEVICE_TO_HOST)
# ......
else
# 可以直接使用VPC的输出图片数据,在outputPic.picture_address指向的内存中。
# ......
}
## 6.7 释放输入、输出内存。
ret = acl.himpi.dvpp_free(input_pic['picture_address'])
ret = acl.himpi.dvpp_free(output_pic['picture_address']))
## 7.销毁通道。
ret = acl.himpi.vpc_destroy_chn(channel_id)
## 8. 媒体数据处理系统去初始化。
ret = acl.himpi.sys_exit()
## 9. 释放运行管理资源(依次释放Context、Device)。
ret = acl.rt.destroy_context(context)
ret = acl.rt.reset_device(0)
## 10.pyACL去初始化。
ret = acl.finalize()
父主题: 媒体数据处理V2
JPEGD图片解码
JPEGD(JPEG Decoder)负责完成图像解码功能,将.jpg、.jpeg、.JPG、.JPEG图片解码成YUV格式图片。关于JPEGD功能的详细介绍及约束请参见JPEGD功能及约束说明。
本节介绍JPEGD图片解码的接口调用流程,同时配合示例代码辅助理解该接口调用流程。
接口调用流程
开发应用时,如果涉及对JPEG图片的解码,则应用程序中必须包含解码的代码逻辑,关于图片解码的接口调用流程,请先参见pyACL接口调用流程了解整体流程,再查看本节中的流程说明。
图1 接口调用的流程

当前系统支持解码JPEG图片,关键接口的说明如下:
-
调用acl.himpi.sys_init接口进行媒体数据处理系统初始化。
-
调用acl.himpi.vdec_create_chn接口创建通道。
-
调用acl.himpi.dvpp_malloc接口申请Device上的内存,存放输入或输出数据。
-
解码前,需调用acl.himpi.vdec_start_recv_stream接口通知解码器启动接收码流,再调用acl.himpi.vdec_send_stream接口发送解码码流,acl.himpi.vdec_send_stream接口是异步接口,调用该接口仅表示任务下发成功,还需要调acl.himpi.vdec_get_frame接口获取解码结果数据,成功获取解码数据后,可以调用acl.himpi.vdec_release_frame接口释放帧相关的资源。
解码结束后,需调用acl.himpi.vdec_stop_recv_stream接口通知解码器停止接收码流。
-
调用acl.himpi.dvpp_free接口释放输入、输出内存。
-
调用acl.himpi.vdec_destroy_chn接口销毁通道。
-
调用acl.himpi.sys_exit接口进行媒体数据处理系统去初始化。
示例代码
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝运行,仅供参考。
## 1.获取软件栈的运行模式,不同运行模式影响后续的接口调用流程(例如是否进行数据传输等)。
run_mode, ret = acl.rt.get_run_mode()
## 2.pyACL 初始化。
ret = acl.init()
## 3.运行管理资源申请(依次申请Device、Context)。
ret = acl.rt.set_device(0)
context, ret = acl.rt.create_context(0)
## 4.初始化媒体数据处理系统。
ret = acl.himpi.sys_init()
## 5.创建通道。
channel_id = 0
attr = {'type': HI_PT_JPEG, 'mode': HI_VDEC_SEND_MODE_FRAME,
'pic_width': 1920, 'pic_height': 1080,
'stream_buf_size': 1920 * 1080, 'frame_buf_size': 0,
'frame_buf_cnt': 9}
ret = acl.himpi.vdec_create_chn(channel_id, attr)
## 6.设置通道属性。
jpegd_param_dict, ret = acl.himpi.vdec_get_chn_param(channel_id)
jpegd_param_dict["pic_param"]["pixel_format"] = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420
jpegd_param_dict["pic_param"]["alpha"] = 255
jpegd_param_dict["display_frame_num"] = 0
ret = acl.himpi.vdec_set_chn_param(i, jpegd_param_dict)
## 7.解码器启动接收码流。
ret = acl.himpi.vdec_start_recv_stream(channel_id)
## 8.发送码流。
## 8.1 申请输入内存。
## input_size表示输入图片占用的内存大小,此处以1024 Byte为例,用户需根据实际情况计算内存大小。
input_size = 1024;
input_addr, ret = acl.himpi.dvpp_malloc(0, input_size);
#如果运行模式为ACL_HOST,则需要申请Host内存,将输入图片数据读入Host内存,再通过acl.rt.memcpy接口将Host的图片数据传输到Device,数据传输完成后,需及时释放Host内存;否则直接将输入图片数据读入Device内存。
#直接将输入图片数据读入Device内存。
if run_mode == ACL_HOST:
# 申请Host内存。
input_buffer, ret= acl.rt.malloc_host(input_size)
# 将输入图片读入内存中。
vdec_file = np.fromfile(vdec_file_path, dtype=np.byte)
vdec_file_size = vdec_file.itemsize * vdec_file.size
bytes_data = vdec_file.tobytes()
vdec_file_ptr = acl.util.bytes_to_ptr(bytes_data)
# 数据传输。
ret = acl.rt.memcpy(input_addr, input_size, vdec_file_ptr, vdec_file_size, ACL_MEMCPY_HOST_TO_DEVICE)
else:
# 将输入图片读入内存中。
vdec_file = np.fromfile(vdec_file_path, dtype=np.byte)
vdec_file_size = vdec_file.itemsize * vdec_file.size
bytes_data = vdec_file.tobytes()
vdec_file_ptr = acl.util.bytes_to_ptr(bytes_data)
# 数据传输。
ret = acl.rt.memcpy(input_addr, input_size, vdec_file_ptr, vdec_file_size, ACL_MEMCPY_DEVICE_TO_DEVICE)
## 8.2 构造存放输入图片信息的字典。
stream = {'end_of_frame': HI_TRUE, 'end_of_stream': HI_FALSE,
'need_display': HI_TRUE, 'pts': 0,
'len': input_size, 'addr': vdec_file_ptr}
img_info, ret = acl.himpi.dvpp_get_image_info(HI_PT_JPEG, stream)
if run_mode == ACL_HOST:
# 如果不使用Host上的数据,需及时释放。
ret = acl.rt.free_host(input_buffer)
stream['addr'] = input_addr
## 8.3 构造存放输出图片信息的字典,并申请输出内存。
out_pic_info = {"width": img_info['width'],
"height": img_info['height'],
"width_stride": img_info['width_stride'],
"height_stride": img_info['height_stride'],
"pixel_format": HI_PIXEL_FORMAT_UNKNOWN,
"buffer_size": img_info['img_buf_size']}
out_buffer, ret = acl.himpi.dvpp_malloc(0, out_pic_info['buffer_size'])
out_pic_info['vir_addr '] = out_buffer
## 8.4 发送需解码的输入图片。
ret = acl.himpi.vdec_send_stream(channel_id, stream, out_pic_info, 0)
## 9.接收解码结果。
## 9.1 获取解码结果。
frame_info, supplement, stream, ret = acl.himpi.vdec_get_frame(channel_id, 0)
if ret == 0:
dec_result = frame_info['v_frame']['frame_flag']
if dec_result == 0: # 0: Decode success
print("Chn %u GetFrame Success, Decode Success \n"%channel_id)
else: # Decode fail
print("Chn %u GetFrame Success, Decode Fail \n"%channel_id)
## 9.2 如果运行模式为ACL_HOST,且Host上需要展示JPEGD输出的图片数据,则需要申请Host内存,通过aclrtMemcpy接口将Device的输出图片数据传输到Host。
## 9.2 获取JPEGD的输出图片数据。
if run_mode == ACL_HOST:
# 申请Host内存。
output_buffer, ret= acl.rt.malloc_host(out_pic_info['buffer_size'])
# 数据传输。
ret = acl.rt.memcpy(output_buffer, out_pic_info['buffer_size'], frame_info['v_frame']['virt_addr'][0], out_pic_info['buffer_size'], ACL_MEMCPY_DEVICE_TO_HOST)
# ......
# 数据使用完成后,及时释放不使用的内存。
ret = acl.rt.free_host(output_buffer)
else:
# 可以直接使用JPEGD的输出图片数据,在outputPic.picture_address指向的内存中。
# ......
## 9.3 释放输入、输出内存。
ret = acl.himpi.dvpp_free(frame_info['v_frame']['virt_addr'][0])
ret = acl.himpi.dvpp_free(stream['addr'])
## 9.4 释放资源。
ret = acl.himpi.vdec_release_frame(channel_id, frame_info)
## 10.解码器停止接收码流。
ret = acl.himpi.vdec_stop_recv_stream(channel_id)
## 7.销毁通道。
ret = acl.himpi.vdec_destroy_chn(channel_id)
## 8. 媒体数据处理系统去初始化。
ret = acl.himpi.sys_exit()
## 9. 释放运行管理资源(依次释放Context、Device)。
ret = acl.rt.destroy_context(context)
ret = acl.rt.reset_device(0)
## 10.pyACL去初始化。
ret = acl.finalize()
父主题: 媒体数据处理V2
JPEGE图片编码
JPEGE(JPEG Encoder)负责完成图像编码功能,将YUV格式图片编码成.jpg图片。关于JPEGE功能的详细介绍请参见JPEGE功能及约束说明。
本节介绍JPEGE图片编码的接口调用流程,同时配合示例代码辅助理解该接口调用流程。
接口调用流程
开发应用时,如果涉及将YUV格式图片编码成JPEG压缩格式的图片文件,则应用程序中必须包含编码的代码逻辑,关于编码的接口调用流程,请先参见pyACL接口调用流程了解整体流程,再查看本节中的流程说明。
图1 接口调用流程

当前系统支持将YUV格式图片编码成JPEG压缩格式的图片文件,关键接口的说明如下:
-
调用acl.himpi.sys_init接口进行媒体数据处理系统初始化。
-
调用acl.himpi.venc_create_chn函数创建完通道。
成功创建通道之后,您可以根据实际需求设置编码的高级参数,例如场景模式、码流控制器的高级参数等,请参见acl.himpi.venc_set_jpeg_param~acl.himpi.venc_compact_jpeg_tables章节中的接口说明。
-
调用acl.himpi.venc_get_fd将通道ID转换为一个文件句柄。
-
调用acl.himpi.sys_create_epoll函数创建DVPP epoll实例。
-
调用acl.himpi.sys_ctl_epoll函数将编码通道的文件句柄添加到epoll实例中,由epoll实例处理。
select或者poll方式,不需要执行该步骤。
-
调用acl.himpi.venc_start_chn函数通知通道准备开始编码。
-
调用acl.himpi.dvpp_malloc接口申请存放Device上输入数据的内存。
-
启动一个用户态线程,调用acl.himpi.sys_wait_epoll函数等待编码完成。
-
之后用户就可以调用acl.himpi.venc_send_frame函数发送待编码的码流。
-
一旦编码完成,acl.himpi.sys_wait_epoll函数或select函数或poll函数就会返回,用户就可以调用acl.himpi.venc_query_status接口查询编码状态,再调用acl.himpi.venc_get_stream函数获取编码结果。
-
用户需要注意的是,编码结果数据使用完成之后,需要及时调用acl.himpi.venc_release_stream函数释放buffer。否则会因编码buffer用完导致后续编码无法进行。
-
调用acl.himpi.dvpp_free接口释放输入内存。
-
当用户不需发送图像到目的通道继续编码时,需要调用acl.himpi.venc_stop_chn函数通知该通道不再接收新的输入图片。
-
调用acl.himpi.sys_ctl_epoll函数从epoll实例中删除编码通道的文件句柄。
-
当用户完成所有编码之后,需要调用acl.himpi.venc_destroy_chn释放编码通道以及内部内存资源。
-
调用acl.himpi.sys_close_epoll函数销毁DVPP epoll实例。
-
调用acl.himpi.sys_exit接口进行媒体数据处理系统去初始化。
示例代码
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝运行,仅供参考。
## 1.获取软件栈的运行模式,不同运行模式影响后续的接口调用流程(例如是否进行数据传输等)。
run_mode, ret = acl.rt.get_run_mode()
## 2.pyACL 初始化。
ret = acl.init()
## 3.运行管理资源申请(依次申请Device、Context)。
ret = acl.rt.set_device(0)
context, ret = acl.rt.create_context(0)
## 4.初始化媒体数据处理系统。
ret = acl.himpi.sys_init()
## 5.创建通道。
channel_id = 0
venc_attr = {'type': HI_PT_JPEG, 'profile': 0,
'max_pic_width': 128, 'pic_width': 128,
'max_pic_height': 128, 'pic_height': 128,
'buf_size': 1024 * 1024 * 2, 'is_by_frame': 1}
jpeg_attr = {'recv_mode':HI_VENC_PIC_RECV_SINGLE,
'mpf_cfg':{'large_thumbnail_num ': 0}}
attr = {'venc_attr':venc_attr, 'jpeg_attr':jpeg_attr}
ret = acl.himpi.venc_create_chn(channel_id, attr)
## 6.设置JPEGE参数。
param, ret = acl.himpi.venc_get_jpeg_param(channel_id)
param['qfactor'] = 100
ret = acl.himpi.venc_set_jpeg_param(channel_id, param)
## 7.通知编码器开始接收输入数据。
recv_param = {'recv_pic_num': -1}
ret = acl.himpi.venc_start_chn(channel_id, recv_param)
## 8.发送输入数据。
## 8.1 申请输入内存。
input_size = 128 * 128 * 3 // 2
input_addr, ret = acl.himpi.dvpp_malloc(0, input_size);
## 如果运行模式为ACL_HOST,则需要申请Host内存,将输入图片数据读入Host内存,再通过acl.rt.memcpy接口将Host的图片数据传输到Device,数据传输完成后,需及时释放Host内存;否则直接将输入图片数据读入Device内存。
## 直接将输入图片数据读入Device内存。
if run_mode == ACL_HOST:
# 将输入图片读入内存中。
jpege_file = np.fromfile(jpege_filee_path, dtype=np.byte)
jpege_file_size = jpege_file.itemsize * jpege_file.size
bytes_data = jpege_file.tobytes()
jpege_file_ptr = acl.util.bytes_to_ptr(bytes_data)
# 数据传输。
ret = acl.rt.memcpy(input_addr, input_size, jpege_file_ptr, jpege_file_size, ACL_MEMCPY_HOST_TO_DEVICE)
else:
# 将输入图片读入内存中。
jpege_file = np.fromfile(jpege_file_path, dtype=np.byte)
jpege_file = jpege_file.itemsize * jpege_file.size
bytes_data = jpege_file.tobytes()
jpege_file_ptr = acl.util.bytes_to_ptr(bytes_data)
# 数据传输。
ret = acl.rt.memcpy(input_addr, input_size, jpege_file_ptr, jpege_file_size, ACL_MEMCPY_DEVICE_TO_DEVICE)
## 8.2 发送输入数据,开始编码。
v_frame = {'width': 128,
'height': 128,
'field': HI_VIDEO_FIELD_FRAME,
'pixel_format': HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420,
'video_format': HI_VIDEO_FORMAT_LINEAR,
'compress_mode': HI_COMPRESS_MODE_NONE,
'dynamic_range': HI_DYNAMIC_RANGE_SDR8,
'color_gamut': HI_COLOR_GAMUT_BT709,
'header_stride': [0, 0, 0],
'width_stride': [128, 0, 0],
'height_stride': [0, 0, 0],
'header_phys_addr': [0, 0, 0],
'phys_addr': [0, 0, 0],
'header_virt_addr': [0, 0, 0],
'virt_addr': [input_addr, 0, 0],
'time_ref': 0,'pts': cur_time}
frame = {'v_frame':v_frame, 'pool_id':0, 'mod_id':HI_ID_VGS}
ret = acl.himpi.venc_send_frame(channel_id, frame, 0)
## 9.获取编码结果。
## 9.1 通过EPOLL处理编码完成事件。
fd = acl.himpi.venc_get_fd(channel_id)
epoll_fd, ret = acl.himpi.sys_create_epoll(10)
event['data'] = fd
event['events'] = HI_DVPP_EPOLL_IN
ret = acl.himpi.sys_ctl_epoll(epoll_fd, HI_DVPP_EPOLL_CTL_ADD, fd, event)
## 编码完成前,会超时阻塞在这里,一旦完成,才会往下执行。
events, eventCount, ret = acl.himpi.sys_wait_epoll(epoll_fd, 3, 1000);
## 9.2 获取编码结果。
status, ret = acl.himpi.venc_query_status(channel_id)
stream = {'pack_cnt': status['cur_packs']}
stream, ret = acl.himpi.venc_get_stream(self.channel_id, stream, 1000)
## 9.3 如果运行模式为ACL_HOST,且Host上需要使用编码输出的码流,则需要申请Host内存,通过acl.rt.memcpy接口将Device的输出码流传输到Host;否则直接使用编码输出码流数据。
## 9.3 获取编码输出码流数据。
if run_mode == ACL_HOST:
# 申请Host内存。
output_buffer, ret= acl.rt.malloc_host(output_ize)
# 数据传输。
ret = acl.rt.memcpy(output_buffer, output_ize, stream['pack'][0]['addr'], output_ize, ACL_MEMCPY_DEVICE_TO_HOST)
# ......
# 数据使用完成后,及时释放不使用的内存。
ret = acl.rt.free_host(output_buffer)
else:
# 可以直接使用编码输出码流数据,在stream['pack'][0]['addr']指向的内存中。
# ......
## 10.释放输入内存和输出码流。
ret = acl.himpi.dvpp_free(input_addr)
ret = acl.himpi.venc_release_stream(channel_id, stream)
## 11.通知编码器停止接收输入数据。
ret = acl.himpi.venc_stop_chn(channel_id)
ret = acl.himpi.sys_ctl_epoll(epoll_fd, HI_DVPP_EPOLL_CTL_DEL, fd, event)
ret = acl.himpi.sys_close_epoll(epoll_fd)
## 12.销毁通道。
ret = acl.himpi.venc_destroy_chn(channel_id)
## 13. 媒体数据处理系统去初始化。
ret = acl.himpi.sys_exit()
## 14. 释放运行管理资源(依次释放Context、Device)。
ret = acl.rt.destroy_context(context)
ret = acl.rt.reset_device(0)
## 15.pyACL去初始化。
ret = acl.finalize()
父主题: 媒体数据处理V2
在线提单