GStreamer rtspsrc切换32次后失效,如何不重启释放资源?
问题描述
我有一台运行嵌入式Linux的设备,能显示摄像头的RTSP流,用户可在窗口模式与全屏模式间切换流。但切换32次后,流停止工作,初步定位问题出在rtspsrc本身。
核心疑问:如何在不重启程序的情况下清理GStreamer相关内存?
现象细节:
- 用
gst-launch-1.0启动管道时,因每次都会终止程序,可正常重启超过32次;但在程序内切换流,当rtspsrc编号递增至31后,再用gst-launch-1.0启动RTSP管道也无法显示流,必须终止所有使用GStreamer的程序才能重置rtspsrc计数。 - 开启
rtspsrc调试(export GST_DEBUG="rtspsrc:6")后,每次启动流都会输出日志,显示rtspsrcX编号持续递增,即便前序流已停止:首次运行日志: **rtspsrc gstrtspsrc.c:8834:gst_rtspsrc_print_sdp_media:<rtspsrc0> RTSP response message** 第二次运行: **rtspsrc gstrtspsrc.c:8855:gst_rtspsrc_print_sdp_media:<rtspsrc1> RTSP response message** 持续启停流,编号会递增至31,此时流不再显示: **rtspsrc gstrtspsrc.c:8855:gst_rtspsrc_print_sdp_media:<rtspsrc31> RTSP response message**
尝试过的操作:
- 每次重启流时新建
GMainContext,但无效;每次调用gst_is_initialized()均返回true。 - 停止流时通过另一线程调用
g_main_loop_quit(loop_),并在stream_video函数内依次将管道状态切换到PAUSED、READY、NULL,再释放管道、上下文和主循环对象,但rtspsrc编号仍持续增长。
程序核心代码:
GMainLoop *loop_; // 两种管道配置,对应窗口和全屏模式 std::string pipeline_win = "rtspsrc location=rtsp://192.168.0.243/0 latency=0 ! rtph264depay ! h264parse ! imxvpudec ! imxipuvideosink window-width=512 window-height=384 sync=false"; std::string pipeline_full = "rtspsrc location=rtsp://192.168.0.243/0 latency=0 ! rtph264depay ! h264parse ! imxvpudec ! imxipuvideosink window-width=1024 window-height=768 sync=false"; void stream_video(std::string pipeline) { GMainContext* context; GstElement *pipelineElement; GstBus *bus = NULL; guint bus_watch_id = 0; GstState state; try { if(!gst_is_initialized()) { std::cout << "GST Is not initialized - initializing " << pipeline.c_str(); gst_init_check(nullptr,nullptr,nullptr); } context = g_main_context_new(); // 尝试新建上下文解决32次限制,但调试时rtspsrc编号仍递增 loop_ = g_main_loop_new (context, FALSE); pipelineElement = gst_parse_launch(pipeline.c_str(), NULL); bus = gst_pipeline_get_bus (GST_PIPELINE (pipelineElement)); bus_watch_id = gst_bus_add_watch (bus, bus_call, loop_); gst_object_unref (bus); bus = NULL; gst_element_set_state(pipelineElement, GST_STATE_READY ); gst_element_set_state(pipelineElement, GST_STATE_PAUSED ); gst_element_set_state(pipelineElement, GST_STATE_PLAYING); if (gst_element_get_state (pipelineElement, &state, NULL, 2*GST_SECOND) == GST_STATE_CHANGE_FAILURE) { std::cout << "gst: Failed to change states State:" << state << " ID: " << stream_id_; } else { std::cout << "gst: Running..." << " ID: " << stream_id_ << " State:" << state << " Loop:" << loop_; g_main_loop_run (loop_); // 阻塞直到loop退出(EOS、错误、停止请求) } // 停止流时切换状态 gst_element_set_state(pipelineElement, GST_STATE_PAUSED); gst_element_set_state(pipelineElement, GST_STATE_READY ); gst_element_set_state(pipelineElement, GST_STATE_NULL); // 状态切换需遵循GStreamer状态机规则 g_source_remove (bus_watch_id); std::cout << "gst: Removing pipelineElement " << pipelineElement; gst_object_unref (GST_OBJECT (pipelineElement)); pipelineElement = NULL; g_main_context_unref (context); context = NULL; g_main_loop_unref (loop_); loop_ = nullptr; std::cout << "gst: Deleted pipeline" << " ID: " << stream_id_ << " State: " << state; } catch(const std::exception& e) { std::cout << "Error Caught: stream_video " << e.what(); } return; }
解决方案
1. 确保RTSP资源彻底释放
rtspsrc内部维护网络连接、会话等资源,32次限制大概率是因为停止流时部分底层资源(如socket、RTSP会话句柄)未正确回收。优化步骤:
- 切换到
GST_STATE_NULL后,等待状态切换完成再释放资源,避免资源泄漏:// 切换到NULL状态后等待确认 if (gst_element_get_state(pipelineElement, NULL, NULL, 5*GST_SECOND) != GST_STATE_CHANGE_SUCCESS) { std::cout << "Warning: Failed to set pipeline to NULL state" << std::endl; } - 给
rtspsrc添加close-socket=true属性,强制停止时立即关闭socket:
修改管道字符串:rtspsrc location=rtsp://192.168.0.243/0 latency=0 close-socket=true ! rtph264depay ! h264parse ! imxvpudec ! imxipuvideosink ...
2. 复用管道而非每次重建
频繁创建销毁管道会增加资源泄漏风险,建议复用核心管道元素,仅修改视频输出窗口大小:
- 初始化时创建一次完整管道,保存
rtspsrc、解码链和imxipuvideosink的引用 - 切换窗口/全屏时,直接修改
sink的窗口属性:
这种方式从根源上避免// 假设已保存videosink元素引用 g_object_set(videosink, "window-width", 1024, "window-height", 768, NULL);rtspsrc编号递增问题。
3. 完善总线消息处理
确保bus_call函数正确处理所有总线消息(尤其是ERROR和EOS),避免未处理消息导致资源挂起:
static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data) { GMainLoop *loop = (GMainLoop *) data; switch (GST_MESSAGE_TYPE(msg)) { case GST_MESSAGE_ERROR: { GError *err; gchar *debug; gst_message_parse_error(msg, &err, &debug); g_printerr("Error: %s\n", err->message); g_error_free(err); g_free(debug); g_main_loop_quit(loop); break; } case GST_MESSAGE_EOS: g_main_loop_quit(loop); break; default: break; } return TRUE; }
4. 强制重置GStreamer全局资源(极端场景)
若以上方法无效,可在每次停止流后调用gst_deinit()彻底重置GStreamer,下次启动时重新初始化:
// 销毁管道、上下文和主循环后执行 gst_deinit();
注意:该操作会清理所有GStreamer全局状态,可能影响其他GStreamer模块,单流场景可使用。
5. 排查嵌入式平台资源限制
嵌入式Linux默认进程文件描述符上限可能为32,刚好匹配你的问题现象。排查方法:
# 查看进程文件描述符使用量 lsof -p <进程PID> | wc -l # 查看系统文件描述符限制 ulimit -n
若为文件描述符耗尽,可提高进程限制:
#include <sys/resource.h> struct rlimit rlim; getrlimit(RLIMIT_NOFILE, &rlim); rlim.rlim_cur = 128; // 调整为合适值 setrlimit(RLIMIT_NOFILE, &rlim);
内容的提问来源于stack exchange,提问作者Luke
相关产品推荐
相关产品推荐

