static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Store the aspect ratio reported by the source mlt_deque_push_back_double( MLT_FRAME_IMAGE_STACK( frame ), mlt_frame_get_aspect_ratio( frame ) ); // Push this on to the service stack mlt_frame_push_service( frame, filter ); // Push the get_image method on to the stack mlt_frame_push_get_image( frame, filter_get_image ); return frame; }
static inline double smoothstep( const double e1, const double e2, const double a ) { if ( a < e1 ) return 0.0; if ( a > e2 ) return 1.0; double v = ( a - e1 ) / ( e2 - e1 ); return ( v * v * ( 3 - 2 * v ) ); } /** Get the images and apply the luminance of the mask to the alpha of the frame. */ static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { // Fetch the data from the stack (mix, mask, filter) double mix = mlt_deque_pop_back_double( MLT_FRAME_IMAGE_STACK( this ) ); mlt_frame mask = mlt_frame_pop_service( this ); mlt_filter filter = mlt_frame_pop_service( this ); // Obtain the constants double softness = mlt_properties_get_double( MLT_FILTER_PROPERTIES( filter ), "softness" ); int use_luminance = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "use_luminance" ); int invert = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "invert" ) * 255; // Render the frame *format = mlt_image_yuv422; if ( mlt_frame_get_image( this, image, format, width, height, writable ) == 0 && ( !use_luminance || ( int )mix != 1 ) ) { // Get the alpha mask of the source uint8_t *alpha = mlt_frame_get_alpha_mask( this );
static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int track ) { mlt_tractor self = parent->child; // We only respond to the first track requests if ( track == 0 && self->producer != NULL ) { int i = 0; int done = 0; mlt_frame temp = NULL; int count = 0; int image_count = 0; // Get the properties of the parent producer mlt_properties properties = MLT_PRODUCER_PROPERTIES( parent ); // Try to obtain the multitrack associated to the tractor mlt_multitrack multitrack = mlt_properties_get_data( properties, "multitrack", NULL ); // Or a specific producer mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL ); // Determine whether this tractor feeds to the consumer or stops here int global_feed = mlt_properties_get_int( properties, "global_feed" ); // If we don't have one, we're in trouble... if ( multitrack != NULL ) { // The output frame will hold the 'global' data feeds (ie: those which are targetted for the final frame) mlt_deque data_queue = mlt_deque_init( ); // Used to garbage collect all frames char label[ 30 ]; // Get the id of the tractor char *id = mlt_properties_get( properties, "_unique_id" ); // Will be used to store the frame properties object mlt_properties frame_properties = NULL; // We'll store audio and video frames to use here mlt_frame audio = NULL; mlt_frame video = NULL; mlt_frame first_video = NULL; // Temporary properties mlt_properties temp_properties = NULL; // Get the multitrack's producer mlt_producer target = MLT_MULTITRACK_PRODUCER( multitrack ); mlt_producer_seek( target, mlt_producer_frame( parent ) ); mlt_producer_set_speed( target, mlt_producer_get_speed( parent ) ); // We will create one frame and attach everything to it *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) ); // Get the properties of the frame frame_properties = MLT_FRAME_PROPERTIES( *frame ); // Loop through each of the tracks we're harvesting for ( i = 0; !done; i ++ ) { // Get a frame from the producer mlt_service_get_frame( self->producer, &temp, i ); // Get the temporary properties temp_properties = MLT_FRAME_PROPERTIES( temp ); // Pass all unique meta properties from the producer's frame to the new frame mlt_properties_lock( temp_properties ); int props_count = mlt_properties_count( temp_properties ); int j; for ( j = 0; j < props_count; j ++ ) { char *name = mlt_properties_get_name( temp_properties, j ); if ( !strncmp( name, "meta.", 5 ) && !mlt_properties_get( frame_properties, name ) ) mlt_properties_set( frame_properties, name, mlt_properties_get_value( temp_properties, j ) ); } mlt_properties_unlock( temp_properties ); // Copy the format conversion virtual functions if ( ! (*frame)->convert_image && temp->convert_image ) (*frame)->convert_image = temp->convert_image; if ( ! (*frame)->convert_audio && temp->convert_audio ) (*frame)->convert_audio = temp->convert_audio; // Check for last track done = mlt_properties_get_int( temp_properties, "last_track" ); // Handle fx only tracks if ( mlt_properties_get_int( temp_properties, "fx_cut" ) ) { int hide = ( video == NULL ? 1 : 0 ) | ( audio == NULL ? 2 : 0 ); mlt_properties_set_int( temp_properties, "hide", hide ); } // We store all frames with a destructor on the output frame sprintf( label, "_%s_%d", id, count ++ ); mlt_properties_set_data( frame_properties, label, temp, 0, ( mlt_destructor )mlt_frame_close, NULL ); // We want to append all 'final' feeds to the global queue if ( !done && mlt_properties_get_data( temp_properties, "data_queue", NULL ) != NULL ) { // Move the contents of this queue on to the output frames data queue mlt_deque sub_queue = mlt_properties_get_data( MLT_FRAME_PROPERTIES( temp ), "data_queue", NULL ); mlt_deque temp = mlt_deque_init( ); while ( global_feed && mlt_deque_count( sub_queue ) ) { mlt_properties p = mlt_deque_pop_back( sub_queue ); if ( mlt_properties_get_int( p, "final" ) ) mlt_deque_push_back( data_queue, p ); else mlt_deque_push_back( temp, p ); } while( mlt_deque_count( temp ) ) mlt_deque_push_front( sub_queue, mlt_deque_pop_back( temp ) ); mlt_deque_close( temp ); } // Now do the same with the global queue but without the conditional behaviour if ( mlt_properties_get_data( temp_properties, "global_queue", NULL ) != NULL ) { mlt_deque sub_queue = mlt_properties_get_data( MLT_FRAME_PROPERTIES( temp ), "global_queue", NULL ); while ( mlt_deque_count( sub_queue ) ) { mlt_properties p = mlt_deque_pop_back( sub_queue ); mlt_deque_push_back( data_queue, p ); } } // Pick up first video and audio frames if ( !done && !mlt_frame_is_test_audio( temp ) && !( mlt_properties_get_int( temp_properties, "hide" ) & 2 ) ) { // Order of frame creation is starting to get problematic if ( audio != NULL ) { mlt_deque_push_front( MLT_FRAME_AUDIO_STACK( temp ), producer_get_audio ); mlt_deque_push_front( MLT_FRAME_AUDIO_STACK( temp ), audio ); } audio = temp; } if ( !done && !mlt_frame_is_test_card( temp ) && !( mlt_properties_get_int( temp_properties, "hide" ) & 1 ) ) { if ( video != NULL ) { mlt_deque_push_front( MLT_FRAME_IMAGE_STACK( temp ), producer_get_image ); mlt_deque_push_front( MLT_FRAME_IMAGE_STACK( temp ), video ); } video = temp; if ( first_video == NULL ) first_video = temp; mlt_properties_set_int( MLT_FRAME_PROPERTIES( temp ), "image_count", ++ image_count ); image_count = 1; } } // Now stack callbacks if ( audio != NULL ) { mlt_frame_push_audio( *frame, audio ); mlt_frame_push_audio( *frame, producer_get_audio ); } if ( video != NULL ) { mlt_properties video_properties = MLT_FRAME_PROPERTIES( first_video ); mlt_frame_push_service( *frame, video ); mlt_frame_push_service( *frame, producer_get_image ); if ( global_feed ) mlt_properties_set_data( frame_properties, "data_queue", data_queue, 0, NULL, NULL ); mlt_properties_set_data( video_properties, "global_queue", data_queue, 0, destroy_data_queue, NULL ); mlt_properties_set_int( frame_properties, "width", mlt_properties_get_int( video_properties, "width" ) ); mlt_properties_set_int( frame_properties, "height", mlt_properties_get_int( video_properties, "height" ) ); mlt_properties_pass_list( frame_properties, video_properties, "meta.media.width, meta.media.height" ); mlt_properties_set_int( frame_properties, "progressive", mlt_properties_get_int( video_properties, "progressive" ) ); mlt_properties_set_double( frame_properties, "aspect_ratio", mlt_properties_get_double( video_properties, "aspect_ratio" ) ); mlt_properties_set_int( frame_properties, "image_count", image_count ); mlt_properties_set_data( frame_properties, "_producer", mlt_frame_get_original_producer( first_video ), 0, NULL, NULL ); } else { destroy_data_queue( data_queue ); } mlt_frame_set_position( *frame, mlt_producer_frame( parent ) ); mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_audio", audio == NULL ); mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_image", video == NULL ); } else if ( producer != NULL ) { mlt_producer_seek( producer, mlt_producer_frame( parent ) ); mlt_producer_set_speed( producer, mlt_producer_get_speed( parent ) ); mlt_service_get_frame( self->producer, frame, track ); } else { mlt_log( MLT_PRODUCER_SERVICE( parent ), MLT_LOG_ERROR, "tractor without a multitrack!!\n" ); mlt_service_get_frame( self->producer, frame, track ); } // Prepare the next frame mlt_producer_prepare_next( parent ); // Indicate our found status return 0; } else { // Generate a test card *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) ); return 0; } }
static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; // Get the properties from the frame mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); // Pop the top of stack now mlt_filter filter = mlt_frame_pop_service( frame ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); // Retrieve the aspect ratio double aspect_ratio = mlt_deque_pop_back_double( MLT_FRAME_IMAGE_STACK( frame ) ); double consumer_aspect = mlt_profile_sar( mlt_service_profile( MLT_FILTER_SERVICE( filter ) ) ); // Correct Width/height if necessary if ( *width == 0 || *height == 0 ) { *width = profile->width; *height = profile->height; } // Assign requested width/height from our subordinate int owidth = *width; int oheight = *height; // Check for the special case - no aspect ratio means no problem :-) if ( aspect_ratio == 0.0 ) aspect_ratio = consumer_aspect; // Reset the aspect ratio mlt_properties_set_double( properties, "aspect_ratio", aspect_ratio ); // XXX: This is a hack, but it forces the force_full_luma to apply by doing a RGB // conversion because range scaling only occurs on YUV->RGB. And we do it here, // after the deinterlace filter, which only operates in YUV to avoid a YUV->RGB->YUV->?. // Instead, it will go YUV->RGB->?. if ( mlt_properties_get_int( properties, "force_full_luma" ) ) *format = mlt_image_rgb24a; // Hmmm... char *rescale = mlt_properties_get( properties, "rescale.interp" ); if ( rescale != NULL && !strcmp( rescale, "none" ) ) return mlt_frame_get_image( frame, image, format, width, height, writable ); if ( mlt_properties_get_int( properties, "distort" ) == 0 ) { // Normalise the input and out display aspect int normalised_width = profile->width; int normalised_height = profile->height; int real_width = mlt_properties_get_int( properties, "meta.media.width" ); int real_height = mlt_properties_get_int( properties, "meta.media.height" ); if ( real_width == 0 ) real_width = mlt_properties_get_int( properties, "width" ); if ( real_height == 0 ) real_height = mlt_properties_get_int( properties, "height" ); double input_ar = aspect_ratio * real_width / real_height; double output_ar = consumer_aspect * owidth / oheight; // fprintf( stderr, "real %dx%d normalised %dx%d output %dx%d sar %f in-dar %f out-dar %f\n", // real_width, real_height, normalised_width, normalised_height, owidth, oheight, aspect_ratio, input_ar, output_ar); // Optimised for the input_ar > output_ar case (e.g. widescreen on standard) int scaled_width = rint( ( input_ar * normalised_width ) / output_ar ); int scaled_height = normalised_height; // Now ensure that our images fit in the output frame if ( scaled_width > normalised_width ) { scaled_width = normalised_width; scaled_height = rint( ( output_ar * normalised_height ) / input_ar ); } // Now calculate the actual image size that we want owidth = rint( scaled_width * owidth / normalised_width ); oheight = rint( scaled_height * oheight / normalised_height ); // Tell frame we have conformed the aspect to the consumer mlt_frame_set_aspect_ratio( frame, consumer_aspect ); } mlt_properties_set_int( properties, "distort", 0 ); // Now pass on the calculations down the line mlt_properties_set_int( properties, "resize_width", *width ); mlt_properties_set_int( properties, "resize_height", *height ); // If there will be padding, then we need packed image format. if ( *format == mlt_image_yuv420p ) { int iwidth = mlt_properties_get_int( properties, "width" ); int iheight = mlt_properties_get_int( properties, "height" ); if ( iwidth < owidth || iheight < oheight ) *format = mlt_image_yuv422; } // Now get the image if ( *format == mlt_image_yuv422 ) owidth -= owidth % 2; error = mlt_frame_get_image( frame, image, format, &owidth, &oheight, writable ); if ( error == 0 && *image ) { int bpp; mlt_image_format_size( *format, owidth, oheight, &bpp ); *image = frame_resize_image( frame, *width, *height, bpp ); } return error; }
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <framework/mlt_filter.h> #include <string.h> #include <framework/mlt_factory.h> #include <framework/mlt_frame.h> #include <framework/mlt_producer.h> #include <framework/mlt_geometry.h> /** Get the images and apply the luminance of the mask to the alpha of the frame. */ static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int use_alpha = mlt_deque_pop_back_int( MLT_FRAME_IMAGE_STACK( this ) ); int midpoint = mlt_deque_pop_back_int( MLT_FRAME_IMAGE_STACK( this ) ); int invert = mlt_deque_pop_back_int( MLT_FRAME_IMAGE_STACK( this ) ); // Render the frame *format = mlt_image_yuv422; if ( mlt_frame_get_image( this, image, format, width, height, writable ) == 0 ) { uint8_t *p = *image; uint8_t A = invert? 235 : 16; uint8_t B = invert? 16 : 235; int size = *width * *height; if ( !use_alpha ) { while( size -- )