1 /*
2 * producer_pixbuf.c -- raster image loader based upon gdk-pixbuf
3 * Copyright (C) 2003-2018 Meltytech, LLC
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include <framework/mlt_producer.h>
21 #include <framework/mlt_frame.h>
22 #include <framework/mlt_cache.h>
23 #include <framework/mlt_log.h>
24 #include <framework/mlt_tokeniser.h>
25 #include <gdk-pixbuf/gdk-pixbuf.h>
26
27 #ifdef USE_EXIF
28 #include <libexif/exif-data.h>
29 #endif
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <pthread.h>
35 #include <math.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39 #include <dirent.h>
40 #include <ctype.h>
41
42 // this protects concurrent access to gdk_pixbuf
43 static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
44
45 typedef struct producer_pixbuf_s *producer_pixbuf;
46
47 struct producer_pixbuf_s
48 {
49 struct mlt_producer_s parent;
50
51 // File name list
52 mlt_properties filenames;
53 mlt_position *outs;
54 int count;
55 int image_idx;
56 int pixbuf_idx;
57 int width;
58 int height;
59 uint8_t *alpha;
60 uint8_t *image;
61 mlt_cache_item image_cache;
62 mlt_cache_item alpha_cache;
63 mlt_cache_item pixbuf_cache;
64 GdkPixbuf *pixbuf;
65 mlt_image_format format;
66 };
67
68 static void load_filenames( producer_pixbuf self, mlt_properties producer_properties );
69 static int refresh_pixbuf( producer_pixbuf self, mlt_frame frame );
70 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
71 static void producer_close( mlt_producer parent );
72
refresh_length(mlt_properties properties,producer_pixbuf self)73 static void refresh_length( mlt_properties properties, producer_pixbuf self )
74 {
75 if ( self->count > mlt_properties_get_int( properties, "length" ) ||
76 mlt_properties_get_int( properties, "autolength" ) )
77 {
78 int ttl = mlt_properties_get_int( properties, "ttl" );
79 mlt_position length = self->count * ttl;
80 mlt_properties_set_position( properties, "length", length );
81 mlt_properties_set_position( properties, "out", length - 1 );
82 }
83 }
84
on_property_changed(mlt_service owner,mlt_producer producer,char * name)85 static void on_property_changed( mlt_service owner, mlt_producer producer, char *name )
86 {
87 if ( !strcmp( name, "ttl" ) )
88 refresh_length( MLT_PRODUCER_PROPERTIES(producer), producer->child );
89 }
90
producer_pixbuf_init(char * filename)91 mlt_producer producer_pixbuf_init( char *filename )
92 {
93 producer_pixbuf self = calloc( 1, sizeof( struct producer_pixbuf_s ) );
94 if ( self != NULL && mlt_producer_init( &self->parent, self ) == 0 )
95 {
96 mlt_producer producer = &self->parent;
97
98 // Get the properties interface
99 mlt_properties properties = MLT_PRODUCER_PROPERTIES( &self->parent );
100
101 // Reject if animation.
102 GError *error = NULL;
103 pthread_mutex_lock( &g_mutex );
104 GdkPixbufAnimation *anim = gdk_pixbuf_animation_new_from_file( filename, &error );
105 if ( anim )
106 {
107 gboolean is_anim = !gdk_pixbuf_animation_is_static_image( anim );
108 g_object_unref( anim );
109 if ( is_anim )
110 {
111 pthread_mutex_unlock( &g_mutex );
112 mlt_producer_close( &self->parent );
113 free( self );
114 return NULL;
115 }
116 }
117 pthread_mutex_unlock( &g_mutex );
118
119 // Callback registration
120 producer->get_frame = producer_get_frame;
121 producer->close = ( mlt_destructor )producer_close;
122
123 // Set the default properties
124 mlt_properties_set( properties, "resource", filename );
125 mlt_properties_set_int( properties, "ttl", 25 );
126 mlt_properties_set_int( properties, "aspect_ratio", 1 );
127 mlt_properties_set_int( properties, "progressive", 1 );
128 mlt_properties_set_int( properties, "seekable", 1 );
129 mlt_properties_set_int( properties, "loop", 1 );
130
131 // Validate the resource
132 if ( filename )
133 load_filenames( self, properties );
134 if ( self->count )
135 {
136 mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
137 if ( frame )
138 {
139 mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
140 mlt_properties_set_data( frame_properties, "producer_pixbuf", self, 0, NULL, NULL );
141 mlt_frame_set_position( frame, mlt_producer_position( producer ) );
142 refresh_pixbuf( self, frame );
143 mlt_cache_item_close( self->pixbuf_cache );
144 mlt_frame_close( frame );
145 }
146 }
147 if ( self->width == 0 )
148 {
149 producer_close( producer );
150 producer = NULL;
151 }
152 else
153 {
154 mlt_events_listen( properties, self, "property-changed", (mlt_listener) on_property_changed );
155 }
156 return producer;
157 }
158 free( self );
159 return NULL;
160 }
161
load_svg(producer_pixbuf self,mlt_properties properties,const char * filename)162 static int load_svg( producer_pixbuf self, mlt_properties properties, const char *filename )
163 {
164 int result = 0;
165
166 // Read xml string
167 if ( strstr( filename, "<svg" ) )
168 {
169 // Generate a temporary file for the svg
170 char fullname[ 1024 ] = "/tmp/mlt.XXXXXX";
171 int fd = g_mkstemp( fullname );
172
173 if ( fd > -1 )
174 {
175 // Write the svg into the temp file
176 ssize_t remaining_bytes;
177 const char *xml = filename;
178
179 // Strip leading crap
180 while ( xml[0] != '<' )
181 xml++;
182
183 remaining_bytes = strlen( xml );
184 while ( remaining_bytes > 0 )
185 remaining_bytes -= write( fd, xml + strlen( xml ) - remaining_bytes, remaining_bytes );
186 close( fd );
187
188 mlt_properties_set( self->filenames, "0", fullname );
189
190 // Teehe - when the producer closes, delete the temp file and the space allo
191 mlt_properties_set_data( properties, "__temporary_file__", fullname, 0, ( mlt_destructor )unlink, NULL );
192 result = 1;
193 }
194 }
195 return result;
196 }
197
load_sequence_sprintf(producer_pixbuf self,mlt_properties properties,const char * filename)198 static int load_sequence_sprintf( producer_pixbuf self, mlt_properties properties, const char *filename )
199 {
200 int result = 0;
201
202 // Obtain filenames with pattern
203 if ( strchr( filename, '%' ) != NULL )
204 {
205 // handle picture sequences
206 int i = mlt_properties_get_int( properties, "begin" );
207 int gap = 0;
208 char full[1024];
209 int keyvalue = 0;
210 char key[ 50 ];
211
212 while ( gap < 100 )
213 {
214 struct stat buf;
215 snprintf( full, 1023, filename, i ++ );
216 if ( stat( full, &buf ) == 0 )
217 {
218 sprintf( key, "%d", keyvalue ++ );
219 mlt_properties_set( self->filenames, key, full );
220 gap = 0;
221 }
222 else
223 {
224 gap ++;
225 }
226 }
227 if ( mlt_properties_count( self->filenames ) > 0 )
228 {
229 mlt_properties_set_int( properties, "ttl", 1 );
230 result = 1;
231 }
232 }
233 return result;
234 }
235
load_sequence_deprecated(producer_pixbuf self,mlt_properties properties,const char * filename)236 static int load_sequence_deprecated( producer_pixbuf self, mlt_properties properties, const char *filename )
237 {
238 int result = 0;
239 const char *start;
240
241 // This approach is deprecated in favor of the begin querystring parameter.
242 // Obtain filenames with pattern containing a begin value, e.g. foo%1234d.png
243 if ( ( start = strchr( filename, '%' ) ) )
244 {
245 const char *end = ++start;
246 while ( isdigit( *end ) ) end++;
247 if ( end > start && ( end[0] == 'd' || end[0] == 'i' || end[0] == 'u' ) )
248 {
249 int n = end - start;
250 char *s = calloc( 1, n + 1 );
251 strncpy( s, start, n );
252 mlt_properties_set( properties, "begin", s );
253 free( s );
254 s = calloc( 1, strlen( filename ) + 2 );
255 strncpy( s, filename, start - filename );
256 sprintf( s + ( start - filename ), ".%d%s", n, end );
257 result = load_sequence_sprintf( self, properties, s );
258 free( s );
259 }
260 }
261 return result;
262 }
263
load_sequence_querystring(producer_pixbuf self,mlt_properties properties,const char * filename)264 static int load_sequence_querystring( producer_pixbuf self, mlt_properties properties, const char *filename )
265 {
266 int result = 0;
267
268 // Obtain filenames with pattern and begin value in query string
269 if ( strchr( filename, '%' ) && strchr( filename, '?' ) )
270 {
271 // Split filename into pattern and query string
272 char *s = strdup( filename );
273 char *querystring = strrchr( s, '?' );
274 *querystring++ = '\0';
275 if ( strstr( filename, "begin=" ) )
276 mlt_properties_set( properties, "begin", strstr( querystring, "begin=" ) + 6 );
277 else if ( strstr( filename, "begin:" ) )
278 mlt_properties_set( properties, "begin", strstr( querystring, "begin:" ) + 6 );
279 // Coerce to an int value so serialization does not have any extra query string cruft
280 mlt_properties_set_int( properties, "begin", mlt_properties_get_int( properties, "begin" ) );
281 result = load_sequence_sprintf( self, properties, s );
282 free( s );
283 }
284 return result;
285 }
286
load_folder(producer_pixbuf self,mlt_properties properties,const char * filename)287 static int load_folder( producer_pixbuf self, mlt_properties properties, const char *filename )
288 {
289 int result = 0;
290
291 // Obtain filenames with folder
292 if ( strstr( filename, "/.all." ) != NULL )
293 {
294 char wildcard[ 1024 ];
295 char *dir_name = strdup( filename );
296 char *extension = strrchr( dir_name, '.' );
297
298 *( strstr( dir_name, "/.all." ) + 1 ) = '\0';
299 sprintf( wildcard, "*%s", extension );
300
301 mlt_properties_dir_list( self->filenames, dir_name, wildcard, 1 );
302
303 free( dir_name );
304 result = 1;
305 }
306 return result;
307 }
308
load_sequence_csv(producer_pixbuf self,mlt_properties properties,const char * filename)309 static int load_sequence_csv( producer_pixbuf self, mlt_properties properties, const char *filename )
310 {
311 int result = 0;
312 const char *csv_extension = strstr( filename, ".csv" );
313
314 if ( csv_extension != NULL && csv_extension[4] == '\0' )
315 {
316 FILE *csv = fopen( filename, "r" );
317 if ( csv != NULL )
318 {
319 int keyvalue = 0;
320 int out = 0;
321
322 int nlines = 0;
323 while ( !feof( csv ) )
324 {
325 char line[ 1024 ];
326 if ( fgets( line, 1024, csv ) != NULL )
327 {
328 nlines++;
329 }
330 }
331
332 self->outs = (mlt_position*)malloc(nlines * sizeof(mlt_position));
333
334 fseek(csv, 0, SEEK_SET);
335 int index = 0;
336 while ( !feof( csv ) )
337 {
338 char line[ 1024 ];
339 if ( fgets( line, 1024, csv ) != NULL )
340 {
341 char *sep = strchr(line, ';');
342 if ( sep != NULL )
343 {
344 int ttl = 0;
345 int n = 0;
346 char key[ 50 ];
347 struct stat buf;
348
349 *sep++ = '\0';
350 n = sscanf( sep, "%d", &ttl);
351 if ( n == 0 )
352 {
353 break;
354 }
355
356 if ( stat( line, &buf ) != 0 )
357 {
358 break;
359 }
360
361 out += ttl;
362 mlt_log_debug( MLT_PRODUCER_SERVICE( &self->parent ), "file:'%s' ttl=%d out=%d\n", line, ttl, out );
363
364 sprintf( key, "%d", keyvalue++ );
365 mlt_properties_set( self->filenames, key, line );
366 self->outs[index++] = out;
367 }
368 }
369 }
370
371 fclose( csv );
372 result = 1;
373 }
374 }
375
376 return result;
377 }
378
load_filenames(producer_pixbuf self,mlt_properties properties)379 static void load_filenames( producer_pixbuf self, mlt_properties properties )
380 {
381 char *filename = mlt_properties_get( properties, "resource" );
382 self->filenames = mlt_properties_new( );
383 self->outs = NULL;
384
385 if (!load_svg( self, properties, filename ) &&
386 !load_sequence_querystring( self, properties, filename ) &&
387 !load_sequence_sprintf( self, properties, filename ) &&
388 !load_sequence_deprecated( self, properties, filename ) &&
389 !load_sequence_csv( self, properties, filename ) &&
390 !load_folder( self, properties, filename ) )
391 {
392 mlt_properties_set( self->filenames, "0", filename );
393 }
394 self->count = mlt_properties_count( self->filenames );
395 refresh_length( properties, self );
396 }
397
reorient_with_exif(producer_pixbuf self,int image_idx,GdkPixbuf * pixbuf)398 static GdkPixbuf* reorient_with_exif( producer_pixbuf self, int image_idx, GdkPixbuf *pixbuf )
399 {
400 #ifdef USE_EXIF
401 mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( &self->parent );
402 ExifData *d = exif_data_new_from_file( mlt_properties_get_value( self->filenames, image_idx ) );
403 ExifEntry *entry;
404 int exif_orientation = 0;
405
406 /* get orientation and rotate image accordingly if necessary */
407 if (d)
408 {
409 if ( ( entry = exif_content_get_entry ( d->ifd[EXIF_IFD_0], EXIF_TAG_ORIENTATION ) ) )
410 exif_orientation = exif_get_short (entry->data, exif_data_get_byte_order (d));
411
412 /* Free the EXIF data */
413 exif_data_unref(d);
414 }
415
416 // Remember EXIF value, might be useful for someone
417 mlt_properties_set_int( producer_props, "_exif_orientation" , exif_orientation );
418
419 if ( exif_orientation > 1 )
420 {
421 GdkPixbuf *processed = NULL;
422 GdkPixbufRotation matrix = GDK_PIXBUF_ROTATE_NONE;
423
424 // Rotate image according to exif data
425 switch ( exif_orientation ) {
426 case 2:
427 processed = gdk_pixbuf_flip ( pixbuf, TRUE );
428 break;
429 case 3:
430 matrix = GDK_PIXBUF_ROTATE_UPSIDEDOWN;
431 processed = pixbuf;
432 break;
433 case 4:
434 processed = gdk_pixbuf_flip ( pixbuf, FALSE );
435 break;
436 case 5:
437 matrix = GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE;
438 processed = gdk_pixbuf_flip ( pixbuf, TRUE );
439 break;
440 case 6:
441 matrix = GDK_PIXBUF_ROTATE_CLOCKWISE;
442 processed = pixbuf;
443 break;
444 case 7:
445 matrix = GDK_PIXBUF_ROTATE_CLOCKWISE;
446 processed = gdk_pixbuf_flip ( pixbuf, TRUE );
447 break;
448 case 8:
449 matrix = GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE;
450 processed = pixbuf;
451 break;
452 }
453 if ( processed )
454 {
455 pixbuf = gdk_pixbuf_rotate_simple( processed, matrix );
456 g_object_unref( processed );
457 }
458 }
459 #endif
460 return pixbuf;
461 }
462
refresh_pixbuf(producer_pixbuf self,mlt_frame frame)463 static int refresh_pixbuf( producer_pixbuf self, mlt_frame frame )
464 {
465 // Obtain properties of frame and producer
466 mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
467 mlt_producer producer = &self->parent;
468 mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
469
470 // Check if user wants us to reload the image
471 if ( mlt_properties_get_int( producer_props, "force_reload" ) )
472 {
473 self->pixbuf = NULL;
474 self->image = NULL;
475 mlt_properties_set_int( producer_props, "force_reload", 0 );
476 }
477
478 // Get the original position of this frame
479 mlt_position position = mlt_frame_original_position( frame );
480 position += mlt_producer_get_in( producer );
481
482 int loop = mlt_properties_get_int( producer_props, "loop" );
483
484 // Image index
485 int current_idx = 0;
486 if ( !self->outs )
487 {
488 // Get the time to live for each frame
489 double ttl = mlt_properties_get_int( producer_props, "ttl" );
490 if (loop) {
491 current_idx = ( int )floor( ( double )position / ttl ) % self->count;
492 } else {
493 current_idx = MIN(( double )position / ttl, self->count - 1);
494 }
495 }
496 else
497 {
498 int total_ttl = ( int )floor( self->outs[self->count - 1]);
499 mlt_position looped_pos = loop ? ( int )floor( position ) % total_ttl : position;
500
501 while ( current_idx < self->count )
502 {
503 if (self->outs[ current_idx ] > looped_pos)
504 {
505 break;
506 }
507
508 current_idx++;
509 }
510
511 if ( current_idx >= self->count )
512 {
513 current_idx = self->count - 1;
514 }
515
516 mlt_log_debug( MLT_PRODUCER_SERVICE( &self->parent ), "position=%d current_idx=%d\n", position, current_idx );
517 }
518
519 int disable_exif = mlt_properties_get_int( producer_props, "disable_exif" );
520
521 if ( current_idx != self->pixbuf_idx )
522 self->pixbuf = NULL;
523 if ( !self->pixbuf || mlt_properties_get_int( producer_props, "_disable_exif" ) != disable_exif )
524 {
525 GError *error = NULL;
526
527 self->image = NULL;
528 pthread_mutex_lock( &g_mutex );
529 self->pixbuf = gdk_pixbuf_new_from_file( mlt_properties_get_value( self->filenames, current_idx ), &error );
530 if ( self->pixbuf )
531 {
532 // Read the exif value for this file
533 if ( !disable_exif )
534 self->pixbuf = reorient_with_exif( self, current_idx, self->pixbuf );
535
536 // Register this pixbuf for destruction and reuse
537 mlt_cache_item_close( self->pixbuf_cache );
538 mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf", self->pixbuf, 0, ( mlt_destructor )g_object_unref );
539 self->pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" );
540 self->pixbuf_idx = current_idx;
541
542 // Store the width/height of the pixbuf temporarily
543 self->width = gdk_pixbuf_get_width( self->pixbuf );
544 self->height = gdk_pixbuf_get_height( self->pixbuf );
545
546 mlt_events_block( producer_props, NULL );
547 mlt_properties_set_int( producer_props, "meta.media.width", self->width );
548 mlt_properties_set_int( producer_props, "meta.media.height", self->height );
549 mlt_properties_set_int( producer_props, "_disable_exif", disable_exif );
550 mlt_events_unblock( producer_props, NULL );
551
552 }
553 pthread_mutex_unlock( &g_mutex );
554 }
555
556 // Set width/height of frame
557 mlt_properties_set_int( properties, "width", self->width );
558 mlt_properties_set_int( properties, "height", self->height );
559
560 return current_idx;
561 }
562
refresh_image(producer_pixbuf self,mlt_frame frame,mlt_image_format format,int width,int height)563 static void refresh_image( producer_pixbuf self, mlt_frame frame, mlt_image_format format, int width, int height )
564 {
565 // Obtain properties of frame and producer
566 mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
567 mlt_producer producer = &self->parent;
568
569 // Get index and pixbuf
570 int current_idx = refresh_pixbuf( self, frame );
571
572 // optimization for subsequent iterations on single picture
573 if ( current_idx != self->image_idx || width != self->width || height != self->height )
574 self->image = NULL;
575 mlt_log_debug( MLT_PRODUCER_SERVICE( producer ), "image %p pixbuf %p idx %d current_idx %d pixbuf_idx %d width %d\n",
576 self->image, self->pixbuf, current_idx, self->image_idx, self->pixbuf_idx, width );
577
578 // If we have a pixbuf and we need an image
579 if ( self->pixbuf && ( !self->image || ( format != mlt_image_none && format != mlt_image_glsl && format != self->format ) ) )
580 {
581 char *interps = mlt_properties_get( properties, "rescale.interp" );
582 if ( interps ) interps = strdup( interps );
583 int interp = GDK_INTERP_BILINEAR;
584
585 if ( !interps ) {
586 // Keep bilinear by default
587 }
588 else if ( strcmp( interps, "nearest" ) == 0 )
589 interp = GDK_INTERP_NEAREST;
590 else if ( strcmp( interps, "tiles" ) == 0 )
591 interp = GDK_INTERP_TILES;
592 else if ( strcmp( interps, "hyper" ) == 0 || strcmp( interps, "bicubic" ) == 0 )
593 interp = GDK_INTERP_HYPER;
594 free( interps );
595
596 // Note - the original pixbuf is already safe and ready for destruction
597 pthread_mutex_lock( &g_mutex );
598 GdkPixbuf* pixbuf = gdk_pixbuf_scale_simple( self->pixbuf, width, height, interp );
599
600 // Store width and height
601 self->width = width;
602 self->height = height;
603
604 // Allocate/define image
605 int has_alpha = gdk_pixbuf_get_has_alpha( pixbuf );
606 int src_stride = gdk_pixbuf_get_rowstride( pixbuf );
607 int dst_stride = self->width * ( has_alpha ? 4 : 3 );
608 self->format = has_alpha ? mlt_image_rgb24a : mlt_image_rgb24;
609 int image_size = mlt_image_format_size( self->format, width, height, NULL );
610 self->image = mlt_pool_alloc( image_size );
611 self->alpha = NULL;
612
613 if ( src_stride != dst_stride )
614 {
615 int y = self->height;
616 uint8_t *src = gdk_pixbuf_get_pixels( pixbuf );
617 uint8_t *dst = self->image;
618 while ( y-- )
619 {
620 memcpy( dst, src, dst_stride );
621 dst += dst_stride;
622 src += src_stride;
623 }
624 }
625 else
626 {
627 memcpy( self->image, gdk_pixbuf_get_pixels( pixbuf ), src_stride * height );
628 }
629 pthread_mutex_unlock( &g_mutex );
630
631 // Convert image to requested format
632 if ( format != mlt_image_none && format != mlt_image_glsl && format != self->format && frame->convert_image )
633 {
634 // cache copies of the image and alpha buffers
635 uint8_t *buffer = self->image;
636 if ( buffer )
637 {
638 mlt_frame_set_image( frame, self->image, image_size, mlt_pool_release );
639 mlt_properties_set_int( properties, "width", self->width );
640 mlt_properties_set_int( properties, "height", self->height );
641 mlt_properties_set_int( properties, "format", self->format );
642
643 if ( !frame->convert_image( frame, &self->image, &self->format, format ) )
644 {
645 buffer = self->image;
646 image_size = mlt_image_format_size( self->format, self->width, self->height, NULL );
647 self->image = mlt_pool_alloc( image_size );
648 // We use height-1 because mlt_image_format_size() uses height + 1.
649 // XXX Remove -1 when mlt_image_format_size() is changed.
650 memcpy( self->image, buffer, mlt_image_format_size( self->format, self->width, self->height - 1, NULL ) );
651 }
652 }
653 if ( ( buffer = mlt_frame_get_alpha( frame ) ) )
654 {
655 self->alpha = mlt_pool_alloc( width * height );
656 memcpy( self->alpha, buffer, width * height );
657 }
658 }
659
660 // Update the cache
661 mlt_cache_item_close( self->image_cache );
662 mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image", self->image, image_size, mlt_pool_release );
663 self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image" );
664 self->image_idx = current_idx;
665 mlt_cache_item_close( self->alpha_cache );
666 self->alpha_cache = NULL;
667 if ( self->alpha )
668 {
669 mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha", self->alpha, width * height, mlt_pool_release );
670 self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha" );
671 }
672
673 // Finished with pixbuf now
674 g_object_unref( pixbuf );
675 }
676
677 // Set width/height of frame
678 mlt_properties_set_int( properties, "width", self->width );
679 mlt_properties_set_int( properties, "height", self->height );
680 }
681
producer_get_image(mlt_frame frame,uint8_t ** buffer,mlt_image_format * format,int * width,int * height,int writable)682 static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
683 {
684 int error = 0;
685
686 // Obtain properties of frame and producer
687 mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
688 producer_pixbuf self = mlt_properties_get_data( properties, "producer_pixbuf", NULL );
689 mlt_producer producer = &self->parent;
690
691 // Use the width and height suggested by the rescale filter because we can do our own scaling.
692 if ( mlt_properties_get_int( properties, "rescale_width" ) > 0 )
693 *width = mlt_properties_get_int( properties, "rescale_width" );
694 if ( mlt_properties_get_int( properties, "rescale_height" ) > 0 )
695 *height = mlt_properties_get_int( properties, "rescale_height" );
696
697 // Restore pixbuf and image
698 mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) );
699 self->pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" );
700 self->pixbuf = mlt_cache_item_data( self->pixbuf_cache, NULL );
701 self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image" );
702 self->image = mlt_cache_item_data( self->image_cache, NULL );
703 self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha" );
704 self->alpha = mlt_cache_item_data( self->alpha_cache, NULL );
705
706 // Refresh the image
707 refresh_image( self, frame, *format, *width, *height );
708
709 // Get width and height (may have changed during the refresh)
710 *width = self->width;
711 *height = self->height;
712 *format = self->format;
713
714 // NB: Cloning is necessary with this producer (due to processing of images ahead of use)
715 // The fault is not in the design of mlt, but in the implementation of the pixbuf producer...
716 if ( self->image )
717 {
718 // Clone the image
719 int image_size = mlt_image_format_size( self->format, self->width, self->height, NULL );
720 uint8_t *image_copy = mlt_pool_alloc( image_size );
721 // We use height-1 because mlt_image_format_size() uses height + 1.
722 // XXX Remove -1 when mlt_image_format_size() is changed.
723 memcpy( image_copy, self->image,
724 mlt_image_format_size( self->format, self->width, self->height - 1, NULL ) );
725 // Now update properties so we free the copy after
726 mlt_frame_set_image( frame, image_copy, image_size, mlt_pool_release );
727 // We're going to pass the copy on
728 *buffer = image_copy;
729 mlt_log_debug( MLT_PRODUCER_SERVICE( &self->parent ), "%dx%d (%s)\n",
730 self->width, self->height, mlt_image_format_name( *format ) );
731 // Clone the alpha channel
732 if ( self->alpha )
733 {
734 image_copy = mlt_pool_alloc( self->width * self->height );
735 memcpy( image_copy, self->alpha, self->width * self->height );
736 mlt_frame_set_alpha( frame, image_copy, self->width * self->height, mlt_pool_release );
737 }
738 }
739 else
740 {
741 error = 1;
742 }
743
744 // Release references and locks
745 mlt_cache_item_close( self->pixbuf_cache );
746 mlt_cache_item_close( self->image_cache );
747 mlt_cache_item_close( self->alpha_cache );
748 mlt_service_unlock( MLT_PRODUCER_SERVICE( &self->parent ) );
749
750 return error;
751 }
752
producer_get_frame(mlt_producer producer,mlt_frame_ptr frame,int index)753 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
754 {
755 // Get the real structure for this producer
756 producer_pixbuf self = producer->child;
757
758 // Fetch the producers properties
759 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
760
761 if ( self->filenames == NULL && mlt_properties_get( producer_properties, "resource" ) != NULL )
762 load_filenames( self, producer_properties );
763
764 // Generate a frame
765 *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
766
767 if ( *frame != NULL && self->count > 0 )
768 {
769 // Obtain properties of frame and producer
770 mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
771
772 // Set the producer on the frame properties
773 mlt_properties_set_data( properties, "producer_pixbuf", self, 0, NULL, NULL );
774
775 // Update timecode on the frame we're creating
776 mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
777
778 // Refresh the pixbuf
779 self->pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" );
780 self->pixbuf = mlt_cache_item_data( self->pixbuf_cache, NULL );
781 refresh_pixbuf( self, *frame );
782 mlt_cache_item_close( self->pixbuf_cache );
783
784 // Set producer-specific frame properties
785 mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( producer_properties, "progressive" ) );
786
787 double force_ratio = mlt_properties_get_double( producer_properties, "force_aspect_ratio" );
788 if ( force_ratio > 0.0 )
789 mlt_properties_set_double( properties, "aspect_ratio", force_ratio );
790 else
791 mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "aspect_ratio" ) );
792
793 // Push the get_image method
794 mlt_frame_push_get_image( *frame, producer_get_image );
795 }
796
797 // Calculate the next timecode
798 mlt_producer_prepare_next( producer );
799
800 return 0;
801 }
802
producer_close(mlt_producer parent)803 static void producer_close( mlt_producer parent )
804 {
805 producer_pixbuf self = parent->child;
806 parent->close = NULL;
807 mlt_service_cache_purge( MLT_PRODUCER_SERVICE(parent) );
808 mlt_producer_close( parent );
809 free( self->outs );
810 self->outs = NULL;
811 mlt_properties_close( self->filenames );
812 free( self );
813 }
814