Skip to main content

VDEC视频解码无报错,但无解码结果数据、CPU占用率高

用户进程异常退出后重启进程失败

现象描述

用户进程卡住或者用户强制退出进程后,再次重启,重启后发现进程无法正常启动。类似的日志信息如下:

AscendCL日志信息:aclrtProcessReport failed

aclrtProcessReport failed, ret = 107012
aclrtProcessReport failed, ret = 107012

Runtime日志信息:halResourceIdAlloc xxx failed

[ERROR] DRV(2086,rtstest_host):2021-06-09-02:14:46.034.368 [ascend][curpid: 2086, 2086][drv][tsdrv][halResourceIdAlloc 477]id is exhausted, type(0 stream), range[0, 1024), dev_id(0), tsid(0).
[ERROR] RUNTIME(2086,rtstest_host):2021-06-09-02:14:46.034.380 [npu_driver.cc:285]2086 StreamIdAlloc:[driver interface] halResourceIdAlloc streamid failed: device_id=0, tsId=0, drvRetCode=48!
[ERROR] RUNTIME(2086,rtstest_host):2021-06-09-02:14:46.034.401 [stream.cc:448]2086 Setup:Failed to alloc stream id, retCode=0x702001a.
[ERROR] RUNTIME(2086,rtstest_host):2021-06-09-02:14:46.034.416 [context.cc:1251]2086 StreamCreate:Setup stream failed, retCode=0x702001a.
[ERROR] RUNTIME(2086,rtstest_host):2021-06-09-02:14:46.034.440 [logger.cc:211]2086 StreamCreate:Create stream failed, priority=7 ,flags=0.
[ERROR] RUNTIME(2086,rtstest_host):2021-06-09-02:14:46.034.458 [api_c.cc:461]2086 rtStreamCreateWithFlags:ErrCode=207008, desc=[driver error:no stream resource], InnerCode=0x702001a
[ERROR] RUNTIME(2086,rtstest_host):2021-06-09-02:14:46.034.469 [error_message_manage.cc:26]2086 ReportFuncErrorReason:rtStreamCreateWithFlags execute failed, reason=[driver error:no stream resource]

可能原因

通过日志分析无法正常重启的原因可能是public taskid、stream id、eventid等资源申请不到引起的:

  • 资源已经被其他进程占用完。
  • 上一个进程退出时还未完全释放完资源。

处理步骤

针对上述可能原因,可以按以下方式处理:

  • 等待一分钟后再重新启动进程,保证上一个进程资源释放完成。
  • 停止其他进程或者等其他进程执行完成后再启动进程。
  • 如果通过上述方式处理后仍然申请失败,建议检查是否超过了可用的资源上限,如果未超上限,则需要重启环境强行释放资源、恢复环境。

父主题: FAQ

VDEC视频解码异常导致进程卡死,无法退出

现象描述

用户进程卡死,无法退出。

查看应用类日志,一直重复提示信息“fault kernel_name=DvppSendVdecFrame”、“Kernel task happen error, retCode=0x28, [aicpu timeout]”,表示AI CPU异常,无法处理VDEC解码任务,导致任务超时。

日志片段举例如下:

[ERROR] RUNTIME(pid,pName):DateTimeMS [task.cc:878]1827 PreCheckTaskErr:[DVPP][DEFAULT]Kernel task happen error, retCode=0x28, [aicpu timeout].
[ERROR] RUNTIME(pid,pName):DateTimeMS [task.cc:676]1827 PrintAicpuErrorInfo:[DVPP][DEFAULT]Aicpu kernel execute failed, device_id=0, stream_id=177, task_id=4, fault so_name=libdvpp_kernels.so, fault kernel_name=DvppSendVdecFrame, fault op_name=, extend_info=.
[ERROR] RUNTIME(pid,pName):DateTimeMS [task.cc:878]1831 PreCheckTaskErr:[DVPP][DEFAULT]Kernel task happen error, retCode=0x28, [aicpu timeout].
[ERROR] RUNTIME(pid,pName):DateTimeMS [task.cc:676]1831 PrintAicpuErrorInfo:[DVPP][DEFAULT]Aicpu kernel execute failed, device_id=0, stream_id=170, task_id=8, fault so_name=libdvpp_kernels.so, fault kernel_name=DvppSendVdecFrame, fault op_name=, extend_info=.
[ERROR] RUNTIME(pid,pName):DateTimeMS [engine.cc:960]1766 ReportExceptProc:[DVPP][DEFAULT]Task exception! device_id=0, stream_id=107, task_id=8, type=1, retCode=0x28.
[ERROR] RUNTIME(pid,pName):DateTimeMS [engine.cc:960]1773 ReportExceptProc:[DVPP][DEFAULT]Task exception! device_id=0, stream_id=130, task_id=4, type=1, retCode=0x28.

