Blender V4.5
movie_read.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 * SPDX-FileCopyrightText: 2024-2025 Blender Authors
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later */
5
9
10#include <algorithm>
11#include <cctype>
12#include <climits>
13#include <cmath>
14#include <cstdio>
15#include <cstdlib>
16#include <sys/types.h>
17
18#include "BLI_path_utils.hh"
19#include "BLI_string.h"
20#include "BLI_task.hh"
21#include "BLI_threads.h"
22#include "BLI_utildefines.h"
23
24#include "DNA_scene_types.h"
25
26#include "MEM_guardedalloc.h"
27
29#include "IMB_imbuf.hh"
30#include "IMB_imbuf_types.hh"
31
32#include "MOV_read.hh"
33
34#include "IMB_metadata.hh"
36#include "movie_read.hh"
37
38#ifdef WITH_FFMPEG
39# include "ffmpeg_swscale.hh"
40# include "movie_util.hh"
41
42extern "C" {
43# include <libavcodec/avcodec.h>
44# include <libavformat/avformat.h>
45# include <libavutil/imgutils.h>
46# include <libavutil/rational.h>
47# include <libswscale/swscale.h>
48
49# include "ffmpeg_compat.h"
50}
51
52#endif /* WITH_FFMPEG */
53
54#ifdef WITH_FFMPEG
55static void free_anim_ffmpeg(MovieReader *anim);
56#endif
57
59{
60 if (anim == nullptr) {
61 return;
62 }
63
64#ifdef WITH_FFMPEG
65 free_anim_ffmpeg(anim);
66#endif
69
70 MEM_delete(anim);
71}
72
73void MOV_get_filename(const MovieReader *anim, char *filename, int filename_maxncpy)
74{
75 BLI_path_split_file_part(anim->filepath, filename, filename_maxncpy);
76}
77
79{
80 if (anim->state == MovieReader::State::Valid) {
81#ifdef WITH_FFMPEG
82 BLI_assert(anim->pFormatCtx != nullptr);
83 av_log(anim->pFormatCtx, AV_LOG_DEBUG, "METADATA FETCH\n");
84
85 AVDictionaryEntry *entry = nullptr;
86 while (true) {
87 entry = av_dict_get(anim->pFormatCtx->metadata, "", entry, AV_DICT_IGNORE_SUFFIX);
88 if (entry == nullptr) {
89 break;
90 }
91
92 /* Delay creation of the property group until there is actual metadata to put in there. */
94 IMB_metadata_set_field(anim->metadata, entry->key, entry->value);
95 }
96#endif
97 }
98 return anim->metadata;
99}
100
101MovieReader *MOV_open_file(const char *filepath,
102 int ib_flags,
103 int streamindex,
104 char colorspace[IM_MAX_SPACE])
105{
106 MovieReader *anim;
107
108 BLI_assert(!BLI_path_is_rel(filepath));
109
110 anim = MEM_new<MovieReader>("anim struct");
111 if (anim != nullptr) {
112 /* Initialize colorspace to default if not yet set. */
113 const char *default_colorspace = IMB_colormanagement_role_colorspace_name_get(
115 if (colorspace && colorspace[0] == '\0') {
116 BLI_strncpy(colorspace, default_colorspace, IM_MAX_SPACE);
117 }
118
119 /* Inherit colorspace from argument if provided. */
120 STRNCPY(anim->colorspace, colorspace ? colorspace : default_colorspace);
121
122 STRNCPY(anim->filepath, filepath);
123 anim->ib_flags = ib_flags;
124 anim->streamindex = streamindex;
125 }
126 return anim;
127}
128
130{
131#if !defined(WITH_FFMPEG)
132 UNUSED_VARS(anim);
133#endif
134
135#ifdef WITH_FFMPEG
136 if (anim->pCodecCtx != nullptr) {
137 return true;
138 }
139#endif
140 return false;
141}
142
143void MOV_set_multiview_suffix(MovieReader *anim, const char *suffix)
144{
145 STRNCPY(anim->suffix, suffix);
146}
147
148#ifdef WITH_FFMPEG
149
150static double ffmpeg_stream_start_time_get(const AVStream *stream)
151{
152 if (stream->start_time == AV_NOPTS_VALUE) {
153 return 0.0;
154 }
155
156 return stream->start_time * av_q2d(stream->time_base);
157}
158
159static int ffmpeg_container_frame_count_get(const AVFormatContext *pFormatCtx,
160 const AVStream *video_stream,
161 const double frame_rate)
162{
163 /* Find audio stream to guess the duration of the video.
164 * Sometimes the audio AND the video stream have a start offset.
165 * The difference between these is the offset we want to use to
166 * calculate the video duration.
167 */
168 const double video_start = ffmpeg_stream_start_time_get(video_stream);
169 double audio_start = 0;
170
171 for (int i = 0; i < pFormatCtx->nb_streams; i++) {
172 if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
173 AVStream *audio_stream = pFormatCtx->streams[i];
174 audio_start = ffmpeg_stream_start_time_get(audio_stream);
175 break;
176 }
177 }
178
179 double stream_dur;
180
181 if (video_start > audio_start) {
182 stream_dur = double(pFormatCtx->duration) / AV_TIME_BASE - (video_start - audio_start);
183 }
184 else {
185 /* The video stream starts before or at the same time as the audio stream!
186 * We have to assume that the video stream is as long as the full pFormatCtx->duration.
187 */
188 stream_dur = double(pFormatCtx->duration) / AV_TIME_BASE;
189 }
190
191 return lround(stream_dur * frame_rate);
192}
193
194static int ffmpeg_frame_count_get(const AVFormatContext *pFormatCtx,
195 const AVStream *video_stream,
196 const double frame_rate)
197{
198 /* Use stream duration to determine frame count. */
199 if (video_stream->duration != AV_NOPTS_VALUE) {
200 const double stream_dur = video_stream->duration * av_q2d(video_stream->time_base);
201 return lround(stream_dur * frame_rate);
202 }
203
204 /* Fall back to manually estimating the video stream duration.
205 * This is because the video stream duration can be shorter than the `pFormatCtx->duration`.
206 */
207 if (pFormatCtx->duration != AV_NOPTS_VALUE) {
208 return ffmpeg_container_frame_count_get(pFormatCtx, video_stream, frame_rate);
209 }
210
211 /* Read frame count from the stream if we can. Note, that this value can not be trusted. */
212 if (video_stream->nb_frames != 0) {
213 return video_stream->nb_frames;
214 }
215
216 /* The duration has not been set, happens for single JPEG2000 images.
217 * NOTE: Leave the duration zeroed, although it could set to 1 so the file is recognized
218 * as a movie with 1 frame, leave as-is since image loading code-paths are preferred
219 * in this case. The following assertion should be valid in this case. */
220 BLI_assert(pFormatCtx->duration == AV_NOPTS_VALUE);
221 return 0;
222}
223
224static int calc_pix_fmt_max_component_bits(AVPixelFormat fmt)
225{
226 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
227 if (desc == nullptr) {
228 return 0;
229 }
230 int bits = 0;
231 for (int i = 0; i < desc->nb_components; i++) {
232 bits = max_ii(bits, desc->comp[i].depth);
233 }
234 return bits;
235}
236
237static AVFormatContext *init_format_context(const char *filepath,
238 int video_stream_index,
239 int &r_stream_index,
240 const AVCodec *forced_video_decoder)
241{
242 AVFormatContext *format_ctx = nullptr;
243 if (forced_video_decoder != nullptr) {
244 format_ctx = avformat_alloc_context();
245 format_ctx->video_codec_id = forced_video_decoder->id;
246 format_ctx->video_codec = forced_video_decoder;
247 }
248
249 if (avformat_open_input(&format_ctx, filepath, nullptr, nullptr) != 0) {
250 return nullptr;
251 }
252
253 if (avformat_find_stream_info(format_ctx, nullptr) < 0) {
254 avformat_close_input(&format_ctx);
255 return nullptr;
256 }
257
258 av_dump_format(format_ctx, 0, filepath, 0);
259
260 /* Find the video stream */
261 r_stream_index = -1;
262 for (int i = 0; i < format_ctx->nb_streams; i++) {
263 if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
264 if (video_stream_index > 0) {
265 video_stream_index--;
266 continue;
267 }
268 r_stream_index = i;
269 break;
270 }
271 }
272
273 if (r_stream_index == -1) {
274 avformat_close_input(&format_ctx);
275 return nullptr;
276 }
277
278 return format_ctx;
279}
280
281static AVFormatContext *init_format_context_vpx_workarounds(const char *filepath,
282 int video_stream_index,
283 int &r_stream_index,
284 const AVCodec *&r_codec)
285{
286 AVFormatContext *format_ctx = init_format_context(
287 filepath, video_stream_index, r_stream_index, nullptr);
288 if (format_ctx == nullptr) {
289 return nullptr;
290 }
291
292 /* By default FFMPEG uses built-in VP8/VP9 decoders, however those do not detect
293 * alpha channel (see FFMPEG issue #8344 https://trac.ffmpeg.org/ticket/8344).
294 * The trick for VP8/VP9 is to explicitly force use of LIBVPX decoder.
295 * Only do this where alpha_mode=1 metadata is set. Note that in order to work,
296 * the previously initialized format context must be closed and a fresh one
297 * with explicitly requested codec must be created. */
298 r_codec = nullptr;
299 const AVStream *video_stream = format_ctx->streams[r_stream_index];
300 if (ELEM(video_stream->codecpar->codec_id, AV_CODEC_ID_VP8, AV_CODEC_ID_VP9)) {
301 AVDictionaryEntry *tag = nullptr;
302 tag = av_dict_get(video_stream->metadata, "alpha_mode", tag, AV_DICT_IGNORE_SUFFIX);
303 if (tag && STREQ(tag->value, "1")) {
304 r_codec = avcodec_find_decoder_by_name(
305 video_stream->codecpar->codec_id == AV_CODEC_ID_VP8 ? "libvpx" : "libvpx-vp9");
306 if (r_codec != nullptr) {
307 avformat_close_input(&format_ctx);
308 format_ctx = avformat_alloc_context();
309 format_ctx = init_format_context(filepath, video_stream_index, r_stream_index, r_codec);
310 if (format_ctx == nullptr) {
311 return nullptr;
312 }
313 }
314 }
315 }
316
317 if (r_codec == nullptr) {
318 /* Use default decoder. */
319 r_codec = avcodec_find_decoder(video_stream->codecpar->codec_id);
320 }
321
322 return format_ctx;
323}
324
325static int startffmpeg(MovieReader *anim)
326{
327 if (anim == nullptr) {
328 return -1;
329 }
330
331 int video_stream_index;
332 const AVCodec *pCodec = nullptr;
333 AVFormatContext *pFormatCtx = init_format_context_vpx_workarounds(
334 anim->filepath, anim->streamindex, video_stream_index, pCodec);
335 if (pFormatCtx == nullptr || pCodec == nullptr) {
336 avformat_close_input(&pFormatCtx);
337 return -1;
338 }
339
340 AVCodecContext *pCodecCtx = avcodec_alloc_context3(nullptr);
341 AVStream *video_stream = pFormatCtx->streams[video_stream_index];
342 avcodec_parameters_to_context(pCodecCtx, video_stream->codecpar);
343 pCodecCtx->workaround_bugs = FF_BUG_AUTODETECT;
344
345 if (pCodec->capabilities & AV_CODEC_CAP_OTHER_THREADS) {
346 pCodecCtx->thread_count = 0;
347 }
348 else {
349 pCodecCtx->thread_count = BLI_system_thread_count();
350 }
351
352 if (pCodec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
353 pCodecCtx->thread_type = FF_THREAD_FRAME;
354 }
355 else if (pCodec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
356 pCodecCtx->thread_type = FF_THREAD_SLICE;
357 }
358
359 if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0) {
360 avformat_close_input(&pFormatCtx);
361 return -1;
362 }
363 if (pCodecCtx->pix_fmt == AV_PIX_FMT_NONE) {
364 avcodec_free_context(&anim->pCodecCtx);
365 avformat_close_input(&pFormatCtx);
366 return -1;
367 }
368
369 /* Check if we need the "never seek, only decode one frame" ffmpeg bug workaround. */
370 const bool is_ogg_container = STREQ(pFormatCtx->iformat->name, "ogg");
371 const bool is_non_ogg_video = video_stream->codecpar->codec_id != AV_CODEC_ID_THEORA;
372 const bool is_video_thumbnail = (video_stream->disposition & AV_DISPOSITION_ATTACHED_PIC) != 0;
373 anim->never_seek_decode_one_frame = is_ogg_container && is_non_ogg_video && is_video_thumbnail;
374
375 anim->frame_rate = av_guess_frame_rate(pFormatCtx, video_stream, nullptr);
376 if (anim->never_seek_decode_one_frame) {
377 /* Files that need this workaround have nonsensical frame rates too, resulting
378 * in "millions of frames" if done through regular math. Treat frame-rate as 24/1 instead. */
379 anim->frame_rate = {24, 1};
380 }
381 int frs_num = anim->frame_rate.num;
382 double frs_den = anim->frame_rate.den;
383
384 frs_den *= AV_TIME_BASE;
385
386 while (frs_num % 10 == 0 && frs_den >= 2.0 && frs_num > 10) {
387 frs_num /= 10;
388 frs_den /= 10;
389 }
390
391 anim->frs_sec = frs_num;
392 anim->frs_sec_base = frs_den / AV_TIME_BASE;
393 /* Save the relative start time for the video. IE the start time in relation to where playback
394 * starts. */
395 anim->start_offset = ffmpeg_stream_start_time_get(video_stream);
396 anim->duration_in_frames = ffmpeg_frame_count_get(
397 pFormatCtx, video_stream, av_q2d(anim->frame_rate));
398
399 anim->x = pCodecCtx->width;
400 anim->y = pCodecCtx->height;
401 anim->video_rotation = ffmpeg_get_video_rotation(video_stream);
402
403 /* Decode >8bit videos into floating point image. */
404 anim->is_float = calc_pix_fmt_max_component_bits(pCodecCtx->pix_fmt) > 8;
405
406 anim->pFormatCtx = pFormatCtx;
407 anim->pCodecCtx = pCodecCtx;
408 anim->pCodec = pCodec;
409 anim->videoStream = video_stream_index;
410
411 anim->cur_position = 0;
412 anim->cur_pts = -1;
413 anim->cur_key_frame_pts = -1;
414 anim->cur_packet = av_packet_alloc();
415 anim->cur_packet->stream_index = -1;
416
417 anim->pFrame = av_frame_alloc();
418 anim->pFrame_backup = av_frame_alloc();
419 anim->pFrame_backup_complete = false;
420 anim->pFrame_complete = false;
421 anim->pFrameDeinterlaced = av_frame_alloc();
422 anim->pFrameRGB = av_frame_alloc();
423 /* Ideally we'd use AV_PIX_FMT_RGBAF32LE for floats, but currently (ffmpeg 6.1)
424 * swscale does not support that as destination. So using AV_PIX_FMT_GBRAPF32LE
425 * with manual interleaving to RGBA floats. */
426 anim->pFrameRGB->format = anim->is_float ? AV_PIX_FMT_GBRAPF32LE : AV_PIX_FMT_RGBA;
427 anim->pFrameRGB->width = anim->x;
428 anim->pFrameRGB->height = anim->y;
429
430 const size_t align = ffmpeg_get_buffer_alignment();
431 if (av_frame_get_buffer(anim->pFrameRGB, align) < 0) {
432 fprintf(stderr, "Could not allocate frame data.\n");
433 avcodec_free_context(&anim->pCodecCtx);
434 avformat_close_input(&anim->pFormatCtx);
435 av_packet_free(&anim->cur_packet);
436 av_frame_free(&anim->pFrameRGB);
437 av_frame_free(&anim->pFrameDeinterlaced);
438 av_frame_free(&anim->pFrame);
439 av_frame_free(&anim->pFrame_backup);
440 anim->pCodecCtx = nullptr;
441 return -1;
442 }
443
444 if (anim->ib_flags & IB_animdeinterlace) {
445 anim->pFrameDeinterlaced->format = anim->pCodecCtx->pix_fmt;
446 anim->pFrameDeinterlaced->width = anim->pCodecCtx->width;
447 anim->pFrameDeinterlaced->height = anim->pCodecCtx->height;
448 av_image_fill_arrays(
449 anim->pFrameDeinterlaced->data,
450 anim->pFrameDeinterlaced->linesize,
452 av_image_get_buffer_size(
453 anim->pCodecCtx->pix_fmt, anim->pCodecCtx->width, anim->pCodecCtx->height, 1),
454 "ffmpeg deinterlace"),
455 anim->pCodecCtx->pix_fmt,
456 anim->pCodecCtx->width,
457 anim->pCodecCtx->height,
458 1);
459 }
460
461 /* Use full_chroma_int + accurate_rnd YUV->RGB conversion flags. Otherwise
462 * the conversion is not fully accurate and introduces some banding and color
463 * shifts, particularly in dark regions. See issue #111703 or upstream
464 * ffmpeg ticket https://trac.ffmpeg.org/ticket/1582 */
465 anim->img_convert_ctx = ffmpeg_sws_get_context(anim->x,
466 anim->y,
467 anim->pCodecCtx->pix_fmt,
468 anim->pCodecCtx->color_range == AVCOL_RANGE_JPEG,
469 anim->pCodecCtx->colorspace,
470 anim->x,
471 anim->y,
472 anim->pFrameRGB->format,
473 false,
474 -1,
475 SWS_POINT | SWS_FULL_CHR_H_INT |
476 SWS_ACCURATE_RND);
477
478 if (!anim->img_convert_ctx) {
479 fprintf(stderr,
480 "ffmpeg: swscale can't transform from pixel format %s to %s (%s)\n",
481 av_get_pix_fmt_name(anim->pCodecCtx->pix_fmt),
482 av_get_pix_fmt_name((AVPixelFormat)anim->pFrameRGB->format),
483 anim->filepath);
484 avcodec_free_context(&anim->pCodecCtx);
485 avformat_close_input(&anim->pFormatCtx);
486 av_packet_free(&anim->cur_packet);
487 av_frame_free(&anim->pFrameRGB);
488 av_frame_free(&anim->pFrameDeinterlaced);
489 av_frame_free(&anim->pFrame);
490 av_frame_free(&anim->pFrame_backup);
491 anim->pCodecCtx = nullptr;
492 return -1;
493 }
494
495 return 0;
496}
497
498static double ffmpeg_steps_per_frame_get(const MovieReader *anim)
499{
500 const AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
501 const AVRational time_base = v_st->time_base;
502 return av_q2d(av_inv_q(av_mul_q(anim->frame_rate, time_base)));
503}
504
505/* Store backup frame.
506 * With VFR movies, if PTS is not matched perfectly, scanning continues to look for next PTS.
507 * It is likely to overshoot and scanning stops. Having previous frame backed up, it is possible
508 * to use it when overshoot happens.
509 */
510static void ffmpeg_double_buffer_backup_frame_store(MovieReader *anim, int64_t pts_to_search)
511{
512 /* `anim->pFrame` is beyond `pts_to_search`. Don't store it. */
513 if (anim->pFrame_backup_complete && anim->cur_pts >= pts_to_search) {
514 return;
515 }
516 if (!anim->pFrame_complete) {
517 return;
518 }
519
520 if (anim->pFrame_backup_complete) {
521 av_frame_unref(anim->pFrame_backup);
522 }
523
524 av_frame_move_ref(anim->pFrame_backup, anim->pFrame);
525 anim->pFrame_backup_complete = true;
526}
527
528/* Free stored backup frame. */
529static void ffmpeg_double_buffer_backup_frame_clear(MovieReader *anim)
530{
531 if (anim->pFrame_backup_complete) {
532 av_frame_unref(anim->pFrame_backup);
533 }
534 anim->pFrame_backup_complete = false;
535}
536
537/* Return recently decoded frame. If it does not exist, return frame from backup buffer. */
538static AVFrame *ffmpeg_double_buffer_frame_fallback_get(MovieReader *anim)
539{
540 av_log(anim->pFormatCtx, AV_LOG_ERROR, "DECODE UNHAPPY: PTS not matched!\n");
541
542 if (anim->pFrame_complete) {
543 return anim->pFrame;
544 }
545 if (anim->pFrame_backup_complete) {
546 return anim->pFrame_backup;
547 }
548 return nullptr;
549}
550
551/* Convert from ffmpeg planar GBRA layout to ImBuf interleaved RGBA, applying
552 * video rotation in the same go if needed. */
553static void float_planar_to_interleaved(const AVFrame *frame, const int rotation, ImBuf *ibuf)
554{
555 using namespace blender;
556 const size_t src_linesize = frame->linesize[0];
557 BLI_assert_msg(frame->linesize[1] == src_linesize && frame->linesize[2] == src_linesize &&
558 frame->linesize[3] == src_linesize,
559 "ffmpeg frame should be 4 same size planes for a floating point image case");
560 threading::parallel_for(IndexRange(ibuf->y), 256, [&](const IndexRange y_range) {
561 const int size_x = ibuf->x;
562 const int size_y = ibuf->y;
563 if (rotation == 90) {
564 /* 90 degree rotation. */
565 for (const int64_t y : y_range) {
566 int64_t src_offset = src_linesize * (size_y - y - 1);
567 const float *src_g = reinterpret_cast<const float *>(frame->data[0] + src_offset);
568 const float *src_b = reinterpret_cast<const float *>(frame->data[1] + src_offset);
569 const float *src_r = reinterpret_cast<const float *>(frame->data[2] + src_offset);
570 const float *src_a = reinterpret_cast<const float *>(frame->data[3] + src_offset);
571 float *dst = ibuf->float_buffer.data + (y + (size_x - 1) * size_y) * 4;
572 for (int x = 0; x < size_x; x++) {
573 dst[0] = *src_r++;
574 dst[1] = *src_g++;
575 dst[2] = *src_b++;
576 dst[3] = *src_a++;
577 dst -= size_y * 4;
578 }
579 }
580 }
581 else if (rotation == 180) {
582 /* 180 degree rotation. */
583 for (const int64_t y : y_range) {
584 int64_t src_offset = src_linesize * (size_y - y - 1);
585 const float *src_g = reinterpret_cast<const float *>(frame->data[0] + src_offset);
586 const float *src_b = reinterpret_cast<const float *>(frame->data[1] + src_offset);
587 const float *src_r = reinterpret_cast<const float *>(frame->data[2] + src_offset);
588 const float *src_a = reinterpret_cast<const float *>(frame->data[3] + src_offset);
589 float *dst = ibuf->float_buffer.data + ((size_y - y - 1) * size_x + size_x - 1) * 4;
590 for (int x = 0; x < size_x; x++) {
591 dst[0] = *src_r++;
592 dst[1] = *src_g++;
593 dst[2] = *src_b++;
594 dst[3] = *src_a++;
595 dst -= 4;
596 }
597 }
598 }
599 else if (rotation == 270) {
600 /* 270 degree rotation. */
601 for (const int64_t y : y_range) {
602 int64_t src_offset = src_linesize * (size_y - y - 1);
603 const float *src_g = reinterpret_cast<const float *>(frame->data[0] + src_offset);
604 const float *src_b = reinterpret_cast<const float *>(frame->data[1] + src_offset);
605 const float *src_r = reinterpret_cast<const float *>(frame->data[2] + src_offset);
606 const float *src_a = reinterpret_cast<const float *>(frame->data[3] + src_offset);
607 float *dst = ibuf->float_buffer.data + (size_y - y - 1) * 4;
608 for (int x = 0; x < size_x; x++) {
609 dst[0] = *src_r++;
610 dst[1] = *src_g++;
611 dst[2] = *src_b++;
612 dst[3] = *src_a++;
613 dst += size_y * 4;
614 }
615 }
616 }
617 else if (rotation == 0) {
618 /* No rotation. */
619 for (const int64_t y : y_range) {
620 int64_t src_offset = src_linesize * (size_y - y - 1);
621 const float *src_g = reinterpret_cast<const float *>(frame->data[0] + src_offset);
622 const float *src_b = reinterpret_cast<const float *>(frame->data[1] + src_offset);
623 const float *src_r = reinterpret_cast<const float *>(frame->data[2] + src_offset);
624 const float *src_a = reinterpret_cast<const float *>(frame->data[3] + src_offset);
625 float *dst = ibuf->float_buffer.data + size_x * y * 4;
626 for (int x = 0; x < size_x; x++) {
627 *dst++ = *src_r++;
628 *dst++ = *src_g++;
629 *dst++ = *src_b++;
630 *dst++ = *src_a++;
631 }
632 }
633 }
634 });
635
636 if (ELEM(rotation, 90, 270)) {
637 std::swap(ibuf->x, ibuf->y);
638 }
639}
640
646static void ffmpeg_postprocess(MovieReader *anim, AVFrame *input, ImBuf *ibuf)
647{
648 int filter_y = 0;
649
650 /* This means the data wasn't read properly,
651 * this check stops crashing */
652 if (input->data[0] == nullptr && input->data[1] == nullptr && input->data[2] == nullptr &&
653 input->data[3] == nullptr)
654 {
655 fprintf(stderr,
656 "ffmpeg_fetchibuf: "
657 "data not read properly...\n");
658 return;
659 }
660
661 av_log(anim->pFormatCtx,
662 AV_LOG_DEBUG,
663 " POSTPROC: AVFrame planes: %p %p %p %p\n",
664 input->data[0],
665 input->data[1],
666 input->data[2],
667 input->data[3]);
668
669 if (anim->ib_flags & IB_animdeinterlace) {
670 if (ffmpeg_deinterlace(anim->pFrameDeinterlaced,
671 anim->pFrame,
672 anim->pCodecCtx->pix_fmt,
673 anim->pCodecCtx->width,
674 anim->pCodecCtx->height) < 0)
675 {
676 filter_y = true;
677 }
678 else {
679 input = anim->pFrameDeinterlaced;
680 }
681 }
682
683 bool already_rotated = false;
684 if (anim->is_float) {
685 /* Float images are converted into planar GBRA layout by swscale (since
686 * it does not support direct YUV->RGBA float interleaved conversion).
687 * Do vertical flip and interleave into RGBA manually. */
688 /* Decode, then do vertical flip into destination. */
689 ffmpeg_sws_scale_frame(anim->img_convert_ctx, anim->pFrameRGB, input);
690
691 float_planar_to_interleaved(anim->pFrameRGB, anim->video_rotation, ibuf);
692 already_rotated = true;
693 }
694 else {
695 /* If final destination image layout matches that of decoded RGB frame (including
696 * any line padding done by ffmpeg for SIMD alignment), we can directly
697 * decode into that, doing the vertical flip in the same step. Otherwise have
698 * to do a separate flip. */
699 const int ibuf_linesize = ibuf->x * 4;
700 const int rgb_linesize = anim->pFrameRGB->linesize[0];
701 bool scale_to_ibuf = (rgb_linesize == ibuf_linesize);
702 /* swscale on arm64 before ffmpeg 6.0 (libswscale major version 7)
703 * could not handle negative line sizes. That has been fixed in all major
704 * ffmpeg releases in early 2023, but easier to just check for "below 7". */
705# if (defined(__aarch64__) || defined(_M_ARM64)) && (LIBSWSCALE_VERSION_MAJOR < 7)
706 scale_to_ibuf = false;
707# endif
708 uint8_t *rgb_data = anim->pFrameRGB->data[0];
709
710 if (scale_to_ibuf) {
711 /* Decode RGB and do vertical flip directly into destination image, by using negative
712 * line size. */
713 anim->pFrameRGB->linesize[0] = -ibuf_linesize;
714 anim->pFrameRGB->data[0] = ibuf->byte_buffer.data + (ibuf->y - 1) * ibuf_linesize;
715
716 ffmpeg_sws_scale_frame(anim->img_convert_ctx, anim->pFrameRGB, input);
717
718 anim->pFrameRGB->linesize[0] = rgb_linesize;
719 anim->pFrameRGB->data[0] = rgb_data;
720 }
721 else {
722 /* Decode, then do vertical flip into destination. */
723 ffmpeg_sws_scale_frame(anim->img_convert_ctx, anim->pFrameRGB, input);
724
725 /* Use negative line size to do vertical image flip. */
726 const int src_linesize[4] = {-rgb_linesize, 0, 0, 0};
727 const uint8_t *const src[4] = {
728 rgb_data + (anim->y - 1) * rgb_linesize, nullptr, nullptr, nullptr};
729 int dst_size = av_image_get_buffer_size(AVPixelFormat(anim->pFrameRGB->format),
730 anim->pFrameRGB->width,
731 anim->pFrameRGB->height,
732 1);
733 av_image_copy_to_buffer(ibuf->byte_buffer.data,
734 dst_size,
735 src,
736 src_linesize,
737 AVPixelFormat(anim->pFrameRGB->format),
738 anim->x,
739 anim->y,
740 1);
741 }
742 }
743
744 if (filter_y) {
745 IMB_filtery(ibuf);
746 }
747
748 /* Rotate video if display matrix is multiple of 90 degrees. */
749 if (!already_rotated && ELEM(anim->video_rotation, 90, 180, 270)) {
751 }
752}
753
754static void final_frame_log(MovieReader *anim,
755 int64_t frame_pts_start,
756 int64_t frame_pts_end,
757 const char *str)
758{
759 av_log(anim->pFormatCtx,
760 AV_LOG_INFO,
761 "DECODE HAPPY: %s frame PTS range %" PRId64 " - %" PRId64 ".\n",
762 str,
763 frame_pts_start,
764 frame_pts_end);
765}
766
767static bool ffmpeg_pts_isect(int64_t pts_start, int64_t pts_end, int64_t pts_to_search)
768{
769 return pts_start <= pts_to_search && pts_to_search < pts_end;
770}
771
772/* Return frame that matches `pts_to_search`, nullptr if matching frame does not exist. */
773static AVFrame *ffmpeg_frame_by_pts_get(MovieReader *anim, int64_t pts_to_search)
774{
775 /* NOTE: `frame->pts + frame->pkt_duration` does not always match pts of next frame.
776 * See footage from #86361. Here it is OK to use, because PTS must match current or backup frame.
777 * If there is no current frame, return nullptr.
778 */
779 if (!anim->pFrame_complete) {
780 return nullptr;
781 }
782
783 if (anim->never_seek_decode_one_frame) {
784 /* If we only decode one frame, return it. */
785 return anim->pFrame;
786 }
787
788 const bool backup_frame_ready = anim->pFrame_backup_complete;
789 const int64_t recent_start = av_get_pts_from_frame(anim->pFrame);
790 const int64_t recent_end = recent_start + av_get_frame_duration_in_pts_units(anim->pFrame);
791 const int64_t backup_start = backup_frame_ready ? av_get_pts_from_frame(anim->pFrame_backup) : 0;
792
793 AVFrame *best_frame = nullptr;
794 if (ffmpeg_pts_isect(recent_start, recent_end, pts_to_search)) {
795 final_frame_log(anim, recent_start, recent_end, "Recent");
796 best_frame = anim->pFrame;
797 }
798 else if (backup_frame_ready && ffmpeg_pts_isect(backup_start, recent_start, pts_to_search)) {
799 final_frame_log(anim, backup_start, recent_start, "Backup");
800 best_frame = anim->pFrame_backup;
801 }
802 return best_frame;
803}
804
805static void ffmpeg_decode_store_frame_pts(MovieReader *anim)
806{
807 anim->cur_pts = av_get_pts_from_frame(anim->pFrame);
808
809# ifdef FFMPEG_OLD_KEY_FRAME_QUERY_METHOD
810 if (anim->pFrame->key_frame)
811# else
812 if (anim->pFrame->flags & AV_FRAME_FLAG_KEY)
813# endif
814 {
815 anim->cur_key_frame_pts = anim->cur_pts;
816 }
817
818 av_log(anim->pFormatCtx,
819 AV_LOG_DEBUG,
820 " FRAME DONE: cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
821 av_get_pts_from_frame(anim->pFrame),
822 int64_t(anim->cur_pts));
823}
824
825static int ffmpeg_read_video_frame(MovieReader *anim, AVPacket *packet)
826{
827 int ret = 0;
828 while ((ret = av_read_frame(anim->pFormatCtx, packet)) >= 0) {
829 if (packet->stream_index == anim->videoStream) {
830 break;
831 }
832 av_packet_unref(packet);
833 packet->stream_index = -1;
834 }
835
836 return ret;
837}
838
839/* decode one video frame also considering the packet read into cur_packet */
840static int ffmpeg_decode_video_frame(MovieReader *anim)
841{
842 av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n");
843
844 /* Sometimes, decoder returns more than one frame per sent packet. Check if frames are available.
845 * This frames must be read, otherwise decoding will fail. See #91405. */
846 anim->pFrame_complete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
847 if (anim->pFrame_complete) {
848 av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE FROM CODEC BUFFER\n");
849 ffmpeg_decode_store_frame_pts(anim);
850 return 1;
851 }
852
853 int rval = 0;
854 if (anim->cur_packet->stream_index == anim->videoStream) {
855 av_packet_unref(anim->cur_packet);
856 anim->cur_packet->stream_index = -1;
857 }
858
859 while ((rval = ffmpeg_read_video_frame(anim, anim->cur_packet)) >= 0) {
860 if (anim->cur_packet->stream_index != anim->videoStream) {
861 continue;
862 }
863
864 av_log(anim->pFormatCtx,
865 AV_LOG_DEBUG,
866 "READ: strID=%d dts=%" PRId64 " pts=%" PRId64 " %s\n",
867 anim->cur_packet->stream_index,
868 (anim->cur_packet->dts == AV_NOPTS_VALUE) ? -1 : int64_t(anim->cur_packet->dts),
869 (anim->cur_packet->pts == AV_NOPTS_VALUE) ? -1 : int64_t(anim->cur_packet->pts),
870 (anim->cur_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : "");
871
872 avcodec_send_packet(anim->pCodecCtx, anim->cur_packet);
873 anim->pFrame_complete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
874
875 if (anim->pFrame_complete) {
876 ffmpeg_decode_store_frame_pts(anim);
877 break;
878 }
879 av_packet_unref(anim->cur_packet);
880 anim->cur_packet->stream_index = -1;
881 }
882
883 if (rval == AVERROR_EOF) {
884 /* Flush any remaining frames out of the decoder. */
885 avcodec_send_packet(anim->pCodecCtx, nullptr);
886 anim->pFrame_complete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
887
888 if (anim->pFrame_complete) {
889 ffmpeg_decode_store_frame_pts(anim);
890 rval = 0;
891 }
892 }
893
894 if (rval < 0) {
895 av_packet_unref(anim->cur_packet);
896 anim->cur_packet->stream_index = -1;
897
898 char error_str[AV_ERROR_MAX_STRING_SIZE];
899 av_make_error_string(error_str, AV_ERROR_MAX_STRING_SIZE, rval);
900
901 av_log(anim->pFormatCtx,
902 AV_LOG_ERROR,
903 " DECODE READ FAILED: av_read_frame() "
904 "returned error: %s\n",
905 error_str);
906 }
907
908 return (rval >= 0);
909}
910
911static int64_t ffmpeg_get_seek_pts(MovieReader *anim, int64_t pts_to_search)
912{
913 /* FFMPEG seeks internally using DTS values instead of PTS. In some files DTS and PTS values are
914 * offset and sometimes FFMPEG fails to take this into account when seeking.
915 * Therefore we need to seek backwards a certain offset to make sure the frame we want is in
916 * front of us. It is not possible to determine the exact needed offset,
917 * this value is determined experimentally.
918 * NOTE: Too big offset can impact performance. Current 3 frame offset has no measurable impact.
919 */
920 int64_t seek_pts = pts_to_search - (ffmpeg_steps_per_frame_get(anim) * 3);
921
922 seek_pts = std::max<int64_t>(seek_pts, 0);
923 return seek_pts;
924}
925
926/* This gives us an estimate of which pts our requested frame will have.
927 * Note that this might be off a bit in certain video files, but it should still be close enough.
928 */
929static int64_t ffmpeg_get_pts_to_search(MovieReader *anim,
930 const MovieIndex *tc_index,
931 int position)
932{
933 int64_t pts_to_search;
934
935 if (tc_index) {
936 int new_frame_index = tc_index->get_frame_index(position);
937 pts_to_search = tc_index->get_pts(new_frame_index);
938 }
939 else {
940 AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
941 int64_t start_pts = v_st->start_time;
942
943 pts_to_search = round(position * ffmpeg_steps_per_frame_get(anim));
944
945 if (start_pts != AV_NOPTS_VALUE) {
946 pts_to_search += start_pts;
947 }
948 }
949 return pts_to_search;
950}
951
952static bool ffmpeg_is_first_frame_decode(MovieReader *anim)
953{
954 return anim->pFrame_complete == false;
955}
956
957static void ffmpeg_scan_log(MovieReader *anim, int64_t pts_to_search)
958{
959 int64_t frame_pts_start = av_get_pts_from_frame(anim->pFrame);
960 int64_t frame_pts_end = frame_pts_start + av_get_frame_duration_in_pts_units(anim->pFrame);
961 av_log(anim->pFormatCtx,
962 AV_LOG_DEBUG,
963 " SCAN WHILE: PTS range %" PRId64 " - %" PRId64 " in search of %" PRId64 "\n",
964 frame_pts_start,
965 frame_pts_end,
966 pts_to_search);
967}
968
969/* Decode frames one by one until its PTS matches pts_to_search. */
970static void ffmpeg_decode_video_frame_scan(MovieReader *anim, int64_t pts_to_search)
971{
972 const int64_t start_gop_frame = anim->cur_key_frame_pts;
973 bool decode_error = false;
974
975 while (!decode_error && anim->cur_pts < pts_to_search) {
976 ffmpeg_scan_log(anim, pts_to_search);
977 ffmpeg_double_buffer_backup_frame_store(anim, pts_to_search);
978 decode_error = ffmpeg_decode_video_frame(anim) < 1;
979
980 /* We should not get a new GOP keyframe while scanning if seeking is working as intended.
981 * If this condition triggers, there may be and error in our seeking code.
982 * NOTE: This seems to happen if DTS value is used for seeking in ffmpeg internally. There
983 * seems to be no good way to handle such case. */
984 if (anim->seek_before_decode && start_gop_frame != anim->cur_key_frame_pts) {
985 av_log(anim->pFormatCtx, AV_LOG_ERROR, "SCAN: Frame belongs to an unexpected GOP!\n");
986 }
987 }
988}
989
990/* Wrapper over av_seek_frame(), for formats that doesn't have its own read_seek() or
991 * read_seek2() functions defined. When seeking in these formats, rule to seek to last
992 * necessary I-frame is not honored. It is not even guaranteed that I-frame, that must be
993 * decoded will be read. See https://trac.ffmpeg.org/ticket/1607 & #86944. */
994static int ffmpeg_generic_seek_workaround(MovieReader *anim,
995 int64_t *requested_pts,
996 int64_t pts_to_search)
997{
998 int64_t current_pts = *requested_pts;
999 int64_t offset = 0;
1000
1001 int64_t cur_pts, prev_pts = -1;
1002
1003 /* Step backward frame by frame until we find the key frame we are looking for. */
1004 while (current_pts != 0) {
1005 current_pts = *requested_pts - int64_t(round(offset * ffmpeg_steps_per_frame_get(anim)));
1006 current_pts = std::max(current_pts, int64_t(0));
1007
1008 /* Seek to timestamp. */
1009 if (av_seek_frame(anim->pFormatCtx, anim->videoStream, current_pts, AVSEEK_FLAG_BACKWARD) < 0)
1010 {
1011 break;
1012 }
1013
1014 /* Read first video stream packet. */
1015 AVPacket *read_packet = av_packet_alloc();
1016 while (av_read_frame(anim->pFormatCtx, read_packet) >= 0) {
1017 if (read_packet->stream_index == anim->videoStream) {
1018 break;
1019 }
1020 av_packet_unref(read_packet);
1021 }
1022
1023 /* If this packet contains an I-frame, this could be the frame that we need. */
1024 bool is_key_frame = read_packet->flags & AV_PKT_FLAG_KEY;
1025 /* We need to check the packet timestamp as the key frame could be for a GOP forward in the
1026 * video stream. So if it has a larger timestamp than the frame we want, ignore it.
1027 */
1028 cur_pts = timestamp_from_pts_or_dts(read_packet->pts, read_packet->dts);
1029 av_packet_free(&read_packet);
1030
1031 if (is_key_frame) {
1032 if (cur_pts <= pts_to_search) {
1033 /* We found the I-frame we were looking for! */
1034 break;
1035 }
1036 }
1037
1038 if (cur_pts == prev_pts) {
1039 /* We got the same key frame packet twice.
1040 * This probably means that we have hit the beginning of the stream. */
1041 break;
1042 }
1043
1044 prev_pts = cur_pts;
1045 offset++;
1046 }
1047
1048 *requested_pts = current_pts;
1049
1050 /* Re-seek to timestamp that gave I-frame, so it can be read by decode function. */
1051 return av_seek_frame(anim->pFormatCtx, anim->videoStream, current_pts, AVSEEK_FLAG_BACKWARD);
1052}
1053
1054/* Read packet until timestamp matches `anim->cur_packet`, thus recovering internal `anim` stream
1055 * position state. */
1056static void ffmpeg_seek_recover_stream_position(MovieReader *anim)
1057{
1058 AVPacket *temp_packet = av_packet_alloc();
1059 while (ffmpeg_read_video_frame(anim, temp_packet) >= 0) {
1060 int64_t current_pts = timestamp_from_pts_or_dts(anim->cur_packet->pts, anim->cur_packet->dts);
1061 int64_t temp_pts = timestamp_from_pts_or_dts(temp_packet->pts, temp_packet->dts);
1062 av_packet_unref(temp_packet);
1063
1064 if (current_pts == temp_pts) {
1065 break;
1066 }
1067 }
1068 av_packet_free(&temp_packet);
1069}
1070
1071/* Check if seeking and mainly flushing codec buffers is needed. */
1072static bool ffmpeg_seek_buffers_need_flushing(MovieReader *anim, int position, int64_t seek_pos)
1073{
1074 /* Get timestamp of packet read after seeking. */
1075 AVPacket *temp_packet = av_packet_alloc();
1076 ffmpeg_read_video_frame(anim, temp_packet);
1077 int64_t gop_pts = timestamp_from_pts_or_dts(temp_packet->pts, temp_packet->dts);
1078 av_packet_unref(temp_packet);
1079 av_packet_free(&temp_packet);
1080
1081 /* Seeking gives packet, that is currently read. No seeking was necessary, so buffers don't have
1082 * to be flushed. */
1083 if (gop_pts == timestamp_from_pts_or_dts(anim->cur_packet->pts, anim->cur_packet->dts)) {
1084 return false;
1085 }
1086
1087 /* Packet after seeking is same key frame as current, and further in time. No seeking was
1088 * necessary, so buffers don't have to be flushed. But stream position has to be recovered. */
1089 if (gop_pts == anim->cur_key_frame_pts && position > anim->cur_position) {
1090 ffmpeg_seek_recover_stream_position(anim);
1091 return false;
1092 }
1093
1094 /* Seeking was necessary, but we have read packets. Therefore we must seek again. */
1095 av_seek_frame(anim->pFormatCtx, anim->videoStream, seek_pos, AVSEEK_FLAG_BACKWARD);
1096 anim->cur_key_frame_pts = gop_pts;
1097 return true;
1098}
1099
1100/* Seek to last necessary key frame. */
1101static int ffmpeg_seek_to_key_frame(MovieReader *anim,
1102 int position,
1103 const MovieIndex *tc_index,
1104 int64_t pts_to_search)
1105{
1106 int64_t seek_pos;
1107 int ret;
1108
1109 if (tc_index) {
1110 /* We can use timestamps generated from our indexer to seek. */
1111 int new_frame_index = tc_index->get_frame_index(position);
1112
1113 uint64_t pts = tc_index->get_seek_pos_pts(new_frame_index);
1114 uint64_t dts = tc_index->get_seek_pos_dts(new_frame_index);
1115
1116 anim->cur_key_frame_pts = timestamp_from_pts_or_dts(pts, dts);
1117
1118 av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pts = %" PRIu64 "\n", pts);
1119 av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %" PRIu64 "\n", dts);
1120
1121 av_log(anim->pFormatCtx, AV_LOG_DEBUG, "Using PTS from timecode as seek_pos\n");
1122 ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, pts, AVSEEK_FLAG_BACKWARD);
1123 }
1124 else {
1125 /* We have to manually seek with ffmpeg to get to the key frame we want to start decoding from.
1126 */
1127 seek_pos = ffmpeg_get_seek_pts(anim, pts_to_search);
1128 av_log(
1129 anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek seek_pos = %" PRId64 "\n", seek_pos);
1130
1131 AVFormatContext *format_ctx = anim->pFormatCtx;
1132
1133 /* This used to check if the codec implemented "read_seek" or "read_seek2". However this is
1134 * now hidden from us in FFMPEG 7.0. While not as accurate, usually the AVFMT_TS_DISCONT is
1135 * set for formats where we need to apply the seek workaround to (like in MPEGTS). */
1136 if (!(format_ctx->iformat->flags & AVFMT_TS_DISCONT)) {
1137 ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, seek_pos, AVSEEK_FLAG_BACKWARD);
1138 }
1139 else {
1140 ret = ffmpeg_generic_seek_workaround(anim, &seek_pos, pts_to_search);
1141 av_log(anim->pFormatCtx,
1142 AV_LOG_DEBUG,
1143 "Adjusted final seek seek_pos = %" PRId64 "\n",
1144 seek_pos);
1145 }
1146
1147 if (ret <= 0 && !ffmpeg_seek_buffers_need_flushing(anim, position, seek_pos)) {
1148 return 0;
1149 }
1150 }
1151
1152 if (ret < 0) {
1153 av_log(anim->pFormatCtx,
1154 AV_LOG_ERROR,
1155 "FETCH: "
1156 "error while seeking to DTS = %" PRId64 " (frameno = %d, PTS = %" PRId64
1157 "): errcode = %d\n",
1158 seek_pos,
1159 position,
1160 pts_to_search,
1161 ret);
1162 }
1163 /* Flush the internal buffers of ffmpeg. This needs to be done after seeking to avoid decoding
1164 * errors. */
1165 avcodec_flush_buffers(anim->pCodecCtx);
1166 ffmpeg_double_buffer_backup_frame_clear(anim);
1167
1168 anim->cur_pts = -1;
1169
1170 if (anim->cur_packet->stream_index == anim->videoStream) {
1171 av_packet_unref(anim->cur_packet);
1172 anim->cur_packet->stream_index = -1;
1173 }
1174
1175 return ret;
1176}
1177
1178static bool ffmpeg_must_decode(MovieReader *anim, int position)
1179{
1180 return !anim->pFrame_complete || anim->cur_position != position;
1181}
1182
1183static bool ffmpeg_must_seek(MovieReader *anim, int position)
1184{
1185 bool must_seek = position != anim->cur_position + 1 || ffmpeg_is_first_frame_decode(anim);
1186 anim->seek_before_decode = must_seek;
1187 return must_seek;
1188}
1189
1190static ImBuf *ffmpeg_fetchibuf(MovieReader *anim, int position, IMB_Timecode_Type tc)
1191{
1192 if (anim == nullptr) {
1193 return nullptr;
1194 }
1195
1196 av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: seek_pos=%d\n", position);
1197
1198 const MovieIndex *tc_index = movie_open_index(anim, tc);
1199 int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position);
1200 AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
1201 double frame_rate = av_q2d(v_st->r_frame_rate);
1202 double pts_time_base = av_q2d(v_st->time_base);
1203 int64_t start_pts = v_st->start_time;
1204
1205 if (anim->never_seek_decode_one_frame) {
1206 /* If we must only ever decode one frame, and never seek, do so here. */
1207 if (!anim->pFrame_complete) {
1208 ffmpeg_decode_video_frame(anim);
1209 }
1210 }
1211 else {
1212 /* For all regular video files, do the seek/decode as needed. */
1213 av_log(anim->pFormatCtx,
1214 AV_LOG_DEBUG,
1215 "FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, start_pts=%" PRId64
1216 ")\n",
1217 int64_t(pts_to_search),
1218 pts_time_base,
1219 frame_rate,
1220 start_pts);
1221
1222 if (ffmpeg_must_decode(anim, position)) {
1223 if (ffmpeg_must_seek(anim, position)) {
1224 ffmpeg_seek_to_key_frame(anim, position, tc_index, pts_to_search);
1225 }
1226
1227 ffmpeg_decode_video_frame_scan(anim, pts_to_search);
1228 }
1229 }
1230
1231 /* Update resolution as it can change per-frame with WebM. See #100741 & #100081. */
1232 anim->x = anim->pCodecCtx->width;
1233 anim->y = anim->pCodecCtx->height;
1234
1235 const AVPixFmtDescriptor *pix_fmt_descriptor = av_pix_fmt_desc_get(anim->pCodecCtx->pix_fmt);
1236
1237 int planes = R_IMF_PLANES_RGBA;
1238 if ((pix_fmt_descriptor->flags & AV_PIX_FMT_FLAG_ALPHA) == 0) {
1239 planes = R_IMF_PLANES_RGB;
1240 }
1241
1242 ImBuf *cur_frame_final = IMB_allocImBuf(anim->x, anim->y, planes, 0);
1243
1244 /* Allocate the storage explicitly to ensure the memory is aligned. */
1245 const size_t align = ffmpeg_get_buffer_alignment();
1246 const size_t pixel_size = anim->is_float ? 16 : 4;
1247 uint8_t *buffer_data = static_cast<uint8_t *>(
1248 MEM_mallocN_aligned(pixel_size * anim->x * anim->y, align, "ffmpeg ibuf"));
1249 if (anim->is_float) {
1250 IMB_assign_float_buffer(cur_frame_final, (float *)buffer_data, IB_TAKE_OWNERSHIP);
1252 }
1253 else {
1254 IMB_assign_byte_buffer(cur_frame_final, buffer_data, IB_TAKE_OWNERSHIP);
1256 }
1257
1258 AVFrame *final_frame = ffmpeg_frame_by_pts_get(anim, pts_to_search);
1259 if (final_frame == nullptr) {
1260 /* No valid frame was decoded for requested PTS, fall back on most recent decoded frame, even
1261 * if it is incorrect. */
1262 final_frame = ffmpeg_double_buffer_frame_fallback_get(anim);
1263 }
1264
1265 /* Even with the fallback from above it is possible that the current decode frame is nullptr. In
1266 * this case skip post-processing and return current image buffer. */
1267 if (final_frame != nullptr) {
1268 ffmpeg_postprocess(anim, final_frame, cur_frame_final);
1269 }
1270
1271 anim->cur_position = position;
1272
1273 return cur_frame_final;
1274}
1275
1276static void free_anim_ffmpeg(MovieReader *anim)
1277{
1278 if (anim == nullptr) {
1279 return;
1280 }
1281
1282 if (anim->pCodecCtx) {
1283 avcodec_free_context(&anim->pCodecCtx);
1284 avformat_close_input(&anim->pFormatCtx);
1285 av_packet_free(&anim->cur_packet);
1286
1287 av_frame_free(&anim->pFrame);
1288 av_frame_free(&anim->pFrame_backup);
1289 av_frame_free(&anim->pFrameRGB);
1290 if (anim->pFrameDeinterlaced->data[0] != nullptr) {
1291 MEM_freeN(anim->pFrameDeinterlaced->data[0]);
1292 }
1293 av_frame_free(&anim->pFrameDeinterlaced);
1294 ffmpeg_sws_release_context(anim->img_convert_ctx);
1295 }
1296 anim->duration_in_frames = 0;
1297}
1298
1299#endif
1300
1305static bool anim_getnew(MovieReader *anim)
1306{
1307 if (anim == nullptr) {
1308 /* Nothing to initialize. */
1309 return false;
1310 }
1311
1313
1314#ifdef WITH_FFMPEG
1315 free_anim_ffmpeg(anim);
1316 if (startffmpeg(anim)) {
1318 return false;
1319 }
1320#endif
1322 return true;
1323}
1324
1326{
1327 ImBuf *ibuf = nullptr;
1328 int position = 0;
1329
1331 if (ibuf) {
1332 IMB_freeImBuf(ibuf);
1333 position = anim->duration_in_frames / 2;
1334 ibuf = MOV_decode_frame(anim, position, IMB_TC_NONE, IMB_PROXY_NONE);
1335
1336 char value[128];
1338 SNPRINTF(value, "%i", anim->x);
1339 IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Width", value);
1340 SNPRINTF(value, "%i", anim->y);
1341 IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Height", value);
1342 SNPRINTF(value, "%i", anim->duration_in_frames);
1343 IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Frames", value);
1344
1345#ifdef WITH_FFMPEG
1346 if (anim->pFormatCtx) {
1347 AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
1348 AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, nullptr);
1349 if (frame_rate.num != 0) {
1350 double duration = anim->duration_in_frames / av_q2d(frame_rate);
1351 SNPRINTF(value, "%g", av_q2d(frame_rate));
1352 IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::FPS", value);
1353 SNPRINTF(value, "%g", duration);
1354 IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Duration", value);
1355 IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Codec", anim->pCodec->long_name);
1356 }
1357 }
1358#endif
1359 }
1360 return ibuf;
1361}
1362
1364 int position,
1366 IMB_Proxy_Size preview_size)
1367{
1368 ImBuf *ibuf = nullptr;
1369 if (anim == nullptr) {
1370 return nullptr;
1371 }
1372
1373 if (preview_size == IMB_PROXY_NONE) {
1375 if (!anim_getnew(anim)) {
1376 return nullptr;
1377 }
1378 }
1379
1380 if (position < 0) {
1381 return nullptr;
1382 }
1383 if (position >= anim->duration_in_frames) {
1384 return nullptr;
1385 }
1386 }
1387 else {
1388 MovieReader *proxy = movie_open_proxy(anim, preview_size);
1389
1390 if (proxy) {
1391 position = MOV_calc_frame_index_with_timecode(anim, tc, position);
1392
1393 return MOV_decode_frame(proxy, position, IMB_TC_NONE, IMB_PROXY_NONE);
1394 }
1395 }
1396
1397#ifdef WITH_FFMPEG
1398 if (anim->state == MovieReader::State::Valid) {
1399 ibuf = ffmpeg_fetchibuf(anim, position, tc);
1400 if (ibuf) {
1401 anim->cur_position = position;
1402 }
1403 }
1404#endif
1405
1406 if (ibuf) {
1407 STRNCPY(ibuf->filepath, anim->filepath);
1408 ibuf->fileframe = anim->cur_position + 1;
1409 }
1410 return ibuf;
1411}
1412
1414{
1415 if (tc == IMB_TC_NONE) {
1416 return anim->duration_in_frames;
1417 }
1418
1419 const MovieIndex *idx = movie_open_index(anim, tc);
1420 if (!idx) {
1421 return anim->duration_in_frames;
1422 }
1423
1424 return idx->get_duration();
1425}
1426
1428{
1429 return anim->start_offset;
1430}
1431
1432float MOV_get_fps(const MovieReader *anim)
1433{
1434 if (anim->frs_sec > 0 && anim->frs_sec_base > 0) {
1435 return float(double(anim->frs_sec) / anim->frs_sec_base);
1436 }
1437 return 0.0f;
1438}
1439
1440bool MOV_get_fps_num_denom(const MovieReader *anim, short &r_fps_num, float &r_fps_denom)
1441{
1442 if (anim->frs_sec > 0 && anim->frs_sec_base > 0) {
1443 if (anim->frs_sec > SHRT_MAX) {
1444 /* If numerator is larger than the max short, we need to approximate. */
1445 r_fps_num = SHRT_MAX;
1446 r_fps_denom = float(anim->frs_sec_base * double(SHRT_MAX) / double(anim->frs_sec));
1447 }
1448 else {
1449 r_fps_num = anim->frs_sec;
1450 r_fps_denom = float(anim->frs_sec_base);
1451 }
1452 return true;
1453 }
1454 return false;
1455}
1456
1458{
1459 return ELEM(anim->video_rotation, 90, 270) ? anim->y : anim->x;
1460}
1461
1463{
1464 return ELEM(anim->video_rotation, 90, 270) ? anim->x : anim->y;
1465}
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
MINLINE int max_ii(int a, int b)
void void void BLI_path_split_file_part(const char *filepath, char *file, size_t file_maxncpy) ATTR_NONNULL(1
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
int BLI_system_thread_count(void)
Definition threads.cc:253
#define UNUSED_VARS(...)
#define ELEM(...)
#define STREQ(a, b)
@ R_IMF_PLANES_RGB
@ R_IMF_PLANES_RGBA
@ COLOR_ROLE_DEFAULT_BYTE
const char * IMB_colormanagement_role_colorspace_name_get(int role)
void IMB_assign_float_buffer(ImBuf *ibuf, float *buffer_data, ImBufOwnership ownership)
void IMB_freeImBuf(ImBuf *ibuf)
bool IMB_rotate_orthogonal(ImBuf *ibuf, int degrees)
ImBuf * IMB_allocImBuf(unsigned int x, unsigned int y, unsigned char planes, unsigned int flags)
void IMB_filtery(ImBuf *ibuf)
Definition filter.cc:63
void IMB_assign_byte_buffer(ImBuf *ibuf, uint8_t *buffer_data, ImBufOwnership ownership)
IMB_Proxy_Size
@ IMB_PROXY_NONE
#define IM_MAX_SPACE
@ IB_TAKE_OWNERSHIP
@ IB_animdeinterlace
void IMB_metadata_set_field(IDProperty *metadata, const char *key, const char *value)
Definition metadata.cc:68
void IMB_metadata_ensure(IDProperty **metadata)
Definition metadata.cc:23
void IMB_metadata_free(IDProperty *metadata)
Definition metadata.cc:32
Read Guarded memory(de)allocation.
IMB_Timecode_Type
Definition MOV_enums.hh:44
@ IMB_TC_NONE
Definition MOV_enums.hh:46
long long int int64_t
unsigned long long int uint64_t
const ColorSpace * colormanage_colorspace_get_named(const char *name)
#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 ffmpeg_get_video_rotation(const AVStream *stream)
#define input
#define round
#define PRIu64
Definition inttypes.h:132
#define PRId64
Definition inttypes.h:78
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_mallocN_aligned(size_t len, size_t alignment, const char *str)
Definition mallocn.cc:138
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
const MovieIndex * movie_open_index(MovieReader *anim, IMB_Timecode_Type tc)
void MOV_close_proxies(MovieReader *anim)
int MOV_calc_frame_index_with_timecode(MovieReader *anim, IMB_Timecode_Type tc, int position)
MovieReader * movie_open_proxy(MovieReader *anim, IMB_Proxy_Size preview_size)
static bool anim_getnew(MovieReader *anim)
ImBuf * MOV_decode_preview_frame(MovieReader *anim)
void MOV_close(MovieReader *anim)
Definition movie_read.cc:58
MovieReader * MOV_open_file(const char *filepath, int ib_flags, int streamindex, char colorspace[IM_MAX_SPACE])
double MOV_get_start_offset_seconds(const MovieReader *anim)
int MOV_get_duration_frames(MovieReader *anim, IMB_Timecode_Type tc)
bool MOV_is_initialized_and_valid(const MovieReader *anim)
void MOV_get_filename(const MovieReader *anim, char *filename, int filename_maxncpy)
Definition movie_read.cc:73
void MOV_set_multiview_suffix(MovieReader *anim, const char *suffix)
int MOV_get_image_width(const MovieReader *anim)
float MOV_get_fps(const MovieReader *anim)
IDProperty * MOV_load_metadata(MovieReader *anim)
Definition movie_read.cc:78
ImBuf * MOV_decode_frame(MovieReader *anim, int position, IMB_Timecode_Type tc, IMB_Proxy_Size preview_size)
bool MOV_get_fps_num_denom(const MovieReader *anim, short &r_fps_num, float &r_fps_denom)
int MOV_get_image_height(const MovieReader *anim)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
return ret
const ColorSpace * colorspace
const ColorSpace * colorspace
char filepath[IMB_FILEPATH_SIZE]
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
IDProperty * metadata
int get_duration() const
int get_frame_index(int frameno) const
uint64_t get_seek_pos_dts(int frame_index) const
uint64_t get_pts(int frame_index) const
uint64_t get_seek_pos_pts(int frame_index) const
char colorspace[64]
Definition movie_read.hh:91
double start_offset
Definition movie_read.hh:41
double frs_sec_base
Definition movie_read.hh:40
char filepath[1024]
Definition movie_read.hh:47
int duration_in_frames
Definition movie_read.hh:38
char suffix[64]
Definition movie_read.hh:92
IDProperty * metadata
Definition movie_read.hh:94
int video_rotation
Definition movie_read.hh:44
i
Definition text_draw.cc:230