// when the returned producer is closed).
#include <framework/mlt.h>
+#include <framework/mlt_log.h>
#include <stdlib.h>
#include <string.h>
-#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
mlt_dummy_filter_type,
mlt_dummy_transition_type,
mlt_dummy_producer_type,
+ mlt_dummy_consumer_type
};
struct deserialise_context_s
mlt_properties params;
mlt_profile profile;
int pass;
+ char *lc_numeric;
+ mlt_consumer consumer;
+ int multi_consumer;
+ int consumer_count;
};
typedef struct deserialise_context_s *deserialise_context;
+/** Trim the leading and trailing whitespace from a string in-place.
+*/
+static char* trim( char *s )
+{
+ int n;
+ if ( s && ( n = strlen( s ) ) )
+ {
+ int i = 0;
+ while ( i < n && isspace( s[i] ) ) i++;
+ while ( --n && isspace( s[n] ) );
+ n = n - i + 1;
+ if ( n > 0 )
+ memmove( s, s + i, n );
+ s[ n ] = 0;
+ }
+ return s;
+}
+
/** Convert the numerical current branch address to a dot-delimited string.
*/
-static char *serialise_branch( deserialise_context this, char *s )
+static char *serialise_branch( deserialise_context context, char *s )
{
int i;
s[0] = 0;
- for ( i = 0; i < this->depth; i++ )
+ for ( i = 0; i < context->depth; i++ )
{
int len = strlen( s );
- snprintf( s + len, BRANCH_SIG_LEN - len, "%d.", this->branch[ i ] );
+ snprintf( s + len, BRANCH_SIG_LEN - len, "%d.", context->branch[ i ] );
}
return s;
}
/** Push a service.
*/
-static int context_push_service( deserialise_context this, mlt_service that, enum service_type type )
+static int context_push_service( deserialise_context context, mlt_service that, enum service_type type )
{
- int ret = this->stack_service_size >= STACK_SIZE - 1;
+ int ret = context->stack_service_size >= STACK_SIZE - 1;
if ( ret == 0 )
{
- this->stack_service[ this->stack_service_size ] = that;
- this->stack_types[ this->stack_service_size++ ] = type;
+ context->stack_service[ context->stack_service_size ] = that;
+ context->stack_types[ context->stack_service_size++ ] = type;
// Record the tree branch on which this service lives
if ( that != NULL && mlt_properties_get( MLT_SERVICE_PROPERTIES( that ), "_xml_branch" ) == NULL )
{
char s[ BRANCH_SIG_LEN ];
- mlt_properties_set( MLT_SERVICE_PROPERTIES( that ), "_xml_branch", serialise_branch( this, s ) );
+ mlt_properties_set( MLT_SERVICE_PROPERTIES( that ), "_xml_branch", serialise_branch( context, s ) );
}
}
return ret;
/** Pop a service.
*/
-static mlt_service context_pop_service( deserialise_context this, enum service_type *type )
+static mlt_service context_pop_service( deserialise_context context, enum service_type *type )
{
mlt_service result = NULL;
- if ( this->stack_service_size > 0 )
+
+ *type = invalid_type;
+ if ( context->stack_service_size > 0 )
{
- result = this->stack_service[ -- this->stack_service_size ];
+ result = context->stack_service[ -- context->stack_service_size ];
if ( type != NULL )
- *type = this->stack_types[ this->stack_service_size ];
+ *type = context->stack_types[ context->stack_service_size ];
}
return result;
}
/** Push a node.
*/
-static int context_push_node( deserialise_context this, xmlNodePtr node )
+static int context_push_node( deserialise_context context, xmlNodePtr node )
{
- int ret = this->stack_node_size >= STACK_SIZE - 1;
+ int ret = context->stack_node_size >= STACK_SIZE - 1;
if ( ret == 0 )
- this->stack_node[ this->stack_node_size ++ ] = node;
+ context->stack_node[ context->stack_node_size ++ ] = node;
return ret;
}
/** Pop a node.
*/
-static xmlNodePtr context_pop_node( deserialise_context this )
+static xmlNodePtr context_pop_node( deserialise_context context )
{
xmlNodePtr result = NULL;
- if ( this->stack_node_size > 0 )
- result = this->stack_node[ -- this->stack_node_size ];
+ if ( context->stack_node_size > 0 )
+ result = context->stack_node[ -- context->stack_node_size ];
return result;
}
break;
default:
result = 0;
- fprintf( stderr, "Producer defined inside something that isn't a container\n" );
+ mlt_log_warning( container, "Producer defined inside something that isn't a container\n" );
break;
};
/** Attach filters defined on that to this.
*/
-static void attach_filters( mlt_service this, mlt_service that )
+static void attach_filters( mlt_service service, mlt_service that )
{
if ( that != NULL )
{
mlt_filter filter = NULL;
for ( i = 0; ( filter = mlt_service_filter( that, i ) ) != NULL; i ++ )
{
- mlt_service_attach( this, filter );
+ mlt_service_attach( service, filter );
attach_filters( MLT_FILTER_SERVICE( filter ), MLT_FILTER_SERVICE( filter ) );
}
}
p->progressive = my_profile->progressive;
p->sample_aspect_den = my_profile->sample_aspect_den;
p->sample_aspect_num = my_profile->sample_aspect_num;
+ p->colorspace = my_profile->colorspace;
p->is_explicit = 1;
mlt_profile_close( my_profile );
}
p->frame_rate_num = strtol( _s(atts[ 1 ]), NULL, 0 );
else if ( xmlStrcmp( atts[ 0 ], _x("frame_rate_den") ) == 0 )
p->frame_rate_den = strtol( _s(atts[ 1 ]), NULL, 0 );
+ else if ( xmlStrcmp( atts[ 0 ], _x("colorspace") ) == 0 )
+ p->colorspace = strtol( _s(atts[ 1 ]), NULL, 0 );
}
}
mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
track_service( context->destructors, service, (mlt_destructor) mlt_tractor_close );
+ mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( service ), context->lc_numeric );
for ( ; atts != NULL && *atts != NULL; atts += 2 )
mlt_properties_set( MLT_SERVICE_PROPERTIES( service ), (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
}
else
{
- fprintf( stderr, "Invalid state for tractor\n" );
+ mlt_log_warning( tractor, "Invalid state for tractor\n" );
}
}
tractor = mlt_tractor_new( );
parent = MLT_TRACTOR_SERVICE( tractor );
track_service( context->destructors, parent, (mlt_destructor) mlt_tractor_close );
+ mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( parent ), context->lc_numeric );
type = mlt_tractor_type;
// Flag it as a synthesised tractor for clean up later
}
else
{
- fprintf( stderr, "Invalid multitrack position\n" );
+ mlt_log_warning( parent, "Invalid multitrack position\n" );
}
}
mlt_service service = context_pop_service( context, &type );
if ( service == NULL || type != mlt_multitrack_type )
- fprintf( stderr, "End multitrack in the wrong state...\n" );
+ mlt_log_warning( service, "End multitrack in the wrong state...\n" );
}
static void on_start_playlist( deserialise_context context, const xmlChar *name, const xmlChar **atts)
}
else
{
- fprintf( stderr, "Invalid state of playlist end\n" );
+ mlt_log_warning( service, "Invalid state of playlist end %d\n", type );
}
}
mlt_service producer = NULL;
qualify_property( context, properties, "resource" );
- char *resource = mlt_properties_get( properties, "resource" );
+ char *resource = trim( mlt_properties_get( properties, "resource" ) );
// Let Kino-SMIL src be a synonym for resource
if ( resource == NULL )
{
qualify_property( context, properties, "src" );
- resource = mlt_properties_get( properties, "src" );
+ resource = trim( mlt_properties_get( properties, "src" ) );
}
// Instantiate the producer
if ( mlt_properties_get( properties, "mlt_service" ) != NULL )
{
- char temp[ 1024 ];
- strncpy( temp, mlt_properties_get( properties, "mlt_service" ), 1024 );
- if ( resource != NULL )
+ char *service_name = trim( mlt_properties_get( properties, "mlt_service" ) );
+ if ( resource )
{
+ char *temp = calloc( 1, strlen( service_name ) + strlen( resource ) + 2 );
+ strcat( temp, service_name );
strcat( temp, ":" );
- strncat( temp, resource, 1023 - strlen( temp ) );
+ strcat( temp, resource );
+ producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, temp ) );
+ free( temp );
+ }
+ else
+ {
+ producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, service_name ) );
}
- producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, temp ) );
}
// Just in case the plugin requested doesn't exist...
- if ( producer == NULL && resource != NULL )
+ if ( !producer && resource )
producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, resource ) );
-
- if ( producer == NULL )
+ if ( !producer )
+ mlt_log_warning( service, "failed to load producer \"%s\"\n", resource );
+ if ( !producer )
producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, "+INVALID.txt" ) );
-
- if ( producer == NULL )
+ if ( !producer )
producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, "colour:red" ) );
+ if ( !producer )
+ {
+ mlt_service_close( service );
+ return;
+ }
// Track this producer
track_service( context->destructors, producer, (mlt_destructor) mlt_producer_close );
+ mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( producer ), context->lc_numeric );
- // Propogate the properties
+ // Propagate the properties
qualify_property( context, properties, "resource" );
qualify_property( context, properties, "luma" );
qualify_property( context, properties, "luma.resource" );
// Get the parent properties
properties = MLT_SERVICE_PROPERTIES( parent );
- char *resource = mlt_properties_get( properties, "resource" );
+ char *resource = trim( mlt_properties_get( properties, "resource" ) );
// Put the parent producer back
context_push_service( context, parent, type );
}
else
{
- fprintf( stderr, "blank without a playlist - a definite no no\n" );
+ mlt_log_warning( service, "blank without a playlist - a definite no no\n" );
}
}
}
else
{
- fprintf( stderr, "Entry not part of a playlist...\n" );
+ mlt_log_warning( parent, "Entry not part of a playlist...\n" );
}
context_push_service( context, parent, parent_type );
if ( entry == NULL && entry_type != mlt_entry_type )
{
- fprintf( stderr, "Invalid state at end of entry\n" );
+ mlt_log_warning( entry, "Invalid state at end of entry\n" );
}
}
else if ( parent_type == mlt_multitrack_type )
multitrack = MLT_MULTITRACK( parent );
else
- fprintf( stderr, "track contained in an invalid container\n" );
+ mlt_log_warning( track, "track contained in an invalid container\n" );
if ( multitrack != NULL )
{
}
else
{
- fprintf( stderr, "Invalid state at end of track\n" );
+ mlt_log_warning( track, "Invalid state at end of track\n" );
}
}
if ( service != NULL && type == mlt_dummy_filter_type )
{
- mlt_service filter = MLT_SERVICE( mlt_factory_filter( context->profile, mlt_properties_get( properties, "mlt_service" ), NULL ) );
+ char *id = trim( mlt_properties_get( properties, "mlt_service" ) );
+ mlt_service filter = MLT_SERVICE( mlt_factory_filter( context->profile, id, NULL ) );
mlt_properties filter_props = MLT_SERVICE_PROPERTIES( filter );
+ if ( !filter )
+ {
+ mlt_log_warning( service, "failed to load filter \"%s\"\n", id );
+ if ( parent )
+ context_push_service( context, parent, parent_type );
+ mlt_service_close( service );
+ return;
+ }
+
track_service( context->destructors, filter, (mlt_destructor) mlt_filter_close );
+ mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( filter ), context->lc_numeric );
// Propogate the properties
qualify_property( context, properties, "resource" );
}
else
{
- fprintf( stderr, "filter closed with invalid parent...\n" );
+ mlt_log_warning( service, "filter closed with invalid parent...\n" );
}
// Close the dummy filter service
}
else
{
- fprintf( stderr, "Invalid top of stack on filter close\n" );
+ mlt_log_warning( service, "Invalid top of stack on filter close\n" );
}
}
if ( service != NULL && type == mlt_dummy_transition_type )
{
- char *id = mlt_properties_get( properties, "mlt_service" );
+ char *id = trim( mlt_properties_get( properties, "mlt_service" ) );
mlt_service effect = MLT_SERVICE( mlt_factory_transition( context->profile, id, NULL ) );
mlt_properties effect_props = MLT_SERVICE_PROPERTIES( effect );
+ if ( !effect )
+ {
+ mlt_log_warning( service, "failed to load transition \"%s\"\n", id );
+ if ( parent )
+ context_push_service( context, parent, parent_type );
+ mlt_service_close( service );
+ return;
+ }
track_service( context->destructors, effect, (mlt_destructor) mlt_transition_close );
+ mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( effect ), context->lc_numeric );
// Propogate the properties
qualify_property( context, properties, "resource" );
mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
if ( mlt_properties_get_int( properties, "a_track" ) == mlt_properties_get_int( properties, "b_track" ) )
mlt_properties_set_int( properties, "b_track", mlt_properties_get_int( properties, "a_track" ) + 1 );
- mlt_field_plant_transition( field, MLT_TRANSITION( effect ),
+ mlt_field_plant_transition( field, MLT_TRANSITION( effect ),
mlt_properties_get_int( properties, "a_track" ),
mlt_properties_get_int( properties, "b_track" ) );
- mlt_transition_set_in_and_out( MLT_TRANSITION( effect ),
+ mlt_transition_set_in_and_out( MLT_TRANSITION( effect ),
mlt_properties_get_int( properties, "in" ),
mlt_properties_get_int( properties, "out" ) );
}
else
{
- fprintf( stderr, "Misplaced transition - ignoring\n" );
+ mlt_log_warning( service, "Misplaced transition - ignoring\n" );
}
// Put the parent back on the stack
}
else
{
- fprintf( stderr, "transition closed with invalid parent...\n" );
+ mlt_log_warning( service, "transition closed with invalid parent...\n" );
}
// Close the dummy filter service
}
else
{
- fprintf( stderr, "Invalid top of stack on transition close\n" );
+ mlt_log_warning( service, "Invalid top of stack on transition close\n" );
+ }
+}
+
+static void on_start_consumer( deserialise_context context, const xmlChar *name, const xmlChar **atts)
+{
+ if ( context->pass == 1 )
+ {
+ mlt_consumer consumer = mlt_consumer_new( context->profile );
+ mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
+
+ mlt_properties_set_lcnumeric( properties, context->lc_numeric );
+ context_push_service( context, MLT_CONSUMER_SERVICE(consumer), mlt_dummy_consumer_type );
+
+ // Set the properties from attributes
+ for ( ; atts != NULL && *atts != NULL; atts += 2 )
+ mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] );
+ }
+}
+
+static void on_end_consumer( deserialise_context context, const xmlChar *name )
+{
+ if ( context->pass == 1 )
+ {
+ // Get the consumer from the stack
+ enum service_type type;
+ mlt_service service = context_pop_service( context, &type );
+
+ if ( service && type == mlt_dummy_consumer_type )
+ {
+ mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
+ qualify_property( context, properties, "resource" );
+ qualify_property( context, properties, "target" );
+ char *resource = trim( mlt_properties_get( properties, "resource" ) );
+
+ if ( context->multi_consumer > 1 )
+ {
+ // Instantiate the multi consumer
+ if ( !context->consumer )
+ {
+ context->consumer = mlt_factory_consumer( context->profile, "multi", NULL );
+ if ( context->consumer )
+ {
+ // Track this consumer
+ track_service( context->destructors, MLT_CONSUMER_SERVICE(context->consumer), (mlt_destructor) mlt_consumer_close );
+ mlt_properties_set_lcnumeric( MLT_CONSUMER_PROPERTIES(context->consumer), context->lc_numeric );
+ }
+ }
+ if ( context->consumer )
+ {
+ // Set this service instance on multi consumer
+ char key[20];
+ snprintf( key, sizeof(key), "%d", context->consumer_count++ );
+ mlt_properties_set_data( MLT_CONSUMER_PROPERTIES(context->consumer), key, service, 0,
+ (mlt_destructor) mlt_service_close, NULL );
+ }
+ }
+ else
+ {
+ // Instantiate the consumer
+ char *id = trim( mlt_properties_get( properties, "mlt_service" ) );
+ context->consumer = mlt_factory_consumer( context->profile, id, resource );
+ if ( context->consumer )
+ {
+ // Track this consumer
+ track_service( context->destructors, MLT_CONSUMER_SERVICE(context->consumer), (mlt_destructor) mlt_consumer_close );
+ mlt_properties_set_lcnumeric( MLT_CONSUMER_PROPERTIES(context->consumer), context->lc_numeric );
+
+ // Inherit the properties
+ mlt_properties_inherit( MLT_CONSUMER_PROPERTIES(context->consumer), properties );
+ }
+ // Close the dummy
+ mlt_service_close( service );
+ }
+ }
}
}
}
else
{
- fprintf( stderr, "Property without a service '%s'?\n", ( const char * )name );
+ mlt_log_warning( service, "Property without a service '%s'?\n", ( const char * )name );
}
}
// Serialise the tree to get value
xmlDocDumpMemory( context->value_doc, &value, &size );
mlt_properties_set( properties, context->property, _s(value) );
+#ifdef WIN32
+ xmlFreeFunc xmlFree = NULL;
+ xmlMemGet( &xmlFree, NULL, NULL, NULL);
+#endif
xmlFree( value );
xmlFreeDoc( context->value_doc );
context->value_doc = NULL;
}
else
{
- fprintf( stderr, "Property without a service '%s'??\n", (const char *)name );
+ mlt_log_warning( service, "Property without a service '%s'??\n", (const char *)name );
}
}
xmlStrcmp( name, _x("profile") ) == 0 ||
xmlStrcmp( name, _x("profileinfo") ) == 0 )
on_start_profile( context, name, atts );
+ if ( xmlStrcmp( name, _x("consumer") ) == 0 )
+ context->multi_consumer++;
return;
}
context->branch[ context->depth ] ++;
context->depth ++;
// Build a tree from nodes within a property value
- if ( context->is_value == 1 )
+ if ( context->is_value == 1 && context->pass == 1 )
{
xmlNodePtr node = xmlNewNode( NULL, name );
on_start_transition( context, name, atts );
else if ( xmlStrcmp( name, _x("property") ) == 0 )
on_start_property( context, name, atts );
+ else if ( xmlStrcmp( name, _x("consumer") ) == 0 )
+ on_start_consumer( context, name, atts );
else if ( xmlStrcmp( name, _x("westley") ) == 0 || xmlStrcmp( name, _x("mlt") ) == 0 )
+ {
for ( ; atts != NULL && *atts != NULL; atts += 2 )
- mlt_properties_set( context->producer_map, ( const char * )atts[ 0 ], ( const char * )atts[ 1 ] );
+ {
+ if ( xmlStrcmp( atts[0], _x("LC_NUMERIC") ) )
+ mlt_properties_set( context->producer_map, _s( atts[0] ), _s(atts[1] ) );
+ else if ( !context->lc_numeric )
+ context->lc_numeric = strdup( _s( atts[1] ) );
+ }
+ }
}
static void on_end_element( void *ctx, const xmlChar *name )
deserialise_context context = ( deserialise_context )( xmlcontext->_private );
//printf("on_end_element: %s\n", name );
- if ( context->is_value == 1 && xmlStrcmp( name, _x("property") ) != 0 )
+ if ( context->is_value == 1 && context->pass == 1 && xmlStrcmp( name, _x("property") ) != 0 )
context_pop_node( context );
else if ( xmlStrcmp( name, _x("multitrack") ) == 0 )
on_end_multitrack( context, name );
on_end_filter( context, name );
else if ( xmlStrcmp( name, _x("transition") ) == 0 )
on_end_transition( context, name );
+ else if ( xmlStrcmp( name, _x("consumer") ) == 0 )
+ on_end_consumer( context, name );
context->branch[ context->depth ] = 0;
context->depth --;
return e;
}
+/** Handle error messages from the parser.
+*/
+static void on_error( void * ctx, const char * msg, ...)
+{
+ struct _xmlError* err_ptr = xmlCtxtGetLastError(ctx);
+ mlt_log_error( NULL, "XML producer parse error: %s\trow: %d\tcol: %d\n",
+ err_ptr->message, err_ptr->line, err_ptr->int2 );
+}
+
/** Convert a hexadecimal character to its value.
*/
static int tohex( char p )
// We need to track the number of registered filters
mlt_properties_set_int( context->destructors, "registered", 0 );
- // Setup SAX callbacks
- sax->startElement = on_start_element;
-
// Setup libxml2 SAX parsing
xmlInitParser();
xmlSubstituteEntitiesDefault( 1 );
xmlcontext->sax = NULL;
xmlcontext->_private = NULL;
xmlFreeParserCtxt( xmlcontext );
+ context->stack_node_size = 0;
+ context->stack_service_size = 0;
// Setup the second pass
context->pass ++;
}
// Setup SAX callbacks
+ sax->startElement = on_start_element;
sax->endElement = on_end_element;
sax->characters = on_characters;
sax->cdataBlock = on_characters;
sax->internalSubset = on_internal_subset;
sax->entityDecl = on_entity_declaration;
sax->getEntity = on_get_entity;
+ sax->error = on_error;
+ sax->fatalError = on_error;
// Parse
xmlcontext->sax = sax;
for ( i = mlt_properties_count( properties ) - 1; i >= 1; i -- )
{
char *name = mlt_properties_get_name( properties, i );
- if ( mlt_properties_get_data( properties, name, NULL ) == service )
+ if ( mlt_properties_get_data_at( properties, i, NULL ) == service )
{
mlt_properties_set_data( properties, name, service, 0, NULL, NULL );
break;
mlt_properties_set( properties, "_xml", "was here" );
mlt_properties_set_int( properties, "_mlt_service_hidden", 1 );
}
+
+ // Make consumer available
+ mlt_properties_inc_ref( MLT_CONSUMER_PROPERTIES( context->consumer ) );
+ mlt_properties_set_data( properties, "consumer", context->consumer, 0,
+ (mlt_destructor) mlt_consumer_close, NULL );
}
else
{
if ( context->params != NULL )
mlt_properties_close( context->params );
mlt_properties_close( context->destructors );
+ if ( context->lc_numeric )
+ free( context->lc_numeric );
free( context );
free( filename );