<commit_before>/**
 * \file mlt_chain.c
 * \brief link service class
 * \see mlt_chain_s
 *
 * Copyright (C) 2020 Meltytech, LLC
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "mlt_chain.h"
#include "mlt_factory.h"
#include "mlt_frame.h"
#include "mlt_log.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/** \brief private service definition */

typedef struct
{
	int link_count;
	int link_size;
	mlt_link* links;
	mlt_producer source;
	mlt_profile source_profile;
	mlt_properties source_parameters;
	mlt_producer begin;
}
mlt_chain_base;

/* Forward references to static methods.
*/

static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
static void relink_chain( mlt_chain self );
static void chain_property_changed( mlt_service owner, mlt_chain self, char *name );
static void source_property_changed( mlt_service owner, mlt_chain self, char *name );

/** Construct a chain.
 *
 * \public \memberof mlt_chain_s
 * \return the new chain
 */

mlt_chain mlt_chain_init( mlt_profile profile )
{
	mlt_chain self = calloc( 1, sizeof( struct mlt_chain_s ) );
	if ( self != NULL )
	{
		mlt_producer producer = &self->parent;
		if ( mlt_producer_init( producer, self ) == 0 )
		{
			mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );

			mlt_properties_set( properties, "mlt_type", "chain" );
			mlt_properties_clear( properties, "resource");
			mlt_properties_clear( properties, "mlt_service");
			mlt_properties_clear( properties, "in");
			mlt_properties_clear( properties, "out");
			mlt_properties_clear( properties, "length");

			producer->get_frame = producer_get_frame;
			producer->close = ( mlt_destructor )mlt_chain_close;
			producer->close_object = self;

			mlt_service_set_profile( MLT_CHAIN_SERVICE( self ), profile );

			// Generate local space
			self->local = calloc( 1, sizeof( mlt_chain_base ) );
			mlt_chain_base* base = self->local;
			base->source_profile = NULL;

			// Listen to property changes to pass along to the source
			mlt_events_listen( MLT_CHAIN_PROPERTIES(self), self, "property-changed", ( mlt_listener )chain_property_changed );
		}
		else
		{
			free( self );
			self = NULL;
		}
	}
	return self;
}

/** Set the source producer.
 *
 * \public \memberof mlt_chain_s
 * \param self a chain
 * \param source the new source producer
 */

void mlt_chain_set_source( mlt_chain self, mlt_producer source )
{
	int error = self == NULL || source == NULL;
	if ( error == 0 )
	{
		mlt_chain_base* base = self->local;
		mlt_properties source_properties = MLT_PRODUCER_PROPERTIES( source );
		int n = 0;
		int i = 0;

		// Clean up from previous source
		mlt_producer_close( base->source );
		mlt_properties_close( base->source_parameters );
		mlt_profile_close( base->source_profile );

		// Save the source producer
		base->source = source;
		mlt_properties_inc_ref( source_properties );

		// Create a list of all parameters used by the source producer so that
		// they can be passed between the source producer and this chain.
		base->source_parameters = mlt_properties_new();
		mlt_repository repository = mlt_factory_repository();
		char* source_metadata_name = strdup( mlt_properties_get( source_properties, "mlt_service" ) );
		// If the service name ends in "-novalidate", then drop the ending and search for the metadata of the main service.
		// E.g. "avformat-novalidate" -> "avformat"
		char* novalidate_ptr = strstr(source_metadata_name, "-novalidate");
		if ( novalidate_ptr ) *novalidate_ptr = '\0';
		mlt_properties source_metadata = mlt_repository_metadata( repository, mlt_service_producer_type, source_metadata_name );
		free( source_metadata_name );
		if ( source_metadata )
		{
			mlt_properties params = (mlt_properties) mlt_properties_get_data( source_metadata, "parameters", NULL );
			if ( params )
			{
				n = mlt_properties_count( params );
				for ( i = 0; i < n; i++ )
				{
					mlt_properties param = (mlt_properties) mlt_properties_get_data( params, mlt_properties_get_name( params, i ), NULL );
					char* identifier = mlt_properties_get( param, "identifier" );
					if ( identifier )
					{
						// Set the value to 1 to indicate the parameter exists.
						mlt_properties_set_int( base->source_parameters, identifier, 1 );
					}
				}
			}
		}

		// Pass parameters and properties from the source producer to this chain.
		// Some properties may have been set during source initialization.
		n = mlt_properties_count( source_properties );
		mlt_events_block( MLT_CHAIN_PROPERTIES(self), self );
		for ( i = 0; i < n; i++ )
		{
			char* name = mlt_properties_get_name( source_properties, i );
			if ( mlt_properties_get_int( base->source_parameters, name ) ||
				 !strcmp( name, "mlt_service" ) ||
				 !strcmp( name, "_mlt_service_hidden" ) ||
				 !strcmp( name, "seekable" ) ||
				 !strncmp( name, "meta.", 5 ) )
			{
				mlt_properties_pass_property( MLT_CHAIN_PROPERTIES(self), source_properties, name );
			}
		}
		// If a length has not been specified for this chain, copy in/out/length from the source producer
		if ( !mlt_producer_get_length( MLT_CHAIN_PRODUCER(self) ) ) {
			mlt_properties_set_position( MLT_CHAIN_PROPERTIES(self), "length", mlt_producer_get_length( base->source ) );
			mlt_producer_set_in_and_out( MLT_CHAIN_PRODUCER(self), mlt_producer_get_in( base->source ), mlt_producer_get_out( base->source ) );
		}
		mlt_events_unblock( MLT_CHAIN_PROPERTIES(self), self );

		// Monitor property changes from the source to pass to the chain.
		mlt_events_listen( source_properties, self, "property-changed", ( mlt_listener )source_property_changed );

		// This chain will control the speed and in/out
		mlt_producer_set_speed( base->source, 0.0 );
		// Approximate infinite length
		mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( base->source ), "length", 0x7fffffff );
		mlt_producer_set_in_and_out( base->source, 0, mlt_producer_get_length( base->source ) - 1 );

		// Reconfigure the chain
		relink_chain( self );
		mlt_events_fire( MLT_CHAIN_PROPERTIES(self), "chain-changed", mlt_event_data_none() );
	}
}

