]> git.sesse.net Git - mlt/commitdiff
add support for timecode and clock time strings to the framework
authorDan Dennedy <dan@dennedy.org>
Sat, 31 Mar 2012 23:39:05 +0000 (16:39 -0700)
committerDan Dennedy <dan@dennedy.org>
Mon, 2 Apr 2012 01:42:13 +0000 (18:42 -0700)
configure
src/framework/mlt_properties.c
src/framework/mlt_properties.h
src/framework/mlt_property.c
src/framework/mlt_property.h
src/framework/mlt_types.h
src/mlt++/MltProperties.cpp
src/mlt++/MltProperties.h

index 33b8d8e6f0b1ad816c84dcf149f3a6e1fd87f8b4..89307f346b9c671a8e39d714786f79d5b97c69a9 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 export version=0.7.9
-export soversion=4
+export soversion=5
 
 show_help()
 {
index c381e239a0828a97ed3bbe94ecb74f9f4043d186..4f691e1d03b96230853924b2258efc7135a942ec 100644 (file)
@@ -871,8 +871,11 @@ int mlt_properties_parse( mlt_properties self, const char *namevalue )
 
 int mlt_properties_get_int( mlt_properties self, const char *name )
 {
+       mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
+       double fps = mlt_profile_fps( profile );
+       property_list *list = self->local;
        mlt_property value = mlt_properties_find( self, name );
-       return value == NULL ? 0 : mlt_property_get_int( value );
+       return value == NULL ? 0 : mlt_property_get_int( value, fps, list->locale );
 }
 
 /** Set a property to an integer value.
@@ -955,9 +958,11 @@ int mlt_properties_set_int64( mlt_properties self, const char *name, int64_t val
 
 double mlt_properties_get_double( mlt_properties self, const char *name )
 {
+       mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
+       double fps = mlt_profile_fps( profile );
        mlt_property value = mlt_properties_find( self, name );
        property_list *list = self->local;
-       return value == NULL ? 0 : mlt_property_get_double_l( value, list->locale );
+       return value == NULL ? 0 : mlt_property_get_double( value, fps, list->locale );
 }
 
 /** Set a property to a floating point value.
@@ -998,8 +1003,11 @@ int mlt_properties_set_double( mlt_properties self, const char *name, double val
 
 mlt_position mlt_properties_get_position( mlt_properties self, const char *name )
 {
+       mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
+       double fps = mlt_profile_fps( profile );
+       property_list *list = self->local;
        mlt_property value = mlt_properties_find( self, name );
-       return value == NULL ? 0 : mlt_property_get_position( value );
+       return value == NULL ? 0 : mlt_property_get_position( value, fps, list->locale );
 }
 
 /** Set a property to a position value.
@@ -1952,3 +1960,26 @@ void mlt_properties_unlock( mlt_properties self )
        if ( self )
                pthread_mutex_unlock( &( ( property_list* )( self->local ) )->mutex );
 }
+
+/** Get a time string associated to the name.
+ *
+ * Do not free the returned string. It's lifetime is controlled by the property.
+ * \public \memberof mlt_properties_s
+ * \param self a properties list
+ * \param name the property to get
+ * \param format the time format that you want
+ * \return the property's time value or NULL if \p name does not exist or there is no profile
+ */
+
+char *mlt_properties_get_time( mlt_properties self, const char* name, mlt_time_format format )
+{
+       mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL );
+       if ( profile )
+       {
+               double fps = mlt_profile_fps( profile );
+               mlt_property value = mlt_properties_find( self, name );
+               property_list *list = self->local;
+               return value == NULL ? NULL : mlt_property_get_time( value, format, fps, list->locale );
+       }
+       return NULL;
+}
index 57c653ff7ff725cda73574a815293765089d4977..449f3630706aaef1e5049fa9747ad5d621a8c2a8 100644 (file)
@@ -88,5 +88,6 @@ extern mlt_properties mlt_properties_parse_yaml( const char *file );
 extern char *mlt_properties_serialise_yaml( mlt_properties self );
 extern void mlt_properties_lock( mlt_properties self );
 extern void mlt_properties_unlock( mlt_properties self );
+extern char *mlt_properties_get_time( mlt_properties, const char* name, mlt_time_format );
 
 #endif
