当前位置:首页 > 体育 > 正文

用Go语言怎么做视频直播?从零开始搭一个自己的直播服务

  • 体育
  • 2026-07-23 04:30:30
  • 17
摘要: 说实话,我一开始也觉得“视频直播”这事儿挺玄乎的,拿Go语言来做直播? 你可能会想,这玩意儿不是该用C++或者Node.js那些...

说实话,我一开始也觉得“视频直播”这事儿挺玄乎的。拿Go语言来做直播? 你可能会想,这玩意儿不是该用C++或者Node.js那些吗?但后来我真做了一遍发现,Go的并发模型和标准库简直是为流媒体量身定做的——goroutine处理流数据,channel做缓冲,编译出来一个二进制就能跑,省心到离谱。

h2: 直播到底在直播什么?

先别急着写代码,你得理解直播的本质:一边采集,一边传输,一边播放,不是先录好再发,而是实时地“推”出去。

核心流程就三步:

  1. 采集:摄像头、麦克风或者屏幕画面抓下来
  2. 编码:把原始数据压缩成H.264/H.265视频流和AAC音频流
  3. 传输:通过RTMP或者HLS协议推送到服务器,再分发给观众

用Go做这件事,最爽的一点是:你不需要从头写编解码器,有很多现成的库帮你干脏活累活,你只需要用Go把它们粘起来。

h2: 选对工具,事半功倍

我踩过不少坑,最后发现这几个库最靠谱:

库名 用途 备注
github.com/deepch/RTSPtoRTMP RTSP转RTMP 适合摄像头推流
github.com/nareix/joy4 RTMP推流/拉流 纯Go实现,性能不错
github.com/pion/webrtc/v3 WebRTC低延迟推拉流 延时能到几百毫秒
github.com/gorilla/websocket WebSocket传输 配合自定义协议用
gocv.io/x/gocv 摄像头采集、OpenCV处理 需要安装OpenCV

个人推荐:如果是局域网或小规模直播,用joy4就够了,如果要做Web端低延迟,上pion/webrtc,别问我为什么知道——我试过用joy4推流到B站,跑了三天没崩。

h2: 动手写一个最小可用的直播推流器

我给你写个最简版本。别怕,代码就那么几行

h3: 第一步:用Go采集摄像头画面

// 这个代码需要OpenCV支持,所以先装好 gocv
package main
import (
    "gocv.io/x/gocv"
)
func main() {
    webcam, _ := gocv.OpenDevice(0) // 打开默认摄像头
    window := gocv.NewWindow("Live")
    img := gocv.NewMat()
    for {
        webcam.Read(&img)
        if img.Empty() {
            continue
        }
        window.IMShow(img)
        if window.WaitKey(1) >= 0 {
            break
        }
    }
}

这里有个小坑:OpenCV采集到的帧是Mat格式,要转成[]byte才能走网络,你用img.ToBytes()就行。

h3: 第二步:用FFmpeg做编码(Go调用CLI)

Go本身没有硬件编码器,但可以调用FFmpeg的子进程,别觉得这是取巧——很多生产环境就这么干。

package main
import (
    "os/exec"
)
func startEncoder() {
    cmd := exec.Command("ffmpeg",
        "-f", "avfoundation",   // macOS用avfoundation,Linux用v4l2
        "-i", "0:0",             // 输入设备
        "-c:v", "libx264",       // 视频编码器
        "-b:v", "1000k",         // 码率
        "-f", "flv",             // 输出格式
        "rtmp://live.example.com/live/stream", // 推流地址
    )
    cmd.Stdout = nil
    cmd.Stderr = nil
    cmd.Run()
}

这时你可能会问:这不就甩锅给FFmpeg了吗?,但好处是——FFmpeg支持各种硬件加速(NVENC、VAAPI),你Go代码根本不需要改,换个参数就行。

h3: 第三步:用纯Go实现RTMP推流(不依赖外部工具)

如果你想让二进制干净一点,用joy4库试试:

