Blender V4.3
indexer.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011 Peter Schlaile <peter [at] schlaile [dot] de>.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cstdlib>
10
11#include "MEM_guardedalloc.h"
12
13#include "BLI_array.hh"
14#include "BLI_endian_defines.h"
15#include "BLI_endian_switch.h"
16#include "BLI_fileops.h"
17#include "BLI_ghash.h"
18#include "BLI_math_base.h"
19#include "BLI_path_utils.hh"
20#include "BLI_string.h"
21#include "BLI_string_utils.hh"
22#include "BLI_threads.h"
23#include "BLI_time.h"
24#include "BLI_utildefines.h"
25
26#ifdef _WIN32
27# include "BLI_winstuff.h"
28#endif
29
30#include "BKE_writeffmpeg.hh"
31
32#include "IMB_anim.hh"
33#include "IMB_imbuf.hh"
34#include "IMB_indexer.hh"
35#include "imbuf.hh"
36
37#ifdef WITH_FFMPEG
38extern "C" {
39# include "ffmpeg_compat.h"
40# include <libavutil/imgutils.h>
41}
42#endif
43
44static const char binary_header_str[] = "BlenMIdx";
45static const char temp_ext[] = "_part";
46
49static const float proxy_fac[] = {0.25, 0.50, 0.75, 1.00};
50
51#define INDEX_FILE_VERSION 2
52
53/* ----------------------------------------------------------------------
54 * - time code index functions
55 * ---------------------------------------------------------------------- */
56
58{
59
60 anim_index_builder *rv = MEM_cnew<anim_index_builder>("index builder");
61
62 fprintf(stderr, "Starting work on index: %s\n", filepath);
63
64 STRNCPY(rv->filepath, filepath);
65
66 STRNCPY(rv->filepath_temp, filepath);
67 BLI_string_join(rv->filepath_temp, sizeof(rv->filepath_temp), filepath, temp_ext);
68
70
71 rv->fp = BLI_fopen(rv->filepath_temp, "wb");
72
73 if (!rv->fp) {
74 fprintf(stderr,
75 "Couldn't open index target: %s! "
76 "Index build broken!\n",
77 rv->filepath_temp);
78 MEM_freeN(rv);
79 return nullptr;
80 }
81
82 fprintf(rv->fp,
83 "%s%c%.3d",
85 (ENDIAN_ORDER == B_ENDIAN) ? 'V' : 'v',
87
88 return rv;
89}
90
92 int frameno,
93 uint64_t seek_pos,
94 uint64_t seek_pos_pts,
95 uint64_t seek_pos_dts,
96 uint64_t pts)
97{
98 fwrite(&frameno, sizeof(int), 1, fp->fp);
99 fwrite(&seek_pos, sizeof(uint64_t), 1, fp->fp);
100 fwrite(&seek_pos_pts, sizeof(uint64_t), 1, fp->fp);
101 fwrite(&seek_pos_dts, sizeof(uint64_t), 1, fp->fp);
102 fwrite(&pts, sizeof(uint64_t), 1, fp->fp);
103}
104
106 uchar *buffer,
107 int data_size,
108 int frameno,
109 uint64_t seek_pos,
110 uint64_t seek_pos_pts,
111 uint64_t seek_pos_dts,
112 uint64_t pts)
113{
114 if (fp->proc_frame) {
116 e.frameno = frameno;
117 e.seek_pos = seek_pos;
118 e.seek_pos_pts = seek_pos_pts;
119 e.seek_pos_dts = seek_pos_dts;
120 e.pts = pts;
121
122 fp->proc_frame(fp, buffer, data_size, &e);
123 }
124 else {
125 IMB_index_builder_add_entry(fp, frameno, seek_pos, seek_pos_pts, seek_pos_dts, pts);
126 }
127}
128
130{
131 if (fp->delete_priv_data) {
132 fp->delete_priv_data(fp);
133 }
134
135 fclose(fp->fp);
136
137 if (rollback) {
138 unlink(fp->filepath_temp);
139 }
140 else {
141 unlink(fp->filepath);
143 }
144
145 MEM_freeN(fp);
146}
147
148ImBufAnimIndex *IMB_indexer_open(const char *filepath)
149{
150 char header[13];
151 ImBufAnimIndex *idx;
152 FILE *fp = BLI_fopen(filepath, "rb");
153 int i;
154
155 if (!fp) {
156 return nullptr;
157 }
158
159 if (fread(header, 12, 1, fp) != 1) {
160 fprintf(stderr, "Couldn't read indexer file: %s\n", filepath);
161 fclose(fp);
162 return nullptr;
163 }
164
165 header[12] = 0;
166
167 if (memcmp(header, binary_header_str, 8) != 0) {
168 fprintf(stderr, "Error reading %s: Binary file type string mismatch\n", filepath);
169 fclose(fp);
170 return nullptr;
171 }
172
173 if (atoi(header + 9) != INDEX_FILE_VERSION) {
174 fprintf(stderr, "Error reading %s: File version mismatch\n", filepath);
175 fclose(fp);
176 return nullptr;
177 }
178
179 idx = MEM_cnew<ImBufAnimIndex>("ImBufAnimIndex");
180
181 STRNCPY(idx->filepath, filepath);
182
183 fseek(fp, 0, SEEK_END);
184
185 idx->num_entries = (ftell(fp) - 12) / (sizeof(int) + /* framepos */
186 sizeof(uint64_t) + /* seek_pos */
187 sizeof(uint64_t) + /* seek_pos_pts */
188 sizeof(uint64_t) + /* seek_pos_dts */
189 sizeof(uint64_t) /* pts */
190 );
191
192 fseek(fp, 12, SEEK_SET);
193
194 idx->entries = static_cast<anim_index_entry *>(
195 MEM_callocN(sizeof(anim_index_entry) * idx->num_entries, "anim_index_entries"));
196
197 size_t items_read = 0;
198 for (i = 0; i < idx->num_entries; i++) {
199 items_read += fread(&idx->entries[i].frameno, sizeof(int), 1, fp);
200 items_read += fread(&idx->entries[i].seek_pos, sizeof(uint64_t), 1, fp);
201 items_read += fread(&idx->entries[i].seek_pos_pts, sizeof(uint64_t), 1, fp);
202 items_read += fread(&idx->entries[i].seek_pos_dts, sizeof(uint64_t), 1, fp);
203 items_read += fread(&idx->entries[i].pts, sizeof(uint64_t), 1, fp);
204 }
205
206 if (UNLIKELY(items_read != idx->num_entries * 5)) {
207 fprintf(stderr, "Error: Element data size mismatch in: %s\n", filepath);
208 MEM_freeN(idx->entries);
209 MEM_freeN(idx);
210 fclose(fp);
211 return nullptr;
212 }
213
214 if ((ENDIAN_ORDER == B_ENDIAN) != (header[8] == 'V')) {
215 for (i = 0; i < idx->num_entries; i++) {
221 }
222 }
223
224 fclose(fp);
225
226 return idx;
227}
228
230{
231 /* This is hard coded, because our current timecode files return non zero seek position for index
232 * 0. Only when seeking to 0 it is guaranteed, that first packet will be read. */
233 if (frame_index <= 0) {
234 return 0;
235 }
236 if (frame_index >= idx->num_entries) {
237 frame_index = idx->num_entries - 1;
238 }
239 return idx->entries[frame_index].seek_pos;
240}
241
243{
244 if (frame_index < 0) {
245 frame_index = 0;
246 }
247 if (frame_index >= idx->num_entries) {
248 frame_index = idx->num_entries - 1;
249 }
250 return idx->entries[frame_index].seek_pos_pts;
251}
252
254{
255 if (frame_index < 0) {
256 frame_index = 0;
257 }
258 if (frame_index >= idx->num_entries) {
259 frame_index = idx->num_entries - 1;
260 }
261 return idx->entries[frame_index].seek_pos_dts;
262}
263
265{
266 int len = idx->num_entries;
267 int half;
268 int middle;
269 int first = 0;
270
271 /* Binary-search (lower bound) the right index. */
272
273 while (len > 0) {
274 half = len >> 1;
275 middle = first;
276
277 middle += half;
278
279 if (idx->entries[middle].frameno < frameno) {
280 first = middle;
281 first++;
282 len = len - half - 1;
283 }
284 else {
285 len = half;
286 }
287 }
288
289 if (first == idx->num_entries) {
290 return idx->num_entries - 1;
291 }
292
293 return first;
294}
295
297{
298 if (frame_index < 0) {
299 frame_index = 0;
300 }
301 if (frame_index >= idx->num_entries) {
302 frame_index = idx->num_entries - 1;
303 }
304 return idx->entries[frame_index].pts;
305}
306
308{
309 if (idx->num_entries == 0) {
310 return 0;
311 }
312 return idx->entries[idx->num_entries - 1].frameno + 1;
313}
314
315int IMB_indexer_can_scan(ImBufAnimIndex *idx, int old_frame_index, int new_frame_index)
316{
317 /* makes only sense, if it is the same I-Frame and we are not
318 * trying to run backwards in time... */
319 return (IMB_indexer_get_seek_pos(idx, old_frame_index) ==
320 IMB_indexer_get_seek_pos(idx, new_frame_index) &&
321 old_frame_index < new_frame_index);
322}
323
325{
326 MEM_freeN(idx->entries);
327 MEM_freeN(idx);
328}
329
331{
332 switch (pr_size) {
333 case IMB_PROXY_NONE:
334 return -1;
335 case IMB_PROXY_25:
336 return 0;
337 case IMB_PROXY_50:
338 return 1;
339 case IMB_PROXY_75:
340 return 2;
341 case IMB_PROXY_100:
342 return 3;
343 default:
344 BLI_assert_msg(0, "Unhandled proxy size enum!");
345 return -1;
346 }
347}
348
350{
351 switch (tc) {
352 case IMB_TC_NONE:
353 return -1;
355 return 0;
357 return 1;
358 default:
359 BLI_assert_msg(0, "Unhandled timecode type enum!");
360 return -1;
361 }
362}
363
364/* ----------------------------------------------------------------------
365 * - rebuild helper functions
366 * ---------------------------------------------------------------------- */
367
368static void get_index_dir(ImBufAnim *anim, char *index_dir, size_t index_dir_maxncpy)
369{
370 if (!anim->index_dir[0]) {
371 char filename[FILE_MAXFILE];
372 char dirname[FILE_MAXDIR];
373 BLI_path_split_dir_file(anim->filepath, dirname, sizeof(dirname), filename, sizeof(filename));
374 BLI_path_join(index_dir, index_dir_maxncpy, dirname, "BL_proxy", filename);
375 }
376 else {
377 BLI_strncpy(index_dir, anim->index_dir, index_dir_maxncpy);
378 }
379}
380
381void IMB_anim_get_filename(ImBufAnim *anim, char *filename, int filename_maxncpy)
382{
383 BLI_path_split_file_part(anim->filepath, filename, filename_maxncpy);
384}
385
387 IMB_Proxy_Size preview_size,
388 char *filepath,
389 bool temp)
390{
391 char index_dir[FILE_MAXDIR];
392 int i = IMB_proxy_size_to_array_index(preview_size);
393
394 BLI_assert(i >= 0);
395
396 char proxy_name[256];
397 char stream_suffix[20];
398 const char *name = (temp) ? "proxy_%d%s_part.avi" : "proxy_%d%s.avi";
399
400 stream_suffix[0] = 0;
401
402 if (anim->streamindex > 0) {
403 SNPRINTF(stream_suffix, "_st%d", anim->streamindex);
404 }
405
406 SNPRINTF(proxy_name, name, int(proxy_fac[i] * 100), stream_suffix, anim->suffix);
407
408 get_index_dir(anim, index_dir, sizeof(index_dir));
409
410 if (BLI_path_ncmp(anim->filepath, index_dir, FILE_MAXDIR) == 0) {
411 return false;
412 }
413
414 BLI_path_join(filepath, FILE_MAXFILE + FILE_MAXDIR, index_dir, proxy_name);
415 return true;
416}
417
418static void get_tc_filepath(ImBufAnim *anim, IMB_Timecode_Type tc, char *filepath)
419{
420 char index_dir[FILE_MAXDIR];
421 int i = IMB_timecode_to_array_index(tc);
422
423 BLI_assert(i >= 0);
424
425 const char *index_names[] = {
426 "record_run%s%s.blen_tc",
427 "record_run_no_gaps%s%s.blen_tc",
428 };
429
430 char stream_suffix[20];
431 char index_name[256];
432
433 stream_suffix[0] = 0;
434
435 if (anim->streamindex > 0) {
436 SNPRINTF(stream_suffix, "_st%d", anim->streamindex);
437 }
438
439 SNPRINTF(index_name, index_names[i], stream_suffix, anim->suffix);
440
441 get_index_dir(anim, index_dir, sizeof(index_dir));
442
443 BLI_path_join(filepath, FILE_MAXFILE + FILE_MAXDIR, index_dir, index_name);
444}
445
446/* ----------------------------------------------------------------------
447 * - common rebuilder structures
448 * ---------------------------------------------------------------------- */
449
451
452/* ----------------------------------------------------------------------
453 * - ffmpeg rebuilder
454 * ---------------------------------------------------------------------- */
455
456#ifdef WITH_FFMPEG
457
458struct proxy_output_ctx {
459 AVFormatContext *of;
460 AVStream *st;
461 AVCodecContext *c;
462 const AVCodec *codec;
463 SwsContext *sws_ctx;
464 AVFrame *frame;
465 int cfra;
466 IMB_Proxy_Size proxy_size;
467 int orig_height;
468 ImBufAnim *anim;
469};
470
471static proxy_output_ctx *alloc_proxy_output_ffmpeg(
472 ImBufAnim *anim, AVStream *st, IMB_Proxy_Size proxy_size, int width, int height, int quality)
473{
474 proxy_output_ctx *rv = MEM_cnew<proxy_output_ctx>("alloc_proxy_output");
475
476 char filepath[FILE_MAX];
477
478 rv->proxy_size = proxy_size;
479 rv->anim = anim;
480
481 get_proxy_filepath(rv->anim, rv->proxy_size, filepath, true);
482 if (!BLI_file_ensure_parent_dir_exists(filepath)) {
483 MEM_freeN(rv);
484 return nullptr;
485 }
486
487 rv->of = avformat_alloc_context();
488 rv->of->oformat = av_guess_format("avi", nullptr, nullptr);
489
490 rv->of->url = av_strdup(filepath);
491
492 fprintf(stderr, "Starting work on proxy: %s\n", rv->of->url);
493
494 rv->st = avformat_new_stream(rv->of, nullptr);
495 rv->st->id = 0;
496
497 rv->codec = avcodec_find_encoder(AV_CODEC_ID_H264);
498
499 rv->c = avcodec_alloc_context3(rv->codec);
500
501 if (!rv->codec) {
502 fprintf(stderr,
503 "No ffmpeg encoder available? "
504 "Proxy not built!\n");
505 avcodec_free_context(&rv->c);
506 avformat_free_context(rv->of);
507 MEM_freeN(rv);
508 return nullptr;
509 }
510
511 rv->c->width = width;
512 rv->c->height = height;
513 rv->c->gop_size = 10;
514 rv->c->max_b_frames = 0;
515
516 if (rv->codec->pix_fmts) {
517 rv->c->pix_fmt = rv->codec->pix_fmts[0];
518 }
519 else {
520 rv->c->pix_fmt = AV_PIX_FMT_YUVJ420P;
521 }
522
523 rv->c->sample_aspect_ratio = rv->st->sample_aspect_ratio = st->sample_aspect_ratio;
524
525 rv->c->time_base.den = 25;
526 rv->c->time_base.num = 1;
527 rv->st->time_base = rv->c->time_base;
528
529 /* This range matches #eFFMpegCrf. `crf_range_min` corresponds to lowest quality,
530 * `crf_range_max` to highest quality. */
531 const int crf_range_min = 32;
532 const int crf_range_max = 17;
533 int crf = round_fl_to_int((quality / 100.0f) * (crf_range_max - crf_range_min) + crf_range_min);
534
535 AVDictionary *codec_opts = nullptr;
536 /* High quality preset value. */
537 av_dict_set_int(&codec_opts, "crf", crf, 0);
538 /* Prefer smaller file-size. Presets from `veryslow` to `veryfast` produce output with very
539 * similar file-size, but there is big difference in performance.
540 * In some cases `veryfast` preset will produce smallest file-size. */
541 av_dict_set(&codec_opts, "preset", "veryfast", 0);
542 av_dict_set(&codec_opts, "tune", "fastdecode", 0);
543
544 if (rv->codec->capabilities & AV_CODEC_CAP_OTHER_THREADS) {
545 rv->c->thread_count = 0;
546 }
547 else {
548 rv->c->thread_count = BLI_system_thread_count();
549 }
550
551 if (rv->codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
552 rv->c->thread_type = FF_THREAD_FRAME;
553 }
554 else if (rv->codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
555 rv->c->thread_type = FF_THREAD_SLICE;
556 }
557
558 if (rv->of->flags & AVFMT_GLOBALHEADER) {
559 rv->c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
560 }
561
562 avcodec_parameters_from_context(rv->st->codecpar, rv->c);
563
564 int ret = avio_open(&rv->of->pb, filepath, AVIO_FLAG_WRITE);
565
566 if (ret < 0) {
567 char error_str[AV_ERROR_MAX_STRING_SIZE];
568 av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret);
569
570 fprintf(stderr,
571 "Couldn't open IO: %s\n"
572 "Proxy not built!\n",
573 error_str);
574 avcodec_free_context(&rv->c);
575 avformat_free_context(rv->of);
576 MEM_freeN(rv);
577 return nullptr;
578 }
579
580 ret = avcodec_open2(rv->c, rv->codec, &codec_opts);
581 if (ret < 0) {
582 char error_str[AV_ERROR_MAX_STRING_SIZE];
583 av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret);
584
585 fprintf(stderr,
586 "Couldn't open codec: %s\n"
587 "Proxy not built!\n",
588 error_str);
589 avcodec_free_context(&rv->c);
590 avformat_free_context(rv->of);
591 MEM_freeN(rv);
592 return nullptr;
593 }
594
595 rv->orig_height = st->codecpar->height;
596
597 if (st->codecpar->width != width || st->codecpar->height != height ||
598 st->codecpar->format != rv->c->pix_fmt)
599 {
600 const size_t align = ffmpeg_get_buffer_alignment();
601 rv->frame = av_frame_alloc();
602 rv->frame->format = rv->c->pix_fmt;
603 rv->frame->width = width;
604 rv->frame->height = height;
605 av_frame_get_buffer(rv->frame, align);
606
607 rv->sws_ctx = BKE_ffmpeg_sws_get_context(st->codecpar->width,
608 rv->orig_height,
609 AVPixelFormat(st->codecpar->format),
610 width,
611 height,
612 rv->c->pix_fmt,
613 SWS_FAST_BILINEAR);
614 }
615
616 ret = avformat_write_header(rv->of, nullptr);
617 if (ret < 0) {
618 char error_str[AV_ERROR_MAX_STRING_SIZE];
619 av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret);
620
621 fprintf(stderr,
622 "Couldn't write header: %s\n"
623 "Proxy not built!\n",
624 error_str);
625
626 if (rv->frame) {
627 av_frame_free(&rv->frame);
628 }
629
630 avcodec_free_context(&rv->c);
631 avformat_free_context(rv->of);
632 MEM_freeN(rv);
633 return nullptr;
634 }
635
636 return rv;
637}
638
639static void add_to_proxy_output_ffmpeg(proxy_output_ctx *ctx, AVFrame *frame)
640{
641 if (!ctx) {
642 return;
643 }
644
645 if (ctx->sws_ctx && frame &&
646 (frame->data[0] || frame->data[1] || frame->data[2] || frame->data[3]))
647 {
648 BKE_ffmpeg_sws_scale_frame(ctx->sws_ctx, ctx->frame, frame);
649 }
650
651 frame = ctx->sws_ctx ? (frame ? ctx->frame : nullptr) : frame;
652
653 if (frame) {
654 frame->pts = ctx->cfra++;
655 }
656
657 int ret = avcodec_send_frame(ctx->c, frame);
658 if (ret < 0) {
659 /* Can't send frame to encoder. This shouldn't happen. */
660 char error_str[AV_ERROR_MAX_STRING_SIZE];
661 av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret);
662
663 fprintf(stderr, "Can't send video frame: %s\n", error_str);
664 return;
665 }
666 AVPacket *packet = av_packet_alloc();
667
668 while (ret >= 0) {
669 ret = avcodec_receive_packet(ctx->c, packet);
670
671 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
672 /* No more packets to flush. */
673 break;
674 }
675 if (ret < 0) {
676 char error_str[AV_ERROR_MAX_STRING_SIZE];
677 av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret);
678
679 fprintf(stderr,
680 "Error encoding proxy frame %d for '%s': %s\n",
681 ctx->cfra - 1,
682 ctx->of->url,
683 error_str);
684 break;
685 }
686
687 packet->stream_index = ctx->st->index;
688 av_packet_rescale_ts(packet, ctx->c->time_base, ctx->st->time_base);
689# ifdef FFMPEG_USE_DURATION_WORKAROUND
690 my_guess_pkt_duration(ctx->of, ctx->st, packet);
691# endif
692
693 int write_ret = av_interleaved_write_frame(ctx->of, packet);
694 if (write_ret != 0) {
695 char error_str[AV_ERROR_MAX_STRING_SIZE];
696 av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, write_ret);
697
698 fprintf(stderr,
699 "Error writing proxy frame %d "
700 "into '%s': %s\n",
701 ctx->cfra - 1,
702 ctx->of->url,
703 error_str);
704 break;
705 }
706 }
707
708 av_packet_free(&packet);
709}
710
711static void free_proxy_output_ffmpeg(proxy_output_ctx *ctx, int rollback)
712{
713 char filepath[FILE_MAX];
714 char filepath_tmp[FILE_MAX];
715
716 if (!ctx) {
717 return;
718 }
719
720 if (!rollback) {
721 /* Flush the remaining packets. */
722 add_to_proxy_output_ffmpeg(ctx, nullptr);
723 }
724
725 avcodec_flush_buffers(ctx->c);
726
727 av_write_trailer(ctx->of);
728
729 avcodec_free_context(&ctx->c);
730
731 if (ctx->of->oformat) {
732 if (!(ctx->of->oformat->flags & AVFMT_NOFILE)) {
733 avio_close(ctx->of->pb);
734 }
735 }
736 avformat_free_context(ctx->of);
737
738 if (ctx->sws_ctx) {
739 BKE_ffmpeg_sws_release_context(ctx->sws_ctx);
740 ctx->sws_ctx = nullptr;
741 }
742 if (ctx->frame) {
743 av_frame_free(&ctx->frame);
744 }
745
746 get_proxy_filepath(ctx->anim, ctx->proxy_size, filepath_tmp, true);
747
748 if (rollback) {
749 unlink(filepath_tmp);
750 }
751 else {
752 get_proxy_filepath(ctx->anim, ctx->proxy_size, filepath, false);
753 unlink(filepath);
754 BLI_rename(filepath_tmp, filepath);
755 }
756
757 MEM_freeN(ctx);
758}
759
761
762struct FFmpegIndexBuilderContext : public IndexBuildContext {
763
764 AVFormatContext *iFormatCtx;
765 AVCodecContext *iCodecCtx;
766 const AVCodec *iCodec;
767 AVStream *iStream;
768 int videoStream;
769
770 int num_proxy_sizes;
771
772 proxy_output_ctx *proxy_ctx[IMB_PROXY_MAX_SLOT];
774
775 int tcs_in_use;
776 int proxy_sizes_in_use;
777
778 uint64_t seek_pos;
779 uint64_t seek_pos_pts;
780 uint64_t seek_pos_dts;
781 uint64_t last_seek_pos;
782 uint64_t last_seek_pos_pts;
783 uint64_t last_seek_pos_dts;
784 uint64_t start_pts;
785 double frame_rate;
786 double pts_time_base;
787 int frameno, frameno_gapless;
788 int start_pts_set;
789
790 bool build_only_on_bad_performance;
791 bool building_cancelled;
792};
793
794static IndexBuildContext *index_ffmpeg_create_context(ImBufAnim *anim,
795 int tcs_in_use,
796 int proxy_sizes_in_use,
797 int quality,
798 bool build_only_on_bad_performance)
799{
800 FFmpegIndexBuilderContext *context = MEM_cnew<FFmpegIndexBuilderContext>(
801 "FFmpeg index builder context");
802 int num_proxy_sizes = IMB_PROXY_MAX_SLOT;
803 int i, streamcount;
804
805 context->tcs_in_use = tcs_in_use;
806 context->proxy_sizes_in_use = proxy_sizes_in_use;
807 context->num_proxy_sizes = IMB_PROXY_MAX_SLOT;
808 context->build_only_on_bad_performance = build_only_on_bad_performance;
809
810 memset(context->proxy_ctx, 0, sizeof(context->proxy_ctx));
811 memset(context->indexer, 0, sizeof(context->indexer));
812
813 if (avformat_open_input(&context->iFormatCtx, anim->filepath, nullptr, nullptr) != 0) {
814 MEM_freeN(context);
815 return nullptr;
816 }
817
818 if (avformat_find_stream_info(context->iFormatCtx, nullptr) < 0) {
819 avformat_close_input(&context->iFormatCtx);
820 MEM_freeN(context);
821 return nullptr;
822 }
823
824 streamcount = anim->streamindex;
825
826 /* Find the video stream */
827 context->videoStream = -1;
828 for (i = 0; i < context->iFormatCtx->nb_streams; i++) {
829 if (context->iFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
830 if (streamcount > 0) {
831 streamcount--;
832 continue;
833 }
834 context->videoStream = i;
835 break;
836 }
837 }
838
839 if (context->videoStream == -1) {
840 avformat_close_input(&context->iFormatCtx);
841 MEM_freeN(context);
842 return nullptr;
843 }
844
845 context->iStream = context->iFormatCtx->streams[context->videoStream];
846
847 context->iCodec = avcodec_find_decoder(context->iStream->codecpar->codec_id);
848
849 if (context->iCodec == nullptr) {
850 avformat_close_input(&context->iFormatCtx);
851 MEM_freeN(context);
852 return nullptr;
853 }
854
855 context->iCodecCtx = avcodec_alloc_context3(nullptr);
856 avcodec_parameters_to_context(context->iCodecCtx, context->iStream->codecpar);
857 context->iCodecCtx->workaround_bugs = FF_BUG_AUTODETECT;
858
859 if (context->iCodec->capabilities & AV_CODEC_CAP_OTHER_THREADS) {
860 context->iCodecCtx->thread_count = 0;
861 }
862 else {
863 context->iCodecCtx->thread_count = BLI_system_thread_count();
864 }
865
866 if (context->iCodec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
867 context->iCodecCtx->thread_type = FF_THREAD_FRAME;
868 }
869 else if (context->iCodec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
870 context->iCodecCtx->thread_type = FF_THREAD_SLICE;
871 }
872
873 if (avcodec_open2(context->iCodecCtx, context->iCodec, nullptr) < 0) {
874 avformat_close_input(&context->iFormatCtx);
875 avcodec_free_context(&context->iCodecCtx);
876 MEM_freeN(context);
877 return nullptr;
878 }
879
880 for (i = 0; i < num_proxy_sizes; i++) {
881 if (proxy_sizes_in_use & proxy_sizes[i]) {
882 int width = context->iCodecCtx->width * proxy_fac[i];
883 int height = context->iCodecCtx->height * proxy_fac[i];
884 width += width % 2;
885 height += height % 2;
886 context->proxy_ctx[i] = alloc_proxy_output_ffmpeg(
887 anim, context->iStream, proxy_sizes[i], width, height, quality);
888 if (!context->proxy_ctx[i]) {
889 proxy_sizes_in_use &= ~int(proxy_sizes[i]);
890 }
891 }
892 }
893
894 if (context->proxy_ctx[0] == nullptr && context->proxy_ctx[1] == nullptr &&
895 context->proxy_ctx[2] == nullptr && context->proxy_ctx[3] == nullptr)
896 {
897 avformat_close_input(&context->iFormatCtx);
898 avcodec_free_context(&context->iCodecCtx);
899 MEM_freeN(context);
900 return nullptr; /* Nothing to transcode. */
901 }
902
903 for (i = 0; i < tc_types.size(); i++) {
904 if (tcs_in_use & tc_types[i]) {
905 char filepath[FILE_MAX];
906
907 get_tc_filepath(anim, tc_types[i], filepath);
908
909 context->indexer[i] = IMB_index_builder_create(filepath);
910 if (!context->indexer[i]) {
911 tcs_in_use &= ~int(tc_types[i]);
912 }
913 }
914 }
915
916 return (IndexBuildContext *)context;
917}
918
919static void index_rebuild_ffmpeg_finish(FFmpegIndexBuilderContext *context, const bool stop)
920{
921 int i;
922
923 const bool do_rollback = stop || context->building_cancelled;
924
925 for (i = 0; i < tc_types.size(); i++) {
926 if (context->tcs_in_use & tc_types[i]) {
927 IMB_index_builder_finish(context->indexer[i], do_rollback);
928 }
929 }
930
931 for (i = 0; i < context->num_proxy_sizes; i++) {
932 if (context->proxy_sizes_in_use & proxy_sizes[i]) {
933 free_proxy_output_ffmpeg(context->proxy_ctx[i], do_rollback);
934 }
935 }
936
937 avcodec_free_context(&context->iCodecCtx);
938 avformat_close_input(&context->iFormatCtx);
939
940 MEM_freeN(context);
941}
942
943static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *context,
944 AVPacket *curr_packet,
945 AVFrame *in_frame)
946{
947 int i;
948 uint64_t s_pos = context->seek_pos;
949 uint64_t s_pts = context->seek_pos_pts;
950 uint64_t s_dts = context->seek_pos_dts;
951 uint64_t pts = av_get_pts_from_frame(in_frame);
952
953 for (i = 0; i < context->num_proxy_sizes; i++) {
954 add_to_proxy_output_ffmpeg(context->proxy_ctx[i], in_frame);
955 }
956
957 if (!context->start_pts_set) {
958 context->start_pts = pts;
959 context->start_pts_set = true;
960 }
961
962 context->frameno = floor(
963 (pts - context->start_pts) * context->pts_time_base * context->frame_rate + 0.5);
964
965 int64_t seek_pos_pts = timestamp_from_pts_or_dts(context->seek_pos_pts, context->seek_pos_dts);
966
967 if (pts < seek_pos_pts) {
968 /* Decoding starts *always* on I-Frames. In this case our position is
969 * before our seek I-Frame. So we need to pick the previous available
970 * I-Frame to be able to decode this one properly.
971 */
972 s_pos = context->last_seek_pos;
973 s_pts = context->last_seek_pos_pts;
974 s_dts = context->last_seek_pos_dts;
975 }
976
977 for (i = 0; i < tc_types.size(); i++) {
978 if (context->tcs_in_use & tc_types[i]) {
979 int tc_frameno = context->frameno;
980
981 if (tc_types[i] == IMB_TC_RECORD_RUN_NO_GAPS) {
982 tc_frameno = context->frameno_gapless;
983 }
984
985 IMB_index_builder_proc_frame(context->indexer[i],
986 curr_packet->data,
987 curr_packet->size,
988 tc_frameno,
989 s_pos,
990 s_pts,
991 s_dts,
992 pts);
993 }
994 }
995
996 context->frameno_gapless++;
997}
998
999static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context,
1000 const bool *stop,
1001 bool *do_update,
1002 float *progress)
1003{
1004 AVFrame *in_frame = av_frame_alloc();
1005 AVPacket *next_packet = av_packet_alloc();
1006 uint64_t stream_size;
1007
1008 stream_size = avio_size(context->iFormatCtx->pb);
1009
1010 context->frame_rate = av_q2d(
1011 av_guess_frame_rate(context->iFormatCtx, context->iStream, nullptr));
1012 context->pts_time_base = av_q2d(context->iStream->time_base);
1013
1014 while (av_read_frame(context->iFormatCtx, next_packet) >= 0) {
1015 float next_progress =
1016 float(int(floor(double(next_packet->pos) * 100 / double(stream_size) + 0.5))) / 100;
1017
1018 if (*progress != next_progress) {
1019 *progress = next_progress;
1020 *do_update = true;
1021 }
1022
1023 if (*stop) {
1024 break;
1025 }
1026
1027 if (next_packet->stream_index == context->videoStream) {
1028 int ret = avcodec_send_packet(context->iCodecCtx, next_packet);
1029 while (ret >= 0) {
1030 ret = avcodec_receive_frame(context->iCodecCtx, in_frame);
1031
1032 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
1033 /* No more frames to flush. */
1034 break;
1035 }
1036 if (ret < 0) {
1037 char error_str[AV_ERROR_MAX_STRING_SIZE];
1038 av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret);
1039 fprintf(stderr, "Error decoding proxy frame: %s\n", error_str);
1040 break;
1041 }
1042
1043 if (next_packet->flags & AV_PKT_FLAG_KEY) {
1044 context->last_seek_pos = context->seek_pos;
1045 context->last_seek_pos_pts = context->seek_pos_pts;
1046 context->last_seek_pos_dts = context->seek_pos_dts;
1047
1048 context->seek_pos = in_frame->pkt_pos;
1049 context->seek_pos_pts = in_frame->pts;
1050 context->seek_pos_dts = in_frame->pkt_dts;
1051 }
1052
1053 index_rebuild_ffmpeg_proc_decoded_frame(context, next_packet, in_frame);
1054 }
1055 }
1056 av_packet_unref(next_packet);
1057 }
1058
1059 /* process pictures still stuck in decoder engine after EOF
1060 * according to ffmpeg docs using nullptr packets.
1061 *
1062 * At least, if we haven't already stopped... */
1063
1064 if (!*stop) {
1065 int ret = avcodec_send_packet(context->iCodecCtx, nullptr);
1066
1067 while (ret >= 0) {
1068 ret = avcodec_receive_frame(context->iCodecCtx, in_frame);
1069
1070 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
1071 /* No more frames to flush. */
1072 break;
1073 }
1074 if (ret < 0) {
1075 char error_str[AV_ERROR_MAX_STRING_SIZE];
1076 av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret);
1077 fprintf(stderr, "Error flushing proxy frame: %s\n", error_str);
1078 break;
1079 }
1080 index_rebuild_ffmpeg_proc_decoded_frame(context, next_packet, in_frame);
1081 }
1082 }
1083
1084 av_packet_free(&next_packet);
1085 av_free(in_frame);
1086
1087 return 1;
1088}
1089
1090/* Get number of frames, that can be decoded in specified time period. */
1091static int indexer_performance_get_decode_rate(FFmpegIndexBuilderContext *context,
1092 const double time_period)
1093{
1094 AVFrame *in_frame = av_frame_alloc();
1095 AVPacket *packet = av_packet_alloc();
1096
1097 const double start = BLI_time_now_seconds();
1098 int frames_decoded = 0;
1099
1100 while (av_read_frame(context->iFormatCtx, packet) >= 0) {
1101 if (packet->stream_index != context->videoStream) {
1102 av_packet_unref(packet);
1103 continue;
1104 }
1105
1106 int ret = avcodec_send_packet(context->iCodecCtx, packet);
1107 while (ret >= 0) {
1108 ret = avcodec_receive_frame(context->iCodecCtx, in_frame);
1109
1110 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
1111 break;
1112 }
1113
1114 if (ret < 0) {
1115 char error_str[AV_ERROR_MAX_STRING_SIZE];
1116 av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, ret);
1117 fprintf(stderr, "Error decoding proxy frame: %s\n", error_str);
1118 break;
1119 }
1120 frames_decoded++;
1121 }
1122
1123 const double end = BLI_time_now_seconds();
1124
1125 if (end > start + time_period) {
1126 break;
1127 }
1128 av_packet_unref(packet);
1129 }
1130
1131 av_packet_free(&packet);
1132 av_frame_free(&in_frame);
1133
1134 avcodec_flush_buffers(context->iCodecCtx);
1135 av_seek_frame(context->iFormatCtx, -1, 0, AVSEEK_FLAG_BACKWARD);
1136 return frames_decoded;
1137}
1138
1139/* Read up to 10k movie packets and return max GOP size detected.
1140 * Number of packets is arbitrary. It should be as large as possible, but processed within
1141 * reasonable time period, so detected GOP size is as close to real as possible. */
1142static int indexer_performance_get_max_gop_size(FFmpegIndexBuilderContext *context)
1143{
1144 AVPacket *packet = av_packet_alloc();
1145
1146 const int packets_max = 10000;
1147 int packet_index = 0;
1148 int max_gop = 0;
1149 int cur_gop = 0;
1150
1151 while (av_read_frame(context->iFormatCtx, packet) >= 0) {
1152 if (packet->stream_index != context->videoStream) {
1153 av_packet_unref(packet);
1154 continue;
1155 }
1156 packet_index++;
1157 cur_gop++;
1158
1159 if (packet->flags & AV_PKT_FLAG_KEY) {
1160 max_gop = max_ii(max_gop, cur_gop);
1161 cur_gop = 0;
1162 }
1163
1164 if (packet_index > packets_max) {
1165 break;
1166 }
1167 av_packet_unref(packet);
1168 }
1169
1170 av_packet_free(&packet);
1171
1172 av_seek_frame(context->iFormatCtx, -1, 0, AVSEEK_FLAG_BACKWARD);
1173 return max_gop;
1174}
1175
1176/* Assess scrubbing performance of provided file. This function is not meant to be very exact.
1177 * It compares number of frames decoded in reasonable time with largest detected GOP size.
1178 * Because seeking happens in single GOP, it means, that maximum seek time can be detected this
1179 * way.
1180 * Since proxies use GOP size of 10 frames, skip building if detected GOP size is less or
1181 * equal.
1182 */
1183static bool indexer_need_to_build_proxy(FFmpegIndexBuilderContext *context)
1184{
1185 if (!context->build_only_on_bad_performance) {
1186 return true;
1187 }
1188
1189 /* Make sure, that file is not cold read. */
1190 indexer_performance_get_decode_rate(context, 0.1);
1191 /* Get decode rate per 100ms. This is arbitrary, but seems to be good baseline cadence of
1192 * seeking. */
1193 const int decode_rate = indexer_performance_get_decode_rate(context, 0.1);
1194 const int max_gop_size = indexer_performance_get_max_gop_size(context);
1195
1196 if (max_gop_size <= 10 || max_gop_size < decode_rate) {
1197 printf("Skipping proxy building for %s: Decoding performance is already good.\n",
1198 context->iFormatCtx->url);
1199 context->building_cancelled = true;
1200 return false;
1201 }
1202
1203 return true;
1204}
1205
1206#endif
1207
1208/* ----------------------------------------------------------------------
1209 * - public API
1210 * ---------------------------------------------------------------------- */
1211
1213 IMB_Timecode_Type tcs_in_use,
1214 int proxy_sizes_in_use,
1215 int quality,
1216 const bool overwrite,
1217 GSet *file_list,
1218 bool build_only_on_bad_performance)
1219{
1220 int proxy_sizes_to_build = proxy_sizes_in_use;
1221 int i;
1222
1223 /* Don't generate the same file twice! */
1224 if (file_list) {
1225 for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
1226 IMB_Proxy_Size proxy_size = proxy_sizes[i];
1227 if (proxy_size & proxy_sizes_to_build) {
1228 char filepath[FILE_MAX];
1229 if (get_proxy_filepath(anim, proxy_size, filepath, false) == false) {
1230 return nullptr;
1231 }
1232 void **filepath_key_p;
1233 if (!BLI_gset_ensure_p_ex(file_list, filepath, &filepath_key_p)) {
1234 *filepath_key_p = BLI_strdup(filepath);
1235 }
1236 else {
1237 proxy_sizes_to_build &= ~int(proxy_size);
1238 printf("Proxy: %s already registered for generation, skipping\n", filepath);
1239 }
1240 }
1241 }
1242 }
1243
1244 if (!overwrite) {
1245 int built_proxies = IMB_anim_proxy_get_existing(anim);
1246 if (built_proxies != 0) {
1247
1248 for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
1249 IMB_Proxy_Size proxy_size = proxy_sizes[i];
1250 if (proxy_size & built_proxies) {
1251 char filepath[FILE_MAX];
1252 if (get_proxy_filepath(anim, proxy_size, filepath, false) == false) {
1253 return nullptr;
1254 }
1255 printf("Skipping proxy: %s\n", filepath);
1256 }
1257 }
1258 }
1259 proxy_sizes_to_build &= ~built_proxies;
1260 }
1261
1262 fflush(stdout);
1263
1264 if (proxy_sizes_to_build == 0) {
1265 return nullptr;
1266 }
1267
1268 IndexBuildContext *context = nullptr;
1269#ifdef WITH_FFMPEG
1270 if (anim->state == ImBufAnim::State::Valid) {
1271 context = index_ffmpeg_create_context(
1272 anim, tcs_in_use, proxy_sizes_to_build, quality, build_only_on_bad_performance);
1273 }
1274#else
1275 UNUSED_VARS(build_only_on_bad_performance);
1276#endif
1277
1278 return context;
1279
1280 UNUSED_VARS(tcs_in_use, proxy_sizes_in_use, quality);
1281}
1282
1284 /* NOLINTNEXTLINE: readability-non-const-parameter. */
1285 bool *stop,
1286 /* NOLINTNEXTLINE: readability-non-const-parameter. */
1287 bool *do_update,
1288 /* NOLINTNEXTLINE: readability-non-const-parameter. */
1289 float *progress)
1290{
1291#ifdef WITH_FFMPEG
1292 if (context != nullptr) {
1293 if (indexer_need_to_build_proxy((FFmpegIndexBuilderContext *)context)) {
1294 index_rebuild_ffmpeg((FFmpegIndexBuilderContext *)context, stop, do_update, progress);
1295 }
1296 }
1297#endif
1298 UNUSED_VARS(context, stop, do_update, progress);
1299}
1300
1302{
1303#ifdef WITH_FFMPEG
1304 if (context != nullptr) {
1305 index_rebuild_ffmpeg_finish((FFmpegIndexBuilderContext *)context, stop);
1306 }
1307#endif
1308 /* static defined at top of the file */
1309 UNUSED_VARS(context, stop, proxy_sizes);
1310}
1311
1313{
1314 int i;
1315
1316 for (i = 0; i < IMB_PROXY_MAX_SLOT; i++) {
1317 if (anim->proxy_anim[i]) {
1318 IMB_close_anim(anim->proxy_anim[i]);
1319 anim->proxy_anim[i] = nullptr;
1320 }
1321 }
1322
1323 if (anim->record_run) {
1325 anim->record_run = nullptr;
1326 }
1327 if (anim->no_gaps) {
1329 anim->no_gaps = nullptr;
1330 }
1331
1332 anim->proxies_tried = 0;
1333 anim->indices_tried = 0;
1334}
1335
1336void IMB_anim_set_index_dir(ImBufAnim *anim, const char *dir)
1337{
1338 if (STREQ(anim->index_dir, dir)) {
1339 return;
1340 }
1341 STRNCPY(anim->index_dir, dir);
1342
1343 IMB_free_indices(anim);
1344}
1345
1347{
1348 char filepath[FILE_MAX];
1349 int i = IMB_proxy_size_to_array_index(preview_size);
1350
1351 if (i < 0) {
1352 return nullptr;
1353 }
1354
1355 if (anim->proxy_anim[i]) {
1356 return anim->proxy_anim[i];
1357 }
1358
1359 if (anim->proxies_tried & preview_size) {
1360 return nullptr;
1361 }
1362
1363 get_proxy_filepath(anim, preview_size, filepath, false);
1364
1365 /* proxies are generated in the same color space as animation itself */
1366 anim->proxy_anim[i] = IMB_open_anim(filepath, 0, 0, anim->colorspace);
1367
1368 anim->proxies_tried |= preview_size;
1369
1370 return anim->proxy_anim[i];
1371}
1372
1374{
1375 char filepath[FILE_MAX];
1376
1377 ImBufAnimIndex **index = nullptr;
1378
1379 if (tc == IMB_TC_RECORD_RUN) {
1380 index = &anim->record_run;
1381 }
1382 else if (tc == IMB_TC_RECORD_RUN_NO_GAPS) {
1383 index = &anim->no_gaps;
1384 }
1385
1386 if (anim->indices_tried & tc) {
1387 return nullptr;
1388 }
1389 if (index == nullptr) {
1390 return nullptr;
1391 }
1392
1393 get_tc_filepath(anim, tc, filepath);
1394
1395 *index = IMB_indexer_open(filepath);
1396
1397 anim->indices_tried |= tc;
1398
1399 return *index;
1400}
1401
1403{
1404 ImBufAnimIndex *idx = IMB_anim_open_index(anim, tc);
1405
1406 if (!idx) {
1407 return position;
1408 }
1409
1410 return IMB_indexer_get_frame_index(idx, position);
1411}
1412
1414{
1415 const int num_proxy_sizes = IMB_PROXY_MAX_SLOT;
1416 int existing = IMB_PROXY_NONE;
1417 int i;
1418 for (i = 0; i < num_proxy_sizes; i++) {
1419 IMB_Proxy_Size proxy_size = proxy_sizes[i];
1420 char filepath[FILE_MAX];
1421 get_proxy_filepath(anim, proxy_size, filepath, false);
1422 if (BLI_exists(filepath)) {
1423 existing |= int(proxy_size);
1424 }
1425 }
1426 return existing;
1427}
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
#define B_ENDIAN
#define ENDIAN_ORDER
BLI_INLINE void BLI_endian_switch_uint64(uint64_t *val) ATTR_NONNULL(1)
BLI_INLINE void BLI_endian_switch_int32(int *val) ATTR_NONNULL(1)
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:350
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
int BLI_rename(const char *from, const char *to) ATTR_NONNULL()
Definition fileops_c.cc:438
bool BLI_file_ensure_parent_dir_exists(const char *filepath) ATTR_NONNULL(1)
Definition fileops_c.cc:429
struct GSet GSet
Definition BLI_ghash.h:341
bool BLI_gset_ensure_p_ex(GSet *gs, const void *key, void ***r_key)
Definition BLI_ghash.c:971
MINLINE int round_fl_to_int(float a)
MINLINE int max_ii(int a, int b)
#define BLI_path_ncmp
#define FILE_MAXFILE
#define FILE_MAX
#define BLI_path_join(...)
void void void BLI_path_split_file_part(const char *filepath, char *file, size_t file_maxncpy) ATTR_NONNULL(1
void BLI_path_split_dir_file(const char *filepath, char *dir, size_t dir_maxncpy, char *file, size_t file_maxncpy) ATTR_NONNULL(1
#define FILE_MAXDIR
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define BLI_string_join(...)
unsigned char uchar
int BLI_system_thread_count(void)
Definition threads.cc:253
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.c:65
#define UNUSED_VARS(...)
#define UNLIKELY(x)
#define STREQ(a, b)
Compatibility-like things for windows.
const char * dirname(char *path)
ImBufAnim * IMB_open_anim(const char *filepath, int ib_flags, int streamindex, char colorspace[IM_MAX_SPACE])
void IMB_close_anim(ImBufAnim *anim)
Definition anim_movie.cc:76
IMB_Proxy_Size
@ IMB_PROXY_100
@ IMB_PROXY_MAX_SLOT
@ IMB_PROXY_75
@ IMB_PROXY_50
@ IMB_PROXY_25
@ IMB_PROXY_NONE
IMB_Timecode_Type
@ IMB_TC_RECORD_RUN_NO_GAPS
@ IMB_TC_NONE
@ IMB_TC_NUM_TYPES
@ IMB_TC_RECORD_RUN
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
Definition half.h:42
#define printf
unsigned short half
int len
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
FFMPEG_INLINE size_t ffmpeg_get_buffer_alignment()
FFMPEG_INLINE int64_t timestamp_from_pts_or_dts(int64_t pts, int64_t dts)
FFMPEG_INLINE int64_t av_get_pts_from_frame(AVFrame *picture)
FFMPEG_INLINE void my_guess_pkt_duration(AVFormatContext *s, AVStream *st, AVPacket *pkt)
void IMB_index_builder_proc_frame(anim_index_builder *fp, uchar *buffer, int data_size, int frameno, uint64_t seek_pos, uint64_t seek_pos_pts, uint64_t seek_pos_dts, uint64_t pts)
Definition indexer.cc:105
static bool get_proxy_filepath(ImBufAnim *anim, IMB_Proxy_Size preview_size, char *filepath, bool temp)
Definition indexer.cc:386
void IMB_anim_index_rebuild(IndexBuildContext *context, bool *stop, bool *do_update, float *progress)
Definition indexer.cc:1283
static const char temp_ext[]
Definition indexer.cc:45
uint64_t IMB_indexer_get_seek_pos(ImBufAnimIndex *idx, int frame_index)
Definition indexer.cc:229
void IMB_indexer_close(ImBufAnimIndex *idx)
Definition indexer.cc:324
#define INDEX_FILE_VERSION
Definition indexer.cc:51
anim_index_builder * IMB_index_builder_create(const char *filepath)
Definition indexer.cc:57
int IMB_indexer_get_frame_index(ImBufAnimIndex *idx, int frameno)
Definition indexer.cc:264
void IMB_anim_set_index_dir(ImBufAnim *anim, const char *dir)
Definition indexer.cc:1336
int IMB_anim_index_get_frame_index(ImBufAnim *anim, IMB_Timecode_Type tc, int position)
Definition indexer.cc:1402
static void get_tc_filepath(ImBufAnim *anim, IMB_Timecode_Type tc, char *filepath)
Definition indexer.cc:418
int IMB_indexer_get_duration(ImBufAnimIndex *idx)
Definition indexer.cc:307
ImBufAnimIndex * IMB_anim_open_index(ImBufAnim *anim, IMB_Timecode_Type tc)
Definition indexer.cc:1373
int IMB_timecode_to_array_index(IMB_Timecode_Type tc)
Definition indexer.cc:349
uint64_t IMB_indexer_get_seek_pos_pts(ImBufAnimIndex *idx, int frame_index)
Definition indexer.cc:242
ImBufAnim * IMB_anim_open_proxy(ImBufAnim *anim, IMB_Proxy_Size preview_size)
Definition indexer.cc:1346
uint64_t IMB_indexer_get_seek_pos_dts(ImBufAnimIndex *idx, int frame_index)
Definition indexer.cc:253
static const IMB_Proxy_Size proxy_sizes[]
Definition indexer.cc:47
void IMB_anim_index_rebuild_finish(IndexBuildContext *context, const bool stop)
Definition indexer.cc:1301
static const char binary_header_str[]
Definition indexer.cc:44
uint64_t IMB_indexer_get_pts(ImBufAnimIndex *idx, int frame_index)
Definition indexer.cc:296
void IMB_index_builder_finish(anim_index_builder *fp, int rollback)
Definition indexer.cc:129
int IMB_anim_proxy_get_existing(ImBufAnim *anim)
Definition indexer.cc:1413
void IMB_index_builder_add_entry(anim_index_builder *fp, int frameno, uint64_t seek_pos, uint64_t seek_pos_pts, uint64_t seek_pos_dts, uint64_t pts)
Definition indexer.cc:91
static void get_index_dir(ImBufAnim *anim, char *index_dir, size_t index_dir_maxncpy)
Definition indexer.cc:368
static const float proxy_fac[]
Definition indexer.cc:49
IndexBuildContext * IMB_anim_index_rebuild_context(ImBufAnim *anim, IMB_Timecode_Type tcs_in_use, int proxy_sizes_in_use, int quality, const bool overwrite, GSet *file_list, bool build_only_on_bad_performance)
Definition indexer.cc:1212
int IMB_indexer_can_scan(ImBufAnimIndex *idx, int old_frame_index, int new_frame_index)
Definition indexer.cc:315
void IMB_anim_get_filename(ImBufAnim *anim, char *filename, int filename_maxncpy)
Definition indexer.cc:381
int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size)
Definition indexer.cc:330
ImBufAnimIndex * IMB_indexer_open(const char *filepath)
Definition indexer.cc:148
void IMB_free_indices(ImBufAnim *anim)
Definition indexer.cc:1312
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
ccl_device_inline float2 floor(const float2 a)
return ret
__int64 int64_t
Definition stdint.h:89
unsigned __int64 uint64_t
Definition stdint.h:90
char filepath[1024]
anim_index_entry * entries
char index_dir[768]
Definition IMB_anim.hh:64
ImBufAnimIndex * no_gaps
Definition IMB_anim.hh:71
ImBufAnimIndex * record_run
Definition IMB_anim.hh:70
State state
Definition IMB_anim.hh:30
int streamindex
Definition IMB_anim.hh:41
char suffix[64]
Definition IMB_anim.hh:74
ImBufAnim * proxy_anim[IMB_PROXY_MAX_SLOT]
Definition IMB_anim.hh:69
char filepath[1024]
Definition IMB_anim.hh:39
int indices_tried
Definition IMB_anim.hh:67
char colorspace[64]
Definition IMB_anim.hh:73
int proxies_tried
Definition IMB_anim.hh:66
void(* proc_frame)(anim_index_builder *idx, unsigned char *buffer, int data_size, anim_index_entry *entry)
char filepath_temp[FILE_MAX]
char filepath[FILE_MAX]
void(* delete_priv_data)(anim_index_builder *idx)
uint64_t pts
uint64_t seek_pos
uint64_t seek_pos_pts
int frameno
uint64_t seek_pos_dts