int mlt_frame_is_test_card( mlt_frame self ) { mlt_properties properties = MLT_FRAME_PROPERTIES( self ); return ( mlt_deque_count( self->stack_image ) == 0 && !mlt_properties_get_data( properties, "image", NULL ) ) || mlt_properties_get_int( properties, "test_image" ); }
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; }
int mlt_frame_is_test_audio( mlt_frame self ) { mlt_properties properties = MLT_FRAME_PROPERTIES( self ); return ( mlt_deque_count( self->stack_audio ) == 0 && !mlt_properties_get_data( properties, "audio", NULL ) ) || mlt_properties_get_int( properties, "test_audio" ); }
void mlt_consumer_purge( mlt_consumer self ) { if ( self && self->ahead ) { pthread_mutex_lock( &self->queue_mutex ); while ( mlt_deque_count( self->queue ) ) mlt_frame_close( mlt_deque_pop_back( self->queue ) ); pthread_cond_broadcast( &self->queue_cond ); pthread_mutex_unlock( &self->queue_mutex ); } }
static void consumer_work_stop( mlt_consumer self ) { // Make sure we're running if ( self->started ) { // Inform thread to stop self->ahead = 0; // Broadcast to the queue condition in case it's waiting pthread_mutex_lock( &self->queue_mutex ); pthread_cond_broadcast( &self->queue_cond ); pthread_mutex_unlock( &self->queue_mutex ); // Broadcast to the put condition in case it's waiting pthread_mutex_lock( &self->put_mutex ); pthread_cond_broadcast( &self->put_cond ); pthread_mutex_unlock( &self->put_mutex ); // Broadcast to the done condition in case it's waiting pthread_mutex_lock( &self->done_mutex ); pthread_cond_broadcast( &self->done_cond ); pthread_mutex_unlock( &self->done_mutex ); // Join the threads pthread_t *thread; while ( ( thread = mlt_deque_pop_back( self->worker_threads ) ) ) pthread_join( *thread, NULL ); // Deallocate the array of threads if ( self->threads ) free( self->threads ); // Indicate that worker threads no longer running self->started = 0; // Destroy the mutexes pthread_mutex_destroy( &self->queue_mutex ); pthread_mutex_destroy( &self->done_mutex ); // Destroy the conditions pthread_cond_destroy( &self->queue_cond ); pthread_cond_destroy( &self->done_cond ); // Wipe the queues while ( mlt_deque_count( self->queue ) ) mlt_frame_close( mlt_deque_pop_back( self->queue ) ); // Close the queues mlt_deque_close( self->queue ); mlt_deque_close( self->worker_threads ); } }
void consumer_purge( mlt_consumer parent ) { consumer_sdl self = parent->child; if ( self->running ) { pthread_mutex_lock( &self->video_mutex ); while ( mlt_deque_count( self->queue ) ) 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 int vdpau_get_buffer( AVCodecContext *codec_context, AVFrame *frame ) { int error = 0; producer_avformat self = codec_context->opaque; mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "vdpau_get_buffer\n" ); if ( self->vdpau && mlt_deque_count( self->vdpau->deque ) ) { struct vdpau_render_state *render = mlt_deque_pop_front( self->vdpau->deque ); if ( render ) { frame->data[0] = (uint8_t*) render; frame->data[1] = (uint8_t*) render; frame->data[2] = (uint8_t*) render; frame->linesize[0] = 0; frame->linesize[1] = 0; frame->linesize[2] = 0; frame->type = FF_BUFFER_TYPE_USER; render->state = FF_VDPAU_STATE_USED_FOR_REFERENCE; frame->reordered_opaque = codec_context->reordered_opaque; if ( frame->reference ) { self->vdpau->ip_age[0] = self->vdpau->ip_age[1] + 1; self->vdpau->ip_age[1] = 1; self->vdpau->b_age++; } else { self->vdpau->ip_age[0] ++; self->vdpau->ip_age[1] ++; self->vdpau->b_age = 1; } } else { mlt_log_warning( MLT_PRODUCER_SERVICE(self->parent), "VDPAU surface underrun\n" ); error = -1; } } else { mlt_log_warning( MLT_PRODUCER_SERVICE(self->parent), "VDPAU surface underrun\n" ); error = -1; } return error; }
mlt_frame mlt_consumer_rt_frame( mlt_consumer self ) { // Frame to return mlt_frame frame = NULL; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); // Check if the user has requested real time or not if ( self->real_time > 1 || self->real_time < -1 ) { // see above return worker_get_frame( self, properties ); } else if ( self->real_time == 1 || self->real_time == -1 ) { int size = 1; // Is the read ahead running? if ( self->ahead == 0 ) { int buffer = mlt_properties_get_int( properties, "buffer" ); int prefill = mlt_properties_get_int( properties, "prefill" ); consumer_read_ahead_start( self ); if ( buffer > 1 ) size = prefill > 0 && prefill < buffer ? prefill : buffer; } // Get frame from queue pthread_mutex_lock( &self->queue_mutex ); while( self->ahead && mlt_deque_count( self->queue ) < size ) pthread_cond_wait( &self->queue_cond, &self->queue_mutex ); frame = mlt_deque_pop_front( self->queue ); pthread_cond_broadcast( &self->queue_cond ); pthread_mutex_unlock( &self->queue_mutex ); } else // real_time == 0 { // Get the frame in non real time frame = mlt_consumer_get_frame( self ); // This isn't true, but from the consumers perspective it is if ( frame != NULL ) mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 ); } return frame; }
void stop() { // Stop the audio and video output streams immediately if ( m_deckLinkOutput ) { m_deckLinkOutput->StopScheduledPlayback( 0, 0, 0 ); m_deckLinkOutput->DisableAudioOutput(); m_deckLinkOutput->DisableVideoOutput(); } while ( mlt_deque_count( m_videoFrameQ ) ) { m_videoFrame = (IDeckLinkMutableVideoFrame*) mlt_deque_pop_back( m_videoFrameQ ); m_videoFrame->Release(); } m_videoFrame = 0; if ( m_fifo ) sample_fifo_close( m_fifo ); }
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 void consumer_read_ahead_stop( mlt_consumer self ) { // Make sure we're running if ( self->started ) { // Inform thread to stop self->ahead = 0; // Broadcast to the condition in case it's waiting pthread_mutex_lock( &self->queue_mutex ); pthread_cond_broadcast( &self->queue_cond ); pthread_mutex_unlock( &self->queue_mutex ); // Broadcast to the put condition in case it's waiting pthread_mutex_lock( &self->put_mutex ); pthread_cond_broadcast( &self->put_cond ); pthread_mutex_unlock( &self->put_mutex ); // Join the thread pthread_join( self->ahead_thread, NULL ); self->started = 0; // Destroy the frame queue mutex pthread_mutex_destroy( &self->queue_mutex ); // Destroy the condition pthread_cond_destroy( &self->queue_cond ); // Wipe the queue while ( mlt_deque_count( self->queue ) ) mlt_frame_close( mlt_deque_pop_back( self->queue ) ); // Close the queue mlt_deque_close( self->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; }
static int vdpau_decoder_init( producer_avformat self ) { mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "vdpau_decoder_init\n" ); int success = 1; self->video_codec->opaque = self; self->video_codec->get_format = vdpau_get_format; self->video_codec->get_buffer = vdpau_get_buffer; self->video_codec->release_buffer = vdpau_release_buffer; self->video_codec->draw_horiz_band = vdpau_draw_horiz; self->video_codec->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD; self->video_codec->pix_fmt = PIX_FMT_VDPAU_H264; VdpDecoderProfile profile = VDP_DECODER_PROFILE_H264_HIGH; uint32_t max_references = self->video_codec->refs; pthread_mutex_lock( &mlt_sdl_mutex ); VdpStatus status = vdp_decoder_create( self->vdpau->device, profile, self->video_codec->width, self->video_codec->height, max_references, &self->vdpau->decoder ); pthread_mutex_unlock( &mlt_sdl_mutex ); if ( status == VDP_STATUS_OK ) { int i, n = FFMIN( self->video_codec->refs + 2, MAX_VDPAU_SURFACES ); self->vdpau->deque = mlt_deque_init(); for ( i = 0; i < n; i++ ) { if ( VDP_STATUS_OK == vdp_surface_create( self->vdpau->device, VDP_CHROMA_TYPE_420, self->video_codec->width, self->video_codec->height, &self->vdpau->render_states[i].surface ) ) { mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "successfully created VDPAU surface %x\n", self->vdpau->render_states[i].surface ); mlt_deque_push_back( self->vdpau->deque, &self->vdpau->render_states[i] ); } else { mlt_log_info( MLT_PRODUCER_SERVICE(self->parent), "failed to create VDPAU surface %dx%d\n", self->video_codec->width, self->video_codec->height ); while ( mlt_deque_count( self->vdpau->deque ) ) { struct vdpau_render_state *render = mlt_deque_pop_front( self->vdpau->deque ); vdp_surface_destroy( render->surface ); } mlt_deque_close( self->vdpau->deque ); success = 0; break; } } if ( self->vdpau ) self->vdpau->b_age = self->vdpau->ip_age[0] = self->vdpau->ip_age[1] = 256*256*256*64; // magic from Avidemux } else { success = 0; self->vdpau->decoder = VDP_INVALID_HANDLE; mlt_log_error( MLT_PRODUCER_SERVICE(self->parent), "VDPAU failed to initialize decoder (%s)\n", vdp_get_error_string( status ) ); } return success; }
virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived( IDeckLinkVideoInputFrame* video, IDeckLinkAudioInputPacket* audio ) { if ( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "preview" ) && mlt_producer_get_speed( getProducer() ) == 0.0 && !mlt_deque_count( m_queue )) { pthread_cond_broadcast( &m_condition ); return S_OK; } // Create mlt_frame mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( getProducer() ) ); // Copy video if ( video ) { if ( !( video->GetFlags() & bmdFrameHasNoInputSource ) ) { int size = video->GetRowBytes() * ( video->GetHeight() + m_vancLines ); void* image = mlt_pool_alloc( size ); void* buffer = 0; unsigned char* p = (unsigned char*) image; int n = size / 2; \ // Initialize VANC lines to nominal black while ( --n ) { *p ++ = 16; *p ++ = 128; } // Capture VANC if ( m_vancLines > 0 ) { IDeckLinkVideoFrameAncillary* vanc = 0; if ( video->GetAncillaryData( &vanc ) == S_OK && vanc ) { for ( int i = 1; i < m_vancLines + 1; i++ ) { if ( vanc->GetBufferForVerticalBlankingLine( i, &buffer ) == S_OK ) swab( (char*) buffer, (char*) image + ( i - 1 ) * video->GetRowBytes(), video->GetRowBytes() ); else mlt_log_debug( getProducer(), "failed capture vanc line %d\n", i ); } SAFE_RELEASE(vanc); } } // Capture image video->GetBytes( &buffer ); if ( image && buffer ) { size = video->GetRowBytes() * video->GetHeight(); swab( (char*) buffer, (char*) image + m_vancLines * video->GetRowBytes(), size ); mlt_frame_set_image( frame, (uint8_t*) image, size, mlt_pool_release ); } else if ( image ) { mlt_log_verbose( getProducer(), "no video\n" ); mlt_pool_release( image ); } } else { mlt_log_verbose( getProducer(), "no signal\n" ); mlt_frame_close( frame ); frame = 0; } // Get timecode IDeckLinkTimecode* timecode = 0; if ( video->GetTimecode( bmdTimecodeVITC, &timecode ) == S_OK && timecode ) { DLString timecodeString = 0; if ( timecode->GetString( &timecodeString ) == S_OK ) { char* s = getCString( timecodeString ); mlt_properties_set( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.markup", s ); mlt_log_debug( getProducer(), "timecode %s\n", s ); freeCString( s ); } freeDLString( timecodeString ); SAFE_RELEASE( timecode ); } } else { mlt_log_verbose( getProducer(), "no video\n" ); mlt_frame_close( frame ); frame = 0; } // Copy audio if ( frame && audio ) { int channels = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" ); int size = audio->GetSampleFrameCount() * channels * sizeof(int16_t); mlt_audio_format format = mlt_audio_s16; void* pcm = mlt_pool_alloc( size ); void* buffer = 0; audio->GetBytes( &buffer ); if ( buffer ) { memcpy( pcm, buffer, size ); mlt_frame_set_audio( frame, pcm, format, size, mlt_pool_release ); mlt_properties_set_int( MLT_FRAME_PROPERTIES(frame), "audio_samples", audio->GetSampleFrameCount() ); } else { mlt_log_verbose( getProducer(), "no audio\n" ); mlt_pool_release( pcm ); } } else { mlt_log_verbose( getProducer(), "no audio\n" ); } // Put frame in queue if ( frame ) { int queueMax = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "buffer" ); pthread_mutex_lock( &m_mutex ); if ( mlt_deque_count( m_queue ) < queueMax ) { mlt_deque_push_back( m_queue, frame ); pthread_cond_broadcast( &m_condition ); } else { mlt_frame_close( frame ); mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "dropped", ++m_dropped ); mlt_log_warning( getProducer(), "frame dropped %d\n", m_dropped ); } pthread_mutex_unlock( &m_mutex ); } return S_OK; }
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; }
int mlt_frame_is_test_audio( mlt_frame self ) { return mlt_deque_count( self->stack_audio ) == 0 || mlt_properties_get_int( MLT_FRAME_PROPERTIES( self ), "test_audio" ); }
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; }
int mlt_frame_is_test_card( mlt_frame self ) { return mlt_deque_count( self->stack_image ) == 0 || mlt_properties_get_int( MLT_FRAME_PROPERTIES( self ), "test_image" ); }
mlt_frame getFrame() { struct timeval now; struct timespec tm; double fps = mlt_producer_get_fps( getProducer() ); mlt_position position = mlt_producer_position( getProducer() ); mlt_frame frame = mlt_cache_get_frame( m_cache, position ); // Allow the buffer to fill to the requested initial buffer level. if ( m_isBuffering ) { int prefill = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "prefill" ); int buffer = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "buffer" ); m_isBuffering = false; prefill = prefill > buffer ? buffer : prefill; pthread_mutex_lock( &m_mutex ); while ( mlt_deque_count( m_queue ) < prefill ) { // Wait up to buffer/fps seconds gettimeofday( &now, NULL ); long usec = now.tv_sec * 1000000 + now.tv_usec; usec += 1000000 * buffer / fps; tm.tv_sec = usec / 1000000; tm.tv_nsec = (usec % 1000000) * 1000; if ( pthread_cond_timedwait( &m_condition, &m_mutex, &tm ) ) break; } pthread_mutex_unlock( &m_mutex ); } if ( !frame ) { // Wait if queue is empty pthread_mutex_lock( &m_mutex ); while ( mlt_deque_count( m_queue ) < 1 ) { // Wait up to twice frame duration gettimeofday( &now, NULL ); long usec = now.tv_sec * 1000000 + now.tv_usec; usec += 2000000 / fps; tm.tv_sec = usec / 1000000; tm.tv_nsec = (usec % 1000000) * 1000; if ( pthread_cond_timedwait( &m_condition, &m_mutex, &tm ) ) // Stop waiting if error (timed out) break; } frame = ( mlt_frame ) mlt_deque_pop_front( m_queue ); pthread_mutex_unlock( &m_mutex ); // add to cache if ( frame ) { mlt_frame_set_position( frame, position ); mlt_cache_put_frame( m_cache, frame ); } } // Set frame timestamp and properties if ( frame ) { mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) ); mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_properties_set_int( properties, "progressive", profile->progressive ); mlt_properties_set_int( properties, "meta.media.progressive", profile->progressive ); mlt_properties_set_int( properties, "top_field_first", m_topFieldFirst ); mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) ); mlt_properties_set_int( properties, "meta.media.sample_aspect_num", profile->sample_aspect_num ); mlt_properties_set_int( properties, "meta.media.sample_aspect_den", profile->sample_aspect_den ); mlt_properties_set_int( properties, "meta.media.frame_rate_num", profile->frame_rate_num ); mlt_properties_set_int( properties, "meta.media.frame_rate_den", profile->frame_rate_den ); mlt_properties_set_int( properties, "width", profile->width ); mlt_properties_set_int( properties, "meta.media.width", profile->width ); mlt_properties_set_int( properties, "height", profile->height ); mlt_properties_set_int( properties, "meta.media.height", profile->height ); mlt_properties_set_int( properties, "format", mlt_image_yuv422 ); mlt_properties_set_int( properties, "colorspace", m_colorspace ); mlt_properties_set_int( properties, "meta.media.colorspace", m_colorspace ); mlt_properties_set_int( properties, "audio_frequency", 48000 ); mlt_properties_set_int( properties, "audio_channels", mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" ) ); } else mlt_log_warning( getProducer(), "buffer underrun\n" ); return frame; }
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; }
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" ); // Get the current time gettimeofday( &now, NULL ); // Determine start time 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 ) { // 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; } } // This frame can now be closed mlt_frame_close( next ); next = NULL; } // This consumer is stopping. But audio has already been played for all // the frames in the queue. Spit out all the frames so that the display has // the option to catch up with the audio. if ( next != NULL ) { consumer_play_video( self, next ); mlt_frame_close( next ); next = NULL; } while ( mlt_deque_count( self->queue ) > 0 ) { next = mlt_deque_pop_front( self->queue ); consumer_play_video( self, next ); mlt_frame_close( next ); next = NULL; } mlt_consumer_stopped( &self->parent ); return NULL; }
static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int track ) { mlt_tractor self = parent->child; // We only respond to the first track requests if ( track == 0 && self->producer != NULL ) { int i = 0; int done = 0; mlt_frame temp = NULL; int count = 0; int image_count = 0; // Get the properties of the parent producer mlt_properties properties = MLT_PRODUCER_PROPERTIES( parent ); // Try to obtain the multitrack associated to the tractor mlt_multitrack multitrack = mlt_properties_get_data( properties, "multitrack", NULL ); // Or a specific producer mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL ); // Determine whether this tractor feeds to the consumer or stops here int global_feed = mlt_properties_get_int( properties, "global_feed" ); // If we don't have one, we're in trouble... if ( multitrack != NULL ) { // The output frame will hold the 'global' data feeds (ie: those which are targetted for the final frame) mlt_deque data_queue = mlt_deque_init( ); // Used to garbage collect all frames char label[ 30 ]; // Get the id of the tractor char *id = mlt_properties_get( properties, "_unique_id" ); // Will be used to store the frame properties object mlt_properties frame_properties = NULL; // We'll store audio and video frames to use here mlt_frame audio = NULL; mlt_frame video = NULL; mlt_frame first_video = NULL; // Temporary properties mlt_properties temp_properties = NULL; // Get the multitrack's producer mlt_producer target = MLT_MULTITRACK_PRODUCER( multitrack ); mlt_producer_seek( target, mlt_producer_frame( parent ) ); mlt_producer_set_speed( target, mlt_producer_get_speed( parent ) ); // We will create one frame and attach everything to it *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) ); // Get the properties of the frame frame_properties = MLT_FRAME_PROPERTIES( *frame ); // Loop through each of the tracks we're harvesting for ( i = 0; !done; i ++ ) { // Get a frame from the producer mlt_service_get_frame( self->producer, &temp, i ); // Get the temporary properties temp_properties = MLT_FRAME_PROPERTIES( temp ); // Pass all unique meta properties from the producer's frame to the new frame mlt_properties_lock( temp_properties ); int props_count = mlt_properties_count( temp_properties ); int j; for ( j = 0; j < props_count; j ++ ) { char *name = mlt_properties_get_name( temp_properties, j ); if ( !strncmp( name, "meta.", 5 ) && !mlt_properties_get( frame_properties, name ) ) mlt_properties_set( frame_properties, name, mlt_properties_get_value( temp_properties, j ) ); } mlt_properties_unlock( temp_properties ); // Copy the format conversion virtual functions if ( ! (*frame)->convert_image && temp->convert_image ) (*frame)->convert_image = temp->convert_image; if ( ! (*frame)->convert_audio && temp->convert_audio ) (*frame)->convert_audio = temp->convert_audio; // Check for last track done = mlt_properties_get_int( temp_properties, "last_track" ); // Handle fx only tracks if ( mlt_properties_get_int( temp_properties, "fx_cut" ) ) { int hide = ( video == NULL ? 1 : 0 ) | ( audio == NULL ? 2 : 0 ); mlt_properties_set_int( temp_properties, "hide", hide ); } // We store all frames with a destructor on the output frame sprintf( label, "_%s_%d", id, count ++ ); mlt_properties_set_data( frame_properties, label, temp, 0, ( mlt_destructor )mlt_frame_close, NULL ); // We want to append all 'final' feeds to the global queue if ( !done && mlt_properties_get_data( temp_properties, "data_queue", NULL ) != NULL ) { // Move the contents of this queue on to the output frames data queue mlt_deque sub_queue = mlt_properties_get_data( MLT_FRAME_PROPERTIES( temp ), "data_queue", NULL ); mlt_deque temp = mlt_deque_init( ); while ( global_feed && mlt_deque_count( sub_queue ) ) { mlt_properties p = mlt_deque_pop_back( sub_queue ); if ( mlt_properties_get_int( p, "final" ) ) mlt_deque_push_back( data_queue, p ); else mlt_deque_push_back( temp, p ); } while( mlt_deque_count( temp ) ) mlt_deque_push_front( sub_queue, mlt_deque_pop_back( temp ) ); mlt_deque_close( temp ); } // Now do the same with the global queue but without the conditional behaviour if ( mlt_properties_get_data( temp_properties, "global_queue", NULL ) != NULL ) { mlt_deque sub_queue = mlt_properties_get_data( MLT_FRAME_PROPERTIES( temp ), "global_queue", NULL ); while ( mlt_deque_count( sub_queue ) ) { mlt_properties p = mlt_deque_pop_back( sub_queue ); mlt_deque_push_back( data_queue, p ); } } // Pick up first video and audio frames if ( !done && !mlt_frame_is_test_audio( temp ) && !( mlt_properties_get_int( temp_properties, "hide" ) & 2 ) ) { // Order of frame creation is starting to get problematic if ( audio != NULL ) { mlt_deque_push_front( MLT_FRAME_AUDIO_STACK( temp ), producer_get_audio ); mlt_deque_push_front( MLT_FRAME_AUDIO_STACK( temp ), audio ); } audio = temp; } if ( !done && !mlt_frame_is_test_card( temp ) && !( mlt_properties_get_int( temp_properties, "hide" ) & 1 ) ) { if ( video != NULL ) { mlt_deque_push_front( MLT_FRAME_IMAGE_STACK( temp ), producer_get_image ); mlt_deque_push_front( MLT_FRAME_IMAGE_STACK( temp ), video ); } video = temp; if ( first_video == NULL ) first_video = temp; mlt_properties_set_int( MLT_FRAME_PROPERTIES( temp ), "image_count", ++ image_count ); image_count = 1; } } // Now stack callbacks if ( audio != NULL ) { mlt_frame_push_audio( *frame, audio ); mlt_frame_push_audio( *frame, producer_get_audio ); } if ( video != NULL ) { mlt_properties video_properties = MLT_FRAME_PROPERTIES( first_video ); mlt_frame_push_service( *frame, video ); mlt_frame_push_service( *frame, producer_get_image ); if ( global_feed ) mlt_properties_set_data( frame_properties, "data_queue", data_queue, 0, NULL, NULL ); mlt_properties_set_data( video_properties, "global_queue", data_queue, 0, destroy_data_queue, NULL ); mlt_properties_set_int( frame_properties, "width", mlt_properties_get_int( video_properties, "width" ) ); mlt_properties_set_int( frame_properties, "height", mlt_properties_get_int( video_properties, "height" ) ); mlt_properties_pass_list( frame_properties, video_properties, "meta.media.width, meta.media.height" ); mlt_properties_set_int( frame_properties, "progressive", mlt_properties_get_int( video_properties, "progressive" ) ); mlt_properties_set_double( frame_properties, "aspect_ratio", mlt_properties_get_double( video_properties, "aspect_ratio" ) ); mlt_properties_set_int( frame_properties, "image_count", image_count ); mlt_properties_set_data( frame_properties, "_producer", mlt_frame_get_original_producer( first_video ), 0, NULL, NULL ); } else { destroy_data_queue( data_queue ); } mlt_frame_set_position( *frame, mlt_producer_frame( parent ) ); mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_audio", audio == NULL ); mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_image", video == NULL ); } else if ( producer != NULL ) { mlt_producer_seek( producer, mlt_producer_frame( parent ) ); mlt_producer_set_speed( producer, mlt_producer_get_speed( parent ) ); mlt_service_get_frame( self->producer, frame, track ); } else { mlt_log( MLT_PRODUCER_SERVICE( parent ), MLT_LOG_ERROR, "tractor without a multitrack!!\n" ); mlt_service_get_frame( self->producer, frame, track ); } // Prepare the next frame mlt_producer_prepare_next( parent ); // Indicate our found status return 0; } else { // Generate a test card *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) ); return 0; } }
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; }