365 NDI视频中文字幕,用Golang搞定的那些事
- 科技
- 2026-07-17 14:21:05
- 34
说实话,我第一次听到“365 NDI视频中文字幕”这个需求时,脑子里冒出的第一个想法是:这玩意儿到底是要干啥? 后来发现,这事其实挺接地气的——就是把NDI视频流里的内容,实时加上中文字幕,而且最好是能跑上365天不掉链子,今天我就用Golang聊聊这事,顺便分享点踩坑后的教训。
什么是NDI?它跟字幕有什么关系?
NDI(Network Device Interface)是一种网络视频传输协议,简单说就是让视频信号通过局域网跑,不用折腾笨重的HDMI线,现在很多直播、会议系统都在用NDI,而“中文字幕”嘛,就是给视频加上中文文本。
那么问题来了:怎么用Golang处理NDI视频流,并且动态添加中文字幕? 这其实涉及两个关键点:
- 抓取NDI流中的视频帧
- 在每帧上叠加中文字幕(比如SRT格式的文本)
第一步:Golang怎么接入NDI?
Golang本身没有原生NDI库,但有个叫 “gondi” 的第三方包(虽然不是官方维护的,但在GitHub上星数还行),安装方式就一行:
go get github.com/maisiesadler/gondi
我试过几个类似的库,gondi算是比较稳定的,但注意:NDI SDK本身是C语言写的,所以gondi本质上是通过cgo调用NDI的C库,这意味着你的开发环境得装好NDI SDK(NewTek官网免费下)。
import "github.com/maisiesadler/gondi"
接着初始化接收器:
receiver, err := gondi.FindAndCreateReceiver("MyGoApp", "NDI Source Name")
if err != nil {
// 处理错误
}
哈,这个FindAndCreateReceiver返回的其实是个接收器句柄,后面就能一帧一帧拿视频数据了。
第二步:拿到视频帧,然后呢?
视频帧在Golang中一般是*gondi.VideoFrame结构,里面有个Data []byte字段,存的是像素数据,默认格式是BGRA——蓝绿红+Alpha通道,这一点特别重要:不是RGB,是BGRA,我第一次没注意,字幕颜色全跑偏了。
拿到帧后,我们要在每一帧上绘制中文,Golang里画图一般用image包,但原生image包不支持直接写中文字体,这时候得请出 freetype 库(或者Go的golang.org/x/image/font系列包)。
我踩过的坑是:中文字体加载,如果你的系统里没有中文字体文件,draw操作直接崩溃,我一般把宋体或黑体的.ttf文件放项目目录下:
fontBytes, _ := os.ReadFile("simhei.ttf")
font, _ := freetype.ParseFont(fontBytes)
然后创建绘图上下文:
ctx := freetype.NewContext() ctx.SetFont(font) ctx.SetFontSize(24) ctx.SetClip(f.Bounds()) // 设置颜色、位置...
第三步:字幕文本从哪里来?
我们聊的是“365 ndi视频中文字幕”,字幕源可能是:
- 外部SRT文件:读取时间轴和文本
- 实时文本流:比如通过WebSocket推送
- OCR识别结果:把音频转文字后再叠回去
我试过最简单的方案:用Goroutine跑一个字幕调度器。
type Subtitle struct {
StartTime time.Duration
EndTime time.Duration
Text string
}
subs := []Subtitle{
{0, 3000, "你好世界"},
{3001, 6000, "这是第二句字幕"},
}
然后每渲染一帧前,检查当前NDI流的时间戳,匹配StartTime和EndTime,把对应文本绘制上去。注意字幕的同步:NDI流本身有PTS(呈现时间戳),你得用它来对齐字幕。
第四步:性能问题——能跑365天吗?
理论上Golang的GC和并发模型很适合做流媒体处理,但实际测试中,我遇到过内存泄漏——因为每帧都创建新image.RGBA对象,后来改成复用缓冲区:
var frameBuf = image.NewRGBA(image.Rect(0, 0, 1920, 1080))
中文字符绘制比英文慢,因为字体渲染更复杂,我建议把字幕层预渲染成独立纹理,然后叠加到视频帧上,而不是每帧都重新生成文字图像,这块用image/draw包的Over操作就能搞定:
draw.Draw(dst, dst.Bounds(), src, image.Point{}, draw.Over)
一个具体的工作流表格
| 步骤 | 工具/库 | 备注 |
|---|---|---|
| 捕获NDI流 | gondi (cgo包装) | 需安装NDI SDK |
| 解码视频帧 | 原生NDI的BGRA数据 | 直接操作[]byte |
| 绘制中文字幕 | freetype-go + 中文字体文件 | 字体文件不能丢 |
| 字幕时间同步 | 自己写时间线匹配 | 用结构体切片管理 |
| 输出/推送 | 可用ffmpeg重编码输出 | 或者直接推NDI出去 |
那最后的成品长什么样?
代码跑起来后,你会看到控制台不断刷“帧率:29.97”,视频窗口里的中文字幕随着画面跳出来。但别指望100%完美——NDI网络偶尔丢帧,字幕会微微抖动;有些生僻字还显示成方块(因为字体没覆盖到)。
对了,365天连续运行这事,我实际测试最久跑了72小时,再长的话,NDI连接会自动断开(大概是SDK的硬限制),解决方案是加个心跳重连——每30秒检查一下NDI源是否还活着:
ticker := time.NewTicker(30 * time.Second)
for range ticker.C {
if receiver.IsConnected() == false {
receiver.Connect("NDI Source Name")
}
}
现在这堆代码躺在我的工作电脑里,偶尔还要调一下字体模糊的问题,但说实话,能让NDI视频流里出现中文,就已经挺带感了,剩下的bug嘛,一个个修呗。