/** Get the source producer.
 *<commit_msg>Preserve the producer creation time when creating a chain<commit_after>/**
 * \file mlt_chain.c
 * \brief link service class
 * \see mlt_chain_s
 *
 * Copyright (C) 2020 Meltytech, LLC
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "mlt_chain.h"
#include "mlt_factory.h"
#include "mlt_frame.h"
#include "mlt_log.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/** \brief private service definition */

typedef struct
{
	int link_count;
	int link_size;
	mlt_link* links;
	mlt_producer source;
	mlt_profile source_profile;
	mlt_properties source_parameters;
	mlt_producer begin;
}
mlt_chain_base;

/* Forward references to static methods.
*/

static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
static void relink_chain( mlt_chain self );
static void chain_property_changed( mlt_service owner, mlt_chain self, char *name );
static void source_property_changed( mlt_service owner, mlt_chain self, char *name );

/** Construct a chain.
 *
 * \public \memberof mlt_chain_s
 * \return the new chain
 */

mlt_chain mlt_chain_init( mlt_profile profile )
{
	mlt_chain self = calloc( 1, sizeof( struct mlt_chain_s ) );
	if ( self != NULL )
	{
		mlt_producer producer = &self->parent;
		if ( mlt_producer_init( producer, self ) == 0 )
		{
			mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );

			mlt_properties_set( properties, "mlt_type", "chain" );
			mlt_properties_clear( properties, "resource");
			mlt_properties_clear( properties, "mlt_service");
			mlt_properties_clear( properties, "in");
			mlt_properties_clear( properties, "out");
			mlt_properties_clear( properties, "length");

			producer->get_frame = producer_get_frame;
			producer->close = ( mlt_destructor )mlt_chain_close;
			producer->close_object = self;

			mlt_service_set_profile( MLT_CHAIN_SERVICE( self ), profile );

			// Generate local space
			self->local = calloc( 1, sizeof( mlt_chain_base ) );
			mlt_chain_base* base = self->local;
			base->source_profile = NULL;

			// Listen to property changes to pass along to the source
			mlt_events_listen( MLT_CHAIN_PROPERTIES(self), self, "property-changed", ( mlt_listener )chain_property_changed );
		}
		else
		{
			free( self );
			self = NULL;
		}
	}
	return self;
}

/** Set the source producer.
 *
 * \public \memberof mlt_chain_s
 * \param self a chain
 * \param source the new source producer
 */

