Ver Fonte

add: rtsp,添加第一个版本,局域网可以跑通, 外网的话,UDP丢包严重,也可能是lwip配置问题

Wendal Chen há 3 meses atrás
pai
commit
76b8471f7a

+ 12 - 0
components/network/libsntp/luat_sntp.c

@@ -358,3 +358,15 @@ int ntp_get(int adapter_index){
     }
     return ret;
 }
+
+uint64_t luat_sntp_time64_ms() {
+    uint64_t tick64 = luat_mcu_tick64();
+    uint32_t us_period = luat_mcu_us_period();
+    uint64_t ll_sec = tick64 /us_period/ 1000 / 1000;
+    uint64_t ll_ms  = (tick64 /us_period/ 1000) % 1000;
+    uint64_t tmp = ll_sec + g_sntp_ctx.sysboot_diff_sec;
+    //LLOGD("ntp sec: sec=%llu, ms=%u", tmp, ll_ms);
+    tmp *= 1000;
+    tmp += ll_ms + g_sntp_ctx.sysboot_diff_ms;
+    return tmp;
+}

+ 531 - 0
components/rtsp/binding/luat_lib_rtsp.c

@@ -0,0 +1,531 @@
+/*
+@module  rtsp
+@summary RTSP 直播推流
+@version 1.0
+@date    2025.12.11
+@tag     LUAT_USE_RTSP
+@usage
+-- RTSP推流示例
+local rtsp = rtsp.create("rtsp://example.com:554/stream")
+rtsp:setCallback(function(state, ...)
+    if state == rtsp.STATE_CONNECTED then
+        print("已连接到推流服务器")
+    elseif state == rtsp.STATE_PLAYING then
+        print("已开始推流")
+    elseif state == rtsp.STATE_ERROR then
+        print("出错:", ...)
+    end
+end)
+rtsp:connect()
+
+-- 开始处理
+rtsp:start()
+
+-- 30秒后停止
+sys.wait(30000)
+rtsp:stop()
+
+-- 断开连接
+rtsp:disconnect()
+rtsp:destroy()
+*/
+
+#include "luat_base.h"
+#include "luat_rtsp_push.h"
+#include "luat_msgbus.h"
+#include "luat_mem.h"
+#include "lauxlib.h"
+#include <stdlib.h>
+#include "lwip/timeouts.h"
+#include "lwip/tcpip.h"
+
+#define LUAT_LOG_TAG "rtsp"
+#include "luat_log.h"
+
+typedef struct {
+    rtsp_ctx_t *rtsp;
+    int callback_ref;
+} luat_rtsp_userdata_t;
+
+/**
+创建RTSP推流上下文
+@api rtsp.create(url)
+@string url RTSP服务器地址, 格式: rtsp://host:port/stream
+@return userdata RTSP上下文对象
+@usage
+local rtsp = rtsp.create("rtsp://example.com:554/stream")
+*/
+static int l_rtsp_create(lua_State *L) {
+    const char *url = luaL_checkstring(L, 1);
+    
+    luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)lua_newuserdata(L, sizeof(luat_rtsp_userdata_t));
+    if (!ud) {
+        LLOGE("内存分配失败");
+        lua_pushnil(L);
+        return 1;
+    }
+    
+    ud->rtsp = rtsp_create();
+    if (!ud->rtsp) {
+        LLOGE("RTSP上下文创建失败");
+        lua_pushnil(L);
+        return 1;
+    }
+    ud->rtsp->user_data = (void *)ud;
+    
+    ud->callback_ref = LUA_NOREF;
+    
+    if (rtsp_set_url(ud->rtsp, url) != 0) {
+        LLOGE("RTSP URL设置失败");
+        rtsp_destroy(ud->rtsp);
+        lua_pushnil(L);
+        return 1;
+    }
+    
+    luaL_getmetatable(L, "rtsp_ctx");
+    lua_setmetatable(L, -2);
+    
+    LLOGD("RTSP上下文创建成功: %s", url);
+    return 1;
+}
+
+/**
+设置RTSP状态回调函数
+@api rtsp:setCallback(func)
+@function func 回调函数, 参数为 (state, ...) 
+@return nil 无返回值
+@usage
+rtsp:setCallback(function(state, ...)
+    if state == rtsp.STATE_IDLE then
+        print("空闲状态")
+    elseif state == rtsp.STATE_CONNECTING then
+        print("正在连接")
+    elseif state == rtsp.STATE_OPTIONS then
+        print("发送OPTIONS")
+    elseif state == rtsp.STATE_DESCRIBE then
+        print("发送DESCRIBE")
+    elseif state == rtsp.STATE_SETUP then
+        print("发送SETUP")
+    elseif state == rtsp.STATE_PLAY then
+        print("发送PLAY请求")
+    elseif state == rtsp.STATE_PLAYING then
+        print("正在推流")
+    elseif state == rtsp.STATE_DISCONNECTING then
+        print("正在断开")
+    elseif state == rtsp.STATE_ERROR then
+        print("错误:", ...)
+    end
+end)
+*/
+static int l_rtsp_set_callback(lua_State *L) {
+    luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
+    if (!ud || !ud->rtsp) {
+        lua_pushboolean(L, 0);
+        return 1;
+    }
+    
+    if (lua_isfunction(L, 2)) {
+        if (ud->callback_ref != LUA_NOREF) {
+            luaL_unref(L, LUA_REGISTRYINDEX, ud->callback_ref);
+        }
+        lua_pushvalue(L, 2);
+        ud->callback_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+        LLOGD("RTSP回调函数已设置");
+    } else if (lua_isnil(L, 2)) {
+        if (ud->callback_ref != LUA_NOREF) {
+            luaL_unref(L, LUA_REGISTRYINDEX, ud->callback_ref);
+            ud->callback_ref = LUA_NOREF;
+        }
+        LLOGD("RTSP回调函数已清除");
+    } else {
+        LLOGE("参数错误,需要function或nil");
+        lua_pushboolean(L, 0);
+        return 1;
+    }
+    
+    lua_pushboolean(L, 1);
+    return 1;
+}
+
+static int l_rtsp_handler(lua_State *L, void *udata) {
+    rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
+    luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)msg->ptr;
+    if (!ud || ud->callback_ref == LUA_NOREF) {
+        return 0;
+    }
+    int state = msg->arg1;
+    lua_rawgeti(L, LUA_REGISTRYINDEX, ud->callback_ref);
+    if (lua_isfunction(L, -1)) {
+        lua_pushinteger(L, state);
+        lua_call(L, 1, 0);
+    }
+    return 0;
+}
+
+/**
+状态回调函数(内部使用)
+*/
+static void l_state_callback(rtsp_ctx_t *ctx, rtsp_state_t oldstate, rtsp_state_t newstate, int error_code) {
+    rtos_msg_t msg = {0};
+    msg.handler = l_rtsp_handler;
+    msg.ptr = ctx->user_data;
+    msg.arg1 = (int)newstate;
+    msg.arg2 = (int)oldstate;
+    LLOGD("RTSP状态(%d)回调消息入队 %p %p", (int)newstate, &msg, ctx->user_data);
+    // luat_msgbus_put(&msg, 0);
+}
+
+/**
+连接到RTSP服务器
+@api rtsp:connect()
+@return boolean 成功返回true, 失败返回false
+@usage
+local ok = rtsp:connect()
+if ok then
+    print("连接请求已发送")
+else
+    print("连接失败")
+end
+*/
+static int l_rtsp_connect(lua_State *L) {
+    luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
+    if (!ud || !ud->rtsp) {
+        lua_pushboolean(L, 0);
+        return 1;
+    }
+    
+    rtsp_set_state_callback(ud->rtsp, l_state_callback);
+    
+    int ret = tcpip_callback_with_block(rtsp_connect, (void *)ud->rtsp, 0);
+    LLOGD("RTSP连接请求: %s", ret == 0 ? "成功" : "失败");
+    lua_pushboolean(L, ret == 0 ? 1 : 0);
+    return 1;
+}
+
+/**
+断开RTSP连接
+@api rtsp:disconnect()
+@return boolean 成功返回true, 失败返回false
+@usage
+rtsp:disconnect()
+*/
+static int l_rtsp_disconnect(lua_State *L) {
+    luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
+    if (!ud || !ud->rtsp) {
+        lua_pushboolean(L, 0);
+        return 1;
+    }
+    
+    int ret = rtsp_disconnect(ud->rtsp);
+    LLOGD("RTSP断开连接: %s", ret == 0 ? "成功" : "失败");
+    lua_pushboolean(L, ret == 0 ? 1 : 0);
+    return 1;
+}
+
+static void t_rtsp_poll(void *arg) {
+    rtsp_ctx_t *ctx = (rtsp_ctx_t *)arg;
+    rtsp_poll(ctx);
+    sys_timeout(20, t_rtsp_poll, ctx);
+}
+
+/**
+处理RTSP事件
+@api rtsp:start()
+@return nil 无返回值
+@usage
+rtsp:start()
+*/
+static int l_rtsp_start(lua_State *L) {
+    luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
+    if (!ud || !ud->rtsp) {
+        return 0;
+    }
+    sys_timeout(20, t_rtsp_poll, ud->rtsp);
+    return 0;
+}
+
+/**
+停止处理RTSP事件
+@api rtsp:stop()
+@return nil 无返回值
+@usage
+rtsp:stop()
+*/
+static int l_rtsp_stop(lua_State *L) {
+    luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
+    if (!ud || !ud->rtsp) {
+        return 0;
+    }
+    // 移除定时器回调
+    sys_untimeout(t_rtsp_poll, ud->rtsp);
+    return 0;
+}
+
+/**
+获取RTSP连接状态
+@api rtsp:getState()
+@return int 当前状态值
+@usage
+local state = rtsp:getState()
+if state == rtsp.STATE_CONNECTED then
+    print("已连接")
+elseif state == rtsp.STATE_PLAYING then
+    print("正在推流")
+end
+*/
+static int l_rtsp_get_state(lua_State *L) {
+    luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
+    if (!ud || !ud->rtsp) {
+        lua_pushinteger(L, -1);
+        return 1;
+    }
+    
+    rtsp_state_t state = rtsp_get_state(ud->rtsp);
+    lua_pushinteger(L, (lua_Integer)state);
+    return 1;
+}
+
+/**
+设置H.264 SPS参数
+@api rtsp:setSPS(sps_data)
+@string sps_data 或 
+@userdata sps_data H.264序列参数集数据
+@return boolean 成功返回true, 失败返回false
+@usage
+local sps = string.fromBinary("\x67\x42...") -- H.264 SPS数据
+rtsp:setSPS(sps)
+*/
+static int l_rtsp_set_sps(lua_State *L) {
+    luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
+    if (!ud || !ud->rtsp) {
+        lua_pushboolean(L, 0);
+        return 1;
+    }
+    
+    size_t len = 0;
+    const uint8_t *data = (const uint8_t *)luaL_checklstring(L, 2, &len);
+    
+    if (rtsp_set_sps(ud->rtsp, data, (uint32_t)len) == RTSP_OK) {
+        LLOGD("SPS已设置: %u字节", (uint32_t)len);
+        lua_pushboolean(L, 1);
+    } else {
+        lua_pushboolean(L, 0);
+    }
+    return 1;
+}
+
+/**
+设置H.264 PPS参数
+@api rtsp:setPPS(pps_data)
+@string pps_data 或 
+@userdata pps_data H.264图像参数集数据
+@return boolean 成功返回true, 失败返回false
+@usage
+local pps = string.fromBinary("\x68\xCB...") -- H.264 PPS数据
+rtsp:setPPS(pps)
+*/
+static int l_rtsp_set_pps(lua_State *L) {
+    luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
+    if (!ud || !ud->rtsp) {
+        lua_pushboolean(L, 0);
+        return 1;
+    }
+    
+    size_t len = 0;
+    const uint8_t *data = (const uint8_t *)luaL_checklstring(L, 2, &len);
+    
+    if (rtsp_set_pps(ud->rtsp, data, (uint32_t)len) == RTSP_OK) {
+        LLOGD("PPS已设置: %u字节", (uint32_t)len);
+        lua_pushboolean(L, 1);
+    } else {
+        lua_pushboolean(L, 0);
+    }
+    return 1;
+}
+
+/**
+推送H.264视频帧
+@api rtsp:pushFrame(frame_data, timestamp)
+@string frame_data 或 
+@userdata frame_data H.264编码的视频帧数据
+@int timestamp 时间戳(毫秒), 可选,为0则使用内部时间戳
+@return int 成功时返回已发送或已入队的字节数, 失败返回负数
+@usage
+-- 持续推送H.264帧
+local frame_data = ... -- 获取H.264帧数据
+local timestamp = sys.now() % 0x100000000
+local ret = rtsp:pushFrame(frame_data, timestamp)
+if ret >= 0 then
+    print("已发送", ret, "字节")
+else
+    print("发送失败:", ret)
+end
+*/
+static int l_rtsp_push_frame(lua_State *L) {
+    luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
+    if (!ud || !ud->rtsp) {
+        lua_pushinteger(L, -1);
+        return 1;
+    }
+    
+    size_t len = 0;
+    const uint8_t *data = (const uint8_t *)luaL_checklstring(L, 2, &len);
+    uint32_t timestamp = 0;
+    
+    if (!lua_isnil(L, 3)) {
+        timestamp = (uint32_t)luaL_checkinteger(L, 3);
+    }
+    
+    if (len == 0 || len > (1024 * 1024)) {
+        LLOGE("帧数据大小无效: %u", (uint32_t)len);
+        lua_pushinteger(L, RTSP_ERR_INVALID_PARAM);
+        return 1;
+    }
+    
+    int ret = rtsp_push_h264_frame(ud->rtsp, data, (uint32_t)len, timestamp);
+    lua_pushinteger(L, ret);
+    return 1;
+}
+
+/**
+获取RTSP统计信息
+@api rtsp:getStats()
+@return table 统计信息表
+@usage
+local stats = rtsp:getStats()
+print("已发送字节数:", stats.bytes_sent)
+print("已发送视频帧数:", stats.video_frames_sent)
+print("已发送RTP包数:", stats.rtp_packets_sent)
+*/
+static int l_rtsp_get_stats(lua_State *L) {
+    luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
+    if (!ud || !ud->rtsp) {
+        lua_pushnil(L);
+        return 1;
+    }
+    
+    rtsp_stats_t stats = {0};
+    rtsp_get_stats(ud->rtsp, &stats);
+    
+    lua_newtable(L);
+    lua_pushinteger(L, stats.bytes_sent);
+    lua_setfield(L, -2, "bytes_sent");
+    lua_pushinteger(L, stats.video_frames_sent);
+    lua_setfield(L, -2, "video_frames_sent");
+    lua_pushinteger(L, stats.rtp_packets_sent);
+    lua_setfield(L, -2, "rtp_packets_sent");
+    lua_pushinteger(L, stats.connection_time);
+    lua_setfield(L, -2, "connection_time");
+    lua_pushinteger(L, stats.last_video_timestamp);
+    lua_setfield(L, -2, "last_video_timestamp");
+    
+    return 1;
+}
+
+/**
+销毁RTSP上下文,释放所有资源
+@api rtsp:destroy()
+@return nil 无返回值
+@usage
+rtsp:destroy()
+*/
+static int l_rtsp_destroy(lua_State *L) {
+    luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
+    if (!ud || !ud->rtsp) {
+        return 0;
+    }
+    
+    if (ud->callback_ref != LUA_NOREF) {
+        luaL_unref(L, LUA_REGISTRYINDEX, ud->callback_ref);
+        ud->callback_ref = LUA_NOREF;
+    }
+    
+    rtsp_destroy(ud->rtsp);
+    ud->rtsp = NULL;
+    
+    LLOGD("RTSP上下文已销毁");
+    return 0;
+}
+
+static int l_rtsp_gc(lua_State *L) {
+    luat_rtsp_userdata_t *ud = (luat_rtsp_userdata_t *)luaL_checkudata(L, 1, "rtsp_ctx");
+    if (ud && ud->rtsp) {
+        if (ud->callback_ref != LUA_NOREF) {
+            luaL_unref(L, LUA_REGISTRYINDEX, ud->callback_ref);
+        }
+        rtsp_destroy(ud->rtsp);
+        ud->rtsp = NULL;
+    }
+    return 0;
+}
+
+#include "rotable2.h"
+
+static const rotable_Reg_t reg_rtsp_ctx[] = {
+    {"setCallback",   ROREG_FUNC(l_rtsp_set_callback)},
+    {"connect",       ROREG_FUNC(l_rtsp_connect)},
+    {"disconnect",    ROREG_FUNC(l_rtsp_disconnect)},
+    {"start",         ROREG_FUNC(l_rtsp_start)},
+    {"stop",          ROREG_FUNC(l_rtsp_stop)},
+    {"getState",      ROREG_FUNC(l_rtsp_get_state)},
+    {"setSPS",        ROREG_FUNC(l_rtsp_set_sps)},
+    {"setPPS",        ROREG_FUNC(l_rtsp_set_pps)},
+    {"pushFrame",     ROREG_FUNC(l_rtsp_push_frame)},
+    {"getStats",      ROREG_FUNC(l_rtsp_get_stats)},
+    {"destroy",       ROREG_FUNC(l_rtsp_destroy)},
+    {"__gc",          ROREG_FUNC(l_rtsp_gc)},
+    {NULL,            ROREG_INT(0)}
+};
+
+static const rotable_Reg_t reg_rtsp[] = {
+    {"create",            ROREG_FUNC(l_rtsp_create)},
+    
+    // RTSP状态常量
+    {"STATE_IDLE",        ROREG_INT(RTSP_STATE_IDLE)},
+    {"STATE_CONNECTING",  ROREG_INT(RTSP_STATE_CONNECTING)},
+    {"STATE_OPTIONS",     ROREG_INT(RTSP_STATE_OPTIONS)},
+    {"STATE_DESCRIBE",    ROREG_INT(RTSP_STATE_DESCRIBE)},
+    {"STATE_SETUP",       ROREG_INT(RTSP_STATE_SETUP)},
+    {"STATE_PLAY",        ROREG_INT(RTSP_STATE_PLAY)},
+    {"STATE_PLAYING",     ROREG_INT(RTSP_STATE_PLAYING)},
+    {"STATE_DISCONNECTING",ROREG_INT(RTSP_STATE_DISCONNECTING)},
+    {"STATE_ERROR",       ROREG_INT(RTSP_STATE_ERROR)},
+    
+    // 返回值常量
+    {"OK",                ROREG_INT(RTSP_OK)},
+    {"ERR_FAILED",        ROREG_INT(RTSP_ERR_FAILED)},
+    {"ERR_INVALID_PARAM", ROREG_INT(RTSP_ERR_INVALID_PARAM)},
+    {"ERR_NO_MEMORY",     ROREG_INT(RTSP_ERR_NO_MEMORY)},
+    {"ERR_CONNECT_FAILED",ROREG_INT(RTSP_ERR_CONNECT_FAILED)},
+    {"ERR_HANDSHAKE_FAILED",ROREG_INT(RTSP_ERR_HANDSHAKE_FAILED)},
+    {"ERR_NETWORK",       ROREG_INT(RTSP_ERR_NETWORK)},
+    {"ERR_TIMEOUT",       ROREG_INT(RTSP_ERR_TIMEOUT)},
+    {"ERR_BUFFER_OVERFLOW",ROREG_INT(RTSP_ERR_BUFFER_OVERFLOW)},
+    
+    {NULL,                ROREG_INT(0)}
+};
+
+static int _rtsp_struct_newindex(lua_State *L) {
+	const rotable_Reg_t* reg = reg_rtsp_ctx;
+    const char* key = luaL_checkstring(L, 2);
+	while (1) {
+		if (reg->name == NULL)
+			return 0;
+		if (!strcmp(reg->name, key)) {
+			lua_pushcfunction(L, reg->value.value.func);
+			return 1;
+		}
+		reg ++;
+	}
+}
+
+LUAMOD_API int luaopen_rtsp(lua_State *L) {
+    luat_newlib2(L, reg_rtsp);
+    
+    luaL_newmetatable(L, "rtsp_ctx");
+    lua_pushcfunction(L, _rtsp_struct_newindex);
+    lua_setfield(L, -2, "__index");
+    lua_pop(L, 1);
+    
+    return 1;
+}

