vdec失败,内存增长
收藏回复举报
vdec失败,内存增长
t('forum.solved') 已解决
新人帖
发表于2024-05-23 14:58:58
0 查看
Atlas 300V pro卡使用ffmpeg解码多路视频并且推理,每路循环解码一个视频,不同路解码不同的视频。一开始能够成功解码每一路视频,解码完成后重复解码这段视频,4路视频稳定占用显存600MB左右。解码了一段时间后,显存突然增加很快,最后增长到16个G,总共内存是21527MB。

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();
}

我要发帖子