// 打开解码器,初始化相关参数
bool DvppDecoder::open_decoder(const acldvppStreamFormat stream_type, const int width, const int height,
const std::shared_ptr<AVCodecParameters> &codecpar, AVRational time_base) {
if (decoder_init_flag) close_decoder();// 关闭之前的解码器
aclError aclRet = ACL_SUCCESS;
// 第一次初始化
if (!cb_open_flag) {
decoder_channelId = channelId++;
aclError ret = aclInit(NULL);
if (ret != 0) logs::error("dvpp decoder aclInit faile ! rrorCode = {}", static_cast<int32_t>(ret));
ret = aclrtSetDevice(deviceId_);
if (ret != 0) { logs::error("aclrtSetDevice fail! rrorCode = {}", static_cast<int32_t>(ret)); }
ret = aclrtCreateContext(&context_, deviceId_);// 0xffff2c0ec750
if (ret != 0) { logs::error("aclrtCreateContext fail! rrorCode = {}", static_cast<int32_t>(ret)); }
ret = aclrtCreateStream(&stream_);
if (ret != 0) { logs::error("aclrtCreateStream fail! rrorCode = {}", static_cast<int32_t>(ret)); }
logs::info("dvpp decoder deviceId_ context_ stream_ [{} {} {}]", deviceId_, context_, stream_);
// 创建通道描述,设置属性
vdecChannelDesc_ = aclvdecCreateChannelDesc();
if (vdecChannelDesc_ == nullptr) { logs::error("aclvdecCreateChannelDesc fail! rrorCode"); }
cb_open_flag = true;
}
// H264 或者 H265
stream_type_ = stream_type;
codecpar_ = codecpar;
// 初始化 比特流过滤器
init_video_params(codecpar, time_base);
threadData = std::make_shared<ThreadData>();
threadData->context = context_;
pthread_create(&threadId_, nullptr, ThreadFunc, &threadData);
if (vdecChannelDesc_ != nullptr) {
aclRet |= aclvdecDestroyChannel(vdecChannelDesc_);
if (aclRet != ACL_SUCCESS) {
logs::error("aclvdecDestroyChannel fail! rrorCode = {}", static_cast<uint32_t>(aclRet));
} else
logs::error("open_decoder aclvdecDestroyChannel success");
//aclvdecDestroyChannelDesc(vdecChannelDesc_);
}
// 设置通道属性
aclRet |= aclvdecSetChannelDescChannelId(vdecChannelDesc_, decoder_channelId);
aclRet |= aclvdecSetChannelDescThreadId(vdecChannelDesc_, threadId_);
aclRet |= aclvdecSetChannelDescChannelId(vdecChannelDesc_, decoder_channelId);
aclRet |= aclvdecSetChannelDescCallback(vdecChannelDesc_, decode_callback);
aclRet |= aclvdecSetChannelDescChannelId(vdecChannelDesc_, decoder_channelId);
aclRet |= aclvdecSetChannelDescEnType(vdecChannelDesc_, stream_type_);
aclRet |= aclvdecSetChannelDescOutPicFormat(vdecChannelDesc_, format_);
aclRet |= aclvdecCreateChannel(vdecChannelDesc_);
aclRet |= aclvdecSetChannelDescChannelId(vdecChannelDesc_, decoder_channelId);
decoder_init_flag = true;
return true;
}
// 解码函数
bool DvppDecoder::decode(const std::shared_ptr<AVPacket> &packet, int packet_idx,
const Output_Image_Callback &output_image_callback) {
aclError aclRet = ACL_SUCCESS;
aclRet = aclrtSetCurrentContext(context_);
if (aclRet != ACL_SUCCESS) {
logs::error("decode aclrtSetCurrentContext failed, errorCode = %d", static_cast<int32_t>(aclRet));
return false;
}
// send video packet to ffmpeg
if (av_bsf_send_packet(bsf_ctx_, packet.get())) { logs::error("Fail to call av_bsf_send_packet"); }
// receive single frame from ffmpeg
while ((av_bsf_receive_packet(bsf_ctx_, packet.get()) == 0)) {
aclRet = acldvppMalloc(&inBufferDev, packet->size);
aclRet |= aclrtMemcpy(inBufferDev, packet->size, packet->data, packet->size, ACL_MEMCPY_HOST_TO_DEVICE);
if (aclRet != ACL_SUCCESS) {
logs::error("decode acldvppMalloc failed : {}");
acldvppFree(inBufferDev);
return false;
}
// Create input video stream description information, set the properties of the stream information
acldvppStreamDesc *streamInputDesc_ = acldvppCreateStreamDesc();
if (streamInputDesc_ == nullptr) {
logs::error("acldvppCreateStreamDesc fail! rrorCode");
return false;
}
// 设置 输入视频 gpu 内存 和 大小
aclRet |= acldvppSetStreamDescData(streamInputDesc_, inBufferDev);
aclRet |= acldvppSetStreamDescSize(streamInputDesc_, packet->size);
if (aclRet != ACL_SUCCESS) {
logs::error("streamInputDesc set attribute fail! [{}]", aclRet);
acldvppFree(inBufferDev);
return false;
}
// 申请 输出图片 gpu 内存
void *g_picOutBufferDev = nullptr;
aclRet = acldvppMalloc(&g_picOutBufferDev, image_size);// 设置输出图片的大小
if (aclRet != 0) {
logs::error("g_picOutBufferDev acldvppMalloc fail! rrorCode = {}", static_cast<int32_t>(aclRet));
acldvppFree(inBufferDev);
return false;
}
// 创建 输出图片描述
acldvppPicDesc *picOutputDesc_ = acldvppCreatePicDesc();
if (picOutputDesc_ == nullptr) {
logs::error("decode acldvppCreatePicDesc fail! rrorCode = {}", static_cast<int32_t>(aclRet));
acldvppFree(inBufferDev);
return false;
}
// 设置 输出图片描述 属性
aclError ret = ACL_SUCCESS;
ret |= acldvppSetPicDescData(picOutputDesc_, g_picOutBufferDev);
ret |= acldvppSetPicDescSize(picOutputDesc_, image_size);
ret |= acldvppSetPicDescWidth(picOutputDesc_, static_cast<uint32_t>(width_));
ret |= acldvppSetPicDescHeight(picOutputDesc_, static_cast<uint32_t>(height_));
ret |= acldvppSetPicDescWidthStride(picOutputDesc_, static_cast<uint32_t>(width_stride_));
ret |= acldvppSetPicDescHeightStride(picOutputDesc_, static_cast<uint32_t>(height_stride_));
ret |= acldvppSetPicDescFormat(picOutputDesc_, format_);
if (ret != ACL_SUCCESS) {
logs::error("output image description set attribute failed");
acldvppFree(inBufferDev);
return false;
}
// 发送解码请求
aclRet = aclvdecSendFrame(vdecChannelDesc_, streamInputDesc_, picOutputDesc_, nullptr, userData.get());
if (aclRet != ACL_SUCCESS) {
logs::error("dvpp decoder send frame error : [{} {} {} {}]", packet->size, aclRet, packet_idx,
userData->image_id);
if (inBufferDev != nullptr) {
acldvppFree(inBufferDev);
inBufferDev = nullptr;
}
if (streamInputDesc_ != nullptr) {
acldvppDestroyStreamDesc(streamInputDesc_);
streamInputDesc_ = nullptr;
}
if (picOutputDesc_ != nullptr) {
acldvppDestroyPicDesc(picOutputDesc_);
picOutputDesc_ = nullptr;
}
if (g_picOutBufferDev != nullptr) {
acldvppFree(g_picOutBufferDev);
g_picOutBufferDev = nullptr;
}
close_decoder();
return false;
}
}
return true;
}
// 解码回调函数
// 回调函数,得到解码后的图像帧, out为解码后的图像帧,将 output 的数据 上传到 GxImage 对象中,完成解码的过程。
void decode_callback(acldvppStreamDesc *input, acldvppPicDesc *output, void *userdata) {
if (output != nullptr) {
UserData *userdata_ptr = (UserData *)userdata;
userdata_ptr->image_id++;
if (userdata_ptr->output_image_callback_) userdata_ptr->output_image_callback_(userdata_ptr->image_id, output);
} else {
logs::error("decode_callback output is nullptr");
}
if (input != nullptr) {
void *vdecInBufferDev = acldvppGetStreamDescData(input);
if (vdecInBufferDev != nullptr) {
aclError ret = acldvppFree(vdecInBufferDev);
vdecInBufferDev = nullptr;
}
aclError ret = acldvppDestroyStreamDesc(input);
input = nullptr;
} else {
logs::error("decode_callback failed, input is nullptr");
}
}
// 关闭解码器相关变量
void DvppDecoder::close_decoder() {
cb_open_flag = true;
threadData->thread_run_flag = false;
void *res = nullptr;
pthread_join(threadId_, &res);
av_bsf_free(&bsf_ctx_);
decoder_init_flag = false;
}
// 线程函数
void *ThreadFunc(void *arg) {
auto thread_data = *reinterpret_cast<std::shared_ptr<ThreadData> *>(arg);
if (thread_data->context == nullptr) {
logs::error("sharedContext can not be nullptr");
return reinterpret_cast<void *>(-1);
}
aclError ret = aclrtSetCurrentContext(thread_data->context);
if (ret != ACL_SUCCESS) {
logs::error("aclrtSetCurrentContext failed, errorCode = %d", static_cast<int32_t>(ret));
return reinterpret_cast<void *>(-1);
}
while (thread_data->thread_run_flag) {
// Notice: timeout 1000ms
aclError aclRet = aclrtProcessReport(1000);
}
return nullptr;
}
// 析构函数
DvppDecoder::~DvppDecoder() {
close_decoder();
if (vdecChannelDesc_ != nullptr) {
auto aclRet = aclvdecDestroyChannel(vdecChannelDesc_); // 会阻塞在这里
aclvdecDestroyChannelDesc(vdecChannelDesc_);
vdecChannelDesc_ = nullptr;
}
aclrtDestroyStream(stream_);
stream_ = nullptr;
aclrtDestroyContext(context_);
context_ = nullptr;
aclrtResetDevice(deviceId_);
aclFinalize();
}
aclvdecSendFrame函数并没有返回解码失败,看日志是码流异常导致解码失败,从而显存没有释放?具体是哪里有问题呢?
另外一个问题,析构函数经常阻塞在 auto aclRet = aclvdecDestroyChannel(vdecChannelDesc_); 这一行,这是什么原因呢?
npu日志见附件。// 打开解码器,初始化相关参数 bool DvppDecoder::open_decoder(const acldvppStreamFormat stream_type, const int width, const int height, const std::shared_ptr<AVCodecParameters> &codecpar, AVRational time_base) { if (decoder_init_flag) close_decoder();// 关闭之前的解码器 aclError aclRet = ACL_SUCCESS; // 第一次初始化 if (!cb_open_flag) { decoder_channelId = channelId++; aclError ret = aclInit(NULL); if (ret != 0) logs::error("dvpp decoder aclInit faile ! rrorCode = {}", static_cast<int32_t>(ret)); ret = aclrtSetDevice(deviceId_); if (ret != 0) { logs::error("aclrtSetDevice fail! rrorCode = {}", static_cast<int32_t>(ret)); } ret = aclrtCreateContext(&context_, deviceId_);// 0xffff2c0ec750 if (ret != 0) { logs::error("aclrtCreateContext fail! rrorCode = {}", static_cast<int32_t>(ret)); } ret = aclrtCreateStream(&stream_); if (ret != 0) { logs::error("aclrtCreateStream fail! rrorCode = {}", static_cast<int32_t>(ret)); } logs::info("dvpp decoder deviceId_ context_ stream_ [{} {} {}]", deviceId_, context_, stream_); // 创建通道描述,设置属性 vdecChannelDesc_ = aclvdecCreateChannelDesc(); if (vdecChannelDesc_ == nullptr) { logs::error("aclvdecCreateChannelDesc fail! rrorCode"); } cb_open_flag = true; } // H264 或者 H265 stream_type_ = stream_type; codecpar_ = codecpar; // 初始化 比特流过滤器 init_video_params(codecpar, time_base); threadData = std::make_shared<ThreadData>(); threadData->context = context_; pthread_create(&threadId_, nullptr, ThreadFunc, &threadData); if (vdecChannelDesc_ != nullptr) { aclRet |= aclvdecDestroyChannel(vdecChannelDesc_); if (aclRet != ACL_SUCCESS) { logs::error("aclvdecDestroyChannel fail! rrorCode = {}", static_cast<uint32_t>(aclRet)); } else logs::error("open_decoder aclvdecDestroyChannel success"); //aclvdecDestroyChannelDesc(vdecChannelDesc_); } // 设置通道属性 aclRet |= aclvdecSetChannelDescChannelId(vdecChannelDesc_, decoder_channelId); aclRet |= aclvdecSetChannelDescThreadId(vdecChannelDesc_, threadId_); aclRet |= aclvdecSetChannelDescChannelId(vdecChannelDesc_, decoder_channelId); aclRet |= aclvdecSetChannelDescCallback(vdecChannelDesc_, decode_callback); aclRet |= aclvdecSetChannelDescChannelId(vdecChannelDesc_, decoder_channelId); aclRet |= aclvdecSetChannelDescEnType(vdecChannelDesc_, stream_type_); aclRet |= aclvdecSetChannelDescOutPicFormat(vdecChannelDesc_, format_); aclRet |= aclvdecCreateChannel(vdecChannelDesc_); aclRet |= aclvdecSetChannelDescChannelId(vdecChannelDesc_, decoder_channelId); decoder_init_flag = true; return true; } // 解码函数 bool DvppDecoder::decode(const std::shared_ptr<AVPacket> &packet, int packet_idx, const Output_Image_Callback &output_image_callback) { aclError aclRet = ACL_SUCCESS; aclRet = aclrtSetCurrentContext(context_); if (aclRet != ACL_SUCCESS) { logs::error("decode aclrtSetCurrentContext failed, errorCode = %d", static_cast<int32_t>(aclRet)); return false; } // send video packet to ffmpeg if (av_bsf_send_packet(bsf_ctx_, packet.get())) { logs::error("Fail to call av_bsf_send_packet"); } // receive single frame from ffmpeg while ((av_bsf_receive_packet(bsf_ctx_, packet.get()) == 0)) { aclRet = acldvppMalloc(&inBufferDev, packet->size); aclRet |= aclrtMemcpy(inBufferDev, packet->size, packet->data, packet->size, ACL_MEMCPY_HOST_TO_DEVICE); if (aclRet != ACL_SUCCESS) { logs::error("decode acldvppMalloc failed : {}"); acldvppFree(inBufferDev); return false; } // Create input video stream description information, set the properties of the stream information acldvppStreamDesc *streamInputDesc_ = acldvppCreateStreamDesc(); if (streamInputDesc_ == nullptr) { logs::error("acldvppCreateStreamDesc fail! rrorCode"); return false; } // 设置 输入视频 gpu 内存 和 大小 aclRet |= acldvppSetStreamDescData(streamInputDesc_, inBufferDev); aclRet |= acldvppSetStreamDescSize(streamInputDesc_, packet->size); if (aclRet != ACL_SUCCESS) { logs::error("streamInputDesc set attribute fail! [{}]", aclRet); acldvppFree(inBufferDev); return false; } // 申请 输出图片 gpu 内存 void *g_picOutBufferDev = nullptr; aclRet = acldvppMalloc(&g_picOutBufferDev, image_size);// 设置输出图片的大小 if (aclRet != 0) { logs::error("g_picOutBufferDev acldvppMalloc fail! rrorCode = {}", static_cast<int32_t>(aclRet)); acldvppFree(inBufferDev); return false; } // 创建 输出图片描述 acldvppPicDesc *picOutputDesc_ = acldvppCreatePicDesc(); if (picOutputDesc_ == nullptr) { logs::error("decode acldvppCreatePicDesc fail! rrorCode = {}", static_cast<int32_t>(aclRet)); acldvppFree(inBufferDev); return false; } // 设置 输出图片描述 属性 aclError ret = ACL_SUCCESS; ret |= acldvppSetPicDescData(picOutputDesc_, g_picOutBufferDev); ret |= acldvppSetPicDescSize(picOutputDesc_, image_size); ret |= acldvppSetPicDescWidth(picOutputDesc_, static_cast<uint32_t>(width_)); ret |= acldvppSetPicDescHeight(picOutputDesc_, static_cast<uint32_t>(height_)); ret |= acldvppSetPicDescWidthStride(picOutputDesc_, static_cast<uint32_t>(width_stride_)); ret |= acldvppSetPicDescHeightStride(picOutputDesc_, static_cast<uint32_t>(height_stride_)); ret |= acldvppSetPicDescFormat(picOutputDesc_, format_); if (ret != ACL_SUCCESS) { logs::error("output image description set attribute failed"); acldvppFree(inBufferDev); return false; } // 发送解码请求 aclRet = aclvdecSendFrame(vdecChannelDesc_, streamInputDesc_, picOutputDesc_, nullptr, userData.get()); if (aclRet != ACL_SUCCESS) { logs::error("dvpp decoder send frame error : [{} {} {} {}]", packet->size, aclRet, packet_idx, userData->image_id); if (inBufferDev != nullptr) { acldvppFree(inBufferDev); inBufferDev = nullptr; } if (streamInputDesc_ != nullptr) { acldvppDestroyStreamDesc(streamInputDesc_); streamInputDesc_ = nullptr; } if (picOutputDesc_ != nullptr) { acldvppDestroyPicDesc(picOutputDesc_); picOutputDesc_ = nullptr; } if (g_picOutBufferDev != nullptr) { acldvppFree(g_picOutBufferDev); g_picOutBufferDev = nullptr; } close_decoder(); return false; } } return true; } // 解码回调函数 // 回调函数,得到解码后的图像帧, out为解码后的图像帧,将 output 的数据 上传到 GxImage 对象中,完成解码的过程。 void decode_callback(acldvppStreamDesc *input, acldvppPicDesc *output, void *userdata) { if (output != nullptr) { UserData *userdata_ptr = (UserData *)userdata; userdata_ptr->image_id++; if (userdata_ptr->output_image_callback_) userdata_ptr->output_image_callback_(userdata_ptr->image_id, output); } else { logs::error("decode_callback output is nullptr"); } if (input != nullptr) { void *vdecInBufferDev = acldvppGetStreamDescData(input); if (vdecInBufferDev != nullptr) { aclError ret = acldvppFree(vdecInBufferDev); vdecInBufferDev = nullptr; } aclError ret = acldvppDestroyStreamDesc(input); input = nullptr; } else { logs::error("decode_callback failed, input is nullptr"); } } // 关闭解码器相关变量 void DvppDecoder::close_decoder() { cb_open_flag = true; threadData->thread_run_flag = false; void *res = nullptr; pthread_join(threadId_, &res); av_bsf_free(&bsf_ctx_); decoder_init_flag = false; } // 线程函数 void *ThreadFunc(void *arg) { auto thread_data = *reinterpret_cast<std::shared_ptr<ThreadData> *>(arg); if (thread_data->context == nullptr) { logs::error("sharedContext can not be nullptr"); return reinterpret_cast<void *>(-1); } aclError ret = aclrtSetCurrentContext(thread_data->context); if (ret != ACL_SUCCESS) { logs::error("aclrtSetCurrentContext failed, errorCode = %d", static_cast<int32_t>(ret)); return reinterpret_cast<void *>(-1); } while (thread_data->thread_run_flag) { // Notice: timeout 1000ms aclError aclRet = aclrtProcessReport(1000); } return nullptr; } // 析构函数 DvppDecoder::~DvppDecoder() { close_decoder(); if (vdecChannelDesc_ != nullptr) { auto aclRet = aclvdecDestroyChannel(vdecChannelDesc_); // 会阻塞在这里 aclvdecDestroyChannelDesc(vdecChannelDesc_); vdecChannelDesc_ = nullptr; } aclrtDestroyStream(stream_); stream_ = nullptr; aclrtDestroyContext(context_); context_ = nullptr; aclrtResetDevice(deviceId_); aclFinalize(); }