From: Dan Dennedy Date: Thu, 21 Feb 2013 04:25:58 +0000 (-0800) Subject: Add the new opengl module (opengl branch). X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=cd87c4c715c1c3373cec5baded108ec5bd353fa2;p=mlt Add the new opengl module (opengl branch). --- diff --git a/src/modules/opengl/Makefile b/src/modules/opengl/Makefile new file mode 100644 index 00000000..10276a92 --- /dev/null +++ b/src/modules/opengl/Makefile @@ -0,0 +1,77 @@ +CFLAGS += -I../.. + +LDFLAGS += -L../../framework -lmlt -lm + +include ../../../config.mak + +TARGET = ../libmltopengl$(LIBSUF) + +OBJS = factory.o + +CPPOBJS = fbo_input.o +CPPOBJS += filter_glsl_manager.o +CPPOBJS += filter_movit_blur.o +CPPOBJS += filter_movit_convert.o +CPPOBJS += filter_movit_crop.o +CPPOBJS += filter_deconvolution_sharpen.o +CPPOBJS += filter_movit_diffusion.o +CPPOBJS += filter_movit_glow.o +CPPOBJS += filter_lift_gamma_gain.o +CPPOBJS += filter_movit_mirror.o +CPPOBJS += filter_movit_opacity.o +CPPOBJS += filter_movit_rect.o +CPPOBJS += filter_movit_resample.o +CPPOBJS += filter_movit_resize.o +CPPOBJS += filter_movit_saturation.o +CPPOBJS += filter_movit_vignette.o +CPPOBJS += filter_white_balance.o +CPPOBJS += mlt_movit_input.o +CPPOBJS += transition_movit_mix.o +CPPOBJS += transition_movit_overlay.o + +CXXFLAGS += -Wno-deprecated $(CFLAGS) +CXXFLAGS += `pkg-config --cflags movit 2> /dev/null` + +SHADERDIR = `pkg-config --variable=shaderdir movit` +CXXFLAGS += -DSHADERDIR=\"$(SHADERDIR)\" + +LDFLAGS += -L../../mlt++ -lmlt++ + +ifeq ($(targetos), MinGW) + CXXFLAGS += `pkg-config --cflags glew` + LDFLAGS += -lmovit `pkg-config --libs-only-L glew` -lglew32 -lopengl32 +else + LDFLAGS += `pkg-config --libs movit 2> /dev/null` +ifeq ($(targetos), Darwin) + CXXFLAGS += -FOpenGL + LDFLAGS += -framework OpenGL +else + OBJS += consumer_xgl.o + LDFLAGS += -lpthread -lGL -lX11 +endif +endif + +SRCS := $(OBJS:.o=.c) $(CPPOBJS:.o=.cpp) + +all: $(TARGET) + +$(TARGET): $(OBJS) $(CPPOBJS) + $(CXX) $(SHFLAGS) -o $@ $(OBJS) $(CPPOBJS) $(LDFLAGS) + +depend: $(SRCS) + $(CXX) -MM $(CXXFLAGS) $^ 1>.depend + +distclean: clean + rm -f .depend config.h config.mak + +clean: + rm -f $(OBJS) $(TARGET) $(CPPOBJS) + +install: all + install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt" + install -d "$(DESTDIR)$(datadir)/mlt/opengl/movit" + install -m 644 *.yml "$(DESTDIR)$(datadir)/mlt/opengl" + +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/src/modules/opengl/configure b/src/modules/opengl/configure new file mode 100755 index 00000000..3065ba12 --- /dev/null +++ b/src/modules/opengl/configure @@ -0,0 +1,22 @@ +#!/bin/sh + +if [ "$help" != "1" ] +then + if ! $(pkg-config movit) + then + echo "- movit not found: disabling" + touch ../disable-opengl + exit 0 + fi + + echo > config.mak + case $targetos in + Darwin) + ;; + MinGW) + ;; + *) + ;; + esac + exit 0 +fi diff --git a/src/modules/opengl/consumer_xgl.c b/src/modules/opengl/consumer_xgl.c new file mode 100644 index 00000000..6eae9f1d --- /dev/null +++ b/src/modules/opengl/consumer_xgl.c @@ -0,0 +1,694 @@ +/* + * consumer_xgl.c + * Copyright (C) 2012 Christophe Thommeret + * Author: Christophe Thommeret + * Based on Nehe's GLX port by Mihael.Vrbanec@stud.uni-karlsruhe.de + * http://nehe.gamedev.net/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#define GL_GLEXT_PROTOTYPES + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + + +#define STARTWIDTH 1280 +#define STARTHEIGHT 720 + + +extern int XInitThreads(); + + + +typedef struct consumer_xgl_s *consumer_xgl; + +struct consumer_xgl_s +{ + struct mlt_consumer_s parent; + mlt_properties properties; + mlt_deque queue; + pthread_t thread; + int joined; + int running; + int playing; + int xgl_started; +}; + + +typedef struct +{ + pthread_t thread; + int running; +} thread_video; + + +typedef struct +{ + int width; + int height; + double aspect_ratio; + GLuint texture; + pthread_mutex_t mutex; + int new; + mlt_frame mlt_frame_ref; +} frame_new; + + +typedef struct +{ + int width; + int height; + GLuint fbo; + GLuint texture; +} fbo; + + +typedef struct +{ + Display *dpy; + int screen; + Window win; + GLXContext ctx; +} HiddenContext; + + +typedef struct +{ + Display *dpy; + int screen; + Window win; + GLXContext ctx; + XSetWindowAttributes attr; + int x, y; + unsigned int width, height; + unsigned int depth; +} GLWindow; + + +static GLWindow GLWin; +static HiddenContext hiddenctx; + +static frame_new new_frame; +static fbo fb; +static thread_video vthread; +static consumer_xgl xgl; +static mlt_filter glsl_manager; + + + +static void* video_thread( void *arg ); + +static void update() +{ + int _width = GLWin.width; + int _height = GLWin.height; + GLfloat left, right, top, bottom; + GLfloat war = (GLfloat)_width/(GLfloat)_height; + + if ( war < new_frame.aspect_ratio ) { + left = -1.0; + right = 1.0; + top = war / new_frame.aspect_ratio; + bottom = -war / new_frame.aspect_ratio; + } + else { + top = 1.0; + bottom = -1.0; + left = -new_frame.aspect_ratio / war; + right = new_frame.aspect_ratio / war; + } + + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + glLoadIdentity(); + + glPushMatrix(); + + glTranslatef( _width/2, _height/2, 0 ); + glScalef( _width/2, _height/2, 1.0 ); + + glBindTexture( GL_TEXTURE_2D, fb.texture ); + + glBegin( GL_QUADS ); + glTexCoord2f( 0.0f, 0.0f ); glVertex2f( left, top ); + glTexCoord2f( 0.0f, 1.0f ); glVertex2f( left, bottom ); + glTexCoord2f( 1.0f, 1.0f ); glVertex2f( right, bottom ); + glTexCoord2f( 1.0f, 0.0f ); glVertex2f( right, top ); + glEnd(); + + glPopMatrix(); + + glXSwapBuffers( GLWin.dpy, GLWin.win ); + + if ( !vthread.running ) { + pthread_create( &vthread.thread, NULL, video_thread, NULL ); + vthread.running = 1; + } +} + + + +static void show_frame() +{ +// fprintf(stderr,"show_frame threadID : %ld\n", syscall(SYS_gettid)); + + if ( (fb.width != new_frame.width) || (fb.height != new_frame.height) ) { + glDeleteFramebuffers( 1, &fb.fbo ); + glDeleteTextures( 1, &fb.texture ); + fb.fbo = 0; + fb.width = new_frame.width; + fb.height = new_frame.height; + glGenFramebuffers( 1, &fb.fbo ); + glGenTextures( 1, &fb.texture ); + glBindTexture( GL_TEXTURE_2D, fb.texture ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, fb.width, fb.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glBindFramebuffer( GL_FRAMEBUFFER, fb.fbo ); + glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.texture, 0 ); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + } + + glPushAttrib(GL_VIEWPORT_BIT); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + + glBindFramebuffer( GL_FRAMEBUFFER, fb.fbo ); + + glViewport( 0, 0, new_frame.width, new_frame.height ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0.0, new_frame.width, 0.0, new_frame.height, -1.0, 1.0 ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_2D, new_frame.texture ); + + glBegin( GL_QUADS ); + glTexCoord2f( 0.0f, 0.0f ); glVertex2f( 0.0f, 0.0f ); + glTexCoord2f( 0.0f, 1.0f ); glVertex2f( 0.0f, new_frame.height ); + glTexCoord2f( 1.0f, 1.0f ); glVertex2f( new_frame.width, new_frame.height ); + glTexCoord2f( 1.0f, 0.0f ); glVertex2f( new_frame.width, 0.0f ); + glEnd(); + + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + mlt_events_fire( MLT_CONSUMER_PROPERTIES(&xgl->parent), "consumer-frame-show", new_frame.mlt_frame_ref, NULL ); + mlt_frame_close( new_frame.mlt_frame_ref ); + new_frame.mlt_frame_ref = NULL; + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopAttrib(); + + update(); + + new_frame.new = 0; +} + + + +void* video_thread( void *arg ) +{ + mlt_frame next = NULL; + mlt_consumer consumer = &xgl->parent; + mlt_properties consumer_props = MLT_CONSUMER_PROPERTIES( consumer ); + struct timeval start, end; + double duration = 0; + + gettimeofday( &start, NULL ); + + while ( vthread.running ) + { + // Get a frame from the attached producer + next = mlt_consumer_rt_frame( consumer ); + + if ( !mlt_properties_get_int( MLT_FILTER_PROPERTIES( glsl_manager ), "glsl_supported" ) ) { + fprintf( stderr, "OpenGL Shading Language is not supported on this machine.\n" ); + xgl->running = 0; + break; + } + + // Ensure that we have a frame + if ( next ) + { + mlt_properties properties = MLT_FRAME_PROPERTIES( next ); + if ( mlt_properties_get_int( properties, "rendered" ) == 1 ) + { + // Get the image, width and height + mlt_image_format vfmt = mlt_image_glsl_texture; + int width = 0, height = 0; + GLuint *image = 0; + int error = mlt_frame_get_image( next, (uint8_t**) &image, &vfmt, &width, &height, 0 ); + if ( !error && image && width && height && !new_frame.new ) { + new_frame.width = width; + new_frame.height = height; + new_frame.texture = *image; + new_frame.mlt_frame_ref = next; + new_frame.aspect_ratio = ((double)width / (double)height) * mlt_properties_get_double( properties, "aspect_ratio" ); + new_frame.new = 1; + + int loop = 200; + while ( new_frame.new && --loop ) + usleep( 500 ); + } + else + { + mlt_frame_close( next ); + } + new_frame.new = 0; + + gettimeofday( &end, NULL ); + duration = 1000000.0 / mlt_properties_get_double( consumer_props, "fps" ); + duration -= ( end.tv_sec * 1000000 + end.tv_usec ) - ( start.tv_sec * 1000000 + start.tv_usec ); + if ( duration > 0 ) + usleep( (int)duration ); + gettimeofday( &start, NULL ); + } + else + { + mlt_frame_close( next ); + static int dropped = 0; + mlt_log_info( MLT_CONSUMER_SERVICE(consumer), "dropped video frame %d\n", ++dropped ); + } + } + else + usleep( 1000 ); + } + mlt_consumer_stopped( consumer ); + + return NULL; +} + + + +static void resizeGLScene() +{ + glXMakeCurrent( GLWin.dpy, GLWin.win, GLWin.ctx ); + + if ( GLWin.height == 0 ) + GLWin.height = 1; + if ( GLWin.width == 0 ) + GLWin.width = 1; + glViewport( 0, 0, GLWin.width, GLWin.height ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0.0, GLWin.width, 0.0, GLWin.height, -1.0, 1.0 ); + glMatrixMode( GL_MODELVIEW ); + + update(); +} + + + +static void initGL( void ) +{ + glXMakeCurrent( GLWin.dpy, GLWin.win, GLWin.ctx ); + + glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); + glClearDepth( 1.0f ); + glDepthFunc( GL_LEQUAL ); + glEnable( GL_DEPTH_TEST ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glEnable( GL_BLEND ); + glShadeModel( GL_SMOOTH ); + glEnable( GL_TEXTURE_2D ); + glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); + + typedef int (*GLXSWAPINTERVALSGI) ( int ); + GLXSWAPINTERVALSGI mglXSwapInterval = (GLXSWAPINTERVALSGI)glXGetProcAddressARB( (const GLubyte*)"glXSwapIntervalSGI" ); + if ( mglXSwapInterval ) + mglXSwapInterval( 1 ); + + fb.fbo = 0; + fb.width = STARTWIDTH; + fb.height = STARTHEIGHT; + glGenFramebuffers( 1, &fb.fbo ); + glGenTextures( 1, &fb.texture ); + glBindTexture( GL_TEXTURE_2D, fb.texture ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, fb.width, fb.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glBindFramebuffer( GL_FRAMEBUFFER, fb.fbo ); + glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.texture, 0 ); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + + resizeGLScene(); +} + + + +static void createGLWindow() +{ + const char* title = "OpenGL consumer"; + int width = STARTWIDTH; + int height = STARTHEIGHT; + + int attrListSgl[] = { GLX_RGBA, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, GLX_DEPTH_SIZE, 16, None }; + + int attrListDbl[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_DEPTH_SIZE, 16, None }; + + XVisualInfo *vi; + Colormap cmap; + Atom wmDelete; + Window winDummy; + unsigned int borderDummy; + + GLWin.dpy = XOpenDisplay( 0 ); + GLWin.screen = DefaultScreen( GLWin.dpy ); + + vi = glXChooseVisual( GLWin.dpy, GLWin.screen, attrListDbl ); + if ( !vi ) + vi = glXChooseVisual( GLWin.dpy, GLWin.screen, attrListSgl ); + + GLWin.ctx = glXCreateContext( GLWin.dpy, vi, 0, GL_TRUE ); + + cmap = XCreateColormap( GLWin.dpy, RootWindow( GLWin.dpy, vi->screen ), vi->visual, AllocNone ); + GLWin.attr.colormap = cmap; + GLWin.attr.border_pixel = 0; + + GLWin.attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask; + GLWin.win = XCreateWindow( GLWin.dpy, RootWindow(GLWin.dpy, vi->screen), 0, 0, width, height, + 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &GLWin.attr ); + wmDelete = XInternAtom( GLWin.dpy, "WM_DELETE_WINDOW", True ); + XSetWMProtocols( GLWin.dpy, GLWin.win, &wmDelete, 1 ); + XSetStandardProperties( GLWin.dpy, GLWin.win, title, title, None, NULL, 0, NULL ); + XMapRaised( GLWin.dpy, GLWin.win ); + + glXMakeCurrent( GLWin.dpy, GLWin.win, GLWin.ctx ); + XGetGeometry( GLWin.dpy, GLWin.win, &winDummy, &GLWin.x, &GLWin.y, + &GLWin.width, &GLWin.height, &borderDummy, &GLWin.depth ); + + fprintf(stderr, "Direct Rendering: %s\n",glXIsDirect( GLWin.dpy, GLWin.ctx ) ? "true" : "false" ); + + // Verify GLSL works on this machine + hiddenctx.ctx = glXCreateContext( GLWin.dpy, vi, GLWin.ctx, GL_TRUE ); + if ( hiddenctx.ctx ) { + hiddenctx.dpy = GLWin.dpy; + hiddenctx.screen = GLWin.screen; + hiddenctx.win = RootWindow( hiddenctx.dpy, hiddenctx.screen ); + } + + initGL(); +} + + + +static void killGLWindow() +{ + if ( GLWin.ctx ) { + if ( !glXMakeCurrent( GLWin.dpy, None, NULL ) ) { + printf("Error releasing drawing context : killGLWindow\n"); + } + glXDestroyContext( GLWin.dpy, GLWin.ctx ); + GLWin.ctx = NULL; + } + + if ( hiddenctx.ctx ) + glXDestroyContext( hiddenctx.dpy, hiddenctx.ctx ); + + XCloseDisplay( GLWin.dpy ); +} + + + +static void run() +{ + XEvent event; + + while ( xgl->running ) { + while ( XPending( GLWin.dpy ) > 0 ) { + XNextEvent( GLWin.dpy, &event ); + switch ( event.type ) { + case Expose: + if ( event.xexpose.count != 0 ) + break; + break; + case ConfigureNotify: + if ( (event.xconfigure.width != GLWin.width) || (event.xconfigure.height != GLWin.height) ) { + GLWin.width = event.xconfigure.width; + GLWin.height = event.xconfigure.height; + resizeGLScene(); + } + break; + case KeyPress: + switch ( XLookupKeysym( &event.xkey, 0 ) ) { + case XK_Escape: + xgl->running = 0; + break; + default: { + mlt_producer producer = mlt_properties_get_data( xgl->properties, "transport_producer", NULL ); + char keyboard[ 2 ] = " "; + void (*callback)( mlt_producer, char * ) = mlt_properties_get_data( xgl->properties, "transport_callback", NULL ); + if ( callback != NULL && producer != NULL ) + { + keyboard[ 0 ] = ( char )XLookupKeysym( &event.xkey, 0 ); + callback( producer, keyboard ); + } + break; + } + } + break; + case ClientMessage: + if ( *XGetAtomName( GLWin.dpy, event.xclient.message_type ) == *"WM_PROTOCOLS" ) + xgl->running = 0; + break; + default: + break; + } + } + + if ( new_frame.new ) + show_frame(); + else + usleep( 1000 ); + } +} + + + +void start_xgl( consumer_xgl consumer ) +{ + xgl = consumer; + + pthread_mutex_init( &new_frame.mutex, NULL ); + new_frame.aspect_ratio = 16.0 / 9.0; + new_frame.new = 0; + new_frame.width = STARTWIDTH; + new_frame.height = STARTHEIGHT; + new_frame.mlt_frame_ref = NULL; + + vthread.running = 0; + + createGLWindow(); + run(); + if ( vthread.running ) { + vthread.running = 0; + pthread_join( vthread.thread, NULL ); + } + killGLWindow(); + xgl->running = 0; +} + +static void on_consumer_thread_started( mlt_properties owner, HiddenContext* context ) +{ + fprintf(stderr, "%s: %ld\n", __FUNCTION__, syscall(SYS_gettid)); + // Initialize this thread's OpenGL state + glXMakeCurrent( context->dpy, context->win, context->ctx ); + mlt_events_fire( MLT_FILTER_PROPERTIES(glsl_manager), "init glsl", NULL ); +} + +/** Forward references to static functions. +*/ + +static int consumer_start( mlt_consumer parent ); +static int consumer_stop( mlt_consumer parent ); +static int consumer_is_stopped( mlt_consumer parent ); +static void consumer_close( mlt_consumer parent ); +static void *consumer_thread( void * ); + + + +/** This is what will be called by the factory - anything can be passed in + via the argument, but keep it simple. +*/ + +mlt_consumer consumer_xgl_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + fprintf(stderr, "consumer_xgl_init\n"); + // Create the consumer object + consumer_xgl this = calloc( sizeof( struct consumer_xgl_s ), 1 ); + + // If no malloc'd and consumer init ok + if ( this != NULL && mlt_consumer_init( &this->parent, this, profile ) == 0 ) + { + // Create the queue + this->queue = mlt_deque_init( ); + + // Get the parent consumer object + mlt_consumer parent = &this->parent; + + // We have stuff to clean up, so override the close method + parent->close = consumer_close; + + // get a handle on properties + mlt_service service = MLT_CONSUMER_SERVICE( parent ); + this->properties = MLT_SERVICE_PROPERTIES( service ); + + // Default scaler + mlt_properties_set( this->properties, "rescale", "bilinear" ); + mlt_properties_set( this->properties, "deinterlace_method", "onefield" ); + + // default image format + mlt_properties_set( this->properties, "mlt_image_format", "glsl" ); + + // Default buffer for low latency + mlt_properties_set_int( this->properties, "buffer", 1 ); + + // Ensure we don't join on a non-running object + this->joined = 1; + this->xgl_started = 0; + + // Allow thread to be started/stopped + parent->start = consumer_start; + parent->stop = consumer_stop; + parent->is_stopped = consumer_is_stopped; + + // "init glsl" is required to instantiate glsl filters. + glsl_manager = mlt_factory_filter( profile, "glsl.manager", NULL ); + if ( glsl_manager ) { + mlt_events_listen( this->properties, &hiddenctx, "consumer-thread-started", (mlt_listener) on_consumer_thread_started ); + } else { + mlt_consumer_close( parent ); + parent = NULL; + } + + // Return the consumer produced + return parent; + } + + // malloc or consumer init failed + free( this ); + + // Indicate failure + return NULL; +} + + + +int consumer_start( mlt_consumer parent ) +{ + consumer_xgl this = parent->child; + + if ( !this->running ) + { + consumer_stop( parent ); + + this->running = 1; + this->joined = 0; + + pthread_create( &this->thread, NULL, consumer_thread, this ); + } + + return 0; +} + + + +int consumer_stop( mlt_consumer parent ) +{ + // Get the actual object + consumer_xgl this = parent->child; + + if ( this->running && this->joined == 0 ) + { + // Kill the thread and clean up + this->joined = 1; + this->running = 0; + + if ( this->thread ) + pthread_join( this->thread, NULL ); + } + + return 0; +} + + + +int consumer_is_stopped( mlt_consumer parent ) +{ + consumer_xgl this = parent->child; + return !this->running; +} + + + +static void *consumer_thread( void *arg ) +{ + // Identify the arg + consumer_xgl this = arg; + + XInitThreads(); + start_xgl( this ); + + return NULL; +} + + + +/** Callback to allow override of the close method. +*/ + +static void consumer_close( mlt_consumer parent ) +{ + // Get the actual object + consumer_xgl this = parent->child; + + // Stop the consumer + ///mlt_consumer_stop( parent ); + mlt_filter_close( glsl_manager ); + + // Now clean up the rest + mlt_consumer_close( parent ); + + // Close the queue + mlt_deque_close( this->queue ); + + // Finally clean up this + free( this ); +} diff --git a/src/modules/opengl/factory.c b/src/modules/opengl/factory.c new file mode 100644 index 00000000..b67273c4 --- /dev/null +++ b/src/modules/opengl/factory.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013 Dan Dennedy + * factory.c -- the factory method interfaces + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + + + +extern mlt_consumer consumer_xgl_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_glsl_manager_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_movit_blur_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_movit_convert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_movit_crop_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_deconvolution_sharpen_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_movit_diffusion_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_movit_glow_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_movit_mirror_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_movit_opacity_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_movit_rect_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_movit_resample_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_movit_resize_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_movit_saturation_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_movit_vignette_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_white_balance_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_transition transition_movit_mix_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_transition transition_movit_overlay_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); + +static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) +{ + char file[ PATH_MAX ]; + snprintf( file, PATH_MAX, "%s/opengl/%s", mlt_environment( "MLT_DATA" ), (char*) data ); + return mlt_properties_parse_yaml( file ); +} + +MLT_REPOSITORY +{ +#if !defined(__DARWIN__) && !defined(WIN32) + MLT_REGISTER( consumer_type, "xgl", consumer_xgl_init ); +#endif + MLT_REGISTER( filter_type, "glsl.manager", filter_glsl_manager_init ); + MLT_REGISTER( filter_type, "movit.blur", filter_movit_blur_init ); + MLT_REGISTER( filter_type, "movit.convert", filter_movit_convert_init ); + MLT_REGISTER( filter_type, "movit.crop", filter_movit_crop_init ); + MLT_REGISTER( filter_type, "movit.diffusion", filter_movit_diffusion_init ); + MLT_REGISTER( filter_type, "movit.glow", filter_movit_glow_init ); + MLT_REGISTER( filter_type, "movit.lift_gamma_gain", filter_lift_gamma_gain_init ); + MLT_REGISTER( filter_type, "movit.mirror", filter_movit_mirror_init ); + MLT_REGISTER( filter_type, "movit.opacity", filter_movit_opacity_init ); + MLT_REGISTER( filter_type, "movit.rect", filter_movit_rect_init ); + MLT_REGISTER( filter_type, "movit.resample", filter_movit_resample_init ); + MLT_REGISTER( filter_type, "movit.resize", filter_movit_resize_init ); + MLT_REGISTER( filter_type, "movit.saturation", filter_movit_saturation_init ); + MLT_REGISTER( filter_type, "movit.sharpen", filter_deconvolution_sharpen_init ); + MLT_REGISTER( filter_type, "movit.vignette", filter_movit_vignette_init ); + MLT_REGISTER( filter_type, "movit.white_balance", filter_white_balance_init ); + MLT_REGISTER( transition_type, "movit.mix", transition_movit_mix_init ); + MLT_REGISTER( transition_type, "movit.overlay", transition_movit_overlay_init ); + + MLT_REGISTER_METADATA( filter_type, "movit.blur", metadata, "filter_movit_blur.yml" ); + MLT_REGISTER_METADATA( filter_type, "movit.diffusion", metadata, "filter_movit_diffusion.yml" ); + MLT_REGISTER_METADATA( filter_type, "movit.glow", metadata, "filter_movit_glow.yml" ); + MLT_REGISTER_METADATA( filter_type, "movit.lift_gamma_gain", metadata, "filter_lift_gamma_gain.yml" ); + MLT_REGISTER_METADATA( filter_type, "movit.mirror", metadata, "filter_movit_mirror.yml" ); + MLT_REGISTER_METADATA( filter_type, "movit.opacity", metadata, "filter_movit_opacity.yml" ); + MLT_REGISTER_METADATA( filter_type, "movit.rect", metadata, "filter_movit_rect.yml" ); + MLT_REGISTER_METADATA( filter_type, "movit.saturation", metadata, "filter_movit_saturation.yml" ); + MLT_REGISTER_METADATA( filter_type, "movit.sharpen", metadata, "filter_deconvolution_sharpen.yml" ); + MLT_REGISTER_METADATA( filter_type, "movit.vignette", metadata, "filter_movit_vignette.yml" ); + MLT_REGISTER_METADATA( filter_type, "movit.white_balance", metadata, "filter_white_balance.yml" ); + MLT_REGISTER_METADATA( transition_type, "movit.mix", metadata, "transition_movit_mix.yml" ); + MLT_REGISTER_METADATA( transition_type, "movit.overlay", metadata, "transition_movit_overlay.yml" ); +} diff --git a/src/modules/opengl/fbo_input.cpp b/src/modules/opengl/fbo_input.cpp new file mode 100644 index 00000000..fafe3339 --- /dev/null +++ b/src/modules/opengl/fbo_input.cpp @@ -0,0 +1,51 @@ +/* + * fbo_input.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "fbo_input.h" +#include + +// comes from unexported effect_util.h +extern void set_uniform_int(GLuint glsl_program_num, const std::string &prefix, const std::string &key, int value); + +FBOInput::FBOInput(unsigned width, unsigned height) + : texture_num(0) + , needs_mipmaps(false) + , width(width) + , height(height) +{ + register_int("needs_mipmaps", &needs_mipmaps); +} + +void FBOInput::set_gl_state(GLuint glsl_program_num, const std::string& prefix, unsigned *sampler_num) +{ + glActiveTexture(GL_TEXTURE0 + *sampler_num); + check_error(); + glBindTexture(GL_TEXTURE_2D, texture_num); + check_error(); + + // Bind it to a sampler. + set_uniform_int(glsl_program_num, prefix, "tex", *sampler_num); + ++*sampler_num; +} + +std::string FBOInput::output_fragment_shader() +{ + return read_file("flat_input.frag"); +// return "uniform sampler2D PREFIX(tex); vec4 FUNCNAME(vec2 tc) { return texture2D(PREFIX(tex), tc); }\n"; +} diff --git a/src/modules/opengl/fbo_input.h b/src/modules/opengl/fbo_input.h new file mode 100644 index 00000000..0000e03d --- /dev/null +++ b/src/modules/opengl/fbo_input.h @@ -0,0 +1,50 @@ +/* + * fbo_input.h + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef FBO_INPUT_H +#define FBO_INPUT_H + +#include + +class FBOInput : public Input +{ +public: + FBOInput(unsigned width, unsigned height); + + virtual std::string effect_type_id() const { return "FBOInput"; } + void finalize() {} + bool can_output_linear_gamma() const { return false; } + AlphaHandling alpha_handling() const { return OUTPUT_POSTMULTIPLIED_ALPHA; } + std::string output_fragment_shader(); + void set_gl_state(GLuint glsl_program_num, const std::string& prefix, unsigned *sampler_num); + unsigned get_width() const { return width; } + unsigned get_height() const { return height; } + Colorspace get_color_space() const { return COLORSPACE_sRGB; } + GammaCurve get_gamma_curve() const { return GAMMA_sRGB; } + void set_texture(GLuint texture) { + texture_num = texture; + } + +private: + GLuint texture_num; + int needs_mipmaps; + unsigned width, height; +}; + +#endif // FBO_INPUT_H diff --git a/src/modules/opengl/filter_deconvolution_sharpen.cpp b/src/modules/opengl/filter_deconvolution_sharpen.cpp new file mode 100644 index 00000000..546c489f --- /dev/null +++ b/src/modules/opengl/filter_deconvolution_sharpen.cpp @@ -0,0 +1,65 @@ +/* + * filter_deconvolution_sharpen.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "glsl_manager.h" +#include + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + if ( !mlt_frame_is_test_card( frame ) ) { + Effect* effect = GlslManager::get_effect( filter, frame ); + if ( !effect ) + GlslManager::add_effect( filter, frame, new DeconvolutionSharpenEffect() ); + if ( effect ) { + mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ); + bool ok = effect->set_int( "matrix_size", mlt_properties_get_int( filter_props, "matrix_size" ) ); + ok |= effect->set_float( "circle_radius", mlt_properties_get_double( filter_props, "circle_radius" ) ); + ok |= effect->set_float( "gaussian_radius", mlt_properties_get_double( filter_props, "gaussian_radius" ) ); + ok |= effect->set_float( "correlation", mlt_properties_get_double( filter_props, "correlation" ) ); + ok |= effect->set_float( "noise", mlt_properties_get_double( filter_props, "noise" ) ); + assert(ok); + } + } + return frame; +} + +extern "C" { + +mlt_filter filter_deconvolution_sharpen_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_properties_set_int( properties, "matrix_size", 5 ); + mlt_properties_set_double( properties, "circle_radius", 2.0 ); + mlt_properties_set_double( properties, "gaussian_radius", 0.0 ); + mlt_properties_set_double( properties, "correlation", 0.95 ); + mlt_properties_set_double( properties, "noise", 0.01 ); + filter->process = process; + } + return filter; +} + +} diff --git a/src/modules/opengl/filter_deconvolution_sharpen.yml b/src/modules/opengl/filter_deconvolution_sharpen.yml new file mode 100644 index 00000000..df0e3d9f --- /dev/null +++ b/src/modules/opengl/filter_deconvolution_sharpen.yml @@ -0,0 +1,53 @@ +schema_version: 0.1 +type: filter +identifier: movit.sharpen +title: Deconvolution Sharpen (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: > + Deconvolution Sharpen is a filter that sharpens by way of deconvolution + (i.e., trying to reverse the blur kernel, as opposed to just boosting high + frequencies), more specifically by FIR Wiener filters. It is the same + algorithm as used by the (now largely abandoned) Refocus plug-in for GIMP, + and I suspect the same as in Photoshop's “Smart Sharpen” filter. + The effect gives generally better results than unsharp masking, but can be very + GPU intensive, and requires a fair bit of tweaking to get good results without + ringing and/or excessive noise. It should be mentioned that for the larger + +parameters: + - identifier: matrix_size + title: Matrix Size + type: integer + minimum: 0 + maximum: 10 + default: 5 + + - identifier: circle_radius + title: Circle Radius + type: float + minimum: 0 + default: 2 + + - identifier: gaussian_radius + title: Gaussian Radius + type: float + minimum: 0 + default: 0 + + - identifier: correlation + title: Correlation + type: float + minimum: 0 + default: 0.95 + + - identifier: noise + title: Noise Level + type: float + minimum: 0 + default: 0.01 + diff --git a/src/modules/opengl/filter_glsl_manager.cpp b/src/modules/opengl/filter_glsl_manager.cpp new file mode 100644 index 00000000..e8947108 --- /dev/null +++ b/src/modules/opengl/filter_glsl_manager.cpp @@ -0,0 +1,285 @@ +/* + * filter_glsl_manager.cpp + * Copyright (C) 2011-2012 Christophe Thommeret + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "glsl_manager.h" +#include +#include +#include "mlt_movit_input.h" +#include "mlt_flip_effect.h" + +extern "C" { +#include +} + +#ifdef WIN32 +#define SYS_gettid (224) +#define syscall(X) (((X == SYS_gettid) && GetCurrentThreadId()) || 0) +#else +#include +#include +#endif + +void deleteManager(GlslManager *p) +{ + delete p; +} + +GlslManager::GlslManager() + : Mlt::Filter( mlt_filter_new() ) + , pbo(0) +{ + mlt_filter filter = get_filter(); + if ( filter ) { + // Set the mlt_filter child in case we choose to override virtual functions. + filter->child = this; + mlt_properties_set_data(mlt_global_properties(), "glslManager", this, 0, + (mlt_destructor) deleteManager, NULL); + + mlt_events_register( get_properties(), "init glsl", NULL ); + listen("init glsl", this, (mlt_listener) GlslManager::onInit); + } +} + +GlslManager::~GlslManager() +{ + mlt_log_debug(get_service(), "%s\n", __FUNCTION__); + while (fbo_list.peek_back()) + delete (glsl_fbo) fbo_list.pop_back(); + while (texture_list.peek_back()) + delete (glsl_texture) texture_list.pop_back(); + delete pbo; +} + +GlslManager* GlslManager::get_instance() +{ + return (GlslManager*) mlt_properties_get_data(mlt_global_properties(), "glslManager", 0); +} + +glsl_fbo GlslManager::get_fbo(int width, int height) +{ + for (int i = 0; i < fbo_list.count(); ++i) { + glsl_fbo fbo = (glsl_fbo) fbo_list.peek(i); + if (!fbo->used && (fbo->width == width) && (fbo->height == height)) { + fbo->used = 1; + return fbo; + } + } + GLuint fb = 0; + glGenFramebuffers(1, &fb); + if (!fb) + return NULL; + + glsl_fbo fbo = new glsl_fbo_s; + if (!fbo) { + glDeleteFramebuffers(1, &fb); + return NULL; + } + fbo->fbo = fb; + fbo->width = width; + fbo->height = height; + fbo->used = 1; + fbo_list.push_back(fbo); + return fbo; +} + +void GlslManager::release_fbo(glsl_fbo fbo) +{ + fbo->used = 0; +} + +glsl_texture GlslManager::get_texture(int width, int height, GLint internal_format) +{ + for (int i = 0; i < texture_list.count(); ++i) { + glsl_texture tex = (glsl_texture) texture_list.peek(i); + if (!tex->used && (tex->width == width) && (tex->height == height) && (tex->internal_format == internal_format)) { + glBindTexture(GL_TEXTURE_2D, tex->texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glBindTexture( GL_TEXTURE_2D, 0); + tex->used = 1; + return tex; + } + } + GLuint tex = 0; + glGenTextures(1, &tex); + if (!tex) + return NULL; + + glsl_texture gtex = new glsl_texture_s; + if (!gtex) { + glDeleteTextures(1, &tex); + return NULL; + } + glBindTexture( GL_TEXTURE_2D, tex ); + glTexImage2D( GL_TEXTURE_2D, 0, internal_format, width, height, 0, internal_format, GL_UNSIGNED_BYTE, NULL ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glBindTexture( GL_TEXTURE_2D, 0 ); + + gtex->texture = tex; + gtex->width = width; + gtex->height = height; + gtex->internal_format = internal_format; + gtex->used = 1; + texture_list.push_back(gtex); + return gtex; +} + +void GlslManager::release_texture(glsl_texture texture) +{ + texture->used = 0; +} + +glsl_pbo GlslManager::get_pbo(int size) +{ + if (!pbo) { + GLuint pb = 0; + glGenBuffers(1, &pb); + if (!pb) + return NULL; + + pbo = new glsl_pbo_s; + if (!pbo) { + glDeleteBuffers(1, &pb); + return NULL; + } + pbo->pbo = pb; + } + if (size > pbo->size) { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo->pbo); + glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, size, NULL, GL_STREAM_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0); + pbo->size = size; + } + return pbo; +} + +void GlslManager::onInit( mlt_properties owner, GlslManager* filter ) +{ + mlt_log_verbose( filter->get_service(), "%s: %d\n", __FUNCTION__, syscall(SYS_gettid) ); +#ifdef WIN32 + std::string path = std::string(mlt_environment("MLT_APPDIR")).append("\\share\\movit"); +#elif defined(__DARWIN__) && defined(RELOCATABLE) + std::string path = std::string(mlt_environment("MLT_APPDIR")).append("/share/movit"); +#else + std::string path = std::string(getenv("MLT_MOVIT_PATH") ? getenv("MLT_MOVIT_PATH") : SHADERDIR); +#endif + ::init_movit( path, mlt_log_get_level() == MLT_LOG_DEBUG? MOVIT_DEBUG_ON : MOVIT_DEBUG_OFF ); + filter->set( "glsl_supported", movit_initialized ); +} + +extern "C" { + +mlt_filter filter_glsl_manager_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + GlslManager* g = GlslManager::get_instance(); + if (g) + g->inc_ref(); + else + g = new GlslManager(); + return g->get_filter(); +} + +} // extern "C" + +static void deleteChain( EffectChain* chain ) +{ + delete chain; +} + +bool GlslManager::init_chain( mlt_service service ) +{ + bool error = true; + mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); + EffectChain* chain = (EffectChain*) mlt_properties_get_data( properties, "movit chain", NULL ); + if ( !chain ) { + mlt_profile profile = mlt_service_profile( service ); + Input* input = new MltInput( profile->width, profile->height ); + chain = new EffectChain( profile->display_aspect_num, profile->display_aspect_den ); + chain->add_input( input ); + mlt_properties_set_data( properties, "movit chain", chain, 0, (mlt_destructor) deleteChain, NULL ); + mlt_properties_set_data( properties, "movit input", input, 0, NULL, NULL ); + mlt_properties_set_int( properties, "_movit finalized", 0 ); + error = false; + } + return error; +} + +EffectChain* GlslManager::get_chain( mlt_service service ) +{ + return (EffectChain*) mlt_properties_get_data( MLT_SERVICE_PROPERTIES(service), "movit chain", NULL ); +} + +MltInput *GlslManager::get_input( mlt_service service ) +{ + return (MltInput*) mlt_properties_get_data( MLT_SERVICE_PROPERTIES(service), "movit input", NULL ); +} + +void GlslManager::reset_finalized( mlt_service service ) +{ + mlt_properties_set_int( MLT_SERVICE_PROPERTIES(service), "_movit finalized", 0 ); +} + +Effect* GlslManager::get_effect( mlt_filter filter, mlt_frame frame ) +{ + mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); + mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); + char *unique_id = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "_unique_id" ); + return (Effect*) mlt_properties_get_data( properties, unique_id, NULL ); +} + +Effect* GlslManager::add_effect( mlt_filter filter, mlt_frame frame, Effect* effect ) +{ + mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); + mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); + char *unique_id = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "_unique_id" ); + EffectChain* chain = (EffectChain*) mlt_properties_get_data( properties, "movit chain", NULL ); + chain->add_effect( effect ); + mlt_properties_set_data( properties, unique_id, effect, 0, NULL, NULL ); + return effect; +} + +Effect* GlslManager::add_effect( mlt_filter filter, mlt_frame frame, Effect* effect, Effect* input_b ) +{ + mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); + mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); + char *unique_id = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "_unique_id" ); + EffectChain* chain = (EffectChain*) mlt_properties_get_data( properties, "movit chain", NULL ); + chain->add_effect( effect, chain->last_added_effect(), + input_b? input_b : chain->last_added_effect() ); + mlt_properties_set_data( properties, unique_id, effect, 0, NULL, NULL ); + return effect; +} + +void GlslManager::render( mlt_service service, void* chain, GLuint fbo, int width, int height ) +{ + EffectChain* effect_chain = (EffectChain*) chain; + mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); + if ( !mlt_properties_get_int( properties, "_movit finalized" ) ) { + mlt_properties_set_int( properties, "_movit finalized", 1 ); + effect_chain->add_effect( new Mlt::VerticalFlip() ); + effect_chain->finalize(); + } + effect_chain->render_to_fbo( fbo, width, height ); +} diff --git a/src/modules/opengl/filter_lift_gamma_gain.cpp b/src/modules/opengl/filter_lift_gamma_gain.cpp new file mode 100644 index 00000000..8129063a --- /dev/null +++ b/src/modules/opengl/filter_lift_gamma_gain.cpp @@ -0,0 +1,78 @@ +/* + * filter_lift_gamma_gain.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "glsl_manager.h" +#include + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + if ( !mlt_frame_is_test_card( frame ) ) { + Effect* effect = GlslManager::get_effect( filter, frame ); + if ( !effect ) + effect = GlslManager::add_effect( filter, frame, new LiftGammaGainEffect ); + if ( effect ) { + mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ); + RGBTriplet triplet( + mlt_properties_get_double( filter_props, "lift_r" ), + mlt_properties_get_double( filter_props, "lift_g" ), + mlt_properties_get_double( filter_props, "lift_b" ) + ); + bool ok = effect->set_vec3( "lift", (float*) &triplet ); + triplet.r = mlt_properties_get_double( filter_props, "gamma_r" ); + triplet.g = mlt_properties_get_double( filter_props, "gamma_g" ); + triplet.b = mlt_properties_get_double( filter_props, "gamma_b" ); + ok |= effect->set_vec3( "gamma", (float*) &triplet ); + triplet.r = mlt_properties_get_double( filter_props, "gain_r" ); + triplet.g = mlt_properties_get_double( filter_props, "gain_g" ); + triplet.b = mlt_properties_get_double( filter_props, "gain_b" ); + ok |= effect->set_vec3( "gain", (float*) &triplet ); + assert(ok); + } + } + return frame; +} + +extern "C" { + +mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_properties_set_double( properties, "lift_r", 0.0 ); + mlt_properties_set_double( properties, "lift_g", 0.0 ); + mlt_properties_set_double( properties, "lift_b", 0.0 ); + mlt_properties_set_double( properties, "gamma_r", 1.0 ); + mlt_properties_set_double( properties, "gamma_g", 1.0 ); + mlt_properties_set_double( properties, "gamma_b", 1.0 ); + mlt_properties_set_double( properties, "gain_r", 1.0 ); + mlt_properties_set_double( properties, "gain_g", 1.0 ); + mlt_properties_set_double( properties, "gain_b", 1.0 ); + filter->process = process; + } + return filter; +} + +} diff --git a/src/modules/opengl/filter_lift_gamma_gain.yml b/src/modules/opengl/filter_lift_gamma_gain.yml new file mode 100644 index 00000000..09dfed25 --- /dev/null +++ b/src/modules/opengl/filter_lift_gamma_gain.yml @@ -0,0 +1,80 @@ +schema_version: 0.1 +type: filter +identifier: movit.lift_gamma_gain +title: Lift, Gamma, and Gain (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: > + A simple lift/gamma/gain effect, used for color grading. +notes: > + Very roughly speaking, lift=shadows, gamma=midtones and gain=highlights, + although all parameters affect the entire curve. Mathematically speaking, + it is a bit unusual to look at gamma as a color, but it works pretty well + in practice. + The classic formula is: output = (gain * (x + lift * (1-x)))^(1/gamma). + The lift is actually a case where we actually would _not_ want linear light; + since black by definition becomes equal to the lift color, we want lift to + be pretty close to black, but in linear light that means lift affects the + rest of the curve relatively little. Thus, we actually convert to gamma 2.2 + before lift, and then back again afterwards. (Gain and gamma are, + up to constants, commutative with the de-gamma operation.) + +parameters: + - identifier: lift_r + title: Lift Red + type: float + minimum: 0.0 + default: 0.0 + + - identifier: lift_g + title: Lift Green + type: float + minimum: 0.0 + default: 0.0 + + - identifier: lift_b + title: Lift Blue + type: float + minimum: 0.0 + default: 0.0 + + - identifier: gamma_r + title: Gamma Red + type: float + minimum: 0.0 + default: 1.0 + + - identifier: gamma_g + title: Gamma Green + type: float + minimum: 0.0 + default: 1.0 + + - identifier: gamma_b + title: Gamma Blue + type: float + minimum: 0.0 + default: 1.0 + + - identifier: gain_r + title: Gain Red + type: float + minimum: 0.0 + default: 1.0 + + - identifier: gain_g + title: Gain Green + type: float + minimum: 0.0 + default: 1.0 + + - identifier: gain_b + title: Gain Blue + type: float + minimum: 0.0 + default: 1.0 diff --git a/src/modules/opengl/filter_movit_blur.cpp b/src/modules/opengl/filter_movit_blur.cpp new file mode 100644 index 00000000..5c66e976 --- /dev/null +++ b/src/modules/opengl/filter_movit_blur.cpp @@ -0,0 +1,57 @@ +/* + * filter_movit_blur.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "glsl_manager.h" +#include + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + if ( !mlt_frame_is_test_card( frame ) ) { + Effect* effect = GlslManager::get_effect( filter, frame ); + if ( !effect ) + effect = GlslManager::add_effect( filter, frame, new BlurEffect() ); + if ( effect ) { + mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ); + bool ok = effect->set_float( "radius", mlt_properties_get_double( filter_props, "radius" ) ); + assert(ok); + } + } + return frame; +} + +extern "C" { + +mlt_filter filter_movit_blur_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_properties_set_double( properties, "radius", 3 ); + filter->process = process; + } + return filter; +} + +} diff --git a/src/modules/opengl/filter_movit_blur.yml b/src/modules/opengl/filter_movit_blur.yml new file mode 100644 index 00000000..3109af7f --- /dev/null +++ b/src/modules/opengl/filter_movit_blur.yml @@ -0,0 +1,22 @@ +schema_version: 0.1 +type: filter +identifier: movit.blur +title: Blur (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: > + A separable 2D blur implemented by a combination of mipmap filtering + and convolution (essentially giving a convolution with a piecewise linear + approximation to the true impulse response). + +parameters: + - identifier: radius + title: Radius + type: float + minimum: 0 + default: 3 diff --git a/src/modules/opengl/filter_movit_convert.cpp b/src/modules/opengl/filter_movit_convert.cpp new file mode 100644 index 00000000..12c75dad --- /dev/null +++ b/src/modules/opengl/filter_movit_convert.cpp @@ -0,0 +1,325 @@ +/* + * filter_movit_convert.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include "glsl_manager.h" +#include +#include +#include "mlt_movit_input.h" + + +static void yuv422_to_yuv422p( uint8_t *yuv422, uint8_t *yuv422p, int width, int height ) +{ + uint8_t *Y = yuv422p; + uint8_t *U = Y + width * height; + uint8_t *V = U + width * height / 2; + int n = width * height / 2 + 1; + while ( --n ) { + *Y++ = *yuv422++; + *U++ = *yuv422++; + *Y++ = *yuv422++; + *V++ = *yuv422++; + } +} + +static int convert_on_cpu( mlt_frame frame, uint8_t **image, mlt_image_format *format, mlt_image_format output_format ) +{ + int error = 0; + mlt_filter cpu_csc = (mlt_filter) mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "cpu_csc", NULL ); + if ( cpu_csc ) { + int (* save_fp )( mlt_frame self, uint8_t **image, mlt_image_format *input, mlt_image_format output ) + = frame->convert_image; + frame->convert_image = NULL; + mlt_filter_process( cpu_csc, frame ); + error = frame->convert_image( frame, image, format, output_format ); + frame->convert_image = save_fp; + } else { + error = 1; + } + return error; +} + +static int convert_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, mlt_image_format output_format ) +{ + // Nothing to do! + if ( *format == output_format ) + return 0; + + mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); + + mlt_log_verbose( NULL, "filter_movit_convert: %s -> %s\n", + mlt_image_format_name( *format ), mlt_image_format_name( output_format ) ); + + // Use CPU if glsl not initialized or not supported. + GlslManager* glsl = GlslManager::get_instance(); + if ( !glsl || !glsl->get_int("glsl_supported" ) ) + return convert_on_cpu( frame, image, format, output_format ); + + // Do non-GL image conversions on a CPU-based image converter. + if ( *format != mlt_image_glsl && output_format != mlt_image_glsl && output_format != mlt_image_glsl_texture ) + return convert_on_cpu( frame, image, format, output_format ); + + int error = 0; + int width = mlt_properties_get_int( properties, "width" ); + int height = mlt_properties_get_int( properties, "height" ); + int img_size = mlt_image_format_size( *format, width, height, NULL ); + mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); + mlt_service service = MLT_PRODUCER_SERVICE(producer); + EffectChain* chain = GlslManager::get_chain( service ); + MltInput* input = GlslManager::get_input( service ); + + // Use a temporary chain to convert image in RAM to OpenGL texture. + if ( output_format == mlt_image_glsl_texture && *format != mlt_image_glsl ) { + input = new MltInput( width, height ); + chain = new EffectChain( width, height ); + chain->add_input( input ); + } + + if ( *format != mlt_image_glsl ) { + if ( *format == mlt_image_rgb24a || *format == mlt_image_opengl ) { + input->useFlatInput( chain, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, width, height ); + input->set_pixel_data( *image ); + } + else if ( *format == mlt_image_rgb24 ) { + input->useFlatInput( chain, FORMAT_RGB, width, height ); + input->set_pixel_data( *image ); + } + else if ( *format == mlt_image_yuv420p ) { + ImageFormat image_format; + YCbCrFormat ycbcr_format; + if ( 709 == mlt_properties_get_int( properties, "colorspace" ) ) { + image_format.color_space = COLORSPACE_REC_709; + image_format.gamma_curve = GAMMA_REC_709; + ycbcr_format.luma_coefficients = YCBCR_REC_709; + } else if ( 576 == mlt_properties_get_int( properties, "height" ) ) { + image_format.color_space = COLORSPACE_REC_601_625; + image_format.gamma_curve = GAMMA_REC_601; + ycbcr_format.luma_coefficients = YCBCR_REC_601; + } else { + image_format.color_space = COLORSPACE_REC_601_525; + image_format.gamma_curve = GAMMA_REC_601; + ycbcr_format.luma_coefficients = YCBCR_REC_601; + } + ycbcr_format.full_range = mlt_properties_get_int( properties, "force_full_luma" ); + ycbcr_format.chroma_subsampling_x = ycbcr_format.chroma_subsampling_y = 2; + // TODO: make new frame properties set by producers + ycbcr_format.cb_x_position = ycbcr_format.cr_x_position = 0.0f; + ycbcr_format.cb_y_position = ycbcr_format.cr_y_position = 0.5f; + input->useYCbCrInput( chain, image_format, ycbcr_format, width, height ); + input->set_pixel_data( *image ); + } + else if ( *format == mlt_image_yuv422 ) { + ImageFormat image_format; + YCbCrFormat ycbcr_format; + if ( 709 == mlt_properties_get_int( properties, "colorspace" ) ) { + image_format.color_space = COLORSPACE_REC_709; + image_format.gamma_curve = GAMMA_REC_709; + ycbcr_format.luma_coefficients = YCBCR_REC_709; + } else if ( 576 == height ) { + image_format.color_space = COLORSPACE_REC_601_625; + image_format.gamma_curve = GAMMA_REC_601; + ycbcr_format.luma_coefficients = YCBCR_REC_601; + } else { + image_format.color_space = COLORSPACE_REC_601_525; + image_format.gamma_curve = GAMMA_REC_601; + ycbcr_format.luma_coefficients = YCBCR_REC_601; + } + ycbcr_format.full_range = mlt_properties_get_int( properties, "force_full_luma" ); + ycbcr_format.chroma_subsampling_x = 2; + ycbcr_format.chroma_subsampling_y = 1; + // TODO: make new frame properties set by producers + ycbcr_format.cb_x_position = ycbcr_format.cr_x_position = 0.0f; + ycbcr_format.cb_y_position = ycbcr_format.cr_y_position = 0.5f; + input->useYCbCrInput( chain, image_format, ycbcr_format, width, height ); + + // convert chunky to planar + uint8_t* planar = (uint8_t*) mlt_pool_alloc( img_size ); + yuv422_to_yuv422p( *image, planar, width, height ); + input->set_pixel_data( planar ); + mlt_frame_set_image( frame, planar, img_size, mlt_pool_release ); + } + } + + if ( output_format != mlt_image_glsl ) { + glsl_fbo fbo = glsl->get_fbo( width, height ); + + if ( output_format == mlt_image_glsl_texture ) { + glsl_texture texture = glsl->get_texture( width, height, GL_RGBA ); + + glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo ); + check_error(); + glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + check_error(); + + // Using a temporary chain to convert image in RAM to OpenGL texture. + if ( *format != mlt_image_glsl ) + GlslManager::reset_finalized( service ); + GlslManager::render( service, chain, fbo->fbo, width, height ); + + glFinish(); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + check_error(); + // Using a temporary chain to convert image in RAM to OpenGL texture. + if ( *format != mlt_image_glsl ) + delete chain; + + *image = (uint8_t*) &texture->texture; + mlt_frame_set_image( frame, *image, 0, NULL ); + mlt_properties_set_data( properties, "movit.convert", texture, 0, + (mlt_destructor) GlslManager::release_texture, NULL ); + mlt_properties_set_int( properties, "format", output_format ); + *format = output_format; + } + else { + // Use a PBO to hold the data we read back with glReadPixels() + // (Intel/DRI goes into a slow path if we don't read to PBO) + GLenum gl_format = ( output_format == mlt_image_rgb24a || output_format == mlt_image_opengl )? + GL_RGBA : GL_RGB; + img_size = width * height * ( gl_format == GL_RGB? 3 : 4 ); + glsl_pbo pbo = glsl->get_pbo( img_size ); + glsl_texture texture = glsl->get_texture( width, height, gl_format ); + + if ( fbo && pbo && texture ) { + // Set the FBO + glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo ); + check_error(); + glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + check_error(); + + GlslManager::render( service, chain, fbo->fbo, width, height ); + + // Read FBO into PBO + glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, pbo->pbo ); + check_error(); + glBufferData( GL_PIXEL_PACK_BUFFER_ARB, img_size, NULL, GL_STREAM_READ ); + check_error(); + glReadPixels( 0, 0, width, height, gl_format, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0) ); + check_error(); + + // Copy from PBO + uint8_t* buf = (uint8_t*) glMapBuffer( GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY ); + check_error(); + + if ( output_format == mlt_image_yuv422 || output_format == mlt_image_yuv420p ) { + *image = buf; + *format = mlt_image_rgb24; + error = convert_on_cpu( frame, image, format, output_format ); + } + else { + *image = (uint8_t*) mlt_pool_alloc( img_size ); + mlt_frame_set_image( frame, *image, img_size, mlt_pool_release ); + memcpy( *image, buf, img_size ); + } + + // Release PBO and FBO + glUnmapBuffer( GL_PIXEL_PACK_BUFFER_ARB ); + check_error(); + glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, 0 ); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + check_error(); + glBindTexture( GL_TEXTURE_2D, 0 ); + check_error(); + GlslManager::release_texture( texture ); + + mlt_properties_set_int( properties, "format", output_format ); + *format = output_format; + } + else { + error = 1; + } + } + if ( fbo ) GlslManager::release_fbo( fbo ); + } + else { + mlt_properties_set_int( properties, "format", output_format ); + *format = output_format; + } + + return error; +} + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + // Set a default colorspace on the frame if not yet set by the producer. + // The producer may still change it during get_image. + // This way we do not have to modify each producer to set a valid colorspace. + mlt_properties properties = MLT_FRAME_PROPERTIES(frame); + if ( mlt_properties_get_int( properties, "colorspace" ) <= 0 ) + mlt_properties_set_int( properties, "colorspace", mlt_service_profile( MLT_FILTER_SERVICE(filter) )->colorspace ); + + frame->convert_image = convert_image; + + mlt_filter cpu_csc = (mlt_filter) mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), "cpu_csc", NULL ); + mlt_properties_inc_ref( MLT_FILTER_PROPERTIES(cpu_csc) ); + mlt_properties_set_data( properties, "cpu_csc", cpu_csc, 0, + (mlt_destructor) mlt_filter_close, NULL ); + + return frame; +} + +static mlt_filter create_filter( mlt_profile profile, char *effect ) +{ + mlt_filter filter = NULL; + char *id = strdup( effect ); + char *arg = strchr( id, ':' ); + if ( arg != NULL ) + *arg ++ = '\0'; + + // The swscale and avcolor_space filters require resolution as arg to test compatibility + if ( !strcmp( effect, "avcolor_space" ) ) + arg = (char*) profile->width; + + filter = mlt_factory_filter( profile, id, arg ); + if ( filter ) + mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_loader", 1 ); + free( id ); + return filter; +} + +extern "C" { + +mlt_filter filter_movit_convert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) + { + mlt_filter cpu_csc = create_filter( profile, "avcolor_space" ); + if ( !cpu_csc ) + cpu_csc = create_filter( profile, "imageconvert" ); + if ( cpu_csc ) + mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "cpu_csc", cpu_csc, 0, + (mlt_destructor) mlt_filter_close, NULL ); + filter->process = process; + } + return filter; +} + +} diff --git a/src/modules/opengl/filter_movit_crop.cpp b/src/modules/opengl/filter_movit_crop.cpp new file mode 100644 index 00000000..1bacd1a7 --- /dev/null +++ b/src/modules/opengl/filter_movit_crop.cpp @@ -0,0 +1,116 @@ +/* + * filter_movit_crop.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "glsl_manager.h" +#include + +static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + int error = 0; + mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); + mlt_image_format requested_format = *format; + + // Correct width/height if necessary + *width = mlt_properties_get_int( properties, "crop.original_width" ); + *height = mlt_properties_get_int( properties, "crop.original_height" ); + if ( *width == 0 || *height == 0 ) + { + *width = mlt_properties_get_int( properties, "meta.media.width" ); + *height = mlt_properties_get_int( properties, "meta.media.height" ); + } + if ( *width == 0 || *height == 0 ) + { + *width = profile->width; + *height = profile->height; + } + mlt_properties_set_int( properties, "rescale_width", *width ); + mlt_properties_set_int( properties, "rescale_height", *height ); + + // Get the image as requested +// *format = (mlt_image_format) mlt_properties_get_int( MLT_PRODUCER_PROPERTIES(producer), "_movit image_format" ); + *format = mlt_image_none; + error = mlt_frame_get_image( frame, image, format, width, height, writable ); + + // Skip processing if requested. + if ( requested_format == mlt_image_none ) + return error; + + if ( !error && *format != mlt_image_glsl && frame->convert_image ) { + // Pin the requested format to the first one returned. +// mlt_properties_set_int( MLT_PRODUCER_PROPERTIES(producer), "_movit image_format", *format ); + error = frame->convert_image( frame, image, format, mlt_image_glsl ); + } + if ( !error ) { + double left = mlt_properties_get_double( properties, "crop.left" ); + double right = mlt_properties_get_double( properties, "crop.right" ); + double top = mlt_properties_get_double( properties, "crop.top" ); + double bottom = mlt_properties_get_double( properties, "crop.bottom" ); + int owidth = *width - left - right; + int oheight = *height - top - bottom; + owidth = owidth < 0 ? 0 : owidth; + oheight = oheight < 0 ? 0 : oheight; + + mlt_log_debug( MLT_FILTER_SERVICE(filter), "%dx%d -> %dx%d\n", *width, *height, owidth, oheight); + + Effect* effect = GlslManager::get_effect( filter, frame ); + if ( effect ) { + bool ok = effect->set_int( "width", owidth ); + ok |= effect->set_int( "height", oheight ); + ok |= effect->set_float( "left", -left ); + ok |= effect->set_float( "top", -top ); + assert(ok); + *width = owidth; + *height = oheight; + } + } + + return error; +} + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); + if ( !GlslManager::init_chain( MLT_PRODUCER_SERVICE(producer) ) ) { + Effect* effect = GlslManager::add_effect( filter, frame, new PaddingEffect ); + RGBATuple border_color( 0.0f, 0.0f, 0.0f, 1.0f ); + bool ok = effect->set_vec4( "border_color", (float*) &border_color ); + assert(ok); + } + mlt_frame_push_service( frame, filter ); + mlt_frame_push_get_image( frame, get_image ); + return frame; +} + +extern "C" +mlt_filter filter_movit_crop_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) { + filter->process = process; + } + return filter; +} diff --git a/src/modules/opengl/filter_movit_diffusion.cpp b/src/modules/opengl/filter_movit_diffusion.cpp new file mode 100644 index 00000000..31d705bd --- /dev/null +++ b/src/modules/opengl/filter_movit_diffusion.cpp @@ -0,0 +1,59 @@ +/* + * filter_movit_diffusion.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "glsl_manager.h" +#include + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + if ( !mlt_frame_is_test_card( frame ) ) { + Effect* effect = GlslManager::get_effect( filter, frame ); + if ( !effect ) + effect = GlslManager::add_effect( filter, frame, new DiffusionEffect() ); + if ( effect ) { + mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ); + bool ok = effect->set_float( "radius", mlt_properties_get_double( filter_props, "radius" ) ); + ok |= effect->set_float( "blurred_mix_amount", mlt_properties_get_double( filter_props, "mix" ) ); + assert(ok); + } + } + return frame; +} + +extern "C" { + +mlt_filter filter_movit_diffusion_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_properties_set_double( properties, "radius", 3.0 ); + mlt_properties_set_double( properties, "mix", 0.3 ); + filter->process = process; + } + return filter; +} + +} diff --git a/src/modules/opengl/filter_movit_diffusion.yml b/src/modules/opengl/filter_movit_diffusion.yml new file mode 100644 index 00000000..e97d1233 --- /dev/null +++ b/src/modules/opengl/filter_movit_diffusion.yml @@ -0,0 +1,35 @@ +schema_version: 0.1 +type: filter +identifier: movit.diffusion +title: Diffusion (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: > + There are many different effects that go under the name of "diffusion", + seemingly all of the inspired by the effect you get when you put a + diffusion filter in front of your camera lens. The effect most people + want is a general flattening/smoothing of the light, and reduction of + fine detail (most notably, blemishes in people's skin), without ruining + edges, which a regular blur would do. + We do a relatively simple version, sometimes known as "white diffusion", + where we first blur the picture, and then overlay it on the original + using the original as a matte. + +parameters: + - identifier: radius + title: Blur Radius + type: float + minimum: 0.0 + default: 3.0 + + - identifier: mix + title: Blurriness + type: float + minimum: 0.0 + maximum: 1.0 + default: 0.3 diff --git a/src/modules/opengl/filter_movit_glow.cpp b/src/modules/opengl/filter_movit_glow.cpp new file mode 100644 index 00000000..4026477a --- /dev/null +++ b/src/modules/opengl/filter_movit_glow.cpp @@ -0,0 +1,61 @@ +/* + * filter_movit_glow.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "glsl_manager.h" +#include + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + if ( !mlt_frame_is_test_card( frame ) ) { + Effect* effect = GlslManager::get_effect( filter, frame ); + if ( !effect ) + effect = GlslManager::add_effect( filter, frame, new GlowEffect() ); + if ( effect ) { + mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ); + bool ok = effect->set_float( "radius", mlt_properties_get_double( filter_props, "radius" ) ); + ok |= effect->set_float( "blurred_mix_amount", mlt_properties_get_double( filter_props, "blur_mix" ) ); + ok |= effect->set_float( "highlight_cutoff", mlt_properties_get_double( filter_props, "highlight_cutoff" ) ); + assert(ok); + } + } + return frame; +} + +extern "C" { + +mlt_filter filter_movit_glow_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_properties_set_double( properties, "radius", 20.0 ); + mlt_properties_set_double( properties, "blur_mix", 1.0 ); + mlt_properties_set_double( properties, "highlight_cutoff", 0.2 ); + filter->process = process; + } + return filter; +} + +} diff --git a/src/modules/opengl/filter_movit_glow.yml b/src/modules/opengl/filter_movit_glow.yml new file mode 100644 index 00000000..258f6832 --- /dev/null +++ b/src/modules/opengl/filter_movit_glow.yml @@ -0,0 +1,35 @@ +schema_version: 0.1 +type: filter +identifier: movit.glow +title: Glow (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: > + Cut out the highlights of the image (everything above a certain threshold), + blur them, and overlay them onto the original image. + +parameters: + - identifier: radius + title: Radius + type: float + minimum: 0.0 + default: 20.0 + + - identifier: blur_mix + title: Highlight Blurriness + type: float + minimum: 0.0 + maximum: 1.0 + default: 1.0 + + - identifier: highlight_cutoff + title: Highlight Cutoff Threshold + type: float + minimum: 0.0 + maximum: 1.0 + default: 0.2 diff --git a/src/modules/opengl/filter_movit_mirror.cpp b/src/modules/opengl/filter_movit_mirror.cpp new file mode 100644 index 00000000..8b6ab1fe --- /dev/null +++ b/src/modules/opengl/filter_movit_mirror.cpp @@ -0,0 +1,51 @@ +/* + * filter_movit_mirror.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "glsl_manager.h" +#include + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + if ( !mlt_frame_is_test_card( frame ) ) { + Effect* effect = GlslManager::get_effect( filter, frame ); + if ( !effect ) + GlslManager::add_effect( filter, frame, new MirrorEffect() ); + } + return frame; +} + +extern "C" { + +mlt_filter filter_movit_mirror_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) { + filter->process = process; + } + return filter; +} + +} + diff --git a/src/modules/opengl/filter_movit_mirror.yml b/src/modules/opengl/filter_movit_mirror.yml new file mode 100644 index 00000000..123b3436 --- /dev/null +++ b/src/modules/opengl/filter_movit_mirror.yml @@ -0,0 +1,12 @@ +schema_version: 0.1 +type: filter +identifier: movit.mirror +title: Mirror (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +description: A simple horizontal mirroring. +tags: + - Video diff --git a/src/modules/opengl/filter_movit_opacity.cpp b/src/modules/opengl/filter_movit_opacity.cpp new file mode 100644 index 00000000..6a7333d1 --- /dev/null +++ b/src/modules/opengl/filter_movit_opacity.cpp @@ -0,0 +1,67 @@ +/* + * filter_movit_opacity.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "glsl_manager.h" +#include + +static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + Effect* effect = GlslManager::get_effect( filter, frame ); + bool ok = effect->set_float( "strength_first", mlt_properties_get_double( properties, "opacity" ) ); + assert(ok); + *format = mlt_image_glsl; + return mlt_frame_get_image( frame, image, format, width, height, writable ); +} + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + if ( !mlt_frame_is_test_card( frame ) ) { + Effect* effect = GlslManager::get_effect( filter, frame ); + if ( !effect ) { + effect = GlslManager::add_effect( filter, frame, new MixEffect, 0 ); + assert(effect); + bool ok = effect->set_float( "strength_first", 1.0f ); + ok |= effect->set_float( "strength_second", 0.0f ); + assert(ok); + } + } + mlt_frame_push_service( frame, filter ); + mlt_frame_push_get_image( frame, get_image ); + return frame; +} + +extern "C" +mlt_filter filter_movit_opacity_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_properties_set( properties, "opacity", arg? arg : "1" ); + filter->process = process; + } + return filter; +} diff --git a/src/modules/opengl/filter_movit_opacity.yml b/src/modules/opengl/filter_movit_opacity.yml new file mode 100644 index 00000000..fa82b60d --- /dev/null +++ b/src/modules/opengl/filter_movit_opacity.yml @@ -0,0 +1,25 @@ +schema_version: 0.1 +type: filter +identifier: movit.opacity +title: Opacity (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: Adjust the opacity of an image through the alpha channel. +notes: > + When used in some transitions this will cause this video to be mixed with + the other video. If the video this is applied to already has an alpha channel, + then this preserves that by multiplying the opacity level with the alpha channel. + This filter is especially handy when used in conjunction with movit.overlay. + +parameters: + - identifier: opacity + title: Opacity + type: float + minimum: 0 + maximum: 1 + default: 1 diff --git a/src/modules/opengl/filter_movit_rect.cpp b/src/modules/opengl/filter_movit_rect.cpp new file mode 100644 index 00000000..004f2f71 --- /dev/null +++ b/src/modules/opengl/filter_movit_rect.cpp @@ -0,0 +1,47 @@ +/* + * filter_movit_rect.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include "glsl_manager.h" + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + // Drive the resize and resample filters on the b_frame for these composite parameters + mlt_properties properties = MLT_FILTER_PROPERTIES(filter); + mlt_properties frame_props = MLT_FRAME_PROPERTIES(frame); + mlt_properties_set( frame_props, "resize.geometry", mlt_properties_get( properties, "geometry" ) ); + mlt_properties_set( frame_props, "resize.fill", mlt_properties_get( properties, "fill" ) ); + mlt_properties_set( frame_props, "resize.halign", mlt_properties_get( properties, "halign" ) ); + mlt_properties_set( frame_props, "resize.valign", mlt_properties_get( properties, "valign" ) ); + return frame; +} + +extern "C" +mlt_filter filter_movit_rect_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties_set( MLT_FILTER_PROPERTIES(filter), "geometry", arg ); + mlt_properties_set_int( MLT_FILTER_PROPERTIES(filter), "fill", 1 ); + filter->process = process; + } + return filter; +} diff --git a/src/modules/opengl/filter_movit_rect.yml b/src/modules/opengl/filter_movit_rect.yml new file mode 100644 index 00000000..a077927a --- /dev/null +++ b/src/modules/opengl/filter_movit_rect.yml @@ -0,0 +1,60 @@ +schema_version: 0.1 +type: filter +identifier: movit.rect +title: Position and Size (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: > + Change the coordinates and scale to fit within a rectangle. + +parameters: + - identifier: geometry + title: Rectangle + type: geometry + description: > + Keyframable rectangle specification. + mutable: yes + + - identifier: fill + title: Upscale to Fill + description: > + Determines whether the image will be scaled up to fill the rectangle + or whether the size will be constrained to 100% of the profile + resolution. + type: integer + default: 1 + minimum: 0 + maximum: 1 + mutable: yes + widget: checkbox + + - identifier: halign + title: Horizontal alignment + description: > + Set the horizontal alignment within the geometry rectangle. + type: string + default: left + values: + - left + - center + - right + mutable: yes + widget: combo + + - identifier: valign + title: Vertical alignment + description: > + Set the vertical alignment within the geometry rectangle. + type: string + default: top + values: + - top + - middle + - bottom + mutable: yes + widget: combo diff --git a/src/modules/opengl/filter_movit_resample.cpp b/src/modules/opengl/filter_movit_resample.cpp new file mode 100644 index 00000000..5ecdfbbd --- /dev/null +++ b/src/modules/opengl/filter_movit_resample.cpp @@ -0,0 +1,100 @@ +/* + * filter_movit_resample.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "glsl_manager.h" +#include + +static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + int error = 0; + mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); + mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); + + // Correct width/height if necessary + if ( *width == 0 || *height == 0 ) + { + *width = profile->width; + *height = profile->height; + } + + int iwidth = *width; + int iheight = *height; + double factor = mlt_properties_get_double( filter_properties, "factor" ); + factor = factor > 0 ? factor : 1.0; + int owidth = *width * factor; + int oheight = *height * factor; + + // If meta.media.width/height exist, we want that as minimum information + if ( mlt_properties_get_int( properties, "meta.media.width" ) ) + { + iwidth = mlt_properties_get_int( properties, "meta.media.width" ); + iheight = mlt_properties_get_int( properties, "meta.media.height" ); + } + + mlt_properties_set_int( properties, "rescale_width", *width ); + mlt_properties_set_int( properties, "rescale_height", *height ); + + // Deinterlace if height is changing to prevent fields mixing on interpolation + if ( iheight != oheight ) + mlt_properties_set_int( properties, "consumer_deinterlace", 1 ); + + // Get the image as requested + if ( *format != mlt_image_none ) + *format = mlt_image_glsl; + error = mlt_frame_get_image( frame, image, format, &iwidth, &iheight, writable ); + if ( !error ) { + Effect* effect = GlslManager::get_effect( filter, frame ); + if ( effect ) { + bool ok = effect->set_int( "width", owidth ); + ok |= effect->set_int( "height", oheight ); + assert(ok); + *width = owidth; + *height = oheight; + } + } + + return error; +} + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + if ( !GlslManager::get_effect( filter, frame ) ) + GlslManager::add_effect( filter, frame, new ResampleEffect ); + mlt_frame_push_service( frame, filter ); + mlt_frame_push_get_image( frame, get_image ); + return frame; +} + +extern "C" +mlt_filter filter_movit_resample_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) { + filter->process = process; + } + return filter; +} diff --git a/src/modules/opengl/filter_movit_resize.cpp b/src/modules/opengl/filter_movit_resize.cpp new file mode 100644 index 00000000..eb433feb --- /dev/null +++ b/src/modules/opengl/filter_movit_resize.cpp @@ -0,0 +1,210 @@ +/* + * filter_movit_resize.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include + +#include "glsl_manager.h" +#include +#include + +static float alignment_parse( char* align ) +{ + int ret = 0.0f; + + if ( align == NULL ); + else if ( isdigit( align[ 0 ] ) ) + ret = atoi( align ); + else if ( align[ 0 ] == 'c' || align[ 0 ] == 'm' ) + ret = 1.0f; + else if ( align[ 0 ] == 'r' || align[ 0 ] == 'b' ) + ret = 2.0f; + + return ret; +} + +static struct mlt_geometry_item_s get_geometry( mlt_profile profile, mlt_filter filter, mlt_frame frame ) +{ + mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); + mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ); + struct mlt_geometry_item_s item; + mlt_geometry geometry = (mlt_geometry) mlt_properties_get_data( filter_props, "geometry", NULL ); + char *string = mlt_properties_get( properties, "resize.geometry" ); + int length = mlt_filter_get_length2( filter, frame ); + + if ( !geometry ) { + geometry = mlt_geometry_init(); + mlt_properties_set_data( filter_props, "geometry", geometry, 0, + (mlt_destructor) mlt_geometry_close, NULL ); + mlt_geometry_parse( geometry, string, length, profile->width, profile->height ); + } else { + mlt_geometry_refresh( geometry, string, length, profile->width, profile->height ); + } + + mlt_geometry_fetch( geometry, &item, mlt_filter_get_position( filter, frame ) ); + + if ( !mlt_properties_get_int( properties, "resize.fill" ) ) { + int x = mlt_properties_get_int( properties, "meta.media.width" ); + item.w = item.w > x ? x : item.w; + x = mlt_properties_get_int( properties, "meta.media.height" ); + item.h = item.h > x ? x : item.h; + } + + return item; +} + +static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + int error = 0; + mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); + + // Retrieve the aspect ratio + double aspect_ratio = mlt_frame_get_aspect_ratio( frame ); + double consumer_aspect = mlt_profile_sar( profile ); + + // Correct Width/height if necessary + if ( *width == 0 || *height == 0 ) + { + *width = profile->width; + *height = profile->height; + } + + // Assign requested width/height from our subordinate + int owidth = *width; + int oheight = *height; + + // Use a geometry to compute position and size + struct mlt_geometry_item_s geometry_item; + geometry_item.x = geometry_item.y = 0.0f; + geometry_item.distort = 0; + if ( mlt_properties_get( properties, "resize.geometry" ) ) { + geometry_item = get_geometry( profile, filter, frame ); + owidth = lrintf( geometry_item.w ); + oheight = lrintf( geometry_item.h ); + } + + // Check for the special case - no aspect ratio means no problem :-) + if ( aspect_ratio == 0.0 ) + aspect_ratio = consumer_aspect; + + // Reset the aspect ratio + mlt_properties_set_double( properties, "aspect_ratio", aspect_ratio ); + + // Skip processing if requested. + char *rescale = mlt_properties_get( properties, "rescale.interp" ); + if ( *format == mlt_image_none || ( rescale && !strcmp( rescale, "none" ) ) ) + return mlt_frame_get_image( frame, image, format, width, height, writable ); + + if ( mlt_properties_get_int( properties, "distort" ) == 0 && + geometry_item.distort == 0 ) + { + // Normalise the input and out display aspect + int normalised_width = profile->width; + int normalised_height = profile->height; + int real_width = mlt_properties_get_int( properties, "meta.media.width" ); + int real_height = mlt_properties_get_int( properties, "meta.media.height" ); + if ( real_width == 0 ) + real_width = mlt_properties_get_int( properties, "width" ); + if ( real_height == 0 ) + real_height = mlt_properties_get_int( properties, "height" ); + double input_ar = aspect_ratio * real_width / real_height; + double output_ar = consumer_aspect * owidth / oheight; + + // Optimised for the input_ar > output_ar case (e.g. widescreen on standard) + int scaled_width = lrint( ( input_ar * normalised_width ) / output_ar ); + int scaled_height = normalised_height; + + // Now ensure that our images fit in the output frame + if ( scaled_width > normalised_width ) + { + scaled_width = normalised_width; + scaled_height = lrint( ( output_ar * normalised_height ) / input_ar ); + } + + // Now calculate the actual image size that we want + owidth = lrint( scaled_width * owidth / normalised_width ); + oheight = lrint( scaled_height * oheight / normalised_height ); + + mlt_log_debug( MLT_FILTER_SERVICE(filter), + "real %dx%d normalised %dx%d output %dx%d sar %f in-dar %f out-dar %f\n", + real_width, real_height, normalised_width, normalised_height, owidth, oheight, aspect_ratio, input_ar, output_ar); + + // Tell frame we have conformed the aspect to the consumer + mlt_frame_set_aspect_ratio( frame, consumer_aspect ); + } + + mlt_properties_set_int( properties, "distort", 0 ); + + // Now get the image + *format = mlt_image_glsl; + error = mlt_frame_get_image( frame, image, format, &owidth, &oheight, writable ); + + // Offset the position according to alignment + float w = float( *width - owidth ); + float h = float( *height - oheight ); + if ( mlt_properties_get( properties, "resize.geometry" ) ) { + // default left if geometry supplied + geometry_item.x += w * alignment_parse( mlt_properties_get( properties, "resize.halign" ) ) / 2.0f; + geometry_item.y += h * alignment_parse( mlt_properties_get( properties, "resize.valign" ) ) / 2.0f; + } else { + // default center if no geometry + geometry_item.x = w * 0.5f; + geometry_item.y = h * 0.5f; + } + + if ( !error ) { + Effect* effect = GlslManager::get_effect( filter, frame ); + if ( effect ) { + bool ok = effect->set_int( "width", *width ); + ok |= effect->set_int( "height", *height ); + ok |= effect->set_float( "left", geometry_item.x ); + ok |= effect->set_float( "top", geometry_item.y ); + assert(ok); + } + } + + return error; +} + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + if ( !GlslManager::get_effect( filter, frame ) ) + GlslManager::add_effect( filter, frame, new PaddingEffect ); + mlt_frame_push_service( frame, filter ); + mlt_frame_push_get_image( frame, get_image ); + return frame; +} + +extern "C" +mlt_filter filter_movit_resize_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) + { + filter->process = process; + } + return filter; +} diff --git a/src/modules/opengl/filter_movit_saturation.cpp b/src/modules/opengl/filter_movit_saturation.cpp new file mode 100644 index 00000000..1e8f9211 --- /dev/null +++ b/src/modules/opengl/filter_movit_saturation.cpp @@ -0,0 +1,57 @@ +/* + * filter_movit_saturation.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "glsl_manager.h" +#include + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + if ( !mlt_frame_is_test_card( frame ) ) { + Effect* effect = GlslManager::get_effect( filter, frame ); + if ( !effect ) + effect = GlslManager::add_effect( filter, frame, new SaturationEffect() ); + if ( effect ) { + mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ); + bool ok = effect->set_float( "saturation", mlt_properties_get_double( filter_props, "saturation" ) ); + assert(ok); + } + } + return frame; +} + +extern "C" { + +mlt_filter filter_movit_saturation_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_properties_set( properties, "saturation", arg? arg : "1.0" ); + filter->process = process; + } + return filter; +} + +} diff --git a/src/modules/opengl/filter_movit_saturation.yml b/src/modules/opengl/filter_movit_saturation.yml new file mode 100644 index 00000000..535af59a --- /dev/null +++ b/src/modules/opengl/filter_movit_saturation.yml @@ -0,0 +1,24 @@ +schema_version: 0.1 +type: filter +identifier: movit.saturation +title: Saturation (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: > + A simple desaturation/saturation effect. We use the Rec. 709 + definition of luminance (in linear light, of course) and linearly + interpolate between that (saturation=0) and the original signal + (saturation=1). Extrapolating that curve further (ie., saturation > 1) + gives us increased saturation if so desired. + +parameters: + - identifier: saturation + title: Saturation + type: float + minimum: 0 + default: 1 diff --git a/src/modules/opengl/filter_movit_vignette.cpp b/src/modules/opengl/filter_movit_vignette.cpp new file mode 100644 index 00000000..b0e33f7e --- /dev/null +++ b/src/modules/opengl/filter_movit_vignette.cpp @@ -0,0 +1,59 @@ +/* + * filter_movit_vignette.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "glsl_manager.h" +#include + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + if ( !mlt_frame_is_test_card( frame ) ) { + Effect* effect = GlslManager::get_effect( filter, frame ); + if ( !effect ) { + effect = GlslManager::add_effect( filter, frame, new VignetteEffect() ); + } + if ( effect ) { + mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ); + bool ok = effect->set_float( "radius", mlt_properties_get_double( filter_props, "radius" ) ); + ok |= effect->set_float( "inner_radius", mlt_properties_get_double( filter_props, "inner_radius" ) ); + assert(ok); + } + } + return frame; +} + +extern "C" { + +mlt_filter filter_movit_vignette_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) { + filter->process = process; + mlt_properties_set_double( MLT_FILTER_PROPERTIES(filter), "radius", 0.3 ); + mlt_properties_set_double( MLT_FILTER_PROPERTIES(filter), "inner_radius", 0.3 ); + } + return filter; +} + +} diff --git a/src/modules/opengl/filter_movit_vignette.yml b/src/modules/opengl/filter_movit_vignette.yml new file mode 100644 index 00000000..3355bd30 --- /dev/null +++ b/src/modules/opengl/filter_movit_vignette.yml @@ -0,0 +1,29 @@ +schema_version: 0.1 +type: filter +identifier: movit.vignette +title: Vignette (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: > + A circular vignette, falling off as cos² of the distance from the center + (the classic formula for approximating a real lens). + +parameters: + - identifier: radius + title: Outer Radius + type: float + minimum: 0.0 + maximum: 1.0 + default: 0.3 + + - identifier: inner_radius + title: Inner Radius + type: float + minimum: 0.0 + maximum: 1.0 + default: 0.3 diff --git a/src/modules/opengl/filter_white_balance.cpp b/src/modules/opengl/filter_white_balance.cpp new file mode 100644 index 00000000..e3f1d9c5 --- /dev/null +++ b/src/modules/opengl/filter_white_balance.cpp @@ -0,0 +1,65 @@ +/* + * filter_white_balance.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "glsl_manager.h" +#include + +static mlt_frame process( mlt_filter filter, mlt_frame frame ) +{ + if ( !mlt_frame_is_test_card( frame ) ) { + Effect* effect = GlslManager::get_effect( filter, frame ); + if ( !effect ) + effect = GlslManager::add_effect( filter, frame, new WhiteBalanceEffect ); + if ( effect ) { + mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ); + int color_int = mlt_properties_get_int( filter_props, "neutral_color" ); + RGBTriplet color( + float((color_int >> 24) & 0xff) / 255.0f, + float((color_int >> 16) & 0xff) / 255.0f, + float((color_int >> 8) & 0xff) / 255.0f + ); + bool ok = effect->set_vec3( "neutral_color", (float*) &color ); + ok |= effect->set_float( "output_color_temperature", mlt_properties_get_double( filter_props, "color_temperature" ) ); + assert(ok); + } + } + return frame; +} + +extern "C" { + +mlt_filter filter_white_balance_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = NULL; + GlslManager* glsl = GlslManager::get_instance(); + + if ( glsl && ( filter = mlt_filter_new() ) ) { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_properties_set( properties, "neutral_color", arg? arg : "#7f7f7f" ); + mlt_properties_set_double( properties, "color_temperature", 6500.0 ); + filter->process = process; + } + return filter; +} + +} diff --git a/src/modules/opengl/filter_white_balance.yml b/src/modules/opengl/filter_white_balance.yml new file mode 100644 index 00000000..d090dbed --- /dev/null +++ b/src/modules/opengl/filter_white_balance.yml @@ -0,0 +1,27 @@ +schema_version: 0.1 +type: filter +identifier: movit.white_balance +title: White Balance (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: Color correction in LMS color space. + +parameters: + - identifier: neutral_color + title: Neutral Color + type: string + widget: color + default: #7f7f7f + + - identifier: color_temperature + title: Color Temperature + type: float + minimum: 1000.0 + maximum: 15000.0 + default: 6500.0 + unit: Kelvin diff --git a/src/modules/opengl/glsl_manager.h b/src/modules/opengl/glsl_manager.h new file mode 100644 index 00000000..1eab9dc4 --- /dev/null +++ b/src/modules/opengl/glsl_manager.h @@ -0,0 +1,101 @@ +/* + * glsl_manager.h + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef GLSL_MANAGER_H +#define GLSL_MANAGER_H + +#include +#include +#include + +#define MAXLISTCOUNT 1024 +typedef struct glsl_list_s *glsl_list; +struct glsl_list_s +{ + void *items[MAXLISTCOUNT]; + int count; + + int ( *add )( glsl_list, void* ); + void* ( *at )( glsl_list, int ); + void* ( *take_at )( glsl_list, int ); + void* ( *take )( glsl_list, void* ); +}; + +struct glsl_texture_s +{ + int used; + GLuint texture; + int width; + int height; + GLint internal_format; +}; +typedef struct glsl_texture_s *glsl_texture; + +struct glsl_fbo_s +{ + int used; + int width; + int height; + GLuint fbo; +}; +typedef struct glsl_fbo_s *glsl_fbo; + +struct glsl_pbo_s +{ + int size; + GLuint pbo; +}; +typedef struct glsl_pbo_s *glsl_pbo; + +class Effect; +class EffectChain; +class MltInput; + +class GlslManager : public Mlt::Filter +{ +public: + GlslManager(); + ~GlslManager(); + static GlslManager* get_instance(); + + glsl_fbo get_fbo(int width, int height); + static void release_fbo(glsl_fbo); + glsl_texture get_texture(int width, int height, GLint internal_format); + static void release_texture(glsl_texture); + glsl_pbo get_pbo(int size); + + static bool init_chain(mlt_service); + static EffectChain* get_chain(mlt_service); + static MltInput* get_input(mlt_service); + static void reset_finalized(mlt_service); + static Effect* get_effect(mlt_filter, mlt_frame); + static Effect* add_effect(mlt_filter, mlt_frame, Effect*); + static Effect* add_effect(mlt_filter, mlt_frame, Effect*, Effect* input_b); + static void render(mlt_service, void *chain, GLuint fbo, int width, int height); + +private: + static void onInit( mlt_properties owner, GlslManager* filter ); + + Mlt::Deque fbo_list; + Mlt::Deque texture_list; + glsl_pbo pbo; + EffectChain* current_chain; +}; + +#endif // GLSL_MANAGER_H diff --git a/src/modules/opengl/mlt_flip_effect.h b/src/modules/opengl/mlt_flip_effect.h new file mode 100644 index 00000000..3fa9b72e --- /dev/null +++ b/src/modules/opengl/mlt_flip_effect.h @@ -0,0 +1,40 @@ +/* + * mlt_flip_effect.h + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef MLT_FLIP_EFFECT_H +#define MLT_FLIP_EFFECT_H + +namespace Mlt +{ + +class VerticalFlip : public Effect { +public: + VerticalFlip() {} + virtual std::string effect_type_id() const { return "MltVerticalFlip"; } + std::string output_fragment_shader() { + return "vec4 FUNCNAME(vec2 tc) { tc.y = 1.0 - tc.y; return INPUT(tc); }\n"; + } + virtual bool needs_linear_light() const { return false; } + virtual bool needs_srgb_primaries() const { return false; } + AlphaHandling alpha_handling() const { return DONT_CARE_ALPHA_TYPE; } +}; + +} // namespace Mlt + +#endif // MLT_FLIP_EFFECT_H diff --git a/src/modules/opengl/mlt_movit_input.cpp b/src/modules/opengl/mlt_movit_input.cpp new file mode 100644 index 00000000..7a1ca03c --- /dev/null +++ b/src/modules/opengl/mlt_movit_input.cpp @@ -0,0 +1,135 @@ +/* + * mlt_movit_input.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "mlt_movit_input.h" +#include "fbo_input.h" + +MltInput::MltInput(unsigned width, unsigned height) + : m_width(width) + , m_height(height) + , output_linear_gamma(false) + , needs_mipmaps(false) + , input(0) + , isRGB(true) +{ + register_int("output_linear_gamma", &output_linear_gamma); + register_int("needs_mipmaps", &needs_mipmaps); +} + +MltInput::~MltInput() +{ + delete input; +} + +std::string MltInput::output_fragment_shader() +{ + assert(input); + return input->output_fragment_shader(); +} + +void MltInput::set_gl_state(GLuint glsl_program_num, const std::string& prefix, unsigned *sampler_num) +{ + assert(input); + input->set_gl_state(glsl_program_num, prefix, sampler_num); +} + +Effect::AlphaHandling MltInput::alpha_handling() const +{ + assert(input); + return input->alpha_handling(); +} + +void MltInput::finalize() +{ + assert(input); + bool ok = input->set_int("output_linear_gamma", output_linear_gamma); + ok |= input->set_int("needs_mipmaps", needs_mipmaps); + assert(ok); + input->finalize(); +} + +bool MltInput::can_output_linear_gamma() const +{ + assert(input); + return input->can_output_linear_gamma(); +} + +Colorspace MltInput::get_color_space() const +{ + assert(input); + return input->get_color_space(); +} +GammaCurve MltInput::get_gamma_curve() const +{ + assert(input); + return input->get_gamma_curve(); +} + +void MltInput::useFlatInput(EffectChain* chain, MovitPixelFormat pix_fmt, unsigned width, unsigned height) +{ + if (!input) { + m_width = width; + m_height = height; + ImageFormat image_format; + image_format.color_space = COLORSPACE_sRGB; + image_format.gamma_curve = GAMMA_sRGB; + input = new FlatInput(image_format, pix_fmt, GL_UNSIGNED_BYTE, width, height); + chain->add_output(image_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED); + chain->set_dither_bits(8); + } +} + +void MltInput::useYCbCrInput(EffectChain* chain, const ImageFormat& image_format, const YCbCrFormat& ycbcr_format, unsigned width, unsigned height) +{ + if (!input) { + m_width = width; + m_height = height; + input = new YCbCrInput(image_format, ycbcr_format, width, height); + ImageFormat output_format; + output_format.color_space = COLORSPACE_sRGB; + output_format.gamma_curve = GAMMA_sRGB; + chain->add_output(output_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED); + chain->set_dither_bits(8); + isRGB = false; + m_ycbcr_format = ycbcr_format; + } +} + +void MltInput::useFBOInput(EffectChain *chain, GLuint texture) +{ + if (!input) { + FBOInput* fboInput = new FBOInput(m_width, m_height); + input = fboInput; + fboInput->set_texture(texture); + } +} + +void MltInput::set_pixel_data(const unsigned char* data) +{ + assert(input); + if (isRGB) { + FlatInput* flat = (FlatInput*) input; + flat->set_pixel_data(data); + } else { + YCbCrInput* ycbcr = (YCbCrInput*) input; + ycbcr->set_pixel_data(0, data); + ycbcr->set_pixel_data(1, &data[m_width * m_height]); + ycbcr->set_pixel_data(2, &data[m_width * m_height + (m_width / m_ycbcr_format.chroma_subsampling_x * m_height / m_ycbcr_format.chroma_subsampling_y)]); + } +} diff --git a/src/modules/opengl/mlt_movit_input.h b/src/modules/opengl/mlt_movit_input.h new file mode 100644 index 00000000..2f5de231 --- /dev/null +++ b/src/modules/opengl/mlt_movit_input.h @@ -0,0 +1,61 @@ +/* + * mlt_movit_input.h + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef MLT_MOVIT_INPUT_H +#define MLT_MOVIT_INPUT_H + +#include +#include +#include + +class MltInput : public Input +{ +public: + MltInput(unsigned width, unsigned height); + ~MltInput(); + + // Effect overrides + std::string effect_type_id() const { return "MltInput"; } + Effect::AlphaHandling alpha_handling() const; + std::string output_fragment_shader(); + void set_gl_state(GLuint glsl_program_num, const std::string& prefix, unsigned *sampler_num); + + // Input ovverrides + void finalize(); + bool can_output_linear_gamma() const; + unsigned get_width() const { return m_width; } + unsigned get_height() const { return m_height; } + Colorspace get_color_space() const; + GammaCurve get_gamma_curve() const; + + // Custom methods + void useFlatInput(EffectChain* chain, MovitPixelFormat pix_fmt, unsigned width, unsigned height); + void useYCbCrInput(EffectChain* chain, const ImageFormat& image_format, const YCbCrFormat& ycbcr_format, unsigned width, unsigned height); + void useFBOInput(EffectChain* chain, GLuint texture); + void set_pixel_data(const unsigned char* data); + +private: + unsigned m_width, m_height; + int output_linear_gamma, needs_mipmaps; + Input *input; + bool isRGB; + YCbCrFormat m_ycbcr_format; +}; + +#endif // MLT_MOVIT_INPUT_H diff --git a/src/modules/opengl/transition_movit_mix.cpp b/src/modules/opengl/transition_movit_mix.cpp new file mode 100644 index 00000000..05d0ce78 --- /dev/null +++ b/src/modules/opengl/transition_movit_mix.cpp @@ -0,0 +1,215 @@ +/* + * transition_movit_mix.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#include +#include +#include + +#include "glsl_manager.h" +#include +#include +#include +#include +#include "mlt_movit_input.h" +#include "mlt_flip_effect.h" + +static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + int error = 0; + + // Get the b frame from the stack + mlt_frame b_frame = (mlt_frame) mlt_frame_pop_frame( a_frame ); + + // Get the transition object + mlt_transition transition = (mlt_transition) mlt_frame_pop_service( a_frame ); + + // Get the properties of the transition + mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); + + // Get the properties of the a frame + mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame ); + + // Get the movit objects + mlt_service service = MLT_TRANSITION_SERVICE( transition ); + EffectChain* chain = GlslManager::get_chain( service ); + Effect* effect = (Effect*) mlt_properties_get_data( properties, "movit effect", NULL ); + MltInput* a_input = GlslManager::get_input( service ); + MltInput* b_input = (MltInput*) mlt_properties_get_data( properties, "movit input B", NULL ); + mlt_image_format output_format = *format; + + // Get the transition parameters + int reverse = mlt_properties_get_int( properties, "reverse" ); + double mix = mlt_properties_get( properties, "mix" ) ? + mlt_properties_get_double( properties, "mix" ) : + mlt_transition_get_progress( transition, a_frame ); + double inverse = 1.0 - mix; + + // Set the movit parameters + bool ok = effect->set_float( "strength_first", reverse ? mix : inverse ); + ok |= effect->set_float( "strength_second", reverse ? inverse : mix ); + assert( ok ); + + // Get the frames' textures + GLuint* texture_id[2] = {0, 0}; + *format = mlt_image_glsl_texture; + mlt_frame_get_image( a_frame, (uint8_t**) &texture_id[0], format, width, height, 0 ); + a_input->useFBOInput( chain, *texture_id[0] ); + *format = mlt_image_glsl_texture; + mlt_frame_get_image( b_frame, (uint8_t**) &texture_id[1], format, width, height, 0 ); + b_input->useFBOInput( chain, *texture_id[1] ); + + // Set resolution to that of the a_frame + *width = mlt_properties_get_int( a_props, "width" ); + *height = mlt_properties_get_int( a_props, "height" ); + + // Setup rendering to an FBO + GlslManager* glsl = GlslManager::get_instance(); + glsl_fbo fbo = glsl->get_fbo( *width, *height ); + if ( output_format == mlt_image_glsl_texture ) { + glsl_texture texture = glsl->get_texture( *width, *height, GL_RGBA ); + + glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo ); + check_error(); + glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + check_error(); + + GlslManager::render( service, chain, fbo->fbo, *width, *height ); + + glFinish(); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + check_error(); + + *image = (uint8_t*) &texture->texture; + mlt_frame_set_image( a_frame, *image, 0, NULL ); + mlt_properties_set_data( properties, "movit.convert", texture, 0, + (mlt_destructor) GlslManager::release_texture, NULL ); + *format = output_format; + } + else { + // Use a PBO to hold the data we read back with glReadPixels() + // (Intel/DRI goes into a slow path if we don't read to PBO) + GLenum gl_format = ( output_format == mlt_image_rgb24a || output_format == mlt_image_opengl )? + GL_RGBA : GL_RGB; + int img_size = *width * *height * ( gl_format == GL_RGB? 3 : 4 ); + glsl_pbo pbo = glsl->get_pbo( img_size ); + glsl_texture texture = glsl->get_texture( *width, *height, gl_format ); + + if ( fbo && pbo && texture ) { + // Set the FBO + glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo ); + check_error(); + glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + check_error(); + + GlslManager::render( service, chain, fbo->fbo, *width, *height ); + + // Read FBO into PBO + glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, pbo->pbo ); + check_error(); + glBufferData( GL_PIXEL_PACK_BUFFER_ARB, img_size, NULL, GL_STREAM_READ ); + check_error(); + glReadPixels( 0, 0, *width, *height, gl_format, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0) ); + check_error(); + + // Copy from PBO + uint8_t* buf = (uint8_t*) glMapBuffer( GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY ); + check_error(); + + *format = gl_format == GL_RGBA ? mlt_image_rgb24a : mlt_image_rgb24; + *image = (uint8_t*) mlt_pool_alloc( img_size ); + mlt_frame_set_image( a_frame, *image, img_size, mlt_pool_release ); + memcpy( *image, buf, img_size ); + + // Release PBO and FBO + glUnmapBuffer( GL_PIXEL_PACK_BUFFER_ARB ); + check_error(); + glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, 0 ); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + check_error(); + glBindTexture( GL_TEXTURE_2D, 0 ); + check_error(); + GlslManager::release_texture( texture ); + } + else { + error = 1; + } + } + if ( fbo ) GlslManager::release_fbo( fbo ); + + return error; +} + +static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) +{ + mlt_service service = MLT_TRANSITION_SERVICE(transition); + + if ( !GlslManager::init_chain( service ) ) { + // Create the Movit effect chain + EffectChain* chain = GlslManager::get_chain( service ); + mlt_profile profile = mlt_service_profile( service ); + Input* b_input = new MltInput( profile->width, profile->height ); + ImageFormat output_format; + output_format.color_space = COLORSPACE_sRGB; + output_format.gamma_curve = GAMMA_sRGB; + chain->add_input( b_input ); + chain->add_output( output_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED ); + chain->set_dither_bits( 8 ); + + Effect* effect = chain->add_effect( new MixEffect(), + GlslManager::get_input( service ), b_input ); + + // Save these new effects on properties for get_image + mlt_properties properties = MLT_TRANSITION_PROPERTIES(transition); + mlt_properties_set_data( properties, "movit effect", effect, 0, NULL, NULL ); + mlt_properties_set_data( properties, "movit input B", b_input, 0, NULL, NULL ); + } + + // Push the transition on to the frame + mlt_frame_push_service( a_frame, transition ); + + // Push the b_frame on to the stack + mlt_frame_push_frame( a_frame, b_frame ); + + // Push the transition method + mlt_frame_push_get_image( a_frame, get_image ); + + return a_frame; +} + +extern "C" +mlt_transition transition_movit_mix_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_transition transition = NULL; + GlslManager* glsl = GlslManager::get_instance(); + if ( glsl && ( transition = mlt_transition_new() ) ) { + transition->process = process; + mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "mix", arg ); + + // Inform apps and framework that this is a video only transition + mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 ); + } + return transition; +} diff --git a/src/modules/opengl/transition_movit_mix.yml b/src/modules/opengl/transition_movit_mix.yml new file mode 100644 index 00000000..f8148507 --- /dev/null +++ b/src/modules/opengl/transition_movit_mix.yml @@ -0,0 +1,35 @@ +schema_version: 0.1 +type: transition +identifier: movit.mix +title: Dissolve (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: A simple video cross-fade or mixing effect. + +parameters: + - identifier: argument + title: Mix Level + description: Performs a dissolve if a mix level is not supplied. + type: float + minimum: 0 + maximum: 1 + + - identifier: mix + title: Mix Level + description: Performs a dissolve if a mix level is not supplied. + type: float + minimum: 0 + maximum: 1 + + - identifier: reverse + title: Reverse + type: integer + mutable: yes + description: > + Reverse the direction of the transition. + default: 0 diff --git a/src/modules/opengl/transition_movit_overlay.cpp b/src/modules/opengl/transition_movit_overlay.cpp new file mode 100644 index 00000000..7ff55c83 --- /dev/null +++ b/src/modules/opengl/transition_movit_overlay.cpp @@ -0,0 +1,200 @@ +/* + * transition_movit_overlay.cpp + * Copyright (C) 2013 Dan Dennedy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#include +#include +#include + +#include "glsl_manager.h" +#include +#include +#include +#include +#include "mlt_movit_input.h" +#include "mlt_flip_effect.h" + +static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + int error = 0; + + // Get the b frame from the stack + mlt_frame b_frame = (mlt_frame) mlt_frame_pop_frame( a_frame ); + + // Get the transition object + mlt_transition transition = (mlt_transition) mlt_frame_pop_service( a_frame ); + + // Get the properties of the transition + mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); + + // Get the properties of the a frame + mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame ); + + // Get the movit objects + mlt_service service = MLT_TRANSITION_SERVICE( transition ); + EffectChain* chain = GlslManager::get_chain( service ); + MltInput* a_input = GlslManager::get_input( service ); + MltInput* b_input = (MltInput*) mlt_properties_get_data( properties, "movit input B", NULL ); + mlt_image_format output_format = *format; + + // Get the frames' textures + GLuint* texture_id[2] = {0, 0}; + *format = mlt_image_glsl_texture; + mlt_frame_get_image( a_frame, (uint8_t**) &texture_id[0], format, width, height, 0 ); + a_input->useFBOInput( chain, *texture_id[0] ); + *format = mlt_image_glsl_texture; + mlt_frame_get_image( b_frame, (uint8_t**) &texture_id[1], format, width, height, 0 ); + b_input->useFBOInput( chain, *texture_id[1] ); + + // Set resolution to that of the a_frame + *width = mlt_properties_get_int( a_props, "width" ); + *height = mlt_properties_get_int( a_props, "height" ); + + // Setup rendering to an FBO + GlslManager* glsl = GlslManager::get_instance(); + glsl_fbo fbo = glsl->get_fbo( *width, *height ); + if ( output_format == mlt_image_glsl_texture ) { + glsl_texture texture = glsl->get_texture( *width, *height, GL_RGBA ); + + glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo ); + check_error(); + glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + check_error(); + + GlslManager::render( service, chain, fbo->fbo, *width, *height ); + + glFinish(); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + check_error(); + + *image = (uint8_t*) &texture->texture; + mlt_frame_set_image( a_frame, *image, 0, NULL ); + mlt_properties_set_data( properties, "movit.convert", texture, 0, + (mlt_destructor) GlslManager::release_texture, NULL ); + *format = output_format; + } + else { + // Use a PBO to hold the data we read back with glReadPixels() + // (Intel/DRI goes into a slow path if we don't read to PBO) + GLenum gl_format = ( output_format == mlt_image_rgb24a || output_format == mlt_image_opengl )? + GL_RGBA : GL_RGB; + int img_size = *width * *height * ( gl_format == GL_RGB? 3 : 4 ); + glsl_pbo pbo = glsl->get_pbo( img_size ); + glsl_texture texture = glsl->get_texture( *width, *height, gl_format ); + + if ( fbo && pbo && texture ) { + // Set the FBO + glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo ); + check_error(); + glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + check_error(); + + GlslManager::render( service, chain, fbo->fbo, *width, *height ); + + // Read FBO into PBO + glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, pbo->pbo ); + check_error(); + glBufferData( GL_PIXEL_PACK_BUFFER_ARB, img_size, NULL, GL_STREAM_READ ); + check_error(); + glReadPixels( 0, 0, *width, *height, gl_format, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0) ); + check_error(); + + // Copy from PBO + uint8_t* buf = (uint8_t*) glMapBuffer( GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY ); + check_error(); + + *format = gl_format == GL_RGBA ? mlt_image_rgb24a : mlt_image_rgb24; + *image = (uint8_t*) mlt_pool_alloc( img_size ); + mlt_frame_set_image( a_frame, *image, img_size, mlt_pool_release ); + memcpy( *image, buf, img_size ); + + // Release PBO and FBO + glUnmapBuffer( GL_PIXEL_PACK_BUFFER_ARB ); + check_error(); + glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, 0 ); + check_error(); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + check_error(); + glBindTexture( GL_TEXTURE_2D, 0 ); + check_error(); + GlslManager::release_texture( texture ); + } + else { + error = 1; + } + } + if ( fbo ) GlslManager::release_fbo( fbo ); + + return error; +} + +static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) +{ + mlt_service service = MLT_TRANSITION_SERVICE(transition); + + if ( !GlslManager::init_chain( service ) ) { + // Create the Movit effect chain + EffectChain* chain = GlslManager::get_chain( service ); + mlt_profile profile = mlt_service_profile( service ); + Input* b_input = new MltInput( profile->width, profile->height ); + ImageFormat output_format; + output_format.color_space = COLORSPACE_sRGB; + output_format.gamma_curve = GAMMA_sRGB; + chain->add_input( b_input ); + chain->add_output( output_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED ); + chain->set_dither_bits( 8 ); + + Effect* effect = chain->add_effect( new OverlayEffect(), + GlslManager::get_input( service ), b_input ); + + // Save these new input on properties for get_image + mlt_properties_set_data( MLT_TRANSITION_PROPERTIES(transition), + "movit input B", b_input, 0, NULL, NULL ); + } + + // Push the transition on to the frame + mlt_frame_push_service( a_frame, transition ); + + // Push the b_frame on to the stack + mlt_frame_push_frame( a_frame, b_frame ); + + // Push the transition method + mlt_frame_push_get_image( a_frame, get_image ); + + return a_frame; +} + +extern "C" +mlt_transition transition_movit_overlay_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_transition transition = NULL; + GlslManager* glsl = GlslManager::get_instance(); + if ( glsl && ( transition = mlt_transition_new() ) ) { + transition->process = process; + + // Inform apps and framework that this is a video only transition + mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 ); + } + return transition; +} diff --git a/src/modules/opengl/transition_movit_overlay.yml b/src/modules/opengl/transition_movit_overlay.yml new file mode 100644 index 00000000..0ae18b27 --- /dev/null +++ b/src/modules/opengl/transition_movit_overlay.yml @@ -0,0 +1,13 @@ +schema_version: 0.1 +type: transition +identifier: movit.overlay +title: Dissolve (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: > + A simple video overlay or alpha-compositing effect using the Porter-Duff over operation.