]> git.sesse.net Git - mlt/commitdiff
Merge pull request #32 from j-b-m/master
authorBrian Matherly <pez4brian@yahoo.com>
Sun, 2 Jun 2013 21:22:35 +0000 (14:22 -0700)
committerBrian Matherly <pez4brian@yahoo.com>
Sun, 2 Jun 2013 21:22:35 +0000 (14:22 -0700)
Fix missing value in producer_count yml

45 files changed:
presets/filter/movit.blur/blur_in [new file with mode: 0644]
presets/filter/movit.blur/blur_in_out [new file with mode: 0644]
presets/filter/movit.blur/blur_out [new file with mode: 0644]
src/framework/Makefile
src/framework/mlt.h
src/framework/mlt.vers
src/framework/mlt_animation.c [new file with mode: 0644]
src/framework/mlt_animation.h [new file with mode: 0644]
src/framework/mlt_frame.c
src/framework/mlt_frame.h
src/framework/mlt_geometry.c
src/framework/mlt_geometry.h
src/framework/mlt_profile.c
src/framework/mlt_properties.c
src/framework/mlt_properties.h
src/framework/mlt_property.c
src/framework/mlt_property.h
src/framework/mlt_types.h
src/mlt++/MltProperties.cpp
src/mlt++/MltProperties.h
src/mlt++/mlt++.vers
src/modules/opengl/filter_deconvolution_sharpen.cpp
src/modules/opengl/filter_deconvolution_sharpen.yml
src/modules/opengl/filter_lift_gamma_gain.cpp
src/modules/opengl/filter_lift_gamma_gain.yml
src/modules/opengl/filter_movit_blur.cpp
src/modules/opengl/filter_movit_blur.yml
src/modules/opengl/filter_movit_diffusion.cpp
src/modules/opengl/filter_movit_diffusion.yml
src/modules/opengl/filter_movit_glow.cpp
src/modules/opengl/filter_movit_glow.yml
src/modules/opengl/filter_movit_opacity.cpp
src/modules/opengl/filter_movit_opacity.yml
src/modules/opengl/filter_movit_rect.cpp
src/modules/opengl/filter_movit_rect.yml
src/modules/opengl/filter_movit_resize.cpp
src/modules/opengl/filter_movit_saturation.cpp
src/modules/opengl/filter_movit_saturation.yml
src/modules/opengl/filter_movit_vignette.cpp
src/modules/opengl/filter_movit_vignette.yml
src/modules/opengl/filter_white_balance.cpp
src/modules/opengl/filter_white_balance.yml
src/modules/opengl/transition_movit_mix.cpp
src/modules/opengl/transition_movit_mix.yml
src/tests/test_properties/test_properties.cpp

diff --git a/presets/filter/movit.blur/blur_in b/presets/filter/movit.blur/blur_in
new file mode 100644 (file)
index 0000000..b834c22
--- /dev/null
@@ -0,0 +1 @@
+radius=0=10.0; :1.0=0.0;
diff --git a/presets/filter/movit.blur/blur_in_out b/presets/filter/movit.blur/blur_in_out
new file mode 100644 (file)
index 0000000..f59ec93
--- /dev/null
@@ -0,0 +1 @@
+radius=0=10.0; :1.0=0; :-1.0=0.0; -1=10.0;
diff --git a/presets/filter/movit.blur/blur_out b/presets/filter/movit.blur/blur_out
new file mode 100644 (file)
index 0000000..f81a177
--- /dev/null
@@ -0,0 +1 @@
+radius=0=0; :-1.0=0.0; -1=10.0;
index e5107c699e160fe0336eb0b45536068ce13b1b16..2c3eac9d5804e7b7a70640ce0a74e0669b70421c 100644 (file)
@@ -47,7 +47,8 @@ OBJS = mlt_frame.o \
           mlt_tokeniser.o \
           mlt_profile.o \
           mlt_log.o \
-          mlt_cache.o
+          mlt_cache.o \
+          mlt_animation.o
 
 INCS = mlt_consumer.h \
           mlt_version.h \
@@ -74,7 +75,8 @@ INCS = mlt_consumer.h \
           mlt_tokeniser.h \
           mlt_profile.h \
           mlt_log.h \
-          mlt_cache.h
+          mlt_cache.h \
+          mlt_animation.h
 
 SRCS := $(OBJS:.o=.c)
 
@@ -85,7 +87,7 @@ endif
 
 CFLAGS += $(RDYNAMIC) -DPREFIX_DATA="\"$(mltdatadir)\"" -DPREFIX_LIB="\"$(moduledir)\""
 
-LDFLAGS += $(LIBDL) -lpthread
+LDFLAGS += $(LIBDL) -lpthread -lm
 
 all:   $(TARGET)
 
index b8adca9a03a088b0e7eb3128ab516c489224ade6..9d93064f6f865af1459762fb5e3551ef553898e9 100644 (file)
 #ifndef _MLT_H_
 #define _MLT_H_
 
