From 1d3ab5568258841b1741b8c2fc88ce4e958e6fc6 Mon Sep 17 00:00:00 2001 From: ddennedy Date: Fri, 26 Mar 2004 07:10:29 +0000 Subject: [PATCH] enhance miracle LOAD command to accept a service: prefix. enhance producer_westley to apply parameters on url as entities. bugfix producer_westley memory leak. git-svn-id: https://mlt.svn.sourceforge.net/svnroot/mlt/trunk/mlt@246 d19143bc-622f-0410-bfdd-b5b2a6649095 --- demo/README | 2 + demo/entity.westley | 10 ++ docs/services.txt | 15 ++- docs/westley.txt | 127 ++++++++++++++++++++++--- src/miracle/miracle_unit_commands.c | 22 ++++- src/modules/westley/producer_westley.c | 125 +++++++++++++++++++++++- 6 files changed, 280 insertions(+), 21 deletions(-) create mode 100644 demo/entity.westley diff --git a/demo/README b/demo/README index 5fc256d6..c0d16180 100644 --- a/demo/README +++ b/demo/README @@ -73,6 +73,8 @@ Clock in and out This image can be a 16 bit PGM (grayscale bitmap) or the luma channel of any video producer. A number of high quality wipes can be downloaded from http://mlt.sf.net/. It also performs field rendering. + The second wipe demonstrates the ability to control the direction of the + wipe as well. Obscure diff --git a/demo/entity.westley b/demo/entity.westley new file mode 100644 index 00000000..a75b0fae --- /dev/null +++ b/demo/entity.westley @@ -0,0 +1,10 @@ + + +]> + + + pango + + + diff --git a/docs/services.txt b/docs/services.txt index 07b6d01e..068a79ad 100644 --- a/docs/services.txt +++ b/docs/services.txt @@ -439,8 +439,18 @@ Producers Constructor Argument - file - an XML text file containing westley XML (schema pending) - + URL - an XML text file containing westley XML (schema/DTD pending) + - Since westley files can be parameterised, the URL syntax is: + {file-name}[?{param-name}{'='|':'}{param-value}[&{param-name}{'='|':'}{param-value}...]] + A colon is allowed instead of an equal sign to pacify inigo, + who tokenises anything with an equal sign as a property + setting. Also, when running inigo from the shell, beware of + the '?' and shell filename expansion. You can surround the URL + with single quotations to prevent expansion. Finally, fezzik + will fail to match the filename when you use parameters, so + preface the url with 'westley:' to force fezzik to load with + the westley service. + Read Only Properties string resource - file location @@ -1119,6 +1129,7 @@ Transitions double softness - only when using a luma map, how soft to make the edges between A and B. 0.0 = no softness. 1.0 = too soft. + int reverse - reverse the direction of the transition. Any property starting with "producer." is passed to the non-PGM luma producer. diff --git a/docs/westley.txt b/docs/westley.txt index 2d5e2919..42f1a935 100644 --- a/docs/westley.txt +++ b/docs/westley.txt @@ -231,9 +231,10 @@ Tractors: Note that it's only applied to the visible parts of the top track. - The requirement to apply a filter to the output, as opposed to a specific track - leads us to the final item in the Rules section above. As an example, let's - assume we wish to watermark all output, then we could use the following: + The requirement to apply a filter to the output, as opposed to a specific + track leads us to the final item in the Rules section above. As an example, + let's assume we wish to watermark all output, then we could use the + following: @@ -278,8 +279,60 @@ Tractors: be better handled at the playout stage itself (ie: as a filter automatically placed between all producers and the consumer). - TODO: transition example + Tracks act like "layers" in an image processing program like the GIMP. The + bottom-most track takes highest priority and higher layers are overlays + and do not appear unless there are gaps in the lower layers or unless + a transition is applied that merges the tracks on the specifed region. + Practically speaking, for A/B video editing it does not mean too much, + and it will work as expected; however, as a general rule apply any CGI + (graphic overlays with pixbuf or titles with pango) on tracks higher than + your video tracks. Also, this means that any audio-only tracks that are + lower than your video tracks will play rather than the audio from the video + clip. Remember, nothing is affected like mixing or compositing until one + applies a transition or appropriate filter. + + + + clip1.dv + + + + + + clip2.mpeg + + + + + + + + + + + + 0 + 1 + luma + + + 0 + 1 + mix + 0.0 + 1.0 + + + + A "luma" transition is a video wipe processor that takes a greyscale bitmap + for the wipe definition. When one does not specify a bitmap, luma performs + a dissolve. The "mix" transition does an audio mix, but it interpolates + between the gain scaling factors between the start and end properties - + in this example, from 0.0 (none of track B) to 1.0 (all of track B). + Because the bottom track starts out with a gap specified using the + element, the upper track appears during the blank segment. See the demos and + services.txt to get an idea of the capabilities of the included transitions. Flexibility: @@ -411,10 +464,54 @@ Flexibility: any embedded XML that contains an element named "property" because westley collects embedded XML until it reaches a closing property tag. - TODO: xml entities - + +Entities and Parameterisation: + + The westley producer parser supports XML entities. An example: + + + + ]> + + + pango + &msg; + + + + If you are embedding another XML document into a property value not using + a CNODE section, then any DOCTYPE section must be relocated before any of + the xml elements to be well-formed. See demo/dvg.westley for an example. + + Entities can be used to parameterise westley! Using the above example, the + entity declared serves as the default value for &msg;. The entity content + can be overridden from the resource property supplied to the westley + producer. The syntax is the familiar, url-encoded query string used with + HTTP, e.g.: file?name=value&name=value... + + There are a couple of rules of usage. The Miracle LOAD command and inigo + command line tool require you to preface the URL with "westley:" because + the query string destroys the filename extension matching peformed by + Fezzik. Also, inigo looks for '=' to tokenise property settings. Therefore, + one uses ':' between name and value instead of '='. Finally, since inigo + is run from the shell, one must enclose the URL within single quotes to + prevent shell filename expansion, or similar. + + Needless to say, the ability to parameterise westley XML compositions is + an extremely powerful tool. The above example is avialable in + demo/entity.westley for you to try out. Override the message from inigo: + inigo 'westley:entity.westley?msg:Amazing!' + + Technically, the entity declaration is not needed in the head of the XML + document if you always supply the parameter. However, you run the risk + of unpredictable behviour without one. Therefore, it is safest and a best + practice to always supply an entity declaration. It is improves the + readability as one does not need to search for the entity references to + see what parameters are available. + -Technique: +Tips and Technique: If one finds the above hierarchical, abbreviated format intuitive, start with a simple template and fill and extend as needed: @@ -452,9 +549,17 @@ Technique: + If you end up making a collection of templates for various situations, then + consider using XML Entities to make the template more effective by moving + anything that should parameterised into an entity. + If you want to have a silent, black background for audio and video fades, - then make the last track simply . Then, - use composite and volume key-framable properties. - TODO: to be continued + then make the top track simply . Then, + use composite and volume effects. See the "Fade from/to black/silence" + demo for an example (demo/mlt_fade_black). - TODO: considerations with mixing multiple audio layers + If you apply the reverse=1 property to a transition like "luma," then + be careful because it also inherently swaps the roles of A and B tracks. + Therefore, you need to might need to swap the a_track and b_track values + if it did not turn out the way you expected. See the "Clock in and out" + for an example (demo/mlt_clock_in_and_out). diff --git a/src/miracle/miracle_unit_commands.c b/src/miracle/miracle_unit_commands.c index 95737f4d..53966d46 100644 --- a/src/miracle/miracle_unit_commands.c +++ b/src/miracle/miracle_unit_commands.c @@ -40,6 +40,7 @@ int miracle_load( command_argument cmd_arg ) char *filename = (char*) cmd_arg->argument; char fullname[1024]; int flush = 1; + char *service; if ( filename[0] == '!' ) { @@ -47,10 +48,25 @@ int miracle_load( command_argument cmd_arg ) filename ++; } - if ( strlen( cmd_arg->root_dir ) && filename[0] == '/' ) - filename++; + service = strchr( filename, ':' ); + if ( service != NULL ) + { + service = filename; + filename = strchr( service, ':' ); + *filename ++ = '\0'; + + if ( strlen( cmd_arg->root_dir ) && filename[0] == '/' ) + filename++; + + snprintf( fullname, 1023, "%s:%s%s", service, cmd_arg->root_dir, filename ); + } + else + { + if ( strlen( cmd_arg->root_dir ) && filename[0] == '/' ) + filename++; - snprintf( fullname, 1023, "%s%s", cmd_arg->root_dir, filename ); + snprintf( fullname, 1023, "%s%s", cmd_arg->root_dir, filename ); + } if (unit == NULL) return RESPONSE_INVALID_UNIT; diff --git a/src/modules/westley/producer_westley.c b/src/modules/westley/producer_westley.c index 38f31507..1f1d6dcc 100644 --- a/src/modules/westley/producer_westley.c +++ b/src/modules/westley/producer_westley.c @@ -20,13 +20,13 @@ // TODO: destroy unreferenced producers (they are currently destroyed // when the returned producer is closed). -// TODO: determine why deserialise_context can not be released. #include "producer_westley.h" #include #include #include #include +#include #include #include // for xmlCreateFileParserCtxt @@ -55,6 +55,9 @@ struct deserialise_context_s xmlDocPtr entity_doc; int depth; int branch[ STACK_SIZE ]; + const xmlChar *publicId; + const xmlChar *systemId; + mlt_properties params; }; typedef struct deserialise_context_s *deserialise_context; @@ -985,6 +988,28 @@ static void on_characters( void *ctx, const xmlChar *ch, int len ) free( value); } +/** Convert parameters parsed from resource into entity declarations. +*/ +static void params_to_entities( deserialise_context context ) +{ + if ( context->params != NULL ) + { + int i; + + // Add our params as entitiy declarations + for ( i = 0; i < mlt_properties_count( context->params ); i++ ) + { + xmlChar *name = ( xmlChar* )mlt_properties_get_name( context->params, i ); + xmlAddDocEntity( context->entity_doc, name, XML_INTERNAL_GENERAL_ENTITY, + context->publicId, context->systemId, ( xmlChar* )mlt_properties_get( context->params, name ) ); + } + + // Flag completion + mlt_properties_close( context->params ); + context->params = NULL; + } +} + // The following 3 facilitate entity substitution in the SAX parser static void on_internal_subset( void *ctx, const xmlChar* name, const xmlChar* publicId, const xmlChar* systemId ) @@ -992,7 +1017,12 @@ static void on_internal_subset( void *ctx, const xmlChar* name, struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx; deserialise_context context = ( deserialise_context )( xmlcontext->_private ); + context->publicId = publicId; + context->systemId = systemId; xmlCreateIntSubset( context->entity_doc, name, publicId, systemId ); + + // Override default entities with our parameters + params_to_entities( context ); } static void on_entity_declaration( void *ctx, const xmlChar* name, int type, @@ -1009,11 +1039,88 @@ xmlEntityPtr on_get_entity( void *ctx, const xmlChar* name ) struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx; deserialise_context context = ( deserialise_context )( xmlcontext->_private ); + // Setup for entity declarations if not ready + if ( xmlGetIntSubset( context->entity_doc ) == NULL ) + { + xmlCreateIntSubset( context->entity_doc, "westley", "", "" ); + context->publicId = ""; + context->systemId = ""; + } + + // Add our parameters if not already + params_to_entities( context ); + return xmlGetDocEntity( context->entity_doc, name ); } +/** Convert a hexadecimal character to its value. +*/ +static int tohex( char p ) +{ + return isdigit( p ) ? p - '0' : tolower( p ) - 'a' + 10; +} -mlt_producer producer_westley_init( char *filename ) +/** Decode a url-encoded string containing hexadecimal character sequences. +*/ +static char *url_decode( char *dest, char *src ) +{ + char *p = dest; + + while ( *src ) + { + if ( *src == '%' ) + { + *p ++ = ( tohex( *( src + 1 ) ) << 4 ) | tohex( *( src + 2 ) ); + src += 3; + } + else + { + *p ++ = *src ++; + } + } + + *p = *src; + return dest; +} + +/** Extract the filename from a URL attaching parameters to a properties list. +*/ +static void parse_url( mlt_properties properties, char *url ) +{ + int i; + int n = strlen( url ); + char *name = NULL; + char *value = NULL; + + for ( i = 0; i < n; i++ ) + { + switch ( url[ i ] ) + { + case '?': + url[ i++ ] = '\0'; + name = &url[ i ]; + break; + + case ':': + case '=': + url[ i++ ] = '\0'; + value = &url[ i ]; + break; + + case '&': + url[ i++ ] = '\0'; + if ( name != NULL && value != NULL ) + mlt_properties_set( properties, name, value ); + name = &url[ i ]; + value = NULL; + break; + } + } + if ( name != NULL && value != NULL ) + mlt_properties_set( properties, name, value ); +} + +mlt_producer producer_westley_init( char *url ) { xmlSAXHandler *sax = calloc( 1, sizeof( xmlSAXHandler ) ); struct deserialise_context_s *context = calloc( 1, sizeof( struct deserialise_context_s ) ); @@ -1021,9 +1128,14 @@ mlt_producer producer_westley_init( char *filename ) int i = 0; struct _xmlParserCtxt *xmlcontext; int well_formed = 0; + char *filename = strdup( url ); context->producer_map = mlt_properties_new(); context->destructors = mlt_properties_new(); + context->params = mlt_properties_new(); + + // Decode URL and parse parameters + parse_url( context->params, url_decode( filename, url ) ); // We need to track the number of registered filters mlt_properties_set_int( context->destructors, "registered", 0 ); @@ -1111,7 +1223,7 @@ mlt_producer producer_westley_init( char *filename ) mlt_properties_set_data( properties, "__destructors__", context->destructors, 0, (mlt_destructor) mlt_properties_close, NULL ); // Now assign additional properties - mlt_properties_set( properties, "resource", filename ); + mlt_properties_set( properties, "resource", url ); // This tells consumer_westley not to deep copy mlt_properties_set( properties, "westley", "was here" ); @@ -1125,9 +1237,12 @@ mlt_producer producer_westley_init( char *filename ) mlt_properties_close( context->destructors ); } - free( context->stack_service ); + // Clean up mlt_properties_close( context->producer_map ); - //free( context ); + if ( context->params != NULL ) + mlt_properties_close( context->params ); + free( context ); + free( filename ); return MLT_PRODUCER( service ); } -- 2.39.2