+ 362 - 0
components/rtsp/include/luat_rtsp_push.h

@@ -0,0 +1,362 @@
+/**
+ * @file luat_rtsp_push.h
+ * @brief RTSP推流组件 - 基于lwip raw API实现
+ * @author LuatOS Team
+ * 
+ * 该组件实现了RTSP(Real Time Streaming Protocol)推流功能,
+ * 支持将H.264视频流推送到RTSP服务器。
+ * 
+ * 主要特性:
+ * - 基于lwip raw socket API,适应嵌入式环境
+ * - 支持自定义H.264帧来源,灵活的NALU帧注入
+ * - 完整的RTSP握手和连接管理
+ * - 支持RTP/RTCP协议实现
+ * - 支持H.264基础配置文件
+ * - C99语法,内存使用优化
+ * 
+ * 调试说明:
+ * - 在 luat_rtsp_push.c 中修改 RTSP_DEBUG_VERBOSE 宏来控制详细日志输出
+ * - 设置为 1 开启详细调试信息,设置为 0 关闭(仅保留关键日志)
+ */
+
+#ifndef __LUAT_RTSP_PUSH_H__
+#define __LUAT_RTSP_PUSH_H__
+
+#include "luat_base.h"
+#include "lwip/tcp.h"
+#include "lwip/udp.h"
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ======================== RTSP常量定义 ======================== */
+
+/** RTSP默认端口 */
+#define RTSP_DEFAULT_PORT 554
+
+/** RTSP缓冲区大小(字节) - 需要足够大以容纳RTSP信令 */
+#define RTSP_BUFFER_SIZE (64 * 1024)
+
+/** RTP缓冲区大小(字节) */
+#define RTP_BUFFER_SIZE (256 * 1024)
+
+/** RTP UDP缓冲区最大包大小 */
+#define RTP_MAX_PACKET_SIZE 1400
+
+/** 发送帧队列最大字节数上限,超出将丢弃未发送帧 */
+#define RTSP_MAX_QUEUE_BYTES (1024 * 1024)
+
+/** RTSP命令超时时间(毫秒) */
+#define RTSP_CMD_TIMEOUT 5000
+
+/* ======================== 返回值定义 ======================== */
+
+/** 操作成功 */
+#define RTSP_OK 0
+
+/** 通用错误 */
+#define RTSP_ERR_FAILED (-1)
+
+/** 参数无效 */
+#define RTSP_ERR_INVALID_PARAM (-2)
+
+/** 内存不足 */
+#define RTSP_ERR_NO_MEMORY (-3)
+
+/** 连接错误 */
+#define RTSP_ERR_CONNECT_FAILED (-4)
+
+/** 握手失败 */
+#define RTSP_ERR_HANDSHAKE_FAILED (-5)
+
+/** 网络错误 */
+#define RTSP_ERR_NETWORK (-6)
+
+/** 超时 */
+#define RTSP_ERR_TIMEOUT (-7)
+
+/** 缓冲区溢出 */
+#define RTSP_ERR_BUFFER_OVERFLOW (-8)
+
+/* ======================== 数据类型定义 ======================== */
+
+/**
+ * RTSP连接状态枚举
+ */
+typedef enum {
+    RTSP_STATE_IDLE = 0,            /**< 空闲状态 */
+    RTSP_STATE_CONNECTING = 1,      /**< 正在连接 */
+    RTSP_STATE_OPTIONS = 2,         /**< 发送OPTIONS请求 */
+    RTSP_STATE_DESCRIBE = 3,        /**< 发送DESCRIBE请求 */
+    RTSP_STATE_SETUP = 4,           /**< 发送SETUP请求 */
+    RTSP_STATE_PLAY = 5,            /**< 发送PLAY请求,准备推流 */
+    RTSP_STATE_PLAYING = 6,         /**< 正在推流 */
+    RTSP_STATE_DISCONNECTING = 7,   /**< 正在断开连接 */
+    RTSP_STATE_ERROR = 8            /**< 错误状态 */
+} rtsp_state_t;
+
+/**
+ * H.264 NALU类型枚举
+ */
+typedef enum {
+    NALU_TYPE_NON_IDR = 1,          /**< 非IDR帧 */
+    NALU_TYPE_IDR = 5,              /**< IDR帧(关键帧) */
+    NALU_TYPE_SEI = 6,              /**< SEI(补充增强信息) */
+    NALU_TYPE_SPS = 7,              /**< SPS(序列参数集) */
+    NALU_TYPE_PPS = 8,              /**< PPS(图像参数集) */
+    NALU_TYPE_AUD = 9               /**< AUD(访问单元分隔符) */
+} nalu_type_t;
+
+/**
+ * H.264视频帧信息结构体
+ */
+typedef struct {
+    uint8_t *data;                  /**< 视频数据指针 */
+    uint32_t len;                   /**< 视频数据长度 */
+    uint32_t timestamp;             /**< 时间戳(ms) */
+    uint8_t nalu_type;              /**< NALU类型 */
+    uint8_t is_keyframe;            /**< 是否为关键帧 */
+} h264_frame_t;
+
+/**
+ * RTSP推流统计信息结构体
+ */
+typedef struct {
+    uint32_t bytes_sent;            /**< 已发送的字节数 */
+    uint32_t video_frames_sent;     /**< 已发送的视频帧数 */
+    uint32_t rtp_packets_sent;      /**< 已发送的RTP包数 */
+    uint32_t connection_time;       /**< 连接持续时间(毫秒) */
+    uint32_t last_video_timestamp;  /**< 最后视频时间戳(毫秒) */
+} rtsp_stats_t;
+
+/**
+ * RTSP推流上下文结构体
+ * 管理单个RTSP连接的所有状态和缓冲区
+ */
+typedef struct {
+    /** ============ 连接信息 ============ */
+    char *url;                      /**< RTSP服务器URL */
+    char *host;                     /**< RTSP服务器主机名/IP地址 */
+    char *stream;                   /**< 推流名 */
+    char *auth;                     /**< 认证信息(用户名:密码) */
+    uint16_t port;                  /**< 连接端口 */
+    
+    /** ============ TCP连接状态(RTSP控制通道) ============ */
+    struct tcp_pcb *control_pcb;    /**< lwip TCP控制块(RTSP信令) */
+    rtsp_state_t state;             /**< 当前连接状态 */
+    uint32_t last_activity_time;    /**< 最后活动时间戳 */
+    
+    /** ============ UDP连接状态(RTP媒体通道) ============ */
+    struct udp_pcb *rtp_pcb;        /**< RTP UDP控制块 */
+    struct udp_pcb *rtcp_pcb;       /**< RTCP UDP控制块 */
+    ip_addr_t remote_ip;            /**< 远端IP地址 */
+    uint16_t remote_rtp_port;       /**< 远端RTP端口 */
+    uint16_t remote_rtcp_port;      /**< 远端RTCP端口 */
+    uint16_t local_rtp_port;        /**< 本地RTP端口 */
+    uint16_t local_rtcp_port;       /**< 本地RTCP端口 */
+    
+    /** ============ RTSP协议状态 ============ */
+    uint32_t cseq;                  /**< RTSP序列号 */
+    char *session_id;               /**< RTSP会话ID */
+    uint32_t video_stream_id;       /**< 视频流ID(SSRC) */
+    
+    /** ============ RTP状态 ============ */
+    uint32_t rtp_sequence;          /**< RTP序列号 */
+    uint32_t rtp_timestamp;         /**< RTP时间戳 */
+    uint32_t rtp_ssrc;              /**< RTP同步源标识符 */
+    
+    /** ============ 缓冲区管理 ============ */
+    uint8_t *recv_buf;              /**< 接收缓冲区 */
+    uint32_t recv_buf_size;         /**< 接收缓冲区大小 */
+    uint32_t recv_pos;              /**< 接收缓冲区写位置 */
+    
+    uint8_t *send_buf;              /**< 发送缓冲区 */
+    uint32_t send_buf_size;         /**< 发送缓冲区大小 */
+    uint32_t send_pos;              /**< 发送缓冲区写位置 */
+    
+    uint8_t *rtp_buf;               /**< RTP发送缓冲区 */
+    uint32_t rtp_buf_size;          /**< RTP缓冲区大小 */
+
+    /** ============ 帧发送队列 ============ */
+    struct rtsp_frame_node *frame_head; /**< 待发送帧队列头 */
+    struct rtsp_frame_node *frame_tail; /**< 待发送帧队列尾 */
+    uint32_t frame_queue_bytes;          /**< 队列占用的总字节数 */
+    
+    /** ============ 时间戳管理 ============ */
+    uint32_t video_timestamp;       /**< 当前视频时间戳(ms) */
+    uint32_t base_timestamp;        /**< 基准时间戳 */
+    uint32_t start_tick;            /**< 启动时刻的系统tick */
+    
+    /** ============ H.264编码信息 ============ */
+    uint8_t *sps_data;              /**< SPS(序列参数集)数据 */
+    uint32_t sps_len;               /**< SPS长度 */
+    uint8_t *pps_data;              /**< PPS(图像参数集)数据 */
+    uint32_t pps_len;               /**< PPS长度 */
+    char *sprop_parameter_sets;     /**< SDP中的sprop-parameter-sets */
+    
+    /** ============ 统计信息 ============ */
+    uint32_t packets_sent;          /**< 已发送的包数 */
+    uint32_t bytes_sent;            /**< 已发送的字节数 */
+    uint32_t video_frames_sent;     /**< 已发送的视频帧数 */
+    uint32_t rtp_packets_sent;      /**< 已发送的RTP包数 */
+    uint32_t last_rtcp_time;        /**< 上次发送RTCP SR的时间(tick) */
+    
+    /** ============ 用户数据 ============ */
+    void *user_data;                /**< 用户自定义数据指针 */
+} rtsp_ctx_t;
+
+/**
+ * RTSP状态变化回调函数类型
+ * 
+ * @param ctx RTSP上下文指针
+ * @param old_state 旧状态
+ * @param new_state 新状态
+ * @param error_code 错误代码(仅在STATE_ERROR时有效)
+ */
+typedef void (*rtsp_state_callback_t)(rtsp_ctx_t *ctx, rtsp_state_t old_state, 
+                                      rtsp_state_t new_state, int error_code);
+
+/* ======================== 核心接口函数 ======================== */
+
+/**
+ * 创建RTSP推流上下文
+ * 
+ * @return RTSP上下文指针,失败返回NULL
+ * @note 返回的指针需要使用rtsp_destroy()释放
+ */
+rtsp_ctx_t* rtsp_create(void);
+
+/**
+ * 销毁RTSP推流上下文,释放所有资源
+ * 
+ * @param ctx RTSP上下文指针
+ * @return 0=成功, 负数=失败
+ */
+int rtsp_destroy(rtsp_ctx_t *ctx);
+
+/**
+ * 设置RTSP服务器URL
+ * 
+ * @param ctx RTSP上下文指针
+ * @param url RTSP服务器地址,格式: rtsp://host:port/stream
+ * @return 0=成功, 负数=失败
+ */
+int rtsp_set_url(rtsp_ctx_t *ctx, const char *url);
+
+/**
+ * 设置状态变化回调函数
+ * 
+ * @param ctx RTSP上下文指针
+ * @param callback 回调函数指针
+ * @return 0=成功, 负数=失败
+ */
+int rtsp_set_state_callback(rtsp_ctx_t *ctx, rtsp_state_callback_t callback);
+
+/**
+ * 连接到RTSP服务器
+ * 
+ * @param ctx RTSP上下文指针
+ * @return 0=成功, 负数=失败
+ * @note 此函数应在lwip tcpip线程中调用
+ */
+int rtsp_connect(rtsp_ctx_t *ctx);
+
+/**
+ * 断开RTSP连接
+ * 
+ * @param ctx RTSP上下文指针
+ * @return 0=成功, 负数=失败
+ */
+int rtsp_disconnect(rtsp_ctx_t *ctx);
+
+/**
+ * 获取当前连接状态
+ * 
+ * @param ctx RTSP上下文指针
+ * @return 当前状态值
+ */
+rtsp_state_t rtsp_get_state(rtsp_ctx_t *ctx);
+
+/**
+ * 设置H.264 SPS数据(序列参数集)
+ * 
+ * @param ctx RTSP上下文指针
+ * @param sps_data SPS数据指针
+ * @param sps_len SPS数据长度
+ * @return 0=成功, 负数=失败
+ * @note 数据会被复制,调用者可以释放原数据
+ */
+int rtsp_set_sps(rtsp_ctx_t *ctx, const uint8_t *sps_data, uint32_t sps_len);
+
+/**
+ * 设置H.264 PPS数据(图像参数集)
+ * 
+ * @param ctx RTSP上下文指针
+ * @param pps_data PPS数据指针
+ * @param pps_len PPS数据长度
+ * @return 0=成功, 负数=失败
+ * @note 数据会被复制,调用者可以释放原数据
+ */
+int rtsp_set_pps(rtsp_ctx_t *ctx, const uint8_t *pps_data, uint32_t pps_len);
+
+/**
+ * 推送H.264视频帧数据
+ * 
+ * 该函数接收H.264编码的视频帧数据(可以是完整的访问单元或单个NALU)
+ * 内部会自动进行RTP打包并通过UDP发送到服务器。
+ * 
+ * @param ctx RTSP上下文指针
+ * @param frame_data 视频帧数据指针(包含起始码或不包含均可)
+ * @param frame_len 视频帧数据长度(字节数)
+ * @param timestamp 时间戳(毫秒),如果为0则使用内部时间戳
+ * @return 0=成功,正数=已入队,负数=失败
+ * 
+ * @note 
+ * - frame_data 可以包含H.264起始码(0x00000001或0x000001)或不包含
+ * - 支持单个NALU或多个NALU的访问单元
+ * - 如果在PLAYING状态下调用,数据会立即发送;否则进入队列
+ * - 返回值为正数时表示字节数已加入队列
+ * 
+ * @example
+ * // 推送一帧H.264视频
+ * uint8_t *frame_data = ...;  // H.264编码帧数据
+ * uint32_t frame_len = ...;   // 帧长度
+ * uint32_t timestamp = 0;     // 使用内部时间戳
+ * 
+ * int ret = rtsp_push_h264_frame(ctx, frame_data, frame_len, timestamp);
+ * if (ret >= 0) {
+ *     printf("成功推送%d字节\n", ret);
+ * } else {
+ *     printf("推送失败: %d\n", ret);
+ * }
+ */
+int rtsp_push_h264_frame(rtsp_ctx_t *ctx, const uint8_t *frame_data, 
+                         uint32_t frame_len, uint32_t timestamp);
+
+/**
+ * 处理RTSP连接事件,应定期调用此函数
+ * 
+ * @param ctx RTSP上下文指针
+ * @return 0=成功, 负数=失败
+ * @note 建议每10-20ms调用一次此函数
+ */
+int rtsp_poll(rtsp_ctx_t *ctx);
+
+/**
+ * 获取推流统计信息
+ * 
+ * @param ctx RTSP上下文指针
+ * @param stats 统计信息指针
+ * @return 0=成功, 负数=失败
+ */
+int rtsp_get_stats(rtsp_ctx_t *ctx, rtsp_stats_t *stats);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LUAT_RTSP_PUSH_H__ */

