ffmpeg 再编译使用 ffmpeg-gl-transition 以丰富视频特效以及支持有透明通道的视频合成

一、背景、需求

FFMPEG 提供的视频动效效果目前看是较为生硬的。
使用这个插件拓展之后,就可以满足下面的需求。

Q:如何拓展支持更好的视频动效呢?
A:即可支持开源的 76 个效果,详见:https://gl-transitions.com/gallery

Q:如果需要在原视频上,增加一些带有透明度的图片、动图,并增加特效该怎么处理?
A:带透明度的动图可转为一张张图片,以如「%06d.png」的方式作为输入源,即可支持合成并增加特效。

Q:想要自定义特效纹理,自定义特效效果怎么办?
A:可以参考开源的 https://gl-transitions.com/editor/displacement 里面的纹理图片,以及 glsl 文件内容,即可自定义纹理和效果。主要是通过插件使 FFMPEG 支持了 glsl 文件以及问题图片的输入合成。

二、 参考与摘要

本文针对的是 2 合 1 的视频特效处理。也就是输入视频为 2 个,输出 1 个。个别特效需要通过入参额外引入蒙版或纹理图片。
FFMPEG 官网文档的 Complex-filtergraphs 说明
参考内容:ffmpeg 使用 OpenGL 制作转场效果
最新改动适配了 ffmpeg-7.0.1,具体详见 我 fork 的 commit 记录

三、部署成功路线

环境:Ubuntu 7.3.0、ubuntu:24.04

按照此文档中:ffmpeg 使用 OpenGL 制作转场效果 的「Linux without EGL」路线,原因:

编译安装尝试过 Linux with EGL 的路线,因为有这句话「We default to EGL rather than GLX on Linux to make it easier to run headless, so xvfb is no longer needed.」。
然而,装好之后合成的视频全黑(编译安装以及合成信息无报错或警告)
安装 GLEWglfw xvfb 等,编译 ffmpeg 使用了配置参数参考:

./configure --enable-nonfree --enable-libass --enable-gpl --enable-libx264 --enable-librsvg --enable-libvpx --enable-opengl --enable-libfreetype --enable-libmp3lame --enable-libtheora --enable-libvorbis --enable-libx265 --enable-libopus --enable-libxvid \
--enable-filter=gltransition --extra-libs='-lGLEW -lglfw -lSOIL'

安装各种库如果遇到问题,可以拿报错信息问问 AI,按照解决方法装上重试就好,这里就不赘述了

四、运行经验分享

  1. 如果自己魔改,调试可加配置项:./configure --enable-debug --disable-optimizations --disable-asm --disable-stripping,参考自:(原)ffmpeg中filter开发过程遇到的坑<一>

  2. 编译安装(sudo make && sudo make install)完成后,需要运行起模拟的显示器环境:

Install xvfb:
`pkg install xorg-vfbserver` (系统环境 FreeBSD)
`apt install xvfb` (系统环境 Ubuntu)
Run xvfb:
`Xvfb :1 -screen 0 1280x1024x16`
Execute ffmpeg-gl-transition example:
`env DISPLAY=:1 ffmpeg -i media/0.mp4 -i media/1.mp4 -filter_complex gltransition -y out.mp4`
or
`env DISPLAY=:1 ffmpeg -i media/0.mp4 -i media/1.mp4 -filter_complex "gltransition=duration=4:offset=1.5:source=crosswarp.glsl" -y out.mp4`

注:这块个人理解就是用 CPU 模拟 GPU 的一些工作吧
headless environments 是指 without a graphical display,此环境下使用 ffmpeg-opengl-transition 需要运行 xvfb,来源于:Issue: Error initializing filter ‘gltransition’

1. 部署使用涉及

  1. 守护进程、后台运行,参考Linux 守护进程的启动方法
nohup Xvfb :1 -screen 0 1280x1024x16 >/dev/null 2>&1 &
  1. 设置环境变量 DISPLAY=:1,临时:export DISPLAY=:1
  2. 设置了环境变量之后,就可以直接使用 ffmpeg 命令 的 gltransition 滤镜了,检查:ffmpeg -h filter=gltransition

2. 排查环境是否设置成功

# 查看 OpenGL 是否存在,如果返回 `Error: couldn't find RGB GLX visual or fbconfig`,跳到问题7
DISPLAY=:1 glxinfo | grep -i opengl

# 查看环境变量,是否返回设置的值
echo $DISPLAY 

# 查看 Xvfb 是否运行
ps -aux | grep -i xvfb

3. 能实现的效果:

OpenGL 的 glsl 文件以及其开源效果
测试了目前所有 76 个效果,全部可用。

