You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在RTMP握手完成后从入站RTMP流中提取流密钥?

如何在RTMP握手完成后从入站RTMP流中提取流密钥?

兄弟,你现在只抓到了RTMP推流流程里的connect阶段消息,流密钥根本不在这个阶段里!我来给你捋清楚整个逻辑:

首先,RTMP推流的完整流程是这样的:

  • 先完成3个包的握手流程(你可能已经处理了,但代码里没体现)
  • 客户端发送connect命令,和服务器的指定应用实例建立连接(就是你看到的rtmp://127.0.0.1/stream里的stream)
  • 服务器回复连接成功后,客户端发createStream请求一个流ID
  • 最后客户端会发送publish命令,你的流密钥(那个UUID)就藏在这个publish命令的AMF参数里!

你当前的代码只读取了最开始的一段buffer,刚好只拿到了connect消息,自然看不到流密钥。而且还有个关键问题:RTMP的命令是用AMF(Action Message Format)二进制编码的,直接把字节转成字符串会丢失很多结构化信息,乱码的部分就是AMF的类型标记和长度字段,没法直接通过字符串检索找到密钥。

那该怎么改呢?给你几个关键步骤:

  1. 先完成RTMP握手:必须先处理握手的3个包(C0/S0、C1/S1、C2/S2),否则后续的消息解析全是错的。
  2. 循环读取后续RTMP消息:connect之后还有createStream、publish等命令,需要持续读取直到拿到publish命令。
  3. 用AMF解析库解析命令:Go里可以用专门的AMF0解析库(比如gortsplib里的amf0包)来解析二进制命令内容,而不是直接转字符串。

给你一个简化的代码思路(需要补充握手和消息头解析的细节):

import (
    "bytes"
    "io"
    "net"

    "github.com/aler9/gortsplib/v2/pkg/amf0"
)

// 假设已经完成RTMP握手,这里直接处理后续命令
func ExtractRTMPStreamKey(conn net.Conn) (string, error) {
    for {
        // 第一步:读取RTMP消息头(需要自己实现或用库,头长度可能是11/7/3字节)
        msgHeader, err := readRTMPMessageHeader(conn)
        if err != nil {
            return "", err
        }

        // 跳过非命令消息(命令消息的类型ID是20)
        if msgHeader.MessageTypeID != 20 {
            skipBuf := make([]byte, msgHeader.MessageLength)
            _, err := io.ReadFull(conn, skipBuf)
            if err != nil {
                return "", err
            }
            continue
        }

        // 读取命令消息的二进制内容
        cmdBuf := make([]byte, msgHeader.MessageLength)
        _, err = io.ReadFull(conn, cmdBuf)
        if err != nil {
            return "", err
        }

        // 解析AMF0格式的命令
        dec := amf0.NewDecoder(bytes.NewReader(cmdBuf))
        cmdName, err := dec.ReadString()
        if err != nil {
            return "", err
        }

        // 找到publish命令
        if cmdName == "publish" {
            // 跳过事务ID(publish命令的第二个参数是事务ID,通常为0)
            _, err = dec.ReadNumber()
            if err != nil {
                return "", err
            }
            // 第三个参数就是流密钥!
            streamKey, err := dec.ReadString()
            if err != nil {
                return "", err
            }
            return streamKey, nil
        }
    }
}

// 这里需要补充RTMP消息头的解析实现,或者直接用成熟的RTMP库
func readRTMPMessageHeader(conn net.Conn) (/* 自定义的消息头结构体 */ interface{}, error) {
    // 实现RTMP消息头解析逻辑,获取消息类型ID、消息长度等
    return nil, nil
}

另外要注意:不同推流客户端(比如OBS、FMLE)的publish命令格式都是遵循RTMP规范的,只要正确解析AMF参数,就能拿到流密钥。别再只看connect阶段的消息啦,往后找publish命令才是正确方向!

备注:内容来源于stack exchange,提问作者lovgrandma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 10:14:30