+/** \mainpage MLT API Reference Documentation
+ * \par
+ * We recommend that you look in <a href="annotated.html"><b>Data Structures</b></a>
+ * or <a href="files.html"><b>Files</b></a>.
+ * \par
+ * Additional documentation about MLT, in general, can be found on the
+ * <a href="http://www.mltframework.org/bin/view/MLT/Documentation">MLT website</a>.
+ */
+
 #ifdef __cplusplus
 extern "C"
 {
index 2ae06f31aebcf7537e88ef3c92cffabfce5f455b..45b0b3ff0d1bb71f8b694c08edb53998f5b10f8c 100644 (file)
@@ -404,6 +404,46 @@ MLT_0.8.8 {
 
 MLT_0.9.0 {
   global:
-    "mlt_service_filter_count";
-    "mlt_service_move_filter";
+    mlt_animation_new;
+    mlt_animation_parse;
+    mlt_animation_refresh;
+    mlt_animation_get_length;
+    mlt_animation_set_length;
+    mlt_animation_parse_item;
+    mlt_animation_get_item;
+    mlt_animation_insert;
+    mlt_animation_remove;
+    mlt_animation_interpolate;
+    mlt_animation_next_key;
+    mlt_animation_prev_key;
+    mlt_animation_serialize_cut;
+    mlt_animation_serialize;
+    mlt_animation_close;
+    mlt_properties_get_color;
+    mlt_properties_set_color;
+    mlt_properties_anim_get;
+    mlt_properties_anim_set;
+    mlt_properties_anim_get_int;
+    mlt_properties_anim_set_int;
+    mlt_properties_anim_get_double;
+    mlt_properties_anim_set_double;
+    mlt_properties_get_animation;
+    mlt_properties_set_rect;
+    mlt_properties_get_rect;
+    mlt_properties_anim_set_rect;
+    mlt_properties_anim_get_rect;
+    mlt_property_interpolate;
+    mlt_property_anim_get_double;
+    mlt_property_anim_get_int;
+    mlt_property_anim_get_string;
+    mlt_property_anim_set_double;
+    mlt_property_anim_set_int;
+    mlt_property_anim_set_string;
+    mlt_property_get_animation;
+    mlt_property_set_rect;
+    mlt_property_get_rect;
+    mlt_property_anim_set_rect;
+    mlt_property_anim_get_rect;
+    mlt_service_filter_count;
+    mlt_service_move_filter;
 } MLT_0.8.8;
diff --git a/src/framework/mlt_animation.c b/src/framework/mlt_animation.c
new file mode 100644 (file)
index 0000000..14b7978
--- /dev/null
@@ -0,0 +1,708 @@
+/**
+ * \file mlt_animation.c
+ * \brief Property Animation class definition
+ * \see mlt_animation_s
+ *
+ * Copyright (C) 2004-2013 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * 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
+ */
+
+#include "mlt_animation.h"
+#include "mlt_tokeniser.h"
+#include "mlt_profile.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** \brief animation list node pointer */
+typedef struct animation_node_s *animation_node;
+/** \brief private animation list node */
+struct animation_node_s
+{
+       struct mlt_animation_item_s item;
+       animation_node next, prev;
+};
+
+/** \brief Property Animation class
+ *
+ * This is the animation engine for a Property object. It is dependent upon
+ * the mlt_property API and used by the various mlt_property_anim_* functions.
+ */
+
+struct mlt_animation_s
+{
+       char *data;           /**< the string representing the animation */
+       int length;           /**< the maximum number of frames to use when interpreting negative keyframe positions */
+       double fps;           /**< framerate to use when converting time clock strings to frame units */
+       locale_t locale;      /**< pointer to a locale to use when converting strings to numeric values */
+       animation_node nodes; /**< a linked list of keyframes (and possibly non-keyframe values) */
+};
+
+/** Create a new animation object.
+ *
+ * \public \memberof mlt_animation_s
+ * \return an animation object
+ */
+
+mlt_animation mlt_animation_new( )
+{
+       mlt_animation self = calloc( 1, sizeof( *self ) );
+       return self;
+}
+
+/** Re-interpolate non-keyframe nodess after a series of insertions or removals.
+ *
+ * \public \memberof mlt_animation_s
+ * \param self an animation
+ */
+
+void mlt_animation_interpolate( mlt_animation self )
+{
+       // Parse all items to ensure non-keyframes are calculated correctly.
+       if ( self->nodes )
+       {
+               animation_node current = self->nodes;
+               while ( current )
+               {
+                       if ( !current->item.is_key )
+                       {
+                               double progress;
+                               mlt_property points[4];
+                               animation_node prev = current->prev;
+                               animation_node next = current->next;
+
+                               while ( prev && !prev->item.is_key ) prev = prev->prev;
+                               while ( next && !next->item.is_key ) next = next->next;
+
+                               if ( !prev )
+                                       current->item.is_key = 1;
+                               points[0] = prev->prev? prev->prev->item.property : prev->item.property;
+                               points[1] = prev->item.property;
+                               points[2] = next->item.property;
+                               points[3] = next->next? next->next->item.property : next->item.property;
+                               progress = current->item.frame - prev->item.frame;
+                               progress /= next->item.frame - prev->item.frame;
+                               mlt_property_interpolate( current->item.property, points, progress,
+                                       self->fps, self->locale, current->item.keyframe_type );
+                       }
+
+                       // Move to the next item
+                       current = current->next;
+               }
+       }
+}
+
+/** Remove a node from the linked list.
+ *
+ * \private \memberof mlt_animation_s
+ * \param self an animation
+ * \param node the node to remove
+ * \return false
+ */
+
+static int mlt_animation_drop( mlt_animation self, animation_node node )
+{
+       if ( node == self->nodes )
+       {
+               self->nodes = node->next;
+               if ( self->nodes ) {
+                       self->nodes->prev = NULL;
+                       self->nodes->item.is_key = 1;
+               }
+       }
+       else if ( node->next && node->prev )
+       {
+               node->prev->next = node->next;
+               node->next->prev = node->prev;
+       }
+       else if ( node->next )
+       {
+               node->next->prev = node->prev;
+       }
+       else if ( node->prev )
+       {
+               node->prev->next = node->next;
+       }
+       mlt_property_close( node->item.property );
+       free( node );
+
+       return 0;
+}
+
+/** Reset an animation and free all strings and properties.
+ *
+ * \private \memberof mlt_animation_s
+ * \param self an animation
+ */
+
+static void mlt_animation_clean( mlt_animation self )
+{
+       if ( self->data )
+               free( self->data );
+       self->data = NULL;
+       while ( self->nodes )
+               mlt_animation_drop( self, self->nodes );
+}
+
+/** Parse a string representing an animation.
+ *
+ * A semicolon is the delimiter between keyframe=value items in the string.
+ * \public \memberof mlt_animation_s
+ * \param self an animation
+ * \param data the string representing an animation
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \param fps the framerate to use when evaluating time strings
+ * \param locale the locale to use when converting strings to numbers
+ * \return true if there was an error
+ */
+
+int mlt_animation_parse(mlt_animation self, const char *data, int length, double fps, locale_t locale )
+{
+       int error = 0;
+       int i = 0;
+       struct mlt_animation_item_s item;
+       mlt_tokeniser tokens = mlt_tokeniser_init( );
+
+       // Clean the existing geometry
+       mlt_animation_clean( self );
+
+       // Update the info on the data
+       if ( data )
+               self->data = strdup( data );
+       self->length = length;
+       self->fps = fps;
+       self->locale = locale;
+       item.property = mlt_property_init();
+
+       // Tokenise
+       if ( data )
+               mlt_tokeniser_parse_new( tokens, (char*) data, ";" );
+
+       // Iterate through each token
+       for ( i = 0; i < mlt_tokeniser_count( tokens ); i++ )
+       {
+               char *value = mlt_tokeniser_get_string( tokens, i );
+
+               // If no data in keyframe, drop it (trailing semicolon)
+               if ( !value || !strcmp( value, "" ) )
+                       continue;
+
+               // Reset item
+               item.frame = item.is_key = 0;
+
+               // Now parse the item
+               mlt_animation_parse_item( self, &item, value );
+
+               // Now insert into place
+               mlt_animation_insert( self, &item );
+       }
+       mlt_animation_interpolate( self );
+
+       // Cleanup
+       mlt_tokeniser_close( tokens );
+       mlt_property_close( item.property );
+
+       return error;
+}
+
+/** Conditionally refresh the animation if it is modified.
+ *
+ * \public \memberof mlt_animation_s
+ * \param self an animation
+ * \param data the string representing an animation
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \return true if there was an error
+ */
+
+int mlt_animation_refresh( mlt_animation self, const char *data, int length )
+{
+       if ( ( length != self->length )|| ( data && ( !self->data || strcmp( data, self->data ) ) ) )
+               return mlt_animation_parse( self, data, length, self->fps, self->locale );
+       return 0;
+}
+
+/** Get the length of the animation.
+ *
+ * If the animation was initialized with a zero or negative value, then this
+ * gets the maximum frame number from animation's list of nodes.
+ * \public \memberof mlt_animation_s
+ * \param self an animation
+ * \return the number of frames
+ */
+
+int mlt_animation_get_length( mlt_animation self )
+{
+       int length = 0;
+       if ( self ) {
+               if ( self->length > 0 ) {
+                       length = self->length;
+               }
+               else if ( self->nodes ) {
+                       animation_node node = self->nodes;
+                       while ( node ) {
+                               if ( node->item.frame > length )
+                                       length = node->item.frame;
+                               node = node->next;
+                       }
+               }
+       }
+       return length;
+}
+
+/** Set the length of the animation.
+ *
+ * The length is used for interpreting negative keyframe positions as relative
+ * to the length. It is also used when serializing an animation as a string.
+ * \public \memberof mlt_animation_s
+ * \param self an animation
+ * \param length the length of the animation in frame units
+ */
+
+void mlt_animation_set_length( mlt_animation self, int length )
+{
+       if ( self )
+               self->length = length;
+}
+
+/** Parse a string representing an animation keyframe=value.
+ *
+ * This function does not affect the animation itself! But it will use some state
+ * of the animation for the parsing (e.g. fps, locale).
+ * It parses into a mlt_animation_item that you provide.
+ * \p item->frame should be specified if the string does not have an equal sign and time field.
+ * If an exclamation point (!) or vertical bar (|) character preceeds the equal sign, then
+ * the keyframe interpolation is set to discrete. If a tilde (~) preceeds the equal sign,
+ * then the keyframe interpolation is set to smooth (spline).
+ *
+ * \public \memberof mlt_animation_s
+ * \param self an animation
+ * \param item an already allocated animation item
+ * \param value the string representing an animation
+ * \return true if there was an error
+ */
+
+int mlt_animation_parse_item( mlt_animation self, mlt_animation_item item, const char *value )
+{
+       int error = 0;
+
+       if ( value && strcmp( value, "" ) )
+       {
+               // Determine if a position has been specified
+               if ( strchr( value, '=' ) )
+               {
+                       // Parse an absolute time value.
+                       // Null terminate the string at the equal sign to prevent interpreting
+                       // a colon in the part to the right of the equal sign as indicative of a
+                       // a time value string.
+                       char *s = strdup( value );
+                       char *p = strchr( s, '=' );
+                       p[0] = '\0';
+                       mlt_property_set_string( item->property, s );
+                       item->frame = mlt_property_get_int( item->property, self->fps, self->locale );
+                       free( s );
+
+                       // The character preceeding the equal sign indicates interpolation method.
+                       p = strchr( value, '=' ) - 1;
+                       if ( p[0] == '|' || p[0] == '!' )
+                               item->keyframe_type = mlt_keyframe_discrete;
+                       else if ( p[0] == '~' )
+                               item->keyframe_type = mlt_keyframe_smooth;
+                       else
+                               item->keyframe_type = mlt_keyframe_linear;
+                       value = &p[2];
+               }
+
+               // Special case - frame < 0
+               if ( item->frame < 0 )
+                       item->frame += mlt_animation_get_length( self );
+
+               // Set remainder of string as item value.
+               mlt_property_set_string( item->property, value );
+               item->is_key = 1;
+       }
+       else
+       {
+               error = 1;
+       }
+
+       return error;
+}
+
+/** Load an animation item for an absolute position.
+ *
+ * This performs interpolation if there is no keyframe at the \p position.
+ * \public \memberof mlt_animation_s
+ * \param self an animation
+ * \param item an already allocated animation item that will be filled in
+ * \param position the frame number for the point in time
+ * \return true if there was an error
+ */
+
+int mlt_animation_get_item( mlt_animation self, mlt_animation_item item, int position )
+{
+       int error = 0;
+       // Need to find the nearest keyframe to the position specifed
+       animation_node node = self->nodes;
+
+       // Iterate through the keyframes until we reach last or have
+       while ( node && node->next && position >= node->next->item.frame )
+               node = node->next;
+
+       if ( node )
+       {
+               item->keyframe_type = node->item.keyframe_type;
+
+               // Position is before the first keyframe.
+               if ( position < node->item.frame )
+               {
+                       item->is_key = 0;
+                       mlt_property_pass( item->property, node->item.property );
+               }
+               // Item exists.
+               else if ( position == node->item.frame )
+               {
+                       item->is_key = node->item.is_key;
+                       mlt_property_pass( item->property, node->item.property );
+               }
+               // Position is after the last keyframe.
+               else if ( !node->next )
+               {
+                       item->is_key = 0;
+                       mlt_property_pass( item->property, node->item.property );
+               }
+               // Interpolation needed.
+               else
+               {
+                       double progress;
+                       mlt_property points[4];
+                       points[0] = node->prev? node->prev->item.property : node->item.property;
+                       points[1] = node->item.property;
+                       points[2] = node->next->item.property;
+                       points[3] = node->next->next? node->next->next->item.property : node->next->item.property;
+                       progress = position - node->item.frame;
+                       progress /= node->next->item.frame - node->item.frame;
+                       mlt_property_interpolate( item->property, points, progress,
+                               self->fps, self->locale, item->keyframe_type );
+                       item->is_key = 0;
+               }
+       }
+       else
+       {
+               item->frame = item->is_key = 0;
+               error = 1;
+       }
+       item->frame = position;
+
+       return error;
+}
+
+/** Insert an animation item.
+ *
+ * \public \memberof mlt_animation_s
+ * \param self an animation
+ * \param item an animation item
+ * \return true if there was an error
+ * \see mlt_animation_parse_item
+ */
+
+int mlt_animation_insert( mlt_animation self, mlt_animation_item item )
+{
+       int error = 0;
+       animation_node node = calloc( 1, sizeof( *node ) );
+       node->item.frame = item->frame;
+       node->item.is_key = 1;
+       node->item.keyframe_type = item->keyframe_type;
+       node->item.property = mlt_property_init();
+       mlt_property_pass( node->item.property, item->property );
+
+       // Determine if we need to insert or append to the list, or if it's a new list
+       if ( self->nodes )
+       {
+               // Get the first item
+               animation_node current = self->nodes;
+
+               // Locate an existing nearby item
+               while ( current->next && item->frame > current->item.frame )
+                       current = current->next;
+
+               if ( item->frame < current->item.frame )
+               {
+                       if ( current == self->nodes )
+                               self->nodes = node;
+                       if ( current->prev )
+                               current->prev->next = node;
+                       node->next = current;
+                       node->prev = current->prev;
+                       current->prev = node;
+               }
+               else if ( item->frame > current->item.frame )
+               {
+                       if ( current->next )
+                               current->next->prev = node;
+                       node->next = current->next;
+                       node->prev = current;
+                       current->next = node;
+               }
+               else
+               {
+                       // Update matching node.
+                       current->item.frame = item->frame;
+                       current->item.is_key = 1;
+                       current->item.keyframe_type = item->keyframe_type;
+                       mlt_property_close( current->item.property );
+                       current->item.property = node->item.property;
+                       free( node );
+               }
+       }
+       else
+       {
+               // Set the first item
+               self->nodes = node;
+       }
+
+       return error;
+}
+
+/** Remove the keyframe at the specified position.
+ *
+ * \public \memberof mlt_animation_s
+ * \param self an animation
+ * \param position the frame number of the animation node to remove
+ * \return true if there was an error
+ */
+
+int mlt_animation_remove( mlt_animation self, int position )
+{
+       int error = 1;
+       animation_node node = self->nodes;
+
+       while ( node && position != node->item.frame )
+               node = node->next;
+
+       if ( node && position == node->item.frame )
+               error = mlt_animation_drop( self, node );
+
+       return error;
+}
+
+/** Get the keyfame at the position or the next following.
+ *
+ * \public \memberof mlt_animation_s
+ * \param self an animation
+ * \param item an already allocated animation item which will be updated
+ * \param position the frame number at which to start looking for the next animation node
+ * \return true if there was an error
+ */
+
+int mlt_animation_next_key( mlt_animation self, mlt_animation_item item, int position )
+{
+       animation_node node = self->nodes;
+
+       while ( node && position > node->item.frame )
+               node = node->next;
+
+       if ( node )
+       {
+               item->frame = node->item.frame;
+               item->is_key = node->item.is_key;
+               item->keyframe_type = node->item.keyframe_type;
+               mlt_property_pass( item->property, node->item.property );
+       }
+
+       return ( node == NULL );
+}
+
+/** Get the keyfame at the position or the next preceeding.
+ *
+ * \public \memberof mlt_animation_s
+ * \param self an animation
+ * \param item an already allocated animation item which will be updated
+ * \param position the frame number at which to start looking for the previous animation node
+ * \return true if there was an error
+ */
+
+int mlt_animation_prev_key( mlt_animation self, mlt_animation_item item, int position )
+{
+       animation_node node = self->nodes;
+
+       while ( node && node->next && position >= node->next->item.frame )
+               node = node->next;
+
+       if ( node )
+       {
+               item->frame = node->item.frame;
+               item->is_key = node->item.is_key;
+               item->keyframe_type = node->item.keyframe_type;
+               mlt_property_pass( item->property, node->item.property );
+       }
+
+       return ( node == NULL );
+}
+
+/** Serialize a cut of the animation.
+ *
+ * The caller is responsible for free-ing the returned string.
+ * \public \memberof mlt_animation_s
+ * \param self an animation
+ * \param in the frame at which to start serializing animation nodes
+ * \param out the frame at which to stop serializing nodes
+ * \return a string representing the animation
+ */
+
+char *mlt_animation_serialize_cut( mlt_animation self, int in, int out )
+{
+       struct mlt_animation_item_s item;
+       char *ret = malloc( 1000 );
+       size_t used = 0;
+       size_t size = 1000;
+
+       item.property = mlt_property_init();
+       if ( in == -1 )
+               in = 0;
+       if ( out == -1 )
+               out = mlt_animation_get_length( self );
+
+       if ( ret )
+       {
+               strcpy( ret, "" );
+
+               item.frame = in;
+
+               while ( 1 )
+               {
+                       size_t item_len = 0;
+
+                       // If it's the first frame, then it's not necessarily a key
+                       if ( item.frame == in )
+                       {
+                               if ( mlt_animation_get_item( self, &item, item.frame ) )
+                                       break;
+
+                               // If the first keyframe is larger than the current position
+                               // then do nothing here
+                               if ( self->nodes->item.frame > item.frame )
+                               {
+                                       item.frame ++;
+                                       continue;
+                               }
+
+                               // To ensure correct seeding
+                               item.is_key = 1;
+                       }
+                       // Typically, we move from keyframe to keyframe
+                       else if ( item.frame < out )
+                       {
+                               if ( mlt_animation_next_key( self, &item, item.frame ) )
+                                       break;
+
+                               // Special case - crop at the out point
+                               if ( item.frame > out )
+                                       mlt_animation_get_item( self, &item, out );
+                       }
+                       // We've handled the last keyframe
+                       else
+                       {
+                               break;
+                       }
+
+                       // Determine length of string to be appended.
+                       if ( item.frame - in != 0 )
+                               item_len += 20;
+                       if ( item.is_key )
+                               item_len += strlen( mlt_property_get_string_l( item.property, self->locale ) );
+
+                       // Reallocate return string to be long enough.
+                       while ( used + item_len + 2 > size ) // +2 for ';' and NULL
+                       {
+                               size += 1000;
+                               ret = realloc( ret, size );
+                       }
+
+                       // Append item delimiter (;) if needed.
+                       if ( ret && used > 0 )
+                       {
+                               used ++;
+                               strcat( ret, ";" );
+                       }
+                       if ( ret )
+                       {
+                               // Append keyframe time and keyframe/value delimiter (=).
+                               const char *s;
+                               switch (item.keyframe_type) {
+                               case mlt_keyframe_discrete:
+                                       s = "|";
+                                       break;
+                               case mlt_keyframe_smooth:
+                                       s = "~";
+                                       break;
+                               default:
+                                       s = "";
+                                       break;
+                               }
+                               sprintf( ret + used, "%d%s=", item.frame - in, s );
+
+                               // Append item value.
+                               if ( item.is_key )
+                                       strcat( ret, mlt_property_get_string_l( item.property, self->locale ) );
+                               used = strlen( ret );
+                       }
+                       item.frame ++;
+               }
+       }
+       mlt_property_close( item.property );
+
+       return ret;
+}
+
+/** Serialize the animation.
+ *
+ * The caller is responsible for free-ing the returned string.
+ * \public \memberof mlt_animation_s
+ * \param self an animation
+ * \return a string representing the animation
+ */
+
+char *mlt_animation_serialize( mlt_animation self )
+{
+       char *ret = mlt_animation_serialize_cut( self, -1, -1 );
+       if ( ret )
+       {
+               if ( self->data )
+                       free( self->data );
+               self->data = ret;
+       }
+       return strdup( ret );
+}
+
+/** Close the animation and deallocate all of its resources.
+ *
+ * \public \memberof mlt_animation_s
+ * \param self the animation to destroy
+ */
+
+void mlt_animation_close( mlt_animation self )
+{
+       if ( self )
+       {
+               mlt_animation_clean( self );
+               free( self );
+       }
+}
diff --git a/src/framework/mlt_animation.h b/src/framework/mlt_animation.h
new file mode 100644 (file)
index 0000000..a102596
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * \file mlt_animation.h
+ * \brief Property Animation class declaration
+ * \see mlt_animation_s
+ *
+ * Copyright (C) 2004-2013 Ushodaya Enterprises Limited
+ * \author Charles Yates <charles.yates@pandora.be>
+ * \author Dan Dennedy <dan@dennedy.org>
+ *
+ * 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
+ */
+
+#ifndef MLT_ANIMATION_H
+#define MLT_ANIMATION_H
+
+#include "mlt_types.h"
+#include "mlt_property.h"
+
+/** \brief An animation item that represents a keyframe-property combination. */
+
+struct mlt_animation_item_s
+{
+       int is_key;                      /**< a boolean of whether this is a key frame or an interpolated item */
+       int frame;                       /**< the frame number for this instance of the property */
+       mlt_property property;           /**< the property for this point in time */
+       mlt_keyframe_type keyframe_type; /**< the method of interpolation for this key frame */
+};
+typedef struct mlt_animation_item_s *mlt_animation_item; /**< pointer to an animation item */
+
+extern mlt_animation mlt_animation_new( );
+extern int mlt_animation_parse(mlt_animation self, const char *data, int length, double fps, locale_t locale );
+extern int mlt_animation_refresh( mlt_animation self, const char *data, int length );
+extern int mlt_animation_get_length( mlt_animation self );
+extern void mlt_animation_set_length( mlt_animation self, int length );
+extern int mlt_animation_parse_item( mlt_animation self, mlt_animation_item item, const char *data );
+extern int mlt_animation_get_item( mlt_animation self, mlt_animation_item item, int position );
+extern int mlt_animation_insert( mlt_animation self, mlt_animation_item item );
+extern int mlt_animation_remove( mlt_animation self, int position );
+extern void mlt_animation_interpolate( mlt_animation self );
+extern int mlt_animation_next_key( mlt_animation self, mlt_animation_item item, int position );
+extern int mlt_animation_prev_key( mlt_animation self, mlt_animation_item item, int position );
+extern char *mlt_animation_serialize_cut( mlt_animation self, int in, int out );
+extern char *mlt_animation_serialize( mlt_animation self );
+extern void mlt_animation_close( mlt_animation self );
+
+#endif
+
index 7c8b5f83145a30a26cf429b223d955f9ea3c5905..5bd40c4c7c4bb24dd6d376125727347476cd4d6b 100644 (file)
@@ -3,8 +3,9 @@
  * \brief interface for all frame classes
  * \see mlt_frame_s
  *
- * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * Copyright (C) 2003-2013 Ushodaya Enterprises Limited
  * \author Charles Yates <charles.yates@pandora.be>
+ * \author Dan Dennedy <dan@dennedy.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -183,7 +184,7 @@ int mlt_frame_set_position( mlt_frame self, mlt_position value )
  *
  * \public \memberof mlt_frame_s
  * \param self a frame
- * \param the get_image callback
+ * \param get_image the get_image callback
  * \return true if error
  */
 
index 74e63b8007143d32b1ab6fba2c72827e08cdd012..ae45e7be2ba3ff38bdfe4ef649bca62ca67a4df3 100644 (file)
@@ -3,8 +3,9 @@
  * \brief interface for all frame classes
  * \see mlt_frame_s
  *
- * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * Copyright (C) 2003-2013 Ushodaya Enterprises Limited
  * \author Charles Yates <charles.yates@pandora.be>
+ * \author Dan Dennedy <dan@dennedy.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
index 301963ddca24b0fe0ed0eea7796b8d11ae09bd4b..6b41d3306e7fd32ff7ac1d7400a53c1f89f33138 100644 (file)
@@ -1,7 +1,10 @@
-/*
- * mlt_geometry.c -- provides the geometry API
+/**
+ * \file mlt_geometry.c
+ * \brief geometry animation API (deprecated)
+ * \deprecated use mlt_animation_s instead
+ *
  * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
- * Author: Charles Yates <charles.yates@pandora.be>
+ * \author Charles Yates <charles.yates@pandora.be>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 #include <stdlib.h>
 #include <string.h>
 
+/** private part of geometry animation item (deprecated)
+ * \deprecated use mlt_animation_s instead
+ */
+
 typedef struct geometry_item_s
 {
        struct mlt_geometry_item_s data;
@@ -34,6 +41,10 @@ typedef struct geometry_item_s
 }
 *geometry_item;
 
+/** private part of geometry object (deprecated)
+ * \deprecated use mlt_animation_s instead
+ */
+
 typedef struct
 {
        char *data;
index e753c67b84008e3de3e7143e2b523e32b45dbd05..fffe2d841abfa0bb5eb804458237cb0c1fe78df9 100644 (file)
@@ -1,7 +1,10 @@
-/*
- * mlt_geometry.h -- provides the geometry API
+/**
+ * \file mlt_geometry.h
+ * \brief geometry animation API (deprecated)
+ * \deprecated use mlt_animation_s instead
+ *
  * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
- * Author: Charles Yates <charles.yates@pandora.be>
+ * \author Charles Yates <charles.yates@pandora.be>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 
 #include "mlt_types.h"
 
+/** geometry animation item (deprecated)
+ * \deprecated use mlt_animation_s instead
+ */
+
 struct mlt_geometry_item_s
 {
        /* Will be 1 when this is a key frame */
@@ -37,6 +44,10 @@ struct mlt_geometry_item_s
        int f[ 5 ];
 };
 
+/** geometry object (deprecated)
+ * \deprecated use mlt_animation_s instead
+ */
+
 struct mlt_geometry_s
 {
        void *local;
index bb496eb3cd3353dda4024d871e5f36f3407cfbb2..d23cd4b89d4cfa357b0402c9861aa9c9a88cbdba 100644 (file)
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <libgen.h>
+#include <math.h>
 
 
 /** the default subdirectory of the datadir for holding profiles */
@@ -417,7 +418,8 @@ void mlt_profile_from_producer( mlt_profile profile, mlt_producer producer )
                        mlt_service_get_frame( MLT_PRODUCER_SERVICE(producer), &fr, 0 );
                        p = MLT_FRAME_PROPERTIES( fr );
 //                     mlt_properties_dump(p, stderr);
-                       if ( mlt_properties_get_int( p, "meta.media.frame_rate_den" ) && mlt_properties_get_int( p, "meta.media.sample_aspect_den" ) )
+                       if ( mlt_properties_get_int( p, "meta.media.frame_rate_den" ) &&
+                                mlt_properties_get_int( p, "meta.media.sample_aspect_den" ) )
                        {
                                profile->width = mlt_properties_get_int( p, "meta.media.width" );
                                profile->height = mlt_properties_get_int( p, "meta.media.height" );
@@ -432,7 +434,8 @@ void mlt_profile_from_producer( mlt_profile profile, mlt_producer producer )
                                profile->sample_aspect_num = mlt_properties_get_int( p, "meta.media.sample_aspect_num" );
                                profile->sample_aspect_den = mlt_properties_get_int( p, "meta.media.sample_aspect_den" );
                                profile->colorspace = mlt_properties_get_int( p, "meta.media.colorspace" );
-                               profile->display_aspect_num = (int) ( (double) profile->sample_aspect_num * profile->width / profile->sample_aspect_den + 0.5 );
+                               profile->display_aspect_num = lrint( (double) profile->sample_aspect_num * profile->width
+                                       / profile->sample_aspect_den );
                                profile->display_aspect_den = profile->height;
                                free( profile->description );
                                profile->description = strdup( "automatic" );
index 6f826e58d8454219bac8f0e85dd724b64347f508..c873daf48e09e317311e9fcba792e1f63347dca1 100644 (file)
@@ -3,7 +3,7 @@
  * \brief Properties class definition
  * \see mlt_properties_s
  *
- * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * Copyright (C) 2003-2013 Ushodaya Enterprises Limited
  * \author Charles Yates <charles.yates@pandora.be>
  * \author Dan Dennedy <dan@dennedy.org>
  *
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+// For strtod_l
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
 #include "mlt_properties.h"
 #include "mlt_property.h"
 #include "mlt_deque.h"
@@ -39,6 +44,7 @@
 #include <sys/stat.h>
 #include <errno.h>
 #include <locale.h>
+#include <float.h>
 
 #define PRESETS_DIR "/presets"
 
@@ -699,9 +705,19 @@ int mlt_properties_set( mlt_properties self, const char *name, const char *value
 
                        // Determine the value
                        if ( isdigit( id[ 0 ] ) )
-                               current = atof( id );
+                       {
+#if defined(__GLIBC__) || defined(__DARWIN__)
+                               property_list *list = self->local;
+                               if ( list->locale )
+                                       current = strtod_l( id, NULL, list->locale );
+                else
+#endif
+                                       current = strtod( id, NULL );
+                       }
                        else
+                       {
                                current = mlt_properties_get_double( self, id );
+                       }
 
                        // Apply the operation
                        switch( op )
@@ -2055,3 +2071,389 @@ char *mlt_properties_get_time( mlt_properties self, const char* name, mlt_time_f
        }
        return NULL;
 }
+
+/** Convert a numeric property to a tuple of color components.
+ *
+ * If the property's string is red, green, blue, white, or black, then it
+ * is converted to the corresponding opaque color tuple. Otherwise, the property
+ * is fetched as an integer and then converted.
+ * \public \memberof mlt_properties_s
+ * \param self a properties list
+ * \param name the property to get
+ * \return a color structure
+ */
+
+mlt_color mlt_properties_get_color( mlt_properties self, const char* name )
+{
+       mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
+       double fps = mlt_profile_fps( profile );
+       property_list *list = self->local;
+       mlt_property value = mlt_properties_find( self, name );
+       mlt_color result = { 0xff, 0xff, 0xff, 0xff };
+       if ( value )
+       {
+               const char *color = mlt_property_get_string_l( value, list->locale );
+               unsigned int color_int = mlt_property_get_int( value, fps, list->locale );
+
+               if ( !strcmp( color, "red" ) )
+               {
+                       result.r = 0xff;
+                       result.g = 0x00;
+                       result.b = 0x00;
+               }
+               else if ( !strcmp( color, "green" ) )
+               {
+                       result.r = 0x00;
+                       result.g = 0xff;
+                       result.b = 0x00;
+               }
+               else if ( !strcmp( color, "blue" ) )
+               {
+                       result.r = 0x00;
+                       result.g = 0x00;
+                       result.b = 0xff;
+               }
+               else if ( !strcmp( color, "black" ) )
+               {
+                       result.r = 0x00;
+                       result.g = 0x00;
+                       result.b = 0x00;
+               }
+               else if ( strcmp( color, "white" ) )
+               {
+                       result.r = ( color_int >> 24 ) & 0xff;
+                       result.g = ( color_int >> 16 ) & 0xff;
+                       result.b = ( color_int >> 8 ) & 0xff;
+                       result.a = ( color_int ) & 0xff;
+               }
+       }
+       return result;
+}
+
+/** Set a property to an integer value by color.
+ *
+ * \public \memberof mlt_properties_s
+ * \param self a properties list
+ * \param name the property to set
+ * \param color the color
+ * \return true if error
+ */
+
+int mlt_properties_set_color( mlt_properties self, const char *name, mlt_color color )
+{
+       int error = 1;
+
+       if ( !self || !name ) return error;
+
+       // Fetch the property to work with
+       mlt_property property = mlt_properties_fetch( self, name );
+
+       // Set it if not NULL
+       if ( property != NULL )
+       {
+               uint32_t value = ( color.r << 24 ) | ( color.g << 16 ) | ( color.b << 8 ) | color.a;
+               error = mlt_property_set_int( property, value );
+               mlt_properties_do_mirror( self, name );
+       }
+
+       mlt_events_fire( self, "property-changed", name, NULL );
+
+       return error;
+}
+
+/** Get a string value by name at a frame position.
+ *
+ * Do not free the returned string. It's lifetime is controlled by the property
+ * and this properties object.
+ * \public \memberof mlt_properties_s
+ * \param self a properties list
+ * \param name the property to get
+ * \param position the frame number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \return the property's string value or NULL if it does not exist
+ */
+
+char* mlt_properties_anim_get( mlt_properties self, const char *name, int position, int length )
+{
+       mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
+       double fps = mlt_profile_fps( profile );
+       mlt_property value = mlt_properties_find( self, name );
+       property_list *list = self->local;
+       return value == NULL ? NULL : mlt_property_anim_get_string( value, fps, list->locale, position, length );
+}
+
+/** Set a property to a string at a frame position.
+ *
+ * The event "property-changed" is fired after the property has been set.
+ *
+ * This makes a copy of the string value you supply.
+ * \public \memberof mlt_properties_s
+ * \param self a properties list
+ * \param name the property to set
+ * \param value the property's new value
+ * \param position the frame number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \return true if error
+ */
+
+int mlt_properties_anim_set( mlt_properties self, const char *name, const char *value, int position, int length )
+{
+       int error = 1;
+
+       if ( !self || !name ) return error;
+
+       // Fetch the property to work with
+       mlt_property property = mlt_properties_fetch( self, name );
+
+       // Set it if not NULL
+       if ( property )
+       {
+               mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
+               double fps = mlt_profile_fps( profile );
+               property_list *list = self->local;
+               error = mlt_property_anim_set_string( property, value,
+                       fps, list->locale, position, length );
+               mlt_properties_do_mirror( self, name );
+       }
+
+       mlt_events_fire( self, "property-changed", name, NULL );
+
+       return error;
+}
+
+/** Get an integer associated to the name at a frame position.
+ *
+ * \public \memberof mlt_properties_s
+ * \param self a properties list
+ * \param name the property to get
+ * \param position the frame number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \return the integer value, 0 if not found (which may also be a legitimate value)
+ */
+
+int mlt_properties_anim_get_int( mlt_properties self, const char *name, int position, int length )
+{
+       mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
+       double fps = mlt_profile_fps( profile );
+       property_list *list = self->local;
+       mlt_property value = mlt_properties_find( self, name );
+       return value == NULL ? 0 : mlt_property_anim_get_int( value, fps, list->locale, position, length );
+}
+
+/** Set a property to an integer value at a frame position.
+ *
+ * \public \memberof mlt_properties_s
+ * \param self a properties list
+ * \param name the property to set
+ * \param value the integer
+ * \param position the frame number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \param keyframe_type the interpolation method for this keyframe
+ * \return true if error
+ */
+
+int mlt_properties_anim_set_int( mlt_properties self, const char *name, int value,
+       int position, int length, mlt_keyframe_type keyframe_type )
+{
+       int error = 1;
+
+       if ( !self || !name ) return error;
+
+       // Fetch the property to work with
+       mlt_property property = mlt_properties_fetch( self, name );
+
+       // Set it if not NULL
+       if ( property != NULL )
+       {
+               mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
+               double fps = mlt_profile_fps( profile );
+               property_list *list = self->local;
+               error = mlt_property_anim_set_int( property, value, fps, list->locale, position, length, keyframe_type );
+               mlt_properties_do_mirror( self, name );
+       }
+
+       mlt_events_fire( self, "property-changed", name, NULL );
+
+       return error;
+}
+
+/** Get a real number associated to the name at a frame position.
+ *
+ * \public \memberof mlt_properties_s
+ * \param self a properties list
+ * \param name the property to get
+ * \param position the frame number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \return the real number, 0 if not found (which may also be a legitimate value)
+ */
+
+double mlt_properties_anim_get_double( mlt_properties self, const char *name, int position, int length )
+{
+       mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
+       double fps = mlt_profile_fps( profile );
+       property_list *list = self->local;
+       mlt_property value = mlt_properties_find( self, name );
+       return value == NULL ? 0.0 : mlt_property_anim_get_double( value, fps, list->locale, position, length );
+}
+
+/** Set a property to a real number at a frame position.
+ *
+ * \public \memberof mlt_properties_s
+ * \param self a properties list
+ * \param name the property to set
+ * \param value the real number
+ * \param position the frame number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \param keyframe_type the interpolation method for this keyframe
+ * \return true if error
+ */
+
+int mlt_properties_anim_set_double( mlt_properties self, const char *name, double value,
+       int position, int length, mlt_keyframe_type keyframe_type )
+{
+       int error = 1;
+
+       if ( !self || !name ) return error;
+
+       // Fetch the property to work with
+       mlt_property property = mlt_properties_fetch( self, name );
+
+       // Set it if not NULL
+       if ( property != NULL )
+       {
+               mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
+               double fps = mlt_profile_fps( profile );
+               property_list *list = self->local;
+               error = mlt_property_anim_set_double( property, value, fps, list->locale, position, length, keyframe_type );
+               mlt_properties_do_mirror( self, name );
+       }
+
+       mlt_events_fire( self, "property-changed", name, NULL );
+
+       return error;
+}
+
+/** Get the animation associated to the name.
+ *
+ * \public \memberof mlt_properties_s
+ * \param self a properties list
+ * \param name the property to get
+ * \return The animation object or NULL if the property has no animation
+ */
+
+mlt_animation mlt_properties_get_animation( mlt_properties self, const char *name )
+{
+       mlt_property value = mlt_properties_find( self, name );
+       return value == NULL ? NULL : mlt_property_get_animation( value );
+}
+
+/** Set a property to a rectangle value.
+ *
+ * \public \memberof mlt_properties_s
+ * \param self a properties list
+ * \param name the property to set
+ * \param value the rectangle
+ * \return true if error
+ */
+
+extern int mlt_properties_set_rect( mlt_properties self, const char *name, mlt_rect value )
+{
+       int error = 1;
+
+       if ( !self || !name ) return error;
+
+       // Fetch the property to work with
+       mlt_property property = mlt_properties_fetch( self, name );
+
+       // Set it if not NULL
+       if ( property != NULL )
+       {
+               error = mlt_property_set_rect( property, value );
+               mlt_properties_do_mirror( self, name );
+       }
+
+       mlt_events_fire( self, "property-changed", name, NULL );
+
+       return error;
+}
+
+/** Get a rectangle associated to the name.
+ *
+ * \public \memberof mlt_properties_s
+ * \param self a properties list
+ * \param name the property to get
+ * \return the rectangle value, the rectangle fields will be DBL_MIN if not found
+ */
+
+extern mlt_rect mlt_properties_get_rect( mlt_properties self, const char* name )
+{
+       property_list *list = self->local;
+       mlt_property value = mlt_properties_find( self, name );
+       mlt_rect rect = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN };
+       return value == NULL ? rect : mlt_property_get_rect( value, list->locale );
+}
+
+/** Set a property to a rectangle value at a frame position.
+ *
+ * \public \memberof mlt_properties_s
+ * \param self a properties list
+ * \param name the property to set
+ * \param value the rectangle
+ * \param position the frame number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \param keyframe_type the interpolation method for this keyframe
+ * \return true if error
+ */
+
+extern int mlt_properties_anim_set_rect( mlt_properties self, const char *name, mlt_rect value,
+       int position, int length , mlt_keyframe_type keyframe_type )
+{
+       int error = 1;
+
+       if ( !self || !name ) return error;
+
+       // Fetch the property to work with
+       mlt_property property = mlt_properties_fetch( self, name );
+
+       // Set it if not NULL
+       if ( property != NULL )
+       {
+               mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
+               double fps = mlt_profile_fps( profile );
+               property_list *list = self->local;
+               error = mlt_property_anim_set_rect( property, value, fps, list->locale, position, length, keyframe_type );
+               mlt_properties_do_mirror( self, name );
+       }
+
+       mlt_events_fire( self, "property-changed", name, NULL );
+
+       return error;
+}
+
+/** Get a rectangle associated to the name at a frame position.
+ *
+ * \public \memberof mlt_properties_s
+ * \param self a properties list
+ * \param name the property to get
+ * \param position the frame number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \return the rectangle value, the rectangle fields will be DBL_MIN if not found
+ */
+
+extern mlt_rect mlt_properties_anim_get_rect( mlt_properties self, const char *name, int position, int length )
+{
+       mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
+       double fps = mlt_profile_fps( profile );
+       property_list *list = self->local;
+       mlt_property value = mlt_properties_find( self, name );
+       mlt_rect rect = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN };
+       return value == NULL ? rect : mlt_property_anim_get_rect( value, fps, list->locale, position, length );
+}
index 449f3630706aaef1e5049fa9747ad5d621a8c2a8..7c4900d35f179bcac8258b1716d51cfcfdb0edc4 100644 (file)
@@ -3,7 +3,7 @@
  * \brief Properties class declaration
  * \see mlt_properties_s
  *
- * Copyright (C) 2003-2011 Ushodaya Enterprises Limited
+ * Copyright (C) 2003-2013 Ushodaya Enterprises Limited
  * \author Charles Yates <charles.yates@pandora.be>
  * \author Dan Dennedy <dan@dennedy.org>
  *
@@ -89,5 +89,20 @@ extern char *mlt_properties_serialise_yaml( mlt_properties self );
 extern void mlt_properties_lock( mlt_properties self );
 extern void mlt_properties_unlock( mlt_properties self );
 extern char *mlt_properties_get_time( mlt_properties, const char* name, mlt_time_format );
+extern mlt_color mlt_properties_get_color( mlt_properties, const char* name );
+extern int mlt_properties_set_color( mlt_properties, const char* name, mlt_color value );
+
+extern char* mlt_properties_anim_get( mlt_properties self, const char *name, int position, int length );
+extern int mlt_properties_anim_set( mlt_properties self, const char *name, const char *value, int position, int length );
+extern int mlt_properties_anim_get_int( mlt_properties self, const char *name, int position, int length );
+extern int mlt_properties_anim_set_int( mlt_properties self, const char *name, int value, int position, int length, mlt_keyframe_type keyframe_type );
+extern double mlt_properties_anim_get_double( mlt_properties self, const char *name, int position, int length );
+extern int mlt_properties_anim_set_double( mlt_properties self, const char *name, double value, int position, int length, mlt_keyframe_type keyframe_type );
+extern mlt_animation mlt_properties_get_animation( mlt_properties self, const char *name );
+
+extern int mlt_properties_set_rect( mlt_properties self, const char *name, mlt_rect value );
+extern mlt_rect mlt_properties_get_rect( mlt_properties self, const char *name );
+extern int mlt_properties_anim_set_rect( mlt_properties self, const char *name, mlt_rect value, int position, int length, mlt_keyframe_type keyframe_type );
+extern mlt_rect mlt_properties_anim_get_rect( mlt_properties self, const char *name, int position, int length );
 
 #endif
index 4d5f57c7c53ed84c1c7acadc77e90e6c0beaf796..801656f5b4b61b06ee653c7039273836b8626588 100644 (file)
@@ -3,8 +3,9 @@
  * \brief Property class definition
  * \see mlt_property_s
  *
- * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * Copyright (C) 2003-2013 Ushodaya Enterprises Limited
  * \author Charles Yates <charles.yates@pandora.be>
+ * \author Dan Dennedy <dan@dennedy.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 #endif
 
 #include "mlt_property.h"
+#include "mlt_animation.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <locale.h>
 #include <pthread.h>
+#include <float.h>
+#include <math.h>
 
 
 /** Bit pattern used internally to indicated representations available.
@@ -46,7 +50,8 @@ typedef enum
        mlt_prop_position = 4,//!< set as a position
        mlt_prop_double = 8,  //!< set as a floating point
        mlt_prop_data = 16,   //!< set as opaque binary
-       mlt_prop_int64 = 32   //!< set as a 64-bit integer
+       mlt_prop_int64 = 32,  //!< set as a 64-bit integer
+       mlt_prop_rect = 64    //!< set as a mlt_rect
 }
 mlt_property_type;
 
@@ -77,6 +82,7 @@ struct mlt_property_s
        mlt_serialiser serialiser;
 
        pthread_mutex_t mutex;
+       mlt_animation animation;
 };
 
 /** Construct a property and initialize it
@@ -85,21 +91,9 @@ struct mlt_property_s
 
 mlt_property mlt_property_init( )
 {
-       mlt_property self = malloc( sizeof( struct mlt_property_s ) );
-       if ( self != NULL )
-       {
-               self->types = 0;
-               self->prop_int = 0;
-               self->prop_position = 0;
-               self->prop_double = 0;
-               self->prop_int64 = 0;
-               self->prop_string = NULL;
-               self->data = NULL;
-               self->length = 0;
-               self->destructor = NULL;
-               self->serialiser = NULL;
+       mlt_property self = calloc( 1, sizeof( *self ) );
+       if ( self )
                pthread_mutex_init( &self->mutex, NULL );
-       }
        return self;
 }
 
@@ -120,6 +114,9 @@ static inline void mlt_property_clear( mlt_property self )
        if ( self->types & mlt_prop_string )
                free( self->prop_string );
 
+       if ( self->animation )
+               mlt_animation_close( self->animation );
+
        // Wipe stuff
        self->types = 0;
        self->prop_int = 0;
@@ -131,6 +128,7 @@ static inline void mlt_property_clear( mlt_property self )
        self->length = 0;
        self->destructor = NULL;
        self->serialiser = NULL;
+       self->animation = NULL;
 }
 
 /** Set the property to an integer value.
@@ -309,7 +307,7 @@ static int time_clock_to_frames( const char *s, double fps, locale_t locale )
        }
        free( copy );
 
-       return fps * ( (hours * 3600) + (minutes * 60) + seconds ) + 0.5;
+       return lrint( fps * ( (hours * 3600) + (minutes * 60) + seconds ) );
 }
 
 /** Parse a SMPTE timecode string.
@@ -355,7 +353,7 @@ static int time_code_to_frames( const char *s, double fps )
        }
        free( copy );
 
-       return frames + ( fps * ( (hours * 3600) + (minutes * 60) + seconds ) + 0.5 );
+       return lrint( fps * ( (hours * 3600) + (minutes * 60) + seconds ) + frames );
 }
 
 /** Convert a string to an integer.
@@ -428,6 +426,8 @@ int mlt_property_get_int( mlt_property self, double fps, locale_t locale )
                return ( int )self->prop_position;
        else if ( self->types & mlt_prop_int64 )
                return ( int )self->prop_int64;
+       else if ( self->types & mlt_prop_rect && self->data )
+               return ( int ) ( (mlt_rect*) self->data )->x;
        else if ( ( self->types & mlt_prop_string ) && self->prop_string )
                return mlt_property_atoi( self->prop_string, fps, locale );
        return 0;
@@ -438,6 +438,8 @@ int mlt_property_get_int( mlt_property self, double fps, locale_t locale )
  * If the string contains a colon it is interpreted as a time value. If it also
  * contains a period or comma character, the string is parsed as a clock value:
  * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF.
+ * If the numeric string ends with '%' then the value is divided by 100 to convert
+ * it into a ratio.
  * \private \memberof mlt_property_s
  * \param value the string to convert
  * \param fps frames per second, used when converting from time value
@@ -455,11 +457,17 @@ static double mlt_property_atof( const char *value, double fps, locale_t locale
        }
        else
        {
+               char *end = NULL;
+               double result;
 #if defined(__GLIBC__) || defined(__DARWIN__)
                if ( locale )
-                       return strtod_l( value, NULL, locale );
+                       result = strtod_l( value, &end, locale );
+        else
 #endif
-               return strtod( value, NULL );
+                       result = strtod( value, &end );
+               if ( end && end[0] == '%' )
+                       result /= 100.0;
+               return result;
        }
 }
 
@@ -482,6 +490,8 @@ double mlt_property_get_double( mlt_property self, double fps, locale_t locale )
                return ( double )self->prop_position;
        else if ( self->types & mlt_prop_int64 )
                return ( double )self->prop_int64;
+       else if ( self->types & mlt_prop_rect && self->data )
+               return ( (mlt_rect*) self->data )->x;
        else if ( ( self->types & mlt_prop_string ) && self->prop_string )
                return mlt_property_atof( self->prop_string, fps, locale );
        return 0;
@@ -507,6 +517,8 @@ mlt_position mlt_property_get_position( mlt_property self, double fps, locale_t
                return ( mlt_position )self->prop_double;
        else if ( self->types & mlt_prop_int64 )
                return ( mlt_position )self->prop_int64;
+       else if ( self->types & mlt_prop_rect && self->data )
+               return ( mlt_position ) ( (mlt_rect*) self->data )->x;
        else if ( ( self->types & mlt_prop_string ) && self->prop_string )
                return ( mlt_position )mlt_property_atoi( self->prop_string, fps, locale );
        return 0;
@@ -547,6 +559,8 @@ int64_t mlt_property_get_int64( mlt_property self )
                return ( int64_t )self->prop_double;
        else if ( self->types & mlt_prop_position )
                return ( int64_t )self->prop_position;
+       else if ( self->types & mlt_prop_rect && self->data )
+               return ( int64_t ) ( (mlt_rect*) self->data )->x;
        else if ( ( self->types & mlt_prop_string ) && self->prop_string )
                return mlt_property_atoll( self->prop_string );
        return 0;
@@ -579,7 +593,7 @@ char *mlt_property_get_string( mlt_property self )
                {
                        self->types |= mlt_prop_string;
                        self->prop_string = malloc( 32 );
-                       sprintf( self->prop_string, "%f", self->prop_double );
+                       sprintf( self->prop_string, "%g", self->prop_double );
                }
                else if ( self->types & mlt_prop_position )
                {
@@ -655,7 +669,7 @@ char *mlt_property_get_string_l( mlt_property self, locale_t locale )
                {
                        self->types |= mlt_prop_string;
                        self->prop_string = malloc( 32 );
-                       sprintf( self->prop_string, "%f", self->prop_double );
+                       sprintf( self->prop_string, "%g", self->prop_double );
                }
                else if ( self->types & mlt_prop_position )
                {
@@ -750,6 +764,16 @@ void mlt_property_pass( mlt_property self, mlt_property that )
                if ( that->prop_string != NULL )
                        self->prop_string = strdup( that->prop_string );
        }
+       else if ( that->types & mlt_prop_rect )
+       {
+               mlt_property_clear( self );
+               self->types = mlt_prop_rect | mlt_prop_data;
+               self->length = that->length;
+               self->data = calloc( 1, self->length );
+               memcpy( self->data, that->data, self->length );
+               self->destructor = free;
+               self->serialiser = that->serialiser;
+       }
        else if ( self->types & mlt_prop_data && self->serialiser != NULL )
        {
                self->types = mlt_prop_string;
@@ -924,3 +948,593 @@ char *mlt_property_get_time( mlt_property self, mlt_time_format format, double f
        // Return the string (may be NULL)
        return self->prop_string;
 }
+
+/** Determine if the property holds a numeric or numeric string value.
+ *
+ * \private \memberof mlt_property_s
+ * \param self a property
+ * \param locale the locale to use for string evaluation
+ * \return true if it is numeric
+ */
+
+static int is_property_numeric( mlt_property self, locale_t locale )
+{
+       int result = ( self->types & mlt_prop_int ) ||
+                       ( self->types & mlt_prop_int64 ) ||
+                       ( self->types & mlt_prop_double ) ||
+                       ( self->types & mlt_prop_position ) ||
+                       ( self->types & mlt_prop_rect );
+
+       // If not already numeric but string is numeric.
+       if ( ( !result && self->types & mlt_prop_string ) && self->prop_string )
+       {
+               double temp;
+               char *p = NULL;
+#if defined(__GLIBC__) || defined(__DARWIN__)
+               if ( locale )
+                       temp = strtod_l( self->prop_string, &p, locale );
+               else
+#endif
+               temp = strtod( self->prop_string, &p );
+               result = ( p != self->prop_string );
+       }
+       return result;
+}
+
+/** A linear interpolation function for animation.
+ *
+ * \private \memberof mlt_property_s
+ */
+
+static inline double linear_interpolate( double y1, double y2, double t )
+{
+       return y1 + ( y2 - y1 ) * t;
+}
+
+/** A smooth spline interpolation for animation.
+ *
+ * For non-closed curves, you need to also supply the tangent vector at the first and last control point.
+ * This is commonly done: T(P[0]) = P[1] - P[0] and T(P[n]) = P[n] - P[n-1].
+ * \private \memberof mlt_property_s
+ */
+
+static inline double catmull_rom_interpolate( double y0, double y1, double y2, double y3, double t )
+{
+       double t2 = t * t;
+       double a0 = -0.5 * y0 + 1.5 * y1 - 1.5 * y2 + 0.5 * y3;
+       double a1 = y0 - 2.5 * y1 + 2 * y2 - 0.5 * y3;
+       double a2 = -0.5 * y0 + 0.5 * y2;
+       double a3 = y1;
+       return a0 * t * t2 + a1 * t2 + a2 * t + a3;
+}
+
+/** Interpolate a new property value given a set of other properties.
+ *
+ * \public \memberof mlt_property_s
+ * \param self the property onto which to set the computed value
+ * \param p an array of at least 1 value in p[1] if \p interp is discrete,
+ *  2 values in p[1] and p[2] if \p interp is linear, or
+ *  4 values in p[0] - p[3] if \p interp is smooth
+ * \param progress a ratio in the range [0, 1] to indicate how far between p[1] and p[2]
+ * \param fps the frame rate, which may be needed for converting a time string to frame units
+ * \param locale the locale, which may be needed for converting a string to a real number
+ * \param interp the interpolation method to use
+ * \return true if there was an error
+ */
+
+int mlt_property_interpolate( mlt_property self, mlt_property p[],
+       double progress, double fps, locale_t locale, mlt_keyframe_type interp )
+{
+       int error = 0;
+       if ( interp != mlt_keyframe_discrete &&
+               is_property_numeric( p[1], locale ) && is_property_numeric( p[2], locale ) )
+       {
+               if ( self->types & mlt_prop_rect )
+               {
+                       mlt_rect value = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN };
+                       if ( interp == mlt_keyframe_linear )
+                       {
+                               mlt_rect points[2];
+                               mlt_rect zero = {0, 0, 0, 0, 0};
+                               points[0] = p[1]? mlt_property_get_rect( p[1], locale ) : zero;
+                               if ( p[2] )
+                               {
+                                       points[1] = mlt_property_get_rect( p[2], locale );
+                                       value.x = linear_interpolate( points[0].x, points[1].x, progress );
+                                       value.y = linear_interpolate( points[0].y, points[1].y, progress );
+                                       value.w = linear_interpolate( points[0].w, points[1].w, progress );
+                                       value.h = linear_interpolate( points[0].h, points[1].h, progress );
+                                       value.o = linear_interpolate( points[0].o, points[1].o, progress );
+                               }
+                               else
+                               {
+                                       value = points[0];
+                               }
+                       }
+                       else if ( interp == mlt_keyframe_smooth )
+                       {
+                               mlt_rect points[4];
+                               mlt_rect zero = {0, 0, 0, 0, 0};
+                               points[1] = p[1]? mlt_property_get_rect( p[1], locale ) : zero;
+                               if ( p[2] )
+                               {
+                                       points[0] = p[0]? mlt_property_get_rect( p[0], locale ) : zero;
+                                       points[2] = p[2]? mlt_property_get_rect( p[2], locale ) : zero;
+                                       points[3] = p[3]? mlt_property_get_rect( p[3], locale ) : zero;
+                                       value.x = catmull_rom_interpolate( points[0].x, points[1].x, points[2].x, points[3].x, progress );
+                                       value.y = catmull_rom_interpolate( points[0].y, points[1].y, points[2].y, points[3].y, progress );
+                                       value.w = catmull_rom_interpolate( points[0].w, points[1].w, points[2].w, points[3].w, progress );
+                                       value.h = catmull_rom_interpolate( points[0].h, points[1].h, points[2].h, points[3].h, progress );
+                                       value.o = catmull_rom_interpolate( points[0].o, points[1].o, points[2].o, points[3].o, progress );
+                               }
+                               else
+                               {
+                                       value = points[1];
+                               }
+                       }
+                       error = mlt_property_set_rect( self, value );
+               }
+               else
+               {
+                       double value;
+                       if ( interp == mlt_keyframe_linear )
+                       {
+                               double points[2];
+                               points[0] = p[1]? mlt_property_get_double( p[1], fps, locale ) : 0;
+                               points[1] = p[2]? mlt_property_get_double( p[2], fps, locale ) : 0;
+                               value = p[2]? linear_interpolate( points[0], points[1], progress ) : points[0];
+                       }
+                       else if ( interp == mlt_keyframe_smooth )
+                       {
+                               double points[4];
+                               points[0] = p[0]? mlt_property_get_double( p[0], fps, locale ) : 0;
+                               points[1] = p[1]? mlt_property_get_double( p[1], fps, locale ) : 0;
+                               points[2] = p[2]? mlt_property_get_double( p[2], fps, locale ) : 0;
+                               points[3] = p[3]? mlt_property_get_double( p[3], fps, locale ) : 0;
+                               value = p[2]? catmull_rom_interpolate( points[0], points[1], points[2], points[3], progress ) : points[1];
+                       }
+                       error = mlt_property_set_double( self, value );
+               }
+       }
+       else
+       {
+               mlt_property_pass( self, p[1] );
+       }
+       return error;
+}
+
+/** Create a new animation or refresh an existing one.
+ *
+ * \private \memberof mlt_property_s
+ * \param self a property
+ * \param fps the frame rate, which may be needed for converting a time string to frame units
+ * \param locale the locale, which may be needed for converting a string to a real number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ */
+
+static void refresh_animation( mlt_property self, double fps, locale_t locale, int length  )
+{
+       if ( !self->animation )
+       {
+               self->animation = mlt_animation_new();
+               if ( self->prop_string )
+               {
+                       mlt_animation_parse( self->animation, self->prop_string, length, fps, locale );
+               }
+               else
+               {
+                       mlt_animation_set_length( self->animation, length );
+                       self->types |= mlt_prop_data;
+                       self->data = self->animation;
+                       self->serialiser = (mlt_serialiser) mlt_animation_serialize;
+               }
+       }
+       else if ( self->prop_string )
+       {
+               mlt_animation_refresh( self->animation, self->prop_string, length );
+       }
+}
+
+/** Get the real number at a frame position.
+ *
+ * \public \memberof mlt_property_s
+ * \param self a property
+ * \param fps the frame rate, which may be needed for converting a time string to frame units
+ * \param locale the locale, which may be needed for converting a string to a real number
+ * \param position the frame number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \return the real number
+ */
+
+double mlt_property_anim_get_double( mlt_property self, double fps, locale_t locale, int position, int length )
+{
+       double result;
+       if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
+       {
+               struct mlt_animation_item_s item;
+               item.property = mlt_property_init();
+
+               refresh_animation( self, fps, locale, length );
+               mlt_animation_get_item( self->animation, &item, position );
+               result = mlt_property_get_double( item.property, fps, locale );
+
+               mlt_property_close( item.property );
+       }
+       else
+       {
+               result = mlt_property_get_double( self, fps, locale );
+       }
+       return result;
+}
+
+/** Get the property as an integer number at a frame position.
+ *
+ * \public \memberof mlt_property_s
+ * \param self a property
+ * \param fps the frame rate, which may be needed for converting a time string to frame units
+ * \param locale the locale, which may be needed for converting a string to a real number
+ * \param position the frame number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \return an integer value
+ */
+
+int mlt_property_anim_get_int( mlt_property self, double fps, locale_t locale, int position, int length )
+{
+       int result;
+       if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
+       {
+               struct mlt_animation_item_s item;
+               item.property = mlt_property_init();
+
+               refresh_animation( self, fps, locale, length );
+               mlt_animation_get_item( self->animation, &item, position );
+               result = mlt_property_get_int( item.property, fps, locale );
+
+               mlt_property_close( item.property );
+       }
+       else
+       {
+               result = mlt_property_get_int( self, fps, locale );
+       }
+       return result;
+}
+
+/** Get the string at certain a frame position.
+ *
+ * \public \memberof mlt_property_s
+ * \param self a property
+ * \param fps the frame rate, which may be needed for converting a time string to frame units
+ * \param locale the locale, which may be needed for converting a string to a real number
+ * \param position the frame number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \return the string representation of the property or NULL if failed
+ */
+
+char* mlt_property_anim_get_string( mlt_property self, double fps, locale_t locale, int position, int length )
+{
+       char *result;
+       if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
+       {
+               struct mlt_animation_item_s item;
+               item.property = mlt_property_init();
+
+               if ( !self->animation )
+                       refresh_animation( self, fps, locale, length );
+               mlt_animation_get_item( self->animation, &item, position );
+
+               pthread_mutex_lock( &self->mutex );
+               if ( self->prop_string )
+                       free( self->prop_string );
+               self->prop_string = mlt_property_get_string_l( item.property, locale );
+               if ( self->prop_string )
+                       self->prop_string = strdup( self->prop_string );
+               self->types |= mlt_prop_string;
+               pthread_mutex_unlock( &self->mutex );
+
+               result = self->prop_string;
+               mlt_property_close( item.property );
+       }
+       else
+       {
+               result = mlt_property_get_string_l( self, locale );
+       }
+       return result;
+}
+
+/** Set a property animation keyframe to a real number.
+ *
+ * \public \memberof mlt_property_s
+ * \param self a property
+ * \param value a double precision floating point value
+ * \param fps the frame rate, which may be needed for converting a time string to frame units
+ * \param locale the locale, which may be needed for converting a string to a real number
+ * \param position the frame number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \param keyframe_type the interpolation method for this keyframe
+ * \return false if successful, true to indicate error
+ */
+
+int mlt_property_anim_set_double( mlt_property self, double value, double fps, locale_t locale,
+       int position, int length, mlt_keyframe_type keyframe_type )
+{
+       int result;
+       struct mlt_animation_item_s item;
+
+       item.property = mlt_property_init();
+       item.frame = position;
+       item.keyframe_type = keyframe_type;
+       mlt_property_set_double( item.property, value );
+
+       refresh_animation( self, fps, locale, length );
+       result = mlt_animation_insert( self->animation, &item );
+       mlt_animation_interpolate( self->animation );
+       mlt_property_close( item.property );
+
+       return result;
+}
+
+/** Set a property animation keyframe to an integer value.
+ *
+ * \public \memberof mlt_property_s
+ * \param self a property
+ * \param value an integer
+ * \param fps the frame rate, which may be needed for converting a time string to frame units
+ * \param locale the locale, which may be needed for converting a string to a real number
+ * \param position the frame number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \param keyframe_type the interpolation method for this keyframe
+ * \return false if successful, true to indicate error
+ */
+
+int mlt_property_anim_set_int( mlt_property self, int value, double fps, locale_t locale,
+       int position, int length, mlt_keyframe_type keyframe_type )
+{
+       int result;
+       struct mlt_animation_item_s item;
+
+       item.property = mlt_property_init();
+       item.frame = position;
+       item.keyframe_type = keyframe_type;
+       mlt_property_set_int( item.property, value );
+
+       refresh_animation( self, fps, locale, length );
+       result = mlt_animation_insert( self->animation, &item );
+       mlt_animation_interpolate( self->animation );
+       mlt_property_close( item.property );
+
+       return result;
+}
+
+/** Set a property animation keyframe to a string.
+ *
+ * Strings only support discrete animation. Do not use this to set a property's
+ * animation string that contains a semicolon-delimited set of values; use
+ * mlt_property_set() for that.
+ * \public \memberof mlt_property_s
+ * \param self a property
+ * \param value a string
+ * \param fps the frame rate, which may be needed for converting a time string to frame units
+ * \param locale the locale, which may be needed for converting a string to a real number
+ * \param position the frame number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \return false if successful, true to indicate error
+ */
+
+int mlt_property_anim_set_string( mlt_property self, const char *value, double fps, locale_t locale, int position, int length )
+{
+       int result;
+       struct mlt_animation_item_s item;
+
+       item.property = mlt_property_init();
+       item.frame = position;
+       item.keyframe_type = mlt_keyframe_discrete;
+       mlt_property_set_string( item.property, value );
+
+       refresh_animation( self, fps, locale, length );
+       result = mlt_animation_insert( self->animation, &item );
+       mlt_animation_interpolate( self->animation );
+       mlt_property_close( item.property );
+
+       return result;
+}
+
+/** Get an object's animation object.
+ *
+ * You might need to call another mlt_property_anim_ function to actually construct
+ * the animation, as this is a simple accessor function.
+ * \public \memberof mlt_property_s
+ * \param self a property
+ * \return the animation object or NULL if there is no animation
+ */
+
+mlt_animation mlt_property_get_animation( mlt_property self )
+{
+    return self->animation;
+}
+
+/** Convert a rectangle value into a string.
+ *
+ * Unlike the deprecated mlt_geometry API, the canonical form of a mlt_rect
+ * is a space delimited "x y w h o" even though many kinds of field delimiters
+ * may be used to convert a string to a rectangle.
+ * \private \memberof mlt_property_s
+ * \param rect the rectangle to convert
+ * \param length not used
+ * \return the string representation of a rectangle
+ */
+
+static char* serialise_mlt_rect( mlt_rect *rect, int length )
+{
+       char* result = calloc( 1, 100 );
+       if ( rect->x != DBL_MIN )
+               sprintf( result + strlen( result ), "%g", rect->x );
+       if ( rect->y != DBL_MIN )
+               sprintf( result + strlen( result ), " %g", rect->y );
+       if ( rect->w != DBL_MIN )
+               sprintf( result + strlen( result ), " %g", rect->w );
+       if ( rect->h != DBL_MIN )
+               sprintf( result + strlen( result ), " %g", rect->h );
+       if ( rect->o != DBL_MIN )
+               sprintf( result + strlen( result ), " %g", rect->o );
+       return result;
+}
+
+/** Set a property to a mlt_rect rectangle.
+ *
+ * \public \memberof mlt_property_s
+ * \param self a property
+ * \param value a rectangle
+ * \return false
+ */
+
+int mlt_property_set_rect( mlt_property self, mlt_rect value )
+{
+       pthread_mutex_lock( &self->mutex );
+       mlt_property_clear( self );
+       self->types = mlt_prop_rect | mlt_prop_data;
+       self->length = sizeof(value);
+       self->data = calloc( 1, self->length );
+       memcpy( self->data, &value, self->length );
+       self->destructor = free;
+       self->serialiser = (mlt_serialiser) serialise_mlt_rect;
+       pthread_mutex_unlock( &self->mutex );
+       return 0;
+}
+
+/** Get the property as a rectangle.
+ *
+ * You can use any non-numeric character(s) as a field delimiter.
+ * If the number has a '%' immediately following it, the number is divided by
+ * 100 to convert it into a real number.
+ * \public \memberof mlt_property_s
+ * \param self a property
+ * \param locale the locale to use for when converting from a string
+ * \return a rectangle value
+ */
+
+mlt_rect mlt_property_get_rect( mlt_property self, locale_t locale )
+{
+       mlt_rect rect = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN };
+       if ( self->types & mlt_prop_rect )
+               rect = *( (mlt_rect*) self->data );
+       else if ( self->types & mlt_prop_double )
+               rect.x = self->prop_double;
+       else if ( self->types & mlt_prop_int )
+               rect.x = ( double )self->prop_int;
+       else if ( self->types & mlt_prop_position )
+               rect.x = ( double )self->prop_position;
+       else if ( self->types & mlt_prop_int64 )
+               rect.x = ( double )self->prop_int64;
+       else if ( ( self->types & mlt_prop_string ) && self->prop_string )
+       {
+               //return mlt_property_atof( self->prop_string, fps, locale );
+               char *value = self->prop_string;
+               char *p = NULL;
+               int count = 0;
+               while ( *value )
+               {
+                       double temp;
+#if defined(__GLIBC__) || defined(__DARWIN__)
+                       if ( locale )
+                               temp = strtod_l( value, &p, locale );
+            else
+#endif
+                               temp = strtod( value, &p );
+                       if ( p != value )
+                       {
+                               if ( p[0] == '%' )
+                                       temp /= 100.0;
+                               if ( *p ) p ++;
+                               switch( count )
+                               {
+                                       case 0: rect.x = temp; break;
+                                       case 1: rect.y = temp; break;
+                                       case 2: rect.w = temp; break;
+                                       case 3: rect.h = temp; break;
+                                       case 4: rect.o = temp; break;
+                               }
+                       }
+                       else
+                       {
+                               p++;
+                       }
+                       value = p;
+                       count ++;
+               }
+       }
+       return rect;
+}
+
+/** Set a property animation keyframe to a rectangle.
+ *
+ * \public \memberof mlt_property_s
+ * \param self a property
+ * \param value a rectangle
+ * \param fps the frame rate, which may be needed for converting a time string to frame units
+ * \param locale the locale, which may be needed for converting a string to a real number
+ * \param position the frame number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \param keyframe_type the interpolation method for this keyframe
+ * \return false if successful, true to indicate error
+ */
+
+int mlt_property_anim_set_rect( mlt_property self, mlt_rect value, double fps, locale_t locale,
+       int position, int length, mlt_keyframe_type keyframe_type )
+{
+       int result;
+       struct mlt_animation_item_s item;
+
+       item.property = mlt_property_init();
+       item.frame = position;
+       item.keyframe_type = keyframe_type;
+       mlt_property_set_rect( item.property, value );
+
+       refresh_animation( self, fps, locale, length );
+       result = mlt_animation_insert( self->animation, &item );
+       mlt_animation_interpolate( self->animation );
+       mlt_property_close( item.property );
+
+       return result;
+}
+
+/** Get a rectangle at a frame position.
+ *
+ * \public \memberof mlt_property_s
+ * \param self a property
+ * \param fps the frame rate, which may be needed for converting a time string to frame units
+ * \param locale the locale, which may be needed for converting a string to a real number
+ * \param position the frame number
+ * \param length the maximum number of frames when interpreting negative keyframe times,
+ *  <=0 if you don't care or need that
+ * \return the rectangle
+ */
+
+mlt_rect mlt_property_anim_get_rect( mlt_property self, double fps, locale_t locale, int position, int length )
+{
+       mlt_rect result;
+       if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
+       {
+               struct mlt_animation_item_s item;
+               item.property = mlt_property_init();
+               item.property->types = mlt_prop_rect;
+
+               refresh_animation( self, fps, locale, length );
+               mlt_animation_get_item( self->animation, &item, position );
+               result = mlt_property_get_rect( item.property, locale );
+
+               mlt_property_close( item.property );
+       }
+       else
+       {
+               result = mlt_property_get_rect( self, locale );
+       }
+       return result;
+}
index 3322434e21130e13d9f84f24f6fa3fb73c474933..c951762ddb44acf1e784d62fea49c68fea598036 100644 (file)
@@ -3,8 +3,9 @@
  * \brief Property class declaration
  * \see mlt_property_s
  *
- * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
+ * Copyright (C) 2003-2013 Ushodaya Enterprises Limited
  * \author Charles Yates <charles.yates@pandora.be>
+ * \author Dan Dennedy <dan@dennedy.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -55,4 +56,18 @@ extern void mlt_property_close( mlt_property self );
 extern void mlt_property_pass( mlt_property self, mlt_property that );
 extern char *mlt_property_get_time( mlt_property self, mlt_time_format, double fps, locale_t );
 
+extern int mlt_property_interpolate( mlt_property self, mlt_property points[], double progress, double fps, locale_t locale, mlt_keyframe_type interp );
+extern double mlt_property_anim_get_double( mlt_property self, double fps, locale_t locale, int position, int length );
+extern int mlt_property_anim_get_int( mlt_property self, double fps, locale_t locale, int position, int length );
+extern char* mlt_property_anim_get_string( mlt_property self, double fps, locale_t locale, int position, int length );
+extern int mlt_property_anim_set_double( mlt_property self, double value, double fps, locale_t locale, int position, int length, mlt_keyframe_type keyframe_type );
+extern int mlt_property_anim_set_int( mlt_property self, int value, double fps, locale_t locale, int position, int length, mlt_keyframe_type keyframe_type );
+extern int mlt_property_anim_set_string( mlt_property self, const char *value, double fps, locale_t locale, int position, int length );
+extern mlt_animation mlt_property_get_animation( mlt_property self );
+
+extern int mlt_property_set_rect( mlt_property self, mlt_rect value );
+extern mlt_rect mlt_property_get_rect( mlt_property self, locale_t locale );
+extern int mlt_property_anim_set_rect( mlt_property self, mlt_rect value, double fps, locale_t locale, int position, int length, mlt_keyframe_type keyframe_type );
+extern mlt_rect mlt_property_anim_get_rect( mlt_property self, double fps, locale_t locale, int position, int length );
+
 #endif
index b625f60073d52125bf0ea025f23f36efed5e14f7..26e410c899acb0a1a6941448b5906d51031a156d 100644 (file)
@@ -75,13 +75,22 @@ typedef enum
 }
 mlt_time_format;
 
