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/anim_movie.c')
-rw-r--r--source/blender/imbuf/intern/anim_movie.c390
1 files changed, 243 insertions, 147 deletions
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index d36c7bbe486..dc39af803e0 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -574,8 +574,19 @@ static int startffmpeg(struct anim *anim)
pCodecCtx->workaround_bugs = 1;
- pCodecCtx->thread_count = BLI_system_thread_count();
- pCodecCtx->thread_type = FF_THREAD_SLICE;
+ if (pCodec->capabilities & AV_CODEC_CAP_AUTO_THREADS) {
+ pCodecCtx->thread_count = 0;
+ }
+ else {
+ pCodecCtx->thread_count = BLI_system_thread_count();
+ }
+
+ if (pCodec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
+ pCodecCtx->thread_type = FF_THREAD_FRAME;
+ }
+ else if (pCodec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
+ pCodecCtx->thread_type = FF_THREAD_SLICE;
+ }
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
avformat_close_input(&pFormatCtx);
@@ -969,44 +980,6 @@ static int ffmpeg_decode_video_frame(struct anim *anim)
return (rval >= 0);
}
-static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_search)
-{
- /* there seem to exist *very* silly GOP lengths out in the wild... */
- int count = 1000;
-
- av_log(anim->pFormatCtx,
- AV_LOG_DEBUG,
- "SCAN start: considering pts=%" PRId64 " in search of %" PRId64 "\n",
- (int64_t)anim->next_pts,
- (int64_t)pts_to_search);
-
- while (count > 0 && anim->next_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)pts_to_search);
- if (!ffmpeg_decode_video_frame(anim)) {
- break;
- }
- count--;
- }
- if (count == 0) {
- 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)pts_to_search);
- }
- if (anim->next_pts == pts_to_search) {
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n");
- }
- else {
- av_log(anim->pFormatCtx, AV_LOG_ERROR, "SCAN UNHAPPY: PTS not matched!\n");
- }
-}
-
static int match_format(const char *name, AVFormatContext *pFormatCtx)
{
const char *p;
@@ -1049,37 +1022,18 @@ static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx)
return false;
}
-static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Type tc)
+static int64_t ffmpeg_get_pts_to_search(struct anim *anim,
+ struct anim_index *tc_index,
+ int position)
{
- int64_t pts_to_search = 0;
- double frame_rate;
- double pts_time_base;
- int64_t st_time;
- struct anim_index *tc_index = 0;
- AVStream *v_st;
- int new_frame_index = 0; /* To quiet gcc barking... */
- int old_frame_index = 0; /* To quiet gcc barking... */
-
- if (anim == NULL) {
- return 0;
- }
-
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: pos=%d\n", position);
-
- if (tc != IMB_TC_NONE) {
- tc_index = IMB_anim_open_index(anim, tc);
- }
-
- v_st = anim->pFormatCtx->streams[anim->videoStream];
-
- frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
-
- st_time = anim->pFormatCtx->start_time;
- pts_time_base = av_q2d(v_st->time_base);
+ int64_t pts_to_search;
+ int64_t st_time = anim->pFormatCtx->start_time;
+ AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
+ double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
+ double pts_time_base = av_q2d(v_st->time_base);
if (tc_index) {
- new_frame_index = IMB_indexer_get_frame_index(tc_index, position);
- old_frame_index = IMB_indexer_get_frame_index(tc_index, anim->curposition);
+ int new_frame_index = IMB_indexer_get_frame_index(tc_index, position);
pts_to_search = IMB_indexer_get_pts(tc_index, new_frame_index);
}
else {
@@ -1089,117 +1043,259 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
pts_to_search += st_time / pts_time_base / AV_TIME_BASE;
}
}
+ return pts_to_search;
+}
- av_log(anim->pFormatCtx,
- AV_LOG_DEBUG,
- "FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, st_time=%" PRId64
- ")\n",
- (int64_t)pts_to_search,
- pts_time_base,
- frame_rate,
- st_time);
+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;
+}
- if (anim->last_frame && anim->last_pts <= pts_to_search && anim->next_pts > 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;
- }
+/* 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) {
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: within preseek interval (no index)\n");
+ return true;
+ }
- ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+ if (tc_index == NULL) {
+ return false;
}
- else if (tc_index && IMB_indexer_can_scan(tc_index, old_frame_index, new_frame_index)) {
+
+ 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);
+}
+
+static bool ffmpeg_is_first_frame_decode(struct anim *anim, int position)
+{
+ return position == 0 && anim->curposition == -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,
+ "SCAN start: considering pts=%" PRId64 " in search of %" PRId64 "\n",
+ (int64_t)anim->next_pts,
+ (int64_t)pts_to_search);
+
+ while (count > 0 && anim->next_pts < pts_to_search) {
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
- "FETCH: within preseek interval "
- "(index tells us)\n");
+ " WHILE: pts=%" PRId64 " in search of %" PRId64 "\n",
+ (int64_t)anim->next_pts,
+ (int64_t)pts_to_search);
+ if (!ffmpeg_decode_video_frame(anim)) {
+ break;
+ }
+ count--;
+ }
+ if (count == 0) {
+ 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)pts_to_search);
+ }
+ if (anim->next_pts == pts_to_search) {
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n");
+ }
+ else {
+ av_log(anim->pFormatCtx, AV_LOG_ERROR, "SCAN UNHAPPY: PTS not matched!\n");
+ }
+}
- ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+/* 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) {
+ 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;
+ }
}
- else if (position != anim->curposition + 1) {
- int64_t pos;
- int ret;
- if (tc_index) {
- uint64_t dts;
+ /* 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);
+}
- pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index);
- dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index);
+/* 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)
+{
+ 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;
- 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);
+ int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position);
- if (ffmpeg_seek_by_byte(anim->pFormatCtx)) {
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE pos\n");
+ int64_t pos;
+ int ret;
- ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BYTE);
- av_update_cur_dts(anim->pFormatCtx, v_st, dts);
- }
- else {
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using DTS pos\n");
- ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, dts, AVSEEK_FLAG_BACKWARD);
- }
+ if (tc_index) {
+ int new_frame_index = IMB_indexer_get_frame_index(tc_index, position);
+ 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);
+
+ 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);
+
+ if (ffmpeg_seek_by_byte(anim->pFormatCtx)) {
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE pos\n");
+
+ ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BYTE);
+ av_update_cur_dts(anim->pFormatCtx, v_st, dts);
}
else {
- pos = (int64_t)position * AV_TIME_BASE / frame_rate;
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using DTS pos\n");
+ ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, dts, AVSEEK_FLAG_BACKWARD);
+ }
+ }
+ 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);
+ 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 (pos < 0) {
+ pos = 0;
+ }
- if (st_time != AV_NOPTS_VALUE) {
- pos += st_time;
- }
+ if (st_time != AV_NOPTS_VALUE) {
+ pos += st_time;
+ }
+
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos);
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos);
+ 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);
}
-
- if (ret < 0) {
- av_log(anim->pFormatCtx,
- AV_LOG_ERROR,
- "FETCH: "
- "error while seeking to DTS = %" PRId64 " (frameno = %d, PTS = %" PRId64
- "): errcode = %d\n",
- pos,
- position,
- (int64_t)pts_to_search,
- ret);
+ else {
+ ret = ffmpeg_generic_seek_workaround(anim, pos);
}
+ }
- avcodec_flush_buffers(anim->pCodecCtx);
+ if (ret < 0) {
+ av_log(anim->pFormatCtx,
+ AV_LOG_ERROR,
+ "FETCH: "
+ "error while seeking to DTS = %" PRId64 " (frameno = %d, PTS = %" PRId64
+ "): errcode = %d\n",
+ pos,
+ position,
+ (int64_t)pts_to_search,
+ ret);
+ }
+ avcodec_flush_buffers(anim->pCodecCtx);
- anim->next_pts = -1;
+ anim->next_pts = -1;
- if (anim->next_packet.stream_index == anim->videoStream) {
- av_free_packet(&anim->next_packet);
- anim->next_packet.stream_index = -1;
- }
+ if (anim->next_packet.stream_index == anim->videoStream) {
+ av_free_packet(&anim->next_packet);
+ anim->next_packet.stream_index = -1;
+ }
- /* memset(anim->pFrame, ...) ?? */
+ /* memset(anim->pFrame, ...) ?? */
+ if (ret < 0) {
+ /* Seek failed. */
+ return;
+ }
- if (ret >= 0) {
- ffmpeg_decode_video_frame_scan(anim, pts_to_search);
- }
+ ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+}
+
+static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Type tc)
+{
+ if (anim == NULL) {
+ return NULL;
+ }
+
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: pos=%d\n", position);
+
+ struct anim_index *tc_index = IMB_anim_open_index(anim, tc);
+ int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position);
+ AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
+ double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
+ double pts_time_base = av_q2d(v_st->time_base);
+ int64_t st_time = anim->pFormatCtx->start_time;
+
+ av_log(anim->pFormatCtx,
+ AV_LOG_DEBUG,
+ "FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, st_time=%" PRId64
+ ")\n",
+ (int64_t)pts_to_search,
+ pts_time_base,
+ frame_rate,
+ st_time);
+
+ 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;
+ }
+
+ 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 (position == 0 && anim->curposition == -1) {
- /* first frame without seeking special case... */
- ffmpeg_decode_video_frame(anim);
+ 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");