Blender V4.3
anim_movie.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cctype>
10#include <climits>
11#include <cmath>
12#include <cstdio>
13#include <cstdlib>
14#include <sys/types.h>
15#ifndef _WIN32
16# include <dirent.h>
17#else
18# include <io.h>
19#endif
20
21#include "BLI_math_base.hh"
22#include "BLI_path_utils.hh"
23#include "BLI_string.h"
24#include "BLI_threads.h"
25#include "BLI_utildefines.h"
26
27#include "DNA_scene_types.h"
28
29#include "MEM_guardedalloc.h"
30
31#include "IMB_imbuf.hh"
32#include "IMB_imbuf_types.hh"
33
36
37#include "IMB_anim.hh"
38#include "IMB_indexer.hh"
39#include "IMB_metadata.hh"
40
41#ifdef WITH_FFMPEG
42# include "BKE_writeffmpeg.hh"
43
44extern "C" {
45# include <libavcodec/avcodec.h>
46# include <libavformat/avformat.h>
47# include <libavutil/imgutils.h>
48# include <libavutil/rational.h>
49# include <libswscale/swscale.h>
50
51# include "ffmpeg_compat.h"
52}
53
54#endif /* WITH_FFMPEG */
55
56#ifdef WITH_FFMPEG
57static void free_anim_ffmpeg(ImBufAnim *anim);
58#endif
59
61{
62 if (anim == nullptr) {
63 printf("free anim, anim == nullptr\n");
64 return;
65 }
66
67#ifdef WITH_FFMPEG
68 free_anim_ffmpeg(anim);
69#endif
70 IMB_free_indices(anim);
72
73 MEM_freeN(anim);
74}
75
77{
78 if (anim == nullptr) {
79 return;
80 }
81
82 IMB_free_anim(anim);
83}
84
86{
87 if (anim == nullptr) {
88 return;
89 }
90
91 IMB_free_indices(anim);
92}
93
95{
96 if (anim->state == ImBufAnim::State::Valid) {
97#ifdef WITH_FFMPEG
98 BLI_assert(anim->pFormatCtx != nullptr);
99 av_log(anim->pFormatCtx, AV_LOG_DEBUG, "METADATA FETCH\n");
100
101 AVDictionaryEntry *entry = nullptr;
102 while (true) {
103 entry = av_dict_get(anim->pFormatCtx->metadata, "", entry, AV_DICT_IGNORE_SUFFIX);
104 if (entry == nullptr) {
105 break;
106 }
107
108 /* Delay creation of the property group until there is actual metadata to put in there. */
110 IMB_metadata_set_field(anim->metadata, entry->key, entry->value);
111 }
112#endif
113 }
114 return anim->metadata;
115}
116
117ImBufAnim *IMB_open_anim(const char *filepath,
118 int ib_flags,
119 int streamindex,
120 char colorspace[IM_MAX_SPACE])
121{
122 ImBufAnim *anim;
123
124 BLI_assert(!BLI_path_is_rel(filepath));
125
126 anim = (ImBufAnim *)MEM_callocN(sizeof(ImBufAnim), "anim struct");
127 if (anim != nullptr) {
128 if (colorspace) {
130 STRNCPY(anim->colorspace, colorspace);
131 }
132 else {
134 anim->colorspace, sizeof(anim->colorspace), COLOR_ROLE_DEFAULT_BYTE);
135 }
136
137 STRNCPY(anim->filepath, filepath);
138 anim->ib_flags = ib_flags;
139 anim->streamindex = streamindex;
140 }
141 return anim;
142}
143
145{
146#if !defined(WITH_FFMPEG)
147 UNUSED_VARS(anim);
148#endif
149
150#ifdef WITH_FFMPEG
151 if (anim->pCodecCtx != nullptr) {
152 return true;
153 }
154#endif
155 return false;
156}
157
158void IMB_suffix_anim(ImBufAnim *anim, const char *suffix)
159{
160 STRNCPY(anim->suffix, suffix);
161}
162
163#ifdef WITH_FFMPEG
164
165static double ffmpeg_stream_start_time_get(AVStream *stream)
166{
167 if (stream->start_time == AV_NOPTS_VALUE) {
168 return 0.0;
169 }
170
171 return stream->start_time * av_q2d(stream->time_base);
172}
173
174static int ffmpeg_container_frame_count_get(AVFormatContext *pFormatCtx, AVStream *video_stream)
175{
176 /* Find audio stream to guess the duration of the video.
177 * Sometimes the audio AND the video stream have a start offset.
178 * The difference between these is the offset we want to use to
179 * calculate the video duration.
180 */
181 const double video_start = ffmpeg_stream_start_time_get(video_stream);
182 double audio_start = 0;
183
184 for (int i = 0; i < pFormatCtx->nb_streams; i++) {
185 if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
186 AVStream *audio_stream = pFormatCtx->streams[i];
187 audio_start = ffmpeg_stream_start_time_get(audio_stream);
188 break;
189 }
190 }
191
192 const AVRational frame_rate = av_guess_frame_rate(pFormatCtx, video_stream, nullptr);
193 double stream_dur;
194
195 if (video_start > audio_start) {
196 stream_dur = double(pFormatCtx->duration) / AV_TIME_BASE - (video_start - audio_start);
197 }
198 else {
199 /* The video stream starts before or at the same time as the audio stream!
200 * We have to assume that the video stream is as long as the full pFormatCtx->duration.
201 */
202 stream_dur = double(pFormatCtx->duration) / AV_TIME_BASE;
203 }
204
205 return lround(stream_dur * av_q2d(frame_rate));
206}
207
208static int ffmpeg_frame_count_get(AVFormatContext *pFormatCtx, AVStream *video_stream)
209{
210 /* Use stream duration to determine frame count. */
211 if (video_stream->duration != AV_NOPTS_VALUE) {
212 const AVRational frame_rate = av_guess_frame_rate(pFormatCtx, video_stream, nullptr);
213 const double stream_dur = video_stream->duration * av_q2d(video_stream->time_base);
214 return lround(stream_dur * av_q2d(frame_rate));
215 }
216
217 /* Fall back to manually estimating the video stream duration.
218 * This is because the video stream duration can be shorter than the `pFormatCtx->duration`.
219 */
220 if (pFormatCtx->duration != AV_NOPTS_VALUE) {
221 return ffmpeg_container_frame_count_get(pFormatCtx, video_stream);
222 }
223
224 /* Read frame count from the stream if we can. Note, that this value can not be trusted. */
225 if (video_stream->nb_frames != 0) {
226 return video_stream->nb_frames;
227 }
228
229 /* The duration has not been set, happens for single JPEG2000 images.
230 * NOTE: Leave the duration zeroed, although it could set to 1 so the file is recognized
231 * as a movie with 1 frame, leave as-is since image loading code-paths are preferred
232 * in this case. The following assertion should be valid in this case. */
233 BLI_assert(pFormatCtx->duration == AV_NOPTS_VALUE);
234 return 0;
235}
236
237static int startffmpeg(ImBufAnim *anim)
238{
239 const AVCodec *pCodec;
240 AVFormatContext *pFormatCtx = nullptr;
241 AVCodecContext *pCodecCtx;
242 AVStream *video_stream;
243 int video_stream_index;
244 int frs_num;
245 double frs_den;
246 int streamcount;
247
248 /* The following for color space determination */
249 int srcRange, dstRange, brightness, contrast, saturation;
250 int *table;
251 const int *inv_table;
252
253 if (anim == nullptr) {
254 return (-1);
255 }
256
257 streamcount = anim->streamindex;
258
259 if (avformat_open_input(&pFormatCtx, anim->filepath, nullptr, nullptr) != 0) {
260 return -1;
261 }
262
263 if (avformat_find_stream_info(pFormatCtx, nullptr) < 0) {
264 avformat_close_input(&pFormatCtx);
265 return -1;
266 }
267
268 av_dump_format(pFormatCtx, 0, anim->filepath, 0);
269
270 /* Find the video stream */
271 video_stream_index = -1;
272
273 for (int i = 0; i < pFormatCtx->nb_streams; i++) {
274 if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
275 if (streamcount > 0) {
276 streamcount--;
277 continue;
278 }
279 video_stream_index = i;
280 break;
281 }
282 }
283
284 if (video_stream_index == -1) {
285 avformat_close_input(&pFormatCtx);
286 return -1;
287 }
288
289 video_stream = pFormatCtx->streams[video_stream_index];
290
291 /* Find the decoder for the video stream */
292 pCodec = avcodec_find_decoder(video_stream->codecpar->codec_id);
293 if (pCodec == nullptr) {
294 avformat_close_input(&pFormatCtx);
295 return -1;
296 }
297
298 pCodecCtx = avcodec_alloc_context3(nullptr);
299 avcodec_parameters_to_context(pCodecCtx, video_stream->codecpar);
300 pCodecCtx->workaround_bugs = FF_BUG_AUTODETECT;
301
302 if (pCodec->capabilities & AV_CODEC_CAP_OTHER_THREADS) {
303 pCodecCtx->thread_count = 0;
304 }
305 else {
306 pCodecCtx->thread_count = BLI_system_thread_count();
307 }
308
309 if (pCodec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
310 pCodecCtx->thread_type = FF_THREAD_FRAME;
311 }
312 else if (pCodec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
313 pCodecCtx->thread_type = FF_THREAD_SLICE;
314 }
315
316 if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0) {
317 avformat_close_input(&pFormatCtx);
318 return -1;
319 }
320 if (pCodecCtx->pix_fmt == AV_PIX_FMT_NONE) {
321 avcodec_free_context(&anim->pCodecCtx);
322 avformat_close_input(&pFormatCtx);
323 return -1;
324 }
325
326 const AVRational frame_rate = av_guess_frame_rate(pFormatCtx, video_stream, nullptr);
327 frs_num = frame_rate.num;
328 frs_den = frame_rate.den;
329
330 frs_den *= AV_TIME_BASE;
331
332 while (frs_num % 10 == 0 && frs_den >= 2.0 && frs_num > 10) {
333 frs_num /= 10;
334 frs_den /= 10;
335 }
336
337 anim->frs_sec = frs_num;
338 anim->frs_sec_base = frs_den;
339 /* Save the relative start time for the video. IE the start time in relation to where playback
340 * starts. */
341 anim->start_offset = ffmpeg_stream_start_time_get(video_stream);
342 anim->duration_in_frames = ffmpeg_frame_count_get(pFormatCtx, video_stream);
343
344 anim->x = pCodecCtx->width;
345 anim->y = pCodecCtx->height;
346
347 anim->pFormatCtx = pFormatCtx;
348 anim->pCodecCtx = pCodecCtx;
349 anim->pCodec = pCodec;
350 anim->videoStream = video_stream_index;
351
352 anim->cur_position = 0;
353 anim->cur_pts = -1;
354 anim->cur_key_frame_pts = -1;
355 anim->cur_packet = av_packet_alloc();
356 anim->cur_packet->stream_index = -1;
357
358 anim->pFrame = av_frame_alloc();
359 anim->pFrame_backup = av_frame_alloc();
360 anim->pFrame_backup_complete = false;
361 anim->pFrame_complete = false;
362 anim->pFrameDeinterlaced = av_frame_alloc();
363 anim->pFrameRGB = av_frame_alloc();
364 anim->pFrameRGB->format = AV_PIX_FMT_RGBA;
365 anim->pFrameRGB->width = anim->x;
366 anim->pFrameRGB->height = anim->y;
367
368 const size_t align = ffmpeg_get_buffer_alignment();
369 if (av_frame_get_buffer(anim->pFrameRGB, align) < 0) {
370 fprintf(stderr, "Could not allocate frame data.\n");
371 avcodec_free_context(&anim->pCodecCtx);
372 avformat_close_input(&anim->pFormatCtx);
373 av_packet_free(&anim->cur_packet);
374 av_frame_free(&anim->pFrameRGB);
375 av_frame_free(&anim->pFrameDeinterlaced);
376 av_frame_free(&anim->pFrame);
377 av_frame_free(&anim->pFrame_backup);
378 anim->pCodecCtx = nullptr;
379 return -1;
380 }
381
382 if (av_image_get_buffer_size(AV_PIX_FMT_RGBA, anim->x, anim->y, 1) != anim->x * anim->y * 4) {
383 fprintf(stderr, "ffmpeg has changed alloc scheme ... ARGHHH!\n");
384 avcodec_free_context(&anim->pCodecCtx);
385 avformat_close_input(&anim->pFormatCtx);
386 av_packet_free(&anim->cur_packet);
387 av_frame_free(&anim->pFrameRGB);
388 av_frame_free(&anim->pFrameDeinterlaced);
389 av_frame_free(&anim->pFrame);
390 av_frame_free(&anim->pFrame_backup);
391 anim->pCodecCtx = nullptr;
392 return -1;
393 }
394
395 if (anim->ib_flags & IB_animdeinterlace) {
396 anim->pFrameDeinterlaced->format = anim->pCodecCtx->pix_fmt;
397 anim->pFrameDeinterlaced->width = anim->pCodecCtx->width;
398 anim->pFrameDeinterlaced->height = anim->pCodecCtx->height;
399 av_image_fill_arrays(
400 anim->pFrameDeinterlaced->data,
401 anim->pFrameDeinterlaced->linesize,
402 static_cast<const uint8_t *>(MEM_callocN(
403 av_image_get_buffer_size(
404 anim->pCodecCtx->pix_fmt, anim->pCodecCtx->width, anim->pCodecCtx->height, 1),
405 "ffmpeg deinterlace")),
406 anim->pCodecCtx->pix_fmt,
407 anim->pCodecCtx->width,
408 anim->pCodecCtx->height,
409 1);
410 }
411
412 anim->img_convert_ctx = BKE_ffmpeg_sws_get_context(anim->x,
413 anim->y,
414 anim->pCodecCtx->pix_fmt,
415 anim->x,
416 anim->y,
417 AV_PIX_FMT_RGBA,
418 SWS_BILINEAR | SWS_PRINT_INFO |
419 SWS_FULL_CHR_H_INT);
420
421 if (!anim->img_convert_ctx) {
422 fprintf(stderr, "Can't transform color space??? Bailing out...\n");
423 avcodec_free_context(&anim->pCodecCtx);
424 avformat_close_input(&anim->pFormatCtx);
425 av_packet_free(&anim->cur_packet);
426 av_frame_free(&anim->pFrameRGB);
427 av_frame_free(&anim->pFrameDeinterlaced);
428 av_frame_free(&anim->pFrame);
429 av_frame_free(&anim->pFrame_backup);
430 anim->pCodecCtx = nullptr;
431 return -1;
432 }
433
434 /* Try do detect if input has 0-255 YCbCR range (JFIF, JPEG, Motion-JPEG). */
435 if (!sws_getColorspaceDetails(anim->img_convert_ctx,
436 (int **)&inv_table,
437 &srcRange,
438 &table,
439 &dstRange,
440 &brightness,
441 &contrast,
442 &saturation))
443 {
444 srcRange = srcRange || anim->pCodecCtx->color_range == AVCOL_RANGE_JPEG;
445 inv_table = sws_getCoefficients(anim->pCodecCtx->colorspace);
446
447 if (sws_setColorspaceDetails(anim->img_convert_ctx,
448 (int *)inv_table,
449 srcRange,
450 table,
451 dstRange,
452 brightness,
453 contrast,
454 saturation))
455 {
456 fprintf(stderr, "Warning: Could not set libswscale colorspace details.\n");
457 }
458 }
459 else {
460 fprintf(stderr, "Warning: Could not set libswscale colorspace details.\n");
461 }
462
463 return 0;
464}
465
466static double ffmpeg_steps_per_frame_get(ImBufAnim *anim)
467{
468 AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
469 AVRational time_base = v_st->time_base;
470 AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, nullptr);
471 return av_q2d(av_inv_q(av_mul_q(frame_rate, time_base)));
472}
473
474/* Store backup frame.
475 * With VFR movies, if PTS is not matched perfectly, scanning continues to look for next PTS.
476 * It is likely to overshoot and scanning stops. Having previous frame backed up, it is possible
477 * to use it when overshoot happens.
478 */
479static void ffmpeg_double_buffer_backup_frame_store(ImBufAnim *anim, int64_t pts_to_search)
480{
481 /* `anim->pFrame` is beyond `pts_to_search`. Don't store it. */
482 if (anim->pFrame_backup_complete && anim->cur_pts >= pts_to_search) {
483 return;
484 }
485 if (!anim->pFrame_complete) {
486 return;
487 }
488
489 if (anim->pFrame_backup_complete) {
490 av_frame_unref(anim->pFrame_backup);
491 }
492
493 av_frame_move_ref(anim->pFrame_backup, anim->pFrame);
494 anim->pFrame_backup_complete = true;
495}
496
497/* Free stored backup frame. */
498static void ffmpeg_double_buffer_backup_frame_clear(ImBufAnim *anim)
499{
500 if (anim->pFrame_backup_complete) {
501 av_frame_unref(anim->pFrame_backup);
502 }
503 anim->pFrame_backup_complete = false;
504}
505
506/* Return recently decoded frame. If it does not exist, return frame from backup buffer. */
507static AVFrame *ffmpeg_double_buffer_frame_fallback_get(ImBufAnim *anim)
508{
509 av_log(anim->pFormatCtx, AV_LOG_ERROR, "DECODE UNHAPPY: PTS not matched!\n");
510
511 if (anim->pFrame_complete) {
512 return anim->pFrame;
513 }
514 if (anim->pFrame_backup_complete) {
515 return anim->pFrame_backup;
516 }
517 return nullptr;
518}
519
525static void ffmpeg_postprocess(ImBufAnim *anim, AVFrame *input, ImBuf *ibuf)
526{
527 int filter_y = 0;
528
529 /* This means the data wasn't read properly,
530 * this check stops crashing */
531 if (input->data[0] == nullptr && input->data[1] == nullptr && input->data[2] == nullptr &&
532 input->data[3] == nullptr)
533 {
534 fprintf(stderr,
535 "ffmpeg_fetchibuf: "
536 "data not read properly...\n");
537 return;
538 }
539
540 av_log(anim->pFormatCtx,
541 AV_LOG_DEBUG,
542 " POSTPROC: AVFrame planes: %p %p %p %p\n",
543 input->data[0],
544 input->data[1],
545 input->data[2],
546 input->data[3]);
547
548 if (anim->ib_flags & IB_animdeinterlace) {
549 if (av_image_deinterlace(anim->pFrameDeinterlaced,
550 anim->pFrame,
551 anim->pCodecCtx->pix_fmt,
552 anim->pCodecCtx->width,
553 anim->pCodecCtx->height) < 0)
554 {
555 filter_y = true;
556 }
557 else {
558 input = anim->pFrameDeinterlaced;
559 }
560 }
561
562 /* If final destination image layout matches that of decoded RGB frame (including
563 * any line padding done by ffmpeg for SIMD alignment), we can directly
564 * decode into that, doing the vertical flip in the same step. Otherwise have
565 * to do a separate flip. */
566 const int ibuf_linesize = ibuf->x * 4;
567 const int rgb_linesize = anim->pFrameRGB->linesize[0];
568 bool scale_to_ibuf = (rgb_linesize == ibuf_linesize);
569 /* swscale on arm64 before ffmpeg 6.0 (libswscale major version 7)
570 * could not handle negative line sizes. That has been fixed in all major
571 * ffmpeg releases in early 2023, but easier to just check for "below 7". */
572# if (defined(__aarch64__) || defined(_M_ARM64)) && (LIBSWSCALE_VERSION_MAJOR < 7)
573 scale_to_ibuf = false;
574# endif
575 uint8_t *rgb_data = anim->pFrameRGB->data[0];
576
577 if (scale_to_ibuf) {
578 /* Decode RGB and do vertical flip directly into destination image, by using negative
579 * line size. */
580 anim->pFrameRGB->linesize[0] = -ibuf_linesize;
581 anim->pFrameRGB->data[0] = ibuf->byte_buffer.data + (ibuf->y - 1) * ibuf_linesize;
582
583 BKE_ffmpeg_sws_scale_frame(anim->img_convert_ctx, anim->pFrameRGB, input);
584
585 anim->pFrameRGB->linesize[0] = rgb_linesize;
586 anim->pFrameRGB->data[0] = rgb_data;
587 }
588 else {
589 /* Decode, then do vertical flip into destination. */
590 BKE_ffmpeg_sws_scale_frame(anim->img_convert_ctx, anim->pFrameRGB, input);
591
592 /* Use negative line size to do vertical image flip. */
593 const int src_linesize[4] = {-rgb_linesize, 0, 0, 0};
594 const uint8_t *const src[4] = {
595 rgb_data + (anim->y - 1) * rgb_linesize, nullptr, nullptr, nullptr};
596 int dst_size = av_image_get_buffer_size(AVPixelFormat(anim->pFrameRGB->format),
597 anim->pFrameRGB->width,
598 anim->pFrameRGB->height,
599 1);
600 av_image_copy_to_buffer(
601 ibuf->byte_buffer.data, dst_size, src, src_linesize, AV_PIX_FMT_RGBA, anim->x, anim->y, 1);
602 }
603
604 if (filter_y) {
605 IMB_filtery(ibuf);
606 }
607}
608
609static void final_frame_log(ImBufAnim *anim,
610 int64_t frame_pts_start,
611 int64_t frame_pts_end,
612 const char *str)
613{
614 av_log(anim->pFormatCtx,
615 AV_LOG_INFO,
616 "DECODE HAPPY: %s frame PTS range %" PRId64 " - %" PRId64 ".\n",
617 str,
618 frame_pts_start,
619 frame_pts_end);
620}
621
622static bool ffmpeg_pts_isect(int64_t pts_start, int64_t pts_end, int64_t pts_to_search)
623{
624 return pts_start <= pts_to_search && pts_to_search < pts_end;
625}
626
627/* Return frame that matches `pts_to_search`, nullptr if matching frame does not exist. */
628static AVFrame *ffmpeg_frame_by_pts_get(ImBufAnim *anim, int64_t pts_to_search)
629{
630 /* NOTE: `frame->pts + frame->pkt_duration` does not always match pts of next frame.
631 * See footage from #86361. Here it is OK to use, because PTS must match current or backup frame.
632 * If there is no current frame, return nullptr.
633 */
634 if (!anim->pFrame_complete) {
635 return nullptr;
636 }
637
638 const bool backup_frame_ready = anim->pFrame_backup_complete;
639 const int64_t recent_start = av_get_pts_from_frame(anim->pFrame);
640 const int64_t recent_end = recent_start + av_get_frame_duration_in_pts_units(anim->pFrame);
641 const int64_t backup_start = backup_frame_ready ? av_get_pts_from_frame(anim->pFrame_backup) : 0;
642
643 AVFrame *best_frame = nullptr;
644 if (ffmpeg_pts_isect(recent_start, recent_end, pts_to_search)) {
645 final_frame_log(anim, recent_start, recent_end, "Recent");
646 best_frame = anim->pFrame;
647 }
648 else if (backup_frame_ready && ffmpeg_pts_isect(backup_start, recent_start, pts_to_search)) {
649 final_frame_log(anim, backup_start, recent_start, "Backup");
650 best_frame = anim->pFrame_backup;
651 }
652 return best_frame;
653}
654
655static void ffmpeg_decode_store_frame_pts(ImBufAnim *anim)
656{
657 anim->cur_pts = av_get_pts_from_frame(anim->pFrame);
658
659# ifdef FFMPEG_OLD_KEY_FRAME_QUERY_METHOD
660 if (anim->pFrame->key_frame) {
661# else
662 if (anim->pFrame->flags & AV_FRAME_FLAG_KEY) {
663# endif
664 anim->cur_key_frame_pts = anim->cur_pts;
665 }
666
667 av_log(anim->pFormatCtx,
668 AV_LOG_DEBUG,
669 " FRAME DONE: cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
670 av_get_pts_from_frame(anim->pFrame),
671 int64_t(anim->cur_pts));
672}
673
674static int ffmpeg_read_video_frame(ImBufAnim *anim, AVPacket *packet)
675{
676 int ret = 0;
677 while ((ret = av_read_frame(anim->pFormatCtx, packet)) >= 0) {
678 if (packet->stream_index == anim->videoStream) {
679 break;
680 }
681 av_packet_unref(packet);
682 packet->stream_index = -1;
683 }
684
685 return ret;
686}
687
688/* decode one video frame also considering the packet read into cur_packet */
689static int ffmpeg_decode_video_frame(ImBufAnim *anim)
690{
691 av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n");
692
693 /* Sometimes, decoder returns more than one frame per sent packet. Check if frames are available.
694 * This frames must be read, otherwise decoding will fail. See #91405. */
695 anim->pFrame_complete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
696 if (anim->pFrame_complete) {
697 av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE FROM CODEC BUFFER\n");
698 ffmpeg_decode_store_frame_pts(anim);
699 return 1;
700 }
701
702 int rval = 0;
703 if (anim->cur_packet->stream_index == anim->videoStream) {
704 av_packet_unref(anim->cur_packet);
705 anim->cur_packet->stream_index = -1;
706 }
707
708 while ((rval = ffmpeg_read_video_frame(anim, anim->cur_packet)) >= 0) {
709 if (anim->cur_packet->stream_index != anim->videoStream) {
710 continue;
711 }
712
713 av_log(anim->pFormatCtx,
714 AV_LOG_DEBUG,
715 "READ: strID=%d dts=%" PRId64 " pts=%" PRId64 " %s\n",
716 anim->cur_packet->stream_index,
717 (anim->cur_packet->dts == AV_NOPTS_VALUE) ? -1 : int64_t(anim->cur_packet->dts),
718 (anim->cur_packet->pts == AV_NOPTS_VALUE) ? -1 : int64_t(anim->cur_packet->pts),
719 (anim->cur_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : "");
720
721 avcodec_send_packet(anim->pCodecCtx, anim->cur_packet);
722 anim->pFrame_complete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
723
724 if (anim->pFrame_complete) {
725 ffmpeg_decode_store_frame_pts(anim);
726 break;
727 }
728 av_packet_unref(anim->cur_packet);
729 anim->cur_packet->stream_index = -1;
730 }
731
732 if (rval == AVERROR_EOF) {
733 /* Flush any remaining frames out of the decoder. */
734 avcodec_send_packet(anim->pCodecCtx, nullptr);
735 anim->pFrame_complete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
736
737 if (anim->pFrame_complete) {
738 ffmpeg_decode_store_frame_pts(anim);
739 rval = 0;
740 }
741 }
742
743 if (rval < 0) {
744 av_packet_unref(anim->cur_packet);
745 anim->cur_packet->stream_index = -1;
746
747 char error_str[AV_ERROR_MAX_STRING_SIZE];
748 av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, rval);
749
750 av_log(anim->pFormatCtx,
751 AV_LOG_ERROR,
752 " DECODE READ FAILED: av_read_frame() "
753 "returned error: %s\n",
754 error_str);
755 }
756
757 return (rval >= 0);
758}
759
760static int match_format(const char *name, AVFormatContext *pFormatCtx)
761{
762 const char *p;
763 int len, namelen;
764
765 const char *names = pFormatCtx->iformat->name;
766
767 if (!name || !names) {
768 return 0;
769 }
770
771 namelen = strlen(name);
772 while ((p = strchr(names, ','))) {
773 len = std::max(int(p - names), namelen);
774 if (!BLI_strncasecmp(name, names, len)) {
775 return 1;
776 }
777 names = p + 1;
778 }
779 return !BLI_strcasecmp(name, names);
780}
781
782static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx)
783{
784 static const char *byte_seek_list[] = {"mpegts", nullptr};
785 const char **p;
786
787 if (pFormatCtx->iformat->flags & AVFMT_TS_DISCONT) {
788 return true;
789 }
790
791 p = byte_seek_list;
792
793 while (*p) {
794 if (match_format(*p++, pFormatCtx)) {
795 return true;
796 }
797 }
798
799 return false;
800}
801
802static int64_t ffmpeg_get_seek_pts(ImBufAnim *anim, int64_t pts_to_search)
803{
804 /* FFMPEG seeks internally using DTS values instead of PTS. In some files DTS and PTS values are
805 * offset and sometimes FFMPEG fails to take this into account when seeking.
806 * Therefore we need to seek backwards a certain offset to make sure the frame we want is in
807 * front of us. It is not possible to determine the exact needed offset,
808 * this value is determined experimentally.
809 * NOTE: Too big offset can impact performance. Current 3 frame offset has no measurable impact.
810 */
811 int64_t seek_pts = pts_to_search - (ffmpeg_steps_per_frame_get(anim) * 3);
812
813 if (seek_pts < 0) {
814 seek_pts = 0;
815 }
816 return seek_pts;
817}
818
819/* This gives us an estimate of which pts our requested frame will have.
820 * Note that this might be off a bit in certain video files, but it should still be close enough.
821 */
822static int64_t ffmpeg_get_pts_to_search(ImBufAnim *anim, ImBufAnimIndex *tc_index, int position)
823{
824 int64_t pts_to_search;
825
826 if (tc_index) {
827 int new_frame_index = IMB_indexer_get_frame_index(tc_index, position);
828 pts_to_search = IMB_indexer_get_pts(tc_index, new_frame_index);
829 }
830 else {
831 AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
832 int64_t start_pts = v_st->start_time;
833
834 pts_to_search = round(position * ffmpeg_steps_per_frame_get(anim));
835
836 if (start_pts != AV_NOPTS_VALUE) {
837 pts_to_search += start_pts;
838 }
839 }
840 return pts_to_search;
841}
842
843static bool ffmpeg_is_first_frame_decode(ImBufAnim *anim)
844{
845 return anim->pFrame_complete == false;
846}
847
848static void ffmpeg_scan_log(ImBufAnim *anim, int64_t pts_to_search)
849{
850 int64_t frame_pts_start = av_get_pts_from_frame(anim->pFrame);
851 int64_t frame_pts_end = frame_pts_start + av_get_frame_duration_in_pts_units(anim->pFrame);
852 av_log(anim->pFormatCtx,
853 AV_LOG_DEBUG,
854 " SCAN WHILE: PTS range %" PRId64 " - %" PRId64 " in search of %" PRId64 "\n",
855 frame_pts_start,
856 frame_pts_end,
857 pts_to_search);
858}
859
860/* Decode frames one by one until its PTS matches pts_to_search. */
861static void ffmpeg_decode_video_frame_scan(ImBufAnim *anim, int64_t pts_to_search)
862{
863 const int64_t start_gop_frame = anim->cur_key_frame_pts;
864 bool decode_error = false;
865
866 while (!decode_error && anim->cur_pts < pts_to_search) {
867 ffmpeg_scan_log(anim, pts_to_search);
868 ffmpeg_double_buffer_backup_frame_store(anim, pts_to_search);
869 decode_error = ffmpeg_decode_video_frame(anim) < 1;
870
871 /* We should not get a new GOP keyframe while scanning if seeking is working as intended.
872 * If this condition triggers, there may be and error in our seeking code.
873 * NOTE: This seems to happen if DTS value is used for seeking in ffmpeg internally. There
874 * seems to be no good way to handle such case. */
875 if (anim->seek_before_decode && start_gop_frame != anim->cur_key_frame_pts) {
876 av_log(anim->pFormatCtx, AV_LOG_ERROR, "SCAN: Frame belongs to an unexpected GOP!\n");
877 }
878 }
879}
880
881/* Wrapper over av_seek_frame(), for formats that doesn't have its own read_seek() or
882 * read_seek2() functions defined. When seeking in these formats, rule to seek to last
883 * necessary I-frame is not honored. It is not even guaranteed that I-frame, that must be
884 * decoded will be read. See https://trac.ffmpeg.org/ticket/1607 & #86944. */
885static int ffmpeg_generic_seek_workaround(ImBufAnim *anim,
886 int64_t *requested_pts,
887 int64_t pts_to_search)
888{
889 int64_t current_pts = *requested_pts;
890 int64_t offset = 0;
891
892 int64_t cur_pts, prev_pts = -1;
893
894 /* Step backward frame by frame until we find the key frame we are looking for. */
895 while (current_pts != 0) {
896 current_pts = *requested_pts - int64_t(round(offset * ffmpeg_steps_per_frame_get(anim)));
897 current_pts = std::max(current_pts, int64_t(0));
898
899 /* Seek to timestamp. */
900 if (av_seek_frame(anim->pFormatCtx, anim->videoStream, current_pts, AVSEEK_FLAG_BACKWARD) < 0)
901 {
902 break;
903 }
904
905 /* Read first video stream packet. */
906 AVPacket *read_packet = av_packet_alloc();
907 while (av_read_frame(anim->pFormatCtx, read_packet) >= 0) {
908 if (read_packet->stream_index == anim->videoStream) {
909 break;
910 }
911 av_packet_unref(read_packet);
912 }
913
914 /* If this packet contains an I-frame, this could be the frame that we need. */
915 bool is_key_frame = read_packet->flags & AV_PKT_FLAG_KEY;
916 /* We need to check the packet timestamp as the key frame could be for a GOP forward in the
917 * video stream. So if it has a larger timestamp than the frame we want, ignore it.
918 */
919 cur_pts = timestamp_from_pts_or_dts(read_packet->pts, read_packet->dts);
920 av_packet_free(&read_packet);
921
922 if (is_key_frame) {
923 if (cur_pts <= pts_to_search) {
924 /* We found the I-frame we were looking for! */
925 break;
926 }
927 }
928
929 if (cur_pts == prev_pts) {
930 /* We got the same key frame packet twice.
931 * This probably means that we have hit the beginning of the stream. */
932 break;
933 }
934
935 prev_pts = cur_pts;
936 offset++;
937 }
938
939 *requested_pts = current_pts;
940
941 /* Re-seek to timestamp that gave I-frame, so it can be read by decode function. */
942 return av_seek_frame(anim->pFormatCtx, anim->videoStream, current_pts, AVSEEK_FLAG_BACKWARD);
943}
944
945/* Read packet until timestamp matches `anim->cur_packet`, thus recovering internal `anim` stream
946 * position state. */
947static void ffmpeg_seek_recover_stream_position(ImBufAnim *anim)
948{
949 AVPacket *temp_packet = av_packet_alloc();
950 while (ffmpeg_read_video_frame(anim, temp_packet) >= 0) {
951 int64_t current_pts = timestamp_from_pts_or_dts(anim->cur_packet->pts, anim->cur_packet->dts);
952 int64_t temp_pts = timestamp_from_pts_or_dts(temp_packet->pts, temp_packet->dts);
953 av_packet_unref(temp_packet);
954
955 if (current_pts == temp_pts) {
956 break;
957 }
958 }
959 av_packet_free(&temp_packet);
960}
961
962/* Check if seeking and mainly flushing codec buffers is needed. */
963static bool ffmpeg_seek_buffers_need_flushing(ImBufAnim *anim, int position, int64_t seek_pos)
964{
965 /* Get timestamp of packet read after seeking. */
966 AVPacket *temp_packet = av_packet_alloc();
967 ffmpeg_read_video_frame(anim, temp_packet);
968 int64_t gop_pts = timestamp_from_pts_or_dts(temp_packet->pts, temp_packet->dts);
969 av_packet_unref(temp_packet);
970 av_packet_free(&temp_packet);
971
972 /* Seeking gives packet, that is currently read. No seeking was necessary, so buffers don't have
973 * to be flushed. */
974 if (gop_pts == timestamp_from_pts_or_dts(anim->cur_packet->pts, anim->cur_packet->dts)) {
975 return false;
976 }
977
978 /* Packet after seeking is same key frame as current, and further in time. No seeking was
979 * necessary, so buffers don't have to be flushed. But stream position has to be recovered. */
980 if (gop_pts == anim->cur_key_frame_pts && position > anim->cur_position) {
981 ffmpeg_seek_recover_stream_position(anim);
982 return false;
983 }
984
985 /* Seeking was necessary, but we have read packets. Therefore we must seek again. */
986 av_seek_frame(anim->pFormatCtx, anim->videoStream, seek_pos, AVSEEK_FLAG_BACKWARD);
987 anim->cur_key_frame_pts = gop_pts;
988 return true;
989}
990
991/* Seek to last necessary key frame. */
992static int ffmpeg_seek_to_key_frame(ImBufAnim *anim,
993 int position,
994 ImBufAnimIndex *tc_index,
995 int64_t pts_to_search)
996{
997 int64_t seek_pos;
998 int ret;
999
1000 if (tc_index) {
1001 /* We can use timestamps generated from our indexer to seek. */
1002 int new_frame_index = IMB_indexer_get_frame_index(tc_index, position);
1003 int old_frame_index = IMB_indexer_get_frame_index(tc_index, anim->cur_position);
1004
1005 if (IMB_indexer_can_scan(tc_index, old_frame_index, new_frame_index)) {
1006 /* No need to seek, return early. */
1007 return 0;
1008 }
1009 uint64_t pts;
1010 uint64_t dts;
1011
1012 seek_pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index);
1013 pts = IMB_indexer_get_seek_pos_pts(tc_index, new_frame_index);
1014 dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index);
1015
1016 anim->cur_key_frame_pts = timestamp_from_pts_or_dts(pts, dts);
1017
1018 av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek seek_pos = %" PRId64 "\n", seek_pos);
1019 av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pts = %" PRIu64 "\n", pts);
1020 av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %" PRIu64 "\n", dts);
1021
1022 if (ffmpeg_seek_by_byte(anim->pFormatCtx)) {
1023 av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE seek_pos\n");
1024
1025 ret = av_seek_frame(anim->pFormatCtx, -1, seek_pos, AVSEEK_FLAG_BYTE);
1026 }
1027 else {
1028 av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using PTS seek_pos\n");
1029 ret = av_seek_frame(
1030 anim->pFormatCtx, anim->videoStream, anim->cur_key_frame_pts, AVSEEK_FLAG_BACKWARD);
1031 }
1032 }
1033 else {
1034 /* We have to manually seek with ffmpeg to get to the key frame we want to start decoding from.
1035 */
1036 seek_pos = ffmpeg_get_seek_pts(anim, pts_to_search);
1037 av_log(
1038 anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek seek_pos = %" PRId64 "\n", seek_pos);
1039
1040 AVFormatContext *format_ctx = anim->pFormatCtx;
1041
1042 /* This used to check if the codec implemented "read_seek" or "read_seek2". However this is
1043 * now hidden from us in ffmpeg 7.0. While not as accurate, usually the AVFMT_TS_DISCONT is
1044 * set for formats where we need to apply the seek workaround to (like in mpegts).
1045 */
1046 if (!(format_ctx->iformat->flags & AVFMT_TS_DISCONT)) {
1047 ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, seek_pos, AVSEEK_FLAG_BACKWARD);
1048 }
1049 else {
1050 ret = ffmpeg_generic_seek_workaround(anim, &seek_pos, pts_to_search);
1051 av_log(anim->pFormatCtx,
1052 AV_LOG_DEBUG,
1053 "Adjusted final seek seek_pos = %" PRId64 "\n",
1054 seek_pos);
1055 }
1056
1057 if (ret <= 0 && !ffmpeg_seek_buffers_need_flushing(anim, position, seek_pos)) {
1058 return 0;
1059 }
1060 }
1061
1062 if (ret < 0) {
1063 av_log(anim->pFormatCtx,
1064 AV_LOG_ERROR,
1065 "FETCH: "
1066 "error while seeking to DTS = %" PRId64 " (frameno = %d, PTS = %" PRId64
1067 "): errcode = %d\n",
1068 seek_pos,
1069 position,
1070 pts_to_search,
1071 ret);
1072 }
1073 /* Flush the internal buffers of ffmpeg. This needs to be done after seeking to avoid decoding
1074 * errors. */
1075 avcodec_flush_buffers(anim->pCodecCtx);
1076 ffmpeg_double_buffer_backup_frame_clear(anim);
1077
1078 anim->cur_pts = -1;
1079
1080 if (anim->cur_packet->stream_index == anim->videoStream) {
1081 av_packet_unref(anim->cur_packet);
1082 anim->cur_packet->stream_index = -1;
1083 }
1084
1085 return ret;
1086}
1087
1088static bool ffmpeg_must_decode(ImBufAnim *anim, int position)
1089{
1090 return !anim->pFrame_complete || anim->cur_position != position;
1091}
1092
1093static bool ffmpeg_must_seek(ImBufAnim *anim, int position)
1094{
1095 bool must_seek = position != anim->cur_position + 1 || ffmpeg_is_first_frame_decode(anim);
1096 anim->seek_before_decode = must_seek;
1097 return must_seek;
1098}
1099
1100static ImBuf *ffmpeg_fetchibuf(ImBufAnim *anim, int position, IMB_Timecode_Type tc)
1101{
1102 if (anim == nullptr) {
1103 return nullptr;
1104 }
1105
1106 av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: seek_pos=%d\n", position);
1107
1108 ImBufAnimIndex *tc_index = IMB_anim_open_index(anim, tc);
1109 int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position);
1110 AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
1111 double frame_rate = av_q2d(v_st->r_frame_rate);
1112 double pts_time_base = av_q2d(v_st->time_base);
1113 int64_t start_pts = v_st->start_time;
1114
1115 av_log(anim->pFormatCtx,
1116 AV_LOG_DEBUG,
1117 "FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, start_pts=%" PRId64
1118 ")\n",
1119 int64_t(pts_to_search),
1120 pts_time_base,
1121 frame_rate,
1122 start_pts);
1123
1124 if (ffmpeg_must_decode(anim, position)) {
1125 if (ffmpeg_must_seek(anim, position)) {
1126 ffmpeg_seek_to_key_frame(anim, position, tc_index, pts_to_search);
1127 }
1128
1129 ffmpeg_decode_video_frame_scan(anim, pts_to_search);
1130 }
1131
1132 /* Update resolution as it can change per-frame with WebM. See #100741 & #100081. */
1133 anim->x = anim->pCodecCtx->width;
1134 anim->y = anim->pCodecCtx->height;
1135
1136 const AVPixFmtDescriptor *pix_fmt_descriptor = av_pix_fmt_desc_get(anim->pCodecCtx->pix_fmt);
1137
1138 int planes = R_IMF_PLANES_RGBA;
1139 if ((pix_fmt_descriptor->flags & AV_PIX_FMT_FLAG_ALPHA) == 0) {
1140 planes = R_IMF_PLANES_RGB;
1141 }
1142
1143 ImBuf *cur_frame_final = IMB_allocImBuf(anim->x, anim->y, planes, 0);
1144
1145 /* Allocate the storage explicitly to ensure the memory is aligned. */
1146 const size_t align = ffmpeg_get_buffer_alignment();
1147 uint8_t *buffer_data = static_cast<uint8_t *>(
1148 MEM_mallocN_aligned(size_t(4) * anim->x * anim->y, align, "ffmpeg ibuf"));
1149 IMB_assign_byte_buffer(cur_frame_final, buffer_data, IB_TAKE_OWNERSHIP);
1150
1152
1153 AVFrame *final_frame = ffmpeg_frame_by_pts_get(anim, pts_to_search);
1154 if (final_frame == nullptr) {
1155 /* No valid frame was decoded for requested PTS, fall back on most recent decoded frame, even
1156 * if it is incorrect. */
1157 final_frame = ffmpeg_double_buffer_frame_fallback_get(anim);
1158 }
1159
1160 /* Even with the fallback from above it is possible that the current decode frame is nullptr. In
1161 * this case skip post-processing and return current image buffer. */
1162 if (final_frame != nullptr) {
1163 ffmpeg_postprocess(anim, final_frame, cur_frame_final);
1164 }
1165
1166 anim->cur_position = position;
1167
1168 return cur_frame_final;
1169}
1170
1171static void free_anim_ffmpeg(ImBufAnim *anim)
1172{
1173 if (anim == nullptr) {
1174 return;
1175 }
1176
1177 if (anim->pCodecCtx) {
1178 avcodec_free_context(&anim->pCodecCtx);
1179 avformat_close_input(&anim->pFormatCtx);
1180 av_packet_free(&anim->cur_packet);
1181
1182 av_frame_free(&anim->pFrame);
1183 av_frame_free(&anim->pFrame_backup);
1184 av_frame_free(&anim->pFrameRGB);
1185 if (anim->pFrameDeinterlaced->data[0] != nullptr) {
1186 MEM_freeN(anim->pFrameDeinterlaced->data[0]);
1187 }
1188 av_frame_free(&anim->pFrameDeinterlaced);
1189 BKE_ffmpeg_sws_release_context(anim->img_convert_ctx);
1190 }
1191 anim->duration_in_frames = 0;
1192}
1193
1194#endif
1195
1200static bool anim_getnew(ImBufAnim *anim)
1201{
1202 if (anim == nullptr) {
1203 /* Nothing to initialize. */
1204 return false;
1205 }
1206
1208
1209#ifdef WITH_FFMPEG
1210 free_anim_ffmpeg(anim);
1211 if (startffmpeg(anim)) {
1213 return false;
1214 }
1215#endif
1217 return true;
1218}
1219
1221{
1222 ImBuf *ibuf = nullptr;
1223 int position = 0;
1224
1226 if (ibuf) {
1227 IMB_freeImBuf(ibuf);
1228 position = anim->duration_in_frames / 2;
1229 ibuf = IMB_anim_absolute(anim, position, IMB_TC_NONE, IMB_PROXY_NONE);
1230
1231 char value[128];
1233 SNPRINTF(value, "%i", anim->x);
1234 IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Width", value);
1235 SNPRINTF(value, "%i", anim->y);
1236 IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Height", value);
1237 SNPRINTF(value, "%i", anim->duration_in_frames);
1238 IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Frames", value);
1239
1240#ifdef WITH_FFMPEG
1241 if (anim->pFormatCtx) {
1242 AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
1243 AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, nullptr);
1244 if (frame_rate.num != 0) {
1245 double duration = anim->duration_in_frames / av_q2d(frame_rate);
1246 SNPRINTF(value, "%g", av_q2d(frame_rate));
1247 IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::FPS", value);
1248 SNPRINTF(value, "%g", duration);
1249 IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Duration", value);
1250 IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Codec", anim->pCodec->long_name);
1251 }
1252 }
1253#endif
1254 }
1255 return ibuf;
1256}
1257
1259 int position,
1261 IMB_Proxy_Size preview_size)
1262{
1263 ImBuf *ibuf = nullptr;
1264 if (anim == nullptr) {
1265 return nullptr;
1266 }
1267
1268 if (preview_size == IMB_PROXY_NONE) {
1270 if (!anim_getnew(anim)) {
1271 return nullptr;
1272 }
1273 }
1274
1275 if (position < 0) {
1276 return nullptr;
1277 }
1278 if (position >= anim->duration_in_frames) {
1279 return nullptr;
1280 }
1281 }
1282 else {
1283 ImBufAnim *proxy = IMB_anim_open_proxy(anim, preview_size);
1284
1285 if (proxy) {
1286 position = IMB_anim_index_get_frame_index(anim, tc, position);
1287
1288 return IMB_anim_absolute(proxy, position, IMB_TC_NONE, IMB_PROXY_NONE);
1289 }
1290 }
1291
1292#ifdef WITH_FFMPEG
1293 if (anim->state == ImBufAnim::State::Valid) {
1294 ibuf = ffmpeg_fetchibuf(anim, position, tc);
1295 if (ibuf) {
1296 anim->cur_position = position;
1297 }
1298 }
1299#endif
1300
1301 if (ibuf) {
1302 SNPRINTF(ibuf->filepath, "%s.%04d", anim->filepath, anim->cur_position + 1);
1303 }
1304 return ibuf;
1305}
1306
1307/***/
1308
1310{
1311 ImBufAnimIndex *idx;
1312 if (tc == IMB_TC_NONE) {
1313 return anim->duration_in_frames;
1314 }
1315
1316 idx = IMB_anim_open_index(anim, tc);
1317 if (!idx) {
1318 return anim->duration_in_frames;
1319 }
1320
1321 return IMB_indexer_get_duration(idx);
1322}
1323
1325{
1326 return anim->start_offset;
1327}
1328
1330 bool no_av_base,
1331 short *r_frs_sec,
1332 float *r_frs_sec_base)
1333{
1334 double frs_sec_base_double;
1335 if (anim->frs_sec) {
1336 if (anim->frs_sec > SHRT_MAX) {
1337 /* We cannot store original rational in our short/float format,
1338 * we need to approximate it as best as we can... */
1339 *r_frs_sec = SHRT_MAX;
1340 frs_sec_base_double = anim->frs_sec_base * double(SHRT_MAX) / double(anim->frs_sec);
1341 }
1342 else {
1343 *r_frs_sec = anim->frs_sec;
1344 frs_sec_base_double = anim->frs_sec_base;
1345 }
1346#ifdef WITH_FFMPEG
1347 if (no_av_base) {
1348 *r_frs_sec_base = float(frs_sec_base_double / AV_TIME_BASE);
1349 }
1350 else {
1351 *r_frs_sec_base = float(frs_sec_base_double);
1352 }
1353#else
1354 UNUSED_VARS(no_av_base);
1355 *r_frs_sec_base = float(frs_sec_base_double);
1356#endif
1357 BLI_assert(*r_frs_sec > 0);
1358 BLI_assert(*r_frs_sec_base > 0.0f);
1359
1360 return true;
1361 }
1362 return false;
1363}
1364
1366{
1367 return anim->x;
1368}
1369
1371{
1372 return anim->y;
1373}
#define BLI_assert(a)
Definition BLI_assert.h:50
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
int char char int int BLI_strncasecmp(const char *s1, const char *s2, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
int char char int BLI_strcasecmp(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
int BLI_system_thread_count(void)
Definition threads.cc:253
#define UNUSED_VARS(...)
typedef double(DMatrix)[4][4]
@ R_IMF_PLANES_RGB
@ R_IMF_PLANES_RGBA
@ COLOR_ROLE_DEFAULT_BYTE
int IMB_anim_index_get_frame_index(ImBufAnim *anim, IMB_Timecode_Type tc, int position)
Definition indexer.cc:1402
#define IM_MAX_SPACE
Definition IMB_imbuf.hh:49
void IMB_filtery(ImBuf *ibuf)
Definition filter.cc:65
void IMB_assign_byte_buffer(ImBuf *ibuf, uint8_t *buffer_data, ImBufOwnership ownership)
IMB_Proxy_Size
@ IMB_PROXY_NONE
IMB_Timecode_Type
@ IMB_TC_NONE
Contains defines and structs used throughout the imbuf module.
@ IB_TAKE_OWNERSHIP
@ IB_animdeinterlace
uint64_t IMB_indexer_get_seek_pos(ImBufAnimIndex *idx, int frame_index)
Definition indexer.cc:229
int IMB_indexer_get_frame_index(ImBufAnimIndex *idx, int frameno)
Definition indexer.cc:264
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
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
uint64_t IMB_indexer_get_pts(ImBufAnimIndex *idx, int frame_index)
Definition indexer.cc:296
int IMB_indexer_can_scan(ImBufAnimIndex *idx, int old_frame_index, int new_frame_index)
Definition indexer.cc:315
void IMB_free_indices(ImBufAnim *anim)
Definition indexer.cc:1312
void IMB_metadata_set_field(IDProperty *metadata, const char *key, const char *value)
Definition metadata.cc:69
void IMB_metadata_ensure(IDProperty **metadata)
Definition metadata.cc:24
void IMB_metadata_free(IDProperty *metadata)
Definition metadata.cc:33
Read Guarded memory(de)allocation.
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its saturation
bool IMB_anim_can_produce_frames(const ImBufAnim *anim)
int IMB_anim_get_image_height(ImBufAnim *anim)
ImBuf * IMB_anim_absolute(ImBufAnim *anim, int position, IMB_Timecode_Type tc, IMB_Proxy_Size preview_size)
double IMD_anim_get_offset(ImBufAnim *anim)
static bool anim_getnew(ImBufAnim *anim)
bool IMB_anim_get_fps(const ImBufAnim *anim, bool no_av_base, short *r_frs_sec, float *r_frs_sec_base)
int IMB_anim_get_image_width(ImBufAnim *anim)
ImBuf * IMB_anim_previewframe(ImBufAnim *anim)
void IMB_free_anim(ImBufAnim *anim)
Definition anim_movie.cc:60
IDProperty * IMB_anim_load_metadata(ImBufAnim *anim)
Definition anim_movie.cc:94
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
void IMB_close_anim_proxies(ImBufAnim *anim)
Definition anim_movie.cc:85
int IMB_anim_get_duration(ImBufAnim *anim, IMB_Timecode_Type tc)
void IMB_suffix_anim(ImBufAnim *anim, const char *suffix)
void colorspace_set_default_role(char *colorspace, int size, int role)
ColorSpace * colormanage_colorspace_get_named(const char *name)
#define printf
int len
draw_view in_light_buf[] float
#define str(s)
FFMPEG_INLINE int64_t av_get_frame_duration_in_pts_units(const AVFrame *picture)
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 int av_image_deinterlace(AVFrame *dst, const AVFrame *src, enum AVPixelFormat pix_fmt, int width, int height)
struct ImBuf * IMB_allocImBuf(unsigned int, unsigned int, unsigned char, unsigned int)
void IMB_freeImBuf(ImBuf *)
#define PRIu64
Definition inttypes.h:132
#define PRId64
Definition inttypes.h:78
void * MEM_mallocN_aligned(size_t len, size_t alignment, const char *str)
Definition mallocn.cc:110
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
T round(const T &a)
return ret
__int64 int64_t
Definition stdint.h:89
unsigned char uint8_t
Definition stdint.h:78
unsigned __int64 uint64_t
Definition stdint.h:90
int cur_position
Definition IMB_anim.hh:31
double start_offset
Definition IMB_anim.hh:35
int frs_sec
Definition IMB_anim.hh:33
State state
Definition IMB_anim.hh:30
int duration_in_frames
Definition IMB_anim.hh:32
int streamindex
Definition IMB_anim.hh:41
char suffix[64]
Definition IMB_anim.hh:74
double frs_sec_base
Definition IMB_anim.hh:34
IDProperty * metadata
Definition IMB_anim.hh:76
char filepath[1024]
Definition IMB_anim.hh:39
int ib_flags
Definition IMB_anim.hh:29
char colorspace[64]
Definition IMB_anim.hh:73
ColorSpace * colorspace
char filepath[IMB_FILEPATH_SIZE]
ImBufByteBuffer byte_buffer
IDProperty * metadata