#include "DvppDec.h" #include "DvppSourceManager.h" #include "../../util/vpc_util.h" struct Vdec_CallBack_UserData { uint64_t frameId; long startTime; long sendTime; // void* vdecOutputBuf; DvppDec* self; shared_ptr inBufNode; Vdec_CallBack_UserData() { frameId = 0; } }; static const int g_pkt_size = 1024 * 1024; DvppDec::DvppDec(){ m_decode_thread = 0; m_cached_mem = nullptr; } DvppDec::~DvppDec(){ releaseResource(); } bool DvppDec::init_vdpp(DvppDecConfig cfg){ m_dec_name = cfg.dec_name; LOG_INFO("[{}]- Init device start...", m_dec_name); m_dvpp_deviceId = atoi(cfg.dev_id.c_str()); if(cfg.codec_id == 0){ // 66:Baseline,77:Main,>=100:High if(cfg.profile == 77){ enType = H264_MAIN_LEVEL; }else if(cfg.profile < 77){ enType = H264_BASELINE_LEVEL; }else{ enType = H264_HIGH_LEVEL; } }else if(cfg.codec_id == 1){ // h265只有main enType = H265_MAIN_LEVEL; }else { LOG_ERROR("[{}]- codec_id is not supported!", m_dec_name); return false; } post_decoded_cbk = cfg.post_decoded_cbk; m_pktQueueptr = cfg.pktQueueptr; // DvppSourceManager 创建时包含 aclInit,析构时包含 aclFinalize DvppSourceManager* pSrcMgr = DvppSourceManager::getInstance(); m_context = pSrcMgr->getContext(m_dvpp_deviceId); m_dvpp_channel = pSrcMgr->getChannel(m_dvpp_deviceId); if(m_dvpp_channel < 0){ LOG_ERROR("[{}]-该设备channel已经用完了!", m_dec_name); return false; } do { CHECK_AND_BREAK(aclrtSetCurrentContext(m_context), "aclrtSetCurrentContext failed !"); int ret = 0; // int ret = picConverter.init(m_context, m_dec_name); // if(ret != ACL_ERROR_NONE){ // LOG_ERROR("[{}]- acldvppMalloc failed!, ret:{}", m_dec_name, ret); // break; // } // queue_size 最小应大于16,否则关键帧之间距离太远的时候会导致回调函数与循环队列卡死 for (size_t i = 0; i < 20; i++){ void *vdecInputbuf = nullptr; ret = acldvppMalloc((void **)&vdecInputbuf, g_pkt_size); if(ret != ACL_ERROR_NONE){ LOG_ERROR("[{}]- acldvppMalloc failed!, ret:{}", m_dec_name, ret); // 析构函数中有对channel 的补救性释放,所以这里可以直接return return false;; } m_vec_vdec.push_back(vdecInputbuf); } if(!m_vdecQueue.init(m_vec_vdec)){ break; } m_vdec_out_size = cfg.width * cfg.height * 3 / 2; LOG_INFO("[{}]- init vdpp success! device:{} channel:{}", m_dec_name, m_dvpp_deviceId, m_dvpp_channel); return true; } while (0); LOG_INFO("[{}]- init vdpp failed!", m_dec_name); // 初始化失败,释放channel pSrcMgr->releaseChannel(m_dvpp_deviceId, m_dvpp_channel); return false; } bool DvppDec::start(){ m_bRunning = true; pthread_create(&m_decode_thread,0, [](void* arg) { DvppDec* a=(DvppDec*)arg; a->decode_thread(); return (void*)0; } ,this); return true; } static void *ReportThd(void *arg) { DvppDec *self = (DvppDec *)arg; if(nullptr != self){ self->doProcessReport(); } return (void *)0; } void DvppDec::doProcessReport(){ aclError ret = aclrtSetDevice(m_dvpp_deviceId); if(ret != ACL_ERROR_NONE){ // cout << "aclrtSetDevice failed" << endl; LOG_ERROR("aclrtSetDevice failed !"); return ; } aclrtContext ctx; ret = aclrtCreateContext(&ctx, m_dvpp_deviceId); if (ret != ACL_ERROR_NONE) { // cout << "aclrtCreateContext failed " << endl; LOG_ERROR("aclrtCreateContext failed !"); return ; } CHECK_AND_RETURN_NOVALUE(aclrtSetCurrentContext(ctx), "aclrtSetCurrentContext failed"); // 阻塞等待vdec线程开始 while (!m_bExitReportThd) { aclrtProcessReport(1000); } ret = aclrtDestroyContext(ctx); if(ret != ACL_ERROR_NONE){ LOG_ERROR("aclrtDestroyContext failed !"); } LOG_INFO("doProcessReport exit."); } static void VdecCallback(acldvppStreamDesc *input, acldvppPicDesc *output, void *pUserData) { Vdec_CallBack_UserData *userData = (Vdec_CallBack_UserData *) pUserData; if(nullptr != userData){ DvppDec* self = userData->self; if(self != nullptr){ self->doVdppVdecCallBack(input, output); } delete userData; userData = nullptr; } } void DvppDec::doVdppVdecCallBack(acldvppStreamDesc *input, acldvppPicDesc *output){ // dvpp_crop(output); CHECK_AND_RETURN_NOVALUE(aclrtSetCurrentContext(m_context), "aclrtSetCurrentContext failed"); void *inputDataDev = acldvppGetStreamDescData(input); void *outputDataDev = acldvppGetPicDescData(output); uint32_t outputSize = acldvppGetPicDescSize(output); uint32_t width = acldvppGetPicDescWidth(output); uint32_t width_stride = acldvppGetPicDescWidthStride(output); uint32_t height = acldvppGetPicDescHeight(output); uint32_t height_stride = acldvppGetPicDescHeightStride(output); DvppDataMemory* mem = new DvppDataMemory(width, width_stride, height, height_stride, outputSize, m_dec_name, to_string(m_dvpp_deviceId), false, (unsigned char *)outputDataDev); post_decoded_cbk(m_postDecArg, mem); if(m_bSnapShoting){ // 缓存snapshot std::unique_lock locker(m_cached_mutex); m_cached_mem = new DvppDataMemory(-1, width, width_stride, height, height_stride, outputSize, m_dec_name, to_string(m_dvpp_deviceId), false); if(m_cached_mem != nullptr){ aclrtMemcpy(m_cached_mem->getMem(), outputSize, (unsigned char *)outputDataDev, outputSize, ACL_MEMCPY_DEVICE_TO_DEVICE); } locker.unlock(); m_cached_cond.notify_one(); m_bSnapShoting = false; } // DvppDataMemory* rgbMem = picConverter.convert2bgr(output, width, height, false); // if(rgbMem != nullptr){ // #ifdef TEST_DECODER // // D2H // if(vdecHostAddr == nullptr){ // CHECK_NOT_RETURN(aclrtMallocHost(&vdecHostAddr, width * height * 3), "aclrtMallocHost failed"); // } // uint32_t data_size = rgbMem->getSize(); // CHECK_AND_RETURN_NOVALUE(aclrtMemcpy(vdecHostAddr, data_size, rgbMem->getMem(), data_size, ACL_MEMCPY_DEVICE_TO_HOST), "D2H aclrtMemcpy failed"); // // 保存vdec结果 // if(count_frame > 45 && count_frame < 50) // { // string file_name = "./yuv_pic/vdec_out_"+ m_dec_name +".rgb" ; // FILE *outputFile = fopen(file_name.c_str(), "a"); // if(outputFile){ // fwrite(vdecHostAddr, data_size, sizeof(char), outputFile); // fclose(outputFile); // } // } // else if(count_frame > 50 && vdecHostAddr != nullptr){ // CHECK_NOT_RETURN(aclrtFreeHost(vdecHostAddr), "aclrtFreeHost failed"); // vdecHostAddr = nullptr; // } // count_frame++; // #endif // post_decoded_cbk(m_postDecArg, rgbMem); // }else{ // LOG_ERROR("[{}]- convert2bgr failed !", m_dec_name); // } // // 测试 // acldvppFree(outputDataDev); // outputDataDev = nullptr; m_vdecQueue.addHead(); CHECK_AND_RETURN_NOVALUE(acldvppDestroyStreamDesc(input), "acldvppDestroyStreamDesc failed"); CHECK_AND_RETURN_NOVALUE(acldvppDestroyPicDesc(output), "acldvppDestroyPicDesc failed"); } void DvppDec::close(){ m_bRunning=false; if(m_decode_thread != 0){ pthread_join(m_decode_thread,0); } } bool DvppDec::sendVdecEos(aclvdecChannelDesc *vdecChannelDesc){ // create stream desc acldvppStreamDesc *streamInputDesc = acldvppCreateStreamDesc(); if (streamInputDesc == nullptr) { LOG_ERROR("[{}]- fail to create input stream desc", m_dec_name); return false; } aclError ret = acldvppSetStreamDescEos(streamInputDesc, 1); if (ret != ACL_SUCCESS) { LOG_ERROR("[{}]- fail to set eos for stream desc, errorCode = {}", m_dec_name, static_cast(ret)); (void)acldvppDestroyStreamDesc(streamInputDesc); return false; } // send vdec eos frame. when all vdec callback are completed, aclvdecSendFrame can be returned. LOG_INFO("[{}]- send eos", m_dec_name); ret = aclvdecSendFrame(vdecChannelDesc, streamInputDesc, nullptr, nullptr, nullptr); (void)acldvppDestroyStreamDesc(streamInputDesc); if (ret != ACL_SUCCESS) { LOG_ERROR("[{}]- fail to send eos frame, ret={}", m_dec_name, ret); return false; } return true; } void DvppDec::releaseResource(){ for(int i = 0; i < m_vec_vdec.size(); i++){ if(m_vec_vdec[i] != nullptr){ acldvppFree(m_vec_vdec[i]); m_vec_vdec[i] = nullptr; } } m_vec_vdec.clear(); DvppSourceManager* pSrcMgr = DvppSourceManager::getInstance(); pSrcMgr->releaseChannel(m_dvpp_deviceId, m_dvpp_channel); } void DvppDec::decode_thread(){ long startTime = UtilTools::get_cur_time_ms(); int ret = -1; // // dvpp解码参数 // CHECK_AND_RETURN_NOVALUE(aclrtSetCurrentContext(m_context), "aclrtSetCurrentContext failed"); m_bExitReportThd = false; pthread_t report_thread; ret = pthread_create(&report_thread, nullptr, ReportThd, (void *)this); if(ret != 0){ LOG_ERROR("[{}]- pthread_create failed", m_dec_name); return; } aclrtSetDevice(m_dvpp_deviceId); aclrtContext ctx; ret = aclrtCreateContext(&ctx, m_dvpp_deviceId); if (ret != ACL_ERROR_NONE) { // cout << "aclrtCreateContext failed " << endl; LOG_ERROR("aclrtCreateContext failed !"); return ; } // 创建aclvdecChannelDesc类型的数据 aclvdecChannelDesc *vdecChannelDesc = aclvdecCreateChannelDesc(); if (vdecChannelDesc == nullptr) { LOG_ERROR("[{}]- aclvdecCreateChannelDesc failed", m_dec_name); return; } do{ // 创建 channel dec结构体 // 通道ID在dvpp层面为0~31 CHECK_AND_BREAK(aclvdecSetChannelDescChannelId(vdecChannelDesc, m_dvpp_channel), "aclvdecSetChannelDescChannelId failed"); CHECK_AND_BREAK(aclvdecSetChannelDescThreadId(vdecChannelDesc, report_thread), "aclvdecSetChannelDescThreadId failed"); CHECK_AND_BREAK(aclvdecSetChannelDescCallback(vdecChannelDesc, VdecCallback), "aclvdecSetChannelDescCallback failed"); CHECK_AND_BREAK(aclvdecSetChannelDescEnType(vdecChannelDesc, enType), "aclvdecSetChannelDescEnType failed"); CHECK_AND_BREAK(aclvdecSetChannelDescOutPicFormat(vdecChannelDesc, PIXEL_FORMAT_YUV_SEMIPLANAR_420), "aclvdecSetChannelDescOutPicFormat failed"); CHECK_AND_BREAK(aclvdecCreateChannel(vdecChannelDesc), "aclvdecCreateChannel failed"); uint64_t frame_count = 0; bool bBreak = false; while (m_bRunning) { if (m_bPause){ std::this_thread::sleep_for(std::chrono::milliseconds(3)); continue; } int ret = sentFrame(vdecChannelDesc, frame_count); if(ret == 2){ break; bBreak = true; }else if(ret == 1){ continue; } frame_count++; } // 尽量保证数据全部解码完成 int sum = 0; if(!bBreak){ aclrtSetDevice(m_dvpp_deviceId); aclrtSetCurrentContext(ctx); while(!m_pktQueueptr->isEmpty()){ int ret = sentFrame(vdecChannelDesc, frame_count); if(ret == 2){ break; } std::this_thread::sleep_for(std::chrono::milliseconds(3)); sum++; if(sum > 40){ // 避免卡死 break; } } } sendVdecEos(vdecChannelDesc); CHECK_NOT_RETURN(aclvdecDestroyChannel(vdecChannelDesc), "aclvdecDestroyChannel failed"); }while(0); CHECK_NOT_RETURN(aclvdecDestroyChannelDesc(vdecChannelDesc), "aclvdecDestroyChannelDesc failed"); // report_thread 需后于destroy退出 m_bRunning = false; m_bExitReportThd = true; CHECK_NOT_RETURN(pthread_join(report_thread, nullptr), "pthread_join failed"); releaseResource(); LOG_INFO("[{}]- decode thread exit.", m_dec_name); } int DvppDec::sentFrame(aclvdecChannelDesc *vdecChannelDesc, uint64_t frame_count){ AVPacket * pkt = m_pktQueueptr->getHead(); if(pkt == nullptr){ std::this_thread::sleep_for(std::chrono::milliseconds(10)); return 1; } // 解码 void *vdecInputbuf = m_vdecQueue.getTail(); if(vdecInputbuf == nullptr){ std::this_thread::sleep_for(std::chrono::milliseconds(3)); return 1; } int ret = aclrtMemcpy(vdecInputbuf, pkt->size, pkt->data, pkt->size, ACL_MEMCPY_HOST_TO_DEVICE); if(ACL_ERROR_NONE != ret){ LOG_ERROR("[{}]- aclrtMemcpy failed", m_dec_name); return 2; } void *vdecOutputBuf = nullptr; ret = acldvppMalloc((void **)&vdecOutputBuf, m_vdec_out_size); if(ret != ACL_ERROR_NONE){ LOG_ERROR("[{}]- acldvppMalloc failed", m_dec_name); return 2; } acldvppStreamDesc *input_stream_desc = nullptr; acldvppPicDesc *output_pic_desc = nullptr; do{ input_stream_desc = acldvppCreateStreamDesc(); if (input_stream_desc == nullptr) { LOG_ERROR("[{}]- acldvppCreateStreamDesc failed", m_dec_name); break; } output_pic_desc = acldvppCreatePicDesc(); if (output_pic_desc == nullptr) { LOG_ERROR("[{}]- acldvppCreatePicDesc failed", m_dec_name); break; } CHECK_AND_BREAK(acldvppSetStreamDescData(input_stream_desc, vdecInputbuf), "acldvppSetStreamDescData failed"); CHECK_AND_BREAK(acldvppSetStreamDescSize(input_stream_desc, pkt->size), "acldvppSetStreamDescSize failed"); CHECK_AND_BREAK(acldvppSetPicDescData(output_pic_desc, vdecOutputBuf), "acldvppSetPicDescData failed"); CHECK_AND_BREAK(acldvppSetPicDescSize(output_pic_desc, m_vdec_out_size), "acldvppSetPicDescSize failed"); Vdec_CallBack_UserData *user_data = NULL; user_data = new Vdec_CallBack_UserData; user_data->frameId = frame_count; // user_data->startTime = startTime; user_data->sendTime = UtilTools::get_cur_time_ms(); user_data->self = this; ret = aclvdecSendFrame(vdecChannelDesc, input_stream_desc, output_pic_desc, nullptr, reinterpret_cast(user_data)); av_packet_unref(pkt); m_pktQueueptr->addHead(); if(ret != ACL_ERROR_NONE){ delete user_data; user_data = nullptr; LOG_ERROR("[{}]- aclvdecSendFrame failed", m_dec_name); break; } m_vdecQueue.addTail(); return 0; }while (0); // 报错情形 if(input_stream_desc){ CHECK_NOT_RETURN(acldvppDestroyStreamDesc(input_stream_desc), "acldvppDestroyStreamDesc failed"); } if(output_pic_desc){ CHECK_NOT_RETURN(acldvppDestroyPicDesc(output_pic_desc), "acldvppDestroyPicDesc failed"); } if (vdecOutputBuf){ acldvppFree(vdecOutputBuf); vdecOutputBuf = nullptr; } return 1; } void DvppDec::setPostDecArg(const void* postDecArg){ m_postDecArg = postDecArg; } void DvppDec::pause(){ m_bPause = true; } void DvppDec::resume(){ m_bPause = false; } DeviceMemory* DvppDec::snapshot(){ // 开始抓拍 m_bSnapShoting = true; std::unique_lock locker(m_cached_mutex); while (m_cached_mem == nullptr) m_cached_cond.wait(locker); // Unlock mutex and wait to be notified locker.unlock(); DeviceMemory* mem = m_cached_mem; m_cached_mem = nullptr; return mem; }