diff options
author | Sebastian Parborg <darkdefende@gmail.com> | 2021-06-05 03:47:02 +0300 |
---|---|---|
committer | Jeroen Bakker <jeroen@blender.org> | 2021-06-30 10:37:21 +0300 |
commit | 7eb3e77b94f51bdeee719425ec4a533f662c5edd (patch) | |
tree | 03ba0461fd2337aaa237530b59d72031df899872 /source | |
parent | 13ab6b7bb60c3af82c6f99c2177f2fb2576ccd22 (diff) |
FFmpeg: Fix seeking not returning the correct frame when not using TC index
Fixed the logic for seeking in ffmpeg video files.
The main fix is that we now apply a small offset in ffmpeg_get_seek_pos
to make sure we don't get the frame in front of the seek position when
seeking backward.
The rest of the changes is general cleanup and untangling code.
Reviewed By: Richard Antalik
Differential Revision: http://developer.blender.org/D11492
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/imbuf/IMB_imbuf.h | 2 | ||||
-rw-r--r-- | source/blender/imbuf/intern/IMB_anim.h | 11 | ||||
-rw-r--r-- | source/blender/imbuf/intern/anim_movie.c | 360 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_sequence_types.h | 2 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_sequencer.c | 6 | ||||
-rw-r--r-- | source/blender/sequencer/intern/render.c | 2 | ||||
-rw-r--r-- | source/blender/sequencer/intern/strip_add.c | 3 |
7 files changed, 195 insertions, 191 deletions
diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index d131e4dacdc..9c84127105a 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -380,8 +380,6 @@ bool IMB_anim_can_produce_frames(const struct anim *anim); */ int ismovie(const char *filepath); -void IMB_anim_set_preseek(struct anim *anim, int preseek); -int IMB_anim_get_preseek(struct anim *anim); int IMB_anim_get_image_width(struct anim *anim); int IMB_anim_get_image_height(struct anim *anim); diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h index 7d7864306a1..cfeffcca0ea 100644 --- a/source/blender/imbuf/intern/IMB_anim.h +++ b/source/blender/imbuf/intern/IMB_anim.h @@ -87,7 +87,7 @@ struct anim_index; struct anim { int ib_flags; int curtype; - int curposition; /* index 0 = 1e, 1 = 2e, enz. */ + int cur_position; /* index 0 = 1e, 1 = 2e, enz. */ int duration_in_frames; int frs_sec; double frs_sec_base; @@ -105,7 +105,6 @@ struct anim { int orientation; size_t framesize; int interlacing; - int preseek; int streamindex; /* avi */ @@ -132,10 +131,10 @@ struct anim { struct SwsContext *img_convert_ctx; int videoStream; - struct ImBuf *last_frame; - int64_t last_pts; - int64_t next_pts; - AVPacket *next_packet; + struct ImBuf *cur_frame_final; + int64_t cur_pts; + int64_t cur_key_frame_pts; + AVPacket *cur_packet; #endif char index_dir[768]; diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 7c95983dfd5..4b5da22227b 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -433,8 +433,7 @@ static int startavi(struct anim *anim) anim->orientation = 0; anim->framesize = anim->x * anim->y * 4; - anim->curposition = 0; - anim->preseek = 0; + anim->cur_position = 0; # if 0 printf("x:%d y:%d size:%d interl:%d dur:%d\n", @@ -650,12 +649,12 @@ static int startffmpeg(struct anim *anim) anim->orientation = 0; anim->framesize = anim->x * anim->y * 4; - anim->curposition = -1; - anim->last_frame = 0; - anim->last_pts = -1; - anim->next_pts = -1; - anim->next_packet = av_packet_alloc(); - anim->next_packet->stream_index = -1; + anim->cur_position = -1; + anim->cur_frame_final = 0; + anim->cur_pts = -1; + anim->cur_key_frame_pts = -1; + anim->cur_packet = av_packet_alloc(); + anim->cur_packet->stream_index = -1; anim->pFrame = av_frame_alloc(); anim->pFrameComplete = false; @@ -671,7 +670,7 @@ static int startffmpeg(struct anim *anim) fprintf(stderr, "Could not allocate frame data.\n"); avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); - av_packet_free(&anim->next_packet); + av_packet_free(&anim->cur_packet); av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); av_frame_free(&anim->pFrame); @@ -684,7 +683,7 @@ static int startffmpeg(struct anim *anim) fprintf(stderr, "ffmpeg has changed alloc scheme ... ARGHHH!\n"); avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); - av_packet_free(&anim->next_packet); + av_packet_free(&anim->cur_packet); av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); av_frame_free(&anim->pFrame); @@ -706,13 +705,6 @@ static int startffmpeg(struct anim *anim) 1); } - if (pCodecCtx->has_b_frames) { - anim->preseek = 25; /* FIXME: detect gopsize ... */ - } - else { - anim->preseek = 0; - } - anim->img_convert_ctx = sws_getContext(anim->x, anim->y, anim->pCodecCtx->pix_fmt, @@ -728,7 +720,7 @@ static int startffmpeg(struct anim *anim) fprintf(stderr, "Can't transform color space??? Bailing out...\n"); avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); - av_packet_free(&anim->next_packet); + av_packet_free(&anim->cur_packet); av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); av_frame_free(&anim->pFrame); @@ -769,13 +761,13 @@ static int startffmpeg(struct anim *anim) /* postprocess the image in anim->pFrame and do color conversion * and deinterlacing stuff. * - * Output is anim->last_frame + * Output is anim->cur_frame_final */ static void ffmpeg_postprocess(struct anim *anim) { AVFrame *input = anim->pFrame; - ImBuf *ibuf = anim->last_frame; + ImBuf *ibuf = anim->cur_frame_final; int filter_y = 0; if (!anim->pFrameComplete) { @@ -902,7 +894,7 @@ static void ffmpeg_postprocess(struct anim *anim) } } -/* decode one video frame also considering the packet read into next_packet */ +/* decode one video frame also considering the packet read into cur_packet */ static int ffmpeg_decode_video_frame(struct anim *anim) { @@ -910,40 +902,43 @@ static int ffmpeg_decode_video_frame(struct anim *anim) av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n"); - if (anim->next_packet->stream_index == anim->videoStream) { - av_packet_unref(anim->next_packet); - anim->next_packet->stream_index = -1; + if (anim->cur_packet->stream_index == anim->videoStream) { + av_packet_unref(anim->cur_packet); + anim->cur_packet->stream_index = -1; } - while ((rval = av_read_frame(anim->pFormatCtx, anim->next_packet)) >= 0) { + while ((rval = av_read_frame(anim->pFormatCtx, anim->cur_packet)) >= 0) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, "%sREAD: strID=%d (VID: %d) dts=%" PRId64 " pts=%" PRId64 " %s\n", - (anim->next_packet->stream_index == anim->videoStream) ? "->" : " ", - anim->next_packet->stream_index, + (anim->cur_packet->stream_index == anim->videoStream) ? "->" : " ", + anim->cur_packet->stream_index, anim->videoStream, - (anim->next_packet->dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet->dts, - (anim->next_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet->pts, - (anim->next_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : ""); - if (anim->next_packet->stream_index == anim->videoStream) { + (anim->cur_packet->dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->dts, + (anim->cur_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->pts, + (anim->cur_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : ""); + if (anim->cur_packet->stream_index == anim->videoStream) { anim->pFrameComplete = 0; - avcodec_send_packet(anim->pCodecCtx, anim->next_packet); + avcodec_send_packet(anim->pCodecCtx, anim->cur_packet); anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; if (anim->pFrameComplete) { - anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); + anim->cur_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); + if (anim->pFrame->key_frame) { + anim->cur_key_frame_pts = anim->cur_pts; + } av_log(anim->pFormatCtx, AV_LOG_DEBUG, - " FRAME DONE: next_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", + " FRAME DONE: cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", (anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts, - (int64_t)anim->next_pts); + (int64_t)anim->cur_pts); break; } } - av_packet_unref(anim->next_packet); - anim->next_packet->stream_index = -1; + av_packet_unref(anim->cur_packet); + anim->cur_packet->stream_index = -1; } if (rval == AVERROR_EOF) { @@ -954,20 +949,23 @@ static int ffmpeg_decode_video_frame(struct anim *anim) anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; if (anim->pFrameComplete) { - anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); + anim->cur_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); + if (anim->pFrame->key_frame) { + anim->cur_key_frame_pts = anim->cur_pts; + } av_log(anim->pFormatCtx, AV_LOG_DEBUG, - " FRAME DONE (after EOF): next_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", + " FRAME DONE (after EOF): cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", (anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts, - (int64_t)anim->next_pts); + (int64_t)anim->cur_pts); rval = 0; } } if (rval < 0) { - av_packet_unref(anim->next_packet); - anim->next_packet->stream_index = -1; + av_packet_unref(anim->cur_packet); + anim->cur_packet->stream_index = -1; av_log(anim->pFormatCtx, AV_LOG_ERROR, @@ -1021,6 +1019,35 @@ static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx) return false; } +static int64_t ffmpeg_get_seek_pos(struct anim *anim, int position) +{ + AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; + double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL)); + int64_t st_time = anim->pFormatCtx->start_time; + int64_t pos = (int64_t)(position)*AV_TIME_BASE; + /* Step back half a time base position to make sure that we get the requested + * frame and not the one after it. + */ + pos -= (AV_TIME_BASE / 2); + pos /= frame_rate; + + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + "NO INDEX seek pos = %" PRId64 ", st_time = %" PRId64 "\n", + pos, + (st_time != AV_NOPTS_VALUE) ? st_time : 0); + + if (pos < 0) { + pos = 0; + } + + if (st_time != AV_NOPTS_VALUE) { + pos += st_time; + } + + return pos; +} + static int64_t ffmpeg_get_pts_to_search(struct anim *anim, struct anim_index *tc_index, int position) @@ -1045,77 +1072,60 @@ static int64_t ffmpeg_get_pts_to_search(struct anim *anim, return pts_to_search; } +/* Check if the pts will get us the same frame that we already have in memory from last decode. */ static bool ffmpeg_pts_matches_last_frame(struct anim *anim, int64_t pts_to_search) { - return anim->last_frame && anim->last_pts <= pts_to_search && anim->next_pts > pts_to_search; -} - -/* Requested video frame is expected to be found within different GOP as last decoded frame. - * Seeking to new position and scanning is fastest way to get requested frame. - * Check whether ffmpeg_can_scan() and ffmpeg_pts_matches_last_frame() is false before using this - * function. */ -static bool ffmpeg_can_seek(struct anim *anim, int position) -{ - return position != anim->curposition + 1; -} - -/* Requested video frame is expected to be found within same GOP as last decoded frame. - * Decoding frames in sequence until frame matches requested one is fastest way to get it. */ -static bool ffmpeg_can_scan(struct anim *anim, int position, struct anim_index *tc_index) -{ - if (position > anim->curposition + 1 && anim->preseek && !tc_index && - position - (anim->curposition + 1) < anim->preseek) { - return true; - } - - if (tc_index == NULL) { - return false; + if (anim->pFrame && anim->cur_frame_final) { + return labs(anim->cur_pts - pts_to_search) < anim->pFrame->pkt_duration; } - int new_frame_index = IMB_indexer_get_frame_index(tc_index, position); - int old_frame_index = IMB_indexer_get_frame_index(tc_index, anim->curposition); - return IMB_indexer_can_scan(tc_index, old_frame_index, new_frame_index); + return false; } static bool ffmpeg_is_first_frame_decode(struct anim *anim, int position) { - return position == 0 && anim->curposition == -1; + return position == 0 && anim->cur_position == -1; } /* Decode frames one by one until its PTS matches pts_to_search. */ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_search) { - av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: within preseek interval\n"); - - /* there seem to exist *very* silly GOP lengths out in the wild... */ - int count = 1000; + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: within current GOP\n"); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN start: considering pts=%" PRId64 " in search of %" PRId64 "\n", - (int64_t)anim->next_pts, + (int64_t)anim->cur_pts, (int64_t)pts_to_search); - while (count > 0 && anim->next_pts < pts_to_search) { + int64_t start_gop_frame = anim->cur_key_frame_pts; + + while (anim->cur_pts < pts_to_search) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, " WHILE: pts=%" PRId64 " in search of %" PRId64 "\n", - (int64_t)anim->next_pts, + (int64_t)anim->cur_pts, (int64_t)pts_to_search); if (!ffmpeg_decode_video_frame(anim)) { break; } - count--; + + if (start_gop_frame != anim->cur_key_frame_pts) { + break; + } } - if (count == 0) { + + if (start_gop_frame != anim->cur_key_frame_pts) { + /* We went into an other GOP frame. This should never happen as we should have positioned us + * correctly by seeking into the GOP frame that contains the frame we want. */ av_log(anim->pFormatCtx, AV_LOG_ERROR, "SCAN failed: completely lost in stream, " "bailing out at PTS=%" PRId64 ", searching for PTS=%" PRId64 "\n", - (int64_t)anim->next_pts, + (int64_t)anim->cur_pts, (int64_t)pts_to_search); } - if (anim->next_pts == pts_to_search) { + if (anim->cur_pts == pts_to_search) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n"); } else { @@ -1123,22 +1133,23 @@ 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) +/* Wrapper over av_seek_frame(), for formats that doesn't have its 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; + 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. */ + * 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); + current_pos = *requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate); current_pos = max_ii(current_pos, 0); /* Seek to timestamp. */ @@ -1147,42 +1158,51 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t requested_p } /* 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) { + AVPacket *read_packet = av_packet_alloc(); + while (av_read_frame(anim->pFormatCtx, read_packet) >= 0) { + if (anim->cur_packet->stream_index == anim->videoStream) { 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) { + bool is_key_frame = read_packet->flags & AV_PKT_FLAG_KEY; + av_packet_free(&read_packet); + if (is_key_frame) { break; } } + *requested_pos = current_pos; + /* 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) +/* Seek to last necessary key frame. */ +static int ffmpeg_seek_to_key_frame(struct anim *anim, int position, struct anim_index *tc_index) { AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream]; - double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL)); - int64_t st_time = anim->pFormatCtx->start_time; - - int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position); int64_t pos; int ret; if (tc_index) { + /* We can use timestamps generated from our indexer to seek. */ int new_frame_index = IMB_indexer_get_frame_index(tc_index, position); + int old_frame_index = IMB_indexer_get_frame_index(tc_index, anim->cur_position); + + if (IMB_indexer_can_scan(tc_index, old_frame_index, new_frame_index)) { + /* No need to seek, return early. */ + return 0; + } uint64_t dts; pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index); dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index); + anim->cur_key_frame_pts = pos; + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pos = %" PRId64 "\n", pos); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %" PRIu64 "\n", dts); @@ -1198,22 +1218,9 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_ } } else { - pos = (int64_t)(position)*AV_TIME_BASE / frame_rate; - - av_log(anim->pFormatCtx, - AV_LOG_DEBUG, - "NO INDEX seek pos = %" PRId64 ", st_time = %" PRId64 "\n", - pos, - (st_time != AV_NOPTS_VALUE) ? st_time : 0); - - if (pos < 0) { - pos = 0; - } - - if (st_time != AV_NOPTS_VALUE) { - pos += st_time; - } - + /* We have to manually seek with ffmpeg to get to the key frame we want to start decoding from. + */ + pos = ffmpeg_get_seek_pos(anim, position); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos); AVFormatContext *format_ctx = anim->pFormatCtx; @@ -1222,11 +1229,44 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_ ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD); } else { - ret = ffmpeg_generic_seek_workaround(anim, pos); + ret = ffmpeg_generic_seek_workaround(anim, &pos); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "Adjusted final seek pos = %" PRId64 "\n", pos); + } + + if (ret >= 0) { + /* Double check if we need to seek and decode all packets. */ + AVPacket *current_gop_start_packet = av_packet_alloc(); + while (av_read_frame(anim->pFormatCtx, current_gop_start_packet) >= 0) { + if (current_gop_start_packet->stream_index == anim->videoStream) { + break; + } + av_packet_unref(current_gop_start_packet); + } + bool same_gop = current_gop_start_packet->pts == anim->cur_key_frame_pts; + + if (same_gop && position > anim->cur_position) { + /* Change back to our old frame position so we can simply continue decoding from there. */ + AVPacket *temp = av_packet_alloc(); + while (av_read_frame(anim->pFormatCtx, temp) >= 0) { + if (temp->stream_index == anim->videoStream && temp->pts == anim->cur_packet->pts) { + break; + } + av_packet_unref(temp); + } + av_packet_free(¤t_gop_start_packet); + av_packet_free(&temp); + return 0; + } + + anim->cur_key_frame_pts = current_gop_start_packet->pts; + av_packet_free(¤t_gop_start_packet); + /* Seek back so we are at the correct position after we decoded a frame. */ + av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD); } } if (ret < 0) { + int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position); av_log(anim->pFormatCtx, AV_LOG_ERROR, "FETCH: " @@ -1234,25 +1274,19 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_ "): errcode = %d\n", pos, position, - (int64_t)pts_to_search, + pts_to_search, ret); } avcodec_flush_buffers(anim->pCodecCtx); - anim->next_pts = -1; + anim->cur_pts = -1; - if (anim->next_packet->stream_index == anim->videoStream) { - av_packet_unref(anim->next_packet); - anim->next_packet->stream_index = -1; - } - - /* memset(anim->pFrame, ...) ?? */ - if (ret < 0) { - /* Seek failed. */ - return; + if (anim->cur_packet->stream_index == anim->videoStream) { + av_packet_unref(anim->cur_packet); + anim->cur_packet->stream_index = -1; } - ffmpeg_decode_video_frame_scan(anim, pts_to_search); + return ret; } static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Type tc) @@ -1282,25 +1316,22 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ if (ffmpeg_pts_matches_last_frame(anim, pts_to_search)) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, - "FETCH: frame repeat: last: %" PRId64 " next: %" PRId64 "\n", - (int64_t)anim->last_pts, - (int64_t)anim->next_pts); - IMB_refImBuf(anim->last_frame); - anim->curposition = position; - return anim->last_frame; + "FETCH: frame repeat: pts: %" PRId64 "\n", + (int64_t)anim->cur_pts); + IMB_refImBuf(anim->cur_frame_final); + anim->cur_position = position; + return anim->cur_frame_final; } - if (ffmpeg_can_scan(anim, position, tc_index) || ffmpeg_is_first_frame_decode(anim, position)) { - ffmpeg_decode_video_frame_scan(anim, pts_to_search); - } - else if (ffmpeg_can_seek(anim, position)) { - ffmpeg_seek_and_decode(anim, position, tc_index); - } - else { + if (position == anim->cur_position + 1 || ffmpeg_is_first_frame_decode(anim, position)) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: no seek necessary, just continue...\n"); + ffmpeg_decode_video_frame(anim); + } + else if (ffmpeg_seek_to_key_frame(anim, position, tc_index) >= 0) { + ffmpeg_decode_video_frame_scan(anim, pts_to_search); } - IMB_freeImBuf(anim->last_frame); + IMB_freeImBuf(anim->cur_frame_final); /* Certain versions of FFmpeg have a bug in libswscale which ends up in crash * when destination buffer is not properly aligned. For example, this happens @@ -1320,23 +1351,20 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ * * The issue was reported to FFmpeg under ticket #8747 in the FFmpeg tracker * and is fixed in the newer versions than 4.3.1. */ - anim->last_frame = IMB_allocImBuf(anim->x, anim->y, 32, 0); - anim->last_frame->rect = MEM_mallocN_aligned((size_t)4 * anim->x * anim->y, 32, "ffmpeg ibuf"); - anim->last_frame->mall |= IB_rect; + anim->cur_frame_final = IMB_allocImBuf(anim->x, anim->y, 32, 0); + anim->cur_frame_final->rect = MEM_mallocN_aligned( + (size_t)4 * anim->x * anim->y, 32, "ffmpeg ibuf"); + anim->cur_frame_final->mall |= IB_rect; - anim->last_frame->rect_colorspace = colormanage_colorspace_get_named(anim->colorspace); + anim->cur_frame_final->rect_colorspace = colormanage_colorspace_get_named(anim->colorspace); ffmpeg_postprocess(anim); - anim->last_pts = anim->next_pts; - - ffmpeg_decode_video_frame(anim); + anim->cur_position = position; - anim->curposition = position; + IMB_refImBuf(anim->cur_frame_final); - IMB_refImBuf(anim->last_frame); - - return anim->last_frame; + return anim->cur_frame_final; } static void free_anim_ffmpeg(struct anim *anim) @@ -1348,7 +1376,7 @@ static void free_anim_ffmpeg(struct anim *anim) if (anim->pCodecCtx) { avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); - av_packet_free(&anim->next_packet); + av_packet_free(&anim->cur_packet); av_frame_free(&anim->pFrame); @@ -1369,7 +1397,7 @@ static void free_anim_ffmpeg(struct anim *anim) av_frame_free(&anim->pFrameDeinterlaced); sws_freeContext(anim->img_convert_ctx); - IMB_freeImBuf(anim->last_frame); + IMB_freeImBuf(anim->cur_frame_final); } anim->duration_in_frames = 0; } @@ -1503,13 +1531,13 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, an_stringenc(anim->name, head, tail, digits, pic); ibuf = IMB_loadiffname(anim->name, IB_rect, anim->colorspace); if (ibuf) { - anim->curposition = position; + anim->cur_position = position; } break; case ANIM_MOVIE: ibuf = movie_fetchibuf(anim, position); if (ibuf) { - anim->curposition = position; + anim->cur_position = position; IMB_convert_rgba_to_abgr(ibuf); } break; @@ -1517,7 +1545,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, case ANIM_AVI: ibuf = avi_fetchibuf(anim, position); if (ibuf) { - anim->curposition = position; + anim->cur_position = position; } break; #endif @@ -1525,7 +1553,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, case ANIM_FFMPEG: ibuf = ffmpeg_fetchibuf(anim, position, tc); if (ibuf) { - anim->curposition = position; + anim->cur_position = position; } filter_y = 0; /* done internally */ break; @@ -1536,7 +1564,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, if (filter_y) { IMB_filtery(ibuf); } - BLI_snprintf(ibuf->name, sizeof(ibuf->name), "%s.%04d", anim->name, anim->curposition + 1); + BLI_snprintf(ibuf->name, sizeof(ibuf->name), "%s.%04d", anim->name, anim->cur_position + 1); } return ibuf; } @@ -1591,16 +1619,6 @@ bool IMB_anim_get_fps(struct anim *anim, short *frs_sec, float *frs_sec_base, bo return false; } -void IMB_anim_set_preseek(struct anim *anim, int preseek) -{ - anim->preseek = preseek; -} - -int IMB_anim_get_preseek(struct anim *anim) -{ - return anim->preseek; -} - int IMB_anim_get_image_width(struct anim *anim) { return anim->x; diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index fa11a7dfd13..752ddea7234 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -172,7 +172,7 @@ typedef struct Sequence { float sat; float mul, handsize; - short anim_preseek; + short anim_preseek; /* UNUSED. */ /** Streamindex for movie or sound files with several streams. */ short streamindex; /** For multicam source selection. */ diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 24d051fecc8..756609aa005 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -2417,12 +2417,6 @@ static void rna_def_movie(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Movie Sequence", "Sequence strip to load a video"); RNA_def_struct_sdna(srna, "Sequence"); - prop = RNA_def_property(srna, "mpeg_preseek", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "anim_preseek"); - RNA_def_property_range(prop, 0, 50); - RNA_def_property_ui_text(prop, "MPEG Preseek", "For MPEG movies, preseek this many frames"); - RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL); - prop = RNA_def_property(srna, "stream_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "streamindex"); RNA_def_property_range(prop, 0, 20); diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index f892f1c1b41..d6e4ea373e1 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -1111,8 +1111,6 @@ static ImBuf *seq_render_movie_strip_view(const SeqRenderData *context, ImBuf *ibuf = NULL; IMB_Proxy_Size psize = SEQ_rendersize_to_proxysize(context->preview_render_size); - IMB_anim_set_preseek(sanim->anim, seq->anim_preseek); - if (SEQ_can_use_proxy(context, seq, psize)) { /* Try to get a proxy image. * Movie proxies are handled by ImBuf module with exception of `custom file` setting. */ diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index 1106f47c477..63b0edc2706 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -549,7 +549,6 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */ if (anim_arr[0] != NULL) { - seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]); seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN); IMB_anim_load_metadata(anim_arr[0]); @@ -691,8 +690,6 @@ void SEQ_add_reload_new_file(Main *bmain, Scene *scene, Sequence *seq, const boo seq->len = IMB_anim_get_duration( sanim->anim, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN); - seq->anim_preseek = IMB_anim_get_preseek(sanim->anim); - seq->len -= seq->anim_startofs; seq->len -= seq->anim_endofs; if (seq->len < 0) { |