ffmpeg gl-transitions 图片合成视频 转场特效

参考 https://github.com/transitive-bullshit/ffmpeg-gl-transition

插件本身支持 GPU 加速功能,需要用到 EGL 安装模式,本案例没有使用 EGL,部署环境为 Ubuntu 18.04

注意0:按照 git 教程来反正我的 Ubuntu 是死活启动失败,报如下错误,最后按照 Dockerfile 来安装才可以正常运行,花费了好长时间,告诫需要的朋友们。

[AVFilterGraph @ 0x55c5007bc180] Error initializing filter 'gltransition'
Error initializing complex filters.
Operation not permitted

注意1:如果是在没有显示设备的服务器上安装运行,需要安装xvfb,然后在 ffmpeg 的执行命令前加xvfb-run -a -s '+iglx -screen 0 1920x1080x24',该命令的具体含义参考四、docker 环境下的开发

一、安装环境

$ sudo apt-get -y update && apt-get -y upgrade
$ sudo apt-get -y install gcc g++ make xorg-dev pkg-config \
libglew2.0 libglew-dev libglfw3-dev \
nasm yasm libx264-dev libx265-dev libvpx-dev libglu1-mesa-dev \
libmp3lame-dev libopus-dev libfdk-aac-dev

二、安装 ffmpeg 和转场特效过滤器 ffmpeg-gl-transition

$ cd ~
$ git clone https://github.com/transitive-bullshit/ffmpeg-gl-transition.git
$ cd ffmpeg-gl-transition
$ git clone https://github.com/gl-transitions/gl-transitions.git
$ cd ..
$ wget https://www.ffmpeg.org/releases/ffmpeg-4.2.2.tar.gz
$ tar -zxvf ffmpeg-4.2.2.tar.gz
$ grep -v "define GL_TRANSITION_USING_EGL" \
ffmpeg-gl-transition/vf_gltransition.c > \
ffmpeg-4.2.2/libavfilter/vf_gltransition.c
$ cd ffmpeg-4.2.2/
$ git apply ../ffmpeg-gl-transition/ffmpeg.diff
$ ./configure \
--enable-libx264 \
--enable-libx265 \
--enable-libvpx  \
--enable-libfdk-aac \
--enable-libmp3lame \
--enable-libopus \
--enable-nonfree \
--enable-gpl \
--enable-opengl \
--enable-filter=gltransition \
--extra-libs='-lGLEW -lglfw -ldl'
$ make -j
$ sudo make install
$ sudo apt-get -y install xvfb

查看 ffmpeg 是否安装成功:

$ ffmpeg

如果报错,可以查看 ffbuild/config.log 日志报错信息,根据报错信息添加依赖

ffmpeg: error while loading shared libraries: libGLEW.so.2.1: cannot open shared object file: No such file or directory

查看 ffmpeg 的时候提示找不到 libGLEW.so.2.1,是因为该文件在 /usr/lib64/ 路径下,ffmpeg 从 /usr/lib/ 路径下查找

$ sudo ln -s /usr/lib64/libGLEW.so.2.1 /usr/lib/libGLEW.so.2.1
$ ffmpeg
ffmpeg version 4.2.2 Copyright (c) 2000-2019 the FFmpeg developers
  built with gcc 7 (Ubuntu 7.4.0-1ubuntu1~18.04.1)
  configuration: --enable-libx264 --enable-gpl --enable-opengl --enable-filter=gltransition --extra-libs='-lGLEW -lglfw'
  libavutil      56. 31.100 / 56. 31.100
  libavcodec     58. 54.100 / 58. 54.100
  libavformat    58. 29.100 / 58. 29.100
  libavdevice    58.  8.100 / 58.  8.100
  libavfilter     7. 57.100 /  7. 57.100
  libswscale      5.  5.100 /  5.  5.100
  libswresample   3.  5.100 /  3.  5.100
  libpostproc    55.  5.100 / 55.  5.100
Hyper fast Audio and Video encoder
usage: ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...

Use -h to get full help or, even better, run 'man ffmpeg'

查看 ffmpeg-gl-transition 是否安装上

$ ffmpeg -v 0 -filters | grep gltransition
 T.. gltransition      VV->V      OpenGL blend transitions

三、图片合成带转场效果视频

1、图片合成视频,指定转场时间和转场特效

$ ffmpeg \
-t 1 -loop 1 -i input0.jpg \
-t 1 -loop 1 -i input1.jpg \
-filter_complex \
"gltransition=offset=0.5:duration=0.5:source=/home/jl/ffmpeg-gl-transition/gl-transitions/transitions/crosswarp.glsl" \
-y out.mp4

