+
+/** Convert frame count to a SMPTE timecode string.
+ *
+ * \private \memberof mlt_property_s
+ * \param frames a frame count
+ * \param fps frames per second
+ * \param[out] s the string to write into - must have enough space to hold largest time string
+ */
+
+static void time_smpte_from_frames( int frames, double fps, char *s )
+{
+ int hours, mins, secs;
+ char frame_sep = ':';
+
+ if ( fps == 30000.0/1001.0 )
+ {
+ fps = 30.0;
+ int i, max_frames = frames;
+ for ( i = 1800; i <= max_frames; i += 1800 )
+ {
+ if ( i % 18000 )
+ {
+ max_frames += 2;
+ frames += 2;
+ }
+ }
+ frame_sep = ';';
+ }
+ hours = frames / ( fps * 3600 );
+ frames -= hours * ( fps * 3600 );
+ mins = frames / ( fps * 60 );
+ frames -= mins * ( fps * 60 );
+ secs = frames / fps;
+ frames -= secs * fps;
+
+ sprintf( s, "%02d:%02d:%02d%c%02d", hours, mins, secs, frame_sep, frames );
+}
+
+/** Convert frame count to a SMIL clock value string.
+ *
+ * \private \memberof mlt_property_s
+ * \param frames a frame count
+ * \param fps frames per second
+ * \param[out] s the string to write into - must have enough space to hold largest time string
+ */
+
+static void time_clock_from_frames( int frames, double fps, char *s )
+{
+ int hours, mins;
+ double secs;
+
+ hours = frames / ( fps * 3600 );
+ frames -= hours * ( fps * 3600 );
+ mins = frames / ( fps * 60 );
+ frames -= mins * ( fps * 60 );
+ secs = (double) frames / fps;
+
+ sprintf( s, "%02d:%02d:%06.3f", hours, mins, secs );
+}
+
+/** Get the property as a time string.
+ *
+ * The time value can be either a SMPTE timecode or SMIL clock value.
+ * The caller is not responsible for deallocating the returned string!
+ * The string is deallocated when the property is closed.
+ * \public \memberof mlt_property_s
+ * \param self a property
+ * \param format the time format that you want
+ * \param fps frames per second
+ * \param locale the locale to use for this conversion
+ * \return a string representation of the property or NULL if failed
+ */
+
+char *mlt_property_get_time( mlt_property self, mlt_time_format format, double fps, locale_t locale )
+{
+ char *orig_localename = NULL;
+ const char *localename = "";
+
+ // Optimization for mlt_time_frames
+ if ( format == mlt_time_frames )
+ return mlt_property_get_string_l( self, locale );
+
+ // Remove existing string
+ if ( self->prop_string )
+ mlt_property_set_int( self, mlt_property_get_int( self, fps, locale ) );
+
+ // Use the specified locale
+ if ( locale )
+ {
+ // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale.
+ // Save the current locale
+#if defined(__DARWIN__)
+ localename = querylocale( LC_NUMERIC, locale );
+#elif defined(__GLIBC__)
+ localename = locale->__names[ LC_NUMERIC ];
+#else
+ // TODO: not yet sure what to do on other platforms
+#endif
+ // Protect damaging the global locale from a temporary locale on another thread.
+ pthread_mutex_lock( &self->mutex );
+
+ // Get the current locale
+ orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
+
+ // Set the new locale
+ setlocale( LC_NUMERIC, localename );
+ }
+ else
+ {
+ // Make sure we have a lock before accessing self->types
+ pthread_mutex_lock( &self->mutex );
+ }
+
+ // Convert number to string
+ if ( self->types & mlt_prop_int )
+ {
+ self->types |= mlt_prop_string;
+ self->prop_string = malloc( 32 );
+ if ( format == mlt_time_clock )
+ time_clock_from_frames( self->prop_int, fps, self->prop_string );
+ else
+ time_smpte_from_frames( self->prop_int, fps, self->prop_string );
+ }
+ else if ( self->types & mlt_prop_position )
+ {
+ self->types |= mlt_prop_string;
+ self->prop_string = malloc( 32 );
+ if ( format == mlt_time_clock )
+ time_clock_from_frames( (int) self->prop_position, fps, self->prop_string );
+ else
+ time_smpte_from_frames( (int) self->prop_position, fps, self->prop_string );
+ }
+ else if ( self->types & mlt_prop_double )
+ {
+ self->types |= mlt_prop_string;
+ self->prop_string = malloc( 32 );
+ if ( format == mlt_time_clock )
+ time_clock_from_frames( self->prop_double, fps, self->prop_string );
+ else
+ time_smpte_from_frames( self->prop_double, fps, self->prop_string );
+ }
+ else if ( self->types & mlt_prop_int64 )
+ {
+ self->types |= mlt_prop_string;
+ self->prop_string = malloc( 32 );
+ if ( format == mlt_time_clock )
+ time_clock_from_frames( (int) self->prop_int64, fps, self->prop_string );
+ else
+ time_smpte_from_frames( (int) self->prop_int64, fps, self->prop_string );
+ }
+
+ // Restore the current locale
+ if ( locale )
+ {
+ setlocale( LC_NUMERIC, orig_localename );
+ free( orig_localename );
+ pthread_mutex_unlock( &self->mutex );
+ }
+ else
+ {
+ // Make sure we have a lock before accessing self->types
+ pthread_mutex_unlock( &self->mutex );
+ }
+
+ // Return the string (may be NULL)
+ return self->prop_string;
+}
+
+static int is_property_numeric( mlt_property self, locale_t locale )
+{
+ int result = ( self->types & mlt_prop_int ) ||
+ ( self->types & mlt_prop_int64 ) ||
+ ( self->types & mlt_prop_double ) ||
+ ( self->types & mlt_prop_position );
+
+ // If not already numeric but string is numeric.
+ if ( ( !result && self->types & mlt_prop_string ) && self->prop_string )
+ {
+ double temp;
+ char *p = NULL;
+#if defined(__GLIBC__) || defined(__DARWIN__)
+ if ( locale )
+ temp = strtod_l( self->prop_string, &p, locale );
+ else
+#endif
+ temp = strtod( self->prop_string, &p );
+ result = ( p != self->prop_string );
+ }
+ return result;
+}
+
+static inline double linearstep( double start, double end, double position, int length )
+{
+ double o = ( end - start ) / length;
+ return start + position * o;
+}
+
+int mlt_property_interpolate(mlt_property self, mlt_property previous, mlt_property next,
+ double position, int length, double fps, locale_t locale )
+{
+ int error = 0;
+ if ( fps > 0 && is_property_numeric( previous, locale ) && is_property_numeric( next, locale ) )
+ {
+ double start = previous? mlt_property_get_double( previous, fps, locale ) : 0;
+ double end = next? mlt_property_get_double( next, fps, locale ) : 0;
+ double value = next? linearstep( start, end, position, length ) : start;
+ error = mlt_property_set_double( self, value );
+ }
+ else
+ {
+ mlt_property_pass( self, previous );
+ }
+ return error;
+}