int mlt_service_detach( mlt_service self, mlt_filter filter ) { int error = self == NULL || filter == NULL; if ( error == 0 ) { int i = 0; mlt_service_base *base = self->local; mlt_properties properties = MLT_SERVICE_PROPERTIES( self ); for ( i = 0; i < base->filter_count; i ++ ) if ( base->filters[ i ] == filter ) break; if ( i < base->filter_count ) { base->filters[ i ] = NULL; for ( i ++ ; i < base->filter_count; i ++ ) base->filters[ i - 1 ] = base->filters[ i ]; base->filter_count --; mlt_events_disconnect( MLT_FILTER_PROPERTIES( filter ), self ); mlt_filter_close( filter ); mlt_events_fire( properties, "service-changed", NULL ); } } return error; }
static int consumer_play_video( consumer_sdl self, mlt_frame frame ) { // Get the properties of this consumer mlt_properties properties = self->properties; mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); return 0; }
int mlt_service_move_filter( mlt_service self, int from, int to ) { int error = -1; if ( self ) { mlt_service_base *base = self->local; if ( from < 0 ) from = 0; if ( from >= base->filter_count ) from = base->filter_count - 1; if ( to < 0 ) to = 0; if ( to >= base->filter_count ) to = base->filter_count - 1; if ( from != to && base->filter_count > 1 ) { mlt_filter filter = base->filters[from]; int i; if ( from > to ) { for ( i = from; i > to; i-- ) base->filters[i] = base->filters[i - 1]; } else { for ( i = from; i < to; i++ ) base->filters[i] = base->filters[i + 1]; } base->filters[to] = filter; mlt_events_fire( MLT_SERVICE_PROPERTIES(self), "service-changed", NULL ); error = 0; } } return error; }
void Controller::seek(int position) { setVolume(m_volume, false); if (m_producer) { // Always pause before seeking (if not already paused). if (!Settings.playerGPU()) if (m_consumer && m_consumer->is_valid() && m_producer->get_speed() != 0) { // Disable real_time behavior and buffering for frame accurate seeking. m_consumer->set("real_time", -1); m_consumer->set("buffer", 0); m_consumer->set("prefill", 0); } m_producer->set_speed(0); m_producer->seek(position); if (m_consumer && m_consumer->is_valid()) { if (m_consumer->is_stopped()) { m_consumer->start(); } else { m_consumer->purge(); refreshConsumer(Settings.playerScrubAudio()); } } } if (m_jackFilter) mlt_events_fire(m_jackFilter->get_properties(), "jack-seek", &position, NULL); }
void consumer_frame_show_cb( mlt_consumer sdl, mlt_consumer parent, mlt_frame frame ) { consumer_sdl self = parent->child; self->last_speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ); self->last_position = mlt_frame_get_position( frame ); mlt_events_fire( MLT_CONSUMER_PROPERTIES( parent ), "consumer-frame-show", frame, NULL ); }
static int consumer_play_video( consumer_sdl self, mlt_frame frame ) { // Get the properties of this consumer mlt_properties properties = self->properties; if ( self->running && !mlt_consumer_is_stopped( &self->parent ) ) mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); return 0; }
static void *consumer_thread( void *arg ) { // Map the argument to the object mlt_consumer consumer = arg; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // Convenience functionality int terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" ); int terminated = 0; // Frame and size mlt_frame frame = NULL; // Loop while running while( !terminated && mlt_properties_get_int( properties, "_running" ) ) { // Get the frame frame = mlt_consumer_rt_frame( consumer ); // Check for termination if ( terminate_on_pause && frame != NULL ) terminated = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0; // Check that we have a frame to work with if ( frame ) { avsync_stats* stats = mlt_properties_get_data( properties, "_stats", NULL ); double fps = mlt_properties_get_double( properties, "fps" ); mlt_position pos = mlt_frame_get_position( frame ); if( !strcmp( mlt_properties_get( properties, "report" ), "frame" ) ) { stats->report_frames = 1; } else { stats->report_frames = 0; } detect_flash( frame, pos, fps, stats ); detect_blip( frame, pos, fps, stats ); calculate_sync( stats ); report_results( stats, pos ); // Close the frame mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); mlt_frame_close( frame ); } } // Indicate that the consumer is stopped mlt_properties_set_int( properties, "_running", 0 ); mlt_consumer_stopped( consumer ); return NULL; }
int mlt_service_attach( mlt_service self, mlt_filter filter ) { int error = self == NULL || filter == NULL; if ( error == 0 ) { int i = 0; mlt_properties properties = MLT_SERVICE_PROPERTIES( self ); mlt_service_base *base = self->local; for ( i = 0; error == 0 && i < base->filter_count; i ++ ) if ( base->filters[ i ] == filter ) error = 1; if ( error == 0 ) { if ( base->filter_count == base->filter_size ) { base->filter_size += 10; base->filters = realloc( base->filters, base->filter_size * sizeof( mlt_filter ) ); } if ( base->filters != NULL ) { mlt_properties props = MLT_FILTER_PROPERTIES( filter ); mlt_properties_inc_ref( MLT_FILTER_PROPERTIES( filter ) ); base->filters[ base->filter_count ++ ] = filter; mlt_properties_set_data( props, "service", self, 0, NULL, NULL ); mlt_events_fire( properties, "service-changed", NULL ); mlt_events_fire( props, "service-changed", NULL ); mlt_service cp = mlt_properties_get_data( properties, "_cut_parent", NULL ); if ( cp ) mlt_events_fire( MLT_SERVICE_PROPERTIES(cp), "service-changed", NULL ); mlt_events_listen( props, self, "service-changed", ( mlt_listener )mlt_service_filter_changed ); mlt_events_listen( props, self, "property-changed", ( mlt_listener )mlt_service_filter_property_changed ); } else { error = 2; } } } return error; }
mlt_transition mlt_factory_transition( mlt_profile profile, const char *service, const void *input ) { mlt_transition obj = NULL; // Offer the application the chance to 'create' mlt_events_fire( event_object, "transition-create-request", service, input, &obj, NULL ); if ( obj == NULL ) { obj = mlt_repository_create( repository, profile, transition_type, service, input ); mlt_events_fire( event_object, "transition-create-done", service, input, obj, NULL ); } if ( obj != NULL ) { mlt_properties properties = MLT_TRANSITION_PROPERTIES( obj ); set_common_properties( properties, profile, "transition", service ); } return obj; }
void Controller::seek(int position) { if (m_producer) { m_producer->set_speed(0); if (m_consumer && m_consumer->is_valid()) m_consumer->purge(); m_producer->seek(position); } if (m_jackFilter) mlt_events_fire(m_jackFilter->get_properties(), "jack-seek", &position, NULL); refreshConsumer(); }
mlt_consumer mlt_factory_consumer( mlt_profile profile, const char *service, const void *input ) { mlt_consumer obj = NULL; if ( service == NULL ) service = mlt_environment( "MLT_CONSUMER" ); // Offer the application the chance to 'create' mlt_events_fire( event_object, "consumer-create-request", service, input, &obj, NULL ); if ( obj == NULL ) { obj = mlt_repository_create( repository, profile, consumer_type, service, input ); mlt_events_fire( event_object, "consumer-create-done", service, input, obj, NULL ); } if ( obj != NULL ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( obj ); set_common_properties( properties, profile, "consumer", service ); } return obj; }
static int jack_sync( jack_transport_state_t state, jack_position_t *jack_pos, void *arg ) { mlt_filter filter = (mlt_filter) arg; mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE(filter) ); mlt_position position = mlt_profile_fps( profile ) * jack_pos->frame / jack_pos->frame_rate + 0.5; int result = 1; mlt_log_debug( MLT_FILTER_SERVICE(filter), "%s frame %u rate %u pos %d last_pos %d\n", JACKSTATE(state), jack_pos->frame, jack_pos->frame_rate, position, mlt_properties_get_position( properties, "_last_pos" ) ); if ( state == JackTransportStopped ) { mlt_events_fire( properties, "jack-stopped", &position, NULL ); mlt_properties_set_int( properties, "_sync_guard", 0 ); } else if ( state == JackTransportStarting ) { result = 0; if ( !mlt_properties_get_int( properties, "_sync_guard" ) ) { mlt_properties_set_int( properties, "_sync_guard", 1 ); mlt_events_fire( properties, "jack-started", &position, NULL ); } else if ( position >= mlt_properties_get_position( properties, "_last_pos" ) - 2 ) { mlt_properties_set_int( properties, "_sync_guard", 0 ); result = 1; } } else { mlt_properties_set_int( properties, "_sync_guard", 0 ); } return result; }
mlt_producer mlt_factory_producer( mlt_profile profile, const char *service, const void *resource ) { mlt_producer obj = NULL; // Pick up the default normalising producer if necessary if ( service == NULL ) service = mlt_environment( "MLT_PRODUCER" ); // Offer the application the chance to 'create' mlt_events_fire( event_object, "producer-create-request", service, resource, &obj, NULL ); // Try to instantiate via the specified service if ( obj == NULL ) { obj = mlt_repository_create( repository, profile, producer_type, service, resource ); mlt_events_fire( event_object, "producer-create-done", service, resource, obj, NULL ); if ( obj != NULL ) { mlt_properties properties = MLT_PRODUCER_PROPERTIES( obj ); set_common_properties( properties, profile, "producer", service ); } } return obj; }
static void *run( void *arg ) { // Map the argument to the object DeckLinkConsumer* decklink = (DeckLinkConsumer*) arg; mlt_consumer consumer = decklink->getConsumer(); // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // Convenience functionality int terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" ); int terminated = 0; // Frame and size mlt_frame frame = NULL; // Loop while running while ( !terminated && mlt_properties_get_int( properties, "running" ) ) { // Get the frame frame = mlt_consumer_rt_frame( consumer ); // Check for termination if ( terminate_on_pause && frame ) terminated = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0; // Check that we have a frame to work with if ( frame ) { decklink->render( frame ); if ( !decklink->isBuffering() ) decklink->wait(); // Close the frame mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); mlt_frame_close( frame ); } } // Indicate that the consumer is stopped decklink->stop(); mlt_properties_set_int( properties, "running", 0 ); mlt_consumer_stopped( consumer ); return NULL; }
static void on_jack_started( mlt_properties owner, mlt_consumer consumer, mlt_position *position ) { mlt_producer producer = mlt_properties_get_data( MLT_CONSUMER_PROPERTIES(consumer), "transport_producer", NULL ); if ( producer ) { if ( mlt_producer_get_speed( producer ) != 0 ) { mlt_properties jack = mlt_properties_get_data( MLT_CONSUMER_PROPERTIES( consumer ), "jack_filter", NULL ); mlt_events_fire( jack, "jack-stop", NULL ); } else { mlt_producer_set_speed( producer, 1 ); mlt_consumer_purge( consumer ); mlt_producer_seek( producer, *position ); mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "refresh", 1 ); } } }
int mlt_field_plant_transition( mlt_field self, mlt_transition that, int a_track, int b_track ) { // Connect the transition to the last producer int result = mlt_transition_connect( that, self->producer, a_track, b_track ); // If successful, then we'll use self for connecting in the future if ( result == 0 ) { // This is now the new producer self->producer = MLT_TRANSITION_SERVICE( that ); // Reconnect tractor to new producer mlt_tractor_connect( self->tractor, self->producer ); // Fire an event mlt_events_fire( mlt_field_properties( self ), "service-changed", NULL ); } return result; }
int mlt_field_plant_filter( mlt_field self, mlt_filter that, int track ) { // Connect the filter to the last producer int result = mlt_filter_connect( that, self->producer, track ); // If successful, then we'll use this for connecting in the future if ( result == 0 ) { // This is now the new producer self->producer = MLT_FILTER_SERVICE( that ); // Reconnect tractor to new producer mlt_tractor_connect( self->tractor, self->producer ); // Fire an event mlt_events_fire( mlt_field_properties( self ), "service-changed", NULL ); } return result; }
void mlt_field_disconnect_service( mlt_field self, mlt_service service ) { mlt_service p = mlt_service_producer( service ); mlt_service c = mlt_service_consumer( service); int i; switch ( mlt_service_identify(c) ) { case filter_type: i = mlt_filter_get_track( MLT_FILTER(c) ); mlt_service_connect_producer( c, p, i ); break; case transition_type: i = mlt_transition_get_a_track ( MLT_TRANSITION(c) ); mlt_service_connect_producer( c, p, i ); MLT_TRANSITION(c)->producer = p; break; case tractor_type: self->producer = p; mlt_tractor_connect( MLT_TRACTOR(c), p ); default: break; } mlt_events_fire( mlt_field_properties( self ), "service-changed", NULL ); }
static int consumer_play_video( consumer_sdl self, mlt_frame frame ) { // Get the properties of this consumer mlt_properties properties = self->properties; // mlt_image_format vfmt = mlt_properties_get_int( properties, "mlt_image_format" ); mlt_image_format vfmt = mlt_image_yuv422; int width = self->width, height = self->height; uint8_t *image; int video_off = mlt_properties_get_int( properties, "video_off" ); int preview_off = mlt_properties_get_int( properties, "preview_off" ); int display_off = video_off | preview_off; if ( self->running && !display_off ) { // Get the image, width and height mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 ); if ( self->running ) { // Determine window's new display aspect ratio int x = mlt_properties_get_int( properties, "window_width" ); if ( x && x != self->window_width ) self->window_width = x; x = mlt_properties_get_int( properties, "window_height" ); if ( x && x != self->window_height ) self->window_height = x; double this_aspect = ( double )self->window_width / self->window_height; // Get the display aspect ratio double display_ratio = mlt_properties_get_double( properties, "display_ratio" ); // Determine frame's display aspect ratio double frame_aspect = mlt_frame_get_aspect_ratio( frame ) * width / height; // Store the width and height received self->width = width; self->height = height; // If using hardware scaler if ( mlt_properties_get( properties, "rescale" ) != NULL && !strcmp( mlt_properties_get( properties, "rescale" ), "none" ) ) { // Use hardware scaler to normalise display aspect ratio self->sdl_rect.w = frame_aspect / this_aspect * self->window_width; self->sdl_rect.h = self->window_height; if ( self->sdl_rect.w > self->window_width ) { self->sdl_rect.w = self->window_width; self->sdl_rect.h = this_aspect / frame_aspect * self->window_height; } } // Special case optimisation to negate odd effect of sample aspect ratio // not corresponding exactly with image resolution. else if ( (int)( this_aspect * 1000 ) == (int)( display_ratio * 1000 ) ) { self->sdl_rect.w = self->window_width; self->sdl_rect.h = self->window_height; } // Use hardware scaler to normalise sample aspect ratio else if ( self->window_height * display_ratio > self->window_width ) { self->sdl_rect.w = self->window_width; self->sdl_rect.h = self->window_width / display_ratio; } else { self->sdl_rect.w = self->window_height * display_ratio; self->sdl_rect.h = self->window_height; } self->sdl_rect.x = ( self->window_width - self->sdl_rect.w ) / 2; self->sdl_rect.y = ( self->window_height - self->sdl_rect.h ) / 2; self->sdl_rect.x -= self->sdl_rect.x % 2; mlt_properties_set_int( self->properties, "rect_x", self->sdl_rect.x ); mlt_properties_set_int( self->properties, "rect_y", self->sdl_rect.y ); mlt_properties_set_int( self->properties, "rect_w", self->sdl_rect.w ); mlt_properties_set_int( self->properties, "rect_h", self->sdl_rect.h ); } if ( self->running && image ) { unsigned char* planes[4]; int strides[4]; mlt_image_format_planes( vfmt, width, height, image, planes, strides ); if ( strides[1] ) { SDL_UpdateYUVTexture( self->sdl_texture, NULL, planes[0], strides[0], planes[1], strides[1], planes[2], strides[2] ); } else { SDL_UpdateTexture( self->sdl_texture, NULL, planes[0], strides[0] ); } SDL_RenderClear( self->sdl_renderer ); SDL_RenderCopy( self->sdl_renderer, self->sdl_texture, NULL, &self->sdl_rect ); SDL_RenderPresent( self->sdl_renderer ); } mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); } else if ( self->running ) { if ( !video_off ) { mlt_image_format preview_format = mlt_properties_get_int( properties, "preview_format" ); vfmt = preview_format == mlt_image_none ? mlt_image_rgb24a : preview_format; mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 ); } mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); } return 0; }
static void consumer_sdl_event_cb( mlt_consumer sdl, mlt_consumer parent, SDL_Event *event ) { mlt_events_fire( MLT_CONSUMER_PROPERTIES( parent ), "consumer-sdl-event", event, NULL ); }
static void *consumer_thread( void *arg ) { // Identify the arg consumer_sdl self = arg; // Get the consumer mlt_consumer consumer = &self->parent; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // internal intialization mlt_frame frame = NULL; int last_position = -1; int eos = 0; int eos_threshold = 20; if ( self->play ) eos_threshold = eos_threshold + mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( self->play ), "buffer" ); // Determine if the application is dealing with the preview int preview_off = mlt_properties_get_int( properties, "preview_off" ); pthread_mutex_lock( &self->refresh_mutex ); self->refresh_count = 0; pthread_mutex_unlock( &self->refresh_mutex ); // Loop until told not to while( self->running ) { // Get a frame from the attached producer frame = mlt_consumer_get_frame( consumer ); // Ensure that we have a frame if ( self->running && frame != NULL ) { // Get the speed of the frame double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ); // Lock during the operation mlt_service_lock( MLT_CONSUMER_SERVICE( consumer ) ); // Get refresh request for the current frame int refresh = mlt_properties_get_int( properties, "refresh" ); // Decrement refresh and clear changed mlt_events_block( properties, properties ); mlt_properties_set_int( properties, "refresh", 0 ); mlt_events_unblock( properties, properties ); // Unlock after the operation mlt_service_unlock( MLT_CONSUMER_SERVICE( consumer ) ); // Set the changed property on this frame for the benefit of still mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "refresh", refresh ); // Make sure the recipient knows that this frame isn't really rendered mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 0 ); // Optimisation to reduce latency if ( speed == 1.0 ) { if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) ) mlt_consumer_purge( self->play ); last_position = mlt_frame_get_position( frame ); } else { //mlt_consumer_purge( self->play ); last_position = -1; } // If we aren't playing normally, then use the still if ( speed != 1 ) { mlt_producer producer = MLT_PRODUCER( mlt_service_get_producer( MLT_CONSUMER_SERVICE( consumer ) ) ); mlt_position duration = producer? mlt_producer_get_playtime( producer ) : -1; int pause = 0; #ifndef SKIP_WAIT_EOS if ( self->active == self->play ) { // Do not interrupt the play consumer near the end if ( duration - self->last_position > eos_threshold ) { // Get a new frame at the sought position mlt_frame_close( frame ); if ( producer ) mlt_producer_seek( producer, self->last_position ); frame = mlt_consumer_get_frame( consumer ); pause = 1; } else { // Send frame with speed 0 to stop it if ( frame && !mlt_consumer_is_stopped( self->play ) ) { mlt_consumer_put_frame( self->play, frame ); frame = NULL; eos = 1; } // Check for end of stream if ( mlt_consumer_is_stopped( self->play ) ) { // Stream has ended mlt_log_verbose( MLT_CONSUMER_SERVICE( consumer ), "END OF STREAM\n" ); pause = 1; eos = 0; // reset eos indicator } else { // Prevent a tight busy loop struct timespec tm = { 0, 100000L }; // 100 usec nanosleep( &tm, NULL ); } } } #else pause = self->active == self->play; #endif if ( pause ) { // Start the still consumer if ( !mlt_consumer_is_stopped( self->play ) ) mlt_consumer_stop( self->play ); self->last_speed = speed; self->active = self->still; self->ignore_change = 0; mlt_consumer_start( self->still ); } // Send the frame to the active child if ( frame && !eos ) { mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "refresh", 1 ); if ( self->active ) mlt_consumer_put_frame( self->active, frame ); } if ( pause && speed == 0.0 ) { mlt_events_fire( properties, "consumer-sdl-paused", NULL ); } } // Allow a little grace time before switching consumers on speed changes else if ( self->ignore_change -- > 0 && self->active != NULL && !mlt_consumer_is_stopped( self->active ) ) { mlt_consumer_put_frame( self->active, frame ); } // Otherwise use the normal player else { if ( !mlt_consumer_is_stopped( self->still ) ) mlt_consumer_stop( self->still ); if ( mlt_consumer_is_stopped( self->play ) ) { self->last_speed = speed; self->active = self->play; self->ignore_change = 0; mlt_consumer_start( self->play ); } if ( self->play ) mlt_consumer_put_frame( self->play, frame ); } // Copy the rectangle info from the active consumer if ( self->running && preview_off == 0 && self->active ) { mlt_properties active = MLT_CONSUMER_PROPERTIES( self->active ); mlt_service_lock( MLT_CONSUMER_SERVICE( consumer ) ); mlt_properties_set_int( properties, "rect_x", mlt_properties_get_int( active, "rect_x" ) ); mlt_properties_set_int( properties, "rect_y", mlt_properties_get_int( active, "rect_y" ) ); mlt_properties_set_int( properties, "rect_w", mlt_properties_get_int( active, "rect_w" ) ); mlt_properties_set_int( properties, "rect_h", mlt_properties_get_int( active, "rect_h" ) ); mlt_service_unlock( MLT_CONSUMER_SERVICE( consumer ) ); } if ( self->active == self->still ) { pthread_mutex_lock( &self->refresh_mutex ); if ( self->running && speed == 0 && self->refresh_count <= 0 ) { mlt_events_fire( properties, "consumer-sdl-paused", NULL ); pthread_cond_wait( &self->refresh_cond, &self->refresh_mutex ); } self->refresh_count --; pthread_mutex_unlock( &self->refresh_mutex ); } } else { if ( frame ) mlt_frame_close( frame ); mlt_consumer_put_frame( self->active, NULL ); self->running = 0; } } if ( self->play ) mlt_consumer_stop( self->play ); if ( self->still ) mlt_consumer_stop( self->still ); return NULL; }
int main( int argc, char **argv ) { int i; mlt_consumer consumer = NULL; FILE *store = NULL; char *name = NULL; mlt_profile profile = NULL; int is_progress = 0; int is_silent = 0; mlt_profile backup_profile; // Handle abnormal exit situations. signal( SIGSEGV, abnormal_exit_handler ); signal( SIGILL, abnormal_exit_handler ); signal( SIGABRT, abnormal_exit_handler ); // Construct the factory mlt_repository repo = mlt_factory_init( NULL ); #if defined(WIN32) && !defined(MELT_NOSDL) is_silent = 1; #endif for ( i = 1; i < argc; i ++ ) { // Check for serialisation switch if ( !strcmp( argv[ i ], "-serialise" ) ) { name = argv[ ++ i ]; if ( name != NULL && strstr( name, ".melt" ) ) store = fopen( name, "w" ); else { if ( name == NULL || name[0] == '-' ) store = stdout; name = NULL; } } // Look for the profile option else if ( !strcmp( argv[ i ], "-profile" ) ) { const char *pname = argv[ ++ i ]; if ( pname && pname[0] != '-' ) profile = mlt_profile_init( pname ); } else if ( !strcmp( argv[ i ], "-progress" ) ) { is_progress = 1; } else if ( !strcmp( argv[ i ], "-progress2" ) ) { is_progress = 2; } // Look for the query option else if ( !strcmp( argv[ i ], "-query" ) ) { const char *pname = argv[ ++ i ]; if ( pname && pname[0] != '-' ) { if ( !strcmp( pname, "consumers" ) || !strcmp( pname, "consumer" ) ) query_services( repo, consumer_type ); else if ( !strcmp( pname, "filters" ) || !strcmp( pname, "filter" ) ) query_services( repo, filter_type ); else if ( !strcmp( pname, "producers" ) || !strcmp( pname, "producer" ) ) query_services( repo, producer_type ); else if ( !strcmp( pname, "transitions" ) || !strcmp( pname, "transition" ) ) query_services( repo, transition_type ); else if ( !strcmp( pname, "profiles" ) || !strcmp( pname, "profile" ) ) query_profiles(); else if ( !strcmp( pname, "presets" ) || !strcmp( pname, "preset" ) ) query_presets(); else if ( !strncmp( pname, "format", 6 ) ) query_formats(); else if ( !strncmp( pname, "acodec", 6 ) || !strcmp( pname, "audio_codecs" ) ) query_acodecs(); else if ( !strncmp( pname, "vcodec", 6 ) || !strcmp( pname, "video_codecs" ) ) query_vcodecs(); else if ( !strncmp( pname, "consumer=", 9 ) ) query_metadata( repo, consumer_type, "consumer", strchr( pname, '=' ) + 1 ); else if ( !strncmp( pname, "filter=", 7 ) ) query_metadata( repo, filter_type, "filter", strchr( pname, '=' ) + 1 ); else if ( !strncmp( pname, "producer=", 9 ) ) query_metadata( repo, producer_type, "producer", strchr( pname, '=' ) + 1 ); else if ( !strncmp( pname, "transition=", 11 ) ) query_metadata( repo, transition_type, "transition", strchr( pname, '=' ) + 1 ); else if ( !strncmp( pname, "profile=", 8 ) ) query_profile( strchr( pname, '=' ) + 1 ); else if ( !strncmp( pname, "preset=", 7 ) ) query_preset( strchr( pname, '=' ) + 1 ); else goto query_all; } else { query_all: query_services( repo, consumer_type ); query_services( repo, filter_type ); query_services( repo, producer_type ); query_services( repo, transition_type ); fprintf( stdout, "# You can query the metadata for a specific service using:\n" "# -query <type>=<identifer>\n" "# where <type> is one of: consumer, filter, producer, or transition.\n" ); } goto exit_factory; } else if ( !strcmp( argv[ i ], "-silent" ) ) { is_silent = 1; } else if ( !strcmp( argv[ i ], "-verbose" ) ) { mlt_log_set_level( MLT_LOG_VERBOSE ); } else if ( !strcmp( argv[ i ], "-version" ) || !strcmp( argv[ i ], "--version" ) ) { fprintf( stdout, "%s " VERSION "\n" "Copyright (C) 2002-2013 Ushodaya Enterprises Limited\n" "<http://www.mltframework.org/>\n" "This is free software; see the source for copying conditions. There is NO\n" "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", basename( argv[0] ) ); goto exit_factory; } else if ( !strcmp( argv[ i ], "-debug" ) ) { mlt_log_set_level( MLT_LOG_DEBUG ); } } if ( !is_silent && !isatty( STDIN_FILENO ) && !is_progress ) is_progress = 1; // Create profile if not set explicitly if ( getenv( "MLT_PROFILE" ) ) profile = mlt_profile_init( NULL ); if ( profile == NULL ) profile = mlt_profile_init( NULL ); else profile->is_explicit = 1; // Look for the consumer option to load profile settings from consumer properties backup_profile = mlt_profile_clone( profile ); load_consumer( &consumer, profile, argc, argv ); // If the consumer changed the profile, then it is explicit. if ( backup_profile && !profile->is_explicit && ( profile->width != backup_profile->width || profile->height != backup_profile->height || profile->sample_aspect_num != backup_profile->sample_aspect_num || profile->sample_aspect_den != backup_profile->sample_aspect_den || profile->frame_rate_den != backup_profile->frame_rate_den || profile->frame_rate_num != backup_profile->frame_rate_num || profile->colorspace != backup_profile->colorspace ) ) profile->is_explicit = 1; mlt_profile_close( backup_profile ); // Get melt producer if ( argc > 1 ) melt = mlt_factory_producer( profile, "melt", &argv[ 1 ] ); if ( melt ) { // Generate an automatic profile if needed. if ( ! profile->is_explicit ) { mlt_profile_from_producer( profile, melt ); mlt_producer_close( melt ); melt = mlt_factory_producer( profile, "melt", &argv[ 1 ] ); } // Reload the consumer with the fully qualified profile. // The producer or auto-profile could have changed the profile. load_consumer( &consumer, profile, argc, argv ); // See if producer has consumer already attached if ( !store && !consumer ) { consumer = MLT_CONSUMER( mlt_service_consumer( MLT_PRODUCER_SERVICE( melt ) ) ); if ( consumer ) { mlt_properties_inc_ref( MLT_CONSUMER_PROPERTIES(consumer) ); // because we explicitly close it mlt_properties_set_data( MLT_CONSUMER_PROPERTIES(consumer), "transport_callback", transport_action, 0, NULL, NULL ); } } // If we have no consumer, default to sdl if ( store == NULL && consumer == NULL ) consumer = create_consumer( profile, NULL ); } // Set transport properties on consumer and produder if ( consumer != NULL && melt != NULL ) { mlt_properties_set_data( MLT_CONSUMER_PROPERTIES( consumer ), "transport_producer", melt, 0, NULL, NULL ); mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( melt ), "transport_consumer", consumer, 0, NULL, NULL ); if ( is_progress ) mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "progress", is_progress ); if ( is_silent ) mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "silent", is_silent ); } if ( argc > 1 && melt != NULL && mlt_producer_get_length( melt ) > 0 ) { // Parse the arguments for ( i = 1; i < argc; i ++ ) { if ( !strcmp( argv[ i ], "-jack" ) ) { setup_jack_transport( consumer, profile ); } else if ( !strcmp( argv[ i ], "-serialise" ) ) { if ( store != stdout ) i ++; } else { if ( store != NULL ) fprintf( store, "%s\n", argv[ i ] ); i ++; while ( argv[ i ] != NULL && argv[ i ][ 0 ] != '-' ) { if ( store != NULL ) fprintf( store, "%s\n", argv[ i ] ); i += 1; } i --; } } if ( consumer != NULL && store == NULL ) { // Get melt's properties mlt_properties melt_props = MLT_PRODUCER_PROPERTIES( melt ); // Get the last group mlt_properties group = mlt_properties_get_data( melt_props, "group", 0 ); // Apply group settings mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); mlt_properties_inherit( properties, group ); // Connect consumer to melt mlt_consumer_connect( consumer, MLT_PRODUCER_SERVICE( melt ) ); // Start the consumer mlt_events_listen( properties, consumer, "consumer-fatal-error", ( mlt_listener )on_fatal_error ); if ( mlt_consumer_start( consumer ) == 0 ) { // Try to exit gracefully upon these signals signal( SIGINT, stop_handler ); signal( SIGTERM, stop_handler ); #ifndef WIN32 signal( SIGHUP, stop_handler ); signal( SIGPIPE, stop_handler ); #endif // Transport functionality transport( melt, consumer ); // Stop the consumer mlt_consumer_stop( consumer ); } } else if ( store != NULL && store != stdout && name != NULL ) { fprintf( stderr, "Project saved as %s.\n", name ); fclose( store ); } } else { show_usage( argv[0] ); } // Disconnect producer from consumer to prevent ref cycles from closing services if ( consumer ) { mlt_consumer_connect( consumer, NULL ); mlt_events_fire( MLT_CONSUMER_PROPERTIES(consumer), "consumer-cleanup", NULL); } // Close the producer if ( melt != NULL ) mlt_producer_close( melt ); // Close the consumer if ( consumer != NULL ) mlt_consumer_close( consumer ); // Close the factory mlt_profile_close( profile ); exit_factory: // Workaround qmelt on OS X from crashing at exit. #if !defined(__MACH__) || !defined(QT_GUI_LIB) mlt_factory_close( ); #endif return 0; }
static void mlt_service_filter_property_changed( mlt_service owner, mlt_service self, char *name ) { mlt_events_fire( MLT_SERVICE_PROPERTIES( self ), "property-changed", name, NULL ); }
void mlt_consumer_stopped( mlt_consumer self ) { mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( self ), "running", 0 ); mlt_events_fire( MLT_CONSUMER_PROPERTIES( self ), "consumer-stopped", NULL ); mlt_event_unblock( self->event_listener ); }
static void transport_action( mlt_producer producer, char *value ) { mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); mlt_multitrack multitrack = mlt_properties_get_data( properties, "multitrack", NULL ); mlt_consumer consumer = mlt_properties_get_data( properties, "transport_consumer", NULL ); mlt_properties jack = mlt_properties_get_data( MLT_CONSUMER_PROPERTIES( consumer ), "jack_filter", NULL ); mlt_position position = producer? mlt_producer_position( producer ) : 0; mlt_properties_set_int( properties, "stats_off", 1 ); if ( strlen( value ) == 1 ) { switch( value[ 0 ] ) { case 'q': case 'Q': mlt_properties_set_int( properties, "done", 1 ); mlt_events_fire( jack, "jack-stop", NULL ); break; case '0': position = 0; mlt_producer_set_speed( producer, 1 ); mlt_producer_seek( producer, position ); mlt_consumer_purge( consumer ); mlt_events_fire( jack, "jack-seek", &position, NULL ); break; case '1': mlt_producer_set_speed( producer, -10 ); break; case '2': mlt_producer_set_speed( producer, -5 ); break; case '3': mlt_producer_set_speed( producer, -2 ); break; case '4': mlt_producer_set_speed( producer, -1 ); break; case '5': mlt_producer_set_speed( producer, 0 ); mlt_consumer_purge( consumer ); mlt_producer_seek( producer, mlt_consumer_position( consumer ) + 1 ); mlt_events_fire( jack, "jack-stop", NULL ); break; case '6': case ' ': if ( !jack || mlt_producer_get_speed( producer ) != 0 ) mlt_producer_set_speed( producer, 1 ); mlt_consumer_purge( consumer ); mlt_events_fire( jack, "jack-start", NULL ); break; case '7': mlt_producer_set_speed( producer, 2 ); break; case '8': mlt_producer_set_speed( producer, 5 ); break; case '9': mlt_producer_set_speed( producer, 10 ); break; case 'd': if ( multitrack != NULL ) { int i = 0; mlt_position last = -1; fprintf( stderr, "\n" ); for ( i = 0; 1; i ++ ) { position = mlt_multitrack_clip( multitrack, mlt_whence_relative_start, i ); if ( position == last ) break; last = position; fprintf( stderr, "%d: %d\n", i, (int)position ); } } break; case 'g': if ( multitrack != NULL ) { position = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 0 ); mlt_producer_seek( producer, position ); mlt_consumer_purge( consumer ); mlt_events_fire( jack, "jack-seek", &position, NULL ); } break; case 'H': if ( producer != NULL ) { position -= mlt_producer_get_fps( producer ) * 60; mlt_consumer_purge( consumer ); mlt_producer_seek( producer, position ); mlt_events_fire( jack, "jack-seek", &position, NULL ); } break; case 'h': if ( producer != NULL ) { position--; mlt_producer_set_speed( producer, 0 ); mlt_consumer_purge( consumer ); mlt_producer_seek( producer, position ); mlt_events_fire( jack, "jack-stop", NULL ); mlt_events_fire( jack, "jack-seek", &position, NULL ); } break; case 'j': if ( multitrack != NULL ) { position = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 1 ); mlt_consumer_purge( consumer ); mlt_producer_seek( producer, position ); mlt_events_fire( jack, "jack-seek", &position, NULL ); } break; case 'k': if ( multitrack != NULL ) { position = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, -1 ); mlt_consumer_purge( consumer ); mlt_producer_seek( producer, position ); mlt_events_fire( jack, "jack-seek", &position, NULL ); } break; case 'l': if ( producer != NULL ) { position++; mlt_consumer_purge( consumer ); if ( mlt_producer_get_speed( producer ) != 0 ) { mlt_producer_set_speed( producer, 0 ); mlt_events_fire( jack, "jack-stop", NULL ); } else { mlt_producer_seek( producer, position ); mlt_events_fire( jack, "jack-seek", &position, NULL ); } } break; case 'L': if ( producer != NULL ) { position += mlt_producer_get_fps( producer ) * 60; mlt_consumer_purge( consumer ); mlt_producer_seek( producer, position ); mlt_events_fire( jack, "jack-seek", &position, NULL ); } break; } mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "refresh", 1 ); } mlt_properties_set_int( properties, "stats_off", 0 ); }
static void mlt_producer_service_changed( mlt_service owner, mlt_producer self ) { mlt_events_fire( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( self ) ), "producer-changed", NULL ); }
static void mlt_producer_property_changed( mlt_service owner, mlt_producer self, char *name ) { if ( !strcmp( name, "in" ) || !strcmp( name, "out" ) || !strcmp( name, "length" ) ) mlt_events_fire( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( self ) ), "producer-changed", NULL ); }
static void *consumer_read_ahead_thread( void *arg ) { // The argument is the consumer mlt_consumer self = arg; // Get the properties of the consumer mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); // Get the width and height int width = mlt_properties_get_int( properties, "width" ); int height = mlt_properties_get_int( properties, "height" ); // See if video is turned off int video_off = mlt_properties_get_int( properties, "video_off" ); int preview_off = mlt_properties_get_int( properties, "preview_off" ); int preview_format = mlt_properties_get_int( properties, "preview_format" ); // Get the audio settings mlt_audio_format afmt = mlt_audio_s16; const char *format = mlt_properties_get( properties, "mlt_audio_format" ); if ( format ) { if ( !strcmp( format, "none" ) ) afmt = mlt_audio_none; else if ( !strcmp( format, "s32" ) ) afmt = mlt_audio_s32; else if ( !strcmp( format, "s32le" ) ) afmt = mlt_audio_s32le; else if ( !strcmp( format, "float" ) ) afmt = mlt_audio_float; else if ( !strcmp( format, "f32le" ) ) afmt = mlt_audio_f32le; else if ( !strcmp( format, "u8" ) ) afmt = mlt_audio_u8; } int counter = 0; double fps = mlt_properties_get_double( properties, "fps" ); int channels = mlt_properties_get_int( properties, "channels" ); int frequency = mlt_properties_get_int( properties, "frequency" ); int samples = 0; void *audio = NULL; // See if audio is turned off int audio_off = mlt_properties_get_int( properties, "audio_off" ); // Get the maximum size of the buffer int buffer = mlt_properties_get_int( properties, "buffer" ) + 1; // General frame variable mlt_frame frame = NULL; uint8_t *image = NULL; // Time structures struct timeval ante; // Average time for get_frame and get_image int count = 0; int skipped = 0; int64_t time_process = 0; int skip_next = 0; mlt_position pos = 0; mlt_position start_pos = 0; mlt_position last_pos = 0; int frame_duration = mlt_properties_get_int( properties, "frame_duration" ); int drop_max = mlt_properties_get_int( properties, "drop_max" ); if ( preview_off && preview_format != 0 ) self->format = preview_format; // Get the first frame frame = mlt_consumer_get_frame( self ); if ( frame ) { // Get the image of the first frame if ( !video_off ) { mlt_events_fire( MLT_CONSUMER_PROPERTIES( self ), "consumer-frame-render", frame, NULL ); mlt_frame_get_image( frame, &image, &self->format, &width, &height, 0 ); } if ( !audio_off ) { samples = mlt_sample_calculator( fps, frequency, counter++ ); mlt_frame_get_audio( frame, &audio, &afmt, &frequency, &channels, &samples ); } // Mark as rendered mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 ); last_pos = start_pos = pos = mlt_frame_get_position( frame ); } // Get the starting time (can ignore the times above) gettimeofday( &ante, NULL ); // Continue to read ahead while ( self->ahead ) { // Put the current frame into the queue pthread_mutex_lock( &self->queue_mutex ); while( self->ahead && mlt_deque_count( self->queue ) >= buffer ) pthread_cond_wait( &self->queue_cond, &self->queue_mutex ); mlt_deque_push_back( self->queue, frame ); pthread_cond_broadcast( &self->queue_cond ); pthread_mutex_unlock( &self->queue_mutex ); // Get the next frame frame = mlt_consumer_get_frame( self ); // If there's no frame, we're probably stopped... if ( frame == NULL ) continue; pos = mlt_frame_get_position( frame ); // Increment the counter used for averaging processing cost count ++; // All non-normal playback frames should be shown if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "_speed" ) != 1 ) { #ifdef DEINTERLACE_ON_NOT_NORMAL_SPEED mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace", 1 ); #endif // Indicate seeking or trick-play start_pos = pos; } // If skip flag not set or frame-dropping disabled if ( !skip_next || self->real_time == -1 ) { if ( !video_off ) { // Reset width/height - could have been changed by previous mlt_frame_get_image width = mlt_properties_get_int( properties, "width" ); height = mlt_properties_get_int( properties, "height" ); // Get the image mlt_events_fire( MLT_CONSUMER_PROPERTIES( self ), "consumer-frame-render", frame, NULL ); mlt_frame_get_image( frame, &image, &self->format, &width, &height, 0 ); } // Indicate the rendered image is available. mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 ); // Reset consecutively-skipped counter skipped = 0; } else // Skip image processing { // Increment the number of consecutively-skipped frames skipped++; // If too many (1 sec) consecutively-skipped frames if ( skipped > drop_max ) { // Reset cost tracker time_process = 0; count = 1; mlt_log_verbose( self, "too many frames dropped - forcing next frame\n" ); } } // Always process audio if ( !audio_off ) { samples = mlt_sample_calculator( fps, frequency, counter++ ); mlt_frame_get_audio( frame, &audio, &afmt, &frequency, &channels, &samples ); } // Get the time to process this frame int64_t time_current = time_difference( &ante ); // If the current time is not suddenly some large amount if ( time_current < time_process / count * 20 || !time_process || count < 5 ) { // Accumulate the cost for processing this frame time_process += time_current; } else { mlt_log_debug( self, "current %"PRId64" threshold %"PRId64" count %d\n", time_current, (int64_t) (time_process / count * 20), count ); // Ignore the cost of this frame's time count--; } // Determine if we started, resumed, or seeked if ( pos != last_pos + 1 ) start_pos = pos; last_pos = pos; // Do not skip the first 20% of buffer at start, resume, or seek if ( pos - start_pos <= buffer / 5 + 1 ) { // Reset cost tracker time_process = 0; count = 1; } // Reset skip flag skip_next = 0; // Only consider skipping if the buffer level is low (or really small) if ( mlt_deque_count( self->queue ) <= buffer / 5 + 1 ) { // Skip next frame if average cost exceeds frame duration. if ( time_process / count > frame_duration ) skip_next = 1; if ( skip_next ) mlt_log_debug( self, "avg usec %"PRId64" (%"PRId64"/%d) duration %d\n", time_process/count, time_process, count, frame_duration); } } // Remove the last frame mlt_frame_close( frame ); return NULL; }
static void on_frame_show( void *dummy, mlt_properties properties, mlt_frame frame ) { mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); }
static void *consumer_worker_thread( void *arg ) { // The argument is the consumer mlt_consumer self = arg; // Get the properties of the consumer mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); // Get the width and height int width = mlt_properties_get_int( properties, "width" ); int height = mlt_properties_get_int( properties, "height" ); mlt_image_format format = self->format; // See if video is turned off int video_off = mlt_properties_get_int( properties, "video_off" ); int preview_off = mlt_properties_get_int( properties, "preview_off" ); int preview_format = mlt_properties_get_int( properties, "preview_format" ); // General frame variable mlt_frame frame = NULL; uint8_t *image = NULL; if ( preview_off && preview_format != 0 ) format = preview_format; // Continue to read ahead while ( self->ahead ) { // Get the next unprocessed frame from the work queue pthread_mutex_lock( &self->queue_mutex ); int index = first_unprocessed_frame( self ); while ( self->ahead && index >= mlt_deque_count( self->queue ) ) { mlt_log_debug( MLT_CONSUMER_SERVICE(self), "waiting in worker index = %d queue count = %d\n", index, mlt_deque_count( self->queue ) ); pthread_cond_wait( &self->queue_cond, &self->queue_mutex ); index = first_unprocessed_frame( self ); } // Mark the frame for processing frame = mlt_deque_peek( self->queue, index ); if ( frame ) { mlt_log_debug( MLT_CONSUMER_SERVICE(self), "worker processing index = %d frame %d queue count = %d\n", index, mlt_frame_get_position(frame), mlt_deque_count( self->queue ) ); frame->is_processing = 1; mlt_properties_inc_ref( MLT_FRAME_PROPERTIES( frame ) ); } pthread_mutex_unlock( &self->queue_mutex ); // If there's no frame, we're probably stopped... if ( frame == NULL ) continue; #ifdef DEINTERLACE_ON_NOT_NORMAL_SPEED // All non normal playback frames should be shown if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "_speed" ) != 1 ) mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace", 1 ); #endif // Get the image if ( !video_off ) { // Fetch width/height again width = mlt_properties_get_int( properties, "width" ); height = mlt_properties_get_int( properties, "height" ); mlt_events_fire( MLT_CONSUMER_PROPERTIES( self ), "consumer-frame-render", frame, NULL ); mlt_frame_get_image( frame, &image, &format, &width, &height, 0 ); } mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 ); mlt_frame_close( frame ); // Tell a waiting thread (non-realtime main consumer thread) that we are done. pthread_mutex_lock( &self->done_mutex ); pthread_cond_broadcast( &self->done_cond ); pthread_mutex_unlock( &self->done_mutex ); } return NULL; }