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