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