用Go语言怎么做视频直播?从零开始搭一个自己的直播服务
- 体育
- 2026-07-23 04:30:30
- 17
说实话,我一开始也觉得“视频直播”这事儿挺玄乎的。拿Go语言来做直播? 你可能会想,这玩意儿不是该用C++或者Node.js那些吗?但后来我真做了一遍发现,Go的并发模型和标准库简直是为流媒体量身定做的——goroutine处理流数据,channel做缓冲,编译出来一个二进制就能跑,省心到离谱。
h2: 直播到底在直播什么?
先别急着写代码,你得理解直播的本质:一边采集,一边传输,一边播放,不是先录好再发,而是实时地“推”出去。
核心流程就三步:
- 采集:摄像头、麦克风或者屏幕画面抓下来
- 编码:把原始数据压缩成H.264/H.265视频流和AAC音频流
- 传输:通过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: 实际部署要踩的坑
我把自己踩过的坑列出来,你避着走:
- 带宽计算:1080p@30fps大概需要3-5Mbps上行。家里宽带上行不够,用服务器中转是必须的。
- 丢包处理:Go的
net包默认不关心UDP丢包。用WebRTC的话它自带FEC和NACK,RTMP则依赖TCP重传。 - 音频同步:视频和音频的时间戳必须精确。我发现一个常见错误:用
time.Now().UnixNano()当时间戳,结果音视频差了半秒——应该用采集时的PTS。 - 内存泄漏:推流时如果客户端断开,
WritePacket会报错。一定要检查error并清理goroutine,否则goroutine越积越多。 - 动态码率: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代码开始。
