实现opencv
This commit is contained in:
parent
2a4d3516ca
commit
62d49d4dc8
@ -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