Compare commits
2 Commits
2a4d3516ca
...
57d3bc4912
Author | SHA1 | Date | |
---|---|---|---|
![]() |
57d3bc4912 | ||
![]() |
62d49d4dc8 |
@ -12,40 +12,50 @@ BIN_FILE = $(PROJECT_NAME)
|
||||
BIN_DIR = binary/$(CONFIG_DIR)
|
||||
OBJ_DIR = object/$(CONFIG_DIR)
|
||||
BIN_PATH = $(BIN_DIR)/$(BIN_FILE)
|
||||
# Qt 工具路径
|
||||
MOC = /usr/lib/aarch64-linux-gnu/qt5/bin/moc
|
||||
UIC = /usr/lib/aarch64-linux-gnu/qt5/bin/uic
|
||||
RCC = /usr/lib/aarch64-linux-gnu/qt5/bin/rcc
|
||||
|
||||
# 使用 pkg-config 获取 Qt 编译标志
|
||||
QT_CFLAGS := $(shell pkg-config --cflags Qt5Core Qt5Gui Qt5Widgets)
|
||||
QT_LIBS := $(shell pkg-config --libs Qt5Core Qt5Gui Qt5Widgets)
|
||||
|
||||
all: $(BIN_PATH)
|
||||
|
||||
include $(MAKE_INCLUDE_DIR)/VimbaCPP.mk
|
||||
include $(MAKE_INCLUDE_DIR)/VimbaImageTransform.mk
|
||||
include $(MAKE_INCLUDE_DIR)/Qt.mk
|
||||
|
||||
|
||||
SOURCE_DIR = $(PROJECT_DIR)/Source
|
||||
|
||||
INCLUDE_DIRS = -I$(SOURCE_DIR) \
|
||||
-I$(EXAMPLES_DIR) \
|
||||
-I$(OBJ_DIR)
|
||||
-I$(OBJ_DIR) \
|
||||
-I/usr/include/aarch64-linux-gnu/qt5 \
|
||||
-I/usr/include/aarch64-linux-gnu/qt5/QtCore \
|
||||
-I/usr/include/aarch64-linux-gnu/qt5/QtGui \
|
||||
-I/usr/include/aarch64-linux-gnu/qt5/QtWidgets
|
||||
|
||||
|
||||
OPENCV_CFLAGS = $(shell $(PKGCFG) --cflags opencv)
|
||||
|
||||
OPENCV_LIBS = $(shell $(PKGCFG) --libs opencv)
|
||||
OPENCV_CFLAGS = $(shell $(PKGCFG) --cflags opencv4)
|
||||
|
||||
OPENCV_LIBS = $(shell $(PKGCFG) --libs opencv4)
|
||||
|
||||
|
||||
|
||||
LIBS = $(VIMBACPP_LIBS) \
|
||||
$(VIMBAIMAGETRANSFORM_LIBS) \
|
||||
$(QTCORE_LIBS) \
|
||||
$(QTGUI_LIBS) \
|
||||
$(QT_LIBS) \
|
||||
$(OPENCV_LIBS)
|
||||
|
||||
DEFINES =
|
||||
|
||||
|
||||
CFLAGS = $(COMMON_CFLAGS) \
|
||||
$(VIMBACPP_CFLAGS) \
|
||||
$(VIMBAIMAGETRANSFORM_CFLAGS) \
|
||||
$(QTCORE_CFLAGS) \
|
||||
$(QTGUI_CFLAGS) \
|
||||
$(QT_CFLAGS) \
|
||||
$(OPENCV_CFLAGS)
|
||||
|
||||
OBJ_FILES = $(OBJ_DIR)/ApiController.o \
|
||||
@ -62,9 +72,7 @@ OBJ_FILES = $(OBJ_DIR)/ApiController.o \
|
||||
GEN_HEADERS = $(OBJ_DIR)/ui_AsynchronousOpenCVRecorder.h
|
||||
|
||||
DEPENDENCIES = VimbaCPP \
|
||||
VimbaImageTransform \
|
||||
QtCore \
|
||||
QtGui
|
||||
VimbaImageTransform
|
||||
|
||||
$(OBJ_DIR)/moc_%.cpp: $(SOURCE_DIR)/%.h $(OBJ_DIR)
|
||||
$(MOC) -o $@ $<
|
||||
|
@ -90,7 +90,18 @@ the use of this software, even if advised of the possibility of such damage.
|
||||
#include "VmbTransform.h"
|
||||
#include <VimbaCPP/Include/VimbaCPP.h>
|
||||
|
||||
#include <opencv2/videoio.hpp> // 必须包含 videoio 以使用 VideoWriter
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
// 替换原有的 enum 定义
|
||||
static const int FOURCC_USER_SELECT = -1; // 替代 CV_FOURCC_PROMPT
|
||||
static const int FOURCC_DEFAULT = cv::VideoWriter::fourcc('I','Y','U','V');
|
||||
static const int FOURCC_MPEG1 = cv::VideoWriter::fourcc('P','I','M','1');
|
||||
static const int FOURCC_MJPEG = cv::VideoWriter::fourcc('M','J','P','G');
|
||||
static const int FOURCC_MPEG42 = cv::VideoWriter::fourcc('M','P','4','2');
|
||||
static const int FOURCC_MPEG43 = cv::VideoWriter::fourcc('M','P','4','3');
|
||||
static const int FOURCC_DIVX = cv::VideoWriter::fourcc('D','I','V','X');
|
||||
static const int FOURCC_X264 = cv::VideoWriter::fourcc('X','2','6','4');
|
||||
//
|
||||
// Base exception
|
||||
//
|
||||
@ -130,17 +141,6 @@ class OpenCVRecorder: public QThread
|
||||
// Example FOURCC codes that can be used with the OpenCVRecorder
|
||||
//
|
||||
VmbUint32_t maxQueueElements() const { return 3; }
|
||||
enum
|
||||
{
|
||||
FOURCC_USER_SELECT = CV_FOURCC_PROMPT,
|
||||
FOURCC_DEFAULT = CV_FOURCC_MACRO('I','Y','U','V'),
|
||||
FOURCC_MPEG1 = CV_FOURCC_MACRO('P','I','M','1'),
|
||||
FOURCC_MJPEG = CV_FOURCC_MACRO('M','J','P','G'),
|
||||
FOURCC_MPEG42 = CV_FOURCC_MACRO('M','P','4','2'),
|
||||
FOURCC_MPEG43 = CV_FOURCC_MACRO('M','P','4','3'),
|
||||
FOURCC_DIVX = CV_FOURCC_MACRO('D','I','V','X'),
|
||||
FOURCC_X264 = CV_FOURCC_MACRO('X','2','6','4'),
|
||||
};
|
||||
//
|
||||
// frame data temporary storage
|
||||
//
|
||||
@ -305,7 +305,7 @@ class OpenCVRecorder: public QThread
|
||||
public:
|
||||
OpenCVRecorder(const QString &fileName, VmbFloat_t fps, VmbUint32_t Width, VmbUint32_t Height)
|
||||
: m_StopThread( false )
|
||||
#ifdef _MSC_VER // codec selection only supported by Windows
|
||||
#ifdef _MSC_VER // Windows 下支持手动选择编码器
|
||||
, m_VideoWriter(fileName.toStdString(), FOURCC_USER_SELECT, fps, cv::Size(Width, Height), true)
|
||||
#else
|
||||
, m_VideoWriter(fileName.toStdString(), FOURCC_X264, fps, cv::Size(Width, Height), true)
|
||||
@ -398,4 +398,20 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class RTSPStreamer {
|
||||
public:
|
||||
RTSPStreamer(const std::string& rtsp_url, int width, int height, int fps);
|
||||
~RTSPStreamer();
|
||||
bool pushFrame(const cv::Mat& frame);
|
||||
bool isConnected() const { return m_connected; }
|
||||
|
||||
private:
|
||||
std::string m_rtspUrl;
|
||||
AVFormatContext* m_fmtCtx = nullptr;
|
||||
AVCodecContext* m_codecCtx = nullptr;
|
||||
AVStream* m_stream = nullptr;
|
||||
SwsContext* m_swsCtx = nullptr;
|
||||
std::atomic<bool> m_connected{false};
|
||||
int m_width, m_height, m_fps;
|
||||
};
|
||||
#endif
|
@ -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();
|
||||
}
|
Loading…
Reference in New Issue
Block a user