音频获取&音频播放功能
视频解码、处理和显示功能(NVR场景)
NVR视频场景说明
NVR,全称Network Video Recorder,即网络视频录像机,是网络视频系统的存储转发部分,NVR与网络摄像机协同工作,完成音频&视频的录像、存储及转发功能,同时,NVR具备本地人机交互界面、视频解码、视频显示及语音对讲功能。
本章节描述NVR视频业务处理流程,包括下面两种:
-
视频解码显示流程涉及视频解码模块(VDEC)、视频处理模块(VPSS)、视频输出模块(VO)、HDMI接口模块, 以及增强的功能,例如Region区域管理功能、TDE图形绘制功能、HIFB叠加图形层管理功能。
在该流程中,通过调用hi_mpi_sys_bind接口将VDEC通道与VPSS组绑定、将VPSS组与VO设备绑定,实现数据从VDEC到VPSS、从VPSS到VO的自动传输。通过底层寄存器,实现VO自动读取HIFB的输出数据。
若TDE、HIFB与VO不在一个应用进程中,则需要确保先启动VO所在的进程,且TDE和HIFB所在的进程中也需要调用hi_mpi_sys_init接口初始化媒体数据处理系统、并在进程退出前调用hi_mpi_sys_exit接口实现媒体数据处理系统去初始化。
-
视频解码智能分析流程涉及视频解码模块(VDEC)、视频处理模块(VPSS)、智能分析(模型推理), 以及增强的功能,例如REGION区域管理模块。区域管理功能的接口调用流程请参见Region区域管理功能。

视频解码显示流程
视频解码显示流程涉及视频解码模块(VDEC)、视频处理模块(VPSS)、视频输出模块(VO)、HDMI接口模块, 以及增强的功能,例如Region区域管理功能、TDE图形绘制功能、HIFB叠加图形层管理功能。
图1 业务启动、运行接口调用流程

NVR视频解码显示业务启动、运行接口调用流程说明如下:
-
调用hi_mpi_sys_init接口完成媒体系统初始化。
-
创建VDEC视频****解码通道(按需创建多个通道),并通知解码器启动接收码流。
- 调用hi_mpi_vdec_create_chn接口创建通道。
- 调用hi_mpi_vdec_get_chn_param接口获取通道属性、按需设置通道属性后,调用hi_mpi_vdec_set_chn_param接口设置通道参数。
- 解码前,需调用hi_mpi_vdec_start_recv_stream接口通知解码器启动接收码流。
-
创建并启用VPSS组(按需创建多个VPSS组)。
- 调用hi_mpi_vpss_create_grp接口创建VPSS组
- 调用hi_mpi_vpss_start_grp接口启用VPSS组。
- 调用hi_mpi_vpss_set_chn_attr接口设置通道属性,在和VO模块配合工作时,通道属性需要设置为Auto模式。
- 调用hi_mpi_vpss_enable_chn接口启动VPSS通道。
-
调用hi_mpi_sys_bind接口绑定视频解码通道和VPSS组,经过VDEC视频解码的输出数据直接被送入对应的VPSS组继续处理。
-
以单个显示器输出4路视频显示为例,绑定信息如下表所示:
VDEC通道
VPSS Group
VO通道
VO视频层
VO设备号
HDMI编号
1
1
1
视频层
VHD0
0
HDMI0
2
2
2
3
3
3
4
4
4
-
以两个显示器输出、每个显示器输出4路视频显示为例,绑定信息如下表所示:
VDEC通道
VPSS Group
VO通道
VO视频层
VO设备号
HDMI编号
1
1
1
视频层
VHD0
0
HDMI0
2
2
2
3
3
3
4
4
4
5
5
5
视频层
VHD1
1
HDMI1
6
6
6
7
7
7
8
8
8
-
-
启用VO设备和VO通道(按需创建多个VO通道)。
- 调用hi_mpi_vo_set_pub_attr配置显示设备属性,通过hi_mpi_vo_enable使能显示设备。
- 调用hi_mpi_vo_set_video_layer_attr配置显示视频层属性,通过hi_mpi_vo_enable_video_layer使能显示视频层。
- 调用hi_mpi_vo_set_chn_attr配置显示通道属性,通过hi_mpi_vo_enable_chn使能显示通道。
-
初始化HDMI外设。
- 调用hi_mpi_hdmi_init初始化HDMI设备,调用hi_mpi_hdmi_open打开HDMI。
- 调用hi_mpi_hdmi_set_attr配置HDMI属性。
- 调用hi_mpi_hdmi_start启动HDMI外设,以便显示视频。
-
调用hi_mpi_sys_bind接口绑定VPSS组和VO通道,经过VPSS处理后的输出数据直接被送入对应的VO通道继续处理。
绑定信息请参见4。
-
循环调用hi_mpi_vdec_send_stream接口,发送每一帧解码码流。
注意:
- 在视频解码通道和VPSS组绑定后,用户调用hi_mpi_vdec_send_stream发送码流时,接口参数中的vdec_pic_info可以设置为NULL, 此时的视频解码通道和VPSS模块绑定(参见4),解码结果数据直接被送入对应的VPSS组继续处理,不支持通过hi_mpi_vdec_get_frame接口获取解码结果数据。
- 视频解码支持两种模式,即回放模式和预览模式,可以通过hi_mpi_vdec_get_display_mode和hi_mpi_vdec_set_display_mode接口进行查询和设置,对于录像播放需要使用回放模式,此时支持播放控制。
图2 资源释放接口调用流程

