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