This commit is contained in:
cxh 2025-10-16 14:01:57 +08:00
parent d802f80213
commit befef76a3e

View File

@ -14,6 +14,12 @@
#include "logger.hpp" #include "logger.hpp"
static bool device_exists(const std::string &path)
{
struct stat st;
return (stat(path.c_str(), &st) == 0);
}
static std::string get_ip_address(const std::string &ifname) static std::string get_ip_address(const std::string &ifname)
{ {
struct ifaddrs *ifaddr, *ifa; struct ifaddrs *ifaddr, *ifa;
@ -129,6 +135,14 @@ RTMPManager::StreamResultInfo RTMPManager::start_camera(const Camera &cam, Strea
return res; return res;
} }
// 新增:设备节点存在性检查
if (!device_exists(cam.device))
{
res.result = 1;
res.reason = "Device not found: " + cam.device;
return res;
}
const std::string key = make_stream_key(cam.name, type); const std::string key = make_stream_key(cam.name, type);
std::unique_ptr<StreamContext> ctx; std::unique_ptr<StreamContext> ctx;
@ -139,15 +153,13 @@ RTMPManager::StreamResultInfo RTMPManager::start_camera(const Camera &cam, Strea
auto it = streams.find(key); auto it = streams.find(key);
if (it != streams.end() && it->second->running.load()) if (it != streams.end() && it->second->running.load())
{ {
res.result = 0; res.result = 1; // 修改为 1表示重复启动不是真成功
res.reason = "Already streaming"; res.reason = "Already streaming";
return res; return res;
} }
ctx = std::make_unique<StreamContext>(); ctx = std::make_unique<StreamContext>();
ctx->running.store(true); ctx->running.store(true);
// 重要:在把 ctx 放入 map 之前,先从 ctx->start_promise 拿到 future
status_future = ctx->start_promise.get_future(); status_future = ctx->start_promise.get_future();
StreamContext *ctx_ptr = ctx.get(); StreamContext *ctx_ptr = ctx.get();
@ -202,7 +214,7 @@ RTMPManager::StreamResultInfo RTMPManager::stop_camera(const std::string &cam_na
streams.erase(it); streams.erase(it);
} }
bool was_running = ctx->status.running; // ✅ 新增:判断是否真的在推流 bool was_running = ctx->running.load(); // ✅ 改为使用 running
ctx->running.store(false); ctx->running.store(false);
if (ctx->thread.joinable()) ctx->thread.join(); if (ctx->thread.joinable()) ctx->thread.join();
@ -262,17 +274,15 @@ void RTMPManager::stream_loop(Camera cam, StreamType type, StreamContext *ctx)
while (true) while (true)
{ {
// 手动停止
if (!ctx->running.load()) if (!ctx->running.load())
{ {
status.running = false; status.running = false;
status.last_result = StreamResult::UNKNOWN; status.last_result = StreamResult::UNKNOWN;
status.last_error = "Stream stopped manually"; status.last_error = "Stream stopped manually";
try_set_start(status); // 若启动阶段还没返回,这里补一次 try_set_start(status);
break; break;
} }
// 首帧超时5s
if (!first_frame && if (!first_frame &&
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - t0).count() > 5) std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - t0).count() > 5)
{ {
@ -301,8 +311,7 @@ void RTMPManager::stream_loop(Camera cam, StreamType type, StreamContext *ctx)
status.running = true; status.running = true;
ctx->status.running = true; ctx->status.running = true;
status.last_result = StreamResult::OK; status.last_result = StreamResult::OK;
status.last_error.clear(); try_set_start(status);
try_set_start(status); // 首帧成功:通知 start_camera
} }
break; break;
} }
@ -331,19 +340,21 @@ void RTMPManager::stream_loop(Camera cam, StreamType type, StreamContext *ctx)
gst_message_unref(msg); gst_message_unref(msg);
// 错误/EOS 后退出循环
if (status.last_result == StreamResult::CONNECTION_FAIL || status.last_result == StreamResult::EOS_RECEIVED) if (status.last_result == StreamResult::CONNECTION_FAIL || status.last_result == StreamResult::EOS_RECEIVED)
{
break; break;
} }
if (pipeline)
{
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(pipeline);
}
if (bus)
{
gst_object_unref(bus);
} }
gst_element_set_state(pipeline, GST_STATE_NULL);
if (bus) gst_object_unref(bus);
gst_object_unref(pipeline);
ctx->status.running = false; ctx->status.running = false;
// 线程收尾:这里不要删 map 里的 ctxstop_camera 已经负责转移并 join
} }
void RTMPManager::stop_all() void RTMPManager::stop_all()
@ -400,21 +411,24 @@ std::vector<RTMPManager::StreamResultInfo> RTMPManager::process_push_request(con
for (int ch : item.channels) for (int ch : item.channels)
{ {
if (ch < 0 || ch >= static_cast<int>(g_app_config.cameras.size())) continue; if (ch < 0 || ch >= static_cast<int>(g_app_config.cameras.size()))
{
StreamResultInfo info;
info.loc = ch;
info.result = 1;
info.reason = "Invalid channel index";
info.url = "";
results.push_back(info);
continue;
}
const auto &cam = g_app_config.cameras[ch]; const auto &cam = g_app_config.cameras[ch];
StreamResultInfo info; StreamResultInfo info;
if (item.switchVal == 0) if (item.switchVal == 0)
{
// 开启推流
info = start_camera(cam, type); info = start_camera(cam, type);
}
else else
{
// 停止推流
info = stop_camera(cam.name, type); info = stop_camera(cam.name, type);
}
results.push_back(info); results.push_back(info);
} }