rtsp_tensorrt/pipeline/output/rtsp_writer.cpp
sladro e13cb3659c feat: 初始化项目结构
- 创建基本项目结构和目录
- 添加CMake构建系统
- 实现基础的配置解析功能
- 添加YOLO推理框架支持
- 集成RTSP和视频流处理功能
- 添加性能监控和日志系统
2024-12-24 16:25:03 +08:00

314 lines
8.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "rtsp_writer.hpp"
#include "../common/logger.hpp"
namespace pipeline {
RtspWriter::RtspWriter() = default;
RtspWriter::~RtspWriter() {
release();
}
bool RtspWriter::init(const OutputTargetConfig& config) {
std::lock_guard<std::mutex> lock(mutex_);
if (is_initialized_) {
Logger::warning("RtspWriter already initialized");
return true;
}
// 基本配置验证
if (config.name.empty()) {
setLastError("Empty name in config");
return false;
}
if (config.path.empty()) {
setLastError("Empty path in config");
return false;
}
if (config.fps <= 0) {
setLastError("Invalid fps in config: " + std::to_string(config.fps));
return false;
}
name_ = config.name;
url_ = config.path;
fps_ = config.fps;
codec_ = config.codec;
bitrate_ = config.bitrate;
// 对于mock URL只初始化编码器
if (url_.substr(0, 7) == "mock://") {
if (!initFFmpeg()) {
return false;
}
is_initialized_ = true;
Logger::info("RtspWriter initialized successfully: " + name_);
return true;
}
// 对于实际URL完整初始化
if (!initFFmpeg()) {
return false;
}
is_initialized_ = true;
Logger::info("RtspWriter initialized successfully: " + name_);
return true;
}
bool RtspWriter::write(const cv::Mat& frame) {
std::lock_guard<std::mutex> lock(mutex_);
if (!is_initialized_) {
setLastError("RtspWriter not initialized");
return false;
}
if (frame.empty()) {
setLastError("Input frame is empty");
return false;
}
try {
// 对于mock URL只更新状态
if (url_.substr(0, 7) == "mock://") {
is_opened_ = true;
return true;
}
// 延迟打开RTSP输出直到收到第一帧
if (!format_ctx_) {
if (!openRtspOutput()) {
return false;
}
}
// 转换图像格式
uint8_t* srcData[4] = {frame.data, nullptr, nullptr, nullptr};
int srcLinesize[4] = {static_cast<int>(frame.step), 0, 0, 0};
sws_scale(sws_ctx_, srcData, srcLinesize, 0, frame.rows,
frame_->data, frame_->linesize);
frame_->pts += av_rescale_q(1, codec_ctx_->time_base, video_stream_->time_base);
// 编码并发送
int ret = avcodec_send_frame(codec_ctx_, frame_);
if (ret < 0) {
setLastError("Error sending frame to encoder");
return false;
}
while (ret >= 0) {
ret = avcodec_receive_packet(codec_ctx_, packet_);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
setLastError("Error receiving packet from encoder");
return false;
}
packet_->stream_index = video_stream_->index;
ret = av_interleaved_write_frame(format_ctx_, packet_);
if (ret < 0) {
setLastError("Error writing packet");
return false;
}
}
is_opened_ = true;
return true;
} catch (const std::exception& e) {
setLastError("Error writing frame: " + std::string(e.what()));
return false;
}
}
bool RtspWriter::isOpened() const {
std::lock_guard<std::mutex> lock(mutex_);
return is_initialized_ && is_opened_;
}
void RtspWriter::release() {
std::lock_guard<std::mutex> lock(mutex_);
cleanup();
is_initialized_ = false;
is_opened_ = false;
}
std::string RtspWriter::getName() const {
return name_;
}
bool RtspWriter::getStatus(std::string& error_msg) const {
std::lock_guard<std::mutex> lock(mutex_);
error_msg = last_error_;
return is_initialized_ && is_opened_;
}
bool RtspWriter::initFFmpeg() {
// 初始化编码器
const AVCodec* codec;
if (codec_ == "h264") {
codec = avcodec_find_encoder(AV_CODEC_ID_H264);
} else if (codec_ == "h265") {
codec = avcodec_find_encoder(AV_CODEC_ID_H265);
} else {
setLastError("Unsupported codec: " + codec_);
return false;
}
if (!codec) {
setLastError("Codec not found");
return false;
}
codec_ctx_ = avcodec_alloc_context3(codec);
if (!codec_ctx_) {
setLastError("Could not allocate codec context");
return false;
}
// 设置编码器参数
codec_ctx_->bit_rate = bitrate_;
codec_ctx_->width = 1920; // 默认分辨率,将在收到第一帧时更新
codec_ctx_->height = 1080;
codec_ctx_->time_base = (AVRational){1, fps_};
codec_ctx_->framerate = (AVRational){fps_, 1};
codec_ctx_->gop_size = 12;
codec_ctx_->pix_fmt = AV_PIX_FMT_YUV420P;
codec_ctx_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
// 对于mock URL不需要真正打开编码器
if (url_.substr(0, 7) == "mock://") {
return true;
}
if (avcodec_open2(codec_ctx_, codec, nullptr) < 0) {
setLastError("Could not open codec");
cleanup();
return false;
}
return true;
}
bool RtspWriter::openRtspOutput() {
// 创建输出上下文
AVDictionary* options = nullptr;
// 设置协议白名单
av_dict_set(&options, "protocol_whitelist", "file,unix,tcp,rtsp,rtp,udp", 0);
avformat_alloc_output_context2(&format_ctx_, nullptr, "rtsp", url_.c_str());
if (!format_ctx_) {
setLastError("Could not create output context");
av_dict_free(&options);
return false;
}
// 创建视频流
video_stream_ = avformat_new_stream(format_ctx_, nullptr);
if (!video_stream_) {
setLastError("Could not create video stream");
av_dict_free(&options);
cleanup();
return false;
}
video_stream_->time_base = codec_ctx_->time_base;
avcodec_parameters_from_context(video_stream_->codecpar, codec_ctx_);
// 分配帧和数据包
frame_ = av_frame_alloc();
if (!frame_) {
setLastError("Could not allocate frame");
av_dict_free(&options);
cleanup();
return false;
}
frame_->format = codec_ctx_->pix_fmt;
frame_->width = codec_ctx_->width;
frame_->height = codec_ctx_->height;
frame_->pts = 0;
if (av_frame_get_buffer(frame_, 0) < 0) {
setLastError("Could not allocate frame data");
av_dict_free(&options);
cleanup();
return false;
}
packet_ = av_packet_alloc();
if (!packet_) {
setLastError("Could not allocate packet");
av_dict_free(&options);
cleanup();
return false;
}
// 创建图像格式转换上下文
sws_ctx_ = sws_getContext(
codec_ctx_->width, codec_ctx_->height, AV_PIX_FMT_BGR24,
codec_ctx_->width, codec_ctx_->height, codec_ctx_->pix_fmt,
SWS_BICUBIC, nullptr, nullptr, nullptr);
if (!sws_ctx_) {
setLastError("Could not create scale context");
av_dict_free(&options);
cleanup();
return false;
}
// 设置RTSP选项
if (url_.substr(0, 7) != "unix://") {
av_dict_set(&options, "rtsp_transport", "tcp", 0);
}
av_dict_set(&options, "muxdelay", "0.1", 0);
if (avformat_write_header(format_ctx_, &options) < 0) {
setLastError("Could not write header");
av_dict_free(&options);
cleanup();
return false;
}
av_dict_free(&options);
return true;
}
void RtspWriter::cleanup() {
if (format_ctx_) {
av_write_trailer(format_ctx_);
avio_closep(&format_ctx_->pb);
avformat_free_context(format_ctx_);
format_ctx_ = nullptr;
}
if (codec_ctx_) {
avcodec_free_context(&codec_ctx_);
}
if (frame_) {
av_frame_free(&frame_);
}
if (packet_) {
av_packet_free(&packet_);
}
if (sws_ctx_) {
sws_freeContext(sws_ctx_);
sws_ctx_ = nullptr;
}
}
void RtspWriter::setLastError(const std::string& error) {
last_error_ = error;
Logger::error("RtspWriter error: " + error);
}
} // namespace pipeline