Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Antalik <richardantalik@gmail.com>2021-03-30 03:52:20 +0300
committerRichard Antalik <richardantalik@gmail.com>2021-03-30 03:58:53 +0300
commit6c33d3d01b6237cbe854b1d6ca54ac9680bb0563 (patch)
tree94f085101d9d04c28399e01f6eda041675bf8dba /source/blender/imbuf/intern/anim_movie.c
parentffbe8035057f2937e9c4e32201fed5ac7776def6 (diff)
Fix T86944: Incorrect seeking in some movies
`av_seek_frame()` failed to seek to nearest I-frame. This seems to be a bug or not implemented feature in FFmpeg. Looks like same issue as ticket https://trac.ffmpeg.org/ticket/1607 on ffmpeg tracker. If seeking is done using format specific function (`read_seek2`) field of `AVInputFormat` is set, `see av_seek_frame()`, use `av_seek_frame()` function. Otherwise use wrapper that actively searches for I-frame packet. Searching is flexible and tries to do minimum amount of work. Currently it is limited to equivalent of 25 frames, which may not be enough for some files, but there may be files with no I-frames at all, so it is best to keep this limit as low as possible. Previously this problem was masked by preseek, which was hard-coded to 25 frames. This was removed in rB88604b79b7d1. If this approach would be unnecessary for some formats, in worst case file would be seeked 2 times which is very fast, so there will be no visible impact on performance. Reviewed By: sergey Differential Revision: https://developer.blender.org/D10845
Diffstat (limited to 'source/blender/imbuf/intern/anim_movie.c')
-rw-r--r--source/blender/imbuf/intern/anim_movie.c53
1 files changed, 52 insertions, 1 deletions
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index 785f27c53e4..d75a81e79f9 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -1124,6 +1124,49 @@ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_sea
}
}
+/* Wrapper over av_seek_frame(), for formats that doesn't have it's own read_seek() or read_seek2()
+ * functions defined. When seeking in these formats, rule to seek to last necessary I-frame is not
+ * honored. It is not even guaranteed that I-frame, that must be decoded will be read. See
+ * https://trac.ffmpeg.org/ticket/1607 and https://developer.blender.org/T86944. */
+static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t requested_pos)
+{
+ AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
+ double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
+ int64_t current_pos = requested_pos;
+
+ /* This time offset maximum limit is arbitrary. If some files fails to decode it may be
+ * increased. Seek performance will be negatively affected. Small initial offset is necessary
+ * because encoder can re-arrange frames as it needs but within it's delay, which is usually
+ * small. */
+ for (int offset = 5; offset < 25; offset++) {
+ current_pos = requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate);
+
+ /* Seek to timestamp. */
+ if (av_seek_frame(anim->pFormatCtx, -1, current_pos, AVSEEK_FLAG_BACKWARD) < 0) {
+ break;
+ }
+
+ /* Read first video stream packet. */
+ AVPacket read_packet = {0};
+ while (av_read_frame(anim->pFormatCtx, &read_packet) >= 0) {
+ if (anim->next_packet.stream_index != anim->videoStream) {
+ continue;
+ }
+ else {
+ break;
+ }
+ }
+
+ /* If this packet contains I-frame, exit loop. This should be the frame that we need. */
+ if (read_packet.flags & AV_PKT_FLAG_KEY) {
+ break;
+ }
+ }
+
+ /* Re-seek to timestamp that gave I-frame, so it can be read by decode function. */
+ return av_seek_frame(anim->pFormatCtx, -1, current_pos, AVSEEK_FLAG_BACKWARD);
+}
+
/* Seek to last necessary I-frame and scan-decode until requested frame is found. */
static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_index *tc_index)
{
@@ -1176,7 +1219,15 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos);
- ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
+ AVFormatContext *format_ctx = anim->pFormatCtx;
+
+ /* Condition based on av_seek_frame() code. */
+ if (format_ctx->iformat->read_seek2 && !format_ctx->iformat->read_seek) {
+ ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
+ }
+ else {
+ ret = ffmpeg_generic_seek_workaround(anim, pos);
+ }
}
if (ret < 0) {