1 /*
2 * producer_image.c -- a QT/QImage based producer for MLT
3 *
4 * NB: This module is designed to be functionally equivalent to the
5 * gtk2 image loading module so it can be used as replacement.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22 #include <framework/mlt_producer.h>
23 #include <framework/mlt_cache.h>
24 #include <framework/mlt_events.h>
25 #include "qimage_wrapper.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <math.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33 #include <ctype.h>
34
35 static void load_filenames( producer_qimage self, mlt_properties producer_properties );
36 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
37 static void producer_close( mlt_producer parent );
38
refresh_length(mlt_properties properties,producer_qimage self)39 static void refresh_length( mlt_properties properties, producer_qimage self )
40 {
41 if ( self->count > mlt_properties_get_int( properties, "length" ) ||
42 mlt_properties_get_int( properties, "autolength" ) )
43 {
44 int ttl = mlt_properties_get_int( properties, "ttl" );
45 mlt_position length = self->count * ttl;
46 mlt_properties_set_position( properties, "length", length );
47 mlt_properties_set_position( properties, "out", length - 1 );
48 }
49 }
50
on_property_changed(mlt_service owner,mlt_producer producer,char * name)51 static void on_property_changed( mlt_service owner, mlt_producer producer, char *name )
52 {
53 if ( !strcmp( name, "ttl" ) )
54 refresh_length( MLT_PRODUCER_PROPERTIES(producer), producer->child );
55 }
56
producer_qimage_init(mlt_profile profile,mlt_service_type type,const char * id,char * filename)57 mlt_producer producer_qimage_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename )
58 {
59 producer_qimage self = calloc( 1, sizeof( struct producer_qimage_s ) );
60 if ( self != NULL && mlt_producer_init( &self->parent, self ) == 0 )
61 {
62 mlt_producer producer = &self->parent;
63
64 // Get the properties interface
65 mlt_properties properties = MLT_PRODUCER_PROPERTIES( &self->parent );
66
67 // Initialize KDE image plugins
68 if ( !init_qimage( producer, filename ) )
69 {
70 // Reject if animation.
71 mlt_producer_close( producer );
72 free( self );
73 return NULL;
74 }
75
76 // Callback registration
77 producer->get_frame = producer_get_frame;
78 producer->close = ( mlt_destructor )producer_close;
79
80 // Set the default properties
81 mlt_properties_set( properties, "resource", filename );
82 mlt_properties_set_int( properties, "ttl", 25 );
83 mlt_properties_set_int( properties, "aspect_ratio", 1 );
84 mlt_properties_set_int( properties, "progressive", 1 );
85 mlt_properties_set_int( properties, "seekable", 1 );
86
87 // Validate the resource
88 if ( filename )
89 load_filenames( self, properties );
90 if ( self->count )
91 {
92 mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
93 if ( frame )
94 {
95 mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
96 mlt_properties_set_data( frame_properties, "producer_qimage", self, 0, NULL, NULL );
97 mlt_frame_set_position( frame, mlt_producer_position( producer ) );
98 int enable_caching = self->count == 1;
99 refresh_qimage( self, frame, enable_caching );
100 if ( enable_caching )
101 {
102 mlt_cache_item_close( self->qimage_cache );
103 }
104 mlt_frame_close( frame );
105 }
106 }
107 if ( self->current_width == 0 )
108 {
109 producer_close( producer );
110 producer = NULL;
111 }
112 else
113 {
114 mlt_events_listen( properties, self, "property-changed", (mlt_listener) on_property_changed );
115 }
116 return producer;
117 }
118 free( self );
119 return NULL;
120 }
121
load_svg(producer_qimage self,mlt_properties properties,const char * filename)122 static int load_svg( producer_qimage self, mlt_properties properties, const char *filename )
123 {
124 int result = 0;
125
126 // Read xml string
127 if ( strstr( filename, "<svg" ) )
128 {
129 make_tempfile( self, filename );
130 result = 1;
131 }
132 return result;
133 }
134
load_sequence_deprecated(producer_qimage self,mlt_properties properties,const char * filename)135 static int load_sequence_deprecated( producer_qimage self, mlt_properties properties, const char *filename )
136 {
137 int result = 0;
138 const char *start;
139
140 // Obtain filenames with pattern containing a begin value, e.g. foo%1234d.png
141 if ( ( start = strchr( filename, '%' ) ) )
142 {
143 const char *end = ++start;
144 while ( isdigit( *end ) ) end++;
145 if ( end > start && ( end[0] == 'd' || end[0] == 'i' || end[0] == 'u' ) )
146 {
147 int n = end - start;
148 char *s = calloc( 1, n + 1 );
149 strncpy( s, start, n );
150 mlt_properties_set( properties, "begin", s );
151 free( s );
152 s = calloc( 1, strlen( filename ) + 2 );
153 strncpy( s, filename, start - filename );
154 sprintf( s + ( start - filename ), ".%d%s", n, end );
155 result = load_sequence_sprintf( self, properties, s );
156 free( s );
157 }
158 }
159 return result;
160 }
161
load_sequence_querystring(producer_qimage self,mlt_properties properties,const char * filename)162 static int load_sequence_querystring( producer_qimage self, mlt_properties properties, const char *filename )
163 {
164 int result = 0;
165
166 // Obtain filenames with pattern and begin value in query string
167 if ( strchr( filename, '%' ) && strchr( filename, '?' ) )
168 {
169 // Split filename into pattern and query string
170 char *s = strdup( filename );
171 char *querystring = strrchr( s, '?' );
172 *querystring++ = '\0';
173 if ( strstr( filename, "begin=" ) )
174 mlt_properties_set( properties, "begin", strstr( querystring, "begin=" ) + 6 );
175 else if ( strstr( filename, "begin:" ) )
176 mlt_properties_set( properties, "begin", strstr( querystring, "begin:" ) + 6 );
177 // Coerce to an int value so serialization does not have any extra query string cruft
178 mlt_properties_set_int( properties, "begin", mlt_properties_get_int( properties, "begin" ) );
179 result = load_sequence_sprintf( self, properties, s );
180 free( s );
181 }
182 return result;
183 }
184
load_folder(producer_qimage self,mlt_properties properties,const char * filename)185 static int load_folder( producer_qimage self, mlt_properties properties, const char *filename )
186 {
187 int result = 0;
188
189 // Obtain filenames within folder
190 if ( strstr( filename, "/.all." ) != NULL )
191 {
192 char wildcard[ 1024 ];
193 char *dir_name = strdup( filename );
194 char *extension = strrchr( dir_name, '.' );
195
196 *( strstr( dir_name, "/.all." ) + 1 ) = '\0';
197 sprintf( wildcard, "*%s", extension );
198
199 mlt_properties_dir_list( self->filenames, dir_name, wildcard, 1 );
200
201 free( dir_name );
202 result = 1;
203 }
204 return result;
205 }
206
load_filenames(producer_qimage self,mlt_properties properties)207 static void load_filenames( producer_qimage self, mlt_properties properties )
208 {
209 char *filename = mlt_properties_get( properties, "resource" );
210 self->filenames = mlt_properties_new( );
211
212 if (!load_svg( self, properties, filename ) &&
213 !load_sequence_querystring( self, properties, filename ) &&
214 !load_sequence_sprintf( self, properties, filename ) &&
215 !load_sequence_deprecated( self, properties, filename ) &&
216 !load_folder( self, properties, filename ) )
217 {
218 mlt_properties_set( self->filenames, "0", filename );
219 }
220 self->count = mlt_properties_count( self->filenames );
221 refresh_length( properties, self );
222 }
223
producer_get_image(mlt_frame frame,uint8_t ** buffer,mlt_image_format * format,int * width,int * height,int writable)224 static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
225 {
226 int error = 0;
227
228 // Obtain properties of frame and producer
229 mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
230 producer_qimage self = mlt_properties_get_data( properties, "producer_qimage", NULL );
231 mlt_producer producer = &self->parent;
232
233 // Use the width and height suggested by the rescale filter because we can do our own scaling.
234 if ( mlt_properties_get_int( properties, "rescale_width" ) > 0 )
235 *width = mlt_properties_get_int( properties, "rescale_width" );
236 if ( mlt_properties_get_int( properties, "rescale_height" ) > 0 )
237 *height = mlt_properties_get_int( properties, "rescale_height" );
238 mlt_service_lock( MLT_PRODUCER_SERVICE( &self->parent ) );
239 int enable_caching = ( self->count <= 1 || mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( producer ), "ttl" ) > 1 );
240
241 // Refresh the image
242 if ( enable_caching )
243 {
244 self->qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" );
245 self->qimage = mlt_cache_item_data( self->qimage_cache, NULL );
246 self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.image" );
247 self->current_image = mlt_cache_item_data( self->image_cache, NULL );
248 self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha" );
249 self->current_alpha = mlt_cache_item_data( self->alpha_cache, &self->alpha_size );
250 }
251 refresh_image( self, frame, *format, *width, *height, enable_caching );
252
253 // Get width and height (may have changed during the refresh)
254 *width = mlt_properties_get_int( properties, "width" );
255 *height = mlt_properties_get_int( properties, "height" );
256 *format = self->format;
257
258 // NB: Cloning is necessary with this producer (due to processing of images ahead of use)
259 // The fault is not in the design of mlt, but in the implementation of the qimage producer...
260 if ( self->current_image )
261 {
262 int image_size = mlt_image_format_size( self->format, self->current_width, self->current_height, NULL );
263 if ( enable_caching )
264 {
265 // Clone the image and the alpha
266 uint8_t *image_copy = mlt_pool_alloc( image_size );
267 memcpy( image_copy, self->current_image, image_size );
268 // Now update properties so we free the copy after
269 mlt_frame_set_image( frame, image_copy, image_size, mlt_pool_release );
270 // We're going to pass the copy on
271 *buffer = image_copy;
272 mlt_log_debug( MLT_PRODUCER_SERVICE( &self->parent ), "%dx%d (%s)\n",
273 self->current_width, self->current_height, mlt_image_format_name( *format ) );
274 // Clone the alpha channel
275 if ( self->current_alpha )
276 {
277 if ( !self->alpha_size )
278 self->alpha_size = self->current_width * self->current_height;
279 uint8_t * alpha_copy = mlt_pool_alloc( self->alpha_size );
280 memcpy( alpha_copy, self->current_alpha, self->alpha_size );
281 mlt_frame_set_alpha( frame, alpha_copy, self->alpha_size, mlt_pool_release );
282 }
283 }
284 else
285 {
286 // For image sequences with ttl = 1 we recreate self->current_image on each frame, no need to clone
287 mlt_frame_set_image( frame, self->current_image, image_size, mlt_pool_release );
288 *buffer = self->current_image;
289 if ( self->current_alpha )
290 {
291 if ( !self->alpha_size )
292 self->alpha_size = self->current_width * self->current_height;
293 mlt_frame_set_alpha( frame, self->current_alpha, self->alpha_size, mlt_pool_release );
294 }
295 }
296 }
297 else
298 {
299 error = 1;
300 }
301
302 if ( enable_caching )
303 {
304 // Release references and locks
305 mlt_cache_item_close( self->qimage_cache );
306 mlt_cache_item_close( self->image_cache );
307 mlt_cache_item_close( self->alpha_cache );
308 }
309 mlt_service_unlock( MLT_PRODUCER_SERVICE( &self->parent ) );
310
311 return error;
312 }
313
producer_get_frame(mlt_producer producer,mlt_frame_ptr frame,int index)314 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
315 {
316 // Get the real structure for this producer
317 producer_qimage self = producer->child;
318
319 // Fetch the producers properties
320 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
321
322 if ( self->filenames == NULL && mlt_properties_get( producer_properties, "resource" ) != NULL )
323 load_filenames( self, producer_properties );
324
325 // Generate a frame
326 *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
327
328 if ( *frame != NULL && self->count > 0 )
329 {
330 // Obtain properties of frame and producer
331 mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
332
333 // Set the producer on the frame properties
334 mlt_properties_set_data( properties, "producer_qimage", self, 0, NULL, NULL );
335
336 // Update timecode on the frame we're creating
337 mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
338
339 // Refresh the image
340 if ( self->count == 1 || mlt_properties_get_int( producer_properties, "ttl" ) > 1 )
341 {
342 self->qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" );
343 self->qimage = mlt_cache_item_data( self->qimage_cache, NULL );
344 refresh_qimage( self, *frame, 1 );
345 mlt_cache_item_close( self->qimage_cache );
346 }
347
348 // Set producer-specific frame properties
349 mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( producer_properties, "progressive" ) );
350 double force_ratio = mlt_properties_get_double( producer_properties, "force_aspect_ratio" );
351 if ( force_ratio > 0.0 )
352 mlt_properties_set_double( properties, "aspect_ratio", force_ratio );
353 else
354 mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "aspect_ratio" ) );
355
356 // Push the get_image method
357 mlt_frame_push_get_image( *frame, producer_get_image );
358 }
359
360 // Calculate the next timecode
361 mlt_producer_prepare_next( producer );
362
363 return 0;
364 }
365
producer_close(mlt_producer parent)366 static void producer_close( mlt_producer parent )
367 {
368 producer_qimage self = parent->child;
369 parent->close = NULL;
370 mlt_service_cache_purge( MLT_PRODUCER_SERVICE(parent) );
371 mlt_producer_close( parent );
372 mlt_properties_close( self->filenames );
373 free( self );
374 }
375