1 /*
2 * producer_xml.c -- a libxml2 parser of mlt service networks
3 * Copyright (C) 2003-2020 Meltytech, LLC
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 // TODO: destroy unreferenced producers (they are currently destroyed
21 // when the returned producer is closed).
22
23 #include "common.h"
24
25 #include <framework/mlt.h>
26 #include <framework/mlt_log.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <unistd.h>
31
32 #include <libxml/parser.h>
33 #include <libxml/parserInternals.h> // for xmlCreateFileParserCtxt
34 #include <libxml/tree.h>
35
36 #define BRANCH_SIG_LEN 4000
37
38 #define _x (const xmlChar*)
39 #define _s (const char*)
40
41 #undef DEBUG
42 #ifdef DEBUG
43 extern xmlDocPtr xml_make_doc( mlt_service service );
44 #endif
45
46 enum service_type
47 {
48 mlt_invalid_type,
49 mlt_unknown_type,
50 mlt_producer_type,
51 mlt_playlist_type,
52 mlt_entry_type,
53 mlt_tractor_type,
54 mlt_multitrack_type,
55 mlt_filter_type,
56 mlt_transition_type,
57 mlt_consumer_type,
58 mlt_field_type,
59 mlt_services_type,
60 mlt_dummy_filter_type,
61 mlt_dummy_transition_type,
62 mlt_dummy_producer_type,
63 mlt_dummy_consumer_type
64 };
65
66 struct deserialise_context_s
67 {
68 mlt_deque stack_types;
69 mlt_deque stack_service;
70 mlt_properties producer_map;
71 mlt_properties destructors;
72 char *property;
73 int is_value;
74 xmlDocPtr value_doc;
75 mlt_deque stack_node;
76 xmlDocPtr entity_doc;
77 int entity_is_replace;
78 mlt_deque stack_branch;
79 const xmlChar *publicId;
80 const xmlChar *systemId;
81 mlt_properties params;
82 mlt_profile profile;
83 mlt_profile consumer_profile;
84 int pass;
85 char *lc_numeric;
86 mlt_consumer consumer;
87 int multi_consumer;
88 int consumer_count;
89 int seekable;
90 mlt_consumer qglsl;
91 };
92 typedef struct deserialise_context_s *deserialise_context;
93
94 /** Trim the leading and trailing whitespace from a string in-place.
95 */
trim(char * s)96 static char* trim( char *s )
97 {
98 int n;
99 if ( s && ( n = strlen( s ) ) )
100 {
101 int i = 0;
102 while ( i < n && isspace( s[i] ) ) i++;
103 while ( --n && isspace( s[n] ) );
104 n = n - i + 1;
105 if ( n > 0 )
106 memmove( s, s + i, n );
107 s[ n ] = 0;
108 }
109 return s;
110 }
111
112 /** Convert the numerical current branch address to a dot-delimited string.
113 */
serialise_branch(deserialise_context context,char * s)114 static char *serialise_branch( deserialise_context context, char *s )
115 {
116 int i, n = mlt_deque_count( context->stack_branch );
117
118 s[0] = 0;
119 for ( i = 0; i < n - 1; i++ )
120 {
121 int len = strlen( s );
122 snprintf( s + len, BRANCH_SIG_LEN - len, "%lu.",
123 (unsigned long) mlt_deque_peek( context->stack_branch, i ) );
124 }
125 return s;
126 }
127
128 /** Push a service.
129 */
130
context_push_service(deserialise_context context,mlt_service that,enum service_type type)131 static void context_push_service( deserialise_context context, mlt_service that, enum service_type type )
132 {
133 mlt_deque_push_back( context->stack_service, that );
134 mlt_deque_push_back_int( context->stack_types, type );
135
136 // Record the tree branch on which this service lives
137 if ( that != NULL && mlt_properties_get( MLT_SERVICE_PROPERTIES( that ), "_xml_branch" ) == NULL )
138 {
139 char s[ BRANCH_SIG_LEN ];
140 mlt_properties_set_string( MLT_SERVICE_PROPERTIES( that ), "_xml_branch", serialise_branch( context, s ) );
141 }
142 }
143
144 /** Pop a service.
145 */
146
context_pop_service(deserialise_context context,enum service_type * type)147 static mlt_service context_pop_service( deserialise_context context, enum service_type *type )
148 {
149 mlt_service result = NULL;
150
151 if ( type ) *type = mlt_invalid_type;
152 if ( mlt_deque_count( context->stack_service ) > 0 )
153 {
154 result = mlt_deque_pop_back( context->stack_service );
155 if ( type != NULL )
156 *type = mlt_deque_pop_back_int( context->stack_types );
157 // Set the service's profile and locale so mlt_property time-to-position conversions can get fps
158 if ( result )
159 {
160 mlt_properties_set_data( MLT_SERVICE_PROPERTIES( result ), "_profile", context->profile, 0, NULL, NULL );
161 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( result ), context->lc_numeric );
162 }
163 }
164 return result;
165 }
166
167 /** Push a node.
168 */
169
context_push_node(deserialise_context context,xmlNodePtr node)170 static void context_push_node( deserialise_context context, xmlNodePtr node )
171 {
172 mlt_deque_push_back( context->stack_node, node );
173 }
174
175 /** Pop a node.
176 */
177
context_pop_node(deserialise_context context)178 static xmlNodePtr context_pop_node( deserialise_context context )
179 {
180 return mlt_deque_pop_back( context->stack_node );
181 }
182
183
184 // Set the destructor on a new service
track_service(mlt_properties properties,void * service,mlt_destructor destructor)185 static void track_service( mlt_properties properties, void *service, mlt_destructor destructor )
186 {
187 int registered = mlt_properties_get_int( properties, "registered" );
188 char *key = mlt_properties_get( properties, "registered" );
189 mlt_properties_set_data( properties, key, service, 0, destructor, NULL );
190 mlt_properties_set_int( properties, "registered", ++ registered );
191 }
192
is_known_prefix(const char * resource)193 static inline int is_known_prefix(const char* resource)
194 {
195 char *prefix = strchr(resource, ':');
196 if (prefix) {
197 const char *whitelist[] = {
198 "alsa",
199 "avfoundation",
200 "dshow",
201 "fbdev",
202 "gdigrab",
203 "jack",
204 "lavfi",
205 "oss",
206 "pulse",
207 "sndio",
208 "video4linux2",
209 "v4l2",
210 "x11grab",
211 "async",
212 "cache",
213 "concat",
214 "crypto",
215 "data",
216 "ffrtmphttp",
217 "file",
218 "ftp",
219 "fs",
220 "gopher",
221 "hls",
222 "http",
223 "httpproxy",
224 "mmsh",
225 "mmst",
226 "pipe",
227 "rtmp",
228 "rtmpt",
229 "rtp",
230 "srtp",
231 "subfile",
232 "tcp",
233 "udp",
234 "udplite",
235 "unix",
236 "color",
237 "colour"
238 };
239 size_t i, n = prefix - resource;
240 for (i = 0; i < sizeof(whitelist) / sizeof(whitelist[0]); ++i) {
241 if (!strncmp(whitelist[i], resource, n))
242 return 1;
243 }
244 }
245 return 0;
246 }
247
248 // Prepend the property value with the document root
qualify_property(deserialise_context context,mlt_properties properties,const char * name)249 static inline void qualify_property( deserialise_context context, mlt_properties properties, const char *name )
250 {
251 const char *resource_orig = mlt_properties_get( properties, name );
252 char *resource = mlt_properties_get( properties, name );
253 if ( resource != NULL && resource[0] )
254 {
255 char *root = mlt_properties_get( context->producer_map, "root" );
256 int n = strlen( root ) + strlen( resource ) + 2;
257 size_t prefix_size = mlt_xml_prefix_size( properties, name, resource );
258
259 // Strip off prefix.
260 if ( prefix_size )
261 resource += prefix_size;
262
263 // Qualify file name properties
264 if ( root != NULL && strcmp( root, "" ) )
265 {
266 char *full_resource = calloc( 1, n );
267 int drive_letter = strlen(resource) > 3 && resource[1] == ':' &&
268 (resource[2] == '/' || resource[2] == '\\');
269 if (resource[0] != '/' && resource[0] != '\\' && !drive_letter && !is_known_prefix(resource))
270 {
271 if ( prefix_size )
272 strncat( full_resource, resource_orig, prefix_size );
273 strcat( full_resource, root );
274 strcat( full_resource, "/" );
275 strcat( full_resource, resource );
276 }
277 else
278 {
279 strcpy( full_resource, resource_orig );
280 }
281 mlt_properties_set_string( properties, name, full_resource );
282 free( full_resource );
283 }
284 }
285 }
286
287
288 /** This function adds a producer to a playlist or multitrack when
289 there is no entry or track element.
290 */
291
add_producer(deserialise_context context,mlt_service service,mlt_position in,mlt_position out)292 static int add_producer( deserialise_context context, mlt_service service, mlt_position in, mlt_position out )
293 {
294 // Return value (0 = service remains top of stack, 1 means it can be removed)
295 int result = 0;
296
297 // Get the parent producer
298 enum service_type type = mlt_invalid_type;
299 mlt_service container = context_pop_service( context, &type );
300 int contained = 0;
301
302 if ( service != NULL && container != NULL )
303 {
304 char *container_branch = mlt_properties_get( MLT_SERVICE_PROPERTIES( container ), "_xml_branch" );
305 char *service_branch = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "_xml_branch" );
306 contained = !strncmp( container_branch, service_branch, strlen( container_branch ) );
307 }
308
309 if ( contained )
310 {
311 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
312 char *hide_s = mlt_properties_get( properties, "hide" );
313
314 // Indicate that this service is no longer top of stack
315 result = 1;
316
317 switch( type )
318 {
319 case mlt_tractor_type:
320 {
321 mlt_multitrack multitrack = mlt_tractor_multitrack( MLT_TRACTOR( container ) );
322 mlt_multitrack_connect( multitrack, MLT_PRODUCER( service ), mlt_multitrack_count( multitrack ) );
323 }
324 break;
325 case mlt_multitrack_type:
326 {
327 mlt_multitrack_connect( MLT_MULTITRACK( container ),
328 MLT_PRODUCER( service ),
329 mlt_multitrack_count( MLT_MULTITRACK( container ) ) );
330 }
331 break;
332 case mlt_playlist_type:
333 {
334 mlt_playlist_append_io( MLT_PLAYLIST( container ), MLT_PRODUCER( service ), in, out );
335 }
336 break;
337 default:
338 result = 0;
339 mlt_log_warning( NULL, "[producer_xml] Producer defined inside something that isn't a container\n" );
340 break;
341 };
342
343 // Set the hide state of the track producer
344 if ( hide_s != NULL )
345 {
346 if ( strcmp( hide_s, "video" ) == 0 )
347 mlt_properties_set_int( properties, "hide", 1 );
348 else if ( strcmp( hide_s, "audio" ) == 0 )
349 mlt_properties_set_int( properties, "hide", 2 );
350 else if ( strcmp( hide_s, "both" ) == 0 )
351 mlt_properties_set_int( properties, "hide", 3 );
352 }
353 }
354
355 // Put the parent producer back
356 if ( container != NULL )
357 context_push_service( context, container, type );
358
359 return result;
360 }
361
362 /** Attach filters defined on that to this.
363 */
364
attach_filters(mlt_service service,mlt_service that)365 static void attach_filters( mlt_service service, mlt_service that )
366 {
367 if ( that != NULL )
368 {
369 int i = 0;
370 mlt_filter filter = NULL;
371 for ( i = 0; ( filter = mlt_service_filter( that, i ) ) != NULL; i ++ )
372 {
373 mlt_service_attach( service, filter );
374 attach_filters( MLT_FILTER_SERVICE( filter ), MLT_FILTER_SERVICE( filter ) );
375 }
376 }
377 }
378
on_start_profile(deserialise_context context,const xmlChar * name,const xmlChar ** atts)379 static void on_start_profile( deserialise_context context, const xmlChar *name, const xmlChar **atts)
380 {
381 mlt_profile p = context->profile;
382 for ( ; atts != NULL && *atts != NULL; atts += 2 )
383 {
384 if ( xmlStrcmp( atts[ 0 ], _x("name") ) == 0 || xmlStrcmp( atts[ 0 ], _x("profile") ) == 0 )
385 {
386 mlt_profile my_profile = mlt_profile_init( _s(atts[ 1 ]) );
387 if ( my_profile )
388 {
389 p->description = strdup( my_profile->description );
390 p->display_aspect_den = my_profile->display_aspect_den;
391 p->display_aspect_num = my_profile->display_aspect_num;
392 p->frame_rate_den = my_profile->frame_rate_den;
393 p->frame_rate_num = my_profile->frame_rate_num;
394 p->width = my_profile->width;
395 p->height = my_profile->height;
396 p->progressive = my_profile->progressive;
397 p->sample_aspect_den = my_profile->sample_aspect_den;
398 p->sample_aspect_num = my_profile->sample_aspect_num;
399 p->colorspace = my_profile->colorspace;
400 p->is_explicit = 1;
401 mlt_profile_close( my_profile );
402 }
403 }
404 else if ( xmlStrcmp( atts[ 0 ], _x("description") ) == 0 )
405 {
406 free( p->description );
407 p->description = strdup( _s(atts[ 1 ]) );
408 p->is_explicit = 1;
409 }
410 else if ( xmlStrcmp( atts[ 0 ], _x("display_aspect_den") ) == 0 )
411 p->display_aspect_den = strtol( _s(atts[ 1 ]), NULL, 0 );
412 else if ( xmlStrcmp( atts[ 0 ], _x("display_aspect_num") ) == 0 )
413 p->display_aspect_num = strtol( _s(atts[ 1 ]), NULL, 0 );
414 else if ( xmlStrcmp( atts[ 0 ], _x("sample_aspect_num") ) == 0 )
415 p->sample_aspect_num = strtol( _s(atts[ 1 ]), NULL, 0 );
416 else if ( xmlStrcmp( atts[ 0 ], _x("sample_aspect_den") ) == 0 )
417 p->sample_aspect_den = strtol( _s(atts[ 1 ]), NULL, 0 );
418 else if ( xmlStrcmp( atts[ 0 ], _x("width") ) == 0 )
419 p->width = strtol( _s(atts[ 1 ]), NULL, 0 );
420 else if ( xmlStrcmp( atts[ 0 ], _x("height") ) == 0 )
421 p->height = strtol( _s(atts[ 1 ]), NULL, 0 );
422 else if ( xmlStrcmp( atts[ 0 ], _x("progressive") ) == 0 )
423 p->progressive = strtol( _s(atts[ 1 ]), NULL, 0 );
424 else if ( xmlStrcmp( atts[ 0 ], _x("frame_rate_num") ) == 0 )
425 p->frame_rate_num = strtol( _s(atts[ 1 ]), NULL, 0 );
426 else if ( xmlStrcmp( atts[ 0 ], _x("frame_rate_den") ) == 0 )
427 p->frame_rate_den = strtol( _s(atts[ 1 ]), NULL, 0 );
428 else if ( xmlStrcmp( atts[ 0 ], _x("colorspace") ) == 0 )
429 p->colorspace = strtol( _s(atts[ 1 ]), NULL, 0 );
430 }
431 }
432
on_start_tractor(deserialise_context context,const xmlChar * name,const xmlChar ** atts)433 static void on_start_tractor( deserialise_context context, const xmlChar *name, const xmlChar **atts)
434 {
435 mlt_tractor tractor = mlt_tractor_new( );
436 mlt_service service = MLT_TRACTOR_SERVICE( tractor );
437 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
438
439 track_service( context->destructors, service, (mlt_destructor) mlt_tractor_close );
440 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( service ), context->lc_numeric );
441
442 for ( ; atts != NULL && *atts != NULL; atts += 2 )
443 mlt_properties_set_string( MLT_SERVICE_PROPERTIES( service ), (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
444
445 mlt_properties_set_int( MLT_TRACTOR_PROPERTIES( tractor ), "global_feed", 1 );
446
447 if ( mlt_properties_get( properties, "id" ) != NULL )
448 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
449
450 context_push_service( context, service, mlt_tractor_type );
451 }
452
on_end_tractor(deserialise_context context,const xmlChar * name)453 static void on_end_tractor( deserialise_context context, const xmlChar *name )
454 {
455 // Get the tractor
456 enum service_type type;
457 mlt_service tractor = context_pop_service( context, &type );
458
459 if ( tractor != NULL && type == mlt_tractor_type )
460 {
461 // See if the tractor should be added to a playlist or multitrack
462 if ( add_producer( context, tractor, 0, mlt_producer_get_out( MLT_PRODUCER( tractor ) ) ) == 0 )
463 context_push_service( context, tractor, type );
464 }
465 else
466 {
467 mlt_log_error( NULL, "[producer_xml] Invalid state for tractor\n" );
468 }
469 }
470
on_start_multitrack(deserialise_context context,const xmlChar * name,const xmlChar ** atts)471 static void on_start_multitrack( deserialise_context context, const xmlChar *name, const xmlChar **atts)
472 {
473 enum service_type type;
474 mlt_service parent = context_pop_service( context, &type );
475
476 // If we don't have a parent, then create one now, providing we're in a state where we can
477 if ( parent == NULL || ( type == mlt_playlist_type || type == mlt_multitrack_type ) )
478 {
479 mlt_tractor tractor = NULL;
480 // Push the parent back
481 if ( parent != NULL )
482 context_push_service( context, parent, type );
483
484 // Create a tractor to contain the multitrack
485 tractor = mlt_tractor_new( );
486 parent = MLT_TRACTOR_SERVICE( tractor );
487 track_service( context->destructors, parent, (mlt_destructor) mlt_tractor_close );
488 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( parent ), context->lc_numeric );
489 type = mlt_tractor_type;
490
491 // Flag it as a synthesised tractor for clean up later
492 mlt_properties_set_int( MLT_SERVICE_PROPERTIES( parent ), "loader_synth", 1 );
493 }
494
495 if ( type == mlt_tractor_type )
496 {
497 mlt_service service = MLT_SERVICE( mlt_tractor_multitrack( MLT_TRACTOR( parent ) ) );
498 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
499 for ( ; atts != NULL && *atts != NULL; atts += 2 )
500 mlt_properties_set_string( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
501
502 if ( mlt_properties_get( properties, "id" ) != NULL )
503 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties,"id" ), service, 0, NULL, NULL );
504
505 context_push_service( context, parent, type );
506 context_push_service( context, service, mlt_multitrack_type );
507 }
508 else
509 {
510 mlt_log_error( NULL, "[producer_xml] Invalid multitrack position\n" );
511 }
512 }
513
on_end_multitrack(deserialise_context context,const xmlChar * name)514 static void on_end_multitrack( deserialise_context context, const xmlChar *name )
515 {
516 // Get the multitrack from the stack
517 enum service_type type;
518 mlt_service service = context_pop_service( context, &type );
519
520 if ( service == NULL || type != mlt_multitrack_type )
521 mlt_log_error( NULL, "[producer_xml] End multitrack in the wrong state...\n" );
522 }
523
on_start_playlist(deserialise_context context,const xmlChar * name,const xmlChar ** atts)524 static void on_start_playlist( deserialise_context context, const xmlChar *name, const xmlChar **atts)
525 {
526 mlt_playlist playlist = mlt_playlist_new( context->profile );
527 mlt_service service = MLT_PLAYLIST_SERVICE( playlist );
528 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
529
530 track_service( context->destructors, service, (mlt_destructor) mlt_playlist_close );
531
532 for ( ; atts != NULL && *atts != NULL; atts += 2 )
533 {
534 mlt_properties_set_string( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
535
536 // Out will be overwritten later as we append, so we need to save it
537 if ( xmlStrcmp( atts[ 0 ], _x("out") ) == 0 )
538 mlt_properties_set_string( properties, "_xml.out", ( const char* )atts[ 1 ] );
539 }
540
541 if ( mlt_properties_get( properties, "id" ) != NULL )
542 mlt_properties_set_data( context->producer_map, mlt_properties_get( properties, "id" ), service, 0, NULL, NULL );
543
544 context_push_service( context, service, mlt_playlist_type );
545 }
546
on_end_playlist(deserialise_context context,const xmlChar * name)547 static void on_end_playlist( deserialise_context context, const xmlChar *name )
548 {
549 // Get the playlist from the stack
550 enum service_type type;
551 mlt_service service = context_pop_service( context, &type );
552
553 if ( service != NULL && type == mlt_playlist_type )
554 {
555 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
556 mlt_position in = -1;
557 mlt_position out = -1;
558
559 if ( mlt_properties_get( properties, "in" ) )
560 in = mlt_properties_get_position( properties, "in" );
561 if ( mlt_properties_get( properties, "out" ) )
562 out = mlt_properties_get_position( properties, "out" );
563
564 // See if the playlist should be added to a playlist or multitrack
565 if ( add_producer( context, service, in, out ) == 0 )
566 context_push_service( context, service, type );
567 }
568 else
569 {
570 mlt_log_error( NULL, "[producer_xml] Invalid state of playlist end %d\n", type );
571 }
572 }
573
on_start_producer(deserialise_context context,const xmlChar * name,const xmlChar ** atts)574 static void on_start_producer( deserialise_context context, const xmlChar *name, const xmlChar **atts)
575 {
576 // use a dummy service to hold properties to allow arbitrary nesting
577 mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
578 mlt_service_init( service, NULL );
579
580 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
581
582 context_push_service( context, service, mlt_dummy_producer_type );
583
584 for ( ; atts != NULL && *atts != NULL; atts += 2 )
585 mlt_properties_set_string( properties, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
586 }
587
on_end_producer(deserialise_context context,const xmlChar * name)588 static void on_end_producer( deserialise_context context, const xmlChar *name )
589 {
590 enum service_type type;
591 mlt_service service = context_pop_service( context, &type );
592 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
593
594 if ( service != NULL && type == mlt_dummy_producer_type )
595 {
596 mlt_service producer = NULL;
597
598 qualify_property( context, properties, "resource" );
599 char *resource = mlt_properties_get( properties, "resource" );
600
601 // Let Kino-SMIL src be a synonym for resource
602 if ( resource == NULL )
603 {
604 qualify_property( context, properties, "src" );
605 resource = mlt_properties_get( properties, "src" );
606 }
607
608 // Instantiate the producer
609 if ( mlt_properties_get( properties, "mlt_service" ) != NULL )
610 {
611 char *service_name = trim( mlt_properties_get( properties, "mlt_service" ) );
612 if ( resource )
613 {
614 // If a document was saved as +INVALID.txt (see below), then ignore the mlt_service and
615 // try to load it just from the resource. This is an attempt to recover the failed
616 // producer in case, for example, a file returns.
617 if (!strcmp("qtext", service_name)) {
618 const char *text = mlt_properties_get( properties, "text" );
619 if (text && !strcmp("INVALID", text)) {
620 service_name = NULL;
621 }
622 } else if (!strcmp("pango", service_name)) {
623 const char *markup = mlt_properties_get( properties, "markup" );
624 if (markup && !strcmp("INVALID", markup)) {
625 service_name = NULL;
626 }
627 }
628 if (service_name) {
629 char *temp = calloc( 1, strlen( service_name ) + strlen( resource ) + 2 );
630 strcat( temp, service_name );
631 strcat( temp, ":" );
632 strcat( temp, resource );
633 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, temp ) );
634 free( temp );
635 }
636 }
637 else
638 {
639 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, service_name ) );
640 }
641 }
642
643 // Just in case the plugin requested doesn't exist...
644 if ( !producer && resource )
645 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, resource ) );
646 if ( !producer ) {
647 mlt_log_error( NULL, "[producer_xml] failed to load producer \"%s\"\n", resource );
648 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, "+INVALID.txt" ) );
649 if (producer) {
650 // Save the original mlt_service for the consumer to serialize it as original.
651 mlt_properties_set_string( MLT_SERVICE_PROPERTIES(producer), "_xml_mlt_service",
652 mlt_properties_get( properties, "mlt_service" ) );
653 }
654 }
655 if ( !producer )
656 producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, "colour:red" ) );
657 if ( !producer )
658 {
659 mlt_service_close( service );
660 free( service );
661 return;
662 }
663
664 // Track this producer
665 track_service( context->destructors, producer, (mlt_destructor) mlt_producer_close );
666 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( producer ), context->lc_numeric );
667 if ( mlt_properties_get( MLT_SERVICE_PROPERTIES( producer ), "seekable" ) )
668 context->seekable &= mlt_properties_get_int( MLT_SERVICE_PROPERTIES( producer ), "seekable" );
669
670 // Propagate the properties
671 qualify_property( context, properties, "resource" );
672 qualify_property( context, properties, "luma" );
673 qualify_property( context, properties, "luma.resource" );
674 qualify_property( context, properties, "composite.luma" );
675 qualify_property( context, properties, "producer.resource" );
676 qualify_property( context, properties, "argument" ); // timewarp producer
677
678 // Handle in/out properties separately
679 mlt_position in = -1;
680 mlt_position out = -1;
681
682 // Get in
683 if ( mlt_properties_get( properties, "in" ) )
684 in = mlt_properties_get_position( properties, "in" );
685 // Let Kino-SMIL clipBegin be a synonym for in
686 else if ( mlt_properties_get( properties, "clipBegin" ) )
687 in = mlt_properties_get_position( properties, "clipBegin" );
688 // Get out
689 if ( mlt_properties_get( properties, "out" ) )
690 out = mlt_properties_get_position( properties, "out" );
691 // Let Kino-SMIL clipEnd be a synonym for out
692 else if ( mlt_properties_get( properties, "clipEnd" ) )
693 out = mlt_properties_get_position( properties, "clipEnd" );
694 // Remove in and out
695 mlt_properties_set_string( properties, "in", NULL );
696 mlt_properties_set_string( properties, "out", NULL );
697
698 // Do not let XML overwrite these important properties set by mlt_factory.
699 mlt_properties_set_string( properties, "mlt_type", NULL );
700 mlt_properties_set_string( properties, "mlt_service", NULL );
701
702 // Inherit the properties
703 mlt_properties_inherit( MLT_SERVICE_PROPERTIES( producer ), properties );
704
705 // Attach all filters from service onto producer
706 attach_filters( producer, service );
707
708 // Add the producer to the producer map
709 if ( mlt_properties_get( properties, "id" ) != NULL )
710 mlt_properties_set_data( context->producer_map, mlt_properties_get(properties, "id"), producer, 0, NULL, NULL );
711
712 // See if the producer should be added to a playlist or multitrack
713 if ( add_producer( context, producer, in, out ) == 0 )
714 {
715 // Otherwise, set in and out on...
716 if ( in != -1 || out != -1 )
717 {
718 // Get the parent service
719 enum service_type type;
720 mlt_service parent = context_pop_service( context, &type );
721 if ( parent != NULL )
722 {
723 // Get the parent properties
724 properties = MLT_SERVICE_PROPERTIES( parent );
725
726 char *resource = mlt_properties_get( properties, "resource" );
727
728 // Put the parent producer back
729 context_push_service( context, parent, type );
730
731 // If the parent is a track or entry
732 if ( resource && ( strcmp( resource, "<entry>" ) == 0 ) )
733 {
734 if ( in > -1 ) mlt_properties_set_position( properties, "in", in );
735 if ( out > -1 ) mlt_properties_set_position( properties, "out", out );
736 }
737 else
738 {
739 // Otherwise, set in and out on producer directly
740 mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
741 }
742 }
743 else
744 {
745 // Otherwise, set in and out on producer directly
746 mlt_producer_set_in_and_out( MLT_PRODUCER( producer ), in, out );
747 }
748 }
749
750 // Push the producer onto the stack
751 context_push_service( context, producer, mlt_producer_type );
752 }
753 }
754
755 if ( service )
756 {
757 mlt_service_close( service );
758 free( service );
759 }
760 }
761
on_start_blank(deserialise_context context,const xmlChar * name,const xmlChar ** atts)762 static void on_start_blank( deserialise_context context, const xmlChar *name, const xmlChar **atts)
763 {
764 // Get the playlist from the stack
765 enum service_type type;
766 mlt_service service = context_pop_service( context, &type );
767
768 if ( type == mlt_playlist_type && service != NULL )
769 {
770 // Look for the length attribute
771 for ( ; atts != NULL && *atts != NULL; atts += 2 )
772 {
773 if ( xmlStrcmp( atts[0], _x("length") ) == 0 )
774 {
775 // Append a blank to the playlist
776 mlt_playlist_blank_time( MLT_PLAYLIST( service ), _s(atts[1]) );
777 break;
778 }
779 }
780
781 // Push the playlist back onto the stack
782 context_push_service( context, service, type );
783 }
784 else
785 {
786 mlt_log_error( NULL, "[producer_xml] blank without a playlist - a definite no no\n" );
787 }
788 }
789
on_start_entry(deserialise_context context,const xmlChar * name,const xmlChar ** atts)790 static void on_start_entry( deserialise_context context, const xmlChar *name, const xmlChar **atts)
791 {
792 mlt_producer entry = NULL;
793 mlt_properties temp = mlt_properties_new( );
794 mlt_properties_set_data( temp, "_profile", context->profile, 0, NULL, NULL );
795 mlt_properties_set_lcnumeric( temp, context->lc_numeric );
796
797 for ( ; atts != NULL && *atts != NULL; atts += 2 )
798 {
799 mlt_properties_set_string( temp, (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
800
801 // Look for the producer attribute
802 if ( xmlStrcmp( atts[ 0 ], _x("producer") ) == 0 )
803 {
804 mlt_producer producer = mlt_properties_get_data( context->producer_map, (const char*) atts[1], NULL );
805 if ( producer != NULL )
806 mlt_properties_set_data( temp, "producer", producer, 0, NULL, NULL );
807 }
808 }
809
810 // If we have a valid entry
811 if ( mlt_properties_get_data( temp, "producer", NULL ) != NULL )
812 {
813 mlt_playlist_clip_info info;
814 enum service_type parent_type = mlt_invalid_type;
815 mlt_service parent = context_pop_service( context, &parent_type );
816 mlt_producer producer = mlt_properties_get_data( temp, "producer", NULL );
817
818 if ( parent_type == mlt_playlist_type )
819 {
820 // Append the producer to the playlist
821 mlt_position in = -1;
822 mlt_position out = -1;
823 if ( mlt_properties_get( temp, "in" ) )
824 in = mlt_properties_get_position( temp, "in" );
825 if ( mlt_properties_get( temp, "out" ) )
826 out = mlt_properties_get_position( temp, "out" );
827 mlt_playlist_append_io( MLT_PLAYLIST( parent ), producer, in, out );
828
829 // Handle the repeat property
830 if ( mlt_properties_get_int( temp, "repeat" ) > 0 )
831 {
832 mlt_playlist_repeat_clip( MLT_PLAYLIST( parent ),
833 mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1,
834 mlt_properties_get_int( temp, "repeat" ) );
835 }
836
837 mlt_playlist_get_clip_info( MLT_PLAYLIST( parent ), &info, mlt_playlist_count( MLT_PLAYLIST( parent ) ) - 1 );
838 entry = info.cut;
839 }
840 else
841 {
842 mlt_log_error( NULL, "[producer_xml] Entry not part of a playlist...\n" );
843 }
844
845 context_push_service( context, parent, parent_type );
846 }
847
848 // Push the cut onto the stack
849 context_push_service( context, MLT_PRODUCER_SERVICE( entry ), mlt_entry_type );
850
851 mlt_properties_close( temp );
852 }
853
on_end_entry(deserialise_context context,const xmlChar * name)854 static void on_end_entry( deserialise_context context, const xmlChar *name )
855 {
856 // Get the entry from the stack
857 enum service_type entry_type = mlt_invalid_type;
858 mlt_service entry = context_pop_service( context, &entry_type );
859
860 if ( entry == NULL && entry_type != mlt_entry_type )
861 {
862 mlt_log_error( NULL, "[producer_xml] Invalid state at end of entry\n" );
863 }
864 }
865
on_start_track(deserialise_context context,const xmlChar * name,const xmlChar ** atts)866 static void on_start_track( deserialise_context context, const xmlChar *name, const xmlChar **atts)
867 {
868 // use a dummy service to hold properties to allow arbitrary nesting
869 mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
870 mlt_service_init( service, NULL );
871
872 // Push the dummy service onto the stack
873 context_push_service( context, service, mlt_entry_type );
874
875 mlt_properties_set_string( MLT_SERVICE_PROPERTIES( service ), "resource", "<track>" );
876
877 for ( ; atts != NULL && *atts != NULL; atts += 2 )
878 {
879 mlt_properties_set_string( MLT_SERVICE_PROPERTIES( service ), (const char*) atts[0], atts[1] == NULL ? "" : (const char*) atts[1] );
880
881 // Look for the producer attribute
882 if ( xmlStrcmp( atts[ 0 ], _x("producer") ) == 0 )
883 {
884 mlt_producer producer = mlt_properties_get_data( context->producer_map, (const char*) atts[1], NULL );
885 if ( producer != NULL )
886 mlt_properties_set_data( MLT_SERVICE_PROPERTIES( service ), "producer", producer, 0, NULL, NULL );
887 }
888 }
889 }
890
on_end_track(deserialise_context context,const xmlChar * name)891 static void on_end_track( deserialise_context context, const xmlChar *name )
892 {
893 // Get the track from the stack
894 enum service_type track_type;
895 mlt_service track = context_pop_service( context, &track_type );
896
897 if ( track != NULL && track_type == mlt_entry_type )
898 {
899 mlt_properties track_props = MLT_SERVICE_PROPERTIES( track );
900 enum service_type parent_type = mlt_invalid_type;
901 mlt_service parent = context_pop_service( context, &parent_type );
902 mlt_multitrack multitrack = NULL;
903
904 mlt_producer producer = mlt_properties_get_data( track_props, "producer", NULL );
905 mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer );
906
907 if ( parent_type == mlt_tractor_type )
908 multitrack = mlt_tractor_multitrack( MLT_TRACTOR( parent ) );
909 else if ( parent_type == mlt_multitrack_type )
910 multitrack = MLT_MULTITRACK( parent );
911 else
912 mlt_log_error( NULL, "[producer_xml] track contained in an invalid container\n" );
913
914 if ( multitrack != NULL )
915 {
916 // Set producer i/o if specified
917 if ( mlt_properties_get( track_props, "in" ) != NULL ||
918 mlt_properties_get( track_props, "out" ) != NULL )
919 {
920 mlt_position in = -1;
921 mlt_position out = -1;
922 if ( mlt_properties_get( track_props, "in" ) )
923 in = mlt_properties_get_position( track_props, "in" );
924 if ( mlt_properties_get( track_props, "out" ) )
925 out = mlt_properties_get_position( track_props, "out" );
926 mlt_producer cut = mlt_producer_cut( MLT_PRODUCER( producer ), in, out );
927 mlt_multitrack_connect( multitrack, cut, mlt_multitrack_count( multitrack ) );
928 mlt_properties_inherit( MLT_PRODUCER_PROPERTIES( cut ), track_props );
929 track_props = MLT_PRODUCER_PROPERTIES( cut );
930 mlt_producer_close( cut );
931 }
932 else
933 {
934 mlt_multitrack_connect( multitrack, producer, mlt_multitrack_count( multitrack ) );
935 }
936
937 // Set the hide state of the track producer
938 char *hide_s = mlt_properties_get( track_props, "hide" );
939 if ( hide_s != NULL )
940 {
941 if ( strcmp( hide_s, "video" ) == 0 )
942 mlt_properties_set_int( producer_props, "hide", 1 );
943 else if ( strcmp( hide_s, "audio" ) == 0 )
944 mlt_properties_set_int( producer_props, "hide", 2 );
945 else if ( strcmp( hide_s, "both" ) == 0 )
946 mlt_properties_set_int( producer_props, "hide", 3 );
947 }
948 }
949
950 if ( parent != NULL )
951 context_push_service( context, parent, parent_type );
952 }
953 else
954 {
955 mlt_log_error( NULL, "[producer_xml] Invalid state at end of track\n" );
956 }
957
958 if ( track )
959 {
960 mlt_service_close( track );
961 free( track );
962 }
963 }
964
on_start_filter(deserialise_context context,const xmlChar * name,const xmlChar ** atts)965 static void on_start_filter( deserialise_context context, const xmlChar *name, const xmlChar **atts)
966 {
967 // use a dummy service to hold properties to allow arbitrary nesting
968 mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
969 mlt_service_init( service, NULL );
970
971 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
972
973 context_push_service( context, service, mlt_dummy_filter_type );
974
975 // Set the properties
976 for ( ; atts != NULL && *atts != NULL; atts += 2 )
977 mlt_properties_set_string( properties, (const char*) atts[0], (const char*) atts[1] );
978 }
979
on_end_filter(deserialise_context context,const xmlChar * name)980 static void on_end_filter( deserialise_context context, const xmlChar *name )
981 {
982 enum service_type type;
983 mlt_service service = context_pop_service( context, &type );
984 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
985
986 enum service_type parent_type = mlt_invalid_type;
987 mlt_service parent = context_pop_service( context, &parent_type );
988
989 if ( service != NULL && type == mlt_dummy_filter_type )
990 {
991 char *id = trim( mlt_properties_get( properties, "mlt_service" ) );
992 mlt_service filter = MLT_SERVICE( mlt_factory_filter( context->profile, id, NULL ) );
993 mlt_properties filter_props = MLT_SERVICE_PROPERTIES( filter );
994
995 if ( !filter )
996 {
997 mlt_log_error( NULL, "[producer_xml] failed to load filter \"%s\"\n", id );
998 if ( parent )
999 context_push_service( context, parent, parent_type );
1000 mlt_service_close( service );
1001 free( service );
1002 return;
1003 }
1004
1005 track_service( context->destructors, filter, (mlt_destructor) mlt_filter_close );
1006 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( filter ), context->lc_numeric );
1007
1008 // Do not let XML overwrite these important properties set by mlt_factory.
1009 mlt_properties_set_string( properties, "mlt_type", NULL );
1010 mlt_properties_set_string( properties, "mlt_service", NULL );
1011
1012 // Propagate the properties
1013 qualify_property( context, properties, "resource" );
1014 qualify_property( context, properties, "luma" );
1015 qualify_property( context, properties, "luma.resource" );
1016 qualify_property( context, properties, "composite.luma" );
1017 qualify_property( context, properties, "producer.resource" );
1018 qualify_property( context, properties, "filename" );
1019 qualify_property( context, properties, "av.file" );
1020 qualify_property( context, properties, "av.filename" );
1021 mlt_properties_inherit( filter_props, properties );
1022
1023 // Attach all filters from service onto filter
1024 attach_filters( filter, service );
1025
1026 // Associate the filter with the parent
1027 if ( parent != NULL )
1028 {
1029 if ( parent_type == mlt_tractor_type && mlt_properties_get( properties, "track" ) )
1030 {
1031 mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
1032 mlt_field_plant_filter( field, MLT_FILTER( filter ), mlt_properties_get_int( properties, "track" ) );
1033 mlt_filter_set_in_and_out( MLT_FILTER( filter ),
1034 mlt_properties_get_int( properties, "in" ),
1035 mlt_properties_get_int( properties, "out" ) );
1036 }
1037 else
1038 {
1039 mlt_service_attach( parent, MLT_FILTER( filter ) );
1040 }
1041
1042 // Put the parent back on the stack
1043 context_push_service( context, parent, parent_type );
1044 }
1045 else
1046 {
1047 mlt_log_error( NULL, "[producer_xml] filter closed with invalid parent...\n" );
1048 }
1049 }
1050 else
1051 {
1052 mlt_log_error( NULL, "[producer_xml] Invalid top of stack on filter close\n" );
1053 }
1054
1055 if ( service )
1056 {
1057 mlt_service_close( service );
1058 free(service);
1059 }
1060 }
1061
on_start_transition(deserialise_context context,const xmlChar * name,const xmlChar ** atts)1062 static void on_start_transition( deserialise_context context, const xmlChar *name, const xmlChar **atts)
1063 {
1064 // use a dummy service to hold properties to allow arbitrary nesting
1065 mlt_service service = calloc( 1, sizeof( struct mlt_service_s ) );
1066 mlt_service_init( service, NULL );
1067
1068 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1069
1070 context_push_service( context, service, mlt_dummy_transition_type );
1071
1072 // Set the properties
1073 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1074 mlt_properties_set_string( properties, (const char*) atts[0], (const char*) atts[1] );
1075 }
1076
on_end_transition(deserialise_context context,const xmlChar * name)1077 static void on_end_transition( deserialise_context context, const xmlChar *name )
1078 {
1079 enum service_type type;
1080 mlt_service service = context_pop_service( context, &type );
1081 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1082
1083 enum service_type parent_type = mlt_invalid_type;
1084 mlt_service parent = context_pop_service( context, &parent_type );
1085
1086 if ( service != NULL && type == mlt_dummy_transition_type )
1087 {
1088 char *id = trim( mlt_properties_get( properties, "mlt_service" ) );
1089 mlt_service effect = MLT_SERVICE( mlt_factory_transition( context->profile, id, NULL ) );
1090 mlt_properties effect_props = MLT_SERVICE_PROPERTIES( effect );
1091
1092 if ( !effect )
1093 {
1094 mlt_log_error( NULL, "[producer_xml] failed to load transition \"%s\"\n", id );
1095 if ( parent )
1096 context_push_service( context, parent, parent_type );
1097 mlt_service_close( service );
1098 free( service );
1099 return;
1100 }
1101 track_service( context->destructors, effect, (mlt_destructor) mlt_transition_close );
1102 mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( effect ), context->lc_numeric );
1103
1104 // Do not let XML overwrite these important properties set by mlt_factory.
1105 mlt_properties_set_string( properties, "mlt_type", NULL );
1106 mlt_properties_set_string( properties, "mlt_service", NULL );
1107
1108 // Propagate the properties
1109 qualify_property( context, properties, "resource" );
1110 qualify_property( context, properties, "luma" );
1111 qualify_property( context, properties, "luma.resource" );
1112 qualify_property( context, properties, "composite.luma" );
1113 qualify_property( context, properties, "producer.resource" );
1114 mlt_properties_inherit( effect_props, properties );
1115
1116 // Attach all filters from service onto effect
1117 attach_filters( effect, service );
1118
1119 // Associate the filter with the parent
1120 if ( parent != NULL )
1121 {
1122 if ( parent_type == mlt_tractor_type )
1123 {
1124 mlt_field field = mlt_tractor_field( MLT_TRACTOR( parent ) );
1125 mlt_field_plant_transition( field, MLT_TRANSITION( effect ),
1126 mlt_properties_get_int( properties, "a_track" ),
1127 mlt_properties_get_int( properties, "b_track" ) );
1128 mlt_transition_set_in_and_out( MLT_TRANSITION( effect ),
1129 mlt_properties_get_int( properties, "in" ),
1130 mlt_properties_get_int( properties, "out" ) );
1131 }
1132 else
1133 {
1134 mlt_log_warning( NULL, "[producer_xml] Misplaced transition - ignoring\n" );
1135 }
1136
1137 // Put the parent back on the stack
1138 context_push_service( context, parent, parent_type );
1139 }
1140 else
1141 {
1142 mlt_log_error( NULL, "[producer_xml] transition closed with invalid parent...\n" );
1143 }
1144
1145 }
1146 else
1147 {
1148 mlt_log_error( NULL, "[producer_xml] Invalid top of stack on transition close\n" );
1149 }
1150
1151 if ( service )
1152 {
1153 mlt_service_close( service );
1154 free( service );
1155 }
1156 }
1157
on_start_consumer(deserialise_context context,const xmlChar * name,const xmlChar ** atts)1158 static void on_start_consumer( deserialise_context context, const xmlChar *name, const xmlChar **atts)
1159 {
1160 if ( context->pass == 1 )
1161 {
1162 mlt_properties properties = mlt_properties_new();
1163
1164 mlt_properties_set_lcnumeric( properties, context->lc_numeric );
1165 context_push_service( context, (mlt_service) properties, mlt_dummy_consumer_type );
1166
1167 // Set the properties from attributes
1168 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1169 mlt_properties_set_string( properties, (const char*) atts[0], (const char*) atts[1] );
1170 }
1171 }
1172
set_preview_scale(mlt_profile * consumer_profile,mlt_profile * profile,double scale)1173 static void set_preview_scale(mlt_profile *consumer_profile, mlt_profile *profile, double scale)
1174 {
1175 *consumer_profile = mlt_profile_clone(*profile);
1176 if (*consumer_profile) {
1177 (*consumer_profile)->width *= scale;
1178 (*consumer_profile)->width -= (*consumer_profile)->width % 2;
1179 (*consumer_profile)->height *= scale;
1180 (*consumer_profile)->height -= (*consumer_profile)->height % 2;
1181 }
1182 }
1183
on_end_consumer(deserialise_context context,const xmlChar * name)1184 static void on_end_consumer( deserialise_context context, const xmlChar *name )
1185 {
1186 if ( context->pass == 1 )
1187 {
1188 // Get the consumer from the stack
1189 enum service_type type;
1190 mlt_properties properties = (mlt_properties) context_pop_service( context, &type );
1191
1192 if ( properties && type == mlt_dummy_consumer_type )
1193 {
1194 qualify_property( context, properties, "resource" );
1195 qualify_property( context, properties, "target" );
1196 char *resource = mlt_properties_get( properties, "resource" );
1197
1198 if ( context->multi_consumer > 1 || context->qglsl || mlt_properties_get_int( context->params, "multi" ) )
1199 {
1200 // Instantiate the multi consumer
1201 if ( !context->consumer )
1202 {
1203 if ( context->qglsl )
1204 context->consumer = context->qglsl;
1205 else
1206 context->consumer = mlt_factory_consumer( context->profile, "multi", NULL );
1207 if ( context->consumer )
1208 {
1209 // Track this consumer
1210 track_service( context->destructors, MLT_CONSUMER_SERVICE(context->consumer), (mlt_destructor) mlt_consumer_close );
1211 mlt_properties_set_lcnumeric( MLT_CONSUMER_PROPERTIES(context->consumer), context->lc_numeric );
1212 }
1213 }
1214 if ( context->consumer )
1215 {
1216 // Set this properties object on multi consumer
1217 mlt_properties consumer_properties = MLT_CONSUMER_PROPERTIES(context->consumer);
1218 char key[20];
1219 snprintf( key, sizeof(key), "%d", context->consumer_count++ );
1220 mlt_properties_inc_ref( properties );
1221 mlt_properties_set_data( consumer_properties, key, properties, 0,
1222 (mlt_destructor) mlt_properties_close, NULL );
1223
1224 // Pass in / out if provided
1225 mlt_properties_pass_list( consumer_properties, properties, "in, out" );
1226
1227 // Pass along quality and performance properties to the multi consumer and its render thread(s).
1228 if ( !context->qglsl )
1229 {
1230 mlt_properties_pass_list( consumer_properties, properties,
1231 "real_time, deinterlace_method, rescale, progressive, top_field_first, channels, channel_layout" );
1232
1233 // We only really know how to optimize real_time for the avformat consumer.
1234 const char *service_name = mlt_properties_get( properties, "mlt_service" );
1235 if ( service_name && !strcmp( "avformat", service_name ) )
1236 mlt_properties_set_int( properties, "real_time", -1 );
1237 }
1238 }
1239 }
1240 else
1241 {
1242 double scale = mlt_properties_get_double(properties, "scale");
1243 if (scale > 0.0) {
1244 set_preview_scale(&context->consumer_profile, &context->profile, scale);
1245 }
1246 // Instantiate the consumer
1247 char *id = trim( mlt_properties_get( properties, "mlt_service" ) );
1248 mlt_profile profile = context->consumer_profile? context->consumer_profile : context->profile;
1249 context->consumer = mlt_factory_consumer( profile, id, resource );
1250 if ( context->consumer )
1251 {
1252 // Track this consumer
1253 track_service( context->destructors, MLT_CONSUMER_SERVICE(context->consumer), (mlt_destructor) mlt_consumer_close );
1254 mlt_properties_set_lcnumeric( MLT_CONSUMER_PROPERTIES(context->consumer), context->lc_numeric );
1255 if (context->consumer_profile) {
1256 mlt_properties_set_data(MLT_CONSUMER_PROPERTIES(context->consumer),
1257 "_profile", context->consumer_profile, sizeof(*context->consumer_profile),
1258 (mlt_destructor) mlt_profile_close, NULL);
1259 }
1260
1261 // Do not let XML overwrite these important properties set by mlt_factory.
1262 mlt_properties_set_string( properties, "mlt_type", NULL );
1263 mlt_properties_set_string( properties, "mlt_service", NULL );
1264
1265 // Inherit the properties
1266 mlt_properties_inherit( MLT_CONSUMER_PROPERTIES(context->consumer), properties );
1267 }
1268 }
1269 }
1270 // Close the dummy
1271 if ( properties )
1272 mlt_properties_close( properties );
1273 }
1274 }
1275
on_start_property(deserialise_context context,const xmlChar * name,const xmlChar ** atts)1276 static void on_start_property( deserialise_context context, const xmlChar *name, const xmlChar **atts)
1277 {
1278 enum service_type type;
1279 mlt_service service = context_pop_service( context, &type );
1280 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1281 const char *value = NULL;
1282
1283 if ( service != NULL )
1284 {
1285 // Set the properties
1286 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1287 {
1288 if ( xmlStrcmp( atts[ 0 ], _x("name") ) == 0 )
1289 context->property = strdup( _s(atts[ 1 ]) );
1290 else if ( xmlStrcmp( atts[ 0 ], _x("value") ) == 0 )
1291 value = _s(atts[ 1 ]);
1292 }
1293
1294 if ( context->property != NULL )
1295 mlt_properties_set_string( properties, context->property, value == NULL ? "" : value );
1296
1297 // Tell parser to collect any further nodes for serialisation
1298 context->is_value = 1;
1299
1300 context_push_service( context, service, type );
1301 }
1302 else
1303 {
1304 mlt_log_error( NULL, "[producer_xml] Property without a service '%s'?\n", ( const char * )name );
1305 }
1306 }
1307
on_end_property(deserialise_context context,const xmlChar * name)1308 static void on_end_property( deserialise_context context, const xmlChar *name )
1309 {
1310 enum service_type type;
1311 mlt_service service = context_pop_service( context, &type );
1312 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1313
1314 if ( service != NULL )
1315 {
1316 // Tell parser to stop building a tree
1317 context->is_value = 0;
1318
1319 // See if there is a xml tree for the value
1320 if ( context->property != NULL && context->value_doc != NULL )
1321 {
1322 xmlChar *value;
1323 int size;
1324
1325 // Serialise the tree to get value
1326 xmlDocDumpMemory( context->value_doc, &value, &size );
1327 mlt_properties_set_string( properties, context->property, _s(value) );
1328 #ifdef _WIN32
1329 xmlFreeFunc xmlFree = NULL;
1330 xmlMemGet( &xmlFree, NULL, NULL, NULL);
1331 #endif
1332 xmlFree( value );
1333 xmlFreeDoc( context->value_doc );
1334 context->value_doc = NULL;
1335 }
1336
1337 // Close this property handling
1338 free( context->property );
1339 context->property = NULL;
1340
1341 context_push_service( context, service, type );
1342 }
1343 else
1344 {
1345 mlt_log_error( NULL, "[producer_xml] Property without a service '%s'??\n", (const char *)name );
1346 }
1347 }
1348
on_start_element(void * ctx,const xmlChar * name,const xmlChar ** atts)1349 static void on_start_element( void *ctx, const xmlChar *name, const xmlChar **atts)
1350 {
1351 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1352 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1353
1354 if ( context->pass == 0 )
1355 {
1356 if ( xmlStrcmp( name, _x("mlt") ) == 0 ||
1357 xmlStrcmp( name, _x("profile") ) == 0 ||
1358 xmlStrcmp( name, _x("profileinfo") ) == 0 )
1359 on_start_profile( context, name, atts );
1360 if ( xmlStrcmp( name, _x("consumer") ) == 0 )
1361 context->multi_consumer++;
1362
1363 // Check for a service beginning with glsl. or movit.
1364 for ( ; atts != NULL && *atts != NULL; atts += 2 ) {
1365 if ( !xmlStrncmp( atts[1], _x("glsl."), 5 ) || !xmlStrncmp( atts[1], _x("movit."), 6 ) ) {
1366 mlt_properties_set_int( context->params, "qglsl", 1 );
1367 break;
1368 }
1369 }
1370 return;
1371 }
1372 mlt_deque_push_back_int( context->stack_branch, mlt_deque_pop_back_int( context->stack_branch ) + 1 );
1373 mlt_deque_push_back_int( context->stack_branch, 0 );
1374
1375 // Build a tree from nodes within a property value
1376 if ( context->is_value == 1 && context->pass == 1 )
1377 {
1378 xmlNodePtr node = xmlNewNode( NULL, name );
1379
1380 if ( context->value_doc == NULL )
1381 {
1382 // Start a new tree
1383 context->value_doc = xmlNewDoc( _x("1.0") );
1384 xmlDocSetRootElement( context->value_doc, node );
1385 }
1386 else
1387 {
1388 // Append child to tree
1389 xmlAddChild( mlt_deque_peek_back( context->stack_node ), node );
1390 }
1391 context_push_node( context, node );
1392
1393 // Set the attributes
1394 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1395 xmlSetProp( node, atts[ 0 ], atts[ 1 ] );
1396 }
1397 else if ( xmlStrcmp( name, _x("tractor") ) == 0 )
1398 on_start_tractor( context, name, atts );
1399 else if ( xmlStrcmp( name, _x("multitrack") ) == 0 )
1400 on_start_multitrack( context, name, atts );
1401 else if ( xmlStrcmp( name, _x("playlist") ) == 0 || xmlStrcmp( name, _x("seq") ) == 0 || xmlStrcmp( name, _x("smil") ) == 0 )
1402 on_start_playlist( context, name, atts );
1403 else if ( xmlStrcmp( name, _x("producer") ) == 0 || xmlStrcmp( name, _x("video") ) == 0 )
1404 on_start_producer( context, name, atts );
1405 else if ( xmlStrcmp( name, _x("blank") ) == 0 )
1406 on_start_blank( context, name, atts );
1407 else if ( xmlStrcmp( name, _x("entry") ) == 0 )
1408 on_start_entry( context, name, atts );
1409 else if ( xmlStrcmp( name, _x("track") ) == 0 )
1410 on_start_track( context, name, atts );
1411 else if ( xmlStrcmp( name, _x("filter") ) == 0 )
1412 on_start_filter( context, name, atts );
1413 else if ( xmlStrcmp( name, _x("transition") ) == 0 )
1414 on_start_transition( context, name, atts );
1415 else if ( xmlStrcmp( name, _x("property") ) == 0 )
1416 on_start_property( context, name, atts );
1417 else if ( xmlStrcmp( name, _x("consumer") ) == 0 )
1418 on_start_consumer( context, name, atts );
1419 else if ( xmlStrcmp( name, _x("westley") ) == 0 || xmlStrcmp( name, _x("mlt") ) == 0 )
1420 {
1421 for ( ; atts != NULL && *atts != NULL; atts += 2 )
1422 {
1423 if ( xmlStrcmp( atts[0], _x("LC_NUMERIC") ) )
1424 mlt_properties_set_string( context->producer_map, _s( atts[0] ), _s(atts[1] ) );
1425 else if ( !context->lc_numeric )
1426 context->lc_numeric = strdup( _s( atts[1] ) );
1427 }
1428 }
1429 }
1430
on_end_element(void * ctx,const xmlChar * name)1431 static void on_end_element( void *ctx, const xmlChar *name )
1432 {
1433 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1434 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1435
1436 if ( context->is_value == 1 && context->pass == 1 && xmlStrcmp( name, _x("property") ) != 0 )
1437 context_pop_node( context );
1438 else if ( xmlStrcmp( name, _x("multitrack") ) == 0 )
1439 on_end_multitrack( context, name );
1440 else if ( xmlStrcmp( name, _x("playlist") ) == 0 || xmlStrcmp( name, _x("seq") ) == 0 || xmlStrcmp( name, _x("smil") ) == 0 )
1441 on_end_playlist( context, name );
1442 else if ( xmlStrcmp( name, _x("track") ) == 0 )
1443 on_end_track( context, name );
1444 else if ( xmlStrcmp( name, _x("entry") ) == 0 )
1445 on_end_entry( context, name );
1446 else if ( xmlStrcmp( name, _x("tractor") ) == 0 )
1447 on_end_tractor( context, name );
1448 else if ( xmlStrcmp( name, _x("property") ) == 0 )
1449 on_end_property( context, name );
1450 else if ( xmlStrcmp( name, _x("producer") ) == 0 || xmlStrcmp( name, _x("video") ) == 0 )
1451 on_end_producer( context, name );
1452 else if ( xmlStrcmp( name, _x("filter") ) == 0 )
1453 on_end_filter( context, name );
1454 else if ( xmlStrcmp( name, _x("transition") ) == 0 )
1455 on_end_transition( context, name );
1456 else if ( xmlStrcmp( name, _x("consumer") ) == 0 )
1457 on_end_consumer( context, name );
1458
1459 mlt_deque_pop_back_int( context->stack_branch );
1460 }
1461
on_characters(void * ctx,const xmlChar * ch,int len)1462 static void on_characters( void *ctx, const xmlChar *ch, int len )
1463 {
1464 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1465 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1466 char *value = calloc( 1, len + 1 );
1467 enum service_type type;
1468 mlt_service service = context_pop_service( context, &type );
1469 mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
1470
1471 if ( service != NULL )
1472 context_push_service( context, service, type );
1473
1474 value[ len ] = 0;
1475 strncpy( value, (const char*) ch, len );
1476
1477 if ( mlt_deque_count( context->stack_node ) )
1478 xmlNodeAddContent( mlt_deque_peek_back( context->stack_node ), ( xmlChar* )value );
1479
1480 // libxml2 generates an on_characters immediately after a get_entity within
1481 // an element value, and we ignore it because it is called again during
1482 // actual substitution.
1483 else if ( context->property != NULL && context->entity_is_replace == 0 )
1484 {
1485 char *s = mlt_properties_get( properties, context->property );
1486 if ( s != NULL )
1487 {
1488 // Append new text to existing content
1489 char *new = calloc( 1, strlen( s ) + len + 1 );
1490 strcat( new, s );
1491 strcat( new, value );
1492 mlt_properties_set_string( properties, context->property, new );
1493 free( new );
1494 }
1495 else
1496 mlt_properties_set_string( properties, context->property, value );
1497 }
1498 context->entity_is_replace = 0;
1499
1500 // Check for a service beginning with glsl. or movit.
1501 if ( !strncmp( value, "glsl.", 5 ) || !strncmp( value, "movit.", 6 ) )
1502 mlt_properties_set_int( context->params, "qglsl", 1 );
1503
1504 free( value);
1505 }
1506
1507 /** Convert parameters parsed from resource into entity declarations.
1508 */
params_to_entities(deserialise_context context)1509 static void params_to_entities( deserialise_context context )
1510 {
1511 if ( context->params != NULL )
1512 {
1513 int i;
1514
1515 // Add our params as entity declarations
1516 for ( i = 0; i < mlt_properties_count( context->params ); i++ )
1517 {
1518 xmlChar *name = ( xmlChar* )mlt_properties_get_name( context->params, i );
1519 xmlAddDocEntity( context->entity_doc, name, XML_INTERNAL_GENERAL_ENTITY,
1520 context->publicId, context->systemId, ( xmlChar* )mlt_properties_get( context->params, _s(name) ) );
1521 }
1522
1523 // Flag completion
1524 mlt_properties_close( context->params );
1525 context->params = NULL;
1526 }
1527 }
1528
1529 // The following 3 facilitate entity substitution in the SAX parser
on_internal_subset(void * ctx,const xmlChar * name,const xmlChar * publicId,const xmlChar * systemId)1530 static void on_internal_subset( void *ctx, const xmlChar* name,
1531 const xmlChar* publicId, const xmlChar* systemId )
1532 {
1533 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1534 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1535
1536 context->publicId = publicId;
1537 context->systemId = systemId;
1538 xmlCreateIntSubset( context->entity_doc, name, publicId, systemId );
1539
1540 // Override default entities with our parameters
1541 params_to_entities( context );
1542 }
1543
1544 // TODO: Check this with Dan... I think this is for parameterisation
1545 // but it's breaking standard escaped entities (like < etc).
on_entity_declaration(void * ctx,const xmlChar * name,int type,const xmlChar * publicId,const xmlChar * systemId,xmlChar * content)1546 static void on_entity_declaration( void *ctx, const xmlChar* name, int type,
1547 const xmlChar* publicId, const xmlChar* systemId, xmlChar* content)
1548 {
1549 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1550 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1551
1552 xmlAddDocEntity( context->entity_doc, name, type, publicId, systemId, content );
1553 }
1554
1555 // TODO: Check this functionality (see on_entity_declaration)
on_get_entity(void * ctx,const xmlChar * name)1556 static xmlEntityPtr on_get_entity( void *ctx, const xmlChar* name )
1557 {
1558 struct _xmlParserCtxt *xmlcontext = ( struct _xmlParserCtxt* )ctx;
1559 deserialise_context context = ( deserialise_context )( xmlcontext->_private );
1560 xmlEntityPtr e = NULL;
1561
1562 // Setup for entity declarations if not ready
1563 if ( xmlGetIntSubset( context->entity_doc ) == NULL )
1564 {
1565 xmlCreateIntSubset( context->entity_doc, _x("mlt"), _x(""), _x("") );
1566 context->publicId = _x("");
1567 context->systemId = _x("");
1568 }
1569
1570 // Add our parameters if not already
1571 params_to_entities( context );
1572
1573 e = xmlGetPredefinedEntity( name );
1574
1575 // Send signal to on_characters that an entity substitutin is pending
1576 if ( e == NULL )
1577 {
1578 e = xmlGetDocEntity( context->entity_doc, name );
1579 if ( e != NULL )
1580 context->entity_is_replace = 1;
1581 }
1582
1583 return e;
1584 }
1585
on_error(void * ctx,const char * msg,...)1586 static void on_error( void * ctx, const char * msg, ... )
1587 {
1588 struct _xmlError* err_ptr = xmlCtxtGetLastError( ctx );
1589
1590 switch( err_ptr->level )
1591 {
1592 case XML_ERR_WARNING:
1593 mlt_log_warning( NULL, "[producer_xml] parse warning: %s\trow: %d\tcol: %d\n",
1594 err_ptr->message, err_ptr->line, err_ptr->int2 );
1595 break;
1596 case XML_ERR_ERROR:
1597 mlt_log_error( NULL, "[producer_xml] parse error: %s\trow: %d\tcol: %d\n",
1598 err_ptr->message, err_ptr->line, err_ptr->int2 );
1599 break;
1600 default:
1601 case XML_ERR_FATAL:
1602 mlt_log_fatal( NULL, "[producer_xml] parse fatal: %s\trow: %d\tcol: %d\n",
1603 err_ptr->message, err_ptr->line, err_ptr->int2 );
1604 break;
1605 }
1606 }
1607
1608 /** Convert a hexadecimal character to its value.
1609 */
tohex(char p)1610 static int tohex( char p )
1611 {
1612 return isdigit( p ) ? p - '0' : tolower( p ) - 'a' + 10;
1613 }
1614
1615 /** Decode a url-encoded string containing hexadecimal character sequences.
1616 */
url_decode(char * dest,char * src)1617 static char *url_decode( char *dest, char *src )
1618 {
1619 char *p = dest;
1620
1621 while ( *src )
1622 {
1623 if ( *src == '%' )
1624 {
1625 *p ++ = ( tohex( *( src + 1 ) ) << 4 ) | tohex( *( src + 2 ) );
1626 src += 3;
1627 }
1628 else
1629 {
1630 *p ++ = *src ++;
1631 }
1632 }
1633
1634 *p = *src;
1635 return dest;
1636 }
1637
1638 /** Extract the filename from a URL attaching parameters to a properties list.
1639 */
parse_url(mlt_properties properties,char * url)1640 static void parse_url( mlt_properties properties, char *url )
1641 {
1642 int i;
1643 int n = strlen( url );
1644 char *name = NULL;
1645 char *value = NULL;
1646 int is_query = 0;
1647
1648 for ( i = 0; i < n; i++ )
1649 {
1650 switch ( url[ i ] )
1651 {
1652 case '?':
1653 url[ i++ ] = '\0';
1654 name = &url[ i ];
1655 is_query = 1;
1656 break;
1657
1658 case ':':
1659 #ifdef _WIN32
1660 if ( url[i + 1] != '/' && url[i + 1] != '\\' )
1661 #endif
1662 case '=':
1663 if ( is_query )
1664 {
1665 url[ i++ ] = '\0';
1666 value = &url[ i ];
1667 }
1668 break;
1669
1670 case '&':
1671 if ( is_query )
1672 {
1673 url[ i++ ] = '\0';
1674 if ( name != NULL && value != NULL )
1675 mlt_properties_set_string( properties, name, value );
1676 name = &url[ i ];
1677 value = NULL;
1678 }
1679 break;
1680 }
1681 }
1682 if ( name != NULL && value != NULL )
1683 mlt_properties_set_string( properties, name, value );
1684 }
1685
1686 // Quick workaround to avoid unnecessary libxml2 warnings
file_exists(char * name)1687 static int file_exists( char *name )
1688 {
1689 int exists = 0;
1690 if ( name != NULL )
1691 {
1692 FILE *f = mlt_fopen( name, "r" );
1693 exists = f != NULL;
1694 if ( exists ) fclose( f );
1695 }
1696 return exists;
1697 }
1698
1699 // This function will add remaining services in the context service stack marked
1700 // with a "xml_retain" property to a property named "xml_retain" on the returned
1701 // service. The property is a mlt_properties data property.
1702
retain_services(struct deserialise_context_s * context,mlt_service service)1703 static void retain_services( struct deserialise_context_s *context, mlt_service service )
1704 {
1705 mlt_properties retain_list = mlt_properties_new();
1706 enum service_type type;
1707 mlt_service retain_service = context_pop_service( context, &type );
1708
1709 while ( retain_service )
1710 {
1711 mlt_properties retain_properties = MLT_SERVICE_PROPERTIES( retain_service );
1712
1713 if ( mlt_properties_get_int( retain_properties, "xml_retain" ) )
1714 {
1715 // Remove the retained service from the destructors list.
1716 int i;
1717 for ( i = mlt_properties_count( context->destructors ) - 1; i >= 1; i -- )
1718 {
1719 const char *name = mlt_properties_get_name( context->destructors, i );
1720 if ( mlt_properties_get_data_at( context->destructors, i, NULL ) == retain_service )
1721 {
1722 mlt_properties_set_data( context->destructors, name, retain_service, 0, NULL, NULL );
1723 break;
1724 }
1725 }
1726 const char *name = mlt_properties_get( retain_properties, "id" );
1727 if ( name )
1728 mlt_properties_set_data( retain_list, name, retain_service, 0,
1729 (mlt_destructor) mlt_service_close, NULL );
1730 }
1731 retain_service = context_pop_service( context, &type );
1732 }
1733 if ( mlt_properties_count( retain_list ) > 0 )
1734 {
1735 mlt_properties_set_data( MLT_SERVICE_PROPERTIES(service), "xml_retain", retain_list, 0,
1736 (mlt_destructor) mlt_properties_close, NULL );
1737 }
1738 else
1739 {
1740 mlt_properties_close( retain_list );
1741 }
1742 }
1743
context_new(mlt_profile profile)1744 static deserialise_context context_new( mlt_profile profile )
1745 {
1746 deserialise_context context = calloc( 1, sizeof( struct deserialise_context_s ) );
1747 if ( context )
1748 {
1749 context->producer_map = mlt_properties_new();
1750 context->destructors = mlt_properties_new();
1751 context->params = mlt_properties_new();
1752 context->profile = profile;
1753 context->seekable = 1;
1754 context->stack_service = mlt_deque_init();
1755 context->stack_types = mlt_deque_init();
1756 context->stack_node = mlt_deque_init();
1757 context->stack_branch = mlt_deque_init();
1758 mlt_deque_push_back_int( context->stack_branch, 0 );
1759 }
1760 return context;
1761 }
1762
context_close(deserialise_context context)1763 static void context_close( deserialise_context context )
1764 {
1765 mlt_properties_close( context->producer_map );
1766 mlt_properties_close( context->destructors );
1767 mlt_properties_close( context->params );
1768 mlt_deque_close( context->stack_service );
1769 mlt_deque_close( context->stack_types );
1770 mlt_deque_close( context->stack_node );
1771 mlt_deque_close( context->stack_branch );
1772 xmlFreeDoc( context->entity_doc );
1773 free( context->lc_numeric );
1774 free( context );
1775 }
1776
producer_xml_init(mlt_profile profile,mlt_service_type servtype,const char * id,char * data)1777 mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, const char *id, char *data )
1778 {
1779 xmlSAXHandler *sax, *sax_orig;
1780 deserialise_context context;
1781 mlt_properties properties = NULL;
1782 int i = 0;
1783 struct _xmlParserCtxt *xmlcontext;
1784 int well_formed = 0;
1785 char *filename = NULL;
1786 int is_filename = strcmp( id, "xml-string" );
1787
1788 // Strip file:// prefix
1789 if ( data && strlen( data ) >= 7 && strncmp( data, "file://", 7 ) == 0 )
1790 data += 7;
1791
1792 if ( data == NULL || !strcmp( data, "" ) )
1793 return NULL;
1794
1795 context = context_new( profile );
1796 if ( context == NULL )
1797 return NULL;
1798
1799 // Decode URL and parse parameters
1800 mlt_properties_set_string( context->producer_map, "root", "" );
1801 if ( is_filename )
1802 {
1803 mlt_properties_set_string( context->params, "_mlt_xml_resource", data );
1804 filename = mlt_properties_get( context->params, "_mlt_xml_resource" );
1805 parse_url( context->params, url_decode( filename, data ) );
1806
1807 // We need the directory prefix which was used for the xml
1808 if ( strchr( filename, '/' ) || strchr( filename, '\\' ) )
1809 {
1810 char *root = NULL;
1811 mlt_properties_set_string( context->producer_map, "root", filename );
1812 root = mlt_properties_get( context->producer_map, "root" );
1813 if ( strchr( root, '/') )
1814 *( strrchr( root, '/' ) ) = '\0';
1815 else if ( strchr( root, '\\') )
1816 *( strrchr( root, '\\' ) ) = '\0';
1817
1818 // If we don't have an absolute path here, we're heading for disaster...
1819 if ( root[ 0 ] != '/' && !strchr( root, ':' ) )
1820 {
1821 char *cwd = getcwd( NULL, 0 );
1822 char *real = malloc( strlen( cwd ) + strlen( root ) + 2 );
1823 sprintf( real, "%s/%s", cwd, root );
1824 mlt_properties_set_string( context->producer_map, "root", real );
1825 free( real );
1826 free( cwd );
1827 }
1828 }
1829
1830 if ( !file_exists( filename ) )
1831 {
1832 // Try the un-converted text encoding as a fallback.
1833 // Fixes launching melt as child process from Shotcut on Windows
1834 // when there are extended characters in the path.
1835 filename = mlt_properties_get( context->params, "_mlt_xml_resource" );
1836 }
1837
1838 if ( !file_exists( filename ) )
1839 {
1840 context_close( context );
1841 return NULL;
1842 }
1843 }
1844
1845 // We need to track the number of registered filters
1846 mlt_properties_set_int( context->destructors, "registered", 0 );
1847
1848 // Setup SAX callbacks for first pass
1849 sax = calloc( 1, sizeof( xmlSAXHandler ) );
1850 sax->startElement = on_start_element;
1851 sax->characters = on_characters;
1852 sax->warning = on_error;
1853 sax->error = on_error;
1854 sax->fatalError = on_error;
1855
1856 // Setup libxml2 SAX parsing
1857 xmlInitParser();
1858 xmlSubstituteEntitiesDefault( 1 );
1859 // This is used to facilitate entity substitution in the SAX parser
1860 context->entity_doc = xmlNewDoc( _x("1.0") );
1861 if ( is_filename )
1862 xmlcontext = xmlCreateFileParserCtxt( filename );
1863 else
1864 xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
1865
1866 // Invalid context - clean up and return NULL
1867 if ( xmlcontext == NULL )
1868 {
1869 context_close( context );
1870 free( sax );
1871 return NULL;
1872 }
1873
1874 // Parse
1875 sax_orig = xmlcontext->sax;
1876 xmlcontext->sax = sax;
1877 xmlcontext->_private = ( void* )context;
1878 xmlParseDocument( xmlcontext );
1879 well_formed = xmlcontext->wellFormed;
1880
1881 // Cleanup after parsing
1882 xmlcontext->sax = sax_orig;
1883 xmlcontext->_private = NULL;
1884 if ( xmlcontext->myDoc )
1885 xmlFreeDoc( xmlcontext->myDoc );
1886 xmlFreeParserCtxt( xmlcontext );
1887
1888 // Bad xml - clean up and return NULL
1889 if ( !well_formed )
1890 {
1891 context_close( context );
1892 free( sax );
1893 return NULL;
1894 }
1895
1896 // Setup the second pass
1897 context->pass ++;
1898 if ( is_filename )
1899 xmlcontext = xmlCreateFileParserCtxt( filename );
1900 else
1901 xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) );
1902
1903 // Invalid context - clean up and return NULL
1904 if ( xmlcontext == NULL )
1905 {
1906 context_close( context );
1907 free( sax );
1908 return NULL;
1909 }
1910
1911 // Reset the stack.
1912 mlt_deque_close( context->stack_service );
1913 mlt_deque_close( context->stack_types );
1914 mlt_deque_close( context->stack_node );
1915 context->stack_service = mlt_deque_init();
1916 context->stack_types = mlt_deque_init();
1917 context->stack_node = mlt_deque_init();
1918
1919 // Create the qglsl consumer now, if requested, so that glsl.manager
1920 // may exist when trying to load glsl. or movit. services.
1921 // The "if requested" part can come from query string qglsl=1 or when
1922 // a service beginning with glsl. or movit. appears in the XML.
1923 if ( mlt_properties_get_int( context->params, "qglsl" ) && strcmp( id, "xml-nogl" )
1924 // Only if glslManager does not yet exist.
1925 && !mlt_properties_get_data( mlt_global_properties(), "glslManager", NULL ) )
1926 context->qglsl = mlt_factory_consumer( profile, "qglsl", NULL );
1927
1928 // Setup SAX callbacks for second pass
1929 sax->endElement = on_end_element;
1930 sax->cdataBlock = on_characters;
1931 sax->internalSubset = on_internal_subset;
1932 sax->entityDecl = on_entity_declaration;
1933 sax->getEntity = on_get_entity;
1934
1935 // Parse
1936 sax_orig = xmlcontext->sax;
1937 xmlcontext->sax = sax;
1938 xmlcontext->_private = ( void* )context;
1939 xmlParseDocument( xmlcontext );
1940 well_formed = xmlcontext->wellFormed;
1941
1942 // Cleanup after parsing
1943 xmlFreeDoc( context->entity_doc );
1944 context->entity_doc = NULL;
1945 free( sax );
1946 xmlMemoryDump( ); // for debugging
1947 xmlcontext->sax = sax_orig;
1948 xmlcontext->_private = NULL;
1949 if ( xmlcontext->myDoc )
1950 xmlFreeDoc( xmlcontext->myDoc );
1951 xmlFreeParserCtxt( xmlcontext );
1952
1953 // Get the last producer on the stack
1954 enum service_type type;
1955 mlt_service service = context_pop_service( context, &type );
1956 if ( well_formed && service != NULL )
1957 {
1958 // Verify it is a producer service (mlt_type="mlt_producer")
1959 // (producer, playlist, multitrack)
1960 char *type = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "mlt_type" );
1961 if ( type == NULL || ( strcmp( type, "mlt_producer" ) != 0 && strcmp( type, "producer" ) != 0 ) )
1962 service = NULL;
1963 }
1964
1965 #ifdef DEBUG
1966 xmlDocPtr doc = xml_make_doc( service );
1967 xmlDocFormatDump( stdout, doc, 1 );
1968 xmlFreeDoc( doc );
1969 service = NULL;
1970 #endif
1971
1972 if ( well_formed && service != NULL )
1973 {
1974 char *title = mlt_properties_get( context->producer_map, "title" );
1975
1976 // Need the complete producer list for various reasons
1977 properties = context->destructors;
1978
1979 // Now make sure we don't have a reference to the service in the properties
1980 for ( i = mlt_properties_count( properties ) - 1; i >= 1; i -- )
1981 {
1982 char *name = mlt_properties_get_name( properties, i );
1983 if ( mlt_properties_get_data_at( properties, i, NULL ) == service )
1984 {
1985 mlt_properties_set_data( properties, name, service, 0, NULL, NULL );
1986 break;
1987 }
1988 }
1989
1990 // We are done referencing destructor property list
1991 // Set this var to service properties for convenience
1992 properties = MLT_SERVICE_PROPERTIES( service );
1993
1994 // Assign the title
1995 mlt_properties_set_string( properties, "title", title );
1996
1997 // Optimise for overlapping producers
1998 mlt_producer_optimise( MLT_PRODUCER( service ) );
1999
2000 // Handle deep copies
2001 if ( getenv( "MLT_XML_DEEP" ) == NULL )
2002 {
2003 // Now assign additional properties
2004 if ( is_filename && (
2005 mlt_service_identify( service ) == tractor_type ||
2006 mlt_service_identify( service ) == playlist_type ||
2007 mlt_service_identify( service ) == multitrack_type ) )
2008 {
2009 mlt_properties_set_int( properties, "_original_type",
2010 mlt_service_identify( service ) );
2011 mlt_properties_set_string( properties, "_original_resource",
2012 mlt_properties_get( properties, "resource" ) );
2013 mlt_properties_set_string( properties, "resource", data );
2014 }
2015
2016 // This tells consumer_xml not to deep copy
2017 mlt_properties_set_string( properties, "xml", "was here" );
2018 }
2019 else
2020 {
2021 // Allow the project to be edited
2022 mlt_properties_set_string( properties, "_xml", "was here" );
2023 mlt_properties_set_int( properties, "_mlt_service_hidden", 1 );
2024 }
2025
2026 // Make consumer available
2027 mlt_properties_inc_ref( MLT_CONSUMER_PROPERTIES( context->consumer ) );
2028 mlt_properties_set_data( properties, "consumer", context->consumer, 0,
2029 (mlt_destructor) mlt_consumer_close, NULL );
2030
2031 mlt_properties_set_int( properties, "seekable", context->seekable );
2032
2033 retain_services( context, service );
2034 }
2035 else
2036 {
2037 // Return null if not well formed
2038 service = NULL;
2039 }
2040
2041 // Clean up
2042 if ( context->qglsl && context->consumer != context->qglsl )
2043 mlt_consumer_close( context->qglsl );
2044 context_close( context );
2045
2046 return MLT_PRODUCER( service );
2047 }
2048