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