diff options
author | Musee Ullah | 2020-01-11 09:26:07 -0800 |
---|---|---|
committer | Musee Ullah | 2020-01-11 09:26:07 -0800 |
commit | f1dd81c47eff912061c7d2fe8fb3d823bc4e8672 (patch) | |
tree | aca07d9599168e4e6df531d72cecf54263effc2d | |
download | aur-f1dd81c47eff912061c7d2fe8fb3d823bc4e8672.tar.gz |
Initial upload: ffmpeg-gl-transition 14.2.2-1
ffmpeg-gl-transition initial build
-rw-r--r-- | .SRCINFO | 86 | ||||
-rw-r--r-- | PKGBUILD | 179 | ||||
-rw-r--r-- | ffmpeg_vf_gltransition.patch | 24 | ||||
-rw-r--r-- | vf_gltransition.c | 563 |
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 +}; |