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:
Diffstat (limited to 'source/blender/imbuf/intern')
-rw-r--r--source/blender/imbuf/intern/IMB_anim.h11
-rw-r--r--source/blender/imbuf/intern/anim_movie.c362
-rw-r--r--source/blender/imbuf/intern/indexer.c57
-rw-r--r--source/blender/imbuf/intern/jpeg.c3
4 files changed, 227 insertions, 206 deletions
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 5918a4bf16e..622b6cbfc16 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,20 +705,13 @@ 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,
anim->x,
anim->y,
AV_PIX_FMT_RGBA,
- SWS_FAST_BILINEAR | SWS_PRINT_INFO | SWS_FULL_CHR_H_INT,
+ SWS_BILINEAR | SWS_PRINT_INFO | SWS_FULL_CHR_H_INT,
NULL,
NULL,
NULL);
@@ -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 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)
+/* 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;
- }
-
- /* 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/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c
index 11ce77e3091..46b28086129 100644
--- a/source/blender/imbuf/intern/indexer.c
+++ b/source/blender/imbuf/intern/indexer.c
@@ -466,13 +466,6 @@ struct proxy_output_ctx {
struct anim *anim;
};
-// work around stupid swscaler 16 bytes alignment bug...
-
-static int round_up(int x, int mod)
-{
- return x + ((mod - (x % mod)) % mod);
-}
-
static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
struct anim *anim, AVStream *st, int proxy_size, int width, int height, int quality)
{
@@ -499,13 +492,6 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
rv->c = avcodec_alloc_context3(NULL);
rv->c->codec_type = AVMEDIA_TYPE_VIDEO;
rv->c->codec_id = AV_CODEC_ID_H264;
- rv->c->width = width;
- rv->c->height = height;
- rv->c->gop_size = 10;
- rv->c->max_b_frames = 0;
- /* Correct wrong default ffmpeg param which crash x264. */
- rv->c->qmin = 10;
- rv->c->qmax = 51;
rv->of->oformat->video_codec = rv->c->codec_id;
rv->codec = avcodec_find_encoder(rv->c->codec_id);
@@ -520,6 +506,13 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
return NULL;
}
+ avcodec_get_context_defaults3(rv->c, rv->codec);
+
+ rv->c->width = width;
+ rv->c->height = height;
+ rv->c->gop_size = 10;
+ rv->c->max_b_frames = 0;
+
if (rv->codec->pix_fmts) {
rv->c->pix_fmt = rv->codec->pix_fmts[0];
}
@@ -533,8 +526,8 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
rv->c->time_base.num = 1;
rv->st->time_base = rv->c->time_base;
- /* This range matches eFFMpegCrf. Crf_range_min corresponds to lowest quality, crf_range_max to
- * highest quality. */
+ /* This range matches #eFFMpegCrf. `crf_range_min` corresponds to lowest quality,
+ * `crf_range_max` to highest quality. */
const int crf_range_min = 32;
const int crf_range_max = 17;
int crf = round_fl_to_int((quality / 100.0f) * (crf_range_max - crf_range_min) + crf_range_min);
@@ -542,8 +535,11 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
AVDictionary *codec_opts = NULL;
/* High quality preset value. */
av_dict_set_int(&codec_opts, "crf", crf, 0);
- /* Prefer smaller file-size. */
- av_dict_set(&codec_opts, "preset", "slow", 0);
+ /* Prefer smaller file-size. Presets from `veryslow` to `veryfast` produce output with very
+ * similar file-size, but there is big difference in performance.
+ * In some cases `veryfast` preset will produce smallest file-size. */
+ av_dict_set(&codec_opts, "preset", "veryfast", 0);
+ av_dict_set(&codec_opts, "tune", "fastdecode", 0);
if (rv->codec->capabilities & AV_CODEC_CAP_AUTO_THREADS) {
rv->c->thread_count = 0;
@@ -595,15 +591,19 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
if (st->codecpar->width != width || st->codecpar->height != height ||
st->codecpar->format != rv->c->pix_fmt) {
rv->frame = av_frame_alloc();
- av_image_fill_arrays(
- rv->frame->data,
- rv->frame->linesize,
- MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, round_up(width, 16), height, 1),
- "alloc proxy output frame"),
- rv->c->pix_fmt,
- round_up(width, 16),
- height,
- 1);
+
+ av_image_fill_arrays(rv->frame->data,
+ rv->frame->linesize,
+ MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, width, height, 1),
+ "alloc proxy output frame"),
+ rv->c->pix_fmt,
+ width,
+ height,
+ 1);
+
+ rv->frame->format = rv->c->pix_fmt;
+ rv->frame->width = width;
+ rv->frame->height = height;
rv->sws_ctx = sws_getContext(st->codecpar->width,
rv->orig_height,
@@ -686,6 +686,9 @@ static void add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *fr
packet->stream_index = ctx->st->index;
av_packet_rescale_ts(packet, ctx->c->time_base, ctx->st->time_base);
+# ifdef FFMPEG_USE_DURATION_WORKAROUND
+ my_guess_pkt_duration(ctx->of, ctx->st, packet);
+# endif
int write_ret = av_interleaved_write_frame(ctx->of, packet);
if (write_ret != 0) {
diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c
index 440375f60dc..48b5b0c34db 100644
--- a/source/blender/imbuf/intern/jpeg.c
+++ b/source/blender/imbuf/intern/jpeg.c
@@ -516,7 +516,8 @@ static void write_jpeg(struct jpeg_compress_struct *cinfo, struct ImBuf *ibuf)
* The first "Blender" is a simple identify to help
* in the read process.
*/
- text_len = BLI_snprintf(text, text_size, "Blender:%s:%s", prop->name, IDP_String(prop));
+ text_len = BLI_snprintf_rlen(
+ text, text_size, "Blender:%s:%s", prop->name, IDP_String(prop));
jpeg_write_marker(cinfo, JPEG_COM, (JOCTET *)text, text_len + 1);
/* TODO(sergey): Ideally we will try to re-use allocation as