NVR视频解码显示业务资源释放接口调用流程说明如下:
- 调用hi_mpi_sys_unbind接口取消VO通道与VPSS组的绑定。
- 调用hi_mpi_sys_unbind接口取消VPSS组与VDEC通道的绑定。
- 释放HDMI外设资源。
- 首先调用hi_mpi_hdmi_stop停止HDMI。
- 调用hi_mpi_hdmi_close关闭HDMI。
- 调用hi_mpi_hdmi_deinit去初始化HDMI设备。
- 释放VO设备、通道资源。
- 调用hi_mpi_vo_disable_chn禁用通道。
- 调用hi_mpi_vo_disable_video_layer禁用视频层。
- 调用hi_mpi_vo_disable禁用显示设备。
- 销毁VPSS组。
- 调用hi_mpi_vpss_disable_chn接口关闭VPSS通道。
- 调用hi_mpi_vpss_stop_grp接口停止VPSS组。
- 调用hi_mpi_vpss_destroy_grp接口销毁VPSS组。
- 销毁VDEC视频解码通道。
- 调用hi_mpi_vdec_stop_recv_stream接口通知解码器停止接收码流。
- 调用hi_mpi_vdec_destroy_chn接口销毁通道。
- 调用hi_mpi_sys_exit接口完成媒体数据处理系统去初始化。
视频解码智能分析流程
视频解码智能分析流程涉及视频解码模块(VDEC)、视频处理模块(VPSS)、智能分析(模型推理), 以及增强的功能,例如REGION区域管理模块。区域管理功能的接口调用流程请参见Region区域管理功能。
图3 业务启动、运行接口调用流程

视频解码智能分析业务启动、运行流程:
-
调用hi_mpi_sys_init接口完成媒体系统初始化。
-
创建VDEC视频解码通道(按需创建多个通道),并通知解码器启动接收码流。
- 调用hi_mpi_vdec_create_chn接口创建通道。
- 调用hi_mpi_vdec_get_chn_param接口获取通道属性、按需设置通道属性后,调用hi_mpi_vdec_set_chn_param接口设置通道参数。
- 解码前,需调用hi_mpi_vdec_start_recv_stream接口通知解码器启动接收码流。
-
创建并启用VPSS组(按需创建多个VPSS组)。
- 调用hi_mpi_vpss_create_grp接口创建VPSS组
- 调用hi_mpi_vpss_start_grp接口启用VPSS组。
- 调用hi_mpi_vpss_set_chn_attr接口设置通道属性,通道属性需要设置为User模式。
- 调用hi_mpi_vpss_enable_chn接口启动VPSS通道。
-
调用hi_mpi_sys_bind接口绑定视频解码通道和VPSS组,经过VDEC视频解码的输出数据直接被送入对应的VPSS组继续处理。
绑定信息请参见4。
-
循环调用hi_mpi_vdec_send_stream接口,发送每一帧解码码流。
注意:
在视频解码通道和VPSS组绑定后,用户调用hi_mpi_vdec_send_stream发送码流时,接口参数中的vdec_pic_info可以设置为NULL, 此时的视频解码通道和VPSS模块绑定(参见4),解码结果数据直接被送入对应的VPSS组继续处理,不支持通过hi_mpi_vdec_get_frame接口获取解码结果数据。
-
调用hi_mpi_vpss_get_chn_frame接口获取VPSS处理后的图像数据,可送入模型推理(参见基础推理应用),推理结束后,最后调用hi_mpi_vpss_release_chn_frame释放一帧通道图像。
注意:
在多通道时,用户可调用hi_mpi_vpss_get_chn_fd接口获取VPSS指定通道的句柄,并通过调用epoll接口(参考hi_mpi_sys_create_epoll相关接口)等待VPSS处理结果。VPSS处理完后,会自动唤醒epoll等待,此时可调用hi_mpi_vpss_get_chn_frame接口获取VPSS处理后的图像数据。
VPSS处理后的图像,在送入模型推理前,若图片尺寸、格式等不满足要求,需要经过DVPP的VPC功能模块、AIPP功能进一步处理,请参见媒体数据处理基础知识中关于VPC、AIPP的介绍。
图4 资源释放接口调用流程

