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