]> git.sesse.net Git - mlt/blob - src/framework/mlt_property.c
Add mlt_properties_set/get_int_pos and Properties::set/get_int.
[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-2009 Ushodaya Enterprises Limited
7  * \author Charles Yates <charles.yates@pandora.be>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 // For strtod_l
25 #ifndef _GNU_SOURCE
26 #define _GNU_SOURCE
27 #endif
28
29 #include "mlt_property.h"
30 #include "mlt_animation.h"
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <locale.h>
36 #include <pthread.h>
37
38
39 /** Bit pattern used internally to indicated representations available.
40 */
41
42 typedef enum
43 {
44         mlt_prop_none = 0,    //!< not set
45         mlt_prop_int = 1,     //!< set as an integer
46         mlt_prop_string = 2,  //!< set as string or already converted to string
47         mlt_prop_position = 4,//!< set as a position
48         mlt_prop_double = 8,  //!< set as a floating point
49         mlt_prop_data = 16,   //!< set as opaque binary
50         mlt_prop_int64 = 32   //!< set as a 64-bit integer
51 }
52 mlt_property_type;
53
54 /** \brief Property class
55  *
56  * A property is like a variant or dynamic type. They are used for many things
57  * in MLT, but in particular they are the parameter mechanism for the plugins.
58  */
59
60 struct mlt_property_s
61 {
62         /// Stores a bit pattern of types available for this property
63         mlt_property_type types;
64
65         /// Atomic type handling
66         int prop_int;
67         mlt_position prop_position;
68         double prop_double;
69         int64_t prop_int64;
70
71         /// String handling
72         char *prop_string;
73
74         /// Generic type handling
75         void *data;
76         int length;
77         mlt_destructor destructor;
78         mlt_serialiser serialiser;
79
80         pthread_mutex_t mutex;
81         mlt_animation animation;
82 };
83
84 /** Construct a property and initialize it
85  * \public \memberof mlt_property_s
86  */
87
88 mlt_property mlt_property_init( )
89 {
90         mlt_property self = calloc( 1, sizeof( *self ) );
91         if ( self )
92                 pthread_mutex_init( &self->mutex, NULL );
93         return self;
94 }
95
96 /** Clear (0/null) a property.
97  *
98  * Frees up any associated resources in the process.
99  * \private \memberof mlt_property_s
100  * \param self a property
101  */
102
103 static inline void mlt_property_clear( mlt_property self )
104 {
105         // Special case data handling
106         if ( self->types & mlt_prop_data && self->destructor != NULL )
107                 self->destructor( self->data );
108
109         // Special case string handling
110         if ( self->types & mlt_prop_string )
111                 free( self->prop_string );
112
113         if ( self->animation )
114                 mlt_animation_close( self->animation );
115
116         // Wipe stuff
117         self->types = 0;
118         self->prop_int = 0;
119         self->prop_position = 0;
120         self->prop_double = 0;
121         self->prop_int64 = 0;
122         self->prop_string = NULL;
123         self->data = NULL;
124         self->length = 0;
125         self->destructor = NULL;
126         self->serialiser = NULL;
127         self->animation = NULL;
128 }
129
130 /** Set the property to an integer value.
131  *
132  * \public \memberof mlt_property_s
133  * \param self a property
134  * \param value an integer
135  * \return false
136  */
137
138 int mlt_property_set_int( mlt_property self, int value )
139 {
140         pthread_mutex_lock( &self->mutex );
141         mlt_property_clear( self );
142         self->types = mlt_prop_int;
143         self->prop_int = value;
144         pthread_mutex_unlock( &self->mutex );
145         return 0;
146 }
147
148 /** Set the property to a floating point value.
149  *
150  * \public \memberof mlt_property_s
151  * \param self a property
152  * \param value a double precision floating point value
153  * \return false
154  */
155
156 int mlt_property_set_double( mlt_property self, double value )
157 {
158         pthread_mutex_lock( &self->mutex );
159         mlt_property_clear( self );
160         self->types = mlt_prop_double;
161         self->prop_double = value;
162         pthread_mutex_unlock( &self->mutex );
163         return 0;
164 }
165
166 /** Set the property to a position value.
167  *
168  * Position is a relative time value in frame units.
169  * \public \memberof mlt_property_s
170  * \param self a property
171  * \param value a position value
172  * \return false
173  */
174
175 int mlt_property_set_position( mlt_property self, mlt_position value )
176 {
177         pthread_mutex_lock( &self->mutex );
178         mlt_property_clear( self );
179         self->types = mlt_prop_position;
180         self->prop_position = value;
181         pthread_mutex_unlock( &self->mutex );
182         return 0;
183 }
184
185 /** Set the property to a string value.
186  *
187  * This makes a copy of the string you supply so you do not need to track
188  * a new reference to it.
189  * \public \memberof mlt_property_s
190  * \param self a property
191  * \param value the string to copy to the property
192  * \return true if it failed
193  */
194
195 int mlt_property_set_string( mlt_property self, const char *value )
196 {
197         pthread_mutex_lock( &self->mutex );
198         if ( value != self->prop_string )
199         {
200                 mlt_property_clear( self );
201                 self->types = mlt_prop_string;
202                 if ( value != NULL )
203                         self->prop_string = strdup( value );
204         }
205         else
206         {
207                 self->types = mlt_prop_string;
208         }
209         pthread_mutex_unlock( &self->mutex );
210         return self->prop_string == NULL;
211 }
212
213 /** Set the property to a 64-bit integer value.
214  *
215  * \public \memberof mlt_property_s
216  * \param self a property
217  * \param value a 64-bit integer
218  * \return false
219  */
220
221 int mlt_property_set_int64( mlt_property self, int64_t value )
222 {
223         pthread_mutex_lock( &self->mutex );
224         mlt_property_clear( self );
225         self->types = mlt_prop_int64;
226         self->prop_int64 = value;
227         pthread_mutex_unlock( &self->mutex );
228         return 0;
229 }
230
231 /** Set a property to an opaque binary value.
232  *
233  * This does not make a copy of the data. You can use a Properties object
234  * with its reference tracking and the destructor function to control
235  * the lifetime of the data. Otherwise, pass NULL for the destructor
236  * function and control the lifetime yourself.
237  * \public \memberof mlt_property_s
238  * \param self a property
239  * \param value an opaque pointer
240  * \param length the number of bytes pointed to by value (optional)
241  * \param destructor a function to use to destroy this binary data (optional, assuming you manage the resource)
242  * \param serialiser a function to use to convert this binary data to a string (optional)
243  * \return false
244  */
245
246 int mlt_property_set_data( mlt_property self, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser )
247 {
248         pthread_mutex_lock( &self->mutex );
249         if ( self->data == value )
250                 self->destructor = NULL;
251         mlt_property_clear( self );
252         self->types = mlt_prop_data;
253         self->data = value;
254         self->length = length;
255         self->destructor = destructor;
256         self->serialiser = serialiser;
257         pthread_mutex_unlock( &self->mutex );
258         return 0;
259 }
260
261 /** Parse a SMIL clock value.
262  *
263  * \private \memberof mlt_property_s
264  * \param s the string to parse
265  * \param fps frames per second
266  * \param locale the locale to use for parsing a real number value
267  * \return position in frames
268  */
269
270 static int time_clock_to_frames( const char *s, double fps, locale_t locale )
271 {
272         char *pos, *copy = strdup( s );
273         int hours = 0, minutes = 0;
274         double seconds;
275
276         s = copy;
277         pos = strrchr( s, ':' );
278         if ( pos ) {
279 #if defined(__GLIBC__) || defined(__DARWIN__)
280                 if ( locale )
281                         seconds = strtod_l( pos + 1, NULL, locale );
282                 else
283 #endif
284                         seconds = strtod( pos + 1, NULL );
285                 *pos = 0;
286                 pos = strrchr( s, ':' );
287                 if ( pos ) {
288                         minutes = atoi( pos + 1 );
289                         *pos = 0;
290                         hours = atoi( s );
291                 }
292                 else {
293                         minutes = atoi( s );
294                 }
295         }
296         else {
297 #if defined(__GLIBC__) || defined(__DARWIN__)
298                 if ( locale )
299                         seconds = strtod_l( s, NULL, locale );
300                 else
301 #endif
302                         seconds = strtod( s, NULL );
303         }
304         free( copy );
305
306         return fps * ( (hours * 3600) + (minutes * 60) + seconds ) + 0.5;
307 }
308
309 /** Parse a SMPTE timecode string.
310  *
311  * \private \memberof mlt_property_s
312  * \param s the string to parse
313  * \param fps frames per second
314  * \return position in frames
315  */
316
317 static int time_code_to_frames( const char *s, double fps )
318 {
319         char *pos, *copy = strdup( s );
320         int hours = 0, minutes = 0, seconds = 0, frames;
321
322         s = copy;
323         pos = strrchr( s, ';' );
324         if ( !pos )
325                 pos = strrchr( s, ':' );
326         if ( pos ) {
327                 frames = atoi( pos + 1 );
328                 *pos = 0;
329                 pos = strrchr( s, ':' );
330                 if ( pos ) {
331                         seconds = atoi( pos + 1 );
332                         *pos = 0;
333                         pos = strrchr( s, ':' );
334                         if ( pos ) {
335                                 minutes = atoi( pos + 1 );
336                                 *pos = 0;
337                                 hours = atoi( s );
338                         }
339                         else {
340                                 minutes = atoi( s );
341                         }
342                 }
343                 else {
344                         seconds = atoi( s );
345                 }
346         }
347         else {
348                 frames = atoi( s );
349         }
350         free( copy );
351
352         return frames + ( fps * ( (hours * 3600) + (minutes * 60) + seconds ) + 0.5 );
353 }
354
355 /** Convert a string to an integer.
356  *
357  * The string must begin with '0x' to be interpreted as hexadecimal.
358  * Otherwise, it is interpreted as base 10.
359  *
360  * If the string begins with '#' it is interpreted as a hexadecimal color value
361  * in the form RRGGBB or AARRGGBB. Color values that begin with '0x' are
362  * always in the form RRGGBBAA where the alpha components are not optional.
363  * Applications and services should expect the binary color value in bytes to
364  * be in the following order: RGBA. This means they will have to cast the int
365  * to an unsigned int. This is especially important when they need to shift
366  * right to obtain RGB without alpha in order to make it do a logical instead
367  * of arithmetic shift.
368  *
369  * If the string contains a colon it is interpreted as a time value. If it also
370  * contains a period or comma character, the string is parsed as a clock value:
371  * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF.
372  * \private \memberof mlt_property_s
373  * \param value a string to convert
374  * \param fps frames per second, used when converting from time value
375  * \param locale the locale to use when converting from time clock value
376  * \return the resultant integer
377  */
378 static int mlt_property_atoi( const char *value, double fps, locale_t locale )
379 {
380         // Parse a hex color value as #RRGGBB or #AARRGGBB.
381         if ( value[0] == '#' )
382         {
383                 unsigned int rgb = strtoul( value + 1, NULL, 16 );
384                 unsigned int alpha = ( strlen( value ) > 7 ) ? ( rgb >> 24 ) : 0xff;
385                 return ( rgb << 8 ) | alpha;
386         }
387         // Do hex and decimal explicitly to avoid decimal value with leading zeros
388         // interpreted as octal.
389         else if ( value[0] == '0' && value[1] == 'x' )
390         {
391                 return strtoul( value + 2, NULL, 16 );
392         }
393         else if ( fps > 0 && strchr( value, ':' ) )
394         {
395                 if ( strchr( value, '.' ) || strchr( value, ',' ) )
396                         return time_clock_to_frames( value, fps, locale );
397                 else
398                         return time_code_to_frames( value, fps );
399         }
400         else
401         {
402                 return strtol( value, NULL, 10 );
403         }
404 }
405
406 /** Get the property as an integer.
407  *
408  * \public \memberof mlt_property_s
409  * \param self a property
410  * \param fps frames per second, used when converting from time value
411  * \param locale the locale to use when converting from time clock value
412  * \return an integer value
413  */
414
415 int mlt_property_get_int( mlt_property self, double fps, locale_t locale )
416 {
417         if ( self->types & mlt_prop_int )
418                 return self->prop_int;
419         else if ( self->types & mlt_prop_double )
420                 return ( int )self->prop_double;
421         else if ( self->types & mlt_prop_position )
422                 return ( int )self->prop_position;
423         else if ( self->types & mlt_prop_int64 )
424                 return ( int )self->prop_int64;
425         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
426                 return mlt_property_atoi( self->prop_string, fps, locale );
427         return 0;
428 }
429
430 /** Convert a string to a floating point number.
431  *
432  * If the string contains a colon it is interpreted as a time value. If it also
433  * contains a period or comma character, the string is parsed as a clock value:
434  * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF.
435  * If the numeric string ends with '%' then the value is divided by 100 to convert
436  * it into a ratio.
437  * \private \memberof mlt_property_s
438  * \param value the string to convert
439  * \param fps frames per second, used when converting from time value
440  * \param locale the locale to use when converting from time clock value
441  * \return the resultant real number
442  */
443 static double mlt_property_atof( const char *value, double fps, locale_t locale )
444 {
445         if ( fps > 0 && strchr( value, ':' ) )
446         {
447                 if ( strchr( value, '.' ) || strchr( value, ',' ) )
448                         return time_clock_to_frames( value, fps, locale );
449                 else
450                         return time_code_to_frames( value, fps );
451         }
452         else
453         {
454                 char *end = NULL;
455                 double result;
456 #if defined(__GLIBC__) || defined(__DARWIN__)
457                 if ( locale )
458                         result = strtod_l( value, &end, locale );
459 #endif
460                 else
461                         result = strtod( value, &end );
462                 if ( *end && end[0] == '%' )
463                         result /= 100.0;
464                 return result;
465         }
466 }
467
468 /** Get the property as a floating point.
469  *
470  * \public \memberof mlt_property_s
471  * \param self a property
472  * \param fps frames per second, used when converting from time value
473  * \param locale the locale to use for this conversion
474  * \return a floating point value
475  */
476
477 double mlt_property_get_double( mlt_property self, double fps, locale_t locale )
478 {
479         if ( self->types & mlt_prop_double )
480                 return self->prop_double;
481         else if ( self->types & mlt_prop_int )
482                 return ( double )self->prop_int;
483         else if ( self->types & mlt_prop_position )
484                 return ( double )self->prop_position;
485         else if ( self->types & mlt_prop_int64 )
486                 return ( double )self->prop_int64;
487         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
488                 return mlt_property_atof( self->prop_string, fps, locale );
489         return 0;
490 }
491
492 /** Get the property as a position.
493  *
494  * A position is an offset time in terms of frame units.
495  * \public \memberof mlt_property_s
496  * \param self a property
497  * \param fps frames per second, used when converting from time value
498  * \param locale the locale to use when converting from time clock value
499  * \return the position in frames
500  */
501
502 mlt_position mlt_property_get_position( mlt_property self, double fps, locale_t locale )
503 {
504         if ( self->types & mlt_prop_position )
505                 return self->prop_position;
506         else if ( self->types & mlt_prop_int )
507                 return ( mlt_position )self->prop_int;
508         else if ( self->types & mlt_prop_double )
509                 return ( mlt_position )self->prop_double;
510         else if ( self->types & mlt_prop_int64 )
511                 return ( mlt_position )self->prop_int64;
512         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
513                 return ( mlt_position )mlt_property_atoi( self->prop_string, fps, locale );
514         return 0;
515 }
516
517 /** Convert a string to a 64-bit integer.
518  *
519  * If the string begins with '0x' it is interpreted as a hexadecimal value.
520  * \private \memberof mlt_property_s
521  * \param value a string
522  * \return a 64-bit integer
523  */
524
525 static inline int64_t mlt_property_atoll( const char *value )
526 {
527         if ( value == NULL )
528                 return 0;
529         else if ( value[0] == '0' && value[1] == 'x' )
530                 return strtoll( value + 2, NULL, 16 );
531         else
532                 return strtoll( value, NULL, 10 );
533 }
534
535 /** Get the property as a signed integer.
536  *
537  * \public \memberof mlt_property_s
538  * \param self a property
539  * \return a 64-bit integer
540  */
541
542 int64_t mlt_property_get_int64( mlt_property self )
543 {
544         if ( self->types & mlt_prop_int64 )
545                 return self->prop_int64;
546         else if ( self->types & mlt_prop_int )
547                 return ( int64_t )self->prop_int;
548         else if ( self->types & mlt_prop_double )
549                 return ( int64_t )self->prop_double;
550         else if ( self->types & mlt_prop_position )
551                 return ( int64_t )self->prop_position;
552         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
553                 return mlt_property_atoll( self->prop_string );
554         return 0;
555 }
556
557 /** Get the property as a string.
558  *
559  * The caller is not responsible for deallocating the returned string!
560  * The string is deallocated when the Property is closed.
561  * This tries its hardest to convert the property to string including using
562  * a serialization function for binary data, if supplied.
563  * \public \memberof mlt_property_s
564  * \param self a property
565  * \return a string representation of the property or NULL if failed
566  */
567
568 char *mlt_property_get_string( mlt_property self )
569 {
570         // Construct a string if need be
571         if ( ! ( self->types & mlt_prop_string ) )
572         {
573                 pthread_mutex_lock( &self->mutex );
574                 if ( self->types & mlt_prop_int )
575                 {
576                         self->types |= mlt_prop_string;
577                         self->prop_string = malloc( 32 );
578                         sprintf( self->prop_string, "%d", self->prop_int );
579                 }
580                 else if ( self->types & mlt_prop_double )
581                 {
582                         self->types |= mlt_prop_string;
583                         self->prop_string = malloc( 32 );
584                         sprintf( self->prop_string, "%f", self->prop_double );
585                 }
586                 else if ( self->types & mlt_prop_position )
587                 {
588                         self->types |= mlt_prop_string;
589                         self->prop_string = malloc( 32 );
590                         sprintf( self->prop_string, "%d", (int)self->prop_position );
591                 }
592                 else if ( self->types & mlt_prop_int64 )
593                 {
594                         self->types |= mlt_prop_string;
595                         self->prop_string = malloc( 32 );
596                         sprintf( self->prop_string, "%"PRId64, self->prop_int64 );
597                 }
598                 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
599                 {
600                         self->types |= mlt_prop_string;
601                         self->prop_string = self->serialiser( self->data, self->length );
602                 }
603                 pthread_mutex_unlock( &self->mutex );
604         }
605
606         // Return the string (may be NULL)
607         return self->prop_string;
608 }
609
610 /** Get the property as a string (with locale).
611  *
612  * The caller is not responsible for deallocating the returned string!
613  * The string is deallocated when the Property is closed.
614  * This tries its hardest to convert the property to string including using
615  * a serialization function for binary data, if supplied.
616  * \public \memberof mlt_property_s
617  * \param self a property
618  * \param locale the locale to use for this conversion
619  * \return a string representation of the property or NULL if failed
620  */
621
622 char *mlt_property_get_string_l( mlt_property self, locale_t locale )
623 {
624         // Optimization for no locale
625         if ( !locale )
626                 return mlt_property_get_string( self );
627
628         // Construct a string if need be
629         if ( ! ( self->types & mlt_prop_string ) )
630         {
631                 // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale.
632                 // Save the current locale
633 #if defined(__DARWIN__)
634                 const char *localename = querylocale( LC_NUMERIC, locale );
635 #elif defined(__GLIBC__)
636                 const char *localename = locale->__names[ LC_NUMERIC ];
637 #else
638                 // TODO: not yet sure what to do on other platforms
639                 const char *localename = "";
640 #endif
641                 // Protect damaging the global locale from a temporary locale on another thread.
642                 pthread_mutex_lock( &self->mutex );
643
644                 // Get the current locale
645                 char *orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
646
647                 // Set the new locale
648                 setlocale( LC_NUMERIC, localename );
649
650                 if ( self->types & mlt_prop_int )
651                 {
652                         self->types |= mlt_prop_string;
653                         self->prop_string = malloc( 32 );
654                         sprintf( self->prop_string, "%d", self->prop_int );
655                 }
656                 else if ( self->types & mlt_prop_double )
657                 {
658                         self->types |= mlt_prop_string;
659                         self->prop_string = malloc( 32 );
660                         sprintf( self->prop_string, "%f", self->prop_double );
661                 }
662                 else if ( self->types & mlt_prop_position )
663                 {
664                         self->types |= mlt_prop_string;
665                         self->prop_string = malloc( 32 );
666                         sprintf( self->prop_string, "%d", (int)self->prop_position );
667                 }
668                 else if ( self->types & mlt_prop_int64 )
669                 {
670                         self->types |= mlt_prop_string;
671                         self->prop_string = malloc( 32 );
672                         sprintf( self->prop_string, "%"PRId64, self->prop_int64 );
673                 }
674                 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
675                 {
676                         self->types |= mlt_prop_string;
677                         self->prop_string = self->serialiser( self->data, self->length );
678                 }
679                 // Restore the current locale
680                 setlocale( LC_NUMERIC, orig_localename );
681                 free( orig_localename );
682                 pthread_mutex_unlock( &self->mutex );
683         }
684
685         // Return the string (may be NULL)
686         return self->prop_string;
687 }
688
689 /** Get the binary data from a property.
690  *
691  * This only works if you previously put binary data into the property.
692  * This does not return a copy of the data; it returns a pointer to it.
693  * If you supplied a destructor function when setting the binary data,
694  * the destructor is used when the Property is closed to free the memory.
695  * Therefore, only free the returned pointer if you did not supply a
696  * destructor function.
697  * \public \memberof mlt_property_s
698  * \param self a property
699  * \param[out] length the size of the binary object in bytes (optional)
700  * \return an opaque data pointer or NULL if not available
701  */
702
703 void *mlt_property_get_data( mlt_property self, int *length )
704 {
705         // Assign length if not NULL
706         if ( length != NULL )
707                 *length = self->length;
708
709         // Return the data (note: there is no conversion here)
710         return self->data;
711 }
712
713 /** Destroy a property and free all related resources.
714  *
715  * \public \memberof mlt_property_s
716  * \param self a property
717  */
718
719 void mlt_property_close( mlt_property self )
720 {
721         mlt_property_clear( self );
722         pthread_mutex_destroy( &self->mutex );
723         free( self );
724 }
725
726 /** Copy a property.
727  *
728  * A Property holding binary data only copies the data if a serialiser
729  * function was supplied when you set the Property.
730  * \public \memberof mlt_property_s
731  * \author Zach <zachary.drew@gmail.com>
732  * \param self a property
733  * \param that another property
734  */
735 void mlt_property_pass( mlt_property self, mlt_property that )
736 {
737         pthread_mutex_lock( &self->mutex );
738         mlt_property_clear( self );
739
740         self->types = that->types;
741
742         if ( self->types & mlt_prop_int64 )
743                 self->prop_int64 = that->prop_int64;
744         else if ( self->types & mlt_prop_int )
745                 self->prop_int = that->prop_int;
746         else if ( self->types & mlt_prop_double )
747                 self->prop_double = that->prop_double;
748         else if ( self->types & mlt_prop_position )
749                 self->prop_position = that->prop_position;
750         if ( self->types & mlt_prop_string )
751         {
752                 if ( that->prop_string != NULL )
753                         self->prop_string = strdup( that->prop_string );
754         }
755         else if ( self->types & mlt_prop_data && self->serialiser != NULL )
756         {
757                 self->types = mlt_prop_string;
758                 self->prop_string = self->serialiser( self->data, self->length );
759         }
760         pthread_mutex_unlock( &self->mutex );
761 }
762
763 /** Convert frame count to a SMPTE timecode string.
764  *
765  * \private \memberof mlt_property_s
766  * \param frames a frame count
767  * \param fps frames per second
768  * \param[out] s the string to write into - must have enough space to hold largest time string
769  */
770
771 static void time_smpte_from_frames( int frames, double fps, char *s )
772 {
773         int hours, mins, secs;
774         char frame_sep = ':';
775
776         if ( fps == 30000.0/1001.0 )
777         {
778                 fps = 30.0;
779                 int i, max_frames = frames;
780                 for ( i = 1800; i <= max_frames; i += 1800 )
781                 {
782                         if ( i % 18000 )
783                         {
784                                 max_frames += 2;
785                                 frames += 2;
786                         }
787                 }
788                 frame_sep = ';';
789         }
790         hours = frames / ( fps * 3600 );
791         frames -= hours * ( fps * 3600 );
792         mins = frames / ( fps * 60 );
793         frames -= mins * ( fps * 60 );
794         secs = frames / fps;
795         frames -= secs * fps;
796
797         sprintf( s, "%02d:%02d:%02d%c%02d", hours, mins, secs, frame_sep, frames );
798 }
799
800 /** Convert frame count to a SMIL clock value string.
801  *
802  * \private \memberof mlt_property_s
803  * \param frames a frame count
804  * \param fps frames per second
805  * \param[out] s the string to write into - must have enough space to hold largest time string
806  */
807
808 static void time_clock_from_frames( int frames, double fps, char *s )
809 {
810         int hours, mins;
811         double secs;
812
813         hours = frames / ( fps * 3600 );
814         frames -= hours * ( fps * 3600 );
815         mins = frames / ( fps * 60 );
816         frames -= mins * ( fps * 60 );
817         secs = (double) frames / fps;
818
819         sprintf( s, "%02d:%02d:%06.3f", hours, mins, secs );
820 }
821
822 /** Get the property as a time string.
823  *
824  * The time value can be either a SMPTE timecode or SMIL clock value.
825  * The caller is not responsible for deallocating the returned string!
826  * The string is deallocated when the property is closed.
827  * \public \memberof mlt_property_s
828  * \param self a property
829  * \param format the time format that you want
830  * \param fps frames per second
831  * \param locale the locale to use for this conversion
832  * \return a string representation of the property or NULL if failed
833  */
834
835 char *mlt_property_get_time( mlt_property self, mlt_time_format format, double fps, locale_t locale )
836 {
837         char *orig_localename = NULL;
838         const char *localename = "";
839
840         // Optimization for mlt_time_frames
841         if ( format == mlt_time_frames )
842                 return mlt_property_get_string_l( self, locale );
843
844         // Remove existing string
845         if ( self->prop_string )
846                 mlt_property_set_int( self, mlt_property_get_int( self, fps, locale ) );
847
848         // Use the specified locale
849         if ( locale )
850         {
851                 // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale.
852                 // Save the current locale
853 #if defined(__DARWIN__)
854                 localename = querylocale( LC_NUMERIC, locale );
855 #elif defined(__GLIBC__)
856                 localename = locale->__names[ LC_NUMERIC ];
857 #else
858                 // TODO: not yet sure what to do on other platforms
859 #endif
860                 // Protect damaging the global locale from a temporary locale on another thread.
861                 pthread_mutex_lock( &self->mutex );
862
863                 // Get the current locale
864                 orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
865
866                 // Set the new locale
867                 setlocale( LC_NUMERIC, localename );
868         }
869         else
870         {
871                 // Make sure we have a lock before accessing self->types
872                 pthread_mutex_lock( &self->mutex );
873         }
874
875         // Convert number to string
876         if ( self->types & mlt_prop_int )
877         {
878                 self->types |= mlt_prop_string;
879                 self->prop_string = malloc( 32 );
880                 if ( format == mlt_time_clock )
881                         time_clock_from_frames( self->prop_int, fps, self->prop_string );
882                 else
883                         time_smpte_from_frames( self->prop_int, fps, self->prop_string );
884         }
885         else if ( self->types & mlt_prop_position )
886         {
887                 self->types |= mlt_prop_string;
888                 self->prop_string = malloc( 32 );
889                 if ( format == mlt_time_clock )
890                         time_clock_from_frames( (int) self->prop_position, fps, self->prop_string );
891                 else
892                         time_smpte_from_frames( (int) self->prop_position, fps, self->prop_string );
893         }
894         else if ( self->types & mlt_prop_double )
895         {
896                 self->types |= mlt_prop_string;
897                 self->prop_string = malloc( 32 );
898                 if ( format == mlt_time_clock )
899                         time_clock_from_frames( self->prop_double, fps, self->prop_string );
900                 else
901                         time_smpte_from_frames( self->prop_double, fps, self->prop_string );
902         }
903         else if ( self->types & mlt_prop_int64 )
904         {
905                 self->types |= mlt_prop_string;
906                 self->prop_string = malloc( 32 );
907                 if ( format == mlt_time_clock )
908                         time_clock_from_frames( (int) self->prop_int64, fps, self->prop_string );
909                 else
910                         time_smpte_from_frames( (int) self->prop_int64, fps, self->prop_string );
911         }
912
913         // Restore the current locale
914         if ( locale )
915         {
916                 setlocale( LC_NUMERIC, orig_localename );
917                 free( orig_localename );
918                 pthread_mutex_unlock( &self->mutex );
919         }
920         else
921         {
922                 // Make sure we have a lock before accessing self->types
923                 pthread_mutex_unlock( &self->mutex );
924         }
925
926         // Return the string (may be NULL)
927         return self->prop_string;
928 }
929
930 static int is_property_numeric( mlt_property self, locale_t locale )
931 {
932         int result = ( self->types & mlt_prop_int ) ||
933                         ( self->types & mlt_prop_int64 ) ||
934                         ( self->types & mlt_prop_double ) ||
935                         ( self->types & mlt_prop_position );
936
937         // If not already numeric but string is numeric.
938         if ( ( !result && self->types & mlt_prop_string ) && self->prop_string )
939         {
940                 double temp;
941                 char *p = NULL;
942 #if defined(__GLIBC__) || defined(__DARWIN__)
943                 if ( locale )
944                         temp = strtod_l( self->prop_string, &p, locale );
945                 else
946 #endif
947                 temp = strtod( self->prop_string, &p );
948                 result = ( p != self->prop_string );
949         }
950         return result;
951 }
952
953 static inline double linear_interpolate( double y1, double y2, double t )
954 {
955         return y1 + ( y2 - y1 ) * t;
956 }
957
958 //  For non-closed curves, you need to also supply the tangent vector at the first and last control point.
959 // This is commonly done: T(P[0]) = P[1] - P[0] and T(P[n]) = P[n] - P[n-1].
960 static inline double catmull_rom_interpolate( double y0, double y1, double y2, double y3, double t )
961 {
962         double t2 = t * t;
963         double a0 = -0.5 * y0 + 1.5 * y1 - 1.5 * y2 + 0.5 * y3;
964         double a1 = y0 - 2.5 * y1 + 2 * y2 - 0.5 * y3;
965         double a2 = -0.5 * y0 + 0.5 * y2;
966         double a3 = y1;
967         return a0 * t * t2 + a1 * t2 + a2 * t + a3;
968 }
969
970 int mlt_property_interpolate( mlt_property self, mlt_property p[],
971         double progress, double fps, locale_t locale, mlt_keyframe_type interp )
972 {
973         int error = 0;
974         if ( interp != mlt_keyframe_discrete &&
975                 is_property_numeric( p[1], locale ) && is_property_numeric( p[2], locale ) )
976         {
977                 double value;
978                 if ( interp == mlt_keyframe_linear )
979                 {
980                         double points[2];
981                         points[0] = p[1]? mlt_property_get_double( p[1], fps, locale ) : 0;
982                         points[1] = p[2]? mlt_property_get_double( p[2], fps, locale ) : 0;
983                         value = p[2]? linear_interpolate( points[0], points[1], progress ) : points[0];
984                 }
985                 else if ( interp == mlt_keyframe_smooth )
986                 {
987                         double points[4];
988                         points[0] = p[0]? mlt_property_get_double( p[0], fps, locale ) : 0;
989                         points[1] = p[1]? mlt_property_get_double( p[1], fps, locale ) : 0;
990                         points[2] = p[2]? mlt_property_get_double( p[2], fps, locale ) : 0;
991                         points[3] = p[3]? mlt_property_get_double( p[3], fps, locale ) : 0;
992                         value = p[2]? catmull_rom_interpolate( points[0], points[1], points[2], points[3], progress ) : points[1];
993                 }
994                 error = mlt_property_set_double( self, value );
995         }
996         else
997         {
998                 mlt_property_pass( self, p[1] );
999         }
1000         return error;
1001 }
1002
1003 static void refresh_animation( mlt_property self, double fps, locale_t locale, int length  )
1004 {
1005         if ( !self->animation )
1006         {
1007                 self->animation = mlt_animation_new();
1008                 if ( self->prop_string )
1009                 {
1010                         mlt_animation_parse( self->animation, self->prop_string, length, fps, locale );
1011                 }
1012                 else
1013                 {
1014                         mlt_animation_set_length( self->animation, length );
1015                         self->types |= mlt_prop_data;
1016                         self->data = self->animation;
1017                         self->serialiser = (mlt_serialiser) mlt_animation_serialize;
1018                 }
1019         }
1020         else if ( self->prop_string )
1021         {
1022                 mlt_animation_refresh( self->animation, self->prop_string, length );
1023         }
1024 }
1025
1026 double mlt_property_get_double_pos( mlt_property self, double fps, locale_t locale, int position, int length )
1027 {
1028         double result;
1029         if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
1030         {
1031                 struct mlt_animation_item_s item;
1032                 item.property = mlt_property_init();
1033
1034                 refresh_animation( self, fps, locale, length );
1035                 mlt_animation_get_item( self->animation, &item, position );
1036                 result = mlt_property_get_double( item.property, fps, locale );
1037
1038                 mlt_property_close( item.property );
1039         }
1040         else
1041         {
1042                 result = mlt_property_get_double( self, fps, locale );
1043         }
1044         return result;
1045 }
1046
1047 int mlt_property_get_int_pos( mlt_property self, double fps, locale_t locale, int position, int length )
1048 {
1049         int result;
1050         if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
1051         {
1052                 struct mlt_animation_item_s item;
1053                 item.property = mlt_property_init();
1054
1055                 refresh_animation( self, fps, locale, length );
1056                 mlt_animation_get_item( self->animation, &item, position );
1057                 result = mlt_property_get_int( item.property, fps, locale );
1058
1059                 mlt_property_close( item.property );
1060         }
1061         else
1062         {
1063                 result = mlt_property_get_int( self, fps, locale );
1064         }
1065         return result;
1066 }
1067
1068 /** Set a property animation keyframe to a real number.
1069  *
1070  * \public \memberof mlt_property_s
1071  * \param self a property
1072  * \param value a double precision floating point value
1073  * \return false if successful, true to indicate error
1074  */
1075
1076 int mlt_property_set_double_pos( mlt_property self, double value, double fps, locale_t locale,
1077         mlt_keyframe_type keyframe_type, int position, int length )
1078 {
1079         int result;
1080         struct mlt_animation_item_s item;
1081
1082         item.property = mlt_property_init();
1083         item.frame = position;
1084         item.keyframe_type = keyframe_type;
1085         mlt_property_set_double( item.property, value );
1086
1087         refresh_animation( self, fps, locale, length );
1088         result = mlt_animation_insert( self->animation, &item );
1089         mlt_animation_interpolate( self->animation );
1090         mlt_property_close( item.property );
1091
1092         return result;
1093 }
1094
1095 /** Set a property animation keyframe to an integer value.
1096  *
1097  * \public \memberof mlt_property_s
1098  * \param self a property
1099  * \param value a double precision floating point value
1100  * \return false if successful, true to indicate error
1101  */
1102
1103 int mlt_property_set_int_pos( mlt_property self, int value, double fps, locale_t locale,
1104         mlt_keyframe_type keyframe_type, int position, int length )
1105 {
1106         int result;
1107         struct mlt_animation_item_s item;
1108
1109         item.property = mlt_property_init();
1110         item.frame = position;
1111         item.keyframe_type = keyframe_type;
1112         mlt_property_set_int( item.property, value );
1113
1114         refresh_animation( self, fps, locale, length );
1115         result = mlt_animation_insert( self->animation, &item );
1116         mlt_animation_interpolate( self->animation );
1117         mlt_property_close( item.property );
1118
1119         return result;
1120 }