mlt_consumer consumer;
int multi_consumer;
int consumer_count;
+ int seekable;
+ mlt_consumer qglsl;
};
typedef struct deserialise_context_s *deserialise_context;
{
mlt_service result = NULL;
- *type = invalid_type;
+ if ( type ) *type = mlt_invalid_type;
if ( context->stack_service_size > 0 )
{
result = context->stack_service[ -- context->stack_service_size ];
if ( type != NULL )
*type = context->stack_types[ context->stack_service_size ];
+ // Set the service's profile and locale so mlt_property time-to-position conversions can get fps
+ if ( result )
+ {
+ mlt_properties_set_data( MLT_SERVICE_PROPERTIES( result ), "_profile", context->profile, 0, NULL, NULL );
+ mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( result ), context->lc_numeric );
+ }
}
return result;
}
static void on_start_playlist( deserialise_context context, const xmlChar *name, const xmlChar **atts)
{
- mlt_playlist playlist = mlt_playlist_init( );
+ mlt_playlist playlist = mlt_playlist_new( context->profile );
mlt_service service = MLT_PLAYLIST_SERVICE( playlist );
mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
mlt_properties_set( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
}
-// Parse a SMIL clock value (as produced by Kino 0.9.1) and return position in frames
-static mlt_position parse_clock_value( char *value, double fps )
-{
- // This implementation expects a fully specified clock value - no optional
- // parts (e.g. 1:05)
- char *pos, *copy = strdup( value );
- int hh, mm, ss, ms;
- mlt_position result = -1;
-
- value = copy;
- pos = strchr( value, ':' );
- if ( !pos )
- return result;
- *pos = '\0';
- hh = atoi( value );
- value = pos + 1;
-
- pos = strchr( value, ':' );
- if ( !pos )
- return result;
- *pos = '\0';
- mm = atoi( value );
- value = pos + 1;
-
- pos = strchr( value, '.' );
- if ( !pos )
- return result;
- *pos = '\0';
- ss = atoi( value );
- value = pos + 1;
-
- ms = atoi( value );
- free( copy );
- result = ( fps * ( ( (hh * 3600) + (mm * 60) + ss ) * 1000 + ms ) / 1000 + 0.5 );
-
- return result;
-}
-
static void on_end_producer( deserialise_context context, const xmlChar *name )
{
enum service_type type;
mlt_service producer = NULL;
qualify_property( context, properties, "resource" );
- char *resource = trim( mlt_properties_get( properties, "resource" ) );
+ char *resource = mlt_properties_get( properties, "resource" );
// Let Kino-SMIL src be a synonym for resource
if ( resource == NULL )
{
qualify_property( context, properties, "src" );
- resource = trim( mlt_properties_get( properties, "src" ) );
+ resource = mlt_properties_get( properties, "src" );
}
// Instantiate the producer
if ( !producer )
{
mlt_service_close( service );
+ free( 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 );
+ if ( mlt_properties_get( MLT_SERVICE_PROPERTIES( producer ), "seekable" ) )
+ context->seekable &= mlt_properties_get_int( MLT_SERVICE_PROPERTIES( producer ), "seekable" );
// Propagate the properties
qualify_property( context, properties, "resource" );
mlt_position out = -1;
// Get in
- if ( mlt_properties_get( properties, "in" ) != NULL )
+ if ( mlt_properties_get( properties, "in" ) )
in = mlt_properties_get_position( properties, "in" );
// Let Kino-SMIL clipBegin be a synonym for in
- if ( mlt_properties_get( properties, "clipBegin" ) != NULL )
- {
- if ( strchr( mlt_properties_get( properties, "clipBegin" ), ':' ) )
- // Parse clock value
- in = parse_clock_value( mlt_properties_get( properties, "clipBegin" ),
- mlt_producer_get_fps( MLT_PRODUCER( producer ) ) );
- else
- // Parse frames value
- in = mlt_properties_get_position( properties, "clipBegin" );
- }
+ else if ( mlt_properties_get( properties, "clipBegin" ) )
+ in = mlt_properties_get_position( properties, "clipBegin" );
// Get out
- if ( mlt_properties_get( properties, "out" ) != NULL )
+ if ( mlt_properties_get( properties, "out" ) )
out = mlt_properties_get_position( properties, "out" );
// Let Kino-SMIL clipEnd be a synonym for out
- if ( mlt_properties_get( properties, "clipEnd" ) != NULL )
- {
- if ( strchr( mlt_properties_get( properties, "clipEnd" ), ':' ) )
- // Parse clock value
- out = parse_clock_value( mlt_properties_get( properties, "clipEnd" ),
- mlt_producer_get_fps( MLT_PRODUCER( producer ) ) );
- else
- // Parse frames value
- out = mlt_properties_get_position( properties, "clipEnd" );
- }
+ else if ( mlt_properties_get( properties, "clipEnd" ) )
+ out = mlt_properties_get_position( properties, "clipEnd" );
// Remove in and out
mlt_properties_set( properties, "in", NULL );
mlt_properties_set( properties, "out", NULL );
// Get the parent properties
properties = MLT_SERVICE_PROPERTIES( parent );
- char *resource = trim( mlt_properties_get( properties, "resource" ) );
+ char *resource = mlt_properties_get( properties, "resource" );
// Put the parent producer back
context_push_service( context, parent, type );
// Push the producer onto the stack
context_push_service( context, producer, mlt_producer_type );
}
+ }
+ if ( service )
+ {
mlt_service_close( service );
+ free( service );
}
}
// Get the playlist from the stack
enum service_type type;
mlt_service service = context_pop_service( context, &type );
- mlt_position length = 0;
if ( type == mlt_playlist_type && service != NULL )
{
{
if ( xmlStrcmp( atts[0], _x("length") ) == 0 )
{
- length = atoll( _s(atts[1]) );
+ // Append a blank to the playlist
+ mlt_playlist_blank_time( MLT_PLAYLIST( service ), _s(atts[1]) );
break;
}
}
- // Append a blank to the playlist
- mlt_playlist_blank( MLT_PLAYLIST( service ), length - 1 );
-
// Push the playlist back onto the stack
context_push_service( context, service, type );
}
{
mlt_producer entry = NULL;
mlt_properties temp = mlt_properties_new( );
+ mlt_properties_set_data( temp, "_profile", context->profile, 0, NULL, NULL );
+ mlt_properties_set_lcnumeric( temp, context->lc_numeric );
for ( ; atts != NULL && *atts != NULL; atts += 2 )
{
if ( mlt_properties_get_data( temp, "producer", NULL ) != NULL )
{
mlt_playlist_clip_info info;
- enum service_type parent_type = invalid_type;
+ enum service_type parent_type = mlt_invalid_type;
mlt_service parent = context_pop_service( context, &parent_type );
mlt_producer producer = mlt_properties_get_data( temp, "producer", NULL );
static void on_end_entry( deserialise_context context, const xmlChar *name )
{
// Get the entry from the stack
- enum service_type entry_type = invalid_type;
+ enum service_type entry_type = mlt_invalid_type;
mlt_service entry = context_pop_service( context, &entry_type );
if ( entry == NULL && entry_type != mlt_entry_type )
if ( track != NULL && track_type == mlt_entry_type )
{
mlt_properties track_props = MLT_SERVICE_PROPERTIES( track );
- enum service_type parent_type = invalid_type;
+ enum service_type parent_type = mlt_invalid_type;
mlt_service parent = context_pop_service( context, &parent_type );
mlt_multitrack multitrack = NULL;
if ( parent != NULL )
context_push_service( context, parent, parent_type );
-
- mlt_service_close( track );
}
else
{
mlt_log_error( NULL, "[producer_xml] Invalid state at end of track\n" );
}
+
+ if ( track )
+ {
+ mlt_service_close( track );
+ free( track );
+ }
}
static void on_start_filter( deserialise_context context, const xmlChar *name, const xmlChar **atts)
mlt_service service = context_pop_service( context, &type );
mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
- enum service_type parent_type = invalid_type;
+ enum service_type parent_type = mlt_invalid_type;
mlt_service parent = context_pop_service( context, &parent_type );
if ( service != NULL && type == mlt_dummy_filter_type )
if ( parent )
context_push_service( context, parent, parent_type );
mlt_service_close( service );
+ free( service );
return;
}
{
mlt_log_error( NULL, "[producer_xml] filter closed with invalid parent...\n" );
}
-
- // Close the dummy filter service
- mlt_service_close( service );
}
else
{
mlt_log_error( NULL, "[producer_xml] Invalid top of stack on filter close\n" );
}
+
+ if ( service )
+ {
+ mlt_service_close( service );
+ free(service);
+ }
}
static void on_start_transition( deserialise_context context, const xmlChar *name, const xmlChar **atts)
mlt_service service = context_pop_service( context, &type );
mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
- enum service_type parent_type = invalid_type;
+ enum service_type parent_type = mlt_invalid_type;
mlt_service parent = context_pop_service( context, &parent_type );
if ( service != NULL && type == mlt_dummy_transition_type )
if ( parent )
context_push_service( context, parent, parent_type );
mlt_service_close( service );
+ free( service );
return;
}
track_service( context->destructors, effect, (mlt_destructor) mlt_transition_close );
mlt_log_error( NULL, "[producer_xml] transition closed with invalid parent...\n" );
}
- // Close the dummy filter service
- mlt_service_close( service );
}
else
{
mlt_log_error( NULL, "[producer_xml] Invalid top of stack on transition close\n" );
}
+
+ if ( service )
+ {
+ mlt_service_close( service );
+ free( service );
+ }
}
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 properties = mlt_properties_new();
mlt_properties_set_lcnumeric( properties, context->lc_numeric );
- context_push_service( context, MLT_CONSUMER_SERVICE(consumer), mlt_dummy_consumer_type );
+ context_push_service( context, (mlt_service) properties, mlt_dummy_consumer_type );
// Set the properties from attributes
for ( ; atts != NULL && *atts != NULL; atts += 2 )
{
// Get the consumer from the stack
enum service_type type;
- mlt_service service = context_pop_service( context, &type );
+ mlt_properties properties = (mlt_properties) context_pop_service( context, &type );
- if ( service && type == mlt_dummy_consumer_type )
+ if ( properties && 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" ) );
+ char *resource = mlt_properties_get( properties, "resource" );
- if ( context->multi_consumer > 1 )
+ if ( context->multi_consumer > 1 || context->qglsl )
{
// Instantiate the multi consumer
if ( !context->consumer )
{
- context->consumer = mlt_factory_consumer( context->profile, "multi", NULL );
+ if ( context->qglsl )
+ context->consumer = context->qglsl;
+ else
+ context->consumer = mlt_factory_consumer( context->profile, "multi", NULL );
if ( context->consumer )
{
// Track this consumer
}
if ( context->consumer )
{
- // Set this service instance on multi consumer
+ // Set this properties object 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 );
+ mlt_properties_inc_ref( properties );
+ mlt_properties_set_data( MLT_CONSUMER_PROPERTIES(context->consumer), key, properties, 0,
+ (mlt_destructor) mlt_properties_close, NULL );
}
}
else
// Inherit the properties
mlt_properties_inherit( MLT_CONSUMER_PROPERTIES(context->consumer), properties );
}
- // Close the dummy
- mlt_service_close( service );
}
}
+ // Close the dummy
+ if ( properties )
+ mlt_properties_close( properties );
}
}
on_start_profile( context, name, atts );
if ( xmlStrcmp( name, _x("consumer") ) == 0 )
context->multi_consumer++;
+
+ // Check for a service beginning with glsl. or movit.
+ for ( ; atts != NULL && *atts != NULL; atts += 2 ) {
+ if ( !xmlStrncmp( atts[1], _x("glsl."), 5 ) || !xmlStrncmp( atts[1], _x("movit."), 6 ) ) {
+ mlt_properties_set_int( context->params, "qglsl", 1 );
+ break;
+ }
+ }
return;
}
context->branch[ context->depth ] ++;
{
struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
deserialise_context context = ( deserialise_context )( xmlcontext->_private );
- char *value = calloc( len + 1, 1 );
+ char *value = calloc( 1, len + 1 );
enum service_type type;
mlt_service service = context_pop_service( context, &type );
mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
if ( s != NULL )
{
// Append new text to existing content
- char *new = calloc( strlen( s ) + len + 1, 1 );
+ char *new = calloc( 1, strlen( s ) + len + 1 );
strcat( new, s );
strcat( new, value );
mlt_properties_set( properties, context->property, new );
mlt_properties_set( properties, context->property, value );
}
context->entity_is_replace = 0;
-
+
+ // Check for a service beginning with glsl. or movit.
+ if ( !strncmp( value, "glsl.", 5 ) || !strncmp( value, "movit.", 6 ) )
+ mlt_properties_set_int( context->params, "qglsl", 1 );
+
free( value);
}
case ':':
case '=':
- url[ i++ ] = '\0';
- value = &url[ i ];
+#ifdef WIN32
+ if ( url[i] == ':' && url[i + 1] != '/' )
+ {
+#endif
+ url[ i++ ] = '\0';
+ value = &url[ i ];
+#ifdef WIN32
+ }
+#endif
break;
case '&':
return exists;
}
+// This function will add remaing services in the context service stack marked
+// with a "xml_retain" property to a property named "xml_retain" on the returned
+// service. The property is a mlt_properties data property.
+
+static void retain_services( struct deserialise_context_s *context, mlt_service service )
+{
+ mlt_properties retain_list = mlt_properties_new();
+ enum service_type type;
+ mlt_service retain_service = context_pop_service( context, &type );
+
+ while ( retain_service )
+ {
+ mlt_properties retain_properties = MLT_SERVICE_PROPERTIES( retain_service );
+
+ if ( mlt_properties_get_int( retain_properties, "xml_retain" ) )
+ {
+ // Remove the retained service from the destructors list.
+ int i;
+ for ( i = mlt_properties_count( context->destructors ) - 1; i >= 1; i -- )
+ {
+ const char *name = mlt_properties_get_name( context->destructors, i );
+ if ( mlt_properties_get_data_at( context->destructors, i, NULL ) == retain_service )
+ {
+ mlt_properties_set_data( context->destructors, name, retain_service, 0, NULL, NULL );
+ break;
+ }
+ }
+ const char *name = mlt_properties_get( retain_properties, "id" );
+ if ( name )
+ mlt_properties_set_data( retain_list, name, retain_service, 0,
+ (mlt_destructor) mlt_service_close, NULL );
+ }
+ retain_service = context_pop_service( context, &type );
+ }
+ if ( mlt_properties_count( retain_list ) > 0 )
+ {
+ mlt_properties_set_data( MLT_SERVICE_PROPERTIES(service), "xml_retain", retain_list, 0,
+ (mlt_destructor) mlt_properties_close, NULL );
+ }
+}
+
mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, const char *id, char *data )
{
- xmlSAXHandler *sax = calloc( 1, sizeof( xmlSAXHandler ) );
- struct deserialise_context_s *context = calloc( 1, sizeof( struct deserialise_context_s ) );
+ xmlSAXHandler *sax, *sax_orig;
+ struct deserialise_context_s *context;
mlt_properties properties = NULL;
int i = 0;
struct _xmlParserCtxt *xmlcontext;
int well_formed = 0;
char *filename = NULL;
- int info = strcmp( id, "xml-string" ) ? 0 : 1;
+ int is_filename = strcmp( id, "xml-string" );
+
+ // Strip file:// prefix
+ if ( data && strlen( data ) >= 7 && strncmp( data, "file://", 7 ) == 0 )
+ data += 7;
- if ( data == NULL || !strcmp( data, "" ) || ( info == 0 && !file_exists( data ) ) )
+ if ( data == NULL || !strcmp( data, "" ) || ( is_filename && !file_exists( data ) ) )
return NULL;
context = calloc( 1, sizeof( struct deserialise_context_s ) );
context->destructors = mlt_properties_new();
context->params = mlt_properties_new();
context->profile = profile;
+ context->seekable = 1;
// Decode URL and parse parameters
mlt_properties_set( context->producer_map, "root", "" );
- if ( info == 0 )
+ if ( is_filename )
{
filename = strdup( data );
parse_url( context->params, url_decode( filename, data ) );
mlt_properties_set_int( context->destructors, "registered", 0 );
// Setup SAX callbacks for first pass
+ sax = calloc( 1, sizeof( xmlSAXHandler ) );
sax->startElement = on_start_element;
+ sax->characters = on_characters;
sax->warning = on_error;
sax->error = on_error;
sax->fatalError = on_error;
xmlSubstituteEntitiesDefault( 1 );
// This is used to facilitate entity substitution in the SAX parser
context->entity_doc = xmlNewDoc( _x("1.0") );
- if ( info == 0 )
+ if ( is_filename )
xmlcontext = xmlCreateFileParserCtxt( filename );
else
xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
}
// Parse
+ sax_orig = xmlcontext->sax;
xmlcontext->sax = sax;
xmlcontext->_private = ( void* )context;
xmlParseDocument( xmlcontext );
well_formed = xmlcontext->wellFormed;
// Cleanup after parsing
- xmlcontext->sax = NULL;
+ xmlcontext->sax = sax_orig;
xmlcontext->_private = NULL;
+ if ( xmlcontext->myDoc )
+ xmlFreeDoc( xmlcontext->myDoc );
xmlFreeParserCtxt( xmlcontext );
context->stack_node_size = 0;
context->stack_service_size = 0;
// Setup the second pass
context->pass ++;
- if ( info == 0 )
+ if ( is_filename )
xmlcontext = xmlCreateFileParserCtxt( filename );
else
xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
return NULL;
}
+ // Create the qglsl consumer now, if requested, so that glsl.manager
+ // may exist when trying to load glsl. or movit. services.
+ // The "if requested" part can come from query string qglsl=1 or when
+ // a service beginning with glsl. or movit. appears in the XML.
+ if ( mlt_properties_get_int( context->params, "qglsl" ) && strcmp( id, "xml-nogl" ) )
+ context->qglsl = mlt_factory_consumer( profile, "qglsl", NULL );
+
// Setup SAX callbacks for second pass
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;
// Parse
+ sax_orig = xmlcontext->sax;
xmlcontext->sax = sax;
xmlcontext->_private = ( void* )context;
xmlParseDocument( xmlcontext );
xmlFreeDoc( context->entity_doc );
free( sax );
xmlMemoryDump( ); // for debugging
+ xmlcontext->sax = sax_orig;
+ xmlcontext->_private = NULL;
+ if ( xmlcontext->myDoc )
+ xmlFreeDoc( xmlcontext->myDoc );
+ xmlFreeParserCtxt( xmlcontext );
// Get the last producer on the stack
enum service_type type;
if ( getenv( "MLT_XML_DEEP" ) == NULL )
{
// Now assign additional properties
- if ( info == 0 )
+ if ( is_filename && (
+ mlt_service_identify( service ) == tractor_type ||
+ mlt_service_identify( service ) == playlist_type ||
+ mlt_service_identify( service ) == multitrack_type ) )
+ {
+ mlt_properties_set_int( properties, "_original_type",
+ mlt_service_identify( service ) );
+ mlt_properties_set( properties, "_original_resource",
+ mlt_properties_get( properties, "resource" ) );
mlt_properties_set( properties, "resource", data );
+ }
// This tells consumer_xml not to deep copy
mlt_properties_set( properties, "xml", "was here" );
mlt_properties_inc_ref( MLT_CONSUMER_PROPERTIES( context->consumer ) );
mlt_properties_set_data( properties, "consumer", context->consumer, 0,
(mlt_destructor) mlt_consumer_close, NULL );
+
+ mlt_properties_set_int( properties, "seekable", context->seekable );
+
+ retain_services( context, service );
}
else
{
}
// Clean up
+ if ( context->qglsl && context->consumer != context->qglsl )
+ mlt_consumer_close( context->qglsl );
mlt_properties_close( context->producer_map );
if ( context->params != NULL )
mlt_properties_close( context->params );