效果如下图:

参数:

  • -t
    图片转为视频的持续时间
  • -loop
    图片循环,如果设置为1,则循环输入,预设值为0。
  • -i
    输入图片
  • -t 1 -loop 1 -i input0.jpg
    整体意思即为图片循环生成持续1秒的视频
  • -filter_complex
    过滤器
  • gltransition
    指定使用 ffmpeg-gl-transitions 的过滤器
  • offset
    (浮点型,默认 0)指定转场效果在 0.5s 开始
  • duration
    (浮点型,默认 1)转场效果持续时间
  • source
    转场效果指定的文件的路径,注意一定要使用全路径,~ 路径识别不了,转场文件可以从 gl-transitions 获取,如果不指定 source,则转场效果为默认的淡入淡出效果。
  • -y
    如果输出文件已存在则覆盖

注意: 该表达式只是一个简单的事例,合成视频的时长为第二个视频的时长。

2、多图片合成带音乐的、时长叠加的视频

$ ffmpeg -hide_banner \
-t 3 -loop 1 -i input0.jpg \
-t 3 -loop 1 -i input1.jpg \
-t 2 -loop 1 -i input2.jpg \
-stream_loop -1 -i music.mp3 -acodec aac \
-filter_complex "\
[0]split[v_sp_0_0][v_sp_0_1];[v_sp_0_0]trim=0:2[v_tr_0_0];[v_sp_0_1]trim=2:3[v_tr_0_1];[v_tr_0_1]setpts=PTS-STARTPTS[v_st_0];\
[1]split[v_sp_1_0][v_sp_1_1];[v_sp_1_0]trim=0:2[v_tr_1_0];[v_sp_1_1]trim=2:3[v_tr_1_1];[v_tr_1_1]setpts=PTS-STARTPTS[v_st_1];\
[v_st_0][v_tr_1_0]gltransition=duration=1:source=/home/jl/ffmpeg-gl-transition/gl-transitions/transitions/SimpleZoom.glsl[v0];\
[v_st_1][2]gltransition=duration=1:source=/home/jl/ffmpeg-gl-transition/gl-transitions/transitions/CrossZoom.glsl[v1];\
[v_tr_0_0][v0][v1]concat=n=3[v];\
afade=t=out:st=5:d=2" \
-map "[v]" \
-map "3:a" \
-t 7 \
-shortest \
-c:v libx264 \
-profile:v main \
-pix_fmt yuv420p \
-preset fast \
-y \
out.mp4

参数:

  • -hide_banner
    所有 FFmpeg 工具通常都会显示版权声明,构建选项和库版本,此选项可用于禁止打印此信息。
  • -stream_loop
    输入流循环的次数,0 表示无循环,-1 表示无限循环,即音乐循环播放。
  • -acodec aac
    设置音频编解码为 acc 模式
  • [0]split[v_sp_0_0][v_sp_0_1];[v_sp_0_0]trim=0:2.0[v_tr_0_0];[v_sp_0_1]trim=2.0:3[v_tr_0_1];[v_tr_0_1]setpts=PTS-STARTPTS[v_st_0]
    将第一个视频拆分为 0-2s 和 2s-3s 两个视频;帧是从每个输入视频按时间戳顺序获取的,因此,如果它们的初始时间戳不同,则最好将两个输入通过 setpts = PTS-STARTPTS 过滤器传递,以使它们以相同的0时间戳开始,即为了让 2s-3s 的视频和第二个视频合并作为转场视频,需要将 2s-3s 视频的初始时间戳设为 0。
  • [v_st_0][v_tr_1_0]gltransition=duration=1:source=/home/jl/ffmpeg-gl-transition/gl-transitions/transitions/SimpleZoom.glsl[v0]
    将第一个视频拆分出来的 2s-3s 的视频与第二个视频的初始视频合并生成时长 1s 的转场视频。
  • [v_tr_0_0][v0][v1]concat=n=3[v]
    合并视频为一个完整的输出视频
  • afade=t=out:st=5:d=2
    设置从 5s 开始,持续 2s 的音乐淡出效果。转场时间会合并到下一个视频的初始时间中,所以合成视频的时长为 2+2+2=6s,视频在播放时最后 1-2s 通常就黑屏了,所以视频时长加 1s。
  • -map "[v]"
    将合成的视频输入流 v 指定为输出文件的源
  • -map "3:a"
    将第四个文件作为视频音频文件
  • -t 7
    输出视频时长为 7s
  • shortest
    最短的输入流结束时,完成编码。
  • -c:v libx264
    输出视频编码格式
  • -profile:v main
    指定视频画质,baseline:基本画质,extended:进阶画质,main:主流化之,high:高级画质。
  • -pix_fmt yuv420p
    设置像素格式为 yuv420p
  • -preset fast
    调节编码速度和质量的平衡,有ultrafast、superfast、veryfast、faster、fast、medium、slow、slower、veryslow、placebo这10个选项,编码速度从快到慢,视频质量越来越好。

