深入SRS 4.0源码用GDB与日志追踪RTMP推流全链路当你第一次成功用FFmpeg向SRS服务器推流时是否好奇过这段视频数据究竟经历了怎样的旅程本文将带你化身代码侦探用GDB调试器和SRS日志还原RTMP推流的完整调用链路。这不是普通的源码阅读指南而是一次沉浸式的调试实战——我们会像外科医生解剖血管般逐层揭开SrsRtmpConn的生命周期。1. 调试环境搭建与工具准备在开始追踪代码之前需要配置好能随时打断点、查变量的开发环境。推荐使用VSCode配合gdb调试比纯命令行更直观。以下是关键准备步骤# 编译调试版SRS (关键配置) ./configure --debugfull --with-gdbon make -j8必备工具清单GDB增强插件gdb-dashboard或gef内存可视化日志分析工具lnav彩色高亮RTMP状态变化网络抓包tcpdump过滤1935端口tcpdump -i any port 1935 -w rtmp.pcap提示在~/.gdbinit中添加set print pretty on能让结构体输出更易读遇到连接问题时先确认SRS基础功能正常。用这个最小化测试命令验证推流ffmpeg -re -i test.flv -c copy -f flv rtmp://localhost/live/stream2. RTMP握手阶段的代码追踪当客户端首次连接1935端口时SRS会创建SrsRtmpConn实例。我们在src/app/srs_app_rtmp_conn.cpp的构造函数设断点b SrsRtmpConn::SrsRtmpConn commands print *this continue end握手过程分为三个关键阶段对应日志中会出现以下标记[INFO] RTMP handshake C0C1 received [DEBUG] RTMP handshake S0S1S2 sent [TRACE] RTMP handshake C2 validated关键数据结构观察// 握手状态机 enum SrsRtmpHandshakePhase { SrsRtmpHandshakeUninitialized 0, SrsRtmpHandshakeC0C1Done, SrsRtmpHandshakeS0S1S2Done, SrsRtmpHandshakeComplete };用gdb监控阶段变化watch this-handshake_phase3. 媒体数据传输的调用栈解析成功握手后真正的媒体数据处理开始。这个阶段最常出现的三个核心类是SrsRtmpConn连接载体维护状态机SrsProtocol协议解析器处理chunk拆分SrsRtmpServer业务逻辑入口当收到视频数据包时典型调用栈如下通过bt full获取#0 SrsProtocol::recv_message (this0x61700000b900, pmsg0x7fffffffd5a0) #1 SrsRtmpConn::stream_service_cycle (this0x60b00000a800) #2 SrsRtmpConn::do_cycle (this0x60b00000a800) #3 SrsSTCoroutine::cycle (this0x60b00000a700)关键断点设置技巧# 监控视频tag处理 b SrsProtocol::on_recv_message if msg-header.message_type 9 # 观察时间戳计算 b SrsRtmpServer::on_publish if strcmp(stream, live/stream) 04. 状态协程与事件循环揭秘SRS高性能的核心在于state-threads的巧妙运用。每个客户端连接都运行在独立的st_thread中// 典型协程调度流程 st_thread_create(conn_thread, conn_obj); st_cond_wait(conn-conn_done);用这些gdb命令观察协程状态info threads # 查看所有系统线程 thread apply all bt # 获取全部线程堆栈 p *(st_netfd_t*)0x60b00000b000 # 查看文件描述符状态重要观察点当客户端网络延迟时st_read会主动yield协程日志中出现[TRACE] st: yield标记协程切换使用info st命令查看协程状态需gef插件5. 典型问题诊断与调试技巧案例1推流中断无报错检查点SrsRtmpConn::stream_service_cycle返回值关键日志[ERROR] recv msg failed, ret%d调试命令catch syscall close bt案例2时间戳跳跃问题检查SrsRtmpServer::on_publish中的stream-set_ag调用监控SrsMessageHeader.timestamp变化watch *(uint32_t*)0x61700000c100内存诊断技巧# 跟踪对象创建/销毁 break malloc if size sizeof(SrsRtmpConn) break free6. 从调试到二次开发理解了调用链路后可以尝试定制开发。比如添加HDR支持在SrsRtmpServer::on_publish中检测metadata扩展SrsVideoFrame结构体struct SrsVideoFrame { bool is_hdr; uint32_t color_primaries; // ... };调试时可以用条件断点聚焦问题b SrsRtmpServer::on_metadata if metadata.find(HDR) ! metadata.end()掌握这些调试方法后下次遇到SRS的异常日志时你就能快速定位到src/app/srs_app_rtmp_conn.cpp:428这样的具体位置而不是对着文档盲目猜测。记住最好的学习方式就是让代码在断点处停下来亲眼看看变量里藏着的秘密。
保姆级教程:结合 GDB 和 SRS 4.0 日志,一步步拆解 RTMP 推流的核心调用链路
发布时间:2026/6/7 8:35:07
深入SRS 4.0源码用GDB与日志追踪RTMP推流全链路当你第一次成功用FFmpeg向SRS服务器推流时是否好奇过这段视频数据究竟经历了怎样的旅程本文将带你化身代码侦探用GDB调试器和SRS日志还原RTMP推流的完整调用链路。这不是普通的源码阅读指南而是一次沉浸式的调试实战——我们会像外科医生解剖血管般逐层揭开SrsRtmpConn的生命周期。1. 调试环境搭建与工具准备在开始追踪代码之前需要配置好能随时打断点、查变量的开发环境。推荐使用VSCode配合gdb调试比纯命令行更直观。以下是关键准备步骤# 编译调试版SRS (关键配置) ./configure --debugfull --with-gdbon make -j8必备工具清单GDB增强插件gdb-dashboard或gef内存可视化日志分析工具lnav彩色高亮RTMP状态变化网络抓包tcpdump过滤1935端口tcpdump -i any port 1935 -w rtmp.pcap提示在~/.gdbinit中添加set print pretty on能让结构体输出更易读遇到连接问题时先确认SRS基础功能正常。用这个最小化测试命令验证推流ffmpeg -re -i test.flv -c copy -f flv rtmp://localhost/live/stream2. RTMP握手阶段的代码追踪当客户端首次连接1935端口时SRS会创建SrsRtmpConn实例。我们在src/app/srs_app_rtmp_conn.cpp的构造函数设断点b SrsRtmpConn::SrsRtmpConn commands print *this continue end握手过程分为三个关键阶段对应日志中会出现以下标记[INFO] RTMP handshake C0C1 received [DEBUG] RTMP handshake S0S1S2 sent [TRACE] RTMP handshake C2 validated关键数据结构观察// 握手状态机 enum SrsRtmpHandshakePhase { SrsRtmpHandshakeUninitialized 0, SrsRtmpHandshakeC0C1Done, SrsRtmpHandshakeS0S1S2Done, SrsRtmpHandshakeComplete };用gdb监控阶段变化watch this-handshake_phase3. 媒体数据传输的调用栈解析成功握手后真正的媒体数据处理开始。这个阶段最常出现的三个核心类是SrsRtmpConn连接载体维护状态机SrsProtocol协议解析器处理chunk拆分SrsRtmpServer业务逻辑入口当收到视频数据包时典型调用栈如下通过bt full获取#0 SrsProtocol::recv_message (this0x61700000b900, pmsg0x7fffffffd5a0) #1 SrsRtmpConn::stream_service_cycle (this0x60b00000a800) #2 SrsRtmpConn::do_cycle (this0x60b00000a800) #3 SrsSTCoroutine::cycle (this0x60b00000a700)关键断点设置技巧# 监控视频tag处理 b SrsProtocol::on_recv_message if msg-header.message_type 9 # 观察时间戳计算 b SrsRtmpServer::on_publish if strcmp(stream, live/stream) 04. 状态协程与事件循环揭秘SRS高性能的核心在于state-threads的巧妙运用。每个客户端连接都运行在独立的st_thread中// 典型协程调度流程 st_thread_create(conn_thread, conn_obj); st_cond_wait(conn-conn_done);用这些gdb命令观察协程状态info threads # 查看所有系统线程 thread apply all bt # 获取全部线程堆栈 p *(st_netfd_t*)0x60b00000b000 # 查看文件描述符状态重要观察点当客户端网络延迟时st_read会主动yield协程日志中出现[TRACE] st: yield标记协程切换使用info st命令查看协程状态需gef插件5. 典型问题诊断与调试技巧案例1推流中断无报错检查点SrsRtmpConn::stream_service_cycle返回值关键日志[ERROR] recv msg failed, ret%d调试命令catch syscall close bt案例2时间戳跳跃问题检查SrsRtmpServer::on_publish中的stream-set_ag调用监控SrsMessageHeader.timestamp变化watch *(uint32_t*)0x61700000c100内存诊断技巧# 跟踪对象创建/销毁 break malloc if size sizeof(SrsRtmpConn) break free6. 从调试到二次开发理解了调用链路后可以尝试定制开发。比如添加HDR支持在SrsRtmpServer::on_publish中检测metadata扩展SrsVideoFrame结构体struct SrsVideoFrame { bool is_hdr; uint32_t color_primaries; // ... };调试时可以用条件断点聚焦问题b SrsRtmpServer::on_metadata if metadata.find(HDR) ! metadata.end()掌握这些调试方法后下次遇到SRS的异常日志时你就能快速定位到src/app/srs_app_rtmp_conn.cpp:428这样的具体位置而不是对着文档盲目猜测。记住最好的学习方式就是让代码在断点处停下来亲眼看看变量里藏着的秘密。