summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorMusee Ullah2020-01-11 09:26:07 -0800
committerMusee Ullah2020-01-11 09:26:07 -0800
commitf1dd81c47eff912061c7d2fe8fb3d823bc4e8672 (patch)
treeaca07d9599168e4e6df531d72cecf54263effc2d
downloadaur-f1dd81c47eff912061c7d2fe8fb3d823bc4e8672.tar.gz
Initial upload: ffmpeg-gl-transition 14.2.2-1
ffmpeg-gl-transition initial build
-rw-r--r--.SRCINFO86
-rw-r--r--PKGBUILD179
-rw-r--r--ffmpeg_vf_gltransition.patch24
-rw-r--r--vf_gltransition.c563
4 files changed, 852 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 000000000000..4bcefd9e2e28
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,86 @@
+pkgbase = ffmpeg-gl-transition
+ pkgdesc = Complete solution to record, convert and stream audio and video, with support for GL-transitions video filters
+ pkgver = 4.2.2
+ pkgrel = 1
+ epoch = 1
+ url = https://ffmpeg.org/
+ arch = x86_64
+ license = GPL3
+ makedepends = ffnvcodec-headers
+ makedepends = git
+ makedepends = ladspa
+ makedepends = nasm
+ depends = alsa-lib
+ depends = aom
+ depends = bzip2
+ depends = fontconfig
+ depends = fribidi
+ depends = libglvnd
+ depends = glew
+ depends = glfw-x11
+ depends = gmp
+ depends = gnutls
+ depends = gsm
+ depends = intel-media-sdk
+ depends = jack
+ depends = lame
+ depends = libass.so
+ depends = libavc1394
+ depends = libbluray.so
+ depends = libdav1d.so
+ depends = libdrm
+ depends = libfreetype.so
+ depends = libiec61883
+ depends = libmodplug
+ depends = libomxil-bellagio
+ depends = libpulse
+ depends = libraw1394
+ depends = libsoxr
+ depends = libssh
+ depends = libtheora
+ depends = libva.so
+ depends = libva-drm.so
+ depends = libva-x11.so
+ depends = libvdpau
+ depends = libvidstab.so
+ depends = libvorbisenc.so
+ depends = libvorbis.so
+ depends = libvpx.so
+ depends = libwebp
+ depends = libx11
+ depends = libx264.so
+ depends = libx265.so
+ depends = libxcb
+ depends = libxext
+ depends = libxml2
+ depends = libxv
+ depends = libxvidcore.so
+ depends = mesa
+ depends = opencore-amr
+ depends = openjpeg2
+ depends = opus
+ depends = sdl2
+ depends = speex
+ depends = v4l-utils
+ depends = xz
+ depends = zlib
+ optdepends = ladspa: LADSPA filters
+ provides = libavcodec.so
+ provides = libavdevice.so
+ provides = libavfilter.so
+ provides = libavformat.so
+ provides = libavutil.so
+ provides = libpostproc.so
+ provides = libswresample.so
+ provides = libswscale.so
+ provides = ffmpeg
+ conflicts = ffmpeg
+ source = git+https://git.ffmpeg.org/ffmpeg.git#tag=192d1d34eb3668fa27f433e96036340e1e5077a0
+ source = https://raw.githubusercontent.com/transitive-bullshit/ffmpeg-gl-transition/master/vf_gltransition.c
+ source = ffmpeg_vf_gltransition.patch
+ sha256sums = SKIP
+ sha256sums = 4d044f161913805236dbf5e78188bb5a17af10a43dbd3269d0284b12f78aee3e
+ sha256sums = 4853e888cb3fbda247e05faa591d45b640bdadf0115db3669ef08493258a0cb4
+
+pkgname = ffmpeg-gl-transition
+
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 000000000000..9e0dd1774c1c
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,179 @@
+# Maintainer: Musee Ullah <lae@idolactiviti.es>
+# Contributor: Maxime Gauduin <alucryd@archlinux.org>
+# Contributor: Bartłomiej Piotrowski <bpiotrowski@archlinux.org>
+# Contributor: Ionut Biru <ibiru@archlinux.org>
+# Contributor: Tom Newsom <Jeepster@gmx.co.uk>
+# Contributor: Paul Mattal <paul@archlinux.org>
+
+pkgname=ffmpeg-gl-transition
+pkgver=4.2.2
+pkgrel=1
+epoch=1
+pkgdesc='Complete solution to record, convert and stream audio and video, with support for GL-transitions video filters'
+arch=(x86_64)
+url=https://ffmpeg.org/
+license=(GPL3)
+depends=(
+ alsa-lib
+ aom
+ bzip2
+ fontconfig
+ fribidi
+ libglvnd
+ glew
+ glfw-x11
+ gmp
+ gnutls
+ gsm
+ intel-media-sdk
+ jack
+ lame
+ libass.so
+ libavc1394
+ libbluray.so
+ libdav1d.so
+ libdrm
+ libfreetype.so
+ libiec61883
+ libmodplug
+ libomxil-bellagio
+ libpulse
+ libraw1394
+ libsoxr
+ libssh
+ libtheora
+ libva.so
+ libva-drm.so
+ libva-x11.so
+ libvdpau
+ libvidstab.so
+ libvorbisenc.so
+ libvorbis.so
+ libvpx.so
+ libwebp
+ libx11
+ libx264.so
+ libx265.so
+ libxcb
+ libxext
+ libxml2
+ libxv
+ libxvidcore.so
+ mesa
+ opencore-amr
+ openjpeg2
+ opus
+ sdl2
+ speex
+ v4l-utils
+ xz
+ zlib
+)
+makedepends=(
+ ffnvcodec-headers
+ git
+ ladspa
+ nasm
+)
+optdepends=('ladspa: LADSPA filters')
+provides=(
+ libavcodec.so
+ libavdevice.so
+ libavfilter.so
+ libavformat.so
+ libavutil.so
+ libpostproc.so
+ libswresample.so
+ libswscale.so
+ ffmpeg
+)
+conflicts=(ffmpeg)
+source=(git+https://git.ffmpeg.org/ffmpeg.git#tag=192d1d34eb3668fa27f433e96036340e1e5077a0
+ https://raw.githubusercontent.com/transitive-bullshit/ffmpeg-gl-transition/master/vf_gltransition.c
+ ffmpeg_vf_gltransition.patch)
+sha256sums=(SKIP
+ 4d044f161913805236dbf5e78188bb5a17af10a43dbd3269d0284b12f78aee3e
+ 4853e888cb3fbda247e05faa591d45b640bdadf0115db3669ef08493258a0cb4)
+
+pkgver() {
+ cd ffmpeg
+
+ git describe --tags | sed 's/^n//'
+}
+
+prepare() {
+ cd ffmpeg
+
+ git cherry-pick -n dc0806dd25882f41f6085c8356712f95fded56c7
+
+ cp "${srcdir}/vf_gltransition.c" libavfilter/
+ git apply "${srcdir}/ffmpeg_vf_gltransition.patch"
+}
+
+build() {
+ cd ffmpeg
+
+ export PKG_CONFIG_PATH=/opt/intel/mediasdk/lib/pkgconfig
+
+ ./configure \
+ --prefix=/usr \
+ --disable-debug \
+ --disable-static \
+ --disable-stripping \
+ --enable-fontconfig \
+ --enable-gmp \
+ --enable-gnutls \
+ --enable-gpl \
+ --enable-ladspa \
+ --enable-libaom \
+ --enable-libass \
+ --enable-libbluray \
+ --enable-libdav1d \
+ --enable-libdrm \
+ --enable-libfreetype \
+ --enable-libfribidi \
+ --enable-libgsm \
+ --enable-libiec61883 \
+ --enable-libjack \
+ --enable-libmfx \
+ --enable-libmodplug \
+ --enable-libmp3lame \
+ --enable-libopencore_amrnb \
+ --enable-libopencore_amrwb \
+ --enable-libopenjpeg \
+ --enable-libopus \
+ --enable-libpulse \
+ --enable-libsoxr \
+ --enable-libspeex \
+ --enable-libssh \
+ --enable-libtheora \
+ --enable-libv4l2 \
+ --enable-libvidstab \
+ --enable-libvorbis \
+ --enable-libvpx \
+ --enable-libwebp \
+ --enable-libx264 \
+ --enable-libx265 \
+ --enable-libxcb \
+ --enable-libxml2 \
+ --enable-libxvid \
+ --enable-nvdec \
+ --enable-nvenc \
+ --enable-omx \
+ --enable-shared \
+ --enable-version3 \
+ --enable-opengl \
+ --enable-filter=gltransition \
+ --extra-libs="-lGLEW -lEGL"
+
+ make
+ make tools/qt-faststart
+ make doc/ff{mpeg,play}.1
+}
+
+package() {
+ make DESTDIR="${pkgdir}" -C ffmpeg install install-man
+ install -Dm 755 ffmpeg/tools/qt-faststart "${pkgdir}"/usr/bin/
+}
+
+# vim: ts=2 sw=2 et:
diff --git a/ffmpeg_vf_gltransition.patch b/ffmpeg_vf_gltransition.patch
new file mode 100644
index 000000000000..92f101172c39
--- /dev/null
+++ b/ffmpeg_vf_gltransition.patch
@@ -0,0 +1,24 @@
+diff --git a/libavfilter/Makefile b/libavfilter/Makefile
+index a90ca30ad7..c0fc73be46 100644
+--- a/libavfilter/Makefile
++++ b/libavfilter/Makefile
+@@ -367,6 +367,7 @@ OBJS-$(CONFIG_YADIF_FILTER) += vf_yadif.o
+ OBJS-$(CONFIG_ZMQ_FILTER) += f_zmq.o
+ OBJS-$(CONFIG_ZOOMPAN_FILTER) += vf_zoompan.o
+ OBJS-$(CONFIG_ZSCALE_FILTER) += vf_zscale.o
++OBJS-$(CONFIG_GLTRANSITION_FILTER) += vf_gltransition.o
+
+ OBJS-$(CONFIG_ALLRGB_FILTER) += vsrc_testsrc.o
+ OBJS-$(CONFIG_ALLYUV_FILTER) += vsrc_testsrc.o
+diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
+index 6eac828616..0570c1c2aa 100644
+--- a/libavfilter/allfilters.c
++++ b/libavfilter/allfilters.c
+@@ -357,6 +357,7 @@ extern AVFilter ff_vf_yadif;
+ extern AVFilter ff_vf_zmq;
+ extern AVFilter ff_vf_zoompan;
+ extern AVFilter ff_vf_zscale;
++extern AVFilter ff_vf_gltransition;
+
+ extern AVFilter ff_vsrc_allrgb;
+ extern AVFilter ff_vsrc_allyuv;
diff --git a/vf_gltransition.c b/vf_gltransition.c
new file mode 100644
index 000000000000..e123ce67898a
--- /dev/null
+++ b/vf_gltransition.c
@@ -0,0 +1,563 @@
+/**
+ * FFmpeg filter for applying GLSL transitions between video streams.
+ *
+ * @see https://gl-transitions.com/
+ */
+
+#include "libavutil/opt.h"
+#include "internal.h"
+#include "framesync.h"
+
+#ifndef __APPLE__
+# define GL_TRANSITION_USING_EGL //remove this line if you don't want to use EGL
+#endif
+
+#ifdef __APPLE__
+# define __gl_h_
+# define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED
+# include <OpenGL/gl3.h>
+#else
+# include <GL/glew.h>
+#endif
+
+#ifdef GL_TRANSITION_USING_EGL
+# include <EGL/egl.h>
+#else
+# include <GLFW/glfw3.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <float.h>
+
+#define FROM (0)
+#define TO (1)
+
+#define PIXEL_FORMAT (GL_RGB)
+
+#ifdef GL_TRANSITION_USING_EGL
+static const EGLint configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_BLUE_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_RED_SIZE, 8,
+ EGL_DEPTH_SIZE, 8,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
+ EGL_NONE};
+#endif
+static const float position[12] = {
+ -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f
+};
+
+static const GLchar *v_shader_source =
+ "attribute vec2 position;\n"
+ "varying vec2 _uv;\n"
+ "void main(void) {\n"
+ " gl_Position = vec4(position, 0, 1);\n"
+ " vec2 uv = position * 0.5 + 0.5;\n"
+ " _uv = vec2(uv.x, 1.0 - uv.y);\n"
+ "}\n";
+
+static const GLchar *f_shader_template =
+ "varying vec2 _uv;\n"
+ "uniform sampler2D from;\n"
+ "uniform sampler2D to;\n"
+ "uniform float progress;\n"
+ "uniform float ratio;\n"
+ "uniform float _fromR;\n"
+ "uniform float _toR;\n"
+ "\n"
+ "vec4 getFromColor(vec2 uv) {\n"
+ " return texture2D(from, vec2(uv.x, 1.0 - uv.y));\n"
+ "}\n"
+ "\n"
+ "vec4 getToColor(vec2 uv) {\n"
+ " return texture2D(to, vec2(uv.x, 1.0 - uv.y));\n"
+ "}\n"
+ "\n"
+ "\n%s\n"
+ "void main() {\n"
+ " gl_FragColor = transition(_uv);\n"
+ "}\n";
+
+// default to a basic fade effect
+static const GLchar *f_default_transition_source =
+ "vec4 transition (vec2 uv) {\n"
+ " return mix(\n"
+ " getFromColor(uv),\n"
+ " getToColor(uv),\n"
+ " progress\n"
+ " );\n"
+ "}\n";
+
+typedef struct {
+ const AVClass *class;
+ FFFrameSync fs;
+
+ // input options
+ double duration;
+ double offset;
+ char *source;
+
+ // timestamp of the first frame in the output, in the timebase units
+ int64_t first_pts;
+
+ // uniforms
+ GLuint from;
+ GLuint to;
+ GLint progress;
+ GLint ratio;
+ GLint _fromR;
+ GLint _toR;
+
+ // internal state
+ GLuint posBuf;
+ GLuint program;
+#ifdef GL_TRANSITION_USING_EGL
+ EGLDisplay eglDpy;
+ EGLConfig eglCfg;
+ EGLSurface eglSurf;
+ EGLContext eglCtx;
+#else
+ GLFWwindow *window;
+#endif
+
+ GLchar *f_shader_source;
+} GLTransitionContext;
+
+#define OFFSET(x) offsetof(GLTransitionContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+
+static const AVOption gltransition_options[] = {
+ { "duration", "transition duration in seconds", OFFSET(duration), AV_OPT_TYPE_DOUBLE, {.dbl=1.0}, 0, DBL_MAX, FLAGS },
+ { "offset", "delay before startingtransition in seconds", OFFSET(offset), AV_OPT_TYPE_DOUBLE, {.dbl=0.0}, 0, DBL_MAX, FLAGS },
+ { "source", "path to the gl-transition source file (defaults to basic fade)", OFFSET(source), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
+ {NULL}
+};
+
+FRAMESYNC_DEFINE_CLASS(gltransition, GLTransitionContext, fs);
+
+static GLuint build_shader(AVFilterContext *ctx, const GLchar *shader_source, GLenum type)
+{
+ GLuint shader = glCreateShader(type);
+ if (!shader || !glIsShader(shader)) {
+ return 0;
+ }
+
+ glShaderSource(shader, 1, &shader_source, 0);
+ glCompileShader(shader);
+
+ GLint status;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+
+ return (status == GL_TRUE ? shader : 0);
+}
+
+static int build_program(AVFilterContext *ctx)
+{
+ GLuint v_shader, f_shader;
+ GLTransitionContext *c = ctx->priv;
+
+ if (!(v_shader = build_shader(ctx, v_shader_source, GL_VERTEX_SHADER))) {
+ av_log(ctx, AV_LOG_ERROR, "invalid vertex shader\n");
+ return -1;
+ }
+
+ char *source = NULL;
+
+ if (c->source) {
+ FILE *f = fopen(c->source, "rb");
+
+ if (!f) {
+ av_log(ctx, AV_LOG_ERROR, "invalid transition source file \"%s\"\n", c->source);
+ return -1;
+ }
+
+ fseek(f, 0, SEEK_END);
+ unsigned long fsize = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ source = malloc(fsize + 1);
+ fread(source, fsize, 1, f);
+ fclose(f);
+
+ source[fsize] = 0;
+ }
+
+ const char *transition_source = source ? source : f_default_transition_source;
+
+ int len = strlen(f_shader_template) + strlen(transition_source);
+ c->f_shader_source = av_calloc(len, sizeof(*c->f_shader_source));
+ if (!c->f_shader_source) {
+ return AVERROR(ENOMEM);
+ }
+
+ snprintf(c->f_shader_source, len * sizeof(*c->f_shader_source), f_shader_template, transition_source);
+ av_log(ctx, AV_LOG_DEBUG, "\n%s\n", c->f_shader_source);
+
+ if (source) {
+ free(source);
+ source = NULL;
+ }
+
+ if (!(f_shader = build_shader(ctx, c->f_shader_source, GL_FRAGMENT_SHADER))) {
+ av_log(ctx, AV_LOG_ERROR, "invalid fragment shader\n");
+ return -1;
+ }
+
+ c->program = glCreateProgram();
+ glAttachShader(c->program, v_shader);
+ glAttachShader(c->program, f_shader);
+ glLinkProgram(c->program);
+
+ GLint status;
+ glGetProgramiv(c->program, GL_LINK_STATUS, &status);
+ return status == GL_TRUE ? 0 : -1;
+}
+
+static void setup_vbo(GLTransitionContext *c)
+{
+ glGenBuffers(1, &c->posBuf);
+ glBindBuffer(GL_ARRAY_BUFFER, c->posBuf);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(position), position, GL_STATIC_DRAW);
+
+ GLint loc = glGetAttribLocation(c->program, "position");
+ glEnableVertexAttribArray(loc);
+ glVertexAttribPointer(loc, 2, GL_FLOAT, GL_FALSE, 0, 0);
+}
+
+static void setup_tex(AVFilterLink *fromLink)
+{
+ AVFilterContext *ctx = fromLink->dst;
+ GLTransitionContext *c = ctx->priv;
+
+ { // from
+ glGenTextures(1, &c->from);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, c->from);
+
+ 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, GL_RGB, fromLink->w, fromLink->h, 0, PIXEL_FORMAT, GL_UNSIGNED_BYTE, NULL);
+
+ glUniform1i(glGetUniformLocation(c->program, "from"), 0);
+ }
+
+ { // to
+ glGenTextures(1, &c->to);
+ glActiveTexture(GL_TEXTURE0 + 1);
+ glBindTexture(GL_TEXTURE_2D, c->to);
+
+ 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, GL_RGB, fromLink->w, fromLink->h, 0, PIXEL_FORMAT, GL_UNSIGNED_BYTE, NULL);
+
+ glUniform1i(glGetUniformLocation(c->program, "to"), 1);
+ }
+}
+
+static void setup_uniforms(AVFilterLink *fromLink)
+{
+ AVFilterContext *ctx = fromLink->dst;
+ GLTransitionContext *c = ctx->priv;
+
+ c->progress = glGetUniformLocation(c->program, "progress");
+ glUniform1f(c->progress, 0.0f);
+
+ // TODO: this should be output ratio
+ c->ratio = glGetUniformLocation(c->program, "ratio");
+ glUniform1f(c->ratio, fromLink->w / (float)fromLink->h);
+
+ c->_fromR = glGetUniformLocation(c->program, "_fromR");
+ glUniform1f(c->_fromR, fromLink->w / (float)fromLink->h);
+
+ // TODO: initialize this in config_props for "to" input
+ c->_toR = glGetUniformLocation(c->program, "_toR");
+ glUniform1f(c->_toR, fromLink->w / (float)fromLink->h);
+}
+
+static int setup_gl(AVFilterLink *inLink)
+{
+ AVFilterContext *ctx = inLink->dst;
+ GLTransitionContext *c = ctx->priv;
+
+
+#ifdef GL_TRANSITION_USING_EGL
+ //init EGL
+ // 1. Initialize EGL
+ c->eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ EGLint major, minor;
+ eglInitialize(c->eglDpy, &major, &minor);
+ av_log(ctx, AV_LOG_DEBUG, "%d%d", major, minor);
+ // 2. Select an appropriate configuration
+ EGLint numConfigs;
+ EGLint pbufferAttribs[] = {
+ EGL_WIDTH,
+ inLink->w,
+ EGL_HEIGHT,
+ inLink->h,
+ EGL_NONE,
+ };
+ eglChooseConfig(c->eglDpy, configAttribs, &c->eglCfg, 1, &numConfigs);
+ // 3. Create a surface
+ c->eglSurf = eglCreatePbufferSurface(c->eglDpy, c->eglCfg,
+ pbufferAttribs);
+ // 4. Bind the API
+ eglBindAPI(EGL_OPENGL_API);
+ // 5. Create a context and make it current
+ c->eglCtx = eglCreateContext(c->eglDpy, c->eglCfg, EGL_NO_CONTEXT, NULL);
+ eglMakeCurrent(c->eglDpy, c->eglSurf, c->eglSurf, c->eglCtx);
+#else
+ //glfw
+
+ glfwWindowHint(GLFW_VISIBLE, 0);
+ c->window = glfwCreateWindow(inLink->w, inLink->h, "", NULL, NULL);
+ if (!c->window) {
+ av_log(ctx, AV_LOG_ERROR, "setup_gl ERROR");
+ return -1;
+ }
+ glfwMakeContextCurrent(c->window);
+
+#endif
+
+#ifndef __APPLE__
+ glewExperimental = GL_TRUE;
+ glewInit();
+#endif
+
+ glViewport(0, 0, inLink->w, inLink->h);
+
+ int ret;
+ if((ret = build_program(ctx)) < 0) {
+ return ret;
+ }
+
+ glUseProgram(c->program);
+ setup_vbo(c);
+ setup_uniforms(inLink);
+ setup_tex(inLink);
+
+ return 0;
+}
+
+static AVFrame *apply_transition(FFFrameSync *fs,
+ AVFilterContext *ctx,
+ AVFrame *fromFrame,
+ const AVFrame *toFrame)
+{
+ GLTransitionContext *c = ctx->priv;
+ AVFilterLink *fromLink = ctx->inputs[FROM];
+ AVFilterLink *toLink = ctx->inputs[TO];
+ AVFilterLink *outLink = ctx->outputs[0];
+ AVFrame *outFrame;
+
+ outFrame = ff_get_video_buffer(outLink, outLink->w, outLink->h);
+ if (!outFrame) {
+ return NULL;
+ }
+
+ av_frame_copy_props(outFrame, fromFrame);
+
+#ifdef GL_TRANSITION_USING_EGL
+ eglMakeCurrent(c->eglDpy, c->eglSurf, c->eglSurf, c->eglCtx);
+#else
+ glfwMakeContextCurrent(c->window);
+#endif
+
+ glUseProgram(c->program);
+
+ const float ts = ((fs->pts - c->first_pts) / (float)fs->time_base.den) - c->offset;
+ const float progress = FFMAX(0.0f, FFMIN(1.0f, ts / c->duration));
+ // av_log(ctx, AV_LOG_ERROR, "transition '%s' %llu %f %f\n", c->source, fs->pts - c->first_pts, ts, progress);
+ glUniform1f(c->progress, progress);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, c->from);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, fromFrame->linesize[0] / 3);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fromLink->w, fromLink->h, 0, PIXEL_FORMAT, GL_UNSIGNED_BYTE, fromFrame->data[0]);
+
+ glActiveTexture(GL_TEXTURE0 + 1);
+ glBindTexture(GL_TEXTURE_2D, c->to);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, toFrame->linesize[0] / 3);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, toLink->w, toLink->h, 0, PIXEL_FORMAT, GL_UNSIGNED_BYTE, toFrame->data[0]);
+
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+ glPixelStorei(GL_PACK_ROW_LENGTH, outFrame->linesize[0] / 3);
+ glReadPixels(0, 0, outLink->w, outLink->h, PIXEL_FORMAT, GL_UNSIGNED_BYTE, (GLvoid *)outFrame->data[0]);
+
+ glPixelStorei(GL_PACK_ROW_LENGTH, 0);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+
+ av_frame_free(&fromFrame);
+
+ return outFrame;
+}
+
+static int blend_frame(FFFrameSync *fs)
+{
+ AVFilterContext *ctx = fs->parent;
+ GLTransitionContext *c = ctx->priv;
+
+ AVFrame *fromFrame, *toFrame, *outFrame;
+ int ret;
+
+ ret = ff_framesync_dualinput_get(fs, &fromFrame, &toFrame);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (c->first_pts == AV_NOPTS_VALUE && fromFrame && fromFrame->pts != AV_NOPTS_VALUE) {
+ c->first_pts = fromFrame->pts;
+ }
+
+ if (!toFrame) {
+ return ff_filter_frame(ctx->outputs[0], fromFrame);
+ }
+
+ outFrame = apply_transition(fs, ctx, fromFrame, toFrame);
+ if (!outFrame) {
+ return AVERROR(ENOMEM);
+ }
+
+ return ff_filter_frame(ctx->outputs[0], outFrame);
+}
+
+static av_cold int init(AVFilterContext *ctx)
+{
+ GLTransitionContext *c = ctx->priv;
+ c->fs.on_event = blend_frame;
+ c->first_pts = AV_NOPTS_VALUE;
+
+
+#ifndef GL_TRANSITION_USING_EGL
+ if (!glfwInit())
+ {
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx) {
+ GLTransitionContext *c = ctx->priv;
+ ff_framesync_uninit(&c->fs);
+
+#ifdef GL_TRANSITION_USING_EGL
+ if (c->eglDpy) {
+ glDeleteTextures(1, &c->from);
+ glDeleteTextures(1, &c->to);
+ glDeleteBuffers(1, &c->posBuf);
+ glDeleteProgram(c->program);
+ eglTerminate(c->eglDpy);
+ }
+#else
+ if (c->window) {
+ glDeleteTextures(1, &c->from);
+ glDeleteTextures(1, &c->to);
+ glDeleteBuffers(1, &c->posBuf);
+ glDeleteProgram(c->program);
+ glfwDestroyWindow(c->window);
+ }
+#endif
+
+ if (c->f_shader_source) {
+ av_freep(&c->f_shader_source);
+ }
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+ static const enum AVPixelFormat formats[] = {
+ AV_PIX_FMT_RGB24,
+ AV_PIX_FMT_NONE
+ };
+
+ return ff_set_common_formats(ctx, ff_make_format_list(formats));
+}
+
+static int activate(AVFilterContext *ctx)
+{
+ GLTransitionContext *c = ctx->priv;
+ return ff_framesync_activate(&c->fs);
+}
+
+static int config_output(AVFilterLink *outLink)
+{
+ AVFilterContext *ctx = outLink->src;
+ GLTransitionContext *c = ctx->priv;
+ AVFilterLink *fromLink = ctx->inputs[FROM];
+ AVFilterLink *toLink = ctx->inputs[TO];
+ int ret;
+
+ if (fromLink->format != toLink->format) {
+ av_log(ctx, AV_LOG_ERROR, "inputs must be of same pixel format\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (fromLink->w != toLink->w || fromLink->h != toLink->h) {
+ av_log(ctx, AV_LOG_ERROR, "First input link %s parameters "
+ "(size %dx%d) do not match the corresponding "
+ "second input link %s parameters (size %dx%d)\n",
+ ctx->input_pads[FROM].name, fromLink->w, fromLink->h,
+ ctx->input_pads[TO].name, toLink->w, toLink->h);
+ return AVERROR(EINVAL);
+ }
+
+ outLink->w = fromLink->w;
+ outLink->h = fromLink->h;
+ // outLink->time_base = fromLink->time_base;
+ outLink->frame_rate = fromLink->frame_rate;
+
+ if ((ret = ff_framesync_init_dualinput(&c->fs, ctx)) < 0) {
+ return ret;
+ }
+
+ return ff_framesync_configure(&c->fs);
+}
+
+static const AVFilterPad gltransition_inputs[] = {
+ {
+ .name = "from",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .config_props = setup_gl,
+ },
+ {
+ .name = "to",
+ .type = AVMEDIA_TYPE_VIDEO,
+ },
+ {NULL}
+};
+
+static const AVFilterPad gltransition_outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .config_props = config_output,
+ },
+ {NULL}
+};
+
+AVFilter ff_vf_gltransition = {
+ .name = "gltransition",
+ .description = NULL_IF_CONFIG_SMALL("OpenGL blend transitions"),
+ .priv_size = sizeof(GLTransitionContext),
+ .preinit = gltransition_framesync_preinit,
+ .init = init,
+ .uninit = uninit,
+ .query_formats = query_formats,
+ .activate = activate,
+ .inputs = gltransition_inputs,
+ .outputs = gltransition_outputs,
+ .priv_class = &gltransition_class,
+ .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
+};