#include "FFNvDecoder.h" #include #include #include #include using namespace std; // 参考博客: https://blog.csdn.net/qq_40116098/article/details/120704340 static AVPixelFormat get_hw_format(AVCodecContext *avctx, const AVPixelFormat *pix_fmts) { FFNvDecoder* _this = (FFNvDecoder*)avctx->opaque; const AVPixelFormat *p; for (p = pix_fmts; *p != -1; p++) { if (*p == _this->getHwPixFmt()) return *p; } //cout << "Failed to get HW surface format"; return AV_PIX_FMT_NONE; } FFNvDecoder::FFNvDecoder() { // 初始化解码对象 fmt_ctx = nullptr; avctx = nullptr; m_bRunning = false; stream = nullptr; stream_index = -1; hw_pix_fmt = AV_PIX_FMT_NONE; name = ""; m_bPause = false; m_bReal = true; m_decode_thread = 0; m_post_decode_thread = 0; } FFNvDecoder::~FFNvDecoder() { } bool FFNvDecoder::init(FFDecConfig& cfg) { m_cfg = cfg; fstream infile(cfg.uri); if (infile.is_open()){ m_bReal = false; infile.close(); }else { m_bReal = true; } return init(cfg.uri.c_str(), cfg.gpuid.c_str(),cfg.force_tcp); } bool FFNvDecoder::init(const char* uri, const char* gpuid, bool force_tcp) { av_register_all(); 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", "3000000", 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; return false; } // 查找流信息 if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) { cout << "Cannot find input stream information"; 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"; return false; } string cuvid_dec_name = string(decoder->name) + "_cuvid"; AVCodec *vcodec = avcodec_find_decoder_by_name(cuvid_dec_name.c_str()); if (!(avctx = avcodec_alloc_context3(vcodec))) return (bool)AVERROR(ENOMEM); // 得到视频流对象 stream = fmt_ctx->streams[stream_index]; if (avcodec_parameters_to_context(avctx, stream->codecpar) < 0) return false; avctx->opaque = this; // 设置解码器管理器的像素格式回调函数 avctx->get_format = get_hw_format; hw_pix_fmt = AV_PIX_FMT_CUDA; // 打开解码器流 AVDictionary *op = nullptr; av_dict_set( &op, "gpu", gpuid, 0 ); if (avcodec_open2(avctx, vcodec, &op) < 0) { cout << "Failed to open codec for stream" << stream_index; return false; } return true; } void FFNvDecoder::start(){ m_bRunning = true; pthread_create(&m_decode_thread,0, [](void* arg) { FFNvDecoder* a=(FFNvDecoder*)arg; a->decode_thread(); return (void*)0; } ,this); pthread_create(&m_post_decode_thread,0, [](void* arg) { FFNvDecoder* a=(FFNvDecoder*)arg; a->post_decode_thread(); return (void*)0; } ,this); } void FFNvDecoder::decode_thread() { AVPacket* pkt ; pkt = av_packet_alloc(); av_init_packet( pkt ); while (m_bRunning) { if (!m_bReal) { if (m_bPause) { std::this_thread::sleep_for(std::chrono::milliseconds(3)); continue; } } AVFrame * gpuFrame = mFrameQueue.getTail(); if (gpuFrame == nullptr) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } int result = av_read_frame(fmt_ctx, pkt); if (result == AVERROR_EOF) { cout << "Failed to read frame!" << endl; break; } if (result < 0) { cout << "Failed to read frame!" << endl; break; } if (m_bReal) { if (m_bPause) { av_packet_unref(pkt); std::this_thread::sleep_for(std::chrono::milliseconds(3)); continue; } } if (stream_index == pkt->stream_index) { result = avcodec_send_packet(avctx, pkt); if (result < 0) { cout << "Failed to send pkt:" << result << endl; continue; } result = avcodec_receive_frame(avctx, gpuFrame); if (result == AVERROR(EAGAIN) || result == AVERROR_EOF || result < 0) { cout << "Failed to receive frame"<< endl; continue; } mFrameQueue.addTail(); } av_packet_unref(pkt); } m_bRunning = false; cout << "decode thread exited." << endl; } void FFNvDecoder::post_decode_thread() { while (m_bRunning) { AVFrame * gpuFrame = mFrameQueue.getHead(); if (gpuFrame == nullptr) { std::this_thread::sleep_for(std::chrono::milliseconds(3)); continue; } post_decoded_cbk(m_userPtr, gpuFrame); mFrameQueue.addHead(); } cout << "post decode thread exited." << endl; } void FFNvDecoder::close() { m_bRunning=false; if(m_decode_thread != 0){ pthread_join(m_decode_thread,0); } if (m_post_decode_thread != 0) { pthread_join(m_post_decode_thread,0); } if (avctx) { if (avctx->hw_device_ctx) { av_buffer_unref(&avctx->hw_device_ctx); } avcodec_free_context(&avctx); } if (fmt_ctx) { avformat_close_input(&fmt_ctx); } } void FFNvDecoder::setName(string nm){ name = nm; } string FFNvDecoder::getName(){ return name; } AVPixelFormat FFNvDecoder::getHwPixFmt() { return hw_pix_fmt; } bool FFNvDecoder::isRunning() { return m_bRunning; } bool FFNvDecoder::getResolution( int &width, int &height ) { if (avctx != nullptr) { width = avctx->width; height = avctx->height; return true; } return false; } void FFNvDecoder::pause() { m_bPause = true; } void FFNvDecoder::resume() { m_bPause = false; }