From 265e080ca00a91bde6f5812f8e498e5901675a59 Mon Sep 17 00:00:00 2001 From: Dan Dennedy Date: Tue, 25 Mar 2014 20:48:54 -0700 Subject: [PATCH] Add movit.luma transition. This can be improved by adding an invert parameter to the Movit effect and by supplying full 16-bit PGM to the Movit input, but it is a start. --- src/modules/opengl/Makefile | 1 + src/modules/opengl/factory.c | 3 + src/modules/opengl/filter_glsl_manager.cpp | 12 ++ src/modules/opengl/filter_glsl_manager.h | 2 + src/modules/opengl/filter_movit_convert.cpp | 44 +++- src/modules/opengl/transition_movit_luma.cpp | 200 +++++++++++++++++++ src/modules/opengl/transition_movit_luma.yml | 56 ++++++ 7 files changed, 310 insertions(+), 8 deletions(-) create mode 100644 src/modules/opengl/transition_movit_luma.cpp create mode 100644 src/modules/opengl/transition_movit_luma.yml diff --git a/src/modules/opengl/Makefile b/src/modules/opengl/Makefile index 2197b802..83152d4e 100644 --- a/src/modules/opengl/Makefile +++ b/src/modules/opengl/Makefile @@ -25,6 +25,7 @@ CPPOBJS += filter_movit_saturation.o CPPOBJS += filter_movit_vignette.o CPPOBJS += filter_movit_white_balance.o CPPOBJS += mlt_movit_input.o +CPPOBJS += transition_movit_luma.o CPPOBJS += transition_movit_mix.o CPPOBJS += transition_movit_overlay.o diff --git a/src/modules/opengl/factory.c b/src/modules/opengl/factory.c index 4b655302..59ddee66 100644 --- a/src/modules/opengl/factory.c +++ b/src/modules/opengl/factory.c @@ -40,6 +40,7 @@ extern mlt_filter filter_movit_resize_init( mlt_profile profile, mlt_service_typ 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_luma_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 ); @@ -71,6 +72,7 @@ MLT_REPOSITORY 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.luma_mix", transition_movit_luma_init ); MLT_REGISTER( transition_type, "movit.mix", transition_movit_mix_init ); MLT_REGISTER( transition_type, "movit.overlay", transition_movit_overlay_init ); @@ -85,6 +87,7 @@ MLT_REPOSITORY MLT_REGISTER_METADATA( filter_type, "movit.sharpen", metadata, "filter_movit_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_movit_white_balance.yml" ); + MLT_REGISTER_METADATA( transition_type, "movit.luma_mix", metadata, "transition_movit_luma.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/filter_glsl_manager.cpp b/src/modules/opengl/filter_glsl_manager.cpp index 0fd62853..1523b8e3 100644 --- a/src/modules/opengl/filter_glsl_manager.cpp +++ b/src/modules/opengl/filter_glsl_manager.cpp @@ -356,6 +356,18 @@ void GlslManager::set_effect_secondary_input( mlt_service service, mlt_frame fra set_frame_specific_data( service, frame, "_movit effect secondary input frame", input_frame, 0, NULL, NULL ); } +void GlslManager::get_effect_third_input( mlt_service service, mlt_frame frame, mlt_service *input_service, mlt_frame *input_frame) +{ + *input_service = (mlt_service) get_frame_specific_data( service, frame, "_movit effect third input", NULL ); + *input_frame = (mlt_frame) get_frame_specific_data( service, frame, "_movit effect third input frame", NULL ); +} + +void GlslManager::set_effect_third_input( mlt_service service, mlt_frame frame, mlt_service input_service, mlt_frame input_frame ) +{ + set_frame_specific_data( service, frame, "_movit effect third input", input_service, 0, NULL, NULL ); + set_frame_specific_data( service, frame, "_movit effect third input frame", input_frame, 0, NULL, NULL ); +} + int GlslManager::render_frame_texture(EffectChain *chain, mlt_frame frame, int width, int height, uint8_t **image) { glsl_texture texture = get_texture( width, height, GL_RGBA8 ); diff --git a/src/modules/opengl/filter_glsl_manager.h b/src/modules/opengl/filter_glsl_manager.h index 35e81b13..abaafb0e 100644 --- a/src/modules/opengl/filter_glsl_manager.h +++ b/src/modules/opengl/filter_glsl_manager.h @@ -117,6 +117,8 @@ public: static void set_effect_input(mlt_service, mlt_frame, mlt_service); static void get_effect_secondary_input(mlt_service, mlt_frame, mlt_service*, mlt_frame*); static void set_effect_secondary_input(mlt_service, mlt_frame, mlt_service, mlt_frame); + static void get_effect_third_input(mlt_service, mlt_frame, mlt_service*, mlt_frame*); + static void set_effect_third_input(mlt_service, mlt_frame, mlt_service, mlt_frame); int render_frame_texture(movit::EffectChain*, mlt_frame, int width, int height, uint8_t **image); int render_frame_rgba(movit::EffectChain*, mlt_frame, int width, int height, uint8_t **image); diff --git a/src/modules/opengl/filter_movit_convert.cpp b/src/modules/opengl/filter_movit_convert.cpp index 5d40f34b..40229ab5 100644 --- a/src/modules/opengl/filter_movit_convert.cpp +++ b/src/modules/opengl/filter_movit_convert.cpp @@ -131,6 +131,13 @@ static void build_fingerprint( mlt_service service, mlt_frame frame, std::string fingerprint->push_back( ')' ); } + GlslManager::get_effect_third_input( service, frame, &input_b, &frame_b ); + if ( input_b ) { + fingerprint->push_back( '(' ); + build_fingerprint( input_b, frame_b, fingerprint ); + fingerprint->push_back( ')' ); + } + fingerprint->push_back( '(' ); fingerprint->append( mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "_unique_id" ) ); @@ -164,12 +171,17 @@ static Effect* build_movit_chain( mlt_service service, mlt_frame frame, GlslChai GlslManager::set_effect( service, frame, NULL ); mlt_service input_a = GlslManager::get_effect_input( service, frame ); - mlt_service input_b; - mlt_frame frame_b; + mlt_service input_b, input_c; + mlt_frame frame_b, frame_c; GlslManager::get_effect_secondary_input( service, frame, &input_b, &frame_b ); + GlslManager::get_effect_third_input( service, frame, &input_c, &frame_c ); Effect *effect_a = build_movit_chain( input_a, frame, chain ); - if ( input_b ) { + if ( input_c && input_b ) { + Effect *effect_b = build_movit_chain( input_b, frame_b, chain ); + Effect *effect_c = build_movit_chain( input_c, frame_c, chain ); + chain->effect_chain->add_effect( effect, effect_a, effect_b, effect_c ); + } else if ( input_b ) { Effect *effect_b = build_movit_chain( input_b, frame_b, chain ); chain->effect_chain->add_effect( effect, effect_a, effect_b ); } else { @@ -201,6 +213,10 @@ static void dispose_movit_effects( mlt_service service, mlt_frame frame ) if ( input_b ) { dispose_movit_effects( input_b, frame_b ); } + GlslManager::get_effect_third_input( service, frame, &input_b, &frame_b ); + if ( input_b ) { + dispose_movit_effects( input_b, frame_b ); + } } static void finalize_movit_chain( mlt_service leaf_service, mlt_frame frame ) @@ -259,23 +275,30 @@ static void set_movit_parameters( GlslChain *chain, mlt_service service, mlt_fra if ( input_b ) { set_movit_parameters( chain, input_b, frame_b ); } - + GlslManager::get_effect_third_input( service, frame, &input_b, &frame_b ); + if ( input_b ) { + set_movit_parameters( chain, input_b, frame_b ); + } + mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); int count = mlt_properties_count( properties ); for (int i = 0; i < count; ++i) { const char *name = mlt_properties_get_name( properties, i ); - if (strncmp(name, "movit.parms.float.", strlen("movit.parms.float.")) == 0) { + if (strncmp(name, "movit.parms.float.", strlen("movit.parms.float.")) == 0 && + mlt_properties_get_value( properties, i )) { bool ok = effect->set_float(name + strlen("movit.parms.float."), mlt_properties_get_double( properties, name )); assert(ok); } - if (strncmp(name, "movit.parms.int.", strlen("movit.parms.int.")) == 0) { + if (strncmp(name, "movit.parms.int.", strlen("movit.parms.int.")) == 0 && + mlt_properties_get_value( properties, i )) { bool ok = effect->set_int(name + strlen("movit.parms.int."), mlt_properties_get_int( properties, name )); assert(ok); } if (strncmp(name, "movit.parms.vec3.", strlen("movit.parms.vec3.")) == 0 && - strcmp(name + strlen(name) - 3, "[0]") == 0) { + strcmp(name + strlen(name) - 3, "[0]") == 0 && + mlt_properties_get_value( properties, i )) { float val[3]; char *name_copy = strdup(name); char *index_char = name_copy + strlen(name_copy) - 2; @@ -290,7 +313,8 @@ static void set_movit_parameters( GlslChain *chain, mlt_service service, mlt_fra free(name_copy); } if (strncmp(name, "movit.parms.vec4.", strlen("movit.parms.vec4.")) == 0 && - strcmp(name + strlen(name) - 3, "[0]") == 0) { + strcmp(name + strlen(name) - 3, "[0]") == 0 && + mlt_properties_get_value( properties, i )) { float val[4]; char *name_copy = strdup(name); char *index_char = name_copy + strlen(name_copy) - 2; @@ -328,6 +352,10 @@ static void dispose_pixel_pointers( GlslChain *chain, mlt_service service, mlt_f if ( input_b ) { dispose_pixel_pointers( chain, input_b, frame_b ); } + GlslManager::get_effect_third_input( service, frame, &input_b, &frame_b ); + if ( input_b ) { + dispose_pixel_pointers( chain, input_b, frame_b ); + } } static int movit_render( EffectChain *chain, mlt_frame frame, mlt_image_format *format, mlt_image_format output_format, int width, int height, uint8_t **image ) diff --git a/src/modules/opengl/transition_movit_luma.cpp b/src/modules/opengl/transition_movit_luma.cpp new file mode 100644 index 00000000..2354ebff --- /dev/null +++ b/src/modules/opengl/transition_movit_luma.cpp @@ -0,0 +1,200 @@ +/* + * transition_movit_luma.cpp + * Copyright (C) 2014 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 "filter_glsl_manager.h" +#include +#include +#include +#include +#include +#include "mlt_movit_input.h" + +using namespace movit; + +static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + int error; + + // Get the transition object + mlt_transition transition = (mlt_transition) mlt_frame_pop_service( a_frame ); + mlt_service service = MLT_TRANSITION_SERVICE( transition ); + + // Get the b frame from the stack + mlt_frame b_frame = (mlt_frame) mlt_frame_pop_frame( a_frame ); + mlt_frame c_frame = (mlt_frame) mlt_frame_pop_frame( a_frame ); + + // Get the properties of the transition + mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); + + mlt_service_lock( service ); + + // Get the transition parameters + mlt_position position = mlt_transition_get_position( transition, a_frame ); + mlt_position length = mlt_transition_get_length( transition ); + int reverse = mlt_properties_get_int( properties, "reverse" ); + double mix = mlt_transition_get_progress( transition, a_frame ); + double inverse = 1.0 - mix; + double softness = mlt_properties_anim_get_double( properties, "softness", position, length ); + + if ( c_frame ) + { + // Set the Movit parameters. + mlt_properties_set( properties, "movit.parms.float.strength_first", NULL ); + mlt_properties_set( properties, "movit.parms.float.strength_second", NULL ); + mlt_properties_set_double( properties, "movit.parms.float.progress", reverse ? inverse : mix ); + mlt_properties_set_double( properties, "movit.parms.float.transition_width", 1.0 / (softness + 1.0e-4) ); + + uint8_t *a_image, *b_image, *c_image; + + // Get the images. + *format = mlt_image_glsl; + error = mlt_frame_get_image( a_frame, &a_image, format, width, height, writable ); + error = mlt_frame_get_image( b_frame, &b_image, format, width, height, writable ); + error = mlt_frame_get_image( c_frame, &c_image, format, width, height, writable ); + + GlslManager::set_effect_input( service, a_frame, (mlt_service) a_image ); + GlslManager::set_effect_secondary_input( service, a_frame, (mlt_service) b_image, b_frame ); + GlslManager::set_effect_third_input( service, a_frame, (mlt_service) c_image, c_frame ); + GlslManager::set_effect( service, a_frame, new LumaMixEffect() ); + } + else + { + // Set the Movit parameters. + mlt_properties_set( properties, "movit.parms.float.progress", NULL ); + mlt_properties_set( properties, "movit.parms.float.transition_width", NULL ); + mlt_properties_set_double( properties, "movit.parms.float.strength_first", reverse ? mix : inverse ); + mlt_properties_set_double( properties, "movit.parms.float.strength_second", reverse ? inverse : mix ); + + uint8_t *a_image, *b_image; + + // Get the two images. + *format = mlt_image_glsl; + error = mlt_frame_get_image( a_frame, &a_image, format, width, height, writable ); + error = mlt_frame_get_image( b_frame, &b_image, format, width, height, writable ); + + GlslManager::set_effect_input( service, a_frame, (mlt_service) a_image ); + GlslManager::set_effect_secondary_input( service, a_frame, (mlt_service) b_image, b_frame ); + GlslManager::set_effect( service, a_frame, new MixEffect() ); + } + *image = (uint8_t *) service; + + mlt_service_unlock( service ); + return error; +} + +static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) +{ + mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); + + // Obtain the wipe producer. + char *resource = mlt_properties_get( properties, "resource" ); + char *last_resource = mlt_properties_get( properties, "_resource" ); + int invert = mlt_properties_get_int( properties, "invert" ); + int last_invert = mlt_properties_get_int( properties, "_invert" ); + mlt_producer producer = (mlt_producer) mlt_properties_get_data( properties, "instance", NULL ); + + // If we haven't created the wipe producer or it has changed + if ( resource ) + if ( ( !producer || strcmp( resource, last_resource ) ) || ( invert != last_invert ) ) { + char temp[ 512 ]; + char *extension = strrchr( resource, '.' ); + + // Store the last resource now + mlt_properties_set( properties, "_resource", resource ); + mlt_properties_set_int( properties, "_invert", invert ); + + // This is a hack - the idea is that we can indirectly reference the + // luma modules pgm or png images by a short cut like %luma01.pgm - we then replace + // the % with the full path to the image and use it if it exists, if not, check for + // the file ending in a .png, and failing that, default to a fade in + if ( strchr( resource, '%' ) ) { + FILE *test; + sprintf( temp, "%s/lumas/%s/%s", mlt_environment( "MLT_DATA" ), mlt_environment( "MLT_NORMALISATION" ), strchr( resource, '%' ) + 1 ); + test = fopen( temp, "r" ); + + if ( test == NULL ) + { + strcat( temp, ".png" ); + test = fopen( temp, "r" ); + } + + if ( test ) + fclose( test ); + else + strcpy( temp, "colour:0x00000080" ); + + resource = temp; + extension = strrchr( resource, '.' ); + } + + mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) ); + producer = mlt_factory_producer( profile, NULL, resource ); + if ( producer != NULL ) { + mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" ); + if ( invert ) { + mlt_filter filter = mlt_factory_filter( profile, "invert", NULL ); + if ( filter ) + mlt_producer_attach( producer, filter ); + } + } + mlt_properties_set_data( properties, "instance", producer, 0, (mlt_destructor) mlt_producer_close, NULL ); + } + // We may still not have a producer in which case, we do nothing + if ( producer ) { + mlt_frame wipe = NULL; + mlt_position position = mlt_transition_get_position( transition, a_frame ); + mlt_properties_pass( MLT_PRODUCER_PROPERTIES( producer ), properties, "producer." ); + mlt_producer_seek( producer, position ); + if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &wipe, 0 ) == 0 ) { + char *name = mlt_properties_get( properties, "_unique_id" ); + mlt_properties_set_data( MLT_FRAME_PROPERTIES(a_frame), name, wipe, 0, (mlt_destructor) mlt_frame_close, NULL ); + mlt_properties_set_int( MLT_FRAME_PROPERTIES(wipe), "distort", 1 ); + mlt_frame_push_frame( a_frame, wipe ); + } else { + mlt_frame_push_frame( a_frame, NULL ); + } + } else { + mlt_frame_push_frame( a_frame, NULL ); + } + mlt_frame_push_frame( a_frame, b_frame ); + mlt_frame_push_service( a_frame, transition ); + mlt_frame_push_get_image( a_frame, get_image ); + + return a_frame; +} + +extern "C" +mlt_transition transition_movit_luma_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 ), "resource", 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_luma.yml b/src/modules/opengl/transition_movit_luma.yml new file mode 100644 index 00000000..40c8449c --- /dev/null +++ b/src/modules/opengl/transition_movit_luma.yml @@ -0,0 +1,56 @@ +schema_version: 0.2 +type: transition +identifier: movit.luma_mix +title: Wipe (GLSL) +version: 1 +copyright: Dan Dennedy +creator: Steinar H. Gunderson +license: GPLv2 +language: en +tags: + - Video +description: A generic dissolve and wipe transition processor. + +parameters: + - identifier: resource + argument: yes + title: Wipe File + description: Gradient image or dissolve if not supplied. + type: string + mutable: yes + + - identifier: softness + title: Softness + description: The blurriness of the edges of the transition. + type: float + minimum: 0 + maximum: 1 + default: 0 + mutable: yes + + - identifier: reverse + title: Reverse + type: integer + mutable: yes + description: Reverse the direction of the transition. + default: 0 + minimum: 0 + maximum: 1 + widget: checkbox + + - identifier: invert + title: Invert + type: integer + mutable: yes + description: Invert the wipe. + default: 0 + minimum: 0 + maximum: 1 + widget: checkbox + + - identifier: producer.* + title: Producer + mutable: yes + description: > + Properties may be set on the encapsulated producer that reads resource. + readonly: no -- 2.39.2