用Golang给在一起365纪念日视频加点烟火气
- 单站
- 2026-07-08 08:02:45
- 284
在一起365纪念日视频:用Golang给回忆写一段代码
一年前的今天,你和她/他可能刚认识,或者刚决定在一起,那时候的你们,估计没想过时间会过得这么快,365天,说长不长,说短不短,但足够让两个人从陌生到熟悉,从试探到依赖,而当这个节点来临,很多人会想做点什么来纪念——比如一个“在一起365纪念日视频”。
我呢,是个爱折腾的Golang程序员,别人可能用剪映、PR、Final Cut Pro来剪片子,我想着:能不能用代码来生成这个视频?用Golang写一个程序,把这一年的照片、文字、音乐整合到一起,输出一个独一无二的纪念视频。听起来有点技术宅,但做出来的效果其实挺暖的。
第一步:准备素材——照片、文字、音乐
做视频之前,得先想好要放什么素材,我翻了翻手机相册,发现这一年的照片还真不少:第一次一起做饭、第一次吵架后和好的自拍、一起去看的日落、她/他睡着时我偷拍的表情……这些照片,每一张都能让我想起当天的天气、对方穿的衣服、说的话。
我决定用Golang的image包来读取这些照片,再用ffmpeg把它们合成视频帧,Golang标准库里的image/jpeg和image/png可以直接处理图片,但我需要控制每张照片的停留时长、转场效果、再加点文字字幕。
- 照片列表:我写了一个
photos.txt,每一行是照片路径和对应的时间戳。 - :第1天:第一次见面”、“第100天:我们养了猫”、“第365天:有你真好”。
- 背景音乐:挑了首我们都喜欢的歌,用
os/exec调用ffmpeg混音。
说实话,这一步费了不少时间,因为照片尺寸不一样,有的横屏、有的竖屏,直接拼起来会拉伸变形,我写了个函数,把图片统一缩放到1920x1080,然后用黑色背景填充空白部分,效果还行,至少画面是统一的。
第二步:生成视频帧——Golang的image包实战
视频就是连续的图片,每秒24帧或30帧,我选了24帧,因为慢一点,适合纪念视频的节奏,但一张照片要停留多久?我定了3秒,也就是72帧(24帧/秒 * 3秒),这样50张照片,就是150秒,约2分半钟,不长不短。
代码逻辑大概是这样的:
for i, photo := range photoList {
img := readImage(photo.Path)
for frame := 0; frame < 72; frame++ {
// 在图片上画文字
drawText(img, photo.Text, time.Now())
// 保存为帧图片
saveFrame(img, i, frame)
}
}
我用了golang.org/x/image/font和golang.org/x/image/font/basicfont来在图片上写字,字体是默认的等宽字体,清晰度一般,但够用了,如果你想要更漂亮的字体,可以下载.ttf文件,用freetype库渲染。
这里有个坑:文字渲染的位置、大小、颜色都要调,我试了好多次,最后放在图片左下角,白色字体,带黑色描边,这样不管照片背景是什么颜色都能看清,描边的代码我参考了fogleman/gg这个库的思路,但其实用image/draw也能实现。
第三步:合成视频——用ffmpeg命令行
所有帧图片生成后,用ffmpeg把它们合成为视频文件,Golang里用os/exec执行命令行:
cmd := exec.Command("ffmpeg", "-framerate", "24", "-i", "frame_%d.png", "-c:v", "libx264", "-pix_fmt", "yuv420p", "output.mp4")
cmd.Run()
这个过程很慢,50张照片、3600帧(50*72),我的MacBook Pro跑了两分多钟,但看到生成的视频能正常播放时,那种成就感真的很难形容。
然后加背景音乐:
cmd = exec.Command("ffmpeg", "-i", "output.mp4", "-i", "music.mp3", "-c:v", "copy", "-c:a", "aac", "-shortest", "final_video.mp4")
cmd.Run()
这里要注意音频时长和视频时长匹配,我用了-shortest参数,如果音乐比视频长,它会自动截断;如果音乐短,视频末尾会静音,为了效果更好,我提前用Audacity把音乐剪到了2分30秒左右。
第四步:加特效和字幕——让视频更精致
纯图片切换的纪念视频有点单调,我加了简单的交叉淡入淡出效果,这个在ffmpeg里用-filter_complex实现:
ffmpeg -i input.mp4 -vf "fade=in:0:30,fade=out:last:30" output.mp4
但我不想全部淡入淡出,而是每张照片切换时淡入淡出,这个有点复杂,我最后用了mpv的--vf参数做测试,然后写了个脚本批量处理每张照片的帧。
真正的转场效果其实没完全做出来,因为我时间有限,而且ffmpeg的滤镜语法我记不太清,最后我选择了“硬切”——就是直接切,每张照片切换时加一个0.1秒的白色闪光,模拟相机快门效果,这个实现简单:在切换的帧上画一个全白矩形,透明度逐渐降低。
字幕的话,我每张照片底部都写了日期和一句话,用draw.DrawMask把文字画上去,位置固定,字体大小14pt,颜色白色,有些照片文字被内容遮挡了,应该调整位置,但我没来得及改,就这样吧——不完美反而真实。
第五步:导出和分享——把Golang生成的视频发给对方
最终视频是.mp4格式,用H.264编码,大部分设备都能播放,我把它传到百度网盘,把链接发给女朋友,备注:“用Golang写的纪念视频,你看一下。”
她看完后笑着说:“你们程序员表达爱的方式真特别。”我问她哪里特别,她说:“别人送花、送礼物,你送了一串代码的输出结果。”但我知道,她挺喜欢的,因为她说“每个日期和文字都对得上,你是不是翻了我朋友圈?”其实我是对照手机相册的时间戳写的,每条文字背后都有故事。
一些值得注意的细节和坑
| 环节 | 遇到的问题 | 解决办法 |
|---|---|---|
| 图片分辨率不统一 | 横图竖图混在一起,合成后变形 | 统一缩放至1920x1080,空白用黑色填充 |
| 文字被图片内容遮挡 | 白色字在天空背景下看不清 | 加黑色描边(draw.DrawMask两次) |
| 音频时长不匹配 | 音乐比视频长或短 | 用-shortest参数,或提前剪辑 |
| 帧图片过多,磁盘爆满 | 3600帧图片占了几百MB | 合成后批量删除临时帧(os.Remove) |
| 字体不够美观 | 默认字体平淡无奇 | 下载开源字体如Source Han Serif |
如果你也想尝试,建议先用10张照片测试流程。调试通过后再批量处理全部照片,否则生成几千帧图片后才发现错误,会很麻烦。
代码之外的思考:纪念的意义是什么
写这个程序花了我两个周末,第一个周末在解决图片缩放和文字渲染,第二个周末在调ffmpeg的转场效果,过程中我反复看那些照片,记住了一些差点忘记的细节:那张在路边摊吃烧烤的照片,是去年夏天,她辣到不停喝冰水;那张两人坐地铁的照片,是她靠在我肩膀上睡着了,我单手拍的。
有时候我觉得,这个视频不只是为了纪念“在一起365天”,更是为了提醒自己:时间虽然过去了,但那些瞬间还在,而用Golang来制作,只是我表达这些的一种方式。
后来我又想,如果明年纪念日,我是不是应该写一个更高级的版本:加弹幕动画、加背景音乐波形图、甚至是AR特效?但想想还是算了,这个版本虽然粗糙,现在”的产物,明年如果我变得更强了,那明年的视频也会更好,就这样一步步来,挺好的。
最后生成的那个“在一起365纪念日视频”,现在还在我手机里,有时候翻出来看,还是觉得——虽然代码写得不完美,但回忆挺完美的。

下一篇:澳大利亚一海滩惊现神秘金属球