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