NVR视频解码智能分析业务资源释放接口调用流程说明如下:
- 调用hi_mpi_sys_unbind接口取消VPSS组与VDEC通道的绑定。
- 销毁VPSS组。
- 调用hi_mpi_vpss_disable_chn接口关闭VPSS通道。
- 调用hi_mpi_vpss_stop_grp接口停止VPSS组。
- 调用hi_mpi_vpss_destroy_grp接口销毁VPSS组。
- 销毁VDEC视频解码通道。
- 调用hi_mpi_vdec_stop_recv_stream接口通知解码器停止接收码流。
- 调用hi_mpi_vdec_destroy_chn接口销毁通道。
- 调用hi_mpi_sys_exit接口完成媒体数据处理系统去初始化。
Region区域管理功能
叠加在视频上的OSD (On Screen Display)和遮挡在视频上的色块统称为区域。区域管理模块,用于统一管理这些区域资源,用于在视频上显示一些特定信息(如通道号、时间戳等)、或在视频中填充色块用于遮挡。
区域管理功能(REGION)必须配合VPSS模块一起使用,且区域管理功能需关联的VPSS组、VPSS通道已创建,接口调用流程如下所示。

- 初始化:
- 调用hi_mpi_rgn_create创建区域。
- 调用hi_mpi_rgn_attach_to_chn接口将区域叠加到VPSS通道上。
- 按需修改区域信息:
- 设置区域通道显示属性:
- 调用hi_mpi_rgn_get_display_attr接口获取区域当前的通道显示属性。
- 修改通道显示属性结构体重参数,调用hi_mpi_rgn_set_display_attr接口设置区域的通道显示属性。
- 设置区域的显示画布信息:
- 调用hi_mpi_rgn_get_canvas_info接口获取当前区域的显示画布信息。
- 调用hi_mpi_rgn_update_canvas接口更新显示画布信息。
- 设置区域通道显示属性:
- 资源释放:
- 调用hi_mpi_rgn_detach_from_chn接口将指定区域从VPSS通道中删除。
- 调用hi_mpi_rgn_destroy接口销毁区域。
TDE图形绘制功能
TDE是图形二维加速引擎,它利用硬件为 OSD(On Screen Display)和 GUI(Graphics User Interface)提供快速的图形绘制功能,主要有快速拷贝、快速色彩填充、模式填充(当前仅支持Alpha Blending操作)。
图5 TDE接口调用流程

-
调用hi_tde_open接口打开TDE设备。
-
调用hi_tde_begin_job接口创建TDE任务。
-
调用各命令执行接口,例如hi_tde_quick_copy、hi_tde_quick_fill、hi_tde_pattern_fill。
调用命令执行接口前,需先调用HIFB提供的**int ioctl (int fd, FBIOGET_FSCREENINFO, fb_fix_screeninfo *fix)**接口获取显存用户态地址,作为目标位图的内存地址,TDE任务执行完成后,目标位图的数据会存放在该内存地址中,作为HIFB的输入数据。
-
若添加命令失败,则调用hi_tde_cancel_job接口取消任务;若添加命令成功,则调用hi_tde_end_job接口提交任务。
-
等待TDE任务完成。
目前有等待指定TDE任务执行完成(调用hi_tde_wait_for_done接口)、等待当前TDE设备上所有任务执行完成(调用hi_tde_wait_all_done接口)两种方式。
HIFB叠加图形层管理功能
HIFB用于管理叠加图形层,它不仅提供Linux Framebuffer的基本功能,还在Linux Framebuffer的基础上增加图层显示起始位置修改、层间Alpha等扩展功能。
图6 HIFB接口调用流程

-
通过系统调用open打开fb设备。设备文件fb0
fb4对应图层G0G4。其中,G0和G1为高清图层、G2为鼠标图层、G3和G4为标清图层。各图层对应的fb设备、VO设备、支持的颜色格式、分辨率等说明,请参见表1。
-
通过系统调用ioctl,传入命令码FBIOPUT_VSCREENINFO设置屏幕基本属性。
-
通过系统调用ioctl,传入命令码FBIOGET_FSCREENINFO获取显存用户态地址。
-
通过系统调用mmap映射用户态虚拟地址。
-
通过系统调用ioctl,传入功能相关的命令码设置功能属性。
-
通过系统调用munmap解除用户态虚拟地址映射。
-
通过系统调用close关闭fb设备。
如果HIFB与VO在同一个进程中配合使用,则需要在禁用VO设备(即调用hi_mpi_vo_disable)后关闭fb设备。
如果HIFB与VO不在同一个进程中,则需要先停VO所在的应用进程。
父主题: 媒体数据处理V2
语音对讲功能(NVR场景)
NVR音频场景说明
NVR,全称Network Video Recorder,即网络视频录像机,是网络视频系统的存储转发部分,NVR与网络摄像机协同工作,完成音频&视频的录像、存储及转发功能,同时,NVR具备本地人机交互界面、视频解码、视频显示及语音对讲功能。
**本章节描述NVR音频业务(语音对讲功能)的接口调用流程。**在语音对讲功能中,包括媒体数据处理系统初始化&去初始化、接收IP Camera发送的语音并播放声音、录制声音并向IP Camera发送语音,涉及的模块包括公共模块、音频输入模块(AI),音频编码模块(AENC),音频输出模块(AO)、音频解码模块(ADEC)。

