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