diff options
Diffstat (limited to 'source/blender/imbuf/intern/indexer.c')
-rw-r--r-- | source/blender/imbuf/intern/indexer.c | 133 |
1 files changed, 127 insertions, 6 deletions
diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 7aeee2740c0..a85ba65d014 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -37,6 +37,8 @@ # include "BLI_winstuff.h" #endif +#include "PIL_time.h" + #include "IMB_anim.h" #include "IMB_indexer.h" #include "imbuf.h" @@ -814,12 +816,16 @@ typedef struct FFmpegIndexBuilderContext { double pts_time_base; int frameno, frameno_gapless; int start_pts_set; + + bool build_only_on_bad_performance; + bool building_cancelled; } FFmpegIndexBuilderContext; static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, IMB_Timecode_Type tcs_in_use, IMB_Proxy_Size proxy_sizes_in_use, - int quality) + int quality, + bool build_only_on_bad_performance) { FFmpegIndexBuilderContext *context = MEM_callocN(sizeof(FFmpegIndexBuilderContext), "FFmpeg index builder context"); @@ -831,6 +837,7 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, context->proxy_sizes_in_use = proxy_sizes_in_use; context->num_proxy_sizes = IMB_PROXY_MAX_SLOT; context->num_indexers = IMB_TC_MAX_SLOT; + context->build_only_on_bad_performance = build_only_on_bad_performance; memset(context->proxy_ctx, 0, sizeof(context->proxy_ctx)); memset(context->indexer, 0, sizeof(context->indexer)); @@ -936,15 +943,17 @@ static void index_rebuild_ffmpeg_finish(FFmpegIndexBuilderContext *context, int { int i; + const bool do_rollback = stop || context->building_cancelled; + for (i = 0; i < context->num_indexers; i++) { if (context->tcs_in_use & tc_types[i]) { - IMB_index_builder_finish(context->indexer[i], stop); + IMB_index_builder_finish(context->indexer[i], do_rollback); } } for (i = 0; i < context->num_proxy_sizes; i++) { if (context->proxy_sizes_in_use & proxy_sizes[i]) { - free_proxy_output_ffmpeg(context->proxy_ctx[i], stop); + free_proxy_output_ffmpeg(context->proxy_ctx[i], do_rollback); } } @@ -1095,6 +1104,111 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, return 1; } +/* Get number of frames, that can be decoded in specified time period. */ +static int indexer_performance_get_decode_rate(FFmpegIndexBuilderContext *context, + const double time_period) +{ + AVFrame *in_frame = av_frame_alloc(); + AVPacket *packet = av_packet_alloc(); + + const double start = PIL_check_seconds_timer(); + int frames_decoded = 0; + + while (av_read_frame(context->iFormatCtx, packet) >= 0) { + if (packet->stream_index != context->videoStream) { + continue; + } + + int ret = avcodec_send_packet(context->iCodecCtx, packet); + while (ret >= 0) { + ret = avcodec_receive_frame(context->iCodecCtx, in_frame); + + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } + + if (ret < 0) { + fprintf(stderr, "Error decoding proxy frame: %s\n", av_err2str(ret)); + break; + } + frames_decoded++; + } + + const double end = PIL_check_seconds_timer(); + + if (end > start + time_period) { + break; + } + } + + avcodec_flush_buffers(context->iCodecCtx); + av_seek_frame(context->iFormatCtx, -1, 0, AVSEEK_FLAG_BACKWARD); + return frames_decoded; +} + +/* Read up to 10k movie packets and return max GOP size detected. + * Number of packets is arbitrary. It should be as large as possible, but processed within + * reasonable time period, so detected GOP size is as close to real as possible. */ +static int indexer_performance_get_max_gop_size(FFmpegIndexBuilderContext *context) +{ + AVPacket *packet = av_packet_alloc(); + + const int packets_max = 10000; + int packet_index = 0; + int max_gop = 0; + int cur_gop = 0; + + while (av_read_frame(context->iFormatCtx, packet) >= 0) { + if (packet->stream_index != context->videoStream) { + continue; + } + packet_index++; + cur_gop++; + + if (packet->flags & AV_PKT_FLAG_KEY) { + max_gop = max_ii(max_gop, cur_gop); + cur_gop = 0; + } + + if (packet_index > packets_max) { + break; + } + } + + av_seek_frame(context->iFormatCtx, -1, 0, AVSEEK_FLAG_BACKWARD); + return max_gop; +} + +/* Assess scrubbing performance of provided file. This function is not meant to be very exact. + * It compares number number of frames decoded in reasonable time with largest detected GOP size. + * Because seeking happens in single GOP, it means, that maximum seek time can be detected this + * way. + * Since proxies use GOP size of 10 frames, skip building if detected GOP size is less or + * equal. + */ +static bool indexer_need_to_build_proxy(FFmpegIndexBuilderContext *context) +{ + if (!context->build_only_on_bad_performance) { + return true; + } + + /* Make sure, that file is not cold read. */ + indexer_performance_get_decode_rate(context, 0.1); + /* Get decode rate per 100ms. This is arbitrary, but seems to be good baseline cadence of + * seeking. */ + const int decode_rate = indexer_performance_get_decode_rate(context, 0.1); + const int max_gop_size = indexer_performance_get_max_gop_size(context); + + if (max_gop_size <= 10 || max_gop_size < decode_rate) { + printf("Skipping proxy building for %s: Decoding performance is already good.\n", + context->iFormatCtx->url); + context->building_cancelled = true; + return false; + } + + return true; +} + #endif /* ---------------------------------------------------------------------- @@ -1274,7 +1388,8 @@ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Proxy_Size proxy_sizes_in_use, int quality, const bool overwrite, - GSet *file_list) + GSet *file_list, + bool build_only_on_bad_performance) { IndexBuildContext *context = NULL; IMB_Proxy_Size proxy_sizes_to_build = proxy_sizes_in_use; @@ -1328,9 +1443,13 @@ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, switch (anim->curtype) { #ifdef WITH_FFMPEG case ANIM_FFMPEG: - context = index_ffmpeg_create_context(anim, tcs_in_use, proxy_sizes_to_build, quality); + context = index_ffmpeg_create_context( + anim, tcs_in_use, proxy_sizes_to_build, quality, build_only_on_bad_performance); break; +#else + UNUSED_VARS(build_only_on_bad_performance); #endif + #ifdef WITH_AVI default: context = index_fallback_create_context(anim, tcs_in_use, proxy_sizes_to_build, quality); @@ -1358,7 +1477,9 @@ void IMB_anim_index_rebuild(struct IndexBuildContext *context, switch (context->anim_type) { #ifdef WITH_FFMPEG case ANIM_FFMPEG: - index_rebuild_ffmpeg((FFmpegIndexBuilderContext *)context, stop, do_update, progress); + if (indexer_need_to_build_proxy((FFmpegIndexBuilderContext *)context)) { + index_rebuild_ffmpeg((FFmpegIndexBuilderContext *)context, stop, do_update, progress); + } break; #endif #ifdef WITH_AVI |