From 236717e4ecc23e458e191b6c9a2ba70915b80937 Mon Sep 17 00:00:00 2001 From: Dan Dennedy Date: Tue, 28 Jan 2014 22:40:53 -0800 Subject: [PATCH] Add xml_retain property support to xml module. This is used to serialize and deserialize extra services that are not part of the lastmost service's graph. This is useful, for example, to save and load a media bin as a playlist in addition to the main multitrack graph. Or, it can be used for compound documents. --- src/modules/xml/consumer_xml.c | 25 +++++++++++++++++++ src/modules/xml/consumer_xml.yml | 11 ++++++++ src/modules/xml/producer_xml.c | 43 ++++++++++++++++++++++++++++++++ src/modules/xml/producer_xml.yml | 10 ++++++++ 4 files changed, 89 insertions(+) diff --git a/src/modules/xml/consumer_xml.c b/src/modules/xml/consumer_xml.c index 01a34de3..92988f14 100644 --- a/src/modules/xml/consumer_xml.c +++ b/src/modules/xml/consumer_xml.c @@ -392,6 +392,7 @@ static void serialise_multitrack( serialise_context context, mlt_service service xmlNewProp( track, _x("in"), _x( mlt_properties_get_time( properties, "in", context->time_format ) ) ); xmlNewProp( track, _x("out"), _x( mlt_properties_get_time( properties, "out", context->time_format ) ) ); serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, context->store ); + serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, "xml_" ); if ( !context->no_meta ) serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, "meta." ); serialise_service_filters( context, MLT_PRODUCER_SERVICE( producer ), track ); @@ -446,6 +447,7 @@ static void serialise_playlist( serialise_context context, mlt_service service, // Store application specific properties serialise_store_properties( context, properties, child, context->store ); + serialise_store_properties( context, properties, child, "xml_" ); if ( !context->no_meta ) serialise_store_properties( context, properties, child, "meta." ); @@ -485,6 +487,7 @@ static void serialise_playlist( serialise_context context, mlt_service service, if ( mlt_producer_is_cut( info.cut ) ) { serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, context->store ); + serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, "xml_" ); if ( !context->no_meta ) serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( info.cut ), entry, "meta." ); serialise_service_filters( context, MLT_PRODUCER_SERVICE( info.cut ), entry ); @@ -534,6 +537,7 @@ static void serialise_tractor( serialise_context context, mlt_service service, x // Store application specific properties serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, context->store ); + serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, "xml_" ); if ( !context->no_meta ) serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, "meta." ); @@ -691,6 +695,25 @@ static void serialise_service( serialise_context context, mlt_service service, x } } +static void serialise_other( mlt_properties properties, struct serialise_context_s *context, xmlNodePtr root ) +{ + int i; + mlt_properties_debug( properties, __FUNCTION__, stderr ); + for ( i = 0; i < mlt_properties_count( properties ); i++ ) + { + const char* name = mlt_properties_get_name( properties, i ); + if ( strlen(name) > 10 && !strncmp( name, "xml_retain", 10 ) ) + { + mlt_service service = mlt_properties_get_data_at( properties, i, NULL ); + if ( service ) + { + mlt_properties_set_int( MLT_SERVICE_PROPERTIES( service ), "xml_retain", 1 ); + serialise_service( context, service, root ); + } + } + } +} + xmlDocPtr xml_make_doc( mlt_consumer consumer, mlt_service service ) { mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); @@ -772,11 +795,13 @@ xmlDocPtr xml_make_doc( mlt_consumer consumer, mlt_service service ) // In pass one, we serialise the end producers and playlists, // adding them to a map keyed by address. + serialise_other( MLT_SERVICE_PROPERTIES( service ), context, root ); serialise_service( context, service, root ); // In pass two, we serialise the tractor and reference the // producers and playlists context->pass++; + serialise_other( MLT_SERVICE_PROPERTIES( service ), context, root ); serialise_service( context, service, root ); // Cleanup resource diff --git a/src/modules/xml/consumer_xml.yml b/src/modules/xml/consumer_xml.yml index 7a599d75..b75a90e3 100644 --- a/src/modules/xml/consumer_xml.yml +++ b/src/modules/xml/consumer_xml.yml @@ -13,11 +13,22 @@ tags: description: > Serialise the service network to XML. See docs/mlt-xml.txt for more information. + +notes: > + If you set a data property beginning with (and longer than) "xml_retain" on + the service connected to this consumer where the data is a mlt_service + pointer, then the pointed at service will also be serialized before the + connected service. This can be useful, for example, to save a playlist as + a media bin along with a multitrack. You can serialize more than one of these + additional services by setting more than property, each with a unique key + beginning with "xml_retain". + bugs: - Untested arbitrary nesting of multitracks and playlists. - > Property "id" is generated as service type followed by number if no property named "id" exists, but it fails to guarantee uniqueness. + parameters: - identifier: resource argument: yes diff --git a/src/modules/xml/producer_xml.c b/src/modules/xml/producer_xml.c index b161bd6a..086657f4 100644 --- a/src/modules/xml/producer_xml.c +++ b/src/modules/xml/producer_xml.c @@ -1568,6 +1568,47 @@ static int file_exists( char *file ) 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, *sax_orig; @@ -1816,6 +1857,8 @@ mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, (mlt_destructor) mlt_consumer_close, NULL ); mlt_properties_set_int( properties, "seekable", context->seekable ); + + retain_services( context, service ); } else { diff --git a/src/modules/xml/producer_xml.yml b/src/modules/xml/producer_xml.yml index 02988de2..0df379ac 100644 --- a/src/modules/xml/producer_xml.yml +++ b/src/modules/xml/producer_xml.yml @@ -12,6 +12,16 @@ tags: - Video description: | Construct a service network from an XML description. See docs/mlt-xml.txt. + +notes: > + If there is a service with a property "xml_retain=1" that is not the + producer, and if it also has an "id" property; then the extra service + is put into a properties list keyed on the id property. Then, that + properties list is placed as a property on the returned service with + the name "xml_retain". This lets an application retrieve additional + deserialized services that are not the lastmost producer or anywhere in + its graph. + parameters: - identifier: argument title: File -- 2.39.2