]> git.sesse.net Git - mlt/blob - src/modules/gtk2/producer_pixbuf.c
01203874af3788cbe5bbcabe08d5dddb4347543e
[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 <framework/mlt_tokeniser.h>
26 #include <gdk-pixbuf/gdk-pixbuf.h>
27
28 #include "config.h"
29
30 #ifdef USE_EXIF
31 #include <libexif/exif-data.h>
32 #endif
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <pthread.h>
38 #include <math.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include <dirent.h>
43 #include <ctype.h>
44
45 // this protects concurrent access to gdk_pixbuf
46 static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
47
48 typedef struct producer_pixbuf_s *producer_pixbuf;
49
50 struct producer_pixbuf_s
51 {
52         struct mlt_producer_s parent;
53
54         // File name list
55         mlt_properties filenames;
56         int count;
57         int image_idx;
58         int pixbuf_idx;
59         int width;
60         int height;
61         uint8_t *alpha;
62         uint8_t *image;
63         mlt_cache_item image_cache;
64         mlt_cache_item alpha_cache;
65         mlt_cache_item pixbuf_cache;
66         GdkPixbuf *pixbuf;
67         mlt_image_format format;
68 };
69
70 static void load_filenames( producer_pixbuf self, mlt_properties producer_properties );
71 static int refresh_pixbuf( producer_pixbuf self, mlt_frame frame );
72 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
73 static void producer_close( mlt_producer parent );
74
75 mlt_producer producer_pixbuf_init( char *filename )
76 {
77         producer_pixbuf self = calloc( 1, sizeof( struct producer_pixbuf_s ) );
78         if ( self != NULL && mlt_producer_init( &self->parent, self ) == 0 )
79         {
80                 mlt_producer producer = &self->parent;
81
82                 // Get the properties interface
83                 mlt_properties properties = MLT_PRODUCER_PROPERTIES( &self->parent );
84         
85                 // Callback registration
86                 producer->get_frame = producer_get_frame;
87                 producer->close = ( mlt_destructor )producer_close;
88
89                 // Set the default properties
90                 mlt_properties_set( properties, "resource", filename );
91                 mlt_properties_set_int( properties, "ttl", 25 );
92                 mlt_properties_set_int( properties, "aspect_ratio", 1 );
93                 mlt_properties_set_int( properties, "progressive", 1 );
94                 mlt_properties_set_int( properties, "seekable", 1 );
95                 mlt_properties_set_int( properties, "loop", 1 );
96
97                 // Validate the resource
98                 if ( filename )
99                         load_filenames( self, properties );
100                 if ( self->count )
101                 {
102                         mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
103                         if ( frame )
104                         {
105                                 mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
106                                 mlt_properties_set_data( frame_properties, "producer_pixbuf", self, 0, NULL, NULL );
107                                 mlt_frame_set_position( frame, mlt_producer_position( producer ) );
108                                 refresh_pixbuf( self, frame );
109                                 mlt_cache_item_close( self->pixbuf_cache );
110                                 mlt_frame_close( frame );
111                         }
112                 }
113                 if ( self->width == 0 )
114                 {
115                         producer_close( producer );
116                         producer = NULL;
117                 }
118                 return producer;
119         }
120         free( self );
121         return NULL;
122 }
123
124 static int load_svg( producer_pixbuf self, mlt_properties properties, const char *filename )
125 {
126         int result = 0;
127
128         // Read xml string
129         if ( strstr( filename, "<svg" ) )
130         {
131                 // Generate a temporary file for the svg
132                 char fullname[ 1024 ] = "/tmp/mlt.XXXXXX";
133                 int fd = g_mkstemp( fullname );
134
135                 if ( fd > -1 )
136                 {
137                         // Write the svg into the temp file
138                         ssize_t remaining_bytes;
139                         const char *xml = filename;
140
141                         // Strip leading crap
142                         while ( xml[0] != '<' )
143                                 xml++;
144
145                         remaining_bytes = strlen( xml );
146                         while ( remaining_bytes > 0 )
147                                 remaining_bytes -= write( fd, xml + strlen( xml ) - remaining_bytes, remaining_bytes );
148                         close( fd );
149
150                         mlt_properties_set( self->filenames, "0", fullname );
151
152                         // Teehe - when the producer closes, delete the temp file and the space allo
153                         mlt_properties_set_data( properties, "__temporary_file__", fullname, 0, ( mlt_destructor )unlink, NULL );
154                         result = 1;
155                 }
156         }
157         return result;
158 }
159
160 static int load_sequence_sprintf( producer_pixbuf self, mlt_properties properties, const char *filename )
161 {
162         int result = 0;
163
164         // Obtain filenames with pattern
165         if ( strchr( filename, '%' ) != NULL )
166         {
167                 // handle picture sequences
168                 int i = mlt_properties_get_int( properties, "begin" );
169                 int gap = 0;
170                 char full[1024];
171                 int keyvalue = 0;
172                 char key[ 50 ];
173
174                 while ( gap < 100 )
175                 {
176                         struct stat buf;
177                         snprintf( full, 1023, filename, i ++ );
178                         if ( stat( full, &buf ) == 0 )
179                         {
180                                 sprintf( key, "%d", keyvalue ++ );
181                                 mlt_properties_set( self->filenames, key, full );
182                                 gap = 0;
183                         }
184                         else
185                         {
186                                 gap ++;
187                         }
188                 }
189                 if ( mlt_properties_count( self->filenames ) > 0 )
190                 {
191                         mlt_properties_set_int( properties, "ttl", 1 );
192                         result = 1;
193                 }
194         }
195         return result;
196 }
197
198 static int load_sequence_deprecated( producer_pixbuf self, mlt_properties properties, const char *filename )
199 {
200         int result = 0;
201         const char *start;
202
203         // This approach is deprecated in favor of the begin querystring parameter.
204         // Obtain filenames with pattern containing a begin value, e.g. foo%1234d.png
205         if ( ( start = strchr( filename, '%' ) ) )
206         {
207                 const char *end = ++start;
208                 while ( isdigit( *end ) ) end++;
209                 if ( end > start && ( end[0] == 'd' || end[0] == 'i' || end[0] == 'u' ) )
210                 {
211                         int n = end - start;
212                         char *s = calloc( 1, n + 1 );
213                         strncpy( s, start, n );
214                         mlt_properties_set( properties, "begin", s );
215                         free( s );
216                         s = calloc( 1, strlen( filename ) + 2 );
217                         strncpy( s, filename, start - filename );
218                         sprintf( s + ( start - filename ), ".%d%s", n, end );
219                         result = load_sequence_sprintf( self, properties, s );
220                         free( s );
221                 }
222         }
223         return result;
224 }
225
226 static int load_sequence_querystring( producer_pixbuf self, mlt_properties properties, const char *filename )
227 {
228         int result = 0;
229
230         // Obtain filenames with pattern and begin value in query string
231         if ( strchr( filename, '%' ) && strchr( filename, '?' ) )
232         {
233                 // Split filename into pattern and query string
234                 char *s = strdup( filename );
235                 char *querystring = strrchr( s, '?' );
236                 *querystring++ = '\0';
237                 if ( strstr( filename, "begin=" ) )
238                         mlt_properties_set( properties, "begin", strstr( querystring, "begin=" ) + 6 );
239                 else if ( strstr( filename, "begin:" ) )
240                         mlt_properties_set( properties, "begin", strstr( querystring, "begin:" ) + 6 );
241                 // Coerce to an int value so serialization does not have any extra query string cruft
242                 mlt_properties_set_int( properties, "begin", mlt_properties_get_int( properties, "begin" ) );
243                 result = load_sequence_sprintf( self, properties, s );
244                 free( s );
245         }
246         return result;
247 }
248
249 static int load_folder( producer_pixbuf self, mlt_properties properties, const char *filename )
250 {
251         int result = 0;
252
253         // Obtain filenames with folder
254         if ( strstr( filename, "/.all." ) != NULL )
255         {
256                 char wildcard[ 1024 ];
257                 char *dir_name = strdup( filename );
258                 char *extension = strrchr( dir_name, '.' );
259
260                 *( strstr( dir_name, "/.all." ) + 1 ) = '\0';
261                 sprintf( wildcard, "*%s", extension );
262
263                 mlt_properties_dir_list( self->filenames, dir_name, wildcard, 1 );
264
265                 free( dir_name );
266                 result = 1;
267         }
268         return result;
269 }
270
271 static void load_filenames( producer_pixbuf self, mlt_properties properties )
272 {
273         char *filename = mlt_properties_get( properties, "resource" );
274         self->filenames = mlt_properties_new( );
275
276         if (!load_svg( self, properties, filename ) &&
277                 !load_sequence_querystring( self, properties, filename ) &&
278                 !load_sequence_sprintf( self, properties, filename ) &&
279                 !load_sequence_deprecated( self, properties, filename ) &&
280                 !load_folder( self, properties, filename ) )
281         {
282                 mlt_properties_set( self->filenames, "0", filename );
283         }
284         self->count = mlt_properties_count( self->filenames );
285 }
286
287 static GdkPixbuf* reorient_with_exif( producer_pixbuf self, int image_idx, GdkPixbuf *pixbuf )
288 {
289 #ifdef USE_EXIF
290         mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( &self->parent );
291         ExifData *d = exif_data_new_from_file( mlt_properties_get_value( self->filenames, image_idx ) );
292         ExifEntry *entry;
293         int exif_orientation = 0;
294
295         /* get orientation and rotate image accordingly if necessary */
296         if (d)
297         {
298                 if ( ( entry = exif_content_get_entry ( d->ifd[EXIF_IFD_0], EXIF_TAG_ORIENTATION ) ) )
299                         exif_orientation = exif_get_short (entry->data, exif_data_get_byte_order (d));
300
301                 /* Free the EXIF data */
302                 exif_data_unref(d);
303         }
304
305         // Remember EXIF value, might be useful for someone
306         mlt_properties_set_int( producer_props, "_exif_orientation" , exif_orientation );
307
308         if ( exif_orientation > 1 )
309         {
310                 GdkPixbuf *processed = NULL;
311                 GdkPixbufRotation matrix = GDK_PIXBUF_ROTATE_NONE;
312
313                 // Rotate image according to exif data
314                 switch ( exif_orientation ) {
315                   case 2:
316                           processed = gdk_pixbuf_flip ( pixbuf, TRUE );
317                           break;
318                   case 3:
319                           matrix = GDK_PIXBUF_ROTATE_UPSIDEDOWN;
320                           processed = pixbuf;
321                           break;
322                   case 4:
323                           processed = gdk_pixbuf_flip ( pixbuf, FALSE );
324                           break;
325                   case 5:
326                           matrix = GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE;
327                           processed = gdk_pixbuf_flip ( pixbuf, TRUE );
328                           break;
329                   case 6:
330                           matrix = GDK_PIXBUF_ROTATE_CLOCKWISE;
331                           processed = pixbuf;
332                           break;
333                   case 7:
334                           matrix = GDK_PIXBUF_ROTATE_CLOCKWISE;
335                           processed = gdk_pixbuf_flip ( pixbuf, TRUE );
336                           break;
337                   case 8:
338                           matrix = GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE;
339                           processed = pixbuf;
340                           break;
341                 }
342                 if ( processed )
343                 {
344                         pixbuf = gdk_pixbuf_rotate_simple( processed, matrix );
345                         g_object_unref( processed );
346                 }
347         }
348 #endif
349         return pixbuf;
350 }
351
352 static int refresh_pixbuf( producer_pixbuf self, mlt_frame frame )
353 {
354         // Obtain properties of frame and producer
355         mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
356         mlt_producer producer = &self->parent;
357         mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
358
359         // Check if user wants us to reload the image
360         if ( mlt_properties_get_int( producer_props, "force_reload" ) )
361         {
362                 self->pixbuf = NULL;
363                 self->image = NULL;
364                 mlt_properties_set_int( producer_props, "force_reload", 0 );
365         }
366
367         // Get the time to live for each frame
368         double ttl = mlt_properties_get_int( producer_props, "ttl" );
369
370         // Get the original position of this frame
371         mlt_position position = mlt_frame_original_position( frame );
372         position += mlt_producer_get_in( producer );
373
374         // Image index
375         int loop = mlt_properties_get_int( producer_props, "loop" );
376         int current_idx;
377         if (loop) {
378                 current_idx = ( int )floor( ( double )position / ttl ) % self->count;
379         } else {
380                 current_idx = MIN(( double )position / ttl, self->count - 1);
381         }
382
383         // Key for the cache
384         char image_key[ 10 ];
385         sprintf( image_key, "%d", current_idx );
386
387         int disable_exif = mlt_properties_get_int( producer_props, "disable_exif" );
388
389         if ( current_idx != self->pixbuf_idx )
390                 self->pixbuf = NULL;
391         if ( !self->pixbuf || mlt_properties_get_int( producer_props, "_disable_exif" ) != disable_exif )
392         {
393                 GError *error = NULL;
394
395                 self->image = NULL;
396                 pthread_mutex_lock( &g_mutex );
397                 self->pixbuf = gdk_pixbuf_new_from_file( mlt_properties_get_value( self->filenames, current_idx ), &error );
398                 if ( self->pixbuf )
399                 {
400                         // Read the exif value for this file
401                         if ( !disable_exif )
402                                 self->pixbuf = reorient_with_exif( self, current_idx, self->pixbuf );
403
404                         // Register this pixbuf for destruction and reuse
405                         mlt_cache_item_close( self->pixbuf_cache );
406                         mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf", self->pixbuf, 0, ( mlt_destructor )g_object_unref );
407                         self->pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" );
408                         self->pixbuf_idx = current_idx;
409
410                         // Store the width/height of the pixbuf temporarily
411                         self->width = gdk_pixbuf_get_width( self->pixbuf );
412                         self->height = gdk_pixbuf_get_height( self->pixbuf );
413
414                         mlt_events_block( producer_props, NULL );
415                         mlt_properties_set_int( producer_props, "meta.media.width", self->width );
416                         mlt_properties_set_int( producer_props, "meta.media.height", self->height );
417                         mlt_properties_set_int( producer_props, "_disable_exif", disable_exif );
418                         mlt_events_unblock( producer_props, NULL );
419
420                 }
421                 pthread_mutex_unlock( &g_mutex );
422         }
423
424         // Set width/height of frame
425         mlt_properties_set_int( properties, "width", self->width );
426         mlt_properties_set_int( properties, "height", self->height );
427
428         return current_idx;
429 }
430
431 static void refresh_image( producer_pixbuf self, mlt_frame frame, mlt_image_format format, int width, int height )
432 {
433         // Obtain properties of frame and producer
434         mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
435         mlt_producer producer = &self->parent;
436
437         // Get index and pixbuf
438         int current_idx = refresh_pixbuf( self, frame );
439
440         // optimization for subsequent iterations on single picture
441         if ( current_idx != self->image_idx || width != self->width || height != self->height )
442                 self->image = NULL;
443         mlt_log_debug( MLT_PRODUCER_SERVICE( producer ), "image %p pixbuf %p idx %d current_idx %d pixbuf_idx %d width %d\n",
444                 self->image, self->pixbuf, current_idx, self->image_idx, self->pixbuf_idx, width );
445
446         // If we have a pixbuf and we need an image
447         if ( self->pixbuf && ( !self->image || ( format != mlt_image_none && format != self->format ) ) )
448         {
449                 char *interps = mlt_properties_get( properties, "rescale.interp" );
450                 if ( interps ) interps = strdup( interps );
451                 int interp = GDK_INTERP_BILINEAR;
452
453                 if ( !interps ) {
454                         // Keep bilinear by default
455                 }
456                 else if ( strcmp( interps, "nearest" ) == 0 )
457                         interp = GDK_INTERP_NEAREST;
458                 else if ( strcmp( interps, "tiles" ) == 0 )
459                         interp = GDK_INTERP_TILES;
460                 else if ( strcmp( interps, "hyper" ) == 0 || strcmp( interps, "bicubic" ) == 0 )
461                         interp = GDK_INTERP_HYPER;
462                 if ( interps ) free( interps );
463
464                 // Note - the original pixbuf is already safe and ready for destruction
465                 pthread_mutex_lock( &g_mutex );
466                 GdkPixbuf* pixbuf = gdk_pixbuf_scale_simple( self->pixbuf, width, height, interp );
467
468                 // Store width and height
469                 self->width = width;
470                 self->height = height;
471
472                 // Allocate/define image
473                 int has_alpha = gdk_pixbuf_get_has_alpha( pixbuf );
474                 int src_stride = gdk_pixbuf_get_rowstride( pixbuf );
475                 int dst_stride = self->width * ( has_alpha ? 4 : 3 );
476                 int image_size = dst_stride * ( height + 1 );
477                 self->image = mlt_pool_alloc( image_size );
478                 self->alpha = NULL;
479                 self->format = has_alpha ? mlt_image_rgb24a : mlt_image_rgb24;
480
481                 if ( src_stride != dst_stride )
482                 {
483                         int y = self->height;
484                         uint8_t *src = gdk_pixbuf_get_pixels( pixbuf );
485                         uint8_t *dst = self->image;
486                         while ( y-- )
487                         {
488                                 memcpy( dst, src, dst_stride );
489                                 dst += dst_stride;
490                                 src += src_stride;
491                         }
492                 }
493                 else
494                 {
495                         memcpy( self->image, gdk_pixbuf_get_pixels( pixbuf ), src_stride * height );
496                 }
497                 pthread_mutex_unlock( &g_mutex );
498
499                 // Convert image to requested format
500                 if ( format != mlt_image_none && format != self->format )
501                 {
502                         uint8_t *buffer = NULL;
503
504                         // First, set the image so it can be converted when we get it
505                         mlt_frame_replace_image( frame, self->image, self->format, width, height );
506                         mlt_frame_set_image( frame, self->image, image_size, mlt_pool_release );
507                         self->format = format;
508
509                         // get_image will do the format conversion
510                         mlt_frame_get_image( frame, &buffer, &format, &width, &height, 0 );
511
512                         // cache copies of the image and alpha buffers
513                         if ( buffer )
514                         {
515                                 image_size = mlt_image_format_size( format, width, height, NULL );
516                                 self->image = mlt_pool_alloc( image_size );
517                                 memcpy( self->image, buffer, image_size );
518                         }
519                         if ( ( buffer = mlt_frame_get_alpha_mask( frame ) ) )
520                         {
521                                 self->alpha = mlt_pool_alloc( width * height );
522                                 memcpy( self->alpha, buffer, width * height );
523                         }
524                 }
525
526                 // Update the cache
527                 mlt_cache_item_close( self->image_cache );
528                 mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image", self->image, image_size, mlt_pool_release );
529                 self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image" );
530                 self->image_idx = current_idx;
531                 mlt_cache_item_close( self->alpha_cache );
532                 self->alpha_cache = NULL;
533                 if ( self->alpha )
534                 {
535                         mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha", self->alpha, width * height, mlt_pool_release );
536                         self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha" );
537                 }
538
539                 // Finished with pixbuf now
540                 g_object_unref( pixbuf );
541         }
542
543         // Set width/height of frame
544         mlt_properties_set_int( properties, "width", self->width );
545         mlt_properties_set_int( properties, "height", self->height );
546 }
547
548 static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
549 {
550         int error = 0;
551         
552         // Obtain properties of frame and producer
553         mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
554         producer_pixbuf self = mlt_properties_get_data( properties, "producer_pixbuf", NULL );
555         mlt_producer producer = &self->parent;
556
557         // Use the width and height suggested by the rescale filter because we can do our own scaling.
558         if ( mlt_properties_get_int( properties, "rescale_width" ) > 0 )
559                 *width = mlt_properties_get_int( properties, "rescale_width" );
560         if ( mlt_properties_get_int( properties, "rescale_height" ) > 0 )
561                 *height = mlt_properties_get_int( properties, "rescale_height" );
562
563         // Restore pixbuf and image
564         mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) );
565         self->pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" );
566         self->pixbuf = mlt_cache_item_data( self->pixbuf_cache, NULL );
567         self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image" );
568         self->image = mlt_cache_item_data( self->image_cache, NULL );
569         self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha" );
570         self->alpha = mlt_cache_item_data( self->alpha_cache, NULL );
571
572         // Refresh the image
573         refresh_image( self, frame, *format, *width, *height );
574
575         // Get width and height (may have changed during the refresh)
576         *width = self->width;
577         *height = self->height;
578         *format = self->format;
579
580         // NB: Cloning is necessary with this producer (due to processing of images ahead of use)
581         // The fault is not in the design of mlt, but in the implementation of the pixbuf producer...
582         if ( self->image )
583         {
584                 // Clone the image
585                 int image_size = mlt_image_format_size( self->format, self->width, self->height, NULL );
586                 uint8_t *image_copy = mlt_pool_alloc( image_size );
587                 memcpy( image_copy, self->image, image_size );
588                 // Now update properties so we free the copy after
589                 mlt_frame_set_image( frame, image_copy, image_size, mlt_pool_release );
590                 // We're going to pass the copy on
591                 *buffer = image_copy;
592                 mlt_log_debug( MLT_PRODUCER_SERVICE( &self->parent ), "%dx%d (%s)\n",
593                         self->width, self->height, mlt_image_format_name( *format ) );
594                 // Clone the alpha channel
595                 if ( self->alpha )
596                 {
597                         image_copy = mlt_pool_alloc( self->width * self->height );
598                         memcpy( image_copy, self->alpha, self->width * self->height );
599                         mlt_frame_set_alpha( frame, image_copy, self->width * self->height, mlt_pool_release );
600                 }
601         }
602         else
603         {
604                 error = 1;
605         }
606
607         // Release references and locks
608         mlt_cache_item_close( self->pixbuf_cache );
609         mlt_cache_item_close( self->image_cache );
610         mlt_cache_item_close( self->alpha_cache );
611         mlt_service_unlock( MLT_PRODUCER_SERVICE( &self->parent ) );
612
613         return error;
614 }
615
616 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
617 {
618         // Get the real structure for this producer
619         producer_pixbuf self = producer->child;
620
621         // Fetch the producers properties
622         mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
623
624         if ( self->filenames == NULL && mlt_properties_get( producer_properties, "resource" ) != NULL )
625                 load_filenames( self, producer_properties );
626
627         // Generate a frame
628         *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
629
630         if ( *frame != NULL && self->count > 0 )
631         {
632                 // Obtain properties of frame and producer
633                 mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
634
635                 // Set the producer on the frame properties
636                 mlt_properties_set_data( properties, "producer_pixbuf", self, 0, NULL, NULL );
637
638                 // Update timecode on the frame we're creating
639                 mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
640
641                 // Refresh the pixbuf
642                 self->pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" );
643                 self->pixbuf = mlt_cache_item_data( self->pixbuf_cache, NULL );
644                 refresh_pixbuf( self, *frame );
645                 mlt_cache_item_close( self->pixbuf_cache );
646
647                 // Set producer-specific frame properties
648                 mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( producer_properties, "progressive" ) );
649                 
650                 double force_ratio = mlt_properties_get_double( producer_properties, "force_aspect_ratio" );
651                 if ( force_ratio > 0.0 )
652                         mlt_properties_set_double( properties, "aspect_ratio", force_ratio );
653                 else
654                         mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "aspect_ratio" ) );
655
656                 // Push the get_image method
657                 mlt_frame_push_get_image( *frame, producer_get_image );
658         }
659
660         // Calculate the next timecode
661         mlt_producer_prepare_next( producer );
662
663         return 0;
664 }
665
666 static void producer_close( mlt_producer parent )
667 {
668         producer_pixbuf self = parent->child;
669         parent->close = NULL;
670         mlt_service_cache_purge( MLT_PRODUCER_SERVICE(parent) );
671         mlt_producer_close( parent );
672         mlt_properties_close( self->filenames );
673         free( self );
674 }