static void *consumer_thread( void *arg ) { // Identify the arg consumer_sdl self = arg; // Get the consumer mlt_consumer consumer = &self->parent; // Convenience functionality int terminate_on_pause = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "terminate_on_pause" ); int terminated = 0; // Video thread pthread_t thread; // internal intialization int init_audio = 1; int init_video = 1; mlt_frame frame = NULL; int duration = 0; int64_t playtime = 0; struct timespec tm = { 0, 100000 }; // Loop until told not to while( self->running ) { // Get a frame from the attached producer frame = !terminated? mlt_consumer_rt_frame( consumer ) : NULL; // Check for termination if ( terminate_on_pause && frame ) terminated = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0; // Ensure that we have a frame if ( frame ) { // Play audio init_audio = consumer_play_audio( self, frame, init_audio, &duration ); // Determine the start time now if ( self->playing && init_video ) { // Create the video thread pthread_create( &thread, NULL, video_thread, self ); // Video doesn't need to be initialised any more init_video = 0; } // Set playtime for this frame mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "playtime", playtime ); while ( self->running && mlt_deque_count( self->queue ) > 15 ) nanosleep( &tm, NULL ); // Push this frame to the back of the queue pthread_mutex_lock( &self->video_mutex ); if ( self->is_purge ) { mlt_frame_close( frame ); frame = NULL; self->is_purge = 0; } else { mlt_deque_push_back( self->queue, frame ); pthread_cond_broadcast( &self->video_cond ); } pthread_mutex_unlock( &self->video_mutex ); // Calculate the next playtime playtime += ( duration * 1000 ); } else if ( terminated ) { if ( init_video || mlt_deque_count( self->queue ) == 0 ) break; else nanosleep( &tm, NULL ); } } self->running = 0; // Unblock sdl_preview if ( mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "put_mode" ) && mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "put_pending" ) ) { frame = mlt_consumer_get_frame( consumer ); if ( frame ) mlt_frame_close( frame ); frame = NULL; } // Kill the video thread if ( init_video == 0 ) { pthread_mutex_lock( &self->video_mutex ); pthread_cond_broadcast( &self->video_cond ); pthread_mutex_unlock( &self->video_mutex ); pthread_join( thread, NULL ); } while( mlt_deque_count( self->queue ) ) mlt_frame_close( mlt_deque_pop_back( self->queue ) ); self->audio_avail = 0; return 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 consumer_props = MLT_CONSUMER_PROPERTIES( consumer ); // Video thread pthread_t thread; // internal intialization int init_audio = 1; int init_video = 1; mlt_frame frame = NULL; mlt_properties properties = NULL; int duration = 0; int64_t playtime = 0; struct timespec tm = { 0, 100000 }; // int last_position = -1; 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_rt_frame( consumer ); // Ensure that we have a frame if ( frame ) { // Get the frame properties properties = MLT_FRAME_PROPERTIES( frame ); // Get the speed of the frame double speed = mlt_properties_get_double( properties, "_speed" ); // Clear refresh mlt_events_block( consumer_props, consumer_props ); mlt_properties_set_int( consumer_props, "refresh", 0 ); mlt_events_unblock( consumer_props, consumer_props ); // Play audio init_audio = consumer_play_audio( self, frame, init_audio, &duration ); // Determine the start time now if ( self->playing && init_video ) { // Create the video thread pthread_create( &thread, NULL, video_thread, self ); // Video doesn't need to be initialised any more init_video = 0; } // Set playtime for this frame mlt_properties_set_int( properties, "playtime", playtime ); while ( self->running && speed != 0 && mlt_deque_count( self->queue ) > 15 ) nanosleep( &tm, NULL ); // Push this frame to the back of the queue if ( self->running && speed ) { pthread_mutex_lock( &self->video_mutex ); if ( self->is_purge && speed == 1.0 ) { mlt_frame_close( frame ); frame = NULL; self->is_purge = 0; } else { mlt_deque_push_back( self->queue, frame ); pthread_cond_broadcast( &self->video_cond ); } pthread_mutex_unlock( &self->video_mutex ); // Calculate the next playtime playtime += ( duration * 1000 ); } else if ( self->running ) { pthread_mutex_lock( &self->refresh_mutex ); consumer_play_video( self, frame ); mlt_frame_close( frame ); frame = NULL; self->refresh_count --; if ( self->refresh_count <= 0 ) { pthread_cond_wait( &self->refresh_cond, &self->refresh_mutex ); } pthread_mutex_unlock( &self->refresh_mutex ); } // Optimisation to reduce latency if ( speed == 1.0 ) { // TODO: disabled due to misbehavior on parallel-consumer // if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) ) // mlt_consumer_purge( consumer ); // last_position = mlt_frame_get_position( frame ); } else { mlt_consumer_purge( consumer ); // last_position = -1; } } } // Kill the video thread if ( init_video == 0 ) { pthread_mutex_lock( &self->video_mutex ); pthread_cond_broadcast( &self->video_cond ); pthread_mutex_unlock( &self->video_mutex ); pthread_join( thread, NULL ); } if ( frame ) { // The video thread has cleared out the queue. But the audio was played // for this frame. So play the video before stopping so the display has // the option to catch up with the audio. consumer_play_video( self, frame ); mlt_frame_close( frame ); frame = NULL; } self->audio_avail = 0; return NULL; }