跳转至

56 rtmp-rtmp推流

作者:王城钧 | 最后修改:2026-03-19

一、概述

RTMP(Real-Time Messaging Protocol,实时消息传输协议)是基于 TCP 的实时音视频传输协议,主打低延迟、高稳定性,是直播推流领域的经典标准,广泛用于直播开播、音视频实时回传等场景。

二、核心示例

1、核心示例是指:使用本库文件提供的核心 API,开发的基础业务逻辑的演示代码;

2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;

3、更加完整和详细的 demo,请参考 LuatOS 仓库 中各个产品目录下的 demo/rtmp

-- 引入excamera扩展库
local excamera = require("excamera")

local RTMP_TASK_NAME = "rtmp_app_task" -- RTMP任务名称

-- HTTP请求获取RTMP服务器地址函数
local function rtmp_http_request()
    local get_device_id = netdrv.mac(socket.LWIP_STA) -- Air8101用STA MAC作为设备ID
    log.info("打印设备的ID号", get_device_id)

    local url = "http://video.luatos.com:10030/api-system/deviceVideo/get" .. "/" .. get_device_id
    log.info("打印的URL", url)
    local camera_header = {
        ["Accept-Encoding"] = "identity",
        ["Host"] = "video.luatos.com:10030",
        ["Content-Type"] = "application/json"
    }
    local post_body = {
        deviceAccess = "8",     -- 8 代表接入方式为RTMP
        deviceUser = "admin",   -- 平台录像机设置的设备用户(不是登录用的用户名)
        devicePsd = "Air123456" -- 平台录像机设置的设备密码(不是登录用的密码)
    }
    -- 发送POST请求并等待响应
    local code, headers, body = http.request("POST", url, camera_header, json.encode(post_body)).wait()
    log.info("打印的请求code", code)

    if code ~= 200 then
        log.error("HTTP请求失败")
        return false, nil
    end

    log.info("打印的请求body", body)
    local json_body = json.decode(body)
    if not json_body or json_body.code ~= 200 then
        log.error("请求视频URL失败", json_body and json_body.msg or "未知错误")
        return false, nil
    end

    local rtmp_url = json_body.data.urlList[1]
    log.info("请求得到的RTMP地址", rtmp_url)
    if not rtmp_url then
        log.error("未获取到RTMP地址")
        return false, nil
    end
    return true, rtmp_url
end


local g_s_rtmp_state

-- RTMP状态回调函数
-- 连接过程中,如果连接失败,state状态依次为STATE_IDLE->STATE_DISCONNECTING->STATE_IDLE->STATE_ERROR
-- 已经建立了连接,推流过程中,如果本地调用disconnect接口,state状态依次为STATE_IDLE->STATE_DISCONNECTING->STATE_IDLE
-- 已经建立了连接,推流过程中,如果网络或者服务器出现了异常,或者本地发送数据出现了异常,state状态依次为STATE_IDLE->STATE_DISCONNECTING->STATE_IDLE->STATE_ERROR
local function rtmp_state_callback(state)
    -- 打印RTMP状态变化基础日志
    log.info("rtmp_state_callback state", state)

    -- 根据不同状态执行对应逻辑
    if state == rtmp.STATE_IDLE then
        log.info("空闲状态,可能和推流时效有关,需要等待一段时间,再尝试重连")
        if g_s_rtmp_state==rtmp.STATE_DISCONNECTING then
            sys.sendMsg(RTMP_TASK_NAME, "RTMP_EVENT", "DISCONNECTED")
        end
    elseif state == rtmp.STATE_CONNECTING then
        log.info("正在连接")
    elseif state == rtmp.STATE_HANDSHAKING then
        log.info("握手中")
    elseif state == rtmp.STATE_CONNECTED then
        log.info("已连接")
        sys.sendMsg(RTMP_TASK_NAME, "RTMP_EVENT", "CONNECTED")
    elseif state == rtmp.STATE_PUBLISHING then
        log.info("推流中")
    elseif state == rtmp.STATE_DISCONNECTING then
        log.info("正在断开")
    elseif state == rtmp.STATE_ERROR then
        log.info("错误")
    end
    g_s_rtmp_state = state
