static void get_filedate_str( mlt_filter filter, mlt_frame frame, char* text ) { mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); char* filename = mlt_properties_get( producer_properties, "resource"); struct stat file_info; if( !stat(filename, &file_info)) { struct tm* time_info = gmtime( &(file_info.st_mtime) ); char date[11] = ""; strftime( date, 11, "%Y/%m/%d", time_info ); strncat( text, date, MAX_TEXT_LEN - strlen( text ) - 1); } }
static void mlt_producer_set_clones( mlt_producer self, int clones ) { mlt_producer parent = mlt_producer_cut_parent( self ); mlt_properties properties = MLT_PRODUCER_PROPERTIES( parent ); int existing = mlt_properties_get_int( properties, "_clones" ); int i = 0; char key[ 25 ]; // If the number of existing clones is different, then create/remove as necessary if ( existing != clones ) { if ( existing < clones ) { for ( i = existing; i < clones; i ++ ) { mlt_producer clone = mlt_producer_clone( parent ); sprintf( key, "_clone.%d", i ); mlt_properties_set_data( properties, key, clone, 0, ( mlt_destructor )mlt_producer_close, NULL ); } } else { for ( i = clones; i < existing; i ++ ) { sprintf( key, "_clone.%d", i ); mlt_properties_set_data( properties, key, NULL, 0, NULL, NULL ); } } } // Ensure all properties on the parent are passed to the clones for ( i = 0; i < clones; i ++ ) { mlt_producer clone = NULL; sprintf( key, "_clone.%d", i ); clone = mlt_properties_get_data( properties, key, NULL ); if ( clone != NULL ) mlt_properties_pass( MLT_PRODUCER_PROPERTIES( clone ), properties, "" ); } // Update the number of clones on the properties mlt_properties_set_int( properties, "_clones", clones ); }
mlt_position mlt_filter_get_length2( mlt_filter self, mlt_frame frame ) { mlt_properties properties = MLT_SERVICE_PROPERTIES( &self->parent ); mlt_position in = mlt_properties_get_position( properties, "in" ); mlt_position out = mlt_properties_get_position( properties, "out" ); if ( out == 0 && frame ) { // If always active, use the frame's producer mlt_producer producer = mlt_frame_get_original_producer( frame ); if ( producer ) { producer = mlt_producer_cut_parent( producer ); in = mlt_producer_get_in( producer ); out = mlt_producer_get_out( producer ); } } return ( out > 0 ) ? ( out - in + 1 ) : 0; }
mlt_producer mlt_producer_cut( mlt_producer self, int in, int out ) { mlt_producer result = mlt_producer_new( mlt_service_profile( MLT_PRODUCER_SERVICE( self ) ) ); mlt_producer parent = mlt_producer_cut_parent( self ); mlt_properties properties = MLT_PRODUCER_PROPERTIES( result ); mlt_properties parent_props = MLT_PRODUCER_PROPERTIES( parent ); mlt_events_block( MLT_PRODUCER_PROPERTIES( result ), MLT_PRODUCER_PROPERTIES( result ) ); // Special case - allow for a cut of the entire producer (this will squeeze all other cuts to 0) if ( in <= 0 ) in = 0; if ( ( out < 0 || out >= mlt_producer_get_length( parent ) ) && !mlt_producer_is_blank( self ) ) out = mlt_producer_get_length( parent ) - 1; mlt_properties_inc_ref( parent_props ); mlt_properties_set_int( properties, "_cut", 1 ); mlt_properties_set_data( properties, "_cut_parent", parent, 0, ( mlt_destructor )mlt_producer_close, NULL ); mlt_properties_set_position( properties, "length", mlt_properties_get_position( parent_props, "length" ) ); mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( parent_props, "aspect_ratio" ) ); mlt_producer_set_in_and_out( result, in, out ); return result; }
static int producer_get_frame( mlt_service service, mlt_frame_ptr frame, int index ) { int result = 1; mlt_producer self = service != NULL ? service->child : NULL; if ( self != NULL && !mlt_producer_is_cut( self ) ) { // Get the properties of this producer mlt_properties properties = MLT_PRODUCER_PROPERTIES( self ); // Determine eof handling char *eof = mlt_properties_get( MLT_PRODUCER_PROPERTIES( self ), "eof" ); // Get the speed of the producer double speed = mlt_producer_get_speed( self ); // We need to use the clone if it's specified mlt_producer clone = mlt_properties_get_data( properties, "use_clone", NULL ); // If no clone is specified, use self clone = clone == NULL ? self : clone; // A properly instatiated producer will have a get_frame method... if ( self->get_frame == NULL || ( eof && !strcmp( eof, "continue" ) && mlt_producer_position( self ) > mlt_producer_get_out( self ) ) ) { // Generate a test frame *frame = mlt_frame_init( service ); // Set the position result = mlt_frame_set_position( *frame, mlt_producer_position( self ) ); // Mark as a test card mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_image", 1 ); mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_audio", 1 ); // Calculate the next position mlt_producer_prepare_next( self ); } else { // Get the frame from the implementation result = self->get_frame( clone, frame, index ); } // Copy the fps and speed of the producer onto the frame properties = MLT_FRAME_PROPERTIES( *frame ); mlt_properties_set_double( properties, "_speed", speed ); mlt_properties_set_int( properties, "test_audio", mlt_frame_is_test_audio( *frame ) ); mlt_properties_set_int( properties, "test_image", mlt_frame_is_test_card( *frame ) ); if ( mlt_properties_get_data( properties, "_producer", NULL ) == NULL ) mlt_properties_set_data( properties, "_producer", service, 0, NULL, NULL ); } else if ( self != NULL ) { // Get the speed of the cut double speed = mlt_producer_get_speed( self ); // Get the parent of the cut mlt_producer parent = mlt_producer_cut_parent( self ); // Get the properties of the parent mlt_properties parent_properties = MLT_PRODUCER_PROPERTIES( parent ); // Get the properties of the cut mlt_properties properties = MLT_PRODUCER_PROPERTIES( self ); // Determine the clone index int clone_index = mlt_properties_get_int( properties, "_clone" ); // Determine the clone to use mlt_producer clone = self; if ( clone_index > 0 ) { char key[ 25 ]; sprintf( key, "_clone.%d", clone_index - 1 ); clone = mlt_properties_get_data( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( self ) ), key, NULL ); if ( clone == NULL ) mlt_log( service, MLT_LOG_ERROR, "requested clone doesn't exist %d\n", clone_index ); clone = clone == NULL ? self : clone; } else { clone = parent; } // We need to seek to the correct position in the clone mlt_producer_seek( clone, mlt_producer_get_in( self ) + mlt_properties_get_int( properties, "_position" ) ); // Assign the clone property to the parent mlt_properties_set_data( parent_properties, "use_clone", clone, 0, NULL, NULL ); // Now get the frame from the parents service result = mlt_service_get_frame( MLT_PRODUCER_SERVICE( parent ), frame, index ); // We're done with the clone now mlt_properties_set_data( parent_properties, "use_clone", NULL, 0, NULL, NULL ); // This is useful and required by always_active transitions to determine in/out points of the cut if ( mlt_properties_get_data( MLT_FRAME_PROPERTIES( *frame ), "_producer", NULL ) == MLT_PRODUCER_SERVICE( parent ) ) mlt_properties_set_data( MLT_FRAME_PROPERTIES( *frame ), "_producer", self, 0, NULL, NULL ); mlt_properties_set_double( MLT_FRAME_PROPERTIES( *frame ), "_speed", speed ); mlt_producer_prepare_next( self ); } else { *frame = mlt_frame_init( service ); result = 0; } // Pass on all meta properties from the producer/cut on to the frame if ( *frame != NULL && self != NULL ) { int i = 0; mlt_properties p_props = MLT_PRODUCER_PROPERTIES( self ); mlt_properties f_props = MLT_FRAME_PROPERTIES( *frame ); mlt_properties_lock( p_props ); int count = mlt_properties_count( p_props ); for ( i = 0; i < count; i ++ ) { char *name = mlt_properties_get_name( p_props, i ); if ( !strncmp( name, "meta.", 5 ) ) mlt_properties_set( f_props, name, mlt_properties_get_value( p_props, i ) ); else if ( !strncmp( name, "set.", 4 ) ) mlt_properties_set( f_props, name + 4, mlt_properties_get_value( p_props, i ) ); } mlt_properties_unlock( p_props ); } return result; }
static void mlt_producer_service_changed( mlt_service owner, mlt_producer self ) { mlt_events_fire( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( self ) ), "producer-changed", NULL ); }
static void mlt_producer_property_changed( mlt_service owner, mlt_producer self, char *name ) { if ( !strcmp( name, "in" ) || !strcmp( name, "out" ) || !strcmp( name, "length" ) ) mlt_events_fire( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( self ) ), "producer-changed", NULL ); }
void GlslManager::unlock_service( mlt_frame frame ) { Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) ); producer.unlock(); }
Effect* GlslManager::get_effect( mlt_filter filter, mlt_frame frame ) { Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) ); char *unique_id = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "_unique_id" ); return (Effect*) GlslManager::get_instance()->effect_list( producer ).get_data( unique_id ); }
static void get_resource_str( mlt_filter filter, mlt_frame frame, char* text ) { mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); strncat( text, mlt_properties_get( producer_properties, "resource" ), MAX_TEXT_LEN - strlen( text ) - 1 ); }
unsigned char *mlt_frame_get_waveform( mlt_frame self, int w, int h ) { int16_t *pcm = NULL; mlt_properties properties = MLT_FRAME_PROPERTIES( self ); mlt_audio_format format = mlt_audio_s16; int frequency = 16000; int channels = 2; mlt_producer producer = mlt_frame_get_original_producer( self ); double fps = mlt_producer_get_fps( mlt_producer_cut_parent( producer ) ); int samples = mlt_sample_calculator( fps, frequency, mlt_frame_get_position( self ) ); // Increase audio resolution proportional to requested image size while ( samples < w ) { frequency += 16000; samples = mlt_sample_calculator( fps, frequency, mlt_frame_get_position( self ) ); } // Get the pcm data mlt_frame_get_audio( self, (void**)&pcm, &format, &frequency, &channels, &samples ); // Make an 8-bit buffer large enough to hold rendering int size = w * h; if ( size <= 0 ) return NULL; unsigned char *bitmap = ( unsigned char* )mlt_pool_alloc( size ); if ( bitmap != NULL ) memset( bitmap, 0, size ); else return NULL; mlt_properties_set_data( properties, "waveform", bitmap, size, ( mlt_destructor )mlt_pool_release, NULL ); // Render vertical lines int16_t *ubound = pcm + samples * channels; int skip = samples / w; skip = !skip ? 1 : skip; unsigned char gray = 0xFF / skip; int i, j, k; // Iterate sample stream and along x coordinate for ( i = 0; pcm < ubound; i++ ) { // pcm data has channels interleaved for ( j = 0; j < channels; j++, pcm++ ) { // Determine sample's magnitude from 2s complement; int pcm_magnitude = *pcm < 0 ? ~(*pcm) + 1 : *pcm; // The height of a line is the ratio of the magnitude multiplied by // the vertical resolution of a single channel int height = h * pcm_magnitude / channels / 2 / 32768; // Determine the starting y coordinate - left top, right bottom int displacement = h * (j * 2 + 1) / channels / 2 - ( *pcm < 0 ? 0 : height ); // Position buffer pointer using y coordinate, stride, and x coordinate unsigned char *p = bitmap + i / skip + displacement * w; // Draw vertical line for ( k = 0; k < height + 1; k++ ) if ( *pcm < 0 ) p[ w * k ] = ( k == 0 ) ? 0xFF : p[ w * k ] + gray; else p[ w * k ] = ( k == height ) ? 0xFF : p[ w * k ] + gray; } } return bitmap; }
static int convert_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, mlt_image_format output_format ) { // Nothing to do! if ( *format == output_format ) return 0; mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_log_debug( NULL, "filter_movit_convert: %s -> %s\n", mlt_image_format_name( *format ), mlt_image_format_name( output_format ) ); // Use CPU if glsl not initialized or not supported. GlslManager* glsl = GlslManager::get_instance(); if ( !glsl || !glsl->get_int("glsl_supported" ) ) return convert_on_cpu( frame, image, format, output_format ); // Do non-GL image conversions on a CPU-based image converter. if ( *format != mlt_image_glsl && output_format != mlt_image_glsl && output_format != mlt_image_glsl_texture ) return convert_on_cpu( frame, image, format, output_format ); int error = 0; int width = mlt_properties_get_int( properties, "width" ); int height = mlt_properties_get_int( properties, "height" ); int img_size = mlt_image_format_size( *format, width, height, NULL ); mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); mlt_service service = MLT_PRODUCER_SERVICE(producer); GlslManager::get_instance()->lock_service( frame ); EffectChain* chain = GlslManager::get_chain( service ); MltInput* input = GlslManager::get_input( service ); if ( !chain || !input ) { GlslManager::get_instance()->unlock_service( frame ); return 2; } if ( *format != mlt_image_glsl ) { bool finalize_chain = false; if ( output_format == mlt_image_glsl_texture ) { // We might already have a texture from a previous conversion from mlt_image_glsl. glsl_texture texture = (glsl_texture) mlt_properties_get_data( properties, "movit.convert.texture", NULL ); // XXX: requires a special property set on the frame by the app for now // because we do not have reliable way to clear the texture property // when a downstream filter has changed image. if ( texture && mlt_properties_get_int( properties, "movit.convert.use_texture") ) { *image = (uint8_t*) &texture->texture; mlt_frame_set_image( frame, *image, 0, NULL ); mlt_properties_set_int( properties, "format", output_format ); *format = output_format; GlslManager::get_instance()->unlock_service( frame ); return error; } else { // Use a separate chain to convert image in RAM to OpenGL texture. // Use cached chain if available and compatible. Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) ); chain = (EffectChain*) producer.get_data( "movit.convert.chain" ); input = (MltInput*) producer.get_data( "movit.convert.input" ); int w = producer.get_int( "movit.convert.width" ); int h = producer.get_int( "movit.convert.height" ); mlt_image_format f = (mlt_image_format) producer.get_int( "movit.convert.format" ); if ( !chain || width != w || height != h || output_format != f ) { chain = new EffectChain( width, height ); input = new MltInput( width, height ); chain->add_input( input ); chain->add_effect( new Mlt::VerticalFlip() ); finalize_chain = true; producer.set( "movit.convert.chain", chain, 0, (mlt_destructor) delete_chain ); producer.set( "movit.convert.input", input, 0 ); producer.set( "movit.convert.width", width ); producer.set( "movit.convert.height", height ); producer.set( "movit.convert.format", output_format ); } } } if ( *format == mlt_image_rgb24a || *format == mlt_image_opengl ) { input->useFlatInput( chain, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, width, height ); input->set_pixel_data( *image ); } else if ( *format == mlt_image_rgb24 ) { input->useFlatInput( chain, FORMAT_RGB, width, height ); input->set_pixel_data( *image ); } else if ( *format == mlt_image_yuv420p ) { ImageFormat image_format; YCbCrFormat ycbcr_format; if ( 709 == mlt_properties_get_int( properties, "colorspace" ) ) { image_format.color_space = COLORSPACE_REC_709; image_format.gamma_curve = GAMMA_REC_709; ycbcr_format.luma_coefficients = YCBCR_REC_709; } else if ( 576 == mlt_properties_get_int( properties, "height" ) ) { image_format.color_space = COLORSPACE_REC_601_625; image_format.gamma_curve = GAMMA_REC_601; ycbcr_format.luma_coefficients = YCBCR_REC_601; } else { image_format.color_space = COLORSPACE_REC_601_525; image_format.gamma_curve = GAMMA_REC_601; ycbcr_format.luma_coefficients = YCBCR_REC_601; } ycbcr_format.full_range = mlt_properties_get_int( properties, "force_full_luma" ); ycbcr_format.chroma_subsampling_x = ycbcr_format.chroma_subsampling_y = 2; // TODO: make new frame properties set by producers ycbcr_format.cb_x_position = ycbcr_format.cr_x_position = 0.0f; ycbcr_format.cb_y_position = ycbcr_format.cr_y_position = 0.5f; input->useYCbCrInput( chain, image_format, ycbcr_format, width, height ); input->set_pixel_data( *image ); } else if ( *format == mlt_image_yuv422 ) { ImageFormat image_format; YCbCrFormat ycbcr_format; if ( 709 == mlt_properties_get_int( properties, "colorspace" ) ) { image_format.color_space = COLORSPACE_REC_709; image_format.gamma_curve = GAMMA_REC_709; ycbcr_format.luma_coefficients = YCBCR_REC_709; } else if ( 576 == height ) { image_format.color_space = COLORSPACE_REC_601_625; image_format.gamma_curve = GAMMA_REC_601; ycbcr_format.luma_coefficients = YCBCR_REC_601; } else { image_format.color_space = COLORSPACE_REC_601_525; image_format.gamma_curve = GAMMA_REC_601; ycbcr_format.luma_coefficients = YCBCR_REC_601; } ycbcr_format.full_range = mlt_properties_get_int( properties, "force_full_luma" ); ycbcr_format.chroma_subsampling_x = 2; ycbcr_format.chroma_subsampling_y = 1; // TODO: make new frame properties set by producers ycbcr_format.cb_x_position = ycbcr_format.cr_x_position = 0.0f; ycbcr_format.cb_y_position = ycbcr_format.cr_y_position = 0.5f; input->useYCbCrInput( chain, image_format, ycbcr_format, width, height ); // convert chunky to planar uint8_t* planar = (uint8_t*) mlt_pool_alloc( img_size ); yuv422_to_yuv422p( *image, planar, width, height ); input->set_pixel_data( planar ); mlt_frame_set_image( frame, planar, img_size, mlt_pool_release ); } // Finalize the separate conversion chain if needed. if ( finalize_chain ) chain->finalize(); } if ( output_format != mlt_image_glsl ) { glsl_fbo fbo = glsl->get_fbo( width, height ); if ( output_format == mlt_image_glsl_texture ) { glsl_texture texture = glsl->get_texture( width, height, GL_RGBA ); glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo ); check_error(); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); check_error(); glBindFramebuffer( GL_FRAMEBUFFER, 0 ); check_error(); GlslManager::render( service, chain, fbo->fbo, width, height ); glFinish(); check_error(); glBindFramebuffer( GL_FRAMEBUFFER, 0 ); check_error(); *image = (uint8_t*) &texture->texture; mlt_frame_set_image( frame, *image, 0, NULL ); mlt_properties_set_data( properties, "movit.convert.texture", texture, 0, (mlt_destructor) GlslManager::release_texture, NULL ); mlt_properties_set_int( properties, "format", output_format ); *format = output_format; } else { // Use a PBO to hold the data we read back with glReadPixels() // (Intel/DRI goes into a slow path if we don't read to PBO) GLenum gl_format = ( output_format == mlt_image_rgb24a || output_format == mlt_image_opengl )? GL_RGBA : GL_RGB; img_size = width * height * ( gl_format == GL_RGB? 3 : 4 ); glsl_pbo pbo = glsl->get_pbo( img_size ); glsl_texture texture = glsl->get_texture( width, height, gl_format ); if ( fbo && pbo && texture ) { // Set the FBO glBindFramebuffer( GL_FRAMEBUFFER, fbo->fbo ); check_error(); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); check_error(); glBindFramebuffer( GL_FRAMEBUFFER, 0 ); check_error(); GlslManager::render( service, chain, fbo->fbo, width, height ); // Read FBO into PBO glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, pbo->pbo ); check_error(); glBufferData( GL_PIXEL_PACK_BUFFER_ARB, img_size, NULL, GL_STREAM_READ ); check_error(); glReadPixels( 0, 0, width, height, gl_format, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0) ); check_error(); // Copy from PBO uint8_t* buf = (uint8_t*) glMapBuffer( GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY ); check_error(); *image = (uint8_t*) mlt_pool_alloc( img_size ); mlt_frame_set_image( frame, *image, img_size, mlt_pool_release ); memcpy( *image, buf, img_size ); if ( output_format == mlt_image_yuv422 || output_format == mlt_image_yuv420p ) { *format = mlt_image_rgb24; error = convert_on_cpu( frame, image, format, output_format ); } // Release PBO and FBO glUnmapBuffer( GL_PIXEL_PACK_BUFFER_ARB ); check_error(); glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, 0 ); check_error(); glBindFramebuffer( GL_FRAMEBUFFER, 0 ); check_error(); glBindTexture( GL_TEXTURE_2D, 0 ); check_error(); mlt_properties_set_data( properties, "movit.convert.texture", texture, 0, (mlt_destructor) GlslManager::release_texture, NULL); mlt_properties_set_int( properties, "format", output_format ); *format = output_format; } else { error = 1; } } if ( fbo ) GlslManager::release_fbo( fbo ); } else { mlt_properties_set_int( properties, "format", output_format ); *format = output_format; } GlslManager::get_instance()->unlock_service( frame ); return error; }
int mlt_producer_is_blank( mlt_producer self ) { return self == NULL || !strcmp( mlt_properties_get( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( self ) ), "resource" ), "blank" ); }
int mlt_parser_start( mlt_parser self, mlt_service object ) { int error = 0; mlt_service_type type = mlt_service_identify( object ); switch( type ) { case invalid_type: error = self->on_invalid( self, object ); break; case unknown_type: error = self->on_unknown( self, object ); break; case producer_type: if ( mlt_producer_is_cut( ( mlt_producer )object ) ) error = mlt_parser_start( self, ( mlt_service )mlt_producer_cut_parent( ( mlt_producer )object ) ); error = self->on_start_producer( self, ( mlt_producer )object ); if ( error == 0 ) { int i = 0; while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL ) error = mlt_parser_start( self, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) ); } error = self->on_end_producer( self, ( mlt_producer )object ); break; case playlist_type: error = self->on_start_playlist( self, ( mlt_playlist )object ); if ( error == 0 ) { int i = 0; while ( error == 0 && i < mlt_playlist_count( ( mlt_playlist )object ) ) mlt_parser_start( self, ( mlt_service )mlt_playlist_get_clip( ( mlt_playlist )object, i ++ ) ); i = 0; while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL ) error = mlt_parser_start( self, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) ); } error = self->on_end_playlist( self, ( mlt_playlist )object ); break; case tractor_type: error = self->on_start_tractor( self, ( mlt_tractor )object ); if ( error == 0 ) { int i = 0; mlt_service next = mlt_service_producer( object ); mlt_parser_start( self, ( mlt_service )mlt_tractor_multitrack( ( mlt_tractor )object ) ); while ( next != ( mlt_service )mlt_tractor_multitrack( ( mlt_tractor )object ) ) { mlt_parser_start( self, next ); next = mlt_service_producer( next ); } while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL ) error = mlt_parser_start( self, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) ); } error = self->on_end_tractor( self, ( mlt_tractor )object ); break; case multitrack_type: error = self->on_start_multitrack( self, ( mlt_multitrack )object ); if ( error == 0 ) { int i = 0; while ( i < mlt_multitrack_count( ( mlt_multitrack )object ) ) { self->on_start_track( self ); mlt_parser_start( self, ( mlt_service )mlt_multitrack_track( ( mlt_multitrack )object , i ++ ) ); self->on_end_track( self ); } i = 0; while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL ) error = mlt_parser_start( self, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) ); } error = self->on_end_multitrack( self, ( mlt_multitrack )object ); break; case filter_type: error = self->on_start_filter( self, ( mlt_filter )object ); if ( error == 0 ) { int i = 0; while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL ) error = mlt_parser_start( self, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) ); } error = self->on_end_filter( self, ( mlt_filter )object ); break; case transition_type: error = self->on_start_transition( self, ( mlt_transition )object ); if ( error == 0 ) { int i = 0; while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL ) error = mlt_parser_start( self, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) ); } error = self->on_end_transition( self, ( mlt_transition )object ); break; case field_type: break; case consumer_type: break; } return error; }