]> git.sesse.net Git - mlt/blob - src/framework/mlt_property.c
Add mlt_property_get_double_pos() and mlt_property_get_int_pos().
[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  * \private \memberof mlt_property_s
436  * \param value the string to convert
437  * \param fps frames per second, used when converting from time value
438  * \param locale the locale to use when converting from time clock value
439  * \return the resultant real number
440  */
441 static double mlt_property_atof( const char *value, double fps, locale_t locale )
442 {
443         if ( fps > 0 && strchr( value, ':' ) )
444         {
445                 if ( strchr( value, '.' ) || strchr( value, ',' ) )
446                         return time_clock_to_frames( value, fps, locale );
447                 else
448                         return time_code_to_frames( value, fps );
449         }
450         else
451         {
452 #if defined(__GLIBC__) || defined(__DARWIN__)
453                 if ( locale )
454                         return strtod_l( value, NULL, locale );
455 #endif
456                 return strtod( value, NULL );
457         }
458 }
459
460 /** Get the property as a floating point.
461  *
462  * \public \memberof mlt_property_s
463  * \param self a property
464  * \param fps frames per second, used when converting from time value
465  * \param locale the locale to use for this conversion
466  * \return a floating point value
467  */
468
469 double mlt_property_get_double( mlt_property self, double fps, locale_t locale )
470 {
471         if ( self->types & mlt_prop_double )
472                 return self->prop_double;
473         else if ( self->types & mlt_prop_int )
474                 return ( double )self->prop_int;
475         else if ( self->types & mlt_prop_position )
476                 return ( double )self->prop_position;
477         else if ( self->types & mlt_prop_int64 )
478                 return ( double )self->prop_int64;
479         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
480                 return mlt_property_atof( self->prop_string, fps, locale );
481         return 0;
482 }
483
484 /** Get the property as a position.
485  *
486  * A position is an offset time in terms of frame units.
487  * \public \memberof mlt_property_s
488  * \param self a property
489  * \param fps frames per second, used when converting from time value
490  * \param locale the locale to use when converting from time clock value
491  * \return the position in frames
492  */
493
494 mlt_position mlt_property_get_position( mlt_property self, double fps, locale_t locale )
495 {
496         if ( self->types & mlt_prop_position )
497                 return self->prop_position;
498         else if ( self->types & mlt_prop_int )
499                 return ( mlt_position )self->prop_int;
500         else if ( self->types & mlt_prop_double )
501                 return ( mlt_position )self->prop_double;
502         else if ( self->types & mlt_prop_int64 )
503                 return ( mlt_position )self->prop_int64;
504         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
505                 return ( mlt_position )mlt_property_atoi( self->prop_string, fps, locale );
506         return 0;
507 }
508
509 /** Convert a string to a 64-bit integer.
510  *
511  * If the string begins with '0x' it is interpreted as a hexadecimal value.
512  * \private \memberof mlt_property_s
513  * \param value a string
514  * \return a 64-bit integer
515  */
516
517 static inline int64_t mlt_property_atoll( const char *value )
518 {
519         if ( value == NULL )
520                 return 0;
521         else if ( value[0] == '0' && value[1] == 'x' )
522                 return strtoll( value + 2, NULL, 16 );
523         else
524                 return strtoll( value, NULL, 10 );
525 }
526
527 /** Get the property as a signed integer.
528  *
529  * \public \memberof mlt_property_s
530  * \param self a property
531  * \return a 64-bit integer
532  */
533
534 int64_t mlt_property_get_int64( mlt_property self )
535 {
536         if ( self->types & mlt_prop_int64 )
537                 return self->prop_int64;
538         else if ( self->types & mlt_prop_int )
539                 return ( int64_t )self->prop_int;
540         else if ( self->types & mlt_prop_double )
541                 return ( int64_t )self->prop_double;
542         else if ( self->types & mlt_prop_position )
543                 return ( int64_t )self->prop_position;
544         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
545                 return mlt_property_atoll( self->prop_string );
546         return 0;
547 }
548
549 /** Get the property as a string.
550  *
551  * The caller is not responsible for deallocating the returned string!
552  * The string is deallocated when the Property is closed.
553  * This tries its hardest to convert the property to string including using
554  * a serialization function for binary data, if supplied.
555  * \public \memberof mlt_property_s
556  * \param self a property
557  * \return a string representation of the property or NULL if failed
558  */
559
560 char *mlt_property_get_string( mlt_property self )
561 {
562         // Construct a string if need be
563         if ( ! ( self->types & mlt_prop_string ) )
564         {
565                 pthread_mutex_lock( &self->mutex );
566                 if ( self->types & mlt_prop_int )
567                 {
568                         self->types |= mlt_prop_string;
569                         self->prop_string = malloc( 32 );
570                         sprintf( self->prop_string, "%d", self->prop_int );
571                 }
572                 else if ( self->types & mlt_prop_double )
573                 {
574                         self->types |= mlt_prop_string;
575                         self->prop_string = malloc( 32 );
576                         sprintf( self->prop_string, "%f", self->prop_double );
577                 }
578                 else if ( self->types & mlt_prop_position )
579                 {
580                         self->types |= mlt_prop_string;
581                         self->prop_string = malloc( 32 );
582                         sprintf( self->prop_string, "%d", (int)self->prop_position );
583                 }
584                 else if ( self->types & mlt_prop_int64 )
585                 {
586                         self->types |= mlt_prop_string;
587                         self->prop_string = malloc( 32 );
588                         sprintf( self->prop_string, "%"PRId64, self->prop_int64 );
589                 }
590                 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
591                 {
592                         self->types |= mlt_prop_string;
593                         self->prop_string = self->serialiser( self->data, self->length );
594                 }
595                 pthread_mutex_unlock( &self->mutex );
596         }
597
598         // Return the string (may be NULL)
599         return self->prop_string;
600 }
601
602 /** Get the property as a string (with locale).
603  *
604  * The caller is not responsible for deallocating the returned string!
605  * The string is deallocated when the Property is closed.
606  * This tries its hardest to convert the property to string including using
607  * a serialization function for binary data, if supplied.
608  * \public \memberof mlt_property_s
609  * \param self a property
610  * \param locale the locale to use for this conversion
611  * \return a string representation of the property or NULL if failed
612  */
613
614 char *mlt_property_get_string_l( mlt_property self, locale_t locale )
615 {
616         // Optimization for no locale
617         if ( !locale )
618                 return mlt_property_get_string( self );
619
620         // Construct a string if need be
621         if ( ! ( self->types & mlt_prop_string ) )
622         {
623                 // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale.
624                 // Save the current locale
625 #if defined(__DARWIN__)
626                 const char *localename = querylocale( LC_NUMERIC, locale );
627 #elif defined(__GLIBC__)
628                 const char *localename = locale->__names[ LC_NUMERIC ];
629 #else
630                 // TODO: not yet sure what to do on other platforms
631                 const char *localename = "";
632 #endif
633                 // Protect damaging the global locale from a temporary locale on another thread.
634                 pthread_mutex_lock( &self->mutex );
635
636                 // Get the current locale
637                 char *orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
638
639                 // Set the new locale
640                 setlocale( LC_NUMERIC, localename );
641
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, "%f", 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                 // Restore the current locale
672                 setlocale( LC_NUMERIC, orig_localename );
673                 free( orig_localename );
674                 pthread_mutex_unlock( &self->mutex );
675         }
676
677         // Return the string (may be NULL)
678         return self->prop_string;
679 }
680
681 /** Get the binary data from a property.
682  *
683  * This only works if you previously put binary data into the property.
684  * This does not return a copy of the data; it returns a pointer to it.
685  * If you supplied a destructor function when setting the binary data,
686  * the destructor is used when the Property is closed to free the memory.
687  * Therefore, only free the returned pointer if you did not supply a
688  * destructor function.
689  * \public \memberof mlt_property_s
690  * \param self a property
691  * \param[out] length the size of the binary object in bytes (optional)
692  * \return an opaque data pointer or NULL if not available
693  */
694
695 void *mlt_property_get_data( mlt_property self, int *length )
696 {
697         // Assign length if not NULL
698         if ( length != NULL )
699                 *length = self->length;
700
701         // Return the data (note: there is no conversion here)
702         return self->data;
703 }
704
705 /** Destroy a property and free all related resources.
706  *
707  * \public \memberof mlt_property_s
708  * \param self a property
709  */
710
711 void mlt_property_close( mlt_property self )
712 {
713         mlt_property_clear( self );
714         pthread_mutex_destroy( &self->mutex );
715         free( self );
716 }
717
718 /** Copy a property.
719  *
720  * A Property holding binary data only copies the data if a serialiser
721  * function was supplied when you set the Property.
722  * \public \memberof mlt_property_s
723  * \author Zach <zachary.drew@gmail.com>
724  * \param self a property
725  * \param that another property
726  */
727 void mlt_property_pass( mlt_property self, mlt_property that )
728 {
729         pthread_mutex_lock( &self->mutex );
730         mlt_property_clear( self );
731
732         self->types = that->types;
733
734         if ( self->types & mlt_prop_int64 )
735                 self->prop_int64 = that->prop_int64;
736         else if ( self->types & mlt_prop_int )
737                 self->prop_int = that->prop_int;
738         else if ( self->types & mlt_prop_double )
739                 self->prop_double = that->prop_double;
740         else if ( self->types & mlt_prop_position )
741                 self->prop_position = that->prop_position;
742         if ( self->types & mlt_prop_string )
743         {
744                 if ( that->prop_string != NULL )
745                         self->prop_string = strdup( that->prop_string );
746         }
747         else if ( self->types & mlt_prop_data && self->serialiser != NULL )
748         {
749                 self->types = mlt_prop_string;
750                 self->prop_string = self->serialiser( self->data, self->length );
751         }
752         pthread_mutex_unlock( &self->mutex );
753 }
754
755 /** Convert frame count to a SMPTE timecode string.
756  *
757  * \private \memberof mlt_property_s
758  * \param frames a frame count
759  * \param fps frames per second
760  * \param[out] s the string to write into - must have enough space to hold largest time string
761  */
762
763 static void time_smpte_from_frames( int frames, double fps, char *s )
764 {
765         int hours, mins, secs;
766         char frame_sep = ':';
767
768         if ( fps == 30000.0/1001.0 )
769         {
770                 fps = 30.0;
771                 int i, max_frames = frames;
772                 for ( i = 1800; i <= max_frames; i += 1800 )
773                 {
774                         if ( i % 18000 )
775                         {
776                                 max_frames += 2;
777                                 frames += 2;
778                         }
779                 }
780                 frame_sep = ';';
781         }
782         hours = frames / ( fps * 3600 );
783         frames -= hours * ( fps * 3600 );
784         mins = frames / ( fps * 60 );
785         frames -= mins * ( fps * 60 );
786         secs = frames / fps;
787         frames -= secs * fps;
788
789         sprintf( s, "%02d:%02d:%02d%c%02d", hours, mins, secs, frame_sep, frames );
790 }
791
792 /** Convert frame count to a SMIL clock value string.
793  *
794  * \private \memberof mlt_property_s
795  * \param frames a frame count
796  * \param fps frames per second
797  * \param[out] s the string to write into - must have enough space to hold largest time string
798  */
799
800 static void time_clock_from_frames( int frames, double fps, char *s )
801 {
802         int hours, mins;
803         double secs;
804
805         hours = frames / ( fps * 3600 );
806         frames -= hours * ( fps * 3600 );
807         mins = frames / ( fps * 60 );
808         frames -= mins * ( fps * 60 );
809         secs = (double) frames / fps;
810
811         sprintf( s, "%02d:%02d:%06.3f", hours, mins, secs );
812 }
813
814 /** Get the property as a time string.
815  *
816  * The time value can be either a SMPTE timecode or SMIL clock value.
817  * The caller is not responsible for deallocating the returned string!
818  * The string is deallocated when the property is closed.
819  * \public \memberof mlt_property_s
820  * \param self a property
821  * \param format the time format that you want
822  * \param fps frames per second
823  * \param locale the locale to use for this conversion
824  * \return a string representation of the property or NULL if failed
825  */
826
827 char *mlt_property_get_time( mlt_property self, mlt_time_format format, double fps, locale_t locale )
828 {
829         char *orig_localename = NULL;
830         const char *localename = "";
831
832         // Optimization for mlt_time_frames
833         if ( format == mlt_time_frames )
834                 return mlt_property_get_string_l( self, locale );
835
836         // Remove existing string
837         if ( self->prop_string )
838                 mlt_property_set_int( self, mlt_property_get_int( self, fps, locale ) );
839
840         // Use the specified locale
841         if ( locale )
842         {
843                 // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale.
844                 // Save the current locale
845 #if defined(__DARWIN__)
846                 localename = querylocale( LC_NUMERIC, locale );
847 #elif defined(__GLIBC__)
848                 localename = locale->__names[ LC_NUMERIC ];
849 #else
850                 // TODO: not yet sure what to do on other platforms
851 #endif
852                 // Protect damaging the global locale from a temporary locale on another thread.
853                 pthread_mutex_lock( &self->mutex );
854
855                 // Get the current locale
856                 orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
857
858                 // Set the new locale
859                 setlocale( LC_NUMERIC, localename );
860         }
861         else
862         {
863                 // Make sure we have a lock before accessing self->types
864                 pthread_mutex_lock( &self->mutex );
865         }
866
867         // Convert number to string
868         if ( self->types & mlt_prop_int )
869         {
870                 self->types |= mlt_prop_string;
871                 self->prop_string = malloc( 32 );
872                 if ( format == mlt_time_clock )
873                         time_clock_from_frames( self->prop_int, fps, self->prop_string );
874                 else
875                         time_smpte_from_frames( self->prop_int, fps, self->prop_string );
876         }
877         else if ( self->types & mlt_prop_position )
878         {
879                 self->types |= mlt_prop_string;
880                 self->prop_string = malloc( 32 );
881                 if ( format == mlt_time_clock )
882                         time_clock_from_frames( (int) self->prop_position, fps, self->prop_string );
883                 else
884                         time_smpte_from_frames( (int) self->prop_position, fps, self->prop_string );
885         }
886         else if ( self->types & mlt_prop_double )
887         {
888                 self->types |= mlt_prop_string;
889                 self->prop_string = malloc( 32 );
890                 if ( format == mlt_time_clock )
891                         time_clock_from_frames( self->prop_double, fps, self->prop_string );
892                 else
893                         time_smpte_from_frames( self->prop_double, fps, self->prop_string );
894         }
895         else if ( self->types & mlt_prop_int64 )
896         {
897                 self->types |= mlt_prop_string;
898                 self->prop_string = malloc( 32 );
899                 if ( format == mlt_time_clock )
900                         time_clock_from_frames( (int) self->prop_int64, fps, self->prop_string );
901                 else
902                         time_smpte_from_frames( (int) self->prop_int64, fps, self->prop_string );
903         }
904
905         // Restore the current locale
906         if ( locale )
907         {
908                 setlocale( LC_NUMERIC, orig_localename );
909                 free( orig_localename );
910                 pthread_mutex_unlock( &self->mutex );
911         }
912         else
913         {
914                 // Make sure we have a lock before accessing self->types
915                 pthread_mutex_unlock( &self->mutex );
916         }
917
918         // Return the string (may be NULL)
919         return self->prop_string;
920 }
921
922 static int is_property_numeric( mlt_property self, locale_t locale )
923 {
924         int result = ( self->types & mlt_prop_int ) ||
925                         ( self->types & mlt_prop_int64 ) ||
926                         ( self->types & mlt_prop_double ) ||
927                         ( self->types & mlt_prop_position );
928
929         // If not already numeric but string is numeric.
930         if ( ( !result && self->types & mlt_prop_string ) && self->prop_string )
931         {
932                 double temp;
933                 char *p = NULL;
934 #if defined(__GLIBC__) || defined(__DARWIN__)
935                 if ( locale )
936                         temp = strtod_l( self->prop_string, &p, locale );
937                 else
938 #endif
939                 temp = strtod( self->prop_string, &p );
940                 result = ( p != self->prop_string );
941         }
942         return result;
943 }
944
945 static inline double linearstep( double start, double end, double position, int length )
946 {
947         double o = ( end - start ) / length;
948         return start + position * o;
949 }
950
951 int mlt_property_interpolate(mlt_property self, mlt_property previous, mlt_property next,
952         double position, int length, double fps, locale_t locale )
953 {
954         int error = 0;
955         if ( fps > 0 && is_property_numeric( previous, locale ) && is_property_numeric( next, locale ) )
956         {
957                 double start = previous? mlt_property_get_double( previous, fps, locale ) : 0;
958                 double end = next? mlt_property_get_double( next, fps, locale ) : 0;
959                 double value = next? linearstep( start, end, position, length ) : start;
960                 error = mlt_property_set_double( self, value );
961         }
962         else
963         {
964                 mlt_property_pass( self, previous );
965         }
966         return error;
967 }
968
969 static void refresh_animation( mlt_property self, double fps, locale_t locale, int length  )
970 {
971         if ( !self->animation )
972         {
973                 self->animation = mlt_animation_new();
974                 mlt_animation_parse( self->animation, self->prop_string, length, fps, locale );
975         }
976         else
977         {
978                 mlt_animation_refresh( self->animation, self->prop_string, length );
979         }
980 }
981
982 double mlt_property_get_double_pos( mlt_property self, double fps, locale_t locale, int position, int length )
983 {
984         double result;
985         if ( ( self->types & mlt_prop_string ) && self->prop_string )
986         {
987                 struct mlt_animation_item_s item;
988                 item.property = mlt_property_init();
989
990                 refresh_animation( self, fps, locale, length );
991                 mlt_animation_get_item( self->animation, &item, position );
992                 result = mlt_property_get_double( item.property, fps, locale );
993
994                 mlt_property_close( item.property );
995         }
996         else
997         {
998                 result = mlt_property_get_double( self, fps, locale );
999         }
1000         return result;
1001 }
1002
1003 int mlt_property_get_int_pos( mlt_property self, double fps, locale_t locale, int position, int length )
1004 {
1005         int result;
1006         if ( ( self->types & mlt_prop_string ) && self->prop_string )
1007         {
1008                 struct mlt_animation_item_s item;
1009                 item.property = mlt_property_init();
1010
1011                 refresh_animation( self, fps, locale, length );
1012                 mlt_animation_get_item( self->animation, &item, position );
1013                 result = mlt_property_get_int( item.property, fps, locale );
1014
1015                 mlt_property_close( item.property );
1016         }
1017         else
1018         {
1019                 result = mlt_property_get_int( self, fps, locale );
1020         }
1021         return result;
1022 }