接收IP Camera发送的语音并播放声音
图1 接收IP Camera发送的语音****并播放声音

接口调用流程说明如下:
-
调用hi_mpi_adec_create_chn接口创建音频解码通道。
-
启用AO音频输出设备和通道:
-
调用hi_mpi_ao_set_pub_attr接口设置AO设备属性。
-
调用hi_mpi_ao_enable接口启动AO设备。
-
调用hi_mpi_ao_enable_chn接口启动AO通道
-
调用hi_mpi_ao_enable_resample接口启用AO重采样功能。
由于AO的采样率固定为48kHz,G.711a、G.711u协议的采样率仅支持8kHz,因此需启用重采样功能;而48kHz在AAC协议采样率支持的范围内,因此使用AAC协议时,在AO时无需重采样。
-
-
调用hi_mpi_sys_bind接口绑定ADEC与AO。
ADEC设备ID ADEC通道号 AO设备ID AO通道号 0 0 2 0 0 1 3 0 -
循环调用hi_mpi_adec_send_stream接口将每一帧待解码音频数据发送给解码器进行解码。
解码后的音频数据,根据3中的绑定关系,被自动发送到对应的AO设备,用于音频播放。
-
音频播放完成后,在退出流程中,先调用hi_mpi_sys_unbind接口解绑ADEC与AO,再依次调用hi_mpi_ao_disable_resample接口禁用AO重采样功能、调用hi_mpi_ao_disable_chn接口禁用AO通道、调用hi_mpi_ao_disable接口禁用AO设备,最后调用hi_mpi_adec_destroy_chn接口进行销毁ADEC通道。
录制声音并向IP Camera发送语音
图2 录制声音并向IP Camera发送语音

接口调用流程说明如下:
-
启用AI音频输入设备和通道:
-
调用hi_mpi_ai_set_pub_attr接口设置AI设备属性。
-
调用hi_mpi_ai_enable接口启动AI设备。
-
调用hi_mpi_ai_set_chn_attr接口设置AI通道属性。
-
调用hi_mpi_ai_enable_chn接口启动AI通道。
-
调用hi_mpi_ai_enable_resample接口启用AI重采样功能。
由于AI的采样率固定为48kHz,G.711a、G.711u协议的采样率仅支持8kHz,因此需启用重采样功能;而48kHz在AAC协议采样率支持的范围内,因此使用AAC协议时,在AI时无需重采样。
-
-
调用hi_mpi_aenc_create_chn接口创建音频编码通道。
-
调用hi_mpi_sys_bind接口绑定AI与AENC。
AI设备ID AI通道号 AENC设备ID AENC通道号 2 0 0 0 -
循环调用hi_mpi_aenc_get_stream获取编码数据,编码数据使用完成后,及时调用hi_mpi_aenc_release_stream接口释放编码数据。
经过AI设备获取到的音频数据,根据3中的绑定关系,被自动发送到对应的AENC通道进行编码,用于向IP Camera发送语音。
-
发送语音完成后,在退出流程中,先调用hi_mpi_sys_unbind接口解绑AI与AENC,再调用hi_mpi_aenc_destroy_chn接口进行销毁AENC通道,最后依次调用hi_mpi_ai_disable_resample接口禁用AI重采样功能、调用hi_mpi_ai_disable_chn接口禁用AI通道、调用hi_mpi_ai_disable接口禁用AI设备。
父主题: 媒体数据处理V2
音频获取&音频播放功能
音频获取功能

- 调用hi_mpi_sys_init接口初始化媒体公共模块。
- 调用hi_mpi_ai_set_pub_attr接口配置属性。
- 依次调用hi_mpi_ai_enable接口使能AI设备、调用hi_mpi_ai_enable_chn接口使能AI通道。
- 调用hi_mpi_ai_get_frame获取录音数据进行处理,之后调用hi_mpi_ai_release_frame释放音频帧,循环往复。
- AI采集音频结束时,先调用hi_mpi_ai_disable_chn接口禁用通道,然后调用hi_mpi_ai_disable接口禁用AI设备。
- 调用hi_mpi_sys_exit接口释放媒体公共模块的初始化资源。
音频播放功能

