跳转至

rtsp 直播推流

一、概述

rtsp 是合宙 LuatOS 的 RTSP 推流核心库,作用是把 H.264 编码的视频帧按 RTP 打包后推送到 RTSP 服务器(如合宙 RTMP/RTSP 测试服务器、自建流媒体服务),常用于摄像头模组的实时视频回传。

要点:

1、模块名为 rtsp,用法是先 rtsp.create(url) 得到上下文对象,再按 setCallback → connect → start 的顺序建立推流,之后循环调用 pushFrame 送帧;

2、只负责传输,不负责编码。SPS/PPS 需要自己从编码器取到,用 setSPS/setPPS 告知库,视频帧必须是 H.264 的 NALU 数据;

3、连接过程是异步的,connect() 返回 true 只代表请求已提交,真正的结果要看状态回调或 getState();

4、非 PLAYING 状态下调用 pushFrame 的帧会被直接丢弃并返回 0,所以送帧要等状态变成 rtsp.STATE_PLAYING 之后再开始。

⚠️ 该库需要固件在编译时打开 RTSP 组件(源码标记 LUAT_USE_RTSP)。如果脚本里 rtsp 为 nil,说明当前固件没有包含该库,需要向对接 FAE 确认固件版本。

⚠️ 源码注释里出现的 rtsp:setFECParam 并未在注册表中导出,调用会直接报 nil,请勿使用。

二、核心示例

-- 前提:已通过摄像头与 H.264 编码拿到 SPS/PPS 与视频帧
local rtsp = rtsp.create("rtsp://example.com:554/stream")
if not rtsp then
    log.error("rtsp", "上下文创建失败")
    return
end

-- 状态回调:只有 state 一个参数
rtsp:setCallback(function(state)
    log.info("rtsp state", state)
end)

-- 传输模式:局域网用 udp,公网建议 tcp
rtsp:setTransportMode("tcp")

-- 告知库 SPS/PPS(从编码器取到的 NALU 数据)
rtsp:setSPS(sps_data)
rtsp:setPPS(pps_data)

-- 连接并启动事件轮询
rtsp:connect()
rtsp:start()

-- 等进入推流状态后再送帧
sys.waitUntil("RTSP_PLAYING", 10000)
while true do
    local frame, is_key = get_h264_frame()   -- 业务侧取帧
    if frame then
        local ret = rtsp:pushFrame(frame, mcu.ticks() % 0x100000000)
        if ret < 0 then
            log.warn("rtsp", "推帧失败", ret)
        end
    end
    sys.wait(40)
end

-- 结束:先停轮询,再断开,最后销毁
rtsp:stop()
rtsp:disconnect()
rtsp:destroy()

三、常量详解

3.1 状态常量

常量 值 含义
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_PLAYING 或 rtsp.STATE_PLAY,照抄注释里的写法会拿到 nil。

3.2 返回值常量

常量 含义
rtsp.OK 成功
rtsp.ERR_FAILED 一般性失败
rtsp.ERR_INVALID_PARAM 参数非法(如帧长度为 0 或超过 1MB)
rtsp.ERR_NO_MEMORY 内存不足
rtsp.ERR_CONNECT_FAILED 连接失败
rtsp.ERR_HANDSHAKE_FAILED 握手(OPTIONS/DESCRIBE/SETUP)失败
rtsp.ERR_NETWORK 网络错误
rtsp.ERR_TIMEOUT 超时
rtsp.ERR_BUFFER_OVERFLOW 缓冲区溢出

pushFrame 失败时返回的是这些负值,可以直接和常量比较。

3.3 传输模式常量

常量 值 适用场景
rtsp.TRANSPORT_UDP 0 默认值,局域网、丢包少的链路,延迟最低
rtsp.TRANSPORT_TCP 1 公网、NAT 后面、丢包多的链路,靠 TCP 重传保证完整
rtsp.TRANSPORT_UDP_FEC 2 UDP 加前向纠错,兼顾延迟与抗丢包

四、函数详解

rtsp.create(url)

功能

创建 RTSP 推流上下文。

参数

url

参数含义:RTSP 服务器地址;
数据类型:string;
取值范围:形如 rtsp://host:port/stream;
注意事项:必须带协议头 rtsp://,端口不写时由库按默认 554 处理;
参数示例:"rtsp://example.com:554/stream";

返回值

含义说明:RTSP 上下文对象;
数据类型:userdata;
取值范围:创建成功返回上下文,内部申请内存失败或 URL 解析失败返回 nil;
注意事项:返回 nil 时不要再调用后续接口;
返回示例:userdata: 0x...

示例

local rtsp = rtsp.create("rtsp://example.com:554/stream")
if not rtsp then
    log.error("rtsp", "create failed")
end

rtsp:setCallback(func)

功能

设置状态回调函数,状态变化时被调用。

参数

func

