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