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 mlt_frame_close( mlt_frame self ) { if ( self != NULL && mlt_properties_dec_ref( MLT_FRAME_PROPERTIES( self ) ) <= 0 ) { mlt_deque_close( self->stack_image ); mlt_deque_close( self->stack_audio ); while( mlt_deque_peek_back( self->stack_service ) ) mlt_service_close( mlt_deque_pop_back( self->stack_service ) ); mlt_deque_close( self->stack_service ); mlt_properties_close( &self->parent ); free( self ); } }
static void consumer_close( mlt_consumer parent ) { // Get the actual object consumer_sdl self = parent->child; // Stop the consumer mlt_consumer_stop( parent ); // Now clean up the rest mlt_consumer_close( parent ); // Close the queue mlt_deque_close( self->queue ); // Destroy mutexes pthread_mutex_destroy( &self->audio_mutex ); pthread_cond_destroy( &self->audio_cond ); pthread_mutex_destroy( &self->video_mutex ); pthread_cond_destroy( &self->video_cond ); pthread_mutex_destroy( &self->refresh_mutex ); pthread_cond_destroy( &self->refresh_cond ); // Finally clean up this free( self ); }
~DeckLinkConsumer() { if ( m_deckLinkOutput ) m_deckLinkOutput->Release(); if ( m_deckLink ) m_deckLink->Release(); if ( m_videoFrameQ ) mlt_deque_close( m_videoFrameQ ); }
~DeckLinkProducer() { if ( m_queue ) { stop(); mlt_deque_close( m_queue ); pthread_mutex_destroy( &m_mutex ); pthread_cond_destroy( &m_condition ); mlt_cache_close( m_cache ); } SAFE_RELEASE( m_decklinkInput ); SAFE_RELEASE( m_decklink ); }
virtual ~DeckLinkConsumer() { mlt_log_debug( getConsumer(), "%s: entering\n", __FUNCTION__ ); SAFE_RELEASE( m_displayMode ); SAFE_RELEASE( m_deckLinkKeyer ); SAFE_RELEASE( m_deckLinkOutput ); SAFE_RELEASE( m_deckLink ); mlt_deque_close( m_aqueue ); mlt_deque_close( m_frames ); op(OP_EXIT, 0); mlt_log_debug( getConsumer(), "%s: waiting for op thread\n", __FUNCTION__ ); pthread_join(m_op_thread, NULL); mlt_log_debug( getConsumer(), "%s: finished op thread\n", __FUNCTION__ ); pthread_mutex_destroy(&m_op_lock); pthread_mutex_destroy(&m_op_arg_mutex); pthread_cond_destroy(&m_op_arg_cond); mlt_log_debug( getConsumer(), "%s: exiting\n", __FUNCTION__ ); }
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 void vdpau_producer_close( producer_avformat self ) { if ( self->vdpau ) { mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "vdpau_producer_close\n" ); int i; for ( i = 0; i < MAX_VDPAU_SURFACES; i++ ) { if ( self->vdpau->render_states[i].surface != VDP_INVALID_HANDLE ) vdp_surface_destroy( self->vdpau->render_states[i].surface ); self->vdpau->render_states[i].surface = VDP_INVALID_HANDLE; } mlt_deque_close( self->vdpau->deque ); if ( self->vdpau->buffer ) mlt_pool_release( self->vdpau->buffer ); self->vdpau->buffer = NULL; vdpau_fini( self ); } }
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 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 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; }
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; } }