void mlt_chain_set_source( mlt_chain self, mlt_producer source )
{
	int error = self == NULL || source == NULL;
	if ( error == 0 )
	{
		mlt_chain_base* base = self->local;
		mlt_properties source_properties = MLT_PRODUCER_PROPERTIES( source );
		int n = 0;
		int i = 0;

		// Clean up from previous source
		mlt_producer_close( base->source );
		mlt_properties_close( base->source_parameters );
		mlt_profile_close( base->source_profile );

		// Save the source producer
		base->source = source;
		mlt_properties_inc_ref( source_properties );

		// Create a list of all parameters used by the source producer so that
		// they can be passed between the source producer and this chain.
		base->source_parameters = mlt_properties_new();
		mlt_repository repository = mlt_factory_repository();
		char* source_metadata_name = strdup( mlt_properties_get( source_properties, "mlt_service" ) );
		// If the service name ends in "-novalidate", then drop the ending and search for the metadata of the main service.
		// E.g. "avformat-novalidate" -> "avformat"
		char* novalidate_ptr = strstr(source_metadata_name, "-novalidate");
		if ( novalidate_ptr ) *novalidate_ptr = '\0';
		mlt_properties source_metadata = mlt_repository_metadata( repository, mlt_service_producer_type, source_metadata_name );
		free( source_metadata_name );
		if ( source_metadata )
		{
			mlt_properties params = (mlt_properties) mlt_properties_get_data( source_metadata, "parameters", NULL );
			if ( params )
			{
				n = mlt_properties_count( params );
				for ( i = 0; i < n; i++ )
				{
					mlt_properties param = (mlt_properties) mlt_properties_get_data( params, mlt_properties_get_name( params, i ), NULL );
					char* identifier = mlt_properties_get( param, "identifier" );
					if ( identifier )
					{
						// Set the value to 1 to indicate the parameter exists.
						mlt_properties_set_int( base->source_parameters, identifier, 1 );
					}
				}
			}
		}

		// Pass parameters and properties from the source producer to this chain.
		// Some properties may have been set during source initialization.
		n = mlt_properties_count( source_properties );
		mlt_events_block( MLT_CHAIN_PROPERTIES(self), self );
		for ( i = 0; i < n; i++ )
		{
			char* name = mlt_properties_get_name( source_properties, i );
			if ( mlt_properties_get_int( base->source_parameters, name ) ||
				 !strcmp( name, "mlt_service" ) ||
				 !strcmp( name, "_mlt_service_hidden" ) ||
				 !strcmp( name, "seekable" ) ||
				 !strcmp( name, "creation_time" ) ||
				 !strncmp( name, "meta.", 5 ) )
			{
				mlt_properties_pass_property( MLT_CHAIN_PROPERTIES(self), source_properties, name );
			}
		}
		// If a length has not been specified for this chain, copy in/out/length from the source producer
		if ( !mlt_producer_get_length( MLT_CHAIN_PRODUCER(self) ) ) {
			mlt_properties_set_position( MLT_CHAIN_PROPERTIES(self), "length", mlt_producer_get_length( base->source ) );
			mlt_producer_set_in_and_out( MLT_CHAIN_PRODUCER(self), mlt_producer_get_in( base->source ), mlt_producer_get_out( base->source ) );
		}
		mlt_events_unblock( MLT_CHAIN_PROPERTIES(self), self );

		// Monitor property changes from the source to pass to the chain.
		mlt_events_listen( source_properties, self, "property-changed", ( mlt_listener )source_property_changed );

		// This chain will control the speed and in/out
		mlt_producer_set_speed( base->source, 0.0 );
		// Approximate infinite length
		mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( base->source ), "length", 0x7fffffff );
		mlt_producer_set_in_and_out( base->source, 0, mlt_producer_get_length( base->source ) - 1 );

		// Reconfigure the chain
		relink_chain( self );
		mlt_events_fire( MLT_CHAIN_PROPERTIES(self), "chain-changed", mlt_event_data_none() );
	}
}

/** Get the source producer.
 *
{'fraction_non_alphanumeric': 0.09980330735047716, 'fraction_numerical': 0.005099439061703213, 'mean_word_length': 4.233701868089973, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2068702', 'n_tokens_mistral': 4910, 'n_tokens_neox': 4467, 'n_words': 1287}