rework android-rmt display, add a few buttons
[goodguy/history.git] / cinelerra-5.0 / cinelerra / indexstate.C
index 52eb0c8a802ae0e0888e4a8c9eeb60969b8b7026..6de431985f1a828955e30f8153a954c46652f541 100644 (file)
  */
 
 
+#include "arraylist.h"
 #include "asset.h"
+#include "clip.h"
 #include "filexml.h"
 #include "format.inc"
+#include "indexfile.h"
 #include "indexstate.h"
-#include "indexstate.inc"
 #include "language.h"
+#include "mainerror.h"
+#include "mutex.h"
 
 #include <stdio.h>
+#include <string.h>
 
+int IndexMarks::find(int64_t no)
+{
+       int l = -1, r = size();
+        while( (r - l) > 1 ) {
+               int i = (l+r) >> 1;
+               if( no > values[i].no ) l = i; else r = i;
+       }
+       return l;
+}
 
-IndexState::IndexState()
+IndexChannel::IndexChannel(IndexState *state, int64_t length)
 {
-       reset();
+       this->state = state;
+       this->length = length;
+       min = max = 0;
+       bfr = inp = 0;
+       size = 0;
+       zidx = 0;
 }
 
-IndexState::~IndexState()
+IndexChannel::~IndexChannel()
 {
-       delete [] index_offsets;
-       delete [] index_sizes;
-// Don't delete index buffer since it is shared with the index thread.
+       delete [] bfr;
 }
 
-void IndexState::reset()
+void IndexChannel::put_entry()
+{
+       inp->min = min;
+       inp->max = max;
+       ++inp;
+       max = -1; min = 1;
+       zidx = 0;
+}
+
+int64_t IndexChannel::pos()
+{
+       return used() * state->index_zoom + zidx;
+}
+
+void IndexChannel::pad_data(int64_t pos)
+{
+       CLAMP(pos, 0, length);
+       while( this->pos() < pos ) put_entry();
+       inp = bfr + pos / state->index_zoom;
+       zidx = pos % state->index_zoom;
+}
+
+void IndexState::reset_index()
 {
        index_status = INDEX_NOTTESTED;
-       index_start = old_index_end = index_end = 0;
-       index_offsets = 0;
-       index_sizes = 0;
+       index_start = 0;
        index_zoom = 0;
        index_bytes = 0;
-       index_buffer = 0;
-       channels = 0;
+       index_entries.remove_all_objects();
+       index_channels.remove_all_objects();
 }
 
-void IndexState::dump()
+void IndexState::reset_markers()
 {
-       printf("IndexState::dump this=%p\n", this);
-       printf("    channels=%d index_status=%d index_zoom=" _LD
-               " index_bytes=" _LD " index_offsets=%p\n",
-               channels, index_status, index_zoom,
-               index_bytes, index_offsets);
-       if(index_sizes)
-       {
-               printf("    index_sizes=");
-               for(int i = 0; i < channels; i++)
-                       printf(_LD " ", index_sizes[i]);
-               printf("\n");
-       }
+       marker_status = MARKERS_NOTTESTED;
+       video_markers.remove_all_objects();
+       audio_markers.remove_all_objects();
 }
 