+ 1854 - 0
components/rtsp/src/luat_rtsp_push.c

@@ -0,0 +1,1854 @@
+/**
+ * @file luat_rtsp_push.c
+ * @brief RTSP推流组件实现 - 基于lwip raw API
+ * @author LuatOS Team
+ * 
+ * 实现了RTSP协议的核心功能,包括:
+ * - TCP连接管理(RTSP控制通道)
+ * - UDP连接管理(RTP媒体通道)
+ * - RTSP握手流程(OPTIONS, DESCRIBE, SETUP, PLAY)
+ * - RTP打包和发送
+ * - H.264视频数据处理
+ * - 网络数据收发
+ */
+
+#include "luat_rtsp_push.h"
+#include "luat_debug.h"
+#include "luat_mcu.h"
+#include "luat_mem.h"
+#include "luat_rtos.h"
+#include "luat_sntp.h"
+#include "lwip/tcp.h"
+#include "lwip/udp.h"
+#include "lwip/tcpip.h"
+#include "lwip/timeouts.h"
+#include "lwip/ip_addr.h"
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#define LUAT_LOG_TAG "rtsp_push"
+#include "luat_log.h"
+
+/* ======================== 调试开关 ======================== */
+
+/** 启用详细调试日志 (0=关闭, 1=开启) */
+#define RTSP_DEBUG_VERBOSE 0
+
+#if RTSP_DEBUG_VERBOSE
+    #define RTSP_LOGV(...) LLOGD(__VA_ARGS__)
+#else
+    #define RTSP_LOGV(...)
+#endif
+
+/* ======================== 内部常量定义 ======================== */
+
+/** 内部帧队列节点结构体 */
+struct rtsp_frame_node {
+    uint8_t *data;
+    uint32_t len;
+    uint32_t timestamp;
+    struct rtsp_frame_node *next;
+};
+
+/** RTP固定头大小 */
+#define RTP_HEADER_SIZE 12
+
+/** RTP H.264 Payload Type */
+#define RTP_PAYLOAD_TYPE_H264 96
+
+/** RTP时间戳基数(90000 Hz for video) */
+#define RTP_TIMESTAMP_BASE 90000
+
+/** H.264 NALU类型定义 */
+#define NALU_TYPE_SLICE    1
+#define NALU_TYPE_DPA      2
+#define NALU_TYPE_DPB      3
+#define NALU_TYPE_DPC      4
+#define NALU_TYPE_IDR      5
+#define NALU_TYPE_SEI      6
+#define NALU_TYPE_SPS      7
+#define NALU_TYPE_PPS      8
+#define NALU_TYPE_AUD      9
+
+/** FU-A分片标识 */
+#define FU_A_TYPE          28
+
+rtsp_ctx_t *g_rtsp_ctx;
+
+/* ======================== 内部函数声明 ======================== */
+
+/**
+ * 解析URL并提取主机名、端口、流名
+ */
+static int rtsp_parse_url(rtsp_ctx_t *ctx, const char *url);
+
+/**
+ * 详细解析SPS数据
+ */
+static void rtsp_parse_sps_detail(const uint8_t *sps_data, uint32_t sps_len);
+
+/**
+ * 详细解析PPS数据
+ */
+static void rtsp_parse_pps_detail(const uint8_t *pps_data, uint32_t pps_len);
+
+/**
+ * 详细解析SPS数据
+ */
+static void rtsp_parse_sps_detail(const uint8_t *sps_data, uint32_t sps_len);
+
+/**
+ * 详细解析PPS数据
+ */
+static void rtsp_parse_pps_detail(const uint8_t *pps_data, uint32_t pps_len);
+
+/**
+ * TCP连接回调函数
+ */
+static err_t rtsp_tcp_connect_callback(void *arg, struct tcp_pcb *pcb, err_t err);
+
+/**
+ * TCP接收回调函数
+ */
+static err_t rtsp_tcp_recv_callback(void *arg, struct tcp_pcb *pcb, 
+                                   struct pbuf *p, err_t err);
+
+/**
+ * TCP错误回调函数
+ */
+static void rtsp_tcp_error_callback(void *arg, err_t err);
+
+/**
+ * 状态转移函数
+ */
+static void rtsp_set_state(rtsp_ctx_t *ctx, rtsp_state_t new_state, int error_code);
+
+/**
+ * 发送RTSP命令
+ */
+static int rtsp_send_command(rtsp_ctx_t *ctx, const char *method, 
+                             const char *resource, const char *extra_headers,
+                             const char *data, uint32_t data_len);
+
+/**
+ * 处理RTSP响应
+ */
+static int rtsp_handle_response(rtsp_ctx_t *ctx);
+
+/**
+ * 发送RTP包
+ */
+static int rtsp_send_rtp_packet(rtsp_ctx_t *ctx, const uint8_t *data, 
+                                uint32_t len, uint32_t timestamp, uint8_t marker);
+
+/**
+ * 解析H.264帧并构建RTP包
+ */
+static int rtsp_build_rtp_payload(rtsp_ctx_t *ctx, const uint8_t *frame_data, 
+                                  uint32_t frame_len, uint32_t timestamp);
+
+/**
+ * 从队列中提取并发送帧
+ */
+static int rtsp_send_queued_frames(rtsp_ctx_t *ctx);
+
+/**
+ * 清空帧队列
+ */
+static void rtsp_clear_frame_queue(rtsp_ctx_t *ctx);
+
+/** 简单的大小写不敏感比较 */
+static int rtsp_strncasecmp(const char *a, const char *b, size_t n);
+
+/**
+ * Base64编码函数
+ */
+static int rtsp_base64_encode(const uint8_t *src, uint32_t src_len, char *dst, uint32_t dst_len);
+
+/**
+ * 查找下一个NALU起始码位置
+ */
+static const uint8_t* rtsp_find_next_nalu(const uint8_t *data, uint32_t len, uint32_t *start_code_len);
+
+/**
+ * 发送单个NALU(支持FU-A分片)
+ */
+static int rtsp_send_nalu(rtsp_ctx_t *ctx, const uint8_t *nalu_data, 
+                          uint32_t nalu_len, uint32_t timestamp, uint8_t is_last_nalu);
+
+/**
+ * 转换系统时间到NTP时间戳
+ */
+static void rtsp_get_ntp_timestamp(uint32_t *ntp_sec, uint32_t *ntp_frac);
+
+/**
+ * 发送RTCP Sender Report
+ */
+static int rtsp_send_rtcp_sr(rtsp_ctx_t *ctx);
+
+/* ======================== 核心实现 ======================== */
+
+/**
+ * 创建RTSP推流上下文
+ */
+rtsp_ctx_t* rtsp_create(void) {
+    rtsp_ctx_t *ctx = (rtsp_ctx_t *)luat_heap_malloc(sizeof(rtsp_ctx_t));
+    if (!ctx) {
+        LLOGE("内存分配失败");
+        return NULL;
+    }
+    
+    memset(ctx, 0, sizeof(rtsp_ctx_t));
+    
+    // 初始化缓冲区
+    ctx->recv_buf = (uint8_t *)luat_heap_malloc(RTSP_BUFFER_SIZE);
+    if (!ctx->recv_buf) {
+        LLOGE("接收缓冲区分配失败");
+        luat_heap_free(ctx);
+        return NULL;
+    }
+    ctx->recv_buf_size = RTSP_BUFFER_SIZE;
+    ctx->recv_pos = 0;
+    
+    ctx->send_buf = (uint8_t *)luat_heap_malloc(RTSP_BUFFER_SIZE);
+    if (!ctx->send_buf) {
+        LLOGE("发送缓冲区分配失败");
+        luat_heap_free(ctx->recv_buf);
+        luat_heap_free(ctx);
+        return NULL;
+    }
+    ctx->send_buf_size = RTSP_BUFFER_SIZE;
+    ctx->send_pos = 0;
+    
+    ctx->rtp_buf = (uint8_t *)luat_heap_malloc(RTP_BUFFER_SIZE);
+    if (!ctx->rtp_buf) {
+        LLOGE("RTP缓冲区分配失败");
+        luat_heap_free(ctx->send_buf);
+        luat_heap_free(ctx->recv_buf);
+        luat_heap_free(ctx);
+        return NULL;
+    }
+    ctx->rtp_buf_size = RTP_BUFFER_SIZE;
+    
+    // 初始化RTSP状态
+    ctx->state = RTSP_STATE_IDLE;
+    ctx->cseq = 1;
+    ctx->rtp_sequence = (uint32_t)luat_mcu_ticks();
+    ctx->rtp_ssrc = (uint32_t)luat_mcu_ticks();
+    ctx->start_tick = luat_mcu_ticks();
+    ctx->last_rtcp_time = luat_mcu_ticks();
+    
+    // 初始化帧队列
+    ctx->frame_head = NULL;
+    ctx->frame_tail = NULL;
+    ctx->frame_queue_bytes = 0;
+    
+    g_rtsp_ctx = ctx;
+    LLOGD("RTSP上下文创建成功");
+    return ctx;
+}
+
+/**
+ * 销毁RTSP推流上下文
+ */
+int rtsp_destroy(rtsp_ctx_t *ctx) {
+    if (!ctx) {
+        return RTSP_ERR_INVALID_PARAM;
+    }
+    
+    // 断开连接
+    if (ctx->state != RTSP_STATE_IDLE) {
+        rtsp_disconnect(ctx);
+    }
+    
+    // 清空队列
+    rtsp_clear_frame_queue(ctx);
+    
+    // 释放TCP连接
+    if (ctx->control_pcb) {
+        tcp_abort(ctx->control_pcb);
+        ctx->control_pcb = NULL;
+    }
+    
+    // 释放UDP连接
+    if (ctx->rtp_pcb) {
+        udp_remove(ctx->rtp_pcb);
+        ctx->rtp_pcb = NULL;
+    }
+    if (ctx->rtcp_pcb) {
+        udp_remove(ctx->rtcp_pcb);
+        ctx->rtcp_pcb = NULL;
+    }
+    
+    // 释放缓冲区
+    if (ctx->recv_buf) {
+        luat_heap_free(ctx->recv_buf);
+        ctx->recv_buf = NULL;
+    }
+    if (ctx->send_buf) {
+        luat_heap_free(ctx->send_buf);
+        ctx->send_buf = NULL;
+    }
+    if (ctx->rtp_buf) {
+        luat_heap_free(ctx->rtp_buf);
+        ctx->rtp_buf = NULL;
+    }
+    
+    // 释放字符串
+    if (ctx->url) {
+        luat_heap_free(ctx->url);
+        ctx->url = NULL;
+    }
+    if (ctx->host) {
+        luat_heap_free(ctx->host);
+        ctx->host = NULL;
+    }
+    if (ctx->stream) {
+        luat_heap_free(ctx->stream);
+        ctx->stream = NULL;
+    }
+    if (ctx->auth) {
+        luat_heap_free(ctx->auth);
+        ctx->auth = NULL;
+    }
+    if (ctx->session_id) {
+        luat_heap_free(ctx->session_id);
+        ctx->session_id = NULL;
+    }
+    if (ctx->sps_data) {
+        luat_heap_free(ctx->sps_data);
+        ctx->sps_data = NULL;
+    }
+    if (ctx->pps_data) {
+        luat_heap_free(ctx->pps_data);
+        ctx->pps_data = NULL;
+    }
+    if (ctx->sprop_parameter_sets) {
+        luat_heap_free(ctx->sprop_parameter_sets);
+        ctx->sprop_parameter_sets = NULL;
+    }
+    
+    luat_heap_free(ctx);
+    LLOGD("RTSP上下文已销毁");
+    return RTSP_OK;
+}
+
+/**
+ * 设置RTSP服务器URL
+ */
+int rtsp_set_url(rtsp_ctx_t *ctx, const char *url) {
+    if (!ctx || !url) {
+        return RTSP_ERR_INVALID_PARAM;
+    }
+    
+    if (ctx->url) {
+        luat_heap_free(ctx->url);
+    }
+    
+    ctx->url = (char *)luat_heap_malloc(strlen(url) + 1);
+    if (!ctx->url) {
+        LLOGE("URL内存分配失败");
+        return RTSP_ERR_NO_MEMORY;
+    }
+    
+    strcpy(ctx->url, url);
+    
+    if (rtsp_parse_url(ctx, url) != RTSP_OK) {
+        LLOGE("URL解析失败: %s", url);
+        return RTSP_ERR_INVALID_PARAM;
+    }
+    
+    LLOGD("RTSP URL设置: %s", url);
+    return RTSP_OK;
+}
+
+/**
+ * 设置状态变化回调函数
+ */
+int rtsp_set_state_callback(rtsp_ctx_t *ctx, rtsp_state_callback_t callback) {
+    if (!ctx) {
+        return RTSP_ERR_INVALID_PARAM;
+    }
+    
+    ctx->user_data = (void *)callback;
+    return RTSP_OK;
+}
+
+/**
+ * 设置H.264 SPS数据
+ */
+int rtsp_set_sps(rtsp_ctx_t *ctx, const uint8_t *sps_data, uint32_t sps_len) {
+    if (!ctx || !sps_data || sps_len == 0 || sps_len > 1024) {
+        return RTSP_ERR_INVALID_PARAM;
+    }
+    
+    if (ctx->sps_data) {
+        luat_heap_free(ctx->sps_data);
+    }
+    
+    ctx->sps_data = (uint8_t *)luat_heap_malloc(sps_len);
+    if (!ctx->sps_data) {
+        LLOGE("SPS数据内存分配失败");
+        return RTSP_ERR_NO_MEMORY;
+    }
+    
+    memcpy(ctx->sps_data, sps_data, sps_len);
+    ctx->sps_len = sps_len;
+    
+    LLOGD("SPS设置完毕: %u字节", sps_len);
+    return RTSP_OK;
+}
+
+/**
+ * 设置H.264 PPS数据
+ */
+int rtsp_set_pps(rtsp_ctx_t *ctx, const uint8_t *pps_data, uint32_t pps_len) {
+    if (!ctx || !pps_data || pps_len == 0 || pps_len > 1024) {
+        return RTSP_ERR_INVALID_PARAM;
+    }
+    
+    if (ctx->pps_data) {
+        luat_heap_free(ctx->pps_data);
+    }
+    
+    ctx->pps_data = (uint8_t *)luat_heap_malloc(pps_len);
+    if (!ctx->pps_data) {
+        LLOGE("PPS数据内存分配失败");
+        return RTSP_ERR_NO_MEMORY;
+    }
+    
+    memcpy(ctx->pps_data, pps_data, pps_len);
+    ctx->pps_len = pps_len;
+    
+    LLOGD("PPS设置完毕: %u字节", pps_len);
+    return RTSP_OK;
+}
+
+/**
+ * 推送H.264视频帧
+ */
+int rtsp_push_h264_frame(rtsp_ctx_t *ctx, const uint8_t *frame_data, 
+                         uint32_t frame_len, uint32_t timestamp) {
+    if (!ctx || !frame_data || frame_len == 0) {
+        return RTSP_ERR_INVALID_PARAM;
+    }
+    
+    // 如果在PLAYING状态,直接处理;否则进入队列
+    if (ctx->state == RTSP_STATE_PLAYING) {
+        // 直接构建RTP包并发送
+        return rtsp_build_rtp_payload(ctx, frame_data, frame_len, timestamp);
+    } else if (1) {
+        return 0; // 丢弃非PLAYING状态下的帧
+    } else {
+        LLOGI("当前状态非PLAYING,帧加入发送队列 %d %d", frame_len, ctx->state);
+        // 加入队列
+        struct rtsp_frame_node *node = (struct rtsp_frame_node *)
+            luat_heap_malloc(sizeof(struct rtsp_frame_node));
+        if (!node) {
+            LLOGE("帧队列节点内存分配失败");
+            return RTSP_ERR_NO_MEMORY;
+        }
+        
+        node->data = (uint8_t *)luat_heap_malloc(frame_len);
+        if (!node->data) {
+            LLOGE("帧数据内存分配失败");
+            luat_heap_free(node);
+            return RTSP_ERR_NO_MEMORY;
+        }
+        
+        memcpy(node->data, frame_data, frame_len);
+        node->len = frame_len;
+        node->timestamp = timestamp;
+        node->next = NULL;
+        
+        // 检查队列大小
+        if (ctx->frame_queue_bytes + frame_len > RTSP_MAX_QUEUE_BYTES) {
+            // 丢弃最早的非关键帧
+            if (ctx->frame_head && ctx->frame_head != ctx->frame_tail) {
+                struct rtsp_frame_node *temp = ctx->frame_head;
+                ctx->frame_head = ctx->frame_head->next;
+                ctx->frame_queue_bytes -= temp->len;
+                
+                luat_heap_free(temp->data);
+                luat_heap_free(temp);
+                LLOGD("队列溢出,丢弃一帧");
+            }
+        }
+        
+        if (!ctx->frame_head) {
+            ctx->frame_head = node;
+        } else {
+            ctx->frame_tail->next = node;
+        }
+        ctx->frame_tail = node;
+        ctx->frame_queue_bytes += frame_len;
+        
+        //RTSP_LOGV("帧已加入队列: %u字节, 队列总大小: %u字节", frame_len, ctx->frame_queue_bytes);
+        return (int)frame_len;
+    }
+}
+
+/**
+ * 获取当前连接状态
+ */
+rtsp_state_t rtsp_get_state(rtsp_ctx_t *ctx) {
+    if (!ctx) {
+        return RTSP_STATE_IDLE;
+    }
+    return ctx->state;
+}
+
+/**
+ * 处理RTSP事件轮询
+ */
+int rtsp_poll(rtsp_ctx_t *ctx) {
+    if (!ctx) {
+        return RTSP_ERR_INVALID_PARAM;
+    }
+    
+    uint32_t now = luat_mcu_ticks();
+    
+    // 检查连接超时
+    if (ctx->state != RTSP_STATE_IDLE && ctx->state != RTSP_STATE_ERROR && ctx->state != RTSP_STATE_PLAYING) {
+        if (now - ctx->last_activity_time > RTSP_CMD_TIMEOUT) {
+            LLOGE("RTSP连接超时");
+            rtsp_set_state(ctx, RTSP_STATE_ERROR, RTSP_ERR_TIMEOUT);
+            return RTSP_ERR_TIMEOUT;
+        }
+    }
+    
+    // 发送队列中的帧
+    if (ctx->state == RTSP_STATE_PLAYING) {
+        rtsp_send_queued_frames(ctx);
+        
+        // 定期发送RTCP Sender Report (每5秒一次)
+        if (now - ctx->last_rtcp_time >= 5000) {
+            LLOGD("触发RTCP SR发送: 距上次%u毫秒", now - ctx->last_rtcp_time);
+            int ret = rtsp_send_rtcp_sr(ctx);
+            if (ret == RTSP_OK) {
+                ctx->last_rtcp_time = now;
+            } else {
+                LLOGE("RTCP SR发送失败: %d", ret);
+            }
+        }
+    }
+    
+    return RTSP_OK;
+}
+
+/**
+ * 获取推流统计信息
+ */
+int rtsp_get_stats(rtsp_ctx_t *ctx, rtsp_stats_t *stats) {
+    if (!ctx || !stats) {
+        return RTSP_ERR_INVALID_PARAM;
+    }
+    
+    stats->bytes_sent = ctx->bytes_sent;
+    stats->video_frames_sent = ctx->video_frames_sent;
+    stats->rtp_packets_sent = ctx->rtp_packets_sent;
+    stats->connection_time = luat_mcu_ticks() - ctx->start_tick;
+    stats->last_video_timestamp = ctx->video_timestamp;
+    
+    return RTSP_OK;
+}
+
+/* ======================== 内部函数实现 ======================== */
+
+/**
+ * 解析RTSP URL
+ */
+static int rtsp_parse_url(rtsp_ctx_t *ctx, const char *url) {
+    if (strncmp(url, "rtsp://", 7) != 0) {
+        LLOGE("非法的RTSP URL格式");
+        return RTSP_ERR_INVALID_PARAM;
+    }
+    
+    const char *ptr = url + 7;
+    const char *host_end = strchr(ptr, ':');
+    const char *stream_start;
+    
+    // 解析主机名
+    if (!host_end) {
+        host_end = strchr(ptr, '/');
+        if (!host_end) {
+            host_end = ptr + strlen(ptr);
+        }
+    }
+    
+    int host_len = host_end - ptr;
+    ctx->host = (char *)luat_heap_malloc(host_len + 1);
+    if (!ctx->host) {
+        return RTSP_ERR_NO_MEMORY;
+    }
+    strncpy(ctx->host, ptr, host_len);
+    ctx->host[host_len] = '\0';
+    
+    // 解析端口
+    if (*host_end == ':') {
+        ctx->port = (uint16_t)atoi(host_end + 1);
+        stream_start = strchr(host_end + 1, '/');
+    } else {
+        ctx->port = RTSP_DEFAULT_PORT;
+        stream_start = host_end;
+    }
+    
+    // 解析流名
+    if (stream_start && *stream_start == '/') {
+        stream_start++;
+        int stream_len = strlen(stream_start);
+        ctx->stream = (char *)luat_heap_malloc(stream_len + 1);
+        if (!ctx->stream) {
+            return RTSP_ERR_NO_MEMORY;
+        }
+        strcpy(ctx->stream, stream_start);
+    } else {
+        ctx->stream = (char *)luat_heap_malloc(1);
+        if (!ctx->stream) {
+            return RTSP_ERR_NO_MEMORY;
+        }
+        ctx->stream[0] = '\0';
+    }
+    
+    LLOGD("RTSP URL解析: host=%s, port=%u, stream=%s", 
+          ctx->host, ctx->port, ctx->stream);
+    
+    return RTSP_OK;
+}
+
+/**
+ * 状态转移
+ */
+static void rtsp_set_state(rtsp_ctx_t *ctx, rtsp_state_t new_state, int error_code) {
+    if (ctx->state == new_state) {
+        return;
+    }
+    
+    rtsp_state_t old_state = ctx->state;
+    ctx->state = new_state;
+    ctx->last_activity_time = luat_mcu_ticks();
+    
+    LLOGD("RTSP状态转移: %d -> %d", (int)old_state, (int)new_state);
+    
+    // 调用回调函数
+    rtsp_state_callback_t callback = (rtsp_state_callback_t)ctx->user_data;
+    if (callback) {
+        callback(ctx, old_state, new_state, error_code);
+    }
+}
+
+/**
+ * 发送RTSP命令
+ */
+static int rtsp_send_command(rtsp_ctx_t *ctx, const char *method, 
+                             const char *resource, const char *extra_headers,
+                             const char *data, uint32_t data_len) {
+    if (!ctx->control_pcb) {
+        LLOGE("RTSP TCP连接未建立");
+        return RTSP_ERR_CONNECT_FAILED;
+    }
+    
+    // 构建RTSP请求行
+    int len = snprintf((char *)ctx->send_buf, ctx->send_buf_size,
+        "%s %s RTSP/1.0\r\n"
+        "CSeq: %u\r\n"
+        "User-Agent: LuatOS-RTSP/1.0\r\n",
+        method, resource, ctx->cseq++);
+    
+    if (len < 0 || len >= (int)ctx->send_buf_size) {
+        LLOGE("发送缓冲区溢出");
+        return RTSP_ERR_BUFFER_OVERFLOW;
+    }
+    
+    // 添加Session头(如果已建立会话)
+    if (ctx->session_id) {
+        int n = snprintf((char *)ctx->send_buf + len, ctx->send_buf_size - len,
+            "Session: %s\r\n", ctx->session_id);
+        if (n < 0 || len + n >= (int)ctx->send_buf_size) {
+            LLOGE("发送缓冲区溢出");
+            return RTSP_ERR_BUFFER_OVERFLOW;
+        }
+        len += n;
+    }
+    
+    // 添加额外头部
+    if (extra_headers) {
+        int n = strlen(extra_headers);
+        if (len + n >= (int)ctx->send_buf_size) {
+            LLOGE("发送缓冲区溢出");
+            return RTSP_ERR_BUFFER_OVERFLOW;
+        }
+        memcpy(ctx->send_buf + len, extra_headers, n);
+        len += n;
+    }
+    
+    // 添加空行分隔头部和body(仅当没有data时自动添加)
+    if (data_len == 0) {
+        if (len + 2 >= (int)ctx->send_buf_size) {
+            LLOGE("发送缓冲区溢出");
+            return RTSP_ERR_BUFFER_OVERFLOW;
+        }
+        memcpy(ctx->send_buf + len, "\r\n", 2);
+        len += 2;
+    }
+    
+    RTSP_LOGV("RTSP命令:\n%.*s", len, (char *)ctx->send_buf);
+    
+    // 发送TCP头部数据
+    err_t err = tcp_write(ctx->control_pcb, ctx->send_buf, len, TCP_WRITE_FLAG_COPY);
+    if (err != ERR_OK) {
+        LLOGE("TCP头部发送失败: %d", err);
+        return RTSP_ERR_NETWORK;
+    }
+    
+    // 发送body数据(如果有)
+    if (data && data_len > 0) {
+        // 需要发送空行 + body
+        err = tcp_write(ctx->control_pcb, (const void *)"\r\n", 2, TCP_WRITE_FLAG_COPY);
+        if (err != ERR_OK) {
+            LLOGE("TCP空行发送失败: %d", err);
+            return RTSP_ERR_NETWORK;
+        }
+        
+        err = tcp_write(ctx->control_pcb, (const void *)data, data_len, TCP_WRITE_FLAG_COPY);
+        if (err != ERR_OK) {
+            LLOGE("TCP数据体发送失败: %d", err);
+            return RTSP_ERR_NETWORK;
+        }
+    }
+    
+    tcp_output(ctx->control_pcb);
+    return RTSP_OK;
+}
+
+/**
+ * TCP连接回调
+ */
+static err_t rtsp_tcp_connect_callback(void *arg, struct tcp_pcb *pcb, err_t err) {
+    rtsp_ctx_t *ctx = (rtsp_ctx_t *)arg;
+    
+    if (err != ERR_OK) {
+        LLOGE("TCP连接失败: %d", err);
+        rtsp_set_state(ctx, RTSP_STATE_ERROR, RTSP_ERR_CONNECT_FAILED);
+        return ERR_ABRT;
+    }
+    
+    LLOGD("TCP连接成功");
+    rtsp_set_state(ctx, RTSP_STATE_OPTIONS, 0);
+    
+    // 设置TCP回调函数
+    tcp_recv(pcb, rtsp_tcp_recv_callback);
+    tcp_err(pcb, rtsp_tcp_error_callback);
+    tcp_arg(pcb, ctx);
+    
+    // 发送OPTIONS请求(使用具体的stream URI)
+    if (rtsp_send_command(ctx, "OPTIONS", ctx->url, NULL, NULL, 0) == RTSP_OK) {
+        rtsp_set_state(ctx, RTSP_STATE_OPTIONS, 0);
+    }
+    
+    return ERR_OK;
+}
+
+/**
+ * TCP接收回调
+ */
+static err_t rtsp_tcp_recv_callback(void *arg, struct tcp_pcb *pcb, 
+                                   struct pbuf *p, err_t err) {
+    rtsp_ctx_t *ctx = (rtsp_ctx_t *)arg;
+    
+    if (err != ERR_OK) {
+        LLOGE("TCP接收错误: %d", err);
+        if (p) pbuf_free(p);
+        return ERR_ABRT;
+    }
+    
+    if (!p) {
+        LLOGD("TCP连接已关闭");
+        rtsp_set_state(ctx, RTSP_STATE_IDLE, 0);
+        return ERR_OK;
+    }
+    
+    // 复制数据到接收缓冲区
+    if (ctx->recv_pos + p->tot_len > ctx->recv_buf_size) {
+        LLOGE("接收缓冲区溢出");
+        pbuf_free(p);
+        return ERR_ABRT;
+    }
+    
+    pbuf_copy_partial(p, ctx->recv_buf + ctx->recv_pos, p->tot_len, 0);
+    ctx->recv_pos += p->tot_len;
+    
+    // 更新活动时间
+    ctx->last_activity_time = luat_mcu_ticks();
+    
+    RTSP_LOGV("接收RTSP数据: %u字节", p->tot_len);
+    pbuf_free(p);
+    
+    // 处理RTSP响应
+    rtsp_handle_response(ctx);
+    tcp_recved(pcb, p->tot_len);
+    
+    return ERR_OK;
+}
+
+/**
+ * TCP错误回调
+ */
+static void rtsp_tcp_error_callback(void *arg, err_t err) {
+    rtsp_ctx_t *ctx = (rtsp_ctx_t *)arg;
+    LLOGE("TCP错误: %d", err);
+    rtsp_set_state(ctx, RTSP_STATE_ERROR, RTSP_ERR_NETWORK);
+}
+
+/**
+ * 处理RTSP响应
+ */
+static int rtsp_handle_response(rtsp_ctx_t *ctx) {
+    // 检查是否已收到完整的响应
+    char *resp_end = strstr((char *)ctx->recv_buf, "\r\n\r\n");
+    if (!resp_end) {
+        // 尚未接收完整响应
+        return RTSP_OK;
+    }
+    
+    // 打印完整的RTSP响应以便调试
+    int resp_len = (resp_end - (char *)ctx->recv_buf) + 4;
+    LLOGD("RTSP响应内容:\n%.*s", resp_len, (char *)ctx->recv_buf);
+    
+    // 解析响应行: RTSP/1.0 <status> <reason>
+    char *line_start = (char *)ctx->recv_buf;
+    char *line_end = strstr(line_start, "\r\n");
+    if (!line_end) {
+        LLOGE("无效的RTSP响应");
+        rtsp_set_state(ctx, RTSP_STATE_ERROR, RTSP_ERR_HANDSHAKE_FAILED);
+        return RTSP_ERR_HANDSHAKE_FAILED;
+    }
+    
+    *line_end = '\0';
+    int status_code = 0;
+    if (sscanf(line_start, "RTSP/1.0 %d", &status_code) != 1) {
+        LLOGE("RTSP响应行解析失败");
+        *line_end = '\r';
+        rtsp_set_state(ctx, RTSP_STATE_ERROR, RTSP_ERR_HANDSHAKE_FAILED);
+        return RTSP_ERR_HANDSHAKE_FAILED;
+    }
+    *line_end = '\r';
+    
+    LLOGD("RTSP响应: 状态码=%d", status_code);
+    
+    if (status_code != 200) {
+        LLOGE("RTSP服务器错误: %d", status_code);
+        rtsp_set_state(ctx, RTSP_STATE_ERROR, RTSP_ERR_HANDSHAKE_FAILED);
+        return RTSP_ERR_HANDSHAKE_FAILED;
+    }
+    
+    // 解析响应头
+    char *header_start = line_end + 2;
+    char *session_id = NULL;
+    
+    while (header_start < resp_end) {
+        line_end = strstr(header_start, "\r\n");
+        if (!line_end) break;
+        
+        *line_end = '\0';
+        
+        // 提取Session头
+        if (rtsp_strncasecmp(header_start, "Session:", 8) == 0) {
+            char *value = header_start + 8;
+            while (*value == ' ' || *value == '\t') value++;
+            
+            // Session ID可能包含超时参数 (e.g. "12345678;timeout=60")
+            char *semicolon = strchr(value, ';');
+            int len = semicolon ? (semicolon - value) : strlen(value);
+            
+            if (len > 0) {
+                session_id = (char *)luat_heap_malloc(len + 1);
+                if (session_id) {
+                    strncpy(session_id, value, len);
+                    session_id[len] = '\0';
+                    LLOGD("提取Session ID: %s", session_id);
+                }
+            }
+        }
+        
+        // 提取Transport头
+        if (rtsp_strncasecmp(header_start, "Transport:", 10) == 0) {
+            char *value = header_start + 10;
+            while (*value == ' ' || *value == '\t') value++;
+            
+            // 解析server_port参数
+            char *server_port = strstr(value, "server_port=");
+            if (server_port) {
+                server_port += 12;  // strlen("server_port=")
+                int rtp_port = 0, rtcp_port = 0;
+                if (sscanf(server_port, "%d-%d", &rtp_port, &rtcp_port) == 2) {
+                    ctx->remote_rtp_port = (uint16_t)rtp_port;
+                    ctx->remote_rtcp_port = (uint16_t)rtcp_port;
+                    LLOGD("服务器端口: RTP=%u, RTCP=%u", 
+                          ctx->remote_rtp_port, ctx->remote_rtcp_port);
+                }
+            }
+            
+            // 解析client_port参数(如果服务器返回)
+            char *client_port = strstr(value, "client_port=");
+            if (client_port) {
+                client_port += 12;  // strlen("client_port=")
+                int rtp_port = 0, rtcp_port = 0;
+                if (sscanf(client_port, "%d-%d", &rtp_port, &rtcp_port) == 2) {
+                    ctx->local_rtp_port = (uint16_t)rtp_port;
+                    ctx->local_rtcp_port = (uint16_t)rtcp_port;
+                    LLOGD("本地端口: RTP=%u, RTCP=%u", 
+                          ctx->local_rtp_port, ctx->local_rtcp_port);
+                }
+            }
+        }
+        
+        *line_end = '\r';
+        header_start = line_end + 2;
+    }
+    
+    // 保存Session ID
+    if (session_id && !ctx->session_id) {
+        ctx->session_id = session_id;
+    } else if (session_id) {
+        luat_heap_free(session_id);
+    }
+    
+    // 根据当前状态处理下一步
+    switch (ctx->state) {
+        case RTSP_STATE_OPTIONS:
+            // OPTIONS成功,发送ANNOUNCE
+            {
+                char sdp[2048];
+                int sdp_len = 0;
+                
+                // 如果有SPS/PPS,构建完整的SDP;否则使用基础SDP
+                if (ctx->sps_data && ctx->sps_len > 0 && ctx->pps_data && ctx->pps_len > 0) {
+                    // 计算profile-level-id (从SPS中提取)
+                    char profile_level_id[16] = {0};
+                    if (ctx->sps_len >= 4) {
+                        snprintf(profile_level_id, sizeof(profile_level_id), "%02X%02X%02X",
+                                ctx->sps_data[1], ctx->sps_data[2], ctx->sps_data[3]);
+                    } else {
+                        strcpy(profile_level_id, "42C01E");  // 默认Baseline Profile Level 3.0
+                    }
+                    
+                    // Base64编码SPS
+                    char sps_b64[512] = {0};
+                    int sps_b64_len = rtsp_base64_encode(ctx->sps_data, ctx->sps_len, 
+                                                         sps_b64, sizeof(sps_b64));
+                    
+                    // Base64编码PPS
+                    char pps_b64[512] = {0};
+                    int pps_b64_len = rtsp_base64_encode(ctx->pps_data, ctx->pps_len,
+                                                         pps_b64, sizeof(pps_b64));
+                    
+                    if (sps_b64_len > 0 && pps_b64_len > 0) {
+                        // 构建完整的SDP
+                        sdp_len = snprintf(sdp, sizeof(sdp),
+                            "v=0\r\n"
+                            "o=- 0 0 IN IP4 127.0.0.1\r\n"
+                            "s=LuatOS RTSP Stream\r\n"
+                            "c=IN IP4 0.0.0.0\r\n"
+                            "t=0 0\r\n"
+                            "m=video 0 RTP/AVP 96\r\n"
+                            "a=rtpmap:96 H264/90000\r\n"
+                            "a=fmtp:96 packetization-mode=1;profile-level-id=%s;sprop-parameter-sets=%s\r\n"
+                            "a=control:streamid=0\r\n",
+                            profile_level_id, (ctx->sprop_parameter_sets ? ctx->sprop_parameter_sets : "") );
+                        
+                        LLOGD("完整SDP生成: profile-level-id=%s", profile_level_id);
+                    }
+                }
+                
+                // 如果没有生成完整SDP,使用基础SDP
+                if (sdp_len <= 0) {
+                    sdp_len = snprintf(sdp, sizeof(sdp),
+                        "v=0\r\n"
+                        "o=- 0 0 IN IP4 127.0.0.1\r\n"
+                        "s=LuatOS RTSP Stream\r\n"
+                        "c=IN IP4 %s\r\n"
+                        "t=0 0\r\n"
+                        "m=video 0 RTP/AVP 96\r\n"
+                        "a=rtpmap:96 H264/90000\r\n"
+                        "a=fmtp:96 profile-level-id=1; sprop-parameter-sets=Z0IAKeNQCgC2QgAAB9AAAOpg0YAAmJgAL/L3gAE=,aM48gA==\r\n"
+                        "a=control:streamid=0\r\n", ctx->host);
+                    
+                    LLOGD("使用基础SDP(无SPS/PPS)");
+                }
+                
+                if (sdp_len > 0 && sdp_len < (int)sizeof(sdp)) {
+                    char headers[512] = {0};
+                    snprintf(headers, sizeof(headers),
+                        "Content-Type: application/sdp\r\n"
+                        "Content-Length: %d\r\n", sdp_len);
+                    
+                    if (rtsp_send_command(ctx, "ANNOUNCE", ctx->url, headers, sdp, sdp_len) == RTSP_OK) {
+                        rtsp_set_state(ctx, RTSP_STATE_DESCRIBE, 0);
+                    }
+                }
+            }
+            break;
+            
+        case RTSP_STATE_DESCRIBE:
+            // ANNOUNCE/DESCRIBE成功,发送SETUP
+            {
+                char setup_headers[512] = {0};
+                snprintf(setup_headers, sizeof(setup_headers),
+                    "Transport: RTP/AVP/UDP;unicast;client_port=%u-%u;mode=record\r\n",
+                    ctx->local_rtp_port ? ctx->local_rtp_port : 19030,
+                    ctx->local_rtcp_port ? ctx->local_rtcp_port : 19031);
+                
+                char resource[512] = {0};
+                snprintf(resource, sizeof(resource), "%s/streamid=0", ctx->url);
+                
+                if (rtsp_send_command(ctx, "SETUP", resource, setup_headers, NULL, 0) == RTSP_OK) {
+                    rtsp_set_state(ctx, RTSP_STATE_SETUP, 0);
+                }
+            }
+            break;
+            
+        case RTSP_STATE_SETUP:
+            // SETUP成功,发送PLAY
+            if (rtsp_send_command(ctx, "RECORD", ctx->url, NULL, NULL, 0) == RTSP_OK) {
+                rtsp_set_state(ctx, RTSP_STATE_PLAY, 0);
+            }
+            break;
+            
+        case RTSP_STATE_PLAY:
+            // RECORD/PLAY成功,进入推流状态
+            rtsp_set_state(ctx, RTSP_STATE_PLAYING, 0);
+            LLOGD("RTSP握手完成,开始推流");
+            
+            // 立即发送一次RTCP Sender Report
+            LLOGD("RTSP就绪,发送初始RTCP SR");
+            rtsp_send_rtcp_sr(ctx);
+            ctx->last_rtcp_time = luat_mcu_ticks();
+            
+            // 通知摄像头开始采集
+            extern int luat_camera_capture(int id, uint8_t quality, const char *path);
+            luat_camera_capture(0, 80, "rtsp");
+            break;
+            
+        default:
+            break;
+    }
+    
+    // 清空接收缓冲区
+    resp_len = (resp_end - (char *)ctx->recv_buf) + 4;
+    if (ctx->recv_pos > (uint32_t)resp_len) {
+        memmove(ctx->recv_buf, ctx->recv_buf + resp_len, ctx->recv_pos - resp_len);
+        ctx->recv_pos -= resp_len;
+    } else {
+        ctx->recv_pos = 0;
+    }
+    
+    return RTSP_OK;
+}
+
+/**
+ * 发送RTP包
+ */
+static int rtsp_send_rtp_packet(rtsp_ctx_t *ctx, const uint8_t *data, 
+                                uint32_t len, uint32_t timestamp, uint8_t marker) {
+    if (!ctx->rtp_pcb) {
+        LLOGE("RTP UDP连接未建立");
+        return RTSP_ERR_CONNECT_FAILED;
+    }
+    
+    // 构建RTP头
+    uint8_t rtp_header[RTP_HEADER_SIZE];
+    uint32_t rtp_ts = (timestamp * RTP_TIMESTAMP_BASE) / 1000;
+    
+    // 计算与上个包的时间戳差值(用于诊断时间戳跳跃)
+    static uint32_t last_rtp_ts = 0;
+    int32_t ts_delta = (int32_t)(rtp_ts - last_rtp_ts);
+    
+    RTSP_LOGV("RTP包: seq=%u, ts=%u, ts_delta=%d, marker=%u, len=%u, timestamp_ms=%u",
+              ctx->rtp_sequence, rtp_ts, ts_delta, marker, len, timestamp);
+    
+    // 如果时间戳跳跃异常大(可能导致抖动),记录警告
+    if (last_rtp_ts > 0 && (ts_delta < -90000 || ts_delta > 270000)) {
+        LLOGW("时间戳异常跳跃: seq=%u, last_ts=%u, curr_ts=%u, delta=%d (应在-90000~270000范围内,对应帧率25~120fps)",
+              ctx->rtp_sequence, last_rtp_ts, rtp_ts, ts_delta);
+    }
+    last_rtp_ts = rtp_ts;
+    
+    // V=2, P=0, X=0, CC=0
+    rtp_header[0] = (2 << 6) | 0;
+    // M=marker, PT=96(H.264)
+    rtp_header[1] = (marker << 7) | RTP_PAYLOAD_TYPE_H264;
+    // Sequence number (big-endian)
+    rtp_header[2] = (ctx->rtp_sequence >> 8) & 0xFF;
+    rtp_header[3] = ctx->rtp_sequence & 0xFF;
+    // Timestamp (big-endian)
+    rtp_header[4] = (rtp_ts >> 24) & 0xFF;
+    rtp_header[5] = (rtp_ts >> 16) & 0xFF;
+    rtp_header[6] = (rtp_ts >> 8) & 0xFF;
+    rtp_header[7] = rtp_ts & 0xFF;
+    // SSRC (big-endian)
+    rtp_header[8] = (ctx->rtp_ssrc >> 24) & 0xFF;
+    rtp_header[9] = (ctx->rtp_ssrc >> 16) & 0xFF;
+    rtp_header[10] = (ctx->rtp_ssrc >> 8) & 0xFF;
+    rtp_header[11] = ctx->rtp_ssrc & 0xFF;
+    
+    ctx->rtp_sequence++;
+    
+    // 构建完整RTP包
+    if (RTP_HEADER_SIZE + len > ctx->rtp_buf_size) {
+        LLOGE("RTP缓冲区溢出");
+        return RTSP_ERR_BUFFER_OVERFLOW;
+    }
+    
+    memcpy(ctx->rtp_buf, rtp_header, RTP_HEADER_SIZE);
+    memcpy(ctx->rtp_buf + RTP_HEADER_SIZE, data, len);
+    
+    // 发送UDP包
+    struct pbuf *pb = pbuf_alloc(PBUF_TRANSPORT, RTP_HEADER_SIZE + len, PBUF_RAM);
+    if (!pb) {
+        LLOGE("UDP数据包分配失败");
+        return RTSP_ERR_NO_MEMORY;
+    }
+    
+    memcpy(pb->payload, ctx->rtp_buf, RTP_HEADER_SIZE + len);
+    
+    err_t err = udp_sendto(ctx->rtp_pcb, pb, &ctx->remote_ip, ctx->remote_rtp_port);
+    pbuf_free(pb);
+    
+    if (err != ERR_OK) {
+        LLOGE("RTP UDP发送失败: err=%d, remote_ip=%s, remote_rtp_port=%u, len=%u", 
+              err, ipaddr_ntoa(&ctx->remote_ip), ctx->remote_rtp_port, RTP_HEADER_SIZE + len);
+        return RTSP_ERR_NETWORK;
+    }
+    
+    ctx->bytes_sent += RTP_HEADER_SIZE + len;
+    ctx->rtp_packets_sent++;
+    
+    // RTSP_LOGV("RTP包已发送: %u字节, 序列号: %u", RTP_HEADER_SIZE + len, ctx->rtp_sequence - 1);
+    
+    return RTSP_OK;
+}
+
+/**
+ * 构建RTP载荷 - 支持多NALU解析和FU-A分片
+ */
+static int rtsp_build_rtp_payload(rtsp_ctx_t *ctx, const uint8_t *frame_data, 
+                                  uint32_t frame_len, uint32_t timestamp) {
+    if (!frame_data || frame_len == 0) {
+        return RTSP_ERR_INVALID_PARAM;
+    }
+    
+    // 如果传入的 timestamp 为 0,使用上下文中的时间戳
+    if (timestamp == 0) {
+        timestamp = ctx->video_timestamp;
+    }
+    const uint8_t *ptr = frame_data;
+    uint32_t remaining = frame_len;
+    int nalu_count = 0;
+    int total_sent = 0;
+    
+    RTSP_LOGV("处理帧数据: %u字节", frame_len);
+    
+    // 解析并发送所有NALU
+    while (remaining > 0) {
+        // 查找当前NALU的起始码
+        uint32_t start_code_len = 0;
+        const uint8_t *nalu_start = rtsp_find_next_nalu(ptr, remaining, &start_code_len);
+        
+        if (!nalu_start) {
+            // 没有找到起始码,可能整个数据就是一个NALU
+            if (nalu_count == 0 && remaining > 0) {
+                nalu_start = ptr;
+                start_code_len = 0;
+            } else {
+                break;
+            }
+        }
+        
+        // 跳过起始码
+        const uint8_t *nalu_data = nalu_start + start_code_len;
+        uint32_t nalu_available = remaining - (nalu_start - ptr) - start_code_len;
+        
+        if (nalu_available == 0) {
+            break;
+        }
+        
+        // 查找下一个NALU起始码,确定当前NALU长度
+        uint32_t next_start_code_len = 0;
+        const uint8_t *next_nalu = rtsp_find_next_nalu(nalu_data + 1, nalu_available - 1, &next_start_code_len);
+        
+        uint32_t nalu_len;
+        if (next_nalu) {
+            nalu_len = next_nalu - nalu_data;
+        } else {
+            nalu_len = nalu_available;
+        }
+        
+        if (nalu_len > 0) {
+            uint8_t nalu_type = nalu_data[0] & 0x1F;
+            RTSP_LOGV("发现NALU: 类型=%u, 长度=%u", nalu_type, nalu_len);
+            
+            // 自动保存SPS/PPS
+            if (nalu_type == NALU_TYPE_SPS && (!ctx->sps_data || ctx->sps_len != nalu_len)) {
+                if (ctx->sps_data) {
+                    luat_heap_free(ctx->sps_data);
+                }
+                ctx->sps_data = (uint8_t *)luat_heap_malloc(nalu_len);
+                if (ctx->sps_data) {
+                    memcpy(ctx->sps_data, nalu_data, nalu_len);
+                    ctx->sps_len = nalu_len;
+                    LLOGD("自动提取SPS: %u字节", nalu_len);
+                    // 详细解析SPS
+                    rtsp_parse_sps_detail(nalu_data, nalu_len);
+                    // 打印sprop-sps(Base64)
+                    char b64_sps[256];
+                    if (rtsp_base64_encode(nalu_data, nalu_len, b64_sps, sizeof(b64_sps)) >= 0) {
+                        LLOGD("sprop-sps=%s", b64_sps);
+                        // 如果已有PPS,合成sprop-parameter-sets
+                        if (ctx->pps_data && ctx->pps_len > 0) {
+                            char b64_pps[256];
+                            if (rtsp_base64_encode(ctx->pps_data, ctx->pps_len, b64_pps, sizeof(b64_pps)) >= 0) {
+                                size_t total_len = strlen(b64_sps) + 1 + strlen(b64_pps) + 1;
+                                if (ctx->sprop_parameter_sets) {
+                                    luat_heap_free(ctx->sprop_parameter_sets);
+                                    ctx->sprop_parameter_sets = NULL;
+                                }
+                                ctx->sprop_parameter_sets = (char*)luat_heap_malloc(total_len);
+                                if (ctx->sprop_parameter_sets) {
+                                    snprintf(ctx->sprop_parameter_sets, total_len, "%s,%s", b64_sps, b64_pps);
+                                    LLOGD("sprop-parameter-sets=%s", ctx->sprop_parameter_sets);
+                                } else {
+                                    LLOGW("分配sprop-parameter-sets失败");
+                                }
+                            }
+                        }
+                    } else {
+                        LLOGW("sprop-sps Base64编码失败");
+                    }
+                }
+            } else if (nalu_type == NALU_TYPE_PPS && (!ctx->pps_data || ctx->pps_len != nalu_len)) {
+                if (ctx->pps_data) {
+                    luat_heap_free(ctx->pps_data);
+                }
+                ctx->pps_data = (uint8_t *)luat_heap_malloc(nalu_len);
+                if (ctx->pps_data) {
+                    memcpy(ctx->pps_data, nalu_data, nalu_len);
+                    ctx->pps_len = nalu_len;
+                    LLOGD("自动提取PPS: %u字节", nalu_len);
+                    // 详细解析PPS
+                    rtsp_parse_pps_detail(nalu_data, nalu_len);
+                    // 打印sprop-pps(Base64)
+                    char b64_pps[256];
+                    if (rtsp_base64_encode(nalu_data, nalu_len, b64_pps, sizeof(b64_pps)) >= 0) {
+                        LLOGD("sprop-pps=%s", b64_pps);
+                        // 如果已有SPS,合成sprop-parameter-sets
+                        if (ctx->sps_data && ctx->sps_len > 0) {
+                            char b64_sps[256];
+                            if (rtsp_base64_encode(ctx->sps_data, ctx->sps_len, b64_sps, sizeof(b64_sps)) >= 0) {
+                                size_t total_len = strlen(b64_sps) + 1 + strlen(b64_pps) + 1;
+                                if (ctx->sprop_parameter_sets) {
+                                    luat_heap_free(ctx->sprop_parameter_sets);
+                                    ctx->sprop_parameter_sets = NULL;
+                                }
+                                ctx->sprop_parameter_sets = (char*)luat_heap_malloc(total_len);
+                                if (ctx->sprop_parameter_sets) {
+                                    snprintf(ctx->sprop_parameter_sets, total_len, "%s,%s", b64_sps, b64_pps);
+                                    LLOGD("sprop-parameter-sets=%s", ctx->sprop_parameter_sets);
+                                } else {
+                                    LLOGW("分配sprop-parameter-sets失败");
+                                }
+                            }
+                        }
+                    } else {
+                        LLOGW("sprop-pps Base64编码失败");
+                    }
+                }
+            }
+            
+            // 发送NALU(非SPS/PPS或在推流状态下也发送SPS/PPS)
+            if (nalu_type != NALU_TYPE_AUD) {  // 跳过AUD
+                // 判断是否为最后一个NALU
+                uint8_t is_last = (next_nalu == NULL) ? 1 : 0;
+                
+                int ret = rtsp_send_nalu(ctx, nalu_data, nalu_len, timestamp, is_last);
+                if (ret >= 0) {
+                    total_sent += ret;
+                    nalu_count++;
+                } else {
+                    LLOGE("NALU发送失败: %d", ret);
+                    return ret;
+                }
+            }
+        }
+        
+        // 移动到下一个NALU
+        if (next_nalu) {
+            uint32_t consumed = next_nalu - ptr;
+            ptr = next_nalu;
+            remaining -= consumed;
+        } else {
+            break;
+        }
+    }
+    
+    if (nalu_count > 0) {
+        ctx->video_frames_sent++;
+        ctx->video_timestamp = timestamp;
+        RTSP_LOGV("帧发送完成: %d个NALU, 总计%d字节", nalu_count, total_sent);
+    }
+    
+    return total_sent > 0 ? total_sent : RTSP_ERR_FAILED;
+}
+
+/**
+ * 发送队列中的帧
+ */
+static int rtsp_send_queued_frames(rtsp_ctx_t *ctx) {
+    int sent_count = 0;
+    
+    while (ctx->frame_head && sent_count < 5) {  // 每次轮询最多发送5帧
+        struct rtsp_frame_node *node = ctx->frame_head;
+        
+        if (rtsp_build_rtp_payload(ctx, node->data, node->len, node->timestamp) == RTSP_OK) {
+            ctx->frame_head = node->next;
+            ctx->frame_queue_bytes -= node->len;
+            
+            luat_heap_free(node->data);
+            luat_heap_free(node);
+            
+            sent_count++;
+        } else {
+            break;  // 发送失败,暂停发送
+        }
+    }
+    
+    if (!ctx->frame_head) {
+        ctx->frame_tail = NULL;
+    }
+    
+    //RTSP_LOGV("队列帧发送: %d帧, 剩余队列: %u字节", sent_count, ctx->frame_queue_bytes);
+    
+    return RTSP_OK;
+}
+
+/**
+ * 清空帧队列
+ */
+static void rtsp_clear_frame_queue(rtsp_ctx_t *ctx) {
+    struct rtsp_frame_node *node = ctx->frame_head;
+    
+    while (node) {
+        struct rtsp_frame_node *next = node->next;
+        if (node->data) {
+            luat_heap_free(node->data);
+        }
+        luat_heap_free(node);
+        node = next;
+    }
+    
+    ctx->frame_head = NULL;
+    ctx->frame_tail = NULL;
+    ctx->frame_queue_bytes = 0;
+}
+
+/**
+ * 大小写不敏感字符串比较(用于HTTP头解析)
+ */
+static int rtsp_strncasecmp(const char *a, const char *b, size_t n) {
+    for (size_t i = 0; i < n; i++) {
+        int ca = tolower((unsigned char)a[i]);
+        int cb = tolower((unsigned char)b[i]);
+        if (ca != cb || ca == 0) {
+            return ca - cb;
+        }
+    }
+    return 0;
+}
+
+/**
+ * Base64编码函数
+ */
+static int rtsp_base64_encode(const uint8_t *src, uint32_t src_len, char *dst, uint32_t dst_len) {
+    static const char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+    uint32_t i = 0, j = 0;
+    
+    // 计算所需的输出长度
+    uint32_t required_len = ((src_len + 2) / 3) * 4;
+    if (required_len >= dst_len) {
+        return -1;  // 缓冲区不足
+    }
+    
+    while (i < src_len) {
+        uint32_t octet_a = i < src_len ? src[i++] : 0;
+        uint32_t octet_b = i < src_len ? src[i++] : 0;
+        uint32_t octet_c = i < src_len ? src[i++] : 0;
+        
+        uint32_t triple = (octet_a << 16) + (octet_b << 8) + octet_c;
+        
+        dst[j++] = base64_table[(triple >> 18) & 0x3F];
+        dst[j++] = base64_table[(triple >> 12) & 0x3F];
+        dst[j++] = base64_table[(triple >> 6) & 0x3F];
+        dst[j++] = base64_table[triple & 0x3F];
+    }
+    
+    // 添加填充
+    uint32_t mod = src_len % 3;
+    if (mod == 1) {
+        dst[j - 1] = '=';
+        dst[j - 2] = '=';
+    } else if (mod == 2) {
+        dst[j - 1] = '=';
+    }
+    
+    dst[j] = '\0';
+    return j;
+}
+
+/**
+ * 查找下一个NALU起始码
+ * @return 起始码的位置,如果未找到返回NULL
+ */
+static const uint8_t* rtsp_find_next_nalu(const uint8_t *data, uint32_t len, uint32_t *start_code_len) {
+    if (len < 3) {
+        return NULL;
+    }
+    
+    for (uint32_t i = 0; i < len - 2; i++) {
+        // 查找0x000001或0x00000001
+        if (data[i] == 0 && data[i+1] == 0) {
+            if (data[i+2] == 1) {
+                *start_code_len = 3;
+                return &data[i];
+            } else if (i < len - 3 && data[i+2] == 0 && data[i+3] == 1) {
+                *start_code_len = 4;
+                return &data[i];
+            }
+        }
+    }
+    
+    return NULL;
+}
+
+/**
+ * 发送单个NALU(支持FU-A分片)
+ */
+static int rtsp_send_nalu(rtsp_ctx_t *ctx, const uint8_t *nalu_data, 
+                          uint32_t nalu_len, uint32_t timestamp, uint8_t is_last_nalu) {
+    if (!nalu_data || nalu_len == 0) {
+        return RTSP_ERR_INVALID_PARAM;
+    }
+    
+    uint8_t nalu_header = nalu_data[0];
+    uint8_t nalu_type = nalu_header & 0x1F;
+    uint8_t nri = nalu_header & 0x60;
+    
+    // 如果NALU小于最大载荷,直接发送
+    if (nalu_len <= RTP_MAX_PACKET_SIZE) {
+        int ret = rtsp_send_rtp_packet(ctx, nalu_data, nalu_len, timestamp, is_last_nalu);
+        return (ret == RTSP_OK) ? (int)nalu_len : ret;
+    }
+    
+    // 需要FU-A分片
+    RTSP_LOGV("NALU过大(%u字节),使用FU-A分片", nalu_len);
+    
+    uint32_t nalu_payload_len = nalu_len - 1;  // 去掉NALU头
+    const uint8_t *nalu_payload = nalu_data + 1;
+    uint32_t fragment_size = RTP_MAX_PACKET_SIZE - 2;  // FU indicator + FU header
+    uint32_t offset = 0;
+    uint32_t fragment_count = 0;
+    int total_sent = 0;
+    
+    while (offset < nalu_payload_len) {
+        uint32_t this_fragment_size = (nalu_payload_len - offset > fragment_size) ? 
+                                      fragment_size : (nalu_payload_len - offset);
+        
+        uint8_t fu_packet[RTP_MAX_PACKET_SIZE];
+        
+        // FU indicator: F(1bit) + NRI(2bits) + Type(5bits=28)
+        fu_packet[0] = nri | FU_A_TYPE;
+        
+        // FU header: S(1bit) + E(1bit) + R(1bit) + Type(5bits)
+        uint8_t fu_header = nalu_type;
+        if (offset == 0) {
+            fu_header |= 0x80;  // S bit (start)
+        }
+        if (offset + this_fragment_size >= nalu_payload_len) {
+            fu_header |= 0x40;  // E bit (end)
+        }
+        fu_packet[1] = fu_header;
+        
+        // 复制分片数据
+        memcpy(fu_packet + 2, nalu_payload + offset, this_fragment_size);
+        
+        // 发送分片,只有最后一个分片且是最后一个NALU时才设置marker
+        uint8_t marker = ((fu_header & 0x40) && is_last_nalu) ? 1 : 0;
+        
+        int ret = rtsp_send_rtp_packet(ctx, fu_packet, 2 + this_fragment_size, timestamp, marker);
+        if (ret != RTSP_OK) {
+            LLOGE("FU-A分片发送失败: %d", ret);
+            return ret;
+        }
+        
+        offset += this_fragment_size;
+        fragment_count++;
+        total_sent += (2 + this_fragment_size);
+    }
+    
+    RTSP_LOGV("FU-A分片完成: %u个分片", fragment_count);
+    return total_sent;
+}
+
+/**
+ * 转换系统时间到NTP时间戳
+ * NTP时间戳: 64位, 高32位是秒数(从1900年1月1日开始), 低32位是小数秒
+ */
+static void rtsp_get_ntp_timestamp(uint32_t *ntp_sec, uint32_t *ntp_frac) {
+    // 使用 SNTP 获取准确的 NTP 时间戳(毫秒单位,从1970年开始)
+    extern uint64_t luat_sntp_time64_ms();
+    uint64_t unix_ms = luat_sntp_time64_ms();
+    
+    // NTP时间戳从1900年开始,1970年1月1日对应的秒数为2208988800秒
+    const uint64_t NTP_OFFSET_SEC = 2208988800UL;
+    
+    // 将UNIX时间(毫秒)转换为NTP时间
+    // 1. UNIX秒数 = unix_ms / 1000
+    // 2. NTP秒数 = UNIX秒数 + NTP_OFFSET
+    uint64_t unix_sec = unix_ms / 1000;
+    uint32_t unix_ms_frac = (uint32_t)(unix_ms % 1000);
+    
+    // NTP秒数
+    *ntp_sec = (uint32_t)(unix_sec + NTP_OFFSET_SEC);
+    
+    // NTP小数部分: 将毫秒转换为 NTP 小数格式 (2^32 * ms / 1000)
+    *ntp_frac = (unix_ms_frac * 4294967296ULL) / 1000;
+    
+    RTSP_LOGV("NTP时间戳: unix_ms=%llu, unix_sec=%llu, ntp_sec=%u, ntp_frac=%u", 
+              unix_ms, unix_sec, *ntp_sec, *ntp_frac);
+}
+
+/**
+ * 发送RTCP Sender Report
+ * RTCP SR格式 (RFC 3550):
+ * 0                   1                   2                   3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |V=2|P|    RC   |   PT=SR=200   |             length            |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                         SSRC of sender                        |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |              NTP timestamp, most significant word             |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |             NTP timestamp, least significant word             |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                         RTP timestamp                         |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                     sender's packet count                     |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                      sender's octet count                     |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+static int rtsp_send_rtcp_sr(rtsp_ctx_t *ctx) {
+    if (!ctx->rtcp_pcb || ctx->remote_rtcp_port == 0) {
+        LLOGD("RTCP未配置,跳过SR发送: rtcp_pcb=%p, remote_rtcp_port=%u", 
+              ctx->rtcp_pcb, ctx->remote_rtcp_port);
+        return RTSP_OK;
+    }
+    
+    LLOGD("准备发送RTCP SR: packets=%u, bytes=%u, remote_ip=%s, remote_rtcp_port=%u",
+          ctx->rtp_packets_sent, ctx->bytes_sent, 
+          ipaddr_ntoa(&ctx->remote_ip), ctx->remote_rtcp_port);
+    
+    uint8_t rtcp_packet[28];  // RTCP SR固定头部28字节(不含接收报告块)
+    uint32_t ntp_sec, ntp_frac;
+    
+    // 获取NTP时间戳
+    rtsp_get_ntp_timestamp(&ntp_sec, &ntp_frac);
+    
+    // 计算RTP时间戳
+    uint32_t rtp_ts = (ctx->video_timestamp * RTP_TIMESTAMP_BASE) / 1000;
+    
+    // 构建RTCP SR包
+    // V=2, P=0, RC=0 (无接收报告块)
+    rtcp_packet[0] = (2 << 6) | 0;
+    // PT=200 (SR)
+    rtcp_packet[1] = 200;
+    // Length = 6 (28字节 / 4 - 1)
+    rtcp_packet[2] = 0;
+    rtcp_packet[3] = 6;
+    
+    // SSRC (big-endian)
+    rtcp_packet[4] = (ctx->rtp_ssrc >> 24) & 0xFF;
+    rtcp_packet[5] = (ctx->rtp_ssrc >> 16) & 0xFF;
+    rtcp_packet[6] = (ctx->rtp_ssrc >> 8) & 0xFF;
+    rtcp_packet[7] = ctx->rtp_ssrc & 0xFF;
+    
+    // NTP timestamp - 高32位 (big-endian)
+    rtcp_packet[8] = (ntp_sec >> 24) & 0xFF;
+    rtcp_packet[9] = (ntp_sec >> 16) & 0xFF;
+    rtcp_packet[10] = (ntp_sec >> 8) & 0xFF;
+    rtcp_packet[11] = ntp_sec & 0xFF;
+    
+    // NTP timestamp - 低32位 (big-endian)
+    rtcp_packet[12] = (ntp_frac >> 24) & 0xFF;
+    rtcp_packet[13] = (ntp_frac >> 16) & 0xFF;
+    rtcp_packet[14] = (ntp_frac >> 8) & 0xFF;
+    rtcp_packet[15] = ntp_frac & 0xFF;
+    
+    // RTP timestamp (big-endian)
+    rtcp_packet[16] = (rtp_ts >> 24) & 0xFF;
+    rtcp_packet[17] = (rtp_ts >> 16) & 0xFF;
+    rtcp_packet[18] = (rtp_ts >> 8) & 0xFF;
+    rtcp_packet[19] = rtp_ts & 0xFF;
+    
+    // Sender's packet count (big-endian)
+    rtcp_packet[20] = (ctx->rtp_packets_sent >> 24) & 0xFF;
+    rtcp_packet[21] = (ctx->rtp_packets_sent >> 16) & 0xFF;
+    rtcp_packet[22] = (ctx->rtp_packets_sent >> 8) & 0xFF;
+    rtcp_packet[23] = ctx->rtp_packets_sent & 0xFF;
+    
+    // Sender's octet count (big-endian)
+    rtcp_packet[24] = (ctx->bytes_sent >> 24) & 0xFF;
+    rtcp_packet[25] = (ctx->bytes_sent >> 16) & 0xFF;
+    rtcp_packet[26] = (ctx->bytes_sent >> 8) & 0xFF;
+    rtcp_packet[27] = ctx->bytes_sent & 0xFF;
+    
+    // 发送RTCP SR包
+    struct pbuf *pb = pbuf_alloc(PBUF_TRANSPORT, 28, PBUF_RAM);
+    if (!pb) {
+        LLOGE("RTCP SR包分配失败");
+        return RTSP_ERR_NO_MEMORY;
+    }
+    
+    memcpy(pb->payload, rtcp_packet, 28);
+    
+    LLOGD("执行udp_sendto: rtcp_pcb=%p, remote_ip=%s, remote_rtcp_port=%u, len=28",
+          ctx->rtcp_pcb, ipaddr_ntoa(&ctx->remote_ip), ctx->remote_rtcp_port);
+    
+    err_t err = udp_sendto(ctx->rtcp_pcb, pb, &ctx->remote_ip, ctx->remote_rtcp_port);
+    
+    LLOGD("udp_sendto执行完毕: err=%d (%s)", err, 
+          err == ERR_OK ? "成功" : (err == ERR_MEM ? "内存不足" : 
+          (err == ERR_RTE ? "路由错误" : "其他错误")));
+    
+    pbuf_free(pb);
+    
+    if (err != ERR_OK) {
+        LLOGE("RTCP SR发送失败: err=%d, remote_ip=%s, remote_rtcp_port=%u", 
+              err, ipaddr_ntoa(&ctx->remote_ip), ctx->remote_rtcp_port);
+        return RTSP_ERR_NETWORK;
+    }
+    
+    LLOGD("RTCP SR已成功发送: SSRC=0x%08X, packets=%u, bytes=%u, NTP=%u.%u, RTP_TS=%u", 
+          ctx->rtp_ssrc, ctx->rtp_packets_sent, ctx->bytes_sent, ntp_sec, ntp_frac, rtp_ts);
+    
+    return RTSP_OK;
+}
+
+/**
+ * 连接到RTSP服务器
+ */
+int rtsp_connect(rtsp_ctx_t *ctx) {
+    if (!ctx || !ctx->host || ctx->port == 0) {
+        LLOGE("RTSP上下文无效或URL未设置");
+        return RTSP_ERR_INVALID_PARAM;
+    }
+    
+    if (ctx->state != RTSP_STATE_IDLE) {
+        LLOGE("RTSP已处于连接状态");
+        return RTSP_ERR_FAILED;
+    }
+    
+    // 创建TCP控制连接
+    ctx->control_pcb = tcp_new();
+    if (!ctx->control_pcb) {
+        LLOGE("TCP控制块创建失败");
+        return RTSP_ERR_NO_MEMORY;
+    }
+    
+    // 创建RTP UDP连接
+    ctx->rtp_pcb = udp_new();
+    if (!ctx->rtp_pcb) {
+        LLOGE("RTP UDP块创建失败");
+        tcp_abort(ctx->control_pcb);
+        ctx->control_pcb = NULL;
+        return RTSP_ERR_NO_MEMORY;
+    }
+    
+    // 创建RTCP UDP连接
+    ctx->rtcp_pcb = udp_new();
+    if (!ctx->rtcp_pcb) {
+        LLOGE("RTCP UDP块创建失败");
+        tcp_abort(ctx->control_pcb);
+        udp_remove(ctx->rtp_pcb);
+        ctx->control_pcb = NULL;
+        ctx->rtp_pcb = NULL;
+        return RTSP_ERR_NO_MEMORY;
+    }
+    
+    rtsp_set_state(ctx, RTSP_STATE_CONNECTING, 0);
+    
+    // 发起TCP连接
+    ip_addr_t remote_ip;
+    if (ipaddr_aton(ctx->host, &remote_ip) == 0) {
+        // TODO: DNS解析
+        LLOGE("IP地址转换失败: %s", ctx->host);
+        tcp_abort(ctx->control_pcb);
+        udp_remove(ctx->rtp_pcb);
+        udp_remove(ctx->rtcp_pcb);
+        ctx->control_pcb = NULL;
+        ctx->rtp_pcb = NULL;
+        ctx->rtcp_pcb = NULL;
+        rtsp_set_state(ctx, RTSP_STATE_ERROR, RTSP_ERR_CONNECT_FAILED);
+        return RTSP_ERR_CONNECT_FAILED;
+    }
+    
+    // 保存远端IP地址
+    ip_addr_copy(ctx->remote_ip, remote_ip);
+    LLOGD("解析服务器地址: %s -> %s", ctx->host, ipaddr_ntoa(&ctx->remote_ip));
+    
+    tcp_arg(ctx->control_pcb, (void *)ctx);
+    tcp_connect(ctx->control_pcb, &remote_ip, ctx->port, rtsp_tcp_connect_callback);
+    
+    LLOGD("RTSP连接请求已发送: %s:%u", ctx->host, ctx->port);
+    return RTSP_OK;
+}
+
+/**
+ * 断开RTSP连接
+ */
+int rtsp_disconnect(rtsp_ctx_t *ctx) {
+    if (!ctx) {
+        return RTSP_ERR_INVALID_PARAM;
+    }
+    
+    rtsp_set_state(ctx, RTSP_STATE_DISCONNECTING, 0);
+    
+    // 关闭TCP连接
+    if (ctx->control_pcb) {
+        tcp_close(ctx->control_pcb);
+        ctx->control_pcb = NULL;
+    }
+    
+    // 关闭UDP连接
+    if (ctx->rtp_pcb) {
+        udp_remove(ctx->rtp_pcb);
+        ctx->rtp_pcb = NULL;
+    }
+    if (ctx->rtcp_pcb) {
+        udp_remove(ctx->rtcp_pcb);
+        ctx->rtcp_pcb = NULL;
+    }
+    
+    rtsp_clear_frame_queue(ctx);
+    rtsp_set_state(ctx, RTSP_STATE_IDLE, 0);
+    
+    LLOGD("RTSP连接已断开");
+    return RTSP_OK;
+}
+
+/**
+ * 详细解析SPS数据
+ */
+static void rtsp_parse_sps_detail(const uint8_t *sps_data, uint32_t sps_len) {
+    if (!sps_data || sps_len < 4) {
+        LLOGE("SPS数据无效");
+        return;
+    }
+    
+    LLOGD("========== SPS详细信息 ==========");
+    
+    // NALU头 (1字节)
+    uint8_t nalu_header = sps_data[0];
+    uint8_t forbidden_zero = (nalu_header >> 7) & 0x01;
+    uint8_t nal_ref_idc = (nalu_header >> 5) & 0x03;
+    uint8_t nal_unit_type = nalu_header & 0x1F;
+    
+    LLOGD("NALU Header: 0x%02X", nalu_header);
+    LLOGD("  forbidden_zero_bit: %u", forbidden_zero);
+    LLOGD("  nal_ref_idc: %u", nal_ref_idc);
+    LLOGD("  nal_unit_type: %u (SPS)", nal_unit_type);
+    
+    if (sps_len < 4) {
+        LLOGD("SPS数据太短,无法解析详细参数");
+        return;
+    }
+    
+    // Profile & Level (3字节)
+    uint8_t profile_idc = sps_data[1];
+    uint8_t constraint_flags = sps_data[2];
+    uint8_t level_idc = sps_data[3];
+    
+    LLOGD("Profile IDC: %u (0x%02X)", profile_idc, profile_idc);
+    const char *profile_name = "Unknown";
+    switch (profile_idc) {
+        case 66: profile_name = "Baseline"; break;
+        case 77: profile_name = "Main"; break;
+        case 88: profile_name = "Extended"; break;
+        case 100: profile_name = "High"; break;
+        case 110: profile_name = "High 10"; break;
+        case 122: profile_name = "High 4:2:2"; break;
+        case 244: profile_name = "High 4:4:4"; break;
+    }
+    LLOGD("  Profile: %s", profile_name);
+    
+    LLOGD("Constraint Flags: 0x%02X", constraint_flags);
+    LLOGD("  constraint_set0_flag: %u", (constraint_flags >> 7) & 0x01);
+    LLOGD("  constraint_set1_flag: %u", (constraint_flags >> 6) & 0x01);
+    LLOGD("  constraint_set2_flag: %u", (constraint_flags >> 5) & 0x01);
+    LLOGD("  constraint_set3_flag: %u", (constraint_flags >> 4) & 0x01);
+    LLOGD("  constraint_set4_flag: %u", (constraint_flags >> 3) & 0x01);
+    LLOGD("  constraint_set5_flag: %u", (constraint_flags >> 2) & 0x01);
+    
+    LLOGD("Level IDC: %u (Level %.1f)", level_idc, level_idc / 10.0);
+    
+    // 打印原始数据 (前16字节或全部)
+    uint32_t print_len = sps_len > 16 ? 16 : sps_len;
+    char hex_str[128];
+    int pos = 0;
+    for (uint32_t i = 0; i < print_len && pos < 120; i++) {
+        pos += snprintf(hex_str + pos, sizeof(hex_str) - pos, "%02X ", sps_data[i]);
+    }
+    if (sps_len > 16) {
+        snprintf(hex_str + pos, sizeof(hex_str) - pos, "...");
+    }
+    LLOGD("SPS Raw Data (%u bytes): %s", sps_len, hex_str);
+    LLOGD("==================================\n");
+}
+
+/**
+ * 详细解析PPS数据
+ */
+static void rtsp_parse_pps_detail(const uint8_t *pps_data, uint32_t pps_len) {
+    if (!pps_data || pps_len < 1) {
+        LLOGE("PPS数据无效");
+        return;
+    }
+    
+    LLOGD("========== PPS详细信息 ==========");
+    
+    // NALU头 (1字节)
+    uint8_t nalu_header = pps_data[0];
+    uint8_t forbidden_zero = (nalu_header >> 7) & 0x01;
+    uint8_t nal_ref_idc = (nalu_header >> 5) & 0x03;
+    uint8_t nal_unit_type = nalu_header & 0x1F;
+    
+    LLOGD("NALU Header: 0x%02X", nalu_header);
+    LLOGD("  forbidden_zero_bit: %u", forbidden_zero);
+    LLOGD("  nal_ref_idc: %u", nal_ref_idc);
+    LLOGD("  nal_unit_type: %u (PPS)", nal_unit_type);
+    
+    // 打印原始数据 (前16字节或全部)
+    uint32_t print_len = pps_len > 16 ? 16 : pps_len;
+    char hex_str[128];
+    int pos = 0;
+    for (uint32_t i = 0; i < print_len && pos < 120; i++) {
+        pos += snprintf(hex_str + pos, sizeof(hex_str) - pos, "%02X ", pps_data[i]);
+    }
+    if (pps_len > 16) {
+        snprintf(hex_str + pos, sizeof(hex_str) - pos, "...");
+    }
+    LLOGD("PPS Raw Data (%u bytes): %s", pps_len, hex_str);
+    LLOGD("==================================\n");
+}