package main
import (
    "github.com/nareix/joy4/av"
    "github.com/nareix/joy4/format/rtmp"
)
func pushStream() {
    conn, _ := rtmp.Dial("rtmp://live.example.com/live/stream")
    var streams []av.CodecData
    // 这里需要填写视频和音频的编解码信息
    conn.WriteHeader(streams)
    for {
        // 从摄像头或文件读取packet
        var pkt av.Packet
        // ... 填充pkt数据 ...
        conn.WritePacket(pkt)
    }
}

注意joy4的文档比较简略,看源码是个好选择,我自己就是读了它的writer.go才搞清楚怎么传metadata的。

h2: 进阶玩法:WebRTC低延迟直播

RTMP延迟有2-5秒,聊天还行,但如果你在做互动直播(比如连麦、在线教学),就得用WebRTC。

h3: 用pion/webrtc搭建信令服务器

package main
import (
    "github.com/pion/webrtc/v3"
    "github.com/pion/webrtc/v3/examples/internal/signal"
    "github.com/pion/webrtc/v3/pkg/media"
)
func main() {
    // 创建PeerConnection配置
    config := webrtc.Configuration{
        ICE: webrtc.ICEConfig{
            Servers: []webrtc.ICEServer{
                {URLs: []string{"stun:stun.l.google.com:19302"}},
            },
        },
    }
    pc, _ := webrtc.NewPeerConnection(config)
    // 添加视频轨道
    videoTrack, _ := pc.NewTrack(webrtc.DefaultPayloadTypeVP8, 1000, "video", "pion")
    _, _ = pc.AddTrack(videoTrack)
    // 这里需要实现信令交换(通过WebSocket或者HTTP)
    // 然后不断往videoTrack写入视频帧
    go func() {
        for {
            videoTrack.WriteSample(media.Sample{
                Data:     frameData,         // 你的视频帧数据
                Timestamp: time.Now().UnixNano() / 1e6,
            })
            time.Sleep(33 * time.Millisecond) // 30fps
        }
    }()
    // 信令处理(简化版)
    signal.DoSignaling(pc)
}

坑点提醒:WebRTC的STUN/TURN服务器配置得小心。公网部署必须搭TURN服务器,不然很多用户连不上,用coturn搭一个就行。

h2: 实际部署要踩的坑

我把自己踩过的坑列出来,你避着走:

  1. 带宽计算:1080p@30fps大概需要3-5Mbps上行。家里宽带上行不够,用服务器中转是必须的。
  2. 丢包处理:Go的net包默认不关心UDP丢包。用WebRTC的话它自带FEC和NACK,RTMP则依赖TCP重传。
  3. 音频同步:视频和音频的时间戳必须精确。我发现一个常见错误:用time.Now().UnixNano()当时间戳,结果音视频差了半秒——应该用采集时的PTS
  4. 内存泄漏:推流时如果客户端断开,WritePacket会报错。一定要检查error并清理goroutine,否则goroutine越积越多。
  5. 动态码率:WiFi信号波动时,固定码率会卡死。可以用bbr算法或者手动降码率,Go里传个channel控制就行。

h2: 一个跑起来不担心的小技巧

如果你只是想做个内部用的直播系统,别整WebRTC那么复杂的信令。用Go搭个RTMP服务器,然后用ffplay或者VLC直接拉流

// 使用 joy4 的 server 示例
import "github.com/nareix/joy4/rtmp"
func main() {
    server := &rtmp.Server{}
    server.HandlePublish = func(conn *rtmp.Conn) {
        // 收到推流请求,转发给所有拉流客户端
    }
    server.ListenAndServe()
}

这样你的Go二进制文件就同时是推流端和流媒体服务器,小团队用起来爽得很——一个文件搞定所有。

h2: 最后说个自己的糗事

第一次写Go直播的时候,我忘记关闭ffmpeg的stdin,结果进程挂在那,内存涨到2G才发现。所以你看到上面代码里没有写复杂的管道通信——不是我不会,是不想让你也进这个坑。一个进程管一件事,多个goroutine通信用channel,比硬在子进程管道里传数据好十倍。

好,现在你打开编辑器,试试把第一段代码跑起来。你的生活视频,应该从一行Go代码开始。

用Go语言怎么做视频直播?从零开始搭一个自己的直播服务