1 // cvirtual.c
2 // LiVES
3 // (c) G. Finch 2008 - 2020 <salsaman@gmail.com>
4 // released under the GNU GPL 3 or later
5 // see file ../COPYING or www.gnu.org for licensing details
6 
7 // functions for handling "virtual" clips (CLIP_TYPE_FILE)
8 
9 #include "main.h"
10 
11 // frame_index is for files of type CLIP_TYPE_FILE
12 // a positive number is a pointer to a frame within the video file
13 // -1 means frame is stored as the corresponding image file
14 // e.g 00000001.jpg or 00000010.png etc.
15 
16 #include "resample.h"
17 #include "cvirtual.h"
18 
19 /** count virtual frames between start and end (inclusive) */
count_virtual_frames(frames_t * findex,frames_t start,frames_t end)20 frames_t count_virtual_frames(frames_t *findex, frames_t start, frames_t end) {
21   frames_t count = 0;
22   for (register int i = start - 1; i < end; i++) if (findex[i] != -1) count++;
23   return count;
24 }
25 
26 
create_frame_index(int fileno,boolean init,frames_t start_offset,frames_t nframes)27 boolean create_frame_index(int fileno, boolean init, frames_t start_offset, frames_t nframes) {
28   lives_clip_t *sfile = mainw->files[fileno];
29   size_t idxsize = (ALIGN_CEIL(nframes * sizeof(frames_t), DEF_ALIGN)) / DEF_ALIGN;
30   if (!IS_VALID_CLIP(fileno) || sfile->frame_index) return FALSE;
31   sfile->frame_index = (frames_t *)lives_calloc(idxsize, DEF_ALIGN);
32   if (!sfile->frame_index) return FALSE;
33   if (init) for (int i = 0; i < sfile->frames; i++) sfile->frame_index[i] = i + start_offset;
34   return TRUE;
35 }
36 
37 
extend_frame_index(int fileno,frames_t start,frames_t end)38 static boolean extend_frame_index(int fileno, frames_t start, frames_t end) {
39   lives_clip_t *sfile = mainw->files[fileno];
40   size_t idxsize = (ALIGN_CEIL(end * sizeof(frames_t), DEF_ALIGN)) / DEF_ALIGN;
41   if (!IS_VALID_CLIP(fileno) || start > end) return FALSE;
42   if (sfile->frame_index_back) lives_free(sfile->frame_index_back);
43   sfile->frame_index_back = sfile->frame_index;
44   sfile->frame_index = (frames_t *)lives_calloc(idxsize, DEF_ALIGN);
45   if (!sfile->frame_index) {
46     sfile->frame_index = sfile->frame_index_back;
47     sfile->frame_index_back = NULL;
48     return FALSE;
49   }
50   for (int i = start; i < end; i++) sfile->frame_index[i] = -1;
51   return TRUE;
52 }
53 
54 
55 // save frame_index to disk
save_frame_index(int fileno)56 boolean save_frame_index(int fileno) {
57   int fd, i;
58   int retval;
59   char *fname, *fname_new;
60   lives_clip_t *sfile = mainw->files[fileno];
61 
62   if (fileno == 0) return TRUE;
63 
64   if (!sfile || !sfile->frame_index) return FALSE;
65 
66   fname = lives_build_filename(prefs->workdir, sfile->handle, FRAME_INDEX_FNAME "." LIVES_FILE_EXT_BACK, NULL);
67   fname_new = lives_build_filename(prefs->workdir, sfile->handle, FRAME_INDEX_FNAME, NULL);
68 
69   do {
70     retval = 0;
71     fd = lives_create_buffered(fname, DEF_FILE_PERMS);
72     if (fd < 0) {
73       retval = do_write_failed_error_s_with_retry(fname, lives_strerror(errno));
74     } else {
75       for (i = 0; i < sfile->frames; i++) {
76         lives_write_le_buffered(fd, &sfile->frame_index[i], sizeof(frames_t), TRUE);
77         if (THREADVAR(write_failed) == fd + 1) {
78           THREADVAR(write_failed) = 0;
79           break;
80         }
81       }
82 
83       lives_close_buffered(fd);
84 
85       if (mainw->is_exiting) return TRUE;
86 
87       if (THREADVAR(write_failed) == fd + 1) {
88         THREADVAR(write_failed) = 0;
89         retval = do_write_failed_error_s_with_retry(fname, NULL);
90       } else {
91         if (sget_file_size(fname) != (off_t)sfile->frames * sizeof(frames_t)) {
92           retval = do_write_failed_error_s_with_retry(fname, NULL);
93         } else {
94           lives_cp(fname, fname_new);
95           if (sget_file_size(fname_new) != (off_t)sfile->frames * sizeof(frames_t)) {
96             retval = do_write_failed_error_s_with_retry(fname, NULL);
97 	    // *INDENT-OFF*
98           }}}}
99     // *INDENT-ON*
100   } while (retval == LIVES_RESPONSE_RETRY);
101 
102   lives_free(fname);
103   lives_free(fname_new);
104 
105   if (retval == LIVES_RESPONSE_CANCEL) return FALSE;
106 
107   return TRUE;
108 }
109 
110 // load frame_index from disk
111 // returns -1 (error)
112 // or maxframe pointed to in clip
113 
load_frame_index(int fileno)114 frames_t load_frame_index(int fileno) {
115   lives_clip_t *sfile = mainw->files[fileno];
116   off_t filesize;
117   char *fname, *fname_back;
118   boolean backuptried = FALSE;
119   int fd, retval;
120   frames_t maxframe = -1;
121 
122   int i;
123 
124   if (!sfile || sfile->frame_index) return -1;
125 
126   lives_freep((void **)&sfile->frame_index);
127 
128   fname = lives_build_filename(prefs->workdir, sfile->handle, FRAME_INDEX_FNAME, NULL);
129   filesize = sget_file_size(fname);
130 
131   if (filesize <= 0) {
132     lives_free(fname);
133     return 0;
134   }
135 
136   if (filesize >> 2 > (off_t)sfile->frames) sfile->frames = (frames_t)(filesize >> 2);
137   fname_back = lives_build_filename(prefs->workdir, sfile->handle, FRAME_INDEX_FNAME "." LIVES_FILE_EXT_BACK, NULL);
138 
139   do {
140     retval = 0;
141     fd = lives_open_buffered_rdonly(fname);
142     if (fd < 0) {
143       THREADVAR(read_failed) = 0;
144       retval = do_read_failed_error_s_with_retry(fname, lives_strerror(errno));
145       if (!backuptried) {
146         fd = lives_open_buffered_rdonly(fname_back);
147         if (fd >= 0) {
148           lives_close_buffered(fd);
149           if (findex_bk_dialog(fname_back)) {
150             lives_cp(fname_back, fname);
151             backuptried = TRUE;
152             continue;
153           }
154         }
155       } else if (sfile->frame_index_back) {
156         if (findex_bk_dialog(fname_back)) {
157           sfile->frame_index = sfile->frame_index_back;
158         }
159       }
160       if (retval == LIVES_RESPONSE_CANCEL) {
161         lives_free(fname);
162         lives_free(fname_back);
163         return -1;
164       }
165     } else {
166       LiVESResponseType response;
167       char *what = (_("creating the frame index for the clip"));
168       do {
169         response = LIVES_RESPONSE_OK;
170         create_frame_index(fileno, FALSE, 0, sfile->frames);
171         if (!cfile->frame_index) {
172           response = do_memory_error_dialog(what, sfile->frames * sizeof(frames_t));
173         }
174       } while (response == LIVES_RESPONSE_RETRY);
175       lives_free(what);
176       if (response == LIVES_RESPONSE_CANCEL) {
177         break;
178       }
179 
180       for (i = 0; i < sfile->frames; i++) {
181         lives_read_le_buffered(fd, &sfile->frame_index[i], sizeof(frames_t), FALSE);
182         if (THREADVAR(read_failed) == fd + 1) {
183           break;
184         }
185         if (sfile->frame_index[i] > maxframe) {
186           maxframe = sfile->frame_index[i];
187         }
188       }
189       lives_close_buffered(fd);
190 
191       if (THREADVAR(read_failed) == fd + 1) {
192         THREADVAR(read_failed) = 0;
193         retval = do_read_failed_error_s_with_retry(fname, NULL);
194       }
195 
196       if (!backuptried) {
197         backuptried = TRUE;
198         fd = lives_open_buffered_rdonly(fname_back);
199         if (fd >= 0) {
200           LiVESList *list = NULL;
201           frames_t vframe;
202           int count = 0;
203           for (; lives_read_le_buffered(fd, &vframe, sizeof(frames_t), TRUE) == sizeof(frames_t); count++) {
204             if (THREADVAR(read_failed) == fd + 1) break;
205             list = lives_list_prepend(list, LIVES_INT_TO_POINTER(vframe));
206           }
207           lives_close_buffered(fd);
208           if (THREADVAR(read_failed) == fd + 1) {
209             THREADVAR(read_failed) = 0;
210           } else if (count) {
211             frames_t *f_index = sfile->frame_index;
212             list = lives_list_reverse(list);
213             lives_freep((void **)&sfile->frame_index_back);
214             sfile->frame_index = NULL;
215             create_frame_index(fileno, FALSE, 0, count);
216             sfile->frame_index_back = sfile->frame_index;
217             sfile->frame_index = f_index;
218             if (sfile->frame_index_back) {
219               LiVESList *xlist;
220               sfile->old_frames = count;
221               count = 0;
222               for (xlist = list; xlist; xlist = xlist->next) {
223                 sfile->frame_index_back[count++] = LIVES_POINTER_TO_INT(xlist->data);
224 		// *INDENT-OFF*
225 	      }}}
226 	  if (list) lives_list_free(list);
227 	}}}} while (retval == LIVES_RESPONSE_RETRY);
228   // *INDENT-ON*
229 
230   lives_free(fname);
231   lives_free(fname_back);
232 
233   if (maxframe >= 0) sfile->clip_type = CLIP_TYPE_FILE;
234   return ++maxframe;
235 }
236 
237 
del_frame_index(lives_clip_t * sfile)238 void del_frame_index(lives_clip_t *sfile) {
239   // physically delete the frame_index for a clip
240   // only done once all
241 
242   char *idxfile;
243 
244   // cannot call check_if_non_virtual() else we end up recursing
245 
246   if (sfile->frame_index) {
247     for (frames_t i = 1; i <= sfile->frames; i++) {
248       if (sfile->frame_index[i - 1] != -1) {
249         LIVES_ERROR("deleting frame_index with virtual frames in it !");
250         return;
251       }
252     }
253   }
254 
255   if (sfile != clipboard) {
256     idxfile = lives_build_filename(prefs->workdir, sfile->handle, FRAME_INDEX_FNAME, NULL);
257     lives_rm(idxfile);
258     lives_free(idxfile);
259   }
260 
261   lives_freep((void **)&sfile->frame_index);
262 }
263 
264 
scan_frames(lives_clip_t * sfile,frames_t vframes,frames_t last_real_frame)265 static frames_t scan_frames(lives_clip_t *sfile, frames_t vframes, frames_t last_real_frame) {
266   frames_t i;
267   for (i = 0; i < sfile->frames; i++) {
268     // assume all real frames up to last_real_frame are there
269     if ((sfile->frame_index[i] == -1 && i >= last_real_frame) || (sfile->frame_index[i] > vframes)) return i;
270   }
271   return i;
272 }
273 
274 
resolve_img_type(lives_clip_t * sfile)275 lives_img_type_t resolve_img_type(lives_clip_t *sfile) {
276   lives_img_type_t ximgtype;
277   int nimty = (int)N_IMG_TYPES;
278   char *fname;
279   for (frames_t i = sfile->frames - 1; i >= 0; i--) {
280     if (!sfile->frame_index || sfile->frame_index[i] == -1) {
281       for (int j = 1; j < nimty; j++) {
282         ximgtype = (lives_img_type_t)j;
283         fname = make_image_file_name(sfile, i + 1, get_image_ext_for_type(ximgtype));
284         if (lives_file_test(fname, LIVES_FILE_TEST_EXISTS)) {
285           lives_free(fname);
286           return j;
287         }
288         lives_free(fname);
289 	// *INDENT-OFF*
290       }}}
291   // *INDENT-ON*
292   return IMG_TYPE_BEST;
293 }
294 
295 
check_clip_integrity(int fileno,const lives_clip_data_t * cdata,frames_t maxframe)296 boolean check_clip_integrity(int fileno, const lives_clip_data_t *cdata, frames_t maxframe) {
297   lives_clip_t *sfile = mainw->files[fileno], *binf = NULL;
298   lives_img_type_t empirical_img_type = sfile->img_type, oemp = empirical_img_type;
299   lives_img_type_t ximgtype;
300   frames_t last_real_frame = sfile->frames;
301   int nimty = (int)N_IMG_TYPES, j;
302   boolean has_missing_frames = FALSE, bad_imgfmts = FALSE;
303   boolean mismatch = FALSE;
304   boolean isfirst = TRUE;
305   boolean backup_more_correct = FALSE;
306   char *fname;
307 
308   frames_t i;
309   // check clip integrity upon loading
310 
311   // check that cached values match with sfile (on disk) values
312   // also check sfile->frame_index to make sure all frames are present
313 
314   // return FALSE if we find any omissions/inconsistencies
315 
316   /* if (sfile->frames > maxframe) { */
317   /*   has_missing_frames = TRUE; */
318   /*   sfile->frames = maxframe; */
319   /* } */
320 
321   if (prefs->vj_mode) return TRUE;
322 
323   // check the image type
324   for (i = sfile->frames - 1; i >= 0; i--) {
325     if (!sfile->frame_index || sfile->frame_index[i] == -1) {
326       // this is a non-virtual frame
327       ximgtype = empirical_img_type;
328       fname = NULL;
329       if (ximgtype != IMG_TYPE_UNKNOWN) fname = make_image_file_name(sfile, i + 1, get_image_ext_for_type(ximgtype));
330       if (!fname || !lives_file_test(fname, LIVES_FILE_TEST_EXISTS)) {
331         if (fname) lives_free(fname);
332         for (j = 1; j < nimty; j++) {
333           ximgtype = (lives_img_type_t)j;
334           if (ximgtype == empirical_img_type) continue;
335           fname = make_image_file_name(sfile, i + 1, get_image_ext_for_type(ximgtype));
336           if (lives_file_test(fname, LIVES_FILE_TEST_EXISTS)) {
337             if (isfirst) {
338               empirical_img_type = ximgtype;
339               if (oemp == IMG_TYPE_UNKNOWN) oemp = ximgtype;
340             } else {
341               if (ximgtype == oemp) empirical_img_type = oemp;
342               bad_imgfmts = TRUE;
343             }
344             lives_free(fname);
345             isfirst = FALSE;
346             break;
347           }
348           lives_free(fname);
349         }
350         if (j == nimty) {
351           has_missing_frames = TRUE;
352           if (prefs->show_dev_opts) {
353             g_printerr("clip %s is missing image frame %d\n", sfile->handle, i + 1);
354           }
355         } else {
356           last_real_frame = i;
357           isfirst = FALSE;
358 	  // *INDENT-OFF*
359 	}}
360       else lives_free(fname);
361     }}
362   // *INDENT-ON*
363 
364   if (empirical_img_type == IMG_TYPE_UNKNOWN) {
365     /// this is possible if clip is only virtual frames
366     empirical_img_type = sfile->img_type = IMG_TYPE_BEST; // read_headers() will have set this to "jpeg" (default)
367   }
368 
369   if (cdata) {
370     // check frame count
371     if (maxframe > cdata->nframes || has_missing_frames) {
372       if (prefs->show_dev_opts) {
373         if (maxframe > cdata->nframes) {
374           g_printerr("frame count mismatch for clip %d,  %s, maxframe is %d, decoder claims only %ld\nRescaning...",
375                      fileno, sfile->handle, maxframe, cdata-> nframes);
376         }
377       }
378 
379       has_missing_frames = TRUE;
380       sfile->frames = scan_frames(sfile, cdata->nframes, last_real_frame);
381       if (prefs->show_dev_opts) {
382         g_printerr("rescan counted %d frames\n.", sfile->frames);
383       }
384     }
385   }
386 
387   if (sfile->frame_index) {
388     frames_t lgoodframe = -1;
389     int goodidx;
390     frames_t xframes = sfile->frames;
391 
392     if (sfile->frame_index_back) {
393       if (sfile->old_frames > sfile->frames) xframes = sfile->old_frames;
394       backup_more_correct = TRUE;
395     }
396 
397     // check and attempt to correct frame_index
398     for (i = 0; i < xframes; i++) {
399       frames_t fr;
400       if (i < sfile->frames) fr = sfile->frame_index[i];
401       else fr = sfile->frame_index_back[i];
402       if (fr < -1 || (!cdata && (frames64_t)fr > sfile->frames - 1)
403           || (cdata && (frames64_t)fr > cdata->nframes - 1)) {
404         if (i >= sfile->frames) {
405           backup_more_correct = FALSE;
406           break;
407         }
408         if (backup_more_correct && i < sfile->old_frames) {
409           frames_t fr2 = sfile->frame_index_back[i];
410           if (fr2 < -1 || (!cdata && (frames64_t)fr2 > sfile->frames - 1)
411               || (cdata && (frames64_t)fr2 > cdata->nframes - 1)) {
412             backup_more_correct = FALSE;
413           }
414         }
415 
416         if (prefs->show_dev_opts) {
417           g_printerr("bad frame index %d, points to %d.....", i, fr);
418         }
419         if (fr < sfile->frames) has_missing_frames = TRUE;
420         fname = make_image_file_name(sfile, i + 1, get_image_ext_for_type(empirical_img_type));
421         if (lives_file_test(fname, LIVES_FILE_TEST_EXISTS)) {
422           sfile->frame_index[i] = -1;
423           if (prefs->show_dev_opts) {
424             g_printerr("relinked to image frame %d\n", i + 1);
425           }
426         } else {
427           if (backup_more_correct && i < sfile->old_frames && sfile->frame_index_back[i] == -1)
428             backup_more_correct = FALSE;
429           if (lgoodframe != -1) {
430             sfile->frame_index[i] = lgoodframe + i - goodidx;
431             if (prefs->show_dev_opts) {
432               g_printerr("relinked to clip frame %d\n", lgoodframe + i - goodidx);
433             }
434           } else {
435             sfile->frame_index[i] = i;
436             if (prefs->show_dev_opts) {
437               g_printerr("reset to clip frame %d\n", i);
438             }
439           }
440           if (sfile->frame_index[i] >= cdata->nframes) sfile->frame_index[i] = cdata->nframes - 1;
441         }
442         lives_free(fname);
443       } else {
444         if (cdata && fr != -1) {
445           lgoodframe = fr;
446           goodidx = i;
447 	  // *INDENT-OFF*
448         }}}}
449   // *INDENT-ON*
450   if (has_missing_frames && backup_more_correct) {
451     lives_freep((void **)&sfile->frame_index);
452     sfile->frame_index = sfile->frame_index_back;
453     if (sfile->old_frames > sfile->frames) {
454       sfile->frames = sfile->old_frames;
455       sfile->frames = scan_frames(sfile, sfile->frames, last_real_frame);
456     } else sfile->frames = sfile->old_frames;
457   }
458 
459   if (sfile->frames > 0) {
460     int hsize = sfile->hsize, chsize = hsize;
461     int vsize = sfile->vsize, cvsize = vsize;
462     frames_t last_img_frame = -1;
463     if (last_real_frame > 0) {
464       if (sfile->frame_index) {
465         for (i = last_real_frame; i > 0; i--) {
466           if (sfile->frame_index[i - 1] == -1) {
467             last_img_frame = i;
468             break;
469           }
470         }
471       } else last_img_frame = last_real_frame;
472       if (last_img_frame > -1) {
473         sfile->img_type = empirical_img_type;
474         get_frames_sizes(fileno, last_img_frame, &hsize, &vsize);
475       }
476     }
477 
478     if (cdata) {
479       if (!prefs->auto_nobord) {
480         chsize = cdata->frame_width * weed_palette_get_pixels_per_macropixel(cdata->current_palette);
481         cvsize = cdata->frame_height;
482       } else {
483         chsize = cdata->width * weed_palette_get_pixels_per_macropixel(cdata->current_palette);
484         cvsize = cdata->height;
485       }
486     }
487 
488     if (chsize == hsize && hsize != sfile->hsize) sfile->hsize = hsize;
489     if (cvsize == vsize && vsize != sfile->vsize) sfile->vsize = vsize;
490 
491     if (last_real_frame > 0) {
492       if (hsize == sfile->hsize && vsize == sfile->vsize) {
493         frames_t fframe = 0;
494         /// last frame is most likely to return correct size
495         /// we should also check first frame, as it is more likely to be wrong
496         if (sfile->clip_type == CLIP_TYPE_DISK) fframe = 1;
497         else {
498           for (i = 1; i < last_real_frame; i++) {
499             if (sfile->frame_index[i - 1] == -1) {
500               fname = make_image_file_name(sfile, i, get_image_ext_for_type(empirical_img_type));
501               if (lives_file_test(fname, LIVES_FILE_TEST_EXISTS)) {
502                 fframe = i;
503                 lives_free(fname);
504                 break;
505               }
506               has_missing_frames = TRUE;
507               lives_free(fname);
508 	      // *INDENT-OFF*
509 	    }}}
510 	if (fframe) get_frames_sizes(fileno, fframe, &hsize, &vsize);
511       }}
512     // *INDENT-ON*
513 
514     if (sfile->hsize != hsize || sfile->vsize != vsize) {
515       LiVESResponseType resp = do_resize_dlg(sfile->hsize, sfile->vsize, hsize, vsize);
516       if (prefs->show_dev_opts) {
517         g_printerr("incorrect frame size %d X %d, corrected to %d X %d\n", hsize, vsize, sfile->hsize, sfile->vsize);
518       }
519       if (resp == LIVES_RESPONSE_ACCEPT) {
520         sfile->hsize = hsize;
521         sfile->vsize = vsize;
522       } else if (resp == LIVES_RESPONSE_YES) {
523         int missing = 0, nbadsized = 0;
524         threaded_dialog_push();
525         do_threaded_dialog(_("Resizing all frames\n"), TRUE);
526         lives_widget_show_all(mainw->proc_ptr->processing);
527         if (resize_all(fileno, sfile->hsize, sfile->vsize, empirical_img_type, FALSE,
528                        &nbadsized, &missing)) {
529           g_printerr("resize detected %d bad sized, %d missing \n", nbadsized, missing);
530           if (missing) has_missing_frames = TRUE;
531           if (mainw->cancelled == CANCEL_NONE) bad_imgfmts = FALSE;
532           else mismatch = TRUE;
533         }
534         mainw->cancelled = CANCEL_NONE;
535         end_threaded_dialog();
536         threaded_dialog_pop();
537       } else mismatch = TRUE;
538     }
539   }
540   if (bad_imgfmts) {
541     LiVESResponseType resp = do_imgfmts_error(empirical_img_type);
542     if (resp == LIVES_RESPONSE_OK) {
543       int missing = 0, nbadsized = 0;
544       threaded_dialog_push();
545       do_threaded_dialog(_("Correcting Image Formats\n"), TRUE);
546       if (resize_all(fileno, sfile->hsize, sfile->vsize, empirical_img_type, FALSE,
547                      &nbadsized, &missing)) {
548         g_printerr("change fmts detected %d bad sized, %d missing \n", nbadsized, missing);
549         if (missing) has_missing_frames = TRUE;
550         if (mainw->cancelled == CANCEL_NONE) bad_imgfmts = FALSE;
551         else mismatch = TRUE;
552       }
553       mainw->cancelled = CANCEL_NONE;
554       end_threaded_dialog();
555       threaded_dialog_pop();
556     } else mismatch = TRUE;
557     mainw->cancelled = CANCEL_NONE;
558   }
559 
560   if (has_missing_frames) mismatch = TRUE;
561   else {
562     if (sfile->frame_index) {
563       for (i = 0; i < sfile->frames; i++) {
564         if (sfile->frame_index[i] != -1) {
565           fname = make_image_file_name(sfile, i + 1, get_image_ext_for_type(empirical_img_type));
566           if (lives_file_test(fname, LIVES_FILE_TEST_EXISTS)) {
567             lives_rm(fname);
568 	    // *INDENT-OFF*
569 	  }
570 	  lives_free(fname);
571 	}}}}
572   // *INDENT-ON*
573   if (cdata && (fabs(sfile->fps - (double)cdata->fps) > prefs->fps_tolerance)) {
574     if (prefs->show_dev_opts) {
575       g_printerr("fps mismtach, claimed %f, cdata said %f\n", sfile->fps, cdata->fps);
576     }
577     mismatch = TRUE;
578   }
579   if (sfile->img_type != empirical_img_type) {
580     if (prefs->show_dev_opts) {
581       g_printerr("corrected image type from %d to %d\n", sfile->img_type, empirical_img_type);
582     }
583     sfile->img_type = empirical_img_type;
584   }
585 
586   if (mismatch) goto mismatch;
587 
588   /*
589     // ignore since we may have resampled audio
590     if (sfile->achans != cdata->achans || sfile->arps != cdata->arate || sfile->asampsize != cdata->asamps ||
591       cdata->asigned == (sfile->signed_endian & AFORM_UNSIGNED)) return FALSE;
592   */
593 
594   // all things equal as far as we can tell
595   return TRUE;
596 
597 mismatch:
598   // something mismatched - commence further investigation
599 
600   if ((binf = clip_forensic(mainw->current_file))) {
601     if (has_missing_frames) {
602       if (cdata) {
603         if (binf->frames == cdata->nframes  && binf->frames < sfile->frames) sfile->frames = binf->frames;
604         else if (binf->frames == sfile->frames && binf->frames < sfile->frames) {
605           if (sfile->frames > cdata->nframes) if (!extend_frame_index(fileno, cdata->nframes, sfile->frames)) return FALSE;
606           ((lives_clip_data_t *)cdata)->nframes = sfile->frames;
607         }
608       } else if (binf->frames <= sfile->frames) sfile->frames = binf->frames;
609     }
610     if (binf->fps == cdata->fps)  {
611       ((lives_clip_data_t *)cdata)->fps =  sfile->pb_fps = sfile->fps;
612     }
613   }
614 
615   sfile->img_type = empirical_img_type;
616 
617   sfile->needs_update = TRUE;
618 
619   sfile->afilesize = reget_afilesize_inner(fileno);
620 
621   if (has_missing_frames && sfile->frame_index) {
622     if (sfile->frames > maxframe) extend_frame_index(fileno, maxframe, sfile->frames);
623     save_frame_index(fileno);
624   }
625   return FALSE;
626 }
627 
628 
first_virtual_frame(int fileno,frames_t start,frames_t end)629 frames_t first_virtual_frame(int fileno, frames_t start, frames_t end) {
630   // check all franes in frame_index betweem start and end inclusive
631   // if we find a virtual frame, we stop checking and return the frame number
632   // if all are non - virtual we return 0
633   lives_clip_t *sfile = mainw->files[fileno];
634 
635   if (!sfile->frame_index) return  0;
636   for (frames_t i = start; i <= end; i++) {
637     if (sfile->frame_index[i - 1] != -1) return i;
638   }
639 
640   return 0;
641 }
642 
643 
check_if_non_virtual(int fileno,frames_t start,frames_t end)644 boolean check_if_non_virtual(int fileno, frames_t start, frames_t end) {
645   // check if there are no virtual frames from start to end inclusive in clip fileno
646 
647   // return FALSE if any virtual frames are found in the region
648   // return TRUE if all frames in region are non-virtual
649 
650   // also may change the clip_type and the interlace
651 
652   lives_clip_t *sfile = mainw->files[fileno];
653   frames_t i;
654 
655   if (sfile->clip_type != CLIP_TYPE_FILE) return TRUE;
656 
657   if (sfile->frame_index) {
658     for (i = start; i <= end; i++) {
659       if (sfile->frame_index[i - 1] != -1) return FALSE;
660     }
661   }
662 
663   if (start > 1 || end < sfile->frames) return TRUE;
664 
665   // no virtual frames in entire clip - change to CLIP_TYPE_DISK
666 
667   sfile->clip_type = CLIP_TYPE_DISK;
668   del_frame_index(sfile);
669   close_clip_decoder(fileno);
670 
671   if (sfile->interlace != LIVES_INTERLACE_NONE) {
672     sfile->interlace = LIVES_INTERLACE_NONE; // all frames should have been deinterlaced
673     sfile->deinterlace = FALSE;
674     if (fileno > 0) {
675       if (!save_clip_value(fileno, CLIP_DETAILS_INTERLACE, &sfile->interlace))
676         do_header_write_error(fileno);
677     }
678   }
679 
680   return TRUE;
681 }
682 
683 #define DS_SPACE_CHECK_FRAMES 100
684 
save_decoded(int fileno,frames_t i,LiVESPixbuf * pixbuf,boolean silent,int progress)685 static boolean save_decoded(int fileno, frames_t i, LiVESPixbuf * pixbuf, boolean silent, int progress) {
686   lives_clip_t *sfile = mainw->files[fileno];
687   boolean retb;
688   int retval;
689   LiVESError *error = NULL;
690   char *oname = make_image_file_name(sfile, i, get_image_ext_for_type(sfile->img_type));
691 
692   do {
693     retval = LIVES_RESPONSE_NONE;
694     retb = lives_pixbuf_save(pixbuf, oname, sfile->img_type, 100 - prefs->ocp, sfile->hsize, sfile->vsize, &error);
695     if (error && !silent) {
696       retval = do_write_failed_error_s_with_retry(oname, error->message);
697       lives_error_free(error);
698       error = NULL;
699     } else if (!retb) {
700       retval = do_write_failed_error_s_with_retry(oname, NULL);
701     }
702   } while (retval == LIVES_RESPONSE_RETRY);
703 
704   lives_freep((void **)&oname);
705 
706   if (progress % DS_SPACE_CHECK_FRAMES == 1) {
707     if (!check_storage_space(fileno, FALSE)) {
708       retval = LIVES_RESPONSE_CANCEL;
709     }
710   }
711 
712   if (retval == LIVES_RESPONSE_CANCEL) return FALSE;
713   return TRUE;
714 }
715 
716 
717 #define STRG_CHECK 1000
718 
virtual_to_images(int sfileno,frames_t sframe,frames_t eframe,boolean update_progress,LiVESPixbuf ** pbr)719 frames_t virtual_to_images(int sfileno, frames_t sframe, frames_t eframe, boolean update_progress, LiVESPixbuf **pbr) {
720   // pull frames from a clip to images
721   // from sframe to eframe inclusive (first frame is 1)
722 
723   // if update_progress, set mainw->msg with number of frames pulled
724 
725   // should be threadsafe apart from progress update
726 
727   // if pbr is non-null, it will be set to point to the pulled pixbuf
728 
729   // return FALSE on write error
730 
731   lives_clip_t *sfile = mainw->files[sfileno];
732   LiVESPixbuf *pixbuf = NULL;
733   lives_proc_thread_t tinfo = THREADVAR(tinfo);
734   int progress = 1, count = 0;
735   frames_t i;
736 
737   if (tinfo) lives_proc_thread_set_cancellable(tinfo);
738 
739   if (sframe < 1) sframe = 1;
740 
741   for (i = sframe; i <= eframe; i++) {
742     if (i > sfile->frames) break;
743 
744     if (sfile->pumper && lives_proc_thread_cancelled(sfile->pumper)) break;
745 
746     if (update_progress) {
747       threaded_dialog_spin((double)(i - sframe) / (double)(eframe - sframe + 1));
748     }
749 
750     if (sfile->frame_index[i - 1] >= 0) {
751       if (pbr && pixbuf) lives_widget_object_unref(pixbuf);
752 
753       pixbuf = pull_lives_pixbuf_at_size(sfileno, i, get_image_ext_for_type(sfile->img_type),
754                                          q_gint64((i - 1.) / sfile->fps, sfile->fps), sfile->hsize,
755                                          sfile->vsize, LIVES_INTERP_BEST, FALSE);
756 
757       if (!pixbuf) return -i;
758       if (!save_decoded(sfileno, i, pixbuf, pbr != NULL, progress)) {
759         check_storage_space(-1, TRUE);
760         return -i;
761       }
762       if (!pbr) {
763         if (pixbuf) lives_widget_object_unref(pixbuf);
764         pixbuf = NULL;
765       }
766       if (++count == STRG_CHECK) {
767         if (!check_storage_space(-1, TRUE)) break;
768       }
769 
770       // another thread may have called check_if_non_virtual - TODO : use a mutex
771       if (!sfile->frame_index) break;
772       sfile->frame_index[i - 1] = -1;
773 
774       if (update_progress) {
775         // sig_progress...
776         lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "%d", progress++);
777         threaded_dialog_spin((double)(i - sframe) / (double)(eframe - sframe + 1));
778         lives_widget_context_update();
779       }
780 
781       if (mainw->cancelled != CANCEL_NONE) {
782         if (!check_if_non_virtual(sfileno, 1, sfile->frames)) save_frame_index(sfileno);
783         if (pbr) *pbr = pixbuf;
784         return i;
785       }
786     }
787   }
788 
789   if (pbr) *pbr = pixbuf;
790   if (!check_if_non_virtual(sfileno, 1, sfile->frames) && !save_frame_index(sfileno)) {
791     check_storage_space(-1, FALSE);
792     return -i;
793   }
794   return i;
795 }
796 
797 
restore_gamma_cb(int gamma_type)798 static void restore_gamma_cb(int gamma_type) {
799   // experimental, will move to another file
800   lives_clip_t *ocb = clipboard, *ncb;
801   LiVESPixbuf *pixbuf;
802   int cf = mainw->current_file;
803   int ogamma;
804   int i, found = -1, progress = 0;
805   boolean is_stored = FALSE;
806 
807   for (i = 0; i < mainw->ncbstores; i++) {
808     if (mainw->cbstores[i] == clipboard) is_stored = TRUE;
809     if (mainw->cbstores[i]->gamma_type == cfile->gamma_type) found = i;
810     if (found > -1 && is_stored) break;
811   }
812   if (!is_stored) {
813     if (mainw->ncbstores >= MAX_CBSTORES) return;
814     mainw->cbstores[mainw->ncbstores++] = clipboard;
815   }
816   if (found > -1) {
817     clipboard = mainw->cbstores[found];
818     return;
819   }
820   // not found,
821   // we'll actually make a copy of the clipboard but with the new gamma_type
822   clipboard = NULL;
823   init_clipboard();
824   lives_memcpy(clipboard, ocb, sizeof(lives_clip_t));
825   lives_memcpy(clipboard->frame_index, ocb->frame_index, clipboard->frames * sizeof(frames_t));
826   ncb = clipboard;
827   ogamma = ocb->gamma_type;
828   ocb->gamma_type = ncb->gamma_type = gamma_type; // force conversion
829   for (i = 0; i < clipboard->frames; i++) {
830     if (clipboard->frame_index[i] == -1) {
831       progress++;
832       clipboard = ocb;
833       pixbuf = pull_lives_pixbuf_at_size(0, i, get_image_ext_for_type(ocb->img_type),
834                                          0, 0, 0, LIVES_INTERP_BEST, FALSE);
835       clipboard = ncb;
836       if (!save_decoded(0, i, pixbuf, FALSE, progress)) {
837         mainw->current_file = 0;
838         close_current_file(cf);
839         clipboard = ocb;
840         ocb->gamma_type = ogamma;
841         return;
842       }
843     }
844   }
845   ocb->gamma_type = ogamma;
846 }
847 
848 
realize_all_frames(int clipno,const char * msg,boolean enough)849 frames_t realize_all_frames(int clipno, const char *msg, boolean enough) {
850   // if enough is set, we show Enough button instead of Cancel.
851   frames_t ret;
852   int current_file = mainw->current_file;
853   mainw->cancelled = CANCEL_NONE;
854   if (!IS_VALID_CLIP(clipno)) return 0;
855 
856   // if its the clipboard and we have exotic gamma types we need to do a special thing
857   // - fix the gamma_type of the clipboard existing frames before inserting in cfile
858   if (clipno == 0 && prefs->btgamma && CURRENT_CLIP_HAS_VIDEO && cfile->gamma_type
859       != clipboard->gamma_type) {
860     restore_gamma_cb(cfile->gamma_type);
861   }
862 
863   if (!check_if_non_virtual(clipno, 1, mainw->files[clipno]->frames)) {
864     mainw->current_file = clipno;
865     cfile->progress_start = 1;
866     cfile->progress_end = count_virtual_frames(cfile->frame_index, 1, cfile->frames);
867     if (enough) mainw->cancel_type = CANCEL_SOFT; // force "Enough" button to be shown
868     do_threaded_dialog((char *)msg, TRUE);
869     lives_widget_show_all(mainw->proc_ptr->processing);
870     mainw->cancel_type = CANCEL_KILL;
871     ret = virtual_to_images(mainw->current_file, 1, cfile->frames, TRUE, NULL);
872     end_threaded_dialog();
873     mainw->current_file = current_file;
874 
875     if (mainw->cancelled != CANCEL_NONE) {
876       mainw->cancelled = CANCEL_USER;
877       return ret;
878     }
879     if (ret <= 0) return ret;
880   }
881   return mainw->files[clipno]->frames;
882 }
883 
884 
insert_images_in_virtual(int sfileno,frames_t where,frames_t frames,frames_t * frame_index,frames_t start)885 void insert_images_in_virtual(int sfileno, frames_t where, frames_t frames, frames_t *frame_index, frames_t start) {
886   // insert physical (frames) images (or virtual possibly) into sfile at position where [0 = before first frame]
887   // this is the virtual (book-keeping) part
888 
889   // need to update the frame_index
890 
891   // this is for clip type CLIP_TYPE_FILE only
892 
893   lives_clip_t *sfile = mainw->files[sfileno];
894   LiVESResponseType response;
895 
896   char *what;
897   frames_t nframes = sfile->frames;
898   frames_t i, j = start - 1;
899 
900   if (!sfile->frame_index) return;
901 
902   what = (_("creating the new frame index for the clip"));
903   lives_freep((void **)&sfile->frame_index_back);
904 
905   sfile->frame_index_back = sfile->frame_index;
906   sfile->frame_index = NULL;
907 
908   do {
909     response = LIVES_RESPONSE_OK;
910     create_frame_index(sfileno, FALSE, 0, nframes + frames);
911     if (!sfile->frame_index) {
912       response = do_memory_error_dialog(what, (nframes + frames) * sizeof(frames_t));
913     }
914   } while (response == LIVES_RESPONSE_RETRY);
915   lives_free(what);
916   if (response == LIVES_RESPONSE_CANCEL) {
917     sfile->frame_index = sfile->frame_index_back;
918     sfile->frame_index_back = NULL;
919     return;
920   }
921 
922   lives_memcpy(sfile->frame_index, sfile->frame_index_back, where * sizeof(frames_t));
923 
924   for (i = where; i < where + frames; i++) {
925     if (frame_index && frame_index[j] != -1) sfile->frame_index[i] = frame_index[j];
926     else sfile->frame_index[i] = -1;
927     if (++j >= sfile->frames) j = 0;
928   }
929 
930   lives_memcpy(&sfile->frame_index[where + frames], &sfile->frame_index_back[where], (nframes - where) * sizeof(frames_t));
931 
932   sfile->frames += frames;
933   save_frame_index(sfileno);
934   sfile->frames -= frames;
935 }
936 
937 
delete_frames_from_virtual(int sfileno,frames_t start,frames_t end)938 void delete_frames_from_virtual(int sfileno, frames_t start, frames_t end) {
939   // delete (frames) images from sfile at position start to end
940   // this is the virtual (book-keeping) part
941 
942   // need to update the frame_index
943 
944   // this is for clip type CLIP_TYPE_FILE only
945 
946   lives_clip_t *sfile = mainw->files[sfileno];
947   LiVESResponseType response;
948 
949   char *what = (_("creating the new frame index for the clip"));
950   frames_t nframes = sfile->frames, frames = end - start + 1;
951 
952   lives_freep((void **)&sfile->frame_index_back);
953 
954   sfile->frame_index_back = sfile->frame_index;
955   sfile->frame_index = NULL;
956 
957   if (nframes - frames == 0) {
958     del_frame_index(sfile);
959     return;
960   }
961 
962   do {
963     response = LIVES_RESPONSE_OK;
964     create_frame_index(sfileno, FALSE, 0, nframes - frames);
965     if (!sfile->frame_index) {
966       response = do_memory_error_dialog(what, (nframes - frames) * sizeof(frames_t));
967     }
968   } while (response == LIVES_RESPONSE_RETRY);
969   lives_free(what);
970   if (response == LIVES_RESPONSE_CANCEL) {
971     sfile->frame_index = sfile->frame_index_back;
972     sfile->frame_index_back = NULL;
973     return;
974   }
975 
976   lives_memcpy(sfile->frame_index, sfile->frame_index_back, (start - 1) * sizeof(frames_t));
977   lives_memcpy(&sfile->frame_index[start - 1], &sfile->frame_index_back[end], (nframes - end) * sizeof(frames_t));
978 
979   sfile->frames = nframes - frames;
980   save_frame_index(sfileno);
981   sfile->frames = nframes;
982 }
983 
984 
reverse_frame_index(int sfileno)985 void reverse_frame_index(int sfileno) {
986   // reverse order of (virtual) frames in clip (only used fro clipboard)
987   lives_clip_t *sfile = mainw->files[sfileno];
988   int bck;
989 
990   if (!sfile || !sfile->frame_index) return;
991 
992   for (frames_t i = 0; i < sfile->frames >> 1; i++) {
993     bck = sfile->frame_index[i];
994     sfile->frame_index[i] = sfile->frame_index[sfile->frames - 1 - i];
995     sfile->frame_index[sfile->frames - 1 - i] = bck;
996   }
997 }
998 
999 
restore_frame_index_back(int sfileno)1000 void restore_frame_index_back(int sfileno) {
1001   // undo an operation
1002   // this is the virtual (book-keeping) part
1003 
1004   // need to update the frame_index
1005 
1006   // this is for clip type CLIP_TYPE_FILE only
1007 
1008   lives_clip_t *sfile = mainw->files[sfileno];
1009 
1010   lives_freep((void **)&sfile->frame_index);
1011 
1012   sfile->frame_index = sfile->frame_index_back;
1013   sfile->frame_index_back = NULL;
1014 
1015   if (sfile->frame_index) {
1016     sfile->clip_type = CLIP_TYPE_FILE;
1017     save_frame_index(sfileno);
1018   } else {
1019     del_frame_index(sfile);
1020     sfile->clip_type = CLIP_TYPE_DISK;
1021   }
1022 }
1023 
1024 
clean_images_from_virtual(lives_clip_t * sfile,frames_t oldsframe,frames_t oldframes)1025 void clean_images_from_virtual(lives_clip_t *sfile, frames_t oldsframe, frames_t oldframes) {
1026   // remove images on disk where the frame_index points to a frame in
1027   // the original clip
1028 
1029   // only needed if frames were reordered when rendered and the process is
1030   // then undone
1031 
1032   // oldsframe is > 1 if we rendered to a selection
1033 
1034   // should be threadsafe, provided the frame_index does not change
1035 
1036   // the only purpose of this is to reclaim disk space
1037 
1038   char *iname = NULL;
1039 
1040   if (!sfile || !sfile->frame_index) return;
1041 
1042   for (frames_t i = oldsframe; i <= oldframes; i++) {
1043     threaded_dialog_spin(0.);
1044     if ((i <= sfile->frames && sfile->frame_index[i - 1] != -1) || i > sfile->frames) {
1045       iname = make_image_file_name(sfile, i, get_image_ext_for_type(sfile->img_type));
1046       lives_rm(iname);
1047     }
1048   }
1049 }
1050 
1051 
frame_index_copy(frames_t * findex,frames_t nframes,frames_t offset)1052 frames_t *frame_index_copy(frames_t *findex, frames_t nframes, frames_t offset) {
1053   // copy first nframes from findex and return them, adding offset to each value
1054   // no checking is done to make sure nframes is in range
1055 
1056   // start at frame offset
1057   frames_t *findexc = (frames_t *)lives_calloc(nframes, sizeof(frames_t));;
1058   for (int i = 0; i < nframes; i++) findexc[i] = findex[i + offset];
1059   return findexc;
1060 }
1061 
1062 
is_virtual_frame(int sfileno,frames_t frame)1063 boolean is_virtual_frame(int sfileno, frames_t frame) {
1064   // frame is virtual if it is still inside a video clip (read only)
1065   // once a frame is on disk as an image it is no longer virtual
1066 
1067   // frame starts at 1 here
1068 
1069   // a CLIP_TYPE_FILE with no virtual frames becomes a CLIP_TYPE_DISK
1070 
1071   lives_clip_t *sfile = mainw->files[sfileno];
1072   if (!IS_VALID_CLIP(sfileno)) return FALSE;
1073   if (!sfile->frame_index) return FALSE;
1074   if (frame < 1 || frame > sfile->frames) return FALSE;
1075   if (sfile->frame_index[frame - 1] != -1) return TRUE;
1076   return FALSE;
1077 }
1078 
1079 
insert_blank_frames(int sfileno,frames_t nframes,frames_t after,int palette)1080 void insert_blank_frames(int sfileno, frames_t nframes, frames_t after, int palette) {
1081   // insert blank frames in clip (only valid just after clip is opened)
1082 
1083   // this is ugly, it should be moved to another file
1084 
1085   lives_clip_t *sfile = mainw->files[sfileno];
1086   LiVESPixbuf *blankp = NULL;
1087   LiVESError *error = NULL;
1088   char oname[PATH_MAX];
1089   char nname[PATH_MAX];
1090   char *tmp;
1091 
1092   frames_t i;
1093 
1094   if (first_virtual_frame(sfileno, 1, sfile->frames) != 0) {
1095     for (i = after + 1; i <= sfile->frames; i++) {
1096       if (!sfile->frame_index || sfile->frame_index[i - 1] == -1) {
1097         tmp = make_image_file_name(sfile, i, get_image_ext_for_type(sfile->img_type));
1098         lives_snprintf(oname, PATH_MAX, "%s", tmp);
1099         lives_free(tmp);
1100         if (lives_file_test(oname, LIVES_FILE_TEST_EXISTS)) {
1101           tmp = make_image_file_name(sfile, i + nframes, get_image_ext_for_type(sfile->img_type));
1102           lives_snprintf(nname, PATH_MAX, "%s", tmp);
1103           lives_free(tmp);
1104           lives_mv(oname, nname);
1105           if (THREADVAR(com_failed)) {
1106             return;
1107 	    // *INDENT-OFF*
1108           }}}}}
1109   // *INDENT-ON*
1110 
1111   for (i = after; i < after + nframes; i++) {
1112     tmp = make_image_file_name(sfile, i + 1, get_image_ext_for_type(sfile->img_type));
1113     lives_snprintf(oname, PATH_MAX, "%s", tmp);
1114     lives_free(tmp);
1115     if (!blankp) blankp = lives_pixbuf_new_blank(sfile->hsize, sfile->vsize, palette);
1116     lives_pixbuf_save(blankp, oname, sfile->img_type, 100 - prefs->ocp, sfile->hsize, sfile->vsize, &error);
1117     if (error) {
1118       char *msg = lives_strdup_printf(_("Padding: Unable to write blank frame with size %d x %d to %s"),
1119                                       sfile->hsize, sfile->vsize, oname);
1120       LIVES_ERROR(msg);
1121       lives_free(msg);
1122       lives_error_free(error);
1123       break;
1124     }
1125   }
1126 
1127   nframes = i - after; // in case we bailed
1128 
1129   if (sfile->clip_type == CLIP_TYPE_FILE)
1130     insert_images_in_virtual(sfileno, after, nframes, NULL, 0);
1131 
1132   sfile->frames += nframes;
1133 
1134   if (blankp) lives_widget_object_unref(blankp);
1135 }
1136 
1137 
pull_frame_idle(livespointer data)1138 boolean pull_frame_idle(livespointer data) {
1139   if (cfile->fx_frame_pump >= cfile->end) return FALSE;
1140   if (virtual_to_images(mainw->current_file, cfile->fx_frame_pump, cfile->fx_frame_pump, FALSE, NULL) <= 0) {
1141     return FALSE;
1142   }
1143   cfile->fx_frame_pump++;
1144   return TRUE;
1145 }
1146