]> git.sesse.net Git - mlt/blob - src/modules/gtk2/filter_dynamictext.c
d0f42344abb6d34bb42aa50c0934b197be4cffcd
[mlt] / src / modules / gtk2 / filter_dynamictext.c
1 /*
2  * filter_dynamictext.c -- dynamic text overlay filter
3  * Copyright (C) 2011 Ushodaya Enterprises Limited
4  * Author: Brian Matherly <pez4brian@yahoo.com>
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include <framework/mlt.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <math.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()
29
30 #define MAX_TEXT_LEN 512
31
32 /** Get the next token and indicate whether it is enclosed in "# #".
33 */
34 static int get_next_token(char* str, int* pos, char* token, int* is_keyword)
35 {
36         int token_pos = 0;
37         int str_len = strlen( str );
38
39         if( (*pos) >= str_len || str[*pos] == '\0' )
40         {
41                 return 0;
42         }
43
44         if( str[*pos] == '#' )
45         {
46                 *is_keyword = 1;
47                 (*pos)++;
48         }
49         else
50         {
51                 *is_keyword = 0;
52         }
53
54         while( *pos < str_len && token_pos < MAX_TEXT_LEN - 1)
55         {
56                 if( str[*pos] == '\\' && str[(*pos) + 1] == '#' )
57                 {
58                         // Escape Sequence - "#" preceeded by "\" - copy the # into the token.
59                         token[token_pos] = '#';
60                         token_pos++;
61                         (*pos)++; // skip "\"
62                         (*pos)++; // skip "#"
63                 }
64                 else if( str[*pos] == '#' )
65                 {
66                         if( *is_keyword )
67                         {
68                                 // Found the end of the keyword
69                                 (*pos)++;
70                         }
71                         break;
72                 }
73                 else
74                 {
75                         token[token_pos] = str[*pos];
76                         token_pos++;
77                         (*pos)++;
78                 }
79         }
80
81         token[token_pos] = '\0';
82
83         return 1;
84 }
85
86 static void get_timecode_str( mlt_filter filter, mlt_frame frame, char* text )
87 {
88         int frames = mlt_frame_get_position( frame );
89         double fps = mlt_profile_fps( mlt_service_profile( MLT_FILTER_SERVICE( filter ) ) );
90         char tc[12] = "";
91         if (fps == 0)
92         {
93                 strncat( text, "-", MAX_TEXT_LEN - strlen( text ) - 1 );
94         }
95         else
96         {
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 );
105         }
106 }
107
108 static void get_frame_str( mlt_filter filter, mlt_frame frame, char* text )
109 {
110         int pos = mlt_frame_get_position( frame );
111         char s[12];
112         snprintf( s, sizeof( s ) - 1, "%d", pos );
113         strncat( text, s, MAX_TEXT_LEN - strlen( text ) - 1 );
114 }
115
116 static void get_filedate_str( mlt_filter filter, mlt_frame frame, char* text )
117 {
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;
122
123         if( !stat(filename, &file_info))
124         {
125                 struct tm* time_info = gmtime( &(file_info.st_mtime) );
126                 char date[11] = "";
127                 strftime( date, 11, "%Y/%m/%d", time_info );
128                 strncat( text, date, MAX_TEXT_LEN - strlen( text ) - 1);
129         }
130 }
131
132 static void get_localfiledate_str( mlt_filter filter, mlt_frame frame, char* text )
133 {
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;
138
139         if( !stat( filename, &file_info ) )
140         {
141                 struct tm* time_info = localtime( &(file_info.st_mtime) );
142                 char date[11] = "";
143                 strftime( date, 11, "%Y/%m/%d", time_info );
144                 strncat( text, date, MAX_TEXT_LEN - strlen( text ) - 1);
145         }
146 }
147
148 static void get_resource_str( mlt_filter filter, mlt_frame frame, char* text )
149 {
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 );
153 }
154
155 /** Perform substitution for keywords that are enclosed in "# #".
156 */
157
158 static void substitute_keywords(mlt_filter filter, char* result, char* value, mlt_frame frame)
159 {
160         char keyword[MAX_TEXT_LEN] = "";
161         int pos = 0;
162         int is_keyword = 0;
163
164         while ( get_next_token(value, &pos, keyword, &is_keyword) )
165         {
166                 if(!is_keyword)
167                 {
168                         strncat( result, keyword, MAX_TEXT_LEN - strlen( result ) - 1 );
169                 }
170                 else if ( !strcmp( keyword, "timecode" ) )
171                 {
172                         get_timecode_str( filter, frame, result );
173                 }
174                 else if ( !strcmp( keyword, "frame" ) )
175                 {
176                         get_frame_str( filter, frame, result );
177                 }
178                 else if ( !strcmp( keyword, "filedate" ) )
179                 {
180                         get_filedate_str( filter, frame, result );
181                 }
182                 else if ( !strcmp( keyword, "localfiledate" ) )
183                 {
184                         get_localfiledate_str( filter, frame, result );
185                 }
186                 else if ( !strcmp( keyword, "resource" ) )
187                 {
188                         get_resource_str( filter, frame, result );
189                 }
190                 else
191                 {
192                         // replace keyword with property value from this frame
193                         mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
194                         char *frame_value = mlt_properties_get( frame_properties, keyword );
195                         if( frame_value )
196                         {
197                                 strncat( result, frame_value, MAX_TEXT_LEN - strlen(result) - 1 );
198                         }
199                 }
200         }
201 }
202
203 static void apply_filter( mlt_filter filter, mlt_frame frame )
204 {
205         mlt_properties my_properties = MLT_FILTER_PROPERTIES( filter );
206         mlt_filter watermark = mlt_properties_get_data( my_properties, "_watermark", NULL );
207         mlt_properties watermark_properties = MLT_FILTER_PROPERTIES( watermark );
208         char* dynamic_text = mlt_properties_get( my_properties, "argument" );
209
210         // Check for keywords in dynamic text
211         if ( dynamic_text )
212         {
213                 // Apply keyword substitution before passing the text to the filter.
214                 char result[MAX_TEXT_LEN] = "";
215                 substitute_keywords( filter, result, dynamic_text, frame );
216                 mlt_properties_set( watermark_properties, "producer.markup", (char*) result );
217         }
218
219         // Pass the properties to the watermark filter composite transition
220         mlt_properties_set( watermark_properties, "composite.geometry", mlt_properties_get( my_properties, "geometry" ) );
221         mlt_properties_set( watermark_properties, "composite.halign", mlt_properties_get( my_properties, "halign" ) );
222         mlt_properties_set( watermark_properties, "composite.valign", mlt_properties_get( my_properties, "valign" ) );
223
224         // Pass the properties to the watermark filter pango producer
225         mlt_properties_set( watermark_properties, "producer.family", mlt_properties_get( my_properties, "family" ) );
226         mlt_properties_set( watermark_properties, "producer.size", mlt_properties_get( my_properties, "size" ) );
227         mlt_properties_set( watermark_properties, "producer.weight", mlt_properties_get( my_properties, "weight" ) );
228         mlt_properties_set( watermark_properties, "producer.fgcolour", mlt_properties_get( my_properties, "fgcolour" ) );
229         mlt_properties_set( watermark_properties, "producer.bgcolour", mlt_properties_get( my_properties, "bgcolour" ) );
230         mlt_properties_set( watermark_properties, "producer.olcolour", mlt_properties_get( my_properties, "olcolour" ) );
231         mlt_properties_set( watermark_properties, "producer.pad", mlt_properties_get( my_properties, "pad" ) );
232         mlt_properties_set( watermark_properties, "producer.outline", mlt_properties_get( my_properties, "outline" ) );
233 }
234
235 /** Get the image.
236 */
237
238 static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
239 {
240         // Pop the service
241         mlt_filter filter = mlt_frame_pop_service( frame );
242
243         mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
244
245         apply_filter( filter, frame );
246
247         mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
248
249         // Need to get the image
250         return mlt_frame_get_image( frame, image, format, width, height, 1 );
251 }
252
253 /** Filter processing.
254 */
255
256 static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
257 {
258         // Push the filter
259         mlt_frame_push_service( frame, filter );
260
261         // Register the get image method
262         mlt_frame_push_get_image( frame, filter_get_image );
263
264         mlt_filter watermark = mlt_properties_get_data( MLT_FILTER_PROPERTIES(filter), "_watermark", NULL );
265         if ( watermark )
266         {
267                 mlt_filter_process( watermark, frame );
268         }
269
270         // Return the frame
271         return frame;
272 }
273
274 /** Constructor for the filter.
275 */
276
277 mlt_filter filter_dynamictext_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg )
278 {
279         // Create the filter
280         mlt_filter filter = mlt_filter_new( );
281         mlt_filter watermark = mlt_factory_filter( profile, "watermark", "pango:" );
282
283         // Initialize it
284         if ( filter && watermark )
285         {
286                 // Get the properties
287                 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
288
289                 // Store the watermark filter for future use
290                 mlt_properties_set_data( properties, "_watermark", watermark, 0, (mlt_destructor)mlt_filter_close, NULL );
291
292                 // Assign default values
293                 mlt_properties_set( properties, "argument", arg ? arg: "#timecode#" );
294                 mlt_properties_set( properties, "geometry", "0%/0%:100%x100%:100" );
295                 mlt_properties_set( properties, "family", "Sans" );
296                 mlt_properties_set( properties, "size", "48" );
297                 mlt_properties_set( properties, "weight", "400" );
298                 mlt_properties_set( properties, "fgcolour", "0x000000ff" );
299                 mlt_properties_set( properties, "bgcolour", "0x00000020" );
300                 mlt_properties_set( properties, "olcolour", "0x00000000" );
301                 mlt_properties_set( properties, "pad", "0" );
302                 mlt_properties_set( properties, "halign", "left" );
303                 mlt_properties_set( properties, "valign", "top" );
304                 mlt_properties_set( properties, "outline", "0" );
305
306                 // Specify the processing method
307                 filter->process = filter_process;
308         }
309         else // filter or watermark failed for some reason
310         {
311                 if( filter )
312                 {
313                         mlt_filter_close( filter );
314                 }
315
316                 if( watermark )
317                 {
318                         mlt_filter_close( watermark );
319                 }
320
321                 filter = NULL;
322         }
323
324         return filter;
325 }