kunlang_video/src/rtsp_manager.cpp
2025-09-17 09:44:40 +08:00

287 lines
8.5 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.

// rtsp_manager.cpp
#include "rtsp_manager.hpp"
#include "logger.hpp"
#include <iostream>
#include <algorithm>
#include <thread>
#include <chrono>
// 静态变量定义
GMainLoop *RTSPManager::loop = nullptr;
GMainContext *RTSPManager::main_context = nullptr;
GstRTSPServer *RTSPManager::server = nullptr;
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;
void RTSPManager::init()
{
gst_init(nullptr, nullptr);
LOG_INFO("[RTSP] GStreamer initialized.");
}
// 创建 media factory
GstRTSPMediaFactory *RTSPManager::create_media_factory(const Camera &cam)
{
int out_width = cam.width;
int out_height = cam.height;
// 如果是 1280x960则裁剪到 1280x720
std::string videocrop_str;
if (cam.width == 1280 && cam.height == 960)
{
out_height = 720;
int crop_top = (960 - 720) / 2;
videocrop_str = " ! videocrop top=" + std::to_string(crop_top) + " bottom=" + std::to_string(crop_top);
}
std::string launch_str =
"( v4l2src device=" + cam.device +
" ! video/x-raw,format=NV12,width=" + std::to_string(cam.width) +
",height=" + std::to_string(cam.height) +
",framerate=" + std::to_string(cam.fps) + "/1"
" ! queue max-size-buffers=8 leaky=2" +
videocrop_str +
" ! queue max-size-buffers=8 leaky=2"
" ! mpph264enc"
" ! h264parse"
" ! rtph264pay name=pay0 pt=96 )";
GstRTSPMediaFactory *factory = gst_rtsp_media_factory_new();
gst_rtsp_media_factory_set_launch(factory, launch_str.c_str());
gst_rtsp_media_factory_set_shared(factory, TRUE);
// 使用 media-configure 信号代替 media-created
g_signal_connect_data(factory, "media-configure", G_CALLBACK(on_media_created),
g_strdup(cam.name.c_str()), (GClosureNotify)g_free, static_cast<GConnectFlags>(0));
return factory;
}
// 启动 RTSP server
void RTSPManager::start(const std::vector<Camera> &cameras)
{
server = gst_rtsp_server_new();
gst_rtsp_server_set_service(server, "8554");
loop = g_main_loop_new(nullptr, FALSE);
main_context = g_main_loop_get_context(loop);
gst_rtsp_server_attach(server, nullptr);
LOG_INFO("[RTSP] Server running on rtsp://localhost:8554");
g_main_loop_run(loop);
if (server)
{
g_object_unref(server);
server = nullptr;
}
if (loop)
{
g_main_loop_unref(loop);
loop = nullptr;
}
LOG_INFO("[RTSP] Server stopped.");
}
// media-configure 信号处理
void RTSPManager::on_media_created(GstRTSPMediaFactory *factory, GstRTSPMedia *media, gpointer user_data)
{
const char *cam_name = static_cast<const char *>(user_data);
g_object_ref(media); // 增加引用计数,防止被提前销毁
{
std::lock_guard<std::mutex> lock(media_map_mutex);
media_map[cam_name].push_back(media);
}
// 连接 unprepared 信号,当 pipeline 被销毁时移除
g_signal_connect_data(media, "unprepared", G_CALLBACK(on_media_unprepared),
g_strdup(cam_name), (GClosureNotify)g_free, static_cast<GConnectFlags>(0));
}
// unprepared 信号处理
void RTSPManager::on_media_unprepared(GstRTSPMedia *media, gpointer user_data)
{
const char *cam_name = static_cast<const char *>(user_data);
{
std::lock_guard<std::mutex> lock(media_map_mutex);
auto it = media_map.find(cam_name);
if (it != media_map.end())
{
auto &media_list = it->second;
media_list.erase(std::remove(media_list.begin(), media_list.end(), media), media_list.end());
if (media_list.empty())
{
media_map.erase(it);
}
}
}
g_object_unref(media); // 释放引用
}
// 挂载摄像头
gboolean RTSPManager::mount_camera_in_main(gpointer data)
{
Camera *cam = static_cast<Camera *>(data);
if (!cam || !server)
{
delete cam;
return G_SOURCE_REMOVE;
}
GstRTSPMountPoints *mounts = gst_rtsp_server_get_mount_points(server);
if (!mounts)
{
delete cam;
return G_SOURCE_REMOVE;
}
std::string mount_point = "/" + cam->name;
GstRTSPMediaFactory *factory = create_media_factory(*cam);
if (!factory)
{
g_object_unref(mounts);
delete cam;
return G_SOURCE_REMOVE;
}
gst_rtsp_mount_points_add_factory(mounts, mount_point.c_str(), factory);
g_object_unref(mounts);
{
std::lock_guard<std::mutex> lock(mounted_factories_mutex);
mounted_factories[cam->name] = factory;
streaming_status[cam->name] = true;
}
LOG_INFO("[RTSP] Camera '" + cam->name + "' mounted at rtsp://localhost:8554" + mount_point);
delete cam;
return G_SOURCE_REMOVE;
}
// 卸载摄像头
gboolean RTSPManager::unmount_camera_in_main(gpointer data)
{
Camera *cam = static_cast<Camera *>(data);
if (!cam || !server)
{
delete cam;
return G_SOURCE_REMOVE;
}
std::string cam_name = cam->name;
std::string mount_point = "/" + cam_name;
// 停止所有媒体
{
std::lock_guard<std::mutex> lock(media_map_mutex);
auto it = media_map.find(cam_name);
if (it != media_map.end())
{
for (GstRTSPMedia *media : it->second)
{
gst_element_set_state(gst_rtsp_media_get_element(media), GST_STATE_NULL);
// g_object_unref(media);
}
it->second.clear();
media_map.erase(it);
}
}
// 卸载 factory
GstRTSPMountPoints *mounts = gst_rtsp_server_get_mount_points(server);
if (mounts)
{
gst_rtsp_mount_points_remove_factory(mounts, mount_point.c_str());
g_object_unref(mounts);
}
{
std::lock_guard<std::mutex> lock(mounted_factories_mutex);
auto it = mounted_factories.find(cam_name);
if (it != mounted_factories.end())
{
if (it->second && G_IS_OBJECT(it->second))
g_object_unref(it->second);
mounted_factories.erase(it);
}
streaming_status[cam_name] = false;
}
LOG_INFO("[RTSP] Camera '" + cam_name + "' unmounted.");
delete cam;
return G_SOURCE_REMOVE;
}
// 公共挂载/卸载接口
void RTSPManager::mount_camera(const Camera &cam)
{
Camera *camCopy = new Camera(cam);
g_main_context_invoke(main_context, [](gpointer data) -> gboolean
{ return RTSPManager::mount_camera_in_main(data); }, camCopy);
}
void RTSPManager::unmount_camera(const Camera &cam)
{
Camera *camCopy = new Camera(cam);
g_main_context_invoke(main_context, [](gpointer data) -> gboolean
{ return RTSPManager::unmount_camera_in_main(data); }, camCopy);
}
// 是否正在流
bool RTSPManager::is_streaming(const std::string &cam_name)
{
std::lock_guard<std::mutex> lock(mounted_factories_mutex);
auto it = streaming_status.find(cam_name);
return it != streaming_status.end() ? it->second : false;
}
// 停止 server
void RTSPManager::stop()
{
// 先卸载所有挂载摄像头
std::vector<std::string> cams_to_unmount;
{
std::lock_guard<std::mutex> lock(mounted_factories_mutex);
for (const auto &kv : mounted_factories)
cams_to_unmount.push_back(kv.first);
}
for (const auto &cam_name : cams_to_unmount)
{
Camera cam;
cam.name = cam_name;
unmount_camera(cam);
}
// 等待所有流停止(最多 5 秒)
auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
while (is_any_streaming() && std::chrono::steady_clock::now() < deadline)
{
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
// 退出 main loop
if (loop)
{
g_main_context_invoke(main_context, [](gpointer data) -> gboolean
{
g_main_loop_quit(static_cast<GMainLoop *>(data));
return G_SOURCE_REMOVE; }, loop);
}
}
// 新增接口:检查是否还有摄像头在流
bool RTSPManager::is_any_streaming()
{
std::lock_guard<std::mutex> lock(mounted_factories_mutex);
for (const auto &kv : streaming_status)
if (kv.second)
return true;
return false;
}