static inline int first_unprocessed_frame( mlt_consumer self ) { int index = self->real_time <= 0 ? 0 : self->process_head; while ( index < mlt_deque_count( self->queue ) && MLT_FRAME( mlt_deque_peek( self->queue, index ) )->is_processing ) index++; return index; }
void consumer_purge( mlt_consumer parent ) { consumer_sdl self = parent->child; if ( self->running ) { pthread_mutex_lock( &self->video_mutex ); mlt_frame frame = MLT_FRAME( mlt_deque_peek_back( self->queue ) ); // When playing rewind or fast forward then we need to keep one // frame in the queue to prevent playback stalling. double speed = frame? mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame), "_speed" ) : 0; int n = ( speed == 0.0 || speed == 1.0 ) ? 0 : 1; while ( mlt_deque_count( self->queue ) > n ) mlt_frame_close( mlt_deque_pop_back( self->queue ) ); self->is_purge = 1; pthread_cond_broadcast( &self->video_cond ); pthread_mutex_unlock( &self->video_mutex ); } }
static mlt_frame worker_get_frame( mlt_consumer self, mlt_properties properties ) { // Frame to return mlt_frame frame = NULL; double fps = mlt_properties_get_double( properties, "fps" ); int threads = abs( self->real_time ); int buffer = mlt_properties_get_int( properties, "_buffer" ); buffer = buffer > 0 ? buffer : mlt_properties_get_int( properties, "buffer" ); // This is a heuristic to determine a suitable minimum buffer size for the number of threads. int headroom = 2 + threads * threads; buffer = buffer < headroom ? headroom : buffer; // Start worker threads if not already started. if ( ! self->ahead ) { int prefill = mlt_properties_get_int( properties, "prefill" ); prefill = prefill > 0 && prefill < buffer ? prefill : buffer; consumer_work_start( self ); // Fill the work queue. int i = buffer; while ( self->ahead && i-- ) { frame = mlt_consumer_get_frame( self ); if ( frame ) { pthread_mutex_lock( &self->queue_mutex ); mlt_deque_push_back( self->queue, frame ); pthread_cond_signal( &self->queue_cond ); pthread_mutex_unlock( &self->queue_mutex ); } } // Wait for prefill while ( self->ahead && first_unprocessed_frame( self ) < prefill ) { pthread_mutex_lock( &self->done_mutex ); pthread_cond_wait( &self->done_cond, &self->done_mutex ); pthread_mutex_unlock( &self->done_mutex ); } self->process_head = threads; } // mlt_log_verbose( MLT_CONSUMER_SERVICE(self), "size %d done count %d work count %d process_head %d\n", // threads, first_unprocessed_frame( self ), mlt_deque_count( self->queue ), self->process_head ); // Feed the work queue while ( self->ahead && mlt_deque_count( self->queue ) < buffer ) { frame = mlt_consumer_get_frame( self ); if ( ! frame ) return frame; pthread_mutex_lock( &self->queue_mutex ); mlt_deque_push_back( self->queue, frame ); pthread_cond_signal( &self->queue_cond ); pthread_mutex_unlock( &self->queue_mutex ); } // Wait if not realtime. mlt_frame head_frame = MLT_FRAME( mlt_deque_peek_front( self->queue ) ); while ( self->ahead && self->real_time < 0 && !( head_frame && mlt_properties_get_int( MLT_FRAME_PROPERTIES( head_frame ), "rendered" ) ) ) { pthread_mutex_lock( &self->done_mutex ); pthread_cond_wait( &self->done_cond, &self->done_mutex ); pthread_mutex_unlock( &self->done_mutex ); } // Get the frame from the queue. pthread_mutex_lock( &self->queue_mutex ); frame = mlt_deque_pop_front( self->queue ); pthread_mutex_unlock( &self->queue_mutex ); // Adapt the worker process head to the runtime conditions. if ( self->real_time > 0 ) { if ( frame && mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "rendered" ) ) { self->consecutive_dropped = 0; if ( self->process_head > threads && self->consecutive_rendered >= self->process_head ) self->process_head--; else self->consecutive_rendered++; } else { self->consecutive_rendered = 0; if ( self->process_head < buffer - threads && self->consecutive_dropped > threads ) self->process_head++; else self->consecutive_dropped++; } // mlt_log_verbose( MLT_CONSUMER_SERVICE(self), "dropped %d rendered %d process_head %d\n", // self->consecutive_dropped, self->consecutive_rendered, self->process_head ); // Check for too many consecutively dropped frames if ( self->consecutive_dropped > mlt_properties_get_int( properties, "drop_max" ) ) { int orig_buffer = mlt_properties_get_int( properties, "buffer" ); int prefill = mlt_properties_get_int( properties, "prefill" ); mlt_log_verbose( self, "too many frames dropped - " ); // If using a default low-latency buffer level (SDL) and below the limit if ( ( orig_buffer == 1 || prefill == 1 ) && buffer < (threads + 1) * 10 ) { // Auto-scale the buffer to compensate mlt_log_verbose( self, "increasing buffer to %d\n", buffer + threads ); mlt_properties_set_int( properties, "_buffer", buffer + threads ); self->consecutive_dropped = fps / 2; } else { // Tell the consumer to render it mlt_log_verbose( self, "forcing next frame\n" ); mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 ); self->consecutive_dropped = 0; } } } return frame; }