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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Antalik <richardantalik@gmail.com>2021-03-26 14:40:46 +0300
committerRichard Antalik <richardantalik@gmail.com>2021-03-26 14:43:46 +0300
commit847002e1f88b6f4906ce039b3cd69570664052d5 (patch)
tree970e31719fb941b11040450b5fd711c71976586d /source/blender/imbuf
parent43455f385709ede032de0c39792bad422be87a92 (diff)
FFMPEG: refactor seeking
Split seeking section of `ffmpeg_fetchibuf()` function into multiple smaller functions. Conditional statements are moved to own funtions with human readable names, so code flow is more clear. To remove one branch of seeking, first frame is now decoded by scanning, which will do only one iteration. So nothing has technically changed. Reviewed By: sergey Differential Revision: https://developer.blender.org/D10638
Diffstat (limited to 'source/blender/imbuf')
-rw-r--r--source/blender/imbuf/intern/anim_movie.c333
1 files changed, 185 insertions, 148 deletions
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index d36c7bbe486..3a19bc36f6c 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -969,44 +969,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 +1011,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 +1032,211 @@ 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");
-
- ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+ " 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");
}
- else if (position != anim->curposition + 1) {
- int64_t pos;
- int ret;
+}
- if (tc_index) {
- uint64_t dts;
+/* 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;
- pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index);
- dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index);
+ int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position);
- 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 pos;
+ int ret;
- if (ffmpeg_seek_by_byte(anim->pFormatCtx)) {
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE pos\n");
+ if (tc_index) {
+ int new_frame_index = IMB_indexer_get_frame_index(tc_index, position);
+ uint64_t dts;
- 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);
- }
- }
- else {
- pos = (int64_t)position * AV_TIME_BASE / frame_rate;
+ 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,
- "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, "TC INDEX seek pos = %" PRId64 "\n", pos);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %" PRIu64 "\n", dts);
- if (pos < 0) {
- pos = 0;
- }
+ if (ffmpeg_seek_by_byte(anim->pFormatCtx)) {
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE pos\n");
- if (st_time != AV_NOPTS_VALUE) {
- pos += st_time;
- }
+ 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);
+ }
+ }
+ else {
+ pos = (int64_t)(position)*AV_TIME_BASE / frame_rate;
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos);
+ 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);
- ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
+ if (pos < 0) {
+ pos = 0;
}
- 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);
+ if (st_time != AV_NOPTS_VALUE) {
+ pos += st_time;
}
- avcodec_flush_buffers(anim->pCodecCtx);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos);
- anim->next_pts = -1;
+ ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
+ }
- if (anim->next_packet.stream_index == anim->videoStream) {
- av_free_packet(&anim->next_packet);
- anim->next_packet.stream_index = -1;
- }
+ 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;
- /* memset(anim->pFrame, ...) ?? */
+ if (anim->next_packet.stream_index == anim->videoStream) {
+ av_free_packet(&anim->next_packet);
+ anim->next_packet.stream_index = -1;
+ }
- if (ret >= 0) {
- ffmpeg_decode_video_frame_scan(anim, pts_to_search);
- }
+ /* memset(anim->pFrame, ...) ?? */
+ if (ret < 0) {
+ /* Seek failed. */
+ return;
+ }
+
+ 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");