1
This commit is contained in:
parent
67742b59ee
commit
3d66c2ae1b
@ -9,6 +9,7 @@
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "logger.hpp"
|
||||
|
||||
@ -21,9 +22,14 @@ std::unordered_map<std::string, bool> RTSPManager::streaming_status;
|
||||
std::unordered_map<std::string, GstRTSPMediaFactory*> RTSPManager::mounted_factories;
|
||||
std::mutex RTSPManager::mounted_factories_mutex;
|
||||
|
||||
std::unordered_map<std::string, std::vector<GstRTSPMedia*>> RTSPManager::media_map;
|
||||
std::mutex RTSPManager::media_map_mutex;
|
||||
// ==============================
|
||||
// ✅ 不再保存 GstRTSPMedia* 裸指针
|
||||
// 改为:每路 camera 的“活跃 session 计数”
|
||||
// ==============================
|
||||
static std::unordered_map<std::string, int> g_client_count;
|
||||
static std::mutex g_client_count_mutex;
|
||||
|
||||
// 日志降噪:media-configure 过于频繁时只提示
|
||||
static std::unordered_map<std::string, std::chrono::steady_clock::time_point> last_media_ts;
|
||||
static std::mutex last_media_ts_mutex;
|
||||
|
||||
@ -98,11 +104,6 @@ GstRTSPMediaFactory* RTSPManager::create_media_factory(const Camera& cam)
|
||||
std::to_string(w) + ",height=" + std::to_string(h) + ",framerate=" + std::to_string(cam.fps) + "/1";
|
||||
|
||||
// 关键:tee 一路给 RTSP,一路给 fakesink,保持 v4l2src / mpph264enc 永远在跑
|
||||
//
|
||||
// 结构:
|
||||
// v4l2src -> caps -> tee name=t
|
||||
// tee src0: t. -> queue -> mpph264enc -> h264parse -> rtph264pay (给 RTSP)
|
||||
// tee src1: t. -> queue -> fakesink (假消费者,让 DMA 持续 dequeue)
|
||||
std::string launch_str = "( v4l2src device=" + cam.device +
|
||||
" io-mode=2 is-live=true do-timestamp=true"
|
||||
" ! " +
|
||||
@ -125,7 +126,7 @@ GstRTSPMediaFactory* RTSPManager::create_media_factory(const Camera& cam)
|
||||
// 所有客户端共享同一个 pipeline,避免频繁拉起 v4l2 / encoder
|
||||
gst_rtsp_media_factory_set_shared(factory, TRUE);
|
||||
|
||||
// 客户端断开时不要乱 reset,交给我们自己处理 / 或干脆不动
|
||||
// 客户端断开时不要乱 suspend/reset
|
||||
gst_rtsp_media_factory_set_suspend_mode(factory, GST_RTSP_SUSPEND_MODE_NONE);
|
||||
|
||||
g_signal_connect_data(factory, "media-configure", G_CALLBACK(on_media_created), g_strdup(cam.name.c_str()),
|
||||
@ -139,7 +140,7 @@ void RTSPManager::start(const std::vector<Camera>& cams)
|
||||
server = gst_rtsp_server_new();
|
||||
gst_rtsp_server_set_service(server, "8554");
|
||||
|
||||
// ✅ 在 attach 之前设置 backlog(限制 pending 连接队列)
|
||||
// ✅ attach 之前设置 backlog
|
||||
gst_rtsp_server_set_backlog(server, 32);
|
||||
|
||||
loop = g_main_loop_new(nullptr, FALSE);
|
||||
@ -162,6 +163,12 @@ void RTSPManager::start(const std::vector<Camera>& cams)
|
||||
streaming_status[cam.name] = true;
|
||||
}
|
||||
|
||||
// 初始化计数
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_client_count_mutex);
|
||||
g_client_count.emplace(cam.name, 0);
|
||||
}
|
||||
|
||||
LOG_INFO("[RTSP] Camera '" + cam.name + "' mounted at rtsp://0.0.0.0:8554" + mount_point);
|
||||
}
|
||||
|
||||
@ -190,52 +197,62 @@ void RTSPManager::on_media_created(GstRTSPMediaFactory*, GstRTSPMedia* media, gp
|
||||
{
|
||||
const char* cam_name = static_cast<const char*>(user_data);
|
||||
|
||||
bool suppressed = false;
|
||||
// ✅ 只做日志降噪:别让“抑制”影响生命周期处理
|
||||
bool noisy = false;
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(last_media_ts_mutex);
|
||||
auto& last = last_media_ts[cam_name];
|
||||
if (last.time_since_epoch().count() != 0 && now - last < std::chrono::seconds(2))
|
||||
{
|
||||
suppressed = true; // 只标记,不 return
|
||||
}
|
||||
if (last.time_since_epoch().count() != 0 && now - last < std::chrono::seconds(2)) noisy = true;
|
||||
last = now;
|
||||
}
|
||||
|
||||
if (suppressed) { LOG_WARN(std::string("[RTSP] media-configure suppressed (too frequent): ") + cam_name); }
|
||||
int count_after = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_client_count_mutex);
|
||||
int& c = g_client_count[cam_name]; // 若不存在会创建;这里无所谓
|
||||
c++;
|
||||
count_after = c;
|
||||
}
|
||||
|
||||
if (noisy)
|
||||
{
|
||||
LOG_WARN(std::string("[RTSP] media-configure frequent: ") + cam_name +
|
||||
" (active_sessions=" + std::to_string(count_after) + ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INFO(std::string("[RTSP] media-configure for camera: ") + cam_name);
|
||||
}
|
||||
|
||||
// ✅ 生命周期一定要完整绑定
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(media_map_mutex);
|
||||
media_map[cam_name].push_back(media);
|
||||
LOG_INFO(std::string("[RTSP] media-configure for camera: ") + cam_name +
|
||||
" (active_sessions=" + std::to_string(count_after) + ")");
|
||||
}
|
||||
|
||||
// ✅ 生命周期绑定:unprepared 一定会对应减计数
|
||||
g_signal_connect_data(media, "unprepared", G_CALLBACK(on_media_unprepared), g_strdup(cam_name),
|
||||
(GClosureNotify)g_free, (GConnectFlags)0);
|
||||
}
|
||||
|
||||
void RTSPManager::on_media_unprepared(GstRTSPMedia* media, gpointer user_data)
|
||||
void RTSPManager::on_media_unprepared(GstRTSPMedia* /*media*/, gpointer user_data)
|
||||
{
|
||||
const char* cam_name = static_cast<const char*>(user_data);
|
||||
LOG_INFO(std::string("[RTSP] media-unprepared: ") + cam_name);
|
||||
|
||||
std::lock_guard<std::mutex> lock(media_map_mutex);
|
||||
auto it = media_map.find(cam_name);
|
||||
if (it != media_map.end())
|
||||
int count_after = 0;
|
||||
{
|
||||
auto& vec = it->second;
|
||||
vec.erase(std::remove(vec.begin(), vec.end(), media), vec.end());
|
||||
if (vec.empty()) media_map.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_WARN(std::string("[RTSP] media-unprepared but no entry in media_map for camera: ") + cam_name);
|
||||
std::lock_guard<std::mutex> lock(g_client_count_mutex);
|
||||
auto it = g_client_count.find(cam_name);
|
||||
if (it == g_client_count.end())
|
||||
{
|
||||
// 可能发生在 unmount 后 teardown 仍在进行的边界情况
|
||||
LOG_WARN(std::string("[RTSP] media-unprepared but cam not in client_count: ") + cam_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (it->second > 0) it->second--;
|
||||
count_after = it->second;
|
||||
}
|
||||
|
||||
LOG_INFO(std::string("[RTSP] media-unprepared: ") + cam_name + " (active_sessions=" + std::to_string(count_after) +
|
||||
")");
|
||||
|
||||
// ✅ 不要 g_object_unref(media) —— gst-rtsp-server 会处理
|
||||
}
|
||||
|
||||
@ -262,6 +279,11 @@ gboolean RTSPManager::mount_camera_in_main(gpointer data)
|
||||
streaming_status[cam->name] = true;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_client_count_mutex);
|
||||
g_client_count[cam->name] = 0;
|
||||
}
|
||||
|
||||
LOG_INFO("[RTSP] Camera '" + cam->name + "' mounted at rtsp://localhost:8554" + mount_point);
|
||||
|
||||
delete cam;
|
||||
@ -280,14 +302,7 @@ gboolean RTSPManager::unmount_camera_in_main(gpointer data)
|
||||
std::string cam_name = cam->name;
|
||||
std::string mount_point = "/" + cam_name;
|
||||
|
||||
// ✅ 不要手动 gst_rtsp_media_unprepare / set_state(NULL)
|
||||
// 只移除我们自己的记录。media 的 teardown 交给 gst-rtsp-server。
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(media_map_mutex);
|
||||
media_map.erase(cam_name);
|
||||
}
|
||||
|
||||
// 移除 factory
|
||||
// ✅ 仅移除 mount;media teardown 交给 gst-rtsp-server
|
||||
GstRTSPMountPoints* mounts = gst_rtsp_server_get_mount_points(server);
|
||||
if (mounts)
|
||||
{
|
||||
@ -300,16 +315,17 @@ gboolean RTSPManager::unmount_camera_in_main(gpointer data)
|
||||
auto it = mounted_factories.find(cam_name);
|
||||
if (it != mounted_factories.end())
|
||||
{
|
||||
// ⚠️ 这里是否 unref factory 取决于 mount_points 是否持有引用。
|
||||
// 一般 remove_factory 后会释放其引用;你再 unref 可能导致过度 unref。
|
||||
// 为了稳,建议:先别手动 unref,避免 double-unref。
|
||||
// if (it->second) g_object_unref(it->second);
|
||||
|
||||
// 为了避免 double-unref:这里不手动 unref factory
|
||||
mounted_factories.erase(it);
|
||||
}
|
||||
streaming_status[cam_name] = false;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_client_count_mutex);
|
||||
g_client_count.erase(cam_name);
|
||||
}
|
||||
|
||||
LOG_INFO("[RTSP] Camera '" + cam_name + "' unmounted.");
|
||||
delete cam;
|
||||
return G_SOURCE_REMOVE;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user