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"
41 /** Bit pattern used internally to indicated representations available.
46 mlt_prop_none = 0, //!< not set
47 mlt_prop_int = 1, //!< set as an integer
48 mlt_prop_string = 2, //!< set as string or already converted to string
49 mlt_prop_position = 4,//!< set as a position
50 mlt_prop_double = 8, //!< set as a floating point
51 mlt_prop_data = 16, //!< set as opaque binary
52 mlt_prop_int64 = 32, //!< set as a 64-bit integer
53 mlt_prop_rect = 64 //!< set as a mlt_rect
57 /** \brief Property class
59 * A property is like a variant or dynamic type. They are used for many things
60 * in MLT, but in particular they are the parameter mechanism for the plugins.
65 /// Stores a bit pattern of types available for this property
66 mlt_property_type types;
68 /// Atomic type handling
70 mlt_position prop_position;
77 /// Generic type handling
80 mlt_destructor destructor;
81 mlt_serialiser serialiser;
83 pthread_mutex_t mutex;
84 mlt_animation animation;
87 /** Construct a property and initialize it
88 * \public \memberof mlt_property_s
91 mlt_property mlt_property_init( )
93 mlt_property self = calloc( 1, sizeof( *self ) );
95 pthread_mutex_init( &self->mutex, NULL );
99 /** Clear (0/null) a property.
101 * Frees up any associated resources in the process.
102 * \private \memberof mlt_property_s
103 * \param self a property
106 static inline void mlt_property_clear( mlt_property self )
108 // Special case data handling
109 if ( self->types & mlt_prop_data && self->destructor != NULL )
110 self->destructor( self->data );
112 // Special case string handling
113 if ( self->types & mlt_prop_string )
114 free( self->prop_string );
116 if ( self->animation )
117 mlt_animation_close( self->animation );
122 self->prop_position = 0;
123 self->prop_double = 0;
124 self->prop_int64 = 0;
125 self->prop_string = NULL;
128 self->destructor = NULL;
129 self->serialiser = NULL;
130 self->animation = NULL;
133 /** Set the property to an integer value.
135 * \public \memberof mlt_property_s
136 * \param self a property
137 * \param value an integer
141 int mlt_property_set_int( mlt_property self, int value )
143 pthread_mutex_lock( &self->mutex );
144 mlt_property_clear( self );
145 self->types = mlt_prop_int;
146 self->prop_int = value;
147 pthread_mutex_unlock( &self->mutex );
151 /** Set the property to a floating point value.
153 * \public \memberof mlt_property_s
154 * \param self a property
155 * \param value a double precision floating point value
159 int mlt_property_set_double( mlt_property self, double value )
161 pthread_mutex_lock( &self->mutex );
162 mlt_property_clear( self );
163 self->types = mlt_prop_double;
164 self->prop_double = value;
165 pthread_mutex_unlock( &self->mutex );
169 /** Set the property to a position value.
171 * Position is a relative time value in frame units.
172 * \public \memberof mlt_property_s
173 * \param self a property
174 * \param value a position value
178 int mlt_property_set_position( mlt_property self, mlt_position value )
180 pthread_mutex_lock( &self->mutex );
181 mlt_property_clear( self );
182 self->types = mlt_prop_position;
183 self->prop_position = value;
184 pthread_mutex_unlock( &self->mutex );
188 /** Set the property to a string value.
190 * This makes a copy of the string you supply so you do not need to track
191 * a new reference to it.
192 * \public \memberof mlt_property_s
193 * \param self a property
194 * \param value the string to copy to the property
195 * \return true if it failed
198 int mlt_property_set_string( mlt_property self, const char *value )
200 pthread_mutex_lock( &self->mutex );
201 if ( value != self->prop_string )
203 mlt_property_clear( self );
204 self->types = mlt_prop_string;
206 self->prop_string = strdup( value );
210 self->types = mlt_prop_string;
212 pthread_mutex_unlock( &self->mutex );
213 return self->prop_string == NULL;
216 /** Set the property to a 64-bit integer value.
218 * \public \memberof mlt_property_s
219 * \param self a property
220 * \param value a 64-bit integer
224 int mlt_property_set_int64( mlt_property self, int64_t value )
226 pthread_mutex_lock( &self->mutex );
227 mlt_property_clear( self );
228 self->types = mlt_prop_int64;
229 self->prop_int64 = value;
230 pthread_mutex_unlock( &self->mutex );
234 /** Set a property to an opaque binary value.
236 * This does not make a copy of the data. You can use a Properties object
237 * with its reference tracking and the destructor function to control
238 * the lifetime of the data. Otherwise, pass NULL for the destructor
239 * function and control the lifetime yourself.
240 * \public \memberof mlt_property_s
241 * \param self a property
242 * \param value an opaque pointer
243 * \param length the number of bytes pointed to by value (optional)
244 * \param destructor a function to use to destroy this binary data (optional, assuming you manage the resource)
245 * \param serialiser a function to use to convert this binary data to a string (optional)
249 int mlt_property_set_data( mlt_property self, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser )
251 pthread_mutex_lock( &self->mutex );
252 if ( self->data == value )
253 self->destructor = NULL;
254 mlt_property_clear( self );
255 self->types = mlt_prop_data;
257 self->length = length;
258 self->destructor = destructor;
259 self->serialiser = serialiser;
260 pthread_mutex_unlock( &self->mutex );
264 /** Parse a SMIL clock value.
266 * \private \memberof mlt_property_s
267 * \param s the string to parse
268 * \param fps frames per second
269 * \param locale the locale to use for parsing a real number value
270 * \return position in frames
273 static int time_clock_to_frames( const char *s, double fps, locale_t locale )
275 char *pos, *copy = strdup( s );
276 int hours = 0, minutes = 0;
280 pos = strrchr( s, ':' );
282 #if defined(__GLIBC__) || defined(__DARWIN__)
284 seconds = strtod_l( pos + 1, NULL, locale );
287 seconds = strtod( pos + 1, NULL );
289 pos = strrchr( s, ':' );
291 minutes = atoi( pos + 1 );
300 #if defined(__GLIBC__) || defined(__DARWIN__)
302 seconds = strtod_l( s, NULL, locale );
305 seconds = strtod( s, NULL );
309 return lrint( fps * ( (hours * 3600) + (minutes * 60) + seconds ) );
312 /** Parse a SMPTE timecode string.
314 * \private \memberof mlt_property_s
315 * \param s the string to parse
316 * \param fps frames per second
317 * \return position in frames
320 static int time_code_to_frames( const char *s, double fps )
322 char *pos, *copy = strdup( s );
323 int hours = 0, minutes = 0, seconds = 0, frames;
326 pos = strrchr( s, ';' );
328 pos = strrchr( s, ':' );
330 frames = atoi( pos + 1 );
332 pos = strrchr( s, ':' );
334 seconds = atoi( pos + 1 );
336 pos = strrchr( s, ':' );
338 minutes = atoi( pos + 1 );
355 return lrint( fps * ( (hours * 3600) + (minutes * 60) + seconds ) + frames );
358 /** Convert a string to an integer.
360 * The string must begin with '0x' to be interpreted as hexadecimal.
361 * Otherwise, it is interpreted as base 10.
363 * If the string begins with '#' it is interpreted as a hexadecimal color value
364 * in the form RRGGBB or AARRGGBB. Color values that begin with '0x' are
365 * always in the form RRGGBBAA where the alpha components are not optional.
366 * Applications and services should expect the binary color value in bytes to
367 * be in the following order: RGBA. This means they will have to cast the int
368 * to an unsigned int. This is especially important when they need to shift
369 * right to obtain RGB without alpha in order to make it do a logical instead
370 * of arithmetic shift.
372 * If the string contains a colon it is interpreted as a time value. If it also
373 * contains a period or comma character, the string is parsed as a clock value:
374 * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF.
375 * \private \memberof mlt_property_s
376 * \param value a string to convert
377 * \param fps frames per second, used when converting from time value
378 * \param locale the locale to use when converting from time clock value
379 * \return the resultant integer
381 static int mlt_property_atoi( const char *value, double fps, locale_t locale )
383 // Parse a hex color value as #RRGGBB or #AARRGGBB.
384 if ( value[0] == '#' )
386 unsigned int rgb = strtoul( value + 1, NULL, 16 );
387 unsigned int alpha = ( strlen( value ) > 7 ) ? ( rgb >> 24 ) : 0xff;
388 return ( rgb << 8 ) | alpha;
390 // Do hex and decimal explicitly to avoid decimal value with leading zeros
391 // interpreted as octal.
392 else if ( value[0] == '0' && value[1] == 'x' )
394 return strtoul( value + 2, NULL, 16 );
396 else if ( fps > 0 && strchr( value, ':' ) )
398 if ( strchr( value, '.' ) || strchr( value, ',' ) )
399 return time_clock_to_frames( value, fps, locale );
401 return time_code_to_frames( value, fps );
405 return strtol( value, NULL, 10 );
409 /** Get the property as an integer.
411 * \public \memberof mlt_property_s
412 * \param self a property
413 * \param fps frames per second, used when converting from time value
414 * \param locale the locale to use when converting from time clock value
415 * \return an integer value
418 int mlt_property_get_int( mlt_property self, double fps, locale_t locale )
420 if ( self->types & mlt_prop_int )
421 return self->prop_int;
422 else if ( self->types & mlt_prop_double )
423 return ( int )self->prop_double;
424 else if ( self->types & mlt_prop_position )
425 return ( int )self->prop_position;
426 else if ( self->types & mlt_prop_int64 )
427 return ( int )self->prop_int64;
428 else if ( self->types & mlt_prop_rect && self->data )
429 return ( int ) ( (mlt_rect*) self->data )->x;
430 else if ( ( self->types & mlt_prop_string ) && self->prop_string )
431 return mlt_property_atoi( self->prop_string, fps, locale );
435 /** Convert a string to a floating point number.
437 * If the string contains a colon it is interpreted as a time value. If it also
438 * contains a period or comma character, the string is parsed as a clock value:
439 * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF.
440 * If the numeric string ends with '%' then the value is divided by 100 to convert
442 * \private \memberof mlt_property_s
443 * \param value the string to convert
444 * \param fps frames per second, used when converting from time value
445 * \param locale the locale to use when converting from time clock value
446 * \return the resultant real number
448 static double mlt_property_atof( const char *value, double fps, locale_t locale )
450 if ( fps > 0 && strchr( value, ':' ) )
452 if ( strchr( value, '.' ) || strchr( value, ',' ) )
453 return time_clock_to_frames( value, fps, locale );
455 return time_code_to_frames( value, fps );
461 #if defined(__GLIBC__) || defined(__DARWIN__)
463 result = strtod_l( value, &end, locale );
466 result = strtod( value, &end );
467 if ( end && end[0] == '%' )
473 /** Get the property as a floating point.
475 * \public \memberof mlt_property_s
476 * \param self a property
477 * \param fps frames per second, used when converting from time value
478 * \param locale the locale to use for this conversion
479 * \return a floating point value
482 double mlt_property_get_double( mlt_property self, double fps, locale_t locale )
484 if ( self->types & mlt_prop_double )
485 return self->prop_double;
486 else if ( self->types & mlt_prop_int )
487 return ( double )self->prop_int;
488 else if ( self->types & mlt_prop_position )
489 return ( double )self->prop_position;
490 else if ( self->types & mlt_prop_int64 )
491 return ( double )self->prop_int64;
492 else if ( self->types & mlt_prop_rect && self->data )
493 return ( (mlt_rect*) self->data )->x;
494 else if ( ( self->types & mlt_prop_string ) && self->prop_string )
495 return mlt_property_atof( self->prop_string, fps, locale );
499 /** Get the property as a position.
501 * A position is an offset time in terms of frame units.
502 * \public \memberof mlt_property_s
503 * \param self a property
504 * \param fps frames per second, used when converting from time value
505 * \param locale the locale to use when converting from time clock value
506 * \return the position in frames
509 mlt_position mlt_property_get_position( mlt_property self, double fps, locale_t locale )
511 if ( self->types & mlt_prop_position )
512 return self->prop_position;
513 else if ( self->types & mlt_prop_int )
514 return ( mlt_position )self->prop_int;
515 else if ( self->types & mlt_prop_double )
516 return ( mlt_position )self->prop_double;
517 else if ( self->types & mlt_prop_int64 )
518 return ( mlt_position )self->prop_int64;
519 else if ( self->types & mlt_prop_rect && self->data )
520 return ( mlt_position ) ( (mlt_rect*) self->data )->x;
521 else if ( ( self->types & mlt_prop_string ) && self->prop_string )
522 return ( mlt_position )mlt_property_atoi( self->prop_string, fps, locale );
526 /** Convert a string to a 64-bit integer.
528 * If the string begins with '0x' it is interpreted as a hexadecimal value.
529 * \private \memberof mlt_property_s
530 * \param value a string
531 * \return a 64-bit integer
534 static inline int64_t mlt_property_atoll( const char *value )
538 else if ( value[0] == '0' && value[1] == 'x' )
539 return strtoll( value + 2, NULL, 16 );
541 return strtoll( value, NULL, 10 );
544 /** Get the property as a signed integer.
546 * \public \memberof mlt_property_s
547 * \param self a property
548 * \return a 64-bit integer
551 int64_t mlt_property_get_int64( mlt_property self )
553 if ( self->types & mlt_prop_int64 )
554 return self->prop_int64;
555 else if ( self->types & mlt_prop_int )
556 return ( int64_t )self->prop_int;
557 else if ( self->types & mlt_prop_double )
558 return ( int64_t )self->prop_double;
559 else if ( self->types & mlt_prop_position )
560 return ( int64_t )self->prop_position;
561 else if ( self->types & mlt_prop_rect && self->data )
562 return ( int64_t ) ( (mlt_rect*) self->data )->x;
563 else if ( ( self->types & mlt_prop_string ) && self->prop_string )
564 return mlt_property_atoll( self->prop_string );
568 /** Get the property as a string.
570 * The caller is not responsible for deallocating the returned string!
571 * The string is deallocated when the Property is closed.
572 * This tries its hardest to convert the property to string including using
573 * a serialization function for binary data, if supplied.
574 * \public \memberof mlt_property_s
575 * \param self a property
576 * \return a string representation of the property or NULL if failed
579 char *mlt_property_get_string( mlt_property self )
581 // Construct a string if need be
582 if ( ! ( self->types & mlt_prop_string ) )
584 pthread_mutex_lock( &self->mutex );
585 if ( self->types & mlt_prop_int )
587 self->types |= mlt_prop_string;
588 self->prop_string = malloc( 32 );
589 sprintf( self->prop_string, "%d", self->prop_int );
591 else if ( self->types & mlt_prop_double )
593 self->types |= mlt_prop_string;
594 self->prop_string = malloc( 32 );
595 sprintf( self->prop_string, "%g", self->prop_double );
597 else if ( self->types & mlt_prop_position )
599 self->types |= mlt_prop_string;
600 self->prop_string = malloc( 32 );
601 sprintf( self->prop_string, "%d", (int)self->prop_position );
603 else if ( self->types & mlt_prop_int64 )
605 self->types |= mlt_prop_string;
606 self->prop_string = malloc( 32 );
607 sprintf( self->prop_string, "%"PRId64, self->prop_int64 );
609 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
611 self->types |= mlt_prop_string;
612 self->prop_string = self->serialiser( self->data, self->length );
614 pthread_mutex_unlock( &self->mutex );
617 // Return the string (may be NULL)
618 return self->prop_string;
621 /** Get the property as a string (with locale).
623 * The caller is not responsible for deallocating the returned string!
624 * The string is deallocated when the Property is closed.
625 * This tries its hardest to convert the property to string including using
626 * a serialization function for binary data, if supplied.
627 * \public \memberof mlt_property_s
628 * \param self a property
629 * \param locale the locale to use for this conversion
630 * \return a string representation of the property or NULL if failed
633 char *mlt_property_get_string_l( mlt_property self, locale_t locale )
635 // Optimization for no locale
637 return mlt_property_get_string( self );
639 // Construct a string if need be
640 if ( ! ( self->types & mlt_prop_string ) )
642 // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale.
643 // Save the current locale
644 #if defined(__DARWIN__)
645 const char *localename = querylocale( LC_NUMERIC, locale );
646 #elif defined(__GLIBC__)
647 const char *localename = locale->__names[ LC_NUMERIC ];
649 // TODO: not yet sure what to do on other platforms
650 const char *localename = "";
652 // Protect damaging the global locale from a temporary locale on another thread.
653 pthread_mutex_lock( &self->mutex );
655 // Get the current locale
656 char *orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
658 // Set the new locale
659 setlocale( LC_NUMERIC, localename );
661 if ( self->types & mlt_prop_int )
663 self->types |= mlt_prop_string;
664 self->prop_string = malloc( 32 );
665 sprintf( self->prop_string, "%d", self->prop_int );
667 else if ( self->types & mlt_prop_double )
669 self->types |= mlt_prop_string;
670 self->prop_string = malloc( 32 );
671 sprintf( self->prop_string, "%g", self->prop_double );
673 else if ( self->types & mlt_prop_position )
675 self->types |= mlt_prop_string;
676 self->prop_string = malloc( 32 );
677 sprintf( self->prop_string, "%d", (int)self->prop_position );
679 else if ( self->types & mlt_prop_int64 )
681 self->types |= mlt_prop_string;
682 self->prop_string = malloc( 32 );
683 sprintf( self->prop_string, "%"PRId64, self->prop_int64 );
685 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
687 self->types |= mlt_prop_string;
688 self->prop_string = self->serialiser( self->data, self->length );
690 // Restore the current locale
691 setlocale( LC_NUMERIC, orig_localename );
692 free( orig_localename );
693 pthread_mutex_unlock( &self->mutex );
696 // Return the string (may be NULL)
697 return self->prop_string;
700 /** Get the binary data from a property.
702 * This only works if you previously put binary data into the property.
703 * This does not return a copy of the data; it returns a pointer to it.
704 * If you supplied a destructor function when setting the binary data,
705 * the destructor is used when the Property is closed to free the memory.
706 * Therefore, only free the returned pointer if you did not supply a
707 * destructor function.
708 * \public \memberof mlt_property_s
709 * \param self a property
710 * \param[out] length the size of the binary object in bytes (optional)
711 * \return an opaque data pointer or NULL if not available
714 void *mlt_property_get_data( mlt_property self, int *length )
716 // Assign length if not NULL
717 if ( length != NULL )
718 *length = self->length;
720 // Return the data (note: there is no conversion here)
724 /** Destroy a property and free all related resources.
726 * \public \memberof mlt_property_s
727 * \param self a property
730 void mlt_property_close( mlt_property self )
732 mlt_property_clear( self );
733 pthread_mutex_destroy( &self->mutex );
739 * A Property holding binary data only copies the data if a serialiser
740 * function was supplied when you set the Property.
741 * \public \memberof mlt_property_s
742 * \author Zach <zachary.drew@gmail.com>
743 * \param self a property
744 * \param that another property
746 void mlt_property_pass( mlt_property self, mlt_property that )
748 pthread_mutex_lock( &self->mutex );
749 mlt_property_clear( self );
751 self->types = that->types;
753 if ( self->types & mlt_prop_int64 )
754 self->prop_int64 = that->prop_int64;
755 else if ( self->types & mlt_prop_int )
756 self->prop_int = that->prop_int;
757 else if ( self->types & mlt_prop_double )
758 self->prop_double = that->prop_double;
759 else if ( self->types & mlt_prop_position )
760 self->prop_position = that->prop_position;
761 if ( self->types & mlt_prop_string )
763 if ( that->prop_string != NULL )
764 self->prop_string = strdup( that->prop_string );
766 else if ( that->types & mlt_prop_rect )
768 mlt_property_clear( self );
769 self->types = mlt_prop_rect | mlt_prop_data;
770 self->length = that->length;
771 self->data = calloc( 1, self->length );
772 memcpy( self->data, that->data, self->length );
773 self->destructor = free;
774 self->serialiser = that->serialiser;
776 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
778 self->types = mlt_prop_string;
779 self->prop_string = self->serialiser( self->data, self->length );
781 pthread_mutex_unlock( &self->mutex );
784 /** Convert frame count to a SMPTE timecode string.
786 * \private \memberof mlt_property_s
787 * \param frames a frame count
788 * \param fps frames per second
789 * \param[out] s the string to write into - must have enough space to hold largest time string
792 static void time_smpte_from_frames( int frames, double fps, char *s )
794 int hours, mins, secs;
795 char frame_sep = ':';
797 if ( fps == 30000.0/1001.0 )
800 int i, max_frames = frames;
801 for ( i = 1800; i <= max_frames; i += 1800 )
811 hours = frames / ( fps * 3600 );
812 frames -= hours * ( fps * 3600 );
813 mins = frames / ( fps * 60 );
814 frames -= mins * ( fps * 60 );
816 frames -= secs * fps;
818 sprintf( s, "%02d:%02d:%02d%c%02d", hours, mins, secs, frame_sep, frames );
821 /** Convert frame count to a SMIL clock value string.
823 * \private \memberof mlt_property_s
824 * \param frames a frame count
825 * \param fps frames per second
826 * \param[out] s the string to write into - must have enough space to hold largest time string
829 static void time_clock_from_frames( int frames, double fps, char *s )
834 hours = frames / ( fps * 3600 );
835 frames -= hours * ( fps * 3600 );
836 mins = frames / ( fps * 60 );
837 frames -= mins * ( fps * 60 );
838 secs = (double) frames / fps;
840 sprintf( s, "%02d:%02d:%06.3f", hours, mins, secs );
843 /** Get the property as a time string.
845 * The time value can be either a SMPTE timecode or SMIL clock value.
846 * The caller is not responsible for deallocating the returned string!
847 * The string is deallocated when the property is closed.
848 * \public \memberof mlt_property_s
849 * \param self a property
850 * \param format the time format that you want
851 * \param fps frames per second
852 * \param locale the locale to use for this conversion
853 * \return a string representation of the property or NULL if failed
856 char *mlt_property_get_time( mlt_property self, mlt_time_format format, double fps, locale_t locale )
858 char *orig_localename = NULL;
859 const char *localename = "";
861 // Optimization for mlt_time_frames
862 if ( format == mlt_time_frames )
863 return mlt_property_get_string_l( self, locale );
865 // Remove existing string
866 if ( self->prop_string )
867 mlt_property_set_int( self, mlt_property_get_int( self, fps, locale ) );
869 // Use the specified locale
872 // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale.
873 // Save the current locale
874 #if defined(__DARWIN__)
875 localename = querylocale( LC_NUMERIC, locale );
876 #elif defined(__GLIBC__)
877 localename = locale->__names[ LC_NUMERIC ];
879 // TODO: not yet sure what to do on other platforms
881 // Protect damaging the global locale from a temporary locale on another thread.
882 pthread_mutex_lock( &self->mutex );
884 // Get the current locale
885 orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
887 // Set the new locale
888 setlocale( LC_NUMERIC, localename );
892 // Make sure we have a lock before accessing self->types
893 pthread_mutex_lock( &self->mutex );
896 // Convert number to string
897 if ( self->types & mlt_prop_int )
899 self->types |= mlt_prop_string;
900 self->prop_string = malloc( 32 );
901 if ( format == mlt_time_clock )
902 time_clock_from_frames( self->prop_int, fps, self->prop_string );
904 time_smpte_from_frames( self->prop_int, fps, self->prop_string );
906 else if ( self->types & mlt_prop_position )
908 self->types |= mlt_prop_string;
909 self->prop_string = malloc( 32 );
910 if ( format == mlt_time_clock )
911 time_clock_from_frames( (int) self->prop_position, fps, self->prop_string );
913 time_smpte_from_frames( (int) self->prop_position, fps, self->prop_string );
915 else if ( self->types & mlt_prop_double )
917 self->types |= mlt_prop_string;
918 self->prop_string = malloc( 32 );
919 if ( format == mlt_time_clock )
920 time_clock_from_frames( self->prop_double, fps, self->prop_string );
922 time_smpte_from_frames( self->prop_double, fps, self->prop_string );
924 else if ( self->types & mlt_prop_int64 )
926 self->types |= mlt_prop_string;
927 self->prop_string = malloc( 32 );
928 if ( format == mlt_time_clock )
929 time_clock_from_frames( (int) self->prop_int64, fps, self->prop_string );
931 time_smpte_from_frames( (int) self->prop_int64, fps, self->prop_string );
934 // Restore the current locale
937 setlocale( LC_NUMERIC, orig_localename );
938 free( orig_localename );
939 pthread_mutex_unlock( &self->mutex );
943 // Make sure we have a lock before accessing self->types
944 pthread_mutex_unlock( &self->mutex );
947 // Return the string (may be NULL)
948 return self->prop_string;
951 static int is_property_numeric( mlt_property self, locale_t locale )
953 int result = ( self->types & mlt_prop_int ) ||
954 ( self->types & mlt_prop_int64 ) ||
955 ( self->types & mlt_prop_double ) ||
956 ( self->types & mlt_prop_position ) ||
957 ( self->types & mlt_prop_rect );
959 // If not already numeric but string is numeric.
960 if ( ( !result && self->types & mlt_prop_string ) && self->prop_string )
964 #if defined(__GLIBC__) || defined(__DARWIN__)
966 temp = strtod_l( self->prop_string, &p, locale );
969 temp = strtod( self->prop_string, &p );
970 result = ( p != self->prop_string );
975 static inline double linear_interpolate( double y1, double y2, double t )
977 return y1 + ( y2 - y1 ) * t;
980 // For non-closed curves, you need to also supply the tangent vector at the first and last control point.
981 // This is commonly done: T(P[0]) = P[1] - P[0] and T(P[n]) = P[n] - P[n-1].
982 static inline double catmull_rom_interpolate( double y0, double y1, double y2, double y3, double t )
985 double a0 = -0.5 * y0 + 1.5 * y1 - 1.5 * y2 + 0.5 * y3;
986 double a1 = y0 - 2.5 * y1 + 2 * y2 - 0.5 * y3;
987 double a2 = -0.5 * y0 + 0.5 * y2;
989 return a0 * t * t2 + a1 * t2 + a2 * t + a3;
992 int mlt_property_interpolate( mlt_property self, mlt_property p[],
993 double progress, double fps, locale_t locale, mlt_keyframe_type interp )
996 if ( interp != mlt_keyframe_discrete &&
997 is_property_numeric( p[1], locale ) && is_property_numeric( p[2], locale ) )
999 if ( self->types & mlt_prop_rect )
1001 mlt_rect value = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN };
1002 if ( interp == mlt_keyframe_linear )
1005 mlt_rect zero = {0, 0, 0, 0, 0};
1006 points[0] = p[1]? mlt_property_get_rect( p[1], locale ) : zero;
1009 points[1] = mlt_property_get_rect( p[2], locale );
1010 value.x = linear_interpolate( points[0].x, points[1].x, progress );
1011 value.y = linear_interpolate( points[0].y, points[1].y, progress );
1012 value.w = linear_interpolate( points[0].w, points[1].w, progress );
1013 value.h = linear_interpolate( points[0].h, points[1].h, progress );
1014 value.o = linear_interpolate( points[0].o, points[1].o, progress );
1021 else if ( interp == mlt_keyframe_smooth )
1024 mlt_rect zero = {0, 0, 0, 0, 0};
1025 points[1] = p[1]? mlt_property_get_rect( p[1], locale ) : zero;
1028 points[0] = p[0]? mlt_property_get_rect( p[0], locale ) : zero;
1029 points[2] = p[2]? mlt_property_get_rect( p[2], locale ) : zero;
1030 points[3] = p[3]? mlt_property_get_rect( p[3], locale ) : zero;
1031 value.x = catmull_rom_interpolate( points[0].x, points[1].x, points[2].x, points[3].x, progress );
1032 value.y = catmull_rom_interpolate( points[0].y, points[1].y, points[2].y, points[3].y, progress );
1033 value.w = catmull_rom_interpolate( points[0].w, points[1].w, points[2].w, points[3].w, progress );
1034 value.h = catmull_rom_interpolate( points[0].h, points[1].h, points[2].h, points[3].h, progress );
1035 value.o = catmull_rom_interpolate( points[0].o, points[1].o, points[2].o, points[3].o, progress );
1042 error = mlt_property_set_rect( self, value );
1047 if ( interp == mlt_keyframe_linear )
1050 points[0] = p[1]? mlt_property_get_double( p[1], fps, locale ) : 0;
1051 points[1] = p[2]? mlt_property_get_double( p[2], fps, locale ) : 0;
1052 value = p[2]? linear_interpolate( points[0], points[1], progress ) : points[0];
1054 else if ( interp == mlt_keyframe_smooth )
1057 points[0] = p[0]? mlt_property_get_double( p[0], fps, locale ) : 0;
1058 points[1] = p[1]? mlt_property_get_double( p[1], fps, locale ) : 0;
1059 points[2] = p[2]? mlt_property_get_double( p[2], fps, locale ) : 0;
1060 points[3] = p[3]? mlt_property_get_double( p[3], fps, locale ) : 0;
1061 value = p[2]? catmull_rom_interpolate( points[0], points[1], points[2], points[3], progress ) : points[1];
1063 error = mlt_property_set_double( self, value );
1068 mlt_property_pass( self, p[1] );
1073 static void refresh_animation( mlt_property self, double fps, locale_t locale, int length )
1075 if ( !self->animation )
1077 self->animation = mlt_animation_new();
1078 if ( self->prop_string )
1080 mlt_animation_parse( self->animation, self->prop_string, length, fps, locale );
1084 mlt_animation_set_length( self->animation, length );
1085 self->types |= mlt_prop_data;
1086 self->data = self->animation;
1087 self->serialiser = (mlt_serialiser) mlt_animation_serialize;
1090 else if ( self->prop_string )
1092 mlt_animation_refresh( self->animation, self->prop_string, length );
1096 double mlt_property_anim_get_double( mlt_property self, double fps, locale_t locale, int position, int length )
1099 if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
1101 struct mlt_animation_item_s item;
1102 item.property = mlt_property_init();
1104 refresh_animation( self, fps, locale, length );
1105 mlt_animation_get_item( self->animation, &item, position );
1106 result = mlt_property_get_double( item.property, fps, locale );
1108 mlt_property_close( item.property );
1112 result = mlt_property_get_double( self, fps, locale );
1117 int mlt_property_anim_get_int( mlt_property self, double fps, locale_t locale, int position, int length )
1120 if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
1122 struct mlt_animation_item_s item;
1123 item.property = mlt_property_init();
1125 refresh_animation( self, fps, locale, length );
1126 mlt_animation_get_item( self->animation, &item, position );
1127 result = mlt_property_get_int( item.property, fps, locale );
1129 mlt_property_close( item.property );
1133 result = mlt_property_get_int( self, fps, locale );
1138 char* mlt_property_anim_get_string( mlt_property self, double fps, locale_t locale, int position, int length )
1141 if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
1143 struct mlt_animation_item_s item;
1144 item.property = mlt_property_init();
1146 if ( !self->animation )
1147 refresh_animation( self, fps, locale, length );
1148 mlt_animation_get_item( self->animation, &item, position );
1150 pthread_mutex_lock( &self->mutex );
1151 if ( self->prop_string )
1152 free( self->prop_string );
1153 self->prop_string = mlt_property_get_string_l( item.property, locale );
1154 if ( self->prop_string )
1155 self->prop_string = strdup( self->prop_string );
1156 self->types |= mlt_prop_string;
1157 pthread_mutex_unlock( &self->mutex );
1159 result = self->prop_string;
1160 mlt_property_close( item.property );
1164 result = mlt_property_get_string_l( self, locale );
1169 /** Set a property animation keyframe to a real number.
1171 * \public \memberof mlt_property_s
1172 * \param self a property
1173 * \param value a double precision floating point value
1174 * \return false if successful, true to indicate error
1177 int mlt_property_anim_set_double( mlt_property self, double value, double fps, locale_t locale,
1178 int position, int length, mlt_keyframe_type keyframe_type )
1181 struct mlt_animation_item_s item;
1183 item.property = mlt_property_init();
1184 item.frame = position;
1185 item.keyframe_type = keyframe_type;
1186 mlt_property_set_double( item.property, value );
1188 refresh_animation( self, fps, locale, length );
1189 result = mlt_animation_insert( self->animation, &item );
1190 mlt_animation_interpolate( self->animation );
1191 mlt_property_close( item.property );
1196 /** Set a property animation keyframe to an integer value.
1198 * \public \memberof mlt_property_s
1199 * \param self a property
1200 * \param value a double precision floating point value
1201 * \return false if successful, true to indicate error
1204 int mlt_property_anim_set_int( mlt_property self, int value, double fps, locale_t locale,
1205 int position, int length, mlt_keyframe_type keyframe_type )
1208 struct mlt_animation_item_s item;
1210 item.property = mlt_property_init();
1211 item.frame = position;
1212 item.keyframe_type = keyframe_type;
1213 mlt_property_set_int( item.property, value );
1215 refresh_animation( self, fps, locale, length );
1216 result = mlt_animation_insert( self->animation, &item );
1217 mlt_animation_interpolate( self->animation );
1218 mlt_property_close( item.property );
1223 int mlt_property_anim_set_string( mlt_property self, const char *value, double fps, locale_t locale, int position, int length )
1226 struct mlt_animation_item_s item;
1228 item.property = mlt_property_init();
1229 item.frame = position;
1230 item.keyframe_type = mlt_keyframe_discrete;
1231 mlt_property_set_string( item.property, value );
1233 refresh_animation( self, fps, locale, length );
1234 result = mlt_animation_insert( self->animation, &item );
1235 mlt_animation_interpolate( self->animation );
1236 mlt_property_close( item.property );
1241 static char* serialise_mlt_rect( mlt_rect *rect, int length )
1243 char* result = calloc( 1, 100 );
1244 if ( rect->x != DBL_MIN )
1245 sprintf( result + strlen( result ), "%g", rect->x );
1246 if ( rect->y != DBL_MIN )
1247 sprintf( result + strlen( result ), " %g", rect->y );
1248 if ( rect->w != DBL_MIN )
1249 sprintf( result + strlen( result ), " %g", rect->w );
1250 if ( rect->h != DBL_MIN )
1251 sprintf( result + strlen( result ), " %g", rect->h );
1252 if ( rect->o != DBL_MIN )
1253 sprintf( result + strlen( result ), " %g", rect->o );
1257 /** Set a property to a mlt_rect rectangle.
1259 * \public \memberof mlt_property_s
1260 * \param self a property
1261 * \param value a mlt_rect
1265 int mlt_property_set_rect( mlt_property self, mlt_rect value )
1267 pthread_mutex_lock( &self->mutex );
1268 mlt_property_clear( self );
1269 self->types = mlt_prop_rect | mlt_prop_data;
1270 self->length = sizeof(value);
1271 self->data = calloc( 1, self->length );
1272 memcpy( self->data, &value, self->length );
1273 self->destructor = free;
1274 self->serialiser = (mlt_serialiser) serialise_mlt_rect;
1275 pthread_mutex_unlock( &self->mutex );
1279 /** Get the property as a floating point.
1281 * \public \memberof mlt_property_s
1282 * \param self a property
1283 * \param fps frames per second, used when converting from time value
1284 * \param locale the locale to use for this conversion
1285 * \return a rectangle value
1288 mlt_rect mlt_property_get_rect( mlt_property self, locale_t locale )
1290 mlt_rect rect = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN };
1291 if ( self->types & mlt_prop_rect )
1292 rect = *( (mlt_rect*) self->data );
1293 else if ( self->types & mlt_prop_double )
1294 rect.x = self->prop_double;
1295 else if ( self->types & mlt_prop_int )
1296 rect.x = ( double )self->prop_int;
1297 else if ( self->types & mlt_prop_position )
1298 rect.x = ( double )self->prop_position;
1299 else if ( self->types & mlt_prop_int64 )
1300 rect.x = ( double )self->prop_int64;
1301 else if ( ( self->types & mlt_prop_string ) && self->prop_string )
1303 //return mlt_property_atof( self->prop_string, fps, locale );
1304 char *value = self->prop_string;
1310 #if defined(__GLIBC__) || defined(__DARWIN__)
1312 temp = strtod_l( value, &p, locale );
1315 temp = strtod( value, &p );
1323 case 0: rect.x = temp; break;
1324 case 1: rect.y = temp; break;
1325 case 2: rect.w = temp; break;
1326 case 3: rect.h = temp; break;
1327 case 4: rect.o = temp; break;
1341 /** Set a property animation keyframe to a rectangle.
1343 * \public \memberof mlt_property_s
1344 * \param self a property
1345 * \param value a rectangle
1346 * \return false if successful, true to indicate error
1349 int mlt_property_anim_set_rect( mlt_property self, mlt_rect value, double fps, locale_t locale,
1350 int position, int length, mlt_keyframe_type keyframe_type )
1353 struct mlt_animation_item_s item;
1355 item.property = mlt_property_init();
1356 item.frame = position;
1357 item.keyframe_type = keyframe_type;
1358 mlt_property_set_rect( item.property, value );
1360 refresh_animation( self, fps, locale, length );
1361 result = mlt_animation_insert( self->animation, &item );
1362 mlt_animation_interpolate( self->animation );
1363 mlt_property_close( item.property );
1368 mlt_rect mlt_property_anim_get_rect( mlt_property self, double fps, locale_t locale, int position, int length )
1371 if ( self->animation || ( ( self->types & mlt_prop_string ) && self->prop_string ) )
1373 struct mlt_animation_item_s item;
1374 item.property = mlt_property_init();
1375 item.property->types = mlt_prop_rect;
1377 refresh_animation( self, fps, locale, length );
1378 mlt_animation_get_item( self->animation, &item, position );
1379 result = mlt_property_get_rect( item.property, locale );
1381 mlt_property_close( item.property );
1385 result = mlt_property_get_rect( self, locale );
1390 mlt_animation mlt_property_get_animation( mlt_property self )
1392 return self->animation;