index 3cc9f8b8bdfc3151916067fb1f11b9c714bdde5e..d3863abe0fd4b6c5a868d54ef75d436242afb36a 100644 (file)
@@ -264,10 +264,105 @@ int mlt_property_set_data( mlt_property self, void *value, int length, mlt_destr
        return 0;
 }
 
-/** Convert a base 10 or base 16 string to an integer.
+/** Parse a SMIL clock value.
+ *
+ * \private \memberof mlt_property_s
+ * \param s the string to parse
+ * \param fps frames per second
+ * \param locale the locale to use for parsing a real number value
+ * \return position in frames
+ */
+
+static int time_clock_to_frames( const char *s, double fps, locale_t locale )
+{
+       char *pos, *copy = strdup( s );
+       int hours = 0, minutes = 0;
+       double seconds;
+
+       s = copy;
+       pos = strrchr( s, ':' );
+       if ( pos ) {
+#if defined(__GLIBC__) || defined(__DARWIN__)
+               if ( locale )
+                       seconds = strtod_l( pos + 1, NULL, locale );
+               else
+#endif
+                       seconds = strtod( pos + 1, NULL );
+               *pos = 0;
+               pos = strrchr( s, ':' );
+               if ( pos ) {
+                       minutes = atoi( pos + 1 );
+                       *pos = 0;
+                       hours = atoi( s );
+               }
+               else {
+                       minutes = atoi( s );
+               }
+       }
+       else {
+#if defined(__GLIBC__) || defined(__DARWIN__)
+               if ( locale )
+                       seconds = strtod_l( s, NULL, locale );
+               else
+#endif
+                       seconds = strtod( s, NULL );
+       }
+       free( copy );
+
+       return fps * ( (hours * 3600) + (minutes * 60) + seconds ) + 0.5;
+}
+
+/** Parse a SMPTE timecode string.
+ *
+ * \private \memberof mlt_property_s
+ * \param s the string to parse
+ * \param fps frames per second
+ * \return position in frames
+ */
+
+static int time_code_to_frames( const char *s, double fps )
+{
+       char *pos, *copy = strdup( s );
+       int hours = 0, minutes = 0, seconds = 0, frames;
+
+       s = copy;
+       pos = strrchr( s, ';' );
+       if ( !pos )
+               pos = strrchr( s, ':' );
+       if ( pos ) {
+               frames = atoi( pos + 1 );
+               *pos = 0;
+               pos = strrchr( s, ':' );
+               if ( pos ) {
+                       seconds = atoi( pos + 1 );
+                       *pos = 0;
+                       pos = strrchr( s, ':' );
+                       if ( pos ) {
+                               minutes = atoi( pos + 1 );
+                               *pos = 0;
+                               hours = atoi( s );
+                       }
+                       else {
+                               minutes = atoi( s );
+                       }
+               }
+               else {
+                       seconds = atoi( s );
+               }
+       }
+       else {
+               frames = atoi( s );
+       }
+       free( copy );
+
+       return frames + ( fps * ( (hours * 3600) + (minutes * 60) + seconds ) + 0.5 );
+}
+
+/** Convert a string to an integer.
  *
  * The string must begin with '0x' to be interpreted as hexadecimal.
  * Otherwise, it is interpreted as base 10.
+ *
  * If the string begins with '#' it is interpreted as a hexadecimal color value
  * in the form RRGGBB or AARRGGBB. Color values that begin with '0x' are
  * always in the form RRGGBBAA where the alpha components are not optional.
@@ -277,14 +372,17 @@ int mlt_property_set_data( mlt_property self, void *value, int length, mlt_destr
  * right to obtain RGB without alpha in order to make it do a logical instead
  * of arithmetic shift.
  *
+ * If the string contains a colon it is interpreted as a time value. If it also
+ * contains a period or comma character, the string is parsed as a clock value:
+ * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF.
  * \private \memberof mlt_property_s
  * \param value a string to convert
+ * \param fps frames per second, used when converting from time value
+ * \param locale the locale to use when converting from time clock value
  * \return the resultant integer
  */
