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
This commit is contained in:
Sebastian Parborg 2021-06-05 02:47:02 +02:00 committed by Jeroen Bakker
parent 13ab6b7bb6
commit 7eb3e77b94
Notes: blender-bot 2023-07-10 10:12:37 +02:00
Referenced by issue #88449, Blender LTS: Maintenance Task 2.93
8 changed files with 195 additions and 192 deletions

View File

@ -1381,7 +1381,6 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel):
col = layout.column()
col.prop(strip, "filepath", text="")
col.prop(strip.colorspace_settings, "name", text="Color Space")
col.prop(strip, "mpeg_preseek")
col.prop(strip, "stream_index")
col.prop(strip, "use_deinterlace")

View File

@ -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);

View File

@ -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];

View File

@ -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 (anim->pFrame && anim->cur_frame_final) {
return labs(anim->cur_pts - pts_to_search) < anim->pFrame->pkt_duration;
}
if (tc_index == NULL) {
return false;
}
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(&current_gop_start_packet);
av_packet_free(&temp);
return 0;
}
anim->cur_key_frame_pts = current_gop_start_packet->pts;
av_packet_free(&current_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;
if (anim->cur_packet->stream_index == anim->videoStream) {
av_packet_unref(anim->cur_packet);
anim->cur_packet->stream_index = -1;
}
/* memset(anim->pFrame, ...) ?? */
if (ret < 0) {
/* Seek failed. */
return;
}
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)) {
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);
}
else if (ffmpeg_can_seek(anim, position)) {
ffmpeg_seek_and_decode(anim, position, tc_index);
}
else {
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: no seek necessary, just continue...\n");
}
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;
anim->cur_position = position;
ffmpeg_decode_video_frame(anim);
IMB_refImBuf(anim->cur_frame_final);
anim->curposition = position;
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;

View File

@ -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. */

View File

@ -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);

View File

@ -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. */

View File

@ -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) {