3、合成带背景图的视频

ffmpeg gl-transitions 图片合成视频 转场特效_第1张图片

如上背景图为 1080x1920 尺寸的,中间的透明区域为 1080x720 的,当我们需要将上面的图片合成的视频以如下背景图作为背景的时候,那么我们的视频尺寸就必须缩放为 1080x720 的视频,我们当然可以通过 -s 1080x720 来缩放上面生成的视频,但是会存在视频变形的情况,我们可以通过如下命令来等比例缩放图片从而一步到位生成带背景的视频。

$ ffmpeg -hide_banner \
-t 3 -loop 1 -i input0.jpg \
-t 3 -loop 1 -i input1.jpg \
-t 2 -loop 1 -i input2.jpg \
-i bg.png \
-stream_loop -1 -i music.mp3 -acodec aac \
-filter_complex "\
[0]scale=min(iw*720/ih\,1080):min(720\,ih*1080/iw),pad=1080:720:(1080-iw)/2:(720-ih)/2[p0];\
[1]scale=min(iw*720/ih\,1080):min(720\,ih*1080/iw),pad=1080:720:(1080-iw)/2:(720-ih)/2[p1];\
[2]scale=min(iw*720/ih\,1080):min(720\,ih*1080/iw),pad=1080:720:(1080-iw)/2:(720-ih)/2[p2];\
[p0]split[v_sp_0_0][v_sp_0_1];[v_sp_0_0]trim=0:2[v_tr_0_0];[v_sp_0_1]trim=2:3[v_tr_0_1];[v_tr_0_1]setpts=PTS-STARTPTS[v_st_0];\
[p1]split[v_sp_1_0][v_sp_1_1];[v_sp_1_0]trim=0:2[v_tr_1_0];[v_sp_1_1]trim=2:3[v_tr_1_1];[v_tr_1_1]setpts=PTS-STARTPTS[v_st_1];\
[v_st_0][v_tr_1_0]gltransition=duration=1:source=/home/jl/ffmpeg-gl-transition/gl-transitions/transitions/SimpleZoom.glsl[v0];\
[v_st_1][p2]gltransition=duration=1:source=/home/jl/ffmpeg-gl-transition/gl-transitions/transitions/CrossZoom.glsl[v1];\
[v_tr_0_0][v0][v1]concat=n=3[out];\
[3][out]overlay=0:600[v];\
afade=t=out:st=5:d=2" \
-map "[v]" \
-map "4:a" \
-t 7 \
-shortest \
-c:v libx264 \
-profile:v main \
-pix_fmt yuv420p \
-preset fast \
-y \
out.mp4

参数:

  • -i bg.png
    指定背景图
  • [0]scale=min(iw*720/ih\,1080):min(720\,ih*1080/iw),pad=1080:720:(1080-iw)/2:(720-ih)/2[p0]
    等比例缩放为 1080x720 的居中图片,pad指定尺寸不足的地方以黑色背景填充。
  • 缩放扩展:scale=1080:720:force_original_aspect_ratio=decrease,pad=1080:720:(ow-iw)/2:(oh-ih)/2
    force_original_aspect_ratio参数,decrease指定图片缩小,increase扩大。
  • [3][out]overlay=0:600[v]
    将合成的视频覆盖到第四个输入文件(即背景图)上,x 轴位移 0,y 轴位移 600((1920-720)/2=600)。

合成视频效果如下
ffmpeg gl-transitions 图片合成视频 转场特效_第2张图片
注意:有时候会因为生成视频的采样纵横比不同而报如下错误

[Parsed_concat_16 @ 0x55f8751e47c0] Input link in2:v0 parameters (size 1080x720, SAR 0:1) do not match the corresponding output link in0:v0 parameters (1080x720, SAR 1214:1215)
[Parsed_concat_16 @ 0x55f8751e47c0] Failed to configure output pad on Parsed_concat_16
Error reinitializing filters!
Failed to inject frame into filter network: Invalid argument
Error while processing the decoded data for stream #4:0
Conversion failed!

SAR :Sample aspect ratio,采样纵横比。
concat 滤波器要求它的所有视频输入具有相同的分辨率和采样纵横比,报错信息显示SAR 1214:1215,近乎正方形1:1,所以我们可以在图片缩放生成视频的时候指定采样纵横比为正方形setsar=1/1从而解决该问题。