- 调用hi_mpi_sys_init接口初始化媒体公共模块。
- 调用hi_mpi_ao_set_pub_attr接口配置属性。
- 依次调用hi_mpi_ao_enable接口使能AO设备、调用hi_mpi_ao_enable_chn接口使能AO通道。
- 周期性的获取数据,并调用hi_mpi_ao_send_frame接口进行播音。
- AO播放音频结束时,先调用hi_mpi_ao_get_chn_delay接口获取AO通道中当前音频延时大小,延时为0后再调用hi_mpi_ao_disable_chn接口禁用通道,然后调用hi_mpi_ao_disable接口禁用AO设备。
- 调用hi_mpi_sys_exit接口释放媒体公共模块的初始化资源。
父主题: 媒体数据处理V2
VPC图片处理典型功能
VPC(Vision Preprocessing Core)负责图像处理功能,支持对图片做抠图、缩放、格式转换等操作。关于VPC功能的详细介绍请参见功能说明,关于VPC功能对输入、输出的约束要求,请参见约束说明。
本节以抠图、缩放为例说明VPC图像处理时的接口调用流程,同时配合以下典型功能的示例代码辅助理解该接口调用流程:
典型功能接口调用流程(以缩放为例)
开发应用时,如果涉及抠图、缩放等图片处理,则应用程序中必须包含图片处理的代码逻辑,关于图片处理的接口调用流程,请先参见AscendCL接口调用流程了解整体流程,再查看本节中的流程说明。
图1 主要接口调用流程