end

-- RTMP main task 的任务处理函数
local function rtmp_task()
    local camera_opened, msg, rtmpc, success, rtmp_url

    while true do
        -- 1. 如果当前时间点设置的默认网卡还没有连接成功,一直在这里循环等待
        while not socket.adapter(socket.dft()) do
            log.warn("rtmp_task", "wait IP_READY", socket.dft())
            -- 在此处阻塞等待默认网卡连接成功的消息"IP_READY"
            -- 或者等待1秒超时退出阻塞等待状态;
            -- 注意:此处的1000毫秒超时不要修改的更长;
            -- 因为当使用exnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改默认使用的网卡
            -- 当exnetif.set_priority_order的调用时序和此处的socket.adapter(socket.dft())判断时序有可能不匹配
            -- 此处的1秒,能够保证,即使时序不匹配,也能1秒钟退出阻塞状态,再去判断socket.adapter(socket.dft())
            sys.waitUntil("IP_READY", 1000)
        end

        -- 检测到了IP_READY消息
        log.info("rtmp_task", "recv IP_READY", socket.dft())

        -- 清空此task绑定的消息队列中的未处理的消息
        sys.cleanMsg(RTMP_TASK_NAME)

        -- 2. HTTP请求获取RTMP服务器地址
        success, rtmp_url = rtmp_http_request()
        if not success then
            log.error("获取RTMP地址失败")
            goto EXCEPTION_PROC
        end

        -- 3. 配置摄像头
        camera_opened = excamera.open({
            id = camera.USB,
            sensor_width = 1280,
            sensor_height = 720,
            usb_port = 1
        })

        if not camera_opened then
            log.error("摄像头初始化失败")
            goto EXCEPTION_PROC
        end

        -- 启动摄像头
        log.info("启动摄像头...")
        if not excamera.rtmp() then
            log.error("无法启动摄像头")
            goto EXCEPTION_PROC
        end

        -- 创建RTMP客户端
        rtmpc = rtmp.create(rtmp_url)
        if not rtmpc then
            log.error("rtmp.create", "创建RTMP客户端失败")
            goto EXCEPTION_PROC
        end
        log.info("rtmp.create", "RTMP客户端创建成功")

        -- 设置RTMP状态回调
        rtmpc:setCallback(rtmp_state_callback)

        -- 连接RTMP服务器
        log.info("开始连接RTMP服务器...")
        success = rtmpc:connect()
        if not success then
            log.error("连接RTMP服务器失败")
            goto EXCEPTION_PROC
        end

        -- 推流状态的处理调度逻辑
        while true do
            -- 等待消息
            msg = sys.waitMsg(RTMP_TASK_NAME, "RTMP_EVENT")
            if msg then
                log.info("rtmp_task waitMsg", msg[2], msg[3], msg[4])

                -- 连接成功
                if msg[2] == "CONNECTED" then
                    -- 直接启动推流,不检查返回值
                    log.info("准备开始推流")
                    rtmpc:start()
                    log.info("推流已启动")

            -- 连接失败/连接断开
            elseif msg[2] == "DISCONNECTED" then
                break

            -- 需要主动关闭连接
            -- 用户需要主动关闭rtmp连接时,可以调用sys.sendMsg(RTMP_TASK_NAME, "RTMP_EVENT", "CLOSE")
            elseif msg[2] == "CLOSE" then
                -- 主动断开rtmp client连接
                rtmpc:disconnect()
            end
            end
        end

        -- 出现异常
        ::EXCEPTION_PROC::

        -- 清空此task绑定的消息队列中的未处理的消息
        sys.cleanMsg(RTMP_TASK_NAME)

        -- 5. 关闭推流
        log.info("推流结束,开始释放资源")

        -- 关闭摄像头
        if camera_opened then
            excamera.close()
            log.info("excamera已关闭")
        end

        -- 关闭RTMP客户端
        if rtmpc then
            rtmpc:stop()
            log.info("RTMP推流已停止")
            rtmpc:disconnect()
            log.info("RTMP连接已断开")
            rtmpc:destroy()
            log.info("RTMP客户端已销毁")
        end

        -- 确保所有状态重置
        log.info("所有资源已释放,5秒后重连")

        -- 5秒后跳转到循环体开始位置,自动发起重连
        sys.wait(5000)
    end
