]> git.sesse.net Git - mlt/blob - src/framework/mlt_property.c
Implement LC_NUMERIC handling for non-glibc and non-Darwin OS.
[mlt] / src / framework / mlt_property.c
1 /**
2  * \file mlt_property.c
3  * \brief Property class definition
4  * \see mlt_property_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_property.h"
31 #include "mlt_animation.h"
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <locale.h>
37 #include <pthread.h>
38 #include <float.h>
39 #include <math.h>
40
41
42 /** Bit pattern used internally to indicated representations available.
43 */
44
45 typedef enum
46 {
47         mlt_prop_none = 0,    //!< not set
48         mlt_prop_int = 1,     //!< set as an integer
49         mlt_prop_string = 2,  //!< set as string or already converted to string
50         mlt_prop_position = 4,//!< set as a position
51         mlt_prop_double = 8,  //!< set as a floating point
52         mlt_prop_data = 16,   //!< set as opaque binary
53         mlt_prop_int64 = 32,  //!< set as a 64-bit integer
54         mlt_prop_rect = 64    //!< set as a mlt_rect
55 }
56 mlt_property_type;
57
58 /** \brief Property class
59  *
60  * A property is like a variant or dynamic type. They are used for many things
61  * in MLT, but in particular they are the parameter mechanism for the plugins.
62  */
63
64 struct mlt_property_s
65 {
66         /// Stores a bit pattern of types available for this property
67         mlt_property_type types;
68
69         /// Atomic type handling
70         int prop_int;
71         mlt_position prop_position;
72         double prop_double;
73         int64_t prop_int64;
74
75         /// String handling
76         char *prop_string;
77
78         /// Generic type handling
79         void *data;
80         int length;
81         mlt_destructor destructor;
82         mlt_serialiser serialiser;
83
84         pthread_mutex_t mutex;
85         mlt_animation animation;
86 };
87
88 /** Construct a property and initialize it
89  * \public \memberof mlt_property_s
90  */
91
92 mlt_property mlt_property_init( )
93 {
94         mlt_property self = calloc( 1, sizeof( *self ) );
95         if ( self )
96                 pthread_mutex_init( &self->mutex, NULL );
97         return self;
98 }
99
100 /** Clear (0/null) a property.
101  *
102  * Frees up any associated resources in the process.
103  * \private \memberof mlt_property_s
104  * \param self a property
105  */
106
107 static inline void mlt_property_clear( mlt_property self )
108 {
109         // Special case data handling
110         if ( self->types & mlt_prop_data && self->destructor != NULL )
111                 self->destructor( self->data );
112
113         // Special case string handling
114         if ( self->types & mlt_prop_string )
115                 free( self->prop_string );
116
117         if ( self->animation )
118                 mlt_animation_close( self->animation );
119
120         // Wipe stuff
121         self->types = 0;
122         self->prop_int = 0;
123         self->prop_position = 0;
124         self->prop_double = 0;
125         self->prop_int64 = 0;
126         self->prop_string = NULL;
127         self->data = NULL;
128         self->length = 0;
129         self->destructor = NULL;
130         self->serialiser = NULL;
131         self->animation = NULL;
132 }
133
134 /** Set the property to an integer value.
135  *
136  * \public \memberof mlt_property_s
137  * \param self a property
138  * \param value an integer
139  * \return false
140  */
141
142 int mlt_property_set_int( mlt_property self, int value )
143 {
144         pthread_mutex_lock( &self->mutex );
145         mlt_property_clear( self );
146         self->types = mlt_prop_int;
147         self->prop_int = value;
148         pthread_mutex_unlock( &self->mutex );
149         return 0;
150 }
151
152 /** Set the property to a floating point value.
153  *
154  * \public \memberof mlt_property_s
155  * \param self a property
156  * \param value a double precision floating point value
157  * \return false
158  */
159
160 int mlt_property_set_double( mlt_property self, double value )
161 {
162         pthread_mutex_lock( &self->mutex );
163         mlt_property_clear( self );
164         self->types = mlt_prop_double;
165         self->prop_double = value;
166         pthread_mutex_unlock( &self->mutex );
167         return 0;
168 }
169
170 /** Set the property to a position value.
171  *
172  * Position is a relative time value in frame units.
173  * \public \memberof mlt_property_s
174  * \param self a property
175  * \param value a position value
176  * \return false
177  */
178
179 int mlt_property_set_position( mlt_property self, mlt_position value )
180 {
181         pthread_mutex_lock( &self->mutex );
182         mlt_property_clear( self );
183         self->types = mlt_prop_position;
184         self->prop_position = value;
185         pthread_mutex_unlock( &self->mutex );
186         return 0;
187 }
188
189 /** Set the property to a string value.
190  *
191  * This makes a copy of the string you supply so you do not need to track
192  * a new reference to it.
193  * \public \memberof mlt_property_s
194  * \param self a property
195  * \param value the string to copy to the property
196  * \return true if it failed
197  */
198
199 int mlt_property_set_string( mlt_property self, const char *value )
200 {
201         pthread_mutex_lock( &self->mutex );
202         if ( value != self->prop_string )
203         {
204                 mlt_property_clear( self );
205                 self->types = mlt_prop_string;
206                 if ( value != NULL )
207                         self->prop_string = strdup( value );
208         }
209         else
210         {
211                 self->types = mlt_prop_string;
212         }
213         pthread_mutex_unlock( &self->mutex );
214         return self->prop_string == NULL;
215 }
216
217 /** Set the property to a 64-bit integer value.
218  *
219  * \public \memberof mlt_property_s
220  * \param self a property
221  * \param value a 64-bit integer
222  * \return false
223  */
224
225 int mlt_property_set_int64( mlt_property self, int64_t value )
226 {
227         pthread_mutex_lock( &self->mutex );
228         mlt_property_clear( self );
229         self->types = mlt_prop_int64;
230         self->prop_int64 = value;
231         pthread_mutex_unlock( &self->mutex );
232         return 0;
233 }
234
235 /** Set a property to an opaque binary value.
236  *
237  * This does not make a copy of the data. You can use a Properties object
238  * with its reference tracking and the destructor function to control
239  * the lifetime of the data. Otherwise, pass NULL for the destructor
240  * function and control the lifetime yourself.
241  * \public \memberof mlt_property_s
242  * \param self a property
243  * \param value an opaque pointer
244  * \param length the number of bytes pointed to by value (optional)
245  * \param destructor a function to use to destroy this binary data (optional, assuming you manage the resource)
246  * \param serialiser a function to use to convert this binary data to a string (optional)
247  * \return false
248  */
249
250 int mlt_property_set_data( mlt_property self, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser )
251 {
252         pthread_mutex_lock( &self->mutex );
253         if ( self->data == value )
254                 self->destructor = NULL;
255         mlt_property_clear( self );
256         self->types = mlt_prop_data;
257         self->data = value;
258         self->length = length;
259         self->destructor = destructor;
260         self->serialiser = serialiser;
261         pthread_mutex_unlock( &self->mutex );
262         return 0;
263 }
264
265 /** Parse a SMIL clock value.
266  *
267  * \private \memberof mlt_property_s
268  * \param self a property
269  * \param s the string to parse
270  * \param fps frames per second
271  * \param locale the locale to use for parsing a real number value
272  * \return position in frames
273  */
274
275 static int time_clock_to_frames( mlt_property self, const char *s, double fps, locale_t locale )
276 {
277         char *pos, *copy = strdup( s );
278         int hours = 0, minutes = 0;
279         double seconds;
280
281         s = copy;
282         pos = strrchr( s, ':' );
283
284 #if !defined(__GLIBC__) && !defined(__DARWIN__)
285         char *orig_localename = NULL;
286         if ( locale )
287         {
288                 // Protect damaging the global locale from a temporary locale on another thread.
289                 pthread_mutex_lock( &self->mutex );
290                 
291                 // Get the current locale
292                 orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
293                 
294                 // Set the new locale
295                 setlocale( LC_NUMERIC, locale );
296         }
297 #endif
298
299         if ( pos ) {
300 #if defined(__GLIBC__) || defined(__DARWIN__)
301                 if ( locale )
302                         seconds = strtod_l( pos + 1, NULL, locale );
303                 else
304 #endif
305                         seconds = strtod( pos + 1, NULL );
306                 *pos = 0;
307                 pos = strrchr( s, ':' );
308                 if ( pos ) {
309                         minutes = atoi( pos + 1 );
310                         *pos = 0;
311                         hours = atoi( s );
312                 }
313                 else {
314                         minutes = atoi( s );
315                 }
316         }
317         else {
318 #if defined(__GLIBC__) || defined(__DARWIN__)
319                 if ( locale )
320                         seconds = strtod_l( s, NULL, locale );
321                 else
322 #endif
323                         seconds = strtod( s, NULL );
324         }
325
326 #if !defined(__GLIBC__) && !defined(__DARWIN__)
327         if ( locale ) {
328                 // Restore the current locale
329                 setlocale( LC_NUMERIC, orig_localename );
330                 free( orig_localename );
331                 pthread_mutex_unlock( &self->mutex );
332         }
333 #endif
334
335         free( copy );
336
337         return lrint( fps * ( (hours * 3600) + (minutes * 60) + seconds ) );
338 }
339
340 /** Parse a SMPTE timecode string.
341  *
342  * \private \memberof mlt_property_s
343  * \param self a property
344  * \param s the string to parse
345  * \param fps frames per second
346  * \return position in frames
347  */
348
349 static int time_code_to_frames( mlt_property self, const char *s, double fps )
350 {
351         char *pos, *copy = strdup( s );
352         int hours = 0, minutes = 0, seconds = 0, frames;
353
354         s = copy;
355         pos = strrchr( s, ';' );
356         if ( !pos )
357                 pos = strrchr( s, ':' );
358         if ( pos ) {
359                 frames = atoi( pos + 1 );
360                 *pos = 0;
361                 pos = strrchr( s, ':' );
362                 if ( pos ) {
363                         seconds = atoi( pos + 1 );
364                         *pos = 0;
365                         pos = strrchr( s, ':' );
366                         if ( pos ) {
367                                 minutes = atoi( pos + 1 );
368                                 *pos = 0;
369                                 hours = atoi( s );
370                         }
371                         else {
372                                 minutes = atoi( s );
373                         }
374                 }
375                 else {
376                         seconds = atoi( s );
377                 }
378         }
379         else {
380                 frames = atoi( s );
381         }
382         free( copy );
383
384         return lrint( fps * ( (hours * 3600) + (minutes * 60) + seconds ) + frames );
385 }
386
387 /** Convert a string to an integer.
388  *
389  * The string must begin with '0x' to be interpreted as hexadecimal.
390  * Otherwise, it is interpreted as base 10.
391  *
392  * If the string begins with '#' it is interpreted as a hexadecimal color value
393  * in the form RRGGBB or AARRGGBB. Color values that begin with '0x' are
394  * always in the form RRGGBBAA where the alpha components are not optional.
395  * Applications and services should expect the binary color value in bytes to
396  * be in the following order: RGBA. This means they will have to cast the int
397  * to an unsigned int. This is especially important when they need to shift
398  * right to obtain RGB without alpha in order to make it do a logical instead
399  * of arithmetic shift.
400  *
401  * If the string contains a colon it is interpreted as a time value. If it also
402  * contains a period or comma character, the string is parsed as a clock value:
403  * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF.
404  * \private \memberof mlt_property_s
405  * \param self a property
406  * \param fps frames per second, used when converting from time value
407  * \param locale the locale to use when converting from time clock value
408  * \return the resultant integer
409  */
410 static int mlt_property_atoi( mlt_property self, double fps, locale_t locale )
411 {
412         const char *value = self->prop_string;
413         
414         // Parse a hex color value as #RRGGBB or #AARRGGBB.
415         if ( value[0] == '#' )
416         {
417                 unsigned int rgb = strtoul( value + 1, NULL, 16 );
418                 unsigned int alpha = ( strlen( value ) > 7 ) ? ( rgb >> 24 ) : 0xff;
419                 return ( rgb << 8 ) | alpha;
420         }
421         // Do hex and decimal explicitly to avoid decimal value with leading zeros
422         // interpreted as octal.
423         else if ( value[0] == '0' && value[1] == 'x' )
424         {
425                 return strtoul( value + 2, NULL, 16 );
426         }
427         else if ( fps > 0 && strchr( value, ':' ) )
428         {
429                 if ( strchr( value, '.' ) || strchr( value, ',' ) )
430                         return time_clock_to_frames( self, value, fps, locale );
431                 else
432                         return time_code_to_frames( self, value, fps );
433         }
434         else
435         {
436                 return strtol( value, NULL, 10 );
437         }
438 }
439
440 /** Get the property as an integer.
441  *
442  * \public \memberof mlt_property_s
443  * \param self a property
444  * \param fps frames per second, used when converting from time value
445  * \param locale the locale to use when converting from time clock value
446  * \return an integer value
447  */
448
449 int mlt_property_get_int( mlt_property self, double fps, locale_t locale )
450 {
451         if ( self->types & mlt_prop_int )
452                 return self->prop_int;
453         else if ( self->types & mlt_prop_double )
454                 return ( int )self->prop_double;
455         else if ( self->types & mlt_prop_position )
456                 return ( int )self->prop_position;
457         else if ( self->types & mlt_prop_int64 )
458                 return ( int )self->prop_int64;
459         else if ( self->types & mlt_prop_rect && self->data )
460                 return ( int ) ( (mlt_rect*) self->data )->x;
461         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
462                 return mlt_property_atoi( self, fps, locale );
463         return 0;
464 }
465
466 /** Convert a string to a floating point number.
467  *
468  * If the string contains a colon it is interpreted as a time value. If it also
469  * contains a period or comma character, the string is parsed as a clock value:
470  * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF.
471  * If the numeric string ends with '%' then the value is divided by 100 to convert
472  * it into a ratio.
473  * \private \memberof mlt_property_s
474  * \param self a property
475  * \param fps frames per second, used when converting from time value
476  * \param locale the locale to use when converting from time clock value
477  * \return the resultant real number
478  */
479 static double mlt_property_atof( mlt_property self, double fps, locale_t locale )
480 {
481         const char *value = self->prop_string;
482
483     if ( fps > 0 && strchr( value, ':' ) )
484         {
485                 if ( strchr( value, '.' ) || strchr( value, ',' ) )
486                         return time_clock_to_frames( self, value, fps, locale );
487                 else
488                         return time_code_to_frames( self, value, fps );
489         }
490         else
491         {
492                 char *end = NULL;
493                 double result;
494
495 #if defined(__GLIBC__) || defined(__DARWIN__)
496                 if ( locale )
497                         result = strtod_l( value, &end, locale );
498                 else
499 #else
500                 char *orig_localename = NULL;
501                 if ( locale ) {
502                         // Protect damaging the global locale from a temporary locale on another thread.
503                         pthread_mutex_lock( &self->mutex );
504                         
505                         // Get the current locale
506                         orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
507                         
508                         // Set the new locale
509                         setlocale( LC_NUMERIC, locale );
510                 }
511 #endif
512
513                         result = strtod( value, &end );
514                 if ( end && end[0] == '%' )
515                         result /= 100.0;
516
517 #if !defined(__GLIBC__) && !defined(__DARWIN__)
518                 if ( locale ) {
519                         // Restore the current locale
520                         setlocale( LC_NUMERIC, orig_localename );
521                         free( orig_localename );
522                         pthread_mutex_unlock( &self->mutex );
523                 }
524 #endif
525
526                 return result;
527         }
528 }
529
530 /** Get the property as a floating point.
531  *
532  * \public \memberof mlt_property_s
533  * \param self a property
534  * \param fps frames per second, used when converting from time value
535  * \param locale the locale to use for this conversion
536  * \return a floating point value
537  */
538
539 double mlt_property_get_double( mlt_property self, double fps, locale_t locale )
540 {
541         if ( self->types & mlt_prop_double )
542                 return self->prop_double;
543         else if ( self->types & mlt_prop_int )
544                 return ( double )self->prop_int;
545         else if ( self->types & mlt_prop_position )
546                 return ( double )self->prop_position;
547         else if ( self->types & mlt_prop_int64 )
548                 return ( double )self->prop_int64;
549         else if ( self->types & mlt_prop_rect && self->data )
550                 return ( (mlt_rect*) self->data )->x;
551         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
552                 return mlt_property_atof( self, fps, locale );
553         return 0;
554 }
555
556 /** Get the property as a position.
557  *
558  * A position is an offset time in terms of frame units.
559  * \public \memberof mlt_property_s
560  * \param self a property
561  * \param fps frames per second, used when converting from time value
562  * \param locale the locale to use when converting from time clock value
563  * \return the position in frames
564  */
565
566 mlt_position mlt_property_get_position( mlt_property self, double fps, locale_t locale )
567 {
568         if ( self->types & mlt_prop_position )
569                 return self->prop_position;
570         else if ( self->types & mlt_prop_int )
571                 return ( mlt_position )self->prop_int;
572         else if ( self->types & mlt_prop_double )
573                 return ( mlt_position )self->prop_double;
574         else if ( self->types & mlt_prop_int64 )
575                 return ( mlt_position )self->prop_int64;
576         else if ( self->types & mlt_prop_rect && self->data )
577                 return ( mlt_position ) ( (mlt_rect*) self->data )->x;
578         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
579                 return ( mlt_position )mlt_property_atoi( self, fps, locale );
580         return 0;
581 }
582
583 /** Convert a string to a 64-bit integer.
584  *
585  * If the string begins with '0x' it is interpreted as a hexadecimal value.
586  * \private \memberof mlt_property_s
587  * \param value a string
588  * \return a 64-bit integer
589  */
590
591 static inline int64_t mlt_property_atoll( const char *value )
592 {
593         if ( value == NULL )
594                 return 0;
595         else if ( value[0] == '0' && value[1] == 'x' )
596                 return strtoll( value + 2, NULL, 16 );
597         else
598                 return strtoll( value, NULL, 10 );
599 }
600
601 /** Get the property as a signed integer.
602  *
603  * \public \memberof mlt_property_s
604  * \param self a property
605  * \return a 64-bit integer
606  */
607
608 int64_t mlt_property_get_int64( mlt_property self )
609 {
610         if ( self->types & mlt_prop_int64 )
611                 return self->prop_int64;
612         else if ( self->types & mlt_prop_int )
613                 return ( int64_t )self->prop_int;
614         else if ( self->types & mlt_prop_double )
615                 return ( int64_t )self->prop_double;
616         else if ( self->types & mlt_prop_position )
617                 return ( int64_t )self->prop_position;
618         else if ( self->types & mlt_prop_rect && self->data )
619                 return ( int64_t ) ( (mlt_rect*) self->data )->x;
620         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
621                 return mlt_property_atoll( self->prop_string );
622         return 0;
623 }
624
625 /** Get the property as a string.
626  *
627  * The caller is not responsible for deallocating the returned string!
628  * The string is deallocated when the Property is closed.
629  * This tries its hardest to convert the property to string including using
630  * a serialization function for binary data, if supplied.
631  * \public \memberof mlt_property_s
632  * \param self a property
633  * \return a string representation of the property or NULL if failed
634  */
635
636 char *mlt_property_get_string( mlt_property self )
637 {
638         // Construct a string if need be
639         if ( ! ( self->types & mlt_prop_string ) )
640         {
641                 pthread_mutex_lock( &self->mutex );
642                 if ( self->types & mlt_prop_int )
643                 {
644                         self->types |= mlt_prop_string;
645                         self->prop_string = malloc( 32 );
646                         sprintf( self->prop_string, "%d", self->prop_int );
647                 }
648                 else if ( self->types & mlt_prop_double )
649                 {
650                         self->types |= mlt_prop_string;
651                         self->prop_string = malloc( 32 );
652                         sprintf( self->prop_string, "%g", self->prop_double );
653                 }
654                 else if ( self->types & mlt_prop_position )
655                 {
656                         self->types |= mlt_prop_string;
657                         self->prop_string = malloc( 32 );
658                         sprintf( self->prop_string, "%d", (int)self->prop_position );
659                 }
660                 else if ( self->types & mlt_prop_int64 )
661                 {
662                         self->types |= mlt_prop_string;
663                         self->prop_string = malloc( 32 );
664                         sprintf( self->prop_string, "%"PRId64, self->prop_int64 );
665                 }
666                 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
667                 {
668                         self->types |= mlt_prop_string;
669                         self->prop_string = self->serialiser( self->data, self->length );
670                 }
671                 pthread_mutex_unlock( &self->mutex );
672         }
673
674         // Return the string (may be NULL)
675         return self->prop_string;
676 }
677
678 /** Get the property as a string (with locale).
679  *
680  * The caller is not responsible for deallocating the returned string!
681  * The string is deallocated when the Property is closed.
682  * This tries its hardest to convert the property to string including using
683  * a serialization function for binary data, if supplied.
684  * \public \memberof mlt_property_s
685  * \param self a property
686  * \param locale the locale to use for this conversion
687  * \return a string representation of the property or NULL if failed
688  */
689
690 char *mlt_property_get_string_l( mlt_property self, locale_t locale )
691 {
692         // Optimization for no locale
693         if ( !locale )
694                 return mlt_property_get_string( self );
695
696         // Construct a string if need be
697         if ( ! ( self->types & mlt_prop_string ) )
698         {
699                 // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale.
700                 // Save the current locale
701 #if defined(__DARWIN__)
702                 const char *localename = querylocale( LC_NUMERIC, locale );
703 #elif defined(__GLIBC__)
704                 const char *localename = locale->__names[ LC_NUMERIC ];
705 #else
706                 const char *localename = locale;
707 #endif
708                 // Protect damaging the global locale from a temporary locale on another thread.
709                 pthread_mutex_lock( &self->mutex );
710
711                 // Get the current locale
712                 char *orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
713
714                 // Set the new locale
715                 setlocale( LC_NUMERIC, localename );
716
717                 if ( self->types & mlt_prop_int )
718                 {
719                         self->types |= mlt_prop_string;
720                         self->prop_string = malloc( 32 );
721                         sprintf( self->prop_string, "%d", self->prop_int );
722                 }
723                 else if ( self->types & mlt_prop_double )
724                 {
725                         self->types |= mlt_prop_string;
726                         self->prop_string = malloc( 32 );
727                         sprintf( self->prop_string, "%g", self->prop_double );
728                 }
729                 else if ( self->types & mlt_prop_position )
730                 {
731                         self->types |= mlt_prop_string;
732                         self->prop_string = malloc( 32 );
733                         sprintf( self->prop_string, "%d", (int)self->prop_position );
734                 }
735                 else if ( self->types & mlt_prop_int64 )
736                 {
737                         self->types |= mlt_prop_string;
738                         self->prop_string = malloc( 32 );
739                         sprintf( self->prop_string, "%"PRId64, self->prop_int64 );
740                 }
741                 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
742                 {
743                         self->types |= mlt_prop_string;
744                         self->prop_string = self->serialiser( self->data, self->length );
745                 }
746                 // Restore the current locale
747                 setlocale( LC_NUMERIC, orig_localename );
748                 free( orig_localename );
749                 pthread_mutex_unlock( &self->mutex );
750         }
751
752         // Return the string (may be NULL)
753         return self->prop_string;
754 }
755
756 /** Get the binary data from a property.
757  *
758  * This only works if you previously put binary data into the property.
759  * This does not return a copy of the data; it returns a pointer to it.
760  * If you supplied a destructor function when setting the binary data,
761  * the destructor is used when the Property is closed to free the memory.
762  * Therefore, only free the returned pointer if you did not supply a
763  * destructor function.
764  * \public \memberof mlt_property_s
765  * \param self a property
766  * \param[out] length the size of the binary object in bytes (optional)
767  * \return an opaque data pointer or NULL if not available
768  */
769
770 void *mlt_property_get_data( mlt_property self, int *length )
771 {
772         // Assign length if not NULL
773         if ( length != NULL )
774                 *length = self->length;
775
776         // Return the data (note: there is no conversion here)
777         return self->data;
778 }
779
780 /** Destroy a property and free all related resources.
781  *
782  * \public \memberof mlt_property_s
783  * \param self a property
784  */
785
786 void mlt_property_close( mlt_property self )
787 {
788         mlt_property_clear( self );
789         pthread_mutex_destroy( &self->mutex );
790         free( self );
791 }
792
793 /** Copy a property.
794  *
795  * A Property holding binary data only copies the data if a serialiser
796  * function was supplied when you set the Property.
797  * \public \memberof mlt_property_s
798  * \author Zach <zachary.drew@gmail.com>
799  * \param self a property
800  * \param that another property
801  */
802 void mlt_property_pass( mlt_property self, mlt_property that )
803 {
804         pthread_mutex_lock( &self->mutex );
805         mlt_property_clear( self );
806
807         self->types = that->types;
808
809         if ( self->types & mlt_prop_int64 )
810                 self->prop_int64 = that->prop_int64;
811         else if ( self->types & mlt_prop_int )
812                 self->prop_int = that->prop_int;
813         else if ( self->types & mlt_prop_double )
814                 self->prop_double = that->prop_double;
815         else if ( self->types & mlt_prop_position )
816                 self->prop_position = that->prop_position;
817         if ( self->types & mlt_prop_string )
818         {
819                 if ( that->prop_string != NULL )
820                         self->prop_string = strdup( that->prop_string );
821         }
822         else if ( that->types & mlt_prop_rect )
823         {
824                 mlt_property_clear( self );
825                 self->types = mlt_prop_rect | mlt_prop_data;
826                 self->length = that->length;
827                 self->data = calloc( 1, self->length );
828                 memcpy( self->data, that->data, self->length );
829                 self->destructor = free;
830                 self->serialiser = that->serialiser;
831         }
832         else if ( self->types & mlt_prop_data && that->serialiser != NULL )
833         {
834                 self->types = mlt_prop_string;
835                 self->prop_string = that->serialiser( that->data, that->length );
836         }
837         pthread_mutex_unlock( &self->mutex );
838 }
839
840 /** Convert frame count to a SMPTE timecode string.
841  *
842  * \private \memberof mlt_property_s
843  * \param frames a frame count
844  * \param fps frames per second
845  * \param[out] s the string to write into - must have enough space to hold largest time string
846  */
847
848 static void time_smpte_from_frames( int frames, double fps, char *s )
849 {
850         int hours, mins, secs;
851         char frame_sep = ':';
852
853         if ( fps == 30000.0/1001.0 )
854         {
855                 fps = 30.0;
856                 int i, max_frames = frames;
857                 for ( i = 1800; i <= max_frames; i += 1800 )
858                 {
859                         if ( i % 18000 )
860                         {
861                                 max_frames += 2;
862                                 frames += 2;
863                         }
864                 }
865                 frame_sep = ';';
866         }
867         hours = frames / ( fps * 3600 );
868         frames -= hours * ( fps * 3600 );
869         mins = frames / ( fps * 60 );
870         frames -= mins * ( fps * 60 );
871         secs = frames / fps;
872         frames -= secs * fps;
873
874         sprintf( s, "%02d:%02d:%02d%c%02d", hours, mins, secs, frame_sep, frames );
875 }
876
877 /** Convert frame count to a SMIL clock value string.
878  *
879  * \private \memberof mlt_property_s
880  * \param frames a frame count
881  * \param fps frames per second
882  * \param[out] s the string to write into - must have enough space to hold largest time string
883  */
884
885 static void time_clock_from_frames( int frames, double fps, char *s )
886 {
887         int hours, mins;
888         double secs;
889
890         hours = frames / ( fps * 3600 );
891         frames -= hours * ( fps * 3600 );
892         mins = frames / ( fps * 60 );
893         frames -= mins * ( fps * 60 );
894         secs = (double) frames / fps;
895
896         sprintf( s, "%02d:%02d:%06.3f", hours, mins, secs );
897 }
898
899 /** Get the property as a time string.
900  *
901  * The time value can be either a SMPTE timecode or SMIL clock value.
902  * The caller is not responsible for deallocating the returned string!
903  * The string is deallocated when the property is closed.
904  * \public \memberof mlt_property_s
905  * \param self a property
906  * \param format the time format that you want
907  * \param fps frames per second
908  * \param locale the locale to use for this conversion
909  * \return a string representation of the property or NULL if failed
910  */
911
912 char *mlt_property_get_time( mlt_property self, mlt_time_format format, double fps, locale_t locale )
913 {
914         char *orig_localename = NULL;
915         const char *localename = "C";
916
917         // Optimization for mlt_time_frames
918         if ( format == mlt_time_frames )
919                 return mlt_property_get_string_l( self, locale );
920
921         // Remove existing string
922         if ( self->prop_string )
923                 mlt_property_set_int( self, mlt_property_get_int( self, fps, locale ) );
924
925         // Use the specified locale
926         if ( locale )
927         {
928                 // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale.
929                 // Save the current locale
930 #if defined(__DARWIN__)
931                 localename = querylocale( LC_NUMERIC, locale );
932 #elif defined(__GLIBC__)
933                 localename = locale->__names[ LC_NUMERIC ];
934 #else
935                 // TODO: not yet sure what to do on other platforms
936 #endif
937                 // Protect damaging the global locale from a temporary locale on another thread.
938                 pthread_mutex_lock( &self->mutex );
939
940                 // Get the current locale
941                 orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
942
943                 // Set the new locale
944                 setlocale( LC_NUMERIC, localename );
945         }
946         else
947         {
948                 // Make sure we have a lock before accessing self->types
949                 pthread_mutex_lock( &self->mutex );
950         }
951
952         // Convert number to string
953         if ( self->types & mlt_prop_int )
954         {
955                 self->types |= mlt_prop_string;
956                 self->prop_string = malloc( 32 );
957                 if ( format == mlt_time_clock )
958                         time_clock_from_frames( self->prop_int, fps, self->prop_string );
959                 else
960                         time_smpte_from_frames( self->prop_int, fps, self->prop_string );
961         }
962         else if ( self->types & mlt_prop_position )
963         {
964                 self->types |= mlt_prop_string;
965                 self->prop_string = malloc( 32 );
966                 if ( format == mlt_time_clock )
967                         time_clock_from_frames( (int) self->prop_position, fps, self->prop_string );
968                 else
969                         time_smpte_from_frames( (int) self->prop_position, fps, self->prop_string );
970         }
971         else if ( self->types & mlt_prop_double )
972         {
973                 self->types |= mlt_prop_string;
974                 self->prop_string = malloc( 32 );
975                 if ( format == mlt_time_clock )
976                         time_clock_from_frames( self->prop_double, fps, self->prop_string );
977                 else
978                         time_smpte_from_frames( self->prop_double, fps, self->prop_string );
979         }
980         else if ( self->types & mlt_prop_int64 )
981         {
982                 self->types |= mlt_prop_string;
983                 self->prop_string = malloc( 32 );
984                 if ( format == mlt_time_clock )
985                         time_clock_from_frames( (int) self->prop_int64, fps, self->prop_string );
986                 else
987                         time_smpte_from_frames( (int) self->prop_int64, fps, self->prop_string );
988         }
989
990         // Restore the current locale
991         if ( locale )
992         {
993                 setlocale( LC_NUMERIC, orig_localename );
994                 free( orig_localename );
995                 pthread_mutex_unlock( &self->mutex );
996         }
997         else
998         {
999                 // Make sure we have a lock before accessing self->types
1000                 pthread_mutex_unlock( &self->mutex );
1001         }
1002
1003         // Return the string (may be NULL)
1004         return self->prop_string;
1005 }
1006
1007 /** Determine if the property holds a numeric or numeric string value.
1008  *
1009  * \private \memberof mlt_property_s
1010  * \param self a property
1011  * \param locale the locale to use for string evaluation
1012  * \return true if it is numeric
1013  */
1014
1015 static int is_property_numeric( mlt_property self, locale_t locale )
1016 {
1017         int result = ( self->types & mlt_prop_int ) ||
1018                         ( self->types & mlt_prop_int64 ) ||
1019                         ( self->types & mlt_prop_double ) ||
1020                         ( self->types & mlt_prop_position ) ||
1021                         ( self->types & mlt_prop_rect );
1022
1023         // If not already numeric but string is numeric.
1024         if ( ( !result && self->types & mlt_prop_string ) && self->prop_string )
1025         {
1026                 double temp;
1027                 char *p = NULL;
1028                 
1029 #if defined(__GLIBC__) || defined(__DARWIN__)
1030                 if ( locale )
1031                         temp = strtod_l( self->prop_string, &p, locale );
1032                 else
1033 #else
1034                 char *orig_localename = NULL;
1035                 if ( locale ) {
1036                         // Protect damaging the global locale from a temporary locale on another thread.
1037                         pthread_mutex_lock( &self->mutex );
1038                         
1039                         // Get the current locale
1040                         orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
1041                         
1042                         // Set the new locale
1043                         setlocale( LC_NUMERIC, locale );
1044                 }
1045 #endif
1046
1047                 temp = strtod( self->prop_string, &p );
1048
1049 #if !defined(__GLIBC__) && !defined(__DARWIN__)
1050                 if ( locale ) {
1051                         // Restore the current locale
1052                         setlocale( LC_NUMERIC, orig_localename );
1053                         free( orig_localename );
1054                         pthread_mutex_unlock( &self->mutex );
1055                 }
1056 #endif
1057
1058                 result = ( p != self->prop_string );
1059         }
1060         return result;
1061 }
1062
1063 /** A linear interpolation function for animation.
1064  *
1065  * \private \memberof mlt_property_s
1066  */
1067
1068 static inline double linear_interpolate( double y1, double y2, double t )
1069 {
1070         return y1 + ( y2 - y1 ) * t;
1071 }
1072
1073 /** A smooth spline interpolation for animation.
1074  *
1075  * For non-closed curves, you need to also supply the tangent vector at the first and last control point.
1076  * This is commonly done: T(P[0]) = P[1] - P[0] and T(P[n]) = P[n] - P[n-1].
1077  * \private \memberof mlt_property_s
1078  */
1079
1080 static inline double catmull_rom_interpolate( double y0, double y1, double y2, double y3, double t )
1081 {
1082         double t2 = t * t;
1083         double a0 = -0.5 * y0 + 1.5 * y1 - 1.5 * y2 + 0.5 * y3;
1084         double a1 = y0 - 2.5 * y1 + 2 * y2 - 0.5 * y3;
1085         double a2 = -0.5 * y0 + 0.5 * y2;
1086         double a3 = y1;
1087         return a0 * t * t2 + a1 * t2 + a2 * t + a3;
1088 }
1089
1090 /** Interpolate a new property value given a set of other properties.
1091  *
1092  * \public \memberof mlt_property_s
1093  * \param self the property onto which to set the computed value
1094  * \param p an array of at least 1 value in p[1] if \p interp is discrete,
1095  *  2 values in p[1] and p[2] if \p interp is linear, or
1096  *  4 values in p[0] - p[3] if \p interp is smooth
1097  * \param progress a ratio in the range [0, 1] to indicate how far between p[1] and p[2]
1098  * \param fps the frame rate, which may be needed for converting a time string to frame units
1099  * \param locale the locale, which may be needed for converting a string to a real number
1100  * \param interp the interpolation method to use
1101  * \return true if there was an error
1102  */
1103
1104 int mlt_property_interpolate( mlt_property self, mlt_property p[],
1105         double progress, double fps, locale_t locale, mlt_keyframe_type interp )
1106 {
1107         int error = 0;
1108         if ( interp != mlt_keyframe_discrete &&
1109                 is_property_numeric( p[1], locale ) && is_property_numeric( p[2], locale ) )
1110         {
1111                 if ( self->types & mlt_prop_rect )
1112                 {
1113                         mlt_rect value = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN };
1114                         if ( interp == mlt_keyframe_linear )
1115                         {
1116                                 mlt_rect points[2];
1117                                 mlt_rect zero = {0, 0, 0, 0, 0};
1118                                 points[0] = p[1]? mlt_property_get_rect( p[1], locale ) : zero;
1119                                 if ( p[2] )
1120                                 {
1121                                         points[1] = mlt_property_get_rect( p[2], locale );
1122                                         value.x = linear_interpolate( points[0].x, points[1].x, progress );
1123                                         value.y = linear_interpolate( points[0].y, points[1].y, progress );
1124                                         value.w = linear_interpolate( points[0].w, points[1].w, progress );
1125                                         value.h = linear_interpolate( points[0].h, points[1].h, progress );
1126                                         value.o = linear_interpolate( points[0].o, points[1].o, progress );
1127                                 }
1128                                 else
1129                                 {
1130                                         value = points[0];
1131                                 }
1132                         }
1133                         else if ( interp == mlt_keyframe_smooth )
1134                         {
1135                                 mlt_rect points[4];
1136                                 mlt_rect zero = {0, 0, 0, 0, 0};
1137                                 points[1] = p[1]? mlt_property_get_rect( p[1], locale ) : zero;
1138                                 if ( p[2] )
1139                                 {
1140                                         points[0] = p[0]? mlt_property_get_rect( p[0], locale ) : zero;
1141                                         points[2] = p[2]? mlt_property_get_rect( p[2], locale ) : zero;
1142                                         points[3] = p[3]? mlt_property_get_rect( p[3], locale ) : zero;
1143                                         value.x = catmull_rom_interpolate( points[0].x, points[1].x, points[2].x, points[3].x, progress );
1144                                         value.y = catmull_rom_interpolate( points[0].y, points[1].y, points[2].y, points[3].y, progress );
1145                                         value.w = catmull_rom_interpolate( points[0].w, points[1].w, points[2].w, points[3].w, progress );
1146                                         value.h = catmull_rom_interpolate( points[0].h, points[1].h, points[2].h, points[3].h, progress );
1147                                         value.o = catmull_rom_interpolate( points[0].o, points[1].o, points[2].o, points[3].o, progress );
1148                                 }
1149                                 else
1150                                 {
1151                                         value = points[1];
1152                                 }
1153                         }
1154                         error = mlt_property_set_rect( self, value );
1155                 }
1156                 else
1157                 {
1158                         double value = 0.0;
1159                         if ( interp == mlt_keyframe_linear )
1160                         {
1161                                 double points[2];
1162                                 points[0] = p[1]? mlt_property_get_double( p[1], fps, locale ) : 0;
1163                                 points[1] = p[2]? mlt_property_get_double( p[2], fps, locale ) : 0;
1164                                 value = p[2]? linear_interpolate( points[0], points[1], progress ) : points[0];
1165                         }
1166                         else if ( interp == mlt_keyframe_smooth )
1167                         {
1168                                 double points[4];
1169                                 points[0] = p[0]? mlt_property_get_double( p[0], fps, locale ) : 0;
1170                                 points[1] = p[1]? mlt_property_get_double( p[1], fps, locale ) : 0;
1171                                 points[2] = p[2]? mlt_property_get_double( p[2], fps, locale ) : 0;
1172                                 points[3] = p[3]? mlt_property_get_double( p[3], fps, locale ) : 0;
1173                                 value = p[2]? catmull_rom_interpolate( points[0], points[1], points[2], points[3], progress ) : points[1];
1174                         }
1175                         error = mlt_property_set_double( self, value );
1176                 }
1177         }
1178         else
1179         {
1180                 mlt_property_pass( self, p[1] );
1181         }
1182         return error;
1183 }
1184
1185 /** Create a new animation or refresh an existing one.
1186  *
1187  * \private \memberof mlt_property_s
1188  * \param self a property
1189  * \param fps the frame rate, which may be needed for converting a time string to frame units
1190  * \param locale the locale, which may be needed for converting a string to a real number
1191  * \param length the maximum number of frames when interpreting negative keyframe times,
1192  *  <=0 if you don't care or need that
1193  */
1194
1195 static void refresh_animation( mlt_property self, double fps, locale_t locale, int length  )
1196 {
1197         if ( !self->animation )
1198         {
1199                 self->animation = mlt_animation_new();
1200                 if ( self->prop_string )
1201                 {
1202                         mlt_animation_parse( self->animation, self->prop_string, length, fps, locale );
1203                 }
1204                 else
1205                 {
1206                         mlt_animation_set_length( self->animation, length );
1207                         pthread_mutex_lock( &self->mutex );
1208                         self->types |= mlt_prop_data;
1209                         self->data = self->animation;
1210                         self->serialiser = (mlt_serialiser) mlt_animation_serialize;
1211                         pthread_mutex_unlock( &self->mutex );
1212                 }
1213         }
1214         else if ( self->prop_string )
1215         {
1216                 mlt_animation_refresh( self->animation, self->prop_string, length );
1217         }
1218 }
1219
1220 /** Get the real number at a frame position.
1221  *
1222  * \public \memberof mlt_property_s
1223  * \param self a property
1224  * \param fps the frame rate, which may be needed for converting a time string to frame units
1225  * \param locale the locale, which may be needed for converting a string to a real number
1226  * \param position the frame number
1227  * \param length the maximum number of frames when interpreting negative keyframe times,
1228  *  <=0 if you don't care or need that
1229  * \return the real number
1230  */
1231
1232 double mlt_property_anim_get_double( mlt_property self, double fps, locale_t locale, int position, int length )
1233 {
1234         pthread_mutex_lock( &self->mutex );
1235         double result;
1236         if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
1237         {
1238                 struct mlt_animation_item_s item;
1239                 item.property = mlt_property_init();
1240
1241                 refresh_animation( self, fps, locale, length );
1242                 mlt_animation_get_item( self->animation, &item, position );
1243                 result = mlt_property_get_double( item.property, fps, locale );
1244
1245                 mlt_property_close( item.property );
1246         }
1247         else
1248         {
1249                 result = mlt_property_get_double( self, fps, locale );
1250         }
1251         pthread_mutex_unlock( &self->mutex );
1252         return result;
1253 }
1254
1255 /** Get the property as an integer number at a frame position.
1256  *
1257  * \public \memberof mlt_property_s
1258  * \param self a property
1259  * \param fps the frame rate, which may be needed for converting a time string to frame units
1260  * \param locale the locale, which may be needed for converting a string to a real number
1261  * \param position the frame number
1262  * \param length the maximum number of frames when interpreting negative keyframe times,
1263  *  <=0 if you don't care or need that
1264  * \return an integer value
1265  */
1266
1267 int mlt_property_anim_get_int( mlt_property self, double fps, locale_t locale, int position, int length )
1268 {
1269         pthread_mutex_lock( &self->mutex );
1270         int result;
1271         if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
1272         {
1273                 struct mlt_animation_item_s item;
1274                 item.property = mlt_property_init();
1275
1276                 refresh_animation( self, fps, locale, length );
1277                 mlt_animation_get_item( self->animation, &item, position );
1278                 result = mlt_property_get_int( item.property, fps, locale );
1279
1280                 mlt_property_close( item.property );
1281         }
1282         else
1283         {
1284                 result = mlt_property_get_int( self, fps, locale );
1285         }
1286         pthread_mutex_unlock( &self->mutex );
1287         return result;
1288 }
1289
1290 /** Get the string at certain a frame position.
1291  *
1292  * \public \memberof mlt_property_s
1293  * \param self a property
1294  * \param fps the frame rate, which may be needed for converting a time string to frame units
1295  * \param locale the locale, which may be needed for converting a string to a real number
1296  * \param position the frame number
1297  * \param length the maximum number of frames when interpreting negative keyframe times,
1298  *  <=0 if you don't care or need that
1299  * \return the string representation of the property or NULL if failed
1300  */
1301
1302 char* mlt_property_anim_get_string( mlt_property self, double fps, locale_t locale, int position, int length )
1303 {
1304         pthread_mutex_lock( &self->mutex );
1305         char *result;
1306         if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
1307         {
1308                 struct mlt_animation_item_s item;
1309                 item.property = mlt_property_init();
1310
1311                 if ( !self->animation )
1312                         refresh_animation( self, fps, locale, length );
1313                 mlt_animation_get_item( self->animation, &item, position );
1314
1315                 if ( self->prop_string )
1316                         free( self->prop_string );
1317                 self->prop_string = mlt_property_get_string_l( item.property, locale );
1318                 if ( self->prop_string )
1319                         self->prop_string = strdup( self->prop_string );
1320                 self->types |= mlt_prop_string;
1321
1322                 result = self->prop_string;
1323                 mlt_property_close( item.property );
1324         }
1325         else
1326         {
1327                 result = mlt_property_get_string_l( self, locale );
1328         }
1329         pthread_mutex_unlock( &self->mutex );
1330         return result;
1331 }
1332
1333 /** Set a property animation keyframe to a real number.
1334  *
1335  * \public \memberof mlt_property_s
1336  * \param self a property
1337  * \param value a double precision floating point value
1338  * \param fps the frame rate, which may be needed for converting a time string to frame units
1339  * \param locale the locale, which may be needed for converting a string to a real number
1340  * \param position the frame number
1341  * \param length the maximum number of frames when interpreting negative keyframe times,
1342  *  <=0 if you don't care or need that
1343  * \param keyframe_type the interpolation method for this keyframe
1344  * \return false if successful, true to indicate error
1345  */
1346
1347 int mlt_property_anim_set_double( mlt_property self, double value, double fps, locale_t locale,
1348         int position, int length, mlt_keyframe_type keyframe_type )
1349 {
1350         int result;
1351         struct mlt_animation_item_s item;
1352
1353         item.property = mlt_property_init();
1354         item.frame = position;
1355         item.keyframe_type = keyframe_type;
1356         mlt_property_set_double( item.property, value );
1357
1358         refresh_animation( self, fps, locale, length );
1359         result = mlt_animation_insert( self->animation, &item );
1360         mlt_animation_interpolate( self->animation );
1361         mlt_property_close( item.property );
1362
1363         return result;
1364 }
1365
1366 /** Set a property animation keyframe to an integer value.
1367  *
1368  * \public \memberof mlt_property_s
1369  * \param self a property
1370  * \param value an integer
1371  * \param fps the frame rate, which may be needed for converting a time string to frame units
1372  * \param locale the locale, which may be needed for converting a string to a real number
1373  * \param position the frame number
1374  * \param length the maximum number of frames when interpreting negative keyframe times,
1375  *  <=0 if you don't care or need that
1376  * \param keyframe_type the interpolation method for this keyframe
1377  * \return false if successful, true to indicate error
1378  */
1379
1380 int mlt_property_anim_set_int( mlt_property self, int value, double fps, locale_t locale,
1381         int position, int length, mlt_keyframe_type keyframe_type )
1382 {
1383         int result;
1384         struct mlt_animation_item_s item;
1385
1386         item.property = mlt_property_init();
1387         item.frame = position;
1388         item.keyframe_type = keyframe_type;
1389         mlt_property_set_int( item.property, value );
1390
1391         refresh_animation( self, fps, locale, length );
1392         result = mlt_animation_insert( self->animation, &item );
1393         mlt_animation_interpolate( self->animation );
1394         mlt_property_close( item.property );
1395
1396         return result;
1397 }
1398
1399 /** Set a property animation keyframe to a string.
1400  *
1401  * Strings only support discrete animation. Do not use this to set a property's
1402  * animation string that contains a semicolon-delimited set of values; use
1403  * mlt_property_set() for that.
1404  * \public \memberof mlt_property_s
1405  * \param self a property
1406  * \param value a string
1407  * \param fps the frame rate, which may be needed for converting a time string to frame units
1408  * \param locale the locale, which may be needed for converting a string to a real number
1409  * \param position the frame number
1410  * \param length the maximum number of frames when interpreting negative keyframe times,
1411  *  <=0 if you don't care or need that
1412  * \return false if successful, true to indicate error
1413  */
1414
1415 int mlt_property_anim_set_string( mlt_property self, const char *value, double fps, locale_t locale, int position, int length )
1416 {
1417         int result;
1418         struct mlt_animation_item_s item;
1419
1420         item.property = mlt_property_init();
1421         item.frame = position;
1422         item.keyframe_type = mlt_keyframe_discrete;
1423         mlt_property_set_string( item.property, value );
1424
1425         refresh_animation( self, fps, locale, length );
1426         result = mlt_animation_insert( self->animation, &item );
1427         mlt_animation_interpolate( self->animation );
1428         mlt_property_close( item.property );
1429
1430         return result;
1431 }
1432
1433 /** Get an object's animation object.
1434  *
1435  * You might need to call another mlt_property_anim_ function to actually construct
1436  * the animation, as this is a simple accessor function.
1437  * \public \memberof mlt_property_s
1438  * \param self a property
1439  * \return the animation object or NULL if there is no animation
1440  */
1441
1442 mlt_animation mlt_property_get_animation( mlt_property self )
1443 {
1444     return self->animation;
1445 }
1446
1447 /** Convert a rectangle value into a string.
1448  *
1449  * Unlike the deprecated mlt_geometry API, the canonical form of a mlt_rect
1450  * is a space delimited "x y w h o" even though many kinds of field delimiters
1451  * may be used to convert a string to a rectangle.
1452  * \private \memberof mlt_property_s
1453  * \param rect the rectangle to convert
1454  * \param length not used
1455  * \return the string representation of a rectangle
1456  */
1457
1458 static char* serialise_mlt_rect( mlt_rect *rect, int length )
1459 {
1460         char* result = calloc( 1, 100 );
1461         if ( rect->x != DBL_MIN )
1462                 sprintf( result + strlen( result ), "%g", rect->x );
1463         if ( rect->y != DBL_MIN )
1464                 sprintf( result + strlen( result ), " %g", rect->y );
1465         if ( rect->w != DBL_MIN )
1466                 sprintf( result + strlen( result ), " %g", rect->w );
1467         if ( rect->h != DBL_MIN )
1468                 sprintf( result + strlen( result ), " %g", rect->h );
1469         if ( rect->o != DBL_MIN )
1470                 sprintf( result + strlen( result ), " %g", rect->o );
1471         return result;
1472 }
1473
1474 /** Set a property to a mlt_rect rectangle.
1475  *
1476  * \public \memberof mlt_property_s
1477  * \param self a property
1478  * \param value a rectangle
1479  * \return false
1480  */
1481
1482 int mlt_property_set_rect( mlt_property self, mlt_rect value )
1483 {
1484         pthread_mutex_lock( &self->mutex );
1485         mlt_property_clear( self );
1486         self->types = mlt_prop_rect | mlt_prop_data;
1487         self->length = sizeof(value);
1488         self->data = calloc( 1, self->length );
1489         memcpy( self->data, &value, self->length );
1490         self->destructor = free;
1491         self->serialiser = (mlt_serialiser) serialise_mlt_rect;
1492         pthread_mutex_unlock( &self->mutex );
1493         return 0;
1494 }
1495
1496 /** Get the property as a rectangle.
1497  *
1498  * You can use any non-numeric character(s) as a field delimiter.
1499  * If the number has a '%' immediately following it, the number is divided by
1500  * 100 to convert it into a real number.
1501  * \public \memberof mlt_property_s
1502  * \param self a property
1503  * \param locale the locale to use for when converting from a string
1504  * \return a rectangle value
1505  */
1506
1507 mlt_rect mlt_property_get_rect( mlt_property self, locale_t locale )
1508 {
1509         mlt_rect rect = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN };
1510         if ( self->types & mlt_prop_rect )
1511                 rect = *( (mlt_rect*) self->data );
1512         else if ( self->types & mlt_prop_double )
1513                 rect.x = self->prop_double;
1514         else if ( self->types & mlt_prop_int )
1515                 rect.x = ( double )self->prop_int;
1516         else if ( self->types & mlt_prop_position )
1517                 rect.x = ( double )self->prop_position;
1518         else if ( self->types & mlt_prop_int64 )
1519                 rect.x = ( double )self->prop_int64;
1520         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
1521         {
1522                 char *value = self->prop_string;
1523                 char *p = NULL;
1524                 int count = 0;
1525
1526 #if !defined(__GLIBC__) && !defined(__DARWIN__)
1527                 char *orig_localename = NULL;
1528                 if ( locale ) {
1529                         // Protect damaging the global locale from a temporary locale on another thread.
1530                         pthread_mutex_lock( &self->mutex );
1531                         
1532                         // Get the current locale
1533                         orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
1534                         
1535                         // Set the new locale
1536                         setlocale( LC_NUMERIC, locale );
1537                 }
1538 #endif
1539
1540                 while ( *value )
1541                 {
1542                         double temp;
1543 #if defined(__GLIBC__) || defined(__DARWIN__)
1544                         if ( locale )
1545                                 temp = strtod_l( value, &p, locale );
1546             else
1547 #endif
1548                                 temp = strtod( value, &p );
1549                         if ( p != value )
1550                         {
1551                                 if ( p[0] == '%' )
1552                                 {
1553                                         temp /= 100.0;
1554                                         p ++;
1555                                 }
1556
1557                                 // Chomp the delimiter.
1558                                 if ( *p ) p ++;
1559
1560                                 // Assign the value to appropriate field.
1561                                 switch( count )
1562                                 {
1563                                         case 0: rect.x = temp; break;
1564                                         case 1: rect.y = temp; break;
1565                                         case 2: rect.w = temp; break;
1566                                         case 3: rect.h = temp; break;
1567                                         case 4: rect.o = temp; break;
1568                                 }
1569                         }
1570                         else
1571                         {
1572                                 p++;
1573                         }
1574                         value = p;
1575                         count ++;
1576                 }
1577
1578 #if !defined(__GLIBC__) && !defined(__DARWIN__)
1579                 if ( locale ) {
1580                         // Restore the current locale
1581                         setlocale( LC_NUMERIC, orig_localename );
1582                         free( orig_localename );
1583                         pthread_mutex_unlock( &self->mutex );
1584                 }
1585 #endif
1586     }
1587         return rect;
1588 }
1589
1590 /** Set a property animation keyframe to a rectangle.
1591  *
1592  * \public \memberof mlt_property_s
1593  * \param self a property
1594  * \param value a rectangle
1595  * \param fps the frame rate, which may be needed for converting a time string to frame units
1596  * \param locale the locale, which may be needed for converting a string to a real number
1597  * \param position the frame number
1598  * \param length the maximum number of frames when interpreting negative keyframe times,
1599  *  <=0 if you don't care or need that
1600  * \param keyframe_type the interpolation method for this keyframe
1601  * \return false if successful, true to indicate error
1602  */
1603
1604 int mlt_property_anim_set_rect( mlt_property self, mlt_rect value, double fps, locale_t locale,
1605         int position, int length, mlt_keyframe_type keyframe_type )
1606 {
1607         int result;
1608         struct mlt_animation_item_s item;
1609
1610         item.property = mlt_property_init();
1611         item.frame = position;
1612         item.keyframe_type = keyframe_type;
1613         mlt_property_set_rect( item.property, value );
1614
1615         refresh_animation( self, fps, locale, length );
1616         result = mlt_animation_insert( self->animation, &item );
1617         mlt_animation_interpolate( self->animation );
1618         mlt_property_close( item.property );
1619
1620         return result;
1621 }
1622
1623 /** Get a rectangle at a frame position.
1624  *
1625  * \public \memberof mlt_property_s
1626  * \param self a property
1627  * \param fps the frame rate, which may be needed for converting a time string to frame units
1628  * \param locale the locale, which may be needed for converting a string to a real number
1629  * \param position the frame number
1630  * \param length the maximum number of frames when interpreting negative keyframe times,
1631  *  <=0 if you don't care or need that
1632  * \return the rectangle
1633  */
1634
1635 mlt_rect mlt_property_anim_get_rect( mlt_property self, double fps, locale_t locale, int position, int length )
1636 {
1637         pthread_mutex_lock( &self->mutex );
1638         mlt_rect result;
1639         if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
1640         {
1641                 struct mlt_animation_item_s item;
1642                 item.property = mlt_property_init();
1643                 item.property->types = mlt_prop_rect;
1644
1645                 refresh_animation( self, fps, locale, length );
1646                 mlt_animation_get_item( self->animation, &item, position );
1647                 result = mlt_property_get_rect( item.property, locale );
1648
1649                 mlt_property_close( item.property );
1650         }
1651         else
1652         {
1653                 result = mlt_property_get_rect( self, locale );
1654         }
1655         pthread_mutex_unlock( &self->mutex );
1656         return result;
1657 }