实现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