1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * Peter Schlaile <peter [at] schlaile [dot] de> 2011
17  */
18 
19 /** \file
20  * \ingroup imbuf
21  */
22 
23 #include <stdlib.h>
24 
25 #include "MEM_guardedalloc.h"
26 
27 #include "BLI_endian_switch.h"
28 #include "BLI_fileops.h"
29 #include "BLI_ghash.h"
30 #include "BLI_path_util.h"
31 #include "BLI_string.h"
32 #include "BLI_utildefines.h"
33 #ifdef _WIN32
34 #  include "BLI_winstuff.h"
35 #endif
36 
37 #include "IMB_anim.h"
38 #include "IMB_indexer.h"
39 #include "imbuf.h"
40 
41 #include "BKE_global.h"
42 
43 #ifdef WITH_AVI
44 #  include "AVI_avi.h"
45 #endif
46 
47 #ifdef WITH_FFMPEG
48 #  include "ffmpeg_compat.h"
49 #endif
50 
51 static const char magic[] = "BlenMIdx";
52 static const char temp_ext[] = "_part";
53 
54 static const int proxy_sizes[] = {IMB_PROXY_25, IMB_PROXY_50, IMB_PROXY_75, IMB_PROXY_100};
55 static const float proxy_fac[] = {0.25, 0.50, 0.75, 1.00};
56 
57 #ifdef WITH_FFMPEG
58 static int tc_types[] = {
59     IMB_TC_RECORD_RUN,
60     IMB_TC_FREE_RUN,
61     IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN,
62     IMB_TC_RECORD_RUN_NO_GAPS,
63 };
64 #endif
65 
66 #define INDEX_FILE_VERSION 1
67 
68 /* ----------------------------------------------------------------------
69  * - time code index functions
70  * ---------------------------------------------------------------------- */
71 
IMB_index_builder_create(const char * name)72 anim_index_builder *IMB_index_builder_create(const char *name)
73 {
74 
75   anim_index_builder *rv = MEM_callocN(sizeof(struct anim_index_builder), "index builder");
76 
77   fprintf(stderr, "Starting work on index: %s\n", name);
78 
79   BLI_strncpy(rv->name, name, sizeof(rv->name));
80   BLI_strncpy(rv->temp_name, name, sizeof(rv->temp_name));
81 
82   strcat(rv->temp_name, temp_ext);
83 
84   BLI_make_existing_file(rv->temp_name);
85 
86   rv->fp = BLI_fopen(rv->temp_name, "wb");
87 
88   if (!rv->fp) {
89     fprintf(stderr,
90             "Couldn't open index target: %s! "
91             "Index build broken!\n",
92             rv->temp_name);
93     MEM_freeN(rv);
94     return NULL;
95   }
96 
97   fprintf(rv->fp, "%s%c%.3d", magic, (ENDIAN_ORDER == B_ENDIAN) ? 'V' : 'v', INDEX_FILE_VERSION);
98 
99   return rv;
100 }
101 
IMB_index_builder_add_entry(anim_index_builder * fp,int frameno,unsigned long long seek_pos,unsigned long long seek_pos_dts,unsigned long long pts)102 void IMB_index_builder_add_entry(anim_index_builder *fp,
103                                  int frameno,
104                                  unsigned long long seek_pos,
105                                  unsigned long long seek_pos_dts,
106                                  unsigned long long pts)
107 {
108   fwrite(&frameno, sizeof(int), 1, fp->fp);
109   fwrite(&seek_pos, sizeof(unsigned long long), 1, fp->fp);
110   fwrite(&seek_pos_dts, sizeof(unsigned long long), 1, fp->fp);
111   fwrite(&pts, sizeof(unsigned long long), 1, fp->fp);
112 }
113 
IMB_index_builder_proc_frame(anim_index_builder * fp,unsigned char * buffer,int data_size,int frameno,unsigned long long seek_pos,unsigned long long seek_pos_dts,unsigned long long pts)114 void IMB_index_builder_proc_frame(anim_index_builder *fp,
115                                   unsigned char *buffer,
116                                   int data_size,
117                                   int frameno,
118                                   unsigned long long seek_pos,
119                                   unsigned long long seek_pos_dts,
120                                   unsigned long long pts)
121 {
122   if (fp->proc_frame) {
123     anim_index_entry e;
124     e.frameno = frameno;
125     e.seek_pos = seek_pos;
126     e.seek_pos_dts = seek_pos_dts;
127     e.pts = pts;
128 
129     fp->proc_frame(fp, buffer, data_size, &e);
130   }
131   else {
132     IMB_index_builder_add_entry(fp, frameno, seek_pos, seek_pos_dts, pts);
133   }
134 }
135 
IMB_index_builder_finish(anim_index_builder * fp,int rollback)136 void IMB_index_builder_finish(anim_index_builder *fp, int rollback)
137 {
138   if (fp->delete_priv_data) {
139     fp->delete_priv_data(fp);
140   }
141 
142   fclose(fp->fp);
143 
144   if (rollback) {
145     unlink(fp->temp_name);
146   }
147   else {
148     unlink(fp->name);
149     BLI_rename(fp->temp_name, fp->name);
150   }
151 
152   MEM_freeN(fp);
153 }
154 
IMB_indexer_open(const char * name)155 struct anim_index *IMB_indexer_open(const char *name)
156 {
157   char header[13];
158   struct anim_index *idx;
159   FILE *fp = BLI_fopen(name, "rb");
160   int i;
161 
162   if (!fp) {
163     return NULL;
164   }
165 
166   if (fread(header, 12, 1, fp) != 1) {
167     fclose(fp);
168     return NULL;
169   }
170 
171   header[12] = 0;
172 
173   if (memcmp(header, magic, 8) != 0) {
174     fclose(fp);
175     return NULL;
176   }
177 
178   if (atoi(header + 9) != INDEX_FILE_VERSION) {
179     fclose(fp);
180     return NULL;
181   }
182 
183   idx = MEM_callocN(sizeof(struct anim_index), "anim_index");
184 
185   BLI_strncpy(idx->name, name, sizeof(idx->name));
186 
187   fseek(fp, 0, SEEK_END);
188 
189   idx->num_entries = (ftell(fp) - 12) / (sizeof(int) +                /* framepos */
190                                          sizeof(unsigned long long) + /* seek_pos */
191                                          sizeof(unsigned long long) + /* seek_pos_dts */
192                                          sizeof(unsigned long long)   /* pts */
193                                         );
194 
195   fseek(fp, 12, SEEK_SET);
196 
197   idx->entries = MEM_callocN(sizeof(struct anim_index_entry) * idx->num_entries,
198                              "anim_index_entries");
199 
200   for (i = 0; i < idx->num_entries; i++) {
201     fread(&idx->entries[i].frameno, sizeof(int), 1, fp);
202     fread(&idx->entries[i].seek_pos, sizeof(unsigned long long), 1, fp);
203     fread(&idx->entries[i].seek_pos_dts, sizeof(unsigned long long), 1, fp);
204     fread(&idx->entries[i].pts, sizeof(unsigned long long), 1, fp);
205   }
206 
207   if (((ENDIAN_ORDER == B_ENDIAN) != (header[8] == 'V'))) {
208     for (i = 0; i < idx->num_entries; i++) {
209       BLI_endian_switch_int32(&idx->entries[i].frameno);
210       BLI_endian_switch_int64((int64_t *)&idx->entries[i].seek_pos);
211       BLI_endian_switch_int64((int64_t *)&idx->entries[i].seek_pos_dts);
212       BLI_endian_switch_int64((int64_t *)&idx->entries[i].pts);
213     }
214   }
215 
216   fclose(fp);
217 
218   return idx;
219 }
220 
IMB_indexer_get_seek_pos(struct anim_index * idx,int frame_index)221 unsigned long long IMB_indexer_get_seek_pos(struct anim_index *idx, int frame_index)
222 {
223   if (frame_index < 0) {
224     frame_index = 0;
225   }
226   if (frame_index >= idx->num_entries) {
227     frame_index = idx->num_entries - 1;
228   }
229   return idx->entries[frame_index].seek_pos;
230 }
231 
IMB_indexer_get_seek_pos_dts(struct anim_index * idx,int frame_index)232 unsigned long long IMB_indexer_get_seek_pos_dts(struct anim_index *idx, int frame_index)
233 {
234   if (frame_index < 0) {
235     frame_index = 0;
236   }
237   if (frame_index >= idx->num_entries) {
238     frame_index = idx->num_entries - 1;
239   }
240   return idx->entries[frame_index].seek_pos_dts;
241 }
242 
IMB_indexer_get_frame_index(struct anim_index * idx,int frameno)243 int IMB_indexer_get_frame_index(struct anim_index *idx, int frameno)
244 {
245   int len = idx->num_entries;
246   int half;
247   int middle;
248   int first = 0;
249 
250   /* bsearch (lower bound) the right index */
251 
252   while (len > 0) {
253     half = len >> 1;
254     middle = first;
255 
256     middle += half;
257 
258     if (idx->entries[middle].frameno < frameno) {
259       first = middle;
260       first++;
261       len = len - half - 1;
262     }
263     else {
264       len = half;
265     }
266   }
267 
268   if (first == idx->num_entries) {
269     return idx->num_entries - 1;
270   }
271 
272   return first;
273 }
274 
IMB_indexer_get_pts(struct anim_index * idx,int frame_index)275 unsigned long long IMB_indexer_get_pts(struct anim_index *idx, int frame_index)
276 {
277   if (frame_index < 0) {
278     frame_index = 0;
279   }
280   if (frame_index >= idx->num_entries) {
281     frame_index = idx->num_entries - 1;
282   }
283   return idx->entries[frame_index].pts;
284 }
285 
IMB_indexer_get_duration(struct anim_index * idx)286 int IMB_indexer_get_duration(struct anim_index *idx)
287 {
288   if (idx->num_entries == 0) {
289     return 0;
290   }
291   return idx->entries[idx->num_entries - 1].frameno + 1;
292 }
293 
IMB_indexer_can_scan(struct anim_index * idx,int old_frame_index,int new_frame_index)294 int IMB_indexer_can_scan(struct anim_index *idx, int old_frame_index, int new_frame_index)
295 {
296   /* makes only sense, if it is the same I-Frame and we are not
297    * trying to run backwards in time... */
298   return (IMB_indexer_get_seek_pos(idx, old_frame_index) ==
299               IMB_indexer_get_seek_pos(idx, new_frame_index) &&
300           old_frame_index < new_frame_index);
301 }
302 
IMB_indexer_close(struct anim_index * idx)303 void IMB_indexer_close(struct anim_index *idx)
304 {
305   MEM_freeN(idx->entries);
306   MEM_freeN(idx);
307 }
308 
IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size)309 int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size)
310 {
311   switch (pr_size) {
312     case IMB_PROXY_NONE:
313       /* if we got here, something is broken anyways, so sane defaults... */
314       return 0;
315     case IMB_PROXY_25:
316       return 0;
317     case IMB_PROXY_50:
318       return 1;
319     case IMB_PROXY_75:
320       return 2;
321     case IMB_PROXY_100:
322       return 3;
323     default:
324       return 0;
325   }
326 }
327 
IMB_timecode_to_array_index(IMB_Timecode_Type tc)328 int IMB_timecode_to_array_index(IMB_Timecode_Type tc)
329 {
330   switch (tc) {
331     case IMB_TC_NONE: /* if we got here, something is broken anyways,
332                        * so sane defaults... */
333       return 0;
334     case IMB_TC_RECORD_RUN:
335       return 0;
336     case IMB_TC_FREE_RUN:
337       return 1;
338     case IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN:
339       return 2;
340     case IMB_TC_RECORD_RUN_NO_GAPS:
341       return 3;
342     default:
343       return 0;
344   }
345 }
346 
347 /* ----------------------------------------------------------------------
348  * - rebuild helper functions
349  * ---------------------------------------------------------------------- */
350 
get_index_dir(struct anim * anim,char * index_dir,size_t index_dir_len)351 static void get_index_dir(struct anim *anim, char *index_dir, size_t index_dir_len)
352 {
353   if (!anim->index_dir[0]) {
354     char fname[FILE_MAXFILE];
355     BLI_split_dirfile(anim->name, index_dir, fname, index_dir_len, sizeof(fname));
356     BLI_path_append(index_dir, index_dir_len, "BL_proxy");
357     BLI_path_append(index_dir, index_dir_len, fname);
358   }
359   else {
360     BLI_strncpy(index_dir, anim->index_dir, index_dir_len);
361   }
362 }
363 
IMB_anim_get_fname(struct anim * anim,char * file,int size)364 void IMB_anim_get_fname(struct anim *anim, char *file, int size)
365 {
366   char fname[FILE_MAXFILE];
367   BLI_split_dirfile(anim->name, file, fname, size, sizeof(fname));
368   BLI_strncpy(file, fname, size);
369 }
370 
get_proxy_filename(struct anim * anim,IMB_Proxy_Size preview_size,char * fname,bool temp)371 static bool get_proxy_filename(struct anim *anim,
372                                IMB_Proxy_Size preview_size,
373                                char *fname,
374                                bool temp)
375 {
376   char index_dir[FILE_MAXDIR];
377   int i = IMB_proxy_size_to_array_index(preview_size);
378 
379   char proxy_name[256];
380   char stream_suffix[20];
381   const char *name = (temp) ? "proxy_%d%s_part.avi" : "proxy_%d%s.avi";
382 
383   stream_suffix[0] = 0;
384 
385   if (anim->streamindex > 0) {
386     BLI_snprintf(stream_suffix, sizeof(stream_suffix), "_st%d", anim->streamindex);
387   }
388 
389   BLI_snprintf(proxy_name,
390                sizeof(proxy_name),
391                name,
392                (int)(proxy_fac[i] * 100),
393                stream_suffix,
394                anim->suffix);
395 
396   get_index_dir(anim, index_dir, sizeof(index_dir));
397 
398   if (BLI_path_ncmp(anim->name, index_dir, FILE_MAXDIR) == 0) {
399     return false;
400   }
401 
402   BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir, proxy_name);
403   return true;
404 }
405 
get_tc_filename(struct anim * anim,IMB_Timecode_Type tc,char * fname)406 static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *fname)
407 {
408   char index_dir[FILE_MAXDIR];
409   int i = IMB_timecode_to_array_index(tc);
410   const char *index_names[] = {
411       "record_run%s%s.blen_tc",
412       "free_run%s%s.blen_tc",
413       "interp_free_run%s%s.blen_tc",
414       "record_run_no_gaps%s%s.blen_tc",
415   };
416 
417   char stream_suffix[20];
418   char index_name[256];
419 
420   stream_suffix[0] = 0;
421 
422   if (anim->streamindex > 0) {
423     BLI_snprintf(stream_suffix, 20, "_st%d", anim->streamindex);
424   }
425 
426   BLI_snprintf(index_name, 256, index_names[i], stream_suffix, anim->suffix);
427 
428   get_index_dir(anim, index_dir, sizeof(index_dir));
429 
430   BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir, index_name);
431 }
432 
433 /* ----------------------------------------------------------------------
434  * - common rebuilder structures
435  * ---------------------------------------------------------------------- */
436 
437 typedef struct IndexBuildContext {
438   int anim_type;
439 } IndexBuildContext;
440 
441 /* ----------------------------------------------------------------------
442  * - ffmpeg rebuilder
443  * ---------------------------------------------------------------------- */
444 
445 #ifdef WITH_FFMPEG
446 
447 struct proxy_output_ctx {
448   AVFormatContext *of;
449   AVStream *st;
450   AVCodecContext *c;
451   AVCodec *codec;
452   struct SwsContext *sws_ctx;
453   AVFrame *frame;
454   int cfra;
455   int proxy_size;
456   int orig_height;
457   struct anim *anim;
458 };
459 
460 // work around stupid swscaler 16 bytes alignment bug...
461 
round_up(int x,int mod)462 static int round_up(int x, int mod)
463 {
464   return x + ((mod - (x % mod)) % mod);
465 }
466 
alloc_proxy_output_ffmpeg(struct anim * anim,AVStream * st,int proxy_size,int width,int height,int quality)467 static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
468     struct anim *anim, AVStream *st, int proxy_size, int width, int height, int quality)
469 {
470   struct proxy_output_ctx *rv = MEM_callocN(sizeof(struct proxy_output_ctx), "alloc_proxy_output");
471 
472   char fname[FILE_MAX];
473   int ffmpeg_quality;
474 
475   rv->proxy_size = proxy_size;
476   rv->anim = anim;
477 
478   get_proxy_filename(rv->anim, rv->proxy_size, fname, true);
479   BLI_make_existing_file(fname);
480 
481   rv->of = avformat_alloc_context();
482   rv->of->oformat = av_guess_format("avi", NULL, NULL);
483 
484   BLI_strncpy(rv->of->filename, fname, sizeof(rv->of->filename));
485 
486   fprintf(stderr, "Starting work on proxy: %s\n", rv->of->filename);
487 
488   rv->st = avformat_new_stream(rv->of, NULL);
489   rv->st->id = 0;
490 
491   rv->c = rv->st->codec;
492   rv->c->codec_type = AVMEDIA_TYPE_VIDEO;
493   rv->c->codec_id = AV_CODEC_ID_MJPEG;
494   rv->c->width = width;
495   rv->c->height = height;
496 
497   rv->of->oformat->video_codec = rv->c->codec_id;
498   rv->codec = avcodec_find_encoder(rv->c->codec_id);
499 
500   if (!rv->codec) {
501     fprintf(stderr,
502             "No ffmpeg MJPEG encoder available? "
503             "Proxy not built!\n");
504     av_free(rv->of);
505     return NULL;
506   }
507 
508   if (rv->codec->pix_fmts) {
509     rv->c->pix_fmt = rv->codec->pix_fmts[0];
510   }
511   else {
512     rv->c->pix_fmt = AV_PIX_FMT_YUVJ420P;
513   }
514 
515   rv->c->sample_aspect_ratio = rv->st->sample_aspect_ratio = st->codec->sample_aspect_ratio;
516 
517   rv->c->time_base.den = 25;
518   rv->c->time_base.num = 1;
519   rv->st->time_base = rv->c->time_base;
520 
521   /* there's no  way to set JPEG quality in the same way as in AVI JPEG and image sequence,
522    * but this seems to be giving expected quality result */
523   ffmpeg_quality = (int)(1.0f + 30.0f * (1.0f - (float)quality / 100.0f) + 0.5f);
524   av_opt_set_int(rv->c, "qmin", ffmpeg_quality, 0);
525   av_opt_set_int(rv->c, "qmax", ffmpeg_quality, 0);
526 
527   if (rv->of->flags & AVFMT_GLOBALHEADER) {
528     rv->c->flags |= CODEC_FLAG_GLOBAL_HEADER;
529   }
530 
531   if (avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE) < 0) {
532     fprintf(stderr,
533             "Couldn't open outputfile! "
534             "Proxy not built!\n");
535     av_free(rv->of);
536     return 0;
537   }
538 
539   avcodec_open2(rv->c, rv->codec, NULL);
540 
541   rv->orig_height = av_get_cropped_height_from_codec(st->codec);
542 
543   if (st->codec->width != width || st->codec->height != height ||
544       st->codec->pix_fmt != rv->c->pix_fmt) {
545     rv->frame = av_frame_alloc();
546     avpicture_fill((AVPicture *)rv->frame,
547                    MEM_mallocN(avpicture_get_size(rv->c->pix_fmt, round_up(width, 16), height),
548                                "alloc proxy output frame"),
549                    rv->c->pix_fmt,
550                    round_up(width, 16),
551                    height);
552 
553     rv->sws_ctx = sws_getContext(st->codec->width,
554                                  rv->orig_height,
555                                  st->codec->pix_fmt,
556                                  width,
557                                  height,
558                                  rv->c->pix_fmt,
559                                  SWS_FAST_BILINEAR | SWS_PRINT_INFO,
560                                  NULL,
561                                  NULL,
562                                  NULL);
563   }
564 
565   if (avformat_write_header(rv->of, NULL) < 0) {
566     fprintf(stderr,
567             "Couldn't set output parameters? "
568             "Proxy not built!\n");
569     av_free(rv->of);
570     return 0;
571   }
572 
573   return rv;
574 }
575 
add_to_proxy_output_ffmpeg(struct proxy_output_ctx * ctx,AVFrame * frame)576 static int add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *frame)
577 {
578   AVPacket packet = {0};
579   int ret, got_output;
580 
581   av_init_packet(&packet);
582 
583   if (!ctx) {
584     return 0;
585   }
586 
587   if (ctx->sws_ctx && frame &&
588       (frame->data[0] || frame->data[1] || frame->data[2] || frame->data[3])) {
589     sws_scale(ctx->sws_ctx,
590               (const uint8_t *const *)frame->data,
591               frame->linesize,
592               0,
593               ctx->orig_height,
594               ctx->frame->data,
595               ctx->frame->linesize);
596   }
597 
598   frame = ctx->sws_ctx ? (frame ? ctx->frame : 0) : frame;
599 
600   if (frame) {
601     frame->pts = ctx->cfra++;
602   }
603 
604   ret = avcodec_encode_video2(ctx->c, &packet, frame, &got_output);
605   if (ret < 0) {
606     fprintf(stderr, "Error encoding proxy frame %d for '%s'\n", ctx->cfra - 1, ctx->of->filename);
607     return 0;
608   }
609 
610   if (got_output) {
611     if (packet.pts != AV_NOPTS_VALUE) {
612       packet.pts = av_rescale_q(packet.pts, ctx->c->time_base, ctx->st->time_base);
613     }
614     if (packet.dts != AV_NOPTS_VALUE) {
615       packet.dts = av_rescale_q(packet.dts, ctx->c->time_base, ctx->st->time_base);
616     }
617 
618     packet.stream_index = ctx->st->index;
619 
620     if (av_interleaved_write_frame(ctx->of, &packet) != 0) {
621       fprintf(stderr,
622               "Error writing proxy frame %d "
623               "into '%s'\n",
624               ctx->cfra - 1,
625               ctx->of->filename);
626       return 0;
627     }
628 
629     return 1;
630   }
631 
632   return 0;
633 }
634 
free_proxy_output_ffmpeg(struct proxy_output_ctx * ctx,int rollback)635 static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback)
636 {
637   char fname[FILE_MAX];
638   char fname_tmp[FILE_MAX];
639 
640   if (!ctx) {
641     return;
642   }
643 
644   if (!rollback) {
645     while (add_to_proxy_output_ffmpeg(ctx, NULL)) {
646     }
647   }
648 
649   avcodec_flush_buffers(ctx->c);
650 
651   av_write_trailer(ctx->of);
652 
653   avcodec_close(ctx->c);
654 
655   if (ctx->of->oformat) {
656     if (!(ctx->of->oformat->flags & AVFMT_NOFILE)) {
657       avio_close(ctx->of->pb);
658     }
659   }
660   avformat_free_context(ctx->of);
661 
662   if (ctx->sws_ctx) {
663     sws_freeContext(ctx->sws_ctx);
664 
665     MEM_freeN(ctx->frame->data[0]);
666     av_free(ctx->frame);
667   }
668 
669   get_proxy_filename(ctx->anim, ctx->proxy_size, fname_tmp, true);
670 
671   if (rollback) {
672     unlink(fname_tmp);
673   }
674   else {
675     get_proxy_filename(ctx->anim, ctx->proxy_size, fname, false);
676     unlink(fname);
677     BLI_rename(fname_tmp, fname);
678   }
679 
680   MEM_freeN(ctx);
681 }
682 
683 typedef struct FFmpegIndexBuilderContext {
684   int anim_type;
685 
686   AVFormatContext *iFormatCtx;
687   AVCodecContext *iCodecCtx;
688   AVCodec *iCodec;
689   AVStream *iStream;
690   int videoStream;
691 
692   int num_proxy_sizes;
693   int num_indexers;
694 
695   struct proxy_output_ctx *proxy_ctx[IMB_PROXY_MAX_SLOT];
696   anim_index_builder *indexer[IMB_TC_MAX_SLOT];
697 
698   IMB_Timecode_Type tcs_in_use;
699   IMB_Proxy_Size proxy_sizes_in_use;
700 
701   unsigned long long seek_pos;
702   unsigned long long last_seek_pos;
703   unsigned long long seek_pos_dts;
704   unsigned long long seek_pos_pts;
705   unsigned long long last_seek_pos_dts;
706   unsigned long long start_pts;
707   double frame_rate;
708   double pts_time_base;
709   int frameno, frameno_gapless;
710   int start_pts_set;
711 } FFmpegIndexBuilderContext;
712 
index_ffmpeg_create_context(struct anim * anim,IMB_Timecode_Type tcs_in_use,IMB_Proxy_Size proxy_sizes_in_use,int quality)713 static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim,
714                                                       IMB_Timecode_Type tcs_in_use,
715                                                       IMB_Proxy_Size proxy_sizes_in_use,
716                                                       int quality)
717 {
718   FFmpegIndexBuilderContext *context = MEM_callocN(sizeof(FFmpegIndexBuilderContext),
719                                                    "FFmpeg index builder context");
720   int num_proxy_sizes = IMB_PROXY_MAX_SLOT;
721   int num_indexers = IMB_TC_MAX_SLOT;
722   int i, streamcount;
723 
724   context->tcs_in_use = tcs_in_use;
725   context->proxy_sizes_in_use = proxy_sizes_in_use;
726   context->num_proxy_sizes = IMB_PROXY_MAX_SLOT;
727   context->num_indexers = IMB_TC_MAX_SLOT;
728 
729   memset(context->proxy_ctx, 0, sizeof(context->proxy_ctx));
730   memset(context->indexer, 0, sizeof(context->indexer));
731 
732   if (avformat_open_input(&context->iFormatCtx, anim->name, NULL, NULL) != 0) {
733     MEM_freeN(context);
734     return NULL;
735   }
736 
737   if (avformat_find_stream_info(context->iFormatCtx, NULL) < 0) {
738     avformat_close_input(&context->iFormatCtx);
739     MEM_freeN(context);
740     return NULL;
741   }
742 
743   streamcount = anim->streamindex;
744 
745   /* Find the video stream */
746   context->videoStream = -1;
747   for (i = 0; i < context->iFormatCtx->nb_streams; i++) {
748     if (context->iFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
749       if (streamcount > 0) {
750         streamcount--;
751         continue;
752       }
753       context->videoStream = i;
754       break;
755     }
756   }
757 
758   if (context->videoStream == -1) {
759     avformat_close_input(&context->iFormatCtx);
760     MEM_freeN(context);
761     return NULL;
762   }
763 
764   context->iStream = context->iFormatCtx->streams[context->videoStream];
765   context->iCodecCtx = context->iStream->codec;
766 
767   context->iCodec = avcodec_find_decoder(context->iCodecCtx->codec_id);
768 
769   if (context->iCodec == NULL) {
770     avformat_close_input(&context->iFormatCtx);
771     MEM_freeN(context);
772     return NULL;
773   }
774 
775   context->iCodecCtx->workaround_bugs = 1;
776 
777   if (avcodec_open2(context->iCodecCtx, context->iCodec, NULL) < 0) {
778     avformat_close_input(&context->iFormatCtx);
779     MEM_freeN(context);
780     return NULL;
781   }
782 
783   for (i = 0; i < num_proxy_sizes; i++) {
784     if (proxy_sizes_in_use & proxy_sizes[i]) {
785       context->proxy_ctx[i] = alloc_proxy_output_ffmpeg(
786           anim,
787           context->iStream,
788           proxy_sizes[i],
789           context->iCodecCtx->width * proxy_fac[i],
790           av_get_cropped_height_from_codec(context->iCodecCtx) * proxy_fac[i],
791           quality);
792       if (!context->proxy_ctx[i]) {
793         proxy_sizes_in_use &= ~proxy_sizes[i];
794       }
795     }
796   }
797 
798   for (i = 0; i < num_indexers; i++) {
799     if (tcs_in_use & tc_types[i]) {
800       char fname[FILE_MAX];
801 
802       get_tc_filename(anim, tc_types[i], fname);
803 
804       context->indexer[i] = IMB_index_builder_create(fname);
805       if (!context->indexer[i]) {
806         tcs_in_use &= ~tc_types[i];
807       }
808     }
809   }
810 
811   return (IndexBuildContext *)context;
812 }
813 
index_rebuild_ffmpeg_finish(FFmpegIndexBuilderContext * context,int stop)814 static void index_rebuild_ffmpeg_finish(FFmpegIndexBuilderContext *context, int stop)
815 {
816   int i;
817 
818   for (i = 0; i < context->num_indexers; i++) {
819     if (context->tcs_in_use & tc_types[i]) {
820       IMB_index_builder_finish(context->indexer[i], stop);
821     }
822   }
823 
824   for (i = 0; i < context->num_proxy_sizes; i++) {
825     if (context->proxy_sizes_in_use & proxy_sizes[i]) {
826       free_proxy_output_ffmpeg(context->proxy_ctx[i], stop);
827     }
828   }
829 
830   avcodec_close(context->iCodecCtx);
831   avformat_close_input(&context->iFormatCtx);
832 
833   MEM_freeN(context);
834 }
835 
index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext * context,AVPacket * curr_packet,AVFrame * in_frame)836 static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *context,
837                                                     AVPacket *curr_packet,
838                                                     AVFrame *in_frame)
839 {
840   int i;
841   unsigned long long s_pos = context->seek_pos;
842   unsigned long long s_dts = context->seek_pos_dts;
843   unsigned long long pts = av_get_pts_from_frame(context->iFormatCtx, in_frame);
844 
845   for (i = 0; i < context->num_proxy_sizes; i++) {
846     add_to_proxy_output_ffmpeg(context->proxy_ctx[i], in_frame);
847   }
848 
849   if (!context->start_pts_set) {
850     context->start_pts = pts;
851     context->start_pts_set = true;
852   }
853 
854   context->frameno = floor(
855       (pts - context->start_pts) * context->pts_time_base * context->frame_rate + 0.5);
856 
857   /* decoding starts *always* on I-Frames,
858    * so: P-Frames won't work, even if all the
859    * information is in place, when we seek
860    * to the I-Frame presented *after* the P-Frame,
861    * but located before the P-Frame within
862    * the stream */
863 
864   if (pts < context->seek_pos_pts) {
865     s_pos = context->last_seek_pos;
866     s_dts = context->last_seek_pos_dts;
867   }
868 
869   for (i = 0; i < context->num_indexers; i++) {
870     if (context->tcs_in_use & tc_types[i]) {
871       int tc_frameno = context->frameno;
872 
873       if (tc_types[i] == IMB_TC_RECORD_RUN_NO_GAPS) {
874         tc_frameno = context->frameno_gapless;
875       }
876 
877       IMB_index_builder_proc_frame(context->indexer[i],
878                                    curr_packet->data,
879                                    curr_packet->size,
880                                    tc_frameno,
881                                    s_pos,
882                                    s_dts,
883                                    pts);
884     }
885   }
886 
887   context->frameno_gapless++;
888 }
889 
index_rebuild_ffmpeg(FFmpegIndexBuilderContext * context,const short * stop,short * do_update,float * progress)890 static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context,
891                                 const short *stop,
892                                 short *do_update,
893                                 float *progress)
894 {
895   AVFrame *in_frame = 0;
896   AVPacket next_packet;
897   uint64_t stream_size;
898 
899   memset(&next_packet, 0, sizeof(AVPacket));
900 
901   in_frame = av_frame_alloc();
902 
903   stream_size = avio_size(context->iFormatCtx->pb);
904 
905   context->frame_rate = av_q2d(av_guess_frame_rate(context->iFormatCtx, context->iStream, NULL));
906   context->pts_time_base = av_q2d(context->iStream->time_base);
907 
908   while (av_read_frame(context->iFormatCtx, &next_packet) >= 0) {
909     int frame_finished = 0;
910     float next_progress =
911         (float)((int)floor(((double)next_packet.pos) * 100 / ((double)stream_size) + 0.5)) / 100;
912 
913     if (*progress != next_progress) {
914       *progress = next_progress;
915       *do_update = true;
916     }
917 
918     if (*stop) {
919       av_free_packet(&next_packet);
920       break;
921     }
922 
923     if (next_packet.stream_index == context->videoStream) {
924       if (next_packet.flags & AV_PKT_FLAG_KEY) {
925         context->last_seek_pos = context->seek_pos;
926         context->last_seek_pos_dts = context->seek_pos_dts;
927         context->seek_pos = next_packet.pos;
928         context->seek_pos_dts = next_packet.dts;
929         context->seek_pos_pts = next_packet.pts;
930       }
931 
932       avcodec_decode_video2(context->iCodecCtx, in_frame, &frame_finished, &next_packet);
933     }
934 
935     if (frame_finished) {
936       index_rebuild_ffmpeg_proc_decoded_frame(context, &next_packet, in_frame);
937     }
938     av_free_packet(&next_packet);
939   }
940 
941   /* process pictures still stuck in decoder engine after EOF
942    * according to ffmpeg docs using 0-size packets.
943    *
944    * At least, if we haven't already stopped... */
945 
946   /* this creates the 0-size packet and prevents a memory leak. */
947   av_free_packet(&next_packet);
948 
949   if (!*stop) {
950     int frame_finished;
951 
952     do {
953       frame_finished = 0;
954 
955       avcodec_decode_video2(context->iCodecCtx, in_frame, &frame_finished, &next_packet);
956 
957       if (frame_finished) {
958         index_rebuild_ffmpeg_proc_decoded_frame(context, &next_packet, in_frame);
959       }
960     } while (frame_finished);
961   }
962 
963   av_free(in_frame);
964 
965   return 1;
966 }
967 
968 #endif
969 
970 /* ----------------------------------------------------------------------
971  * - internal AVI (fallback) rebuilder
972  * ---------------------------------------------------------------------- */
973 
974 #ifdef WITH_AVI
975 typedef struct FallbackIndexBuilderContext {
976   int anim_type;
977 
978   struct anim *anim;
979   AviMovie *proxy_ctx[IMB_PROXY_MAX_SLOT];
980   IMB_Proxy_Size proxy_sizes_in_use;
981 } FallbackIndexBuilderContext;
982 
alloc_proxy_output_avi(struct anim * anim,char * filename,int width,int height,int quality)983 static AviMovie *alloc_proxy_output_avi(
984     struct anim *anim, char *filename, int width, int height, int quality)
985 {
986   int x, y;
987   AviFormat format;
988   double framerate;
989   AviMovie *avi;
990   /* it doesn't really matter for proxies, but sane defaults help anyways...*/
991   short frs_sec = 25;
992   float frs_sec_base = 1.0;
993 
994   IMB_anim_get_fps(anim, &frs_sec, &frs_sec_base, false);
995 
996   x = width;
997   y = height;
998 
999   framerate = (double)frs_sec / (double)frs_sec_base;
1000 
1001   avi = MEM_mallocN(sizeof(AviMovie), "avimovie");
1002 
1003   format = AVI_FORMAT_MJPEG;
1004 
1005   if (AVI_open_compress(filename, avi, 1, format) != AVI_ERROR_NONE) {
1006     MEM_freeN(avi);
1007     return NULL;
1008   }
1009 
1010   AVI_set_compress_option(avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_WIDTH, &x);
1011   AVI_set_compress_option(avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_HEIGHT, &y);
1012   AVI_set_compress_option(avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_QUALITY, &quality);
1013   AVI_set_compress_option(avi, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_FRAMERATE, &framerate);
1014 
1015   avi->interlace = 0;
1016   avi->odd_fields = 0;
1017 
1018   return avi;
1019 }
1020 
index_fallback_create_context(struct anim * anim,IMB_Timecode_Type UNUSED (tcs_in_use),IMB_Proxy_Size proxy_sizes_in_use,int quality)1021 static IndexBuildContext *index_fallback_create_context(struct anim *anim,
1022                                                         IMB_Timecode_Type UNUSED(tcs_in_use),
1023                                                         IMB_Proxy_Size proxy_sizes_in_use,
1024                                                         int quality)
1025 {
1026   FallbackIndexBuilderContext *context;
1027   int i;
1028 
1029   /* since timecode indices only work with ffmpeg right now,
1030    * don't know a sensible fallback here...
1031    *
1032    * so no proxies...
1033    */
1034   if (proxy_sizes_in_use == IMB_PROXY_NONE) {
1035     return NULL;
1036   }
1037 
1038   context = MEM_callocN(sizeof(FallbackIndexBuilderContext), "fallback index builder context");
1039 
1040   context->anim = anim;
1041   context->proxy_sizes_in_use = proxy_sizes_in_use;
1042 
1043   memset(context->proxy_ctx, 0, sizeof(context->proxy_ctx));
1044 
1045   for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
1046     if (context->proxy_sizes_in_use & proxy_sizes[i]) {
1047       char fname[FILE_MAX];
1048 
1049       get_proxy_filename(anim, proxy_sizes[i], fname, true);
1050       BLI_make_existing_file(fname);
1051 
1052       context->proxy_ctx[i] = alloc_proxy_output_avi(
1053           anim, fname, anim->x * proxy_fac[i], anim->y * proxy_fac[i], quality);
1054     }
1055   }
1056 
1057   return (IndexBuildContext *)context;
1058 }
1059 
index_rebuild_fallback_finish(FallbackIndexBuilderContext * context,int stop)1060 static void index_rebuild_fallback_finish(FallbackIndexBuilderContext *context, int stop)
1061 {
1062   struct anim *anim = context->anim;
1063   char fname[FILE_MAX];
1064   char fname_tmp[FILE_MAX];
1065   int i;
1066 
1067   for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
1068     if (context->proxy_sizes_in_use & proxy_sizes[i]) {
1069       AVI_close_compress(context->proxy_ctx[i]);
1070       MEM_freeN(context->proxy_ctx[i]);
1071 
1072       get_proxy_filename(anim, proxy_sizes[i], fname_tmp, true);
1073       get_proxy_filename(anim, proxy_sizes[i], fname, false);
1074 
1075       if (stop) {
1076         unlink(fname_tmp);
1077       }
1078       else {
1079         unlink(fname);
1080         rename(fname_tmp, fname);
1081       }
1082     }
1083   }
1084 }
1085 
index_rebuild_fallback(FallbackIndexBuilderContext * context,const short * stop,short * do_update,float * progress)1086 static void index_rebuild_fallback(FallbackIndexBuilderContext *context,
1087                                    const short *stop,
1088                                    short *do_update,
1089                                    float *progress)
1090 {
1091   int cnt = IMB_anim_get_duration(context->anim, IMB_TC_NONE);
1092   int i, pos;
1093   struct anim *anim = context->anim;
1094 
1095   for (pos = 0; pos < cnt; pos++) {
1096     struct ImBuf *ibuf = IMB_anim_absolute(anim, pos, IMB_TC_NONE, IMB_PROXY_NONE);
1097     struct ImBuf *tmp_ibuf = IMB_dupImBuf(ibuf);
1098     float next_progress = (float)pos / (float)cnt;
1099 
1100     if (*progress != next_progress) {
1101       *progress = next_progress;
1102       *do_update = true;
1103     }
1104 
1105     if (*stop) {
1106       break;
1107     }
1108 
1109     IMB_flipy(tmp_ibuf);
1110 
1111     for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
1112       if (context->proxy_sizes_in_use & proxy_sizes[i]) {
1113         int x = anim->x * proxy_fac[i];
1114         int y = anim->y * proxy_fac[i];
1115 
1116         struct ImBuf *s_ibuf = IMB_dupImBuf(tmp_ibuf);
1117 
1118         IMB_scalefastImBuf(s_ibuf, x, y);
1119 
1120         IMB_convert_rgba_to_abgr(s_ibuf);
1121 
1122         AVI_write_frame(context->proxy_ctx[i], pos, AVI_FORMAT_RGB32, s_ibuf->rect, x * y * 4);
1123 
1124         /* note that libavi free's the buffer... */
1125         s_ibuf->rect = NULL;
1126 
1127         IMB_freeImBuf(s_ibuf);
1128       }
1129     }
1130 
1131     IMB_freeImBuf(tmp_ibuf);
1132     IMB_freeImBuf(ibuf);
1133   }
1134 }
1135 
1136 #endif /* WITH_AVI */
1137 
1138 /* ----------------------------------------------------------------------
1139  * - public API
1140  * ---------------------------------------------------------------------- */
1141 
IMB_anim_index_rebuild_context(struct anim * anim,IMB_Timecode_Type tcs_in_use,IMB_Proxy_Size proxy_sizes_in_use,int quality,const bool overwrite,GSet * file_list)1142 IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim,
1143                                                   IMB_Timecode_Type tcs_in_use,
1144                                                   IMB_Proxy_Size proxy_sizes_in_use,
1145                                                   int quality,
1146                                                   const bool overwrite,
1147                                                   GSet *file_list)
1148 {
1149   IndexBuildContext *context = NULL;
1150   IMB_Proxy_Size proxy_sizes_to_build = proxy_sizes_in_use;
1151   int i;
1152 
1153   /* Don't generate the same file twice! */
1154   if (file_list) {
1155     for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
1156       IMB_Proxy_Size proxy_size = proxy_sizes[i];
1157       if (proxy_size & proxy_sizes_to_build) {
1158         char filename[FILE_MAX];
1159         if (get_proxy_filename(anim, proxy_size, filename, false) == false) {
1160           return NULL;
1161         }
1162         void **filename_key_p;
1163         if (!BLI_gset_ensure_p_ex(file_list, filename, &filename_key_p)) {
1164           *filename_key_p = BLI_strdup(filename);
1165         }
1166         else {
1167           proxy_sizes_to_build &= ~proxy_size;
1168           printf("Proxy: %s already registered for generation, skipping\n", filename);
1169         }
1170       }
1171     }
1172   }
1173 
1174   if (!overwrite) {
1175     IMB_Proxy_Size built_proxies = IMB_anim_proxy_get_existing(anim);
1176     if (built_proxies != 0) {
1177 
1178       for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
1179         IMB_Proxy_Size proxy_size = proxy_sizes[i];
1180         if (proxy_size & built_proxies) {
1181           char filename[FILE_MAX];
1182           if (get_proxy_filename(anim, proxy_size, filename, false) == false) {
1183             return NULL;
1184           }
1185           printf("Skipping proxy: %s\n", filename);
1186         }
1187       }
1188     }
1189     proxy_sizes_to_build &= ~built_proxies;
1190   }
1191 
1192   fflush(stdout);
1193 
1194   if (proxy_sizes_to_build == 0) {
1195     return NULL;
1196   }
1197 
1198   switch (anim->curtype) {
1199 #ifdef WITH_FFMPEG
1200     case ANIM_FFMPEG:
1201       context = index_ffmpeg_create_context(anim, tcs_in_use, proxy_sizes_to_build, quality);
1202       break;
1203 #endif
1204 #ifdef WITH_AVI
1205     default:
1206       context = index_fallback_create_context(anim, tcs_in_use, proxy_sizes_to_build, quality);
1207       break;
1208 #endif
1209   }
1210 
1211   if (context) {
1212     context->anim_type = anim->curtype;
1213   }
1214 
1215   return context;
1216 
1217   UNUSED_VARS(tcs_in_use, proxy_sizes_in_use, quality);
1218 }
1219 
IMB_anim_index_rebuild(struct IndexBuildContext * context,short * stop,short * do_update,float * progress)1220 void IMB_anim_index_rebuild(struct IndexBuildContext *context,
1221                             short *stop,
1222                             short *do_update,
1223                             float *progress)
1224 {
1225   switch (context->anim_type) {
1226 #ifdef WITH_FFMPEG
1227     case ANIM_FFMPEG:
1228       index_rebuild_ffmpeg((FFmpegIndexBuilderContext *)context, stop, do_update, progress);
1229       break;
1230 #endif
1231 #ifdef WITH_AVI
1232     default:
1233       index_rebuild_fallback((FallbackIndexBuilderContext *)context, stop, do_update, progress);
1234       break;
1235 #endif
1236   }
1237 
1238   UNUSED_VARS(stop, do_update, progress);
1239 }
1240 
IMB_anim_index_rebuild_finish(IndexBuildContext * context,short stop)1241 void IMB_anim_index_rebuild_finish(IndexBuildContext *context, short stop)
1242 {
1243   switch (context->anim_type) {
1244 #ifdef WITH_FFMPEG
1245     case ANIM_FFMPEG:
1246       index_rebuild_ffmpeg_finish((FFmpegIndexBuilderContext *)context, stop);
1247       break;
1248 #endif
1249 #ifdef WITH_AVI
1250     default:
1251       index_rebuild_fallback_finish((FallbackIndexBuilderContext *)context, stop);
1252       break;
1253 #endif
1254   }
1255 
1256   /* static defined at top of the file */
1257   UNUSED_VARS(stop, proxy_sizes);
1258 }
1259 
IMB_free_indices(struct anim * anim)1260 void IMB_free_indices(struct anim *anim)
1261 {
1262   int i;
1263 
1264   for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
1265     if (anim->proxy_anim[i]) {
1266       IMB_close_anim(anim->proxy_anim[i]);
1267       anim->proxy_anim[i] = NULL;
1268     }
1269   }
1270 
1271   for (i = 0; i < IMB_TC_MAX_SLOT; i++) {
1272     if (anim->curr_idx[i]) {
1273       IMB_indexer_close(anim->curr_idx[i]);
1274       anim->curr_idx[i] = NULL;
1275     }
1276   }
1277 
1278   anim->proxies_tried = 0;
1279   anim->indices_tried = 0;
1280 }
1281 
IMB_anim_set_index_dir(struct anim * anim,const char * dir)1282 void IMB_anim_set_index_dir(struct anim *anim, const char *dir)
1283 {
1284   if (STREQ(anim->index_dir, dir)) {
1285     return;
1286   }
1287   BLI_strncpy(anim->index_dir, dir, sizeof(anim->index_dir));
1288 
1289   IMB_free_indices(anim);
1290 }
1291 
IMB_anim_open_proxy(struct anim * anim,IMB_Proxy_Size preview_size)1292 struct anim *IMB_anim_open_proxy(struct anim *anim, IMB_Proxy_Size preview_size)
1293 {
1294   char fname[FILE_MAX];
1295   int i = IMB_proxy_size_to_array_index(preview_size);
1296 
1297   if (anim->proxy_anim[i]) {
1298     return anim->proxy_anim[i];
1299   }
1300 
1301   if (anim->proxies_tried & preview_size) {
1302     return NULL;
1303   }
1304 
1305   get_proxy_filename(anim, preview_size, fname, false);
1306 
1307   /* proxies are generated in the same color space as animation itself */
1308   anim->proxy_anim[i] = IMB_open_anim(fname, 0, 0, anim->colorspace);
1309 
1310   anim->proxies_tried |= preview_size;
1311 
1312   return anim->proxy_anim[i];
1313 }
1314 
IMB_anim_open_index(struct anim * anim,IMB_Timecode_Type tc)1315 struct anim_index *IMB_anim_open_index(struct anim *anim, IMB_Timecode_Type tc)
1316 {
1317   char fname[FILE_MAX];
1318   int i = IMB_timecode_to_array_index(tc);
1319 
1320   if (anim->curr_idx[i]) {
1321     return anim->curr_idx[i];
1322   }
1323 
1324   if (anim->indices_tried & tc) {
1325     return NULL;
1326   }
1327 
1328   get_tc_filename(anim, tc, fname);
1329 
1330   anim->curr_idx[i] = IMB_indexer_open(fname);
1331 
1332   anim->indices_tried |= tc;
1333 
1334   return anim->curr_idx[i];
1335 }
1336 
IMB_anim_index_get_frame_index(struct anim * anim,IMB_Timecode_Type tc,int position)1337 int IMB_anim_index_get_frame_index(struct anim *anim, IMB_Timecode_Type tc, int position)
1338 {
1339   struct anim_index *idx = IMB_anim_open_index(anim, tc);
1340 
1341   if (!idx) {
1342     return position;
1343   }
1344 
1345   return IMB_indexer_get_frame_index(idx, position);
1346 }
1347 
IMB_anim_proxy_get_existing(struct anim * anim)1348 IMB_Proxy_Size IMB_anim_proxy_get_existing(struct anim *anim)
1349 {
1350   const int num_proxy_sizes = IMB_PROXY_MAX_SLOT;
1351   IMB_Proxy_Size existing = 0;
1352   int i;
1353   for (i = 0; i < num_proxy_sizes; i++) {
1354     IMB_Proxy_Size proxy_size = proxy_sizes[i];
1355     char filename[FILE_MAX];
1356     get_proxy_filename(anim, proxy_size, filename, false);
1357     if (BLI_exists(filename)) {
1358       existing |= proxy_size;
1359     }
1360   }
1361   return existing;
1362 }
1363