#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; } FFNvDecoder::~FFNvDecoder() { } bool FFNvDecoder::init(const string& path) { fstream infile(path); if (infile.is_open()){ m_bReal = false; infile.close(); }else { m_bReal = true; } // 查找对应的硬件解码设备 const char* device_name = "cuda"; AVHWDeviceType hw_device_type = av_hwdevice_find_type_by_name(device_name); if (hw_device_type == AV_HWDEVICE_TYPE_NONE) { while ((hw_device_type = av_hwdevice_iterate_types(hw_device_type)) != AV_HWDEVICE_TYPE_NONE) cout << av_hwdevice_get_type_name(hw_device_type); return false; } // 打开输入视频文件 AVDictionary *options = nullptr; av_dict_set( &options, "bufsize", "1024000", 0 ); av_dict_set( &options, "rtsp_transport", "tcp", 0 ); av_dict_set( &options, "listen_timeout", "30", 0 ); // 单位为s fmt_ctx = avformat_alloc_context(); const char* input_file = path.c_str(); 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; } // 判断硬件解码设备是否兼容视频解码器 for (int i = 0;; i++) { const AVCodecHWConfig *config = avcodec_get_hw_config(decoder, i); if (!config) { cout << "Decoder does not support device hw_device_type" << decoder->name << av_hwdevice_get_type_name(hw_device_type); return false; } // 得到硬件解码像素格式 if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == hw_device_type) { hw_pix_fmt = config->pix_fmt; break; } } // 得到解码器管理器 if (!(avctx = avcodec_alloc_context3(decoder))) 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; // 初始化硬件解码器 AVBufferRef *hw_device_ctx = nullptr; if (av_hwdevice_ctx_create(&hw_device_ctx, hw_device_type, nullptr, nullptr, 0) < 0) { cout << "Failed to create specified HW device."; return false; } avctx->hw_device_ctx = av_buffer_ref(hw_device_ctx); // 打开解码器流 if (avcodec_open2(avctx, decoder, nullptr) < 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; } } 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; } AVFrame * gpuFrame = mFrameQueue.getTail(); if (gpuFrame == nullptr) { 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(); // if (gpuFrame->format == hw_pix_fmt) // { // /* retrieve data from GPU to CPU */ // if (av_hwframe_transfer_data(pong_frame, gpuFrame, 0) < 0) // { // cout << "Failed to transfer the data to system memory"; // return; // } // } } 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; pthread_join(m_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; }