[0]scale=min(iw*720/ih\,1080):min(720\,ih*1080/iw),setsar=1/1,pad=1080:720:(1080-iw)/2:(720-ih)/2[p0];\
[1]scale=min(iw*720/ih\,1080):min(720\,ih*1080/iw),setsar=1/1,pad=1080:720:(1080-iw)/2:(720-ih)/2[p1];\
[2]scale=min(iw*720/ih\,1080):min(720\,ih*1080/iw),setsar=1/1,pad=1080:720:(1080-iw)/2:(720-ih)/2[p2];\

4、合成带高斯模糊背景图的视频

ffmpeg gl-transitions 图片合成视频 转场特效_第3张图片

思路为先将图片合成高斯模糊背景视频,然后将生成的视频覆盖到背景视频上。原图为 1200x800,要想生成 3 的效果,就要将当前视频的图片缩放为 2880x1920,然后截取中间的 1080x1920 部分。下面命令前 3 个输入图片是作为高斯背景合成视频使用的。

$ ffmpeg -hide_banner \
-t 2 -loop 1 -i input0.jpg \
-t 2 -loop 1 -i input1.jpg \
-t 2 -loop 1 -i input2.jpg \
-t 3 -loop 1 -i input0.jpg \
-t 3 -loop 1 -i input1.jpg \
-t 2 -loop 1 -i input2.jpg \
-stream_loop -1 -i music.mp3 -acodec aac \
-filter_complex "\
[0][1][2]concat=n=3[bout];\
[bout]scale=2880:1920[bv0];\
[bv0]crop=1080:1920:900:0[bv1];\
[bv1]avgblur=sizeX=90[bv2];\
[bv2]eq=contrast=1:brightness=-0.1[bv];\
[3]scale=min(iw*720/ih\,1080):min(720\,ih*1080/iw),pad=1080:720:(1080-iw)/2:(720-ih)/2[p0];\
[4]scale=min(iw*720/ih\,1080):min(720\,ih*1080/iw),pad=1080:720:(1080-iw)/2:(720-ih)/2[p1];\
[5]scale=min(iw*720/ih\,1080):min(720\,ih*1080/iw),pad=1080:720:(1080-iw)/2:(720-ih)/2[p2];\
[p0]split[v_sp_0_0][v_sp_0_1];[v_sp_0_0]trim=0:2[v_tr_0_0];[v_sp_0_1]trim=2:3[v_tr_0_1];[v_tr_0_1]setpts=PTS-STARTPTS[v_st_0];\
[p1]split[v_sp_1_0][v_sp_1_1];[v_sp_1_0]trim=0:2[v_tr_1_0];[v_sp_1_1]trim=2:3[v_tr_1_1];[v_tr_1_1]setpts=PTS-STARTPTS[v_st_1];\
[v_st_0][v_tr_1_0]gltransition=duration=1:source=/home/jl/ffmpeg-gl-transition/gl-transitions/transitions/SimpleZoom.glsl[v0];\
[v_st_1][p2]gltransition=duration=1:source=/home/jl/ffmpeg-gl-transition/gl-transitions/transitions/LinearBlur.glsl[v1];\
[v_tr_0_0][v0][v1]concat=n=3[out];\
[bv][out]overlay=0:600[v];\
afade=t=out:st=5:d=2" \
-map "[v]" \
-map "6:a" \
-t 7.0 \
-shortest \
-c:v libx264 \
-profile:v main \
-pix_fmt yuv420p \
-preset fast \
-y \
out.mp4

参数:

  • [bout]scale=2880:1920[bv0]
    将前三个视频合成的视频缩放为 2880x1920
  • [bv0]crop=1080:1920:900:0[bv1]
    2880x1920 的视频截取 x 轴位移 900,y 轴位移 0 的中间的 1080x1920 部分。
  • [bv1]avgblur=sizeX=90[bv2]
    均值模糊,sizeX 的值越大越模糊,即产生高斯模糊效果,默认值 0。
  • [bv2]eq=contrast=1:brightness=-0.1[bv]
    降低高斯模糊背景亮度,突出中间的视频。

四、docker 环境下的开发

1、docker 的安装配置

(1) 参考官网 https://docs.docker.com/install/linux/docker-ce/ubuntu/ 安装 docker 。
(2) Docker 需要用户具有 sudo 权限,为了避免每次命令都输入sudo,可以把用户加入 Docker 用户组 官方文档

$ sudo groupadd docker
$ sudo usermod -aG docker $USER
$ newgrp docker

