#include "DvppStreamDecoder.h" struct Vdec_CallBack_UserData { uint64_t frameId; uint64_t frame_nb; long startTime; long sendTime; DvppDecoder* self; Vdec_CallBack_UserData() { frameId = 0; frame_nb = 0; } }; static void *ReportThd(void *arg) { DvppStreamDecoder *self = (DvppStreamDecoder *)arg; if(nullptr != self){ self->doProcessReport(); } return (void *)0; } static void VdecCallback(acldvppStreamDesc *input, acldvppPicDesc *output, void *pUserData) { Vdec_CallBack_UserData *userData = (Vdec_CallBack_UserData *) pUserData; if(nullptr != userData){ DvppStreamDecoder* self = userData->self; if(self != nullptr){ self->doVdppVdecCallBack(input, output, userData); } delete userData; userData = nullptr; } } DvppStreamDecoder::DvppStreamDecoder(/* args */) { } DvppStreamDecoder::~DvppStreamDecoder() { } bool DvppStreamDecoder::init_vdpp(FFDecConfig cfg) { LOG_INFO("[{}]- Init device start...", m_dec_name); m_deviceId = atoi(cfg.gpuid.c_str()); do{ aclError ret = aclrtSetDevice(m_deviceId); if(ret != ACL_ERROR_NONE){ LOG_ERROR("[{}]-aclrtSetDevice failed !", m_dec_name); return false; } ret = aclrtCreateContext(&m_context, m_deviceId); if (ret != ACL_ERROR_NONE) { LOG_ERROR("[{}]-aclrtCreateContext failed !", m_dec_name); return false; } // DvppSourceManager 创建时包含 aclInit,析构时包含 aclFinalize DvppSourceManager* pSrcMgr = DvppSourceManager::getInstance(); m_dvpp_channel = pSrcMgr->getChannel(m_deviceId); if(m_dvpp_channel < 0){ LOG_ERROR("[{}]-该设备channel已经用完了!", m_dec_name); return false; } m_vpcUtils.init(m_deviceId); LOG_INFO("[{}]- init vdpp success! device:{} channel:{}", m_dec_name, m_deviceId, m_dvpp_channel); return true; }while(0); release_dvpp(); return false; } void DvppStreamDecoder::release_dvpp(){ if(m_context){ aclError ret = aclrtDestroyContext(m_context); if(ret != ACL_ERROR_NONE){ LOG_ERROR("[{}]- aclrtDestroyContext failed !", m_dec_name); } m_context = nullptr; } if(m_dvpp_channel >= 0){ DvppSourceManager* pSrcMgr = DvppSourceManager::getInstance(); pSrcMgr->releaseChannel(m_deviceId, m_dvpp_channel); m_dvpp_channel = -1; } } int DvppStreamDecoder::getVdecType(int videoType) { int streamFormat = H264_MAIN_LEVEL; // VDEC only support H265 main level,264 baseline level,main level,high level if (videoType == 1) { streamFormat = H265_MAIN_LEVEL; } else if (videoType == 0) { streamFormat = H264_MAIN_LEVEL; } else { streamFormat = -1; LOG_ERROR("Not support stream, type {}", videoType); } return streamFormat; } void DvppStreamDecoder::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 ; } while (!m_bExitReportThd) { aclrtProcessReport(1000); } ret = aclrtDestroyContext(ctx); if(ret != ACL_ERROR_NONE){ LOG_ERROR("aclrtDestroyContext failed !"); } LOG_INFO("doProcessReport exit."); } void DvppStreamDecoder::doVdppVdecCallBack(acldvppStreamDesc *input, acldvppPicDesc *output, void *pUserData) { // 内部缓存计数减1 m_DvppCacheCounter--; if(nullptr == pUserData){ return; } Vdec_CallBack_UserData *userData = (Vdec_CallBack_UserData *) pUserData; uint64_t frame_nb = userData->frame_nb; m_out_count++; CHECK_AND_RETURN_NOVALUE(aclrtSetCurrentContext(m_context), "aclrtSetCurrentContext failed"); void *inputDataDev = acldvppGetStreamDescData(input); acldvppFree(inputDataDev); inputDataDev = nullptr; 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); do{ int ret = acldvppGetPicDescRetCode(output); if(ret != ACL_ERROR_NONE){ LOG_ERROR("[{}]- decode result error, retCode:{} ", m_dec_name, ret); acldvppFree(outputDataDev); outputDataDev = nullptr; break; } bool bCached = false; if(width > 0 && height > 0 && outputSize > 0){ if (!m_bReal) { while(!m_bExitReportThd) { // 非实时流,即为文件情形,因为不存在花屏问题,为保证不丢帧,这里做个循环等待 m_decoded_data_queue_mtx.lock(); if(m_decoded_data_queue.size() >= 5){ m_decoded_data_queue_mtx.unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(5)); continue; } m_decoded_data_queue_mtx.unlock(); break; } } // cout << m_dec_name << " 解码时间间隔: " << get_cur_time_ms() - last_ts << endl; // last_ts = get_cur_time_ms(); // 换成解码后数据, 这里这样做的是为了保证解码一直持续进行,避免后续操作阻碍文件读取和解码从而导致花屏 DvppDataMemory* mem = nullptr; if (m_bResize && (width > 1920 || height > 1080)) { float srcRatio = width / (float)height; float stdRatio = 1920.0 / 1080.0f ; int outWidth = 1920; int outHeight = 1080; if (srcRatio > stdRatio) { outHeight = static_cast(outWidth * (float)height / width) ; if (outHeight % 2 == 1) { outHeight += 1; } } else if (srcRatio < stdRatio) { outWidth = static_cast(outHeight * (float)width / height) ; if (outWidth % 2 == 1) { outWidth += 1; } } mem = m_vpcUtils.resize(output, outWidth, outHeight); if (mem) { acldvppFree(outputDataDev); outputDataDev = nullptr; mem->setDeviceId(to_string(m_dvpp_deviceId)); mem->setId(m_dec_name); mem->setFrameNb(frame_nb); } } else { mem = new DvppDataMemory(width, width_stride, height, height_stride, outputSize, m_dec_name, to_string(m_dvpp_deviceId), false, frame_nb, (unsigned char *)outputDataDev); } if(mem){ m_decoded_data_queue.push(mem); bCached = true; } } if(!bCached) { LOG_WARN("[{}]- decode result warning, width:{} width_stride:{} height:{} height_stride:{} size:{}", m_dec_name, width, width_stride, height, height_stride, outputSize); acldvppFree(outputDataDev); outputDataDev = nullptr; } }while(0); CHECK_AND_RETURN_NOVALUE(acldvppDestroyStreamDesc(input), "acldvppDestroyStreamDesc failed"); CHECK_AND_RETURN_NOVALUE(acldvppDestroyPicDesc(output), "acldvppDestroyPicDesc failed"); } bool DvppStreamDecoder::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; } int DvppStreamDecoder::sendPkt(aclvdecChannelDesc *vdecChannelDesc, AVPacket* pkt, unsigned long long frame_nb) { void *vdecInputbuf = nullptr; void *vdecOutputBuf = nullptr; acldvppStreamDesc *input_stream_desc = nullptr; acldvppPicDesc *output_pic_desc = nullptr; do{ int ret = acldvppMalloc((void **)&vdecInputbuf, pkt->size); if(ACL_ERROR_NONE != ret){ LOG_ERROR("[{}]- acldvppMalloc failed!, ret:{}", m_dec_name, ret); break; } 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); break; } ret = acldvppMalloc((void **)&vdecOutputBuf, m_vdec_out_size); if(ret != ACL_ERROR_NONE){ LOG_ERROR("[{}]- acldvppMalloc failed", m_dec_name); break; } 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_nb; user_data->frame_nb = frame_nb; // user_data->startTime = startTime; user_data->sendTime = UtilTools::get_cur_time_ms(); user_data->self = this; m_in_count++; // 内部缓存计数加1 m_DvppCacheCounter++; ret = aclvdecSendFrame(vdecChannelDesc, input_stream_desc, output_pic_desc, nullptr, reinterpret_cast(user_data)); if(ret != ACL_ERROR_NONE){ LOG_ERROR("[{}]- aclvdecSendFrame failed", m_dec_name); delete user_data; user_data = nullptr; return -2; } return 0; }while (0); if (vdecInputbuf){ acldvppFree(vdecInputbuf); vdecInputbuf = nullptr; } // 报错情形 if(input_stream_desc){ CHECK_NOT_RETURN(acldvppDestroyStreamDesc(input_stream_desc), "acldvppDestroyStreamDesc failed"); } if (vdecOutputBuf){ acldvppFree(vdecOutputBuf); vdecOutputBuf = nullptr; } if(output_pic_desc){ CHECK_NOT_RETURN(acldvppDestroyPicDesc(output_pic_desc), "acldvppDestroyPicDesc failed"); } return -1; } int DvppStreamDecoder::Decode(int videoType, char* data, int len, int isKey, uint64_t pts) { if (vdecChannelDesc == nullptr) { vdecChannelDesc = aclvdecCreateChannelDesc(); if (vdecChannelDesc == nullptr) { LOG_ERROR("[{}]- aclvdecCreateChannelDesc failed", m_dec_name); return; } pthread_t report_thread; int ret = pthread_create(&report_thread, nullptr, ReportThd, (void *)this); if(ret != 0){ LOG_ERROR("[{}]- pthread_create failed", m_dec_name); return; } acldvppStreamFormat enType = getVdecType(videoType); // 创建 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"); } if (vdecChannelDesc) { m_frame_nb++; AVPacket* pkt = av_packet_alloc(); av_init_packet(pkt); pkt->size = len; pkt->data = (uint8_t*)data; // dvpp 解码 int nSended = sendPkt(vdecChannelDesc, pkt, m_frame_nb); av_packet_free(&pkt); pkt = nullptr; } }