end

local function wifi_sta_func(evt, data)
    -- evt 可能的值有: "CONNECTED", "DISCONNECTED"
    -- 当evt=CONNECTED, data是连接的AP的ssid, 字符串类型
    -- 当evt=DISCONNECTED, data断开的原因, 整数类型
    log.info("收到STA事件", evt, data)
    if evt == "DISCONNECTED" then
        sys.sendMsg(RTMP_TASK_NAME, "RTMP_EVENT", "DISCONNECTED")
    end
end

-- 内存检查函数
local function memory_check()
    while true do
        -- 等待20秒
        sys.wait(20000)
        -- 打印系统内存使用信息
        log.info("系统内存使用情况", rtos.meminfo("sys"))
        -- 打印Lua虚拟机内存使用信息
        log.info("Lua虚拟机内存使用情况", rtos.meminfo("lua"))
    end
end

-- wifi的STA相关事件
sys.subscribe("WLAN_STA_INC", wifi_sta_func)

-- 运行这个task的处理函数rtmp_task
sys.taskInitEx(rtmp_task, RTMP_TASK_NAME)

-- 启动内存检查任务
sys.taskInit(memory_check)

三、常量详解

核心库常量,顾名思义是由合宙 LuatOS 内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;

rtmp.STATE_IDLE

常量含义:RTMP推流空闲状态,表示未进行任何连接操作;
数据类型:number
示例代码:if state == rtmp.STATE_IDLE 
           log.info("空闲状态") 
         end

rtmp.STATE_CONNECTING

常量含义:RTMP推流正在连接状态,表示正在尝试连接到RTMP服务器
数据类型:number
示例代码:if state == rtmp.STATE_CONNECTING 
             log.info("正在连接") 
         end

rtmp.STATE_HANDSHAKING

常量含义:RTMP推流握手中状态,表示正在与RTMP服务器进行握手协商
数据类型:number
示例代码:if state == rtmp.STATE_HANDSHAKING 
            log.info("握手中") 
         end

rtmp.STATE_CONNECTED

常量含义:RTMP推流已连接状态,表示已成功连接到RTMP服务器
数据类型:number
示例代码:if state == rtmp.STATE_CONNECTED 
             log.info("已连接") 
         end

rtmp.STATE_PUBLISHING

常量含义:RTMP推流正在推流状态,表示正在向服务器推送音视频数据;
数据类型:number
示例代码:if state == rtmp.STATE_PUBLISHING 
            log.info("推流中") 
         end

rtmp.STATE_DISCONNECTING

常量含义:RTMP推流正在断开连接状态,表示正在与RTMP服务器断开连接
数据类型:number
示例代码:if state == rtmp.STATE_DISCONNECTING 
             log.info("正在断开") 
         end

rtmp.STATE_ERROR

常量含义:RTMP推流错误状态,表示推流过程中发生错误;
数据类型:number
示例代码:if state == rtmp.STATE_ERROR 
            log.info("错误状态") 
         end

四、函数详解

rtmp.create(url)

功能

创建 RTMP 推流客户端实例,用于建立与 RTMP 服务器的连接并进行视频推流。

注意事项

  1. 返回值有返回 nil 的情况,在调用后必须进行检查返回值是否为 nil,且要做逻辑处理;
  2. 带通配符的 URL 格式为:rtmp://{host_ip}[:{port}]/{app_name}/{stream_name} rtmp 协议头目前只支持 rtmp
  3. 创建实例后,需要调用 rtmp_client:setCallback 设置状态回调函数,以便及时处理连接状态变化和错误情况;

