void process_queue( mlt_deque data_queue, mlt_frame frame, mlt_filter filter ) { if ( data_queue != NULL ) { // Create a new queue for those that we can't handle mlt_deque temp_queue = mlt_deque_init( ); // Iterate through each entry on the queue while ( mlt_deque_peek_front( data_queue ) != NULL ) { // Get the data feed mlt_properties feed = mlt_deque_pop_front( data_queue ); if ( mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "debug" ) != NULL ) mlt_properties_debug( feed, mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "debug" ), stderr ); // Process the data feed... if ( process_feed( feed, filter, frame ) == 0 ) mlt_properties_close( feed ); else mlt_deque_push_back( temp_queue, feed ); } // Now put the unprocessed feeds back on the stack while ( mlt_deque_peek_front( temp_queue ) ) { // Get the data feed mlt_properties feed = mlt_deque_pop_front( temp_queue ); // Put it back on the data queue mlt_deque_push_back( data_queue, feed ); } // Close the temporary queue mlt_deque_close( temp_queue ); } }
static void destroy_data_queue( void *arg ) { if ( arg != NULL ) { // Assign the correct type mlt_deque queue = arg; // Iterate through each item and destroy them while ( mlt_deque_peek_front( queue ) != NULL ) mlt_properties_close( mlt_deque_pop_back( queue ) ); // Close the deque mlt_deque_close( queue ); } }
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; }