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