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