+/** Interpolation methods for animation keyframes */
+
+typedef enum {
+       mlt_keyframe_discrete = 0, /**< non-interpolated; value changes instantaneously at the key frame */
+       mlt_keyframe_linear,       /**< simple, constant pace from this key frame to the next */
+       mlt_keyframe_smooth        /**< eased pacing from this keyframe to the next using a Catmull-Rom spline */
+}
+mlt_keyframe_type;
+
 /** The relative time qualifiers */
 
 typedef enum
 {
-       mlt_whence_relative_start /**< relative to the beginning */
-       mlt_whence_relative_current,/**< relative to the current position */
-       mlt_whence_relative_end     /**< relative to the end */
+       mlt_whence_relative_start = 0, /**< relative to the beginning */
+       mlt_whence_relative_current,   /**< relative to the current position */
+       mlt_whence_relative_end        /**< relative to the end */
 }
 mlt_whence;
 
@@ -89,7 +98,7 @@ mlt_whence;
 
 typedef enum
 {
-       invalid_type,               /**< invalid service */
+       invalid_type = 0,           /**< invalid service */
        unknown_type,               /**< unknown class */
        producer_type,              /**< Producer class */
        tractor_type,               /**< Tractor class */
@@ -114,6 +123,27 @@ typedef double mlt_position;
 typedef int32_t mlt_position;
 #endif
 
+/** A rectangle type with coordinates, size, and opacity */
+
+typedef struct {
+       double x; /**< X coordinate */
+       double y; /**< Y coordinate */
+       double w; /**< width */
+       double h; /**< height */
+       double o; /**< opacity / mix-level */
+}
+mlt_rect;
+
+/** A tuple of color components */
+
+typedef struct {
+       uint8_t r; /**< red */
+       uint8_t g; /**< green */
+       uint8_t b; /**< blue */
+       uint8_t a; /**< alpha */
+}
+mlt_color;
+
 typedef struct mlt_frame_s *mlt_frame, **mlt_frame_ptr; /**< pointer to Frame object */
 typedef struct mlt_property_s *mlt_property;            /**< pointer to Property object */
 typedef struct mlt_properties_s *mlt_properties;        /**< pointer to Properties object */
@@ -135,6 +165,7 @@ typedef struct mlt_profile_s *mlt_profile;              /**< pointer to Profile
 typedef struct mlt_repository_s *mlt_repository;        /**< pointer to Repository object */
 typedef struct mlt_cache_s *mlt_cache;                  /**< pointer to Cache object */
 typedef struct mlt_cache_item_s *mlt_cache_item;        /**< pointer to CacheItem object */
+typedef struct mlt_animation_s *mlt_animation;          /**< pointer to Property Animation object */
 
 typedef void ( *mlt_destructor )( void * );             /**< pointer to destructor function */
 typedef char *( *mlt_serialiser )( void *, int length );/**< pointer to serialization function */
index c80756eb17abe013df6be19d918387dd11a4be45..9648e0329c7c2d30eaf09cbfeb2112f203885373 100644 (file)
@@ -336,3 +336,69 @@ char *Properties::get_time( const char *name, mlt_time_format format )
 {
        return mlt_properties_get_time( get_properties(), name, format );
 }
+
+mlt_color Properties::get_color( const char *name )
+{
+       return mlt_properties_get_color( get_properties(), name );
+}
+
+int Properties::set( const char *name, mlt_color value )
+{
+       return mlt_properties_set_color( get_properties(), name, value );
+}
+
+char *Properties::anim_get( const char *name, int position, int length )
+{
+       return mlt_properties_anim_get( get_properties(), name, position, length );
+}
+
+int Properties::anim_set( const char *name, const char *value, int position, int length )
+{
+       return mlt_properties_anim_set( get_properties(), name, value, position, length );
+}
+
+int Properties::anim_get_int( const char *name, int position, int length )
+{
+       return mlt_properties_anim_get_int( get_properties(), name, position, length );
+}
+
+int Properties::anim_set( const char *name, int value, int position, int length, mlt_keyframe_type keyframe_type )
+{
+       return mlt_properties_anim_set_int( get_properties(), name, value, position, length, keyframe_type );
+}
+
+double Properties::anim_get_double(const char *name, int position, int length)
+{
+       return mlt_properties_anim_get_double( get_properties(), name, position, length );
+}
+
+int Properties::anim_set( const char *name, double value, int position, int length, mlt_keyframe_type keyframe_type )
+{
+       return mlt_properties_anim_set_double( get_properties(), name, value, position, length, keyframe_type );
+}
+
+int Properties::set( const char *name, mlt_rect value )
+{
+       return mlt_properties_set_rect( get_properties(), name, value );
+}
+
+int Properties::set( const char *name, double x, double y, double w, double h, double opacity )
+{
+       mlt_rect value = { x, y, w, h, opacity };
+       return mlt_properties_set_rect( get_properties(), name, value );
+}
+
+mlt_rect Properties::get_rect( const char *name )
+{
+       return mlt_properties_get_rect( get_properties(), name );
+}
+
+int Properties::anim_set( const char *name, mlt_rect value, int position, int length, mlt_keyframe_type keyframe_type )
+{
+       return mlt_properties_anim_set_rect( get_properties(), name, value, position, length, keyframe_type );
+}
+
+mlt_rect Properties::anim_get_rect(const char *name, int position, int length)
+{
+       return mlt_properties_anim_get_rect( get_properties(), name, position, length );
+}
index dfbc5eecdd85c5894e00e77ed5d1767b6c02f695..d2f8e8a3634d915c0168873c0341074b2807e815 100644 (file)
@@ -97,6 +97,24 @@ namespace Mlt
                        int set_lcnumeric( const char *locale );
                        const char *get_lcnumeric( );
                        char *get_time( const char *name, mlt_time_format = mlt_time_smpte );
+                       mlt_color get_color( const char *name );
+                       int set( const char *name , mlt_color value );
+
+                       char* anim_get( const char *name, int position, int length = 0 );
+                       int anim_set( const char *name, const char *value, int position, int length = 0 );
+                       int anim_get_int( const char *name, int position, int length = 0 );
+                       int anim_set( const char *name, int value, int position, int length = 0,
+                               mlt_keyframe_type keyframe_type = mlt_keyframe_linear );
+                       double anim_get_double( const char *name, int position, int length = 0 );
+                       int anim_set( const char *name, double value, int position, int length = 0,
+                               mlt_keyframe_type keyframe_type = mlt_keyframe_linear );
+
+                       int set( const char *name, mlt_rect value );
+                       int set( const char *name, double x, double y, double w, double h, double opacity = 1.0 );
+                       mlt_rect get_rect( const char* name );
+                       int anim_set( const char *name, mlt_rect value, int position, int length = 0,
+                               mlt_keyframe_type keyframe_type = mlt_keyframe_linear );
+                       mlt_rect anim_get_rect( const char *name, int position, int length = 0 );
        };
 }
 
