当前位置:首页 > 旅游 > 正文

用Go语言做一周年纪念视频?365天的故事,代码也能这么暖

  • 旅游
  • 2026-06-28 20:26:39
  • 466
摘要: 去年这时候,我窝在出租屋里,对着屏幕敲下第一行Go代码,不是因为什么高大上的理由——女朋友说:“咱俩一周年,你能不能做个视频?”...

去年这时候,我窝在出租屋里,对着屏幕敲下第一行Go代码,不是因为什么高大上的理由——女朋友说:“咱俩一周年,你能不能做个视频?”我心想,剪辑软件太复杂,不如用代码生成,Go语言和纪念视频就这么扯上了关系。

为什么要用Go做纪念视频?

你可能觉得,做视频不是该用Premiere或者Final Cut吗?对,但那些工具是给专业人士的,我就想要个简单的,能把365张照片串起来,每张显示1秒,配上音乐和文字,Go语言的优势这时候就出来了:

  • 并发处理:加载365张照片,单线程得卡半天,Go的goroutine能同时处理多张图片,速度飞起
  • 跨平台:写完编译一下,Windows、Mac、Linux都能跑,不用装一堆依赖
  • 标准库丰富image包处理图片,os包读写文件,连视频编码都有第三方库支持

说实话,我一开始也犹豫过,Python不是更方便?但试了才发现,Go编译出来的程序就一个二进制文件,扔给女朋友她双击就能用——这才是我要的“傻瓜式”体验。

第一步:收集365天的素材

要做一周年纪念视频,首先得有365张照片,我翻了手机相册,微信聊天记录,甚至从朋友圈截图,按日期分好文件夹,格式统一成.jpg,名字也规范一下:2024-01-01.jpg2024-01-02.jpg……虽然手动改文件名很烦,但Go程序处理起来就简单了。

这里有个小坑:Go的time包解析日期格式时,要用2006-01-02这个魔数(Go团队的设计,很奇葩但习惯了就好),我写了个函数,遍历文件夹,按日期排序:

files, _ := filepath.Glob("photos/*.jpg")
sort.Slice(files, func(i, j int) bool {
    // 解析文件名中的日期,比较大小
})

其实这个排序逻辑我改了三次,第一次按文件名排序不对,第二次按修改时间但照片被重命名过,最后老老实实解析日期字符串。写代码就是这样,你以为简单的需求,总会蹦出意外。

第二步:图片处理与编码

光有图片不够,还得合成视频,Go里有ffmpeg-go这个库,封装了FFmpeg的命令行接口,但我不想依赖外部程序——万一女朋友电脑没装FFmpeg呢?后来找到了goav,直接调用FFmpeg的C库。

安装过程挺折腾,需要编译C代码,Windows上还得装MinGW,但搞定之后,写起来就顺畅了:

步骤 代码量 耗时
读取图片 30行 2小时
调整尺寸 20行 1小时
添加文字水印 50行 3小时
编码为视频 100行 5小时

看到没,一周年的视频,代码量就200行,但调试花了十几个小时,最坑的是文字水印,中文字体在Go里要指定ttf文件路径,我用的是微软雅黑,但Mac上没有——得放个字体文件在程序目录里。

第三步:视频拼接与音频

视频编码出来是一段一段的,因为FFmpeg对内存有限制,我每30张照片生成一个临时视频,最后再拼接,Go的exec包调用FFmpeg做拼接,命令大概是:

ffmpeg -f concat -i filelist.txt -c copy output.mp4

音频更简单,找一首我们俩都喜欢的歌,截取刚好365秒(365张照片,每张1秒),用FFmpeg-ss-t参数剪裁,然后混流。

这里我要吐槽一下自己: 第一次生成的视频,第200张照片之后全都模糊了,因为我把图片分辨率设成了1920x1080,但有些照片是竖屏拍的,强制拉伸导致变形,后来加了居中裁剪的逻辑,把宽高比不一致的图片补白边——虽然不完美,但至少看着正常。

第四步:加字幕和特效

一周年嘛,总得有点仪式感,我在视频开头加了“365天,每天都是你”这样的字幕,Go的image包可以绘制文字,但不太灵活,换成了freetype库,可以控制字体大小、颜色、位置。

写了个循环,每张照片右下角显示“第X天”:

for i, photo := range photos {
    // 在图片上绘制文字
    addLabel(photo, fmt.Sprintf("Day %d", i+1))
}

刚开始文字位置写死了,结果有些照片内容被挡住,后来改成根据图片亮度自动调整文字颜色——亮背景用深色字,暗背景用浅色字。这个功能花了整整一个下午才调好,但效果确实不错。

第五步:性能优化

365张照片,每张1920x1080,处理起来内存占用接近2GB,我一开始用的image.Decode一次加载所有图片,程序直接崩溃,换成了流式处理,读取一张,处理一张,立即编码,然后释放内存:

  • 使用sync.Pool复用图片对象
  • bufio读取文件,减少IO次数
  • 开启4个goroutine并行处理图片

优化后的程序,从30分钟缩短到8分钟。 还得看CPU性能,我的老笔记本风扇呼呼转,但至少没死机。

最终效果

成品视频大概1.5GB,长度6分5秒(365秒),女朋友看完哭了——不是因为感动,是笑我:“这字幕怎么还有错别字?”原来我把“纪念”写成了“记念”,Go代码里字符串拼写错误。

嗯,代码世界里没有“差不多”,但生活里可以有。 一周年视频不算完美,照片排序乱了五六张,音乐在最后两秒有点卡顿,但这些都是真实存在的回忆。

如果你也想用Go做纪念视频,我的建议是:

  1. 先手动整理素材,让程序只处理干净的输入
  2. 借鉴开源项目,比如ffmpeg-gogoav的示例代码
  3. 不要追求完美,视频上有个小瑕疵,反而显得真实
  4. 提前测试环境,字体文件、FFmpeg路径这些容易忽略

365天过去,我又用同样的代码做了二周年纪念视频,这次只改了照片目录和音乐文件,跑了20分钟就生成好了。你看,一周年辛辛苦苦搭的工具,二周年就能直接复用。 这也是为什么我推荐Go——编译后的程序是静态的,只要不改依赖,几年后还能跑。

代码越写越烂,但视频越来越有味道,去年那些模糊的照片,现在看起来格外珍贵。而Go语言,就是这个故事的记录者。

用Go语言做一周年纪念视频?365天的故事,代码也能这么暖