diff --git a/Vimba_6_0/VimbaCPP/Examples/AsynchronousOpenCVRecorder/Qt/Source/RTSPStreamer.cpp b/Vimba_6_0/VimbaCPP/Examples/AsynchronousOpenCVRecorder/Qt/Source/RTSPStreamer.cpp new file mode 100644 index 0000000..39d2872 --- /dev/null +++ b/Vimba_6_0/VimbaCPP/Examples/AsynchronousOpenCVRecorder/Qt/Source/RTSPStreamer.cpp @@ -0,0 +1,104 @@ +RTSPStreamer::RTSPStreamer(const std::string& rtsp_url, int width, int height, int fps) + : m_rtspUrl(rtsp_url), m_width(width), m_height(height), m_fps(fps) { + + // 初始化 FFmpeg + avformat_network_init(); + + // 创建输出上下文 (RTSP) + avformat_alloc_output_context2(&m_fmtCtx, nullptr, "rtsp", rtsp_url.c_str()); + if (!m_fmtCtx) { + std::cerr << "Failed to create RTSP output context" << std::endl; + return; + } + + // 查找 H.264 编码器 + const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264); + if (!codec) { + std::cerr << "H.264 encoder not found" << std::endl; + return; + } + + // 创建编码器上下文 + m_codecCtx = avcodec_alloc_context3(codec); + m_codecCtx->width = width; + m_codecCtx->height = height; + m_codecCtx->time_base = {1, fps}; + m_codecCtx->pix_fmt = AV_PIX_FMT_YUV420P; + + // 打开编码器 + if (avcodec_open2(m_codecCtx, codec, nullptr) < 0) { + std::cerr << "Failed to open H.264 encoder" << std::endl; + return; + } + + // 创建视频流 + m_stream = avformat_new_stream(m_fmtCtx, codec); + avcodec_parameters_from_context(m_stream->codecpar, m_codecCtx); + + // 打开 RTSP 输出 + if (avio_open(&m_fmtCtx->pb, rtsp_url.c_str(), AVIO_FLAG_WRITE) < 0) { + std::cerr << "Failed to open RTSP output" << std::endl; + return; + } + + // 写文件头 + if (avformat_write_header(m_fmtCtx, nullptr) < 0) { + std::cerr << "Failed to write RTSP header" << std::endl; + return; + } + + m_connected = true; +} + +bool RTSPStreamer::pushFrame(const cv::Mat& frame) { + if (!m_connected) return false; + + // 转换 BGR 到 YUV420 + AVFrame* avFrame = av_frame_alloc(); + avFrame->format = AV_PIX_FMT_YUV420P; + avFrame->width = m_width; + avFrame->height = m_height; + av_frame_get_buffer(avFrame, 0); + + SwsContext* swsCtx = sws_getContext( + frame.cols, frame.rows, AV_PIX_FMT_BGR24, + m_width, m_height, AV_PIX_FMT_YUV420P, + SWS_BILINEAR, nullptr, nullptr, nullptr); + + const uint8_t* srcData[1] = {frame.data}; + int srcLinesize[1] = {frame.step}; + sws_scale(swsCtx, srcData, srcLinesize, 0, frame.rows, + avFrame->data, avFrame->linesize); + sws_freeContext(swsCtx); + + // 编码并发送 + AVPacket pkt; + av_init_packet(&pkt); + pkt.data = nullptr; + pkt.size = 0; + + if (avcodec_send_frame(m_codecCtx, avFrame) < 0) { + av_frame_free(&avFrame); + return false; + } + + while (avcodec_receive_packet(m_codecCtx, &pkt) == 0) { + av_packet_rescale_ts(&pkt, m_codecCtx->time_base, m_stream->time_base); + pkt.stream_index = m_stream->index; + av_interleaved_write_frame(m_fmtCtx, &pkt); + av_packet_unref(&pkt); + } + + av_frame_free(&avFrame); + return true; +} + +RTSPStreamer::~RTSPStreamer() { + if (m_fmtCtx) { + av_write_trailer(m_fmtCtx); + avio_close(m_fmtCtx->pb); + avformat_free_context(m_fmtCtx); + } + if (m_codecCtx) avcodec_free_context(&m_codecCtx); + avformat_network_deinit(); +} \ No newline at end of file