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