int mlt_consumer_stop( mlt_consumer self ) { // Get the properies mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); // Just in case... mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopping put waiting\n" ); pthread_mutex_lock( &self->put_mutex ); self->put_active = 0; pthread_cond_broadcast( &self->put_cond ); pthread_mutex_unlock( &self->put_mutex ); // Stop the consumer mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopping consumer\n" ); // Cancel the read ahead threads self->ahead = 0; if ( self->started ) { // Unblock the consumer calling mlt_consumer_rt_frame pthread_mutex_lock( &self->queue_mutex ); pthread_cond_broadcast( &self->queue_cond ); pthread_mutex_unlock( &self->queue_mutex ); } // Invoke the child callback if ( self->stop != NULL ) self->stop( self ); // Check if the user has requested real time or not and stop if necessary mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopping read_ahead\n" ); if ( abs( self->real_time ) == 1 ) consumer_read_ahead_stop( self ); else if ( abs( self->real_time ) > 1 ) consumer_work_stop( self ); // Kill the test card mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL ); // Check and run a post command if ( mlt_properties_get( properties, "post" ) ) if (system( mlt_properties_get( properties, "post" ) ) == -1 ) mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get( properties, "post" ) ); mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopped\n" ); return 0; }
static void foreach_consumer_stop( mlt_consumer consumer ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); mlt_consumer nested = NULL; char key[30]; int index = 0; struct timespec tm = { 0, 1000 * 1000 }; do { snprintf( key, sizeof(key), "%d.consumer", index++ ); nested = mlt_properties_get_data( properties, key, NULL ); if ( nested ) { // Let consumer with terminate_on_pause stop on their own if ( mlt_properties_get_int( MLT_CONSUMER_PROPERTIES(nested), "terminate_on_pause" ) ) { // Send additional dummy frame to unlatch nested consumer's threads mlt_consumer_put_frame( nested, mlt_frame_init( MLT_CONSUMER_SERVICE(consumer) ) ); // wait for stop while ( !mlt_consumer_is_stopped( nested ) ) nanosleep( &tm, NULL ); } else { mlt_consumer_stop( nested ); } } } while ( nested ); }
int mlt_consumer_put_frame( mlt_consumer self, mlt_frame frame ) { int error = 1; // Get the service assoicated to the consumer mlt_service service = MLT_CONSUMER_SERVICE( self ); if ( mlt_service_producer( service ) == NULL ) { struct timeval now; struct timespec tm; pthread_mutex_lock( &self->put_mutex ); while ( self->put_active && self->put != NULL ) { gettimeofday( &now, NULL ); tm.tv_sec = now.tv_sec + 1; tm.tv_nsec = now.tv_usec * 1000; pthread_cond_timedwait( &self->put_cond, &self->put_mutex, &tm ); } if ( self->put_active && self->put == NULL ) self->put = frame; else mlt_frame_close( frame ); pthread_cond_broadcast( &self->put_cond ); pthread_mutex_unlock( &self->put_mutex ); } else { mlt_frame_close( frame ); } return error; }
IDeckLinkDisplayMode* getDisplayMode() { mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( getConsumer() ) ); IDeckLinkDisplayModeIterator* iter = NULL; IDeckLinkDisplayMode* mode = NULL; IDeckLinkDisplayMode* result = 0; if ( m_deckLinkOutput->GetDisplayModeIterator( &iter ) == S_OK ) { while ( !result && iter->Next( &mode ) == S_OK ) { m_width = mode->GetWidth(); m_height = mode->GetHeight(); mode->GetFrameRate( &m_duration, &m_timescale ); m_fps = (double) m_timescale / m_duration; int p = mode->GetFieldDominance() == bmdProgressiveFrame; mlt_log_verbose( getConsumer(), "BMD mode %dx%d %.3f fps prog %d\n", m_width, m_height, m_fps, p ); if ( m_width == profile->width && p == profile->progressive && (int) m_fps == (int) mlt_profile_fps( profile ) && ( m_height == profile->height || ( m_height == 486 && profile->height == 480 ) ) ) result = mode; else SAFE_RELEASE( mode ); } SAFE_RELEASE( iter ); } return result; }
int consumer_start( mlt_consumer parent ) { consumer_sdl self = parent->child; if ( !self->running ) { consumer_stop( parent ); mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent ); char *audio_driver = mlt_properties_get( properties, "audio_driver" ); char *audio_device = mlt_properties_get( properties, "audio_device" ); if ( audio_driver && strcmp( audio_driver, "" ) ) setenv( "SDL_AUDIODRIVER", audio_driver, 1 ); if ( audio_device && strcmp( audio_device, "" ) ) setenv( "AUDIODEV", audio_device, 1 ); pthread_mutex_lock( &mlt_sdl_mutex ); int ret = SDL_Init( SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE ); pthread_mutex_unlock( &mlt_sdl_mutex ); if ( ret < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE(parent), "Failed to initialize SDL: %s\n", SDL_GetError() ); return -1; } self->running = 1; self->joined = 0; pthread_create( &self->thread, NULL, consumer_thread, self ); } return 0; }
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 void *consumer_thread( void *arg ) { mlt_consumer consumer = arg; mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); mlt_frame frame = NULL; // Determine whether to stop at end-of-media int terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" ); int terminated = 0; // Loop while running while ( !terminated && !is_stopped( consumer ) ) { // Get the next 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 && !terminated && !is_stopped( consumer ) ) { if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "rendered" ) ) { if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "_speed" ) == 0 ) foreach_consumer_refresh( consumer ); foreach_consumer_put( consumer, frame ); } else { int dropped = mlt_properties_get_int( properties, "_dropped" ); mlt_log_info( MLT_CONSUMER_SERVICE(consumer), "dropped frame %d\n", ++dropped ); mlt_properties_set_int( properties, "_dropped", dropped ); } mlt_frame_close( frame ); } else { if ( frame && terminated ) { // Send this termination frame to nested consumers for their cancellation foreach_consumer_put( consumer, frame ); } if ( frame ) mlt_frame_close( frame ); terminated = 1; } } // Indicate that the consumer is stopped mlt_consumer_stopped( consumer ); return NULL; }
static void setup_jack_transport( mlt_consumer consumer, mlt_profile profile ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); mlt_filter jack = mlt_factory_filter( profile, "jackrack", NULL ); mlt_properties jack_properties = MLT_FILTER_PROPERTIES(jack); mlt_service_attach( MLT_CONSUMER_SERVICE(consumer), jack ); mlt_properties_set_int( properties, "audio_off", 1 ); mlt_properties_set_data( properties, "jack_filter", jack, 0, (mlt_destructor) mlt_filter_close, NULL ); // mlt_properties_set( jack_properties, "out_1", "system:playback_1" ); // mlt_properties_set( jack_properties, "out_2", "system:playback_2" ); mlt_events_listen( jack_properties, consumer, "jack-started", (mlt_listener) on_jack_started ); mlt_events_listen( jack_properties, consumer, "jack-stopped", (mlt_listener) on_jack_stopped ); }
int consumer_start( mlt_consumer parent ) { consumer_sdl self = parent->child; if ( !self->running ) { consumer_stop( parent ); pthread_mutex_lock( &mlt_sdl_mutex ); int ret = SDL_Init( SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE ); pthread_mutex_unlock( &mlt_sdl_mutex ); if ( ret < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE(parent), "Failed to initialize SDL: %s\n", SDL_GetError() ); return -1; } self->running = 1; self->joined = 0; pthread_create( &self->thread, NULL, consumer_thread, self ); } return 0; }
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; }
static void *video_thread( void *arg ) { // Identify the arg consumer_sdl self = arg; // Obtain time of thread start struct timeval now; int64_t start = 0; int64_t elapsed = 0; struct timespec tm; mlt_frame next = NULL; mlt_properties properties = NULL; double speed = 0; // Get real time flag int real_time = mlt_properties_get_int( self->properties, "real_time" ); #if !defined(__APPLE__) && !defined(_WIN32) if ( setup_sdl_video(self) ) self->running = 0; #endif // Determine start time gettimeofday( &now, NULL ); start = ( int64_t )now.tv_sec * 1000000 + now.tv_usec; while ( self->running ) { // Pop the next frame pthread_mutex_lock( &self->video_mutex ); next = mlt_deque_pop_front( self->queue ); while ( next == NULL && self->running ) { pthread_cond_wait( &self->video_cond, &self->video_mutex ); next = mlt_deque_pop_front( self->queue ); } pthread_mutex_unlock( &self->video_mutex ); if ( !self->running || next == NULL ) break; // Get the properties properties = MLT_FRAME_PROPERTIES( next ); // Get the speed of the frame speed = mlt_properties_get_double( properties, "_speed" ); // Get the current time gettimeofday( &now, NULL ); // Get the elapsed time elapsed = ( ( int64_t )now.tv_sec * 1000000 + now.tv_usec ) - start; // See if we have to delay the display of the current frame if ( mlt_properties_get_int( properties, "rendered" ) == 1 && self->running ) { // Obtain the scheduled playout time int64_t scheduled = mlt_properties_get_int( properties, "playtime" ); // Determine the difference between the elapsed time and the scheduled playout time int64_t difference = scheduled - elapsed; // Smooth playback a bit if ( real_time && ( difference > 20000 && speed == 1.0 ) ) { tm.tv_sec = difference / 1000000; tm.tv_nsec = ( difference % 1000000 ) * 500; nanosleep( &tm, NULL ); } // Show current frame if not too old if ( !real_time || ( difference > -10000 || speed != 1.0 || mlt_deque_count( self->queue ) < 2 ) ) consumer_play_video( self, next ); // If the queue is empty, recalculate start to allow build up again if ( real_time && ( mlt_deque_count( self->queue ) == 0 && speed == 1.0 ) ) { gettimeofday( &now, NULL ); start = ( ( int64_t )now.tv_sec * 1000000 + now.tv_usec ) - scheduled + 20000; } } else { static int dropped = 0; mlt_log_info( MLT_CONSUMER_SERVICE(&self->parent), "dropped video frame %d\n", ++dropped ); } // This frame can now be closed mlt_frame_close( next ); next = NULL; } if ( next != NULL ) mlt_frame_close( next ); mlt_consumer_stopped( &self->parent ); return NULL; }
int mlt_consumer_start( mlt_consumer self ) { if ( !mlt_consumer_is_stopped( self ) ) return 0; // Stop listening to the property-changed event mlt_event_block( self->event_listener ); // Get the properies mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); // Determine if there's a test card producer char *test_card = mlt_properties_get( properties, "test_card" ); // Just to make sure nothing is hanging around... pthread_mutex_lock( &self->put_mutex ); self->put = NULL; self->put_active = 1; pthread_mutex_unlock( &self->put_mutex ); // Deal with it now. if ( test_card != NULL ) { if ( mlt_properties_get_data( properties, "test_card_producer", NULL ) == NULL ) { // Create a test card producer mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); mlt_producer producer = mlt_factory_producer( profile, NULL, test_card ); // Do we have a producer if ( producer != NULL ) { // Test card should loop I guess... mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" ); //mlt_producer_set_speed( producer, 0 ); //mlt_producer_set_in_and_out( producer, 0, 0 ); // Set the test card on the consumer mlt_properties_set_data( properties, "test_card_producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); } } } else { // Allow the hash table to speed things up mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL ); } // The profile could have changed between a stop and a restart. apply_profile_properties( self, mlt_service_profile( MLT_CONSUMER_SERVICE(self) ), properties ); // Set the frame duration in microseconds for the frame-dropping heuristic int frame_rate_num = mlt_properties_get_int( properties, "frame_rate_num" ); int frame_rate_den = mlt_properties_get_int( properties, "frame_rate_den" ); int frame_duration = 0; if ( frame_rate_num && frame_rate_den ) { frame_duration = 1000000 / frame_rate_num * frame_rate_den; } mlt_properties_set_int( properties, "frame_duration", frame_duration ); // Check and run an ante command if ( mlt_properties_get( properties, "ante" ) ) if ( system( mlt_properties_get( properties, "ante" ) ) == -1 ) mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get( properties, "ante" ) ); // Set the real_time preference self->real_time = mlt_properties_get_int( properties, "real_time" ); // For worker threads implementation, buffer must be at least # threads if ( abs( self->real_time ) > 1 && mlt_properties_get_int( properties, "buffer" ) <= abs( self->real_time ) ) mlt_properties_set_int( properties, "_buffer", abs( self->real_time ) + 1 ); // Get the image format to use for rendering threads const char* format = mlt_properties_get( properties, "mlt_image_format" ); if ( format ) { if ( !strcmp( format, "rgb24" ) ) self->format = mlt_image_rgb24; else if ( !strcmp( format, "rgb24a" ) ) self->format = mlt_image_rgb24a; else if ( !strcmp( format, "yuv420p" ) ) self->format = mlt_image_yuv420p; else if ( !strcmp( format, "none" ) ) self->format = mlt_image_none; else self->format = mlt_image_yuv422; } // Start the service if ( self->start != NULL ) return self->start( self ); return 0; }
static void mlt_consumer_property_changed( mlt_properties owner, mlt_consumer self, char *name ) { if ( !strcmp( name, "mlt_profile" ) ) { // Get the properies mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); // Get the current profile mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); // Load the new profile mlt_profile new_profile = mlt_profile_init( mlt_properties_get( properties, name ) ); if ( new_profile ) { // Copy the profile if ( profile != NULL ) { free( profile->description ); memcpy( profile, new_profile, sizeof( struct mlt_profile_s ) ); profile->description = strdup( new_profile->description ); } else { profile = new_profile; } // Apply to properties apply_profile_properties( self, profile, properties ); mlt_profile_close( new_profile ); } } else if ( !strcmp( name, "frame_rate_num" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); if ( profile ) { profile->frame_rate_num = mlt_properties_get_int( properties, "frame_rate_num" ); mlt_properties_set_double( properties, "fps", mlt_profile_fps( profile ) ); } } else if ( !strcmp( name, "frame_rate_den" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); if ( profile ) { profile->frame_rate_den = mlt_properties_get_int( properties, "frame_rate_den" ); mlt_properties_set_double( properties, "fps", mlt_profile_fps( profile ) ); } } else if ( !strcmp( name, "width" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); if ( profile ) profile->width = mlt_properties_get_int( properties, "width" ); } else if ( !strcmp( name, "height" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); if ( profile ) profile->height = mlt_properties_get_int( properties, "height" ); } else if ( !strcmp( name, "progressive" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); if ( profile ) profile->progressive = mlt_properties_get_int( properties, "progressive" ); } else if ( !strcmp( name, "sample_aspect_num" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); if ( profile ) { profile->sample_aspect_num = mlt_properties_get_int( properties, "sample_aspect_num" ); mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) ); } } else if ( !strcmp( name, "sample_aspect_den" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); if ( profile ) { profile->sample_aspect_den = mlt_properties_get_int( properties, "sample_aspect_den" ); mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) ); } } else if ( !strcmp( name, "display_aspect_num" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); if ( profile ) { profile->display_aspect_num = mlt_properties_get_int( properties, "display_aspect_num" ); mlt_properties_set_double( properties, "display_ratio", mlt_profile_dar( profile ) ); } } else if ( !strcmp( name, "display_aspect_den" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); if ( profile ) { profile->display_aspect_den = mlt_properties_get_int( properties, "display_aspect_den" ); mlt_properties_set_double( properties, "display_ratio", mlt_profile_dar( profile ) ); } } else if ( !strcmp( name, "colorspace" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); if ( profile ) profile->colorspace = mlt_properties_get_int( properties, "colorspace" ); } }
mlt_consumer consumer_sdl_audio_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Create the consumer object consumer_sdl self = calloc( 1, sizeof( struct consumer_sdl_s ) ); // If no malloc'd and consumer init ok if ( self != NULL && mlt_consumer_init( &self->parent, self, profile ) == 0 ) { // Create the queue self->queue = mlt_deque_init( ); // Get the parent consumer object mlt_consumer parent = &self->parent; // We have stuff to clean up, so override the close method parent->close = consumer_close; // get a handle on properties mlt_service service = MLT_CONSUMER_SERVICE( parent ); self->properties = MLT_SERVICE_PROPERTIES( service ); // Set the default volume mlt_properties_set_double( self->properties, "volume", 1.0 ); // This is the initialisation of the consumer pthread_mutex_init( &self->audio_mutex, NULL ); pthread_cond_init( &self->audio_cond, NULL); pthread_mutex_init( &self->video_mutex, NULL ); pthread_cond_init( &self->video_cond, NULL); // Default scaler (for now we'll use nearest) mlt_properties_set( self->properties, "rescale", "nearest" ); mlt_properties_set( self->properties, "deinterlace_method", "onefield" ); mlt_properties_set_int( self->properties, "top_field_first", -1 ); // Default buffer for low latency mlt_properties_set_int( self->properties, "buffer", 1 ); // Default audio buffer mlt_properties_set_int( self->properties, "audio_buffer", 2048 ); // Ensure we don't join on a non-running object self->joined = 1; // Allow thread to be started/stopped parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; parent->purge = consumer_purge; // Initialize the refresh handler pthread_cond_init( &self->refresh_cond, NULL ); pthread_mutex_init( &self->refresh_mutex, NULL ); mlt_events_listen( MLT_CONSUMER_PROPERTIES( parent ), self, "property-changed", ( mlt_listener )consumer_refresh_cb ); // Return the consumer produced return parent; } // malloc or consumer init failed free( self ); // Indicate failure return NULL; }
static int consumer_play_audio( consumer_sdl self, mlt_frame frame, int init_audio, int *duration ) { // Get the properties of self consumer mlt_properties properties = self->properties; mlt_audio_format afmt = mlt_audio_s16; // Set the preferred params of the test card signal int channels = mlt_properties_get_int( properties, "channels" ); int dest_channels = channels; int frequency = mlt_properties_get_int( properties, "frequency" ); static int counter = 0; int samples = mlt_sample_calculator( mlt_properties_get_double( self->properties, "fps" ), frequency, counter++ ); int16_t *pcm; int bytes; mlt_frame_get_audio( frame, (void**) &pcm, &afmt, &frequency, &channels, &samples ); *duration = ( ( samples * 1000 ) / frequency ); pcm += mlt_properties_get_int( properties, "audio_offset" ); if ( mlt_properties_get_int( properties, "audio_off" ) ) { self->playing = 1; init_audio = 1; return init_audio; } if ( init_audio == 1 ) { SDL_AudioSpec request; SDL_AudioSpec got; int audio_buffer = mlt_properties_get_int( properties, "audio_buffer" ); // specify audio format memset( &request, 0, sizeof( SDL_AudioSpec ) ); self->playing = 0; request.freq = frequency; request.format = AUDIO_S16SYS; request.channels = dest_channels; request.samples = audio_buffer; request.callback = sdl_fill_audio; request.userdata = (void *)self; if ( SDL_OpenAudio( &request, &got ) != 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE( self ), "SDL failed to open audio: %s\n", SDL_GetError() ); init_audio = 2; } else if ( got.size != 0 ) { SDL_PauseAudio( 0 ); init_audio = 0; } } if ( init_audio == 0 ) { mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); bytes = samples * dest_channels * sizeof(*pcm); pthread_mutex_lock( &self->audio_mutex ); while ( self->running && bytes > ( sizeof( self->audio_buffer) - self->audio_avail ) ) pthread_cond_wait( &self->audio_cond, &self->audio_mutex ); if ( self->running ) { if ( mlt_properties_get_double( properties, "_speed" ) == 1 ) { if ( channels == dest_channels ) { memcpy( &self->audio_buffer[ self->audio_avail ], pcm, bytes ); } else { int16_t *dest = (int16_t*) &self->audio_buffer[ self->audio_avail ]; int i = samples + 1; while ( --i ) { memcpy( dest, pcm, dest_channels * sizeof(*pcm) ); pcm += channels; dest += dest_channels; } } } else { memset( &self->audio_buffer[ self->audio_avail ], 0, bytes ); } self->audio_avail += bytes; } pthread_cond_broadcast( &self->audio_cond ); pthread_mutex_unlock( &self->audio_mutex ); } else { self->playing = 1; } return init_audio; }
mlt_frame mlt_consumer_get_frame( mlt_consumer self ) { // Frame to return mlt_frame frame = NULL; // Get the service assoicated to the consumer mlt_service service = MLT_CONSUMER_SERVICE( self ); // Get the consumer properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); // Get the frame if ( mlt_service_producer( service ) == NULL && mlt_properties_get_int( properties, "put_mode" ) ) { struct timeval now; struct timespec tm; pthread_mutex_lock( &self->put_mutex ); while ( self->put_active && self->put == NULL ) { gettimeofday( &now, NULL ); tm.tv_sec = now.tv_sec + 1; tm.tv_nsec = now.tv_usec * 1000; pthread_cond_timedwait( &self->put_cond, &self->put_mutex, &tm ); } frame = self->put; self->put = NULL; pthread_cond_broadcast( &self->put_cond ); pthread_mutex_unlock( &self->put_mutex ); if ( frame != NULL ) mlt_service_apply_filters( service, frame, 0 ); } else if ( mlt_service_producer( service ) != NULL ) { mlt_service_get_frame( service, &frame, 0 ); } else { frame = mlt_frame_init( service ); } if ( frame != NULL ) { // Get the frame properties mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); // Get the test card producer mlt_producer test_card = mlt_properties_get_data( properties, "test_card_producer", NULL ); // Attach the test frame producer to it. if ( test_card != NULL ) mlt_properties_set_data( frame_properties, "test_card_producer", test_card, 0, NULL, NULL ); // Pass along the interpolation and deinterlace options // TODO: get rid of consumer_deinterlace and use profile.progressive mlt_properties_set( frame_properties, "rescale.interp", mlt_properties_get( properties, "rescale" ) ); mlt_properties_set_int( frame_properties, "consumer_deinterlace", mlt_properties_get_int( properties, "progressive" ) | mlt_properties_get_int( properties, "deinterlace" ) ); mlt_properties_set( frame_properties, "deinterlace_method", mlt_properties_get( properties, "deinterlace_method" ) ); mlt_properties_set_int( frame_properties, "consumer_tff", mlt_properties_get_int( properties, "top_field_first" ) ); } // Return the frame return frame; }
static int setup_sdl_video( consumer_sdl self ) { int error = 0; int sdl_flags = SDL_WINDOW_RESIZABLE; int texture_format = SDL_PIXELFORMAT_YUY2; // Skip this if video is disabled. int video_off = mlt_properties_get_int( self->properties, "video_off" ); int preview_off = mlt_properties_get_int( self->properties, "preview_off" ); if ( video_off || preview_off ) return error; if (!SDL_WasInit(SDL_INIT_VIDEO)) { pthread_mutex_lock( &mlt_sdl_mutex ); int ret = SDL_Init( SDL_INIT_VIDEO ); pthread_mutex_unlock( &mlt_sdl_mutex ); if ( ret < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Failed to initialize SDL: %s\n", SDL_GetError() ); return -1; } } #if 0 // only yuv422 working currently int image_format = mlt_properties_get_int( self->properties, "mlt_image_format" ); if ( image_format ) switch ( image_format ) { case mlt_image_rgb24: texture_format = SDL_PIXELFORMAT_RGB24; break; case mlt_image_rgb24a: texture_format = SDL_PIXELFORMAT_ABGR8888; break; case mlt_image_yuv420p: texture_format = SDL_PIXELFORMAT_IYUV; break; case mlt_image_yuv422: texture_format = SDL_PIXELFORMAT_YUY2; break; default: mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Invalid image format %s\n", mlt_image_format_name( image_format ) ); return -1; } #endif if ( mlt_properties_get_int( self->properties, "fullscreen" ) ) { self->window_width = self->width; self->window_height = self->height; sdl_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; SDL_ShowCursor( SDL_DISABLE ); } pthread_mutex_lock( &mlt_sdl_mutex ); self->sdl_window = SDL_CreateWindow("MLT", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, self->window_width, self->window_height, sdl_flags); self->sdl_renderer = SDL_CreateRenderer(self->sdl_window, -1, SDL_RENDERER_ACCELERATED); if ( self->sdl_renderer ) { self->sdl_texture = SDL_CreateTexture( self->sdl_renderer, texture_format, SDL_TEXTUREACCESS_STREAMING, self->window_width, self->window_height ); if ( self->sdl_texture ) { SDL_SetRenderDrawColor( self->sdl_renderer, 0, 0, 0, 255); } else { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Failed to create SDL texture: %s\n", SDL_GetError() ); error = -1; } } else { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Failed to create SDL renderer: %s\n", SDL_GetError() ); error = -1; } pthread_mutex_unlock( &mlt_sdl_mutex ); return error; }
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; }
mlt_consumer consumer_sdl2_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Create the consumer object consumer_sdl self = calloc( 1, sizeof( struct consumer_sdl_s ) ); // If no malloc'd and consumer init ok if ( self != NULL && mlt_consumer_init( &self->parent, self, profile ) == 0 ) { // Create the queue self->queue = mlt_deque_init( ); // Get the parent consumer object mlt_consumer parent = &self->parent; // We have stuff to clean up, so override the close method parent->close = consumer_close; // get a handle on properties mlt_service service = MLT_CONSUMER_SERVICE( parent ); self->properties = MLT_SERVICE_PROPERTIES( service ); // Set the default volume mlt_properties_set_double( self->properties, "volume", 1.0 ); // This is the initialisation of the consumer pthread_mutex_init( &self->audio_mutex, NULL ); pthread_cond_init( &self->audio_cond, NULL); pthread_mutex_init( &self->video_mutex, NULL ); pthread_cond_init( &self->video_cond, NULL); // Default scaler (for now we'll use nearest) mlt_properties_set( self->properties, "rescale", "nearest" ); mlt_properties_set( self->properties, "deinterlace_method", "onefield" ); mlt_properties_set_int( self->properties, "top_field_first", -1 ); // Default buffer for low latency mlt_properties_set_int( self->properties, "buffer", 1 ); // Default audio buffer mlt_properties_set_int( self->properties, "audio_buffer", 2048 ); #if defined(_WIN32) && SDL_MAJOR_VERSION == 2 mlt_properties_set( self->properties, "audio_driver", "DirectSound" ); #endif // Ensure we don't join on a non-running object self->joined = 1; // process actual param if ( arg && sscanf( arg, "%dx%d", &self->width, &self->height ) ) { mlt_properties_set_int( self->properties, "resolution", 1 ); } else { self->width = mlt_properties_get_int( self->properties, "width" ); self->height = mlt_properties_get_int( self->properties, "height" ); } // Allow thread to be started/stopped parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; parent->purge = consumer_purge; // Register specific events mlt_events_register( self->properties, "consumer-sdl-event", ( mlt_transmitter )consumer_sdl_event ); // Return the consumer produced return parent; } // malloc or consumer init failed free( self ); // Indicate failure return NULL; }
static int consumer_play_audio( consumer_sdl self, mlt_frame frame, int init_audio, int *duration ) { // Get the properties of self consumer mlt_properties properties = self->properties; mlt_audio_format afmt = mlt_audio_s16; // Set the preferred params of the test card signal int channels = mlt_properties_get_int( properties, "channels" ); int frequency = mlt_properties_get_int( properties, "frequency" ); int scrub = mlt_properties_get_int( properties, "scrub_audio" ); static int counter = 0; int samples = mlt_sample_calculator( mlt_properties_get_double( self->properties, "fps" ), frequency, counter++ ); int16_t *pcm; mlt_frame_get_audio( frame, (void**) &pcm, &afmt, &frequency, &channels, &samples ); *duration = ( ( samples * 1000 ) / frequency ); pcm += mlt_properties_get_int( properties, "audio_offset" ); if ( mlt_properties_get_int( properties, "audio_off" ) ) { self->playing = 1; init_audio = 1; return init_audio; } if ( init_audio == 1 ) { SDL_AudioSpec request; SDL_AudioSpec got; SDL_AudioDeviceID dev; int audio_buffer = mlt_properties_get_int( properties, "audio_buffer" ); // specify audio format memset( &request, 0, sizeof( SDL_AudioSpec ) ); self->playing = 0; request.freq = frequency; request.format = AUDIO_S16SYS; request.channels = mlt_properties_get_int( properties, "channels" ); request.samples = audio_buffer; request.callback = sdl_fill_audio; request.userdata = (void *)self; dev = sdl2_open_audio( &request, &got ); if( dev == 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE( self ), "SDL failed to open audio\n" ); init_audio = 2; } else { if( got.channels != request.channels ) { mlt_log_info( MLT_CONSUMER_SERVICE( self ), "Unable to output %d channels. Change to %d\n", request.channels, got.channels ); } mlt_log_info( MLT_CONSUMER_SERVICE( self ), "Audio Opened: driver=%s channels=%d frequency=%d\n", SDL_GetCurrentAudioDriver(), got.channels, got.freq ); SDL_PauseAudioDevice( dev, 0 ); init_audio = 0; self->out_channels = got.channels; } } if ( init_audio == 0 ) { mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); int samples_copied = 0; int dst_stride = self->out_channels * sizeof( *pcm ); pthread_mutex_lock( &self->audio_mutex ); while ( self->running && samples_copied < samples ) { int sample_space = ( sizeof( self->audio_buffer ) - self->audio_avail ) / dst_stride; while ( self->running && sample_space == 0 ) { pthread_cond_wait( &self->audio_cond, &self->audio_mutex ); sample_space = ( sizeof( self->audio_buffer ) - self->audio_avail ) / dst_stride; } if ( self->running ) { int samples_to_copy = samples - samples_copied; if ( samples_to_copy > sample_space ) { samples_to_copy = sample_space; } int dst_bytes = samples_to_copy * dst_stride; if ( scrub || mlt_properties_get_double( properties, "_speed" ) == 1 ) { if ( channels == self->out_channels ) { memcpy( &self->audio_buffer[ self->audio_avail ], pcm, dst_bytes ); pcm += samples_to_copy * channels; } else { int16_t *dest = (int16_t*) &self->audio_buffer[ self->audio_avail ]; int i = samples_to_copy + 1; while ( --i ) { memcpy( dest, pcm, dst_stride ); pcm += channels; dest += self->out_channels; } } } else { memset( &self->audio_buffer[ self->audio_avail ], 0, dst_bytes ); pcm += samples_to_copy * channels; } self->audio_avail += dst_bytes; samples_copied += samples_to_copy; } pthread_cond_broadcast( &self->audio_cond ); } pthread_mutex_unlock( &self->audio_mutex ); } else { self->playing = 1; } return init_audio; }