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