--- /dev/null
+radius=0=10.0; :1.0=0.0;
--- /dev/null
+radius=0=10.0; :1.0=0; :-1.0=0.0; -1=10.0;
--- /dev/null
+radius=0=0; :-1.0=0.0; -1=10.0;
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 \
mlt_tokeniser.h \
mlt_profile.h \
mlt_log.h \
- mlt_cache.h
+ mlt_cache.h \
+ mlt_animation.h
SRCS := $(OBJS:.o=.c)
CFLAGS += $(RDYNAMIC) -DPREFIX_DATA="\"$(mltdatadir)\"" -DPREFIX_LIB="\"$(moduledir)\""
-LDFLAGS += $(LIBDL) -lpthread
+LDFLAGS += $(LIBDL) -lpthread -lm
all: $(TARGET)
#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"
{
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;
--- /dev/null
+/**
+ * \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 );
+ }
+}
--- /dev/null
+/**
+ * \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
+
* \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
*
* \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
*/
* \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
-/*
- * 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;
}
*geometry_item;
+/** private part of geometry object (deprecated)
+ * \deprecated use mlt_animation_s instead
+ */
+
typedef struct
{
char *data;
-/*
- * 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 */
int f[ 5 ];
};
+/** geometry object (deprecated)
+ * \deprecated use mlt_animation_s instead
+ */
+
struct mlt_geometry_s
{
void *local;
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
+#include <math.h>
/** the default subdirectory of the datadir for holding profiles */
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" );
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" );
* \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"
#include <sys/stat.h>
#include <errno.h>
#include <locale.h>
+#include <float.h>
#define PRESETS_DIR "/presets"
// 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 )
}
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 );
+}
* \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>
*
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
* \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.
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;
mlt_serialiser serialiser;
pthread_mutex_t mutex;
+ mlt_animation animation;
};
/** Construct a property and initialize it
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;
}
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;
self->length = 0;
self->destructor = NULL;
self->serialiser = NULL;
+ self->animation = NULL;
}
/** Set the property to an integer value.
}
free( copy );
- return fps * ( (hours * 3600) + (minutes * 60) + seconds ) + 0.5;
+ return lrint( fps * ( (hours * 3600) + (minutes * 60) + seconds ) );
}
/** Parse a SMPTE timecode string.
}
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.
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;
* 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
}
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;
}
}
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;
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;
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;
{
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 )
{
{
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 )
{
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;
// 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;
+}
* \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
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
}
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;
typedef enum
{
- invalid_type, /**< invalid service */
+ invalid_type = 0, /**< invalid service */
unknown_type, /**< unknown class */
producer_type, /**< Producer class */
tractor_type, /**< Tractor class */
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 */
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 */
{
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 );
+}
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 );
};
}
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)";
};
#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;
}
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
#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;
}
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
#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;
}
type: float
minimum: 0
default: 3
+ mutable: yes
#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;
}
type: float
minimum: 0.0
default: 3.0
+ mutable: yes
- identifier: mix
title: Blurriness
minimum: 0.0
maximum: 1.0
default: 0.3
+ mutable: yes
#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;
}
type: float
minimum: 0.0
default: 20.0
+ mutable: yes
- identifier: blur_mix
title: Highlight Blurriness
minimum: 0.0
maximum: 1.0
default: 1.0
+ mutable: yes
- identifier: highlight_cutoff
title: Highlight Cutoff Threshold
minimum: 0.0
maximum: 1.0
default: 0.2
+ mutable: yes
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 );
minimum: 0
maximum: 1
default: 1
+ mutable: yes
// 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" ) );
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;
}
Change the coordinates and scale to fit within a rectangle.
parameters:
- - identifier: geometry
+ - identifier: rect
title: Rectangle
type: geometry
description: >
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;
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 :-)
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;
// 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 ) {
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 );
#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;
}
type: float
minimum: 0
default: 1
+ mutable: yes
#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;
}
minimum: 0.0
maximum: 1.0
default: 0.3
+ mutable: yes
- identifier: inner_radius
title: Inner Radius
minimum: 0.0
maximum: 1.0
default: 0.3
+ mutable: yes
#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;
}
type: string
widget: color
default: 0x7f7f7f00
+ mutable: yes
- identifier: color_temperature
title: Color Temperature
maximum: 15000.0
default: 6500.0
unit: Kelvin
+ mutable: yes
}
// 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;
type: float
minimum: 0
maximum: 1
+ mutable: yes
- identifier: mix
title: Mix Level
type: float
minimum: 0
maximum: 1
+ mutable: yes
- identifier: reverse
title: Reverse
#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()
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);
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)