分布式视频转码需要的三个最基本的视频处理流程通过FFmpeg都可以达到了。
1.切片

1
2
ffmpeg -i input.mp4 -acodec copy -vcodec copy \
  -f segment -segment_time 20 -reset_timestamps 1 -map 0:0 -map 0:1 %d.mp4

2.转码

1
ffmpeg -i input.mp4 -vcodec libx264 -s 1280x720 output.mp4

3.合并

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

  接下来需要设计一个分发系统,将切片分发到各client端,转码完成后再进行合并。之前也在网上搜索过有没有类似的解决方案,然而居然要涉及到Hadoop。个人使用的转码集群系统要用Hadoop来架构那不是闲的蛋疼么,再说处理逻辑也并不复杂。 所以就手撸一个好了,正好在学Golang,而且考虑到Docker化部署client转码集群会很方便,Golang静态编译配合alpine系统可以最小化容器的大小,所以就选定用go来实现。

先上代码:
https://github.com/ggggle/goVideoCompressor
  server.go是服务端代码,client.go是集群端代码。
  server端额外的依赖项除了FFmpeg静态编译版本之外,还需要一个web服务器软件,nginx或者Apache。以及一个ftp服务器软件,推荐使用ProFTPD。由于懒,所以并没有将server端docker化部署。

设计逻辑

server端

1.在server端将原始视频进行切割
2.启动server建立tcp监听,等待client连接
3.与client建立连接后,分配任务,告知client切片位置,client通过http请求下载视频片段
4.根据client通过tcp连接返回的转码结果,维护本地任务列表
5.当所有任务均完成后,退出
6.将切片进行合并,视频转码完成

client端

1.每隔10s尝试连接一次server端
2.连接成功后收到任务分配,通过wget下载切片并进行转换
3.将转换后的切片通过ftp传回server端,并通过tcp连接反馈转码结果
4.断开tcp连接
  以上是最基本的逻辑,更详细的内容可以参看代码。

使用方法

server端

  server内部已经封装了所有需要的ffmpeg命令行操作

  更具体的某个command有哪些参数可以通过以下命令查看

1
./server help s;  ./server help c;  ./server help t;

  可以通过调用以下命令将input.mp4按每块30MB的大小进行分割,分割出来的切块将会保存在当前目录一个名为"12input.mp4"的文件夹里。

1
./server s input.mp4 -s 30

  调用以下命令可以对"12input.mp4"文件夹里的切块进行任务分配,即建立监听端口等待client连接,可以通过 −f 指定FFmpeg转码参数,通过 −p 指定只转换某几块切片,通过 −−port 指定监听端口,默认监听端口为8055。

1
./server c 12input.mp4 -f"-vcodec libx264 -s 1280x720"

  在调用以上命令的同时,会在 /home/vuser 目录建立"12input.mp4"同名目录,并且在目录内自动生成"filelist.txt"和"concat.sh"脚本,用于转换完成后的切片合并。当所有切片转换完成后,在该目录下执行以下命令就可以将所有切片合并在一起,输出"output.mp4"

1
./concat.sh

client端

  client端已经Docker化,可以很方便的进行部署。
DockerHub地址
  整个client镜像的大小仅仅57.5MB,这还是由于ffmpeg的静态编译版本达到了45MB+的大小,占据了很大一部分空间。

1
2
docker run -d -e "SERVER_IP=1.2.4.8" -e "FTP_USERNAME=vuser" -e "FTP_PASSWORD=passwd" \ 
  -e "SERVER_PORT=8055" hey678/myclient

  以上命令在后台运行client,指定server端ip为1.2.4.8,监听端口为8055,server端ftp登录用户名为vuser,密码为passwd。
  然后client端就搭建好了。它会每隔10秒去尝试与server建立连接,接收切片任务并按参数进行转换,转换完成后通过ftp传回。

一次使用流程


  在Google云平台上创建实例,这里选用24核的机器,内存21.75GB已经是最低可配的。硬盘不用很大,最小的10GB固态硬盘即可,系统选择容器优化型的ChromiumOS。虽然$457.96/月的价格似乎很高,但实际上转换完之后立即释放,按秒计费,利用率很高。可以在自动化启动脚本里填写上client的启动命令,这样的话等待实例运行起来之后就会自动执行该脚本,启动client。这里我在一台24核的机器里运行了8个client容器。

1
2
for i in {1..8}; do docker run -d -e "SERVER_IP=1.2.4.8" -e "FTP_USERNAME=vuser" \
  -e "FTP_PASSWORD=passwd" -e "SERVER_PORT=8055" hey678/myclient; done

  一般来说我会创建3台这样配置的机器,也就是有72核进行分布式转码,当转码任务很多时,也会多加一些额外的机器。

  这里找一个不知名的视频来进行转换,首先将该视频切割成33MB大小的块,这个速度很快,根据视频大小,基本30秒之内都能完成。至于想要把块切多大,随心情就好,不过在20~40MB这个区间比较合适。


  开始进行转换,界面将会输出任务分配信息和转码反馈信息,在某一块收到client反馈转化成功的消息后,会从本地删除,所有块转换完成后打印提示消息并自动退出。


  在/home/vuser目录可以找到对应的文件夹,执行concat.sh,输出output.mp4,下载到本地,完成。
  转码完成后如果没有别的视频需要转码记得赶快释放虚拟机实例,不然每一秒都是钱在流失。

PS:
  在代码中附带了digitalOcean虚拟机批量创建销毁,远程批量执行命令的封装代码, doOpt.go,在账号中申请token即可使用,可以很方便的创建销毁容器虚拟机,并运行client。