index f54c09a371d6f442bfa4cc4f045015314489e5c5..6dfb3629209f88adef6110108e8c4df90261bcda 100644 (file)
@@ -440,6 +440,19 @@ MLTPP_0.9.0 {
   global:
     extern "C++" {
       "Mlt::Deque::peek(int)";
+      "Mlt::Properties::anim_get(char const*, int, int)";
+      "Mlt::Properties::anim_get_double(char const*, int, int)";
+      "Mlt::Properties::anim_get_int(char const*, int, int)";
+      "Mlt::Properties::anim_get_rect(char const*, int, int)";
+      "Mlt::Properties::anim_set(char const*, char const*, int, int)";
+      "Mlt::Properties::anim_set(char const*, double, int, int, mlt_keyframe_type)";
+      "Mlt::Properties::anim_set(char const*, int, int, int, mlt_keyframe_type)";
+      "Mlt::Properties::anim_set(char const*, mlt_rect, int, int, mlt_keyframe_type)";
+      "Mlt::Properties::get_color(char const*)";
+      "Mlt::Properties::get_rect(char const*)";
+      "Mlt::Properties::set(char const*, double, double, double, double, double)";
+      "Mlt::Properties::set(char const*, mlt_color)";
+      "Mlt::Properties::set(char const*, mlt_rect)";
       "Mlt::Service::filter_count()";
       "Mlt::Service::move_filter(int, int)";
     };
index 546c489f915d12d1672b53938dfb5e11b1d85379..f93a4674e87b06cb2381a22a447043e978d18441 100644 (file)
 #include "glsl_manager.h"
 #include <movit/deconvolution_sharpen_effect.h>
 
+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 );
+       GlslManager::get_instance()->lock_service( frame );
+       Effect* effect = GlslManager::get_effect( filter, frame );
+       if ( effect ) {
+               mlt_position position = mlt_filter_get_position( filter, frame );
+               mlt_position length = mlt_filter_get_length2( filter, frame );
+               bool ok = effect->set_int( "matrix_size",
+                       mlt_properties_anim_get_int( properties, "matrix_size", position, length ) );
+               ok |= effect->set_float( "cirlce_radius",
+                       mlt_properties_anim_get_double( properties, "circle_radius", position, length ) );
+               ok |= effect->set_float( "gaussian_radius",
+                       mlt_properties_anim_get_double( properties, "gaussian_radius", position, length ) );
+               ok |= effect->set_float( "correlation",
+                       mlt_properties_anim_get_double( properties, "correlation", position, length ) );
+               ok |= effect->set_float( "noise",
+                       mlt_properties_anim_get_double( properties, "noise", position, length ) );
+               assert(ok);
+       }
+       GlslManager::get_instance()->unlock_service( frame );
+       *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 )
+               if ( !GlslManager::get_effect( filter, frame ) )
                        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);
