示例代码

基本原理

示例代码

您可以从样例介绍中获取完整样例代码。

调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。

// 1.AscendCL初始化
aclRet = aclInit(nullptr);

// 2.运行管理资源申请(依次申请Device、Context、Stream)
aclrtContext context_;
aclrtStream stream_;
aclrtSetDevice(0);
aclrtCreateContext(&context_, 0);
aclrtCreateStream(&stream_);

// 3.创建回调函数
void callback(acldvppStreamDesc *input, acldvppPicDesc *output, void *userdata)
{
    static int count = 1;
    if (output != nullptr) {
        // 获取VDEC解码的输出内存,调用自定义函数WriteToFile将输出内存中的数据写入文件后,再调用acldvppFree接口释放输出内存
        void *vdecOutBufferDev = acldvppGetPicDescData(output);
        if (vdecOutBufferDev != nullptr) {
            // 0: vdec success; others, vdec failed
            // retCode为0表示解码成功,为1表示解码失败
            int retCode = acldvppGetPicDescRetCode(output);
            if (retCode == 0) {
                // process task: write file
                uint32_t size = acldvppGetPicDescSize(output);
                std::string fileNameSave = "outdir/image" + std::to_string(count);
                // vdec输出结果在device侧,在WriteToFile方法中进行下述处理
                if (!Utils::WriteToFile(fileNameSave.c_str(), vdecOutBufferDev, size)) {
                    ERROR_LOG("write file failed.");
                }
            } else {
                ERROR_LOG("vdec decode frame failed.");
            }

            // free output vdecOutBufferDev
            aclError ret = acldvppFree(vdecOutBufferDev);
        }
        // 释放acldvppPicDesc类型的数据,表示解码后输出图片描述数据
        aclError ret = acldvppDestroyPicDesc(output);
    }

    // free input vdecInBufferDev and destroy stream desc
    if (input != nullptr) {
        void *vdecInBufferDev = acldvppGetStreamDescData(input);
        if (vdecInBufferDev != nullptr) {
            aclError ret = acldvppFree(vdecInBufferDev);
        }
        // 释放acldvppStreamDesc类型的数据,表示解码的输入码流描述数据
        aclError ret = acldvppDestroyStreamDesc(input);
    }

    INFO_LOG("success to callback %d.", count);
    count++;
}

// 4.创建视频码流处理通道时的通道描述信息,设置视频处理通道描述信息的属性,其中线程、callback回调函数需要用户提前创建。
// vdecChannelDesc_是aclvdecChannelDesc类型
vdecChannelDesc_ = aclvdecCreateChannelDesc();
ret = aclvdecSetChannelDescChannelId(vdecChannelDesc_, 10);
ret = aclvdecSetChannelDescThreadId(vdecChannelDesc_, threadId_);
ret = aclvdecSetChannelDescCallback(vdecChannelDesc_, callback);
// 示例中使用的是H265_MAIN_LEVEL视频编码协议
ret = aclvdecSetChannelDescEnType(vdecChannelDesc_, static_cast<acldvppStreamFormat>(enType_));
// 示例中使用的是PIXEL_FORMAT_YVU_SEMIPLANAR_420
ret = aclvdecSetChannelDescOutPicFormat(vdecChannelDesc_, static_cast<acldvppPixelFormat>(format_));

// 5.创建视频码流处理的通道
ret = aclvdecCreateChannel(vdecChannelDesc_);

// 6.申请输入码流内存(区分运行状态)
// 调用aclrtGetRunMode接口获取软件栈的运行模式,如果调用aclrtGetRunMode接口获取软件栈的运行模式为ACL_HOST,则需要通过aclrtMemcpy接口将输入图片数据传输到Device,数据传输完成后,需及时释放内存;否则直接申请并使用Device的内存
aclrtRunMode runMode;
ret = aclrtGetRunMode(&runMode);
if(runMode == ACL_HOST){ 
    // 申请Host内存inputHostBuff
    void* inputHostBuff= nullptr;
    // inBufferSize_为输入码流大小
    inputHostBuff= malloc(inBufferSize_);
    // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
    ReadPicFile(picName, inputHostBuff, inBufferSize_);
    // 申请Device内存inBufferDev_
    aclRet = acldvppMalloc(&inBufferDev_, inBufferSize_);
    // 通过aclrtMemcpy接口将输入图片数据传输到Device
    aclRet = aclrtMemcpy(inBufferDev_, inBufferSize_, inputHostBuff, inBufferSize_, ACL_MEMCPY_HOST_TO_DEVICE);
    // 数据传输完成后,及时释放内存
    free(inputHostBuff);
} else {
    // 申请Device输入内存dataDev, StreamBufferSize为输入码流大小
    ret = acldvppMalloc(&inBufferDev_, inBufferSize_);
    // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
    ReadPicFile(picName, inBufferDev_, inBufferSize_);
}

// 7.循环10次执行视频解码,输出10张YUV420SP NV12格式的图片
int rest_len = 10;
int32_t count = 0;
while (rest_len > 0) {
    // 7.1 创建输入视频码流描述信息,设置码流信息的属性
    streamInputDesc_ = acldvppCreateStreamDesc(); 
    // inBufferDev_表示Device存放输入视频数据的内存,inBufferSize_表示内存大小  
    ret = acldvppSetStreamDescData(streamInputDesc_, inBufferDev_);
    ret = acldvppSetStreamDescSize(streamInputDesc_, inBufferSize_);

    // 7.2 申请Device内存picOutBufferDev_,用于存放VDEC解码后的输出数据
    ret = acldvppMalloc(&picOutBufferDev_, size);

    // 7.3 创建输出图片描述信息,设置图片描述信息的属性
    // picOutputDesc_是acldvppPicDesc类型
    picOutputDesc_ = acldvppCreatePicDesc();
    ret = acldvppSetPicDescData(picOutputDesc_, picOutBufferDev_);
    ret = acldvppSetPicDescSize(picOutputDesc_, size);
    ret = acldvppSetPicDescFormat(picOutputDesc_, static_cast<acldvppPixelFormat>(format_));

    // 7.4 执行视频码流解码,解码每帧数据后,系统自动调用callback回调函数将解码后的数据写入文件,再及时释放相关资源
    ret = aclvdecSendFrame(vdecChannelDesc_, streamInputDesc_, picOutputDesc_, nullptr, nullptr);
    // ......
    ++count;
    rest_len = rest_len - 1;
    // ......
}

// 8.释放图片处理通道、图片描述信息
ret = aclvdecDestroyChannel(vdecChannelDesc_);
aclvdecDestroyChannelDesc(vdecChannelDesc_);

// 9. 释放运行管理资源(依次释放Stream、Context、Device)
aclrtDestroyStream(stream_);
aclrtDestroyContext(context_);
aclrtResetDevice(0);

// 10.AscendCL去初始化
aclRet = aclFinalize();

// ......

返回码说明

表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

参考帧个数设置错误。

请检查码流实际参考帧个数与用户设置的参考帧个数是否一致。Atlas 推理系列产品,默认参考帧个数为8。

0x20002

VDEC解码帧存大小设置错误。

请检查输入码流实际宽、高与用户设置的宽、高是否一致。

0xA0058001

无效的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

最大的返回码,该模块的错误码必须小于该值。

-