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 <timo...@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 <timo...@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