-               }
        }
+       mlt_frame_push_service( frame, filter );
+       mlt_frame_push_get_image( frame, get_image );
        return frame;
 }
 
index 825fc52ef18e13ee0c1d33ec3c10d229f1f3d107..86b4262589928e40cec6c8da7cfcb922d94612f3 100644 (file)
@@ -26,28 +26,32 @@ parameters:
     minimum: 0
     maximum: 10
     default: 5
+    mutable: yes
 
   - identifier: circle_radius
     title: Circle Radius
     type: float
     minimum: 0
     default: 2
+    mutable: yes
 
   - identifier: gaussian_radius
     title: Gaussian Radius
     type: float
     minimum: 0
     default: 0
+    mutable: yes
 
   - identifier: correlation
     title: Correlation
     type: float
     minimum: 0
     default: 0.95
+    mutable: yes
 
   - identifier: noise
     title: Noise Level
     type: float
     minimum: 0
     default: 0.01
-
+    mutable: yes
index 8129063ab824e9ba0cd2e57bf5a43828fb6b45ef..5bc3a6a26c05d5218dd487337748566ecec690c4 100644 (file)
 #include "glsl_manager.h"
 #include <movit/lift_gamma_gain_effect.h>
 
+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 );
+       GlslManager::get_instance()->lock_service( frame );
+       Effect* effect = GlslManager::get_effect( filter, frame );
+       if ( effect ) {
+               mlt_position position = mlt_filter_get_position( filter, frame );
+               mlt_position length = mlt_filter_get_length2( filter, frame );
+               RGBTriplet triplet(
+                       mlt_properties_anim_get_double( properties, "lift_r", position, length ),
+                       mlt_properties_anim_get_double( properties, "lift_g", position, length ),
+                       mlt_properties_anim_get_double( properties, "lift_b", position, length )
+               );
+               bool ok = effect->set_vec3( "lift", (float*) &triplet );
+               triplet.r = mlt_properties_anim_get_double( properties, "gamma_r", position, length );
+               triplet.g = mlt_properties_anim_get_double( properties, "gamma_g", position, length );
+               triplet.b = mlt_properties_anim_get_double( properties, "gamma_b", position, length );
+               ok |= effect->set_vec3( "gamma", (float*) &triplet );
+               triplet.r = mlt_properties_anim_get_double( properties, "gain_r", position, length );
+               triplet.g = mlt_properties_anim_get_double( properties, "gain_g", position, length );
+               triplet.b = mlt_properties_anim_get_double( properties, "gain_b", position, length );
+               ok |= effect->set_vec3( "gain", (float*) &triplet );
+               assert(ok);
+       }
+       GlslManager::get_instance()->unlock_service( frame );
+       *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 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);
-               }
+               if ( !GlslManager::get_effect( filter, frame ) )
+                       GlslManager::add_effect( filter, frame, new LiftGammaGainEffect );
        }
