1 /*
2  * transition_region.c -- region transition
3  * Copyright (C) 2003-2017 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 "transition_region.h"
21 #include "transition_composite.h"
22 
23 #include <framework/mlt.h>
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
create_instance(mlt_transition transition,char * name,char * value,int count)29 static int create_instance( mlt_transition transition, char *name, char *value, int count )
30 {
31 	// Return from this function
32 	int error = 0;
33 
34 	// Duplicate the value
35 	char *type = strdup( value );
36 
37 	// Pointer to filter argument
38 	char *arg = type == NULL ? NULL : strchr( type, ':' );
39 
40 	// New filter being created
41 	mlt_filter filter = NULL;
42 
43 	// Cleanup type and arg
44 	if ( arg != NULL )
45 		*arg ++ = '\0';
46 
47 	// Create the filter
48 	mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) );
49 	if ( type )
50 		filter = mlt_factory_filter( profile, type, arg );
51 
52 	// If we have a filter, then initialise and store it
53 	if ( filter != NULL )
54 	{
55 		// Properties of transition
56 		mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
57 
58 		// String to hold the property name
59 		char id[ 256 ];
60 
61 		// String to hold the passdown key
62 		char key[ 256 ];
63 
64 		// Construct id
65 		sprintf( id, "_filter_%d", count );
66 
67 		// Counstruct key
68 		sprintf( key, "%s.", name );
69 
70 		// Just in case, let's assume that the filter here has a composite
71 		//mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "composite.geometry", "0%/0%:100%x100%" );
72 		//mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "composite.fill", 1 );
73 
74 		// Pass all the key properties on the filter down
75 		mlt_properties_pass( MLT_FILTER_PROPERTIES( filter ), properties, key );
76 		mlt_properties_pass_list( MLT_FILTER_PROPERTIES( filter ), properties, "in, out, length" );
77 
78 		// Ensure that filter is assigned
79 		mlt_properties_set_data( properties, id, filter, 0, ( mlt_destructor )mlt_filter_close, NULL );
80 	}
81 	else
82 	{
83 		// Indicate that an error has occurred
84 		error = 1;
85 	}
86 
87 	// Cleanup
88 	free( type );
89 
90 	// Return error condition
91 	return error;
92 }
93 
filter_get_alpha_mask(mlt_frame frame)94 static uint8_t *filter_get_alpha_mask( mlt_frame frame )
95 {
96 	uint8_t *alpha = NULL;
97 
98 	// Obtain properties of frame
99 	mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
100 
101 	// Get the shape frame
102 	mlt_frame shape_frame = mlt_properties_get_data( properties, "shape_frame", NULL );
103 
104 	// Get the width and height of the image
105 	int region_width = mlt_properties_get_int( properties, "width" );
106 	int region_height = mlt_properties_get_int( properties, "height" );
107 	uint8_t *image = NULL;
108 	mlt_image_format format = mlt_image_yuv422;
109 
110 	// Get the shape image to trigger alpha creation
111 	mlt_properties_set_int( MLT_FRAME_PROPERTIES( shape_frame ), "distort", 1 );
112 	mlt_frame_get_image( shape_frame, &image, &format, &region_width, &region_height, 0 );
113 
114 	alpha = mlt_frame_get_alpha_mask( shape_frame );
115 
116 	int size = region_width * region_height;
117 	uint8_t *alpha_duplicate = mlt_pool_alloc( size );
118 
119 	// Generate from the Y component of the image if no alpha available
120 	if ( alpha == NULL )
121 	{
122 		alpha = alpha_duplicate;
123 		while ( size -- )
124 		{
125 			*alpha ++ = ( int )( ( ( *image ++ - 16 ) * 299 ) / 255 );
126 			image ++;
127 		}
128 	}
129 	else
130 	{
131 		memcpy( alpha_duplicate, alpha, size );
132 	}
133 	mlt_frame_set_alpha( frame, alpha_duplicate, region_width * region_height, mlt_pool_release );
134 
135 	return alpha_duplicate;
136 }
137 
138 /** Do it :-).
139 */
140 
transition_get_image(mlt_frame frame,uint8_t ** image,mlt_image_format * format,int * width,int * height,int writable)141 static int transition_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
142 {
143 	// Error we will return
144 	int error = 0;
145 
146 	// We will get the 'b frame' from the frame stack
147 	mlt_frame b_frame = mlt_frame_pop_frame( frame );
148 
149 	// Get the watermark transition object
150 	mlt_transition transition = mlt_frame_pop_service( frame );
151 
152 	// Get the properties of the transition
153 	mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
154 
155 	// Get the properties of the a frame
156 	mlt_properties a_props = MLT_FRAME_PROPERTIES( frame );
157 
158 	mlt_service_lock( MLT_TRANSITION_SERVICE( transition ) );
159 
160 	// Get the composite from the transition
161 	mlt_transition composite = mlt_properties_get_data( properties, "composite", NULL );
162 
163 	// Look for the first filter
164 	mlt_filter filter = mlt_properties_get_data( properties, "_filter_0", NULL );
165 
166 	// Get the position
167 	mlt_position position = mlt_transition_get_position( transition, frame );
168 
169 	// Create a composite if we don't have one
170 	if ( composite == NULL )
171 	{
172 		// Create composite via the factory
173 		mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) );
174 		composite = mlt_factory_transition( profile, "composite", NULL );
175 
176 		// If we have one
177 		if ( composite != NULL )
178 		{
179 			// Get the properties
180 			mlt_properties composite_properties = MLT_TRANSITION_PROPERTIES( composite );
181 
182 			// We want to ensure that we don't get a wobble...
183 			//mlt_properties_set_int( composite_properties, "distort", 1 );
184 			mlt_properties_set_int( composite_properties, "progressive", 1 );
185 
186 			// Pass all the composite. properties on the transition down
187 			mlt_properties_pass( composite_properties, properties, "composite." );
188 
189 			// Register the composite for reuse/destruction
190 			mlt_properties_set_data( properties, "composite", composite, 0, ( mlt_destructor )mlt_transition_close, NULL );
191 		}
192 	}
193 	else
194 	{
195 		// Pass all current properties down
196 		mlt_properties composite_properties = MLT_TRANSITION_PROPERTIES( composite );
197 		mlt_properties_pass( composite_properties, properties, "composite." );
198 	}
199 
200 	// Create filters
201 	if ( filter == NULL )
202 	{
203 		// Loop Variable
204 		int i = 0;
205 
206 		// Number of filters created
207 		int count = 0;
208 
209 		// Loop for all properties
210 		for ( i = 0; i < mlt_properties_count( properties ); i ++ )
211 		{
212 			// Get the name of this property
213 			char *name = mlt_properties_get_name( properties, i );
214 
215 			// If the name does not contain a . and matches filter
216 			if ( strchr( name, '.' ) == NULL && !strncmp( name, "filter", 6 ) )
217 			{
218 				// Get the filter constructor
219 				char *value = mlt_properties_get_value( properties, i );
220 
221 				// Create an instance
222 				if ( create_instance( transition, name, value, count ) == 0 )
223 					count ++;
224 			}
225 		}
226 
227 		// Look for the first filter again
228 		filter = mlt_properties_get_data( properties, "_filter_0", NULL );
229 	}
230 	else
231 	{
232 		// Pass all properties down
233 		mlt_filter temp = NULL;
234 
235 		// Loop Variable
236 		int i = 0;
237 
238 		// Number of filters found
239 		int count = 0;
240 
241 		// Loop for all properties
242 		for ( i = 0; i < mlt_properties_count( properties ); i ++ )
243 		{
244 			// Get the name of this property
245 			char *name = mlt_properties_get_name( properties, i );
246 
247 			// If the name does not contain a . and matches filter
248 			if ( strchr( name, '.' ) == NULL && !strncmp( name, "filter", 6 ) )
249 			{
250 				// Strings to hold the id and pass down key
251 				char id[ 256 ];
252 				char key[ 256 ];
253 
254 				// Construct id and key
255 				sprintf( id, "_filter_%d", count );
256 				sprintf( key, "%s.", name );
257 
258 				// Get the filter
259 				temp = mlt_properties_get_data( properties, id, NULL );
260 
261 				if ( temp != NULL )
262 				{
263 					mlt_properties_pass( MLT_FILTER_PROPERTIES( temp ), properties, key );
264 					count ++;
265 				}
266 			}
267 		}
268 	}
269 
270 	mlt_properties_set_int( a_props, "width", *width );
271 	mlt_properties_set_int( a_props, "height", *height );
272 
273 	// Only continue if we have both filter and composite
274 	if ( composite != NULL )
275 	{
276 		// Get the resource of this filter (could be a shape [rectangle/circle] or an alpha provider of choice
277 		const char *resource =  mlt_properties_get( properties, "resource" );
278 
279 		// Get the old resource in case it's changed
280 		char *old_resource =  mlt_properties_get( properties, "_old_resource" );
281 
282 		// String to hold the filter to query on
283 		char id[ 256 ];
284 
285 		// Index to hold the count
286 		int i = 0;
287 
288 		// We will get the 'b frame' from the composite only if it's NULL (region filter)
289 		if ( b_frame == NULL )
290 		{
291 			// Copy the region
292 			b_frame = composite_copy_region( composite, frame, position );
293 
294 			// Ensure a destructor
295 			char name[64];
296 			snprintf( name, sizeof(name), "region %s", mlt_properties_get( properties, "_unique_id" ) );
297 			mlt_properties_set_data( a_props, name, b_frame, 0, ( mlt_destructor )mlt_frame_close, NULL );
298 		}
299 
300 		// Properties of the B frame
301 		mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
302 
303 		// filter_only prevents copying the alpha channel of the shape to the output frame
304 		// by compositing filtered frame over itself
305 		if ( mlt_properties_get_int( properties, "filter_only" ) )
306 		{
307 			char name[64];
308 			snprintf( name, sizeof(name), "region %s", mlt_properties_get( properties, "_unique_id" ) );
309 			frame = composite_copy_region( composite, b_frame, position );
310 			mlt_properties_set_data( b_props, name, frame, 0, ( mlt_destructor )mlt_frame_close, NULL );
311 		}
312 
313 		// Make sure the filter is in the correct position
314 		while ( filter != NULL )
315 		{
316 			// Stack this filter
317 			if ( mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "off" ) == 0 )
318 				mlt_filter_process( filter, b_frame );
319 
320 			// Generate the key for the next
321 			sprintf( id, "_filter_%d", ++ i );
322 
323 			// Get the next filter
324 			filter = mlt_properties_get_data( properties, id, NULL );
325 		}
326 
327 		// Allow filters to be attached to a region filter
328 		filter = mlt_properties_get_data( properties, "_region_filter", NULL );
329 		if ( filter != NULL )
330 			mlt_service_apply_filters( MLT_FILTER_SERVICE( filter ), b_frame, 0 );
331 
332 		// Hmm - this is probably going to go wrong....
333 		mlt_frame_set_position( frame, position );
334 
335 		// Get the b frame and process with composite if successful
336 		mlt_transition_process( composite, frame, b_frame );
337 
338 		// If we have a shape producer copy the alpha mask from the shape frame to the b_frame
339 		if ( strcmp( resource, "rectangle" ) != 0 )
340 		{
341 			// Get the producer from the transition
342 			mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL );
343 
344 			// If We have no producer then create one
345 			if ( producer == NULL || ( old_resource != NULL && strcmp( resource, old_resource ) ) )
346 			{
347 				// Get the factory producer service
348 				char *factory = mlt_properties_get( properties, "factory" );
349 
350 				// Store the old resource
351 				mlt_properties_set( properties, "_old_resource", resource );
352 
353 				// Special case circle resource
354 				if ( strcmp( resource, "circle" ) == 0 )
355 					resource = "pixbuf:<svg width='100' height='100'><circle cx='50' cy='50' r='50' fill='black'/></svg>";
356 
357 				// Create the producer
358 				mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) );
359 				producer = mlt_factory_producer( profile, factory, resource );
360 
361 				// If we have one
362 				if ( producer != NULL )
363 				{
364 					// Get the producer properties
365 					mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
366 
367 					// Ensure that we loop
368 					mlt_properties_set( producer_properties, "eof", "loop" );
369 
370 					// Now pass all producer. properties on the transition down
371 					mlt_properties_pass( producer_properties, properties, "producer." );
372 
373 					// Register the producer for reuse/destruction
374 					mlt_properties_set_data( properties, "producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
375 				}
376 			}
377 
378 			// Now use the shape producer
379 			if ( producer != NULL )
380 			{
381 				// We will get the alpha frame from the producer
382 				mlt_frame shape_frame = NULL;
383 
384 				// Make sure the producer is in the correct position
385 				mlt_producer_seek( producer, position );
386 
387 				// Get the shape frame
388 				if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &shape_frame, 0 ) == 0 )
389 				{
390 					// Ensure that the shape frame will be closed
391 					mlt_properties_set_data( b_props, "shape_frame", shape_frame, 0, ( mlt_destructor )mlt_frame_close, NULL );
392 
393 					// Specify the callback for evaluation
394 					b_frame->get_alpha_mask = filter_get_alpha_mask;
395 				}
396 			}
397 		}
398 
399 		// Get the image
400 		error = mlt_frame_get_image( frame, image, format, width, height, 0 );
401 	}
402 
403 	mlt_service_unlock( MLT_TRANSITION_SERVICE( transition ) );
404 
405 	return error;
406 }
407 
408 /** Filter processing.
409 */
410 
transition_process(mlt_transition transition,mlt_frame a_frame,mlt_frame b_frame)411 static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
412 {
413 	// Push the transition on to the frame
414 	mlt_frame_push_service( a_frame, transition );
415 
416 	// Push the b_frame on to the stack
417 	mlt_frame_push_frame( a_frame, b_frame );
418 
419 	// Push the transition method
420 	mlt_frame_push_get_image( a_frame, transition_get_image );
421 
422 	// Return the frame
423 	return a_frame;
424 }
425 
426 /** Constructor for the transition.
427 */
428 
transition_region_init(mlt_profile profile,mlt_service_type type,const char * id,char * arg)429 mlt_transition transition_region_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
430 {
431 	// Create a new transition
432 	mlt_transition transition = mlt_transition_new( );
433 
434 	// Further initialisation
435 	if ( transition != NULL )
436 	{
437 		// Get the properties from the transition
438 		mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
439 
440 		// Assign the transition process method
441 		transition->process = transition_process;
442 
443 		// Default factory
444 		mlt_properties_set( properties, "factory", mlt_environment( "MLT_PRODUCER" ) );
445 
446 		// Resource defines the shape of the region
447 		mlt_properties_set( properties, "resource", arg == NULL ? "rectangle" : arg );
448 
449 		// Inform apps and framework that this is a video only transition
450 		mlt_properties_set_int( properties, "_transition_type", 1 );
451 	}
452 
453 	// Return the transition
454 	return transition;
455 }
456 
457