#include "DvppDecoder.h" #include "DvppSourceManager.h" #define CHECK_AND_RETURN(ret, message) \ if(ret != 0) {cout << "device: " << m_dvpp_deviceId << ", chn: " << m_dvpp_channel << ", ret: " << ret << ", [ERROR] " << message; return ret;} #define CHECK_NOT_RETURN(ret, message) \ if(ret != 0) {cout << "device: " << m_dvpp_deviceId << ", chn: " << m_dvpp_channel << ", ret: " << ret << ", [ERROR] " << message;} #define CHECK_AND_RETURN_NOVALUE(ret, message) \ if(ret != 0) {cout << "device: " << m_dvpp_deviceId << ", chn: " << m_dvpp_channel << ", ret: " << ret << ", [ERROR] " << message; return;} struct Vdec_CallBack_UserData { uint64_t frameId; long startTime; long sendTime; // void* vdecOutputBuf; DvppDecoder* self; shared_ptr inBufNode; Vdec_CallBack_UserData() { frameId = 0; } }; const int g_pkt_que_size = 10; const int g_pkt_size = 1024 * 1024; #ifdef TEST_DECODER void *vdecHostAddr; #endif static long GetCurTimeUs(){ chrono::time_point tpMicro = chrono::time_point_cast(chrono::system_clock::now()); return tpMicro.time_since_epoch().count(); } DvppDecoder::DvppDecoder() { // 初始化解码对象 fmt_ctx = nullptr; m_bRunning = false; stream = nullptr; stream_index = -1; pix_fmt = AV_PIX_FMT_NONE; m_dec_name = ""; m_bPause = false; m_bReal = true; m_decode_thread = 0; m_post_decode_thread = 0; m_bFinished = false; m_dec_keyframe = false; m_fps = 0.0; } DvppDecoder::~DvppDecoder() { m_dec_keyframe = false; releaseResource(); } bool DvppDecoder::init_FFmpeg(const char* uri, bool force_tcp){ #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100) av_register_all(); #endif #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100) avcodec_register_all(); #endif avformat_network_init(); // 打开输入视频文件 AVDictionary *options = nullptr; av_dict_set( &options, "bufsize", "655360", 0 ); av_dict_set( &options, "rtsp_transport", force_tcp ? "tcp" : "udp", 0 ); // av_dict_set( &options, "listen_timeout", "30", 0 ); // 单位为s av_dict_set( &options, "stimeout", "30000000", 0 ); // 单位为 百万分之一秒 fmt_ctx = avformat_alloc_context(); const char* input_file = uri; if (avformat_open_input(&fmt_ctx, input_file, nullptr, &options) != 0) { cout << "Cannot open input file:" << input_file << endl; return false; } av_dump_format(fmt_ctx, 0, input_file, 0); // 查找流信息 if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) { cout << "Cannot find input stream information" << endl; return false; } // 查找视频流信息 AVCodec *decoder = nullptr; stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0); if (stream_index < 0) { cout << "Cannot find a video stream in the input file" << endl; return false; } AVCodec *vcodec = avcodec_find_decoder(decoder->id); AVCodecContext *avctx = avcodec_alloc_context3(vcodec); if(avctx == nullptr){ cout << "alloc AVCodecContext failed." << endl; return false; } do{ // 得到视频流对象 AVStream* stream = fmt_ctx->streams[stream_index]; AVCodecParameters *codecpar = stream->codecpar; if (avcodec_parameters_to_context(avctx, codecpar) < 0) break; const AVBitStreamFilter * filter = nullptr; if(codecpar->codec_id == AV_CODEC_ID_H264){ // 66:Baseline,77:Main,>=100:High if(codecpar->profile == 77){ enType = H264_MAIN_LEVEL; }else if(codecpar->profile < 77){ enType = H264_BASELINE_LEVEL; }else{ enType = H264_HIGH_LEVEL; } filter = av_bsf_get_by_name("h264_mp4toannexb"); }else if(codecpar->codec_id == AV_CODEC_ID_HEVC){ // h265只有main enType = H265_MAIN_LEVEL; filter = av_bsf_get_by_name("hevc_mp4toannexb"); }else { cout << "codec_id is not supported!" << endl; break; } int ret = av_bsf_alloc(filter, &h264bsfc); if (ret < 0){ break; } avcodec_parameters_copy(h264bsfc->par_in, codecpar); av_bsf_init(h264bsfc); frame_width = codecpar->width; frame_height = codecpar->height; pix_fmt = (AVPixelFormat)codecpar->format; m_fps = av_q2d(stream ->avg_frame_rate); m_vdec_out_size = frame_width * frame_height * 3 /2; cout << "frame_width = " << frame_width << " frame_height = " << frame_height << " fps = " << m_fps << " m_vdec_out_size:" << m_vdec_out_size << endl; cout << "init ffmpeg success!" << endl; return true; }while(0); avcodec_free_context(&avctx); return false; } static void *ReportThd(void *arg) { DvppDecoder *self = (DvppDecoder *)arg; if(nullptr != self){ self->doProcessReport(); } return (void *)0; } void DvppDecoder::doProcessReport(){ // aclrtContext thdContext = nullptr; // CHECK_AND_RETURN_NOVALUE(aclrtCreateContext(&thdContext, m_dvpp_deviceId), "aclrtCreateContext failed"); CHECK_AND_RETURN_NOVALUE(aclrtSetCurrentContext(m_context), "aclrtSetCurrentContext failed"); // 阻塞等待vdec线程开始 int ret; while (m_bRunning) { ret = aclrtProcessReport(1000); if (ret != ACL_ERROR_NONE) { cout << "device: " << m_dvpp_deviceId << ", chn: " << m_dvpp_channel << ", aclrtProcessReport failed, ret: " << ret << endl; } } // CHECK_AND_RETURN_NOVALUE(aclrtDestroyContext(thdContext), "aclrtDestroyContext failed"); } int count_frame = 0; long lastts = 0; static void VdecCallback(acldvppStreamDesc *input, acldvppPicDesc *output, void *pUserData) { cout << "VdecCallback: " << GetCurTimeUs() - lastts << endl; lastts = GetCurTimeUs(); Vdec_CallBack_UserData *userData = (Vdec_CallBack_UserData *) pUserData; DvppDecoder* self = userData->self; if(self != nullptr){ self->doVdppVdecCallBack(input, output, self); } delete userData; userData = nullptr; } void DvppDecoder::doVdppVdecCallBack(acldvppStreamDesc *input, acldvppPicDesc *output, DvppDecoder *self){ 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 height = acldvppGetPicDescHeight(output); cout << "width = " << width << " height = " << height << " data_size:" << outputSize << endl; if (!m_bPause) { DeviceRgbMemory* rgbMem = picConverter.convert2bgr(output, width, height, false); #ifdef TEST_DECODER if(rgbMem != nullptr){ // D2H 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"+ getName() +".rgb" ; FILE *outputFile = fopen(file_name.c_str(), "a"); if(outputFile){ fwrite(vdecHostAddr, data_size, sizeof(char), outputFile); fclose(outputFile); } } count_frame++; } #endif }else{ std::this_thread::sleep_for(std::chrono::milliseconds(3)); } cout << "callback acldvppFree." << endl; acldvppFree((uint8_t*)outputDataDev); outputDataDev = nullptr; m_vdecQueue.addHead(); CHECK_AND_RETURN_NOVALUE(acldvppDestroyStreamDesc(input), "acldvppDestroyStreamDesc failed"); CHECK_AND_RETURN_NOVALUE(acldvppDestroyPicDesc(output), "acldvppDestroyPicDesc failed"); cout << "callback exit." << endl; } bool DvppDecoder::init_vdpp(int devId){ cout << "Init device....\n"; // 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){ cout << "该设备channel已经用完了" << endl; return false; } cout << "devProgram start, device: " << m_dvpp_deviceId << endl; int ret = aclrtSetCurrentContext(m_context); if (ret != ACL_ERROR_NONE) { cout << "aclrtSetCurrentContext failed" << endl; return false; } // queue_size 最小应大于16,否则关键帧之间距离太远的时候会导致回调函数与循环队列卡死 for (size_t i = 0; i < 20; i++){ void *vdecInputbuf = nullptr; int ret = acldvppMalloc((void **)&vdecInputbuf, g_pkt_size); if(ret != ACL_ERROR_NONE){ cout << "acldvppMalloc failed" << endl; return false;; } m_vec_vdec.push_back(vdecInputbuf); } if(!m_vdecQueue.init(m_vec_vdec)){ return false; } #ifdef TEST_DECODER CHECK_NOT_RETURN(aclrtMallocHost(&vdecHostAddr, frame_width * frame_height * 3), "aclrtMallocHost failed"); #endif cout << "init vdpp success!" << endl; return true; } bool DvppDecoder::init(FFDecConfig& cfg){ m_cfg = cfg; fstream infile(cfg.uri); if (infile.is_open()){ m_bReal = false; infile.close(); }else { m_bReal = true; } post_decoded_cbk = cfg.post_decoded_cbk; decode_finished_cbk = cfg.decode_finished_cbk; bool ret = init_FFmpeg(cfg.uri.c_str(), cfg.force_tcp); if(!ret){ return false; } m_dvpp_deviceId = atoi(cfg.gpuid.c_str()); ret = init_vdpp(m_dvpp_deviceId); if (!ret) { releaseFFmpeg(); } ret = picConverter.init(m_context); if(!ret){ picConverter.release(); } return ret; } bool DvppDecoder::start(){ m_bRunning = true; pthread_create(&m_decode_thread,0, [](void* arg) { DvppDecoder* a=(DvppDecoder*)arg; a->decode_thread(); return (void*)0; } ,this); return true; } void DvppDecoder::close(){ m_bRunning=false; if(m_decode_thread != 0){ pthread_join(m_decode_thread,0); } #ifdef TEST_DECODER if(vdecHostAddr != nullptr){ CHECK_NOT_RETURN(aclrtFreeHost(vdecHostAddr), "aclrtFreeHost failed"); } #endif } bool DvppDecoder::sendVdecEos(aclvdecChannelDesc *vdecChannelDesc){ // create stream desc acldvppStreamDesc *streamInputDesc = acldvppCreateStreamDesc(); if (streamInputDesc == nullptr) { cout << "fail to create input stream desc" << endl; return false; } aclError ret = acldvppSetStreamDescEos(streamInputDesc, 1); if (ret != ACL_SUCCESS) { cout << "fail to set eos for stream desc, errorCode = " << static_cast(ret) << endl; (void)acldvppDestroyStreamDesc(streamInputDesc); return false; } // send vdec eos frame. when all vdec callback are completed, aclvdecSendFrame can be returned. ret = aclvdecSendFrame(vdecChannelDesc, streamInputDesc, nullptr, nullptr, nullptr); if (ret != ACL_SUCCESS) { cout << "fail to send eos frame, ret=" << ret << endl; (void)acldvppDestroyStreamDesc(streamInputDesc); return false; } (void)acldvppDestroyStreamDesc(streamInputDesc); return true; } void DvppDecoder::releaseFFmpeg(){ m_dec_keyframe = false; if(h264bsfc){ av_bsf_free(&h264bsfc); h264bsfc = nullptr; } if (fmt_ctx) { avformat_close_input(&fmt_ctx); fmt_ctx = nullptr; } } void DvppDecoder::releaseResource(){ releaseFFmpeg(); for(int i = 0; i < m_vec_vdec.size(); i++){ if(m_vec_vdec[i] != nullptr){ acldvppFree((uint8_t*)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 DvppDecoder::decode_thread(){ int frame_count = 0; long startTime = GetCurTimeUs(); int ret = -1; // dvpp解码参数 CHECK_AND_RETURN_NOVALUE(aclrtSetCurrentContext(m_context), "aclrtSetCurrentContext failed"); pthread_t report_thread; ret = pthread_create(&report_thread, nullptr, ReportThd, (void *)this); if(ret != 0){ cout << "pthread_create failed" << endl; return; } // 创建aclvdecChannelDesc类型的数据 aclvdecChannelDesc *vdecChannelDesc = aclvdecCreateChannelDesc(); if (vdecChannelDesc == nullptr) { cout << "aclvdecCreateChannelDesc failed"; return; } // 创建 channel dec结构体 // 通道ID在dvpp层面为0~31 CHECK_AND_RETURN_NOVALUE(aclvdecSetChannelDescChannelId(vdecChannelDesc, m_dvpp_channel), "aclvdecSetChannelDescChannelId failed"); CHECK_AND_RETURN_NOVALUE(aclvdecSetChannelDescThreadId(vdecChannelDesc, report_thread), "aclvdecSetChannelDescThreadId failed"); CHECK_AND_RETURN_NOVALUE(aclvdecSetChannelDescCallback(vdecChannelDesc, VdecCallback), "aclvdecSetChannelDescCallback failed"); CHECK_AND_RETURN_NOVALUE(aclvdecSetChannelDescEnType(vdecChannelDesc, enType), "aclvdecSetChannelDescEnType failed"); CHECK_AND_RETURN_NOVALUE(aclvdecSetChannelDescOutPicFormat(vdecChannelDesc, PIXEL_FORMAT_YUV_SEMIPLANAR_420), "aclvdecSetChannelDescOutPicFormat failed"); CHECK_AND_RETURN_NOVALUE(aclvdecCreateChannel(vdecChannelDesc), "aclvdecCreateChannel failed"); AVPacket* pkt ; pkt = av_packet_alloc(); av_init_packet( pkt ); acldvppStreamDesc *input_stream_desc = nullptr; acldvppPicDesc *output_pic_desc = nullptr; void *vdecInputbuf = nullptr; void *vdecOutputBuf = nullptr; while (m_bRunning) { if (!m_bReal) { if (m_bPause) { std::this_thread::sleep_for(std::chrono::milliseconds(3)); continue; } } int result = av_read_frame(fmt_ctx, pkt); if (result == AVERROR_EOF || result < 0) { cout << "Failed to read frame!" << endl; break; } if (m_dec_keyframe && !(pkt->flags & AV_PKT_FLAG_KEY)) { av_packet_unref(pkt); continue; } if (stream_index == pkt->stream_index){ ret = av_bsf_send_packet(h264bsfc, pkt); if(ret < 0) { cout << "av_bsf_send_packet error" << endl; } while ((ret = av_bsf_receive_packet(h264bsfc, pkt)) == 0) { // 解码 if(pkt->size > g_pkt_size){ cout << "pkt size 大于 预设" << endl; break; } if(!m_bRunning){ break; } vdecInputbuf = m_vdecQueue.getTail(); if(vdecInputbuf == nullptr){ std::this_thread::sleep_for(std::chrono::milliseconds(3)); // cout << "getTail failed" << endl; continue; } ret = aclrtMemcpy(vdecInputbuf, pkt->size, pkt->data, pkt->size, ACL_MEMCPY_HOST_TO_DEVICE); if(ACL_ERROR_NONE != ret){ cout << "aclrtMemcpy failed" << endl; goto end_flag; } ret = acldvppMalloc((void **)&vdecOutputBuf, m_vdec_out_size); if(ret != ACL_ERROR_NONE){ cout << "acldvppMalloc failed" << endl; goto end_flag; } /************ 解码*************/ input_stream_desc = acldvppCreateStreamDesc(); if (input_stream_desc == nullptr) { cout << "acldvppCreateStreamDesc error" << endl; } output_pic_desc = acldvppCreatePicDesc(); if (output_pic_desc == nullptr) { cout<< "acldvppCreatePicDesc error" << endl; } CHECK_NOT_RETURN(acldvppSetStreamDescData(input_stream_desc, vdecInputbuf), "acldvppSetStreamDescData failed"); CHECK_NOT_RETURN(acldvppSetStreamDescSize(input_stream_desc, pkt->size), "acldvppSetStreamDescSize failed"); CHECK_NOT_RETURN(acldvppSetPicDescData(output_pic_desc, vdecOutputBuf), "acldvppSetPicDescData failed"); CHECK_NOT_RETURN(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 = GetCurTimeUs(); user_data->self = this; // user_data->inBufNode = bufNode; cout << "send frame" << endl; CHECK_NOT_RETURN(aclvdecSendFrame(vdecChannelDesc, input_stream_desc, output_pic_desc, nullptr, reinterpret_cast(user_data)), "aclvdecSendFrame failed"); frame_count++; m_vdecQueue.addTail(); vdecInputbuf = nullptr; vdecOutputBuf = nullptr; } /****************************/ } av_packet_unref(pkt); } end_flag: av_packet_free(&pkt); sendVdecEos(vdecChannelDesc); CHECK_NOT_RETURN(aclvdecDestroyChannel(vdecChannelDesc), "aclvdecDestroyChannel failed"); CHECK_NOT_RETURN(aclvdecDestroyChannelDesc(vdecChannelDesc), "aclvdecDestroyChannelDesc failed"); // report_thread 需后于destroy退出 m_bRunning = false; CHECK_NOT_RETURN(pthread_join(report_thread, nullptr), "pthread_join failed"); if(m_vdecQueue.length() > 0){ cout << m_vdecQueue.length() << endl; } if(vdecOutputBuf != nullptr){ acldvppFree((uint8_t*)vdecOutputBuf); vdecOutputBuf = nullptr; } cout << "read thread exit." << endl; } float DvppDecoder::fps(){ return m_fps; } bool DvppDecoder::isSurport(FFDecConfig& cfg){ bool bRet = init(cfg); return bRet; } bool DvppDecoder::getResolution( int &width, int &height ){ width = frame_width; height = frame_height; return true; } void DvppDecoder::pause(){ m_bPause = true; } void DvppDecoder::resume(){ m_bPause = false; } void DvppDecoder::setDecKeyframe(bool bKeyframe) { m_dec_keyframe = bKeyframe; } bool DvppDecoder::isRunning(){ return m_bRunning; } bool DvppDecoder::isFinished(){ return m_bFinished; } bool DvppDecoder::isPausing(){ return m_bPause; } int DvppDecoder::getCachedQueueLength(){ // TODO return 0; } FFImgInfo* DvppDecoder::snapshot(){ // TODO return nullptr; } void DvppDecoder::setPostDecArg(const void* postDecArg){ m_postDecArg = postDecArg; } void DvppDecoder::setFinishedDecArg(const void* finishedDecArg){ m_finishedDecArg = finishedDecArg; }