This document is split roughly into 3 sections. The first section provides a
basic overview of MLT, the second section shows how it's used and the final
- section shows shows structure and design, with an emphasis on how the system
- is extended.
+ section shows structure and design, with an emphasis on how the system is
+ extended.
Target Audience:
Instead of invoking mlt_factory_producer directly, we'll create a new
function called create_playlist. This function is responsible for creating
- the playlist, creating each producer, appending to the playlist and ensuring
- that all the producers are cleaned up when the playlist is destroyed. The
- last point is important - a close on the playlist won't explicitly close these
- producers. In this example, we use unique "data" properties with destructors
- to ensure closing.
+ the playlist, creating each producer and appending to the playlist.
mlt_producer create_playlist( int argc, char **argv )
{
int i = 0;
for ( i = 1; i < argc; i ++ )
{
- // Define the unique key
- char key[ 256 ];
-
// Create the producer
mlt_producer producer = mlt_factory_producer( NULL, argv[ i ] );
// Add it to the playlist
mlt_playlist_append( playlist, producer );
- // Create a unique key for this producer
- sprintf( key, "producer%d", i );
-
- // Now we need to ensure the producers are destroyed
- mlt_properties_set_data( properties, key, producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
+ // Close the producer (see below)
+ mlt_producer_close( producer );
}
// Return the playlist as a producer
return mlt_playlist_producer( playlist );
}
+ Notice that we close the producer after the append. Actually, what we're
+ doing is closing our reference to it - the playlist creates its own reference
+ to the producer on append and insert, and it will close its reference
+ when the playlist is destroyed[*].
+
+ Note also that if you append multiple instances of the same producer, it
+ will create multiple references to it.
+
Now all we need do is to replace these lines in the main function:
// Create a normalised producer
and we have a means to play multiple clips.
+ [*] This reference functionality was introduced in mlt 0.1.2 - it is 100%
+ compatable with the early mechanism of registering the reference and
+ destructor with the properties of the playlist object.
+
Filters:
and we have a means to play multiple clips with a horribly obtrusive
watermark - just what the world needed, right? ;-)
+ Incidentally, the same thing could be achieved with the more trivial
+ watermark filter inserted between the producer and the consumer.
+
SECTION 3 - STRUCTURE AND DESIGN
--------------------------------
export LD_LIBRARY_PATH=\
`pwd`/../mpeg_sdk_release/bin:\
-`pwd`/../dvcpro_sdk_release/lib
+`pwd`/../dvcpro_sdk_release/lib:\
+`pwd`/../sr_sdk_release/lib
void mlt_consumer_close( mlt_consumer this )
{
- // Get the childs close function
- void ( *consumer_close )( ) = this->close;
+ if ( this != NULL && mlt_properties_dec_ref( mlt_consumer_properties( this ) ) <= 0 )
+ {
+ // Get the childs close function
+ void ( *consumer_close )( ) = this->close;
- // Make sure it only gets called once
- this->close = NULL;
+ // Make sure it only gets called once
+ this->close = NULL;
+ this->parent.close = NULL;
- // Call the childs close if available
- if ( consumer_close != NULL )
- consumer_close( this );
- else
- mlt_service_close( &this->parent );
+ // Call the childs close if available
+ if ( consumer_close != NULL )
+ consumer_close( this );
+ else
+ mlt_service_close( &this->parent );
+ }
}
void mlt_field_close( mlt_field this )
{
- free( this );
+ if ( this != NULL && mlt_properties_dec_ref( mlt_field_properties( this ) ) <= 0 )
+ {
+ //mlt_tractor_close( this->tractor );
+ //mlt_multitrack_close( this->multitrack );
+ free( this );
+ }
}
// Override the get_frame method
service->get_frame = filter_get_frame;
+ // Define the destructor
+ service->close = ( mlt_destructor )mlt_filter_close;
+ service->close_object = this;
+
// Default in, out, track properties
mlt_properties_set_position( properties, "in", 0 );
mlt_properties_set_position( properties, "out", 0 );
void mlt_filter_close( mlt_filter this )
{
- if ( this->close != NULL )
- this->close( this );
- else
- mlt_service_close( &this->parent );
- free( this );
+ if ( this != NULL && mlt_properties_dec_ref( mlt_filter_properties( this ) ) <= 0 )
+ {
+ if ( this->close != NULL )
+ {
+ this->close( this );
+ }
+ else
+ {
+ this->parent.close = NULL;
+ mlt_service_close( &this->parent );
+ }
+ free( this );
+ }
}
void mlt_frame_close( mlt_frame this )
{
- if ( this != NULL )
+ if ( this != NULL && mlt_properties_dec_ref( mlt_frame_properties( this ) ) <= 0 )
{
mlt_deque_close( this->stack_image );
mlt_deque_close( this->stack_audio );
void mlt_multitrack_close( mlt_multitrack this )
{
- // Close the producer
- mlt_producer_close( &this->parent );
+ if ( this != NULL && mlt_properties_dec_ref( mlt_multitrack_properties( this ) ) <= 0 )
+ {
+ // Close the producer
+ mlt_producer_close( &this->parent );
- // Free the list
- free( this->list );
+ // Free the list
+ free( this->list );
- // Free the object
- free( this );
+ // Free the object
+ free( this );
+ }
}
// Override the producer get_frame
producer->get_frame = producer_get_frame;
+ // Define the destructor
+ producer->close = ( mlt_destructor )mlt_playlist_close;
+ producer->close_object = this;
+
// Initialise blank
mlt_producer_init( &this->blank, NULL );
mlt_properties_set( mlt_producer_properties( &this->blank ), "mlt_service", "blank" );
this->count ++;
+ mlt_properties_inc_ref( mlt_producer_properties( producer ) );
+
return mlt_playlist_virtual_refresh( this );
}
int mlt_playlist_clear( mlt_playlist this )
{
+ int i;
+ for ( i = 0; i < this->count; i ++ )
+ mlt_producer_close( this->list[ i ]->producer );
this->count = 0;
mlt_properties_set_double( mlt_playlist_properties( this ), "first_fps", 0 );
return mlt_playlist_virtual_refresh( this );
// Get the clip info of the clip to be removed
mlt_playlist_get_clip_info( this, &where_info, where );
+ // Close the producer associated to the clip info
+ mlt_producer_close( where_info.producer );
+
// Reorganise the list
for ( i = where + 1; i < this->count; i ++ )
this->list[ i - 1 ] = this->list[ i ];
return error;
}
+/** Split a clip on the playlist at the given position.
+*/
+
+int mlt_playlist_split( mlt_playlist this, int clip, mlt_position position )
+{
+ int error = clip < 0 || clip >= this->count;
+ if ( error == 0 )
+ {
+ playlist_entry *entry = this->list[ clip ];
+ if ( position > 0 && position < entry->frame_count )
+ {
+ int in = entry->frame_in;
+ int out = entry->frame_out;
+ mlt_playlist_resize_clip( this, clip, in, in + position );
+ mlt_playlist_insert( this, entry->producer, clip + 1, in + position + 1, out );
+ }
+ else
+ {
+ error = 1;
+ }
+ }
+ return error;
+}
+
+/** Join 1 or more consecutive clips.
+*/
+
+int mlt_playlist_join( mlt_playlist this, int clip, int count, int merge )
+{
+ int error = clip < 0 || ( clip + 1 ) >= this->count;
+ if ( error == 0 )
+ {
+ int i = clip;
+ mlt_playlist new_clip = mlt_playlist_init( );
+ if ( clip + count >= this->count )
+ count = this->count - clip;
+ for ( i = 0; i <= count; i ++ )
+ {
+ playlist_entry *entry = this->list[ clip ];
+ char *resource = mlt_properties_get( mlt_producer_properties( entry->producer ), "resource" );
+ if ( merge && resource != NULL && !strcmp( resource, "<playlist>" ) )
+ {
+ mlt_playlist old_clip = ( mlt_playlist )entry->producer;
+ while( old_clip->count )
+ {
+ entry = old_clip->list[ 0 ];
+ mlt_playlist_append_io( new_clip, entry->producer, entry->frame_in, entry->frame_out );
+ mlt_playlist_remove( old_clip, 0 );
+ }
+ }
+ else
+ {
+ mlt_playlist_append_io( new_clip, entry->producer, entry->frame_in, entry->frame_out );
+ }
+ mlt_playlist_remove( this, clip );
+ }
+ mlt_playlist_insert( this, mlt_playlist_producer( new_clip ), clip, 0, -1 );
+ mlt_playlist_close( new_clip );
+ }
+ return error;
+}
+
/** Get the current frame.
*/
void mlt_playlist_close( mlt_playlist this )
{
- int i = 0;
- mlt_producer_close( &this->parent );
- mlt_producer_close( &this->blank );
- for ( i = 0; i < this->count; i ++ )
- free( this->list[ i ] );
- free( this->list );
- free( this );
+ if ( this != NULL && mlt_properties_dec_ref( mlt_playlist_properties( this ) ) <= 0 )
+ {
+ int i = 0;
+ this->parent.close = NULL;
+ mlt_producer_close( &this->parent );
+ mlt_producer_close( &this->blank );
+ for ( i = 0; i < this->count; i ++ )
+ {
+ mlt_producer_close( this->list[ i ]->producer );
+ free( this->list[ i ] );
+ }
+ free( this->list );
+ free( this );
+ }
}
extern int mlt_playlist_remove( mlt_playlist self, int where );
extern int mlt_playlist_move( mlt_playlist self, int from, int to );
extern int mlt_playlist_resize_clip( mlt_playlist self, int clip, mlt_position in, mlt_position out );
+extern int mlt_playlist_split( mlt_playlist self, int clip, mlt_position position );
+extern int mlt_playlist_join( mlt_playlist self, int clip, int count, int merge );
extern void mlt_playlist_close( mlt_playlist self );
#endif
// The parent is the service
mlt_service parent = &this->parent;
+ // Define the parent close
+ parent->close = ( mlt_destructor )mlt_producer_close;
+ parent->close_object = this;
+
+ // For convenience, we'll assume the close_object is this
+ this->close_object = this;
+
// Get the properties of the parent
mlt_properties properties = mlt_service_properties( parent );
void mlt_producer_close( mlt_producer this )
{
- if ( this->close != NULL )
- this->close( this );
- else
- mlt_service_close( &this->parent );
+ if ( this != NULL && mlt_properties_dec_ref( mlt_producer_properties( this ) ) <= 0 )
+ {
+ this->parent.close = NULL;
+
+ if ( this->close != NULL )
+ this->close( this->close_object );
+ else
+ mlt_service_close( &this->parent );
+ }
}
// Public virtual methods
int ( *get_frame )( mlt_producer, mlt_frame_ptr, int );
- void ( *close )( mlt_producer );
+ mlt_destructor close;
+ void *close_object;
// Private data
void *local;
int count;
int size;
mlt_properties mirror;
+ int ref_count;
}
property_list;
/** Memory leak checks.
*/
+//#define _MLT_PROPERTY_CHECKS_
+
#ifdef _MLT_PROPERTY_CHECKS_
static int properties_created = 0;
static int properties_destroyed = 0;
// Allocate the local structure
this->local = calloc( sizeof( property_list ), 1 );
+
+ // Increment the ref count
+ ( ( property_list * )this->local )->ref_count = 1;
}
// Check that initialisation was successful
}
}
+/** Maintain ref count to allow multiple uses of an mlt object.
+*/
+
+int mlt_properties_inc_ref( mlt_properties this )
+{
+ if ( this != NULL )
+ {
+ property_list *list = this->local;
+ return ++ list->ref_count;
+ }
+ return 0;
+}
+
+/** Maintain ref count to allow multiple uses of an mlt object.
+*/
+
+int mlt_properties_dec_ref( mlt_properties this )
+{
+ if ( this != NULL )
+ {
+ property_list *list = this->local;
+ return -- list->ref_count;
+ }
+ return 0;
+}
+
/** Allow the specification of a mirror.
*/
fprintf( output, "%s=%s\n", list->name[ i ], mlt_properties_get( this, list->name[ i ] ) );
}
+void mlt_properties_debug( mlt_properties this, char *title, FILE *output )
+{
+ fprintf( stderr, "%s: ", title );
+ if ( this != NULL )
+ {
+ property_list *list = this->local;
+ int i = 0;
+ fprintf( output, "[ ref=%d", list->ref_count );
+ for ( i = 0; i < list->count; i ++ )
+ if ( mlt_properties_get( this, list->name[ i ] ) != NULL )
+ fprintf( output, ", %s=%s", list->name[ i ], mlt_properties_get( this, list->name[ i ] ) );
+ fprintf( output, " ]" );
+ }
+ fprintf( stderr, "\n" );
+}
+
/** Close the list.
*/
void mlt_properties_close( mlt_properties this )
{
- if ( this != NULL )
+ if ( this != NULL && mlt_properties_dec_ref( this ) <= 0 )
{
- property_list *list = this->local;
- int index = 0;
-
- // Clean up names and values
- for ( index = list->count - 1; index >= 0; index -- )
+ if ( this->close != NULL )
{
- free( list->name[ index ] );
- mlt_property_close( list->value[ index ] );
+ this->close( this->close_object );
}
-
- // Clear up the list
- free( list->name );
- free( list->value );
- free( list );
-
- // Free this now if this has no child
- if ( this->child == NULL )
- free( this );
+ else
+ {
+ property_list *list = this->local;
+ int index = 0;
#ifdef _MLT_PROPERTY_CHECKS_
- // Increment destroyed count
- properties_destroyed ++;
+ // Show debug info
+ mlt_properties_debug( this, "Closing", stderr );
- // Show current stats - these should match when the app is closed
- fprintf( stderr, "Created %d, destroyed %d\n", properties_created, properties_destroyed );
+ // Increment destroyed count
+ properties_destroyed ++;
+
+ // Show current stats - these should match when the app is closed
+ fprintf( stderr, "Created %d, destroyed %d\n", properties_created, properties_destroyed );
#endif
+
+ // Clean up names and values
+ for ( index = list->count - 1; index >= 0; index -- )
+ {
+ free( list->name[ index ] );
+ mlt_property_close( list->value[ index ] );
+ }
+
+ // Clear up the list
+ free( list->name );
+ free( list->value );
+ free( list );
+
+ // Free this now if this has no child
+ if ( this->child == NULL )
+ free( this );
+ }
}
}
{
void *child;
void *local;
+ mlt_destructor close;
+ void *close_object;
};
/** Public interface.
extern int mlt_properties_init( mlt_properties, void *child );
extern mlt_properties mlt_properties_new( );
extern mlt_properties mlt_properties_load( char *file );
+extern int mlt_properties_inc_ref( mlt_properties self );
+extern int mlt_properties_dec_ref( mlt_properties self );
extern void mlt_properties_mirror( mlt_properties self, mlt_properties that );
extern int mlt_properties_inherit( mlt_properties self, mlt_properties that );
extern int mlt_properties_pass( mlt_properties self, mlt_properties that, char *prefix );
extern int mlt_properties_rename( mlt_properties self, char *source, char *dest );
extern int mlt_properties_count( mlt_properties self );
extern void mlt_properties_dump( mlt_properties self, FILE *output );
+extern void mlt_properties_debug( mlt_properties self, char *title, FILE *output );
extern void mlt_properties_close( mlt_properties self );
#endif
int mlt_service_init( mlt_service this, void *child )
{
+ int error = 0;
+
// Initialise everything to NULL
memset( this, 0, sizeof( struct mlt_service_s ) );
this->get_frame = service_get_frame;
// Initialise the properties
- return mlt_properties_init( &this->parent, this );
+ error = mlt_properties_init( &this->parent, this );
+ if ( error == 0 )
+ {
+ this->parent.close = ( mlt_destructor )mlt_service_close;
+ this->parent.close_object = this;
+ }
+
+ return error;
}
/** Connect a producer service.
// If we have space, assign the input
if ( base->in != NULL && index >= 0 && index < base->size )
{
+ // Get the current service
+ mlt_service current = base->in[ index ];
+
+ // Increment the reference count on this producer
+ if ( producer != NULL )
+ mlt_properties_inc_ref( mlt_service_properties( producer ) );
+
// Now we disconnect the producer service from its consumer
mlt_service_disconnect( producer );
// Now we connect the producer to its connected consumer
mlt_service_connect( producer, this );
+ // Close the current service
+ mlt_service_close( current );
+
// Inform caller that all went well
return 0;
}
// Get the service base
mlt_service_base *base = this->local;
- // There's a bit more required here...
+ // Disconnect
base->out = NULL;
}
}
void mlt_service_close( mlt_service this )
{
- mlt_service_base *base = this->local;
- free( base->in );
- free( base );
- mlt_properties_close( &this->parent );
+ if ( this != NULL && mlt_properties_dec_ref( mlt_service_properties( this ) ) <= 0 )
+ {
+ if ( this->close != NULL )
+ {
+ this->close( this->close_object );
+ }
+ else
+ {
+ mlt_service_base *base = this->local;
+ int i = 0;
+ for ( i = 0; i < base->count; i ++ )
+ if ( base->in[ i ] != NULL )
+ mlt_service_close( base->in[ i ] );
+ free( base->in );
+ free( base );
+ this->parent.close = NULL;
+ mlt_properties_close( &this->parent );
+ }
+ }
}
// Protected virtual
int ( *get_frame )( mlt_service self, mlt_frame_ptr frame, int index );
+ mlt_destructor close;
+ void *close_object;
// Private data
void *local;
*/
static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int track );
-static void producer_close( mlt_producer this );
/** Constructor for the tractor.
*/
mlt_properties_set( mlt_producer_properties( producer ), "mlt_service", "tractor" );
producer->get_frame = producer_get_frame;
- producer->close = producer_close;
+ producer->close = ( mlt_destructor )mlt_tractor_close;
+ producer->close_object = this;
}
else
{
void mlt_tractor_close( mlt_tractor this )
{
- this->parent.close = NULL;
- mlt_producer_close( &this->parent );
- free( this );
-}
-
-/** Close the producer.
-*/
-
-static void producer_close( mlt_producer this )
-{
- mlt_tractor_close( this->child );
+ if ( this != NULL && mlt_properties_dec_ref( mlt_tractor_properties( this ) ) <= 0 )
+ {
+ this->parent.close = NULL;
+ mlt_producer_close( &this->parent );
+ free( this );
+ }
}
mlt_properties properties = mlt_transition_properties( this );
service->get_frame = transition_get_frame;
+ service->close = ( mlt_destructor )mlt_transition_close;
+ service->close_object = this;
mlt_properties_set_position( properties, "in", 0 );
mlt_properties_set_position( properties, "out", 0 );
void mlt_transition_close( mlt_transition this )
{
- if ( this->close != NULL )
- this->close( this );
- else
- mlt_service_close( &this->parent );
+ if ( this != NULL && mlt_properties_dec_ref( mlt_transition_properties( this ) ) <= 0 )
+ {
+ this->parent.close = NULL;
+ if ( this->close != NULL )
+ this->close( this );
+ else
+ mlt_service_close( &this->parent );
+ }
}
// Callback registration
producer->get_frame = producer_get_frame;
- producer->close = producer_close;
+ producer->close = ( mlt_destructor )producer_close;
// Set the default properties
mlt_properties_set( properties, "resource", colour == NULL ? "0x000000ff" : colour );
// Callback registration
this->get_frame = producer_get_frame;
- this->close = producer_close;
+ this->close = ( mlt_destructor )producer_close;
}
return this;
mlt_properties properties = mlt_producer_properties( producer );
producer->get_frame = producer_get_frame;
- producer->close = producer_close;
+ producer->close = ( mlt_destructor )producer_close;
if ( command != NULL )
{
mlt_properties properties = mlt_producer_properties( producer );
// Register transport implementation with the producer
- producer->close = producer_close;
+ producer->close = ( mlt_destructor )producer_close;
// Register our get_frame implementation with the producer
producer->get_frame = producer_get_frame;
// Override the get_frame method
this->get_frame = producer_get_frame;
- this->close = producer_close;
+ this->close = ( mlt_destructor )producer_close;
}
else
{
mlt_producer producer = &this->parent;
producer->get_frame = producer_get_frame;
- producer->close = producer_close;
+ producer->close = ( mlt_destructor )producer_close;
// This is required to initialise gdk-pixbuf
g_type_init();
// Callback registration
producer->get_frame = producer_get_frame;
- producer->close = producer_close;
+ producer->close = ( mlt_destructor )producer_close;
// Set the default properties
mlt_properties_set( properties, "resource", filename );
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" );
if ( service_s != NULL && strcmp( service_s, "blank" ) != 0 )
serialise_service( context, MLT_SERVICE( info.producer ), node );
+ else if ( resource_s != NULL && !strcmp( resource_s, "<playlist>" ) )
+ serialise_playlist( context, MLT_SERVICE( info.producer ), node );
}
}
}
}
else
{
+ char temp[ 20 ];
xmlNode *entry = xmlNewChild( child, NULL, "entry", NULL );
snprintf( key, 10, "%p", MLT_SERVICE( info.producer ) );
xmlNewProp( entry, "producer", mlt_properties_get( context->producer_map, key ) );
- xmlNewProp( entry, "in", mlt_properties_get( mlt_producer_properties( info.producer ), "in" ) );
- xmlNewProp( entry, "out", mlt_properties_get( mlt_producer_properties( info.producer ), "out" ) );
+ sprintf( temp, "%d", info.frame_in );
+ xmlNewProp( entry, "in", temp );
+ sprintf( temp, "%d", info.frame_out );
+ xmlNewProp( entry, "out", temp );
}
}
}
if ( context->pass == 0 )
{
// Recurse on connected producer
- serialise_service( context, mlt_service_get_producer( service ), node );
+ serialise_service( context, mlt_service_producer( service ), node );
}
else
{
xmlNewProp( child, "out", mlt_properties_get( properties, "out" ) );
// Recurse on connected producer
- serialise_service( context, mlt_service_get_producer( service ), child );
+ serialise_service( context, mlt_service_producer( service ), child );
}
}
}
// Get the next connected service
- service = mlt_service_get_producer( service );
+ service = mlt_service_producer( service );
}
}
static int consumer_start( mlt_consumer this )
{
- mlt_service inigo = NULL;
xmlDocPtr doc = NULL;
// Get the producer service
- mlt_service service = mlt_service_get_producer( mlt_consumer_service( this ) );
+ mlt_service service = mlt_service_producer( mlt_consumer_service( this ) );
if ( service != NULL )
{
- // Remember inigo
- if ( mlt_properties_get( mlt_service_properties( service ), "mlt_service" ) != NULL &&
- strcmp( mlt_properties_get( mlt_service_properties( service ), "mlt_service" ), "inigo" ) == 0 )
- inigo = service;
-
doc = westley_make_doc( service );
if ( mlt_properties_get( mlt_consumer_properties( this ), "resource" ) == NULL )