参数

url

参数含义:RTMP服务器地址,带通配符的URL格式为rtmp://{host_ip}[:{port}]/{app_name}/{stream_name}
数据类型:string
取值范围:暂无;
是否必选:必选传入此参数;
注意事项::port 可以省略,默认端口是1935
参数示例:"rtmp://180.152.6.34:1935/stream1live/93ecc063_8d75_4356_82ea_6a78914b649d_0001"

返回值

local rtmp_client = rtmp.create(url)

rtmp_client

含义说明:RTMP上下文对象
数据类型:成功时为userdata,失败时为nil
取值范围:暂无;
注意事项:暂无;
返回示例:rtmp_ctx: 609B2010

示例

local rtmp_client = rtmp.create("rtmp://example.com:1935/live/stream")

rtmp_client:setCallback(func)

功能

设置 RTMP 状态回调函数

注意事项

1.回调函数应保持简洁高效 ,避免执行耗时操作(如复杂计算、阻塞 IO 等),否则可能影响 RTMP 推流的实时性和稳定性;

参数

func

参数含义:RTMP状态回调函数;回调函数的格式为:
        local function rtmp_cb(state)
            log.info("rtmp状态变化", state)
        end
        该回调函数接收state一个参数,在不同事件类型下参数含义有所不同:

        -- 参数含义 :RTMP推流状态码,表示当前推流的状态;
        -- 数据类型 :number;
        -- 取值范围 :见第三章常量
        -- 是否必选 :必须传入此参数;
        -- 注意事项 :暂无;
        -- 参数示例 :rtmp.STATE_CONNECTED
        state

数据类型:function
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:
        local function rtmp_cb(state)
            log.info("rtmp状态变化", state)
            if state == rtmp.STATE_IDLE then
                log.info("rtmp状态变化", "空闲状态,可能和推流时效有关,需要等待一段时间,再尝试重连")
            elseif state == rtmp.STATE_CONNECTING then
                log.info("rtmp状态变化", "正在连接到推流服务器")
            elseif state == rtmp.STATE_HANDSHAKING then
                log.info("rtmp状态变化", "正在与服务器进行握手")
            elseif state == rtmp.STATE_CONNECTED then
                log.info("rtmp状态变化", "已连接到推流服务器")
            elseif state == rtmp.STATE_PUBLISHING then
                log.info("rtmp状态变化", "已开始推流")
            elseif state == rtmp.STATE_DISCONNECTING then
                log.info("rtmp状态变化", "正在断开与服务器的连接")
            elseif state == rtmp.STATE_ERROR then
                log.info("rtmp状态变化", "出错:")
            else
                log.info("rtmp状态变化", "未知状态:", state)
            end
        end
        rtmp_client:setCallback(rtmp_cb)

rtmp_client:connect()

功能

连接到 RTMP 服务器

注意事项

  1. 该接口返回 true 仅表示连接请求成功发起,不代表与 RTMP 服务器的连接已成功建立;
  2. 该接口返回 false 仅表示连接请求失败,通常意味着底层 socket 创建或连接失败;
  3. 调用该接口前必须先通过 rtmp.create() 配置服务器地址,如果未配置必要参数,该接口可能返回 false;
  4. 连接状态的实际变化需要通过注册的状态回调函数来监听,特别是 STATE_CONNECTED 和 STATE_ERROR 状态。

参数

nil

返回值

local result = rtmp_client:connect()

result

含义说明:连接到RTMP服务器的结果;失败返回false 成功返回true
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:true

示例

local result = rtmp_client:connect()
if result then
    log.info("连接请求已发送")
else
    log.info("连接失败")
end

rtmp_client:disconnect()

功能

断开与 RTMP 服务器的连接,但不会释放 RTMP 客户端的资源。

