luat_rtmp_push.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. /**
  2. * @file luat_rtmp_push.h
  3. * @brief RTMP推流组件 - 基于lwip raw API实现
  4. * @author LuatOS Team
  5. *
  6. * 该组件实现了RTMP(Real Time Messaging Protocol)推流功能,
  7. * 支持将H.264视频流推送到RTMP服务器。
  8. *
  9. * 主要特性:
  10. * - 基于lwip raw socket API,适应嵌入式环境
  11. * - 支持自定义H.264帧来源,灵活的NALU帧注入
  12. * - 完整的RTMP握手和连接管理
  13. * - 支持FLV格式视频打包和发送
  14. * - C99语法,内存使用优化
  15. *
  16. * 调试说明:
  17. * - 在 luat_rtmp_push.c 中修改 RTMP_DEBUG_VERBOSE 宏来控制详细日志输出
  18. * - 设置为 1 开启详细调试信息,设置为 0 关闭(仅保留关键日志)
  19. */
  20. #ifndef __LUAT_RTMP_PUSH_H__
  21. #define __LUAT_RTMP_PUSH_H__
  22. #include "luat_base.h"
  23. #include "lwip/tcp.h"
  24. #include <stdint.h>
  25. #include <stddef.h>
  26. #include <stdbool.h>
  27. #ifdef __cplusplus
  28. extern "C" {
  29. #endif
  30. /* ======================== RTMP常量定义 ======================== */
  31. /** RTMP默认块大小(字节) - RTMP规范规定的默认值为128 */
  32. #define RTMP_DEFAULT_CHUNK_SIZE 128
  33. /** RTMP缓冲区大小(字节) - 需要足够大以容纳I帧 */
  34. #define RTMP_BUFFER_SIZE (512 * 1024)
  35. /** 发送帧队列最大字节数上限,超出将丢弃未发送帧(优先丢弃非关键帧) */
  36. #define RTMP_MAX_QUEUE_BYTES (1024 * 1024)
  37. /** RTMP握手数据大小(字节) */
  38. #define RTMP_HANDSHAKE_SIZE 1536
  39. /** RTMP命令超时时间(毫秒) */
  40. #define RTMP_CMD_TIMEOUT 5000
  41. /* ======================== 返回值定义 ======================== */
  42. /** 操作成功 */
  43. #define RTMP_OK 0
  44. /** 通用错误 */
  45. #define RTMP_ERR_FAILED (-1)
  46. /** 参数无效 */
  47. #define RTMP_ERR_INVALID_PARAM (-2)
  48. /** 内存不足 */
  49. #define RTMP_ERR_NO_MEMORY (-3)
  50. /** 连接错误 */
  51. #define RTMP_ERR_CONNECT_FAILED (-4)
  52. /** 握手失败 */
  53. #define RTMP_ERR_HANDSHAKE_FAILED (-5)
  54. /** 网络错误 */
  55. #define RTMP_ERR_NETWORK (-6)
  56. /** 超时 */
  57. #define RTMP_ERR_TIMEOUT (-7)
  58. /** 缓冲区溢出 */
  59. #define RTMP_ERR_BUFFER_OVERFLOW (-8)
  60. /* ======================== 数据类型定义 ======================== */
  61. /**
  62. * RTMP连接状态枚举
  63. */
  64. typedef enum {
  65. RTMP_STATE_IDLE = 0, /**< 空闲状态 */
  66. RTMP_STATE_CONNECTING = 1, /**< 正在连接 */
  67. RTMP_STATE_HANDSHAKING = 2, /**< 握手中 */
  68. RTMP_STATE_CONNECTED = 3, /**< 已连接 */
  69. RTMP_STATE_PUBLISHING = 4, /**< 正在推流 */
  70. RTMP_STATE_DISCONNECTING = 5, /**< 正在断开连接 */
  71. RTMP_STATE_ERROR = 6 /**< 错误状态 */
  72. } rtmp_state_t;
  73. /**
  74. * RTMP消息类型枚举
  75. */
  76. typedef enum {
  77. RTMP_MSG_SET_CHUNK_SIZE = 1, /**< 设置块大小 */
  78. RTMP_MSG_ABORT = 2, /**< 中止消息 */
  79. RTMP_MSG_BYTES_READ = 3, /**< 字节已读 */
  80. RTMP_MSG_CONTROL = 4, /**< 用户控制消息 */
  81. RTMP_MSG_SERVER_BW = 5, /**< 服务器带宽 */
  82. RTMP_MSG_CLIENT_BW = 6, /**< 客户端带宽 */
  83. RTMP_MSG_AUDIO = 8, /**< 音频数据 */
  84. RTMP_MSG_VIDEO = 9, /**< 视频数据 */
  85. RTMP_MSG_AMFDATAFILE = 15, /**< AMF数据 */
  86. RTMP_MSG_COMMAND = 20, /**< 命令(AMF0) */
  87. RTMP_MSG_EXTENDED_COMMAND = 17 /**< 扩展命令 */
  88. } rtmp_msg_type_t;
  89. /**
  90. * H.264 NALU类型枚举
  91. */
  92. typedef enum {
  93. NALU_TYPE_NON_IDR = 1, /**< 非IDR帧 */
  94. NALU_TYPE_IDR = 5, /**< IDR帧(关键帧) */
  95. NALU_TYPE_SEI = 6, /**< SEI(补充增强信息) */
  96. NALU_TYPE_SPS = 7, /**< SPS(序列参数集) */
  97. NALU_TYPE_PPS = 8, /**< PPS(图像参数集) */
  98. NALU_TYPE_AUD = 9 /**< AUD(访问单元分隔符) */
  99. } nalu_type_t;
  100. /**
  101. * H.264视频帧标签结构体
  102. * 用于描述FLV格式中的视频数据帧
  103. */
  104. typedef struct {
  105. uint8_t frame_type; /**< 帧类型: 1=关键帧, 2=普通帧 */
  106. uint8_t codec_id; /**< 编码器ID: 7=H.264 */
  107. uint32_t cts; /**< 时间戳偏移(ms) */
  108. uint8_t *data; /**< 视频数据指针 */
  109. uint32_t len; /**< 视频数据长度 */
  110. } video_tag_t;
  111. /**
  112. * RTMP推流统计信息结构体
  113. * 用于查询RTMP连接的实时统计数据
  114. */
  115. typedef struct {
  116. uint64_t bytes_sent; /**< 已发送的字节数 */
  117. uint32_t video_frames_sent; /**< 已发送的视频帧数 */
  118. uint32_t audio_frames_sent; /**< 已发送的音频帧数 */
  119. uint32_t connection_time; /**< 连接持续时间(毫秒) */
  120. uint32_t packets_sent; /**< 已发送的包数 */
  121. uint32_t last_video_timestamp; /**< 最后视频时间戳(毫秒) */
  122. uint32_t last_audio_timestamp; /**< 最后音频时间戳(毫秒) */
  123. /* 细分统计 */
  124. uint32_t i_frames; /**< 发送的I帧数量 */
  125. uint32_t p_frames; /**< 发送的P帧数量 */
  126. uint64_t i_bytes; /**< 发送的I帧字节数(NAL数据长度累加) */
  127. uint64_t p_bytes; /**< 发送的P帧字节数(NAL数据长度累加) */
  128. uint64_t audio_bytes; /**< 发送的音频字节数 */
  129. uint32_t dropped_frames; /**< 被丢弃的帧数量 */
  130. uint64_t dropped_bytes; /**< 被丢弃的帧字节数 */
  131. } rtmp_stats_t;
  132. /**
  133. * RTMP推流上下文结构体
  134. * 管理单个RTMP连接的所有状态和缓冲区
  135. */
  136. typedef struct {
  137. /** ============ 连接信息 ============ */
  138. char *url; /**< RTMP服务器URL */
  139. char *host; /**< RTMP服务器主机名/IP地址 */
  140. char *app; /**< RTMP应用名 */
  141. char *stream; /**< 推流名 */
  142. char *auth; /**< 认证信息 */
  143. uint16_t port; /**< 连接端口 */
  144. /** ============ TCP连接状态 ============ */
  145. struct tcp_pcb *pcb; /**< lwip TCP控制块 */
  146. rtmp_state_t state; /**< 当前连接状态 */
  147. uint32_t last_activity_time; /**< 最后活动时间戳 */
  148. int handshake_state; /**< 握手状态: 0=发送C0C1, 1=等待S0S1, 2=发送C2, 3=完成 */
  149. /** ============ RTMP协议状态 ============ */
  150. uint32_t in_chunk_size; /**< 输入块大小 */
  151. uint32_t out_chunk_size; /**< 输出块大小 */
  152. uint32_t chunk_size; /**< 当前chunk大小(用于分块发送)*/
  153. uint32_t video_stream_id; /**< 视频流ID */
  154. uint32_t audio_stream_id; /**< 音频流ID */
  155. /** ============ 缓冲区管理 ============ */
  156. uint8_t *recv_buf; /**< 接收缓冲区 */
  157. uint32_t recv_buf_size; /**< 接收缓冲区大小 */
  158. uint32_t recv_pos; /**< 接收缓冲区写位置 */
  159. uint8_t *send_buf; /**< 发送缓冲区 */
  160. uint32_t send_buf_size; /**< 发送缓冲区大小 */
  161. uint32_t send_pos; /**< 发送缓冲区写位置 */
  162. /** ============ 帧发送队列 ============ */
  163. struct rtmp_frame_node *frame_head; /**< 待发送帧队列头 */
  164. struct rtmp_frame_node *frame_tail; /**< 待发送帧队列尾 */
  165. uint32_t frame_queue_bytes; /**< 队列占用的总字节数 */
  166. /** ============ 时间戳管理 ============ */
  167. uint32_t video_timestamp; /**< 当前视频时间戳(ms) */
  168. uint32_t audio_timestamp; /**< 当前音频时间戳(ms) */
  169. uint32_t base_timestamp; /**< 基准时间戳 */
  170. /** ============ 统计信息 ============ */
  171. uint32_t packets_sent; /**< 已发送的包数 */
  172. uint64_t bytes_sent; /**< 已发送的字节数 */
  173. uint32_t command_id; /**< 当前命令ID */
  174. /* 帧统计 */
  175. uint32_t i_frames; /**< 发送的I帧数量 */
  176. uint32_t p_frames; /**< 发送的P帧数量 */
  177. uint64_t i_bytes; /**< 发送的I帧字节数 */
  178. uint64_t p_bytes; /**< 发送的P帧字节数 */
  179. uint32_t audio_frames_sent; /**< 发送的音频帧数量 */
  180. uint64_t audio_bytes; /**< 发送的音频字节数 */
  181. uint32_t dropped_frames; /**< 被丢弃的帧数量 */
  182. uint64_t dropped_bytes; /**< 被丢弃的帧字节数 */
  183. uint32_t last_stats_log_ms; /**< 上次统计日志输出时间 */
  184. uint64_t last_stats_bytes; /**< 上次统计日志输出时的总字节数 */
  185. uint32_t stats_interval_ms; /**< 统计输出间隔(毫秒),默认10000 */
  186. uint32_t stats_window_ms; /**< 统计窗口长度(毫秒),默认与间隔相同 */
  187. uint32_t last_window_ms; /**< 上次窗口采样时间戳(ms) */
  188. uint64_t last_window_bytes; /**< 上次窗口采样时的总字节数 */
  189. /** ============ 用户数据 ============ */
  190. void *user_data; /**< 用户自定义数据指针 */
  191. } rtmp_ctx_t;
  192. /* ======================== 核心接口函数 ======================== */
  193. /**
  194. * 创建RTMP推流上下文
  195. *
  196. * 分配并初始化RTMP上下文结构体,为后续的RTMP连接做准备。
  197. *
  198. * @return 返回RTMP上下文指针,失败返回NULL
  199. */
  200. rtmp_ctx_t* rtmp_create(void);
  201. /**
  202. * 销毁RTMP推流上下文
  203. *
  204. * 释放所有由RTMP上下文占用的资源,包括内存缓冲区和TCP连接。
  205. *
  206. * @param ctx RTMP上下文指针
  207. * @return 返回RTMP_OK表示成功
  208. */
  209. int rtmp_destroy(rtmp_ctx_t *ctx);
  210. /**
  211. * 设置RTMP服务器URL
  212. *
  213. * 解析并设置RTMP服务器地址,支持的格式为:
  214. * - rtmp://hostname:port/app/stream
  215. * - rtmp://hostname/app/stream (使用默认端口1935)
  216. *
  217. * 如果设置过URL,新的设置会覆盖旧的设置。
  218. *
  219. * @param ctx RTMP上下文指针
  220. * @param url RTMP服务器URL字符串
  221. * @return RTMP_OK表示成功,其他值表示失败
  222. */
  223. int rtmp_set_url(rtmp_ctx_t *ctx, const char *url);
  224. /**
  225. * 连接到RTMP服务器
  226. *
  227. * 建立与RTMP服务器的TCP连接,然后执行RTMP握手流程。
  228. * 该函数是非阻塞的,实际的连接过程通过回调函数进行。
  229. *
  230. * @param ctx RTMP上下文指针
  231. * @return RTMP_OK表示连接已启动,其他值表示参数错误或资源不足
  232. */
  233. int rtmp_connect(rtmp_ctx_t *ctx);
  234. /**
  235. * 断开RTMP连接
  236. *
  237. * 主动关闭与RTMP服务器的连接,释放TCP资源。
  238. *
  239. * @param ctx RTMP上下文指针
  240. * @return RTMP_OK表示断开已启动
  241. */
  242. int rtmp_disconnect(rtmp_ctx_t *ctx);
  243. /**
  244. * 发送H.264 NALU帧
  245. *
  246. * 将一个H.264 NALU帧打包为FLV视频标签并发送。
  247. * 支持自动检测关键帧(IDR)和普通帧。
  248. *
  249. * 使用示例:
  250. * @code
  251. * uint8_t nalu_data[1024];
  252. * uint32_t nalu_len = 1024;
  253. * rtmp_send_nalu(ctx, nalu_data, nalu_len, 0); // 时间戳0ms
  254. * @endcode
  255. *
  256. * @param ctx RTMP上下文指针
  257. * @param nalu_data NALU数据指针,包含完整的NALU单元
  258. * @param nalu_len NALU数据长度
  259. * @param timestamp 视频时间戳(毫秒),从0开始递增
  260. * @return RTMP_OK表示发送成功,其他值表示错误
  261. * - RTMP_ERR_INVALID_PARAM: 参数无效
  262. * - RTMP_ERR_BUFFER_OVERFLOW: 缓冲区不足
  263. * - RTMP_ERR_FAILED: 发送失败
  264. */
  265. int rtmp_send_nalu(rtmp_ctx_t *ctx, const uint8_t *nalu_data,
  266. uint32_t nalu_len, uint32_t timestamp);
  267. /**
  268. * 发送多个NALU帧(聚合发送)
  269. *
  270. * 将多个NALU帧聚合打包为单个FLV视频数据包发送,
  271. * 可以提高网络传输效率。
  272. *
  273. * @param ctx RTMP上下文指针
  274. * @param nalus NALU数据指针数组
  275. * @param lengths 对应NALU的长度数组
  276. * @param count NALU帧的个数
  277. * @param timestamp 视频时间戳(毫秒)
  278. * @return RTMP_OK表示发送成功
  279. */
  280. int rtmp_send_nalu_multi(rtmp_ctx_t *ctx, const uint8_t **nalus,
  281. const uint32_t *lengths, uint32_t count,
  282. uint32_t timestamp);
  283. /**
  284. * 发送音频数据帧
  285. *
  286. * 将音频数据打包为RTMP音频消息并发送。
  287. * 支持AAC等多种音频格式。
  288. *
  289. * 使用示例(AAC):
  290. * @code
  291. * // 发送AAC Sequence Header
  292. * uint8_t aac_header[] = {0xAF, 0x00, 0x12, 0x10}; // AAC-LC, 44.1kHz, Stereo
  293. * rtmp_send_audio(ctx, aac_header, sizeof(aac_header), 0);
  294. *
  295. * // 发送AAC音频帧
  296. * uint8_t audio_frame[256];
  297. * audio_frame[0] = 0xAF; // AAC, 44.1kHz, 16bit, Stereo
  298. * audio_frame[1] = 0x01; // AAC raw data
  299. * memcpy(&audio_frame[2], aac_raw_data, aac_raw_len);
  300. * rtmp_send_audio(ctx, audio_frame, 2 + aac_raw_len, timestamp);
  301. * @endcode
  302. *
  303. * 音频标签头格式(第1字节):
  304. * - Bit[7:4]: SoundFormat (10=AAC, 2=MP3, 3=PCM等)
  305. * - Bit[3:2]: SoundRate (0=5.5kHz, 1=11kHz, 2=22kHz, 3=44kHz)
  306. * - Bit[1]: SoundSize (0=8bit, 1=16bit)
  307. * - Bit[0]: SoundType (0=Mono, 1=Stereo)
  308. *
  309. * AAC格式需要第2字节指定AACPacketType:
  310. * - 0 = AAC sequence header (AudioSpecificConfig)
  311. * - 1 = AAC raw data
  312. *
  313. * @param ctx RTMP上下文指针
  314. * @param audio_data 音频数据指针,应包含完整的音频标签(标签头+数据)
  315. * @param audio_len 音频数据总长度
  316. * @param timestamp 音频时间戳(毫秒),从0开始递增
  317. * @return RTMP_OK表示发送成功,其他值表示错误
  318. * - RTMP_ERR_INVALID_PARAM: 参数无效
  319. * - RTMP_ERR_NO_MEMORY: 内存不足
  320. * - RTMP_ERR_FAILED: 发送失败
  321. */
  322. int rtmp_send_audio(rtmp_ctx_t *ctx, const uint8_t *audio_data,
  323. uint32_t audio_len, uint32_t timestamp);
  324. /**
  325. * 获取当前连接状态
  326. *
  327. * @param ctx RTMP上下文指针
  328. * @return 返回当前的rtmp_state_t状态值
  329. */
  330. rtmp_state_t rtmp_get_state(rtmp_ctx_t *ctx);
  331. /**
  332. * 处理RTMP事件(需要定期调用)
  333. *
  334. * 处理TCP连接事件、接收数据、超时检测等。
  335. * 该函数应该在主循环或定时器中定期调用,建议间隔为10-50毫秒。
  336. *
  337. * @param ctx RTMP上下文指针
  338. * @return RTMP_OK表示正常,其他值表示发生错误
  339. */
  340. int rtmp_poll(rtmp_ctx_t *ctx);
  341. /**
  342. * 设置用户自定义数据
  343. *
  344. * 可用于关联用户的上下文信息,在回调函数中可以通过
  345. * rtmp_get_user_data获取。
  346. *
  347. * @param ctx RTMP上下文指针
  348. * @param user_data 用户数据指针
  349. * @return RTMP_OK表示成功
  350. */
  351. int rtmp_set_user_data(rtmp_ctx_t *ctx, void *user_data);
  352. /**
  353. * 获取用户自定义数据
  354. *
  355. * @param ctx RTMP上下文指针
  356. * @return 返回用户设置的数据指针,未设置则返回NULL
  357. */
  358. void* rtmp_get_user_data(rtmp_ctx_t *ctx);
  359. /**
  360. * 获取统计信息
  361. *
  362. * 获取RTMP连接的实时统计数据,包括字节数、帧数、连接时长等。
  363. * 该函数可以在任何时刻调用以查询当前的推流统计信息。
  364. *
  365. * @param ctx RTMP上下文指针
  366. * @param stats 指向rtmp_stats_t结构体的指针,用于返回统计信息
  367. * @return RTMP_OK表示成功,其他值表示失败
  368. * - RTMP_ERR_INVALID_PARAM: ctx或stats参数为NULL
  369. *
  370. * 使用示例:
  371. * @code
  372. * rtmp_stats_t stats;
  373. * if (rtmp_get_stats(ctx, &stats) == RTMP_OK) {
  374. * printf("已发送: %u 字节, %u 视频帧\n",
  375. * stats.bytes_sent, stats.video_frames_sent);
  376. * }
  377. * @endcode
  378. */
  379. int rtmp_get_stats(rtmp_ctx_t *ctx, rtmp_stats_t *stats);
  380. /**
  381. * 设置统计输出间隔
  382. *
  383. * @param ctx RTMP上下文指针
  384. * @param interval_ms 间隔毫秒数(例如10000表示10秒)
  385. * @return RTMP_OK表示成功
  386. */
  387. int rtmp_set_stats_interval(rtmp_ctx_t *ctx, uint32_t interval_ms);
  388. /**
  389. * 设置统计窗口长度
  390. *
  391. * @param ctx RTMP上下文指针
  392. * @param window_ms 窗口毫秒数(例如10000表示10秒)
  393. * @return RTMP_OK表示成功
  394. */
  395. int rtmp_set_stats_window(rtmp_ctx_t *ctx, uint32_t window_ms);
  396. /* ======================== 回调函数定义 ======================== */
  397. /**
  398. * RTMP连接状态变化回调函数类型
  399. *
  400. * @param ctx RTMP上下文指针
  401. * @param old_state 旧状态
  402. * @param new_state 新状态
  403. * @param error_code 如果是ERROR状态,此参数表示错误码
  404. */
  405. typedef void (*rtmp_state_callback)(rtmp_ctx_t *ctx, rtmp_state_t old_state,
  406. rtmp_state_t new_state, int error_code);
  407. /**
  408. * 设置状态变化回调函数
  409. *
  410. * 当RTMP连接状态发生变化时会调用此回调函数。
  411. *
  412. * @param ctx RTMP上下文指针
  413. * @param callback 回调函数指针,传NULL则禁用回调
  414. * @return RTMP_OK表示成功
  415. */
  416. int rtmp_set_state_callback(rtmp_ctx_t *ctx, rtmp_state_callback callback);
  417. #ifdef __cplusplus
  418. }
  419. #endif
  420. #endif /* __LUAT_RTMP_PUSH_H__ */