+ 3 - 0
luat/include/luat_libs.h

@@ -233,4 +233,7 @@ LUAMOD_API int luaopen_airtalk( lua_State *L );
 LUAMOD_API int luaopen_misc(lua_State *L);
 /** rtmp推流库*/
 LUAMOD_API int luaopen_rtmp(lua_State *L);
+/** rtsp推流库*/
+LUAMOD_API int luaopen_rtsp(lua_State *L);
+
 #endif

+ 66 - 0
olddemo/camera/rtsp_usb/main.lua

@@ -0,0 +1,66 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "usb_cam_rtsp"
+VERSION = "1.0.0"
+
+local camera_id = camera.USB
+
+local usb_camera_table = {
+    id = camera_id,
+    sensor_width = 640,
+    sensor_height = 480,
+    usb_port = 1
+}
+
+sys.taskInit(function()
+    wlan.init()
+    wlan.connect("luatos1234", "12341234", 1)
+
+    sys.waitUntil("IP_READY")
+
+    camera.config(0, camera.CONF_UVC_FPS, 15)
+    socket.sntp() -- 这个必须有
+    sys.wait(200)
+    result = camera.init(usb_camera_table)
+    log.info("摄像头初始化", result)
+    if result ~= 0 then
+        log.info("摄像头初始化失败,停止运行")
+        return
+    end
+    camera.start(camera_id)
+
+    -- local rtspc = rtsp.create("rtsp://180.152.6.34:554/stream2live/089dd7d5_04d4_467a_852a_66cbbfc5a9c3_0001")
+    local rtspc = rtsp.create("rtsp://192.168.1.10:554/stream2live/089dd7d5_04d4_467a_852a_66cbbfc5a9c3_0001")
+    rtspc:setCallback(function(state, ...)
+        if state == rtsp.STATE_CONNECTED then
+            log.info("rtsp", "已连接到推流服务器")
+        elseif state == rtsp.STATE_PUBLISHING then
+            log.info("rtsp", "已开始推流")
+        elseif state == rtsp.STATE_ERROR then
+            log.info("rtsp", "出错:", ...)
+        end
+    end)
+    log.info("开始连接到推流服务器...")
+    sys.wait(100)
+    rtspc:connect()
+    sys.wait(300)
+
+    -- 开始处理
+    log.info("rtsp", "开始推流...")
+    rtspc:start() -- 已自动调用 camera.capture(camera_id, "rtsp", 1)
+    
+    while 1 do
+        --- 打印一下内存状态
+        sys.wait(30*1000)
+        log.info("lua", rtos.meminfo())
+        log.info("sys", rtos.meminfo("sys"))
+        log.info("psram", rtos.meminfo("psram"))
+        sys.wait(2000)
+    end
+    -- #################################################
+
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!