3 * \brief Property class definition
6 * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
7 * \author Charles Yates <charles.yates@pandora.be>
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.
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.
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
29 #include "mlt_property.h"
30 #include "mlt_animation.h"
40 /** Bit pattern used internally to indicated representations available.
45 mlt_prop_none = 0, //!< not set
46 mlt_prop_int = 1, //!< set as an integer
47 mlt_prop_string = 2, //!< set as string or already converted to string
48 mlt_prop_position = 4,//!< set as a position
49 mlt_prop_double = 8, //!< set as a floating point
50 mlt_prop_data = 16, //!< set as opaque binary
51 mlt_prop_int64 = 32, //!< set as a 64-bit integer
52 mlt_prop_rect = 64 //!< set as a mlt_rect
56 /** \brief Property class
58 * A property is like a variant or dynamic type. They are used for many things
59 * in MLT, but in particular they are the parameter mechanism for the plugins.
64 /// Stores a bit pattern of types available for this property
65 mlt_property_type types;
67 /// Atomic type handling
69 mlt_position prop_position;
76 /// Generic type handling
79 mlt_destructor destructor;
80 mlt_serialiser serialiser;
82 pthread_mutex_t mutex;
83 mlt_animation animation;
86 /** Construct a property and initialize it
87 * \public \memberof mlt_property_s
90 mlt_property mlt_property_init( )
92 mlt_property self = calloc( 1, sizeof( *self ) );
94 pthread_mutex_init( &self->mutex, NULL );
98 /** Clear (0/null) a property.
100 * Frees up any associated resources in the process.
101 * \private \memberof mlt_property_s
102 * \param self a property
105 static inline void mlt_property_clear( mlt_property self )
107 // Special case data handling
108 if ( self->types & mlt_prop_data && self->destructor != NULL )
109 self->destructor( self->data );
111 // Special case string handling
112 if ( self->types & mlt_prop_string )
113 free( self->prop_string );
115 if ( self->animation )
116 mlt_animation_close( self->animation );
121 self->prop_position = 0;
122 self->prop_double = 0;
123 self->prop_int64 = 0;
124 self->prop_string = NULL;
127 self->destructor = NULL;
128 self->serialiser = NULL;
129 self->animation = NULL;
132 /** Set the property to an integer value.
134 * \public \memberof mlt_property_s
135 * \param self a property
136 * \param value an integer
140 int mlt_property_set_int( mlt_property self, int value )
142 pthread_mutex_lock( &self->mutex );
143 mlt_property_clear( self );
144 self->types = mlt_prop_int;
145 self->prop_int = value;
146 pthread_mutex_unlock( &self->mutex );
150 /** Set the property to a floating point value.
152 * \public \memberof mlt_property_s
153 * \param self a property
154 * \param value a double precision floating point value
158 int mlt_property_set_double( mlt_property self, double value )
160 pthread_mutex_lock( &self->mutex );
161 mlt_property_clear( self );
162 self->types = mlt_prop_double;
163 self->prop_double = value;
164 pthread_mutex_unlock( &self->mutex );
168 /** Set the property to a position value.
170 * Position is a relative time value in frame units.
171 * \public \memberof mlt_property_s
172 * \param self a property
173 * \param value a position value
177 int mlt_property_set_position( mlt_property self, mlt_position value )
179 pthread_mutex_lock( &self->mutex );
180 mlt_property_clear( self );
181 self->types = mlt_prop_position;
182 self->prop_position = value;
183 pthread_mutex_unlock( &self->mutex );
187 /** Set the property to a string value.
189 * This makes a copy of the string you supply so you do not need to track
190 * a new reference to it.
191 * \public \memberof mlt_property_s
192 * \param self a property
193 * \param value the string to copy to the property
194 * \return true if it failed
197 int mlt_property_set_string( mlt_property self, const char *value )
199 pthread_mutex_lock( &self->mutex );
200 if ( value != self->prop_string )
202 mlt_property_clear( self );
203 self->types = mlt_prop_string;
205 self->prop_string = strdup( value );
209 self->types = mlt_prop_string;
211 pthread_mutex_unlock( &self->mutex );
212 return self->prop_string == NULL;
215 /** Set the property to a 64-bit integer value.
217 * \public \memberof mlt_property_s
218 * \param self a property
219 * \param value a 64-bit integer
223 int mlt_property_set_int64( mlt_property self, int64_t value )
225 pthread_mutex_lock( &self->mutex );
226 mlt_property_clear( self );
227 self->types = mlt_prop_int64;
228 self->prop_int64 = value;
229 pthread_mutex_unlock( &self->mutex );
233 /** Set a property to an opaque binary value.
235 * This does not make a copy of the data. You can use a Properties object
236 * with its reference tracking and the destructor function to control
237 * the lifetime of the data. Otherwise, pass NULL for the destructor
238 * function and control the lifetime yourself.
239 * \public \memberof mlt_property_s
240 * \param self a property
241 * \param value an opaque pointer
242 * \param length the number of bytes pointed to by value (optional)
243 * \param destructor a function to use to destroy this binary data (optional, assuming you manage the resource)
244 * \param serialiser a function to use to convert this binary data to a string (optional)
248 int mlt_property_set_data( mlt_property self, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser )
250 pthread_mutex_lock( &self->mutex );
251 if ( self->data == value )
252 self->destructor = NULL;
253 mlt_property_clear( self );
254 self->types = mlt_prop_data;
256 self->length = length;
257 self->destructor = destructor;
258 self->serialiser = serialiser;
259 pthread_mutex_unlock( &self->mutex );
263 /** Parse a SMIL clock value.
265 * \private \memberof mlt_property_s
266 * \param s the string to parse
267 * \param fps frames per second
268 * \param locale the locale to use for parsing a real number value
269 * \return position in frames
272 static int time_clock_to_frames( const char *s, double fps, locale_t locale )
274 char *pos, *copy = strdup( s );
275 int hours = 0, minutes = 0;
279 pos = strrchr( s, ':' );
281 #if defined(__GLIBC__) || defined(__DARWIN__)
283 seconds = strtod_l( pos + 1, NULL, locale );
286 seconds = strtod( pos + 1, NULL );
288 pos = strrchr( s, ':' );
290 minutes = atoi( pos + 1 );
299 #if defined(__GLIBC__) || defined(__DARWIN__)
301 seconds = strtod_l( s, NULL, locale );
304 seconds = strtod( s, NULL );
308 return fps * ( (hours * 3600) + (minutes * 60) + seconds ) + 0.5;
311 /** Parse a SMPTE timecode string.
313 * \private \memberof mlt_property_s
314 * \param s the string to parse
315 * \param fps frames per second
316 * \return position in frames
319 static int time_code_to_frames( const char *s, double fps )
321 char *pos, *copy = strdup( s );
322 int hours = 0, minutes = 0, seconds = 0, frames;
325 pos = strrchr( s, ';' );
327 pos = strrchr( s, ':' );
329 frames = atoi( pos + 1 );
331 pos = strrchr( s, ':' );
333 seconds = atoi( pos + 1 );
335 pos = strrchr( s, ':' );
337 minutes = atoi( pos + 1 );
354 return frames + ( fps * ( (hours * 3600) + (minutes * 60) + seconds ) + 0.5 );
357 /** Convert a string to an integer.
359 * The string must begin with '0x' to be interpreted as hexadecimal.
360 * Otherwise, it is interpreted as base 10.
362 * If the string begins with '#' it is interpreted as a hexadecimal color value
363 * in the form RRGGBB or AARRGGBB. Color values that begin with '0x' are
364 * always in the form RRGGBBAA where the alpha components are not optional.
365 * Applications and services should expect the binary color value in bytes to
366 * be in the following order: RGBA. This means they will have to cast the int
367 * to an unsigned int. This is especially important when they need to shift
368 * right to obtain RGB without alpha in order to make it do a logical instead
369 * of arithmetic shift.
371 * If the string contains a colon it is interpreted as a time value. If it also
372 * contains a period or comma character, the string is parsed as a clock value:
373 * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF.
374 * \private \memberof mlt_property_s
375 * \param value a string to convert
376 * \param fps frames per second, used when converting from time value
377 * \param locale the locale to use when converting from time clock value
378 * \return the resultant integer
380 static int mlt_property_atoi( const char *value, double fps, locale_t locale )
382 // Parse a hex color value as #RRGGBB or #AARRGGBB.
383 if ( value[0] == '#' )
385 unsigned int rgb = strtoul( value + 1, NULL, 16 );
386 unsigned int alpha = ( strlen( value ) > 7 ) ? ( rgb >> 24 ) : 0xff;
387 return ( rgb << 8 ) | alpha;
389 // Do hex and decimal explicitly to avoid decimal value with leading zeros
390 // interpreted as octal.
391 else if ( value[0] == '0' && value[1] == 'x' )
393 return strtoul( value + 2, NULL, 16 );
395 else if ( fps > 0 && strchr( value, ':' ) )
397 if ( strchr( value, '.' ) || strchr( value, ',' ) )
398 return time_clock_to_frames( value, fps, locale );
400 return time_code_to_frames( value, fps );
404 return strtol( value, NULL, 10 );
408 /** Get the property as an integer.
410 * \public \memberof mlt_property_s
411 * \param self a property
412 * \param fps frames per second, used when converting from time value
413 * \param locale the locale to use when converting from time clock value
414 * \return an integer value
417 int mlt_property_get_int( mlt_property self, double fps, locale_t locale )
419 if ( self->types & mlt_prop_int )
420 return self->prop_int;
421 else if ( self->types & mlt_prop_double )
422 return ( int )self->prop_double;
423 else if ( self->types & mlt_prop_position )
424 return ( int )self->prop_position;
425 else if ( self->types & mlt_prop_int64 )
426 return ( int )self->prop_int64;
427 else if ( self->types & mlt_prop_rect && self->data )
428 return ( int ) ( (mlt_rect*) self->data )->x;
429 else if ( ( self->types & mlt_prop_string ) && self->prop_string )
430 return mlt_property_atoi( self->prop_string, fps, locale );
434 /** Convert a string to a floating point number.
436 * If the string contains a colon it is interpreted as a time value. If it also
437 * contains a period or comma character, the string is parsed as a clock value:
438 * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF.
439 * If the numeric string ends with '%' then the value is divided by 100 to convert
441 * \private \memberof mlt_property_s
442 * \param value the string to convert
443 * \param fps frames per second, used when converting from time value
444 * \param locale the locale to use when converting from time clock value
445 * \return the resultant real number
447 static double mlt_property_atof( const char *value, double fps, locale_t locale )
449 if ( fps > 0 && strchr( value, ':' ) )
451 if ( strchr( value, '.' ) || strchr( value, ',' ) )
452 return time_clock_to_frames( value, fps, locale );
454 return time_code_to_frames( value, fps );
460 #if defined(__GLIBC__) || defined(__DARWIN__)
462 result = strtod_l( value, &end, locale );
465 result = strtod( value, &end );
466 if ( end && end[0] == '%' )
472 /** Get the property as a floating point.
474 * \public \memberof mlt_property_s
475 * \param self a property
476 * \param fps frames per second, used when converting from time value
477 * \param locale the locale to use for this conversion
478 * \return a floating point value
481 double mlt_property_get_double( mlt_property self, double fps, locale_t locale )
483 if ( self->types & mlt_prop_double )
484 return self->prop_double;
485 else if ( self->types & mlt_prop_int )
486 return ( double )self->prop_int;
487 else if ( self->types & mlt_prop_position )
488 return ( double )self->prop_position;
489 else if ( self->types & mlt_prop_int64 )
490 return ( double )self->prop_int64;
491 else if ( self->types & mlt_prop_rect && self->data )
492 return ( (mlt_rect*) self->data )->x;
493 else if ( ( self->types & mlt_prop_string ) && self->prop_string )
494 return mlt_property_atof( self->prop_string, fps, locale );
498 /** Get the property as a position.
500 * A position is an offset time in terms of frame units.
501 * \public \memberof mlt_property_s
502 * \param self a property
503 * \param fps frames per second, used when converting from time value
504 * \param locale the locale to use when converting from time clock value
505 * \return the position in frames
508 mlt_position mlt_property_get_position( mlt_property self, double fps, locale_t locale )
510 if ( self->types & mlt_prop_position )
511 return self->prop_position;
512 else if ( self->types & mlt_prop_int )
513 return ( mlt_position )self->prop_int;
514 else if ( self->types & mlt_prop_double )
515 return ( mlt_position )self->prop_double;
516 else if ( self->types & mlt_prop_int64 )
517 return ( mlt_position )self->prop_int64;
518 else if ( self->types & mlt_prop_rect && self->data )
519 return ( mlt_position ) ( (mlt_rect*) self->data )->x;
520 else if ( ( self->types & mlt_prop_string ) && self->prop_string )
521 return ( mlt_position )mlt_property_atoi( self->prop_string, fps, locale );
525 /** Convert a string to a 64-bit integer.
527 * If the string begins with '0x' it is interpreted as a hexadecimal value.
528 * \private \memberof mlt_property_s
529 * \param value a string
530 * \return a 64-bit integer
533 static inline int64_t mlt_property_atoll( const char *value )
537 else if ( value[0] == '0' && value[1] == 'x' )
538 return strtoll( value + 2, NULL, 16 );
540 return strtoll( value, NULL, 10 );
543 /** Get the property as a signed integer.
545 * \public \memberof mlt_property_s
546 * \param self a property
547 * \return a 64-bit integer
550 int64_t mlt_property_get_int64( mlt_property self )
552 if ( self->types & mlt_prop_int64 )
553 return self->prop_int64;
554 else if ( self->types & mlt_prop_int )
555 return ( int64_t )self->prop_int;
556 else if ( self->types & mlt_prop_double )
557 return ( int64_t )self->prop_double;
558 else if ( self->types & mlt_prop_position )
559 return ( int64_t )self->prop_position;
560 else if ( self->types & mlt_prop_rect && self->data )
561 return ( int64_t ) ( (mlt_rect*) self->data )->x;
562 else if ( ( self->types & mlt_prop_string ) && self->prop_string )
563 return mlt_property_atoll( self->prop_string );
567 /** Get the property as a string.
569 * The caller is not responsible for deallocating the returned string!
570 * The string is deallocated when the Property is closed.
571 * This tries its hardest to convert the property to string including using
572 * a serialization function for binary data, if supplied.
573 * \public \memberof mlt_property_s
574 * \param self a property
575 * \return a string representation of the property or NULL if failed
578 char *mlt_property_get_string( mlt_property self )
580 // Construct a string if need be
581 if ( ! ( self->types & mlt_prop_string ) )
583 pthread_mutex_lock( &self->mutex );
584 if ( self->types & mlt_prop_int )
586 self->types |= mlt_prop_string;
587 self->prop_string = malloc( 32 );
588 sprintf( self->prop_string, "%d", self->prop_int );
590 else if ( self->types & mlt_prop_double )
592 self->types |= mlt_prop_string;
593 self->prop_string = malloc( 32 );
594 sprintf( self->prop_string, "%f", self->prop_double );
596 else if ( self->types & mlt_prop_position )
598 self->types |= mlt_prop_string;
599 self->prop_string = malloc( 32 );
600 sprintf( self->prop_string, "%d", (int)self->prop_position );
602 else if ( self->types & mlt_prop_int64 )
604 self->types |= mlt_prop_string;
605 self->prop_string = malloc( 32 );
606 sprintf( self->prop_string, "%"PRId64, self->prop_int64 );
608 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
610 self->types |= mlt_prop_string;
611 self->prop_string = self->serialiser( self->data, self->length );
613 pthread_mutex_unlock( &self->mutex );
616 // Return the string (may be NULL)
617 return self->prop_string;
620 /** Get the property as a string (with locale).
622 * The caller is not responsible for deallocating the returned string!
623 * The string is deallocated when the Property is closed.
624 * This tries its hardest to convert the property to string including using
625 * a serialization function for binary data, if supplied.
626 * \public \memberof mlt_property_s
627 * \param self a property
628 * \param locale the locale to use for this conversion
629 * \return a string representation of the property or NULL if failed
632 char *mlt_property_get_string_l( mlt_property self, locale_t locale )
634 // Optimization for no locale
636 return mlt_property_get_string( self );
638 // Construct a string if need be
639 if ( ! ( self->types & mlt_prop_string ) )
641 // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale.
642 // Save the current locale
643 #if defined(__DARWIN__)
644 const char *localename = querylocale( LC_NUMERIC, locale );
645 #elif defined(__GLIBC__)
646 const char *localename = locale->__names[ LC_NUMERIC ];
648 // TODO: not yet sure what to do on other platforms
649 const char *localename = "";
651 // Protect damaging the global locale from a temporary locale on another thread.
652 pthread_mutex_lock( &self->mutex );
654 // Get the current locale
655 char *orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
657 // Set the new locale
658 setlocale( LC_NUMERIC, localename );
660 if ( self->types & mlt_prop_int )
662 self->types |= mlt_prop_string;
663 self->prop_string = malloc( 32 );
664 sprintf( self->prop_string, "%d", self->prop_int );
666 else if ( self->types & mlt_prop_double )
668 self->types |= mlt_prop_string;
669 self->prop_string = malloc( 32 );
670 sprintf( self->prop_string, "%f", self->prop_double );
672 else if ( self->types & mlt_prop_position )
674 self->types |= mlt_prop_string;
675 self->prop_string = malloc( 32 );
676 sprintf( self->prop_string, "%d", (int)self->prop_position );
678 else if ( self->types & mlt_prop_int64 )
680 self->types |= mlt_prop_string;
681 self->prop_string = malloc( 32 );
682 sprintf( self->prop_string, "%"PRId64, self->prop_int64 );
684 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
686 self->types |= mlt_prop_string;
687 self->prop_string = self->serialiser( self->data, self->length );
689 // Restore the current locale
690 setlocale( LC_NUMERIC, orig_localename );
691 free( orig_localename );
692 pthread_mutex_unlock( &self->mutex );
695 // Return the string (may be NULL)
696 return self->prop_string;
699 /** Get the binary data from a property.
701 * This only works if you previously put binary data into the property.
702 * This does not return a copy of the data; it returns a pointer to it.
703 * If you supplied a destructor function when setting the binary data,
704 * the destructor is used when the Property is closed to free the memory.
705 * Therefore, only free the returned pointer if you did not supply a
706 * destructor function.
707 * \public \memberof mlt_property_s
708 * \param self a property
709 * \param[out] length the size of the binary object in bytes (optional)
710 * \return an opaque data pointer or NULL if not available
713 void *mlt_property_get_data( mlt_property self, int *length )
715 // Assign length if not NULL
716 if ( length != NULL )
717 *length = self->length;
719 // Return the data (note: there is no conversion here)
723 /** Destroy a property and free all related resources.
725 * \public \memberof mlt_property_s
726 * \param self a property
729 void mlt_property_close( mlt_property self )
731 mlt_property_clear( self );
732 pthread_mutex_destroy( &self->mutex );
738 * A Property holding binary data only copies the data if a serialiser
739 * function was supplied when you set the Property.
740 * \public \memberof mlt_property_s
741 * \author Zach <zachary.drew@gmail.com>
742 * \param self a property
743 * \param that another property
745 void mlt_property_pass( mlt_property self, mlt_property that )
747 pthread_mutex_lock( &self->mutex );
748 mlt_property_clear( self );
750 self->types = that->types;
752 if ( self->types & mlt_prop_int64 )
753 self->prop_int64 = that->prop_int64;
754 else if ( self->types & mlt_prop_int )
755 self->prop_int = that->prop_int;
756 else if ( self->types & mlt_prop_double )
757 self->prop_double = that->prop_double;
758 else if ( self->types & mlt_prop_position )
759 self->prop_position = that->prop_position;
760 if ( self->types & mlt_prop_string )
762 if ( that->prop_string != NULL )
763 self->prop_string = strdup( that->prop_string );
765 else if ( that->types & mlt_prop_rect )
767 mlt_property_clear( self );
768 self->types = mlt_prop_rect | mlt_prop_data;
769 self->length = that->length;
770 self->data = calloc( 1, self->length );
771 memcpy( self->data, that->data, self->length );
772 self->destructor = free;
773 self->serialiser = that->serialiser;
775 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
777 self->types = mlt_prop_string;
778 self->prop_string = self->serialiser( self->data, self->length );
780 pthread_mutex_unlock( &self->mutex );
783 /** Convert frame count to a SMPTE timecode string.
785 * \private \memberof mlt_property_s
786 * \param frames a frame count
787 * \param fps frames per second
788 * \param[out] s the string to write into - must have enough space to hold largest time string
791 static void time_smpte_from_frames( int frames, double fps, char *s )
793 int hours, mins, secs;
794 char frame_sep = ':';
796 if ( fps == 30000.0/1001.0 )
799 int i, max_frames = frames;
800 for ( i = 1800; i <= max_frames; i += 1800 )
810 hours = frames / ( fps * 3600 );
811 frames -= hours * ( fps * 3600 );
812 mins = frames / ( fps * 60 );
813 frames -= mins * ( fps * 60 );
815 frames -= secs * fps;
817 sprintf( s, "%02d:%02d:%02d%c%02d", hours, mins, secs, frame_sep, frames );
820 /** Convert frame count to a SMIL clock value string.
822 * \private \memberof mlt_property_s
823 * \param frames a frame count
824 * \param fps frames per second
825 * \param[out] s the string to write into - must have enough space to hold largest time string
828 static void time_clock_from_frames( int frames, double fps, char *s )
833 hours = frames / ( fps * 3600 );
834 frames -= hours * ( fps * 3600 );
835 mins = frames / ( fps * 60 );
836 frames -= mins * ( fps * 60 );
837 secs = (double) frames / fps;
839 sprintf( s, "%02d:%02d:%06.3f", hours, mins, secs );
842 /** Get the property as a time string.
844 * The time value can be either a SMPTE timecode or SMIL clock value.
845 * The caller is not responsible for deallocating the returned string!
846 * The string is deallocated when the property is closed.
847 * \public \memberof mlt_property_s
848 * \param self a property
849 * \param format the time format that you want
850 * \param fps frames per second
851 * \param locale the locale to use for this conversion
852 * \return a string representation of the property or NULL if failed
855 char *mlt_property_get_time( mlt_property self, mlt_time_format format, double fps, locale_t locale )
857 char *orig_localename = NULL;
858 const char *localename = "";
860 // Optimization for mlt_time_frames
861 if ( format == mlt_time_frames )
862 return mlt_property_get_string_l( self, locale );
864 // Remove existing string
865 if ( self->prop_string )
866 mlt_property_set_int( self, mlt_property_get_int( self, fps, locale ) );
868 // Use the specified locale
871 // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale.
872 // Save the current locale
873 #if defined(__DARWIN__)
874 localename = querylocale( LC_NUMERIC, locale );
875 #elif defined(__GLIBC__)
876 localename = locale->__names[ LC_NUMERIC ];
878 // TODO: not yet sure what to do on other platforms
880 // Protect damaging the global locale from a temporary locale on another thread.
881 pthread_mutex_lock( &self->mutex );
883 // Get the current locale
884 orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
886 // Set the new locale
887 setlocale( LC_NUMERIC, localename );
891 // Make sure we have a lock before accessing self->types
892 pthread_mutex_lock( &self->mutex );
895 // Convert number to string
896 if ( self->types & mlt_prop_int )
898 self->types |= mlt_prop_string;
899 self->prop_string = malloc( 32 );
900 if ( format == mlt_time_clock )
901 time_clock_from_frames( self->prop_int, fps, self->prop_string );
903 time_smpte_from_frames( self->prop_int, fps, self->prop_string );
905 else if ( self->types & mlt_prop_position )
907 self->types |= mlt_prop_string;
908 self->prop_string = malloc( 32 );
909 if ( format == mlt_time_clock )
910 time_clock_from_frames( (int) self->prop_position, fps, self->prop_string );
912 time_smpte_from_frames( (int) self->prop_position, fps, self->prop_string );
914 else if ( self->types & mlt_prop_double )
916 self->types |= mlt_prop_string;
917 self->prop_string = malloc( 32 );
918 if ( format == mlt_time_clock )
919 time_clock_from_frames( self->prop_double, fps, self->prop_string );
921 time_smpte_from_frames( self->prop_double, fps, self->prop_string );
923 else if ( self->types & mlt_prop_int64 )
925 self->types |= mlt_prop_string;
926 self->prop_string = malloc( 32 );
927 if ( format == mlt_time_clock )
928 time_clock_from_frames( (int) self->prop_int64, fps, self->prop_string );
930 time_smpte_from_frames( (int) self->prop_int64, fps, self->prop_string );
933 // Restore the current locale
936 setlocale( LC_NUMERIC, orig_localename );
937 free( orig_localename );
938 pthread_mutex_unlock( &self->mutex );
942 // Make sure we have a lock before accessing self->types
943 pthread_mutex_unlock( &self->mutex );
946 // Return the string (may be NULL)
947 return self->prop_string;
950 static int is_property_numeric( mlt_property self, locale_t locale )
952 int result = ( self->types & mlt_prop_int ) ||
953 ( self->types & mlt_prop_int64 ) ||
954 ( self->types & mlt_prop_double ) ||
955 ( self->types & mlt_prop_position ) ||
956 ( self->types & mlt_prop_rect );
958 // If not already numeric but string is numeric.
959 if ( ( !result && self->types & mlt_prop_string ) && self->prop_string )
963 #if defined(__GLIBC__) || defined(__DARWIN__)
965 temp = strtod_l( self->prop_string, &p, locale );
968 temp = strtod( self->prop_string, &p );
969 result = ( p != self->prop_string );
974 static inline double linear_interpolate( double y1, double y2, double t )
976 return y1 + ( y2 - y1 ) * t;
979 // For non-closed curves, you need to also supply the tangent vector at the first and last control point.
980 // This is commonly done: T(P[0]) = P[1] - P[0] and T(P[n]) = P[n] - P[n-1].
981 static inline double catmull_rom_interpolate( double y0, double y1, double y2, double y3, double t )
984 double a0 = -0.5 * y0 + 1.5 * y1 - 1.5 * y2 + 0.5 * y3;
985 double a1 = y0 - 2.5 * y1 + 2 * y2 - 0.5 * y3;
986 double a2 = -0.5 * y0 + 0.5 * y2;
988 return a0 * t * t2 + a1 * t2 + a2 * t + a3;
991 int mlt_property_interpolate( mlt_property self, mlt_property p[],
992 double progress, double fps, locale_t locale, mlt_keyframe_type interp )
995 if ( interp != mlt_keyframe_discrete &&
996 is_property_numeric( p[1], locale ) && is_property_numeric( p[2], locale ) )
998 if ( self->types & mlt_prop_rect )
1000 mlt_rect value = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN };
1001 if ( interp == mlt_keyframe_linear )
1004 mlt_rect zero = {0, 0, 0, 0, 0};
1005 points[0] = p[1]? mlt_property_get_rect( p[1], locale ) : zero;
1008 points[1] = mlt_property_get_rect( p[2], locale );
1009 value.x = linear_interpolate( points[0].x, points[1].x, progress );
1010 value.y = linear_interpolate( points[0].y, points[1].y, progress );
1011 value.w = linear_interpolate( points[0].w, points[1].w, progress );
1012 value.h = linear_interpolate( points[0].h, points[1].h, progress );
1013 value.o = linear_interpolate( points[0].o, points[1].o, progress );
1020 else if ( interp == mlt_keyframe_smooth )
1023 mlt_rect zero = {0, 0, 0, 0, 0};
1024 points[1] = p[1]? mlt_property_get_rect( p[1], locale ) : zero;
1027 points[0] = p[0]? mlt_property_get_rect( p[0], locale ) : zero;
1028 points[2] = p[2]? mlt_property_get_rect( p[2], locale ) : zero;
1029 points[3] = p[3]? mlt_property_get_rect( p[3], locale ) : zero;
1030 value.x = catmull_rom_interpolate( points[0].x, points[1].x, points[2].x, points[3].x, progress );
1031 value.y = catmull_rom_interpolate( points[0].y, points[1].y, points[2].y, points[3].y, progress );
1032 value.w = catmull_rom_interpolate( points[0].w, points[1].w, points[2].w, points[3].w, progress );
1033 value.h = catmull_rom_interpolate( points[0].h, points[1].h, points[2].h, points[3].h, progress );
1034 value.o = catmull_rom_interpolate( points[0].o, points[1].o, points[2].o, points[3].o, progress );
1041 error = mlt_property_set_rect( self, value );
1046 if ( interp == mlt_keyframe_linear )
1049 points[0] = p[1]? mlt_property_get_double( p[1], fps, locale ) : 0;
1050 points[1] = p[2]? mlt_property_get_double( p[2], fps, locale ) : 0;
1051 value = p[2]? linear_interpolate( points[0], points[1], progress ) : points[0];
1053 else if ( interp == mlt_keyframe_smooth )
1056 points[0] = p[0]? mlt_property_get_double( p[0], fps, locale ) : 0;
1057 points[1] = p[1]? mlt_property_get_double( p[1], fps, locale ) : 0;
1058 points[2] = p[2]? mlt_property_get_double( p[2], fps, locale ) : 0;
1059 points[3] = p[3]? mlt_property_get_double( p[3], fps, locale ) : 0;
1060 value = p[2]? catmull_rom_interpolate( points[0], points[1], points[2], points[3], progress ) : points[1];
1062 error = mlt_property_set_double( self, value );
1067 mlt_property_pass( self, p[1] );
1072 static void refresh_animation( mlt_property self, double fps, locale_t locale, int length )
1074 if ( !self->animation )
1076 self->animation = mlt_animation_new();
1077 if ( self->prop_string )
1079 mlt_animation_parse( self->animation, self->prop_string, length, fps, locale );
1083 mlt_animation_set_length( self->animation, length );
1084 self->types |= mlt_prop_data;
1085 self->data = self->animation;
1086 self->serialiser = (mlt_serialiser) mlt_animation_serialize;
1089 else if ( self->prop_string )
1091 mlt_animation_refresh( self->animation, self->prop_string, length );
1095 double mlt_property_get_double_pos( mlt_property self, double fps, locale_t locale, int position, int length )
1098 if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
1100 struct mlt_animation_item_s item;
1101 item.property = mlt_property_init();
1103 refresh_animation( self, fps, locale, length );
1104 mlt_animation_get_item( self->animation, &item, position );
1105 result = mlt_property_get_double( item.property, fps, locale );
1107 mlt_property_close( item.property );
1111 result = mlt_property_get_double( self, fps, locale );
1116 int mlt_property_get_int_pos( mlt_property self, double fps, locale_t locale, int position, int length )
1119 if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
1121 struct mlt_animation_item_s item;
1122 item.property = mlt_property_init();
1124 refresh_animation( self, fps, locale, length );
1125 mlt_animation_get_item( self->animation, &item, position );
1126 result = mlt_property_get_int( item.property, fps, locale );
1128 mlt_property_close( item.property );
1132 result = mlt_property_get_int( self, fps, locale );
1137 /** Set a property animation keyframe to a real number.
1139 * \public \memberof mlt_property_s
1140 * \param self a property
1141 * \param value a double precision floating point value
1142 * \return false if successful, true to indicate error
1145 int mlt_property_set_double_pos( mlt_property self, double value, double fps, locale_t locale,
1146 mlt_keyframe_type keyframe_type, int position, int length )
1149 struct mlt_animation_item_s item;
1151 item.property = mlt_property_init();
1152 item.frame = position;
1153 item.keyframe_type = keyframe_type;
1154 mlt_property_set_double( item.property, value );
1156 refresh_animation( self, fps, locale, length );
1157 result = mlt_animation_insert( self->animation, &item );
1158 mlt_animation_interpolate( self->animation );
1159 mlt_property_close( item.property );
1164 /** Set a property animation keyframe to an integer value.
1166 * \public \memberof mlt_property_s
1167 * \param self a property
1168 * \param value a double precision floating point value
1169 * \return false if successful, true to indicate error
1172 int mlt_property_set_int_pos( mlt_property self, int value, double fps, locale_t locale,
1173 mlt_keyframe_type keyframe_type, int position, int length )
1176 struct mlt_animation_item_s item;
1178 item.property = mlt_property_init();
1179 item.frame = position;
1180 item.keyframe_type = keyframe_type;
1181 mlt_property_set_int( item.property, value );
1183 refresh_animation( self, fps, locale, length );
1184 result = mlt_animation_insert( self->animation, &item );
1185 mlt_animation_interpolate( self->animation );
1186 mlt_property_close( item.property );
1191 static char* serialise_mlt_rect( mlt_rect *rect, int length )
1193 char* result = calloc( 1, 100 );
1194 if ( rect->x != DBL_MIN )
1195 sprintf( result + strlen( result ), "%g", rect->x );
1196 if ( rect->y != DBL_MIN )
1197 sprintf( result + strlen( result ), " %g", rect->y );
1198 if ( rect->w != DBL_MIN )
1199 sprintf( result + strlen( result ), " %g", rect->w );
1200 if ( rect->h != DBL_MIN )
1201 sprintf( result + strlen( result ), " %g", rect->h );
1202 if ( rect->o != DBL_MIN )
1203 sprintf( result + strlen( result ), " %g", rect->o );
1207 /** Set a property to a mlt_rect rectangle.
1209 * \public \memberof mlt_property_s
1210 * \param self a property
1211 * \param value a mlt_rect
1215 int mlt_property_set_rect( mlt_property self, mlt_rect value )
1217 pthread_mutex_lock( &self->mutex );
1218 mlt_property_clear( self );
1219 self->types = mlt_prop_rect | mlt_prop_data;
1220 self->length = sizeof(value);
1221 self->data = calloc( 1, self->length );
1222 memcpy( self->data, &value, self->length );
1223 self->destructor = free;
1224 self->serialiser = (mlt_serialiser) serialise_mlt_rect;
1225 pthread_mutex_unlock( &self->mutex );
1229 /** Get the property as a floating point.
1231 * \public \memberof mlt_property_s
1232 * \param self a property
1233 * \param fps frames per second, used when converting from time value
1234 * \param locale the locale to use for this conversion
1235 * \return a rectangle value
1238 mlt_rect mlt_property_get_rect( mlt_property self, locale_t locale )
1240 mlt_rect rect = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN };
1241 if ( self->types & mlt_prop_rect )
1242 rect = *( (mlt_rect*) self->data );
1243 else if ( self->types & mlt_prop_double )
1244 rect.x = self->prop_double;
1245 else if ( self->types & mlt_prop_int )
1246 rect.x = ( double )self->prop_int;
1247 else if ( self->types & mlt_prop_position )
1248 rect.x = ( double )self->prop_position;
1249 else if ( self->types & mlt_prop_int64 )
1250 rect.x = ( double )self->prop_int64;
1251 else if ( ( self->types & mlt_prop_string ) && self->prop_string )
1253 //return mlt_property_atof( self->prop_string, fps, locale );
1254 char *value = self->prop_string;
1260 #if defined(__GLIBC__) || defined(__DARWIN__)
1262 temp = strtod_l( value, &p, locale );
1265 temp = strtod( value, &p );
1273 case 0: rect.x = temp; break;
1274 case 1: rect.y = temp; break;
1275 case 2: rect.w = temp; break;
1276 case 3: rect.h = temp; break;
1277 case 4: rect.o = temp; break;
1291 /** Set a property animation keyframe to a rectangle.
1293 * \public \memberof mlt_property_s
1294 * \param self a property
1295 * \param value a rectangle
1296 * \return false if successful, true to indicate error
1299 int mlt_property_set_rect_pos( mlt_property self, mlt_rect value, double fps, locale_t locale,
1300 mlt_keyframe_type keyframe_type, int position, int length )
1303 struct mlt_animation_item_s item;
1305 item.property = mlt_property_init();
1306 item.frame = position;
1307 item.keyframe_type = keyframe_type;
1308 mlt_property_set_rect( item.property, value );
1310 refresh_animation( self, fps, locale, length );
1311 result = mlt_animation_insert( self->animation, &item );
1312 mlt_animation_interpolate( self->animation );
1313 mlt_property_close( item.property );
1318 mlt_rect mlt_property_get_rect_pos( mlt_property self, double fps, locale_t locale, int position, int length )
1321 if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
1323 struct mlt_animation_item_s item;
1324 item.property = mlt_property_init();
1325 item.property->types = mlt_prop_rect;
1327 refresh_animation( self, fps, locale, length );
1328 mlt_animation_get_item( self->animation, &item, position );
1329 result = mlt_property_get_rect( item.property, locale );
1331 mlt_property_close( item.property );
1335 result = mlt_property_get_rect( self, locale );