简介本资源是一个基于HappyTime开源多媒体库的RTSP流推送实践示例面向音视频开发工程师、流媒体系统初学者及嵌入式多媒体应用开发者聚焦H.264编码视频数据通过RTSP协议实时推送到服务器的核心实现。压缩包共8个文件含C源码h264.cpp、头文件h264.h、Visual Studio工程配置.vcxproj/.sln/.filters、Makefile构建脚本及一个测试用H.264原始码流文件test.264完整覆盖跨平台编译与调试所需结构包体大小为2.88MB。已有699人学习下载体现了其在实际项目中的参考价值。读者可直接复用该示例理解RTSP推流流程、掌握HappyTime库中H.264 NAL单元封装与发送的关键API调用逻辑并借助test.264验证端到端传输效果是构建视频监控、低延迟直播等实时流媒体应用的重要技术参照。1. 这不是个“Hello World”示例它是一把能捅穿 RTSP 推流黑匣子的螺丝刀你手头有个 H.264 编码的裸流文件比如test.264想把它实时推到本地或远程 RTSP 服务器上——不是用 FFmpeg 命令行糊弄一下而是要嵌入到自己的 C 工程里可控、可调试、可集成进 SDK、能处理断连重试、能注入自定义时间戳、能对接硬件编码器输出的 NALU 流。这时候happytime-rtsp-h264-data-pusher-example就不是“示例”而是你工程里第一个真正能跑通的 RTSP 推流模块原型。它不依赖 Qt、不打包 FFmpeg DLL、不走 GStreamer 插件链纯 Win32 HappyTime 库原生 API 实现编译即用源码透明所有 RTSP OPTIONS/DESCRIBE/SETUP/PLAY 交互逻辑全在RtspPusher.cpp里摊开写死——没有魔法只有 socket 层状态机和 SDP 构造细节。适合正在做 IPC 设备固件升级、安防 SDK 二次开发、边缘盒子视频中继模块的 C 工程师尤其当你被401 Unauthorized卡住两小时、被RTSP/TCP interleaved模式下 packet loss 搞崩溃、或者发现test.264里第一帧是 IDR 却被当成普通 P 帧丢弃时这份代码就是你的后悔药。它不教你怎么搭 RTSP 服务器但教会你怎么让自己的程序成为合格的 RTSP 客户端——从 TCP 连接建立、CSeq 递增规则、Session ID 解析、到 RTP over TCP 的$字节封装全部可打断点、可单步、可改参数。2. 从压缩包解压到 VS2019 编译通过五步落地实操2.1 解压结构与关键文件定位别急着打开 .sln先别双击RtspPusher.sln。打开命令行进入解压目录后执行tree /F你会看到清晰的三层结构根目录happytime-rtsp-h264-data-pusher-example/项目目录RtspPusher/含.vcxproj,.cpp,.h,test.264外部依赖没有lib/或include/子目录 —— 这意味着 HappyTime 库头文件和 lib 必须提前安装系统级路径或手动配置。这是第一个隐性门槛。提示h264.h和h264.cpp不是标准 H.264 解析器而是 HappyTime 封装的 NALU 分割工具类负责从test.264文件中按起始码0x00000001或0x000001切出独立 NALU并打上 RTP 时间戳基于 90kHz 时钟。它不处理 SPS/PPS 提取逻辑这部分由RtspPusher.cpp中SendSDP()手动拼接完成。2.2 HappyTime 库环境准备Windows 下的最小可行依赖HappyTime 并非 NuGet 包也不是 vcpkg 默认源。官方未提供预编译二进制必须自行构建。但本示例已适配HappyTime v1.3.2根据RtspPusher.vcxproj中AdditionalDependencieshappytime.lib/AdditionalDependencies及导入库名反推。实测可用的最小依赖组合为组件版本获取方式说明HappyTime SDK≥ v1.3.2GitHub releases 下载happytime-win-x64.zip解压后将include/全部拷贝至 VS “附加包含目录”lib/happytime.lib放入链接器输入Windows SDK10.0.19041.0VS2019 自带确保项目属性 → 常规 → Windows SDK 版本 ≥ 10.0.19041.0Platform Toolsetv142VS2019 默认不要切到 v143VS2022RtspPusher.vcxproj显式指定PlatformToolsetv142/PlatformToolset注意若你用的是 VS2022请右键项目 → 属性 → 常规 → 平台工具集 → 改为Visual Studio 2019 (v142)否则会报LNK2001: unresolved external symbol _HT_RtspClientCreate—— 这是 ABI 不兼容导致的符号找不到不是代码问题。2.3 修改RtspPusher.cpp中的 RTSP 目标地址三处硬编码必须改打开RtspPusher.cpp搜索rtsp://你会找到3 处需手动修改的地址第 47 行const char* rtsp_url rtsp://127.0.0.1:8554/test;→ 改为你自己的 RTSP 服务器地址如rtsp://192.168.1.100:554/stream1第 122 行if (!ht_rtsp_client_set_url(client, rtsp://127.0.0.1:8554/test)) {→ 同步修改为同上地址第 189 行char sdp[2048] v0\r\no- 0 0 IN IP4 127.0.0.1\r\nsNo Name\r\n...;→ SDP 中cIN IP4 127.0.0.1必须改为你的推流端本机 IP不是服务器 IP否则某些 RTSP 服务器如 Live555会拒绝 SETUP 请求报错461 Unsupported Transport// ✅ 正确改法假设本机 IP 是 192.168.1.50 char sdp[2048] v0\r\no- 0 0 IN IP4 192.168.1.50\r\n sH264 Stream\r\niHAPPYTIME RTSP Pusher\r\n t0 0\r\nacontrol:*\r\narange:npt0-\r\n mvideo 0 RTP/AVP 96\r\n cIN IP4 192.168.1.50\r\n // ← 关键必须是你本机IP bAS:500\r\nartpmap:96 H264/90000\r\n afmtp:96 packetization-mode1;profile-level-id42E01F;sprop-parameter-sets;逻辑说明SDP 中c字段声明媒体流发送方的 IPRTSP 服务器据此决定 RTP 数据该发往哪个地址。若填127.0.0.1服务器会尝试把 RTP 包发回 localhost而你的推流程序监听的是本机真实网卡 IP必然收不到 ACK导致 PLAY 失败。2.4 编译与运行生成RtspPusher.exe后的首条验证命令完成上述修改后VS2019 中按CtrlShiftB编译。成功后会在x64/Debug/下生成RtspPusher.exe。不要双击运行用命令行cd x64\Debug RtspPusher.exe预期输出关键成功标志[INFO] RTSP client created. [INFO] RTSP URL set: rtsp://192.168.1.100:554/stream1 [INFO] Sending OPTIONS... [INFO] OPTIONS response: 200 OK [INFO] Sending DESCRIBE... [INFO] DESCRIBE response: 200 OK [INFO] Parsing SDP... [INFO] Sending SETUP... [INFO] SETUP response: 200 OK, Session: 1234567890ABCDEF [INFO] Sending PLAY... [INFO] PLAY response: 200 OK [INFO] Start sending H.264 NALUs... [INFO] Sent 128 NALUs, total size: 124567 bytes若卡在Sending SETUP...超过 10 秒说明网络不通或服务器未启用 TCP 模式若PLAY返回405 Method Not Allowed说明服务器不支持 PLAY可能是只读流若Sent 0 NALUs检查test.264是否为空或格式错误。3. 抓包验证 SDP 解析确认你推的真是标准 RTSP 流3.1 用 Wireshark 抓RtspPusher.exe的 TCP 流过滤与定位启动 Wireshark选择本机网卡输入显示过滤器tcp.port 554 ip.addr 192.168.1.100运行RtspPusher.exe捕获到完整 RTSP 交互后右键任意一条TCP流 →Follow → TCP Stream。你会看到清晰的明文协议交互OPTIONS rtsp://192.168.1.100:554/stream1 RTSP/1.0 CSeq: 1 User-Agent: HappyTime-RTSP-Pusher/1.0 RTSP/1.0 200 OK CSeq: 1 Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE DESCRIBE rtsp://192.168.1.100:554/stream1 RTSP/1.0 CSeq: 2 Accept: application/sdp RTSP/1.0 200 OK CSeq: 2 Content-Type: application/sdp Content-Length: 321 v0 o- 0 0 IN IP4 192.168.1.50 sH264 Stream ... mvideo 0 RTP/AVP 96 artpmap:96 H264/90000 afmtp:96 packetization-mode1;profile-level-id42E01F;...参数说明packetization-mode1表示使用 STAP-A / FU-A 封装非单NALU模式profile-level-id42E01F对应 Baseline Profile Level 3.1 —— 这是test.264文件的编码参数由h264.cpp从 SPS 中解析得出并写入 SDP。若你的摄像头输出是 Main Profile此处需同步修改profile-level-id如4D0028否则服务器可能拒绝。3.2 用 VLC 验证推流结果不只是“能播”而是“播得对”打开 VLC → 媒体 → 打开网络串流 → 输入rtsp://192.168.1.100:554/stream1→ 播放。关键验证点不是看画面是看信息右键视频 → 详细信息 → 编码H264 - MPEG-4 AVC (part 10)工具 → 编解码信息 → 视频流 → 分辨率、帧率、Profile/Level 必须与test.264一致若画面卡顿、绿屏、花屏立即检查 Wireshark 中 RTP 包是否连续Sequence Number 递增、Timestamp 是否线性增长每帧 3600因 90kHz / 25fps 3600血泪经验曾遇到 VLC 播放正常但大华 NVR 无法添加该流抓包发现 NVR 发送GET_PARAMETER后RtspPusher未响应。解决方案是在RtspPusher.cpp的OnRtspMessage回调中增加else if (strncmp(method, GET_PARAMETER, 13) 0) { ht_rtsp_client_send_response(client, 200, cseq, OK\r\n); }否则某些设备会因超时中断连接。3.3test.264文件格式校验为什么你的自定义 H.264 文件总失败test.264不是 MP4 封装而是原始 Annex B 格式每个 NALU 以0x00000001或0x000001开头。用十六进制编辑器如 HxD打开确认前 10 字节为00 00 00 01 67 42 E0 1F ... // SPS 00 00 00 01 68 CE 0F CB ... // PPS 00 00 00 01 65 88 ... // IDR 帧若你的摄像头输出是 AVCC 格式常见于 Android MediaCodec必须先转换# 使用 ffmpeg 转换AVCC → Annex B ffmpeg -i input.h264 -c copy -vbsf h264_mp4toannexb output.264注意h264.cpp中ParseH264File()函数仅识别0x00000001起始码不支持0x000001短起始码。若你的文件用短码需修改h264.cpp第 68 行if (buf[i] 0x00 buf[i1] 0x00 buf[i2] 0x00 buf[i3] 0x01) { // 原逻辑 } else if (buf[i] 0x00 buf[i1] 0x00 buf[i2] 0x01) { // 新增匹配 0x000001 nalu_start i; nalu_len ...; }4. 避坑五个让工程师凌晨三点还在 reboot 的真实翻车现场4.1 现象编译通过运行时报Access violation reading location 0x00000000原因ht_rtsp_client_create()返回NULL但代码未判空直接调用ht_rtsp_client_set_url()。根源是 HappyTime 库未正确初始化 ——HT_Init()未被调用。解决在main()函数开头插入if (!HT_Init()) { printf([ERROR] HappyTime init failed!\n); return -1; }并在程序退出前调用HT_Uninit()。HappyTime 文档明确要求此步骤但示例代码遗漏。4.2 现象Wireshark 显示 SETUP 成功但无 RTP 包发出原因RtspPusher.cpp第 215 行ht_rtsp_client_start_streaming(client)调用后未检查返回值。若返回HT_ERR_INVALID_STATE说明PLAY响应未收到或解析失败。解决增加判空if (ht_rtsp_client_start_streaming(client) ! HT_OK) { printf([ERROR] Failed to start streaming\n); return -1; }并确保OnRtspMessage回调中对PLAY响应的Session字段提取正确正则匹配Session: ([^\r\n])。4.3 现象VLC 播放首帧后卡死Wireshark 显示 RTP Sequence Number 突然跳变原因h264.cpp中GetNextNalu()函数未重置nal_ref_idc导致 IDR 帧后 P 帧的时间戳计算错误误用上一帧 delta。解决在GetNextNalu()结尾添加// 重置参考帧计数避免时间戳漂移 if ((nalu[0] 0x1F) 5) { // IDR frame last_timestamp 0; }4.4 现象推流到 Live555 服务器时DESCRIBE返回404 Not Found原因Live555 默认路径为/testStream而示例代码硬编码/test。且 Live555 要求路径区分大小写。解决修改rtsp_url为rtsp://192.168.1.100:8554/testStream并确认 Live555 配置文件中testStream已启用。4.5 现象多路推流时第二路ht_rtsp_client_create()返回NULL原因HappyTime v1.3.2 存在全局 socket 句柄泄漏多次HT_Init()/HT_Uninit()后资源耗尽。解决禁止重复初始化。整个进程只调用一次HT_Init()所有推流客户端共享同一实例。若需多路用一个HT_RtspClient实例循环SETUP/PLAY/TEARDOWN切换流而非创建多个 client。5. 进阶把示例改造成生产级模块的四个硬核改造点5.1 支持 RTSP 断连自动重连状态机 指数退避原示例是单次执行失败即退出。生产环境必须支持重连。核心改造在RtspPusher.cpp的OnRtspMessage回调中void OnRtspMessage(void* user_data, HT_RtspClient* client, HT_RtspMessage* msg) { switch (msg-type) { case HT_RTSP_MSG_TYPE_ERROR: printf([ERROR] RTSP error: %d\n, msg-code); // 触发重连 ReconnectWithBackoff(client); break; case HT_RTSP_MSG_TYPE_TEARDOWN: // 服务器主动断开立即重连 ReconnectWithBackoff(client); break; } } void ReconnectWithBackoff(HT_RtspClient* client) { static int retry_count 0; const int max_retries 5; const int base_delay_ms 1000; // 1s 起始延迟 if (retry_count max_retries) { printf([FATAL] Max retries exceeded, aborting.\n); return; } int delay base_delay_ms * (1 retry_count); // 指数退避1s, 2s, 4s, 8s, 16s printf([INFO] Reconnecting in %d ms...\n, delay); Sleep(delay); // 重置 client 状态 ht_rtsp_client_destroy(client); client ht_rtsp_client_create(); ht_rtsp_client_set_url(client, rtsp_url); ht_rtsp_client_set_callback(client, OnRtspMessage, nullptr); // 重新走 OPTIONS→DESCRIBE→SETUP→PLAY 流程 ht_rtsp_client_send_options(client); retry_count; }关键点ht_rtsp_client_destroy()必须调用否则内存泄漏retry_count需全局静态或传入user_data避免多实例干扰。5.2 支持从内存 buffer 实时推流绕过test.264文件依赖h264.cpp当前只读文件。要接入摄像头回调需暴露PushNalu()接口// 新增头文件 h264.h 中声明 extern C { // 推送一帧 NALUdata 指向起始码后的原始字节len 为长度 HT_API int HT_PushNalu(uint8_t* data, int len, uint64_t pts_us); } // 在 h264.cpp 中实现 int HT_PushNalu(uint8_t* data, int len, uint64_t pts_us) { if (!g_rtsp_client) return -1; // 构造 RTP 包简化版实际需加 RTP header uint8_t rtp_packet[65536]; int rtp_len BuildRtpPacket(data, len, pts_us, rtp_packet); // 发送 return ht_rtsp_client_send_rtp(g_rtsp_client, rtp_packet, rtp_len); }然后在摄像头 YUV→H264 编码回调中直接调用void OnEncodedH264(uint8_t* nal_data, int nal_len, uint64_t pts_us) { HT_PushNalu(nal_data, nal_len, pts_us); }5.3 支持 TCP/UDP 传输模式切换通过 URL 参数控制RTSP URL 中加入?transporttcp或?transportudp// 解析 URL 中 transport 参数 char* transport_param strstr(rtsp_url, ?transport); if (transport_param) { if (strstr(transport_param, tcp)) { transport_mode HT_RTSP_TRANSPORT_TCP; } else if (strstr(transport_param, udp)) { transport_mode HT_RTSP_TRANSPORT_UDP; } } ht_rtsp_client_set_transport_mode(client, transport_mode);注意UDP 模式需额外处理 NAT 穿透和丢包重传HappyTime v1.3.2 仅提供基础 UDP 发送不内置 FEC生产环境慎用。5.4 日志分级与外部回调让运维能快速定位问题替换所有printf为可配置日志typedef enum { LOG_LEVEL_DEBUG 0, LOG_LEVEL_INFO, LOG_LEVEL_WARN, LOG_LEVEL_ERROR } LogLevel; // 外部可设置日志回调 static LogCallback g_log_callback nullptr; void SetLogCallback(LogCallback cb) { g_log_callback cb; } void Log(LogLevel level, const char* fmt, ...) { va_list args; va_start(args, fmt); if (g_log_callback) { g_log_callback(level, fmt, args); } va_end(args); } // 使用示例 Log(LOG_LEVEL_INFO, RTSP SETUP success, session%s, session_id);这样上层应用可将日志写入文件、上报 ELK、或触发告警。从那以后我每次集成 RTSP 推流模块都强制走一遍 Wireshark 抓包 VLC 编解码信息 test.264十六进制校验三连。不是信不过代码是信不过自己没看清cIN IP4那一行到底填的是谁的 IP。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
Edge IE模式配置全指南:从临时切换到企业站点列表 1. 为什么 Edge 里还藏着一个 IE 模式很多人第一次在 Edge 里找“兼容模式”,是因为打开某个老系统时页面直接白屏、按钮点不动、下拉框弹不出来。尤其是单位里的 OA、ERP、财务系统,或者一些工业软件附带的 Web 管理界面,它们当年就是按 IE … · 2026/9/23 12:07:46
fp-ts ReaderEither 模块完全指南:组合 Reader 依赖注入与 Either 错误处理 开发工具 【免费下载链接】fp-ts Functional programming in TypeScript 项目地址: https://gitcode.com/gh_mirrors/fp/fp-ts 点击查看 免费下载 导读
ReaderEither 是 fp-ts 中把 Reader(从环境中读取配置/依赖的函数式能力)与 Either&am… · 2026/9/23 12:07:45
COMSOL流注放电仿真关键技术解析 1. 流注放电仿真概述流注放电是气体放电中一种重要的放电形式,广泛存在于高压绝缘、等离子体技术等领域。作为一名长期使用COMSOL进行等离子体仿真的工程师,我发现流注放电的数值模拟存在诸多技术难点,特别是在电子离子密度、电场强度和温度场… · 2026/9/23 12:07:39
3招搞定马云的演讲文本分析,面试性能优化不再虚 3招搞定马云的演讲文本分析,面试性能优化不再虚 上周二,一位刚毕业的学弟在群里哭诉,说大厂二面挂了。面试官问:“如果给你100万条用户评论,你怎么快速提取出‘马云的演讲’这类高频观点,还要保证响应时间低于200ms?”他愣了五秒,只憋出一句… · 2026/9/23 12:47:04
垂域Agent实战指南:从架构设计到线上避坑的完整经验 这些年聊agent的人很多,但真正把它落到业务里、把脏活累活跑通的却没那么多。今天想聊的是我在垂域agent(垂直领域智能体)上的一整套开发与迭代经验——从定位、框架选型,到记忆、技能、编排、评估,再到线上踩坑后的修… · 2026/9/23 12:46:57
编写高质量架构决策记录(ADR)的实用指南 编写高质量架构决策记录(ADR)的实用指南在软件工程中,最昂贵的沟通成本往往发生在“考古”阶段:后来的工程师看着一行看似别扭的架构设计,心里总是充满疑问——“当初为什么不用行业主流的方案 A,反而折腾了… · 2026/9/23 12:46:57
2026年汽车制造业AI应用趋势与核心技术解析 1. 行业背景与盘点意义汽车制造业正在经历百年未有的技术变革期。根据国际汽车工程师学会(SAE)最新报告显示,全球前20大整车厂中已有17家建立了专门的AI研发部门,平均每年投入预算增长达到47%。这种变革不仅发生在特斯拉这样的新势… · 2026/9/23 12:46:57
图解原理:pta平台实战避坑,3天搞定版本升级API变更 图解原理:pta平台实战避坑,3天搞定版本升级API变更 版本升级后 API 全变了,这是无数后端开发者在接手旧项目或接入新工具时的噩梦。 你刚打开代码库,发现原本熟悉的调用方式全部失效,报错信息像天书一样让人头大。… · 2026/9/23 12:46:57
C语言printf函数详解与最佳实践 1. C语言基础输出解析这段代码展示了一个非常基础的C语言程序结构,虽然只有短短几行,但包含了C语言编程中的几个核心概念。让我们先完整看一下这段代码:/* 范例:3-10 */
#include <stdio.h>void main(void)
{printf("%… · 2026/9/23 12:46:51
3招搞定手机怎么下载微信面试难题实战项目解析 3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29