当前系统支持对输入图片做抠图、缩放等处理,关键接口的说明如下:
-
调用hi_mpi_sys_init接口进行媒体数据处理系统初始化。
-
调用hi_mpi_vpc_create_chn接口创建通道。
-
调用hi_mpi_dvpp_malloc接口申请Device上的内存,存放输入或输出数据。
Atlas 200/500 A2推理产品上,还支持使用aclrtMalloc接口申请内存。
调用hi_mpi_dvpp_malloc接口申请的内存为媒体数据处理的专用内存,但专用内存的地址空间有限,若关注内存规划或内存资源有限时,建议调用aclrtMalloc接口申请内存。
-
执行抠图、缩放等,此步骤以缩放为例说明,其它功能(例如格式转换、金字塔等)请参见VPC功能下的接口说明。
调用hi_mpi_vpc_resize接口,对图片进行缩放。hi_mpi_vpc_resize接口是异步接口,调用该接口成功仅表示任务下发成功,还需要调用hi_mpi_vpc_get_process_result接口等待任务完成。
- 可以跟hi_mpi_vpc_resize接口在同一个线程中调用hi_mpi_vpc_get_process_result接口,也可以新起一个线程调用hi_mpi_vpc_get_process_result接口,后者多线程并行,提高效率,但用户需自行实现线程间同步。
- 在实现抠图、缩放等功能时,通过将输入图片和输出图片的格式设置成不同的,达到转换图片格式的目的。
-
调用hi_mpi_dvpp_free接口释放输入、输出内存。
Atlas 200/500 A2推理产品上,若使用aclrtMalloc接口申请内存,则需使用aclrtFree接口释放内存。
-
调用hi_mpi_vpc_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> | <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_vpc_chn chnId;<br>hi_vpc_chn_attr stChnAttr;<br>ret = hi_mpi_vpc_sys_create_chn(&chnId, &stChnAttr);<br>// 5.执行缩放<br>// 5.1 构造存放输入图片信息的结构体<br>hi_vpc_pic_info inputPic;<br>inputPic.picture_width = 1920;<br>inputPic.picture_height = 1080;<br>inputPic.picture_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420;<br>inputPic.picture_width_stride = 1920;<br>inputPic.picture_height_stride = 1080;<br>inputPic.picture_buffer_size = inputPic.picture_width_stride * inputPic.picture_height_stride * 3 / 2;<br>// 5.2 准备输入图片数据<br>// 申请Device内存,用于媒体数据处理<br>ret = hi_mpi_dvpp_malloc(0, &inputPic.picture_address, inputPic.picture_buffer_size);<br>// 如果运行模式为ACL_HOST,则需要申请Host内存,将输入图片数据读入Host内存,再通过aclrtMemcpy接口将Host的图片数据传输到Device,数据传输完成后,需及时释放Host内存;否则直接将输入图片数据读入Device内存<br>if (runMode == ACL_HOST) {<br> void* inputAddr = nullptr;<br> // 申请Host内存<br> aclRet = aclrtMallocHost(&inputAddr, inputPic.picture_buffer_size);<br> // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现<br> ReadPicFile(picName, inputAddr, inputPic.picture_buffer_size);<br> // 数据传输<br> aclRet = aclrtMemcpy(inputPic.picture_address, inputPic.picture_buffer_size, inputAddr,<br> inputPic.picture_buffer_size, ACL_MEMCPY_HOST_TO_DEVICE);<br> // 完成数据传输后,需及时释放内存<br> aclrtFreeHost(inputAddr);<br> inputAddr = nullptr;<br>} <br>else {<br> // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现<br> ReadPicFile(picName, inputPic.picture_address, inputPic.picture_buffer_size);<br> }<br>// 5.3 构造存放输出图片信息的结构体<br>hi_vpc_pic_info outputPic;<br>outputPic.picture_width = 960;<br>outputPic.picture_height = 540;<br>outputPic.picture_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420;<br>outputPic.picture_width_stride = 960;<br>outputPic.picture_height_stride = 540;<br>outputPic.picture_buffer_size = outputPic.picture_width_stride * outputPic.picture_height_stride * 3 / 2;<br>ret = hi_mpi_dvpp_malloc(0, &outputPic.picture_address, outputPic.picture_buffer_size);<br>// 初始化内存<br>if (runMode == ACL_HOST) {<br> aclRet = aclrtMemset(outputPic.picture_address, outputPic.picture_buffer_size, 0, outputPic.picture_buffer_size);<br>} else {<br> memset(outputPic.picture_address, 0, outputPic.picture_buffer_size);<br>}<br>// 5.4 调用缩放接口<br>uint32_t taskID = 0;<br>ret = hi_mpi_vpc_resize(chnId, &inputPic, &outputPic, 0, 0, 0, &taskID, -1);<br>// 5.5 等待任务处理结束,任务处理结束后,输出图片数据在outputPic.picture_address指向的内存中<br>uint32_t taskIDResult = taskID;<br>ret = hi_mpi_vpc_get_process_result(chnId, taskIDResult, -1);<br>// 5.6 如果运行模式为ACL_HOST,且Host上需要展示VPC输出的图片数据,则需要申请Host内存,通过aclrtMemcpy接口将Device的输出图片数据传输到Host;如果Host上不需要展示VPC输出的图片数据,则VPC的输出图片数据可以直接作为模型推理的输入<br>if (g_runMode == ACL_HOST) {<br> hi_vpc_pic_info outputPicHost = outputPic;<br> aclRet = aclrtMallocHost(&outputPicHost.picture_address, outputPic.picture_buffer_size);<br> aclRet = aclrtMemcpy(outputPicHost.picture_address, outputPic.picture_buffer_size, outputPic.picture_address,outputPic.picture_buffer_size, ACL_MEMCPY_DEVICE_TO_HOST);<br> // ......<br> // Host侧的数据使用完成后,释放Host内存<br> aclrtFreeHost(outputPicHost.picture_address);<br> outputPicHost.picture_address = nullptr;<br>} else {<br> // 可以直接使用VPC的输出图片数据,在outputPic.picture_address指向的内存中<br> // TODO:推理相关的代码逻辑<br>}<br>// 5.7 释放输入、输出内存<br>ret = hi_mpi_dvpp_free(inputPic.picture_address);<br>ret = hi_mpi_dvpp_free(outputPic.picture_address);<br>// 6.销毁通道<br>ret = hi_mpi_vpc_destroy_chn(chnId);<br>// 7. 媒体数据处理系统去初始化<br>ret = hi_mpi_sys_exit();<br>// 8. 释放运行管理资源(依次释放Context、Device)<br>aclRet = aclrtDestroyContext(g_context);<br>aclRet = aclrtResetDevice(0);<br>// 9.AscendCL去初始化<br>aclRet = aclFinalize();<br>// ....<br> |
|---|
抠图示例代码
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
// 1.AscendCL初始化
aclRet = aclInit(nullptr);
// 2.运行管理资源申请(依次申请Device、Context)
aclrtContext g_context;
aclRet = aclrtSetDevice(0);
aclRet = aclrtCreateContext(&g_context, 0);
获取软件栈的运行模式,不同运行模式影响后续的接口调用流程(例如是否进行数据传输等)
aclrtRunMode runMode;
aclError aclRet = aclrtGetRunMode(&runMode);
// 3.初始化媒体数据处理系统
int32_t ret = hi_mpi_sys_init();
// 4.创建通道
hi_vpc_chn chnId;
hi_vpc_chn_attr stChnAttr;
ret = hi_mpi_vpc_sys_create_chn(&chnId, &stChnAttr);
// 5.执行抠图
// 5.1 构造存放输入图片信息的结构体
hi_vpc_pic_info inputPic;
inputPic.picture_width = 1920;
inputPic.picture_height = 1080;
inputPic.picture_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420;
inputPic.picture_width_stride = 1920;
inputPic.picture_height_stride = 1080;
inputPic.picture_buffer_size = inputPic.picture_width_stride * inputPic.picture_height_stride * 3 / 2;
// 5.2 准备输入图片数据
// 申请Device内存,用于媒体数据处理
ret = hi_mpi_dvpp_malloc(0, &inputPic.picture_address, inputPic.picture_buffer_size);
// 如果运行模式为ACL_HOST,则需要申请Host内存,将输入图片数据读入Host内存,再通过aclrtMemcpy接口将Host的图片数据传输到Device,数据传输完成后,需及时释放Host内存;否则直接将输入图片数据读入Device内存
if (runMode == ACL_HOST) {
void* inputAddr = nullptr;
// 申请Host内存
aclRet = aclrtMallocHost(&inputAddr, inputPic.picture_buffer_size);
// 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
ReadPicFile(picName, inputAddr, inputPic.picture_buffer_size);
// 数据传输
aclRet = aclrtMemcpy(inputPic.picture_address, inputPic.picture_buffer_size, inputAddr,
inputPic.picture_buffer_size, ACL_MEMCPY_HOST_TO_DEVICE);
// 完成数据传输后,需及时释放内存
aclrtFreeHost(inputAddr);
inputAddr = nullptr;
} else {
// 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
ReadPicFile(picName, inputPic.picture_address, inputPic.picture_buffer_size);
}
// 5.3 构造存放输出图片信息的结构体
// 该参数表示抠图数量
uint32_t multiCount = 1;
// cropRegionInfos数组的大小与抠图数量保持一致
hi_vpc_crop_region_info cropRegionInfos[1];
hi_vpc_pic_info outputPic;
for (uint32_t i = 0; i < multiCount; i++) {
outputPic.picture_width = 960;
outputPic.picture_height = 540;
outputPic.picture_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420;
outputPic.picture_width_stride = 960;
outputPic.picture_height_stride = 540;
outputPic.picture_buffer_size = outputPic.picture_width_stride * outputPic.picture_height_stride * 3 / 2;
ret = hi_mpi_dvpp_malloc(0, &outputPic.picture_address, outputPic.picture_buffer_size);
// 初始化内存
if (runMode == ACL_HOST) {
aclRet = aclrtMemset(outputPic.picture_address, outputPic.picture_buffer_size, 0, outputPic.picture_buffer_size);
} else {
memset(outputPic.picture_address, 0, outputPic.picture_buffer_size);
}
// 表示从输入图片中抠出以左上角为原点、分辨率960*540的子图
cropRegionInfos[i].dest_pic_info = outputPic;
cropRegionInfos[i].crop_region.left_offset = 0;
cropRegionInfos[i].crop_region.top_offset = 0;
cropRegionInfos[i].crop_region.crop_width = 960;
cropRegionInfos[i].crop_region.crop_height = 540;
}
// 5.4 调用抠图接口
uint32_t taskID = 0;
ret = hi_mpi_vpc_crop(chnId, &inputPic, cropRegionInfos, 1, &taskID, -1);
// 5.5 等待任务处理结束,任务处理结束后,输出图片数据在outputPic.picture_address指向的内存中
uint32_t taskIDResult = taskID;
ret = hi_mpi_vpc_get_process_result(chnId, taskIDResult, -1);
// 5.6 如果运行模式为ACL_HOST,且Host上需要展示VPC输出的图片数据,则需要申请Host内存,通过aclrtMemcpy接口将Device的输出图片数据传输到Host;如果Host上不需要展示VPC输出的图片数据,则VPC的输出图片数据可以直接作为模型推理的输入
if (g_runMode == ACL_HOST) {
hi_vpc_pic_info outputPicHost = outputPic;
aclRet = aclrtMallocHost(&outputPicHost.picture_address, outputPic.picture_buffer_size);
aclRet = aclrtMemcpy(outputPicHost.picture_address, outputPic.picture_buffer_size, outputPic.picture_address,outputPic.picture_buffer_size, ACL_MEMCPY_DEVICE_TO_HOST);
// ......
// Host侧的数据使用完成后,释放Host内存
aclrtFreeHost(outputPicHost.picture_address);
outputPicHost.picture_address = nullptr;
} else {
// 可以直接使用VPC的输出图片数据,在outputPic.picture_address指向的内存中
// TODO:推理相关的代码逻辑
}
// 5.7 释放输入、输出内存
ret = hi_mpi_dvpp_free(inputPic.picture_address);
inputPic.picture_address = nullptr;
for (uint32_t i = 0; i < multiCount; i++) {
hi_mpi_dvpp_free(cropRegionInfos[i].dest_pic_info.picture_address);
cropRegionInfos[i].dest_pic_info.picture_address = nullptr;
}
// 6.销毁通道
ret = hi_mpi_vpc_destroy_chn(chnId);
// 7. 媒体数据处理系统去初始化
ret = hi_mpi_sys_exit();
// 8. 释放运行管理资源(依次释放Context、Device)
aclRet = aclrtDestroyContext(g_context);
aclRet = aclrtResetDevice(0);
// 9.AscendCL去初始化
aclRet = aclFinalize();
// ....
父主题: 媒体数据处理V2
JPEGD图片解码
JPEGD(JPEG Decoder)负责完成图像解码功能,将.jpg、.jpeg、.JPG、.JPEG图片解码成YUV格式图片。关于JPEGD功能的详细介绍及约束请参见JPEGD功能及约束说明。
本节介绍JPEGD图片解码的接口调用流程,同时配合示例代码辅助理解该接口调用流程。
接口调用流程
开发应用时,如果涉及对JPEG图片的解码,则应用程序中必须包含解码的代码逻辑,关于图片解码的接口调用流程,请先参见AscendCL接口调用流程了解整体流程,再查看本节中的流程说明。
图1 接口调用的流程

