X-Git-Url: https://git.cinelerra-gg.org/git/?a=blobdiff_plain;f=cinelerra-5.1%2Fcinelerra%2Findexfile.C;fp=cinelerra-5.1%2Fcinelerra%2Findexfile.C;h=5958efa5f85a08aaee6c6b50957d4521d1d95c41;hb=30bdb85eb33a8ee7ba675038a86c6be59c43d7bd;hp=0000000000000000000000000000000000000000;hpb=52fcc46226f9df46f9ce9d0566dc568455a7db0b;p=goodguy%2Fhistory.git diff --git a/cinelerra-5.1/cinelerra/indexfile.C b/cinelerra-5.1/cinelerra/indexfile.C new file mode 100644 index 00000000..5958efa5 --- /dev/null +++ b/cinelerra-5.1/cinelerra/indexfile.C @@ -0,0 +1,913 @@ + +/* + * CINELERRA + * Copyright (C) 1997-2014 Adam Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "arender.h" +#include "asset.h" +#include "bcsignals.h" +#include "bctimer.h" +#include "cache.h" +#include "clip.h" +#include "condition.h" +#include "edit.h" +#include "edl.h" +#include "edlsession.h" +#include "errorbox.h" +#include "file.h" +#include "filesystem.h" +#include "filexml.h" +#include "indexable.h" +#include "indexfile.h" +#include "indexstate.h" +#include "indexthread.h" +#include "language.h" +#include "localsession.h" +#include "mainprogress.h" +#include "mwindowgui.h" +#include "mwindow.h" +#include "preferences.h" +#include "removefile.h" +#include "renderengine.h" +#include "resourcepixmap.h" +#include "samples.h" +#include "theme.h" +#include "timelinepane.h" +#include "trackcanvas.h" +#include "tracks.h" +#include "transportque.h" +#include "vframe.h" + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// check for isofs volume_id for dvd/cdrom + +static int udf_volume_id(const char *path, char *fname) +{ + struct stat st; + if( stat(path,&st) ) return 1; + // search mounted devices + FILE *fp = fopen("/proc/mounts","r"); + if( !fp ) return 1; + + int result = 1; + while( result && !feof(fp) && !ferror(fp) ) { + char devpath[BCTEXTLEN], mpath[BCTEXTLEN]; + char options[BCTEXTLEN], line[BCTEXTLEN]; + char fstype[64], zero1[16], zero2[16]; + if( !fgets(&line[0], sizeof(line)-1, fp) ) break; + int n = sscanf(&line[0], "%s %s %s %s %s %s\n", + devpath, mpath, fstype, options, zero1, zero2); + if( n != 6 ) continue; + // check udf filesystems + if( strcmp(fstype,"udf") != 0 ) continue; + struct stat dst; + if( stat(devpath,&dst) ) continue; + if( st.st_dev != dst.st_rdev ) continue; + int fd = open(devpath,O_RDONLY); + if( fd < 0 ) continue; + struct iso_primary_descriptor id; + if( lseek(fd,0x8000,SEEK_SET) == 0x8000 ) + n = read(fd,&id,sizeof(id)); + close(fd); + if( n != sizeof(id) ) continue; + // look for magic number + if( strncmp(ISO_STANDARD_ID,id.id,sizeof(id.id)) ) continue; + // look for volume_id + if( !isalnum(id.volume_id[0]) ) continue; + char *bp = &id.volume_id[0], *cp = fname; + for( int i=0; i<(int)sizeof(id.volume_id); ++i ) *cp++ = *bp++; + while( --cp>=fname && *cp==' ' ) *cp = 0; + if( !*fname ) continue; + // fname = volume_id _ creation_date + ++cp; *cp++ = '_'; bp = &id.creation_date[0]; + for( int i=0; i<(int)sizeof(id.creation_date)-1; ++i ) { + if( !isdigit(*bp) ) break; + *cp++ = *bp++; + } + *cp++ = 0; + if( cp-fname > 4 ) result = 0; + } + + fclose(fp); + return result; +} + +// Use native sampling rates for files so the same index can be used in +// multiple projects. + +IndexFile::IndexFile(MWindow *mwindow) +{ +//printf("IndexFile::IndexFile 1\n"); + reset(); + this->mwindow = mwindow; +//printf("IndexFile::IndexFile 2\n"); + redraw_timer = new Timer; +} + +IndexFile::IndexFile(MWindow *mwindow, + Indexable *indexable) +{ +//printf("IndexFile::IndexFile 2\n"); + reset(); + this->mwindow = mwindow; + this->indexable = indexable; + redraw_timer = new Timer; + + if(indexable) + { + indexable->add_user(); + source_channels = indexable->get_audio_channels(); + source_samplerate = indexable->get_sample_rate(); + source_length = indexable->get_audio_samples(); + } +} + +IndexFile::~IndexFile() +{ +//printf("IndexFile::~IndexFile 1\n"); + delete redraw_timer; + if(indexable) indexable->remove_user(); + close_source(); +} + +void IndexFile::reset() +{ + fd = 0; + source = 0; + interrupt_flag = 0; + source_length = 0; + source_channels = 0; + indexable = 0; + render_engine = 0; + cache = 0; +} + +IndexState* IndexFile::get_state() +{ + IndexState *index_state = 0; + if(indexable) index_state = indexable->index_state; + return index_state; +} + + + +int IndexFile::open_index() +{ + IndexState *index_state = 0; + int result = 0; + +// use buffer if being built + index_state = get_state(); + + if(index_state->index_status == INDEX_BUILDING) + { +// use buffer + result = 0; + } + else + if(!(result = open_file())) + { +// opened existing file + if(read_info()) + { + result = 1; + close_index(); + } + else + { + index_state->index_status = INDEX_READY; + } + } + else + { + result = 1; + } + + return result; +} + +void IndexFile::delete_index(Preferences *preferences, + Indexable *indexable, const char *suffix) +{ + char index_filename[BCTEXTLEN]; + char source_filename[BCTEXTLEN]; + const char *path = indexable->path; + + get_index_filename(source_filename, + preferences->index_directory, + index_filename, path, suffix); +//printf("IndexFile::delete_index %s %s\n", source_filename, index_filename); + remove_file(index_filename); +} + +int IndexFile::open_file() +{ + int result = 0; + const int debug = 0; + const char *path = indexable->path; + + +//printf("IndexFile::open_file %f\n", indexable->get_frame_rate()); + + get_index_filename(source_filename, + mwindow->preferences->index_directory, + index_filename, + path); + + if(debug) printf("IndexFile::open_file %d index_filename=%s\n", + __LINE__, + index_filename); + fd = fopen(index_filename, "rb"); + if( fd != 0 ) + { +// Index file already exists. +// Get its last size without changing the real asset status. + Indexable *test_indexable = new Indexable(0); + if(indexable) + test_indexable->copy_indexable(indexable); + read_info(test_indexable); + IndexState *index_state = test_indexable->index_state; + + FileSystem fs; + if(fs.get_date(index_filename) < fs.get_date(test_indexable->path)) + { + if(debug) printf("IndexFile::open_file %d index_date=%jd source_date=%jd\n", + __LINE__, + fs.get_date(index_filename), + fs.get_date(test_indexable->path)); + +// index older than source + result = 2; + fclose(fd); + fd = 0; + } + else + if(fs.get_size(test_indexable->path) != index_state->index_bytes) + { +// source file is a different size than index source file + if(debug) printf("IndexFile::open_file %d index_size=%jd source_size=%jd\n", + __LINE__, + index_state->index_bytes, + fs.get_size(test_indexable->path)); + result = 2; + fclose(fd); + fd = 0; + } + else + { + if(debug) printf("IndexFile::open_file %d\n", + __LINE__); + fseek(fd, 0, SEEK_END); + file_length = ftell(fd); + fseek(fd, 0, SEEK_SET); + result = 0; + } + test_indexable->Garbage::remove_user(); + } + else + { +// doesn't exist + if(debug) printf("IndexFile::open_file %d index_filename=%s doesn't exist\n", + __LINE__, + index_filename); + result = 1; + } + + return result; +} + +int IndexFile::open_source() +{ +//printf("IndexFile::open_source %p %s\n", asset, asset->path); + int result = 0; + if(indexable && indexable->is_asset) + { + if(!source) source = new File; + + Asset *asset = (Asset*)indexable; + if(source->open_file(mwindow->preferences, + asset, 1, 0)) + { + //printf("IndexFile::open_source() Couldn't open %s.\n", asset->path); + result = 1; + } + else + { + FileSystem fs; + asset->index_state->index_bytes = fs.get_size(asset->path); + source_length = source->get_audio_length(); + } + } + else + { + TransportCommand command; + command.command = NORMAL_FWD; + command.get_edl()->copy_all((EDL*)indexable); + command.change_type = CHANGE_ALL; + command.realtime = 0; + cache = new CICache(mwindow->preferences); + render_engine = new RenderEngine(0, + mwindow->preferences, 0, 0, 0); + render_engine->set_acache(cache); + render_engine->arm_command(&command); + FileSystem fs; + indexable->index_state->index_bytes = fs.get_size(indexable->path); + } + + return result; +} + +void IndexFile::close_source() +{ + delete source; + source = 0; + + delete render_engine; + render_engine = 0; + + delete cache; + cache = 0; +} + +int64_t IndexFile::get_required_scale() +{ + int64_t result = 1; + + +// get scale of index file +// Total peaks which may be stored in buffer + int64_t peak_count = mwindow->preferences->index_size / + (2 * sizeof(float) * source_channels); + for(result = 1; + source_length / result > peak_count; + result *= 2) + ; + +// Takes too long to draw from source on a CDROM. Make indexes for +// everything. + + return result; +} + +int IndexFile::get_index_filename(char *source_filename, + char *index_directory, + char *index_filename, + const char *input_filename, + const char *suffix) +{ + const char *input_fn = input_filename; + char volume_id[BCTEXTLEN]; +// Replace mount/directory with volume_id if isofs + if( !udf_volume_id(input_filename, volume_id) ) + { + char *cp = strrchr((char*)input_filename,'/'); + if( cp ) input_fn = cp + 1; + for( cp=volume_id; *cp; ++cp ); + *cp++ = '_'; strcpy(cp, input_fn); + input_fn = volume_id; + } +// Replace slashes and dots + int i, j; + int len = strlen(input_fn); + for(i = 0, j = 0; i < len; i++) + { + if(input_fn[i] != '/' && + input_fn[i] != '.') + source_filename[j++] = input_fn[i]; + else + { + if(i > 0) + source_filename[j++] = '_'; + } + } + source_filename[j] = 0; + FileSystem fs; + fs.join_names(index_filename, index_directory, source_filename); + strcat(index_filename, suffix ? suffix : ".idx"); + return 0; +} + +int IndexFile::interrupt_index() +{ + interrupt_flag = 1; + return 0; +} + +// Read data into buffers + +int IndexFile::create_index(MainProgressBar *progress) +{ + int result = 0; +SET_TRACE + + interrupt_flag = 0; + +// open the source file + if(open_source()) return 1; + +SET_TRACE + + get_index_filename(source_filename, + mwindow->preferences->index_directory, + index_filename, + indexable->path); + +SET_TRACE + +// Some file formats have their own sample index. +// Test for index in stream table of contents + if(source && !source->get_index(index_filename)) + { + IndexState *index_state = get_state(); + index_state->index_status = INDEX_READY; + redraw_edits(1); + } + else +// Build index from scratch + { +SET_TRACE + +// Indexes are now built for everything since it takes too long to draw +// from CDROM source. + +// get amount to read at a time in floats + int64_t buffersize = 65536; + char string[BCTEXTLEN]; + sprintf(string, _("Creating %s."), index_filename); + + progress->update_title(string); + progress->update_length(source_length); + redraw_timer->update(); +SET_TRACE + +// thread out index thread + IndexThread *index_thread = new IndexThread(mwindow, + this, + index_filename, + buffersize, + source_length); + index_thread->start_build(); + +// current sample in source file + int64_t position = 0; + int64_t fragment_size = buffersize; + int current_buffer = 0; + + +// pass through file once +// printf("IndexFile::create_index %d source_length=%jd source=%p progress=%p\n", +// __LINE__, +// source_length, +// source, +// progress); +SET_TRACE + while(position < source_length && !result) + { +SET_TRACE + if(source_length - position < fragment_size && fragment_size == buffersize) fragment_size = source_length - position; + + index_thread->input_lock[current_buffer]->lock("IndexFile::create_index 1"); + index_thread->input_len[current_buffer] = fragment_size; + +SET_TRACE + int cancelled = progress->update(position); +//printf("IndexFile::create_index cancelled=%d\n", cancelled); +SET_TRACE + if(cancelled || + index_thread->interrupt_flag || + interrupt_flag) + { + result = 3; + } + + +SET_TRACE + if(source && !result) + { +SET_TRACE + for(int channel = 0; + !result && channel < source_channels; + channel++) + { +// Read from source file + source->set_audio_position(position); + source->set_channel(channel); + + if(source->read_samples( + index_thread->buffer_in[current_buffer][channel], + fragment_size)) + result = 1; + } +SET_TRACE + } + else + if(render_engine && !result) + { +SET_TRACE + if(render_engine->arender) + { + result = render_engine->arender->process_buffer( + index_thread->buffer_in[current_buffer], + fragment_size, + position); + } + else + { + for(int i = 0; i < source_channels; i++) + { + bzero(index_thread->buffer_in[current_buffer][i]->get_data(), + fragment_size * sizeof(double)); + } + } +SET_TRACE + } +SET_TRACE + +// Release buffer to thread + if(!result) + { + index_thread->output_lock[current_buffer]->unlock(); + current_buffer++; + if(current_buffer >= TOTAL_INDEX_BUFFERS) current_buffer = 0; + position += fragment_size; + } + else + { + index_thread->input_lock[current_buffer]->unlock(); + } +SET_TRACE + } + + +// end thread cleanly + index_thread->input_lock[current_buffer]->lock("IndexFile::create_index 2"); + index_thread->last_buffer[current_buffer] = 1; + index_thread->output_lock[current_buffer]->unlock(); + index_thread->stop_build(); + + + delete index_thread; + + } + + + + close_source(); + + + + open_index(); + + close_index(); + + mwindow->edl->set_index_file(indexable); + return 0; +} + + + +int IndexFile::redraw_edits(int force) +{ + int64_t difference = redraw_timer->get_scaled_difference(1000); + + if(difference > 250 || force) + { + redraw_timer->update(); + mwindow->gui->lock_window("IndexFile::redraw_edits"); + mwindow->edl->set_index_file(indexable); + mwindow->gui->draw_indexes(indexable); + mwindow->gui->unlock_window(); + } + return 0; +} + + + + +int IndexFile::draw_index( + TrackCanvas *canvas, + ResourcePixmap *pixmap, + Edit *edit, + int x, + int w) +{ + const int debug = 0; + IndexState *index_state = get_state(); + int pane_number = canvas->pane->number; +//index_state->dump(); + +SET_TRACE + if(debug) printf("IndexFile::draw_index %d\n", __LINE__); + if(index_state->index_zoom == 0) + { + printf(_("IndexFile::draw_index: index has 0 zoom\n")); + return 0; + } + if(debug) printf("IndexFile::draw_index %d\n", __LINE__); + +// test channel number + if(edit->channel > source_channels) return 1; + if(debug) printf("IndexFile::draw_index %d source_samplerate=%d " + "w=%d samplerate=%jd zoom_sample=%jd\n", + __LINE__, source_samplerate, w, + mwindow->edl->session->sample_rate, + mwindow->edl->local_session->zoom_sample); + +// calculate a virtual x where the edit_x should be in floating point + double virtual_edit_x = 1.0 * + edit->track->from_units(edit->startproject) * + mwindow->edl->session->sample_rate / + mwindow->edl->local_session->zoom_sample - + mwindow->edl->local_session->view_start[pane_number]; + +// samples in segment to draw relative to asset + double asset_over_session = (double)source_samplerate / + mwindow->edl->session->sample_rate; + int64_t startsource = (int64_t)(((pixmap->pixmap_x - virtual_edit_x + x) * + mwindow->edl->local_session->zoom_sample + + edit->startsource) * + asset_over_session); +// just in case we get a numerical error + if (startsource < 0) startsource = 0; + int64_t length = (int64_t)(w * + mwindow->edl->local_session->zoom_sample * + asset_over_session); + int64_t lengthindex = length / index_state->index_zoom * 2; + int64_t startindex = startsource / index_state->index_zoom * 2; +// length of index to read in floats +// length of index available in floats + int64_t endindex = index_state->index_status == INDEX_BUILDING ? + index_state->get_channel_used(edit->channel) * 2 : + index_state->get_index_size(edit->channel); +// Clamp length of index to read by available data + if(startindex + lengthindex >= endindex ) + lengthindex = endindex - startindex; + if( lengthindex <= 0 ) return 0; + +// Actual length read from file in bytes + int64_t length_read; +// Start and length of fragment to read from file in bytes. + int64_t startfile, lengthfile; + float *buffer = 0; + int buffer_shared = 0; + int center_pixel = mwindow->edl->local_session->zoom_track / 2; + if( mwindow->edl->session->show_titles ) + center_pixel += mwindow->theme->get_image("title_bg_data")->get_h(); + //int miny = center_pixel - mwindow->edl->local_session->zoom_track / 2; + //int maxy = center_pixel + mwindow->edl->local_session->zoom_track / 2; + int x1 = 0, y1, y2; +// get zoom_sample relative to index zoomx + double index_frames_per_pixel = mwindow->edl->local_session->zoom_sample / + index_state->index_zoom * + asset_over_session; + + + + if(index_state->index_status == INDEX_BUILDING) + { +// index is in RAM, being built + buffer = index_state->get_channel_buffer(edit->channel); + if( !buffer ) return 0; + buffer += startindex; + buffer_shared = 1; + } + else + { +// add channel offset + startindex += index_state->get_index_offset(edit->channel); +// index is stored in a file + buffer = new float[lengthindex + 1]; + buffer_shared = 0; + startfile = index_state->index_start + startindex * sizeof(float); + lengthfile = lengthindex * sizeof(float); + length_read = 0; + + if(startfile < file_length) + { + fseek(fd, startfile, SEEK_SET); + + length_read = lengthfile; + if(startfile + length_read > file_length) + length_read = file_length - startfile; + + (void)fread(buffer, length_read + sizeof(float), 1, fd); + } + + if(length_read < lengthfile) { + int pos = length_read / sizeof(float); + int file_length = lengthfile / sizeof(float); + while( pos < file_length ) buffer[pos++] = 0; + } + } + + canvas->set_color(mwindow->theme->audio_color); + + double current_frame = 0; + float highsample = buffer[0]; + float lowsample = buffer[1]; + int prev_y1 = center_pixel; + int prev_y2 = center_pixel; + int first_frame = 1; + int zoom_y = mwindow->edl->local_session->zoom_y, zoom_y2 = zoom_y / 2; + int max_y = canvas->get_h(); + int zmax_y = center_pixel + zoom_y2 - 1; + if( zmax_y < max_y ) max_y = zmax_y; +SET_TRACE + + for(int bufferposition = 0; + bufferposition < lengthindex; + bufferposition += 2) + { + if(current_frame >= index_frames_per_pixel) + { + + int y1 = (int)(center_pixel - highsample * zoom_y2); + int y2 = (int)(center_pixel - lowsample * zoom_y2); + CLAMP(y1, 0, max_y); int next_y1 = y1; + CLAMP(y2, 0, max_y); int next_y2 = y2; +//printf("draw_line (%f,%f) = %d,%d, %d,%d\n", lowsample, highsample, x1 + x, y1, x1 + x, y2); + +//SET_TRACE +// A different algorithm has to be used if it's 1 sample per pixel and the +// index is used. Now the min and max values are equal so we join the max samples. + if(mwindow->edl->local_session->zoom_sample == 1) + { + canvas->draw_line(x1 + x - 1, prev_y1, x1 + x, y1, pixmap); + } + else + { +// Extend line height if it doesn't connect to previous line + if(!first_frame) + { + if(y1 > prev_y2) y1 = prev_y2 + 1; + if(y2 < prev_y1) y2 = prev_y1 - 1; + } + else + { + first_frame = 0; + } + + + + canvas->draw_line(x1 + x, y1, x1 + x, y2, pixmap); + } + current_frame -= index_frames_per_pixel; + x1++; + prev_y1 = next_y1; + prev_y2 = next_y2; + highsample = buffer[bufferposition]; + lowsample = buffer[bufferposition + 1]; + } + + current_frame++; + highsample = MAX(highsample, buffer[bufferposition]); + lowsample = MIN(lowsample, buffer[bufferposition + 1]); + } +SET_TRACE + +// Get last column + if(current_frame) + { + y1 = (int)(center_pixel - highsample * zoom_y2); + y2 = (int)(center_pixel - lowsample * zoom_y2); + canvas->draw_line(x1 + x, y1, x1 + x, y2, pixmap); + } + +SET_TRACE + + + + if(!buffer_shared) delete [] buffer; +SET_TRACE + if(debug) printf("IndexFile::draw_index %d\n", __LINE__); + return 0; +} + +int IndexFile::close_index() +{ + if(fd) + { + fclose(fd); + fd = 0; + } + return 0; +} + +int IndexFile::remove_index() +{ + IndexState *index_state = get_state(); + if(index_state->index_status == INDEX_READY || + index_state->index_status == INDEX_NOTTESTED) + { + close_index(); + remove(index_filename); + } + return 0; +} + +int IndexFile::read_info(Indexable *test_indexable) +{ + const int debug = 0; + +// Store format in actual asset. +// If it's a nested EDL, we never want the format, just the index info. + if(!test_indexable) test_indexable = indexable; + if(!test_indexable) return 1; + + IndexState * index_state = test_indexable->index_state; + if(index_state->index_status == INDEX_NOTTESTED) + { +// read start of index data + int temp = fread((char*)&(index_state->index_start), sizeof(int64_t), 1, fd); +//printf("IndexFile::read_info %d %f\n", __LINE__, test_indexable->get_frame_rate()); + + if(!temp) return 1; +// read test_indexable info from index + char *data; + + data = new char[index_state->index_start]; + temp = fread(data, index_state->index_start - sizeof(int64_t), 1, fd); + if(!temp) return 1; + + data[index_state->index_start - sizeof(int64_t)] = 0; + FileXML xml; + xml.read_from_string(data); + delete [] data; + + + +// Read the file format & index state. + if(test_indexable->is_asset) + { + Asset *asset = (Asset*)test_indexable; + asset->read(&xml); + +//printf("IndexFile::read_info %d %f\n", __LINE__, asset->get_frame_rate()); + + if(asset->format == FILE_UNKNOWN) + { +if(debug) printf("IndexFile::read_info %d\n", __LINE__); + return 1; + } + } + else + { +// Read only the index state for a nested EDL + int result = 0; +if(debug) printf("IndexFile::read_info %d\n", __LINE__); + while(!result) + { + result = xml.read_tag(); + if(!result) + { + if(xml.tag.title_is("INDEX")) + { + index_state->read_xml(&xml, source_channels); +if(debug) printf("IndexFile::read_info %d\n", __LINE__); +if(debug) index_state->dump(); + result = 1; + } + } + } + } + } + + return 0; +} + + + + + + + +