diff options
Diffstat (limited to 'source/blender/imbuf')
-rw-r--r-- | source/blender/imbuf/intern/anim_movie.c | 333 |
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"); |