luat_lib_rtsp.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. /*
  2. @module rtsp
  3. @summary RTSP 直播推流
  4. @version 1.0
  5. @date 2025.12.11
  6. @tag LUAT_USE_RTSP
  7. @usage
  8. -- RTSP推流示例
  9. local rtsp = rtsp.create("rtsp://example.com:554/stream")
  10. rtsp:setCallback(function(state, ...)
  11. if state == rtsp.STATE_CONNECTED then
  12. print("已连接到推流服务器")
  13. elseif state == rtsp.STATE_PLAYING then
  14. print("已开始推流")
  15. elseif state == rtsp.STATE_ERROR then
  16. print("出错:", ...)
  17. end
  18. end)
  19. rtsp:connect()
  20. -- 开始处理
  21. rtsp:start()
  22. -- 30秒后停止
  23. sys.wait(30000)
  24. rtsp:stop()
  25. -- 断开连接
  26. rtsp:disconnect()
  27. rtsp:destroy()
  28. */
  29. #include "luat_base.h"
  30. #include "luat_rtsp_push.h"
  31. #include "luat_msgbus.h"
  32. #include "luat_mem.h"
  33. #include "lauxlib.h"
  34. #include <stdlib.h>
  35. #include "lwip/timeouts.h"
  36. #include "lwip/tcpip.h"
  37. #define LUAT_LOG_TAG "rtsp"
  38. #include "luat_log.h"
  39. typedef struct {
  40. rtsp_ctx_t *rtsp;
  41. int callback_ref;
  42. } luat_rtsp_userdata_t;
  43. /**
  44. 创建RTSP推流上下文
  45. @api rtsp.create(url)
  46. @string url RTSP服务器地址, 格式: rtsp://host:port/stream
  47. @return userdata RTSP上下文对象
  48. @usage
  49. local rtsp = rtsp.create("rtsp://example.com:554/stream")
  50. */
  51. static int l_rtsp_create(lua_State *L) {
  52. const char *url = luaL_checkstring(L, 1);
  53. luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)lua_newuserdata(L, sizeof(luat_rtsp_userdata_t));
  54. if (!ud) {
  55. LLOGE("内存分配失败");
  56. lua_pushnil(L);
  57. return 1;
  58. }
  59. ud->rtsp = rtsp_create();
  60. if (!ud->rtsp) {
  61. LLOGE("RTSP上下文创建失败");
  62. lua_pushnil(L);
  63. return 1;
  64. }
  65. ud->rtsp->user_data = (void *)ud;
  66. ud->callback_ref = LUA_NOREF;
  67. if (rtsp_set_url(ud->rtsp, url) != 0) {
  68. LLOGE("RTSP URL设置失败");
  69. rtsp_destroy(ud->rtsp);
  70. lua_pushnil(L);
  71. return 1;
  72. }
  73. luaL_getmetatable(L, "rtsp_ctx");
  74. lua_setmetatable(L, -2);
  75. LLOGD("RTSP上下文创建成功: %s", url);
  76. return 1;
  77. }
  78. /**
  79. 设置RTSP状态回调函数
  80. @api rtsp:setCallback(func)
  81. @function func 回调函数, 参数为 (state, ...)
  82. @return nil 无返回值
  83. @usage
  84. rtsp:setCallback(function(state, ...)
  85. if state == rtsp.STATE_IDLE then
  86. print("空闲状态")
  87. elseif state == rtsp.STATE_CONNECTING then
  88. print("正在连接")
  89. elseif state == rtsp.STATE_OPTIONS then
  90. print("发送OPTIONS")
  91. elseif state == rtsp.STATE_DESCRIBE then
  92. print("发送DESCRIBE")
  93. elseif state == rtsp.STATE_SETUP then
  94. print("发送SETUP")
  95. elseif state == rtsp.STATE_PLAY then
  96. print("发送PLAY请求")
  97. elseif state == rtsp.STATE_PLAYING then
  98. print("正在推流")
  99. elseif state == rtsp.STATE_DISCONNECTING then
  100. print("正在断开")
  101. elseif state == rtsp.STATE_ERROR then
  102. print("错误:", ...)
  103. end
  104. end)
  105. */
  106. static int l_rtsp_set_callback(lua_State *L) {
  107. luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
  108. if (!ud || !ud->rtsp) {
  109. lua_pushboolean(L, 0);
  110. return 1;
  111. }
  112. if (lua_isfunction(L, 2)) {
  113. if (ud->callback_ref != LUA_NOREF) {
  114. luaL_unref(L, LUA_REGISTRYINDEX, ud->callback_ref);
  115. }
  116. lua_pushvalue(L, 2);
  117. ud->callback_ref = luaL_ref(L, LUA_REGISTRYINDEX);
  118. LLOGD("RTSP回调函数已设置");
  119. } else if (lua_isnil(L, 2)) {
  120. if (ud->callback_ref != LUA_NOREF) {
  121. luaL_unref(L, LUA_REGISTRYINDEX, ud->callback_ref);
  122. ud->callback_ref = LUA_NOREF;
  123. }
  124. LLOGD("RTSP回调函数已清除");
  125. } else {
  126. LLOGE("参数错误,需要function或nil");
  127. lua_pushboolean(L, 0);
  128. return 1;
  129. }
  130. lua_pushboolean(L, 1);
  131. return 1;
  132. }
  133. static int l_rtsp_handler(lua_State *L, void *udata) {
  134. rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
  135. luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)msg->ptr;
  136. if (!ud || ud->callback_ref == LUA_NOREF) {
  137. return 0;
  138. }
  139. int state = msg->arg1;
  140. lua_rawgeti(L, LUA_REGISTRYINDEX, ud->callback_ref);
  141. if (lua_isfunction(L, -1)) {
  142. lua_pushinteger(L, state);
  143. lua_call(L, 1, 0);
  144. }
  145. return 0;
  146. }
  147. /**
  148. 状态回调函数(内部使用)
  149. */
  150. static void l_state_callback(rtsp_ctx_t *ctx, rtsp_state_t oldstate, rtsp_state_t newstate, int error_code) {
  151. rtos_msg_t msg = {0};
  152. msg.handler = l_rtsp_handler;
  153. msg.ptr = ctx->user_data;
  154. msg.arg1 = (int)newstate;
  155. msg.arg2 = (int)oldstate;
  156. LLOGD("RTSP状态(%d)回调消息入队 %p %p", (int)newstate, &msg, ctx->user_data);
  157. // luat_msgbus_put(&msg, 0);
  158. }
  159. /**
  160. 连接到RTSP服务器
  161. @api rtsp:connect()
  162. @return boolean 成功返回true, 失败返回false
  163. @usage
  164. local ok = rtsp:connect()
  165. if ok then
  166. print("连接请求已发送")
  167. else
  168. print("连接失败")
  169. end
  170. */
  171. static int l_rtsp_connect(lua_State *L) {
  172. luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
  173. if (!ud || !ud->rtsp) {
  174. lua_pushboolean(L, 0);
  175. return 1;
  176. }
  177. rtsp_set_state_callback(ud->rtsp, l_state_callback);
  178. int ret = tcpip_callback_with_block(rtsp_connect, (void *)ud->rtsp, 0);
  179. LLOGD("RTSP连接请求: %s", ret == 0 ? "成功" : "失败");
  180. lua_pushboolean(L, ret == 0 ? 1 : 0);
  181. return 1;
  182. }
  183. /**
  184. 断开RTSP连接
  185. @api rtsp:disconnect()
  186. @return boolean 成功返回true, 失败返回false
  187. @usage
  188. rtsp:disconnect()
  189. */
  190. static int l_rtsp_disconnect(lua_State *L) {
  191. luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
  192. if (!ud || !ud->rtsp) {
  193. lua_pushboolean(L, 0);
  194. return 1;
  195. }
  196. int ret = rtsp_disconnect(ud->rtsp);
  197. LLOGD("RTSP断开连接: %s", ret == 0 ? "成功" : "失败");
  198. lua_pushboolean(L, ret == 0 ? 1 : 0);
  199. return 1;
  200. }
  201. static void t_rtsp_poll(void *arg) {
  202. rtsp_ctx_t *ctx = (rtsp_ctx_t *)arg;
  203. rtsp_poll(ctx);
  204. sys_timeout(20, t_rtsp_poll, ctx);
  205. }
  206. /**
  207. 处理RTSP事件
  208. @api rtsp:start()
  209. @return nil 无返回值
  210. @usage
  211. rtsp:start()
  212. */
  213. static int l_rtsp_start(lua_State *L) {
  214. luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
  215. if (!ud || !ud->rtsp) {
  216. return 0;
  217. }
  218. sys_timeout(20, t_rtsp_poll, ud->rtsp);
  219. return 0;
  220. }
  221. /**
  222. 停止处理RTSP事件
  223. @api rtsp:stop()
  224. @return nil 无返回值
  225. @usage
  226. rtsp:stop()
  227. */
  228. static int l_rtsp_stop(lua_State *L) {
  229. luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
  230. if (!ud || !ud->rtsp) {
  231. return 0;
  232. }
  233. // 移除定时器回调
  234. sys_untimeout(t_rtsp_poll, ud->rtsp);
  235. return 0;
  236. }
  237. /**
  238. 获取RTSP连接状态
  239. @api rtsp:getState()
  240. @return int 当前状态值
  241. @usage
  242. local state = rtsp:getState()
  243. if state == rtsp.STATE_CONNECTED then
  244. print("已连接")
  245. elseif state == rtsp.STATE_PLAYING then
  246. print("正在推流")
  247. end
  248. */
  249. static int l_rtsp_get_state(lua_State *L) {
  250. luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
  251. if (!ud || !ud->rtsp) {
  252. lua_pushinteger(L, -1);
  253. return 1;
  254. }
  255. rtsp_state_t state = rtsp_get_state(ud->rtsp);
  256. lua_pushinteger(L, (lua_Integer)state);
  257. return 1;
  258. }
  259. /**
  260. 设置H.264 SPS参数
  261. @api rtsp:setSPS(sps_data)
  262. @string sps_data 或
  263. @userdata sps_data H.264序列参数集数据
  264. @return boolean 成功返回true, 失败返回false
  265. @usage
  266. local sps = string.fromBinary("\x67\x42...") -- H.264 SPS数据
  267. rtsp:setSPS(sps)
  268. */
  269. static int l_rtsp_set_sps(lua_State *L) {
  270. luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
  271. if (!ud || !ud->rtsp) {
  272. lua_pushboolean(L, 0);
  273. return 1;
  274. }
  275. size_t len = 0;
  276. const uint8_t *data = (const uint8_t *)luaL_checklstring(L, 2, &len);
  277. if (rtsp_set_sps(ud->rtsp, data, (uint32_t)len) == RTSP_OK) {
  278. LLOGD("SPS已设置: %u字节", (uint32_t)len);
  279. lua_pushboolean(L, 1);
  280. } else {
  281. lua_pushboolean(L, 0);
  282. }
  283. return 1;
  284. }
  285. /**
  286. 设置H.264 PPS参数
  287. @api rtsp:setPPS(pps_data)
  288. @string pps_data 或
  289. @userdata pps_data H.264图像参数集数据
  290. @return boolean 成功返回true, 失败返回false
  291. @usage
  292. local pps = string.fromBinary("\x68\xCB...") -- H.264 PPS数据
  293. rtsp:setPPS(pps)
  294. */
  295. static int l_rtsp_set_pps(lua_State *L) {
  296. luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
  297. if (!ud || !ud->rtsp) {
  298. lua_pushboolean(L, 0);
  299. return 1;
  300. }
  301. size_t len = 0;
  302. const uint8_t *data = (const uint8_t *)luaL_checklstring(L, 2, &len);
  303. if (rtsp_set_pps(ud->rtsp, data, (uint32_t)len) == RTSP_OK) {
  304. LLOGD("PPS已设置: %u字节", (uint32_t)len);
  305. lua_pushboolean(L, 1);
  306. } else {
  307. lua_pushboolean(L, 0);
  308. }
  309. return 1;
  310. }
  311. /**
  312. 推送H.264视频帧
  313. @api rtsp:pushFrame(frame_data, timestamp)
  314. @string frame_data 或
  315. @userdata frame_data H.264编码的视频帧数据
  316. @int timestamp 时间戳(毫秒), 可选,为0则使用内部时间戳
  317. @return int 成功时返回已发送或已入队的字节数, 失败返回负数
  318. @usage
  319. -- 持续推送H.264帧
  320. local frame_data = ... -- 获取H.264帧数据
  321. local timestamp = sys.now() % 0x100000000
  322. local ret = rtsp:pushFrame(frame_data, timestamp)
  323. if ret >= 0 then
  324. print("已发送", ret, "字节")
  325. else
  326. print("发送失败:", ret)
  327. end
  328. */
  329. static int l_rtsp_push_frame(lua_State *L) {
  330. luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
  331. if (!ud || !ud->rtsp) {
  332. lua_pushinteger(L, -1);
  333. return 1;
  334. }
  335. size_t len = 0;
  336. const uint8_t *data = (const uint8_t *)luaL_checklstring(L, 2, &len);
  337. uint32_t timestamp = 0;
  338. if (!lua_isnil(L, 3)) {
  339. timestamp = (uint32_t)luaL_checkinteger(L, 3);
  340. }
  341. if (len == 0 || len > (1024 * 1024)) {
  342. LLOGE("帧数据大小无效: %u", (uint32_t)len);
  343. lua_pushinteger(L, RTSP_ERR_INVALID_PARAM);
  344. return 1;
  345. }
  346. int ret = rtsp_push_h264_frame(ud->rtsp, data, (uint32_t)len, timestamp);
  347. lua_pushinteger(L, ret);
  348. return 1;
  349. }
  350. /**
  351. 获取RTSP统计信息
  352. @api rtsp:getStats()
  353. @return table 统计信息表
  354. @usage
  355. local stats = rtsp:getStats()
  356. print("已发送字节数:", stats.bytes_sent)
  357. print("已发送视频帧数:", stats.video_frames_sent)
  358. print("已发送RTP包数:", stats.rtp_packets_sent)
  359. */
  360. static int l_rtsp_get_stats(lua_State *L) {
  361. luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
  362. if (!ud || !ud->rtsp) {
  363. lua_pushnil(L);
  364. return 1;
  365. }
  366. rtsp_stats_t stats = {0};
  367. rtsp_get_stats(ud->rtsp, &stats);
  368. lua_newtable(L);
  369. lua_pushinteger(L, stats.bytes_sent);
  370. lua_setfield(L, -2, "bytes_sent");
  371. lua_pushinteger(L, stats.video_frames_sent);
  372. lua_setfield(L, -2, "video_frames_sent");
  373. lua_pushinteger(L, stats.rtp_packets_sent);
  374. lua_setfield(L, -2, "rtp_packets_sent");
  375. lua_pushinteger(L, stats.connection_time);
  376. lua_setfield(L, -2, "connection_time");
  377. lua_pushinteger(L, stats.last_video_timestamp);
  378. lua_setfield(L, -2, "last_video_timestamp");
  379. return 1;
  380. }
  381. /**
  382. 销毁RTSP上下文,释放所有资源
  383. @api rtsp:destroy()
  384. @return nil 无返回值
  385. @usage
  386. rtsp:destroy()
  387. */
  388. static int l_rtsp_destroy(lua_State *L) {
  389. luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
  390. if (!ud || !ud->rtsp) {
  391. return 0;
  392. }
  393. if (ud->callback_ref != LUA_NOREF) {
  394. luaL_unref(L, LUA_REGISTRYINDEX, ud->callback_ref);
  395. ud->callback_ref = LUA_NOREF;
  396. }
  397. rtsp_destroy(ud->rtsp);
  398. ud->rtsp = NULL;
  399. LLOGD("RTSP上下文已销毁");
  400. return 0;
  401. }
  402. static int l_rtsp_gc(lua_State *L) {
  403. luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
  404. if (ud && ud->rtsp) {
  405. if (ud->callback_ref != LUA_NOREF) {
  406. luaL_unref(L, LUA_REGISTRYINDEX, ud->callback_ref);
  407. }
  408. rtsp_destroy(ud->rtsp);
  409. ud->rtsp = NULL;
  410. }
  411. return 0;
  412. }
  413. #include "rotable2.h"
  414. static const rotable_Reg_t reg_rtsp_ctx[] = {
  415. {"setCallback", ROREG_FUNC(l_rtsp_set_callback)},
  416. {"connect", ROREG_FUNC(l_rtsp_connect)},
  417. {"disconnect", ROREG_FUNC(l_rtsp_disconnect)},
  418. {"start", ROREG_FUNC(l_rtsp_start)},
  419. {"stop", ROREG_FUNC(l_rtsp_stop)},
  420. {"getState", ROREG_FUNC(l_rtsp_get_state)},
  421. {"setSPS", ROREG_FUNC(l_rtsp_set_sps)},
  422. {"setPPS", ROREG_FUNC(l_rtsp_set_pps)},
  423. {"pushFrame", ROREG_FUNC(l_rtsp_push_frame)},
  424. {"getStats", ROREG_FUNC(l_rtsp_get_stats)},
  425. {"destroy", ROREG_FUNC(l_rtsp_destroy)},
  426. {"__gc", ROREG_FUNC(l_rtsp_gc)},
  427. {NULL, ROREG_INT(0)}
  428. };
  429. static const rotable_Reg_t reg_rtsp[] = {
  430. {"create", ROREG_FUNC(l_rtsp_create)},
  431. // RTSP状态常量
  432. {"STATE_IDLE", ROREG_INT(RTSP_STATE_IDLE)},
  433. {"STATE_CONNECTING", ROREG_INT(RTSP_STATE_CONNECTING)},
  434. {"STATE_OPTIONS", ROREG_INT(RTSP_STATE_OPTIONS)},
  435. {"STATE_DESCRIBE", ROREG_INT(RTSP_STATE_DESCRIBE)},
  436. {"STATE_SETUP", ROREG_INT(RTSP_STATE_SETUP)},
  437. {"STATE_PLAY", ROREG_INT(RTSP_STATE_PLAY)},
  438. {"STATE_PLAYING", ROREG_INT(RTSP_STATE_PLAYING)},
  439. {"STATE_DISCONNECTING",ROREG_INT(RTSP_STATE_DISCONNECTING)},
  440. {"STATE_ERROR", ROREG_INT(RTSP_STATE_ERROR)},
  441. // 返回值常量
  442. {"OK", ROREG_INT(RTSP_OK)},
  443. {"ERR_FAILED", ROREG_INT(RTSP_ERR_FAILED)},
  444. {"ERR_INVALID_PARAM", ROREG_INT(RTSP_ERR_INVALID_PARAM)},
  445. {"ERR_NO_MEMORY", ROREG_INT(RTSP_ERR_NO_MEMORY)},
  446. {"ERR_CONNECT_FAILED",ROREG_INT(RTSP_ERR_CONNECT_FAILED)},
  447. {"ERR_HANDSHAKE_FAILED",ROREG_INT(RTSP_ERR_HANDSHAKE_FAILED)},
  448. {"ERR_NETWORK", ROREG_INT(RTSP_ERR_NETWORK)},
  449. {"ERR_TIMEOUT", ROREG_INT(RTSP_ERR_TIMEOUT)},
  450. {"ERR_BUFFER_OVERFLOW",ROREG_INT(RTSP_ERR_BUFFER_OVERFLOW)},
  451. {NULL, ROREG_INT(0)}
  452. };
  453. static int _rtsp_struct_newindex(lua_State *L) {
  454. const rotable_Reg_t* reg = reg_rtsp_ctx;
  455. const char* key = luaL_checkstring(L, 2);
  456. while (1) {
  457. if (reg->name == NULL)
  458. return 0;
  459. if (!strcmp(reg->name, key)) {
  460. lua_pushcfunction(L, reg->value.value.func);
  461. return 1;
  462. }
  463. reg ++;
  464. }
  465. }
  466. LUAMOD_API int luaopen_rtsp(lua_State *L) {
  467. luat_newlib2(L, reg_rtsp);
  468. luaL_newmetatable(L, "rtsp_ctx");
  469. lua_pushcfunction(L, _rtsp_struct_newindex);
  470. lua_setfield(L, -2, "__index");
  471. lua_pop(L, 1);
  472. return 1;
  473. }