大概有 20 个左右的效果,直接使用以上提供的 .glsl 文件合成出不来效果,原因参考:Issue: can only use a few GL Transitions,即是文件中参数默认值未设置的原因,因为不会出现报错,所以没有效果的时候就比较难排查原因。可以手动修改 glsl 文件设置变量的值:

  • 删掉 uniform 关键字,设置属性值,如:
# in diplacement.glsl:
uniform float strength; // = 0.5
float strength = 0.5; //改成这样

另外有一些如 luma.glsldisplacement.glsl 等的效果还需要额外的纹理图片资源才能处理出效果,可通过下面的魔改版「使用额外的纹理图片」内容解决。

特别的 TVStatic.glsl 会报着色器数据类型错误,通过手动修改 glsl 文件解决:

  1. 设置 offset 的值
  2. 删除精度字符 highp

但是合成出来的视频很大,所以没有考虑使用这个电视雪花的效果了。(其它效果 300k,这个 2000k)

4. 可能出现的问题

注:这些是以前还没有 AI 的时候遇到的问题。目前大概率 AI 很方便能解决的了。

  1. 在下载安装 GLEW 之后,依旧找不到对应的 *.so 库?
    搜索出的办法(出处忘了):
  1. 怀疑是环境变量未配置,在 ~/.profile 中最后一行增加 export PATH="/usr/lib64:$PATH" 来配置环境变量
  2. 将 so 库所在的目录 /usr/lib64 写入到 /etc/ld.so.conf 中,然后执行 sudo ldconfig

或者创建软链:

sudo ln -s **.so /usr/lib/
  1. 如何查看环境中 OpenGL 的版本信息?引用自:What is terminal command that can show OpenGL version?

命令需要加上 Run xvfb 时对应的标识:此处为 :1
即: DISPLAY=:1 glxinfo | grep -i opengl

  1. 如何快速测试所有的过场效果?
#!/usr/bin/env bash
# Example of concatenating 2 mp4s together with 1-one transitions between them.
env DISPLAY=:1 /usr/bin/ffmpeg -i /home/vagrant/code/video_maker/public/media/0.mp4 -i /home/vagrant/code/video_maker/public/media/1.mp4 -filter_complex "gltransition=duration=3:offset=1:source=/home/vagrant/code/video_maker/public/transitions/"$1".glsl" -loglevel 24 -y /home/vagrant/code/video_maker/public/media-transition/$1.mp4
# path=$1
# files=$(ls $path)
# for file_a in $files
# do temp_file=`basename $file_a`
# echo `env DISPLAY=:1 /usr/bin/ffmpeg -i /home/vagrant/code/video_maker/public/media/0.mp4 -i /home/vagrant/code/video_maker/public/media/1.mp4 -filter_complex "gltransition=duration=3:offset=1:source=/home/vagrant/code/video_maker/public/transitions/"$temp_file -loglevel 24 -y /home/vagrant/code/video_maker/public/media-transition/$temp_file.mp4`
# done
  1. 大部分遇到的问题都可以在 ffmpeg 使用 OpenGL 制作转场效果 这的 Issues 里找到参考。

  2. 报错:Operation not permitted,通过上面的「排查环境是否设置成功」排查

1、可能因为未运行 Xvfb
$ nohup Xvfb :1 -screen 0 1280x1024x16 >/dev/null 2>&1 &
2、可能环境变量 DISPLAY 未设置

  1. ffmpeg 运行报错 setup_gl ERRORFailed to configure input pad on Parsed_gltransition_0 时,且命令 DISPLAY=:1 glxinfo | grep -i opengl 返回 Error: couldn't find RGB GLX visual or fbconfig 时,(参考:CentOS下 OpenGL安装及编程开发)可尝试:

yum install mesa*
yum install freeglut*

  1. LearnOpenGL:纹理,提到了纹理上下颠倒的问题

7.1. 修改额外纹理的 glsl 文件的方式,如 displacement.glsl

//由
float displacement = texture2D(extra_tex, uv).r * strength;
//改成:
float displacement = texture2D(extra_tex, vec2(uv.x, 1.0 - uv.y)).r * strength;

7.2. 预处理纹理图片(垂直翻转):

ffmpeg -i texture.png -vf vflip texture-flip.png
  1. 转场边缘的问题
    可通过修改文件中 glTexParameteri 的参数来调整,我的需求可由 GL_REPEAT 改成了 GL_CLAMP_TO_EDGE 来满足。参数说明同样是这篇已经介绍得很清楚了:LearnOpenGL:纹理

五、魔改 提的一个 Pull request 然而并没有被 merge 下面这当作是代码改动解释吧

