/** Append to the virtual playlist.
*/
-static int mlt_playlist_virtual_append( mlt_playlist this, mlt_producer producer, mlt_position in, mlt_position out )
+static int mlt_playlist_virtual_append( mlt_playlist this, mlt_producer source, mlt_position in, mlt_position out )
{
+ mlt_producer producer = mlt_producer_is_cut( source ) ? source : mlt_producer_cut( source, in, out );
mlt_properties properties = mlt_producer_properties( producer );
+ // If we have a cut, then use the in/out points from the cut
+ if ( mlt_producer_is_cut( source ) )
+ {
+ mlt_properties_inc_ref( properties );
+ in = mlt_producer_get_in( source );
+ out = mlt_producer_get_out( source );
+ }
+
// Check that we have room
if ( this->count >= this->size )
{
this->count ++;
- mlt_properties_inc_ref( properties );
return mlt_playlist_virtual_refresh( this );
}
// Seek in real producer to relative position
if ( producer != NULL )
{
- position += this->list[ i ]->frame_in;
mlt_producer_seek( producer, position );
}
else if ( !strcmp( eof, "pause" ) && total > 0 )
mlt_producer this_producer = mlt_playlist_producer( this );
mlt_producer_seek( this_producer, 0 );
producer = entry->producer;
- mlt_producer_seek( producer, entry->frame_in );
+ mlt_producer_seek( producer, 0 );
}
else
{
return this;
}
+/** Determine if producer is a cut.
+*/
+
+int mlt_producer_is_cut( mlt_producer this )
+{
+ return mlt_properties_get_int( mlt_producer_properties( this ), "_cut" );
+}
+
+/** Obtain the parent producer.
+*/
+
+mlt_producer mlt_producer_cut_parent( mlt_producer this )
+{
+ mlt_properties properties = mlt_producer_properties( this );
+ if ( mlt_producer_is_cut( this ) )
+ return mlt_properties_get_data( properties, "_cut_parent", NULL );
+ else
+ return this;
+}
+
+/** Create a cut of this producer
+*/
+
+mlt_producer mlt_producer_cut( mlt_producer this, int in, int out )
+{
+ mlt_producer result = mlt_producer_new( );
+ mlt_producer parent = mlt_producer_cut_parent( this );
+ mlt_properties properties = mlt_producer_properties( result );
+ mlt_properties parent_props = mlt_producer_properties( parent );
+
+ // 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 >= mlt_producer_get_playtime( parent ) )
+ out = mlt_producer_get_playtime( 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_position( properties, "in", 0 );
+ mlt_properties_set_position( properties, "out", 0 );
+ mlt_producer_set_in_and_out( result, in, out );
+
+ return result;
+}
+
/** Get the parent service object.
*/
int mlt_producer_seek( mlt_producer this, mlt_position position )
{
// Determine eof handling
- char *eof = mlt_properties_get( mlt_producer_properties( this ), "eof" );
- int use_points = 1 - mlt_properties_get_int( mlt_producer_properties( this ), "ignore_points" );
+ mlt_properties properties = mlt_producer_properties( this );
+ char *eof = mlt_properties_get( properties, "eof" );
+ int use_points = 1 - mlt_properties_get_int( properties, "ignore_points" );
+
+ // Recursive behaviour for cuts...
+ if ( mlt_producer_is_cut( this ) )
+ mlt_producer_seek( mlt_producer_cut_parent( this ), position + mlt_producer_get_in( this ) );
// Check bounds
if ( position < 0 )
int result = 1;
mlt_producer this = service->child;
- // Determine eof handling
- char *eof = mlt_properties_get( mlt_producer_properties( this ), "eof" );
-
- // A properly instatiated producer will have a get_frame method...
- if ( this->get_frame == NULL || ( !strcmp( eof, "continue" ) && mlt_producer_position( this ) > mlt_producer_get_out( this ) ) )
+ if ( !mlt_producer_is_cut( this ) )
{
- // Generate a test frame
- *frame = mlt_frame_init( );
+ // Determine eof handling
+ char *eof = mlt_properties_get( mlt_producer_properties( this ), "eof" );
+
+ // A properly instatiated producer will have a get_frame method...
+ if ( this->get_frame == NULL || ( !strcmp( eof, "continue" ) && mlt_producer_position( this ) > mlt_producer_get_out( this ) ) )
+ {
+ // Generate a test frame
+ *frame = mlt_frame_init( );
- // Set the position
- result = mlt_frame_set_position( *frame, mlt_producer_position( this ) );
+ // Set the position
+ result = mlt_frame_set_position( *frame, mlt_producer_position( this ) );
- // Mark as a test card
- mlt_properties_set_int( mlt_frame_properties( *frame ), "test_image", 1 );
- mlt_properties_set_int( mlt_frame_properties( *frame ), "test_audio", 1 );
+ // Mark as a test card
+ mlt_properties_set_int( mlt_frame_properties( *frame ), "test_image", 1 );
+ mlt_properties_set_int( mlt_frame_properties( *frame ), "test_audio", 1 );
- // Calculate the next position
- mlt_producer_prepare_next( this );
+ // Calculate the next position
+ mlt_producer_prepare_next( this );
+ }
+ else
+ {
+ // Get the frame from the implementation
+ result = this->get_frame( this, frame, index );
+ }
+
+ // Copy the fps and speed of the producer onto the frame
+ mlt_properties properties = mlt_frame_properties( *frame );
+ double speed = mlt_producer_get_speed( this );
+ mlt_properties_set_double( properties, "_speed", speed );
+ mlt_properties_set_double( properties, "fps", mlt_producer_get_fps( this ) );
+ mlt_properties_set_int( properties, "test_audio", mlt_frame_is_test_audio( *frame ) );
+ mlt_properties_set_int( properties, "test_image", mlt_frame_is_test_card( *frame ) );
}
else
{
- // Get the frame from the implementation
- result = this->get_frame( this, frame, index );
+ mlt_properties properties = mlt_producer_properties( this );
+ mlt_producer_seek( this, mlt_properties_get_int( properties, "_position" ) );
+ result = producer_get_frame( mlt_producer_service( mlt_producer_cut_parent( this ) ), frame, index );
+ double speed = mlt_producer_get_speed( this );
+ mlt_properties_set_double( mlt_frame_properties( *frame ), "_speed", speed );
+ mlt_producer_prepare_next( this );
}
- // Copy the fps and speed of the producer onto the frame
- mlt_properties properties = mlt_frame_properties( *frame );
- mlt_properties_set_double( properties, "fps", mlt_producer_get_fps( this ) );
- double speed = mlt_producer_get_speed( this );
- mlt_properties_set_double( properties, "_speed", speed );
- mlt_properties_set_int( properties, "test_audio", mlt_frame_is_test_audio( *frame ) );
- mlt_properties_set_int( properties, "test_image", mlt_frame_is_test_card( *frame ) );
-
- return 0;
+ return result;
}
/** Attach a filter.
extern int mlt_producer_attach( mlt_producer self, mlt_filter filter );
extern int mlt_producer_detach( mlt_producer self, mlt_filter filter );
extern mlt_filter mlt_producer_filter( mlt_producer self, int index );
+extern mlt_producer mlt_producer_cut( mlt_producer self, int in, int out );
+extern int mlt_producer_is_cut( mlt_producer self );
+extern mlt_producer mlt_producer_cut_parent( mlt_producer self );
extern void mlt_producer_close( mlt_producer self );
#endif
/** Memory leak checks.
*/
-//#define _MLT_PROPERTY_CHECKS_
+#define _MLT_PROPERTY_CHECKS_
#ifdef _MLT_PROPERTY_CHECKS_
static int properties_created = 0;
mlt_service_base *base = this->local;
mlt_position position = mlt_properties_get_position( frame_properties, "_position" );
+ // Hmm - special case for cuts - apply filters from the parent first
+ if ( mlt_properties_get_int( filter_properties, "_cut" ) )
+ mlt_service_apply_filters( ( mlt_service )mlt_properties_get_data( filter_properties, "_cut_parent", NULL ), frame, 0 );
+
if ( index == 0 || mlt_properties_get_int( filter_properties, "_filter_private" ) == 0 )
{
// Process the frame with the attached filters
{
if ( info.producer != NULL )
{
- char *service_s = mlt_properties_get( mlt_producer_properties( info.producer ), "mlt_service" );
- char *resource_s = mlt_properties_get( mlt_producer_properties( info.producer ), "resource" );
+ mlt_producer producer = mlt_producer_cut_parent( info.producer );
+ char *service_s = mlt_properties_get( mlt_producer_properties( producer ), "mlt_service" );
+ char *resource_s = mlt_properties_get( mlt_producer_properties( producer ), "resource" );
if ( resource_s != NULL && !strcmp( resource_s, "<tractor>" ) )
{
- serialise_tractor( context, MLT_SERVICE( info.producer ), node );
+ serialise_tractor( context, MLT_SERVICE( producer ), node );
context->pass ++;
- serialise_tractor( context, MLT_SERVICE( info.producer ), node );
+ serialise_tractor( context, MLT_SERVICE( producer ), node );
context->pass --;
}
else if ( service_s != NULL && strcmp( service_s, "blank" ) != 0 )
- serialise_service( context, MLT_SERVICE( info.producer ), node );
+ serialise_service( context, MLT_SERVICE( producer ), node );
else if ( resource_s != NULL && !strcmp( resource_s, "<playlist>" ) )
- serialise_playlist( context, MLT_SERVICE( info.producer ), node );
+ serialise_playlist( context, MLT_SERVICE( producer ), node );
}
}
}
{
if ( ! mlt_playlist_get_clip_info( MLT_PLAYLIST( service ), &info, i ) )
{
- char *service_s = mlt_properties_get( mlt_producer_properties( info.producer ), "mlt_service" );
+ mlt_producer producer = mlt_producer_cut_parent( info.producer );
+ char *service_s = mlt_properties_get( mlt_producer_properties( producer ), "mlt_service" );
if ( service_s != NULL && strcmp( service_s, "blank" ) == 0 )
{
char length[ 20 ];
{
char temp[ 20 ];
xmlNode *entry = xmlNewChild( child, NULL, "entry", NULL );
- id = westley_get_id( context, MLT_SERVICE( info.producer ), westley_existing );
+ id = westley_get_id( context, MLT_SERVICE( producer ), westley_existing );
xmlNewProp( entry, "producer", id );
sprintf( temp, "%d", info.frame_in );
xmlNewProp( entry, "in", temp );
sprintf( temp, "%d", info.frame_out );
xmlNewProp( entry, "out", temp );
+ if ( mlt_producer_is_cut( info.producer ) )
+ serialise_service_filters( context, mlt_producer_service( info.producer ), entry );
}
}
}
}
}
-static void on_start_entry_track( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+static void on_start_entry( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+ mlt_producer entry = NULL;
+ mlt_properties temp = mlt_properties_new( );
+
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ {
+ mlt_properties_set( temp, (char*) atts[0], (char*) atts[1] );
+
+ // Look for the producer attribute
+ if ( strcmp( atts[ 0 ], "producer" ) == 0 )
+ {
+ mlt_producer producer = mlt_properties_get_data( context->producer_map, (char*) atts[1], NULL );
+ if ( producer != NULL )
+ mlt_properties_set_data( temp, "producer", producer, 0, NULL, NULL );
+ }
+ }
+
+ // If we have a valid entry
+ if ( mlt_properties_get_data( temp, "producer", NULL ) != NULL )
+ {
+ mlt_playlist_clip_info info;
+ enum service_type parent_type;
+ mlt_service parent = context_pop_service( context, &parent_type );
+ mlt_producer producer = mlt_properties_get_data( temp, "producer", NULL );
+
+ if ( parent_type == mlt_playlist_type )
+ {
+ // Append the producer to the playlist
+ if ( mlt_properties_get( temp, "in" ) != NULL || mlt_properties_get( temp, "out" ) != NULL )
+ {
+ mlt_playlist_append_io( MLT_PLAYLIST( parent ), producer,
+ mlt_properties_get_position( temp, "in" ),
+ mlt_properties_get_position( temp, "out" ) );
+ }
+ else
+ {
+ mlt_playlist_append( MLT_PLAYLIST( parent ), producer );
+ }
+
+ mlt_playlist_get_clip_info( MLT_PLAYLIST( parent ), &info, mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1 );
+ entry = info.producer;
+ }
+ else
+ {
+ fprintf( stderr, "Entry not part of a playlist...\n" );
+ }
+
+ context_push_service( context, parent, parent_type );
+ }
+
+ // Push the cut onto the stack
+ context_push_service( context, mlt_producer_service( entry ), mlt_entry_type );
+
+ mlt_properties_close( temp );
+}
+
+static void on_end_entry( deserialise_context context, const xmlChar *name )
+{
+ // Get the entry from the stack
+ enum service_type entry_type;
+ mlt_service entry = context_pop_service( context, &entry_type );
+
+ if ( entry == NULL && entry_type != mlt_entry_type )
+ {
+ fprintf( stderr, "Invalid state at end of entry\n" );
+ }
+}
+
+static void on_start_track( deserialise_context context, const xmlChar *name, const xmlChar **atts)
{
// use a dummy service to hold properties to allow arbitrary nesting
mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
// Push the dummy service onto the stack
context_push_service( context, service, mlt_entry_type );
- if ( strcmp( name, "entry" ) == 0 )
- mlt_properties_set( mlt_service_properties( service ), "resource", "<entry>" );
- else
- mlt_properties_set( mlt_service_properties( service ), "resource", "<track>" );
+ mlt_properties_set( mlt_service_properties( service ), "resource", "<track>" );
for ( ; atts != NULL && *atts != NULL; atts += 2 )
{
}
}
-static void on_end_entry( deserialise_context context, const xmlChar *name )
-{
- // Get the entry from the stack
- enum service_type entry_type;
- mlt_service entry = context_pop_service( context, &entry_type );
-
- if ( entry != NULL && entry_type == mlt_entry_type )
- {
- mlt_properties entry_props = mlt_service_properties( entry );
- enum service_type parent_type;
- mlt_service parent = context_pop_service( context, &parent_type );
- mlt_producer producer = mlt_properties_get_data( entry_props, "producer", NULL );
-
- if ( parent_type == mlt_playlist_type )
- {
- // Append the producer to the playlist
- if ( mlt_properties_get( mlt_service_properties( entry ), "in" ) != NULL ||
- mlt_properties_get( mlt_service_properties( entry ), "out" ) != NULL )
- {
- mlt_playlist_append_io( MLT_PLAYLIST( parent ), producer,
- mlt_properties_get_position( mlt_service_properties( entry ), "in" ),
- mlt_properties_get_position( mlt_service_properties( entry ), "out" ) );
- }
- else
- {
- mlt_playlist_append( MLT_PLAYLIST( parent ), producer );
- }
- }
- else
- {
- fprintf( stderr, "Invalid position for an entry...\n" );
- }
-
- if ( parent != NULL )
- context_push_service( context, parent, parent_type );
-
- mlt_service_close( entry );
- }
- else
- {
- fprintf( stderr, "Invalid state at end of entry\n" );
- }
-}
-
static void on_start_filter( deserialise_context context, const xmlChar *name, const xmlChar **atts)
{
// use a dummy service to hold properties to allow arbitrary nesting
on_start_producer( context, name, atts );
else if ( strcmp( name, "blank" ) == 0 )
on_start_blank( context, name, atts );
- else if ( strcmp( name, "entry" ) == 0 || strcmp( name, "track" ) == 0 )
- on_start_entry_track( context, name, atts );
+ else if ( strcmp( name, "entry" ) == 0 )
+ on_start_entry( context, name, atts );
+ else if ( strcmp( name, "track" ) == 0 )
+ on_start_track( context, name, atts );
else if ( strcmp( name, "filter" ) == 0 )
on_start_filter( context, name, atts );
else if ( strcmp( name, "transition" ) == 0 )