-void IndexState::copy_from(IndexState *src)
+IndexState::IndexState()
+ : Garbage("IndexState")
 {
-       if(this == src) return;
-
-       delete [] index_offsets;
-       delete [] index_sizes;
-       index_offsets = 0;
-       index_sizes = 0;
-
-//printf("Asset::update_index 1 %d\n", index_status);
-       index_status = src->index_status;
-       index_zoom = src->index_zoom;    // zoom factor of index data
-       index_start = src->index_start;  // byte start of index data in the index file
-       index_bytes = src->index_bytes;  // Total bytes in source file for comparison before rebuilding the index
-       index_end = src->index_end;
-       old_index_end = src->old_index_end;      // values for index build
-       channels = src->channels;
-
-       if(src->index_offsets)
-       {
-               index_offsets = new int64_t[channels];
-               index_sizes = new int64_t[channels];
-
-               int i;
-               for(i = 0; i < channels; i++)
-               {
-// offsets of channels in index file in floats
-                       index_offsets[i] = src->index_offsets[i];  
-                       index_sizes[i] = src->index_sizes[i];
-               }
+       marker_lock = new Mutex("IndexState::marker_lock");
+       reset_index();
+       reset_markers();
+}
+
+IndexState::~IndexState()
+{
+       reset_index();
+       reset_markers();
+       delete marker_lock;
+}
+
+void IndexState::init_scan(int64_t index_length)
+{
+       index_zoom = 1;
+       int channels = index_channels.size();
+       if( !channels ) return;
+       int64_t max_samples = 0;
+       for( int ch=0; ch<channels; ++ch ) {
+               int64_t len = index_channels[ch]->length;
+               if( max_samples < len ) max_samples = len;
+       }
+       int64_t items = index_length / sizeof(IndexItem);
+       int64_t count = (items - channels) / channels + 1;
+       while( count*index_zoom < max_samples ) index_zoom *= 2;
+       for( int ch=0; ch<channels; ++ch ) {
+               IndexChannel *chn = index_channels[ch];
+               int64_t len = chn->length / index_zoom + 1;
+               chn->alloc(len);
+               chn->put_entry();
        }
+       reset_markers();
+}
 
-// pointer
-       index_buffer = src->index_buffer;
+void IndexState::dump()
+{
+       printf("IndexState::dump this=%p\n", this);
+       printf("    index_status=%d index_zoom=" _LD " index_bytes=" _LD "\n",
+               index_status, index_zoom, index_bytes);
+       printf("    index entries=%d\n", index_entries.size());
+       for( int i=0; i<index_entries.size(); ++i )
+               printf("  %d. ofs=" _LD ", sz=" _LD "\n", i,
+                       index_entries[i]->offset, index_entries[i]->size);
+       printf("\n");
 }
 
 void IndexState::write_xml(FileXML *file)
@@ -114,17 +155,13 @@ void IndexState::write_xml(FileXML *file)
        file->append_tag();
        file->append_newline();
 
-       if(index_offsets)
-       {
-               for(int i = 0; i < channels; i++)
-               {
-                       file->tag.set_title("OFFSET");
-                       file->tag.set_property("FLOAT", index_offsets[i]);
-                       file->append_tag();
-                       file->tag.set_title("SIZE");
-                       file->tag.set_property("FLOAT", index_sizes[i]);
-                       file->append_tag();
-               }
+       for( int i=0; i<index_entries.size(); ++i ) {
+               file->tag.set_title("OFFSET");
+               file->tag.set_property("FLOAT", index_entries[i]->offset);
+               file->append_tag();
+               file->tag.set_title("SIZE");
+               file->tag.set_property("FLOAT", index_entries[i]->size);
+               file->append_tag();
        }
 
        file->append_newline();
@@ -135,18 +172,8 @@ void IndexState::write_xml(FileXML *file)
 
 void IndexState::read_xml(FileXML *file, int channels)
 {
-       this->channels = channels;
-
-       delete [] index_offsets;
-       delete [] index_sizes;
-       index_offsets = new int64_t[channels];
-       index_sizes = new int64_t[channels];
-
-       for(int i = 0; i < channels; i++) 
-       {
-               index_offsets[i] = 0;
-               index_sizes[i] = 0;
-       }
+       index_entries.remove_all_objects();
+       for( int i=0; i<channels; ++i ) add_index_entry(0, 0, 0);
 
        int current_offset = 0;
        int current_size = 0;
@@ -155,102 +182,204 @@ void IndexState::read_xml(FileXML *file, int channels)
        index_zoom = file->tag.get_property("ZOOM", 1);
        index_bytes = file->tag.get_property("BYTES", (int64_t)0);
 
-       while(!result)
-       {
+       while(!result) {
                result = file->read_tag();
-               if(!result)
-               {
-                       if(file->tag.title_is("/INDEX"))
-                       {
+               if(!result) {
+                       if(file->tag.title_is("/INDEX")) {
                                result = 1;
                        }
-                       else
-                       if(file->tag.title_is("OFFSET"))
-                       {
-                               if(current_offset < channels)
-                               {
-                                       index_offsets[current_offset++] = file->tag.get_property("FLOAT", 0);
+                       else if(file->tag.title_is("OFFSET")) {
+                               if(current_offset < channels) {
+                                       int64_t offset = file->tag.get_property("FLOAT", (int64_t)0);
+                                       index_entries[current_offset++]->offset = offset;
 //printf("Asset::read_index %d %d\n", current_offset - 1, index_offsets[current_offset - 1]);
                                }
                        }
-                       else
-                       if(file->tag.title_is("SIZE"))
-                       {
-                               if(current_size < channels)
-                               {
-                                       index_sizes[current_size++] = file->tag.get_property("FLOAT", 0);
+                       else if(file->tag.title_is("SIZE")) {
+                               if(current_size < channels) {
+                                       int64_t size = file->tag.get_property("FLOAT", (int64_t)0);
+                                       index_entries[current_size++]->size = size;
                                }
                        }
                }
        }
 }
 
-int IndexState::write_index(const char *path, 
-       int data_bytes, 
-       Asset *asset,
-       int64_t length_source)
+int IndexState::write_index(const char *index_path, Asset *asset, int64_t zoom, int64_t file_bytes)
 {
-
-       FILE *file;
-       if(!(file = fopen(path, "wb")))
-       {
-// failed to create it
-               printf(_("IndexState::write_index Couldn't write index file %s to disk.\n"), 
-                       path);
+        FILE *fp = fopen(index_path, "wb");
+        if( !fp ) {
+               eprintf(_("IndexState::write_index Couldn't write index file %s to disk.\n"),
+                       index_path);
+               return 1;
        }
+       index_zoom = zoom;
+       index_bytes = file_bytes;
+       index_status = INDEX_READY;
+
+       FileXML xml;
+// write index_state as asset or directly.
+       if( asset )
+               asset->write(&xml, 1, "");
        else
-       {
-               FileXML xml;
-// Pad index start position
-               fwrite((char*)&(index_start), sizeof(int64_t), 1, file);
-
-               index_status = INDEX_READY;
-
-// Write asset encoding information in index file.
-// This also calls back into index_state to write it.
-               if(asset)
-               {
-                       asset->write(&xml, 
-                               1, 
-                               "");
-               }
-               else
-               {
-// Must write index_state directly.
-                       write_xml(&xml);
-               }
+               write_xml(&xml);
+       int64_t len = xml.length() + FileXML::xml_header_size;
+       index_start = sizeof(index_start) + len;
+       fwrite(&index_start, sizeof(index_start), 1, fp);
+       xml.write_to_file(fp);
+
+       int channels = index_entries.size();
+       int64_t max_size = 0;
+       for( int ch=0; ch<channels; ++ch ) {
+               IndexEntry *ent = index_entries[ch];
+               float *bfr = ent->bfr;
+               int64_t size = ent->size;
+               if( max_size < size ) max_size = size;
+               fwrite(bfr, sizeof(float), size, fp);
+       }
+
+       fclose(fp);
+       reset_index();
+       return 0;
+}
 
-               xml.write_to_file(file);
-               index_start = ftell(file);
-               fseek(file, 0, SEEK_SET);
-// Write index start
-               fwrite((char*)&(index_start), sizeof(int64_t), 1, file);
-               fseek(file, index_start, SEEK_SET);
-
-// Write index data
-               fwrite(index_buffer, 
-                       data_bytes, 
-                       1, 
-                       file);
-               fclose(file);
+int IndexState::write_markers(const char *index_path)
+{
+       int vid_size = video_markers.size();
+       int aud_size = audio_markers.size();
+       if( !vid_size && !aud_size ) return 0;
+
+       FILE *fp = 0;
+       char marker_path[BCTEXTLEN];
+       strcpy(marker_path, index_path);
+       char *basename = strrchr(marker_path,'/');
+       if( !basename ) basename = marker_path;
+       char *ext = strrchr(basename, '.');
+       if( ext ) {
+               strcpy(ext, ".mkr");
+               fp = fopen(marker_path, "wb");
        }
 
-// Force reread of header
-       index_status = INDEX_NOTTESTED;
-       index_end = length_source;
-       old_index_end = 0;
-       index_start = 0;
+       char version[] = MARKER_MAGIC_VERSION;
+        if( !fp || !fwrite(version, strlen(version), 1, fp) ) {
+               eprintf(_("IndexState::write_markers Couldn't write marker file %s to disk.\n"),
+                       marker_path);
+               return 1;
+       }
+
+       fwrite(&vid_size, sizeof(vid_size), 1, fp);
+       for( int vidx=0; vidx<vid_size; ++vidx ) {
+               IndexMarks &marks = *video_markers[vidx];
+               int count = marks.size();
+               fwrite(&count, sizeof(count), 1, fp);
+               fwrite(&marks[0], sizeof(marks[0]), count, fp);
+       }
+
+       fwrite(&aud_size, sizeof(aud_size), 1, fp);
+       for( int aidx=0; aidx<aud_size; ++aidx ) {
+               IndexMarks &marks = *audio_markers[aidx];
+               int count = marks.size();
+               fwrite(&count, sizeof(count), 1, fp);
+               fwrite(&marks[0], sizeof(marks[0]), marks.size(), fp);
+       }
+
+       fclose(fp);
        return 0;
 }
 
+int IndexState::read_markers(char *index_dir, char *file_path)
+{
+       int ret = 0;
+       marker_lock->lock("IndexState::read_markers");
+       if( marker_status == MARKERS_NOTTESTED ) {
+               char src_path[BCTEXTLEN], marker_path[BCTEXTLEN];
+               IndexFile::get_index_filename(src_path, index_dir, marker_path, file_path, ".mkr");
+               FILE *fp = fopen(marker_path, "rb");
+               int vsz = strlen(MARKER_MAGIC_VERSION);
+               char version[vsz];
+               if( fp && fread(version, vsz, 1, fp) ) {
+                       if( memcmp(version, MARKER_MAGIC_VERSION, vsz) ) {
+                               eprintf(_("IndexState::read_markers marker file version mismatched\n: %s\n"),
+                                       marker_path);
+                               return 1;
+                       }
+                       ret = read_marks(fp);
+                       if( !ret ) marker_status = MARKERS_READY;
+                       fclose(fp);
+               }
+       }
+       marker_lock->unlock();
+       return ret;
+}
+
+int IndexState::read_marks(FILE *fp)
+{
+       reset_markers();
+       int vid_size = 0;
+       if( !fread(&vid_size, sizeof(vid_size), 1, fp) ) return 1;
+       add_video_markers(vid_size);
+       for( int vidx=0; vidx<vid_size; ++vidx ) {
+               int count = 0;
+               if( !fread(&count, sizeof(count), 1, fp) ) return 1;
+               IndexMarks &marks = *video_markers[vidx];
+               marks.allocate(count);
+               int len = fread(&marks[0], sizeof(marks[0]), count, fp);
+               if( len != count ) return 1;
+               marks.total = count;
+       }
+       int aud_size = 0;
+       if( !fread(&aud_size, sizeof(aud_size), 1, fp) ) return 1;
+       add_audio_markers(aud_size);
+       for( int aidx=0; aidx<aud_size; ++aidx ) {
+               int count = 0;
+               if( !fread(&count, sizeof(count), 1, fp) ) return 1;
+               IndexMarks &marks = *audio_markers[aidx];
+               marks.allocate(count);
+               int len = fread(&marks[0], sizeof(marks[0]), count, fp);
+               if( len != count ) return 1;
+               marks.total = count;
+       }
+       return 0;
+}
+
+int IndexState::create_index(const char *index_path, Asset *asset)
+{
+       index_entries.remove_all_objects();
+       int channels = index_channels.size();
+       int64_t offset = 0;
+       for( int ch=0; ch<channels; ++ch ) {
+               IndexChannel *chn = index_channels[ch];
+               float *bfr = (float *)chn->bfr;
+               int64_t size = 2 * chn->used();
+               add_index_entry(bfr, offset, size);
+               offset += size;
+       }
+
+       write_markers(index_path);
+       return write_index(index_path, asset, index_zoom, index_bytes);
+}
+
 int64_t IndexState::get_index_offset(int channel)
 {
-       return channel < channels && index_offsets ? index_offsets[channel] : 0;
+       return channel >= index_entries.size() ? 0 :
+               index_entries[channel]->offset;
 }
 
 int64_t IndexState::get_index_size(int channel)
 {
-       return channel < channels && index_sizes ? index_sizes[channel] : 0;
+       return channel >= index_entries.size() ? 0 :
+               index_entries[channel]->size;
 }
 
+float *IndexState::get_channel_buffer(int channel)
+{
+       return channel >= index_channels.size() ? 0 :
+               (float *)index_channels[channel]->bfr;
+}
+
+int64_t IndexState::get_channel_used(int channel)
+{
+       return channel >= index_channels.size() ? 0 :
+               index_channels[channel]->used();
+}