1. 自行修改文件 vf_gltransition.c 实现支持输入视频/图片的透明通道

  1. GL_RGB 全替换为 GL_RGBA
  2. glPixelStorei 参数修改,原 ***/3 改成 ***/4,如 fromFrame->linesize[0] / 4
  3. query_formats 方法替换为(参考自官方filter:ffmpeg-4.2.4/libavfilter/vf_rotate.cquery_formats 方法)

2. 上面改完发现无透明通道的视频合成出来变成了绿色,包括默认例子。

改良版

  1. 删除设置项 #define PIXEL_FORMAT (GL_RGB),将 GL_RGBPIXEL_FORMAT 全替换为 c->pix_fmt

  2. 在结构体 GLTransitionContext 增加属性值:

int alpha;//输入值设置通道属性
//判断通道值
int pix_fmt;
int channel_num;
  1. 方法 query_formats 改为:
static int query_formats(AVFilterContext *ctx)
{
    static const enum AVPixelFormat pix_fmts[] = {
        AV_PIX_FMT_RGB24,    AV_PIX_FMT_BGR24,
        AV_PIX_FMT_ARGB,     AV_PIX_FMT_ABGR,
        AV_PIX_FMT_RGBA,     AV_PIX_FMT_BGRA,
        AV_PIX_FMT_NONE
    };
    AVFilterFormats *fmts_list;

    fmts_list = ff_make_format_list(pix_fmts);
    if (!fmts_list) {
      return AVERROR(ENOMEM);
    }
    return ff_set_common_formats(ctx, fmts_list);
}
  1. 增加透明属性:
static const enum AVPixelFormat alpha_pix_fmts[] = {
    AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, AV_PIX_FMT_RGBA,
    AV_PIX_FMT_BGRA, AV_PIX_FMT_NONE
};
  1. 在方法 setup_gl 中增加判断:
c->alpha = ff_fmt_is_in(inLink->format, alpha_pix_fmts);
  av_log(ctx, AV_LOG_DEBUG, "c->alpha: %d, inLink->format: %d\n", c->alpha, inLink->format);

  //get alpha info
  if (c->alpha) {
    c->pix_fmt = GL_RGBA;
    c->channel_num = 4;
  } else {
    c->pix_fmt = GL_RGB;
    c->channel_num = 3;
  }
  1. 方法 glPixelStorei 的参数修改,原 ***/3 改成 ***/c->channel_num,如 fromFrame->linesize[0] / c->channel_num

六、使用额外的纹理图片

目标是支持多输入一个额外的纹理图片,入参为图片路径。

安装 SOIL 来加载图片:

git clone https://github.com/kbranigan/Simple-OpenGL-Image-Library.git
cd Simple-OpenGL-Image-Library
sudo make
sudo make install

./configure 加上 --extra-libs='-lSOIL'
  1. 文件 vf_gltransition.c 头部加上:
#include <SOIL.h>
  1. 结构体 GLTransitionContext 加上:
char *extra_texture; //注意与下面的属性区分
GLuint extra_tex; //生成纹理需要用到,参考from
  1. 输入项说明 gltransition_options 加上:
...
{ "extra_texture", "path to the gl-transition extra_texture file", OFFSET(extra_texture), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
...
  1. 方法 setup_tex 增加:
...
if (c->extra_texture) { // extra_texture
    int width, height, soilPixFmt;
    soilPixFmt = SOIL_LOAD_RGB;
    if (c->pix_fmt == GL_RGBA) {
      soilPixFmt = SOIL_LOAD_RGBA;
    }
    unsigned char* image = SOIL_load_image(c->extra_texture, &width, &height, 0, soilPixFmt);

    glGenTextures(1, &c->extra_tex);
    glActiveTexture(GL_TEXTURE0 + 2);
    glBindTexture(GL_TEXTURE_2D, c->extra_tex);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D, 0, c->pix_fmt, width, height, 0, c->pix_fmt, GL_UNSIGNED_BYTE, image);

    glUniform1i(glGetUniformLocation(c->program, "extra_tex"), 2);

    SOIL_free_image_data(image);
  }
  1. 使用前工作:
  • 修改 glsl 文件,如 displacement.glsl,将其中的 uniform sampler2D displacementMap; 以及着色器中的 displacementMap 改为 uniform sampler2D extra_tex;extra_tex
  • 获取 glsl 文件目录,如 texture_dir/texture.png
ffmpeg -i media/0.mp4 -i media/1.mp4 -filter_complex "gltransition=source=displacement.glsl:extra_texture=texture_dir/texture.png" -y out.mp4

七、一点感想吧

This experience is a “key”, the “value” is help to find out that it’s not impossible to read, to understand, even to change something in ffmpeg/libavfilter like vf_blend.c, not just the vf_gltransition.c file. That is the true treasure.
你能魔改一个,就意味着你能魔改很多很多个哈哈哈哈。

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值