2025-10-15 15:36:48 +08:00
|
|
|
|
// rtmp_manager.cpp
|
2025-10-14 17:13:08 +08:00
|
|
|
|
#include "rtmp_manager.hpp"
|
2025-10-15 14:14:00 +08:00
|
|
|
|
|
2025-10-15 17:01:43 +08:00
|
|
|
|
#include <gst/gst.h>
|
2025-10-15 10:18:02 +08:00
|
|
|
|
|
2025-10-15 14:14:00 +08:00
|
|
|
|
#include "logger.hpp"
|
|
|
|
|
|
|
2025-10-15 08:57:40 +08:00
|
|
|
|
std::unordered_map<std::string, std::unique_ptr<RTMPManager::StreamContext>> RTMPManager::streams;
|
2025-10-14 17:13:08 +08:00
|
|
|
|
std::mutex RTMPManager::streams_mutex;
|
|
|
|
|
|
|
2025-10-15 17:01:43 +08:00
|
|
|
|
std::queue<VideoPushRequest> RTMPManager::request_queue;
|
|
|
|
|
|
std::mutex RTMPManager::queue_mutex;
|
|
|
|
|
|
std::condition_variable RTMPManager::queue_cv;
|
|
|
|
|
|
|
|
|
|
|
|
std::thread RTMPManager::worker_thread;
|
|
|
|
|
|
std::atomic<bool> RTMPManager::running{false};
|
2025-10-14 17:13:08 +08:00
|
|
|
|
|
|
|
|
|
|
void RTMPManager::init()
|
|
|
|
|
|
{
|
|
|
|
|
|
gst_init(nullptr, nullptr);
|
2025-10-15 17:01:43 +08:00
|
|
|
|
running = true;
|
|
|
|
|
|
worker_thread = std::thread(rtmp_worker_thread);
|
|
|
|
|
|
LOG_INFO("[RTMP] Initialized and worker thread started.");
|
2025-10-14 17:13:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-15 17:01:43 +08:00
|
|
|
|
void RTMPManager::stop_all()
|
2025-10-15 08:50:01 +08:00
|
|
|
|
{
|
2025-10-15 17:01:43 +08:00
|
|
|
|
running = false;
|
|
|
|
|
|
queue_cv.notify_all();
|
2025-10-15 08:50:01 +08:00
|
|
|
|
|
2025-10-15 17:01:43 +08:00
|
|
|
|
// 停止所有流
|
2025-10-14 17:13:08 +08:00
|
|
|
|
{
|
2025-10-15 17:01:43 +08:00
|
|
|
|
std::lock_guard<std::mutex> lock(streams_mutex);
|
|
|
|
|
|
for (auto &kv : streams) kv.second->running = false;
|
2025-10-14 17:13:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-15 17:01:43 +08:00
|
|
|
|
if (worker_thread.joinable()) worker_thread.join();
|
2025-10-14 17:13:08 +08:00
|
|
|
|
|
2025-10-15 17:01:43 +08:00
|
|
|
|
LOG_INFO("[RTMP] stop_all completed.");
|
2025-10-14 17:13:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-15 17:01:43 +08:00
|
|
|
|
void RTMPManager::enqueue_video_push_request(const VideoPushRequest &req)
|
2025-10-15 11:27:53 +08:00
|
|
|
|
{
|
2025-10-15 17:01:43 +08:00
|
|
|
|
{
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(queue_mutex);
|
|
|
|
|
|
request_queue.push(req);
|
|
|
|
|
|
}
|
|
|
|
|
|
queue_cv.notify_one();
|
2025-10-15 15:01:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-15 17:01:43 +08:00
|
|
|
|
void RTMPManager::rtmp_worker_thread()
|
2025-10-15 15:01:55 +08:00
|
|
|
|
{
|
2025-10-15 17:01:43 +08:00
|
|
|
|
while (running)
|
2025-10-15 13:09:10 +08:00
|
|
|
|
{
|
2025-10-15 17:01:43 +08:00
|
|
|
|
VideoPushRequest req;
|
|
|
|
|
|
{
|
|
|
|
|
|
std::unique_lock<std::mutex> lock(queue_mutex);
|
|
|
|
|
|
queue_cv.wait(lock, [] { return !request_queue.empty() || !running; });
|
|
|
|
|
|
if (!running) break;
|
|
|
|
|
|
req = request_queue.front();
|
|
|
|
|
|
request_queue.pop();
|
|
|
|
|
|
}
|
2025-10-15 16:21:25 +08:00
|
|
|
|
|
2025-10-15 17:01:43 +08:00
|
|
|
|
nlohmann::json results = nlohmann::json::array();
|
|
|
|
|
|
|
|
|
|
|
|
for (auto &item : req.data)
|
2025-10-15 13:09:10 +08:00
|
|
|
|
{
|
2025-10-15 17:01:43 +08:00
|
|
|
|
for (int ch : item.channels)
|
2025-10-15 16:21:25 +08:00
|
|
|
|
{
|
2025-10-15 17:01:43 +08:00
|
|
|
|
if (ch < 0 || ch >= static_cast<int>(g_app_config.cameras.size())) continue;
|
|
|
|
|
|
Camera &cam = g_app_config.cameras[ch];
|
|
|
|
|
|
StreamResultInfo res;
|
|
|
|
|
|
|
|
|
|
|
|
if (item.switchVal == 0)
|
|
|
|
|
|
res = start_camera(cam, item.streamType);
|
|
|
|
|
|
else
|
|
|
|
|
|
res = stop_camera(cam, item.streamType);
|
|
|
|
|
|
|
|
|
|
|
|
res.loc = ch;
|
|
|
|
|
|
res.url = "rtmp://127.0.0.1/live/" + cam.name;
|
|
|
|
|
|
results.push_back({{"loc", res.loc}, {"url", res.url}, {"result", res.result}, {"reason", res.reason}});
|
2025-10-15 16:21:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-15 15:33:00 +08:00
|
|
|
|
|
2025-10-15 17:01:43 +08:00
|
|
|
|
// 上报 MQTT
|
|
|
|
|
|
nlohmann::json reply;
|
|
|
|
|
|
reply["type"] = "response";
|
|
|
|
|
|
reply["seqNo"] = req.seqNo;
|
|
|
|
|
|
reply["data"] = results;
|
|
|
|
|
|
if (mqtt_client) mqtt_client->publish(g_app_config.mqtt.topics.video_down, reply.dump());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-15 15:33:00 +08:00
|
|
|
|
|
2025-10-15 17:01:43 +08:00
|
|
|
|
// 简化 start_camera / stop_camera,不阻塞 MQTT 回调
|
|
|
|
|
|
RTMPManager::StreamResultInfo RTMPManager::start_camera(const Camera &cam, int streamType)
|
|
|
|
|
|
{
|
|
|
|
|
|
StreamResultInfo res;
|
|
|
|
|
|
std::string key = cam.name + (streamType == 0 ? "_main" : "_sub");
|
2025-10-15 15:33:00 +08:00
|
|
|
|
|
2025-10-15 17:01:43 +08:00
|
|
|
|
{
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(streams_mutex);
|
|
|
|
|
|
if (streams.find(key) != streams.end())
|
2025-10-15 16:21:25 +08:00
|
|
|
|
{
|
2025-10-15 17:01:43 +08:00
|
|
|
|
res.result = 0;
|
|
|
|
|
|
res.reason = "Already streaming";
|
|
|
|
|
|
return res;
|
2025-10-15 16:21:25 +08:00
|
|
|
|
}
|
2025-10-15 17:01:43 +08:00
|
|
|
|
|
|
|
|
|
|
auto ctx = std::make_unique<StreamContext>();
|
|
|
|
|
|
ctx->running = true;
|
|
|
|
|
|
ctx->thread = std::thread(
|
|
|
|
|
|
[cam, streamType, ctx_ptr = ctx.get()]
|
|
|
|
|
|
{
|
|
|
|
|
|
// TODO: GStreamer pipeline loop
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟启动
|
|
|
|
|
|
StreamResultInfo r;
|
|
|
|
|
|
r.result = 0;
|
|
|
|
|
|
r.reason = "Started OK";
|
|
|
|
|
|
ctx_ptr->start_result.set_value(r);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
streams[key] = std::move(ctx);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 非阻塞:直接返回 future 的结果即可
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
res = streams[key]->start_result.get_future().get();
|
2025-10-15 15:33:00 +08:00
|
|
|
|
}
|
2025-10-15 17:01:43 +08:00
|
|
|
|
catch (...)
|
2025-10-15 15:33:00 +08:00
|
|
|
|
{
|
|
|
|
|
|
res.result = 1;
|
2025-10-15 17:01:43 +08:00
|
|
|
|
res.reason = "Exception in start_camera";
|
2025-10-15 13:09:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-15 15:01:55 +08:00
|
|
|
|
return res;
|
|
|
|
|
|
}
|
2025-10-15 11:27:53 +08:00
|
|
|
|
|
2025-10-15 17:01:43 +08:00
|
|
|
|
RTMPManager::StreamResultInfo RTMPManager::stop_camera(const Camera &cam, int streamType)
|
2025-10-15 15:01:55 +08:00
|
|
|
|
{
|
|
|
|
|
|
StreamResultInfo res;
|
2025-10-15 17:01:43 +08:00
|
|
|
|
std::string key = cam.name + (streamType == 0 ? "_main" : "_sub");
|
2025-10-15 11:27:53 +08:00
|
|
|
|
|
2025-10-15 15:01:55 +08:00
|
|
|
|
std::unique_ptr<StreamContext> ctx;
|
|
|
|
|
|
{
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(streams_mutex);
|
|
|
|
|
|
auto it = streams.find(key);
|
|
|
|
|
|
if (it == streams.end())
|
|
|
|
|
|
{
|
|
|
|
|
|
res.result = 0;
|
|
|
|
|
|
res.reason = "Not streaming";
|
|
|
|
|
|
return res;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ctx = std::move(it->second);
|
|
|
|
|
|
streams.erase(it);
|
2025-10-15 17:01:43 +08:00
|
|
|
|
ctx->running = false;
|
2025-10-15 15:01:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-15 16:13:52 +08:00
|
|
|
|
if (ctx->thread.joinable()) ctx->thread.join();
|
2025-10-15 15:01:55 +08:00
|
|
|
|
res.result = 0;
|
2025-10-15 17:01:43 +08:00
|
|
|
|
res.reason = "Stopped OK";
|
2025-10-15 15:01:55 +08:00
|
|
|
|
return res;
|
2025-10-15 17:01:43 +08:00
|
|
|
|
}
|