+       mlt_frame_push_service( frame, filter );
+       mlt_frame_push_get_image( frame, get_image );
        return frame;
 }
 
index 09dfed259634259bceffad5ddd5a89623ddf6abc..fcc55b3d023a4321f3a79c8a6f1d0ed64265fe8d 100644 (file)
@@ -30,51 +30,60 @@ parameters:
     type: float
     minimum: 0.0
     default: 0.0
+    mutable: yes
 
   - identifier: lift_g
     title: Lift Green
     type: float
     minimum: 0.0
     default: 0.0
+    mutable: yes
 
   - identifier: lift_b
     title: Lift Blue
     type: float
     minimum: 0.0
     default: 0.0
+    mutable: yes
 
   - identifier: gamma_r
     title: Gamma Red
     type: float
     minimum: 0.0
     default: 1.0
+    mutable: yes
 
   - identifier: gamma_g
     title: Gamma Green
     type: float
     minimum: 0.0
     default: 1.0
+    mutable: yes
 
   - identifier: gamma_b
     title: Gamma Blue
     type: float
     minimum: 0.0
     default: 1.0
+    mutable: yes
 
   - identifier: gain_r
     title: Gain Red
     type: float
     minimum: 0.0
     default: 1.0
+    mutable: yes
 
   - identifier: gain_g
     title: Gain Green
     type: float
     minimum: 0.0
     default: 1.0
+    mutable: yes
 
   - identifier: gain_b
     title: Gain Blue
     type: float
     minimum: 0.0
     default: 1.0
+    mutable: yes
index 5c66e97697c6f3a7de129a1bd7a8e807cd4ab206..e41e805005757b87c35038a722731b1b993414c8 100644 (file)
 #include "glsl_manager.h"
 #include <movit/blur_effect.h>
 
+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 );
+       GlslManager::get_instance()->lock_service( frame );
+       Effect* effect = GlslManager::get_effect( filter, frame );
+       if ( effect ) {
+               double radius = mlt_properties_anim_get_double( properties, "radius",
+                       mlt_filter_get_position( filter, frame ),
+                       mlt_filter_get_length2( filter, frame ) );
+               bool ok = effect->set_float( "radius", radius );
+               assert(ok);
+       }
+       GlslManager::get_instance()->unlock_service( frame );
+       *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 )
+               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);
+                       assert(effect);
                }
        }
+       mlt_frame_push_service( frame, filter );
+       mlt_frame_push_get_image( frame, get_image );
        return frame;
 }
 
index 3109af7fb601ea4f30747e8bc91c057c17c247ec..c03e165df35b1d2fffcc90c198b8261ff3dd5769 100644 (file)
@@ -20,3 +20,4 @@ parameters:
     type: float
     minimum: 0
     default: 3
+    mutable: yes
index 31d705bd3e568fae822b6179a27be17b7e662120..7434937b55691ce5f03da7bc3b6cbdd1ea934c2d 100644 (file)
 #include "glsl_manager.h"
 #include <movit/diffusion_effect.h>
 
+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 );
+       GlslManager::get_instance()->lock_service( frame );
+       Effect* effect = GlslManager::get_effect( filter, frame );
+       if ( effect ) {
+               mlt_position position = mlt_filter_get_position( filter, frame );
+               mlt_position length = mlt_filter_get_length2( filter, frame );
+               bool ok = effect->set_float( "radius",
+                       mlt_properties_anim_get_double( properties, "radius", position, length ) );
+               ok |= effect->set_float( "blurred_mix_amount",
+                       mlt_properties_anim_get_double( properties, "mix", position, length ) );
+               assert(ok);
+       }
+       GlslManager::get_instance()->unlock_service( frame );
+       *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 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);
-               }
+               if ( !GlslManager::get_effect( filter, frame ) )
+                       GlslManager::add_effect( filter, frame, new DiffusionEffect() );
        }
+       mlt_frame_push_service( frame, filter );
+       mlt_frame_push_get_image( frame, get_image );
        return frame;
 }
 
index e97d123398455e62dea9f294957ab0f9eeb78ebf..6ce14bf433f4ab7880914a18cd7a5c1da5d6487b 100644 (file)
@@ -26,6 +26,7 @@ parameters:
     type: float
     minimum: 0.0
     default: 3.0
+    mutable: yes
 
   - identifier: mix
     title: Blurriness
@@ -33,3 +34,4 @@ parameters:
     minimum: 0.0
     maximum: 1.0
     default: 0.3
+    mutable: yes
index 4026477aee32b560f8ecef1a6a14b67e84f2de33..b333359771e6f4a639085c12784a22719e11a0e8 100644 (file)
 #include "glsl_manager.h"
 #include <movit/glow_effect.h>
 
+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 );
+       GlslManager::get_instance()->lock_service( frame );
+       Effect* effect = GlslManager::get_effect( filter, frame );
+       if ( effect ) {
+               mlt_position position = mlt_filter_get_position( filter, frame );
+               mlt_position length = mlt_filter_get_length2( filter, frame );
+               bool ok = effect->set_float( "radius",
+                       mlt_properties_anim_get_double( properties, "radius", position, length ) );
+               ok |= effect->set_float( "blurred_mix_amount",
+                       mlt_properties_anim_get_double( properties, "blur_mix", position, length ) );
+               ok |= effect->set_float( "highlight_cutoff",
+                       mlt_properties_anim_get_double( properties, "highlight_cutoff", position, length ) );
+               assert(ok);
+       }
+       GlslManager::get_instance()->unlock_service( frame );
+       *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 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);
-               }
+               if ( !GlslManager::get_effect( filter, frame ) )
+                       GlslManager::add_effect( filter, frame, new GlowEffect() );
        }
+       mlt_frame_push_service( frame, filter );
+       mlt_frame_push_get_image( frame, get_image );
        return frame;
 }
 
index 258f68327f7d4ec12321dd47f4f8dee64111b691..6a3028e304b38140f27636d20a997e32ac189735 100644 (file)
@@ -19,6 +19,7 @@ parameters:
     type: float
     minimum: 0.0
     default: 20.0
+    mutable: yes
 
   - identifier: blur_mix
     title: Highlight Blurriness
@@ -26,6 +27,7 @@ parameters:
     minimum: 0.0
     maximum: 1.0
     default: 1.0
+    mutable: yes
 
   - identifier: highlight_cutoff
     title: Highlight Cutoff Threshold
@@ -33,3 +35,4 @@ parameters:
     minimum: 0.0
     maximum: 1.0
     default: 0.2
+    mutable: yes
index 8586182a9a0f2c2c3913597463506d821dd5a040..d0a815350c10dba3fa6e5adaf3f419dcae4a0fda 100644 (file)
@@ -31,7 +31,10 @@ static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format
        GlslManager::get_instance()->lock_service( frame );
        Effect* effect = GlslManager::get_effect( filter, frame );
        if ( effect ) {
-               bool ok = effect->set_float( "strength_first", mlt_properties_get_double( properties, "opacity" ) );
+               mlt_position position = mlt_filter_get_position( filter, frame );
+               mlt_position length = mlt_filter_get_length2( filter, frame );
+               bool ok = effect->set_float( "strength_first",
+                       mlt_properties_anim_get_double( properties, "opacity", position, length ) );
                assert(ok);
        }
        GlslManager::get_instance()->unlock_service( frame );
index fa82b60d8272fb0681227bebb78de3b178c4dcd7..cf4d1d65dac2107114ede725c6ffd7c6080bf0cb 100644 (file)
@@ -23,3 +23,4 @@ parameters:
     minimum: 0
     maximum: 1
     default: 1
+    mutable: yes
index 004f2f7193e9c4a5771a30061525462a9883c65c..a065e0094d621e388e5df092a1e0165cffef311e 100644 (file)
@@ -25,7 +25,7 @@ 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.rect", mlt_properties_get( properties, "rect" ) );
        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" ) );
