static void cache_object_close( mlt_cache cache, void *object, void* data ) { char key[19]; if ( cache->is_frames ) { // Frame caches are easy - just close the object as mlt_frame. mlt_frame_close( object ); return; } // Fetch the cache item from the active list by its owner's address sprintf( key, "%p", object ); mlt_cache_item item = mlt_properties_get_data( cache->active, key, NULL ); if ( item ) { mlt_log( NULL, MLT_LOG_DEBUG, "%s: item %p object %p data %p refcount %d\n", __FUNCTION__, item, item->object, item->data, item->refcount ); if ( item->destructor && --item->refcount <= 0 ) { // Destroy the data object item->destructor( item->data ); item->data = NULL; item->destructor = NULL; // Do not dispose of the cache item because it could likely be used // again. } } // Fetch the cache item from the garbage collection by its data address if ( data ) { sprintf( key, "%p", data ); item = mlt_properties_get_data( cache->garbage, key, NULL ); if ( item ) { mlt_log( NULL, MLT_LOG_DEBUG, "collecting garbage item %p object %p data %p refcount %d\n", item, item->object, item->data, item->refcount ); if ( item->destructor && --item->refcount <= 0 ) { item->destructor( item->data ); item->data = NULL; item->destructor = NULL; // We do not need the garbage-collected cache item mlt_properties_set_data( cache->garbage, key, NULL, 0, NULL, NULL ); } } } }
int mlt_consumer_stop( mlt_consumer self ) { // Get the properies mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); // Just in case... mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopping put waiting\n" ); pthread_mutex_lock( &self->put_mutex ); self->put_active = 0; pthread_cond_broadcast( &self->put_cond ); pthread_mutex_unlock( &self->put_mutex ); // Stop the consumer mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopping consumer\n" ); // Cancel the read ahead threads self->ahead = 0; if ( self->started ) { // Unblock the consumer calling mlt_consumer_rt_frame pthread_mutex_lock( &self->queue_mutex ); pthread_cond_broadcast( &self->queue_cond ); pthread_mutex_unlock( &self->queue_mutex ); } // Invoke the child callback if ( self->stop != NULL ) self->stop( self ); // Check if the user has requested real time or not and stop if necessary mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopping read_ahead\n" ); if ( abs( self->real_time ) == 1 ) consumer_read_ahead_stop( self ); else if ( abs( self->real_time ) > 1 ) consumer_work_stop( self ); // Kill the test card mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL ); // Check and run a post command if ( mlt_properties_get( properties, "post" ) ) if (system( mlt_properties_get( properties, "post" ) ) == -1 ) mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get( properties, "post" ) ); mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopped\n" ); return 0; }
mlt_cache_item mlt_cache_get( mlt_cache cache, void *object ) { mlt_cache_item result = NULL; pthread_mutex_lock( &cache->mutex ); void **hit = shuffle_get_hit( cache, object ); void **alt = cache->current == cache->A ? cache->B : cache->A; if ( hit ) { // copy the hit to the MRU end alt[ cache->count - 1 ] = *hit; hit = &alt[ cache->count - 1 ]; char key[19]; sprintf( key, "%p", *hit ); result = mlt_properties_get_data( cache->active, key, NULL ); if ( result && result->data ) result->refcount++; mlt_log( NULL, MLT_LOG_DEBUG, "%s: get %d = %p, %p\n", __FUNCTION__, cache->count - 1, *hit, result->data ); // swap the current array cache->current = alt; } pthread_mutex_unlock( &cache->mutex ); return result; }
mlt_repository mlt_repository_init( const char *directory ) { // Safety check if ( directory == NULL || strcmp( directory, "" ) == 0 ) return NULL; // Construct the repository mlt_repository self = calloc( 1, sizeof( struct mlt_repository_s )); mlt_properties_init( &self->parent, self ); self->consumers = mlt_properties_new(); self->filters = mlt_properties_new(); self->producers = mlt_properties_new(); self->transitions = mlt_properties_new(); // Get the directory list mlt_properties dir = mlt_properties_new(); int count = mlt_properties_dir_list( dir, directory, NULL, 0 ); int i; // Iterate over files for ( i = 0; i < count; i++ ) { int flags = RTLD_NOW; const char *object_name = mlt_properties_get_value( dir, i); // Very temporary hack to allow the quicktime plugins to work // TODO: extend repository to allow this to be used on a case by case basis if ( strstr( object_name, "libmltkino" ) ) flags |= RTLD_GLOBAL; // Open the shared object void *object = dlopen( object_name, flags ); if ( object != NULL ) { // Get the registration function mlt_repository_callback symbol_ptr = dlsym( object, "mlt_register" ); // Call the registration function if ( symbol_ptr != NULL ) { symbol_ptr( self ); // Register the object file for closure mlt_properties_set_data( &self->parent, object_name, object, 0, ( mlt_destructor )dlclose, NULL ); } else { dlclose( object ); } } else if ( strstr( object_name, "libmlt" ) ) { mlt_log( NULL, MLT_LOG_WARNING, "%s: failed to dlopen %s\n (%s)\n", __FUNCTION__, object_name, dlerror() ); } } mlt_properties_close( dir ); return self; }
void mlt_cache_put_frame( mlt_cache cache, mlt_frame frame ) { pthread_mutex_lock( &cache->mutex ); mlt_frame *hit = shuffle_get_frame( cache, mlt_frame_original_position( frame ) ); mlt_frame *alt = (mlt_frame*) ( cache->current == cache->A ? cache->B : cache->A ); // add the frame to the cache if ( hit ) { // release the old data mlt_frame_close( *hit ); // the MRU end gets the updated data hit = &alt[ cache->count - 1 ]; } else if ( cache->count < cache->size ) { // more room in cache, add it to MRU end hit = &alt[ cache->count++ ]; } else { // release the entry at the LRU end mlt_frame_close( cache->current[0] ); // The MRU end gets the new item hit = &alt[ cache->count - 1 ]; } *hit = mlt_frame_clone( frame, 1 ); mlt_log( NULL, MLT_LOG_DEBUG, "%s: put %d = %p\n", __FUNCTION__, cache->count - 1, frame ); // swap the current array cache->current = (void**) alt; cache->is_frames = 1; pthread_mutex_unlock( &cache->mutex ); }
int ServiceManager::render(WebVfx::Image* outputImage, mlt_position position, mlt_position length, bool hasAlpha) { double time = length > 0 ? position / (double)length : 0; parameters->setPositionAndLength(position, length); if (mlt_properties_get_int(MLT_SERVICE_PROPERTIES(service), "_reload")) { mlt_properties_set_int(MLT_SERVICE_PROPERTIES(service), "_reload", 0); effects->reload(); } // Produce any extra images if (imageProducers) { for (std::vector<ImageProducer*>::iterator it = imageProducers->begin(); it != imageProducers->end(); it++) { ImageProducer* imageProducer = *it; if (imageProducer && imageProducer->isPositionValid(position)) { WebVfx::Image extraImage = imageProducer->produceImage(position, outputImage->width(), outputImage->height(), hasAlpha); if (extraImage.isNull()) { mlt_log(service, MLT_LOG_ERROR, "WebVfx failed to produce image for name %s\n", imageProducer->getName().toLatin1().constData()); return 1; } effects->setImage(imageProducer->getName(), &extraImage); } } } return !effects->render(time, outputImage); }
void mlt_producer_close( mlt_producer self ) { if ( self != NULL && mlt_properties_dec_ref( MLT_PRODUCER_PROPERTIES( self ) ) <= 0 ) { self->parent.close = NULL; if ( self->close != NULL ) { self->close( self->close_object ); } else { int destroy = mlt_producer_is_cut( self ); #if _MLT_PRODUCER_CHECKS_ == 1 // Show debug info mlt_properties_debug( MLT_PRODUCER_PROPERTIES( self ), "Producer closing", stderr ); #endif #ifdef _MLT_PRODUCER_CHECKS_ // Show current stats - these should match when the app is closed mlt_log( MLT_PRODUCER_SERVICE( self ), MLT_LOG_DEBUG, "Producers created %d, destroyed %d\n", producers_created, ++producers_destroyed ); #endif mlt_service_close( &self->parent ); if ( destroy ) free( self ); } } }
void mlt_service_cache_put( mlt_service self, const char *name, void* data, int size, mlt_destructor destructor ) { mlt_log( self, MLT_LOG_DEBUG, "%s: name %s object %p data %p\n", __FUNCTION__, name, self, data ); mlt_cache cache = get_cache( self, name ); if ( cache ) mlt_cache_put( cache, self, data, size, destructor ); }
mlt_cache_item mlt_service_cache_get( mlt_service self, const char *name ) { mlt_log( self, MLT_LOG_DEBUG, "%s: name %s object %p\n", __FUNCTION__, name, self ); mlt_cache_item result = NULL; mlt_cache cache = get_cache( self, name ); if ( cache ) result = mlt_cache_get( cache, self ); return result; }
void mlt_cache_close( mlt_cache cache ) { if ( cache ) { while ( cache->count-- ) { void *object = cache->current[ cache->count ]; mlt_log( NULL, MLT_LOG_DEBUG, "%s: %d = %p\n", __FUNCTION__, cache->count, object ); cache_object_close( cache, object, NULL ); } mlt_properties_close( cache->active ); mlt_properties_close( cache->garbage ); pthread_mutex_destroy( &cache->mutex ); free( cache ); } }
mlt_frame mlt_cache_get_frame( mlt_cache cache, mlt_position position ) { mlt_frame result = NULL; pthread_mutex_lock( &cache->mutex ); mlt_frame *hit = shuffle_get_frame( cache, position ); mlt_frame *alt = (mlt_frame*) ( cache->current == cache->A ? cache->B : cache->A ); if ( hit ) { // copy the hit to the MRU end alt[ cache->count - 1 ] = *hit; hit = &alt[ cache->count - 1 ]; result = mlt_frame_clone( *hit, 1 ); mlt_log( NULL, MLT_LOG_DEBUG, "%s: get %d = %p\n", __FUNCTION__, cache->count - 1, *hit ); // swap the current array cache->current = (void**) alt; } pthread_mutex_unlock( &cache->mutex ); return result; }
static int vdpau_init( producer_avformat self ) { if ( !vdpau_supported ) return 0; mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "vdpau_init\n" ); int success = 0; mlt_properties properties = MLT_PRODUCER_PROPERTIES( self->parent ); Display *display = XOpenDisplay( NULL ); if ( !display || mlt_properties_get_int( properties, "novdpau" ) || ( getenv( "MLT_NO_VDPAU" ) && strcmp( getenv( "MLT_NO_VDPAU" ), "1" ) == 0 ) ) return success; void *object = NULL; if ( !vdpau_init_done ) { int flags = RTLD_NOW; object = dlopen( "/usr/lib/libvdpau.so", flags ); #ifdef ARCH_X86_64 if ( !object ) object = dlopen( "/usr/lib64/libvdpau.so", flags ); if ( !object ) object = dlopen( "/usr/lib/x86_64-linux-gnu/libvdpau.so.1", flags ); #elif ARCH_X86 if ( !object ) object = dlopen( "/usr/lib/i386-linux-gnu/libvdpau.so.1", flags ); #endif if ( !object ) object = dlopen( "/usr/local/lib/libvdpau.so", flags ); if ( object ) vdpau_device_create_x11 = dlsym( object, "vdp_device_create_x11" ); else { mlt_log( MLT_PRODUCER_SERVICE(self->parent), MLT_LOG_WARNING, "%s: failed to dlopen libvdpau.so\n (%s)\n", __FUNCTION__, dlerror() ); // Don't try again. vdpau_supported = 0; return success; } } if ( vdpau_device_create_x11 ) { int screen = mlt_properties_get_int( properties, "x11_screen" ); self->vdpau = calloc( 1, sizeof( *self->vdpau ) ); self->vdpau->device = VDP_INVALID_HANDLE; self->vdpau->decoder = VDP_INVALID_HANDLE; mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "X11 Display = %p\n", display ); if ( VDP_STATUS_OK == vdpau_device_create_x11( display, screen, &self->vdpau->device, &vdp_get_proc_address ) ) { if ( !vdpau_init_done ) { vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_GET_ERROR_STRING, (void**) &vdp_get_error_string ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_GET_API_VERSION, (void**) &vdp_get_api_version ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_GET_INFORMATION_STRING, (void**) &vdp_get_information_string ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_CREATE, (void**) &vdp_surface_create ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_DESTROY, (void**) &vdp_surface_destroy ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR, (void**) &vdp_surface_get_bits ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_DECODER_CREATE, (void**) &vdp_decoder_create ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_DECODER_DESTROY, (void**) &vdp_decoder_destroy ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_DECODER_RENDER, (void**) &vdp_decoder_render ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_DEVICE_DESTROY, (void**) &vdp_device_destroy ); vdpau_init_done = 1; } success = 1; } } if ( !success ) { mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "VDPAU failed to initialize device\n" ); if ( object ) dlclose( object ); free( self->vdpau ); self->vdpau = NULL; } return success; }
int mlt_consumer_start( mlt_consumer self ) { if ( !mlt_consumer_is_stopped( self ) ) return 0; // Stop listening to the property-changed event mlt_event_block( self->event_listener ); // Get the properies mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); // Determine if there's a test card producer char *test_card = mlt_properties_get( properties, "test_card" ); // Just to make sure nothing is hanging around... pthread_mutex_lock( &self->put_mutex ); self->put = NULL; self->put_active = 1; pthread_mutex_unlock( &self->put_mutex ); // Deal with it now. if ( test_card != NULL ) { if ( mlt_properties_get_data( properties, "test_card_producer", NULL ) == NULL ) { // Create a test card producer mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); mlt_producer producer = mlt_factory_producer( profile, NULL, test_card ); // Do we have a producer if ( producer != NULL ) { // Test card should loop I guess... mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" ); //mlt_producer_set_speed( producer, 0 ); //mlt_producer_set_in_and_out( producer, 0, 0 ); // Set the test card on the consumer mlt_properties_set_data( properties, "test_card_producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); } } } else { // Allow the hash table to speed things up mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL ); } // The profile could have changed between a stop and a restart. apply_profile_properties( self, mlt_service_profile( MLT_CONSUMER_SERVICE(self) ), properties ); // Set the frame duration in microseconds for the frame-dropping heuristic int frame_rate_num = mlt_properties_get_int( properties, "frame_rate_num" ); int frame_rate_den = mlt_properties_get_int( properties, "frame_rate_den" ); int frame_duration = 0; if ( frame_rate_num && frame_rate_den ) { frame_duration = 1000000 / frame_rate_num * frame_rate_den; } mlt_properties_set_int( properties, "frame_duration", frame_duration ); // Check and run an ante command if ( mlt_properties_get( properties, "ante" ) ) if ( system( mlt_properties_get( properties, "ante" ) ) == -1 ) mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get( properties, "ante" ) ); // Set the real_time preference self->real_time = mlt_properties_get_int( properties, "real_time" ); // For worker threads implementation, buffer must be at least # threads if ( abs( self->real_time ) > 1 && mlt_properties_get_int( properties, "buffer" ) <= abs( self->real_time ) ) mlt_properties_set_int( properties, "_buffer", abs( self->real_time ) + 1 ); // Get the image format to use for rendering threads const char* format = mlt_properties_get( properties, "mlt_image_format" ); if ( format ) { if ( !strcmp( format, "rgb24" ) ) self->format = mlt_image_rgb24; else if ( !strcmp( format, "rgb24a" ) ) self->format = mlt_image_rgb24a; else if ( !strcmp( format, "yuv420p" ) ) self->format = mlt_image_yuv420p; else if ( !strcmp( format, "none" ) ) self->format = mlt_image_none; else self->format = mlt_image_yuv422; } // Start the service if ( self->start != NULL ) return self->start( self ); return 0; }
static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int track ) { mlt_tractor self = parent->child; // We only respond to the first track requests if ( track == 0 && self->producer != NULL ) { int i = 0; int done = 0; mlt_frame temp = NULL; int count = 0; int image_count = 0; // Get the properties of the parent producer mlt_properties properties = MLT_PRODUCER_PROPERTIES( parent ); // Try to obtain the multitrack associated to the tractor mlt_multitrack multitrack = mlt_properties_get_data( properties, "multitrack", NULL ); // Or a specific producer mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL ); // Determine whether this tractor feeds to the consumer or stops here int global_feed = mlt_properties_get_int( properties, "global_feed" ); // If we don't have one, we're in trouble... if ( multitrack != NULL ) { // The output frame will hold the 'global' data feeds (ie: those which are targetted for the final frame) mlt_deque data_queue = mlt_deque_init( ); // Used to garbage collect all frames char label[ 30 ]; // Get the id of the tractor char *id = mlt_properties_get( properties, "_unique_id" ); // Will be used to store the frame properties object mlt_properties frame_properties = NULL; // We'll store audio and video frames to use here mlt_frame audio = NULL; mlt_frame video = NULL; mlt_frame first_video = NULL; // Temporary properties mlt_properties temp_properties = NULL; // Get the multitrack's producer mlt_producer target = MLT_MULTITRACK_PRODUCER( multitrack ); mlt_producer_seek( target, mlt_producer_frame( parent ) ); mlt_producer_set_speed( target, mlt_producer_get_speed( parent ) ); // We will create one frame and attach everything to it *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) ); // Get the properties of the frame frame_properties = MLT_FRAME_PROPERTIES( *frame ); // Loop through each of the tracks we're harvesting for ( i = 0; !done; i ++ ) { // Get a frame from the producer mlt_service_get_frame( self->producer, &temp, i ); // Get the temporary properties temp_properties = MLT_FRAME_PROPERTIES( temp ); // Pass all unique meta properties from the producer's frame to the new frame mlt_properties_lock( temp_properties ); int props_count = mlt_properties_count( temp_properties ); int j; for ( j = 0; j < props_count; j ++ ) { char *name = mlt_properties_get_name( temp_properties, j ); if ( !strncmp( name, "meta.", 5 ) && !mlt_properties_get( frame_properties, name ) ) mlt_properties_set( frame_properties, name, mlt_properties_get_value( temp_properties, j ) ); } mlt_properties_unlock( temp_properties ); // Copy the format conversion virtual functions if ( ! (*frame)->convert_image && temp->convert_image ) (*frame)->convert_image = temp->convert_image; if ( ! (*frame)->convert_audio && temp->convert_audio ) (*frame)->convert_audio = temp->convert_audio; // Check for last track done = mlt_properties_get_int( temp_properties, "last_track" ); // Handle fx only tracks if ( mlt_properties_get_int( temp_properties, "fx_cut" ) ) { int hide = ( video == NULL ? 1 : 0 ) | ( audio == NULL ? 2 : 0 ); mlt_properties_set_int( temp_properties, "hide", hide ); } // We store all frames with a destructor on the output frame sprintf( label, "_%s_%d", id, count ++ ); mlt_properties_set_data( frame_properties, label, temp, 0, ( mlt_destructor )mlt_frame_close, NULL ); // We want to append all 'final' feeds to the global queue if ( !done && mlt_properties_get_data( temp_properties, "data_queue", NULL ) != NULL ) { // Move the contents of this queue on to the output frames data queue mlt_deque sub_queue = mlt_properties_get_data( MLT_FRAME_PROPERTIES( temp ), "data_queue", NULL ); mlt_deque temp = mlt_deque_init( ); while ( global_feed && mlt_deque_count( sub_queue ) ) { mlt_properties p = mlt_deque_pop_back( sub_queue ); if ( mlt_properties_get_int( p, "final" ) ) mlt_deque_push_back( data_queue, p ); else mlt_deque_push_back( temp, p ); } while( mlt_deque_count( temp ) ) mlt_deque_push_front( sub_queue, mlt_deque_pop_back( temp ) ); mlt_deque_close( temp ); } // Now do the same with the global queue but without the conditional behaviour if ( mlt_properties_get_data( temp_properties, "global_queue", NULL ) != NULL ) { mlt_deque sub_queue = mlt_properties_get_data( MLT_FRAME_PROPERTIES( temp ), "global_queue", NULL ); while ( mlt_deque_count( sub_queue ) ) { mlt_properties p = mlt_deque_pop_back( sub_queue ); mlt_deque_push_back( data_queue, p ); } } // Pick up first video and audio frames if ( !done && !mlt_frame_is_test_audio( temp ) && !( mlt_properties_get_int( temp_properties, "hide" ) & 2 ) ) { // Order of frame creation is starting to get problematic if ( audio != NULL ) { mlt_deque_push_front( MLT_FRAME_AUDIO_STACK( temp ), producer_get_audio ); mlt_deque_push_front( MLT_FRAME_AUDIO_STACK( temp ), audio ); } audio = temp; } if ( !done && !mlt_frame_is_test_card( temp ) && !( mlt_properties_get_int( temp_properties, "hide" ) & 1 ) ) { if ( video != NULL ) { mlt_deque_push_front( MLT_FRAME_IMAGE_STACK( temp ), producer_get_image ); mlt_deque_push_front( MLT_FRAME_IMAGE_STACK( temp ), video ); } video = temp; if ( first_video == NULL ) first_video = temp; mlt_properties_set_int( MLT_FRAME_PROPERTIES( temp ), "image_count", ++ image_count ); image_count = 1; } } // Now stack callbacks if ( audio != NULL ) { mlt_frame_push_audio( *frame, audio ); mlt_frame_push_audio( *frame, producer_get_audio ); } if ( video != NULL ) { mlt_properties video_properties = MLT_FRAME_PROPERTIES( first_video ); mlt_frame_push_service( *frame, video ); mlt_frame_push_service( *frame, producer_get_image ); if ( global_feed ) mlt_properties_set_data( frame_properties, "data_queue", data_queue, 0, NULL, NULL ); mlt_properties_set_data( video_properties, "global_queue", data_queue, 0, destroy_data_queue, NULL ); mlt_properties_set_int( frame_properties, "width", mlt_properties_get_int( video_properties, "width" ) ); mlt_properties_set_int( frame_properties, "height", mlt_properties_get_int( video_properties, "height" ) ); mlt_properties_pass_list( frame_properties, video_properties, "meta.media.width, meta.media.height" ); mlt_properties_set_int( frame_properties, "progressive", mlt_properties_get_int( video_properties, "progressive" ) ); mlt_properties_set_double( frame_properties, "aspect_ratio", mlt_properties_get_double( video_properties, "aspect_ratio" ) ); mlt_properties_set_int( frame_properties, "image_count", image_count ); mlt_properties_set_data( frame_properties, "_producer", mlt_frame_get_original_producer( first_video ), 0, NULL, NULL ); } else { destroy_data_queue( data_queue ); } mlt_frame_set_position( *frame, mlt_producer_frame( parent ) ); mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_audio", audio == NULL ); mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_image", video == NULL ); } else if ( producer != NULL ) { mlt_producer_seek( producer, mlt_producer_frame( parent ) ); mlt_producer_set_speed( producer, mlt_producer_get_speed( parent ) ); mlt_service_get_frame( self->producer, frame, track ); } else { mlt_log( MLT_PRODUCER_SERVICE( parent ), MLT_LOG_ERROR, "tractor without a multitrack!!\n" ); mlt_service_get_frame( self->producer, frame, track ); } // Prepare the next frame mlt_producer_prepare_next( parent ); // Indicate our found status return 0; } else { // Generate a test card *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) ); return 0; } }
void mlt_cache_put( mlt_cache cache, void *object, void* data, int size, mlt_destructor destructor ) { pthread_mutex_lock( &cache->mutex ); void **hit = shuffle_get_hit( cache, object ); void **alt = cache->current == cache->A ? cache->B : cache->A; // add the object to the cache if ( hit ) { // release the old data pthread_mutex_unlock( &cache->mutex ); cache_object_close( cache, *hit, NULL ); pthread_mutex_lock( &cache->mutex ); // the MRU end gets the updated data hit = &alt[ cache->count - 1 ]; } else if ( cache->count < CACHE_SIZE ) { // more room in cache, add it to MRU end hit = &alt[ cache->count++ ]; } else { // release the entry at the LRU end pthread_mutex_unlock( &cache->mutex ); cache_object_close( cache, cache->current[0], NULL ); pthread_mutex_lock( &cache->mutex ); // The MRU end gets the new item hit = &alt[ cache->count - 1 ]; } *hit = object; mlt_log( NULL, MLT_LOG_DEBUG, "%s: put %d = %p, %p\n", __FUNCTION__, cache->count - 1, object, data ); // Fetch the cache item char key[19]; sprintf( key, "%p", object ); mlt_cache_item item = mlt_properties_get_data( cache->active, key, NULL ); if ( !item ) { item = calloc( 1, sizeof( mlt_cache_item_s ) ); if ( item ) mlt_properties_set_data( cache->active, key, item, 0, free, NULL ); } if ( item ) { // If updating the cache item but not all references are released // copy the item to the garbage collection. if ( item->refcount > 0 && item->data ) { mlt_cache_item orphan = calloc( 1, sizeof( mlt_cache_item_s ) ); if ( orphan ) { mlt_log( NULL, MLT_LOG_DEBUG, "adding to garbage collection object %p data %p\n", item->object, item->data ); *orphan = *item; sprintf( key, "%p", orphan->data ); // We store in the garbage collection by data address, not the owner's! mlt_properties_set_data( cache->garbage, key, orphan, 0, free, NULL ); } } // Set/update the cache item item->cache = cache; item->object = object; item->data = data; item->size = size; item->destructor = destructor; item->refcount = 1; } // swap the current array cache->current = alt; pthread_mutex_unlock( &cache->mutex ); }
bool ServiceManager::initialize(int width, int height) { if (effects) return true; mlt_properties properties = MLT_SERVICE_PROPERTIES(service); // Create and initialize Effects const char* fileName = mlt_properties_get(properties, "resource"); if (!fileName) { mlt_log(service, MLT_LOG_ERROR, "No 'resource' property found\n"); return false; } bool isTransparent = mlt_properties_get_int(properties, "transparent") || mlt_service_identify(service) == filter_type; parameters = new ServiceParameters(service); effects = WebVfx::createEffects(fileName, width, height, parameters, isTransparent); if (!effects) { mlt_log(service, MLT_LOG_ERROR, "Failed to create WebVfx Effects for resource %s\n", fileName); return false; } // Iterate over image map - save source and target image names, // and create an ImageProducer for each extra image. char* factory = mlt_properties_get(properties, "factory"); WebVfx::Effects::ImageTypeMapIterator it(effects->getImageTypeMap()); while (it.hasNext()) { it.next(); const QString& imageName = it.key(); switch (it.value()) { case WebVfx::Effects::SourceImageType: sourceImageName = imageName; break; case WebVfx::Effects::TargetImageType: targetImageName = imageName; break; case WebVfx::Effects::ExtraImageType: { if (!imageProducers) imageProducers = new std::vector<ImageProducer*>(3); // Property prefix "producer.<name>." QString producerPrefix("producer."); producerPrefix.append(imageName).append("."); // Find producer.<name>.resource property QString resourceName(producerPrefix); resourceName.append("resource"); char* resource = mlt_properties_get(properties, resourceName.toLatin1().constData()); if (resource) { mlt_producer producer = mlt_factory_producer(mlt_service_profile(service), factory, resource); if (!producer) { mlt_log(service, MLT_LOG_ERROR, "WebVfx failed to create extra image producer for %s\n", resourceName.toLatin1().constData()); return false; } // Copy producer.<name>.* properties onto producer mlt_properties_pass(MLT_PRODUCER_PROPERTIES(producer), properties, producerPrefix.toLatin1().constData()); // Append ImageProducer to vector imageProducers->insert(imageProducers->end(), new ImageProducer(imageName, producer)); } else mlt_log(service, MLT_LOG_WARNING, "WebVfx no producer resource property specified for extra image %s\n", resourceName.toLatin1().constData()); break; } default: mlt_log(service, MLT_LOG_ERROR, "Invalid WebVfx image type %d\n", it.value()); break; } } return true; }
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 int transition_get_frame( mlt_service service, mlt_frame_ptr frame, int index ) { int error = 0; mlt_transition self = service->child; mlt_properties properties = MLT_TRANSITION_PROPERTIES( self ); int accepts_blanks = mlt_properties_get_int( properties, "accepts_blanks" ); int a_track = mlt_properties_get_int( properties, "a_track" ); int b_track = mlt_properties_get_int( properties, "b_track" ); mlt_position in = mlt_properties_get_position( properties, "in" ); mlt_position out = mlt_properties_get_position( properties, "out" ); int always_active = mlt_properties_get_int( properties, "always_active" ); int type = mlt_properties_get_int( properties, "_transition_type" ); int reverse_order = 0; // Ensure that we have the correct order if ( a_track > b_track ) { reverse_order = 1; a_track = b_track; b_track = mlt_properties_get_int( properties, "a_track" ); } // Only act on this operation once per multitrack iteration from the tractor if ( !self->held ) { int active = 0; int i = 0; int a_frame = a_track; int b_frame = b_track; mlt_position position; int ( *invalid )( mlt_frame ) = type == 1 ? mlt_frame_is_test_card : mlt_frame_is_test_audio; // Initialise temporary store if ( self->frames == NULL ) self->frames = calloc( b_track + 1, sizeof( mlt_frame ) ); // Get all frames between a and b for( i = a_track; i <= b_track; i ++ ) mlt_service_get_frame( self->producer, &self->frames[ i ], i ); // We're holding these frames until the last_track frame property is received self->held = 1; // When we need to locate the a_frame switch( type ) { case 1: case 2: // Some transitions (esp. audio) may accept blank frames active = accepts_blanks; // If we're not active then... if ( !active ) { // Hunt for the a_frame while( a_frame <= b_frame && invalid( self->frames[ a_frame ] ) ) a_frame ++; // Determine if we're active now active = a_frame != b_frame && !invalid( self->frames[ b_frame ] ); } break; default: mlt_log( service, MLT_LOG_ERROR, "invalid transition type\n" ); break; } // Now handle the non-always active case if ( active && !always_active && a_frame <= b_track ) { // For non-always-active transitions, we need the current position of the a frame position = mlt_frame_get_position( self->frames[ a_frame ] ); // If a is in range, we're active active = position >= in && ( out == 0 || position <= out ); } // Finally, process the a and b frames if ( active && !mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( self ), "disable" ) ) { int frame_nb = ( !reverse_order && a_frame <= b_track )? a_frame : b_frame; mlt_frame a_frame_ptr = self->frames[ frame_nb ]; frame_nb = ( !reverse_order || a_frame > b_track )? b_frame : a_frame; mlt_frame b_frame_ptr = self->frames[ frame_nb ]; int a_hide = mlt_properties_get_int( MLT_FRAME_PROPERTIES( a_frame_ptr ), "hide" ); int b_hide = mlt_properties_get_int( MLT_FRAME_PROPERTIES( b_frame_ptr ), "hide" ); if ( !( a_hide & type ) && !( b_hide & type ) ) { // Add hooks for pre-processing frames mlt_frame_push_service( a_frame_ptr, self ); mlt_frame_push_get_image( a_frame_ptr, get_image_a ); mlt_frame_push_frame( b_frame_ptr, a_frame_ptr ); mlt_frame_push_service( b_frame_ptr, self ); mlt_frame_push_get_image( b_frame_ptr, get_image_b ); // Process the transition *frame = mlt_transition_process( self, a_frame_ptr, b_frame_ptr ); // We need to ensure that the tractor doesn't consider this frame for output if ( *frame == a_frame_ptr ) b_hide |= type; else a_hide |= type; mlt_properties_set_int( MLT_FRAME_PROPERTIES( a_frame_ptr ), "hide", a_hide ); mlt_properties_set_int( MLT_FRAME_PROPERTIES( b_frame_ptr ), "hide", b_hide ); } } } // Obtain the frame from the cache or the producer we're attached to if ( index >= a_track && index <= b_track ) *frame = self->frames[ index ]; else error = mlt_service_get_frame( self->producer, frame, index ); // Determine if that was the last track self->held = !mlt_properties_get_int( MLT_FRAME_PROPERTIES( *frame ), "last_track" ); return error; }