]> git.sesse.net Git - mlt/blob - src/modules/gtk2/producer_pixbuf.c
b72e9c827ba4ffb6af6c24b12a929e15ac81ca87
[mlt] / src / modules / gtk2 / producer_pixbuf.c
1 /*
2  * producer_pixbuf.c -- raster image loader based upon gdk-pixbuf
3  * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4  * Author: Dan Dennedy <dan@dennedy.org>
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_producer.h>
22 #include <framework/mlt_frame.h>
23 #include <framework/mlt_cache.h>
24 #include <framework/mlt_log.h>
25 #include <gdk-pixbuf/gdk-pixbuf.h>
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <pthread.h>
31 #include <math.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <dirent.h>
36
37 static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
38
39 typedef struct producer_pixbuf_s *producer_pixbuf;
40
41 struct producer_pixbuf_s
42 {
43         struct mlt_producer_s parent;
44
45         // File name list
46         mlt_properties filenames;
47         int count;
48         int image_idx;
49         int pixbuf_idx;
50         int width;
51         int height;
52         uint8_t *image;
53         uint8_t *alpha;
54         mlt_cache_item image_cache;
55         mlt_cache_item alpha_cache;
56         pthread_mutex_t mutex;
57 };
58
59 static void load_filenames( producer_pixbuf this, mlt_properties producer_properties );
60 static void refresh_image( producer_pixbuf this, mlt_frame frame, int width, int height );
61 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
62 static void producer_close( mlt_producer parent );
63
64 mlt_producer producer_pixbuf_init( char *filename )
65 {
66         producer_pixbuf this = calloc( sizeof( struct producer_pixbuf_s ), 1 );
67         if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
68         {
69                 mlt_producer producer = &this->parent;
70
71                 // Get the properties interface
72                 mlt_properties properties = MLT_PRODUCER_PROPERTIES( &this->parent );
73         
74                 // Callback registration
75                 producer->get_frame = producer_get_frame;
76                 producer->close = ( mlt_destructor )producer_close;
77
78                 // Set the default properties
79                 mlt_properties_set( properties, "resource", filename );
80                 mlt_properties_set_int( properties, "ttl", 25 );
81                 mlt_properties_set_int( properties, "aspect_ratio", 1 );
82                 mlt_properties_set_int( properties, "progressive", 1 );
83
84                 // Validate the resource
85                 if ( filename )
86                         load_filenames( this, properties );
87                 if ( this->count )
88                 {
89                         mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
90                         if ( frame )
91                         {
92                                 mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
93                                 pthread_mutex_init( &this->mutex, NULL );
94                                 mlt_properties_set_data( frame_properties, "producer_pixbuf", this, 0, NULL, NULL );
95                                 mlt_frame_set_position( frame, mlt_producer_position( producer ) );
96                                 mlt_properties_set_position( frame_properties, "pixbuf_position", mlt_producer_position( producer ) );
97                                 refresh_image( this, frame, 0, 0 );
98                                 mlt_frame_close( frame );
99                         }
100                 }
101                 if ( this->width == 0 )
102                 {
103                         producer_close( producer );
104                         producer = NULL;
105                 }
106                 return producer;
107         }
108         free( this );
109         return NULL;
110 }
111
112 static void load_filenames( producer_pixbuf this, mlt_properties producer_properties )
113 {
114         char *filename = mlt_properties_get( producer_properties, "resource" );
115         this->filenames = mlt_properties_new( );
116
117         // Read xml string
118         if ( strstr( filename, "<svg" ) )
119         {
120                 // Generate a temporary file for the svg
121                 char fullname[ 1024 ] = "/tmp/mlt.XXXXXX";
122                 int fd = mkstemp( fullname );
123
124                 if ( fd > -1 )
125                 {
126                         // Write the svg into the temp file
127                         ssize_t remaining_bytes;
128                         char *xml = filename;
129                         
130                         // Strip leading crap
131                         while ( xml[0] != '<' )
132                                 xml++;
133                         
134                         remaining_bytes = strlen( xml );
135                         while ( remaining_bytes > 0 )
136                                 remaining_bytes -= write( fd, xml + strlen( xml ) - remaining_bytes, remaining_bytes );
137                         close( fd );
138
139                         mlt_properties_set( this->filenames, "0", fullname );
140
141                         // Teehe - when the producer closes, delete the temp file and the space allo
142                         mlt_properties_set_data( producer_properties, "__temporary_file__", fullname, 0, ( mlt_destructor )unlink, NULL );
143                 }
144         }
145         // Obtain filenames
146         else if ( strchr( filename, '%' ) != NULL )
147         {
148                 // handle picture sequences
149                 int i = mlt_properties_get_int( producer_properties, "begin" );
150                 int gap = 0;
151                 char full[1024];
152                 int keyvalue = 0;
153                 char key[ 50 ];
154
155                 while ( gap < 100 )
156                 {
157                         struct stat buf;
158                         snprintf( full, 1023, filename, i ++ );
159                         if ( stat( full, &buf ) == 0 )
160                         {
161                                 sprintf( key, "%d", keyvalue ++ );
162                                 mlt_properties_set( this->filenames, key, full );
163                                 gap = 0;
164                         }
165                         else
166                         {
167                                 gap ++;
168                         }
169                 }
170                 if ( mlt_properties_count( this->filenames ) > 0 )
171                         mlt_properties_set_int( producer_properties, "ttl", 1 );
172         }
173         else if ( strstr( filename, "/.all." ) != NULL )
174         {
175                 char wildcard[ 1024 ];
176                 char *dir_name = strdup( filename );
177                 char *extension = strrchr( dir_name, '.' );
178
179                 *( strstr( dir_name, "/.all." ) + 1 ) = '\0';
180                 sprintf( wildcard, "*%s", extension );
181
182                 mlt_properties_dir_list( this->filenames, dir_name, wildcard, 1 );
183
184                 free( dir_name );
185         }
186         else
187         {
188                 mlt_properties_set( this->filenames, "0", filename );
189         }
190
191         this->count = mlt_properties_count( this->filenames );
192 }
193
194 static void refresh_image( producer_pixbuf this, mlt_frame frame, int width, int height )
195 {
196         // Obtain properties of frame
197         mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
198
199         // Obtain the producer
200         mlt_producer producer = &this->parent;
201
202         // Obtain properties of producer
203         mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
204
205         // Obtain the cache flag and structure
206         int use_cache = mlt_properties_get_int( producer_props, "cache" );
207         mlt_properties cache = mlt_properties_get_data( producer_props, "_cache", NULL );
208         int update_cache = 0;
209
210         // restore GdkPixbuf
211         pthread_mutex_lock( &this->mutex );
212         mlt_cache_item pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" );
213         GdkPixbuf *pixbuf = mlt_cache_item_data( pixbuf_cache, NULL );
214         GError *error = NULL;
215
216         // restore scaled image
217         this->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image" );
218         this->image = mlt_cache_item_data( this->image_cache, NULL );
219
220         // restore alpha channel
221         this->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha" );
222         this->alpha = mlt_cache_item_data( this->alpha_cache, NULL );
223
224         // Check if user wants us to reload the image
225         if ( mlt_properties_get_int( producer_props, "force_reload" ) ) 
226         {
227                 pixbuf = NULL;
228                 this->image = NULL;
229                 mlt_properties_set_int( producer_props, "force_reload", 0 );
230         }
231
232         // Get the time to live for each frame
233         double ttl = mlt_properties_get_int( producer_props, "ttl" );
234
235         // Get the original position of this frame
236         mlt_position position = mlt_properties_get_position( properties, "pixbuf_position" );
237         position += mlt_producer_get_in( producer );
238
239         // Image index
240         int image_idx = ( int )floor( ( double )position / ttl ) % this->count;
241
242         // Key for the cache
243         char image_key[ 10 ];
244         sprintf( image_key, "%d", image_idx );
245
246         pthread_mutex_lock( &g_mutex );
247
248         // Check if the frame is already loaded
249         if ( use_cache )
250         {
251                 if ( cache == NULL )
252                 {
253                         cache = mlt_properties_new( );
254                         mlt_properties_set_data( producer_props, "_cache", cache, 0, ( mlt_destructor )mlt_properties_close, NULL );
255                 }
256
257                 mlt_frame cached = mlt_properties_get_data( cache, image_key, NULL );
258
259                 if ( cached )
260                 {
261                         this->image_idx = image_idx;
262                         mlt_properties cached_props = MLT_FRAME_PROPERTIES( cached );
263                         this->width = mlt_properties_get_int( cached_props, "width" );
264                         this->height = mlt_properties_get_int( cached_props, "height" );
265                         mlt_properties_set_int( producer_props, "_real_width", mlt_properties_get_int( cached_props, "real_width" ) );
266                         mlt_properties_set_int( producer_props, "_real_height", mlt_properties_get_int( cached_props, "real_height" ) );
267                         this->image = mlt_properties_get_data( cached_props, "image", NULL );
268                         this->alpha = mlt_properties_get_data( cached_props, "alpha", NULL );
269
270                         if ( width != 0 && ( width != this->width || height != this->height ) )
271                                 this->image = NULL;
272                 }
273         }
274
275     // optimization for subsequent iterations on single picture
276         if ( width != 0 && ( image_idx != this->image_idx || width != this->width || height != this->height ) )
277                 this->image = NULL;
278         if ( image_idx != this->pixbuf_idx )
279                 pixbuf = NULL;
280         mlt_log_debug( MLT_PRODUCER_SERVICE( producer ), "image %p pixbuf %p idx %d image_idx %d pixbuf_idx %d width %d\n",
281                 this->image, pixbuf, image_idx, this->image_idx, this->pixbuf_idx, width );
282         if ( !pixbuf && !this->image )
283         {
284                 this->image = NULL;
285                 pixbuf = gdk_pixbuf_new_from_file( mlt_properties_get_value( this->filenames, image_idx ), &error );
286
287                 if ( pixbuf )
288                 {
289                         // Register this pixbuf for destruction and reuse
290                         mlt_cache_item_close( pixbuf_cache );
291                         mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf", pixbuf, 0, ( mlt_destructor )g_object_unref );
292                         pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" );
293                         this->pixbuf_idx = image_idx;
294
295                         mlt_events_block( producer_props, NULL );
296                         mlt_properties_set_int( producer_props, "_real_width", gdk_pixbuf_get_width( pixbuf ) );
297                         mlt_properties_set_int( producer_props, "_real_height", gdk_pixbuf_get_height( pixbuf ) );
298                         mlt_events_unblock( producer_props, NULL );
299
300                         // Store the width/height of the pixbuf temporarily
301                         this->width = gdk_pixbuf_get_width( pixbuf );
302                         this->height = gdk_pixbuf_get_height( pixbuf );
303                 }
304         }
305
306         // If we have a pixbuf and we need an image
307         if ( pixbuf && width > 0 && !this->image )
308         {
309                 char *interps = mlt_properties_get( properties, "rescale.interp" );
310                 int interp = GDK_INTERP_BILINEAR;
311
312                 if ( strcmp( interps, "nearest" ) == 0 )
313                         interp = GDK_INTERP_NEAREST;
314                 else if ( strcmp( interps, "tiles" ) == 0 )
315                         interp = GDK_INTERP_TILES;
316                 else if ( strcmp( interps, "hyper" ) == 0 )
317                         interp = GDK_INTERP_HYPER;
318
319                 // Note - the original pixbuf is already safe and ready for destruction
320                 pixbuf = gdk_pixbuf_scale_simple( pixbuf, width, height, interp );
321
322                 // Store width and height
323                 this->width = width;
324                 this->height = height;
325                 
326                 // Allocate/define image
327                 this->image = mlt_pool_alloc( width * ( height + 1 ) * 2 );
328                 if ( !use_cache )
329                         mlt_cache_item_close( this->image_cache );
330                 mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image", this->image, width * ( height + 1 ) * 2, mlt_pool_release );
331                 this->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image" );
332                 this->image_idx = image_idx;
333
334                 // Extract YUV422 and alpha
335                 if ( gdk_pixbuf_get_has_alpha( pixbuf ) )
336                 {
337                         // Allocate the alpha mask
338                         this->alpha = mlt_pool_alloc( this->width * this->height );
339                         if ( !use_cache )
340                                 mlt_cache_item_close( this->alpha_cache );
341                         mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha", this->alpha, width * height, mlt_pool_release );
342                         this->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha" );
343
344                         // Convert the image
345                         mlt_convert_rgb24a_to_yuv422( gdk_pixbuf_get_pixels( pixbuf ),
346                                                                                   this->width, this->height,
347                                                                                   gdk_pixbuf_get_rowstride( pixbuf ),
348                                                                                   this->image, this->alpha );
349                 }
350                 else
351                 { 
352                         // No alpha to extract
353                         mlt_convert_rgb24_to_yuv422( gdk_pixbuf_get_pixels( pixbuf ),
354                                                                                  this->width, this->height,
355                                                                                  gdk_pixbuf_get_rowstride( pixbuf ),
356                                                                                  this->image );
357                 }
358
359                 // Finished with pixbuf now
360                 g_object_unref( pixbuf );
361
362                 // Ensure we update the cache when we need to
363                 update_cache = use_cache;
364         }
365
366         // release references no longer needed
367         mlt_cache_item_close( pixbuf_cache );
368         if ( width == 0 )
369         {
370                 pthread_mutex_unlock( &this->mutex );
371                 mlt_cache_item_close( this->image_cache );
372                 mlt_cache_item_close( this->alpha_cache );
373         }
374
375         // Set width/height of frame
376         mlt_properties_set_int( properties, "width", this->width );
377         mlt_properties_set_int( properties, "height", this->height );
378         mlt_properties_set_int( properties, "real_width", mlt_properties_get_int( producer_props, "_real_width" ) );
379         mlt_properties_set_int( properties, "real_height", mlt_properties_get_int( producer_props, "_real_height" ) );
380
381         if ( update_cache )
382         {
383                 mlt_frame cached = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
384                 mlt_properties cached_props = MLT_FRAME_PROPERTIES( cached );
385                 mlt_properties_set_int( cached_props, "width", this->width );
386                 mlt_properties_set_int( cached_props, "height", this->height );
387                 mlt_properties_set_int( cached_props, "real_width", mlt_properties_get_int( producer_props, "_real_width" ) );
388                 mlt_properties_set_int( cached_props, "real_height", mlt_properties_get_int( producer_props, "_real_height" ) );
389                 mlt_properties_set_data( cached_props, "image", this->image, this->width * ( this->height + 1 ) * 2, mlt_pool_release, NULL );
390                 mlt_properties_set_data( cached_props, "alpha", this->alpha, this->width * this->height, mlt_pool_release, NULL );
391                 mlt_properties_set_data( cache, image_key, cached, 0, ( mlt_destructor )mlt_frame_close, NULL );
392         }
393
394         pthread_mutex_unlock( &g_mutex );
395 }
396
397 static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
398 {
399         // Obtain properties of frame
400         mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
401
402         // Obtain the producer for this frame
403         producer_pixbuf this = mlt_properties_get_data( properties, "producer_pixbuf", NULL );
404
405         *width = mlt_properties_get_int( properties, "rescale_width" );
406         *height = mlt_properties_get_int( properties, "rescale_height" );
407
408         // Refresh the image
409         refresh_image( this, frame, *width, *height );
410
411         // Get the image size
412         int image_size = this->width * ( this->height + 1 ) * 2;
413         int alpha_size = this->width * this->height;
414
415         // Get width and height (may have changed during the refresh)
416         *width = mlt_properties_get_int( properties, "width" );
417         *height = mlt_properties_get_int( properties, "height" );
418
419         // NB: Cloning is necessary with this producer (due to processing of images ahead of use)
420         // The fault is not in the design of mlt, but in the implementation of the pixbuf producer...
421         if ( this->image )
422         {
423                 if ( *format == mlt_image_yuv422 || *format == mlt_image_yuv420p )
424                 {
425                         // Clone the image and the alpha
426                         uint8_t *image_copy = mlt_pool_alloc( image_size );
427                         uint8_t *alpha_copy = mlt_pool_alloc( alpha_size );
428
429                         memcpy( image_copy, this->image, image_size );
430
431                         // Copy or default the alpha
432                         if ( this->alpha != NULL )
433                                 memcpy( alpha_copy, this->alpha, alpha_size );
434                         else
435                                 memset( alpha_copy, 255, alpha_size );
436
437                         // Now update properties so we free the copy after
438                         mlt_properties_set_data( properties, "image", image_copy, image_size, mlt_pool_release, NULL );
439                         mlt_properties_set_data( properties, "alpha", alpha_copy, alpha_size, mlt_pool_release, NULL );
440
441                         // We're going to pass the copy on
442                         *buffer = image_copy;
443                 }
444                 else if ( *format == mlt_image_rgb24a )
445                 {
446                         // Clone the image and the alpha
447                         image_size = *width * ( *height + 1 ) * 4;
448                         alpha_size = *width * ( *height + 1 );
449                         uint8_t *image_copy = mlt_pool_alloc( image_size );
450                         uint8_t *alpha_copy = mlt_pool_alloc( alpha_size );
451
452                         mlt_convert_yuv422_to_rgb24a( this->image, image_copy, (*width)*(*height) );
453
454                         // Now update properties so we free the copy after
455                         mlt_properties_set_data( properties, "image", image_copy, image_size, mlt_pool_release, NULL );
456                         mlt_properties_set_data( properties, "alpha", alpha_copy, alpha_size, mlt_pool_release, NULL );
457
458                         // We're going to pass the copy on
459                         *buffer = image_copy;
460                 }
461
462         }
463         else
464         {
465                 // TODO: Review all cases of invalid images
466                 *buffer = mlt_pool_alloc( 50 * 50 * 2 );
467                 mlt_properties_set_data( properties, "image", *buffer, image_size, mlt_pool_release, NULL );
468                 *width = 50;
469                 *height = 50;
470         }
471
472         // Release references and locks
473         pthread_mutex_unlock( &this->mutex );
474         mlt_cache_item_close( this->image_cache );
475         mlt_cache_item_close( this->alpha_cache );
476
477         return 0;
478 }
479
480 static uint8_t *producer_get_alpha_mask( mlt_frame this )
481 {
482         // Obtain properties of frame
483         mlt_properties properties = MLT_FRAME_PROPERTIES( this );
484
485         // Return the alpha mask
486         return mlt_properties_get_data( properties, "alpha", NULL );
487 }
488
489 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
490 {
491         // Get the real structure for this producer
492         producer_pixbuf this = producer->child;
493
494         // Fetch the producers properties
495         mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
496
497         if ( this->filenames == NULL && mlt_properties_get( producer_properties, "resource" ) != NULL )
498                 load_filenames( this, producer_properties );
499
500         // Generate a frame
501         *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
502
503         if ( *frame != NULL && this->count > 0 )
504         {
505                 // Obtain properties of frame and producer
506                 mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
507
508                 // Set the producer on the frame properties
509                 mlt_properties_set_data( properties, "producer_pixbuf", this, 0, NULL, NULL );
510
511                 // Update timecode on the frame we're creating
512                 mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
513
514                 // Ensure that we have a way to obtain the position in the get_image
515                 mlt_properties_set_position( properties, "pixbuf_position", mlt_producer_position( producer ) );
516
517                 // Refresh the image
518                 refresh_image( this, *frame, 0, 0 );
519
520                 // Set producer-specific frame properties
521                 mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( producer_properties, "progressive" ) );
522                 mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "aspect_ratio" ) );
523
524                 // Set alpha call back
525                 ( *frame )->get_alpha_mask = producer_get_alpha_mask;
526
527                 // Push the get_image method
528                 mlt_frame_push_get_image( *frame, producer_get_image );
529         }
530
531         // Calculate the next timecode
532         mlt_producer_prepare_next( producer );
533
534         return 0;
535 }
536
537 static void producer_close( mlt_producer parent )
538 {
539         producer_pixbuf this = parent->child;
540         pthread_mutex_destroy( &this->mutex );
541         parent->close = NULL;
542         mlt_producer_close( parent );
543         mlt_properties_close( this->filenames );
544         free( this );
545 }