@@ -39,7 +39,7 @@ mlt_filter filter_movit_rect_init( mlt_profile profile, mlt_service_type type, c
        GlslManager* glsl = GlslManager::get_instance();
 
        if ( glsl && ( filter = mlt_filter_new() ) ) {
-               mlt_properties_set( MLT_FILTER_PROPERTIES(filter), "geometry", arg );
+               mlt_properties_set( MLT_FILTER_PROPERTIES(filter), "rect", arg );
                mlt_properties_set_int( MLT_FILTER_PROPERTIES(filter), "fill", 1 );
                filter->process = process;
        }
index a077927ac3d46ff48e03fc0b38c8e63e4b5e7a33..6816d6702f08dd82075c713b24cdc0e4670a9de2 100644 (file)
@@ -13,7 +13,7 @@ description: >
   Change the coordinates and scale to fit within a rectangle.
 
 parameters:
-  - identifier: geometry
+  - identifier: rect
     title: Rectangle
     type: geometry
     description: > 
index cb1c542f9ea1a480a15b5428a355ac953c107360..9756f37c5954ad30f9afcaf2c4e147c1e33855c4 100644 (file)
@@ -42,36 +42,6 @@ static float alignment_parse( char* align )
        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;
@@ -94,14 +64,27 @@ static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format
        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 );
+       // Use a mlt_rect to compute position and size
+       mlt_rect rect;
+       rect.x = rect.y = 0.0;
+       if ( mlt_properties_get( properties, "resize.rect" ) ) {
+               mlt_position position = mlt_filter_get_position( filter, frame );
+               mlt_position length = mlt_filter_get_length2( filter, frame );
+               rect = mlt_properties_anim_get_rect( properties, "resize.rect", position, length );
+               if ( strchr( mlt_properties_get( properties, "resize.rect" ), '%' ) ) {
+                       rect.x *= profile->width;
+                       rect.w *= profile->width;
+                       rect.y *= profile->height;
+                       rect.h *= profile->height;
+               }
+               if ( !mlt_properties_get_int( properties, "resize.fill" ) ) {
+                       int x = mlt_properties_get_int( properties, "meta.media.width" );
+                       rect.w = rect.w > x ? x : rect.w;
+                       x = mlt_properties_get_int( properties, "meta.media.height" );
+                       rect.h = rect.h > x ? x : rect.h;
+               }
+               owidth = lrintf( rect.w );
+               oheight = lrintf( rect.h );
        }
 
        // Check for the special case - no aspect ratio means no problem :-)
@@ -116,8 +99,7 @@ static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format
        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 )
+       if ( mlt_properties_get_int( properties, "distort" ) == 0 )
        {
                // Normalise the input and out display aspect
                int normalised_width = profile->width;
@@ -163,14 +145,14 @@ static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format
        // 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;
+       if ( mlt_properties_get( properties, "resize.rect" ) ) {
+               // default left if rect supplied
+               rect.x += w * alignment_parse( mlt_properties_get( properties, "resize.halign" ) ) / 2.0f;
+               rect.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;
+               // default center if no rect
+               rect.x = w * 0.5f;
+               rect.y = h * 0.5f;
        }
 
        if ( !error ) {
@@ -179,8 +161,8 @@ static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format
                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 );
+                       ok |= effect->set_float( "left", rect.x );
+                       ok |= effect->set_float( "top", rect.y );
                        assert(ok);
                }
                GlslManager::get_instance()->unlock_service( frame );
index 1e8f9211fb1da791fd7f119247dfd2c433e7a493..9e77c10ed25611a8b8dcbf954310bac155a21527 100644 (file)
 #include "glsl_manager.h"
 #include <movit/saturation_effect.h>
 
+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 );
+       GlslManager::get_instance()->lock_service( frame );
+       Effect* effect = GlslManager::get_effect( filter, frame );
+       if ( effect ) {
+               mlt_position position = mlt_filter_get_position( filter, frame );
+               mlt_position length = mlt_filter_get_length2( filter, frame );
+               bool ok = effect->set_float( "saturation",
+                       mlt_properties_anim_get_double( properties, "saturation", position, length ) );
+               assert(ok);
+       }
+       GlslManager::get_instance()->unlock_service( frame );
+       *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 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);
-               }
+               if ( !GlslManager::get_effect( filter, frame ) )
+                       GlslManager::add_effect( filter, frame, new SaturationEffect() );
        }
+       mlt_frame_push_service( frame, filter );
+       mlt_frame_push_get_image( frame, get_image );
        return frame;
 }
 
index 535af59ac1dbb452f34b1edee22684b1da377252..6681222c882936c2ff63aec4d436f1de9af36660 100644 (file)
@@ -22,3 +22,4 @@ parameters:
     type: float
     minimum: 0
     default: 1
+    mutable: yes
index b0e33f7eb69f65ab44fbdc844efe8fc405be49d7..7259d9bf0be0c566c3e1d507b52152c375ab0cc3 100644 (file)
 #include "glsl_manager.h"
 #include <movit/vignette_effect.h>
 
+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 );
+       GlslManager::get_instance()->lock_service( frame );
+       Effect* effect = GlslManager::get_effect( filter, frame );
+       if ( effect ) {
+               mlt_position position = mlt_filter_get_position( filter, frame );
+               mlt_position length = mlt_filter_get_length2( filter, frame );
+               bool ok = effect->set_float( "radius",
+                       mlt_properties_anim_get_double( properties, "radius", position, length ) );
+               ok |= effect->set_float( "inner_radius",
+                       mlt_properties_anim_get_double( properties, "inner_radius", position, length ) );
+               assert(ok);
+       }
+       GlslManager::get_instance()->unlock_service( frame );
+       *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 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);
-               }
+               if ( !GlslManager::get_effect( filter, frame ) )
+                       GlslManager::add_effect( filter, frame, new VignetteEffect() );
        }
+       mlt_frame_push_service( frame, filter );
+       mlt_frame_push_get_image( frame, get_image );
        return frame;
 }
 
index 3355bd30b23f33f77a1fe59adec4a11b685d38b7..409b8eb0b9f0858f90bd872926af72f6adbb2764 100644 (file)
@@ -20,6 +20,7 @@ parameters:
     minimum: 0.0
     maximum: 1.0
     default: 0.3
+    mutable: yes
 
   - identifier: inner_radius
     title: Inner Radius
@@ -27,3 +28,4 @@ parameters:
     minimum: 0.0
     maximum: 1.0
     default: 0.3
+    mutable: yes
index e3f1d9c5352df84b576c17b555b093a273d35e9f..1b458e7dab6ef12b6d04aba3093decd43166308b 100644 (file)
 #include "glsl_manager.h"
 #include <movit/white_balance_effect.h>
 
+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 );
+       GlslManager::get_instance()->lock_service( frame );
+       Effect* effect = GlslManager::get_effect( filter, frame );
+       if ( effect ) {
+               mlt_position position = mlt_filter_get_position( filter, frame );
+               mlt_position length = mlt_filter_get_length2( filter, frame );
+               int color_int = mlt_properties_anim_get_int( properties, "neutral_color", position, length );
+               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_anim_get_double( properties, "color_temperature", position, length ) );
+               assert(ok);
+       }
+       GlslManager::get_instance()->unlock_service( frame );
+       *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 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);
-               }
+               if ( !GlslManager::get_effect( filter, frame ) )
+                       GlslManager::add_effect( filter, frame, new WhiteBalanceEffect );
        }
+       mlt_frame_push_service( frame, filter );
+       mlt_frame_push_get_image( frame, get_image );
        return frame;
 }
 
index 6c63d335b2a8d9cdd59d1ccfe057f0a439517b72..0d1b6b4db7ece04966f647e2122570915e55d145 100644 (file)
@@ -17,6 +17,7 @@ parameters:
     type: string
     widget: color
     default: 0x7f7f7f00
+    mutable: yes
 
   - identifier: color_temperature
     title: Color Temperature
@@ -25,3 +26,4 @@ parameters:
     maximum: 15000.0
     default: 6500.0
     unit: Kelvin
+    mutable: yes
index 176798a52440ea3f9bb06a09009058fe1993d047..e05c7eab829ed07d0c770f5d46a34033bb07d03c 100644 (file)
@@ -61,9 +61,11 @@ static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *form
        }
 
        // 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_properties_get( properties, "mix" ) ?
-               mlt_properties_get_double( properties, "mix" ) :
+               mlt_properties_anim_get_double( properties, "mix", position, length ) :
                mlt_transition_get_progress( transition, a_frame );
        double inverse = 1.0 - mix;
 
index f814850798853731ed2ba9d6aacd3ab49951b81e..b0711ed046c380c88286d62992042caaaf8e51fd 100644 (file)
@@ -18,6 +18,7 @@ parameters:
     type: float
     minimum: 0
     maximum: 1
+    mutable: yes
 
   - identifier: mix
     title: Mix Level
@@ -25,6 +26,7 @@ parameters:
     type: float
     minimum: 0
     maximum: 1
+    mutable: yes
 
   - identifier: reverse
     title: Reverse
index 9264b488f2b978169ce9bccfbc4aebf1639c3f0c..dee5e56d342aed1c6e66b8ec3041db11368b0e81 100644 (file)
 #include <mlt++/Mlt.h>
 using namespace Mlt;
 