当前系统支持解码JPEG图片,关键接口的说明如下:
-
调用hi_mpi_sys_init接口进行媒体数据处理系统初始化。
-
调用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> | <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_vdec_chn chnId;<br>hi_vdec_chn_attr chnAttr;<br>chnAttr.type = HI_PT_JPEG;<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;<br>ret = hi_mpi_vdec_create_chn(chnId, &chnAttr);<br>// 5.设置通道属性<br>hi_vdec_chn_param chnParam;<br>ret = hi_mpi_vdec_get_chn_param(chnId, &chnParam);<br>chnParam.pic_param.pixel_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420;<br>chnParam.pic_param.alpha = 255;<br>chnParam.display_frame_num = 0;<br>ret = hi_mpi_vdec_set_chn_param(chnId, &chnParam);<br>// 6.解码器启动接收码流<br>ret = hi_mpi_vdec_start_recv_stream(chnId);<br>// 7.发送码流<br>// 7.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>// 7.2 构造存放输入图片信息的结构体<br>hi_vdec_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.end_of_frame = HI_TRUE;<br>stStream.end_of_stream = HI_FALSE;<br>stStream.need_display = HI_TRUE;<br>ret = hi_mpi_dvpp_get_image_info(HI_PT_JPEG, &stStream, &stImgInfo);<br>if (g_runMode == ACL_HOST) {<br> // 如果不使用Host上的数据,需及时释放<br> aclrtFreeHost(hostInputAddr);<br> hostInputAddr = nullptr;<br>}<br>stStream.addr = (uint8_t *)inputAddr;<br>// 7.3 构造存放输出图片信息的结构体,并申请输出内存<br>hi_vdec_pic_info outPicInfo{};<br>void *outBuffer = nullptr;<br>outPicInfo.width = stImgInfo.width;<br>outPicInfo.height = stImgInfo.height;<br>outPicInfo.width_stride = stImgInfo.width_stride;<br>outPicInfo.height_stride = stImgInfo.height_stride;<br>outPicInfo.buffer_size = stImgInfo.img_buf_size;<br>outPicInfo.pixel_format = HI_PIXEL_FORMAT_UNKNOWN;<br>ret = hi_mpi_dvpp_malloc(0, &outBuffer, outPicInfo.buffer_size);<br>outPicInfo.vir_addr = (uint64_t)outBuffer;<br>// 7.4 发送需解码的输入图片<br>ret = hi_mpi_vdec_send_stream(chnId, &stStream, &outPicInfo, 0);<br>// 8.接收解码结果<br>// 8.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> } else { // Decode fail<br> printf("[%s][%d] Chn %u GetFrame Success, Decode Fail \n",__FUNCTION__, __LINE__, chnId);<br> }<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> // 可以直接使用JPEGD的输出图片数据,在frame.v_frame.virt_addr[0]指向的内存中<br> // ......<br>}<br>// 8.3 释放输入、输出内存<br>ret = hi_mpi_dvpp_free(frame.v_frame.virt_addr[0]);<br>ret = hi_mpi_dvpp_free(stream.addr);<br>// 8.4 释放资源<br>ret = hi_mpi_vdec_release_frame(chnId, &frame);<br>// 9.解码器停止接收码流<br>ret = hi_mpi_vdec_stop_recv_stream(chnId);<br>// 10.销毁通道<br>ret = hi_mpi_vdec_destroy_chn(chnId);<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
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
在线提单