]> git.sesse.net Git - mlt/blob - src/modules/gtk2/filter_dynamictext.c
Add filter_dynamictext.
[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
33 static void get_timecode_str( mlt_filter filter, mlt_frame frame, char* text )
34 {
35         int frames = mlt_frame_get_position( frame );
36         double fps = mlt_profile_fps( mlt_service_profile( MLT_FILTER_SERVICE( filter ) ) );
37         char tc[12] = "";
38         if (fps == 0)
39         {
40                 strncat( text, "-", MAX_TEXT_LEN - strlen(text) - 1 );
41         }
42         else
43         {
44                 int seconds = frames / fps;
45                 frames = frames % lrint( fps );
46                 int minutes = seconds / 60;
47                 seconds = seconds % 60;
48                 int hours = minutes / 60;
49                 minutes = minutes % 60;
50                 sprintf(tc, "%.2d:%.2d:%.2d:%.2d", hours, minutes, seconds, frames);
51                 strncat( text, tc, MAX_TEXT_LEN - strlen(text) - 1 );
52         }
53 }
54
55 static void get_frame_str( mlt_filter filter, mlt_frame frame, char* text )
56 {
57         int pos = mlt_frame_get_position( frame );
58         char s[12];
59         snprintf( s, sizeof(s) - 1, "%d", pos );
60         strncat( text, s, MAX_TEXT_LEN - strlen(text) - 1 );
61 }
62
63 static void get_filedate_str( mlt_filter filter, mlt_frame frame, char* text )
64 {
65         mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer( frame ));
66         mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer);
67         char* filename = mlt_properties_get( producer_properties, "resource");
68         struct stat file_info;
69
70         if( !stat(filename, &file_info))
71         {
72                 struct tm* time_info = gmtime( &(file_info.st_mtime) );
73                 char date[11] = "";
74                 strftime( date, 11, "%Y/%m/%d", time_info );
75                 strncat( text, date, MAX_TEXT_LEN - strlen(text) - 1);
76         }
77 }
78
79 /** Perform substitution for keywords that are enclosed in "# #".
80 */
81
82 static void substitute_keywords(mlt_filter filter, char* result, char* value, mlt_frame frame)
83 {
84         char value_copy[MAX_TEXT_LEN] = "";
85         char* keyword = NULL;
86         int ct = 0;
87         int fromStart = 0;
88
89         // Need to copy the value because strtok will modify it.
90         strncpy(value_copy, value, 512);
91         keyword = strtok( value_copy, "#" );
92         fromStart = ( value_copy[0] == '#' ) ? 1 : 0;
93
94         while ( keyword )
95         {
96                 if ( ct % 2 == fromStart )
97                 {
98                         // backslash in front of # suppresses substitution
99                         if ( keyword[ strlen( keyword ) -1 ] == '\\' )
100                         {
101                                 // keep characters except backslash
102                                 strncat( result, keyword, strlen( keyword ) -1 );
103                                 strcat( result, "#" );
104                                 ct++;
105                         }
106                         else
107                         {
108                                 strcat( result, keyword );
109                         }
110                 }
111                 else if ( !strcmp( keyword, "timecode" ) )
112                 {
113                         get_timecode_str(filter, frame, result);
114                 }
115                 else if ( !strcmp( keyword, "frame" ) )
116                 {
117                         get_frame_str(filter, frame, result);
118                 }
119                 else if ( !strcmp( keyword, "filedate" ) )
120                 {
121                         get_filedate_str(filter, frame, result);
122                 }
123                 else if ( !strcmp( keyword, "resource" ) )
124                 {
125                         // special case: replace #resource# with cut parent resource name
126                         mlt_producer producer = mlt_producer_cut_parent(mlt_frame_get_original_producer( frame ));
127                         mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer);
128                         strncat( result, mlt_properties_get( producer_properties, "resource"), MAX_TEXT_LEN - strlen(result) - 1 );
129                 }
130                 else
131                 {
132                         // replace keyword with property value from this frame
133                         mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
134                         char *frame_value = mlt_properties_get( frame_properties, keyword );
135                         if( frame_value )
136                         {
137                                 strncat( result, frame_value, MAX_TEXT_LEN - strlen(result) - 1 );
138                         }
139                 }
140                 keyword = strtok( NULL, "#" );
141                 ct++;
142         }
143 }
144
145 static void apply_filter(mlt_filter filter, mlt_frame frame )
146 {
147         mlt_properties my_properties = MLT_FILTER_PROPERTIES( filter );
148         mlt_filter watermark = mlt_properties_get_data( my_properties, "_watermark", NULL );
149         mlt_properties watermark_properties = MLT_FILTER_PROPERTIES( watermark );
150         char* dynamic_text = mlt_properties_get( my_properties, "argument");
151
152         // Check for keywords in dynamic text
153         if ( dynamic_text )
154         {
155                 // Apply keyword substitution before passing the text to the filter.
156                 char result[512] = "";
157                 substitute_keywords( filter, result, dynamic_text, frame );
158                 mlt_properties_set( watermark_properties, "producer.markup", (char*) result );
159         }
160
161         // Pass the properties to the watermark filter composite transition
162         mlt_properties_set( watermark_properties, "composite.geometry", mlt_properties_get( my_properties, "geometry" ) );
163
164         // Pass the properties to the watermark filter pango producer
165         mlt_properties_set( watermark_properties, "producer.font", mlt_properties_get( my_properties, "font" ) );
166         mlt_properties_set( watermark_properties, "producer.weight", mlt_properties_get( my_properties, "weight" ) );
167         mlt_properties_set( watermark_properties, "producer.fgcolour", mlt_properties_get( my_properties, "fgcolour" ) );
168         mlt_properties_set( watermark_properties, "producer.bgcolour", mlt_properties_get( my_properties, "bgcolour" ) );
169
170         // Process the filter
171         mlt_filter_process( watermark, frame );
172 }
173
174 /** Get the image.
175 */
176
177 static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
178 {
179         // Pop the service
180         mlt_filter filter = mlt_frame_pop_service( frame );
181
182         mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
183
184         apply_filter(filter, frame);
185
186         mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
187
188         // Need to get the image
189         return mlt_frame_get_image( frame, image, format, width, height, 1 );
190 }
191
192
193 /** Filter processing.
194 */
195
196 static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
197 {
198         // Push the filter
199         mlt_frame_push_service( frame, filter );
200
201         // Register the get image method
202         mlt_frame_push_get_image( frame, filter_get_image );
203
204         // Return the frame
205         return frame;
206 }
207
208 /** Constructor for the filter.
209 */
210
211 mlt_filter filter_dynamictext_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg )
212 {
213         // Create the filter
214         mlt_filter filter = mlt_filter_new( );
215         mlt_filter watermark = mlt_factory_filter( profile, "watermark", "pango:" );
216
217         // Initialise it
218         if ( filter && watermark )
219         {
220                 // Get the properties
221                 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
222
223                 // Store the watermark filter for future use
224                 mlt_properties_set_data( properties, "_watermark", watermark, 0, ( mlt_destructor )mlt_filter_close, NULL );
225
226                 // Assign default values
227                 mlt_properties_set( properties, "argument", arg ? arg: "#timecode#" );
228                 mlt_properties_set( properties, "geometry", "0%/0%:100%x100%:100" );
229                 mlt_properties_set( properties, "font", "Sans 48" );
230                 mlt_properties_set( properties, "weight", "400" );
231                 mlt_properties_set( properties, "fgcolour", "0x000000ff" );
232                 mlt_properties_set( properties, "bgcolour", "0x00000020" );
233
234                 // Specify the processing method
235                 filter->process = filter_process;
236         }
237         else // filter or watermark failed for some reason
238         {
239                 if( filter )
240                 {
241                         mlt_filter_close(filter);
242                 }
243
244                 if( watermark )
245                 {
246                         mlt_filter_close(watermark);
247                 }
248
249                 filter = NULL;
250         }
251
252         return filter;
253 }