]> git.sesse.net Git - mlt/commitdiff
enhance miracle LOAD command to accept a service: prefix.
authorddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
Fri, 26 Mar 2004 07:10:29 +0000 (07:10 +0000)
committerddennedy <ddennedy@d19143bc-622f-0410-bfdd-b5b2a6649095>
Fri, 26 Mar 2004 07:10:29 +0000 (07:10 +0000)
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
demo/entity.westley [new file with mode: 0644]
docs/services.txt
docs/westley.txt
src/miracle/miracle_unit_commands.c
src/modules/westley/producer_westley.c

index 5fc256d646913d65a54574f73f61be1d496e4e19..c0d16180115fd8342c2b29b1575023ec7bc01b05 100644 (file)
@@ -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 (file)
index 0000000..a75b0fa
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<!DOCTYPE westley [
+       <!ENTITY msg "Hello world!">
+]>
+<westley>
+  <producer id="producer0">
+    <property name="mlt_service">pango</property>
+       <property name="text" value="&msg;"/>
+  </producer>
+</westley>
index 07b6d01ec9c5547ea6150ea379f688f8bb5de072..068a79ad39fe4fb9dffa716ed389d137cfe1e33b 100644 (file)
@@ -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.
                        
index 2d5e29196ee016d3571c52af6cde6e498ca6d040..42f1a9359b617804d84b7bf4af40300dde4b4612 100644 (file)
@@ -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:
 
        <westley>
          <producer id="producer0">
@@ -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.
+       
+       <westley>
+         <producer id="producer0">
+               <property name="resource">clip1.dv</property>
+         </producer>
+         <playlist id="playlist0">
+               <entry producer="producer0"/>
+         </playlist>
+         <producer id="producer1">
+               <property name="resource">clip2.mpeg</property>
+         </producer>
+         <playlist id="playlist1">
+               <blank length="50"/>
+               <entry producer="producer1"/>
+         </playlist>
+         <tractor id="tractor0" in="0" out="315">
+               <multitrack id="multitrack0">
+                 <track producer="playlist0"/>
+                 <track producer="playlist1"/>
+               </multitrack>
+               <transition id="transition0" in="50" out="74">
+                 <property name="a_track">0</property>
+                 <property name="b_track">1</property>
+                 <property name="mlt_service">luma</property>
+               </transition>
+               <transition id="transition1" in="50" out="74">
+                 <property name="a_track">0</property>
+                 <property name="b_track">1</property>
+                 <property name="mlt_service">mix</property>
+                 <property name="start">0.0</property>
+                 <property name="end">1.0</property>
+               </transition>
+         </tractor>
+       </westley>
 
+       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 <blank>
+       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:
+       
+       <?xml version="1.0"?>
+       <!DOCTYPE westley [
+               <!ENTITY msg "Hello world!">
+       ]>
+       <westley>
+         <producer id="producer0">
+               <property name="mlt_service">pango</property>
+               <property name="text">&msg;</property>
+         </producer>
+       </westley>
+       
+       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:
                  </entry>
                </playlist>
 
+       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 <producer mlt_service="colour"/>. Then,
-       use composite and volume key-framable properties. 
-       TODO: to be continued
+       then make the top track simply <producer mlt_service="colour"/>. 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).
index 95737f4d5d1e85cd4e699a07955dc838276d4014..53966d46a0c939ef897395f4ed7873e0999af0c9 100644 (file)
@@ -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;
index 38f31507abd1e7d931d30f6e21732de8fc853021..1f1d6dcc609866313f979e7b660eae350ff2b2e3 100644 (file)
 
 // 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 <framework/mlt.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
+#include <ctype.h>
 
 #include <libxml/parser.h>
 #include <libxml/parserInternals.h> // 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 );
 }