mlt_multitrack mlt_multitrack_init( ) { // Allocate the multitrack object mlt_multitrack this = calloc( sizeof( struct mlt_multitrack_s ), 1 ); if ( this != NULL ) { mlt_producer producer = &this->parent; if ( mlt_producer_init( producer, this ) == 0 ) { mlt_properties properties = MLT_MULTITRACK_PROPERTIES( this ); producer->get_frame = producer_get_frame; mlt_properties_set_data( properties, "multitrack", this, 0, NULL, NULL ); mlt_properties_set( properties, "log_id", "multitrack" ); mlt_properties_set( properties, "resource", "<multitrack>" ); mlt_properties_set_int( properties, "in", 0 ); mlt_properties_set_int( properties, "out", -1 ); mlt_properties_set_int( properties, "length", 0 ); producer->close = ( mlt_destructor )mlt_multitrack_close; } else { free( this ); this = NULL; } } return this; }
mlt_producer producer_vorbis_init( mlt_profile profile, mlt_service_type type, const char *id, char *file ) { mlt_producer this = NULL; // Check that we have a non-NULL argument if ( file != NULL ) { // Construct the producer this = calloc( 1, sizeof( struct mlt_producer_s ) ); // Initialise it if ( mlt_producer_init( this, NULL ) == 0 ) { // Get the properties mlt_properties properties = MLT_PRODUCER_PROPERTIES( this ); // Set the resource property (required for all producers) mlt_properties_set( properties, "resource", file ); // Register our get_frame implementation this->get_frame = producer_get_frame; // Open the file if ( producer_open( this, profile, file ) != 0 ) { // Clean up mlt_producer_close( this ); this = NULL; } } } return this; }
mlt_multitrack mlt_multitrack_init( ) { // Allocate the multitrack object mlt_multitrack self = calloc( 1, sizeof( struct mlt_multitrack_s ) ); if ( self != NULL ) { mlt_producer producer = &self->parent; if ( mlt_producer_init( producer, self ) == 0 ) { mlt_properties properties = MLT_MULTITRACK_PROPERTIES( self ); producer->get_frame = producer_get_frame; mlt_properties_set_data( properties, "multitrack", self, 0, NULL, NULL ); mlt_properties_set( properties, "log_id", "multitrack" ); mlt_properties_set( properties, "resource", "<multitrack>" ); mlt_properties_set_int( properties, "in", 0 ); mlt_properties_set_int( properties, "out", -1 ); mlt_properties_set_int( properties, "length", 0 ); producer->close = ( mlt_destructor )mlt_multitrack_close; } else { free( self ); self = NULL; } } return self; }
mlt_producer producer_ppm_init( mlt_profile profile, mlt_service_type type, const char *id, char *command ) { producer_ppm this = calloc( sizeof( struct producer_ppm_s ), 1 ); if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 ) { mlt_producer producer = &this->parent; mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )producer_close; if ( command != NULL ) { mlt_properties_set( properties, "resource", command ); this->command = strdup( command ); } else { mlt_properties_set( properties, "resource", "ppm test" ); } return producer; } free( this ); return NULL; }
mlt_tractor mlt_tractor_init( ) { mlt_tractor self = calloc( 1, sizeof( struct mlt_tractor_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, "resource", "<tractor>" ); mlt_properties_set( properties, "mlt_type", "mlt_producer" ); mlt_properties_set( properties, "mlt_service", "tractor" ); mlt_properties_set_int( properties, "in", 0 ); mlt_properties_set_int( properties, "out", -1 ); mlt_properties_set_int( properties, "length", 0 ); producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )mlt_tractor_close; producer->close_object = self; } else { free( self ); self = NULL; } } return self; }
mlt_tractor mlt_tractor_new( ) { mlt_tractor self = calloc( 1, sizeof( struct mlt_tractor_s ) ); if ( self != NULL ) { mlt_producer producer = &self->parent; if ( mlt_producer_init( producer, self ) == 0 ) { mlt_multitrack multitrack = mlt_multitrack_init( ); mlt_field field = mlt_field_new( multitrack, self ); mlt_properties props = MLT_PRODUCER_PROPERTIES( producer ); mlt_properties_set( props, "resource", "<tractor>" ); mlt_properties_set( props, "mlt_type", "mlt_producer" ); mlt_properties_set( props, "mlt_service", "tractor" ); mlt_properties_set_position( props, "in", 0 ); mlt_properties_set_position( props, "out", 0 ); mlt_properties_set_position( props, "length", 0 ); mlt_properties_set_data( props, "multitrack", multitrack, 0, ( mlt_destructor )mlt_multitrack_close, NULL ); mlt_properties_set_data( props, "field", field, 0, ( mlt_destructor )mlt_field_close, NULL ); mlt_events_listen( MLT_MULTITRACK_PROPERTIES( multitrack ), self, "producer-changed", ( mlt_listener )mlt_tractor_listener ); producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )mlt_tractor_close; producer->close_object = self; } else { free( self ); self = NULL; } } return self; }
mlt_producer mlt_producer_new( mlt_profile profile ) { mlt_producer self = malloc( sizeof( struct mlt_producer_s ) ); if ( self ) { if ( mlt_producer_init( self, NULL ) == 0 ) { mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( self ), "_profile", profile, 0, NULL, NULL ); mlt_properties_set_double( MLT_PRODUCER_PROPERTIES( self ), "aspect_ratio", mlt_profile_sar( profile ) ); } } return self; }
mlt_producer producer_qimage_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename ) { producer_qimage self = calloc( sizeof( struct producer_qimage_s ), 1 ); if ( self != NULL && mlt_producer_init( &self->parent, self ) == 0 ) { mlt_producer producer = &self->parent; // Get the properties interface mlt_properties properties = MLT_PRODUCER_PROPERTIES( &self->parent ); // Callback registration #ifdef USE_KDE init_qimage(); #endif producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )producer_close; // Set the default properties mlt_properties_set( properties, "resource", filename ); mlt_properties_set_int( properties, "ttl", 25 ); mlt_properties_set_int( properties, "aspect_ratio", 1 ); mlt_properties_set_int( properties, "progressive", 1 ); mlt_properties_set_int( properties, "seekable", 1 ); // Validate the resource if ( filename ) load_filenames( self, properties ); if ( self->count ) { mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); if ( frame ) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); mlt_properties_set_data( frame_properties, "producer_qimage", self, 0, NULL, NULL ); mlt_frame_set_position( frame, mlt_producer_position( producer ) ); mlt_properties_set_position( frame_properties, "qimage_position", mlt_producer_position( producer ) ); refresh_qimage( self, frame ); mlt_cache_item_close( self->qimage_cache ); mlt_frame_close( frame ); } } if ( self->current_width == 0 ) { producer_close( producer ); producer = NULL; } return producer; } free( self ); return NULL; }
mlt_producer producer_pixbuf_init( char *filename ) { producer_pixbuf self = calloc( 1, sizeof( struct producer_pixbuf_s ) ); if ( self != NULL && mlt_producer_init( &self->parent, self ) == 0 ) { mlt_producer producer = &self->parent; // Get the properties interface mlt_properties properties = MLT_PRODUCER_PROPERTIES( &self->parent ); // Callback registration producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )producer_close; // Set the default properties mlt_properties_set( properties, "resource", filename ); mlt_properties_set_int( properties, "ttl", 25 ); mlt_properties_set_int( properties, "aspect_ratio", 1 ); mlt_properties_set_int( properties, "progressive", 1 ); mlt_properties_set_int( properties, "seekable", 1 ); mlt_properties_set_int( properties, "loop", 1 ); // Validate the resource if ( filename ) load_filenames( self, properties ); if ( self->count ) { mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); if ( frame ) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); mlt_properties_set_data( frame_properties, "producer_pixbuf", self, 0, NULL, NULL ); mlt_frame_set_position( frame, mlt_producer_position( producer ) ); refresh_pixbuf( self, frame ); mlt_cache_item_close( self->pixbuf_cache ); mlt_frame_close( frame ); } } if ( self->width == 0 ) { producer_close( producer ); producer = NULL; } return producer; } free( self ); return NULL; }
mlt_producer producer_pixbuf_init( char *filename ) { producer_pixbuf this = calloc( sizeof( struct producer_pixbuf_s ), 1 ); if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 ) { mlt_producer producer = &this->parent; // Get the properties interface mlt_properties properties = MLT_PRODUCER_PROPERTIES( &this->parent ); // Callback registration producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )producer_close; // Set the default properties mlt_properties_set( properties, "resource", filename ); mlt_properties_set_int( properties, "ttl", 25 ); mlt_properties_set_int( properties, "aspect_ratio", 1 ); mlt_properties_set_int( properties, "progressive", 1 ); // Validate the resource if ( filename ) load_filenames( this, properties ); if ( this->count ) { mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); if ( frame ) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); pthread_mutex_init( &this->mutex, NULL ); mlt_properties_set_data( frame_properties, "producer_pixbuf", this, 0, NULL, NULL ); mlt_frame_set_position( frame, mlt_producer_position( producer ) ); mlt_properties_set_position( frame_properties, "pixbuf_position", mlt_producer_position( producer ) ); refresh_image( this, frame, 0, 0 ); mlt_frame_close( frame ); } } if ( this->width == 0 ) { producer_close( producer ); producer = NULL; } return producer; } free( this ); return NULL; }
mlt_producer producer_kino_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename ) { kino_wrapper wrapper = kino_wrapper_init( ); if ( kino_wrapper_open( wrapper, filename ) ) { producer_kino this = calloc( 1, sizeof( struct producer_kino_s ) ); if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 ) { mlt_producer producer = &this->parent; mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); double fps = kino_wrapper_is_pal( wrapper ) ? 25 : 30000.0 / 1001.0; // Assign the wrapper this->wrapper = wrapper; // Pass wrapper properties (frame rate, count etc) mlt_properties_set_position( properties, "length", kino_wrapper_get_frame_count( wrapper ) ); mlt_properties_set_position( properties, "in", 0 ); mlt_properties_set_position( properties, "out", kino_wrapper_get_frame_count( wrapper ) - 1 ); mlt_properties_set_double( properties, "real_fps", fps ); mlt_properties_set( properties, "resource", filename ); // Register transport implementation with the producer producer->close = ( mlt_destructor )producer_close; // Register our get_frame implementation with the producer producer->get_frame = producer_get_frame; // Return the producer return producer; } free( this ); } kino_wrapper_close( wrapper ); return NULL; }
mlt_producer producer_colour_init( mlt_profile profile, mlt_service_type type, const char *id, char *colour ) { mlt_producer producer = calloc( 1, sizeof( struct mlt_producer_s ) ); if ( producer != NULL && mlt_producer_init( producer, NULL ) == 0 ) { // Get the properties interface mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); // Callback registration producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )producer_close; // Set the default properties mlt_properties_set( properties, "resource", ( !colour || !strcmp( colour, "" ) ) ? "0x000000ff" : colour ); mlt_properties_set( properties, "_resource", "" ); mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) ); return producer; } free( producer ); return NULL; }
mlt_producer producer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Allocate the producer DeckLinkProducer* decklink = new DeckLinkProducer(); mlt_producer producer = (mlt_producer) calloc( 1, sizeof( *producer ) ); // If allocated and initializes if ( decklink && !mlt_producer_init( producer, decklink ) ) { if ( decklink->open( arg? atoi( arg ) : 0 ) ) { mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); // Close DeckLink and defer re-open to get_frame delete decklink; producer->child = NULL; // Set callbacks producer->close = (mlt_destructor) producer_close; producer->get_frame = get_frame; // Set properties mlt_properties_set( properties, "resource", (arg && strcmp( arg, ""))? arg : "0" ); mlt_properties_set_int( properties, "channels", 2 ); mlt_properties_set_int( properties, "buffer", 25 ); mlt_properties_set_int( properties, "prefill", 25 ); // These properties effectively make it infinite. mlt_properties_set_int( properties, "length", INT_MAX ); mlt_properties_set_int( properties, "out", INT_MAX - 1 ); mlt_properties_set( properties, "eof", "loop" ); mlt_event event = mlt_events_listen( properties, properties, "property-changed", (mlt_listener) on_property_changed ); mlt_properties_set_data( properties, "list-devices-event", event, 0, NULL, NULL ); } } return producer; }
mlt_producer producer_kdenlivetitle_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename ) { /* fprintf(stderr, ":::::::::::: CREATE TITLE\n"); */ /* Create a new producer object */ producer_ktitle this = calloc( sizeof( struct producer_ktitle_s ), 1 ); if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 ) { mlt_producer producer = &this->parent; /* Get the properties interface */ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); /* Callback registration */ producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )producer_close; mlt_properties_set( properties, "resource", filename ); //mlt_properties_set_int( properties, "aspect_ratio", 1 ); read_xml(properties); return producer; } free( this ); return NULL; }
mlt_producer producer_kdenlivetitle_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename ) { /* Create a new producer object */ producer_ktitle self = calloc( 1, sizeof( struct producer_ktitle_s ) ); if ( self != NULL && mlt_producer_init( &self->parent, self ) == 0 ) { mlt_producer producer = &self->parent; /* Get the properties interface */ mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); /* Callback registration */ producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )producer_close; mlt_properties_set( properties, "resource", filename ); mlt_properties_set_int( properties, "progressive", 1 ); mlt_properties_set_int( properties, "aspect_ratio", 1 ); mlt_properties_set_int( properties, "seekable", 1 ); read_xml(properties); return producer; } free( self ); return NULL; }
mlt_producer producer_framebuffer_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { if ( !arg ) return NULL; mlt_producer producer = NULL; producer = calloc( 1, sizeof( struct mlt_producer_s ) ); mlt_producer_init( producer, NULL ); // Wrap loader mlt_producer real_producer; // Check if a speed was specified. /** * Speed must be appended to the filename with '?'. To play your video at 50%: melt framebuffer:my_video.mpg?0.5 * Stroboscope effect can be obtained by adding a stobe=x parameter, where x is the number of frames that will be ignored. * You can play the movie backwards by adding reverse=1 * You can freeze the clip at a determined position by adding freeze=frame_pos add freeze_after=1 to freeze only paste position or freeze_before to freeze before it **/ double speed = 0.0; char *props = strdup( arg ); char *ptr = strrchr( props, '?' ); if ( ptr ) { speed = atof( ptr + 1 ); if ( speed != 0.0 ) // If speed was valid, then strip it and the delimiter. // Otherwise, an invalid speed probably means this '?' was not a delimiter. *ptr = '\0'; } real_producer = mlt_factory_producer( profile, "abnormal", props ); free( props ); if (speed == 0.0) speed = 1.0; if ( producer != NULL && real_producer != NULL) { // Get the properties of this producer mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); mlt_properties_set( properties, "resource", arg); // Store the producer and fitler mlt_properties_set_data( properties, "producer", real_producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); // Grab some stuff from the real_producer mlt_properties_pass_list( properties, MLT_PRODUCER_PROPERTIES( real_producer ), "length, width, height, aspect_ratio" ); if ( speed < 0 ) { speed = -speed; mlt_properties_set_int( properties, "reverse", 1 ); } if ( speed != 1.0 ) { double real_length = ( (double) mlt_producer_get_length( real_producer ) ) / speed; mlt_properties_set_position( properties, "length", real_length ); } mlt_properties_set_position( properties, "out", mlt_producer_get_length( producer ) - 1 ); // Since we control the seeking, prevent it from seeking on its own mlt_producer_set_speed( real_producer, 0 ); mlt_producer_set_speed( producer, speed ); // Override the get_frame method producer->get_frame = producer_get_frame; } else { if ( producer ) mlt_producer_close( producer ); if ( real_producer ) mlt_producer_close( real_producer ); producer = NULL; } return producer; }
mlt_producer producer_pango_init( const char *filename ) { producer_pango this = calloc( sizeof( struct producer_pango_s ), 1 ); if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 ) { mlt_producer producer = &this->parent; pthread_mutex_lock( &pango_mutex ); if ( fontmap == NULL ) fontmap = (PangoFT2FontMap*) pango_ft2_font_map_new(); g_type_init(); pthread_mutex_unlock( &pango_mutex ); producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )producer_close; // Get the properties interface mlt_properties properties = MLT_PRODUCER_PROPERTIES( &this->parent ); // Set the default properties mlt_properties_set( properties, "fgcolour", "0xffffffff" ); mlt_properties_set( properties, "bgcolour", "0x00000000" ); mlt_properties_set( properties, "olcolour", "0x00000000" ); mlt_properties_set_int( properties, "align", pango_align_left ); mlt_properties_set_int( properties, "pad", 0 ); mlt_properties_set_int( properties, "outline", 0 ); mlt_properties_set( properties, "text", "" ); mlt_properties_set( properties, "font", NULL ); mlt_properties_set( properties, "family", "Sans" ); mlt_properties_set_int( properties, "size", 48 ); mlt_properties_set( properties, "style", "normal" ); mlt_properties_set( properties, "encoding", "UTF-8" ); mlt_properties_set_int( properties, "weight", PANGO_WEIGHT_NORMAL ); mlt_properties_set_int( properties, "seekable", 1 ); if ( filename == NULL || ( filename && ( !strcmp( filename, "" ) // workaround for old kdenlive countdown generator || strstr( filename, "<producer>" ) ) ) ) { mlt_properties_set( properties, "markup", "" ); } else if ( filename[ 0 ] == '+' || strstr( filename, "/+" ) ) { char *copy = strdup( filename + 1 ); char *markup = copy; if ( strstr( markup, "/+" ) ) markup = strstr( markup, "/+" ) + 2; ( *strrchr( markup, '.' ) ) = '\0'; while ( strchr( markup, '~' ) ) ( *strchr( markup, '~' ) ) = '\n'; mlt_properties_set( properties, "resource", filename ); mlt_properties_set( properties, "markup", markup ); free( copy ); } else if ( strstr( filename, ".mpl" ) ) { int i = 0; mlt_properties contents = mlt_properties_load( filename ); mlt_geometry key_frames = mlt_geometry_init( ); struct mlt_geometry_item_s item; mlt_properties_set( properties, "resource", filename ); mlt_properties_set_data( properties, "contents", contents, 0, ( mlt_destructor )mlt_properties_close, NULL ); mlt_properties_set_data( properties, "key_frames", key_frames, 0, ( mlt_destructor )mlt_geometry_close, NULL ); // Make sure we have at least one entry if ( mlt_properties_get( contents, "0" ) == NULL ) mlt_properties_set( contents, "0", "" ); for ( i = 0; i < mlt_properties_count( contents ); i ++ ) { char *name = mlt_properties_get_name( contents, i ); char *value = mlt_properties_get_value( contents, i ); while ( value != NULL && strchr( value, '~' ) ) ( *strchr( value, '~' ) ) = '\n'; item.frame = atoi( name ); mlt_geometry_insert( key_frames, &item ); } mlt_geometry_interpolate( key_frames ); } else { FILE *f = fopen( filename, "r" ); if ( f != NULL ) { char line[81]; char *markup = NULL; size_t size = 0; line[80] = '\0'; while ( fgets( line, 80, f ) ) { size += strlen( line ) + 1; if ( markup ) { markup = realloc( markup, size ); strcat( markup, line ); } else { markup = strdup( line ); } } fclose( f ); if ( markup[ strlen( markup ) - 1 ] == '\n' ) markup[ strlen( markup ) - 1 ] = '\0'; mlt_properties_set( properties, "resource", filename ); mlt_properties_set( properties, "markup", ( markup == NULL ? "" : markup ) ); free( markup ); } else { producer->close = NULL; mlt_producer_close( producer ); producer = NULL; free( this ); } } return producer; } free( this ); return NULL; }
mlt_producer producer_avformat_init( mlt_profile profile, char *file ) { int error = 0; // Report information about available demuxers and codecs as YAML Tiny if ( file && strstr( file, "f-list" ) ) { fprintf( stderr, "---\nformats:\n" ); AVInputFormat *format = NULL; while ( ( format = av_iformat_next( format ) ) ) fprintf( stderr, " - %s\n", format->name ); fprintf( stderr, "...\n" ); error = 1; } if ( file && strstr( file, "acodec-list" ) ) { fprintf( stderr, "---\naudio_codecs:\n" ); AVCodec *codec = NULL; while ( ( codec = av_codec_next( codec ) ) ) if ( codec->decode && codec->type == CODEC_TYPE_AUDIO ) fprintf( stderr, " - %s\n", codec->name ); fprintf( stderr, "...\n" ); error = 1; } if ( file && strstr( file, "vcodec-list" ) ) { fprintf( stderr, "---\nvideo_codecs:\n" ); AVCodec *codec = NULL; while ( ( codec = av_codec_next( codec ) ) ) if ( codec->decode && codec->type == CODEC_TYPE_VIDEO ) fprintf( stderr, " - %s\n", codec->name ); fprintf( stderr, "...\n" ); error = 1; } if ( error ) return NULL; mlt_producer this = NULL; // Check that we have a non-NULL argument if ( file != NULL ) { // Construct the producer this = calloc( 1, sizeof( struct mlt_producer_s ) ); // Initialise it if ( mlt_producer_init( this, NULL ) == 0 ) { // Get the properties mlt_properties properties = MLT_PRODUCER_PROPERTIES( this ); // Set the resource property (required for all producers) mlt_properties_set( properties, "resource", file ); // Register our get_frame implementation this->get_frame = producer_get_frame; // Open the file if ( producer_open( this, profile, file ) != 0 ) { // Clean up mlt_producer_close( this ); this = NULL; } else { // Close the file to release resources for large playlists - reopen later as needed mlt_properties_set_data( properties, "dummy_context", NULL, 0, NULL, NULL ); mlt_properties_set_data( properties, "audio_context", NULL, 0, NULL, NULL ); mlt_properties_set_data( properties, "video_context", NULL, 0, NULL, NULL ); // Default the user-selectable indices from the auto-detected indices mlt_properties_set_int( properties, "audio_index", mlt_properties_get_int( properties, "_audio_index" ) ); mlt_properties_set_int( properties, "video_index", mlt_properties_get_int( properties, "_video_index" ) ); } } } return this; }