参数含义:回调函数;
数据类型:function 或 nil;
取值范围:nil 表示清除已设置的回调;
注意事项:回调只带一个参数 state,即第三章的状态常量;源码注释中的附加错误码参数目前不会传;
参数示例:function(state) end;

返回值

含义说明:是否设置成功;
数据类型:boolean;
取值范围:true / false;
注意事项:传入非 function 且非 nil 时返回 false;
返回示例:true;

示例

rtsp:setCallback(function(state)
    if state == rtsp.STATE_PLAYING then
        log.info("rtsp", "开始推流")
    elseif state == rtsp.STATE_ERROR then
        log.error("rtsp", "推流出错")
    end
end)

rtsp:connect()

功能

向 RTSP 服务器发起连接,依次完成 OPTIONS、DESCRIBE、SETUP、PLAY 握手。

参数

nil

返回值

含义说明:连接请求是否成功提交;
数据类型:boolean;
取值范围:true / false;
注意事项:true 只代表请求已入队,不代表已连上;是否连上以状态回调或 getState 为准;
返回示例:true;

示例

if rtsp:connect() then
    log.info("rtsp", "连接请求已提交")
end

rtsp:disconnect()

功能

断开与 RTSP 服务器的连接。

参数

nil

返回值

含义说明:断开是否成功;
数据类型:boolean;
取值范围:true / false;
注意事项:断开后状态回到空闲,需要重新连接才能再推流;
返回示例:true;

示例

rtsp:disconnect()

rtsp:start()

功能

启动 RTSP 事件轮询。库内部每 20ms 轮询一次协议状态机,必须调用它,协议才会向前推进。

参数

nil

返回值

nil

注意事项

1、start() 只是启动轮询,不会自己建连接,connect() 要在它之前或之后单独调用;

2、重复调用会重复注册定时器,正常用法是连接前后各调用一次即可,不要放在循环里。

示例

rtsp:start()

rtsp:stop()

功能

停止 RTSP 事件轮询,暂停协议处理。

参数

nil

返回值

nil

注意事项

1、只停轮询,不断开连接,也不释放资源;

2、停止后可以再调 start() 恢复。

示例

rtsp:stop()

rtsp:getState()

功能

获取当前 RTSP 状态。

参数

nil

返回值

含义说明:当前状态值;
数据类型:number;
取值范围:见第三章状态常量;上下文已失效时返回 -1;
注意事项:-1 不等于 STATE_ERROR,它表示对象已不可用;
返回示例:6;

示例

local state = rtsp:getState()
if state == rtsp.STATE_PLAYING then
    log.info("rtsp", "正在推流")
end

rtsp:setSPS(sps_data)

功能

设置 H.264 的 SPS(序列参数集),DESCRIBE 阶段要用它生成 SDP。

参数

sps_data

参数含义:H.264 SPS 原始 NALU 数据;
数据类型:string;
取值范围:长度大于 0;
注意事项:要传编码器输出的原始字节,不要用十六进制字符串或 base64;典型以 0x67 开头;
参数示例:string.char(0x67, 0x42, ...);

返回值

含义说明:是否设置成功;
数据类型:boolean;
取值范围:true / false;
注意事项:上下文失效时返回 false;
返回示例:true;

示例

rtsp:setSPS(sps_data)

rtsp:setPPS(pps_data)

功能

设置 H.264 的 PPS(图像参数集)。

参数

pps_data

参数含义:H.264 PPS 原始 NALU 数据;
数据类型:string;
取值范围:长度大于 0;
注意事项:典型以 0x68 开头;SPS 与 PPS 都要设置,只设一个会导致服务器解不出画面;
参数示例:string.char(0x68, 0xCB, ...);

返回值

含义说明:是否设置成功;
数据类型:boolean;
取值范围:true / false;
注意事项:上下文失效时返回 false;
返回示例:true;

示例

rtsp:setPPS(pps_data)

rtsp:pushFrame(frame_data, timestamp)

功能

推送一帧 H.264 视频数据,库内部完成 RTP 打包与发送。

参数

frame_data

参数含义:H.264 编码的一帧数据;
数据类型:string;
取值范围:1 字节 ~ 1MB;
注意事项:超过 1MB 或长度为 0 直接返回 ERR_INVALID_PARAM;必须传入完整的 NALU(含起始码),不要自己切分包;
参数示例:frame_buf;

timestamp

参数含义:时间戳,单位毫秒;
数据类型:number;
取值范围:32 位无符号整数范围;
注意事项:可选,传 nil 或 0 时库内部自己取时间;同一路流必须单调递增,回绕时按 32 位取模;
参数示例:mcu.ticks() % 0x100000000;

返回值

含义说明:已发送的字节数;
数据类型:number;
取值范围:成功为非负字节数;失败为第三章的负值常量;
注意事项:状态还没到 STATE_PLAYING 时帧会被直接丢弃并返回 0,不报错,容易误判成推流正常;
返回示例:1432;