$USER 为你需要赋权限的用户名

2、docker 环境下 ffmpeg-gl-transition 的安装

(1) 复制 ffmpeg-gl-transition 项目中的 Dockerfile 文件,并创建 image 文件,大概需要十几分钟的时间,视机器配置和网络状况而定。

$ mkdir docker-ffmpeg
$ cp ../docker-ffmpeg/Dockerfile .
$ docker image build -t docker-ffmpeg .

(2) 查看 image 文件并启动容器
注意:docker 访问宿主机文件需要挂载宿主机目录,指定目录后 docker 的所有文件操作都要在这个目录下进行,即下文指定的-v /home/jl:/home/jl

$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
docker-ffmpeg       latest              079580061992        3 days ago          1.96GB
debian              stretch             f6c68e2ad82a        3 weeks ago         101MB
$ id jl
uid=1000(jl) gid=1000(jl)=1000(jl),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),116(lpadmin),126(sambashare),1001(docker)
$ docker container run -d -u 1000:1000 -it -v /home/jl:/home/jl docker-ffmpeg

参数:

  • id jl
    查看用户 jl 的 uid,因为 docker 默认挂载的用户为 root,那么就会导致非 root 用户对 docker 生成的文件没有写权限,那么就需要我们为 docker 指定宿主机的用户。
  • -d
    后台运行 docker
  • -u 1000:1000
    docker 指定宿主机用户
  • -it
    容器的 Shell 映射到当前的 Shell,然后你在本机窗口输入的命令,就会传入容器。
  • -v /home/jl:/home/jl
    容器挂载宿主机的 /home/jl 目录

3、宿主机运行 docker 的 ffmpeg 命令

$ docker container ls --all
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                    PORTS               NAMES
850e26f669c7        docker-ffmpeg       "/bin/sh -c /bin/bash"   3 days ago          Exited (137) 2 days ago                       cool_dubinsky
$ docker container start 850e26f669c7
850e26f669c7
$ docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
850e26f669c7        docker-ffmpeg       "/bin/sh -c /bin/bash"   3 days ago          Up 5 seconds                            cool_dubinsky
$ docker exec 850e26f669c7 xvfb-run -a -s '+iglx -screen 0 1920x1080x24' ffmpeg
ffmpeg version N-96379-g0dc0837960 Copyright (c) 2000-2020 the FFmpeg developers
  built with gcc 6.3.0 (Debian 6.3.0-18+deb9u1) 20170516
  configuration: --enable-libx264 --enable-libx265 --enable-libvpx --enable-libfdk-aac --enable-libmp3lame --enable-libopus --enable-nonfree --enable-gpl --enable-opengl --enable-filter=gltransition --extra-libs='-lGLEW -lglfw -ldl'
  libavutil      56. 38.100 / 56. 38.100
  libavcodec     58. 65.103 / 58. 65.103
  libavformat    58. 35.102 / 58. 35.102
  libavdevice    58.  9.103 / 58.  9.103
  libavfilter     7. 71.100 /  7. 71.100
  libswscale      5.  6.100 /  5.  6.100
  libswresample   3.  6.100 /  3.  6.100
  libpostproc    55.  6.100 / 55.  6.100
Hyper fast Audio and Video encoder
usage: ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...

Use -h to get full help or, even better, run 'man ffmpeg'

参数

  • docker container ls --all
    查看所有的 docker 容器,包括未运行的。
  • docker container start 850e26f669c7
    启动 docker-ffmpeg 容器
  • docker container ls
    显示运行的容器
  • docker exec 850e26f669c7
    运行 docker 850e26f669c7 中的命令
  • xvfb-run -a -s '+iglx -screen 0 1920x1080x24’
    ffmpeg 依赖 opengl,需要桌面环境,xvfb 则提供了一个需要的虚拟的环境;
    -a:获取一个空闲的服务运行,允许多个 xvfb 命令同时执行,否则只能执行一个;
    -s:指定虚拟窗口;
    +iglx:允许创建间接的 GLX 上下文,ffmpeg-gl-transition 依赖 GLX。

上面的视频合成的 ffmpeg 命令都可以通过 docker 容器来运行。
注意:文件路径都要用绝对路径,否则会提示找不到文件。

参考文献:
ffmpeg-gl-transition
gl-transitions
GLTransitions
ffmpeg 官网文档
ffmpeg - 将两个文件合并到ffmpeg时,视频编辑得到异常
ffmpeg实现同视频做模糊背景并前景正常播放
Docker 入门教程
docker挂载volume的用户权限问题,理解docker容器的uid

你可能感兴趣的:(ffmpeg,图片合成视频)