可能原因

Device内存不足,AI CPU无法处理VDEC解码任务,导致任务超时。

处理步骤

  1. 在使用媒体数据处理V1版本的VDEC视频解码功能前,可参考功能及约束说明中“每路VDEC解码的内存消耗计算公式”,预估需使用的Device内存,并合理规划Device上的内存。

  2. 优化应用程序的代码逻辑,增加异常处理机制,获取VDEC解码异常信息,强制退出进程。

    在调用aclinit接口之后,定义异常回调函数,并调用aclrtSetExceptionInfoCallback接口设置异常回调函数,用于获取任务异常信息,以便在异常分支中根据任务异常信息来判断是否退出应用进程。

    基本接口调用逻辑如下:

    1. 定义并实现异常回调函数fn(aclrtExceptionInfoCallback类型),回调函数原型为:typedef void (*aclrtExceptionInfoCallback)(aclrtExceptionInfo *exceptionInfo)

      在异常回调函数fn内调用aclrtGetDeviceIdFromExceptionInfoaclrtGetStreamIdFromExceptionInfoaclrtGetTaskIdFromExceptionInfo接口分别获取Device ID、Stream ID、Task ID。

      根据Stream ID、Task ID判断Device是否异常,若异常,则强制退出进程。

      异常回调函数实现示例如下:

      void dvpp_callback(aclrtExceptionInfo * exception_info)
      {
      uint32_t taskId = aclrtGetTaskIdFromExceptionInfo(exception_info);
      uint32_t streamId = aclrtGetStreamIdFromExceptionInfo(exception_info);
      uint32_t deviceId = aclrtGetDeviceIdFromExceptionInfo(exception_info);

      if(taskId == 0xffffffff) || (streamId == 0xffffffff) {
      //Device异常,强制退出进程
      } else {
      //任务异常,如果频繁出现(例如,统计1秒内触发异常回调函数的次数),进程退出
      }
      return;
      }
    2. 调用aclrtSetExceptionInfoCallback接口设置异常回调函数。

    3. 执行VDEC解码,接口调用流程请参见VDEC视频解码

父主题: FAQ

buf_size参数设置不合理导致视频编码异常

视频编码场景下,需通过hi_venc_attr结构体中buf_size参数值来设置编码缓冲区的内存大小,buf_size参数值设置地不合理,可能会导致视频编码耗时长或编码失败。

现象及可能原因(Ascend RC)

视频编码耗时长或编码失败的场景下,使用proc命令排查问题,proc查询结果中关键信息含义如下:EncStart表示启动编码的帧数,EndSuccessed表示成功编码的帧数,Lost和Disc(Disacrd)表示编码失败的帧数,Recode表示重编的次数。

  1. 编码进程运行过程中,登录Device。
  2. 执行命令cat /proc/umap/h265e或者cat /proc/umap/h264e
    • 出现类似下面红框的现象:Lost和Disc的数量为0或很低,但是Recode的数量比较大,表示大部分帧能够编码成功,但是重编次数太多。

      当实际的编码结果大小大于编码缓冲区中的可用内存大小时,编码模块会自动调整参数重编,减小编码结果数据大小。因此buf_size设置的太小,缓冲帧数少,导致出现重编的概率高,进而导致编码时延增加,帧率变低,性能下降。

    • 出现类似下面红框的现象:Lost和Disc的数量比较大,同时Recode的数量也比较大,表示有比较多的帧编码失败了。

      当实际的编码结果大小大于编码缓冲区中的可用内存大小时,编码模块会自动调整参数重编,减小编码结果数据大小。如果重编次数全部用完,但是编码结果大小依然大于编码缓冲区中的可用内存大小,此时编码模块会将该帧丢弃。因此buf_size设置的太小,缓冲帧数少,导致出现重编的概率高,丢帧概率高。

