示例代码
基本原理
- 调用aclvencCreateChannel接口创建视频编码处理的通道、调用aclvencDestroyChannel接口销毁视频处理的通道。
- 调用aclvencSendFrame接口将YUV420SP格式的图片编码成H264/H265格式的视频码流。
视频编码的结果数据通过回调函数获取。
- 输入、输出相关的约束要求,请参见功能及约束说明。
示例代码
您可以从样例介绍中获取完整样例代码。
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
// 1.AscendCL初始化
aclRet = aclInit(nullptr);
// 2.运行管理资源申请(依次申请Device、Context、Stream)
aclrtContext context_;
aclrtStream stream_;
aclrtSetDevice(0);
aclrtCreateContext(&context_, 0);
aclrtCreateStream(&stream_);
// 3.创建执行回调函数的线程及线程函数
static bool runFlag = true;
void *ThreadFunc(void *arg)
{
// Notice: create context for this thread
int deviceId = 0;
aclrtContext context = nullptr;
aclError ret = aclrtCreateContext(&context, deviceId);
INFO_LOG("process callback thread start ");
while (runFlag) {
// Notice: timeout 1000ms
(void)aclrtProcessReport(1000);
}
// ......
ret = aclrtDestroyContext(context);
return (void*)0;
}
int createThreadErr = pthread_create(&threadId_, nullptr, ThreadFunc, nullptr);
// 4.创建回调函数
void callback(acldvppPicDesc *input, acldvppStreamDesc *outputStreamDesc, void *userdata)
{
// 获取视频编码结果数据,并写入文件
void *outputDev = acldvppGetStreamDescData(outputStreamDesc);
uint32_t streamDescSize = acldvppGetStreamDescSize(outputStreamDesc);
if (!Utils::WriteToFile(g_outFileFp, outputDev, streamDescSize)) {
ERROR_LOG("write file:%s failed.", g_outFile.c_str());
}
INFO_LOG("success to callback, stream size:%u", streamDescSize);
}
// 5.创建视频编码处理通道时的通道描述信息,设置通道描述信息的属性,其中线程、callback回调函数需要用户提前创建
// vencChannelDesc_是aclvdecChannelDesc类型
vencChannelDesc_ = aclvencCreateChannelDesc();
auto ret = aclvencSetChannelDescThreadId(vencChannelDesc_, threadId_);
ret = aclvencSetChannelDescCallback(vencChannelDesc_, callback);
ret = aclvencSetChannelDescEnType(vencChannelDesc_, enType_);
ret = aclvencSetChannelDescPicFormat(vencChannelDesc_, format_);
ret = aclvencSetChannelDescPicWidth(vencChannelDesc_, 128);
ret = aclvencSetChannelDescPicHeight(vencChannelDesc_, 128);
ret = aclvencSetChannelDescKeyFrameInterval(vencChannelDesc_, 16);
// 6.创建视频码流处理的通道、单帧编码配置数据
ret = aclvencCreateChannel(vencChannelDesc_);
// vencFrameConfig_是aclvencFrameConfig类型
vencFrameConfig_ = aclvencCreateFrameConfig();
// 7.申请Device内存dataDev,存放视频编码的输入数据
// 7.1 读入图片数据
const char *fileName = "../dvpp_venc_128x128_nv12.yuv";
FILE *fp = fopen(fileName, "rb+");
fseek(fp, 0, SEEK_END);
long fileLenLong = ftell(fp);
fseek(fp, 0, SEEK_SET);
auto fileLen = static_cast<uint32_t>(fileLenLong);
uint32_t dataSize = fileLen;
// 调用aclrtGetRunMode接口获取软件栈的运行模式,如果调用aclrtGetRunMode接口获取软件栈的运行模式为ACL_HOST,则需要通过aclrtMemcpy接口将输入图片数据传输到Device,数据传输完成后,需及时释放内存;否则直接申请并使用Device的内存
aclrtRunMode runMode;
ret = aclrtGetRunMode(&runMode);
if(runMode == ACL_HOST) {
void *dataHost = malloc(fileLen);
size_t readSize = fread(dataHost, 1, fileLen, fp)
void *dataDev = nullptr;
ret = acldvppMalloc(&dataDev, dataSize);
ret = aclrtMemcpy(dataDev, dataSize, dataHost, fileLen, ACL_MEMCPY_HOST_TO_DEVICE);
// 完成数据传输后,需及时释放内存
free(dataHost);
}
else {
ret = acldvppMalloc(&dataDev, dataSize);
}
// 8.执行视频编码
size_t g_vencCnt = 16;
// 8.1 创建输入图片描述信息,设置输入图片数据的内存地址和内存大小
inputPicputDesc_ = acldvppCreatePicDesc();
ret = acldvppSetPicDescData(inputPicputDesc_, dataDev);
ret = acldvppSetPicDescSize(inputPicputDesc_, dataSize);
// 8.2 设置单帧编码配置数据,不是结束帧
ret = aclvencSetFrameConfigEos(vencFrameConfig_, 0);
ret = aclvencSetFrameConfigForceIFrame(vencFrameConfig_, 0)
// 8.3 创建输出码流描述信息
acldvppStreamDesc *outputStreamDesc = nullptr;
// 8.4 执行视频编码
while (g_vencCnt > 0) {
ret = aclvencSendFrame(vencChannelDesc_, inputPicputDesc_,
static_cast<void *>(outputStreamDesc), vencFrameConfig_, nullptr);
g_vencCnt--;
}
// 8.5 设置单帧编码配置数据,是结束帧
ret = aclvencSetFrameConfigEos(vencFrameConfig_, 1);
ret = aclvencSetFrameConfigForceIFrame(vencFrameConfig_, 0)
// 8.6 执行最后一帧的视频编码
ret = aclvencSendFrame(vencChannelDesc_, nullptr, nullptr, vencFrameConfig_, nullptr);
// 9.释放资源
(void)aclvencDestroyChannel(vencChannelDesc_);
(void)aclvencDestroyChannelDesc(vencChannelDesc_);
(void)acldvppDestroyPicDesc(inputPicputDesc_);
(void)aclvencDestroyFrameConfig(vencFrameConfig_);
// 释放内存和销毁线程
(void)acldvppFree(inBufferDev_);
void *res = nullptr;
int joinThreadErr = pthread_join(threadId_, &res);
// 10. 释放运行管理资源(依次释放Stream、Context、Device)
aclrtDestroyStream(stream_);
aclrtDestroyContext(context_);
aclrtResetDevice(0);
// 11.AscendCL去初始化
aclRet = aclFinalize();
// ......如果调用aclvencSetChannelDescParam接口设置通道描述信息的属性,调用aclvencGetChannelDescParam接口获取通道描述信息中的属性值,示例代码如下:
// 设置回调函数 void *func = (void *)callback; aclvencSetChannelDescParam(vencChannelDesc_, ACL_VENC_CALLBACK_PTR, 8, &func); // 获取回调函数 void *func1 = nullptr; aclvencGetChannelDescParam(vencChannelDesc_, ACL_VENC_CALLBACK_PTR, 8, &len, &func1); // 设置输入图片格式 acldvppPixelFormat format = PIXEL_FORMAT_YUV_SEMIPLANAR_420; aclvencSetChannelDescParam(vencChannelDesc_, ACL_VENC_PIXEL_FORMAT_UINT32, 4, &format); // 获取输入图片格式 acldvppPixelFormat format1 = PIXEL_FORMAT_YUV_SEMIPLANAR_420; aclvencGetChannelDescParam(vencChannelDesc_, ACL_VENC_PIXEL_FORMAT_UINT32, 4, &len, &format1); // 设置图片宽度 uint32_t width = 128; aclvencSetChannelDescParam(vencChannelDesc_, ACL_VENC_PIC_WIDTH_UINT32, 4, &width); // 获取图片高度 uint32_t width1 = 0; aclvencGetChannelDescParam(vencChannelDesc_, ACL_VENC_PIC_WIDTH_UINT32, 4, &len, &width1); // 设置图片高度 uint32_t height = 128; aclvencSetChannelDescParam(vencChannelDesc_, ACL_VENC_PIC_HEIGHT_UINT32, 4, &height); // 获取图片高度 uint32_t height1 = 0; aclvencGetChannelDescParam(vencChannelDesc_, ACL_VENC_PIC_HEIGHT_UINT32, 4, &len, &height1); // 设置编码输出缓存地址 ret = acldvppMalloc(&buf, bufSize); aclvencSetChannelDescParam(vencChannelDesc_, ACL_VENC_BUF_ADDR_PTR, 8, &buf); // 获取编码输出缓存地址 void *buf1 = nullptr; aclvencGetChannelDescParam(vencChannelDesc_, ACL_VENC_BUF_ADDR_PTR, 8, &len, &buf1);
父主题: VENC视频编码