]> git.sesse.net Git - mlt/blob - src/framework/mlt_properties.c
Merge pull request #32 from j-b-m/master
[mlt] / src / framework / mlt_properties.c
1 /**
2  * \file mlt_properties.c
3  * \brief Properties class definition
4  * \see mlt_properties_s
5  *
6  * Copyright (C) 2003-2013 Ushodaya Enterprises Limited
7  * \author Charles Yates <charles.yates@pandora.be>
8  * \author Dan Dennedy <dan@dennedy.org>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 // For strtod_l
26 #ifndef _GNU_SOURCE
27 #define _GNU_SOURCE
28 #endif
29
30 #include "mlt_properties.h"
31 #include "mlt_property.h"
32 #include "mlt_deque.h"
33 #include "mlt_log.h"
34 #include "mlt_factory.h"
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <stdarg.h>
41 #include <pthread.h>
42 #include <sys/types.h>
43 #include <dirent.h>
44 #include <sys/stat.h>
45 #include <errno.h>
46 #include <locale.h>
47 #include <float.h>
48
49 #define PRESETS_DIR "/presets"
50
51 /** \brief private implementation of the property list */
52
53 typedef struct
54 {
55         int hash[ 199 ];
56         char **name;
57         mlt_property *value;
58         int count;
59         int size;
60         mlt_properties mirror;
61         int ref_count;
62         pthread_mutex_t mutex;
63         locale_t locale;
64 }
65 property_list;
66
67 /* Memory leak checks */
68
69 //#define _MLT_PROPERTY_CHECKS_ 2
70 #ifdef _MLT_PROPERTY_CHECKS_
71 static int properties_created = 0;
72 static int properties_destroyed = 0;
73 #endif
74
75 /** Initialize a properties object that was already allocated.
76  *
77  * This does allocate its ::property_list, and it adds a reference count.
78  * \public \memberof mlt_properties_s
79  * \param self the properties structure to initialize
80  * \param child an opaque pointer to a subclass object
81  * \return true if failed
82  */
83
84 int mlt_properties_init( mlt_properties self, void *child )
85 {
86         if ( self != NULL )
87         {
88 #ifdef _MLT_PROPERTY_CHECKS_
89                 // Increment number of properties created
90                 properties_created ++;
91 #endif
92
93                 // NULL all methods
94                 memset( self, 0, sizeof( struct mlt_properties_s ) );
95
96                 // Assign the child of the object
97                 self->child = child;
98
99                 // Allocate the local structure
100                 self->local = calloc( 1, sizeof( property_list ) );
101
102                 // Increment the ref count
103                 ( ( property_list * )self->local )->ref_count = 1;
104                 pthread_mutex_init( &( ( property_list * )self->local )->mutex, NULL );;
105         }
106
107         // Check that initialisation was successful
108         return self != NULL && self->local == NULL;
109 }
110
111 /** Create a properties object.
112  *
113  * This allocates the properties structure and calls mlt_properties_init() on it.
114  * Free the properties object with mlt_properties_close().
115  * \public \memberof mlt_properties_s
116  * \return a new properties object
117  */
118
119 mlt_properties mlt_properties_new( )
120 {
121         // Construct a standalone properties object
122         mlt_properties self = calloc( 1, sizeof( struct mlt_properties_s ) );
123
124         // Initialise self
125         mlt_properties_init( self, NULL );
126
127         // Return the pointer
128         return self;
129 }
130
131 /** Set the numeric locale used for string/double conversions.
132  *
133  * \public \memberof mlt_properties_s
134  * \param self a properties list
135  * \param locale the locale name
136  * \return true if error
137  */
138
139 int mlt_properties_set_lcnumeric( mlt_properties self, const char *locale )
140 {
141         int error = 0;
142
143         if ( self && locale )
144         {
145                 property_list *list = self->local;
146
147 #if defined(__linux__) || defined(__DARWIN__)
148                 if ( list->locale )
149                         freelocale( list->locale );
150                 list->locale = newlocale( LC_NUMERIC_MASK, locale, NULL );
151 #endif
152                 error = list->locale == NULL;
153         }
154         else
155                 error = 1;
156
157         return error;
158 }
159
160 /** Get the numeric locale for this properties object.
161  *
162  * Do not free the result.
163  * \public \memberof mlt_properties_s
164  * \param self a properties list
165  * \return the locale name if this properties has a specific locale it is using, NULL otherwise
166  */
167
168 const char* mlt_properties_get_lcnumeric( mlt_properties self )
169 {
170         property_list *list = self->local;
171         const char *result = NULL;
172
173         if ( list->locale )
174         {
175 #if defined(__DARWIN__)
176                 result = querylocale( LC_NUMERIC, list->locale );
177 #elif defined(__linux__)
178                 result = list->locale->__names[ LC_NUMERIC ];
179 #else
180                 // TODO: not yet sure what to do on other platforms
181 #endif
182         }
183         return result;
184 }
185
186 static int load_properties( mlt_properties self, const char *filename )
187 {
188         // Open the file
189         FILE *file = fopen( filename, "r" );
190
191         // Load contents of file
192         if ( file != NULL )
193         {
194                 // Temp string
195                 char temp[ 1024 ];
196                 char last[ 1024 ] = "";
197
198                 // Read each string from the file
199                 while( fgets( temp, 1024, file ) )
200                 {
201                         // Chomp the new line character from the string
202                         int x = strlen( temp ) - 1;
203                         if ( temp[x] == '\n' || temp[x] == '\r' )
204                                 temp[x] = '\0';
205
206                         // Check if the line starts with a .
207                         if ( temp[ 0 ] == '.' )
208                         {
209                                 char temp2[ 1024 ];
210                                 sprintf( temp2, "%s%s", last, temp );
211                                 strcpy( temp, temp2 );
212                         }
213                         else if ( strchr( temp, '=' ) )
214                         {
215                                 strcpy( last, temp );
216                                 *( strchr( last, '=' ) ) = '\0';
217                         }
218
219                         // Parse and set the property
220                         if ( strcmp( temp, "" ) && temp[ 0 ] != '#' )
221                                 mlt_properties_parse( self, temp );
222                 }
223
224                 // Close the file
225                 fclose( file );
226         }
227         return file? 0 : errno;
228 }
229
230 /** Create a properties object by reading a .properties text file.
231  *
232  * Free the properties object with mlt_properties_close().
233  * \deprecated Please start using mlt_properties_parse_yaml().
234  * \public \memberof mlt_properties_s
235  * \param filename the absolute file name
236  * \return a new properties object
237  */
238
239 mlt_properties mlt_properties_load( const char *filename )
240 {
241         // Construct a standalone properties object
242         mlt_properties self = mlt_properties_new( );
243
244         if ( self != NULL )
245                 load_properties( self, filename );
246
247         // Return the pointer
248         return self;
249 }
250
251 /** Set properties from a preset.
252  *
253  * Presets are typically installed to $prefix/share/mlt/presets/{type}/{service}/[{profile}/]{name}.
254  * For example, "/usr/share/mlt/presets/consumer/avformat/dv_ntsc_wide/DVD"
255  * could be an encoding preset for a widescreen NTSC DVD Video.
256  * Do not specify the type and service in the preset name parameter; these are
257  * inferred automatically from the service to which you are applying the preset.
258  * Using the example above and assuming you are calling this function on the
259  * avformat consumer, the name passed to the function should simply be DVD.
260  * Note that the profile portion of the path is optional, but a profile-specific
261  * preset with the same name as a more generic one is given a higher priority.
262  * \todo Look in a user-specific location - somewhere in the home directory.
263  *
264  * \public \memberof mlt_properties_s
265  * \param self a properties list
266  * \param name the name of a preset in a well-known location or the explicit path
267  * \return true if error
268  */
269
270 int mlt_properties_preset( mlt_properties self, const char *name )
271 {
272         struct stat stat_buff;
273
274         // validate input
275         if ( !( self && name && strlen( name ) ) )
276                 return 1;
277
278         // See if name is an explicit file
279         if ( ! stat( name, &stat_buff ) )
280         {
281                 return load_properties( self, name );
282         }
283         else
284         {
285                 // Look for profile-specific preset before a generic one.
286                 char *data          = getenv( "MLT_PRESETS_PATH" );
287                 const char *type    = mlt_properties_get( self, "mlt_type" );
288                 const char *service = mlt_properties_get( self, "mlt_service" );
289                 const char *profile = mlt_environment( "MLT_PROFILE" );
290                 int error = 0;
291
292                 if ( data )
293                 {
294                         data = strdup( data );
295                 }
296                 else
297                 {
298                         data = malloc( strlen( mlt_environment( "MLT_DATA" ) ) + strlen( PRESETS_DIR ) + 1 );
299                         strcpy( data, mlt_environment( "MLT_DATA" ) );
300                         strcat( data, PRESETS_DIR );
301                 }
302                 if ( data && type && service )
303                 {
304                         char *path = malloc( 5 + strlen(name) + strlen(data) + strlen(type) + strlen(service) + ( profile? strlen(profile) : 0 ) );
305                         sprintf( path, "%s/%s/%s/%s/%s", data, type, service, profile, name );
306                         if ( load_properties( self, path ) )
307                         {
308                                 sprintf( path, "%s/%s/%s/%s", data, type, service, name );
309                                 error = load_properties( self, path );
310                         }
311                         free( path );
312                 }
313                 else
314                 {
315                         error = 1;
316                 }
317                 free( data );
318                 return error;
319         }
320 }
321
322 /** Generate a hash key.
323  *
324  * \private \memberof mlt_properties_s
325  * \param name a string
326  * \return an integer
327  */
328
329 static inline int generate_hash( const char *name )
330 {
331         int hash = 0;
332         int i = 1;
333         while ( *name )
334                 hash = ( hash + ( i ++ * ( *name ++ & 31 ) ) ) % 199;
335         return hash;
336 }
337
338 /** Copy a serializable property to a properties list that is mirroring this one.
339  *
340  * Special case - when a container (such as loader) is protecting another
341  * producer, we need to ensure that properties are passed through to the
342  * real producer.
343  * \private \memberof mlt_properties_s
344  * \param self a properties list
345  * \param name the name of the property to copy
346  */
347
348 static inline void mlt_properties_do_mirror( mlt_properties self, const char *name )
349 {
350         if ( !self ) return;
351         property_list *list = self->local;
352         if ( list->mirror != NULL )
353         {
354                 char *value = mlt_properties_get( self, name );
355                 if ( value != NULL )
356                         mlt_properties_set( list->mirror, name, value );
357         }
358 }
359
360 /** Increment the reference count.
361  *
362  * \public \memberof mlt_properties_s
363  * \param self a properties list
364  * \return the new reference count
365  */
366
367 int mlt_properties_inc_ref( mlt_properties self )
368 {
369         int result = 0;
370         if ( self != NULL )
371         {
372                 property_list *list = self->local;
373                 pthread_mutex_lock( &list->mutex );
374                 result = ++ list->ref_count;
375                 pthread_mutex_unlock( &list->mutex );
376         }
377         return result;
378 }
379
380 /** Decrement the reference count.
381  *
382  * \public \memberof mlt_properties_s
383  * \param self a properties list
384  * \return the new reference count
385  */
386
387 int mlt_properties_dec_ref( mlt_properties self )
388 {
389         int result = 0;
390         if ( self != NULL )
391         {
392                 property_list *list = self->local;
393                 pthread_mutex_lock( &list->mutex );
394                 result = -- list->ref_count;
395                 pthread_mutex_unlock( &list->mutex );
396         }
397         return result;
398 }
399
400 /** Get the reference count.
401  *
402  * \public \memberof mlt_properties_s
403  * \param self a properties list
404  * \return the current reference count
405  */
406
407 int mlt_properties_ref_count( mlt_properties self )
408 {
409         if ( self != NULL )
410         {
411                 property_list *list = self->local;
412                 return list->ref_count;
413         }
414         return 0;
415 }
416
417 /** Set a properties list to be a mirror copy of another.
418  *
419  * Note that this does not copy all existing properties. Rather, you must
420  * call this before setting the properties that you wish to copy.
421  * \public \memberof mlt_properties_s
422  * \param that the properties which will receive copies of the properties as they are set.
423  * \param self the properties to mirror
424  */
425
426 void mlt_properties_mirror( mlt_properties self, mlt_properties that )
427 {
428         if ( !self ) return;
429         property_list *list = self->local;
430         list->mirror = that;
431 }
432
433 /** Copy all serializable properties to another properties list.
434  *
435  * \public \memberof mlt_properties_s
436  * \param self The properties to copy to
437  * \param that The properties to copy from
438  * \return true if error
439  */
440
441 int mlt_properties_inherit( mlt_properties self, mlt_properties that )
442 {
443         if ( !self || !that ) return 1;
444         int count = mlt_properties_count( that );
445         int i = 0;
446         for ( i = 0; i < count; i ++ )
447         {
448                 char *value = mlt_properties_get_value( that, i );
449                 if ( value != NULL )
450                 {
451                         char *name = mlt_properties_get_name( that, i );
452                         mlt_properties_set( self, name, value );
453                 }
454         }
455         return 0;
456 }
457
458 /** Pass all serializable properties that match a prefix to another properties object
459  *
460  * \warning The prefix is stripped from the name when it is set on the \p self properties list!
461  * For example a property named "foo.bar" will match prefix "foo.", but the property
462  * will be named simply "bar" on the receiving properties object.
463  * \public \memberof mlt_properties_s
464  * \param self the properties to copy to
465  * \param that The properties to copy from
466  * \param prefix the property names to match (required)
467  * \return true if error
468  */
469
470 int mlt_properties_pass( mlt_properties self, mlt_properties that, const char *prefix )
471 {
472         if ( !self || !that ) return 1;
473         int count = mlt_properties_count( that );
474         int length = strlen( prefix );
475         int i = 0;
476         for ( i = 0; i < count; i ++ )
477         {
478                 char *name = mlt_properties_get_name( that, i );
479                 if ( !strncmp( name, prefix, length ) )
480                 {
481                         char *value = mlt_properties_get_value( that, i );
482                         if ( value != NULL )
483                                 mlt_properties_set( self, name + length, value );
484                 }
485         }
486         return 0;
487 }
488
489 /** Locate a property by name.
490  *
491  * \private \memberof mlt_properties_s
492  * \param self a properties list
493  * \param name the property to lookup by name
494  * \return the property or NULL for failure
495  */
496
497 static inline mlt_property mlt_properties_find( mlt_properties self, const char *name )
498 {
499         if ( !self || !name ) return NULL;
500         property_list *list = self->local;
501         mlt_property value = NULL;
502         int key = generate_hash( name );
503
504         mlt_properties_lock( self );
505
506         int i = list->hash[ key ] - 1;
507         if ( i >= 0 )
508         {
509                 // Check if we're hashed
510                 if ( list->count > 0 &&
511                         name[ 0 ] == list->name[ i ][ 0 ] &&
512                         !strcmp( list->name[ i ], name ) )
513                         value = list->value[ i ];
514
515                 // Locate the item
516                 for ( i = list->count - 1; value == NULL && i >= 0; i -- )
517                         if ( name[ 0 ] == list->name[ i ][ 0 ] && !strcmp( list->name[ i ], name ) )
518                                 value = list->value[ i ];
519         }
520         mlt_properties_unlock( self );
521
522         return value;
523 }
524
525 /** Add a new property.
526  *
527  * \private \memberof mlt_properties_s
528  * \param self a properties list
529  * \param name the name of the new property
530  * \return the new property
531  */
532
533 static mlt_property mlt_properties_add( mlt_properties self, const char *name )
534 {
535         property_list *list = self->local;
536         int key = generate_hash( name );
537         mlt_property result;
538
539         mlt_properties_lock( self );
540
541         // Check that we have space and resize if necessary
542         if ( list->count == list->size )
543         {
544                 list->size += 50;
545                 list->name = realloc( list->name, list->size * sizeof( const char * ) );
546                 list->value = realloc( list->value, list->size * sizeof( mlt_property ) );
547         }
548
549         // Assign name/value pair
550         list->name[ list->count ] = strdup( name );
551         list->value[ list->count ] = mlt_property_init( );
552
553         // Assign to hash table
554         if ( list->hash[ key ] == 0 )
555                 list->hash[ key ] = list->count + 1;
556
557         // Return and increment count accordingly
558         result = list->value[ list->count ++ ];
559
560         mlt_properties_unlock( self );
561
562         return result;
563 }
564
565 /** Fetch a property by name and add one if not found.
566  *
567  * \private \memberof mlt_properties_s
568  * \param self a properties list
569  * \param name the property to lookup or add
570  * \return the property
571  */
572
573 static mlt_property mlt_properties_fetch( mlt_properties self, const char *name )
574 {
575         // Try to find an existing property first
576         mlt_property property = mlt_properties_find( self, name );
577
578         // If it wasn't found, create one
579         if ( property == NULL )
580                 property = mlt_properties_add( self, name );
581
582         // Return the property
583         return property;
584 }
585
586 /** Copy a property to another properties list.
587  *
588  * \public \memberof mlt_properties_s
589  * \author Zach <zachary.drew@gmail.com>
590  * \param self the properties to copy to
591  * \param that the properties to copy from
592  * \param name the name of the property to copy
593  */
594
595 void mlt_properties_pass_property( mlt_properties self, mlt_properties that, const char *name )
596 {
597         // Make sure the source property isn't null.
598         mlt_property that_prop = mlt_properties_find( that, name );
599         if( that_prop == NULL )
600                 return;
601
602         mlt_property_pass( mlt_properties_fetch( self, name ), that_prop );
603 }
604
605 /** Copy all properties specified in a comma-separated list to another properties list.
606  *
607  * White space is also a delimiter.
608  * \public \memberof mlt_properties_s
609  * \author Zach <zachary.drew@gmail.com>
610  * \param self the properties to copy to
611  * \param that the properties to copy from
612  * \param list a delimited list of property names
613  * \return true if error
614  */
615
616
617 int mlt_properties_pass_list( mlt_properties self, mlt_properties that, const char *list )
618 {
619         if ( !self || !that || !list ) return 1;
620         char *props = strdup( list );
621         char *ptr = props;
622         const char *delim = " ,\t\n";   // Any combination of spaces, commas, tabs, and newlines
623         int count, done = 0;
624
625         while( !done )
626         {
627                 count = strcspn( ptr, delim );
628
629                 if( ptr[count] == '\0' )
630                         done = 1;
631                 else
632                         ptr[count] = '\0';      // Make it a real string
633
634                 mlt_properties_pass_property( self, that, ptr );
635
636                 ptr += count + 1;
637                 if ( !done )
638                         ptr += strspn( ptr, delim );
639         }
640
641         free( props );
642
643         return 0;
644 }
645
646
647 /** Set a property to a string.
648  *
649  * The property name "properties" is reserved to load the preset in \p value.
650  * When the value begins with '@' then it is interpreted as a very simple math
651  * expression containing only the +, -, *, and / operators.
652  * The event "property-changed" is fired after the property has been set.
653  *
654  * This makes a copy of the string value you supply.
655  * \public \memberof mlt_properties_s
656  * \param self a properties list
657  * \param name the property to set
658  * \param value the property's new value
659  * \return true if error
660  */
661
662 int mlt_properties_set( mlt_properties self, const char *name, const char *value )
663 {
664         int error = 1;
665
666         if ( !self || !name ) return error;
667
668         // Fetch the property to work with
669         mlt_property property = mlt_properties_fetch( self, name );
670
671         // Set it if not NULL
672         if ( property == NULL )
673         {
674                 mlt_log( NULL, MLT_LOG_FATAL, "Whoops - %s not found (should never occur)\n", name );
675         }
676         else if ( value == NULL )
677         {
678                 error = mlt_property_set_string( property, value );
679                 mlt_properties_do_mirror( self, name );
680         }
681         else if ( *value != '@' )
682         {
683                 error = mlt_property_set_string( property, value );
684                 mlt_properties_do_mirror( self, name );
685                 if ( !strcmp( name, "properties" ) )
686                         mlt_properties_preset( self, value );
687         }
688         else if ( value[ 0 ] == '@' )
689         {
690                 double total = 0;
691                 double current = 0;
692                 char id[ 255 ];
693                 char op = '+';
694
695                 value ++;
696
697                 while ( *value != '\0' )
698                 {
699                         int length = strcspn( value, "+-*/" );
700
701                         // Get the identifier
702                         strncpy( id, value, length );
703                         id[ length ] = '\0';
704                         value += length;
705
706                         // Determine the value
707                         if ( isdigit( id[ 0 ] ) )
708                         {
709 #if defined(__GLIBC__) || defined(__DARWIN__)
710                                 property_list *list = self->local;
711                                 if ( list->locale )
712                                         current = strtod_l( id, NULL, list->locale );
713                 else
714 #endif
715                                         current = strtod( id, NULL );
716                         }
717                         else
718                         {
719                                 current = mlt_properties_get_double( self, id );
720                         }
721
722                         // Apply the operation
723                         switch( op )
724                         {
725                                 case '+':
726                                         total += current;
727                                         break;
728                                 case '-':
729                                         total -= current;
730                                         break;
731                                 case '*':
732                                         total *= current;
733                                         break;
734                                 case '/':
735                                         total = total / current;
736                                         break;
737                         }
738
739                         // Get the next op
740                         op = *value != '\0' ? *value ++ : ' ';
741                 }
742
743                 error = mlt_property_set_double( property, total );
744                 mlt_properties_do_mirror( self, name );
745         }
746
747         mlt_events_fire( self, "property-changed", name, NULL );
748
749         return error;
750 }
751
752 /** Set or default a property to a string.
753  *
754  * This makes a copy of the string value you supply.
755  * \public \memberof mlt_properties_s
756  * \param self a properties list
757  * \param name the property to set
758  * \param value the string value to set or NULL to use the default
759  * \param def the default string if value is NULL
760  * \return true if error
761  */
762
763 int mlt_properties_set_or_default( mlt_properties self, const char *name, const char *value, const char *def )
764 {
765         return mlt_properties_set( self, name, value == NULL ? def : value );
766 }
767
768 /** Get a string value by name.
769  *
770  * Do not free the returned string. It's lifetime is controlled by the property
771  * and this properties object.
772  * \public \memberof mlt_properties_s
773  * \param self a properties list
774  * \param name the property to get
775  * \return the property's string value or NULL if it does not exist
776  */
777
778 char *mlt_properties_get( mlt_properties self, const char *name )
779 {
780         mlt_property value = mlt_properties_find( self, name );
781         property_list *list = self->local;
782         return value == NULL ? NULL : mlt_property_get_string_l( value, list->locale );
783 }
784
785 /** Get a property name by index.
786  *
787  * Do not free the returned string.
788  * \public \memberof mlt_properties_s
789  * \param self a properties list
790  * \param index the numeric index of the property
791  * \return the name of the property or NULL if index is out of range
792  */
793
794 char *mlt_properties_get_name( mlt_properties self, int index )
795 {
796         if ( !self ) return NULL;
797         property_list *list = self->local;
798         if ( index >= 0 && index < list->count )
799                 return list->name[ index ];
800         return NULL;
801 }
802
803 /** Get a property's string value by index.
804  *
805  * Do not free the returned string.
806  * \public \memberof mlt_properties_s
807  * \param self a properties list
808  * \param index the numeric index of the property
809  * \return the property value as a string or NULL if the index is out of range
810  */
811
812 char *mlt_properties_get_value( mlt_properties self, int index )
813 {
814         if ( !self ) return NULL;
815         property_list *list = self->local;
816         if ( index >= 0 && index < list->count )
817                 return mlt_property_get_string_l( list->value[ index ], list->locale );
818         return NULL;
819 }
820
821 /** Get a data value by index.
822  *
823  * Do not free the returned pointer if you supplied a destructor function when you
824  * set this property.
825  * \public \memberof mlt_properties_s
826  * \param self a properties list
827  * \param index the numeric index of the property
828  * \param[out] size the size of the binary data in bytes or NULL if the index is out of range
829  */
830
831 void *mlt_properties_get_data_at( mlt_properties self, int index, int *size )
832 {
833         if ( !self ) return NULL;
834         property_list *list = self->local;
835         if ( index >= 0 && index < list->count )
836                 return mlt_property_get_data( list->value[ index ], size );
837         return NULL;
838 }
839
840 /** Return the number of items in the list.
841  *
842  * \public \memberof mlt_properties_s
843  * \param self a properties list
844  * \return the number of property objects or -1 if error
845  */
846
847 int mlt_properties_count( mlt_properties self )
848 {
849         if ( !self ) return -1;
850         property_list *list = self->local;
851         return list->count;
852 }
853
854 /** Set a value by parsing a name=value string.
855  *
856  * \public \memberof mlt_properties_s
857  * \param self a properties list
858  * \param namevalue a string containing name and value delimited by '='
859  * \return true if there was an error
860  */
861
862 int mlt_properties_parse( mlt_properties self, const char *namevalue )
863 {
864         if ( !self ) return 1;
865         char *name = strdup( namevalue );
866         char *value = NULL;
867         int error = 0;
868         char *ptr = strchr( name, '=' );
869
870         if ( ptr )
871         {
872                 *( ptr ++ ) = '\0';
873
874                 if ( *ptr != '\"' )
875                 {
876                         value = strdup( ptr );
877                 }
878                 else
879                 {
880                         ptr ++;
881                         value = strdup( ptr );
882                         if ( value != NULL && value[ strlen( value ) - 1 ] == '\"' )
883                                 value[ strlen( value ) - 1 ] = '\0';
884                 }
885         }
886         else
887         {
888                 value = strdup( "" );
889         }
890
891         error = mlt_properties_set( self, name, value );
892
893         free( name );
894         free( value );
895
896         return error;
897 }
898
899 /** Get an integer associated to the name.
900  *
901  * \public \memberof mlt_properties_s
902  * \param self a properties list
903  * \param name the property to get
904  * \return The integer value, 0 if not found (which may also be a legitimate value)
905  */
906
907 int mlt_properties_get_int( mlt_properties self, const char *name )
908 {
909         mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
910         double fps = mlt_profile_fps( profile );
911         property_list *list = self->local;
912         mlt_property value = mlt_properties_find( self, name );
913         return value == NULL ? 0 : mlt_property_get_int( value, fps, list->locale );
914 }
915
916 /** Set a property to an integer value.
917  *
918  * \public \memberof mlt_properties_s
919  * \param self a properties list
920  * \param name the property to set
921  * \param value the integer
922  * \return true if error
923  */
924
925 int mlt_properties_set_int( mlt_properties self, const char *name, int value )
926 {
927         int error = 1;
928
929         if ( !self || !name ) return error;
930
931         // Fetch the property to work with
932         mlt_property property = mlt_properties_fetch( self, name );
933
934         // Set it if not NULL
935         if ( property != NULL )
936         {
937                 error = mlt_property_set_int( property, value );
938                 mlt_properties_do_mirror( self, name );
939         }
940
941         mlt_events_fire( self, "property-changed", name, NULL );
942
943         return error;
944 }
945
946 /** Get a 64-bit integer associated to the name.
947  *
948  * \public \memberof mlt_properties_s
949  * \param self a properties list
950  * \param name the property to get
951  * \return the integer value, 0 if not found (which may also be a legitimate value)
952  */
953
954 int64_t mlt_properties_get_int64( mlt_properties self, const char *name )
955 {
956         mlt_property value = mlt_properties_find( self, name );
957         return value == NULL ? 0 : mlt_property_get_int64( value );
958 }
959
960 /** Set a property to a 64-bit integer value.
961  *
962  * \public \memberof mlt_properties_s
963  * \param self a properties list
964  * \param name the property to set
965  * \param value the integer
966  * \return true if error
967  */
968
969 int mlt_properties_set_int64( mlt_properties self, const char *name, int64_t value )
970 {
971         int error = 1;
972
973         if ( !self || !name ) return error;
974
975         // Fetch the property to work with
976         mlt_property property = mlt_properties_fetch( self, name );
977
978         // Set it if not NULL
979         if ( property != NULL )
980         {
981                 error = mlt_property_set_int64( property, value );
982                 mlt_properties_do_mirror( self, name );
983         }
984
985         mlt_events_fire( self, "property-changed", name, NULL );
986
987         return error;
988 }
989
990 /** Get a floating point value associated to the name.
991  *
992  * \public \memberof mlt_properties_s
993  * \param self a properties list
994  * \param name the property to get
995  * \return the floating point, 0 if not found (which may also be a legitimate value)
996  */
997
998 double mlt_properties_get_double( mlt_properties self, const char *name )
999 {
1000         mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
1001         double fps = mlt_profile_fps( profile );
1002         mlt_property value = mlt_properties_find( self, name );
1003         property_list *list = self->local;
1004         return value == NULL ? 0 : mlt_property_get_double( value, fps, list->locale );
1005 }
1006
1007 /** Set a property to a floating point value.
1008  *
1009  * \public \memberof mlt_properties_s
1010  * \param self a properties list
1011  * \param name the property to set
1012  * \param value the floating point value
1013  * \return true if error
1014  */
1015
1016 int mlt_properties_set_double( mlt_properties self, const char *name, double value )
1017 {
1018         int error = 1;
1019
1020         if ( !self || !name ) return error;
1021
1022         // Fetch the property to work with
1023         mlt_property property = mlt_properties_fetch( self, name );
1024
1025         // Set it if not NULL
1026         if ( property != NULL )
1027         {
1028                 error = mlt_property_set_double( property, value );
1029                 mlt_properties_do_mirror( self, name );
1030         }
1031
1032         mlt_events_fire( self, "property-changed", name, NULL );
1033
1034         return error;
1035 }
1036
1037 /** Get a position value associated to the name.
1038  *
1039  * \public \memberof mlt_properties_s
1040  * \param self a properties list
1041  * \param name the property to get
1042  * \return the position, 0 if not found (which may also be a legitimate value)
1043  */
1044
1045 mlt_position mlt_properties_get_position( mlt_properties self, const char *name )
1046 {
1047         mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
1048         double fps = mlt_profile_fps( profile );
1049         property_list *list = self->local;
1050         mlt_property value = mlt_properties_find( self, name );
1051         return value == NULL ? 0 : mlt_property_get_position( value, fps, list->locale );
1052 }
1053
1054 /** Set a property to a position value.
1055  *
1056  * \public \memberof mlt_properties_s
1057  * \param self a properties list
1058  * \param name the property to get
1059  * \param value the position
1060  * \return true if error
1061  */
1062
1063 int mlt_properties_set_position( mlt_properties self, const char *name, mlt_position value )
1064 {
1065         int error = 1;
1066
1067         if ( !self || !name ) return error;
1068
1069         // Fetch the property to work with
1070         mlt_property property = mlt_properties_fetch( self, name );
1071
1072         // Set it if not NULL
1073         if ( property != NULL )
1074         {
1075                 error = mlt_property_set_position( property, value );
1076                 mlt_properties_do_mirror( self, name );
1077         }
1078
1079         mlt_events_fire( self, "property-changed", name, NULL );
1080
1081         return error;
1082 }
1083
1084 /** Get a binary data value associated to the name.
1085  *
1086  * Do not free the returned pointer if you supplied a destructor function
1087  * when you set this property.
1088  * \public \memberof mlt_properties_s
1089  * \param self a properties list
1090  * \param name the property to get
1091  * \param[out] length The size of the binary data in bytes, if available (often it is not, you should know)
1092  */
1093
1094 void *mlt_properties_get_data( mlt_properties self, const char *name, int *length )
1095 {
1096         mlt_property value = mlt_properties_find( self, name );
1097         return value == NULL ? NULL : mlt_property_get_data( value, length );
1098 }
1099
1100 /** Store binary data as a property.
1101  *
1102  * \public \memberof mlt_properties_s
1103  * \param self a properties list
1104  * \param name the property to set
1105  * \param value an opaque pointer to binary data
1106  * \param length the size of the binary data in bytes (optional)
1107  * \param destroy a function to deallocate the binary data when the property is closed (optional)
1108  * \param serialise a function that can serialize the binary data as text (optional)
1109  * \return true if error
1110  */
1111
1112 int mlt_properties_set_data( mlt_properties self, const char *name, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise )
1113 {
1114         int error = 1;
1115
1116         if ( !self || !name ) return error;
1117
1118         // Fetch the property to work with
1119         mlt_property property = mlt_properties_fetch( self, name );
1120
1121         // Set it if not NULL
1122         if ( property != NULL )
1123                 error = mlt_property_set_data( property, value, length, destroy, serialise );
1124
1125         mlt_events_fire( self, "property-changed", name, NULL );
1126
1127         return error;
1128 }
1129
1130 /** Rename a property.
1131  *
1132  * \public \memberof mlt_properties_s
1133  * \param self a properties list
1134  * \param source the property to rename
1135  * \param dest the new name
1136  * \return true if the name is already in use
1137  */
1138
1139 int mlt_properties_rename( mlt_properties self, const char *source, const char *dest )
1140 {
1141         mlt_property value = mlt_properties_find( self, dest );
1142
1143         if ( value == NULL )
1144         {
1145                 property_list *list = self->local;
1146                 int i = 0;
1147
1148                 // Locate the item
1149                 mlt_properties_lock( self );
1150                 for ( i = 0; i < list->count; i ++ )
1151                 {
1152                         if ( !strcmp( list->name[ i ], source ) )
1153                         {
1154                                 free( list->name[ i ] );
1155                                 list->name[ i ] = strdup( dest );
1156                                 list->hash[ generate_hash( dest ) ] = i + 1;
1157                                 break;
1158                         }
1159                 }
1160                 mlt_properties_unlock( self );
1161         }
1162
1163         return value != NULL;
1164 }
1165
1166 /** Dump the properties to a file handle.
1167  *
1168  * \public \memberof mlt_properties_s
1169  * \param self a properties list
1170  * \param output a file handle
1171  */
1172
1173 void mlt_properties_dump( mlt_properties self, FILE *output )
1174 {
1175         if ( !self || !output ) return;
1176         property_list *list = self->local;
1177         int i = 0;
1178         for ( i = 0; i < list->count; i ++ )
1179                 if ( mlt_properties_get( self, list->name[ i ] ) != NULL )
1180                         fprintf( output, "%s=%s\n", list->name[ i ], mlt_properties_get( self, list->name[ i ] ) );
1181 }
1182
1183 /** Output the properties to a file handle.
1184  *
1185  * This version includes reference counts and does not put each property on a new line.
1186  * \public \memberof mlt_properties_s
1187  * \param self a properties pointer
1188  * \param title a string to preface the output
1189  * \param output a file handle
1190  */
1191 void mlt_properties_debug( mlt_properties self, const char *title, FILE *output )
1192 {
1193         if ( !self || !output ) return;
1194         if ( output == NULL ) output = stderr;
1195         fprintf( output, "%s: ", title );
1196         if ( self != NULL )
1197         {
1198                 property_list *list = self->local;
1199                 int i = 0;
1200                 fprintf( output, "[ ref=%d", list->ref_count );
1201                 for ( i = 0; i < list->count; i ++ )
1202                         if ( mlt_properties_get( self, list->name[ i ] ) != NULL )
1203                                 fprintf( output, ", %s=%s", list->name[ i ], mlt_properties_get( self, list->name[ i ] ) );
1204                         else
1205                                 fprintf( output, ", %s=%p", list->name[ i ], mlt_properties_get_data( self, list->name[ i ], NULL ) );
1206                 fprintf( output, " ]" );
1207         }
1208         fprintf( output, "\n" );
1209 }
1210
1211 /** Save the properties to a file by name.
1212  *
1213  * This uses the dump format - one line per property.
1214  * \public \memberof mlt_properties_s
1215  * \param self a properties list
1216  * \param filename the name of a file to create or overwrite
1217  * \return true if there was an error
1218  */
1219
1220 int mlt_properties_save( mlt_properties self, const char *filename )
1221 {
1222         int error = 1;
1223         if ( !self || !filename ) return error;
1224         FILE *f = fopen( filename, "w" );
1225         if ( f != NULL )
1226         {
1227                 mlt_properties_dump( self, f );
1228                 fclose( f );
1229                 error = 0;
1230         }
1231         return error;
1232 }
1233
1234 /* This is a very basic cross platform fnmatch replacement - it will fail in
1235  * many cases, but for the basic *.XXX and YYY*.XXX, it will work ok.
1236  */
1237
1238 /** Test whether a filename or pathname matches a shell-style pattern.
1239  *
1240  * \private \memberof mlt_properties_s
1241  * \param wild a string containing a wildcard pattern
1242  * \param file the name of a file to test against
1243  * \return true if the file name matches the wildcard pattern
1244  */
1245
1246 static int mlt_fnmatch( const char *wild, const char *file )
1247 {
1248         int f = 0;
1249         int w = 0;
1250
1251         while( f < strlen( file ) && w < strlen( wild ) )
1252         {
1253                 if ( wild[ w ] == '*' )
1254                 {
1255                         w ++;
1256                         if ( w == strlen( wild ) )
1257                                 f = strlen( file );
1258                         while ( f != strlen( file ) && tolower( file[ f ] ) != tolower( wild[ w ] ) )
1259                                 f ++;
1260                 }
1261                 else if ( wild[ w ] == '?' || tolower( file[ f ] ) == tolower( wild[ w ] ) )
1262                 {
1263                         f ++;
1264                         w ++;
1265                 }
1266                 else if ( wild[ 0 ] == '*' )
1267                 {
1268                         w = 0;
1269                 }
1270                 else
1271                 {
1272                         return 0;
1273                 }
1274         }
1275
1276         return strlen( file ) == f &&  strlen( wild ) == w;
1277 }
1278
1279 /** Compare the string or serialized value of two properties.
1280  *
1281  * \private \memberof mlt_properties_s
1282  * \param self a property
1283  * \param that a property
1284  * \return < 0 if \p self less than \p that, 0 if equal, or > 0 if \p self is greater than \p that
1285  */
1286
1287 static int mlt_compare( const void *self, const void *that )
1288 {
1289     return strcmp( mlt_property_get_string( *( const mlt_property * )self ), mlt_property_get_string( *( const mlt_property * )that ) );
1290 }
1291
1292 /** Get the contents of a directory.
1293  *
1294  * Obtains an optionally sorted list of the files found in a directory with a specific wild card.
1295  * Entries in the list have a numeric name (running from 0 to count - 1). Only values change
1296  * position if sort is enabled. Designed to be posix compatible (linux, os/x, mingw etc).
1297  * \public \memberof mlt_properties_s
1298  * \param self a properties list
1299  * \param dirname the name of the directory
1300  * \param pattern a wildcard pattern to filter the directory listing
1301  * \param sort Do you want to sort the directory listing?
1302  * \return the number of items in the directory listing
1303  */
1304
1305 int mlt_properties_dir_list( mlt_properties self, const char *dirname, const char *pattern, int sort )
1306 {
1307         DIR *dir = opendir( dirname );
1308
1309         if ( dir )
1310         {
1311                 char key[ 20 ];
1312                 struct dirent *de = readdir( dir );
1313                 char fullname[ 1024 ];
1314                 while( de != NULL )
1315                 {
1316                         sprintf( key, "%d", mlt_properties_count( self ) );
1317                         snprintf( fullname, 1024, "%s/%s", dirname, de->d_name );
1318                         if ( pattern == NULL )
1319                                 mlt_properties_set( self, key, fullname );
1320                         else if ( de->d_name[ 0 ] != '.' && mlt_fnmatch( pattern, de->d_name ) )
1321                                 mlt_properties_set( self, key, fullname );
1322                         de = readdir( dir );
1323                 }
1324
1325                 closedir( dir );
1326         }
1327
1328         if ( sort && mlt_properties_count( self ) )
1329         {
1330                 property_list *list = self->local;
1331                 mlt_properties_lock( self );
1332                 qsort( list->value, mlt_properties_count( self ), sizeof( mlt_property ), mlt_compare );
1333                 mlt_properties_unlock( self );
1334         }
1335
1336         return mlt_properties_count( self );
1337 }
1338
1339 /** Close a properties object.
1340  *
1341  * Deallocates the properties object and everything it contains.
1342  * \public \memberof mlt_properties_s
1343  * \param self a properties object
1344  */
1345
1346 void mlt_properties_close( mlt_properties self )
1347 {
1348         if ( self != NULL && mlt_properties_dec_ref( self ) <= 0 )
1349         {
1350                 if ( self->close != NULL )
1351                 {
1352                         self->close( self->close_object );
1353                 }
1354                 else
1355                 {
1356                         property_list *list = self->local;
1357                         int index = 0;
1358
1359 #if _MLT_PROPERTY_CHECKS_ == 1
1360                         // Show debug info
1361                         mlt_properties_debug( self, "Closing", stderr );
1362 #endif
1363
1364 #ifdef _MLT_PROPERTY_CHECKS_
1365                         // Increment destroyed count
1366                         properties_destroyed ++;
1367
1368                         // Show current stats - these should match when the app is closed
1369                         mlt_log( NULL, MLT_LOG_DEBUG, "Created %d, destroyed %d\n", properties_created, properties_destroyed );
1370 #endif
1371
1372                         // Clean up names and values
1373                         for ( index = list->count - 1; index >= 0; index -- )
1374                         {
1375                                 mlt_property_close( list->value[ index ] );
1376                                 free( list->name[ index ] );
1377                         }
1378
1379 #if defined(__linux__) || defined(__DARWIN__)
1380                         // Cleanup locale
1381                         if ( list->locale )
1382                                 freelocale( list->locale );
1383 #endif
1384
1385                         // Clear up the list
1386                         pthread_mutex_destroy( &list->mutex );
1387                         free( list->name );
1388                         free( list->value );
1389                         free( list );
1390
1391                         // Free self now if self has no child
1392                         if ( self->child == NULL )
1393                                 free( self );
1394                 }
1395         }
1396 }
1397
1398 /** Determine if the properties list is really just a sequence or ordered list.
1399  *
1400  * \public \memberof mlt_properties_s
1401  * \param properties a properties list
1402  * \return true if all of the property names are numeric (a sequence)
1403  */
1404
1405 int mlt_properties_is_sequence( mlt_properties properties )
1406 {
1407         int i;
1408         int n = mlt_properties_count( properties );
1409         for ( i = 0; i < n; i++ )
1410                 if ( ! isdigit( mlt_properties_get_name( properties, i )[0] ) )
1411                         return 0;
1412         return 1;
1413 }
1414
1415 /** \brief YAML Tiny Parser context structure
1416  *
1417  * YAML is a nifty text format popular in the Ruby world as a cleaner,
1418  * less verbose alternative to XML. See this Wikipedia topic for an overview:
1419  * http://en.wikipedia.org/wiki/YAML
1420  * The YAML specification is at:
1421  * http://yaml.org/
1422  * YAML::Tiny is a Perl module that specifies a subset of YAML that we are
1423  * using here (for the same reasons):
1424  * http://search.cpan.org/~adamk/YAML-Tiny-1.25/lib/YAML/Tiny.pm
1425  * \private
1426  */
1427
1428 struct yaml_parser_context
1429 {
1430         mlt_deque stack;
1431         unsigned int level;
1432         int index;
1433         mlt_deque index_stack;
1434         char block;
1435         char *block_name;
1436         unsigned int block_indent;
1437
1438 };
1439 typedef struct yaml_parser_context *yaml_parser;
1440
1441 /** Remove spaces from the left side of a string.
1442  *
1443  * \param s the string to trim
1444  * \return the number of characters removed
1445  */
1446
1447 static unsigned int ltrim( char **s )
1448 {
1449         unsigned int i = 0;
1450         char *c = *s;
1451         int n = strlen( c );
1452         for ( i = 0; i < n && *c == ' '; i++, c++ );
1453         *s = c;
1454         return i;
1455 }
1456
1457 /** Remove spaces from the right side of a string.
1458  *
1459  * \param s the string to trim
1460  * \return the number of characters removed
1461  */
1462
1463 static unsigned int rtrim( char *s )
1464 {
1465         int n = strlen( s );
1466         int i;
1467         for ( i = n; i > 0 && s[i - 1] == ' '; --i )
1468                 s[i - 1] = 0;
1469         return n - i;
1470 }
1471
1472 /** Parse a line of YAML Tiny.
1473  *
1474  * Adds a property if needed.
1475  * \private \memberof yaml_parser_context
1476  * \param context a YAML Tiny Parser context
1477  * \param namevalue a line of YAML Tiny
1478  * \return true if there was an error
1479  */
1480
1481 static int parse_yaml( yaml_parser context, const char *namevalue )
1482 {
1483         char *name_ = strdup( namevalue );
1484         char *name = name_;
1485         char *value = NULL;
1486         int error = 0;
1487         char *ptr = strchr( name, ':' );
1488         unsigned int indent = ltrim( &name );
1489         mlt_properties properties = mlt_deque_peek_back( context->stack );
1490
1491         // Ascending one more levels in the tree
1492         if ( indent < context->level )
1493         {
1494                 unsigned int i;
1495                 unsigned int n = ( context->level - indent ) / 2;
1496                 for ( i = 0; i < n; i++ )
1497                 {
1498                         mlt_deque_pop_back( context->stack );
1499                         context->index = mlt_deque_pop_back_int( context->index_stack );
1500                 }
1501                 properties = mlt_deque_peek_back( context->stack );
1502                 context->level = indent;
1503         }
1504
1505         // Descending a level in the tree
1506         else if ( indent > context->level && context->block == 0 )
1507         {
1508                 context->level = indent;
1509         }
1510
1511         // If there is a colon that is not part of a block
1512         if ( ptr && ( indent == context->level ) )
1513         {
1514                 // Reset block processing
1515                 if ( context->block_name )
1516                 {
1517                         free( context->block_name );
1518                         context->block_name = NULL;
1519                         context->block = 0;
1520                 }
1521
1522                 // Terminate the name and setup the value pointer
1523                 *( ptr ++ ) = 0;
1524
1525                 // Trim comment
1526                 char *comment = strchr( ptr, '#' );
1527                 if ( comment )
1528                 {
1529                         *comment = 0;
1530                 }
1531
1532                 // Trim leading and trailing spaces from bare value
1533                 ltrim( &ptr );
1534                 rtrim( ptr );
1535
1536                 // No value means a child
1537                 if ( strcmp( ptr, "" ) == 0 )
1538                 {
1539                         mlt_properties child = mlt_properties_new();
1540                         mlt_properties_set_lcnumeric( child, mlt_properties_get_lcnumeric( properties ) );
1541                         mlt_properties_set_data( properties, name, child, 0,
1542                                 ( mlt_destructor )mlt_properties_close, NULL );
1543                         mlt_deque_push_back( context->stack, child );
1544                         mlt_deque_push_back_int( context->index_stack, context->index );
1545                         context->index = 0;
1546                         free( name_ );
1547                         return error;
1548                 }
1549
1550                 // A dash indicates a sequence item
1551                 if ( name[0] == '-' )
1552                 {
1553                         mlt_properties child = mlt_properties_new();
1554                         char key[20];
1555
1556                         mlt_properties_set_lcnumeric( child, mlt_properties_get_lcnumeric( properties ) );
1557                         snprintf( key, sizeof(key), "%d", context->index++ );
1558                         mlt_properties_set_data( properties, key, child, 0,
1559                                 ( mlt_destructor )mlt_properties_close, NULL );
1560                         mlt_deque_push_back( context->stack, child );
1561                         mlt_deque_push_back_int( context->index_stack, context->index );
1562
1563                         name ++;
1564                         context->level += ltrim( &name ) + 1;
1565                         properties = child;
1566                 }
1567
1568                 // Value is quoted
1569                 if ( *ptr == '\"' )
1570                 {
1571                         ptr ++;
1572                         value = strdup( ptr );
1573                         if ( value && value[ strlen( value ) - 1 ] == '\"' )
1574                                 value[ strlen( value ) - 1 ] = 0;
1575                 }
1576
1577                 // Value is folded or unfolded block
1578                 else if ( *ptr == '|' || *ptr == '>' )
1579                 {
1580                         context->block = *ptr;
1581                         context->block_name = strdup( name );
1582                         context->block_indent = 0;
1583                         value = strdup( "" );
1584                 }
1585
1586                 // Bare value
1587                 else
1588                 {
1589                         value = strdup( ptr );
1590                 }
1591         }
1592
1593         // A list of scalars
1594         else if ( name[0] == '-' )
1595         {
1596                 // Reset block processing
1597                 if ( context->block_name )
1598                 {
1599                         free( context->block_name );
1600                         context->block_name = NULL;
1601                         context->block = 0;
1602                 }
1603
1604                 char key[20];
1605
1606                 snprintf( key, sizeof(key), "%d", context->index++ );
1607                 ptr = name + 1;
1608
1609                 // Trim comment
1610                 char *comment = strchr( ptr, '#' );
1611                 if ( comment )
1612                         *comment = 0;
1613
1614                 // Trim leading and trailing spaces from bare value
1615                 ltrim( &ptr );
1616                 rtrim( ptr );
1617
1618                 // Value is quoted
1619                 if ( *ptr == '\"' )
1620                 {
1621                         ptr ++;
1622                         value = strdup( ptr );
1623                         if ( value && value[ strlen( value ) - 1 ] == '\"' )
1624                                 value[ strlen( value ) - 1 ] = 0;
1625                 }
1626
1627                 // Value is folded or unfolded block
1628                 else if ( *ptr == '|' || *ptr == '>' )
1629                 {
1630                         context->block = *ptr;
1631                         context->block_name = strdup( key );
1632                         context->block_indent = 0;
1633                         value = strdup( "" );
1634                 }
1635
1636                 // Bare value
1637                 else
1638                 {
1639                         value = strdup( ptr );
1640                 }
1641
1642                 free( name_ );
1643                 name = name_ = strdup( key );
1644         }
1645
1646         // Non-folded block
1647         else if ( context->block == '|' )
1648         {
1649                 if ( context->block_indent == 0 )
1650                         context->block_indent = indent;
1651                 if ( indent > context->block_indent )
1652                         name = &name_[ context->block_indent ];
1653                 rtrim( name );
1654                 char *old_value = mlt_properties_get( properties, context->block_name );
1655                 value = calloc( 1, strlen( old_value ) + strlen( name ) + 2 );
1656                 strcpy( value, old_value );
1657                 if ( strcmp( old_value, "" ) )
1658                         strcat( value, "\n" );
1659                 strcat( value, name );
1660                 name = context->block_name;
1661         }
1662
1663         // Folded block
1664         else if ( context->block == '>' )
1665         {
1666                 ltrim( &name );
1667                 rtrim( name );
1668                 char *old_value = mlt_properties_get( properties, context->block_name );
1669
1670                 // Blank line (prepended with spaces) is new line
1671                 if ( strcmp( name, "" ) == 0 )
1672                 {
1673                         value = calloc( 1, strlen( old_value ) + 2 );
1674                         strcat( value, old_value );
1675                         strcat( value, "\n" );
1676                 }
1677                 // Concatenate with space
1678                 else
1679                 {
1680                         value = calloc( 1, strlen( old_value ) + strlen( name ) + 2 );
1681                         strcat( value, old_value );
1682                         if ( strcmp( old_value, "" ) && old_value[ strlen( old_value ) - 1 ] != '\n' )
1683                                 strcat( value, " " );
1684                         strcat( value, name );
1685                 }
1686                 name = context->block_name;
1687         }
1688
1689         else
1690         {
1691                 value = strdup( "" );
1692         }
1693
1694         error = mlt_properties_set( properties, name, value );
1695
1696         if ( !strcmp( name, "LC_NUMERIC" ) )
1697                 mlt_properties_set_lcnumeric( properties, value );
1698
1699         free( name_ );
1700         free( value );
1701
1702         return error;
1703 }
1704
1705 /** Parse a YAML Tiny file by name.
1706  *
1707  * \public \memberof mlt_properties_s
1708  * \param filename the name of a text file containing YAML Tiny
1709  * \return a new properties list
1710  */
1711
1712 mlt_properties mlt_properties_parse_yaml( const char *filename )
1713 {
1714         // Construct a standalone properties object
1715         mlt_properties self = mlt_properties_new( );
1716
1717         if ( self )
1718         {
1719                 // Open the file
1720                 FILE *file = fopen( filename, "r" );
1721
1722                 // Load contents of file
1723                 if ( file )
1724                 {
1725                         // Temp string
1726                         char temp[ 1024 ];
1727                         char *ptemp = &temp[ 0 ];
1728
1729                         // Default to LC_NUMERIC = C
1730                         mlt_properties_set_lcnumeric( self, "C" );
1731
1732                         // Parser context
1733                         yaml_parser context = calloc( 1, sizeof( struct yaml_parser_context ) );
1734                         context->stack = mlt_deque_init();
1735                         context->index_stack = mlt_deque_init();
1736                         mlt_deque_push_back( context->stack, self );
1737                         mlt_deque_push_back_int( context->index_stack, 0 );
1738
1739                         // Read each string from the file
1740                         while( fgets( temp, 1024, file ) )
1741                         {
1742                                 // Check for end-of-stream
1743                                 if ( strncmp( ptemp, "...", 3 ) == 0 )
1744                                         break;
1745
1746                                 // Chomp the string
1747                                 temp[ strlen( temp ) - 1 ] = '\0';
1748
1749                                 // Skip blank lines, comment lines, and document separator
1750                                 if ( strcmp( ptemp, "" ) && ptemp[ 0 ] != '#' && strncmp( ptemp, "---", 3 )
1751                                      && strncmp( ptemp, "%YAML", 5 ) && strncmp( ptemp, "% YAML", 6 ) )
1752                                         parse_yaml( context, temp );
1753                         }
1754
1755                         // Close the file
1756                         fclose( file );
1757                         mlt_deque_close( context->stack );
1758                         mlt_deque_close( context->index_stack );
1759                         if ( context->block_name )
1760                                 free( context->block_name );
1761                         free( context );
1762                 }
1763         }
1764
1765         // Return the pointer
1766         return self;
1767 }
1768
1769 /*
1770  * YAML Tiny Serializer
1771  */
1772
1773 /** How many bytes to grow at a time */
1774 #define STRBUF_GROWTH (1024)
1775
1776 /** \brief Private to mlt_properties_s, a self-growing buffer for building strings
1777  * \private
1778  */
1779
1780 struct strbuf_s
1781 {
1782         size_t size;
1783         char *string;
1784 };
1785
1786 typedef struct strbuf_s *strbuf;
1787
1788 /** Create a new string buffer
1789  *
1790  * \private \memberof strbuf_s
1791  * \return a new string buffer
1792  */
1793
1794 static strbuf strbuf_new( )
1795 {
1796         strbuf buffer = calloc( 1, sizeof( struct strbuf_s ) );
1797         buffer->size = STRBUF_GROWTH;
1798         buffer->string = calloc( 1, buffer->size );
1799         return buffer;
1800 }
1801
1802 /** Destroy a string buffer
1803  *
1804  * \private \memberof strbuf_s
1805  * \param buffer the string buffer to close
1806  */
1807
1808 static void strbuf_close( strbuf buffer )
1809 {
1810         // We do not free buffer->string; strbuf user must save that pointer
1811         // and free it.
1812         if ( buffer )
1813                 free( buffer );
1814 }
1815
1816 /** Format a string into a string buffer
1817  *
1818  * A variable number of arguments follows the format string - one for each
1819  * format specifier.
1820  * \private \memberof strbuf_s
1821  * \param buffer the string buffer to write into
1822  * \param format a string that contains text and formatting instructions
1823  * \return the formatted string
1824  */
1825
1826 static char *strbuf_printf( strbuf buffer, const char *format, ... )
1827 {
1828         while ( buffer->string )
1829         {
1830                 va_list ap;
1831                 va_start( ap, format );
1832                 size_t len = strlen( buffer->string );
1833                 size_t remain = buffer->size - len - 1;
1834                 int need = vsnprintf( buffer->string + len, remain, format, ap );
1835                 va_end( ap );
1836                 if ( need > -1 && need < remain )
1837                         break;
1838                 buffer->string[ len ] = 0;
1839                 buffer->size += need + STRBUF_GROWTH;
1840                 buffer->string = realloc( buffer->string, buffer->size );
1841         }
1842         return buffer->string;
1843 }
1844
1845 /** Indent a line of YAML Tiny.
1846  *
1847  * \private \memberof strbuf_s
1848  * \param output a string buffer
1849  * \param indent the number of spaces to indent
1850  */
1851
1852 static inline void indent_yaml( strbuf output, int indent )
1853 {
1854         int j;
1855         for ( j = 0; j < indent; j++ )
1856                 strbuf_printf( output, " " );
1857 }
1858
1859 static void strbuf_escape( strbuf output, const char *value, char c )
1860 {
1861         char *v = strdup( value );
1862         char *s = v;
1863         char *found = strchr( s, c );
1864
1865         while ( found )
1866         {
1867                 *found = '\0';
1868                 strbuf_printf( output, "%s\\%c", s, c );
1869                 s = found + 1;
1870                 found = strchr( s, c );
1871         }
1872         strbuf_printf( output, "%s", s );
1873         free( v );
1874 }
1875
1876 /** Convert a line string into a YAML block literal.
1877  *
1878  * \private \memberof strbuf_s
1879  * \param output a string buffer
1880  * \param value the string to format as a block literal
1881  * \param indent the number of spaces to indent
1882  */
1883
1884 static void output_yaml_block_literal( strbuf output, const char *value, int indent )
1885 {
1886         char *v = strdup( value );
1887         char *sol = v;
1888         char *eol = strchr( sol, '\n' );
1889
1890         while ( eol )
1891         {
1892                 indent_yaml( output, indent );
1893                 *eol = '\0';
1894                 strbuf_printf( output, "%s\n", sol );
1895                 sol = eol + 1;
1896                 eol = strchr( sol, '\n' );
1897         }
1898         indent_yaml( output, indent );
1899         strbuf_printf( output, "%s\n", sol );
1900         free( v );
1901 }
1902
1903 /** Recursively serialize a properties list into a string buffer as YAML Tiny.
1904  *
1905  * \private \memberof mlt_properties_s
1906  * \param self a properties list
1907  * \param output a string buffer to hold the serialized YAML Tiny
1908  * \param indent the number of spaces to indent (for recursion, initialize to 0)
1909  * \param is_parent_sequence Is this properties list really just a sequence (for recursion, initialize to 0)?
1910  */
1911
1912 static void serialise_yaml( mlt_properties self, strbuf output, int indent, int is_parent_sequence )
1913 {
1914         property_list *list = self->local;
1915         int i = 0;
1916
1917         for ( i = 0; i < list->count; i ++ )
1918         {
1919                 // This implementation assumes that all data elements are property lists.
1920                 // Unfortunately, we do not have run time type identification.
1921                 mlt_properties child = mlt_property_get_data( list->value[ i ], NULL );
1922
1923                 if ( mlt_properties_is_sequence( self ) )
1924                 {
1925                         // Ignore hidden/non-serialisable items
1926                         if ( list->name[ i ][ 0 ] != '_' )
1927                         {
1928                                 // Indicate a sequence item
1929                                 indent_yaml( output, indent );
1930                                 strbuf_printf( output, "- " );
1931
1932                                 // If the value can be represented as a string
1933                                 const char *value = mlt_properties_get( self, list->name[ i ] );
1934                                 if ( value && strcmp( value, "" ) )
1935                                 {
1936                                         // Determine if this is an unfolded block literal
1937                                         if ( strchr( value, '\n' ) )
1938                                         {
1939                                                 strbuf_printf( output, "|\n" );
1940                                                 output_yaml_block_literal( output, value, indent + strlen( list->name[ i ] ) + strlen( "|" ) );
1941                                         }
1942                                         else if ( strchr( value, ':' ) || strchr( value, '[' ) )
1943                                         {
1944                                                 strbuf_printf( output, "\"" );
1945                                                 strbuf_escape( output, value, '"' );
1946                                                 strbuf_printf( output, "\"\n", value );
1947                                         }
1948                                         else
1949                                         {
1950                                                 strbuf_printf( output, "%s\n", value );
1951                                         }
1952                                 }
1953                         }
1954                         // Recurse on child
1955                         if ( child )
1956                                 serialise_yaml( child, output, indent + 2, 1 );
1957                 }
1958                 else
1959                 {
1960                         // Assume this is a normal map-oriented properties list
1961                         const char *value = mlt_properties_get( self, list->name[ i ] );
1962
1963                         // Ignore hidden/non-serialisable items
1964                         // If the value can be represented as a string
1965                         if ( list->name[ i ][ 0 ] != '_' && value && strcmp( value, "" ) )
1966                         {
1967                                 if ( is_parent_sequence == 0 )
1968                                         indent_yaml( output, indent );
1969                                 else
1970                                         is_parent_sequence = 0;
1971
1972                                 // Determine if this is an unfolded block literal
1973                                 if ( strchr( value, '\n' ) )
1974                                 {
1975                                         strbuf_printf( output, "%s: |\n", list->name[ i ] );
1976                                         output_yaml_block_literal( output, value, indent + strlen( list->name[ i ] ) + strlen( ": " ) );
1977                                 }
1978                                 else if ( strchr( value, ':' ) || strchr( value, '[' ) )
1979                                 {
1980                                         strbuf_printf( output, "%s: \"", list->name[ i ] );
1981                                         strbuf_escape( output, value, '"' );
1982                                         strbuf_printf( output, "\"\n" );
1983                                 }
1984                                 else
1985                                 {
1986                                         strbuf_printf( output, "%s: %s\n", list->name[ i ], value );
1987                                 }
1988                         }
1989
1990                         // Output a child as a map item
1991                         if ( child )
1992                         {
1993                                 indent_yaml( output, indent );
1994                                 strbuf_printf( output, "%s:\n", list->name[ i ] );
1995
1996                                 // Recurse on child
1997                                 serialise_yaml( child, output, indent + 2, 0 );
1998                         }
1999                 }
2000         }
2001 }
2002
2003 /** Serialize a properties list as a string of YAML Tiny.
2004  *
2005  * The caller MUST free the returned string!
2006  * This operates on properties containing properties as a hierarchical data
2007  * structure.
2008  * \public \memberof mlt_properties_s
2009  * \param self a properties list
2010  * \return a string containing YAML Tiny that represents the properties list
2011  */
2012
2013 char *mlt_properties_serialise_yaml( mlt_properties self )
2014 {
2015         if ( !self ) return NULL;
2016         const char *lc_numeric = mlt_properties_get_lcnumeric( self );
2017         strbuf b = strbuf_new();
2018         strbuf_printf( b, "---\n" );
2019         mlt_properties_set_lcnumeric( self, "C" );
2020         serialise_yaml( self, b, 0, 0 );
2021         mlt_properties_set_lcnumeric( self, lc_numeric );
2022         strbuf_printf( b, "...\n" );
2023         char *ret = b->string;
2024         strbuf_close( b );
2025         return ret;
2026 }
2027
2028 /** Protect a properties list against concurrent access.
2029  *
2030  * \public \memberof mlt_properties_s
2031  * \param self a properties list
2032  */
2033
2034 void mlt_properties_lock( mlt_properties self )
2035 {
2036         if ( self )
2037                 pthread_mutex_lock( &( ( property_list* )( self->local ) )->mutex );
2038 }
2039
2040 /** End protecting a properties list against concurrent access.
2041  *
2042  * \public \memberof mlt_properties_s
2043  * \param self a properties list
2044  */
2045
2046 void mlt_properties_unlock( mlt_properties self )
2047 {
2048         if ( self )
2049                 pthread_mutex_unlock( &( ( property_list* )( self->local ) )->mutex );
2050 }
2051
2052 /** Get a time string associated to the name.
2053  *
2054  * Do not free the returned string. It's lifetime is controlled by the property.
2055  * \public \memberof mlt_properties_s
2056  * \param self a properties list
2057  * \param name the property to get
2058  * \param format the time format that you want
2059  * \return the property's time value or NULL if \p name does not exist or there is no profile
2060  */
2061
2062 char *mlt_properties_get_time( mlt_properties self, const char* name, mlt_time_format format )
2063 {
2064         mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
2065         if ( profile )
2066         {
2067                 double fps = mlt_profile_fps( profile );
2068                 mlt_property value = mlt_properties_find( self, name );
2069                 property_list *list = self->local;
2070                 return value == NULL ? NULL : mlt_property_get_time( value, format, fps, list->locale );
2071         }
2072         return NULL;
2073 }
2074
2075 /** Convert a numeric property to a tuple of color components.
2076  *
2077  * If the property's string is red, green, blue, white, or black, then it
2078  * is converted to the corresponding opaque color tuple. Otherwise, the property
2079  * is fetched as an integer and then converted.
2080  * \public \memberof mlt_properties_s
2081  * \param self a properties list
2082  * \param name the property to get
2083  * \return a color structure
2084  */
2085
2086 mlt_color mlt_properties_get_color( mlt_properties self, const char* name )
2087 {
2088         mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
2089         double fps = mlt_profile_fps( profile );
2090         property_list *list = self->local;
2091         mlt_property value = mlt_properties_find( self, name );
2092         mlt_color result = { 0xff, 0xff, 0xff, 0xff };
2093         if ( value )
2094         {
2095                 const char *color = mlt_property_get_string_l( value, list->locale );
2096                 unsigned int color_int = mlt_property_get_int( value, fps, list->locale );
2097
2098                 if ( !strcmp( color, "red" ) )
2099                 {
2100                         result.r = 0xff;
2101                         result.g = 0x00;
2102                         result.b = 0x00;
2103                 }
2104                 else if ( !strcmp( color, "green" ) )
2105                 {
2106                         result.r = 0x00;
2107                         result.g = 0xff;
2108                         result.b = 0x00;
2109                 }
2110                 else if ( !strcmp( color, "blue" ) )
2111                 {
2112                         result.r = 0x00;
2113                         result.g = 0x00;
2114                         result.b = 0xff;
2115                 }
2116                 else if ( !strcmp( color, "black" ) )
2117                 {
2118                         result.r = 0x00;
2119                         result.g = 0x00;
2120                         result.b = 0x00;
2121                 }
2122                 else if ( strcmp( color, "white" ) )
2123                 {
2124                         result.r = ( color_int >> 24 ) & 0xff;
2125                         result.g = ( color_int >> 16 ) & 0xff;
2126                         result.b = ( color_int >> 8 ) & 0xff;
2127                         result.a = ( color_int ) & 0xff;
2128                 }
2129         }
2130         return result;
2131 }
2132
2133 /** Set a property to an integer value by color.
2134  *
2135  * \public \memberof mlt_properties_s
2136  * \param self a properties list
2137  * \param name the property to set
2138  * \param color the color
2139  * \return true if error
2140  */
2141
2142 int mlt_properties_set_color( mlt_properties self, const char *name, mlt_color color )
2143 {
2144         int error = 1;
2145
2146         if ( !self || !name ) return error;
2147
2148         // Fetch the property to work with
2149         mlt_property property = mlt_properties_fetch( self, name );
2150
2151         // Set it if not NULL
2152         if ( property != NULL )
2153         {
2154                 uint32_t value = ( color.r << 24 ) | ( color.g << 16 ) | ( color.b << 8 ) | color.a;
2155                 error = mlt_property_set_int( property, value );
2156                 mlt_properties_do_mirror( self, name );
2157         }
2158
2159         mlt_events_fire( self, "property-changed", name, NULL );
2160
2161         return error;
2162 }
2163
2164 /** Get a string value by name at a frame position.
2165  *
2166  * Do not free the returned string. It's lifetime is controlled by the property
2167  * and this properties object.
2168  * \public \memberof mlt_properties_s
2169  * \param self a properties list
2170  * \param name the property to get
2171  * \param position the frame number
2172  * \param length the maximum number of frames when interpreting negative keyframe times,
2173  *  <=0 if you don't care or need that
2174  * \return the property's string value or NULL if it does not exist
2175  */
2176
2177 char* mlt_properties_anim_get( mlt_properties self, const char *name, int position, int length )
2178 {
2179         mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
2180         double fps = mlt_profile_fps( profile );
2181         mlt_property value = mlt_properties_find( self, name );
2182         property_list *list = self->local;
2183         return value == NULL ? NULL : mlt_property_anim_get_string( value, fps, list->locale, position, length );
2184 }
2185
2186 /** Set a property to a string at a frame position.
2187  *
2188  * The event "property-changed" is fired after the property has been set.
2189  *
2190  * This makes a copy of the string value you supply.
2191  * \public \memberof mlt_properties_s
2192  * \param self a properties list
2193  * \param name the property to set
2194  * \param value the property's new value
2195  * \param position the frame number
2196  * \param length the maximum number of frames when interpreting negative keyframe times,
2197  *  <=0 if you don't care or need that
2198  * \return true if error
2199  */
2200
2201 int mlt_properties_anim_set( mlt_properties self, const char *name, const char *value, int position, int length )
2202 {
2203         int error = 1;
2204
2205         if ( !self || !name ) return error;
2206
2207         // Fetch the property to work with
2208         mlt_property property = mlt_properties_fetch( self, name );
2209
2210         // Set it if not NULL
2211         if ( property )
2212         {
2213                 mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
2214                 double fps = mlt_profile_fps( profile );
2215                 property_list *list = self->local;
2216                 error = mlt_property_anim_set_string( property, value,
2217                         fps, list->locale, position, length );
2218                 mlt_properties_do_mirror( self, name );
2219         }
2220
2221         mlt_events_fire( self, "property-changed", name, NULL );
2222
2223         return error;
2224 }
2225
2226 /** Get an integer associated to the name at a frame position.
2227  *
2228  * \public \memberof mlt_properties_s
2229  * \param self a properties list
2230  * \param name the property to get
2231  * \param position the frame number
2232  * \param length the maximum number of frames when interpreting negative keyframe times,
2233  *  <=0 if you don't care or need that
2234  * \return the integer value, 0 if not found (which may also be a legitimate value)
2235  */
2236
2237 int mlt_properties_anim_get_int( mlt_properties self, const char *name, int position, int length )
2238 {
2239         mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
2240         double fps = mlt_profile_fps( profile );
2241         property_list *list = self->local;
2242         mlt_property value = mlt_properties_find( self, name );
2243         return value == NULL ? 0 : mlt_property_anim_get_int( value, fps, list->locale, position, length );
2244 }
2245
2246 /** Set a property to an integer value at a frame position.
2247  *
2248  * \public \memberof mlt_properties_s
2249  * \param self a properties list
2250  * \param name the property to set
2251  * \param value the integer
2252  * \param position the frame number
2253  * \param length the maximum number of frames when interpreting negative keyframe times,
2254  *  <=0 if you don't care or need that
2255  * \param keyframe_type the interpolation method for this keyframe
2256  * \return true if error
2257  */
2258
2259 int mlt_properties_anim_set_int( mlt_properties self, const char *name, int value,
2260         int position, int length, mlt_keyframe_type keyframe_type )
2261 {
2262         int error = 1;
2263
2264         if ( !self || !name ) return error;
2265
2266         // Fetch the property to work with
2267         mlt_property property = mlt_properties_fetch( self, name );
2268
2269         // Set it if not NULL
2270         if ( property != NULL )
2271         {
2272                 mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
2273                 double fps = mlt_profile_fps( profile );
2274                 property_list *list = self->local;
2275                 error = mlt_property_anim_set_int( property, value, fps, list->locale, position, length, keyframe_type );
2276                 mlt_properties_do_mirror( self, name );
2277         }
2278
2279         mlt_events_fire( self, "property-changed", name, NULL );
2280
2281         return error;
2282 }
2283
2284 /** Get a real number associated to the name at a frame position.
2285  *
2286  * \public \memberof mlt_properties_s
2287  * \param self a properties list
2288  * \param name the property to get
2289  * \param position the frame number
2290  * \param length the maximum number of frames when interpreting negative keyframe times,
2291  *  <=0 if you don't care or need that
2292  * \return the real number, 0 if not found (which may also be a legitimate value)
2293  */
2294
2295 double mlt_properties_anim_get_double( mlt_properties self, const char *name, int position, int length )
2296 {
2297         mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
2298         double fps = mlt_profile_fps( profile );
2299         property_list *list = self->local;
2300         mlt_property value = mlt_properties_find( self, name );
2301         return value == NULL ? 0.0 : mlt_property_anim_get_double( value, fps, list->locale, position, length );
2302 }
2303
2304 /** Set a property to a real number at a frame position.
2305  *
2306  * \public \memberof mlt_properties_s
2307  * \param self a properties list
2308  * \param name the property to set
2309  * \param value the real number
2310  * \param position the frame number
2311  * \param length the maximum number of frames when interpreting negative keyframe times,
2312  *  <=0 if you don't care or need that
2313  * \param keyframe_type the interpolation method for this keyframe
2314  * \return true if error
2315  */
2316
2317 int mlt_properties_anim_set_double( mlt_properties self, const char *name, double value,
2318         int position, int length, mlt_keyframe_type keyframe_type )
2319 {
2320         int error = 1;
2321
2322         if ( !self || !name ) return error;
2323
2324         // Fetch the property to work with
2325         mlt_property property = mlt_properties_fetch( self, name );
2326
2327         // Set it if not NULL
2328         if ( property != NULL )
2329         {
2330                 mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
2331                 double fps = mlt_profile_fps( profile );
2332                 property_list *list = self->local;
2333                 error = mlt_property_anim_set_double( property, value, fps, list->locale, position, length, keyframe_type );
2334                 mlt_properties_do_mirror( self, name );
2335         }
2336
2337         mlt_events_fire( self, "property-changed", name, NULL );
2338
2339         return error;
2340 }
2341
2342 /** Get the animation associated to the name.
2343  *
2344  * \public \memberof mlt_properties_s
2345  * \param self a properties list
2346  * \param name the property to get
2347  * \return The animation object or NULL if the property has no animation
2348  */
2349
2350 mlt_animation mlt_properties_get_animation( mlt_properties self, const char *name )
2351 {
2352         mlt_property value = mlt_properties_find( self, name );
2353         return value == NULL ? NULL : mlt_property_get_animation( value );
2354 }
2355
2356 /** Set a property to a rectangle value.
2357  *
2358  * \public \memberof mlt_properties_s
2359  * \param self a properties list
2360  * \param name the property to set
2361  * \param value the rectangle
2362  * \return true if error
2363  */
2364
2365 extern int mlt_properties_set_rect( mlt_properties self, const char *name, mlt_rect value )
2366 {
2367         int error = 1;
2368
2369         if ( !self || !name ) return error;
2370
2371         // Fetch the property to work with
2372         mlt_property property = mlt_properties_fetch( self, name );
2373
2374         // Set it if not NULL
2375         if ( property != NULL )
2376         {
2377                 error = mlt_property_set_rect( property, value );
2378                 mlt_properties_do_mirror( self, name );
2379         }
2380
2381         mlt_events_fire( self, "property-changed", name, NULL );
2382
2383         return error;
2384 }
2385
2386 /** Get a rectangle associated to the name.
2387  *
2388  * \public \memberof mlt_properties_s
2389  * \param self a properties list
2390  * \param name the property to get
2391  * \return the rectangle value, the rectangle fields will be DBL_MIN if not found
2392  */
2393
2394 extern mlt_rect mlt_properties_get_rect( mlt_properties self, const char* name )
2395 {
2396         property_list *list = self->local;
2397         mlt_property value = mlt_properties_find( self, name );
2398         mlt_rect rect = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN };
2399         return value == NULL ? rect : mlt_property_get_rect( value, list->locale );
2400 }
2401
2402 /** Set a property to a rectangle value at a frame position.
2403  *
2404  * \public \memberof mlt_properties_s
2405  * \param self a properties list
2406  * \param name the property to set
2407  * \param value the rectangle
2408  * \param position the frame number
2409  * \param length the maximum number of frames when interpreting negative keyframe times,
2410  *  <=0 if you don't care or need that
2411  * \param keyframe_type the interpolation method for this keyframe
2412  * \return true if error
2413  */
2414
2415 extern int mlt_properties_anim_set_rect( mlt_properties self, const char *name, mlt_rect value,
2416         int position, int length , mlt_keyframe_type keyframe_type )
2417 {
2418         int error = 1;
2419
2420         if ( !self || !name ) return error;
2421
2422         // Fetch the property to work with
2423         mlt_property property = mlt_properties_fetch( self, name );
2424
2425         // Set it if not NULL
2426         if ( property != NULL )
2427         {
2428                 mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
2429                 double fps = mlt_profile_fps( profile );
2430                 property_list *list = self->local;
2431                 error = mlt_property_anim_set_rect( property, value, fps, list->locale, position, length, keyframe_type );
2432                 mlt_properties_do_mirror( self, name );
2433         }
2434
2435         mlt_events_fire( self, "property-changed", name, NULL );
2436
2437         return error;
2438 }
2439
2440 /** Get a rectangle associated to the name at a frame position.
2441  *
2442  * \public \memberof mlt_properties_s
2443  * \param self a properties list
2444  * \param name the property to get
2445  * \param position the frame number
2446  * \param length the maximum number of frames when interpreting negative keyframe times,
2447  *  <=0 if you don't care or need that
2448  * \return the rectangle value, the rectangle fields will be DBL_MIN if not found
2449  */
2450
2451 extern mlt_rect mlt_properties_anim_get_rect( mlt_properties self, const char *name, int position, int length )
2452 {
2453         mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
2454         double fps = mlt_profile_fps( profile );
2455         property_list *list = self->local;
2456         mlt_property value = mlt_properties_find( self, name );
2457         mlt_rect rect = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN };
2458         return value == NULL ? rect : mlt_property_anim_get_rect( value, fps, list->locale, position, length );
2459 }