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