void mlt_service_apply_filters( mlt_service self, mlt_frame frame, int index ) { int i; mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); mlt_properties service_properties = MLT_SERVICE_PROPERTIES( self ); mlt_service_base *base = self->local; mlt_position position = mlt_frame_get_position( frame ); mlt_position self_in = mlt_properties_get_position( service_properties, "in" ); mlt_position self_out = mlt_properties_get_position( service_properties, "out" ); if ( index == 0 || mlt_properties_get_int( service_properties, "_filter_private" ) == 0 ) { // Process the frame with the attached filters for ( i = 0; i < base->filter_count; i ++ ) { if ( base->filters[ i ] != NULL ) { mlt_position in = mlt_filter_get_in( base->filters[ i ] ); mlt_position out = mlt_filter_get_out( base->filters[ i ] ); int disable = mlt_properties_get_int( MLT_FILTER_PROPERTIES( base->filters[ i ] ), "disable" ); if ( !disable && ( ( in == 0 && out == 0 ) || ( position >= in && ( position <= out || out == 0 ) ) ) ) { mlt_properties_set_position( frame_properties, "in", in == 0 ? self_in : in ); mlt_properties_set_position( frame_properties, "out", out == 0 ? self_out : out ); mlt_filter_process( base->filters[ i ], frame ); mlt_service_apply_filters( MLT_FILTER_SERVICE( base->filters[ i ] ), frame, index + 1 ); } } } } }
static int filter_get_frame( mlt_service service, mlt_frame_ptr frame, int index ) { mlt_filter self = service->child; // Get coords in/out/track int track = mlt_filter_get_track( self ); int in = mlt_filter_get_in( self ); int out = mlt_filter_get_out( self ); // Get the producer this is connected to mlt_service producer = mlt_service_producer( &self->parent ); // If the frame request is for this filters track, we need to process it if ( index == track || track == -1 ) { int ret = mlt_service_get_frame( producer, frame, index ); if ( ret == 0 ) { mlt_position position = mlt_frame_get_position( *frame ); if ( position >= in && ( out == 0 || position <= out ) ) *frame = mlt_filter_process( self, *frame ); return 0; } else { *frame = mlt_frame_init( service ); return 0; } } else { return mlt_service_get_frame( producer, frame, index ); } }
static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) { // Get the filter properties mlt_properties filter_properties = MLT_FILTER_PROPERTIES( this ); // Get the frame properties mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); // Get the data queue mlt_deque data_queue = mlt_properties_get_data( frame_properties, "data_queue", NULL ); // Get the type of the data feed char *type = mlt_properties_get( filter_properties, "type" ); // Get the in and out points of this filter int in = mlt_filter_get_in( this ); int out = mlt_filter_get_out( this ); // Create the data queue if it doesn't exist if ( data_queue == NULL ) { // Create the queue data_queue = mlt_deque_init( ); // Assign it to the frame with the destructor mlt_properties_set_data( frame_properties, "data_queue", data_queue, 0, destroy_data_queue, NULL ); } // Now create the data feed if ( data_queue != NULL && type != NULL && !strcmp( type, "attr_check" ) ) {
/** Filter processing. */ static mlt_frame filter_process( mlt_filter this, mlt_frame frame ) { // Get the properties of the frame mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); // Get a unique name to store the frame position char *name = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "_unique_id" ); // Assign the frame out point to the filter (just in case we need it later) mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "_out", mlt_properties_get_int( properties, "out" ) ); // Assign the current position to the name mlt_properties_set_position( properties, name, mlt_frame_get_position( frame ) - mlt_filter_get_in( this ) ); // Push the filter on to the stack mlt_frame_push_service( frame, this ); // Push the get_image on to the stack mlt_frame_push_get_image( frame, filter_get_image ); return frame; } /** Constructor for the filter. */ mlt_filter filter_watermark_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) {
static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { // Get the filter mlt_filter filter = mlt_frame_pop_service( frame ); // Get the properties mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); // Get the image int error = 0; *format = mlt_image_rgb24a; // Only process if we have no error and a valid colour space if ( error == 0 ) { mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL ); mlt_transition transition = mlt_properties_get_data( properties, "transition", NULL ); mlt_frame a_frame = NULL; mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); if ( producer == NULL ) { char *background = mlt_properties_get( properties, "background" ); producer = mlt_factory_producer( profile, NULL, background ); mlt_properties_set_data( properties, "producer", producer, 0, (mlt_destructor)mlt_producer_close, NULL ); } if ( transition == NULL ) { transition = mlt_factory_transition( profile, "qtblend", NULL ); mlt_properties_set_data( properties, "transition", transition, 0, (mlt_destructor)mlt_transition_close, NULL ); if ( transition ) mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "b_alpha", 1 ); } if ( producer != NULL && transition != NULL ) { mlt_position position = mlt_filter_get_position( filter, frame ); mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); mlt_position in = mlt_filter_get_in( filter ); mlt_position out = mlt_filter_get_out( filter ); double consumer_ar = mlt_profile_sar( profile ); mlt_transition_set_in_and_out( transition, in, out ); if ( out > 0 ) { mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( producer ), "length", out - in + 1 ); mlt_producer_set_in_and_out( producer, in, out ); } mlt_producer_seek( producer, in + position ); mlt_frame_set_position( frame, position ); mlt_properties_pass( MLT_PRODUCER_PROPERTIES( producer ), properties, "producer." ); mlt_properties_pass( MLT_TRANSITION_PROPERTIES( transition ), properties, "transition." ); mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &a_frame, 0 ); mlt_frame_set_position( a_frame, in + position ); // Set the rescale interpolation to match the frame mlt_properties_set( MLT_FRAME_PROPERTIES( a_frame ), "rescale.interp", mlt_properties_get( frame_properties, "rescale.interp" ) ); // Special case - aspect_ratio = 0 if ( mlt_frame_get_aspect_ratio( frame ) == 0 ) mlt_frame_set_aspect_ratio( frame, consumer_ar ); if ( mlt_frame_get_aspect_ratio( a_frame ) == 0 ) mlt_frame_set_aspect_ratio( a_frame, consumer_ar ); // Add the qtblend transition onto the frame stack mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); mlt_transition_process( transition, a_frame, frame ); if ( mlt_properties_get_int( properties, "use_normalised" ) ) { // Use the normalised width & height mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); *width = profile->width; *height = profile->height; } mlt_frame_get_image( a_frame, image, format, width, height, writable ); mlt_properties_set_data( frame_properties, "affine_frame", a_frame, 0, (mlt_destructor)mlt_frame_close, NULL ); mlt_frame_set_image( frame, *image, *width * *height * 4, NULL ); //mlt_frame_set_alpha( frame, mlt_frame_get_alpha_mask( a_frame ), *width * *height, NULL ); } else { mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); } } return error; }
// Calculate this frames geometry output->x = lerp( ( in->x + ( out->x - in->x ) * position ) / ( float )out->nw * ow, 0, ow ); output->y = lerp( ( in->y + ( out->y - in->y ) * position ) / ( float )out->nh * oh, 0, oh ); output->w = lerp( ( in->w + ( out->w - in->w ) * position ) / ( float )out->nw * ow, 0, ow - output->x ); output->h = lerp( ( in->h + ( out->h - in->h ) * position ) / ( float )out->nh * oh, 0, oh - output->y ); output->mask_w = lerp( in->mask_w + ( out->mask_w - in->mask_w ) * position, 1, -1 ); output->mask_h = lerp( in->mask_h + ( out->mask_h - in->mask_h ) * position, 1, -1 ); } /** Calculate the position for this frame. */ static float position_calculate( mlt_filter this, mlt_frame frame ) { // Get the in and out position mlt_position in = mlt_filter_get_in( this ); mlt_position out = mlt_filter_get_out( this ); // Get the position of the frame mlt_position position = mlt_frame_get_position( frame ); // Now do the calcs return ( float )( position - in ) / ( float )( out - in + 1 ); } /** The averaging function... */ static inline void obscure_average( uint8_t *start, int width, int height, int stride ) { register int y;
static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties frame_props = MLT_FRAME_PROPERTIES( frame ); mlt_properties instance_props = mlt_properties_new(); // Only if mix is specified, otherwise a producer may set the mix if ( mlt_properties_get( properties, "start" ) != NULL ) { // Determine the time position of this frame in the filter duration mlt_properties props = mlt_properties_get_data( frame_props, "_producer", NULL ); int always_active = mlt_properties_get_int( properties, "always_active" ); mlt_position in = !always_active ? mlt_filter_get_in( filter ) : mlt_properties_get_int( props, "in" ); mlt_position out = !always_active ? mlt_filter_get_out( filter ) : mlt_properties_get_int( props, "out" ); int length = mlt_properties_get_int( properties, "length" ); mlt_position time = !always_active ? mlt_frame_get_position( frame ) : mlt_properties_get_int( props, "_frame" ); double mix = ( double )( time - in ) / ( double )( out - in + 1 ); if ( length == 0 ) { // If there is an end mix level adjust mix to the range if ( mlt_properties_get( properties, "end" ) != NULL ) { double start = mlt_properties_get_double( properties, "start" ); double end = mlt_properties_get_double( properties, "end" ); mix = start + ( end - start ) * mix; } // Use constant mix level if only start else if ( mlt_properties_get( properties, "start" ) != NULL ) { mix = mlt_properties_get_double( properties, "start" ); } // Use animated property "split" to get mix level if property is set char* split_property = mlt_properties_get( properties, "split" ); if ( split_property ) { mlt_position pos = mlt_filter_get_position( filter, frame ); mlt_position len = mlt_filter_get_length2( filter, frame ); mix = mlt_properties_anim_get_double( properties, "split", pos, len ); } // Convert it from [0, 1] to [-1, 1] mix = mix * 2.0 - 1.0; // Finally, set the mix property on the frame mlt_properties_set_double( instance_props, "mix", mix ); // Initialise filter previous mix value to prevent an inadvertant jump from 0 mlt_position last_position = mlt_properties_get_position( properties, "_last_position" ); mlt_position current_position = mlt_frame_get_position( frame ); mlt_properties_set_position( properties, "_last_position", current_position ); if ( mlt_properties_get( properties, "_previous_mix" ) == NULL || current_position != last_position + 1 ) mlt_properties_set_double( properties, "_previous_mix", mix ); // Tell the frame what the previous mix level was mlt_properties_set_double( instance_props, "previous_mix", mlt_properties_get_double( properties, "_previous_mix" ) ); // Save the current mix level for the next iteration mlt_properties_set_double( properties, "_previous_mix", mix ); } else { double level = mlt_properties_get_double( properties, "start" ); double mix_start = level; double mix_end = mix_start; double mix_increment = 1.0 / length; if ( time - in < length ) { mix_start *= ( double )( time - in ) / length; mix_end = mix_start + mix_increment; } else if ( time > out - length ) { mix_end = mix_start * ( ( double )( out - time - in ) / length ); mix_start = mix_end - mix_increment; } mix_start = mix_start < 0 ? 0 : mix_start > level ? level : mix_start; mix_end = mix_end < 0 ? 0 : mix_end > level ? level : mix_end; mlt_properties_set_double( instance_props, "previous_mix", mix_start ); mlt_properties_set_double( instance_props, "mix", mix_end ); } mlt_properties_set_int( instance_props, "channel", mlt_properties_get_int( properties, "channel" ) ); mlt_properties_set_int( instance_props, "gang", mlt_properties_get_int( properties, "gang" ) ); } mlt_properties_set_data( frame_props, mlt_properties_get( properties, "_unique_id" ), instance_props, 0, (mlt_destructor) mlt_properties_close, NULL ); // Override the get_audio method mlt_frame_push_audio( frame, filter ); mlt_frame_push_audio( frame, instance_props ); mlt_frame_push_audio( frame, filter_get_audio ); return frame; }