]> git.sesse.net Git - mlt/blob - src/modules/gtk2/producer_pixbuf.c
Version modules and data directories, and melt
[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                 int interp = GDK_INTERP_BILINEAR;
451
452                 if ( !interps ) {
453                         // Keep bilinear by default
454                 }
455                 else if ( strcmp( interps, "nearest" ) == 0 )
456                         interp = GDK_INTERP_NEAREST;
457                 else if ( strcmp( interps, "tiles" ) == 0 )
458                         interp = GDK_INTERP_TILES;
459                 else if ( strcmp( interps, "hyper" ) == 0 || strcmp( interps, "bicubic" ) == 0 )
460                         interp = GDK_INTERP_HYPER;
461
462                 // Note - the original pixbuf is already safe and ready for destruction
463                 pthread_mutex_lock( &g_mutex );
464                 GdkPixbuf* pixbuf = gdk_pixbuf_scale_simple( self->pixbuf, width, height, interp );
465
466                 // Store width and height
467                 self->width = width;
468                 self->height = height;
469
470                 // Allocate/define image
471                 int has_alpha = gdk_pixbuf_get_has_alpha( pixbuf );
472                 int src_stride = gdk_pixbuf_get_rowstride( pixbuf );
473                 int dst_stride = self->width * ( has_alpha ? 4 : 3 );
474                 int image_size = dst_stride * ( height + 1 );
475                 self->image = mlt_pool_alloc( image_size );
476                 self->alpha = NULL;
477                 self->format = has_alpha ? mlt_image_rgb24a : mlt_image_rgb24;
478
479                 if ( src_stride != dst_stride )
480                 {
481                         int y = self->height;
482                         uint8_t *src = gdk_pixbuf_get_pixels( pixbuf );
483                         uint8_t *dst = self->image;
484                         while ( y-- )
485                         {
486                                 memcpy( dst, src, dst_stride );
487                                 dst += dst_stride;
488                                 src += src_stride;
489                         }
490                 }
491                 else
492                 {
493                         memcpy( self->image, gdk_pixbuf_get_pixels( pixbuf ), src_stride * height );
494                 }
495                 pthread_mutex_unlock( &g_mutex );
496
497                 // Convert image to requested format
498                 if ( format != mlt_image_none && format != self->format )
499                 {
500                         uint8_t *buffer = NULL;
501
502                         // First, set the image so it can be converted when we get it
503                         mlt_frame_replace_image( frame, self->image, self->format, width, height );
504                         mlt_frame_set_image( frame, self->image, image_size, mlt_pool_release );
505                         self->format = format;
506
507                         // get_image will do the format conversion
508                         mlt_frame_get_image( frame, &buffer, &format, &width, &height, 0 );
509
510                         // cache copies of the image and alpha buffers
511                         if ( buffer )
512                         {
513                                 image_size = mlt_image_format_size( format, width, height, NULL );
514                                 self->image = mlt_pool_alloc( image_size );
515                                 memcpy( self->image, buffer, image_size );
516                         }
517                         if ( ( buffer = mlt_frame_get_alpha_mask( frame ) ) )
518                         {
519                                 self->alpha = mlt_pool_alloc( width * height );
520                                 memcpy( self->alpha, buffer, width * height );
521                         }
522                 }
523
524                 // Update the cache
525                 mlt_cache_item_close( self->image_cache );
526                 mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image", self->image, image_size, mlt_pool_release );
527                 self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image" );
528                 self->image_idx = current_idx;
529                 mlt_cache_item_close( self->alpha_cache );
530                 self->alpha_cache = NULL;
531                 if ( self->alpha )
532                 {
533                         mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha", self->alpha, width * height, mlt_pool_release );
534                         self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha" );
535                 }
536
537                 // Finished with pixbuf now
538                 g_object_unref( pixbuf );
539         }
540
541         // Set width/height of frame
542         mlt_properties_set_int( properties, "width", self->width );
543         mlt_properties_set_int( properties, "height", self->height );
544 }
545
546 static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
547 {
548         int error = 0;
549         
550         // Obtain properties of frame and producer
551         mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
552         producer_pixbuf self = mlt_properties_get_data( properties, "producer_pixbuf", NULL );
553         mlt_producer producer = &self->parent;
554
555         // Use the width and height suggested by the rescale filter because we can do our own scaling.
556         *width = mlt_properties_get_int( properties, "rescale_width" );
557         *height = mlt_properties_get_int( properties, "rescale_height" );
558
559         // Restore pixbuf and image
560         mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) );
561         self->pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" );
562         self->pixbuf = mlt_cache_item_data( self->pixbuf_cache, NULL );
563         self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image" );
564         self->image = mlt_cache_item_data( self->image_cache, NULL );
565         self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha" );
566         self->alpha = mlt_cache_item_data( self->alpha_cache, NULL );
567
568         // Refresh the image
569         refresh_image( self, frame, *format, *width, *height );
570
571         // Get width and height (may have changed during the refresh)
572         *width = self->width;
573         *height = self->height;
574         *format = self->format;
575
576         // NB: Cloning is necessary with this producer (due to processing of images ahead of use)
577         // The fault is not in the design of mlt, but in the implementation of the pixbuf producer...
578         if ( self->image )
579         {
580                 // Clone the image
581                 int image_size = mlt_image_format_size( self->format, self->width, self->height, NULL );
582                 uint8_t *image_copy = mlt_pool_alloc( image_size );
583                 memcpy( image_copy, self->image, image_size );
584                 // Now update properties so we free the copy after
585                 mlt_frame_set_image( frame, image_copy, image_size, mlt_pool_release );
586                 // We're going to pass the copy on
587                 *buffer = image_copy;
588                 mlt_log_debug( MLT_PRODUCER_SERVICE( &self->parent ), "%dx%d (%s)\n",
589                         self->width, self->height, mlt_image_format_name( *format ) );
590                 // Clone the alpha channel
591                 if ( self->alpha )
592                 {
593                         image_copy = mlt_pool_alloc( self->width * self->height );
594                         memcpy( image_copy, self->alpha, self->width * self->height );
595                         mlt_frame_set_alpha( frame, image_copy, self->width * self->height, mlt_pool_release );
596                 }
597         }
598         else
599         {
600                 error = 1;
601         }
602
603         // Release references and locks
604         mlt_cache_item_close( self->pixbuf_cache );
605         mlt_cache_item_close( self->image_cache );
606         mlt_cache_item_close( self->alpha_cache );
607         mlt_service_unlock( MLT_PRODUCER_SERVICE( &self->parent ) );
608
609         return error;
610 }
611
612 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
613 {
614         // Get the real structure for this producer
615         producer_pixbuf self = producer->child;
616
617         // Fetch the producers properties
618         mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
619
620         if ( self->filenames == NULL && mlt_properties_get( producer_properties, "resource" ) != NULL )
621                 load_filenames( self, producer_properties );
622
623         // Generate a frame
624         *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
625
626         if ( *frame != NULL && self->count > 0 )
627         {
628                 // Obtain properties of frame and producer
629                 mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
630
631                 // Set the producer on the frame properties
632                 mlt_properties_set_data( properties, "producer_pixbuf", self, 0, NULL, NULL );
633
634                 // Update timecode on the frame we're creating
635                 mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
636
637                 // Refresh the pixbuf
638                 self->pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" );
639                 self->pixbuf = mlt_cache_item_data( self->pixbuf_cache, NULL );
640                 refresh_pixbuf( self, *frame );
641                 mlt_cache_item_close( self->pixbuf_cache );
642
643                 // Set producer-specific frame properties
644                 mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( producer_properties, "progressive" ) );
645                 
646                 double force_ratio = mlt_properties_get_double( producer_properties, "force_aspect_ratio" );
647                 if ( force_ratio > 0.0 )
648                         mlt_properties_set_double( properties, "aspect_ratio", force_ratio );
649                 else
650                         mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "aspect_ratio" ) );
651
652                 // Push the get_image method
653                 mlt_frame_push_get_image( *frame, producer_get_image );
654         }
655
656         // Calculate the next timecode
657         mlt_producer_prepare_next( producer );
658
659         return 0;
660 }
661
662 static void producer_close( mlt_producer parent )
663 {
664         producer_pixbuf self = parent->child;
665         parent->close = NULL;
666         mlt_service_cache_purge( MLT_PRODUCER_SERVICE(parent) );
667         mlt_producer_close( parent );
668         mlt_properties_close( self->filenames );
669         free( self );
670 }