static int get_image_b( mlt_frame b_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_transition self = mlt_frame_pop_service( b_frame ); mlt_frame a_frame = mlt_frame_pop_frame( b_frame ); mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame ); mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame ); // Set scaling from A frame if not already provided. if ( !mlt_properties_get( b_props, "rescale.interp" ) ) { const char *rescale = mlt_properties_get( a_props, "rescale.interp" ); if ( !rescale || !strcmp( rescale, "none" ) ) rescale = "nearest"; mlt_properties_set( b_props, "rescale.interp", rescale ); } // Ensure sane aspect ratio if ( mlt_frame_get_aspect_ratio( b_frame ) == 0.0 ) mlt_frame_set_aspect_ratio( b_frame, mlt_profile_sar( mlt_service_profile( MLT_TRANSITION_SERVICE(self) ) ) ); mlt_properties_pass_list( b_props, a_props, "consumer_deinterlace, deinterlace_method, consumer_tff" ); return mlt_frame_get_image( b_frame, image, format, width, height, writable ); }
mlt_producer producer_consumer_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_producer self = mlt_producer_new( profile ); // Encapsulate the real producer mlt_profile temp_profile = mlt_profile_clone( profile ); temp_profile->is_explicit = 0; mlt_producer real_producer = mlt_factory_producer( temp_profile, NULL, arg ); if ( self && real_producer ) { // Override some producer methods self->close = ( mlt_destructor )producer_close; self->get_frame = get_frame; // Get the properties of this producer mlt_properties properties = MLT_PRODUCER_PROPERTIES( self ); mlt_properties_set( properties, "resource", arg ); mlt_properties_pass_list( properties, MLT_PRODUCER_PROPERTIES( real_producer ), "out, length" ); // Done with the producer - will re-open later when we have the profile property mlt_producer_close( real_producer ); } else { if ( self ) mlt_producer_close( self ); if ( real_producer ) mlt_producer_close( real_producer ); self = NULL; } mlt_profile_close( temp_profile ); return self; }
static mlt_consumer generate_consumer( mlt_consumer consumer, mlt_properties props, int index ) { mlt_profile profile = NULL; if ( mlt_properties_get( props, "mlt_profile" ) ) profile = mlt_profile_init( mlt_properties_get( props, "mlt_profile" ) ); if ( !profile ) profile = mlt_profile_clone( mlt_service_profile( MLT_CONSUMER_SERVICE(consumer) ) ); mlt_consumer nested = create_consumer( profile, mlt_properties_get( props, "mlt_service" ), mlt_properties_get( props, "target" ) ); if ( nested ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_properties nested_props = MLT_CONSUMER_PROPERTIES(nested); char key[30]; snprintf( key, sizeof(key), "%d.consumer", index ); mlt_properties_set_data( properties, key, nested, 0, (mlt_destructor) mlt_consumer_close, NULL ); snprintf( key, sizeof(key), "%d.profile", index ); mlt_properties_set_data( properties, key, profile, 0, (mlt_destructor) mlt_profile_close, NULL ); mlt_properties_set_int( nested_props, "put_mode", 1 ); mlt_properties_pass_list( nested_props, properties, "terminate_on_pause" ); mlt_properties_set( props, "consumer", NULL ); // set mlt_profile before other properties to facilitate presets mlt_properties_pass_list( nested_props, props, "mlt_profile" ); mlt_properties_inherit( nested_props, props ); attach_normalisers( profile, MLT_CONSUMER_SERVICE(nested) ); // Relay the first available consumer-frame-show event mlt_event event = mlt_properties_get_data( properties, "frame-show-event", NULL ); if ( !event ) { event = mlt_events_listen( nested_props, properties, "consumer-frame-show", (mlt_listener) on_frame_show ); mlt_properties_set_data( properties, "frame-show-event", event, 0, /*mlt_event_close*/ NULL, NULL ); } } else { mlt_profile_close( profile ); } return nested; }
static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) { if ( frame ) { // Construct a new frame *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); // Stack the producer and producer's get image mlt_frame_push_service_int( *frame, index ); mlt_frame_push_service( *frame, producer ); mlt_frame_push_service( *frame, framebuffer_get_image ); mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(*frame); // Get frame from the real producer mlt_frame first_frame = mlt_properties_get_data( properties, "first_frame", NULL ); if ( first_frame == NULL ) { // Get the frame to cache from the real producer mlt_producer real_producer = mlt_properties_get_data( properties, "producer", NULL ); // Seek the producer to the correct place mlt_producer_seek( real_producer, mlt_producer_position( producer ) ); // Get the frame mlt_service_get_frame( MLT_PRODUCER_SERVICE( real_producer ), &first_frame, index ); // Cache the frame mlt_properties_set_data( properties, "first_frame", first_frame, 0, ( mlt_destructor )mlt_frame_close, NULL ); } mlt_properties_inherit( frame_properties, MLT_FRAME_PROPERTIES(first_frame) ); double force_aspect_ratio = mlt_properties_get_double( properties, "force_aspect_ratio" ); if ( force_aspect_ratio <= 0.0 ) force_aspect_ratio = mlt_properties_get_double( properties, "aspect_ratio" ); mlt_properties_set_double( frame_properties, "aspect_ratio", force_aspect_ratio ); // Give the returned frame temporal identity mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); mlt_properties_set_int( frame_properties, "meta.media.width", mlt_properties_get_int( properties, "width" ) ); mlt_properties_set_int( frame_properties, "meta.media.height", mlt_properties_get_int( properties, "height" ) ); mlt_properties_pass_list( frame_properties, properties, "width, height" ); } return 0; }
static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_transition transition = mlt_properties_get_data(properties, "instance", NULL); char *name = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "transition"); if (!name || !strcmp("", name)) return frame; // Create the transition if needed. if (!transition || !mlt_properties_get(MLT_FILTER_PROPERTIES(transition), "mlt_service") || strcmp(name, mlt_properties_get(MLT_FILTER_PROPERTIES(transition), "mlt_service"))) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); transition = mlt_factory_transition(profile, name, NULL); mlt_properties_set_data(MLT_FILTER_PROPERTIES(filter), "instance", transition, 0, (mlt_destructor) mlt_transition_close, NULL); } if (transition) { mlt_properties transition_props = MLT_TRANSITION_PROPERTIES(transition); int type = mlt_properties_get_int(transition_props, "_transition_type"); int hide = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "hide"); mlt_properties_pass_list(transition_props, properties, "in out"); mlt_properties_pass(transition_props, properties, "transition." ); // Only if video transition on visible track. if ((type & 1) && !mlt_frame_is_test_card(frame) && !(hide & 1)) { mlt_frame_push_service_int(frame, mlt_image_format_id(mlt_properties_get(properties, "mlt_image_format"))); mlt_frame_push_service(frame, transition); mlt_frame_push_get_image(frame, get_image); } if (type == 0) mlt_properties_debug(transition_props, "unknown transition type", stderr); } else { mlt_properties_debug(properties, "mask_failed to create transition", stderr ); } return frame; }
static int consumer_start( mlt_consumer parent ) { consumer_sdl self = parent->child; if ( !self->running ) { // properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent ); mlt_properties play = MLT_CONSUMER_PROPERTIES( self->play ); mlt_properties still = MLT_CONSUMER_PROPERTIES( self->still ); char *window_id = mlt_properties_get( properties, "window_id" ); char *audio_driver = mlt_properties_get( properties, "audio_driver" ); char *video_driver = mlt_properties_get( properties, "video_driver" ); char *audio_device = mlt_properties_get( properties, "audio_device" ); char *output_display = mlt_properties_get( properties, "output_display" ); int progressive = mlt_properties_get_int( properties, "progressive" ) | mlt_properties_get_int( properties, "deinterlace" ); consumer_stop( parent ); self->running = 1; self->joined = 0; self->last_speed = 1; if ( output_display != NULL ) setenv( "DISPLAY", output_display, 1 ); if ( window_id != NULL ) setenv( "SDL_WINDOWID", window_id, 1 ); if ( video_driver != NULL ) setenv( "SDL_VIDEODRIVER", video_driver, 1 ); if ( audio_driver != NULL ) setenv( "SDL_AUDIODRIVER", audio_driver, 1 ); if ( audio_device != NULL ) setenv( "AUDIODEV", audio_device, 1 ); pthread_mutex_lock( &mlt_sdl_mutex ); int ret = SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ); pthread_mutex_unlock( &mlt_sdl_mutex ); if ( ret < 0 ) { fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() ); return -1; } SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL ); SDL_EnableUNICODE( 1 ); // Pass properties down mlt_properties_set_data( play, "transport_producer", mlt_properties_get_data( properties, "transport_producer", NULL ), 0, NULL, NULL ); mlt_properties_set_data( still, "transport_producer", mlt_properties_get_data( properties, "transport_producer", NULL ), 0, NULL, NULL ); mlt_properties_set_data( play, "transport_callback", mlt_properties_get_data( properties, "transport_callback", NULL ), 0, NULL, NULL ); mlt_properties_set_data( still, "transport_callback", mlt_properties_get_data( properties, "transport_callback", NULL ), 0, NULL, NULL ); mlt_properties_set_int( play, "progressive", progressive ); mlt_properties_set_int( still, "progressive", progressive ); mlt_properties_pass_list( play, properties, "deinterlace_method,resize,rescale,width,height,aspect_ratio,display_ratio,preview_off,preview_format,window_background" ",top_field_first,volume,real_time,buffer,prefill,audio_off,frequency,drop_max" ); mlt_properties_pass_list( still, properties, "deinterlace_method,resize,rescale,width,height,aspect_ratio,display_ratio,preview_off,preview_format,window_background" ",top_field_first"); mlt_properties_pass( play, properties, "play." ); mlt_properties_pass( still, properties, "still." ); mlt_properties_set_data( play, "app_lock", mlt_properties_get_data( properties, "app_lock", NULL ), 0, NULL, NULL ); mlt_properties_set_data( still, "app_lock", mlt_properties_get_data( properties, "app_lock", NULL ), 0, NULL, NULL ); mlt_properties_set_data( play, "app_unlock", mlt_properties_get_data( properties, "app_unlock", NULL ), 0, NULL, NULL ); mlt_properties_set_data( still, "app_unlock", mlt_properties_get_data( properties, "app_unlock", NULL ), 0, NULL, NULL ); mlt_properties_set_int( play, "put_mode", 1 ); mlt_properties_set_int( still, "put_mode", 1 ); mlt_properties_set_int( play, "terminate_on_pause", 1 ); // Start the still producer just to initialise the gui mlt_consumer_start( self->still ); self->active = self->still; // Inform child consumers that we control the sdl mlt_properties_set_int( play, "sdl_started", 1 ); mlt_properties_set_int( still, "sdl_started", 1 ); pthread_create( &self->thread, NULL, consumer_thread, self ); } return 0; }
mlt_producer producer_framebuffer_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { if ( !arg ) return NULL; mlt_producer producer = NULL; producer = calloc( 1, sizeof( struct mlt_producer_s ) ); mlt_producer_init( producer, NULL ); // Wrap loader mlt_producer real_producer; // Check if a speed was specified. /** * Speed must be appended to the filename with '?'. To play your video at 50%: melt framebuffer:my_video.mpg?0.5 * Stroboscope effect can be obtained by adding a stobe=x parameter, where x is the number of frames that will be ignored. * You can play the movie backwards by adding reverse=1 * You can freeze the clip at a determined position by adding freeze=frame_pos add freeze_after=1 to freeze only paste position or freeze_before to freeze before it **/ double speed = 0.0; char *props = strdup( arg ); char *ptr = strrchr( props, '?' ); if ( ptr ) { speed = atof( ptr + 1 ); if ( speed != 0.0 ) // If speed was valid, then strip it and the delimiter. // Otherwise, an invalid speed probably means this '?' was not a delimiter. *ptr = '\0'; } real_producer = mlt_factory_producer( profile, "abnormal", props ); free( props ); if (speed == 0.0) speed = 1.0; if ( producer != NULL && real_producer != NULL) { // Get the properties of this producer mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); mlt_properties_set( properties, "resource", arg); // Store the producer and fitler mlt_properties_set_data( properties, "producer", real_producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); // Grab some stuff from the real_producer mlt_properties_pass_list( properties, MLT_PRODUCER_PROPERTIES( real_producer ), "length, width, height, aspect_ratio" ); if ( speed < 0 ) { speed = -speed; mlt_properties_set_int( properties, "reverse", 1 ); } if ( speed != 1.0 ) { double real_length = ( (double) mlt_producer_get_length( real_producer ) ) / speed; mlt_properties_set_position( properties, "length", real_length ); } mlt_properties_set_position( properties, "out", mlt_producer_get_length( producer ) - 1 ); // Since we control the seeking, prevent it from seeking on its own mlt_producer_set_speed( real_producer, 0 ); mlt_producer_set_speed( producer, speed ); // Override the get_frame method producer->get_frame = producer_get_frame; } else { if ( producer ) mlt_producer_close( producer ); if ( real_producer ) mlt_producer_close( real_producer ); producer = NULL; } return producer; }
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 create_instance( mlt_transition transition, char *name, char *value, int count ) { // Return from this function int error = 0; // Duplicate the value char *type = strdup( value ); // Pointer to filter argument char *arg = type == NULL ? NULL : strchr( type, ':' ); // New filter being created mlt_filter filter = NULL; // Cleanup type and arg if ( arg != NULL ) *arg ++ = '\0'; // Create the filter mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) ); if ( type ) filter = mlt_factory_filter( profile, type, arg ); // If we have a filter, then initialise and store it if ( filter != NULL ) { // Properties of transition mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); // String to hold the property name char id[ 256 ]; // String to hold the passdown key char key[ 256 ]; // Construct id sprintf( id, "_filter_%d", count ); // Counstruct key sprintf( key, "%s.", name ); // Just in case, let's assume that the filter here has a composite //mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "composite.geometry", "0%/0%:100%x100%" ); //mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "composite.fill", 1 ); // Pass all the key properties on the filter down mlt_properties_pass( MLT_FILTER_PROPERTIES( filter ), properties, key ); mlt_properties_pass_list( MLT_FILTER_PROPERTIES( filter ), properties, "in, out, length" ); // Ensure that filter is assigned mlt_properties_set_data( properties, id, filter, 0, ( mlt_destructor )mlt_filter_close, NULL ); } else { // Indicate that an error has occurred error = 1; } // Cleanup free( type ); // Return error condition return error; }
// Image stack(able) method static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { // Get the filter object mlt_filter filter = mlt_frame_pop_service( frame ); // Get the filter's property object mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); // Get the frame properties mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); // Get the frame position mlt_position position = mlt_filter_get_position( filter, frame ); // Get the new image int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if( error != 0 ) mlt_properties_debug( frame_properties, "error after mlt_frame_get_image() in autotrack_rectangle", stderr ); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); // Get the geometry object mlt_geometry geometry = mlt_properties_get_data(filter_properties, "filter_geometry", NULL); // Get the current geometry item struct mlt_geometry_item_s boundry; mlt_geometry_fetch(geometry, &boundry, position); // Get the motion vectors struct motion_vector_s *vectors = mlt_properties_get_data( frame_properties, "motion_est.vectors", NULL ); // Cleanse the geometry item boundry.w = boundry.x < 0 ? boundry.w + boundry.x : boundry.w; boundry.h = boundry.y < 0 ? boundry.h + boundry.y : boundry.h; boundry.x = boundry.x < 0 ? 0 : boundry.x; boundry.y = boundry.y < 0 ? 0 : boundry.y; boundry.w = boundry.w < 0 ? 0 : boundry.w; boundry.h = boundry.h < 0 ? 0 : boundry.h; // How did the rectangle move? if( vectors != NULL && boundry.key != 1 ) // Paused? { int method = mlt_properties_get_int( filter_properties, "method" ); // Get the size of macroblocks in pixel units int macroblock_height = mlt_properties_get_int( frame_properties, "motion_est.macroblock_height" ); int macroblock_width = mlt_properties_get_int( frame_properties, "motion_est.macroblock_width" ); int mv_buffer_width = *width / macroblock_width; caculate_motion( vectors, &boundry, macroblock_width, macroblock_height, mv_buffer_width, method, *width, *height ); // Make the geometry object a real boy boundry.key = 1; boundry.f[0] = 1; boundry.f[1] = 1; boundry.f[2] = 1; boundry.f[3] = 1; boundry.f[4] = 1; mlt_geometry_insert(geometry, &boundry); mlt_geometry_interpolate(geometry); } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); if( mlt_properties_get_int( filter_properties, "debug" ) == 1 ) { init_arrows( format, *width, *height ); draw_rectangle_outline(*image, boundry.x, boundry.y, boundry.w, boundry.h, 100); } if( mlt_properties_get_int( filter_properties, "_serialize" ) == 1 ) { // Add the vector change to the list mlt_geometry key_frames = mlt_properties_get_data( filter_properties, "motion_vector_list", NULL ); if ( !key_frames ) { key_frames = mlt_geometry_init(); mlt_properties_set_data( filter_properties, "motion_vector_list", key_frames, 0, (mlt_destructor) mlt_geometry_close, (mlt_serialiser) mlt_geometry_serialise ); if ( key_frames ) mlt_geometry_set_length( key_frames, mlt_filter_get_length2( filter, frame ) ); } if ( key_frames ) { struct mlt_geometry_item_s item; item.frame = (int) mlt_frame_get_position( frame ); item.key = 1; item.x = boundry.x; item.y = boundry.y; item.w = boundry.w; item.h = boundry.h; item.mix = 0; item.f[0] = item.f[1] = item.f[2] = item.f[3] = 1; item.f[4] = 0; mlt_geometry_insert( key_frames, &item ); } } if( mlt_properties_get_int( filter_properties, "obscure" ) == 1 ) { mlt_filter obscure = mlt_properties_get_data( filter_properties, "_obscure", NULL ); mlt_properties_pass_list( MLT_FILTER_PROPERTIES(obscure), filter_properties, "in, out"); // Because filter_obscure needs to be rewritten to use mlt_geometry char geom[100]; sprintf( geom, "%d/%d:%dx%d", (int)boundry.x, (int)boundry.y, (int)boundry.w, (int)boundry.h ); mlt_properties_set( MLT_FILTER_PROPERTIES( obscure ), "start", geom ); mlt_properties_set( MLT_FILTER_PROPERTIES( obscure ), "end", geom ); } if( mlt_properties_get_int( filter_properties, "collect" ) == 1 ) { fprintf( stderr, "%d,%d,%d,%d\n", (int)boundry.x, (int)boundry.y, (int)boundry.w, (int)boundry.h ); fflush( stdout ); } return error; }
static int get_frame( mlt_producer self, mlt_frame_ptr frame, int index ) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); context cx = mlt_properties_get_data( properties, "context", NULL ); if ( !cx ) { // Allocate and initialize our context cx = mlt_pool_alloc( sizeof( struct context_s ) ); memset( cx, 0, sizeof( *cx ) ); mlt_properties_set_data( properties, "context", cx, 0, mlt_pool_release, NULL ); cx->self = self; char *profile_name = mlt_properties_get( properties, "profile" ); if ( !profile_name ) profile_name = mlt_properties_get( properties, "mlt_profile" ); mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( self ) ); if ( profile_name ) { cx->profile = mlt_profile_init( profile_name ); cx->profile->is_explicit = 1; } else { cx->profile = mlt_profile_clone( profile ); cx->profile->is_explicit = 0; } // Encapsulate a real producer for the resource cx->producer = mlt_factory_producer( cx->profile, NULL, mlt_properties_get( properties, "resource" ) ); if ( ( profile_name && !strcmp( profile_name, "auto" ) ) || mlt_properties_get_int( properties, "autoprofile" ) ) { mlt_profile_from_producer( cx->profile, cx->producer ); mlt_producer_close( cx->producer ); cx->producer = mlt_factory_producer( cx->profile, NULL, mlt_properties_get( properties, "resource" ) ); } // Since we control the seeking, prevent it from seeking on its own mlt_producer_set_speed( cx->producer, 0 ); cx->audio_position = -1; // We will encapsulate a consumer cx->consumer = mlt_consumer_new( cx->profile ); // Do not use _pass_list on real_time so that it defaults to 0 in the absence of // an explicit real_time property. mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( cx->consumer ), "real_time", mlt_properties_get_int( properties, "real_time" ) ); mlt_properties_pass_list( MLT_CONSUMER_PROPERTIES( cx->consumer ), properties, "buffer, prefill, deinterlace_method, rescale" ); // Connect it all together mlt_consumer_connect( cx->consumer, MLT_PRODUCER_SERVICE( cx->producer ) ); mlt_consumer_start( cx->consumer ); } // Generate a frame *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( self ) ); if ( *frame ) { // Seek the producer to the correct place // Calculate our positions double actual_position = (double) mlt_producer_frame( self ); if ( mlt_producer_get_speed( self ) != 0 ) actual_position *= mlt_producer_get_speed( self ); mlt_position need_first = floor( actual_position ); mlt_producer_seek( cx->producer, lrint( need_first * mlt_profile_fps( cx->profile ) / mlt_producer_get_fps( self ) ) ); // Get the nested frame mlt_frame nested_frame = mlt_consumer_rt_frame( cx->consumer ); // Stack the producer and our methods on the nested frame mlt_frame_push_service( *frame, nested_frame ); mlt_frame_push_service( *frame, cx ); mlt_frame_push_get_image( *frame, get_image ); mlt_frame_push_audio( *frame, nested_frame ); mlt_frame_push_audio( *frame, cx ); mlt_frame_push_audio( *frame, get_audio ); // Give the returned frame temporal identity mlt_frame_set_position( *frame, mlt_producer_position( self ) ); // Store the nested frame on the produced frame for destruction mlt_properties frame_props = MLT_FRAME_PROPERTIES( *frame ); mlt_properties_set_data( frame_props, "_producer_consumer.frame", nested_frame, 0, (mlt_destructor) mlt_frame_close, NULL ); // Inform the normalizers about our video properties mlt_properties_set_double( frame_props, "aspect_ratio", mlt_profile_sar( cx->profile ) ); mlt_properties_set_int( frame_props, "width", cx->profile->width ); mlt_properties_set_int( frame_props, "height", cx->profile->height ); mlt_properties_set_int( frame_props, "meta.media.width", cx->profile->width ); mlt_properties_set_int( frame_props, "meta.media.height", cx->profile->height ); mlt_properties_set_int( frame_props, "progressive", cx->profile->progressive ); } // Calculate the next timecode mlt_producer_prepare_next( self ); return 0; }