mlt_properties_set_int( properties, "_unique_id", ++ unique_id );
mlt_properties_set( properties, "mlt_type", "consumer" );
mlt_properties_set( properties, "mlt_service", service );
+ mlt_service_attach( mlt_consumer_service( obj ), mlt_factory_filter( "data_show", NULL ) );
}
return obj;
}
{
int i;
mlt_properties frame_properties = mlt_frame_properties( frame );
- mlt_properties filter_properties = mlt_service_properties( this );
+ mlt_properties service_properties = mlt_service_properties( this );
mlt_service_base *base = this->local;
mlt_position position = mlt_frame_get_position( frame );
- mlt_position this_in = mlt_properties_get_position( filter_properties, "in" );
+ mlt_position this_in = mlt_properties_get_position( service_properties, "in" );
+ mlt_position this_out = mlt_properties_get_position( service_properties, "out" );
// Hmm - special case for cuts - apply filters from the parent first
- if ( mlt_properties_get_int( filter_properties, "_cut" ) )
+ if ( mlt_properties_get_int( service_properties, "_cut" ) )
{
+ mlt_service_apply_filters( ( mlt_service )mlt_properties_get_data( service_properties, "_cut_parent", NULL ), frame, 0 );
position -= this_in;
mlt_frame_set_position( frame, position );
- mlt_service_apply_filters( ( mlt_service )mlt_properties_get_data( filter_properties, "_cut_parent", NULL ), frame, 0 );
}
- if ( index == 0 || mlt_properties_get_int( filter_properties, "_filter_private" ) == 0 )
+ if ( index == 0 || mlt_properties_get_int( service_properties, "_filter_private" ) == 0 )
{
// Process the frame with the attached filters
for ( i = 0; i < base->filter_count; i ++ )
{
if ( base->filters[ i ] != NULL )
{
- mlt_properties properties = mlt_filter_properties( base->filters[ i ] );
mlt_position in = mlt_filter_get_in( base->filters[ i ] );
mlt_position out = mlt_filter_get_out( base->filters[ i ] );
if ( ( in == 0 && out == 0 ) || ( position >= in && ( position <= out || out == 0 ) ) )
{
+ mlt_properties_set_position( frame_properties, "in", 0 );
+ mlt_properties_set_position( frame_properties, "out", out == 0 ? this_out - this_in : out - in );
mlt_frame_set_position( frame, position - in );
mlt_filter_process( base->filters[ i ], frame );
mlt_service_apply_filters( mlt_filter_service( base->filters[ i ] ), frame, index + 1 );
}
}
- if ( mlt_properties_get_int( filter_properties, "_cut" ) )
+ if ( mlt_properties_get_int( service_properties, "_cut" ) )
+ {
+ mlt_properties_set_position( frame_properties, "in", this_in );
+ mlt_properties_set_position( frame_properties, "out", this_out );
mlt_frame_set_position( frame, position + this_in );
+ }
}
/** Obtain a frame.
int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index )
{
- if ( this != NULL )
+ if ( this != NULL && this->get_frame != NULL )
{
- int result = this->get_frame( this, frame, index );
+ int result = 0;
+ mlt_properties properties = mlt_service_properties( this );
+ mlt_position in = mlt_properties_get_position( properties, "in" );
+ mlt_position out = mlt_properties_get_position( properties, "out" );
+ result = this->get_frame( this, frame, index );
if ( result == 0 )
+ {
+ if ( in >=0 && out > 0 )
+ {
+ properties = mlt_frame_properties( *frame );
+ mlt_properties_set_position( properties, "in", in );
+ mlt_properties_set_position( properties, "out", out );
+ }
mlt_service_apply_filters( this, *frame, 1 );
+ }
return result;
}
*frame = mlt_frame_init( );
mlt_frame audio = NULL;
mlt_frame video = NULL;
+ // Determine which data_queue to pass on...
+ void *data_queue = NULL;
+
// Get the multitrack's producer
mlt_producer target = mlt_multitrack_producer( multitrack );
mlt_producer_seek( target, mlt_producer_frame( parent ) );
sprintf( label, "_%s_%d", id, count ++ );
mlt_properties_set_data( frame_properties, label, temp, 0, ( mlt_destructor )mlt_frame_close, NULL );
+ // We want the last data_queue
+ if ( mlt_properties_get_data( mlt_frame_properties( temp ), "data_queue", NULL ) != NULL )
+ data_queue = mlt_properties_get_data( mlt_frame_properties( temp ), "data_queue", NULL );
+
// Pick up first video and audio frames
if ( !done && !mlt_frame_is_test_audio( temp ) && !( mlt_properties_get_int( mlt_frame_properties( temp ), "hide" ) & 2 ) )
audio = temp;
if ( video != NULL )
{
+ mlt_properties_set_data( mlt_frame_properties( *frame ), "data_queue", data_queue, 0, NULL, NULL );
mlt_frame_push_service( *frame, video );
mlt_frame_push_service( *frame, producer_get_image );
mlt_properties_inherit( mlt_frame_properties( *frame ), mlt_frame_properties( video ) );
producer_ppm.o \
filter_brightness.o \
filter_channelcopy.o \
+ filter_data_feed.o \
+ filter_data_show.o \
filter_gamma.o \
filter_greyscale.o \
filter_luma.o \
install: all
install -m 755 $(TARGET) "$(prefix)/share/mlt/modules"
+ install -m 644 ../data_fx.properties "$(prefix)/share/mlt/modules"
ifneq ($(wildcard .depend),)
include .depend
cat << EOF >> ../filters.dat
brightness libmltcore.so
channelcopy libmltcore.so
+data_feed libmltcore.so
+data_show libmltcore.so
gamma libmltcore.so
greyscale libmltcore.so
luma libmltcore.so
#include "producer_ppm.h"
#include "filter_brightness.h"
#include "filter_channelcopy.h"
+#include "filter_data.h"
#include "filter_gamma.h"
#include "filter_greyscale.h"
#include "filter_luma.h"
return filter_brightness_init( arg );
if ( !strcmp( id, "channelcopy" ) )
return filter_channelcopy_init( arg );
+ if ( !strcmp( id, "data_feed" ) )
+ return filter_data_feed_init( arg );
+ if ( !strcmp( id, "data_show" ) )
+ return filter_data_show_init( arg );
if ( !strcmp( id, "gamma" ) )
return filter_gamma_init( arg );
if ( !strcmp( id, "greyscale" ) )
--- /dev/null
+/*
+ * filter_data.h -- data feed and show filters
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _MLT_FILTER_DATA_H_
+#define _MLT_FILTER_DATA_H_
+
+#include <framework/mlt_filter.h>
+
+extern mlt_filter filter_data_feed_init( char * );
+extern mlt_filter filter_data_show_init( char * );
+
+#endif
+
--- /dev/null
+/*
+ * filter_data_feed.c -- data feed filter
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "filter_data.h"
+#include <framework/mlt.h>
+
+/** This filter should be used in conjuction with the data_show filter.
+ The concept of the data_feed is that it can be used to pass titles
+ or images to render on the frame, but doesn't actually do it
+ itself. data_feed imposes few rules on what's passed on and the
+ validity is confirmed in data_show before use.
+*/
+
+/** Data queue destructor.
+*/
+
+static void destroy_data_queue( void *arg )
+{
+ if ( arg != NULL )
+ {
+ // Assign the correct type
+ mlt_deque queue = arg;
+
+ // Iterate through each item and destroy them
+ while ( mlt_deque_peek_front( queue ) != NULL )
+ mlt_properties_close( mlt_deque_pop_back( queue ) );
+
+ // Close the deque
+ mlt_deque_close( queue );
+ }
+}
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Get the filter properties
+ mlt_properties filter_properties = mlt_filter_properties( this );
+
+ // Get the frame properties
+ mlt_properties frame_properties = mlt_frame_properties( frame );
+
+ // Get the data queue
+ mlt_deque data_queue = mlt_properties_get_data( frame_properties, "data_queue", NULL );
+
+ // Create the data queue if it doesn't exist
+ if ( data_queue == NULL )
+ {
+ // Create the queue
+ data_queue = mlt_deque_init( );
+
+ // Assign it to the frame with the destructor
+ mlt_properties_set_data( frame_properties, "data_queue", data_queue, 0, destroy_data_queue, NULL );
+ }
+
+ // Now create the data feed
+ if ( data_queue != NULL )
+ {
+ // Get the in and out points of this filter
+ int in = mlt_filter_get_in( this );
+ int out = mlt_filter_get_out( this );
+
+ // Create a new data feed
+ mlt_properties feed = mlt_properties_new( );
+
+ // Get the type of the data feed
+ char *type = mlt_properties_get( filter_properties, "type" );
+
+ // Assign it the base properties
+ mlt_properties_set( feed, "id", mlt_properties_get( filter_properties, "_unique_id" ) );
+ mlt_properties_set( feed, "type", type );
+ mlt_properties_set_position( feed, "position", mlt_frame_get_position( frame ) );
+
+ // Assign in/out of service we're connected to
+ mlt_properties_set_position( feed, "in", mlt_properties_get_position( frame_properties, "in" ) );
+ mlt_properties_set_position( feed, "out", mlt_properties_get_position( frame_properties, "out" ) );
+
+ // Correct in/out to the filter if specified
+ if ( in != 0 )
+ mlt_properties_set_position( feed, "in", in );
+ if ( out != 0 )
+ mlt_properties_set_position( feed, "out", out );
+
+ // Pass the properties which start with a "feed." prefix
+ // Note that 'feed.text' in the filter properties becomes 'text' on the feed
+ mlt_properties_pass( feed, filter_properties, "feed." );
+
+ // Push it on to the queue
+ mlt_deque_push_back( data_queue, feed );
+ }
+
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_data_feed_init( char *arg )
+{
+ // Create the filter
+ mlt_filter this = mlt_filter_new( );
+
+ // Initialise it
+ if ( this != NULL )
+ {
+ // Get the properties
+ mlt_properties properties = mlt_filter_properties( this );
+
+ // Assign the argument (default to titles)
+ mlt_properties_set( properties, "type", arg == NULL ? "titles" : arg );
+
+ // Specify the processing method
+ this->process = filter_process;
+ }
+
+ return this;
+}
+
--- /dev/null
+/*
+ * filter_data_show.c -- data feed filter
+ * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "filter_data.h"
+#include <framework/mlt.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** Handle the profile.
+*/
+
+static mlt_filter obtain_filter( mlt_filter filter, char *type )
+{
+ // Result to return
+ mlt_filter result = NULL;
+
+ // Miscelaneous variable
+ int i = 0;
+ int type_len = strlen( type );
+
+ // Get the properties of the data show filter
+ mlt_properties filter_properties = mlt_filter_properties( filter );
+
+ // Get the profile properties
+ mlt_properties profile_properties = mlt_properties_get_data( filter_properties, "profile_properties", NULL );
+
+ // Obtain the profile_properties if we haven't already
+ if ( profile_properties == NULL )
+ {
+ // Get the profile requested
+ char *profile = mlt_properties_get( filter_properties, "profile" );
+
+ // Load the specified profile or use the default
+ if ( profile != NULL )
+ {
+ profile_properties = mlt_properties_load( profile );
+ }
+ else
+ {
+ // Sometimes C can be laborious..
+ static char *default_file = "/data_fx.properties";
+ char *temp = malloc( strlen( mlt_factory_prefix( ) ) + strlen( default_file ) + 1 );
+ if ( temp != NULL )
+ {
+ strcpy( temp, mlt_factory_prefix( ) );
+ strcat( temp, default_file );
+ profile_properties = mlt_properties_load( temp );
+ free( temp );
+ }
+ }
+
+ // Store for later retrieval
+ mlt_properties_set_data( filter_properties, "profile_properties", profile_properties, 0, ( mlt_destructor )mlt_properties_close, NULL );
+ }
+
+ if ( profile_properties != NULL )
+ {
+ for ( i = 0; i < mlt_properties_count( profile_properties ); i ++ )
+ {
+ char *name = mlt_properties_get_name( profile_properties, i );
+ char *value = mlt_properties_get_value( profile_properties, i );
+
+ if ( result == NULL && !strcmp( name, type ) && result == NULL )
+ result = mlt_factory_filter( value, NULL );
+ else if ( result != NULL && !strncmp( name, type, type_len ) && name[ type_len ] == '.' )
+ mlt_properties_set( mlt_filter_properties( result ), name + type_len + 1, value );
+ else if ( result != NULL )
+ break;
+ }
+ }
+
+ return result;
+}
+
+/** Process the frame for the requested type
+*/
+
+static int process_feed( mlt_properties feed, mlt_filter filter, mlt_frame frame )
+{
+ // Error return
+ int error = 1;
+
+ // Get the properties of the data show filter
+ mlt_properties filter_properties = mlt_filter_properties( filter );
+
+ // Get the type requested by the feeding filter
+ char *type = mlt_properties_get( feed, "type" );
+
+ // Fetch the filter associated to this type
+ mlt_filter requested = mlt_properties_get_data( filter_properties, type, NULL );
+
+ // Calculate the length of the feed
+ int length = mlt_properties_get_int( feed, "out" ) - mlt_properties_get_int( feed, "in" ) + 1;
+
+ // If it doesn't exist, then create it now
+ if ( requested == NULL )
+ {
+ // Source filter from profile
+ requested = obtain_filter( filter, type );
+
+ // Store it on the properties for subsequent retrieval/destruction
+ mlt_properties_set_data( filter_properties, type, requested, 0, ( mlt_destructor )mlt_filter_close, NULL );
+ }
+
+ // If we have one, then process it now...
+ if ( requested != NULL )
+ {
+ int i = 0;
+ mlt_properties properties = mlt_filter_properties( requested );
+ static char *prefix = "properties.";
+ int len = strlen( prefix );
+
+ // Pass properties from feed into requested
+ for ( i = 0; i < mlt_properties_count( properties ); i ++ )
+ {
+ char *name = mlt_properties_get_name( properties, i );
+ char *key = mlt_properties_get_value( properties, i );
+ if ( !strncmp( name, prefix, len ) )
+ {
+ if ( !strncmp( name + len, "length[", 7 ) )
+ {
+ int period = mlt_properties_get_int( properties, "period" );
+ period = period == 0 ? 1 : period;
+ mlt_properties_set_position( properties, key, length / period );
+ }
+ else
+ {
+ char *value = mlt_properties_get( feed, name + len );
+ if ( value != NULL )
+ mlt_properties_set( properties, key, value );
+ }
+ }
+ }
+
+ // Set the original position on the frame
+ mlt_frame_set_position( frame, mlt_properties_get_int( feed, "position" ) );
+
+ // Process the filter
+ mlt_filter_process( requested, frame );
+
+ // Should be ok...
+ error = 0;
+ }
+
+ return error;
+}
+
+/** Get the image.
+*/
+
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+ // Pop the service
+ mlt_filter filter = mlt_frame_pop_service( frame );
+
+ // Get the frame properties
+ mlt_properties frame_properties = mlt_frame_properties( frame );
+
+ // Fetch the data queue
+ mlt_deque data_queue = mlt_properties_get_data( frame_properties, "data_queue", NULL );
+
+ // Iterate through each entry on the queue
+ while ( data_queue != NULL && mlt_deque_peek_front( data_queue ) != NULL )
+ {
+ // Get the data feed
+ mlt_properties feed = mlt_deque_pop_front( data_queue );
+
+ // Process the data feed...
+ process_feed( feed, filter, frame );
+
+ // Close the feed
+ mlt_properties_close( feed );
+ }
+
+ // Need to get the image
+ return mlt_frame_get_image( frame, image, format, width, height, 1 );
+}
+
+
+/** Filter processing.
+*/
+
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+ // Push the filter
+ mlt_frame_push_service( frame, this );
+
+ // Register the get image method
+ mlt_frame_push_get_image( frame, filter_get_image );
+
+ // Return the frame
+ return frame;
+}
+
+/** Constructor for the filter.
+*/
+
+mlt_filter filter_data_show_init( char *arg )
+{
+ // Create the filter
+ mlt_filter this = mlt_filter_new( );
+
+ // Initialise it
+ if ( this != NULL )
+ {
+ // Get the properties
+ mlt_properties properties = mlt_filter_properties( this );
+
+ // Assign the argument (default to titles)
+ mlt_properties_set( properties, "profile", arg == NULL ? NULL : arg );
+
+ // Specify the processing method
+ this->process = filter_process;
+ }
+
+ return this;
+}
+
// Pass all the composite. properties on the filter down
mlt_properties_pass( composite_properties, properties, "composite." );
+
+ // Force a refresh
+ mlt_properties_set_int( composite_properties, "refresh", 1 );
}
// Create a producer if don't have one
static void alignment_calculate( struct geometry_s *geometry )
{
geometry->x += ( geometry->w - geometry->sw ) * geometry->halign / 2;
- geometry->y += ( geometry->h - geometry->sh ) * geometry->valign;
+ geometry->y += ( geometry->h - geometry->sh ) * geometry->valign / 2;
}
/** Calculate the position for this frame.
// Do the calculation
struct geometry_s *start = composite_calculate( &result, this, a_frame, position );
-
+
+ // Get the image from the b frame
+ uint8_t *image_b = NULL;
+ int width_b = *width;
+ int height_b = *height;
+
// Optimisation - no compositing required
if ( result.mix == 0 || ( result.w == 0 && result.h == 0 ) )
return 0;
+ // Need to keep the width/height of the a_frame on the b_frame for titling
+ if ( mlt_properties_get( a_props, "dest_width" ) == NULL )
+ {
+ mlt_properties_set_int( a_props, "dest_width", *width );
+ mlt_properties_set_int( a_props, "dest_height", *height );
+ mlt_properties_set_int( b_props, "dest_width", *width );
+ mlt_properties_set_int( b_props, "dest_height", *height );
+ }
+ else
+ {
+ mlt_properties_set_int( b_props, "dest_width", mlt_properties_get_int( a_props, "dest_width" ) );
+ mlt_properties_set_int( b_props, "dest_height", mlt_properties_get_int( a_props, "dest_height" ) );
+ }
+
// Since we are the consumer of the b_frame, we must pass along these
// consumer properties from the a_frame
+ mlt_properties_set_double( b_props, "consumer_progressive", mlt_properties_get_double( a_props, "consumer_progressive" ) );
mlt_properties_set_double( b_props, "consumer_aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
- // Get the image from the b frame
- uint8_t *image_b = NULL;
- int width_b = *width;
- int height_b = *height;
-
+ // Special case for titling...
+ if ( mlt_properties_get_int( properties, "titles" ) )
+ {
+ if ( mlt_properties_get( b_props, "rescale.interp" ) == NULL )
+ mlt_properties_set( b_props, "rescale.interp", "nearest" );
+ mlt_properties_set( properties, "fill", NULL );
+ width_b = mlt_properties_get_int( a_props, "dest_width" );
+ height_b = mlt_properties_get_int( a_props, "dest_height" );
+ }
+
if ( get_b_frame_image( this, b_frame, &image_b, &width_b, &height_b, &result ) == 0 )
{
uint8_t *dest = *image;
--- /dev/null
+# This properties file describes the fx available to the data_send and
+# data_show filters
+#
+# Syntax is as follows:
+#
+# name=<filter>
+# name.description=<user defined>
+# name.properties.<variable>=<full-property>
+# name.<property>=value
+# etc
+#
+# Typically, the <filter> is a 'region' and additional filters are
+# included as properties using the normal region filter syntax.
+#
+
+#
+# The titles filter definition
+#
+
+titles=region
+titles.description=Titles
+titles.properties.markup=filter[1].producer.markup
+titles.type.markup=text
+titles.period=2
+titles.properties.length[0]=filter[0].composite.out
+titles.properties.length[1]=filter[1].composite.out
+titles.composite.start=5%,70%:90%x20%
+titles.filter[0]=watermark
+titles.filter[0].resource=colour:0x000000
+titles.filter[0].composite.start=0%,0%:100%x100%:0
+titles.filter[0].composite.key[5]=0%,0%:100%x100%:40
+titles.filter[1]=watermark
+titles.filter[1].resource=pango:
+titles.filter[1].producer.markup=Shotcut
+titles.filter[1].composite.start=1%,1%:99%x99%:0
+titles.filter[1].composite.key[8]=1%,1%:99%x99%:100
+titles.filter[1].composite.titles=1
+
+#
+# The top titles filter definition
+#
+
+top-titles=region
+top-titles.description=Top Titles
+top-titles.properties.markup=filter[1].producer.markup
+top-titles.type.markup=text
+titles.period=2
+top-titles.properties.length[0]=filter[0].composite.out
+top-titles.properties.length[1]=filter[1].composite.out
+top-titles.composite.start=5%,5%:90%x20%
+top-titles.filter[0]=watermark
+top-titles.filter[0].resource=colour:0x000000
+top-titles.filter[0].composite.start=0%,0%:100%x100%:0
+top-titles.filter[0].composite.key[5]=0%,0%:100%x100%:40
+top-titles.filter[1]=watermark
+top-titles.filter[1].resource=pango:
+top-titles.filter[1].producer.markup=Shotcut
+top-titles.filter[1].composite.start=1%,1%:99%x99%:0
+top-titles.filter[1].composite.key[8]=1%,1%:99%x99%:100
+top-titles.filter[1].composite.halign=centre
+top-titles.filter[1].composite.titles=1
+
+#
+# OK - Silly example...
+#
+
+tickertape=region
+tickertape.description=Tickertape
+tickertape.properties.markup=filter[1].producer.markup
+tickertape.type.markup=text
+tickertape.properties.length[0]=filter[1].composite.out
+tickertape.composite.start=0%,93%:100%x7%
+tickertape.filter[0]=watermark
+tickertape.filter[0].resource=colour:0x000000
+tickertape.filter[0].composite.start=0%,0%:100%x100%:100
+tickertape.filter[1]=watermark
+tickertape.filter[1].resource=pango:
+tickertape.filter[1].producer.markup=Shotcut
+tickertape.filter[1].composite.start=101%,1%:300%x99%:100
+tickertape.filter[1].composite.end=-300%,1%:300%x99%:100
+tickertape.filter[1].composite.titles=1
// Update other info on the frame
mlt_properties_set_int( properties, "width", 720 );
mlt_properties_set_int( properties, "height", this->is_pal ? 576 : 480 );
- mlt_properties_set_int( properties, "top_field_first", 0 );
+ mlt_properties_set_int( properties, "top_field_first", !this->is_pal ? 0 : ( data[ 5 ] & 0x07 ) == 0 ? 0 : 1 );
// Parse the header for meta info
dv_parse_header( dv_decoder, data );
if ( group != NULL )
properties = group;
}
- else if ( !strcmp( argv[ i ], "-attach" ) || !strcmp( argv[ i ], "-chain" ) )
+ else if ( !strcmp( argv[ i ], "-attach" ) || !strcmp( argv[ i ], "-attach-cut" ) )
{
int type = !strcmp( argv[ i ], "-attach" ) ? 0 : 1;
mlt_filter filter = create_attach( field, argv[ ++ i ], track );
producer = info.cut;
}
+ if ( type == 1 )
+ {
+ mlt_playlist_clip_info info;
+ mlt_playlist_get_clip_info( playlist, &info, mlt_playlist_count( playlist ) - 1 );
+ producer = info.cut;
+ }
+
if ( filter != NULL && mlt_playlist_count( playlist ) > 0 )
{
if ( type == 0 )