生成视频封面 | 青训营笔记

235 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第4篇笔记。

在项目中需要对上传的视频生成封面,面对这样的需求,首先就会想起著名的ffmpegffmpeg是一个录制、转码、流式处理视频和音频的跨平台命令行工具。各个编程语言基本上都有相应的ffmpeg包,它们的基本实现方式都是通过拼接ffmpeg命令,然后在命令行中执行命令。这种方式显然执行效率比较低,对输入输出方式的限制比较大,但是也没有更好的方式了。

项目中通过ffmpeg-go在go中使用ffmpeg,安装:
go get -u github.com/u2takey/ffmpeg-go

运行前需要提前安装ffmpeg:
sudo apt install ffmpeg

视频文件通过http的post请求中发送,后端收到后需要先把文件存储到文件夹中,项目使用gin框架,那么最简单的方式是:

data, err := ctx.FormFile("data")
...
err = ctx.SaveUploadedFile(data, videoName)

ffmpeg抽帧参考官网上的示例

buf := bytes.NewBuffer(nil)
err := ffmpeg.Input(inFileName).
        Filter("select", ffmpeg.Args{fmt.Sprintf("gte(n,%d)", frameNum)}).
        Output("pipe:", ffmpeg.KwArgs{"vframes": 1, "format": "image2", "vcodec": "mjpeg"}).
        WithOutput(buf, os.Stdout).
        Run()
if err != nil {
        panic(err)
}

其中inFileName是视频的存储路径,frameNum是抽取的帧,buf用来存储图片,也可以使用文件路径。

看到其他人的做法也有自己拼接cmd命令的方式实现,也是可以的。

cmd := exec.Command(
   "ffmpeg", "-i", "./public/videos/"+finalName,
   "-vf", "select=eq(n\, 300)", "-frames", "1",
   "./public/covers/"+finalName+".jpg",
)
//cmd.Stderr = os.Stderr // 输出错误信息
if err := cmd.Run(); err != nil {
   log.Fatalln("Video cover generation failed")
}

通过命令行执行可能会存在效率问题,可以考虑使用协程、消息队列或者RPC调用等方式减少生成封面所需要等待的时间。 不知道是否存在可以直接传入bytes的使用方式,使用起来比先把文件存储到本地会方便很多。

除了ffmpeg外,还可以使用云服务商的对象存储服务,如七牛云提供的免费云存储,可以自动生成视频封面。在读取视频、生成封面等情况下会方便很多。