2 * filter_dynamictext.c -- dynamic text overlay filter
3 * Copyright (C) 2011 Ushodaya Enterprises Limited
4 * Author: Brian Matherly <pez4brian@yahoo.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <framework/mlt.h>
25 #include <sys/types.h> // for stat()
26 #include <sys/stat.h> // for stat()
27 #include <unistd.h> // for stat()
28 #include <time.h> // for strftime() and gtime()
30 #define MAX_TEXT_LEN 512
32 /** Get the next token and indicate whether it is enclosed in "# #".
34 static int get_next_token(char* str, int* pos, char* token, int* is_keyword)
37 int str_len = strlen( str );
39 if( (*pos) >= str_len || str[*pos] == '\0' )
44 if( str[*pos] == '#' )
54 while( *pos < str_len && token_pos < MAX_TEXT_LEN - 1)
56 if( str[*pos] == '\\' && str[(*pos) + 1] == '#' )
58 // Escape Sequence - "#" preceeded by "\" - copy the # into the token.
59 token[token_pos] = '#';
64 else if( str[*pos] == '#' )
68 // Found the end of the keyword
75 token[token_pos] = str[*pos];
81 token[token_pos] = '\0';
86 static void get_timecode_str( mlt_filter filter, mlt_frame frame, char* text )
88 int frames = mlt_frame_get_position( frame );
89 double fps = mlt_profile_fps( mlt_service_profile( MLT_FILTER_SERVICE( filter ) ) );
93 strncat( text, "-", MAX_TEXT_LEN - strlen( text ) - 1 );
97 int seconds = frames / fps;
98 frames = frames % lrint( fps );
99 int minutes = seconds / 60;
100 seconds = seconds % 60;
101 int hours = minutes / 60;
102 minutes = minutes % 60;
103 sprintf(tc, "%.2d:%.2d:%.2d:%.2d", hours, minutes, seconds, frames);
104 strncat( text, tc, MAX_TEXT_LEN - strlen( text ) - 1 );
108 static void get_frame_str( mlt_filter filter, mlt_frame frame, char* text )
110 int pos = mlt_frame_get_position( frame );
112 snprintf( s, sizeof( s ) - 1, "%d", pos );
113 strncat( text, s, MAX_TEXT_LEN - strlen( text ) - 1 );
116 static void get_filedate_str( mlt_filter filter, mlt_frame frame, char* text )
118 mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) );
119 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
120 char* filename = mlt_properties_get( producer_properties, "resource");
121 struct stat file_info;
123 if( !stat(filename, &file_info))
125 struct tm* time_info = gmtime( &(file_info.st_mtime) );
127 strftime( date, 11, "%Y/%m/%d", time_info );
128 strncat( text, date, MAX_TEXT_LEN - strlen( text ) - 1);
132 static void get_localfiledate_str( mlt_filter filter, mlt_frame frame, char* text )
134 mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) );
135 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
136 char* filename = mlt_properties_get( producer_properties, "resource" );
137 struct stat file_info;
139 if( !stat( filename, &file_info ) )
141 struct tm* time_info = localtime( &(file_info.st_mtime) );
143 strftime( date, 11, "%Y/%m/%d", time_info );
144 strncat( text, date, MAX_TEXT_LEN - strlen( text ) - 1);
148 static void get_resource_str( mlt_filter filter, mlt_frame frame, char* text )
150 mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) );
151 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
152 strncat( text, mlt_properties_get( producer_properties, "resource" ), MAX_TEXT_LEN - strlen( text ) - 1 );
155 /** Perform substitution for keywords that are enclosed in "# #".
157 static void substitute_keywords(mlt_filter filter, char* result, char* value, mlt_frame frame)
159 char keyword[MAX_TEXT_LEN] = "";
163 while ( get_next_token(value, &pos, keyword, &is_keyword) )
167 strncat( result, keyword, MAX_TEXT_LEN - strlen( result ) - 1 );
169 else if ( !strcmp( keyword, "timecode" ) )
171 get_timecode_str( filter, frame, result );
173 else if ( !strcmp( keyword, "frame" ) )
175 get_frame_str( filter, frame, result );
177 else if ( !strcmp( keyword, "filedate" ) )
179 get_filedate_str( filter, frame, result );
181 else if ( !strcmp( keyword, "localfiledate" ) )
183 get_localfiledate_str( filter, frame, result );
185 else if ( !strcmp( keyword, "resource" ) )
187 get_resource_str( filter, frame, result );
191 // replace keyword with property value from this frame
192 mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
193 char *frame_value = mlt_properties_get( frame_properties, keyword );
196 strncat( result, frame_value, MAX_TEXT_LEN - strlen(result) - 1 );
202 static void setup_producer( mlt_filter filter, mlt_producer producer, mlt_frame frame )
204 mlt_properties my_properties = MLT_FILTER_PROPERTIES( filter );
205 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
206 char* dynamic_text = mlt_properties_get( my_properties, "argument" );
208 // Check for keywords in dynamic text
211 // Apply keyword substitution before passing the text to the filter.
212 char result[MAX_TEXT_LEN] = "";
213 substitute_keywords( filter, result, dynamic_text, frame );
214 mlt_properties_set( producer_properties, "markup", (char*)result );
217 // Pass the properties to the pango producer
218 mlt_properties_set( producer_properties, "family", mlt_properties_get( my_properties, "family" ) );
219 mlt_properties_set( producer_properties, "size", mlt_properties_get( my_properties, "size" ) );
220 mlt_properties_set( producer_properties, "weight", mlt_properties_get( my_properties, "weight" ) );
221 mlt_properties_set( producer_properties, "fgcolour", mlt_properties_get( my_properties, "fgcolour" ) );
222 mlt_properties_set( producer_properties, "bgcolour", mlt_properties_get( my_properties, "bgcolour" ) );
223 mlt_properties_set( producer_properties, "olcolour", mlt_properties_get( my_properties, "olcolour" ) );
224 mlt_properties_set( producer_properties, "pad", mlt_properties_get( my_properties, "pad" ) );
225 mlt_properties_set( producer_properties, "outline", mlt_properties_get( my_properties, "outline" ) );
226 mlt_properties_set( producer_properties, "align", mlt_properties_get( my_properties, "halign" ) );
229 static void setup_transition( mlt_filter filter, mlt_transition transition )
231 mlt_properties my_properties = MLT_FILTER_PROPERTIES( filter );
232 mlt_properties transition_properties = MLT_TRANSITION_PROPERTIES( transition );
234 mlt_properties_set( transition_properties, "geometry", mlt_properties_get( my_properties, "geometry" ) );
235 mlt_properties_set( transition_properties, "halign", mlt_properties_get( my_properties, "halign" ) );
236 mlt_properties_set( transition_properties, "valign", mlt_properties_get( my_properties, "valign" ) );
237 mlt_properties_set_int( transition_properties, "out", mlt_properties_get_int( my_properties, "_out" ) );
238 mlt_properties_set_int( transition_properties, "refresh", 1 );
244 static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
247 mlt_filter filter = mlt_frame_pop_service( frame );
248 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
249 mlt_producer producer = mlt_properties_get_data( properties, "_producer", NULL );
250 mlt_transition transition = mlt_properties_get_data( properties, "_transition", NULL );
251 mlt_frame text_frame = NULL;
252 mlt_position position = 0;
254 // Configure this filter
255 mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
256 setup_producer( filter, producer, frame );
257 setup_transition( filter, transition );
258 mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
260 // Make sure the producer is in the correct position
261 position = mlt_filter_get_position( filter, frame );
262 mlt_producer_seek( producer, position );
264 // Get the b frame and process with transition if successful
265 if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &text_frame, 0 ) == 0 )
267 // Get the a and b frame properties
268 mlt_properties a_props = MLT_FRAME_PROPERTIES( frame );
269 mlt_properties b_props = MLT_FRAME_PROPERTIES( text_frame );
271 // Set the frame and text_frame to be in the same position and have same consumer requirements
272 mlt_frame_set_position( text_frame, position );
273 mlt_frame_set_position( frame, position );
274 mlt_properties_set_int( b_props, "consumer_deinterlace", mlt_properties_get_int( a_props, "consumer_deinterlace" ) );
276 // Apply all filters that are attached to this filter to the b frame
277 mlt_service_apply_filters( MLT_FILTER_SERVICE( filter ), text_frame, 0 );
280 mlt_transition_process( transition, frame, text_frame );
283 *format = mlt_image_yuv422;
284 error = mlt_frame_get_image( frame, image, format, width, height, 1 );
287 mlt_frame_close( text_frame );
293 /** Filter processing.
295 static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
297 // Get the properties of the frame
298 mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
300 // Save the frame out point
301 mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_out", mlt_properties_get_int( properties, "out" ) );
303 // Push the filter on to the stack
304 mlt_frame_push_service( frame, filter );
306 // Push the get_image on to the stack
307 mlt_frame_push_get_image( frame, filter_get_image );
312 /** Constructor for the filter.
314 mlt_filter filter_dynamictext_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
316 mlt_filter filter = mlt_filter_new();
317 mlt_transition transition = mlt_factory_transition( profile, "composite", NULL );
318 mlt_producer producer = producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), "pango:" );
320 if ( filter && transition && producer )
322 mlt_properties my_properties = MLT_FILTER_PROPERTIES( filter );
324 // Register the transition for reuse/destruction
325 mlt_properties_set_data( my_properties, "_transition", transition, 0, ( mlt_destructor )mlt_transition_close, NULL );
327 // Register the producer for reuse/destruction
328 mlt_properties_set_data( my_properties, "_producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
330 // Ensure that we loop
331 mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" );
333 // Assign default values
334 mlt_properties_set( my_properties, "argument", arg ? arg: "#timecode#" );
335 mlt_properties_set( my_properties, "geometry", "0%/0%:100%x100%:100" );
336 mlt_properties_set( my_properties, "family", "Sans" );
337 mlt_properties_set( my_properties, "size", "48" );
338 mlt_properties_set( my_properties, "weight", "400" );
339 mlt_properties_set( my_properties, "fgcolour", "0x000000ff" );
340 mlt_properties_set( my_properties, "bgcolour", "0x00000020" );
341 mlt_properties_set( my_properties, "olcolour", "0x00000000" );
342 mlt_properties_set( my_properties, "pad", "0" );
343 mlt_properties_set( my_properties, "halign", "left" );
344 mlt_properties_set( my_properties, "valign", "top" );
345 mlt_properties_set( my_properties, "outline", "0" );
347 mlt_properties_set_int( my_properties, "_filter_private", 1 );
349 filter->process = filter_process;
355 mlt_filter_close( filter );
360 mlt_transition_close( transition );
365 mlt_producer_close( producer );