rework keyframe hide popup, keyframe auto render, textbox set_selection wide text
[goodguy/history.git] / cinelerra-5.1 / cinelerra / indexstate.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2009 Adam Williams <broadcast at earthling dot net>
5  * 
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * 
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  * 
20  */
21
22
23 #include "arraylist.h"
24 #include "asset.h"
25 #include "clip.h"
26 #include "filexml.h"
27 #include "indexfile.h"
28 #include "indexstate.h"
29 #include "language.h"
30 #include "mainerror.h"
31 #include "mutex.h"
32
33 #include <stdio.h>
34 #include <string.h>
35
36 int IndexMarks::find(int64_t no)
37 {
38         int l = -1, r = size();
39         while( (r - l) > 1 ) {
40                 int i = (l+r) >> 1;
41                 if( no > values[i].no ) l = i; else r = i;
42         }
43         return l;
44 }
45
46 IndexChannel::IndexChannel(IndexState *state, int64_t length)
47 {
48         this->state = state;
49         this->length = length;
50         min = max = 0;
51         bfr = inp = 0;
52         size = 0;
53         zidx = 0;
54 }
55
56 IndexChannel::~IndexChannel()
57 {
58         delete [] bfr;
59 }
60
61 void IndexChannel::put_entry()
62 {
63         inp->min = min;
64         inp->max = max;
65         ++inp;
66         max = -1; min = 1;
67         zidx = 0;
68 }
69
70 int64_t IndexChannel::pos()
71 {
72         return used() * state->index_zoom + zidx;
73 }
74
75 void IndexChannel::pad_data(int64_t pos)
76 {
77         CLAMP(pos, 0, length);
78         while( this->pos() < pos ) put_entry();
79         inp = bfr + pos / state->index_zoom;
80         zidx = pos % state->index_zoom;
81 }
82
83 void IndexState::reset_index()
84 {
85         index_status = INDEX_NOTTESTED;
86         index_start = 0;
87         index_zoom = 0;
88         index_bytes = 0;
89         index_entries.remove_all_objects();
90         index_channels.remove_all_objects();
91 }
92
93 void IndexState::reset_markers()
94 {
95         marker_status = MARKERS_NOTTESTED;
96         video_markers.remove_all_objects();
97         audio_markers.remove_all_objects();
98 }
99
100 IndexState::IndexState()
101  : Garbage("IndexState")
102 {
103         marker_lock = new Mutex("IndexState::marker_lock");
104         reset_index();
105         reset_markers();
106 }
107
108 IndexState::~IndexState()
109 {
110         reset_index();
111         reset_markers();
112         delete marker_lock;
113 }
114
115 void IndexState::init_scan(int64_t index_length)
116 {
117         index_zoom = 1;
118         int channels = index_channels.size();
119         if( !channels ) return;
120         int64_t max_samples = 0;
121         for( int ch=0; ch<channels; ++ch ) {
122                 int64_t len = index_channels[ch]->length;
123                 if( max_samples < len ) max_samples = len;
124         }
125         int64_t items = index_length / sizeof(IndexItem);
126         int64_t count = (items - channels) / channels + 1;
127         while( count*index_zoom < max_samples ) index_zoom *= 2;
128         for( int ch=0; ch<channels; ++ch ) {
129                 IndexChannel *chn = index_channels[ch];
130                 int64_t len = chn->length / index_zoom + 1;
131                 chn->alloc(len);
132                 chn->put_entry();
133         }
134         reset_markers();
135 }
136
137 void IndexState::dump()
138 {
139         printf("IndexState::dump this=%p\n", this);
140         printf("    index_status=%d index_zoom=%jd index_bytes=%jd\n",
141                 index_status, index_zoom, index_bytes);
142         printf("    index entries=%d\n", index_entries.size());
143         for( int i=0; i<index_entries.size(); ++i )
144                 printf("  %d. ofs=%jd, sz=%jd\n", i,
145                         index_entries[i]->offset, index_entries[i]->size);
146         printf("\n");
147 }
148
149 void IndexState::write_xml(FileXML *file)
150 {
151         file->tag.set_title("INDEX");
152         file->tag.set_property("ZOOM", index_zoom);
153         file->tag.set_property("BYTES", index_bytes);
154         file->append_tag();
155         file->append_newline();
156
157         for( int i=0; i<index_entries.size(); ++i ) {
158                 file->tag.set_title("OFFSET");
159                 file->tag.set_property("FLOAT", index_entries[i]->offset);
160                 file->append_tag();
161                 file->tag.set_title("SIZE");
162                 file->tag.set_property("FLOAT", index_entries[i]->size);
163                 file->append_tag();
164         }
165
166         file->append_newline();
167         file->tag.set_title("/INDEX");
168         file->append_tag();
169         file->append_newline();
170 }
171
172 void IndexState::read_xml(FileXML *file, int channels)
173 {
174         index_entries.remove_all_objects();
175         for( int i=0; i<channels; ++i ) add_index_entry(0, 0, 0);
176
177         int current_offset = 0;
178         int current_size = 0;
179         int result = 0;
180
181         index_zoom = file->tag.get_property("ZOOM", 1);
182         index_bytes = file->tag.get_property("BYTES", (int64_t)0);
183
184         while(!result) {
185                 result = file->read_tag();
186                 if(!result) {
187                         if(file->tag.title_is("/INDEX")) {
188                                 result = 1;
189                         }
190                         else if(file->tag.title_is("OFFSET")) {
191                                 if(current_offset < channels) {
192                                         int64_t offset = file->tag.get_property("FLOAT", (int64_t)0);
193                                         index_entries[current_offset++]->offset = offset;
194 //printf("Asset::read_index %d %d\n", current_offset - 1, index_offsets[current_offset - 1]);
195                                 }
196                         }
197                         else if(file->tag.title_is("SIZE")) {
198                                 if(current_size < channels) {
199                                         int64_t size = file->tag.get_property("FLOAT", (int64_t)0);
200                                         index_entries[current_size++]->size = size;
201                                 }
202                         }
203                 }
204         }
205 }
206
207 int IndexState::write_index(const char *index_path, Asset *asset, int64_t zoom, int64_t file_bytes)
208 {
209         FILE *fp = fopen(index_path, "wb");
210         if( !fp ) {
211                 eprintf(_("IndexState::write_index Couldn't write index file %s to disk.\n"),
212                         index_path);
213                 return 1;
214         }
215         index_zoom = zoom;
216         index_bytes = file_bytes;
217         index_status = INDEX_READY;
218
219         FileXML xml;
220 // write index_state as asset or directly.
221         if( asset )
222                 asset->write(&xml, 1, "");
223         else
224                 write_xml(&xml);
225         int64_t len = xml.length() + FileXML::xml_header_size;
226         index_start = sizeof(index_start) + len;
227         fwrite(&index_start, sizeof(index_start), 1, fp);
228         xml.write_to_file(fp);
229
230         int channels = index_entries.size();
231         int64_t max_size = 0;
232         for( int ch=0; ch<channels; ++ch ) {
233                 IndexEntry *ent = index_entries[ch];
234                 float *bfr = ent->bfr;
235                 int64_t size = ent->size;
236                 if( max_size < size ) max_size = size;
237                 fwrite(bfr, sizeof(float), size, fp);
238         }
239
240         fclose(fp);
241         return 0;
242 }
243
244 int IndexState::write_markers(const char *index_path)
245 {
246         int vid_size = video_markers.size();
247         int aud_size = audio_markers.size();
248         if( !vid_size && !aud_size ) return 0;
249
250         FILE *fp = 0;
251         char marker_path[BCTEXTLEN];
252         strcpy(marker_path, index_path);
253         char *basename = strrchr(marker_path,'/');
254         if( !basename ) basename = marker_path;
255         char *ext = strrchr(basename, '.');
256         if( ext ) {
257                 strcpy(ext, ".mkr");
258                 fp = fopen(marker_path, "wb");
259         }
260
261         char version[] = MARKER_MAGIC_VERSION;
262         if( !fp || !fwrite(version, strlen(version), 1, fp) ) {
263                 eprintf(_("IndexState::write_markers Couldn't write marker file %s to disk.\n"),
264                         marker_path);
265                 return 1;
266         }
267
268         fwrite(&vid_size, sizeof(vid_size), 1, fp);
269         for( int vidx=0; vidx<vid_size; ++vidx ) {
270                 IndexMarks &marks = *video_markers[vidx];
271                 int count = marks.size();
272                 fwrite(&count, sizeof(count), 1, fp);
273                 fwrite(&marks[0], sizeof(marks[0]), count, fp);
274         }
275
276         fwrite(&aud_size, sizeof(aud_size), 1, fp);
277         for( int aidx=0; aidx<aud_size; ++aidx ) {
278                 IndexMarks &marks = *audio_markers[aidx];
279                 int count = marks.size();
280                 fwrite(&count, sizeof(count), 1, fp);
281                 fwrite(&marks[0], sizeof(marks[0]), marks.size(), fp);
282         }
283
284         fclose(fp);
285         return 0;
286 }
287
288 int IndexState::read_markers(char *index_dir, char *file_path)
289 {
290         int ret = 0;
291         marker_lock->lock("IndexState::read_markers");
292         if( marker_status == MARKERS_NOTTESTED ) {
293                 char src_path[BCTEXTLEN], marker_path[BCTEXTLEN];
294                 IndexFile::get_index_filename(src_path, index_dir, marker_path, file_path, ".mkr");
295                 FILE *fp = fopen(marker_path, "rb");
296                 int vsz = strlen(MARKER_MAGIC_VERSION);
297                 char version[vsz];
298                 if( fp && fread(version, vsz, 1, fp) ) {
299                         if( memcmp(version, MARKER_MAGIC_VERSION, vsz) ) {
300                                 eprintf(_("IndexState::read_markers marker file version mismatched\n: %s\n"),
301                                         marker_path);
302                                 return 1;
303                         }
304                         ret = read_marks(fp);
305                         if( !ret ) marker_status = MARKERS_READY;
306                         fclose(fp);
307                 }
308         }
309         marker_lock->unlock();
310         return ret;
311 }
312
313 int IndexState::read_marks(FILE *fp)
314 {
315         reset_markers();
316         int vid_size = 0;
317         if( !fread(&vid_size, sizeof(vid_size), 1, fp) ) return 1;
318         add_video_markers(vid_size);
319         for( int vidx=0; vidx<vid_size; ++vidx ) {
320                 int count = 0;
321                 if( !fread(&count, sizeof(count), 1, fp) ) return 1;
322                 IndexMarks &marks = *video_markers[vidx];
323                 marks.allocate(count);
324                 int len = fread(&marks[0], sizeof(marks[0]), count, fp);
325                 if( len != count ) return 1;
326                 marks.total = count;
327         }
328         int aud_size = 0;
329         if( !fread(&aud_size, sizeof(aud_size), 1, fp) ) return 1;
330         add_audio_markers(aud_size);
331         for( int aidx=0; aidx<aud_size; ++aidx ) {
332                 int count = 0;
333                 if( !fread(&count, sizeof(count), 1, fp) ) return 1;
334                 IndexMarks &marks = *audio_markers[aidx];
335                 marks.allocate(count);
336                 int len = fread(&marks[0], sizeof(marks[0]), count, fp);
337                 if( len != count ) return 1;
338                 marks.total = count;
339         }
340         return 0;
341 }
342
343 int IndexState::create_index(const char *index_path, Asset *asset)
344 {
345         index_entries.remove_all_objects();
346         int channels = index_channels.size();
347         int64_t offset = 0;
348         for( int ch=0; ch<channels; ++ch ) {
349                 IndexChannel *chn = index_channels[ch];
350                 float *bfr = (float *)chn->bfr;
351                 int64_t size = 2 * chn->used();
352                 add_index_entry(bfr, offset, size);
353                 offset += size;
354         }
355
356         write_markers(index_path);
357         return write_index(index_path, asset, index_zoom, index_bytes);
358 }
359
360 int64_t IndexState::get_index_offset(int channel)
361 {
362         return channel >= index_entries.size() ? 0 :
363                 index_entries[channel]->offset;
364 }
365
366 int64_t IndexState::get_index_size(int channel)
367 {
368         return channel >= index_entries.size() ? 0 :
369                 index_entries[channel]->size;
370 }
371
372 float *IndexState::get_channel_buffer(int channel)
373 {
374         return channel >= index_channels.size() ? 0 :
375                 (float *)index_channels[channel]->bfr;
376 }
377
378 int64_t IndexState::get_channel_used(int channel)
379 {
380         return channel >= index_channels.size() ? 0 :
381                 index_channels[channel]->used();
382 }
383