[PATCH] Extending dynamic text capabilities

Timon timon37 at gmail.com
Sun Feb 19 11:55:27 UTC 2017


Hi,

Currently dynamic text is extremely limited, I hacked it up to support
more use-cases, mainly:
 - having an in-clip stopwatch (the docs mention this should work but
it didn't for me with 16.12.2)
 - negative countdowns (e.g. rocket launch)

One other case I think would be very useful, but I didn't take a look
at (I don't need it atm):
 - scaled time for slow-motion video or time lapse (then you should
also add year-month-day)

I just added an origin-frame property (that's the frame at which the time is 0).
The formatting is split into separate keywords for hours, minutes,
seconds, and milliseconds.
These can be chained, so if you set "#time_m#:#time_s#" the seconds
will never go beyond 59 (since minutes count that), but if you only
specify #time_s# the seconds will count arbitrarily large. The main
limitation is that the keywords have to be in most-significant-first
order for this to work.

There's a few issues with the patches though:
1. using an origin-time might be better than an origin-frame since it
doesn't depend on the framerate
2. I wasn't sure what the right type would be to specify the
origin-frame/time, and currently it's primitive and limiting
(min/max), also not very convenient
3. how is compatibility/versioning handled between mlt and other
programs including kdenlive
4. getting the profile_fps as part of substitute_keywords is probably not nice?


I'm not very familiar with either kdenlive nor mlt, so I'd appreciate
if someone would take this over and do whatever needs doing, including
discussing and pushing the patch to mlt.


>From 60f483a6d037bce3a1f99045776ba5a30b987855 Mon Sep 17 00:00:00 2001
From: timon37 <timon37 at gmail.com>
Date: Sun, 19 Feb 2017 11:43:08 +0100
Subject: [PATCH] extend filter_dynamictext with an origin frame and more
 formatting options

---
 data/effects/dynamictext.xml | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/data/effects/dynamictext.xml b/data/effects/dynamictext.xml
index 3de4570..64911c0 100644
--- a/data/effects/dynamictext.xml
+++ b/data/effects/dynamictext.xml
@@ -38,9 +38,12 @@
         <paramlistdisplay>Top,Middle,Bottom</paramlistdisplay>
         <name>Vertical Alignment</name>
     </parameter>
+    <parameter type="constant" name="originframe" max="1000000"
min="-1000000" default="0">
+        <name>Origin Frame</name>
+    </parameter>
     <parameter type="keywords" name="argument" default="#timecode#">
         <name>Text</name>
         <keywords>#timecode#;#frame#;#filedate#;#localfiledate#;#meta.media.0.stream.frame_rate#;#meta.media.0.codec.name#;#meta.media.0.codec.bit_rate#;#meta.media.width#;#meta.media.height#;#meta.attr.comment.markup#</keywords>
-        <keywordsdisplay>timecode;frame;file date;local file
date;source frame rate;source codec;source bit rate;source
width;source height;source comment</keywordsdisplay>
+        <keywordsdisplay>timecode;time_h;time_m;time_s;time_ms;time_sign;frame;file
date;local file date;source frame rate;source codec;source bit
rate;source width;source height;source comment</keywordsdisplay>
     </parameter>
 </effect>
-- 
2.10.2


>From 385599b3ea7e51ba559d509cc2df108f76db2525 Mon Sep 17 00:00:00 2001
From: timon37 <timon37 at gmail.com>
Date: Sun, 19 Feb 2017 12:28:02 +0100
Subject: [PATCH] extend filter_dynamictext with an origin frame and more
 formatting options

---
 src/modules/plus/filter_dynamictext.c   | 56 ++++++++++++++++++++++++++++++---
 src/modules/plus/filter_dynamictext.yml | 12 +++++++
 2 files changed, 63 insertions(+), 5 deletions(-)

diff --git a/src/modules/plus/filter_dynamictext.c
b/src/modules/plus/filter_dynamictext.c
index 412c13a..d367073 100644
--- a/src/modules/plus/filter_dynamictext.c
+++ b/src/modules/plus/filter_dynamictext.c
@@ -167,17 +167,59 @@ static void get_resource_str( mlt_filter filter,
mlt_frame frame, char* text )

 /** Perform substitution for keywords that are enclosed in "# #".
 */
-static void substitute_keywords(mlt_filter filter, char* result,
char* value, mlt_frame frame)
+static void substitute_keywords(mlt_filter filter, char* result,
char* value, mlt_frame frame, mlt_position origin)
 {
 	char keyword[MAX_TEXT_LEN] = "";
 	int pos = 0;
 	int is_keyword = 0;
-
+	
+	mlt_position frames = mlt_frame_get_position( frame ) - origin;
+	int frames_negative = frames < 0 ? 1 : 0;
+	frames = abs( frames );
+	
+	double fps = 0;
+	{
+		mlt_profile profile = mlt_properties_get_data(
MLT_FILTER_PROPERTIES( filter ), "_profile", NULL );
+		if ( profile )
+			fps = mlt_profile_fps( profile );
+	}
+	
 	while ( get_next_token(value, &pos, keyword, &is_keyword) )
 	{
+		size_t len = strlen( result );
+		size_t left = MAX_TEXT_LEN - len;
 		if(!is_keyword)
 		{
-			strncat( result, keyword, MAX_TEXT_LEN - strlen( result ) - 1 );
+			strncat( result, keyword, left - 1 );
+		}
+		else if ( !strcmp( keyword, "time_sign" ) )
+		{
+			if ( frames_negative )
+				strncat( result, "-", left - 1 );
+		}
+		else if ( !strcmp( keyword, "time_h" ) )
+		{
+			int num = frames / ( fps * 60 * 60 );
+			snprintf( result + len, left, "%02d", num );
+			frames -= num * fps * 60 * 60;
+		}
+		else if ( !strcmp( keyword, "time_m" ) )
+		{
+			int num = frames / ( fps * 60 );
+			snprintf( result + len, left, "%02d", num );
+			frames -= num * fps * 60;
+		}
+		else if ( !strcmp( keyword, "time_s" ) )
+		{
+			int num = frames / fps;
+			snprintf( result + len, left, "%02d", num );
+			frames -= num * fps;
+		}
+		else if ( !strcmp( keyword, "time_ms" ) )
+		{
+			int num = ( frames / fps ) * 1000;
+			snprintf( result + len, left, "%03d", num );
+			frames -= num / 1000 * fps;
 		}
 		else if ( !strcmp( keyword, "timecode" ) || !strcmp( keyword, "smpte_df" ) )
 		{
@@ -214,7 +256,7 @@ static void substitute_keywords(mlt_filter filter,
char* result, char* value, ml
 			char *frame_value = mlt_properties_get( frame_properties, keyword );
 			if( frame_value )
 			{
-				strncat( result, frame_value, MAX_TEXT_LEN - strlen(result) - 1 );
+				strncat( result, frame_value, left - 1 );
 			}
 		}
 	}
@@ -232,9 +274,12 @@ static int setup_producer( mlt_filter filter,
mlt_producer producer, mlt_frame f
 	// Check for keywords in dynamic text
 	if ( dynamic_text )
 	{
+		int origin = 0;
+		if ( mlt_properties_get( my_properties, "originframe" ) )
+			origin = mlt_properties_get_int( my_properties, "originframe" );
 		// Apply keyword substitution before passing the text to the filter.
 		char result[MAX_TEXT_LEN] = "";
-		substitute_keywords( filter, result, dynamic_text, frame );
+		substitute_keywords( filter, result, dynamic_text, frame, origin );
 		mlt_properties_set( producer_properties, "text", (char*)result );
 	}

@@ -386,6 +431,7 @@ mlt_filter filter_dynamictext_init( mlt_profile
profile, mlt_service_type type,
 		mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" );

 		// Assign default values
+		mlt_properties_set( my_properties, "originframe", "0" );
 		mlt_properties_set( my_properties, "argument", arg ? arg: "#timecode#" );
 		mlt_properties_set( my_properties, "geometry", "0%/0%:100%x100%:100" );
 		mlt_properties_set( my_properties, "family", "Sans" );
diff --git a/src/modules/plus/filter_dynamictext.yml
b/src/modules/plus/filter_dynamictext.yml
index 4035ee7..2e4fb90 100644
--- a/src/modules/plus/filter_dynamictext.yml
+++ b/src/modules/plus/filter_dynamictext.yml
@@ -24,6 +24,11 @@ parameters:
         * #smpte_df#      - SMPTE drop-frame timecode of the frame
         * #smpte_ndf#     - SMPTE non-drop-frame timecode of the frame
         * #timecode#      - same as #smpte_df#
+        * #time_h#        - time in hours
+        * #time_m#        - time in minutes
+        * #time_s#        - time in seconds
+        * #time_ms#       - time in miliseconds
+        * #time_sign#     - a minus if the Origin Frame is in the future
         * #frame#         - frame number of the frame
         * #filedate#      - modification date of the file (GMT)
         * #localfiledate# - modification date of the file (local)
@@ -42,6 +47,13 @@ parameters:
       #timecode#
     widget: text

+  - identifier: originframe
+    title: Origin Frame
+    type: integer
+    default: 0
+    unit: frames
+    widget: spinner
+
   - identifier: geometry
     title: Geometry
     type: geometry
-- 
2.10.2


More information about the kdenlive mailing list