示例

local ret = rtsp:pushFrame(frame, mcu.ticks() % 0x100000000)
if ret == 0 then
    log.warn("rtsp", "未进入推流状态,帧被丢弃")
elseif ret < 0 then
    log.error("rtsp", "推帧失败", ret)
end

rtsp:getStats()

功能

获取推流统计信息。

参数

nil

返回值

含义说明:统计信息表;
数据类型:table;
取值范围:
{
    bytes_sent,           -- 已发送字节数
    video_frames_sent,    -- 已发送视频帧数
    rtp_packets_sent,     -- 已发送 RTP 包数
    connection_time,      -- 连接持续时长,单位毫秒
    last_video_timestamp  -- 最后一帧的时间戳,单位毫秒
}
注意事项:注意 connection_time 的单位是毫秒,不是秒;上下文失效时返回 nil;
返回示例:{bytes_sent=14320, video_frames_sent=10, rtp_packets_sent=12, connection_time=400, last_video_timestamp=392}

示例

local stats = rtsp:getStats()
if stats then
    log.info("rtsp", "帧数", stats.video_frames_sent, "时长(ms)", stats.connection_time)
end

rtsp:getNetworkStats()

功能

获取网络质量统计,用于判断链路是否撑得住当前码率。

参数

nil

返回值

含义说明:网络统计信息表;
数据类型:table;
取值范围:
{
    packet_loss_rate,          -- 丢包率,0.0 ~ 1.0
    rtt_ms,                    -- 往返延迟,毫秒
    jitter_ms,                 -- 抖动,毫秒
    bandwidth_kbps,            -- 估算带宽,Kbps
    rtp_packets_sent,          -- 已发送 RTP 包数
    rtp_packets_lost,          -- 丢失的 RTP 包数
    rtp_bytes_sent,            -- 已发送 RTP 字节数
    rtp_packets_retransmitted, -- 重传的 RTP 包数
    rtp_nack_received          -- 收到的 NACK 请求数
}
注意事项:取不到统计时返回 nil,调用前先判空;
返回示例:{packet_loss_rate=0.012, rtt_ms=48, jitter_ms=7, bandwidth_kbps=1200, ...}

示例

local ns = rtsp:getNetworkStats()
if ns then
    log.info("rtsp", "丢包率", ns.packet_loss_rate, "RTT", ns.rtt_ms)
end

rtsp:setNetworkStatsCallback(func)

功能

设置网络统计回调。当前版本尚未实现,接口保留但不会生效。

参数

func

参数含义:回调函数;
数据类型:function;
取值范围:任意 function;
注意事项:目前传入任何值都只打印一条“暂未实现”的告警;

返回值

含义说明:是否设置成功;
数据类型:boolean;
取值范围:当前恒为 false;
注意事项:需要网络质量数据请改用 getNetworkStats 轮询;
返回示例:false;

示例

-- 当前不生效,仅作占位;周期读取更可靠
if not rtsp:setNetworkStatsCallback(function(stats) end) then
    sys.timerLoopStart(function()
        local ns = rtsp:getNetworkStats()
        if ns then log.info("rtsp", "loss", ns.packet_loss_rate) end
    end, 5000)
end

rtsp:setTransportMode(mode)

功能

设置 RTP 的传输模式。

参数

mode

参数含义:传输模式;
数据类型:string;
取值范围:只接受 "udp"、"tcp"、"udp_fec" 三个字符串,大小写敏感;
注意事项:传其它字符串会打印错误并返回 false;需要在 connect 之前设置;
参数示例:"tcp";

返回值

含义说明:是否设置成功;
数据类型:boolean;
取值范围:true / false;
注意事项:上下文失效时无返回值(返回 nil);
返回示例:true;

示例

rtsp:setTransportMode("tcp")     -- 公网推荐
rtsp:setTransportMode("udp")     -- 局域网推荐
rtsp:setTransportMode("udp_fec") -- 抗丢包折中

rtsp:destroy()

功能

销毁 RTSP 上下文,释放套接字、回调引用与缓冲区。

参数

nil

返回值

nil

注意事项

1、销毁后对象不可再用,要重新推流必须重新 create;

2、正常用法是 stop → disconnect → destroy,直接 destroy 也可以,内部会一并释放;

3、如果忘了调用,GC 时也会通过 __gc 回收,但回收时机不可控,建议显式销毁。

示例

rtsp:stop()
rtsp:disconnect()
rtsp:destroy()
rtsp = nil

五、产品支持说明

目前除 8101 系列外,其他合宙主流模组暂不支持 rtsp 核心库;即使同为 8101 系列,也需要固件在编译时包含 RTSP 组件(LUAT_USE_RTSP)。脚本里 rtsp 为 nil 即表示当前固件未包含,需向对接 FAE 确认固件版本。

搜索