注意事项

  1. 该接口仅断开与服务器的连接,但 不会释放 RTMP 客户端实例的资源 (如内存、配置等);
  2. 该接口返回 true 仅表示断开连接请求成功发起,不代表断开连接操作是否真正成功完成;
  3. 该接口返回 false 仅表示断开连接请求失败,通常意味着底层操作无法执行;
  4. 如需确认是否真正断开连接,建议通过注册的状态回调函数来监听 STATE_IDLE 状态。

参数

nil

返回值

local result = rtmp_client:disconnect()

result

含义说明:断开RTMP服务器的结果;失败返回false 成功返回true
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:true

示例

rtmp_client:disconnect()

rtmp_client:start()

功能

启动 RTMP 推流事件处理轮询机制,用于持续处理 RTMP 连接状态维护、数据收发和推流控制。

参数

nil

返回值

nil

示例

rtmp_client:start()

rtmp_client:getState()

功能

获取 RTMP 连接状态

参数

nil

返回值

local state = rtmp_client:getState()

state

含义说明:RTMP服务器的连接状态
数据类型:number
取值范围:见第三章常量;
注意事项:暂无;
返回示例:rtmp.STATE_CONNECTED

示例

local state = rtmp_client:getState()
if state == rtmp.STATE_CONNECTED then
    log.info("已连接")
elseif state == rtmp.STATE_PUBLISHING then
    log.info("正在推流")
end

rtmp_client:getStats()

功能

获取 RTMP 统计信息

参数

nil

返回值

local stats = rtmp_client:getStats()

stats

含义说明:RTMP统计信息表
         {
            -- 参数含义: 连接时长,单位秒;
            -- 数据类型: number;
            -- 取值范围: 非负整数;
            -- 注意事项: 暂无;
            -- 参数示例: 30;
            connection_time = 0,

            -- 参数含义: 总发送字节数,单位B;
            -- 数据类型: number;
            -- 取值范围: 非负整数;
            -- 注意事项: 暂无;
            -- 参数示例: 5393;
            bytes_sent = 0,

            -- 参数含义: 已发送视频帧数;
            -- 数据类型: number;
            -- 取值范围: 非负整数;
            -- 注意事项: 暂无;
            -- 参数示例: 100;
            video_frames_sent = 0,

            -- 参数含义: 已发送音频帧数;
            -- 数据类型: number;
            -- 取值范围: 非负整数;
            -- 注意事项: 暂无;
            -- 参数示例: 50;
            audio_frames_sent = 0        
         }
数据类型:table
取值范围:无特别限制;
注意事项:暂无;
返回示例:{
            connection_time=56,
            bytes_sent=2937,
            video_frames_sent=0,
            audio_frames_sent=0
         }

示例

local stats = rtmp_client:getStats()
log.info("RTMP统计信息",json.encode(stats))

rtmp_client:stop()

功能

停止 RTMP 事件轮询,暂停推流数据的处理和发送。

注意事项

1.调用 stop() 后,RTMP 客户端会停止事件轮询,不再处理和发送推流数据;

2.此操作不会断开与 RTMP 服务器的连接,只是暂停数据传输;

3.可以通过调用 start() 方法重新开始事件轮询和数据传输;

参数

nil

返回值

nil

示例

rtmp_client:stop()

rtmp_client:destroy()

功能

销毁 RTMP 上下文并释放所有相关资源

注意事项

  1. 调用 destroy() 后,RTMP 客户端相关的所有资源(包括套接字、回调函数引用、内存缓冲区等)都会被释放;
  2. 释放资源后,该 RTMP 客户端实例将无法再使用,若需要重新连接 RTMP 服务器,必须创建新的客户端实例;
  3. 此操作是不可逆的,一旦调用,无法通过任何方式恢复客户端的功能;

参数

nil

返回值

nil

示例

rtmp_client:destroy()

五、产品支持说明

目前除 8101 系列外,其他合宙主流模组暂不支持 rtmp 核心库