-static inline int mlt_property_atoi( const char *value )
+static int mlt_property_atoi( const char *value, double fps, locale_t locale )
 {
-       if ( value == NULL )
-               return 0;
        // Parse a hex color value as #RRGGBB or #AARRGGBB.
        if ( value[0] == '#' )
        {
@@ -298,6 +396,13 @@ static inline int mlt_property_atoi( const char *value )
        {
                return strtoul( value + 2, NULL, 16 );
        }
+       else if ( fps > 0 && strchr( value, ':' ) )
+       {
+               if ( strchr( value, '.' ) || strchr( value, ',' ) )
+                       return time_clock_to_frames( value, fps, locale );
+               else
+                       return time_code_to_frames( value, fps );
+       }
        else
        {
                return strtol( value, NULL, 10 );
@@ -308,10 +413,12 @@ static inline int mlt_property_atoi( const char *value )
  *
  * \public \memberof mlt_property_s
  * \param self a property
+ * \param fps frames per second, used when converting from time value
+ * \param locale the locale to use when converting from time clock value
  * \return an integer value
  */
 
-int mlt_property_get_int( mlt_property self )
+int mlt_property_get_int( mlt_property self, double fps, locale_t locale )
 {
        if ( self->types & mlt_prop_int )
                return self->prop_int;
@@ -322,41 +429,50 @@ int mlt_property_get_int( mlt_property self )
        else if ( self->types & mlt_prop_int64 )
                return ( int )self->prop_int64;
        else if ( ( self->types & mlt_prop_string ) && self->prop_string )
-               return mlt_property_atoi( self->prop_string );
+               return mlt_property_atoi( self->prop_string, fps, locale );
        return 0;
 }
 
-/** Get the property as a floating point.
+/** Convert a string to a floating point number.
  *
- * \public \memberof mlt_property_s
- * \param self a property
- * \return a floating point value
+ * If the string contains a colon it is interpreted as a time value. If it also
+ * contains a period or comma character, the string is parsed as a clock value:
+ * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF.
+ * \private \memberof mlt_property_s
+ * \param value the string to convert
+ * \param fps frames per second, used when converting from time value
+ * \param locale the locale to use when converting from time clock value
+ * \return the resultant real number
  */
-
-double mlt_property_get_double( mlt_property self )
+static double mlt_property_atof( const char *value, double fps, locale_t locale )
 {
-       if ( self->types & mlt_prop_double )
-               return self->prop_double;
-       else if ( self->types & mlt_prop_int )
-               return ( double )self->prop_int;
-       else if ( self->types & mlt_prop_position )
-               return ( double )self->prop_position;
-       else if ( self->types & mlt_prop_int64 )
-               return ( double )self->prop_int64;
-       else if ( ( self->types & mlt_prop_string ) && self->prop_string )
-               return atof( self->prop_string );
-       return 0;
+       if ( fps > 0 && strchr( value, ':' ) )
+       {
+               if ( strchr( value, '.' ) || strchr( value, ',' ) )
+                       return time_clock_to_frames( value, fps, locale );
+               else
+                       return time_code_to_frames( value, fps );
+       }
+       else
+       {
+#if defined(__GLIBC__) || defined(__DARWIN__)
+               if ( locale )
+                       return strtod_l( value, NULL, locale );
+#endif
+               return strtod( value, NULL );
+       }
 }
 
-/** Get the property (with locale) as a floating point.
+/** Get the property as a floating point.
  *
  * \public \memberof mlt_property_s
  * \param self a property
+ * \param fps frames per second, used when converting from time value
  * \param locale the locale to use for this conversion
  * \return a floating point value
  */
 
-double mlt_property_get_double_l( mlt_property self, locale_t locale )
+double mlt_property_get_double( mlt_property self, double fps, locale_t locale )
 {
        if ( self->types & mlt_prop_double )
                return self->prop_double;
@@ -366,12 +482,8 @@ double mlt_property_get_double_l( mlt_property self, locale_t locale )
                return ( double )self->prop_position;
        else if ( self->types & mlt_prop_int64 )
                return ( double )self->prop_int64;
-#if defined(__GLIBC__) || defined(__DARWIN__)
-       else if ( locale && ( self->types & mlt_prop_string ) && self->prop_string )
-               return strtod_l( self->prop_string, NULL, locale );
-#endif
        else if ( ( self->types & mlt_prop_string ) && self->prop_string )
-               return strtod( self->prop_string, NULL );
+               return mlt_property_atof( self->prop_string, fps, locale );
        return 0;
 }
 
@@ -380,10 +492,12 @@ double mlt_property_get_double_l( mlt_property self, locale_t locale )
  * A position is an offset time in terms of frame units.
  * \public \memberof mlt_property_s
  * \param self a property
+ * \param fps frames per second, used when converting from time value
+ * \param locale the locale to use when converting from time clock value
  * \return the position in frames
  */
 
-mlt_position mlt_property_get_position( mlt_property self )
+mlt_position mlt_property_get_position( mlt_property self, double fps, locale_t locale )
 {
        if ( self->types & mlt_prop_position )
                return self->prop_position;
@@ -394,7 +508,7 @@ mlt_position mlt_property_get_position( mlt_property self )
        else if ( self->types & mlt_prop_int64 )
                return ( mlt_position )self->prop_int64;
        else if ( ( self->types & mlt_prop_string ) && self->prop_string )
-               return ( mlt_position )atol( self->prop_string );
+               return ( mlt_position )mlt_property_atoi( self->prop_string, fps, locale );
        return 0;
 }
 
@@ -643,3 +757,160 @@ void mlt_property_pass( mlt_property self, mlt_property that )
        }
        pthread_mutex_unlock( &self->mutex );
 }
+
+/** 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__)
+               localname = 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 );
+       }
+
+       // 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 );
+       }
+
+       // Return the string (may be NULL)
+       return self->prop_string;
+}
index c50302f779d262cb84366f5bb807edb53094cb8a..cad70a36e219cbc976730fd69cea91b570ff9cde 100644 (file)
@@ -43,16 +43,15 @@ extern int mlt_property_set_position( mlt_property self, mlt_position value );
 extern int mlt_property_set_int64( mlt_property self, int64_t value );
 extern int mlt_property_set_string( mlt_property self, const char *value );
 extern int mlt_property_set_data( mlt_property self, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser );
-extern int mlt_property_get_int( mlt_property self );
-extern double mlt_property_get_double( mlt_property self );
-extern double mlt_property_get_double_l( mlt_property self, locale_t );
-extern mlt_position mlt_property_get_position( mlt_property self );
+extern int mlt_property_get_int( mlt_property self, double fps, locale_t );
+extern double mlt_property_get_double( mlt_property self, double fps, locale_t );
+extern mlt_position mlt_property_get_position( mlt_property self, double fps, locale_t );
 extern int64_t mlt_property_get_int64( mlt_property self );
 extern char *mlt_property_get_string( mlt_property self );
 extern char *mlt_property_get_string_l( mlt_property self, locale_t );
 extern void *mlt_property_get_data( mlt_property self, int *length );
 extern void mlt_property_close( mlt_property self );
-
 extern void mlt_property_pass( mlt_property self, mlt_property that );
+extern char *mlt_property_get_time( mlt_property self, mlt_time_format, double fps, locale_t );
 
 #endif
index 99fd4a3ed1ae64a75c1676a473f15ec2290ee035..f242f12cc2b8bf2d58a4d69701f5938a24c86491 100644 (file)
@@ -62,6 +62,16 @@ typedef enum
 }
 mlt_audio_format;
 
+/** The time string formats */
+
+typedef enum
+{
+       mlt_time_frames = 0, /**< frame count */
+       mlt_time_clock,      /**< SMIL clock-value as [[hh:]mm:]ss[.fraction] */
+       mlt_time_smpte       /**< SMPTE timecode as [[[hh:]mm:]ss:]frames */
+}
+mlt_time_format;
+
 /** The relative time qualifiers */
 
 typedef enum
index f37f777aeedb127eaac9de689ec6782e1bad8571..c80756eb17abe013df6be19d918387dd11a4be45 100644 (file)
@@ -331,3 +331,8 @@ const char *Properties::get_lcnumeric( )
 {
        return mlt_properties_get_lcnumeric( get_properties() );
 }
+
+char *Properties::get_time( const char *name, mlt_time_format format )
+{
+       return mlt_properties_get_time( get_properties(), name, format );
+}
index 5a8718c65a36be16cd76fce1d69162611a90607c..dfbc5eecdd85c5894e00e77ed5d1767b6c02f695 100644 (file)
@@ -96,6 +96,7 @@ namespace Mlt
                        int preset( const char *name );
                        int set_lcnumeric( const char *locale );
                        const char *get_lcnumeric( );
+                       char *get_time( const char *name, mlt_time_format = mlt_time_smpte );
        };
 }