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