Example #1
0
mlt_tractor mlt_tractor_new( )
{
	mlt_tractor self = calloc( 1, sizeof( struct mlt_tractor_s ) );
	if ( self != NULL )
	{
		mlt_producer producer = &self->parent;
		if ( mlt_producer_init( producer, self ) == 0 )
		{
			mlt_multitrack multitrack = mlt_multitrack_init( );
			mlt_field field = mlt_field_new( multitrack, self );
			mlt_properties props = MLT_PRODUCER_PROPERTIES( producer );

			mlt_properties_set( props, "resource", "<tractor>" );
			mlt_properties_set( props, "mlt_type", "mlt_producer" );
			mlt_properties_set( props, "mlt_service", "tractor" );
			mlt_properties_set_position( props, "in", 0 );
			mlt_properties_set_position( props, "out", 0 );
			mlt_properties_set_position( props, "length", 0 );
			mlt_properties_set_data( props, "multitrack", multitrack, 0, ( mlt_destructor )mlt_multitrack_close, NULL );
			mlt_properties_set_data( props, "field", field, 0, ( mlt_destructor )mlt_field_close, NULL );

			mlt_events_listen( MLT_MULTITRACK_PROPERTIES( multitrack ), self, "producer-changed", ( mlt_listener )mlt_tractor_listener );

			producer->get_frame = producer_get_frame;
			producer->close = ( mlt_destructor )mlt_tractor_close;
			producer->close_object = self;
		}
		else
		{
			free( self );
			self = NULL;
		}
	}
	return self;
}
Example #2
0
int mlt_producer_seek( mlt_producer self, mlt_position position )
{
	// Determine eof handling
	mlt_properties properties = MLT_PRODUCER_PROPERTIES( self );
	char *eof = mlt_properties_get( properties, "eof" );
	int use_points = 1 - mlt_properties_get_int( properties, "ignore_points" );

	// Recursive behaviour for cuts - repositions parent and then repositions cut
	// hence no return on this condition
	if ( mlt_producer_is_cut( self ) )
		mlt_producer_seek( mlt_producer_cut_parent( self ), position + mlt_producer_get_in( self ) );

	// Check bounds
	if ( position < 0 || mlt_producer_get_playtime( self ) == 0 )
	{
		position = 0;
	}
	else if ( use_points && ( eof == NULL || !strcmp( eof, "pause" ) ) && position >= mlt_producer_get_playtime( self ) )
	{
		mlt_producer_set_speed( self, 0 );
		position = mlt_producer_get_playtime( self ) - 1;
	}
	else if ( use_points && eof && !strcmp( eof, "loop" ) && position >= mlt_producer_get_playtime( self ) )
	{
		position = (int)position % (int)mlt_producer_get_playtime( self );
	}

	// Set the position
	mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( self ), "_position", position );

	// Calculate the absolute frame
	mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( self ), "_frame", use_points * mlt_producer_get_in( self ) + position );

	return 0;
}
Example #3
0
int mlt_producer_set_in_and_out( mlt_producer self, mlt_position in, mlt_position out )
{
	mlt_properties properties = MLT_PRODUCER_PROPERTIES( self );

	// Correct ins and outs if necessary
	if ( in < 0 )
		in = 0;
	else if ( in >= mlt_producer_get_length( self ) )
		in = mlt_producer_get_length( self ) - 1;

	if ( ( out < 0 || out >= mlt_producer_get_length( self ) ) && !mlt_producer_is_blank( self ) )
		out = mlt_producer_get_length( self ) - 1;
	else if ( ( out < 0 || out >= mlt_producer_get_length( self ) ) && mlt_producer_is_blank( self ) )
		mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( self ), "length", out + 1 );
	else if ( out < 0 )
		out = 0;

	// Swap ins and outs if wrong
	if ( out < in )
	{
		mlt_position t = in;
		in = out;
		out = t;
	}

	// Set the values
	mlt_events_block( properties, properties );
	mlt_properties_set_position( properties, "in", in );
	mlt_events_unblock( properties, properties );
	mlt_properties_set_position( properties, "out", out );

	return 0;
}
Example #4
0
int mlt_filter_init( mlt_filter self, void *child )
{
	mlt_service service = &self->parent;
	memset( self, 0, sizeof( struct mlt_filter_s ) );
	self->child = child;
	if ( mlt_service_init( service, self ) == 0 )
	{
		mlt_properties properties = MLT_SERVICE_PROPERTIES( service );

		// Override the get_frame method
		service->get_frame = filter_get_frame;

		// Define the destructor
		service->close = ( mlt_destructor )mlt_filter_close;
		service->close_object = self;

		// Default in, out, track properties
		mlt_properties_set_position( properties, "in", 0 );
		mlt_properties_set_position( properties, "out", 0 );
		mlt_properties_set_int( properties, "track", 0 );

		return 0;
	}
	return 1;
}
Example #5
0
int mlt_frame_set_position( mlt_frame self, mlt_position value )
{
	// Only set the original_position the first time.
	if ( ! mlt_properties_get( MLT_FRAME_PROPERTIES( self ), "original_position" ) )
		mlt_properties_set_position( MLT_FRAME_PROPERTIES( self ), "original_position", value );
	return mlt_properties_set_position( MLT_FRAME_PROPERTIES( self ), "_position", value );
}
Example #6
0
void mlt_service_apply_filters( mlt_service self, mlt_frame frame, int index )
{
	int i;
	mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
	mlt_properties service_properties = MLT_SERVICE_PROPERTIES( self );
	mlt_service_base *base = self->local;
	mlt_position position = mlt_frame_get_position( frame );
	mlt_position self_in = mlt_properties_get_position( service_properties, "in" );
	mlt_position self_out = mlt_properties_get_position( service_properties, "out" );

	if ( index == 0 || mlt_properties_get_int( service_properties, "_filter_private" ) == 0 )
	{
		// Process the frame with the attached filters
		for ( i = 0; i < base->filter_count; i ++ )
		{
			if ( base->filters[ i ] != NULL )
			{
				mlt_position in = mlt_filter_get_in( base->filters[ i ] );
				mlt_position out = mlt_filter_get_out( base->filters[ i ] );
				int disable = mlt_properties_get_int( MLT_FILTER_PROPERTIES( base->filters[ i ] ), "disable" );
				if ( !disable && ( ( in == 0 && out == 0 ) || ( position >= in && ( position <= out || out == 0 ) ) ) )
				{
					mlt_properties_set_position( frame_properties, "in", in == 0 ? self_in : in );
					mlt_properties_set_position( frame_properties, "out", out == 0 ? self_out : out );
					mlt_filter_process( base->filters[ i ], frame );
					mlt_service_apply_filters( MLT_FILTER_SERVICE( base->filters[ i ] ), frame, index + 1 );
				}
			}
		}
	}
}
Example #7
0
int mlt_producer_init( mlt_producer self, void *child )
{
	// Check that we haven't received NULL
	int error = self == NULL;

	// Continue if no error
	if ( error == 0 )
	{
#ifdef _MLT_PRODUCER_CHECKS_
		producers_created ++;
#endif

		// Initialise the producer
		memset( self, 0, sizeof( struct mlt_producer_s ) );

		// Associate with the child
		self->child = child;

		// Initialise the service
		if ( mlt_service_init( &self->parent, self ) == 0 )
		{
			// The parent is the service
			mlt_service parent = &self->parent;

			// Define the parent close
			parent->close = ( mlt_destructor )mlt_producer_close;
			parent->close_object = self;

			// For convenience, we'll assume the close_object is self
			self->close_object = self;

			// Get the properties of the parent
			mlt_properties properties = MLT_SERVICE_PROPERTIES( parent );

			// Set the default properties
			mlt_properties_set( properties, "mlt_type", "mlt_producer" );
			mlt_properties_set_position( properties, "_position", 0.0 );
			mlt_properties_set_double( properties, "_frame", 0 );
			mlt_properties_set_double( properties, "_speed", 1.0 );
			mlt_properties_set_position( properties, "in", 0 );
			char *e = getenv( "MLT_DEFAULT_PRODUCER_LENGTH" );
			int p = e ? atoi( e ) : 15000;
			mlt_properties_set_position( properties, "out", p - 1 );
			mlt_properties_set_position( properties, "length", p );
			mlt_properties_set( properties, "eof", "pause" );
			mlt_properties_set( properties, "resource", "<producer>" );

			// Override service get_frame
			parent->get_frame = producer_get_frame;

			mlt_events_listen( properties, self, "service-changed", ( mlt_listener )mlt_producer_service_changed );
			mlt_events_listen( properties, self, "property-changed", ( mlt_listener )mlt_producer_property_changed );
			mlt_events_register( properties, "producer-changed", NULL );
		}
	}

	return error;
}
Example #8
0
int mlt_producer_clear( mlt_producer self )
{
	if ( self != NULL )
	{
		mlt_properties properties = MLT_PRODUCER_PROPERTIES( self );
		mlt_events_block( properties, properties );
		mlt_properties_set_position( properties, "in", 0 );
		mlt_events_unblock( properties, properties );
		mlt_properties_set_position( properties, "out", -1 );
	}
	return 0;
}
Example #9
0
void mlt_tractor_refresh( mlt_tractor self )
{
	mlt_multitrack multitrack = mlt_tractor_multitrack( self );
	mlt_properties multitrack_props = MLT_MULTITRACK_PROPERTIES( multitrack );
	mlt_properties properties = MLT_TRACTOR_PROPERTIES( self );
	mlt_events_block( multitrack_props, properties );
	mlt_events_block( properties, properties );
	mlt_multitrack_refresh( multitrack );
	mlt_properties_set_position( properties, "in", 0 );
	mlt_properties_set_position( properties, "out", mlt_properties_get_position( multitrack_props, "out" ) );
	mlt_events_unblock( properties, properties );
	mlt_events_unblock( multitrack_props, properties );
	mlt_properties_set_position( properties, "length", mlt_properties_get_position( multitrack_props, "length" ) );
}
Example #10
0
int mlt_filter_connect( mlt_filter self, mlt_service producer, int index )
{
	int ret = mlt_service_connect_producer( &self->parent, producer, index );

	// If the connection was successful, grab the producer, track and reset in/out
	if ( ret == 0 )
	{
		mlt_properties properties = MLT_SERVICE_PROPERTIES( &self->parent );
		mlt_properties_set_position( properties, "in", 0 );
		mlt_properties_set_position( properties, "out", 0 );
		mlt_properties_set_int( properties, "track", index );
	}

	return ret;
}
Example #11
0
static int jackrack_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
{
	// Get the filter service
	mlt_filter filter = mlt_frame_pop_audio( frame );

	// Get the filter properties
	mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter );

	int jack_frequency = mlt_properties_get_int( filter_properties, "_sample_rate" );

	// Get the producer's audio
	*format = mlt_audio_float;
	mlt_frame_get_audio( frame, buffer, format, &jack_frequency, channels, samples );
	
	// TODO: Deal with sample rate differences
	if ( *frequency != jack_frequency )
		mlt_log_error( MLT_FILTER_SERVICE( filter ), "mismatching frequencies JACK = %d actual = %d\n",
			jack_frequency, *frequency );
	*frequency = jack_frequency;

	// Initialise Jack ports and connections if needed
	if ( mlt_properties_get_int( filter_properties, "_samples" ) == 0 )
		mlt_properties_set_int( filter_properties, "_samples", *samples );
	
	// Get the filter-specific properties
	jack_ringbuffer_t **output_buffers = mlt_properties_get_data( filter_properties, "output_buffers", NULL );
	jack_ringbuffer_t **input_buffers = mlt_properties_get_data( filter_properties, "input_buffers", NULL );