+extern "C" {
+#define __DARWIN__
+#include <framework/mlt_property.h>
+#include <framework/mlt_animation.h>
+}
+#include <cfloat>
+
 class TestProperties: public QObject
 {
     Q_OBJECT
+    locale_t locale;
 
 public:
-    TestProperties() {}
+    TestProperties() {
+#if defined(__linux__) || defined(__DARWIN__)
+        locale = newlocale( LC_NUMERIC_MASK, "POSIX", NULL );
+#endif
+    }
 
 private Q_SLOTS:
     void InstantiationIsAReference()
@@ -121,8 +133,8 @@ private Q_SLOTS:
     void DoubleFromString()
     {
         Properties p;
-        const char *s = "-1.234567";
-        double d = -1.234567;
+        const char *s = "-1.23456";
+        double d = -1.23456;
         p.set("key", d);
         QCOMPARE(p.get("key"), s);
         p.set("key", s);
@@ -304,6 +316,589 @@ private Q_SLOTS:
         p.set("key", "0,125");
         QCOMPARE(p.get_double("key"), double(1) / double(8));
     }
+
+    void DoubleAnimation()
+    {
+        double fps = 25.0;
+        mlt_animation a = mlt_animation_new();
+        struct mlt_animation_item_s item;
+
+        mlt_animation_parse(a, "50=1; 60=60; 100=0", 100, fps, locale);
+        mlt_animation_remove(a, 60);
+        char *a_serialized = mlt_animation_serialize(a);
+        QCOMPARE(a_serialized, "50=1;100=0");
+        if (a_serialized) free(a_serialized);
+        item.property = mlt_property_init();
+
+        mlt_animation_get_item(a, &item, 10);
+        QCOMPARE(mlt_property_get_double(item.property, fps, locale), 1.0);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_animation_get_item(a, &item, 50);
+        QCOMPARE(mlt_property_get_double(item.property, fps, locale), 1.0);
+        QCOMPARE(item.is_key, 1);
+
+        mlt_animation_get_item(a, &item, 75);
+        QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.5);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_animation_get_item(a, &item, 100);
+        QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.0);
+        QCOMPARE(item.is_key, 1);
+
+        mlt_animation_get_item(a, &item, 110);
+        QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.0);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_property_close(item.property);
+        mlt_animation_close(a);
+    }
+
+    void IntAnimation()
+    {
+        double fps = 25.0;
+        mlt_animation a = mlt_animation_new();
+        struct mlt_animation_item_s item;
+
+        mlt_animation_parse(a, "50=100; 60=60; 100=0", 100, fps, locale);
+        mlt_animation_remove(a, 60);
+        char *a_serialized = mlt_animation_serialize(a);
+        QCOMPARE(a_serialized, "50=100;100=0");
+        if (a_serialized) free(a_serialized);
+        item.property = mlt_property_init();
+
+        mlt_animation_get_item(a, &item, 10);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_animation_get_item(a, &item, 50);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100);
+        QCOMPARE(item.is_key, 1);
+
+        mlt_animation_get_item(a, &item, 75);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 50);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_animation_get_item(a, &item, 100);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0);
+        QCOMPARE(item.is_key, 1);
+
+        mlt_animation_get_item(a, &item, 110);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_property_close(item.property);
+        mlt_animation_close(a);
+    }
+
+    void AnimationWithTimeValueKeyframes()
+    {
+        double fps = 25.0;
+        mlt_animation a = mlt_animation_new();
+        struct mlt_animation_item_s item;
+
+        mlt_animation_parse(a, ":2.0=1; :4.0=0", 100, fps, locale);
+        char *a_serialized = mlt_animation_serialize(a);
+        // Time serializes to frame units :-\.
+        QCOMPARE(a_serialized, "50=1;100=0");
+        if (a_serialized) free(a_serialized);
+        item.property = mlt_property_init();
+
+        mlt_animation_get_item(a, &item, 10);
+        QCOMPARE(mlt_property_get_double(item.property, fps, locale), 1.0);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_animation_get_item(a, &item, 50);
+        QCOMPARE(mlt_property_get_double(item.property, fps, locale), 1.0);
+        QCOMPARE(item.is_key, 1);
+
+        mlt_animation_get_item(a, &item, 75);
+        QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.5);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_animation_get_item(a, &item, 100);
+        QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.0);
+        QCOMPARE(item.is_key, 1);
+
+        mlt_animation_get_item(a, &item, 110);
+        QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.0);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_property_close(item.property);
+        mlt_animation_close(a);
+    }
+
+    void DiscreteIntAnimation()
+    {
+        double fps = 25.0;
+        mlt_animation a = mlt_animation_new();
+        struct mlt_animation_item_s item;
+
+        mlt_animation_parse(a, "50|=100; 60|=60; 100|=0", 100, fps, locale);
+        char *a_serialized = mlt_animation_serialize(a);
+        QCOMPARE(a_serialized, "50|=100;60|=60;100|=0");
+        if (a_serialized) free(a_serialized);
+        item.property = mlt_property_init();
+
+        mlt_animation_get_item(a, &item, 10);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_animation_get_item(a, &item, 50);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100);
+        QCOMPARE(item.is_key, 1);
+
+        mlt_animation_get_item(a, &item, 55);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_animation_get_item(a, &item, 60);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 60);
+        QCOMPARE(item.is_key, 1);
+
+        mlt_animation_get_item(a, &item, 75);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 60);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_animation_get_item(a, &item, 100);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0);
+        QCOMPARE(item.is_key, 1);
+
+        mlt_animation_get_item(a, &item, 110);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_property_close(item.property);
+        mlt_animation_close(a);
+    }
+
+    void StringAnimation()
+    {
+        double fps = 25.0;
+        mlt_animation a = mlt_animation_new();
+        struct mlt_animation_item_s item;
+
+        mlt_animation_parse(a, "50=hello world; 60=\"good night\"; 100=bar", 100, fps, locale);
+        char *a_serialized = mlt_animation_serialize(a);
+        QCOMPARE(a_serialized, "50=hello world;60=\"good night\";100=bar");
+        if (a_serialized) free(a_serialized);
+        item.property = mlt_property_init();
+
+        mlt_animation_get_item(a, &item, 10);
+        QCOMPARE(mlt_property_get_string(item.property), "hello world");
+        QCOMPARE(item.is_key, 0);
+
+        mlt_animation_get_item(a, &item, 50);
+        QCOMPARE(mlt_property_get_string(item.property), "hello world");
+        QCOMPARE(item.is_key, 1);
+
+        mlt_animation_get_item(a, &item, 75);
+        QCOMPARE(mlt_property_get_string(item.property), "\"good night\"");
+        QCOMPARE(item.is_key, 0);
+
+        mlt_animation_get_item(a, &item, 100);
+        QCOMPARE(mlt_property_get_string(item.property), "bar");
+        QCOMPARE(item.is_key, 1);
+
+        mlt_animation_get_item(a, &item, 110);
+        QCOMPARE(mlt_property_get_string(item.property), "bar");
+        QCOMPARE(item.is_key, 0);
+
+        mlt_property_close(item.property);
+        mlt_animation_close(a);
+    }
+
+    void test_property_anim_get_double()
+    {
+        double fps = 25.0;
+        int len = 0;
+        mlt_property p = mlt_property_init();
+        mlt_property_set_string(p, "10=100; 20=200");
+        QCOMPARE(mlt_property_get_double(p, fps, locale), 10.0);
+        QCOMPARE(mlt_property_anim_get_double(p, fps, locale,  0, len), 100.0);
+        QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 15, len), 150.0);
+        QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 20, len), 200.0);
+
+        mlt_property_set_string(p, "1.5");
+        QCOMPARE(mlt_property_get_double(p, fps, locale), 1.5);
+        QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 10, 100), 1.5);
+
+        mlt_property_close(p);
+    }
+
+    void test_property_anim_get_int()
+    {
+        double fps = 25.0;
+        int len = 100;
+        mlt_property p = mlt_property_init();
+        mlt_property_set_string(p, "10=100; 20=200");
+        QCOMPARE(mlt_property_get_int(p, fps, locale), 10);
+        QCOMPARE(mlt_property_anim_get_int(p, fps, locale,  0, len), 100);
+        QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 15, len), 150);
+        QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 20, len), 200);
+
+        mlt_property_set_string(p, "1.5");
+        QCOMPARE(mlt_property_get_int(p, fps, locale), 1);
+        QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 10, 100), 1);
+
+        mlt_property_close(p);
+    }
+
+    void SmoothIntAnimation()
+    {
+        double fps = 25.0;
+        mlt_animation a = mlt_animation_new();
+        struct mlt_animation_item_s item;
+
+        mlt_animation_parse(a, "0=80;10~=80; 20~=30; 30~=40; 40~=28; 50=90; 60=0; 70=60; 80=20", 100, fps, locale);
+        item.property = mlt_property_init();
+        char *a_serialized = mlt_animation_serialize(a);
+        QCOMPARE(a_serialized, "0=80;10~=80;20~=30;30~=40;40~=28;50=90;60=0;70=60;80=20");
+        if (a_serialized) free(a_serialized);
+
+        mlt_animation_get_item(a, &item, 10);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 80);
+        QCOMPARE(item.is_key, 1);
+
+        mlt_animation_get_item(a, &item, 50);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 90);
+        QCOMPARE(item.is_key, 1);
+
+        mlt_animation_get_item(a, &item, 55);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 45);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_animation_get_item(a, &item, 60);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0);
+        QCOMPARE(item.is_key, 1);
+
+        mlt_animation_get_item(a, &item, 75);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 40);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_animation_get_item(a, &item, 100);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 20);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_animation_get_item(a, &item, 110);
+        QCOMPARE(mlt_property_get_int(item.property, fps, locale), 20);
+        QCOMPARE(item.is_key, 0);
+
+        mlt_property_close(item.property);
+        mlt_animation_close(a);
+    }
+
+    void test_property_anim_set_double()
+    {
+        double fps = 25.0;
+        int len = 100;
+        mlt_property p = mlt_property_init();
+        mlt_property_set_string(p, "10=100; 20=200");
+        mlt_property_anim_set_double(p, 1.5, fps, locale, 30, len, mlt_keyframe_linear);
+        QCOMPARE(mlt_property_get_double(p, fps, locale), 10.0);
+        QCOMPARE(mlt_property_anim_get_double(p, fps, locale,  0, len), 100.0);
+        QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 15, len), 150.0);
+        QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 20, len), 200.0);
+        QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 25, len), 100.75);
+        QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 30, len), 1.5);
+        mlt_property_close(p);
+    }
+
+    void test_property_anim_set_int()
+    {
+        double fps = 25.0;
+        int len = 0;
+        mlt_property p = mlt_property_init();
+        mlt_property_set_string(p, "10=100; 20=200");
+        mlt_property_anim_set_int(p, 300, fps, locale, 30, len, mlt_keyframe_linear);
+        QCOMPARE(mlt_property_get_int(p, fps, locale), 10);
+        QCOMPARE(mlt_property_anim_get_int(p, fps, locale,  0, len), 100);
+        QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 15, len), 150);
+        QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 20, len), 200);
+        QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 25, len), 250);
+        QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 30, len), 300);
+        mlt_property_close(p);
+    }
+
+    void PercentAsRatio()
+    {
+        Properties p;
+        p.set("foo", "12.3%");
+        QCOMPARE(p.get_double("foo"), 0.123);
+        p.set("foo", "456 %");
+        QCOMPARE(p.get_double("foo"), 456.0);
+    }
+
+    void PropertiesAnimInt()
+    {
+        Properties p;
+        p.set_lcnumeric("POSIX");
+
+        // Construct animation from scratch
+        p.anim_set("foo",   0,  0);
+        p.anim_set("foo", 100, 50, -1, mlt_keyframe_smooth);
+        QCOMPARE(p.anim_get_int("foo",  0), 0);
+        QCOMPARE(p.anim_get_int("foo", 25), 50);
+        QCOMPARE(p.anim_get_int("foo", 50), 100);
+        QCOMPARE(p.get("foo"), "0=0;50~=100");
+
+        // Animation from string value
+        p.set("foo", "10=100;20=200");
+        QCOMPARE(p.anim_get_int("foo",  0), 100);
+        QCOMPARE(p.anim_get_int("foo", 15), 150);
+        QCOMPARE(p.anim_get_int("foo", 20), 200);
+
+        // Animation from string using time clock values
+        // Need to set a profile so fps can be used to convert time to frames.
+        Profile profile("dv_pal");
+        p.set("_profile", profile.get_profile(), 0);
+        p.set("foo", ":0.0=100; :2.0=200");
+        QCOMPARE(p.anim_get_int("foo",  0), 100);
+        QCOMPARE(p.anim_get_int("foo", 25), 150);
+        QCOMPARE(p.anim_get_int("foo", 50), 200);
+    }
+
+    void PropertiesAnimDouble()
+    {
+        Properties p;
+        p.set_lcnumeric("POSIX");
+
+        // Construct animation from scratch
+        p.anim_set("foo",   0.0,  0);
+        p.anim_set("foo", 100.0, 50, -1, mlt_keyframe_smooth);
+        QCOMPARE(p.anim_get_double("foo",  0), 0.0);
+        QCOMPARE(p.anim_get_double("foo", 25), 50.0);
+        QCOMPARE(p.anim_get_double("foo", 50), 100.0);
+        QCOMPARE(p.get("foo"), "0=0;50~=100");
+
+        // Animation from string value
+        p.set("foo", "10=100.2;20=200.8");
+        QCOMPARE(p.anim_get_double("foo",  0), 100.2);
+        QCOMPARE(p.anim_get_double("foo", 15), 150.5);
+        QCOMPARE(p.anim_get_double("foo", 20), 200.8);
+
+        // Animation from string using time clock values
+        // Need to set a profile so fps can be used to convert time to frames.
+        Profile profile("dv_pal");
+        p.set("_profile", profile.get_profile(), 0);
+        p.set("foo", ":0.0=100; :2.0=200");
+        QCOMPARE(p.anim_get_double("foo",  0), 100.0);
+        QCOMPARE(p.anim_get_double("foo", 25), 150.0);
+        QCOMPARE(p.anim_get_double("foo", 50), 200.0);
+    }
+
+    void PropertiesStringAnimation()
+    {
+        Properties p;
+        p.anim_set("key", "foo", 10);
+        p.anim_set("key", "bar", 30);
+        QCOMPARE(p.get("key"), "10|=foo;30|=bar");
+        p.set("key", "0=; 10=foo bar; 30=hello world");
+        QCOMPARE(p.anim_get("key",  1), "");
+        QCOMPARE(p.anim_get("key", 15), "foo bar");
+        QCOMPARE(p.anim_get("key", 45), "hello world");
+    }
+
+    void test_mlt_rect()
+    {
+        mlt_property p = mlt_property_init();
+        mlt_rect r = { 1, 2, 3, 4, 5 };
+
+        mlt_property_set_rect( p, r );
+        QCOMPARE(mlt_property_get_string(p), "1 2 3 4 5");
+        r.o = DBL_MIN;
+        mlt_property_set_rect( p, r );
+        QCOMPARE(mlt_property_get_string(p), "1 2 3 4");
+        r.w = DBL_MIN;
+        r.h = DBL_MIN;
+        mlt_property_set_rect( p, r );
+        QCOMPARE(mlt_property_get_string(p), "1 2");
+
+        mlt_property_set_string(p, "1.1/2.2:3.3x4.4:5.5");
+        r = mlt_property_get_rect(p, locale);
+        QCOMPARE(r.x, 1.1);
+        QCOMPARE(r.y, 2.2);
+        QCOMPARE(r.w, 3.3);
+        QCOMPARE(r.h, 4.4);
+        QCOMPARE(r.o, 5.5);
+
+        mlt_property_set_string(p, "1.1 2.2");
+        r = mlt_property_get_rect(p, locale);
+        QCOMPARE(r.x, 1.1);
+        QCOMPARE(r.y, 2.2);
+        QCOMPARE(r.w, DBL_MIN);
+
+        mlt_property_set_int64(p, UINT_MAX);
+        r = mlt_property_get_rect(p, locale);
+        QCOMPARE(r.x, double(UINT_MAX));
+
+        mlt_property_close(p);
+    }
+
+    void SetAndGetRect()
+    {
+        Properties p;
+        mlt_rect r;
+        r.x = 1.1;
+        r.y = 2.2;
+        r.w = 3.3;
+        r.h = 4.4;
+        r.o = 5.5;
+        p.set("key", r);
+        mlt_rect q = p.get_rect("key");
+        QCOMPARE(q.x, 1.1);
+        QCOMPARE(q.y, 2.2);
+        QCOMPARE(q.w, 3.3);
+        QCOMPARE(q.h, 4.4);
+        QCOMPARE(q.o, 5.5);
+        p.set("key", 10, 20, 30, 40);
+        q = p.get_rect("key");
+        QCOMPARE(q.x, 10.0);
+        QCOMPARE(q.y, 20.0);
+        QCOMPARE(q.w, 30.0);
+        QCOMPARE(q.h, 40.0);
+    }
+
+    void RectFromString()
+    {
+        Properties p;
+        p.set_lcnumeric("POSIX");
+        const char *s = "1.1 2.2 3.3 4.4 5.5";
+        mlt_rect r = { 1.1, 2.2, 3.3, 4.4, 5.5 };
+        p.set("key", r);
+        QCOMPARE(p.get("key"), s);
+        p.set("key", s);
+        r = p.get_rect("key");
+        QCOMPARE(r.x, 1.1);
+        QCOMPARE(r.y, 2.2);
+        QCOMPARE(r.w, 3.3);
+        QCOMPARE(r.h, 4.4);
+        QCOMPARE(r.o, 5.5);
+    }
+
+    void RectAnimation()
+    {
+        mlt_rect r1 = { 0, 0, 200, 200, 0 };
+        mlt_rect r2 = { 100, 100, 400, 400, 1.0 };
+        Properties p;
+        p.set_lcnumeric("POSIX");
+
+        // Construct animation from scratch
+        p.anim_set("key", r1,  0);
+        p.anim_set("key", r2, 50);
+        QCOMPARE(p.anim_get_rect("key",  0).x, 0.0);
+        QCOMPARE(p.anim_get_rect("key", 25).x, 50.0);
+        QCOMPARE(p.anim_get_rect("key", 25).y, 50.0);
+        QCOMPARE(p.anim_get_rect("key", 25).w, 300.0);
+        QCOMPARE(p.anim_get_rect("key", 25).h, 300.0);
+        QCOMPARE(p.anim_get_rect("key", 25).o, 0.5);
+        QCOMPARE(p.anim_get_rect("key", 50).x, 100.0);
+        QCOMPARE(p.get("key"), "0=0 0 200 200 0;50=100 100 400 400 1");
+
+        // Animation from string value
+        QCOMPARE(p.anim_get_rect("key",  0).x, 0.0);
+        QCOMPARE(p.anim_get_rect("key",  0).y, 0.0);
+        QCOMPARE(p.anim_get_rect("key",  0).w, 200.0);
+        QCOMPARE(p.anim_get_rect("key",  0).h, 200.0);
+        QCOMPARE(p.anim_get_rect("key",  0).o, 0.0);
+        QCOMPARE(p.anim_get_rect("key", 50).x, 100.0);
+        QCOMPARE(p.anim_get_rect("key", 50).y, 100.0);
+        QCOMPARE(p.anim_get_rect("key", 50).w, 400.0);
+        QCOMPARE(p.anim_get_rect("key", 50).h, 400.0);
+        QCOMPARE(p.anim_get_rect("key", 50).o, 1.0);
+        QCOMPARE(p.anim_get_rect("key", 15).x, 30.0);
+        QCOMPARE(p.anim_get_rect("key", 15).y, 30.0);
+        QCOMPARE(p.anim_get_rect("key", 15).w, 260.0);
+        QCOMPARE(p.anim_get_rect("key", 15).h, 260.0);
+        QCOMPARE(p.anim_get_rect("key", 15).o, 0.3);
+
+        // Smooth animation
+        p.set("key", "0~=0/0:200x200:0; 50=100/100:400x400:1");
+        QCOMPARE(p.anim_get_rect("key",  0).x, 0.0);
+        QCOMPARE(p.anim_get_rect("key",  0).y, 0.0);
+        QCOMPARE(p.anim_get_rect("key",  0).w, 200.0);
+        QCOMPARE(p.anim_get_rect("key",  0).h, 200.0);
+        QCOMPARE(p.anim_get_rect("key",  0).o, 0.0);
+        QCOMPARE(p.anim_get_rect("key", 50).x, 100.0);
+        QCOMPARE(p.anim_get_rect("key", 50).y, 100.0);
+        QCOMPARE(p.anim_get_rect("key", 50).w, 400.0);
+        QCOMPARE(p.anim_get_rect("key", 50).h, 400.0);
+        QCOMPARE(p.anim_get_rect("key", 50).o, 1.0);
+        QCOMPARE(p.anim_get_rect("key", 15).x, 25.8);
+        QCOMPARE(p.anim_get_rect("key", 15).y, 25.8);
+        QCOMPARE(p.anim_get_rect("key", 15).w, 251.6);
+        QCOMPARE(p.anim_get_rect("key", 15).h, 251.6);
+        QCOMPARE(p.anim_get_rect("key", 15).o, 0.258);
+
+        // Using percentages
+        p.set("key", "0=0 0; 50=100% 200%");
+        QCOMPARE(p.anim_get_rect("key", 25).x, 0.5);
+        QCOMPARE(p.anim_get_rect("key", 25).y, 1.0);
+    }
+
+    void ColorFromInt()
+    {
+        Properties p;
+        p.set_lcnumeric("POSIX");
+        p.set("key", (int) 0xaabbccdd);
+        mlt_color color = p.get_color("key");
+        QCOMPARE(color.r, quint8(0xaa));
+        QCOMPARE(color.g, quint8(0xbb));
+        QCOMPARE(color.b, quint8(0xcc));
+        QCOMPARE(color.a, quint8(0xdd));
+        p.set("key", color);
+        QCOMPARE(p.get_int("key"), int(0xaabbccdd));
+    }
+
+    void ColorFromString()
+    {
+        Properties p;
+        p.set_lcnumeric("POSIX");
+        p.set("key", "red");
+        mlt_color color = p.get_color("key");
+        QCOMPARE(color.r, quint8(0xff));
+        QCOMPARE(color.g, quint8(0x00));
+        QCOMPARE(color.b, quint8(0x00));
+        QCOMPARE(color.a, quint8(0xff));
+        p.set("key", "#deadd00d");
+        color = p.get_color("key");
+        QCOMPARE(color.r, quint8(0xad));
+        QCOMPARE(color.g, quint8(0xd0));
+        QCOMPARE(color.b, quint8(0x0d));
+        QCOMPARE(color.a, quint8(0xde));
+    }
+
+    void SetIntAndGetAnim()
+    {
+        Properties p;
+        p.set_lcnumeric("POSIX");
+        p.set("key", 123);
+        QCOMPARE(p.anim_get_int("key", 10, 50), 123);
+        p.set("key", "123");
+        QCOMPARE(p.anim_get_int("key", 10, 50), 123);
+    }
+
+    void SetDoubleAndGetAnim()
+    {
+        Properties p;
+        p.set_lcnumeric("POSIX");
+        p.set("key", 123.0);
+        QCOMPARE(p.anim_get_double("key", 10, 50), 123.0);
+        p.set("key", "123");
+        QCOMPARE(p.anim_get_double("key", 10, 50), 123.0);
+    }
+
+    void AnimNegativeTimevalue()
+    {
+        Properties p;
+        Profile profile("dv_pal");
+        p.set("_profile", profile.get_profile(), 0);
+        p.set_lcnumeric("POSIX");
+        p.set("key", "0=100; -1=200");
+        QCOMPARE(p.anim_get_int("key", 75, 100), 175);
+        p.set("key", "0=100; -1:=200");
+        QCOMPARE(p.anim_get_int("key", 75, 125), 175);
+    }
 };
 
 QTEST_APPLESS_MAIN(TestProperties)