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 ); }
static void get_time_info( mlt_producer producer, mlt_frame frame, time_info* info ) { mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); mlt_position position = mlt_frame_original_position( frame ); info->fps = ceil( mlt_producer_get_fps( producer ) ); char* direction = mlt_properties_get( producer_properties, "direction" ); if( !strcmp( direction, "down" ) ) { mlt_position length = mlt_properties_get_int( producer_properties, "length" ); info->position = length - 1 - position; } else { info->position = position; } char* tc_str = NULL; if( mlt_properties_get_int( producer_properties, "drop" ) ) { tc_str = mlt_properties_frames_to_time( producer_properties, info->position, mlt_time_smpte_df ); } else { tc_str = mlt_properties_frames_to_time( producer_properties, info->position, mlt_time_smpte_ndf ); } sscanf( tc_str, "%02d:%02d:%02d%c%d", &info->hours, &info->minutes, &info->seconds, &info->sep, &info->frames ); }
int frame_cache_put_frame( frame_cache self, mlt_frame frame ) { if ( self == NULL ) return 1; mlt_position frame_position = mlt_frame_original_position( frame ); // We actually need to insert frame into cache if ( frame_cache_frame_index( self, frame_position ) == -1 ) { if ( self->frames_total > 0 ) { mlt_frame first_frame = self->frames[ self->start_pos ]; mlt_position first_frame_position = mlt_frame_original_position( first_frame ); mlt_position last_frame_position = first_frame_position + self->frames_total - 1; // We're trying to insert next frame (in sequence), so no need to delete previous ones if ( frame_position == last_frame_position + 1 ) { // Append new frame at the end of circular buffer if ( self->frames_total < self->size ) { size_t index = ( self->start_pos + self->frames_total ) % self->size; self->frames[ index ] = frame; self->frames_total += 1; } // We need to throw out the earliest frame else { self->frames[ self->start_pos ] = frame; self->start_pos = ( self->start_pos + 1 ) % self->size; } } // We're inserting frame that is not in cache, and is not next in sequence else { frame_cache_purge( self ); self->frames[ self->start_pos ] = frame; self->frames_total += 1; } } else { self->frames[ self->start_pos ] = frame; self->frames_total += 1; } } return 0; }
static mlt_frame* shuffle_get_frame( mlt_cache cache, mlt_position position ) { int i = cache->count; int j = cache->count - 1; mlt_frame *hit = NULL; mlt_frame *alt = (mlt_frame*) ( cache->current == cache->A ? cache->B : cache->A ); if ( cache->count > 0 && cache->count < cache->size ) { // first determine if we have a hit while ( i-- && !hit ) { mlt_frame *o = (mlt_frame*) &cache->current[ i ]; if ( mlt_frame_original_position( *o ) == position ) hit = o; } // if there was no hit, we will not be shuffling out an entry // and are still filling the cache if ( !hit ) ++j; // reset these i = cache->count; hit = NULL; } // shuffle the existing entries to the alternate array while ( i-- ) { mlt_frame *o = (mlt_frame*) &cache->current[ i ]; if ( !hit && mlt_frame_original_position( *o ) == position ) { hit = o; } else if ( j > 0 ) { alt[ --j ] = *o; // mlt_log( NULL, MLT_LOG_DEBUG, "%s: shuffle %d = %p\n", __FUNCTION__, j, alt[j] ); } } return hit; }
mlt_position frame_cache_earliest_frame_position( frame_cache self ) { if ( self == NULL ) return FRAME_CACHE_INVALID_POSITION; if ( self->frames_total == 0 ) return FRAME_CACHE_INVALID_POSITION; mlt_frame earliest_frame = self->frames[ self->start_pos ]; return mlt_frame_original_position( earliest_frame ); }
mlt_position frame_cache_latest_frame_position( frame_cache self ) { if ( self == NULL ) return FRAME_CACHE_INVALID_POSITION; if ( self->frames_total == 0 ) return FRAME_CACHE_INVALID_POSITION; mlt_frame latest_frame = self->frames[ ( self->start_pos + self->frames_total - 1 ) % self->size ]; return mlt_frame_original_position( latest_frame ); }
static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { mlt_filter filter = mlt_frame_pop_audio( frame ); private_data* pdata = (private_data*)filter->child; mlt_position o_pos = mlt_frame_original_position( frame ); // Get the producer's audio *format = mlt_audio_f32le; mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); if( abs( o_pos - pdata->prev_o_pos ) > 1 ) { // Assume this is a new clip and restart // Use original position so that transitions between clips are detected. pdata->reset = 1; mlt_log_info( MLT_FILTER_SERVICE( filter ), "Reset. Old Pos: %d\tNew Pos: %d\n", pdata->prev_o_pos, o_pos ); } check_for_reset( filter, *channels, *frequency ); if( o_pos != pdata->prev_o_pos ) { // Only analyze the audio is the producer is not paused. analyze_audio( filter, *buffer, *samples, *frequency ); } double start_coeff = pdata->start_gain > -90.0 ? pow(10.0, pdata->start_gain / 20.0) : 0.0; double end_coeff = pdata->end_gain > -90.0 ? pow(10.0, pdata->end_gain / 20.0) : 0.0; double coeff_factor = pow( (end_coeff / start_coeff), 1.0 / (double)*samples ); double coeff = start_coeff; float* p = *buffer; int s = 0; int c = 0; for( s = 0; s < *samples; s++ ) { coeff = coeff * coeff_factor; for ( c = 0; c < *channels; c++ ) { *p = *p * coeff; p++; } } pdata->prev_o_pos = o_pos; mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); return 0; }
static ssize_t frame_cache_frame_index( frame_cache self, mlt_position position ) { if ( self->frames_total == 0 ) return -1; mlt_frame first_frame = self->frames[ self->start_pos ]; mlt_position first_frame_position = mlt_frame_original_position( first_frame ); mlt_position last_frame_position = first_frame_position + self->frames_total - 1; if ( position >= first_frame_position && position <= last_frame_position ) { int offset = position - first_frame_position; return ( self->start_pos + offset ) % self->size; } return -1; }
int refresh_qimage( producer_qimage self, mlt_frame frame ) { // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_producer producer = &self->parent; mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); // Check if user wants us to reload the image if ( mlt_properties_get_int( producer_props, "force_reload" ) ) { self->qimage = NULL; self->current_image = NULL; mlt_properties_set_int( producer_props, "force_reload", 0 ); } // Get the time to live for each frame double ttl = mlt_properties_get_int( producer_props, "ttl" ); // Get the original position of this frame mlt_position position = mlt_frame_original_position( frame ); position += mlt_producer_get_in( producer ); // Image index int image_idx = ( int )floor( ( double )position / ttl ) % self->count; // Key for the cache char image_key[ 10 ]; sprintf( image_key, "%d", image_idx ); int disable_exif = mlt_properties_get_int( producer_props, "disable_exif" ); if ( app == NULL ) { if ( qApp ) { app = qApp; } else { #ifdef linux if ( getenv("DISPLAY") == 0 ) { mlt_log_panic( MLT_PRODUCER_SERVICE( producer ), "Error, cannot render titles without an X11 environment.\nPlease either run melt from an X session or use a fake X server like xvfb:\nxvfb-run -a melt (...)\n" ); return -1; } #endif int argc = 1; char* argv[1]; argv[0] = (char*) "xxx"; app = new QApplication( argc, argv ); const char *localename = mlt_properties_get_lcnumeric( MLT_SERVICE_PROPERTIES( MLT_PRODUCER_SERVICE( producer ) ) ); QLocale::setDefault( QLocale( localename ) ); } } if ( image_idx != self->qimage_idx ) self->qimage = NULL; if ( !self->qimage || mlt_properties_get_int( producer_props, "_disable_exif" ) != disable_exif ) { self->current_image = NULL; QImage *qimage = new QImage( QString::fromUtf8( mlt_properties_get_value( self->filenames, image_idx ) ) ); self->qimage = qimage; if ( !qimage->isNull( ) ) { // Read the exif value for this file if ( !disable_exif ) qimage = reorient_with_exif( self, image_idx, qimage ); // Register qimage for destruction and reuse mlt_cache_item_close( self->qimage_cache ); mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage", qimage, 0, ( mlt_destructor )qimage_delete ); self->qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" ); self->qimage_idx = image_idx; // Store the width/height of the qimage self->current_width = qimage->width( ); self->current_height = qimage->height( ); mlt_events_block( producer_props, NULL ); mlt_properties_set_int( producer_props, "meta.media.width", self->current_width ); mlt_properties_set_int( producer_props, "meta.media.height", self->current_height ); mlt_properties_set_int( producer_props, "_disable_exif", disable_exif ); mlt_events_unblock( producer_props, NULL ); } else { delete qimage; self->qimage = NULL; } } // Set width/height of frame mlt_properties_set_int( properties, "width", self->current_width ); mlt_properties_set_int( properties, "height", self->current_height ); return image_idx; }
static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; /* Obtain properties of frame */ mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); /* Obtain the producer for this frame */ producer_ktitle self = mlt_properties_get_data( properties, "producer_kdenlivetitle", NULL ); /* Obtain properties of producer */ mlt_producer producer = &self->parent; mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); if ( mlt_properties_get_int( properties, "rescale_width" ) > 0 ) *width = mlt_properties_get_int( properties, "rescale_width" ); if ( mlt_properties_get_int( properties, "rescale_height" ) > 0 ) *height = mlt_properties_get_int( properties, "rescale_height" ); mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); /* Allocate the image */ if ( mlt_properties_get_int( producer_props, "force_reload" ) ) { if ( mlt_properties_get_int( producer_props, "force_reload" ) > 1 ) read_xml( producer_props ); mlt_properties_set_int( producer_props, "force_reload", 0 ); drawKdenliveTitle( self, frame, *format, *width, *height, mlt_frame_original_position( frame ), 1 ); } else { drawKdenliveTitle( self, frame, *format, *width, *height, mlt_frame_original_position( frame ), 0 ); } // Get width and height (may have changed during the refresh) *width = mlt_properties_get_int( properties, "width" ); *height = mlt_properties_get_int( properties, "height" ); *format = self->format; if ( self->current_image ) { // Clone the image and the alpha int image_size = mlt_image_format_size( self->format, self->current_width, self->current_height, NULL ); uint8_t *image_copy = mlt_pool_alloc( image_size ); // We use height-1 because mlt_image_format_size() uses height + 1. // XXX Remove -1 when mlt_image_format_size() is changed. memcpy( image_copy, self->current_image, mlt_image_format_size( self->format, self->current_width, self->current_height - 1, NULL ) ); // Now update properties so we free the copy after mlt_frame_set_image( frame, image_copy, image_size, mlt_pool_release ); // We're going to pass the copy on *buffer = image_copy; // Clone the alpha channel if ( self->current_alpha ) { image_copy = mlt_pool_alloc( self->current_width * self->current_height ); memcpy( image_copy, self->current_alpha, self->current_width * self->current_height ); mlt_frame_set_alpha( frame, image_copy, self->current_width * self->current_height, mlt_pool_release ); } } else { error = 1; } mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); return error; }
static int producer_get_audio( mlt_frame frame, int16_t** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples ) { mlt_producer producer = (mlt_producer)mlt_frame_pop_audio( frame ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); char* sound = mlt_properties_get( producer_properties, "sound" ); double fps = mlt_producer_get_fps( producer ); mlt_position position = mlt_frame_original_position( frame ); int size = 0; int do_beep = 0; time_info info; if( fps == 0 ) fps = 25; // Correct the returns if necessary *format = mlt_audio_float; *frequency = *frequency <= 0 ? 48000 : *frequency; *channels = *channels <= 0 ? 2 : *channels; *samples = *samples <= 0 ? mlt_sample_calculator( fps, *frequency, position ) : *samples; // Allocate the buffer size = *samples * *channels * sizeof( float ); *buffer = mlt_pool_alloc( size ); mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); get_time_info( producer, frame, &info ); // Determine if this should be a tone or silence. if( strcmp( sound, "none") ) { if( !strcmp( sound, "2pop" ) ) { mlt_position out = mlt_properties_get_int( producer_properties, "out" ); mlt_position frames = out - position; if( frames == ( info.fps * 2 ) ) { do_beep = 1; } } else if( !strcmp( sound, "frame0" ) ) { if( info.frames == 0 ) { do_beep = 1; } } } if( do_beep ) { fill_beep( producer_properties, (float*)*buffer, *frequency, *channels, *samples ); } else { // Fill silence. memset( *buffer, 0, size ); } mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); // Set the buffer for destruction mlt_frame_set_audio( frame, *buffer, *format, size, mlt_pool_release ); return 0; }
int refresh_qimage( producer_qimage self, mlt_frame frame ) { // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_producer producer = &self->parent; mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); // Check if user wants us to reload the image if ( mlt_properties_get_int( producer_props, "force_reload" ) ) { self->qimage = NULL; self->current_image = NULL; mlt_properties_set_int( producer_props, "force_reload", 0 ); } // Get the time to live for each frame double ttl = mlt_properties_get_int( producer_props, "ttl" ); // Get the original position of this frame mlt_position position = mlt_frame_original_position( frame ); position += mlt_producer_get_in( producer ); // Image index int image_idx = ( int )floor( ( double )position / ttl ) % self->count; int disable_exif = mlt_properties_get_int( producer_props, "disable_exif" ); if ( !createQApplicationIfNeeded( MLT_PRODUCER_SERVICE(producer) ) ) return -1; if ( image_idx != self->qimage_idx ) self->qimage = NULL; if ( !self->qimage || mlt_properties_get_int( producer_props, "_disable_exif" ) != disable_exif ) { self->current_image = NULL; QImage *qimage = new QImage( QString::fromUtf8( mlt_properties_get_value( self->filenames, image_idx ) ) ); self->qimage = qimage; if ( !qimage->isNull( ) ) { // Read the exif value for this file if ( !disable_exif ) qimage = reorient_with_exif( self, image_idx, qimage ); // Register qimage for destruction and reuse mlt_cache_item_close( self->qimage_cache ); mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage", qimage, 0, ( mlt_destructor )qimage_delete ); self->qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" ); self->qimage_idx = image_idx; // Store the width/height of the qimage self->current_width = qimage->width( ); self->current_height = qimage->height( ); mlt_events_block( producer_props, NULL ); mlt_properties_set_int( producer_props, "meta.media.width", self->current_width ); mlt_properties_set_int( producer_props, "meta.media.height", self->current_height ); mlt_properties_set_int( producer_props, "_disable_exif", disable_exif ); mlt_events_unblock( producer_props, NULL ); } else { delete qimage; self->qimage = NULL; } } // Set width/height of frame mlt_properties_set_int( properties, "width", self->current_width ); mlt_properties_set_int( properties, "height", self->current_height ); return image_idx; }
static int refresh_pixbuf( producer_pixbuf self, mlt_frame frame ) { // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_producer producer = &self->parent; mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); // Check if user wants us to reload the image if ( mlt_properties_get_int( producer_props, "force_reload" ) ) { self->pixbuf = NULL; self->image = NULL; mlt_properties_set_int( producer_props, "force_reload", 0 ); } // Get the time to live for each frame double ttl = mlt_properties_get_int( producer_props, "ttl" ); // Get the original position of this frame mlt_position position = mlt_frame_original_position( frame ); position += mlt_producer_get_in( producer ); // Image index int loop = mlt_properties_get_int( producer_props, "loop" ); int current_idx; if (loop) { current_idx = ( int )floor( ( double )position / ttl ) % self->count; } else { current_idx = MIN(( double )position / ttl, self->count - 1); } // Key for the cache char image_key[ 10 ]; sprintf( image_key, "%d", current_idx ); int disable_exif = mlt_properties_get_int( producer_props, "disable_exif" ); if ( current_idx != self->pixbuf_idx ) self->pixbuf = NULL; if ( !self->pixbuf || mlt_properties_get_int( producer_props, "_disable_exif" ) != disable_exif ) { GError *error = NULL; self->image = NULL; pthread_mutex_lock( &g_mutex ); self->pixbuf = gdk_pixbuf_new_from_file( mlt_properties_get_value( self->filenames, current_idx ), &error ); if ( self->pixbuf ) { // Read the exif value for this file if ( !disable_exif ) self->pixbuf = reorient_with_exif( self, current_idx, self->pixbuf ); // Register this pixbuf for destruction and reuse mlt_cache_item_close( self->pixbuf_cache ); mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf", self->pixbuf, 0, ( mlt_destructor )g_object_unref ); self->pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" ); self->pixbuf_idx = current_idx; // Store the width/height of the pixbuf temporarily self->width = gdk_pixbuf_get_width( self->pixbuf ); self->height = gdk_pixbuf_get_height( self->pixbuf ); mlt_events_block( producer_props, NULL ); mlt_properties_set_int( producer_props, "meta.media.width", self->width ); mlt_properties_set_int( producer_props, "meta.media.height", self->height ); mlt_properties_set_int( producer_props, "_disable_exif", disable_exif ); mlt_events_unblock( producer_props, NULL ); } pthread_mutex_unlock( &g_mutex ); } // Set width/height of frame mlt_properties_set_int( properties, "width", self->width ); mlt_properties_set_int( properties, "height", self->height ); return current_idx; }