//	pthread_mutex_t *output_lock = mlt_properties_get_data( filter_properties, "output_lock", NULL );
//	pthread_cond_t *output_ready = mlt_properties_get_data( filter_properties, "output_ready", NULL );
	
	// Process the audio
	float *q = (float*) *buffer;
	size_t size = *samples * sizeof(float);
	int j;
//	struct timespec tm = { 0, 0 };

	// Write into output ringbuffer
	for ( j = 0; j < *channels; j++ )
	{
		if ( jack_ringbuffer_write_space( output_buffers[j] ) >= size )
			jack_ringbuffer_write( output_buffers[j], (char*)( q + j * *samples ), size );
	}

	// Synchronization phase - wait for signal from Jack process
	while ( jack_ringbuffer_read_space( input_buffers[ *channels - 1 ] ) < size ) ;
		//pthread_cond_wait( output_ready, output_lock );
		
	// Read from input ringbuffer
	for ( j = 0; j < *channels; j++, q++ )
	{
		if ( jack_ringbuffer_read_space( input_buffers[j] ) >= size )
			jack_ringbuffer_read( input_buffers[j], (char*)( q + j * *samples ), size );
	}

	// help jack_sync() indicate when we are rolling
	mlt_position pos = mlt_frame_get_position( frame );
	mlt_properties_set_position( filter_properties, "_last_pos", pos );

	return 0;
}
Example #12
0
mlt_producer mlt_producer_cut( mlt_producer self, int in, int out )
{
	mlt_producer result = mlt_producer_new( mlt_service_profile( MLT_PRODUCER_SERVICE( self ) ) );
	mlt_producer parent = mlt_producer_cut_parent( self );
	mlt_properties properties = MLT_PRODUCER_PROPERTIES( result );
	mlt_properties parent_props = MLT_PRODUCER_PROPERTIES( parent );

	mlt_properties_set_lcnumeric( properties,
		mlt_properties_get_lcnumeric( MLT_PRODUCER_PROPERTIES( self ) ) );

	mlt_events_block( MLT_PRODUCER_PROPERTIES( result ), MLT_PRODUCER_PROPERTIES( result ) );
	// Special case - allow for a cut of the entire producer (this will squeeze all other cuts to 0)
	if ( in <= 0 )
		in = 0;
	if ( ( out < 0 || out >= mlt_producer_get_length( parent ) ) && !mlt_producer_is_blank( self ) )
		out = mlt_producer_get_length( parent ) - 1;

	mlt_properties_inc_ref( parent_props );
	mlt_properties_set_int( properties, "_cut", 1 );
	mlt_properties_set_data( properties, "_cut_parent", parent, 0, ( mlt_destructor )mlt_producer_close, NULL );
	mlt_properties_set_position( properties, "length", mlt_properties_get_position( parent_props, "length" ) );
	mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( parent_props, "aspect_ratio" ) );
	mlt_producer_set_in_and_out( result, in, out );

	return result;
}
Example #13
0
static int getFrame(mlt_producer producer, mlt_frame_ptr frame, int /*index*/) {
    // Generate a frame
    *frame = mlt_frame_init(MLT_PRODUCER_SERVICE(producer));

    if (*frame) {
        // Obtain properties of frame and producer
        mlt_properties properties = MLT_FRAME_PROPERTIES(*frame);

        // Set the producer on the frame properties
        mlt_properties_set_data(properties, kWebVfxProducerPropertyName, producer, 0, NULL, NULL);

        // Update timecode on the frame we're creating
        mlt_position position = mlt_producer_position(producer);
        mlt_frame_set_position(*frame, position);
        mlt_properties_set_position(properties, kWebVfxPositionPropertyName, position);
        
        // Set producer-specific frame properties
        mlt_properties_set_int(properties, "progressive", 1);

        // Push the get_image method
        mlt_frame_push_get_image(*frame, producerGetImage);
    }

    // Calculate the next timecode
    mlt_producer_prepare_next(producer);

    return 0;
}
Example #14
0
File: mlt_frame.c Project: rayl/MLT
mlt_frame mlt_frame_init( mlt_service service )
{
	// Allocate a frame
	mlt_frame this = calloc( sizeof( struct mlt_frame_s ), 1 );

	if ( this != NULL )
	{
		mlt_profile profile = mlt_service_profile( service );

		// Initialise the properties
		mlt_properties properties = &this->parent;
		mlt_properties_init( properties, this );

		// Set default properties on the frame
		mlt_properties_set_position( properties, "_position", 0.0 );
		mlt_properties_set_data( properties, "image", NULL, 0, NULL, NULL );
		mlt_properties_set_int( properties, "width", profile? profile->width : 720 );
		mlt_properties_set_int( properties, "height", profile? profile->height : 576 );
		mlt_properties_set_int( properties, "normalised_width", profile? profile->width : 720 );
		mlt_properties_set_int( properties, "normalised_height", profile? profile->height : 576 );
		mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( NULL ) );
		mlt_properties_set_data( properties, "audio", NULL, 0, NULL, NULL );
		mlt_properties_set_data( properties, "alpha", NULL, 0, NULL, NULL );

		// Construct stacks for frames and methods
		this->stack_image = mlt_deque_init( );
		this->stack_audio = mlt_deque_init( );
		this->stack_service = mlt_deque_init( );
	}

	return this;
}
Example #15
0
mlt_frame mlt_filter_process( mlt_filter self, mlt_frame frame )
{
	mlt_properties properties = MLT_FILTER_PROPERTIES( self );
	int disable = mlt_properties_get_int( properties, "disable" );
	const char *unique_id = mlt_properties_get( properties, "_unique_id" );
	mlt_position position = mlt_frame_get_position( frame );
	char name[30];

	// Make the properties key from unique id
	snprintf( name, sizeof(name), "pos.%s", unique_id );
	name[sizeof(name) -1] = '\0';

	// Save the position on the frame
	mlt_properties_set_position( MLT_FRAME_PROPERTIES( frame ), name, position );

	if ( disable || self->process == NULL )
	{
		return frame;
	}
	else
	{
		// Add a reference to this filter on the frame
		mlt_properties_inc_ref( MLT_FILTER_PROPERTIES(self) );
		snprintf( name, sizeof(name), "filter.%s", unique_id );
		name[sizeof(name) -1] = '\0';
		mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), name, self, 0,
			(mlt_destructor) mlt_filter_close, NULL );

		return self->process( self, frame );
	}
}
Example #16
0
mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
{
	char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( transition ), "_unique_id" );
	mlt_properties_set_position( MLT_FRAME_PROPERTIES( a_frame ), name, mlt_frame_get_position( a_frame ) );
	mlt_frame_push_service( a_frame, transition );
	mlt_frame_push_frame( a_frame, b_frame );
	mlt_frame_push_get_image( a_frame, transition_get_image );
	return a_frame;
}
Example #17
0
File: hello.c Project: rayl/MLT
mlt_producer create_tracks( int argc, char **argv )
{
	// Create the field
	mlt_field field = mlt_field_init( );

	// Obtain the multitrack
	mlt_multitrack multitrack = mlt_field_multitrack( field );

	// Obtain the tractor
	mlt_tractor tractor = mlt_field_tractor( field );

	// Obtain a composite transition
	mlt_transition transition = mlt_factory_transition( "composite", "10%,10%:15%x15%" );

	// Create track 0
	mlt_producer track0 = create_playlist( argc, argv );

	// Get the length of track0
	mlt_position length = mlt_producer_get_playtime( track0 );

	// Create the watermark track
	mlt_producer track1 = mlt_factory_producer( NULL, "pango:" );

	// Get the properties of track1
	mlt_properties properties = mlt_producer_properties( track1 );

	// Set the properties
	mlt_properties_set( properties, "text", "Hello\nWorld" );
	mlt_properties_set_position( properties, "in", 0 );
	mlt_properties_set_position( properties, "out", length - 1 );
	mlt_properties_set_position( properties, "length", length );

	// Now set the properties on the transition
	properties = mlt_transition_properties( transition );
	mlt_properties_set_position( properties, "in", 0 );
	mlt_properties_set_position( properties, "out", length - 1 );

	// Add our tracks to the multitrack
	mlt_multitrack_connect( multitrack, track0, 0 );
	mlt_multitrack_connect( multitrack, track1, 1 );

	// Now plant the transition
	mlt_field_plant_transition( field, transition, 0, 1 );

	// Now set the properties on the transition
	properties = mlt_tractor_properties( tractor );

	// Ensure clean up and set properties correctly
	mlt_properties_set_data( properties, "multitrack", multitrack, 0, ( mlt_destructor )mlt_multitrack_close, NULL );
	mlt_properties_set_data( properties, "field", field, 0, ( mlt_destructor )mlt_field_close, NULL );
	mlt_properties_set_data( properties, "track0", track0, 0, ( mlt_destructor )mlt_producer_close, NULL );
	mlt_properties_set_data( properties, "track1", track1, 0, ( mlt_destructor )mlt_producer_close, NULL );
	mlt_properties_set_data( properties, "transition", transition, 0, ( mlt_destructor )mlt_transition_close, NULL );
	mlt_properties_set_position( properties, "length", length );
	mlt_properties_set_position( properties, "out", length - 1 );

	// Return the tractor
	return mlt_tractor_producer( tractor );
}
Example #18
0
mlt_producer producer_kino_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename )
{
	kino_wrapper wrapper = kino_wrapper_init( );

	if ( kino_wrapper_open( wrapper, filename ) )
	{
		producer_kino this = calloc( 1, sizeof( struct producer_kino_s ) );

		if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
		{
			mlt_producer producer = &this->parent;
			mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
			double fps = kino_wrapper_is_pal( wrapper ) ? 25 : 30000.0 / 1001.0;
	
			// Assign the wrapper
			this->wrapper = wrapper;

			// Pass wrapper properties (frame rate, count etc)
			mlt_properties_set_position( properties, "length", kino_wrapper_get_frame_count( wrapper ) );
			mlt_properties_set_position( properties, "in", 0 );
			mlt_properties_set_position( properties, "out", kino_wrapper_get_frame_count( wrapper ) - 1 );
			mlt_properties_set_double( properties, "real_fps", fps );
			mlt_properties_set( properties, "resource", filename );

			// Register transport implementation with the producer
			producer->close = ( mlt_destructor )producer_close;
	
			// Register our get_frame implementation with the producer
			producer->get_frame = producer_get_frame;
	
			// Return the producer
			return producer;
		}
		free( this );
	}

	kino_wrapper_close( wrapper );

	return NULL;
}
Example #19
0
int mlt_transition_init( mlt_transition self, void *child )
{
	mlt_service service = &self->parent;
	memset( self, 0, sizeof( struct mlt_transition_s ) );
	self->child = child;
	if ( mlt_service_init( service, self ) == 0 )
	{
		mlt_properties properties = MLT_TRANSITION_PROPERTIES( self );

		service->get_frame = transition_get_frame;
		service->close = ( mlt_destructor )mlt_transition_close;
		service->close_object = self;

		mlt_properties_set_position( properties, "in", 0 );
		mlt_properties_set_position( properties, "out", 0 );
		mlt_properties_set_int( properties, "a_track", 0 );
		mlt_properties_set_int( properties, "b_track", 1 );

		return 0;
	}
	return 1;
}
Example #20
0
static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
{
	// Get the real structure for this producer
	producer_qimage self = producer->child;

	// Fetch the producers properties
	mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );

	if ( self->filenames == NULL && mlt_properties_get( producer_properties, "resource" ) != NULL )
		load_filenames( self, producer_properties );

	// Generate a frame
	*frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );

	if ( *frame != NULL && self->count > 0 )
	{
		// Obtain properties of frame and producer
		mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );

		// Set the producer on the frame properties
		mlt_properties_set_data( properties, "producer_qimage", self, 0, NULL, NULL );

		// Update timecode on the frame we're creating
		mlt_frame_set_position( *frame, mlt_producer_position( producer ) );

		// Ensure that we have a way to obtain the position in the get_image
		mlt_properties_set_position( properties, "qimage_position", mlt_producer_position( producer ) );

		// Refresh the image
		self->qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" );
		self->qimage = mlt_cache_item_data( self->qimage_cache, NULL );
		refresh_qimage( self, *frame );
		mlt_cache_item_close( self->qimage_cache );

		// Set producer-specific frame properties
		mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( producer_properties, "progressive" ) );
		double force_ratio = mlt_properties_get_double( producer_properties, "force_aspect_ratio" );
		if ( force_ratio > 0.0 )
			mlt_properties_set_double( properties, "aspect_ratio", force_ratio );
		else
			mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "aspect_ratio" ) );

		// Push the get_image method
		mlt_frame_push_get_image( *frame, producer_get_image );
	}

	// Calculate the next timecode
	mlt_producer_prepare_next( producer );

	return 0;
}
Example #21
0
mlt_producer producer_qimage_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename )
{
	producer_qimage self = calloc( sizeof( struct producer_qimage_s ), 1 );
	if ( self != NULL && mlt_producer_init( &self->parent, self ) == 0 )
	{
		mlt_producer producer = &self->parent;

		// Get the properties interface
		mlt_properties properties = MLT_PRODUCER_PROPERTIES( &self->parent );
	
		// Callback registration
#ifdef USE_KDE
		init_qimage();
#endif
		producer->get_frame = producer_get_frame;
		producer->close = ( mlt_destructor )producer_close;

		// Set the default properties
		mlt_properties_set( properties, "resource", filename );
		mlt_properties_set_int( properties, "ttl", 25 );
		mlt_properties_set_int( properties, "aspect_ratio", 1 );
		mlt_properties_set_int( properties, "progressive", 1 );
		mlt_properties_set_int( properties, "seekable", 1 );

		// Validate the resource
		if ( filename )
			load_filenames( self, properties );
		if ( self->count )
		{
			mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
			if ( frame )
			{
				mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
				mlt_properties_set_data( frame_properties, "producer_qimage", self, 0, NULL, NULL );
				mlt_frame_set_position( frame, mlt_producer_position( producer ) );
				mlt_properties_set_position( frame_properties, "qimage_position", mlt_producer_position( producer ) );
				refresh_qimage( self, frame );
				mlt_cache_item_close( self->qimage_cache );
				mlt_frame_close( frame );
			}
		}
		if ( self->current_width == 0 )
		{
			producer_close( producer );
			producer = NULL;
		}
		return producer;
	}
	free( self );
	return NULL;
}
Example #22
0
void mlt_multitrack_refresh( mlt_multitrack self )
{
	int i = 0;

	// Obtain the properties of this multitrack
	mlt_properties properties = MLT_MULTITRACK_PROPERTIES( self );

	// We need to ensure that the multitrack reports the longest track as its length
	mlt_position length = 0;

	// Obtain stats on all connected services
	for ( i = 0; i < self->count; i ++ )
	{
		// Get the producer from this index
		mlt_track track = self->list[ i ];
		mlt_producer producer = track->producer;

		// If it's allocated then, update our stats
		if ( producer != NULL )
		{
			// If we have more than 1 track, we must be in continue mode
			if ( self->count > 1 )
				mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "continue" );

			// Determine the longest length
			//if ( !mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( producer ), "hide" ) )
				length = mlt_producer_get_playtime( producer ) > length ? mlt_producer_get_playtime( producer ) : length;
		}
	}

	// Update multitrack properties now - we'll not destroy the in point here
	mlt_events_block( properties, properties );
	mlt_properties_set_position( properties, "length", length );
	mlt_events_unblock( properties, properties );
	mlt_properties_set_position( properties, "out", length - 1 );
}
Example #23
0
mlt_producer producer_pixbuf_init( char *filename )
{
	producer_pixbuf this = calloc( sizeof( struct producer_pixbuf_s ), 1 );
	if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
	{
		mlt_producer producer = &this->parent;

		// Get the properties interface
		mlt_properties properties = MLT_PRODUCER_PROPERTIES( &this->parent );
	
		// Callback registration
		producer->get_frame = producer_get_frame;
		producer->close = ( mlt_destructor )producer_close;

		// Set the default properties
		mlt_properties_set( properties, "resource", filename );
		mlt_properties_set_int( properties, "ttl", 25 );
		mlt_properties_set_int( properties, "aspect_ratio", 1 );
		mlt_properties_set_int( properties, "progressive", 1 );

		// Validate the resource
		if ( filename )
			load_filenames( this, properties );
		if ( this->count )
		{
			mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
			if ( frame )
			{
				mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
				pthread_mutex_init( &this->mutex, NULL );
				mlt_properties_set_data( frame_properties, "producer_pixbuf", this, 0, NULL, NULL );
				mlt_frame_set_position( frame, mlt_producer_position( producer ) );
				mlt_properties_set_position( frame_properties, "pixbuf_position", mlt_producer_position( producer ) );
				refresh_image( this, frame, 0, 0 );
				mlt_frame_close( frame );
			}
		}
		if ( this->width == 0 )
		{
			producer_close( producer );
			producer = NULL;
		}
		return producer;
	}
	free( this );
	return NULL;
}
Example #24
0
mlt_frame mlt_filter_process( mlt_filter self, mlt_frame frame )
{
	mlt_properties properties = MLT_FILTER_PROPERTIES( self );
	int disable = mlt_properties_get_int( properties, "disable" );
	const char *unique_id = mlt_properties_get( properties, "_unique_id" );
	mlt_position position = mlt_frame_get_position( frame );
	char name[20];

	// Make the properties key from unique id
	strcpy( name, "pos." );
	strcat( name, unique_id );

	// Save the position on the frame
	mlt_properties_set_position( MLT_FRAME_PROPERTIES( frame ), name, position );

	if ( disable || self->process == NULL )
		return frame;
	else
		return self->process( self, frame );
}
Example #25
0
static void foreach_consumer_start( mlt_consumer consumer )
{
    mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
    mlt_consumer nested = NULL;
    char key[30];
    int index = 0;

    do {
        snprintf( key, sizeof(key), "%d.consumer", index++ );
        nested = mlt_properties_get_data( properties, key, NULL );
        if ( nested )
        {
            mlt_properties nested_props = MLT_CONSUMER_PROPERTIES(nested);
            mlt_properties_set_position( nested_props, "_multi_position", 0 );
            mlt_properties_set_data( nested_props, "_multi_audio", NULL, 0, NULL, NULL );
            mlt_properties_set_int( nested_props, "_multi_samples", 0 );
            mlt_consumer_start( nested );
        }
    } while ( nested );
}
Example #26
0
static void analyze_audio( mlt_filter filter, void* buffer, int samples )
{
	mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
	private_data* pdata = (private_data*)filter->child;
	int result = -1;
	double loudness = 0.0;

	ebur128_add_frames_float( pdata->r128, buffer, samples );

	if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_program" ) )
	{
		result = ebur128_loudness_global( pdata->r128, &loudness );
		if( result == EBUR128_SUCCESS && loudness != HUGE_VAL && loudness != -HUGE_VAL )
		{
			mlt_properties_set_double( properties, "program", loudness );
		}
	}

	if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_shortterm" ) )
	{
		result = ebur128_loudness_shortterm( pdata->r128, &loudness );
		if( result == EBUR128_SUCCESS && loudness != HUGE_VAL && loudness != -HUGE_VAL )
		{
			mlt_properties_set_double( properties, "shortterm", loudness );
		}
	}

	if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_momentary" ) )
	{
		result = ebur128_loudness_momentary( pdata->r128, &loudness );
		if( result == EBUR128_SUCCESS && loudness != HUGE_VAL && loudness != -HUGE_VAL )
		{
			mlt_properties_set_double( properties, "momentary", loudness );
		}
	}

	if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_range" ) )
	{
		double range = 0;
		result = ebur128_loudness_range( pdata->r128, &range );
		if( result == EBUR128_SUCCESS && range != HUGE_VAL && range != -HUGE_VAL )
		{
			mlt_properties_set_double( properties, "range", range );
		}
	}

	if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_peak" ) )
	{
		double prev_peak = 0.0;
		double max_peak = 0.0;
		int c = 0;
		for( c = 0; c < pdata->r128->channels; c++ )
		{
			double peak;
			result = ebur128_sample_peak( pdata->r128, c, &peak );
			if( result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > max_peak )
			{
				max_peak = peak;
			}
			result = ebur128_prev_sample_peak( pdata->r128, c, &peak );
			if( result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > prev_peak )
			{
				prev_peak = peak;
			}
		}
		mlt_properties_set_double( properties, "max_peak", 20 * log10(max_peak) );
		mlt_properties_set_double( properties, "peak", 20 * log10(prev_peak) );
	}

	if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_true_peak" ) )
	{
		double prev_peak = 0.0;
		double max_peak = 0.0;
		int c = 0;
		for( c = 0; c < pdata->r128->channels; c++ )
		{
			double peak;
			result = ebur128_true_peak( pdata->r128, c, &peak );
			if( result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > max_peak )
			{
				max_peak = peak;
			}
			result = ebur128_prev_true_peak( pdata->r128, c, &peak );
			if( result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > prev_peak )
			{
				prev_peak = peak;
			}
		}
		mlt_properties_set_double( properties, "max_true_peak", 20 * log10(max_peak) );
		mlt_properties_set_double( properties, "true_peak", 20 * log10(prev_peak) );
	}

	mlt_properties_set_position( properties, "frames_processed", mlt_properties_get_position( properties, "frames_processed" ) + 1 );
}
Example #27
0
mlt_producer producer_framebuffer_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
{
	if ( !arg ) return NULL;
	mlt_producer producer = NULL;
	producer = calloc( 1, sizeof( struct mlt_producer_s ) );
	mlt_producer_init( producer, NULL );

	// Wrap loader
	mlt_producer real_producer;
	
	// Check if a speed was specified.
	/** 

	* Speed must be appended to the filename with '?'. To play your video at 50%:
	 melt framebuffer:my_video.mpg?0.5

	* Stroboscope effect can be obtained by adding a stobe=x parameter, where
	 x is the number of frames that will be ignored.

	* You can play the movie backwards by adding reverse=1

	* You can freeze the clip at a determined position by adding freeze=frame_pos
	  add freeze_after=1 to freeze only paste position or freeze_before to freeze before it

	**/

	double speed = 0.0;
	char *props = strdup( arg );
	char *ptr = strrchr( props, '?' );
	
	if ( ptr )
	{
		speed = atof( ptr + 1 );
		if ( speed != 0.0 )
			// If speed was valid, then strip it and the delimiter.
			// Otherwise, an invalid speed probably means this '?' was not a delimiter.
			*ptr = '\0';
	}
		
	real_producer = mlt_factory_producer( profile, "abnormal", props );
	free( props );

	if (speed == 0.0) speed = 1.0;

	if ( producer != NULL && real_producer != NULL)
	{
		// Get the properties of this producer
		mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );

		mlt_properties_set( properties, "resource", arg);

		// Store the producer and fitler
		mlt_properties_set_data( properties, "producer", real_producer, 0, ( mlt_destructor )mlt_producer_close, NULL );

		// Grab some stuff from the real_producer
		mlt_properties_pass_list( properties, MLT_PRODUCER_PROPERTIES( real_producer ), "length, width, height, aspect_ratio" );

		if ( speed < 0 )
		{
			speed = -speed;
			mlt_properties_set_int( properties, "reverse", 1 );
		}

		if ( speed != 1.0 )
		{
			double real_length = ( (double)  mlt_producer_get_length( real_producer ) ) / speed;
			mlt_properties_set_position( properties, "length", real_length );
		}
		mlt_properties_set_position( properties, "out", mlt_producer_get_length( producer ) - 1 );

		// Since we control the seeking, prevent it from seeking on its own
		mlt_producer_set_speed( real_producer, 0 );
		mlt_producer_set_speed( producer, speed );

		// Override the get_frame method
		producer->get_frame = producer_get_frame;
	}
	else
	{
		if ( producer )
			mlt_producer_close( producer );
		if ( real_producer )
			mlt_producer_close( real_producer );

		producer = NULL;
	}
	return producer;
}
Example #28
0
static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
{
	mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
	mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );

	// Only if mix is specified, otherwise a producer may set the mix
	if ( mlt_properties_get( properties, "start" ) )
	{
		// Determine the time position of this frame in the transition duration
		mlt_properties props = mlt_properties_get_data( MLT_FRAME_PROPERTIES( b_frame ), "_producer", NULL );
		mlt_position in = mlt_properties_get_int( props, "in" );
		mlt_position out = mlt_properties_get_int( props, "out" );
		int length = mlt_properties_get_int( properties, "length" );
		mlt_position time = mlt_properties_get_int( props, "_frame" );
		double mix = mlt_transition_get_progress( transition, b_frame );
		if ( mlt_properties_get_int(  properties, "always_active" ) )
			mix = ( double ) ( time - in ) / ( double ) ( out - in + 1 );

		// TODO: Check the logic here - shouldn't we be computing current and next mixing levels in all cases?
		if ( length == 0 )
		{
			// If there is an end mix level adjust mix to the range
			if ( mlt_properties_get( properties, "end" ) )
			{
				double start = mlt_properties_get_double( properties, "start" );
				double end = mlt_properties_get_double( properties, "end" );
				mix = start + ( end - start ) * mix;
			}
			// A negative means total crossfade (uses position)
			else if ( mlt_properties_get_double( properties, "start" ) >= 0 )
			{
				// Otherwise, start/constructor is a constant mix level
		    	mix = mlt_properties_get_double( properties, "start" );
			}
		
			// Finally, set the mix property on the frame
			mlt_properties_set_double( b_props, "audio.mix", mix );
	
			// Initialise transition previous mix value to prevent an inadvertant jump from 0
			mlt_position last_position = mlt_properties_get_position( properties, "_last_position" );
			mlt_position current_position = mlt_frame_get_position( b_frame );
			mlt_properties_set_position( properties, "_last_position", current_position );
			if ( !mlt_properties_get( properties, "_previous_mix" )
			     || current_position != last_position + 1 )
				mlt_properties_set_double( properties, "_previous_mix", mix );
				
			// Tell b frame what the previous mix level was
			mlt_properties_set_double( b_props, "audio.previous_mix", mlt_properties_get_double( properties, "_previous_mix" ) );

			// Save the current mix level for the next iteration
			mlt_properties_set_double( properties, "_previous_mix", mlt_properties_get_double( b_props, "audio.mix" ) );
		
			mlt_properties_set_double( b_props, "audio.reverse", mlt_properties_get_double( properties, "reverse" ) );
		}
		else
		{
			double level = mlt_properties_get_double( properties, "start" );
			double mix_start = level;
			double mix_end = mix_start;
			double mix_increment = 1.0 / length;
			if ( time - in < length )
			{
				mix_start = mix_start * ( ( double )( time - in ) / length );
				mix_end = mix_start + mix_increment;
			}
			else if ( time > out - length )
			{
				mix_end = mix_start * ( ( double )( out - time - in ) / length );
				mix_start = mix_end - mix_increment;
			}

			mix_start = mix_start < 0 ? 0 : mix_start > level ? level : mix_start;
			mix_end = mix_end < 0 ? 0 : mix_end > level ? level : mix_end;
			mlt_properties_set_double( b_props, "audio.previous_mix", mix_start );
			mlt_properties_set_double( b_props, "audio.mix", mix_end );
		}
	}

	// Override the get_audio method
	mlt_frame_push_audio( a_frame, transition );
	mlt_frame_push_audio( a_frame, b_frame );
	mlt_frame_push_audio( a_frame, transition_get_audio );
	
	return a_frame;
}
Example #29
0
static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
{
	// Get the filter from the frame
	mlt_filter this = mlt_frame_pop_audio( frame );

	// Get the properties from the filter
	mlt_properties filter_props = MLT_FILTER_PROPERTIES( this );

	// Get the frame's filter instance properties
	mlt_properties instance_props = mlt_frame_unique_properties( frame, MLT_FILTER_SERVICE( this ) );

	// Get the parameters
	double gain = mlt_properties_get_double( instance_props, "gain" );
	double max_gain = mlt_properties_get_double( instance_props, "max_gain" );
	double limiter_level = 0.5; /* -6 dBFS */
	int normalise =  mlt_properties_get_int( instance_props, "normalise" );
	double amplitude =  mlt_properties_get_double( instance_props, "amplitude" );
	int i, j;
	double sample;
	int16_t peak;

	if ( mlt_properties_get( instance_props, "limiter" ) != NULL )
		limiter_level = mlt_properties_get_double( instance_props, "limiter" );
	
	// Get the producer's audio
	*format = mlt_audio_s16;
	mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
//	fprintf( stderr, "filter_volume: frequency %d\n", *frequency );

	// Determine numeric limits
	int bytes_per_samp = (samp_width - 1) / 8 + 1;
	int samplemax = (1 << (bytes_per_samp * 8 - 1)) - 1;
	int samplemin = -samplemax - 1;

	mlt_service_lock( MLT_FILTER_SERVICE( this ) );

	if ( normalise )
	{
		int window = mlt_properties_get_int( filter_props, "window" );
		double *smooth_buffer = mlt_properties_get_data( filter_props, "smooth_buffer", NULL );

		if ( window > 0 && smooth_buffer != NULL )
		{
			int smooth_index = mlt_properties_get_int( filter_props, "_smooth_index" );
			
			// Compute the signal power and put into smoothing buffer
			smooth_buffer[ smooth_index ] = signal_max_power( *buffer, *channels, *samples, &peak );
//			fprintf( stderr, "filter_volume: raw power %f ", smooth_buffer[ smooth_index ] );
			if ( smooth_buffer[ smooth_index ] > EPSILON )
			{
				mlt_properties_set_int( filter_props, "_smooth_index", ( smooth_index + 1 ) % window );

				// Smooth the data and compute the gain
//				fprintf( stderr, "smoothed %f over %d frames\n", get_smoothed_data( smooth_buffer, window ), window );
				gain *= amplitude / get_smoothed_data( smooth_buffer, window );
			}
		}
		else
		{
			gain *= amplitude / signal_max_power( (int16_t*) *buffer, *channels, *samples, &peak );
		}
	}
	
//	if ( gain > 1.0 && normalise )
//		fprintf(stderr, "filter_volume: limiter level %f gain %f\n", limiter_level, gain );

	if ( max_gain > 0 && gain > max_gain )
		gain = max_gain;

	// Initialise filter's previous gain value to prevent an inadvertant jump from 0
	mlt_position last_position = mlt_properties_get_position( filter_props, "_last_position" );
	mlt_position current_position = mlt_frame_get_position( frame );
	if ( mlt_properties_get( filter_props, "_previous_gain" ) == NULL
	     || current_position != last_position + 1 )
		mlt_properties_set_double( filter_props, "_previous_gain", gain );

	// Start the gain out at the previous
	double previous_gain = mlt_properties_get_double( filter_props, "_previous_gain" );

	// Determine ramp increment
	double gain_step = ( gain - previous_gain ) / *samples;
//	fprintf( stderr, "filter_volume: previous gain %f current gain %f step %f\n", previous_gain, gain, gain_step );

	// Save the current gain for the next iteration
	mlt_properties_set_double( filter_props, "_previous_gain", gain );
	mlt_properties_set_position( filter_props, "_last_position", current_position );

	mlt_service_unlock( MLT_FILTER_SERVICE( this ) );

	// Ramp from the previous gain to the current
	gain = previous_gain;

	int16_t *p = (int16_t*) *buffer;

	// Apply the gain
	for ( i = 0; i < *samples; i++ )
	{
		for ( j = 0; j < *channels; j++ )
		{
			sample = *p * gain;
			*p = ROUND( sample );
		
			if ( gain > 1.0 )
			{
				/* use limiter function instead of clipping */
				if ( normalise )
					*p = ROUND( samplemax * limiter( sample / (double) samplemax, limiter_level ) );
				
				/* perform clipping */
				else if ( sample > samplemax )
					*p = samplemax;
				else if ( sample < samplemin )
					*p = samplemin;
			}
			p++;
		}
		gain += gain_step;
	}
	
	return 0;
}
Example #30
0
static void foreach_consumer_put( mlt_consumer consumer, mlt_frame frame )
{
    mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
    mlt_consumer nested = NULL;
    char key[30];
    int index = 0;

    do {
        snprintf( key, sizeof(key), "%d.consumer", index++ );
        nested = mlt_properties_get_data( properties, key, NULL );
        if ( nested )
        {
            mlt_properties nested_props = MLT_CONSUMER_PROPERTIES(nested);
            double self_fps = mlt_properties_get_double( properties, "fps" );
            double nested_fps = mlt_properties_get_double( nested_props, "fps" );
            mlt_position nested_pos = mlt_properties_get_position( nested_props, "_multi_position" );
            mlt_position self_pos = mlt_frame_get_position( frame );
            double self_time = self_pos / self_fps;
            double nested_time = nested_pos / nested_fps;

            // get the audio for the current frame
            uint8_t *buffer = NULL;
            mlt_audio_format format = mlt_audio_s16;
            int channels = mlt_properties_get_int( properties, "channels" );
            int frequency = mlt_properties_get_int( properties, "frequency" );
            int current_samples = mlt_sample_calculator( self_fps, frequency, self_pos );
            mlt_frame_get_audio( frame, (void**) &buffer, &format, &frequency, &channels, &current_samples );
            int current_size = mlt_audio_format_size( format, current_samples, channels );

            // get any leftover audio
            int prev_size = 0;
            uint8_t *prev_buffer = mlt_properties_get_data( nested_props, "_multi_audio", &prev_size );
            uint8_t *new_buffer = NULL;
            if ( prev_size > 0 )
            {
                new_buffer = mlt_pool_alloc( prev_size + current_size );
                memcpy( new_buffer, prev_buffer, prev_size );
                memcpy( new_buffer + prev_size, buffer, current_size );
                buffer = new_buffer;
            }
            current_size += prev_size;
            current_samples += mlt_properties_get_int( nested_props, "_multi_samples" );

            while ( nested_time <= self_time )
            {
                // put ideal number of samples into cloned frame
                int deeply = index > 1 ? 1 : 0;
                mlt_frame clone_frame = mlt_frame_clone( frame, deeply );
                int nested_samples = mlt_sample_calculator( nested_fps, frequency, nested_pos );
                // -10 is an optimization to avoid tiny amounts of leftover samples
                nested_samples = nested_samples > current_samples - 10 ? current_samples : nested_samples;
                int nested_size = mlt_audio_format_size( format, nested_samples, channels );
                if ( nested_size > 0 )
                {
                    prev_buffer = mlt_pool_alloc( nested_size );
                    memcpy( prev_buffer, buffer, nested_size );
                }
                else
                {
                    prev_buffer = NULL;
                    nested_size = 0;
                }
                mlt_frame_set_audio( clone_frame, prev_buffer, format, nested_size, mlt_pool_release );
                mlt_properties_set_int( MLT_FRAME_PROPERTIES(clone_frame), "audio_samples", nested_samples );
                mlt_properties_set_int( MLT_FRAME_PROPERTIES(clone_frame), "audio_frequency", frequency );
                mlt_properties_set_int( MLT_FRAME_PROPERTIES(clone_frame), "audio_channels", channels );

                // chomp the audio
                current_samples -= nested_samples;
                current_size -= nested_size;
                buffer += nested_size;

                // send frame to nested consumer
                mlt_consumer_put_frame( nested, clone_frame );
                mlt_properties_set_position( nested_props, "_multi_position", ++nested_pos );
                nested_time = nested_pos / nested_fps;
            }

            // save any remaining audio
            if ( current_size > 0 )
            {
                prev_buffer = mlt_pool_alloc( current_size );
                memcpy( prev_buffer, buffer, current_size );
            }
            else
            {
                prev_buffer = NULL;
                current_size = 0;
            }
            mlt_pool_release( new_buffer );
            mlt_properties_set_data( nested_props, "_multi_audio", prev_buffer, current_size, mlt_pool_release, NULL );
            mlt_properties_set_int( nested_props, "_multi_samples", current_samples );
        }
    } while ( nested );
}