Vehicle_Road_Counter/src/videoService/video_pipeline.cpp

160 lines
4.2 KiB
C++
Raw Normal View History

#include "video_pipeline.hpp"
#include <chrono>
VideoPipeline::VideoPipeline() : running_(false) {}
VideoPipeline::~VideoPipeline() {
Stop();
}
void VideoPipeline::Start(const std::string& inputUrl, const std::string& outputUrl) {
if (running_)
return;
running_ = true;
spdlog::info("Starting VideoPipeline with Input: {}", inputUrl);
processingThread_ = std::thread(&VideoPipeline::processLoop, this, inputUrl, outputUrl, false);
}
void VideoPipeline::StartTest(const std::string& filePath, const std::string& outputUrl) {
if (running_)
return;
running_ = true;
spdlog::info("Starting VideoPipeline (File Test Mode) Input: {}", filePath);
// true 表示是文件源
processingThread_ = std::thread(&VideoPipeline::processLoop, this, filePath, outputUrl, true);
}
void VideoPipeline::Stop() {
if (!running_)
return;
running_ = false;
if (processingThread_.joinable()) {
processingThread_.join();
}
spdlog::info("VideoPipeline Stopped.");
}
std::vector<DetectionResult> VideoPipeline::mockInference(const cv::Mat& frame) {
std::vector<DetectionResult> results;
static int dummyX = 100;
static int direction = 5;
// 简单的移动逻辑,模拟每帧的变化
dummyX += direction;
if (dummyX > frame.cols - 200 || dummyX < 0)
direction *= -1;
DetectionResult res;
res.x = dummyX;
res.y = 200;
res.width = 150;
res.height = 300;
res.label = "TEST_CLIP";
res.confidence = 0.95f;
results.push_back(res);
return results;
}
void VideoPipeline::drawOverlay(cv::Mat& frame, const std::vector<DetectionResult>& results) {
for (const auto& res : results) {
cv::rectangle(frame, cv::Rect(res.x, res.y, res.width, res.height), cv::Scalar(0, 255, 0),
2);
std::string text = res.label + " " + std::to_string(res.confidence).substr(0, 4);
cv::putText(frame, text, cv::Point(res.x, res.y - 5), cv::FONT_HERSHEY_SIMPLEX, 0.6,
cv::Scalar(0, 255, 0), 2);
}
cv::putText(frame, "RK3588 H.264 @ 20FPS", cv::Point(20, 50), cv::FONT_HERSHEY_SIMPLEX, 1.0,
cv::Scalar(0, 0, 255), 2);
}
void VideoPipeline::processLoop(std::string inputUrl, std::string outputUrl, bool isFileSource) {
cv::VideoCapture cap;
cap.open(inputUrl); // 文件路径也是通过 open 打开
if (!cap.isOpened()) {
spdlog::error("Failed to open input: {}", inputUrl);
running_ = false;
return;
}
const double TARGET_FPS = 20.0;
// 计算每帧的目标耗时 (毫秒) -> 1000 / 20 = 50ms
const double FRAME_DURATION_MS = 1000.0 / TARGET_FPS;
int width = cap.get(cv::CAP_PROP_FRAME_WIDTH);
int height = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
spdlog::info("Source: {}x{} | Mode: {}", width, height,
isFileSource ? "FILE LOOP" : "LIVE STREAM");
std::stringstream pipeline;
pipeline << "appsrc ! "
<< "videoconvert ! "
<< "video/x-raw,format=NV12,width=" << width << ",height=" << height
<< ",framerate=20/1 ! "
<< "mpph264enc ! "
<< "h264parse ! "
<< "rtspclientsink location=" << outputUrl << " protocols=tcp";
cv::VideoWriter writer;
writer.open(pipeline.str(), cv::CAP_GSTREAMER, 0, TARGET_FPS, cv::Size(width, height), true);
if (!writer.isOpened()) {
spdlog::error("Failed to initialize VideoWriter.");
}
cv::Mat frame;
while (running_) {
// 记录循环开始时间
auto loop_start = std::chrono::steady_clock::now();
if (!cap.read(frame)) {
if (isFileSource) {
spdlog::info("End of file reached, looping...");
cap.set(cv::CAP_PROP_POS_FRAMES, 0);
continue;
} else {
spdlog::warn("Frame read failed. Reconnecting...");
std::this_thread::sleep_for(std::chrono::seconds(1));
cap.release();
cap.open(inputUrl);
continue;
}
}
if (frame.empty())
continue;
// 1. 算法处理
auto results = mockInference(frame);
// 2. 绘制叠加
drawOverlay(frame, results);
// 3. 推流
if (writer.isOpened()) {
writer.write(frame);
}
if (isFileSource) {
auto loop_end = std::chrono::steady_clock::now();
std::chrono::duration<double, std::milli> elapsed = loop_end - loop_start;
double elapsed_ms = elapsed.count();
double wait_ms = FRAME_DURATION_MS - elapsed_ms;
if (wait_ms > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds((int)wait_ms));
}
}
}
cap.release();
writer.release();
}