抖音(极简版)项目问题整理(1) | 青训营笔记

174 阅读3分钟

u=1700510784,156480434&fm=253&fmt=auto&app=138&f=JPEG.jpg

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

在项目开发过程中,我们小组遇到了一些比较不知道如何下手,花了比较长时间的问题,这里整理几个比较棘手问题与当时的解决思路。

截取视频封面

我们小组使用ffmpeg截取视频封面,这个用起来方便又有效,可以自己更改输入的参数,来确定截取的封面是视频第几帧的,通常截取视频第一帧。ffmpeg除了截取视频某帧图片外,还可以执行音频和视频多种格式的录影、转换、串流等许多功能。

FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。

FFmpeg在Linux平台下开发,但它同样也可以在其它操作系统环境中编译运行,包括Windows、Mac OS X等。这个项目最早由Fabrice Bellard发起,2004年至2015年间由Michael Niedermayer主要负责维护。许多FFmpeg的开发人员都来自MPlayer项目,而且当前FFmpeg也是放在MPlayer项目组的服务器上。项目的名称来自MPEG视频编码标准,前面的"FF"代表"Fast Forward"。FFmpeg编码库可以使用GPU加速。

ffmpeg环境配置

首先需要下载ffmpeg这个程序,下载地址:FFMPEG

要根据自身电脑系统选择下载,我们小组成员用的都是win,所以选择下载win版本。下载完后得到一个exe文件,把他放置到环境变量的GOPATH下的bin文件夹里即可。

然后是在项目中从GitHub拉取两个依赖

首先是

go get -u github.com/u2takey/ffmpeg-go ///这个是ffmpeg的仓库

然后是

go get -u github.com/disintegration/imaging ///imaging依赖,用于对图片的操作,保存图片等。

到这里就可以用了,有个要注意的点是,先写代码然后再用go mod tidy的话不能拉取所有需要的依赖,因此还是得手动拉取一下,或者使用goland时在项目文件中import入相应的库即可。

代码部分

ffmpeg-go项目源示例代码

我们先去库的GitHub上学习了相关视频截取图片的用法

官方的示例代码:

func ExampleReadFrameAsJpeg(inFileName string, frameNum int) io.Reader {
	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)
	}
	return buf
}

reader := ExampleReadFrameAsJpeg("./sample_data/in1.mp4", 5)
img, err := imaging.Decode(reader)
if err != nil {
    t.Fatal(err)
}
err = imaging.Save(img, "./sample_data/out1.jpeg")
if err != nil {
    t.Fatal(err)
}

我们组对源代码进行了改动,使之更好理解些。

注意,在import引用时要在"github.com/u2takey/ffmpeg-go"前加上ffmpeg

小组项目中的截取封面函数:

// GetSnapshot 生成视频缩略图并保存(作为封面)
func GetSnapshot(videoPath, snapshotPath string, frameNum int) (snapshotName string) {
   buf := bytes.NewBuffer(nil)
   err := ffmpeg.Input(videoPath).
      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 {
      log.Fatal("生成缩略图失败:", err)
   }

   img, err := imaging.Decode(buf)
   if err != nil {
      log.Fatal("生成缩略图失败:", err)
   }

   err = imaging.Save(img, snapshotPath+".jpeg")
   if err != nil {
      log.Fatal("生成缩略图失败:", err)
   }

   // 成功则返回生成的缩略图名
   names := strings.Split(snapshotPath, "/")
   snapshotName = names[len(names)-1] + ".jpeg"
   return
}

其中函数中的参数的意思分别代表,视频路径: videoPath, 生成的略缩图保存路径: snapshotPath, 略缩图所属帧数: frameNum, 返回值为略缩图文件名: snapshotName。