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"
39 /** Bit pattern used internally to indicated representations available.
44 mlt_prop_none = 0, //!< not set
45 mlt_prop_int = 1, //!< set as an integer
46 mlt_prop_string = 2, //!< set as string or already converted to string
47 mlt_prop_position = 4,//!< set as a position
48 mlt_prop_double = 8, //!< set as a floating point
49 mlt_prop_data = 16, //!< set as opaque binary
50 mlt_prop_int64 = 32 //!< set as a 64-bit integer
54 /** \brief Property class
56 * A property is like a variant or dynamic type. They are used for many things
57 * in MLT, but in particular they are the parameter mechanism for the plugins.
62 /// Stores a bit pattern of types available for this property
63 mlt_property_type types;
65 /// Atomic type handling
67 mlt_position prop_position;
74 /// Generic type handling
77 mlt_destructor destructor;
78 mlt_serialiser serialiser;
80 pthread_mutex_t mutex;
81 mlt_animation animation;
84 /** Construct a property and initialize it
85 * \public \memberof mlt_property_s
88 mlt_property mlt_property_init( )
90 mlt_property self = calloc( 1, sizeof( *self ) );
92 pthread_mutex_init( &self->mutex, NULL );
96 /** Clear (0/null) a property.
98 * Frees up any associated resources in the process.
99 * \private \memberof mlt_property_s
100 * \param self a property
103 static inline void mlt_property_clear( mlt_property self )
105 // Special case data handling
106 if ( self->types & mlt_prop_data && self->destructor != NULL )
107 self->destructor( self->data );
109 // Special case string handling
110 if ( self->types & mlt_prop_string )
111 free( self->prop_string );
113 if ( self->animation )
114 mlt_animation_close( self->animation );
119 self->prop_position = 0;
120 self->prop_double = 0;
121 self->prop_int64 = 0;
122 self->prop_string = NULL;
125 self->destructor = NULL;
126 self->serialiser = NULL;
127 self->animation = NULL;
130 /** Set the property to an integer value.
132 * \public \memberof mlt_property_s
133 * \param self a property
134 * \param value an integer
138 int mlt_property_set_int( mlt_property self, int value )
140 pthread_mutex_lock( &self->mutex );
141 mlt_property_clear( self );
142 self->types = mlt_prop_int;
143 self->prop_int = value;
144 pthread_mutex_unlock( &self->mutex );
148 /** Set the property to a floating point value.
150 * \public \memberof mlt_property_s
151 * \param self a property
152 * \param value a double precision floating point value
156 int mlt_property_set_double( mlt_property self, double value )
158 pthread_mutex_lock( &self->mutex );
159 mlt_property_clear( self );
160 self->types = mlt_prop_double;
161 self->prop_double = value;
162 pthread_mutex_unlock( &self->mutex );
166 /** Set the property to a position value.
168 * Position is a relative time value in frame units.
169 * \public \memberof mlt_property_s
170 * \param self a property
171 * \param value a position value
175 int mlt_property_set_position( mlt_property self, mlt_position value )
177 pthread_mutex_lock( &self->mutex );
178 mlt_property_clear( self );
179 self->types = mlt_prop_position;
180 self->prop_position = value;
181 pthread_mutex_unlock( &self->mutex );
185 /** Set the property to a string value.
187 * This makes a copy of the string you supply so you do not need to track
188 * a new reference to it.
189 * \public \memberof mlt_property_s
190 * \param self a property
191 * \param value the string to copy to the property
192 * \return true if it failed
195 int mlt_property_set_string( mlt_property self, const char *value )
197 pthread_mutex_lock( &self->mutex );
198 if ( value != self->prop_string )
200 mlt_property_clear( self );
201 self->types = mlt_prop_string;
203 self->prop_string = strdup( value );
207 self->types = mlt_prop_string;
209 pthread_mutex_unlock( &self->mutex );
210 return self->prop_string == NULL;
213 /** Set the property to a 64-bit integer value.
215 * \public \memberof mlt_property_s
216 * \param self a property
217 * \param value a 64-bit integer
221 int mlt_property_set_int64( mlt_property self, int64_t value )
223 pthread_mutex_lock( &self->mutex );
224 mlt_property_clear( self );
225 self->types = mlt_prop_int64;
226 self->prop_int64 = value;
227 pthread_mutex_unlock( &self->mutex );
231 /** Set a property to an opaque binary value.
233 * This does not make a copy of the data. You can use a Properties object
234 * with its reference tracking and the destructor function to control
235 * the lifetime of the data. Otherwise, pass NULL for the destructor
236 * function and control the lifetime yourself.
237 * \public \memberof mlt_property_s
238 * \param self a property
239 * \param value an opaque pointer
240 * \param length the number of bytes pointed to by value (optional)
241 * \param destructor a function to use to destroy this binary data (optional, assuming you manage the resource)
242 * \param serialiser a function to use to convert this binary data to a string (optional)
246 int mlt_property_set_data( mlt_property self, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser )
248 pthread_mutex_lock( &self->mutex );
249 if ( self->data == value )
250 self->destructor = NULL;
251 mlt_property_clear( self );
252 self->types = mlt_prop_data;
254 self->length = length;
255 self->destructor = destructor;
256 self->serialiser = serialiser;
257 pthread_mutex_unlock( &self->mutex );
261 /** Parse a SMIL clock value.
263 * \private \memberof mlt_property_s
264 * \param s the string to parse
265 * \param fps frames per second
266 * \param locale the locale to use for parsing a real number value
267 * \return position in frames
270 static int time_clock_to_frames( const char *s, double fps, locale_t locale )
272 char *pos, *copy = strdup( s );
273 int hours = 0, minutes = 0;
277 pos = strrchr( s, ':' );
279 #if defined(__GLIBC__) || defined(__DARWIN__)
281 seconds = strtod_l( pos + 1, NULL, locale );
284 seconds = strtod( pos + 1, NULL );
286 pos = strrchr( s, ':' );
288 minutes = atoi( pos + 1 );
297 #if defined(__GLIBC__) || defined(__DARWIN__)
299 seconds = strtod_l( s, NULL, locale );
302 seconds = strtod( s, NULL );
306 return fps * ( (hours * 3600) + (minutes * 60) + seconds ) + 0.5;
309 /** Parse a SMPTE timecode string.
311 * \private \memberof mlt_property_s
312 * \param s the string to parse
313 * \param fps frames per second
314 * \return position in frames
317 static int time_code_to_frames( const char *s, double fps )
319 char *pos, *copy = strdup( s );
320 int hours = 0, minutes = 0, seconds = 0, frames;
323 pos = strrchr( s, ';' );
325 pos = strrchr( s, ':' );
327 frames = atoi( pos + 1 );
329 pos = strrchr( s, ':' );
331 seconds = atoi( pos + 1 );
333 pos = strrchr( s, ':' );
335 minutes = atoi( pos + 1 );
352 return frames + ( fps * ( (hours * 3600) + (minutes * 60) + seconds ) + 0.5 );
355 /** Convert a string to an integer.
357 * The string must begin with '0x' to be interpreted as hexadecimal.
358 * Otherwise, it is interpreted as base 10.
360 * If the string begins with '#' it is interpreted as a hexadecimal color value
361 * in the form RRGGBB or AARRGGBB. Color values that begin with '0x' are
362 * always in the form RRGGBBAA where the alpha components are not optional.
363 * Applications and services should expect the binary color value in bytes to
364 * be in the following order: RGBA. This means they will have to cast the int
365 * to an unsigned int. This is especially important when they need to shift
366 * right to obtain RGB without alpha in order to make it do a logical instead
367 * of arithmetic shift.
369 * If the string contains a colon it is interpreted as a time value. If it also
370 * contains a period or comma character, the string is parsed as a clock value:
371 * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF.
372 * \private \memberof mlt_property_s
373 * \param value a string to convert
374 * \param fps frames per second, used when converting from time value
375 * \param locale the locale to use when converting from time clock value
376 * \return the resultant integer
378 static int mlt_property_atoi( const char *value, double fps, locale_t locale )
380 // Parse a hex color value as #RRGGBB or #AARRGGBB.
381 if ( value[0] == '#' )
383 unsigned int rgb = strtoul( value + 1, NULL, 16 );
384 unsigned int alpha = ( strlen( value ) > 7 ) ? ( rgb >> 24 ) : 0xff;
385 return ( rgb << 8 ) | alpha;
387 // Do hex and decimal explicitly to avoid decimal value with leading zeros
388 // interpreted as octal.
389 else if ( value[0] == '0' && value[1] == 'x' )
391 return strtoul( value + 2, NULL, 16 );
393 else if ( fps > 0 && strchr( value, ':' ) )
395 if ( strchr( value, '.' ) || strchr( value, ',' ) )
396 return time_clock_to_frames( value, fps, locale );
398 return time_code_to_frames( value, fps );
402 return strtol( value, NULL, 10 );
406 /** Get the property as an integer.
408 * \public \memberof mlt_property_s
409 * \param self a property
410 * \param fps frames per second, used when converting from time value
411 * \param locale the locale to use when converting from time clock value
412 * \return an integer value
415 int mlt_property_get_int( mlt_property self, double fps, locale_t locale )
417 if ( self->types & mlt_prop_int )
418 return self->prop_int;
419 else if ( self->types & mlt_prop_double )
420 return ( int )self->prop_double;
421 else if ( self->types & mlt_prop_position )
422 return ( int )self->prop_position;
423 else if ( self->types & mlt_prop_int64 )
424 return ( int )self->prop_int64;
425 else if ( ( self->types & mlt_prop_string ) && self->prop_string )
426 return mlt_property_atoi( self->prop_string, fps, locale );
430 /** Convert a string to a floating point number.
432 * If the string contains a colon it is interpreted as a time value. If it also
433 * contains a period or comma character, the string is parsed as a clock value:
434 * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF.
435 * \private \memberof mlt_property_s
436 * \param value the string to convert
437 * \param fps frames per second, used when converting from time value
438 * \param locale the locale to use when converting from time clock value
439 * \return the resultant real number
441 static double mlt_property_atof( const char *value, double fps, locale_t locale )
443 if ( fps > 0 && strchr( value, ':' ) )
445 if ( strchr( value, '.' ) || strchr( value, ',' ) )
446 return time_clock_to_frames( value, fps, locale );
448 return time_code_to_frames( value, fps );
452 #if defined(__GLIBC__) || defined(__DARWIN__)
454 return strtod_l( value, NULL, locale );
456 return strtod( value, NULL );
460 /** Get the property as a floating point.
462 * \public \memberof mlt_property_s
463 * \param self a property
464 * \param fps frames per second, used when converting from time value
465 * \param locale the locale to use for this conversion
466 * \return a floating point value
469 double mlt_property_get_double( mlt_property self, double fps, locale_t locale )
471 if ( self->types & mlt_prop_double )
472 return self->prop_double;
473 else if ( self->types & mlt_prop_int )
474 return ( double )self->prop_int;
475 else if ( self->types & mlt_prop_position )
476 return ( double )self->prop_position;
477 else if ( self->types & mlt_prop_int64 )
478 return ( double )self->prop_int64;
479 else if ( ( self->types & mlt_prop_string ) && self->prop_string )
480 return mlt_property_atof( self->prop_string, fps, locale );
484 /** Get the property as a position.
486 * A position is an offset time in terms of frame units.
487 * \public \memberof mlt_property_s
488 * \param self a property
489 * \param fps frames per second, used when converting from time value
490 * \param locale the locale to use when converting from time clock value
491 * \return the position in frames
494 mlt_position mlt_property_get_position( mlt_property self, double fps, locale_t locale )
496 if ( self->types & mlt_prop_position )
497 return self->prop_position;
498 else if ( self->types & mlt_prop_int )
499 return ( mlt_position )self->prop_int;
500 else if ( self->types & mlt_prop_double )
501 return ( mlt_position )self->prop_double;
502 else if ( self->types & mlt_prop_int64 )
503 return ( mlt_position )self->prop_int64;
504 else if ( ( self->types & mlt_prop_string ) && self->prop_string )
505 return ( mlt_position )mlt_property_atoi( self->prop_string, fps, locale );
509 /** Convert a string to a 64-bit integer.
511 * If the string begins with '0x' it is interpreted as a hexadecimal value.
512 * \private \memberof mlt_property_s
513 * \param value a string
514 * \return a 64-bit integer
517 static inline int64_t mlt_property_atoll( const char *value )
521 else if ( value[0] == '0' && value[1] == 'x' )
522 return strtoll( value + 2, NULL, 16 );
524 return strtoll( value, NULL, 10 );
527 /** Get the property as a signed integer.
529 * \public \memberof mlt_property_s
530 * \param self a property
531 * \return a 64-bit integer
534 int64_t mlt_property_get_int64( mlt_property self )
536 if ( self->types & mlt_prop_int64 )
537 return self->prop_int64;
538 else if ( self->types & mlt_prop_int )
539 return ( int64_t )self->prop_int;
540 else if ( self->types & mlt_prop_double )
541 return ( int64_t )self->prop_double;
542 else if ( self->types & mlt_prop_position )
543 return ( int64_t )self->prop_position;
544 else if ( ( self->types & mlt_prop_string ) && self->prop_string )
545 return mlt_property_atoll( self->prop_string );
549 /** Get the property as a string.
551 * The caller is not responsible for deallocating the returned string!
552 * The string is deallocated when the Property is closed.
553 * This tries its hardest to convert the property to string including using
554 * a serialization function for binary data, if supplied.
555 * \public \memberof mlt_property_s
556 * \param self a property
557 * \return a string representation of the property or NULL if failed
560 char *mlt_property_get_string( mlt_property self )
562 // Construct a string if need be
563 if ( ! ( self->types & mlt_prop_string ) )
565 pthread_mutex_lock( &self->mutex );
566 if ( self->types & mlt_prop_int )
568 self->types |= mlt_prop_string;
569 self->prop_string = malloc( 32 );
570 sprintf( self->prop_string, "%d", self->prop_int );
572 else if ( self->types & mlt_prop_double )
574 self->types |= mlt_prop_string;
575 self->prop_string = malloc( 32 );
576 sprintf( self->prop_string, "%f", self->prop_double );
578 else if ( self->types & mlt_prop_position )
580 self->types |= mlt_prop_string;
581 self->prop_string = malloc( 32 );
582 sprintf( self->prop_string, "%d", (int)self->prop_position );
584 else if ( self->types & mlt_prop_int64 )
586 self->types |= mlt_prop_string;
587 self->prop_string = malloc( 32 );
588 sprintf( self->prop_string, "%"PRId64, self->prop_int64 );
590 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
592 self->types |= mlt_prop_string;
593 self->prop_string = self->serialiser( self->data, self->length );
595 pthread_mutex_unlock( &self->mutex );
598 // Return the string (may be NULL)
599 return self->prop_string;
602 /** Get the property as a string (with locale).
604 * The caller is not responsible for deallocating the returned string!
605 * The string is deallocated when the Property is closed.
606 * This tries its hardest to convert the property to string including using
607 * a serialization function for binary data, if supplied.
608 * \public \memberof mlt_property_s
609 * \param self a property
610 * \param locale the locale to use for this conversion
611 * \return a string representation of the property or NULL if failed
614 char *mlt_property_get_string_l( mlt_property self, locale_t locale )
616 // Optimization for no locale
618 return mlt_property_get_string( self );
620 // Construct a string if need be
621 if ( ! ( self->types & mlt_prop_string ) )
623 // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale.
624 // Save the current locale
625 #if defined(__DARWIN__)
626 const char *localename = querylocale( LC_NUMERIC, locale );
627 #elif defined(__GLIBC__)
628 const char *localename = locale->__names[ LC_NUMERIC ];
630 // TODO: not yet sure what to do on other platforms
631 const char *localename = "";
633 // Protect damaging the global locale from a temporary locale on another thread.
634 pthread_mutex_lock( &self->mutex );
636 // Get the current locale
637 char *orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
639 // Set the new locale
640 setlocale( LC_NUMERIC, localename );
642 if ( self->types & mlt_prop_int )
644 self->types |= mlt_prop_string;
645 self->prop_string = malloc( 32 );
646 sprintf( self->prop_string, "%d", self->prop_int );
648 else if ( self->types & mlt_prop_double )
650 self->types |= mlt_prop_string;
651 self->prop_string = malloc( 32 );
652 sprintf( self->prop_string, "%f", self->prop_double );
654 else if ( self->types & mlt_prop_position )
656 self->types |= mlt_prop_string;
657 self->prop_string = malloc( 32 );
658 sprintf( self->prop_string, "%d", (int)self->prop_position );
660 else if ( self->types & mlt_prop_int64 )
662 self->types |= mlt_prop_string;
663 self->prop_string = malloc( 32 );
664 sprintf( self->prop_string, "%"PRId64, self->prop_int64 );
666 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
668 self->types |= mlt_prop_string;
669 self->prop_string = self->serialiser( self->data, self->length );
671 // Restore the current locale
672 setlocale( LC_NUMERIC, orig_localename );
673 free( orig_localename );
674 pthread_mutex_unlock( &self->mutex );
677 // Return the string (may be NULL)
678 return self->prop_string;
681 /** Get the binary data from a property.
683 * This only works if you previously put binary data into the property.
684 * This does not return a copy of the data; it returns a pointer to it.
685 * If you supplied a destructor function when setting the binary data,
686 * the destructor is used when the Property is closed to free the memory.
687 * Therefore, only free the returned pointer if you did not supply a
688 * destructor function.
689 * \public \memberof mlt_property_s
690 * \param self a property
691 * \param[out] length the size of the binary object in bytes (optional)
692 * \return an opaque data pointer or NULL if not available
695 void *mlt_property_get_data( mlt_property self, int *length )
697 // Assign length if not NULL
698 if ( length != NULL )
699 *length = self->length;
701 // Return the data (note: there is no conversion here)
705 /** Destroy a property and free all related resources.
707 * \public \memberof mlt_property_s
708 * \param self a property
711 void mlt_property_close( mlt_property self )
713 mlt_property_clear( self );
714 pthread_mutex_destroy( &self->mutex );
720 * A Property holding binary data only copies the data if a serialiser
721 * function was supplied when you set the Property.
722 * \public \memberof mlt_property_s
723 * \author Zach <zachary.drew@gmail.com>
724 * \param self a property
725 * \param that another property
727 void mlt_property_pass( mlt_property self, mlt_property that )
729 pthread_mutex_lock( &self->mutex );
730 mlt_property_clear( self );
732 self->types = that->types;
734 if ( self->types & mlt_prop_int64 )
735 self->prop_int64 = that->prop_int64;
736 else if ( self->types & mlt_prop_int )
737 self->prop_int = that->prop_int;
738 else if ( self->types & mlt_prop_double )
739 self->prop_double = that->prop_double;
740 else if ( self->types & mlt_prop_position )
741 self->prop_position = that->prop_position;
742 if ( self->types & mlt_prop_string )
744 if ( that->prop_string != NULL )
745 self->prop_string = strdup( that->prop_string );
747 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
749 self->types = mlt_prop_string;
750 self->prop_string = self->serialiser( self->data, self->length );
752 pthread_mutex_unlock( &self->mutex );
755 /** Convert frame count to a SMPTE timecode string.
757 * \private \memberof mlt_property_s
758 * \param frames a frame count
759 * \param fps frames per second
760 * \param[out] s the string to write into - must have enough space to hold largest time string
763 static void time_smpte_from_frames( int frames, double fps, char *s )
765 int hours, mins, secs;
766 char frame_sep = ':';
768 if ( fps == 30000.0/1001.0 )
771 int i, max_frames = frames;
772 for ( i = 1800; i <= max_frames; i += 1800 )
782 hours = frames / ( fps * 3600 );
783 frames -= hours * ( fps * 3600 );
784 mins = frames / ( fps * 60 );
785 frames -= mins * ( fps * 60 );
787 frames -= secs * fps;
789 sprintf( s, "%02d:%02d:%02d%c%02d", hours, mins, secs, frame_sep, frames );
792 /** Convert frame count to a SMIL clock value string.
794 * \private \memberof mlt_property_s
795 * \param frames a frame count
796 * \param fps frames per second
797 * \param[out] s the string to write into - must have enough space to hold largest time string
800 static void time_clock_from_frames( int frames, double fps, char *s )
805 hours = frames / ( fps * 3600 );
806 frames -= hours * ( fps * 3600 );
807 mins = frames / ( fps * 60 );
808 frames -= mins * ( fps * 60 );
809 secs = (double) frames / fps;
811 sprintf( s, "%02d:%02d:%06.3f", hours, mins, secs );
814 /** Get the property as a time string.
816 * The time value can be either a SMPTE timecode or SMIL clock value.
817 * The caller is not responsible for deallocating the returned string!
818 * The string is deallocated when the property is closed.
819 * \public \memberof mlt_property_s
820 * \param self a property
821 * \param format the time format that you want
822 * \param fps frames per second
823 * \param locale the locale to use for this conversion
824 * \return a string representation of the property or NULL if failed
827 char *mlt_property_get_time( mlt_property self, mlt_time_format format, double fps, locale_t locale )
829 char *orig_localename = NULL;
830 const char *localename = "";
832 // Optimization for mlt_time_frames
833 if ( format == mlt_time_frames )
834 return mlt_property_get_string_l( self, locale );
836 // Remove existing string
837 if ( self->prop_string )
838 mlt_property_set_int( self, mlt_property_get_int( self, fps, locale ) );
840 // Use the specified locale
843 // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale.
844 // Save the current locale
845 #if defined(__DARWIN__)
846 localename = querylocale( LC_NUMERIC, locale );
847 #elif defined(__GLIBC__)
848 localename = locale->__names[ LC_NUMERIC ];
850 // TODO: not yet sure what to do on other platforms
852 // Protect damaging the global locale from a temporary locale on another thread.
853 pthread_mutex_lock( &self->mutex );
855 // Get the current locale
856 orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
858 // Set the new locale
859 setlocale( LC_NUMERIC, localename );
863 // Make sure we have a lock before accessing self->types
864 pthread_mutex_lock( &self->mutex );
867 // Convert number to string
868 if ( self->types & mlt_prop_int )
870 self->types |= mlt_prop_string;
871 self->prop_string = malloc( 32 );
872 if ( format == mlt_time_clock )
873 time_clock_from_frames( self->prop_int, fps, self->prop_string );
875 time_smpte_from_frames( self->prop_int, fps, self->prop_string );
877 else if ( self->types & mlt_prop_position )
879 self->types |= mlt_prop_string;
880 self->prop_string = malloc( 32 );
881 if ( format == mlt_time_clock )
882 time_clock_from_frames( (int) self->prop_position, fps, self->prop_string );
884 time_smpte_from_frames( (int) self->prop_position, fps, self->prop_string );
886 else if ( self->types & mlt_prop_double )
888 self->types |= mlt_prop_string;
889 self->prop_string = malloc( 32 );
890 if ( format == mlt_time_clock )
891 time_clock_from_frames( self->prop_double, fps, self->prop_string );
893 time_smpte_from_frames( self->prop_double, fps, self->prop_string );
895 else if ( self->types & mlt_prop_int64 )
897 self->types |= mlt_prop_string;
898 self->prop_string = malloc( 32 );
899 if ( format == mlt_time_clock )
900 time_clock_from_frames( (int) self->prop_int64, fps, self->prop_string );
902 time_smpte_from_frames( (int) self->prop_int64, fps, self->prop_string );
905 // Restore the current locale
908 setlocale( LC_NUMERIC, orig_localename );
909 free( orig_localename );
910 pthread_mutex_unlock( &self->mutex );
914 // Make sure we have a lock before accessing self->types
915 pthread_mutex_unlock( &self->mutex );
918 // Return the string (may be NULL)
919 return self->prop_string;
922 static int is_property_numeric( mlt_property self, locale_t locale )
924 int result = ( self->types & mlt_prop_int ) ||
925 ( self->types & mlt_prop_int64 ) ||
926 ( self->types & mlt_prop_double ) ||
927 ( self->types & mlt_prop_position );
929 // If not already numeric but string is numeric.
930 if ( ( !result && self->types & mlt_prop_string ) && self->prop_string )
934 #if defined(__GLIBC__) || defined(__DARWIN__)
936 temp = strtod_l( self->prop_string, &p, locale );
939 temp = strtod( self->prop_string, &p );
940 result = ( p != self->prop_string );
945 static inline double linear_interpolate( double y1, double y2, double t )
947 return y1 + ( y2 - y1 ) * t;
950 // For non-closed curves, you need to also supply the tangent vector at the first and last control point.
951 // This is commonly done: T(P[0]) = P[1] - P[0] and T(P[n]) = P[n] - P[n-1].
952 static inline double catmull_rom_interpolate( double y0, double y1, double y2, double y3, double t )
955 double a0 = -0.5 * y0 + 1.5 * y1 - 1.5 * y2 + 0.5 * y3;
956 double a1 = y0 - 2.5 * y1 + 2 * y2 - 0.5 * y3;
957 double a2 = -0.5 * y0 + 0.5 * y2;
959 return a0 * t * t2 + a1 * t2 + a2 * t + a3;
962 int mlt_property_interpolate( mlt_property self, mlt_property p[],
963 double progress, double fps, locale_t locale, mlt_keyframe_type interp )
966 if ( interp != mlt_keyframe_discrete && fps > 0 &&
967 is_property_numeric( p[1], locale ) && is_property_numeric( p[2], locale ) )
970 if ( interp == mlt_keyframe_linear )
973 points[0] = p[1]? mlt_property_get_double( p[1], fps, locale ) : 0;
974 points[1] = p[2]? mlt_property_get_double( p[2], fps, locale ) : 0;
975 value = p[2]? linear_interpolate( points[0], points[1], progress ) : points[0];
977 else if ( interp == mlt_keyframe_smooth )
980 points[0] = p[0]? mlt_property_get_double( p[0], fps, locale ) : 0;
981 points[1] = p[1]? mlt_property_get_double( p[1], fps, locale ) : 0;
982 points[2] = p[2]? mlt_property_get_double( p[2], fps, locale ) : 0;
983 points[3] = p[3]? mlt_property_get_double( p[3], fps, locale ) : 0;
984 value = p[2]? catmull_rom_interpolate( points[0], points[1], points[2], points[3], progress ) : points[1];
986 error = mlt_property_set_double( self, value );
990 mlt_property_pass( self, p[1] );
995 static void refresh_animation( mlt_property self, double fps, locale_t locale, int length )
997 if ( !self->animation )
999 self->animation = mlt_animation_new();
1000 mlt_animation_parse( self->animation, self->prop_string, length, fps, locale );
1004 mlt_animation_refresh( self->animation, self->prop_string, length );
1008 double mlt_property_get_double_pos( mlt_property self, double fps, locale_t locale, int position, int length )
1011 if ( ( self->types & mlt_prop_string ) && self->prop_string )
1013 struct mlt_animation_item_s item;
1014 item.property = mlt_property_init();
1016 refresh_animation( self, fps, locale, length );
1017 mlt_animation_get_item( self->animation, &item, position );
1018 result = mlt_property_get_double( item.property, fps, locale );
1020 mlt_property_close( item.property );
1024 result = mlt_property_get_double( self, fps, locale );
1029 int mlt_property_get_int_pos( mlt_property self, double fps, locale_t locale, int position, int length )
1032 if ( ( self->types & mlt_prop_string ) && self->prop_string )
1034 struct mlt_animation_item_s item;
1035 item.property = mlt_property_init();
1037 refresh_animation( self, fps, locale, length );
1038 mlt_animation_get_item( self->animation, &item, position );
1039 result = mlt_property_get_int( item.property, fps, locale );
1041 mlt_property_close( item.property );
1045 result = mlt_property_get_int( self, fps, locale );