处理步骤

创建编码通道时,合理设置buf_size参数,具体参见hi_venc_attr结构体中buf_size成员的说明。

父主题: FAQ

VDEC视频解码无报错,但无解码结果数据、CPU占用率高

现象描述

查看应用类日志,无ERROR报错、无解码结果数据输出,另外,在运行应用程序的Linux服务器上执行top命令,该应用进程的cpu占用率持续升高。

可能原因

  1. 无ERROR、无解码结果数据输出,判断可能是因为解码发帧接口aclvdecSendFrame调用正常,但未触发回调函数,无法获取解码结果数据。

  2. 检查触发回调函数的代码逻辑。

    按照VDEC视频解码的接口调用逻辑:由用户提前创建一个单独的线程,并自定义线程函数,在线程函数内调用aclrtProcessReport接口,通过该接口配置超时时间,等待指定的超时时间后,触发回调函数,获取解码结果数据。

    void *ThreadFunc(aclrtContext sharedContext)
    {
    if (sharedContext == nullptr) {
    ERROR_LOG("sharedContext can not be nullptr");
    return ((void*)(-1));
    }
    INFO_LOG("use shared context for this thread");
    aclError ret = aclrtSetCurrentContext(sharedContext);
    if (ret != ACL_SUCCESS) {
    ERROR_LOG("aclrtSetCurrentContext failed, errorCode = %d", static_cast<int32_t>(ret));
    return ((void*)(-1));
    &#125;

    INFO_LOG("thread start ");
    while (runFlag) &#123;
    // Notice: timeout 1000ms
    (void)aclrtProcessReport(1000);
    &#125;
    return (void*)0;
    &#125;
  3. 如果触发回调函数的接口调用逻辑正确,则在aclrtProcessReport接口处增加日志打印,判断应用运行过程中线程是否成功调用了aclrtProcessReport接口,只有成功调用aclrtProcessReport接口,才会触发回调函数。

    示例代码如下:

    while (runFlag) &#123;
    // Notice: timeout 1000ms
    aclError ret = aclrtProcessReport(1000);
    printf("aclrtProcessReport failed, ret=%d.\n", ret);
    &#125;
  4. 修改代码后,重新编译运行应用。

    在终端屏幕重复出现以下打印信息,表示调用aclrtProcessReport接口失败:

    aclrtProcessReport failed, ret = 107012

    查阅该接口的返回值说明,107012表示线程未订阅或重复订阅。

  5. 检查代码逻辑,检查是否调用aclvdecSetChannelDescThreadId接口绑定用户新建的线程,按照VDEC视频解码的接口调用逻辑,只有调用该接口绑定用户线程,才可以触发调用aclrtProcessReport接口,进而触发回调函数。

  6. 修改代码后,重新编译运行应用,VDEC视频解码正常,正常输出解码结果数据,同时cpu占用率下降。

解决办法

参见VDEC的VDEC视频解码或者参考Link上的VDEC功能样例开发VDEC功能。

其中,需关注以下注意点:

  • 创建新线程,并自定义线程函数,在线程函数内调用aclrtProcessReport接口,等待指定时间后,触发回调函数中的回调函数。

  • 需调用aclvdecSetChannelDescThreadId接口绑定用户创建的新线程。

  • 释放资源时,依次销毁通道、销毁通道描述信息后,才可以销毁中用户创建的新线程。

父主题: FAQ

执行应用程序的权限不足导致AscendCL初始化报错

问题现象

用户进程报错并退出。

查看应用类日志,提示获取Device信息失败,最终导致AscendCL初始化失败,日志片段示例如下:

[ERROR] RUNTIME(89696,main):2023-03-07-17:13:27.994.635 [runtime.cc:1065]89696 CheckHaveDevice:[INIT][DEFAULT]Call halGetDeviceInfo failed: drvRet=4, module type=0, info type=1.
[ERROR] ASCENDCL(89696,main):2023-03-07-17:13:27.994.723 [acl.cpp:164]89696 aclInit: [INIT][DEFAULT][Init][Version]init soc version failed, ret = 507008
[ERROR] RUNTIME(89696,main):2023-03-07-17:13:27.994.774 [api_impl.cc:3490]89696 GetDevErrMsg:report error module_type=3, module_name=EE8888
[ERROR] RUNTIME(89696,main):2023-03-07-17:13:27.994.798 [api_impl.cc:3490]89696 GetDevErrMsg:ctx is NULL!
[ERROR] RUNTIME(89696,main):2023-03-07-17:13:27.994.827 [api_impl.cc:3546]89696 GetDevMsg:Failed to GetDeviceErrMsg, retCode=0x7070001.
[ERROR] RUNTIME(89696,main):2023-03-07-17:13:27.994.849 [logger.cc:1348]89696 GetDevMsg:GetDeviceMsg failed, getMsgType=0.
[ERROR] RUNTIME(89696,main):2023-03-07-17:13:27.994.888 [api_c.cc:3595]89696 rtGetDevMsg:ErrCode=107002, desc=[context pointer null], InnerCode=0x7070001
[ERROR] RUNTIME(89696,main):2023-03-07-17:13:27.994.910 [error_message_manage.cc:49]89696 FuncErrorReason:report error module_type=3, module_name=EE8888
[ERROR] RUNTIME(89696,main):2023-03-07-17:13:27.994.932 [error_message_manage.cc:49]89696 FuncErrorReason:rtGetDevMsg execute failed, reason=[context pointer null]
EL0003: The argument is invalid.
Solution: Try again with a valid argument.
TraceBack (most recent call last):
[Init][Version]init soc version failed, ret = 507008[FUNC:ReportInnerError][FILE:log_inner.cpp][LINE:145]
ctx is NULL![FUNC:GetDevErrMsg][FILE:api_impl.cc][LINE:3490]
rtGetDevMsg execute failed, reason=[context pointer null][FUNC:FuncErrorReason][FILE:error_message_manage.cc][LINE:49]

[ERROR] acl init failed
[ERROR] Sample init resource failed

原因分析

可能存在以下原因:

  • Device状态异常,未正常启动。
  • 执行应用程序的用户权限不足,无法查询Device信息。

解决办法

  1. 首先,确认Device是否正常启动。

    1. 以root用户登录安装Driver包的环境,执行以下命令查询其安装路径。

      cat /etc/ascend_install.info

      在该文件中,Driver_Install_Path_Param表示Driver包的安装路径。

    2. 进入Driver安装路径,使用upgrade-tool工具查看下Device侧运行文件系统版本,如果能正常查询,则说明Device侧已经正常启动。

      ./upgrade-tool --device_index -1 --system_version

      正常查询返回信息类似如下:

  2. 其次,检查运行应用程序的用户权限是否正确。

    要求运行应用程序的用户,需与Driver运行用户在一个属组内。在“cat /etc/passwd”文件中,可查看用户属组,Driver的默认运行用户为HwHiAiUser。

    修改用户属组的命令示例如下:

    usermod -g 组名 用户名
  3. 如果以上方法解决不了问题,则需要参考如下步骤将获取日志,反馈给技术支持。您可以获取日志后单击Link联系技术支持。

    1. 登录到运行应用程序的环境,执行如下命令将日志级别设置为Debug。

      export ASCEND_GLOBAL_LOG_LEVEL=0
    2. 重新运行应用程序。

    3. 从日志存放路径下获取应用类日志。

      存放日志的默认路径为“$HOME/ascend/log”。

    4. 使用msnpureport工具,获取指定Device上的Debug日志。

      命令示例如下,其中deviceID需要设置为指定Device的ID:

      msnpureport -g debug -d deviceID

父主题: FAQ

AI应用进程未退出,导致休眠唤醒失败

问题现象

休眠失败。

查看应用类日志,系统内部的任务分发模块hwts正处于busy状态,检查发现不满足休眠条件,日志片段示例如下:

[ERROR] TSCH(-1,null):2023-01-01-02:53:45.850.781 1 (dieid:0,cpuid:0) device_management_plat.c:563 suspend_ack: suspend pre check fail, hwts is busy
[EVENT] TSCH(-1,null):2023-01-01-02:53:45.850.803 2 (dieid:0,cpuid:0) device_management.c:411 process_low_power_cmd: ts suspend ack ret=1.

原因分析

根据休眠唤醒的流程,休眠前AI应用进程必须先退出,相关硬件资源处于idle态,才允许休眠。不满足休眠条件,会有相关报错,本案例中因为AI应用进程未退出,在休眠唤醒时检测到hwts处于busy状态,因此休眠失败。

解决办法

用户需要确保AI应用进程已经运行结束或者优雅退出,推荐使用kill -2 PID退出相关进程,PID需按照替换为实际进程ID。

父主题: FAQ

复用输出图片描述类型,VDEC视频解码报错,提示有不支持的图片格式

问题现象

循环调用aclvdecSendFrame接口解码视频中的每一帧码流时,在遇到异常帧之后,解码下一帧就会报错,退出应用进程。

分别查看Host侧日志、Device侧日志,发现Device日志中提示the out format 0 is not supported,日志片段如下:

  • Device侧日志:
[ERROR] KERNEL(2234,sklogd):2023-06-13-19:21:22.987.969 [klogd.c:246][652145.056916] [HiDvpp][A618] [Vdec]:vdec_check_resize_param [Line]:6768 pid 23973 usr chn 0 device 0 chn 0 the out format 0 is not supported.
  • Host侧日志:
[ERROR] RUNTIME(17174,AIMCDemo):2023-06-13-19:21:23.664.211 [api_c.cc:721]17184 rtStreamSynchronize:[DVPP][DEFAULT]ErrCode=507018, desc=[aicpu exception], InnerCode=0x715002a
[ERROR] RUNTIME(17174,AIMCDemo):2023-06-13-19:21:23.664.215 [error_message_manage.cc:49]17184 FuncErrorReason:[DVPP][DEFAULT]report error module_type=3, module_name=EE8888
[ERROR] RUNTIME(17174,AIMCDemo):2023-06-13-19:21:23.664.221 [error_message_manage.cc:49]17184 FuncErrorReason:[DVPP][DEFAULT]rtStreamSynchronize execute failed, reason=[aicpu exception]
[INFO] GE(17174,AIMCDemo):2023-06-13-19:21:23.664.227 [error_manager.cc:252]17184 ReportInterErrMessage:report error_message, error_code:EE8888, work_stream_id:1717417184
[ERROR] ASCENDCL(17174,AIMCDemo):2023-06-13-19:21:23.664.234 [video_processor_v200.cpp:1089]17184 aclvdecSendFrame: [DVPP][DEFAULT][Sync][Stream]vdec fail to synchronize sendFrameStream, runtime errorCode = 507018, channelId = 0.

原因分析

检查应用代码,发现循环解码视频中的每一帧码流时,复用aclvdecSendFrame接口的输出图片描述类型acldvppPicDesc,但在下一次解码前没有重新设置输出图片format、width、height、widthStride、heightStride,这时,如果前一帧解码失败,acldvppPicDesc的参数format、width、height、widthStride、heightStride变成默认值0,width、height、widthStride、heightStride为0时,vdec会以实际图片宽高解码输出,但format为0,表示YUV400格式,vdec不支持解码输出该格式,会导致下一帧参数不合法解码失败。

解决方法

优化应用代码逻辑,复用输出图片描述类型acldvppPicDesc时,在下一次解码前需重新设置输出图片format、width、height、widthStride、heightStride。

正例代码片段:

aclError ret;
int restLen = 10;
uint32_t inBufferSize = 0;
void *g_picOutBufferDev;
void *inBufferDev = nullptr;
acldvppPicDesc *picOutputDesc;
size_t dataSize = (INPUT_WIDTH * INPUT_HEIGHT * 3) / 2;

// 申请一个picOutputDesc,每帧复用
picOutputDesc = acldvppCreatePicDesc();
// read file to device memory
ReadFileToDeviceMem(filePath.c_str(), inBufferDev, inBufferSize);
while (restLen > 0) &#123;
// 等待前一个使用picOutputDesc解码帧结束,重新复用picOutputDesc,并针对这一帧重新设置Format、width、height、widthStride、heightStride参数值
ret = acldvppSetPicDescFormat(picOutputDesc, static_cast<acldvppPixelFormat>(1)); // 1:YUV420 semi-planner(nv12)
ret = acldvppSetPicDescWidth(picOutputDesc, 1920);
ret = acldvppSetPicDescHeight(picOutputDesc, 1080);
ret = acldvppSetPicDescWidthStride(picOutputDesc, 1920);
ret = acldvppSetPicDescHeightStride(picOutputDesc, 1080);
ret = acldvppMalloc(&g_picOutBufferDev, dataSize);
ret = acldvppSetPicDescData(picOutputDesc, g_picOutBufferDev);
ret = acldvppSetPicDescSize(picOutputDesc, dataSize);
ret = aclvdecSendFrame(vdecChannelDesc, streamInputDesc, picOutputDesc, nullptr, nullptr);
restLen = restLen - 1;
&#125;

反例代码片段:

aclError ret;
int restLen = 10;
uint32_t inBufferSize = 0;
void *g_picOutBufferDev;
void *inBufferDev = nullptr;
acldvppPicDesc *picOutputDesc;
size_t dataSize = (INPUT_WIDTH * INPUT_HEIGHT * 3) / 2;

// 申请一个picOutputDesc,每帧复用,且对Format、width、height、widthStride、heightStride参数值只设置了一次
picOutputDesc = acldvppCreatePicDesc();
ret = acldvppSetPicDescFormat(picOutputDesc, static_cast<acldvppPixelFormat>(1)); // 1:YUV420 semi-planner(nv12)
ret = acldvppSetPicDescWidth(picOutputDesc, 1920);
ret = acldvppSetPicDescHeight(picOutputDesc, 1080);
ret = acldvppSetPicDescWidthStride(picOutputDesc, 1920);
ret = acldvppSetPicDescHeightStride(picOutputDesc, 1080);
// read file to device memory
ReadFileToDeviceMem(filePath.c_str(), inBufferDev, inBufferSize);
while (restLen > 0) &#123;
// 等待前一个使用picOutputDesc解码帧结束,重新复用picOutputDesc,但没有重新设置Format、width、height、widthStride、heightStride参数值
// 如果前一帧解码失败,picOutputDesc_的参数Format、width、height、widthStride、heightStride变成默认值0,
// width、height、widthStride、heightStride为0时,vdec会以实际图片宽高解码输出,但Format为0,表示YUV400格式,vdec不支持解码输出该格式,会导致本帧参数不合法解码失败
ret = acldvppMalloc(&g_picOutBufferDev, dataSize);
ret = acldvppSetPicDescData(picOutputDesc, g_picOutBufferDev);
ret = acldvppSetPicDescSize(picOutputDesc, dataSize);
ret = aclvdecSendFrame(vdecChannelDesc, streamInputDesc, picOutputDesc, nullptr, nullptr);
restLen = restLen - 1;
&#125;

父主题: FAQ

在线提单