diff --git a/intern/ffmpeg/ffmpeg_compat.h b/intern/ffmpeg/ffmpeg_compat.h index 9c06c8a6d67..1eb6c3ba2dc 100644 --- a/intern/ffmpeg/ffmpeg_compat.h +++ b/intern/ffmpeg/ffmpeg_compat.h @@ -48,6 +48,16 @@ #include +/* Stupid way to distinguish FFmpeg from Libav: + * - FFmpeg's MICRO version starts from 100 and goes up, while + * - Libav's micro is always below 100. + */ +#if LIBAVCODEC_VERSION_MICRO >= 100 +# define AV_USING_FFMPEG +#else +# define AV_USING_LIBAV +#endif + #if (LIBAVFORMAT_VERSION_MAJOR > 52) || ((LIBAVFORMAT_VERSION_MAJOR >= 52) && (LIBAVFORMAT_VERSION_MINOR >= 105)) # define FFMPEG_HAVE_AVIO 1 #endif @@ -428,8 +438,45 @@ void av_frame_free(AVFrame **frame) #endif FFMPEG_INLINE -AVRational av_get_r_frame_rate_compat(const AVStream *stream) +const char* av_get_metadata_key_value(AVDictionary *metadata, const char *key) { + if (metadata == NULL) { + return NULL; + } + AVDictionaryEntry *tag = NULL; + while ((tag = av_dict_get(metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { + if (!strcmp(tag->key, key)) { + return tag->value; + } + } + return NULL; +} + +FFMPEG_INLINE +bool av_check_encoded_with_ffmpeg(AVFormatContext *ctx) +{ + const char* encoder = av_get_metadata_key_value(ctx->metadata, "ENCODER"); + if (encoder != NULL && !strncmp(encoder, "Lavf", 4)) { + return true; + } + return false; +} + +FFMPEG_INLINE +AVRational av_get_r_frame_rate_compat(AVFormatContext *ctx, + const AVStream *stream) +{ + /* If the video is encoded with FFmpeg and we are decoding with FFmpeg + * as well it seems to be more reliable to use r_frame_rate (tbr). + * + * For other cases we fall back to avg_frame_rate (fps) when possible. + */ +#ifdef AV_USING_FFMPEG + if (av_check_encoded_with_ffmpeg(ctx)) { + return stream->r_frame_rate; + } +#endif + #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 23, 1) /* For until r_frame_rate was deprecated use it. */ return stream->r_frame_rate; diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 5472cae3ef2..a770b34ecc6 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -511,7 +511,7 @@ static int startffmpeg(struct anim *anim) return -1; } - frame_rate = av_get_r_frame_rate_compat(pFormatCtx->streams[videoStream]); + frame_rate = av_get_r_frame_rate_compat(pFormatCtx, pFormatCtx->streams[videoStream]); if (pFormatCtx->streams[videoStream]->nb_frames != 0) { anim->duration = pFormatCtx->streams[videoStream]->nb_frames; } @@ -989,7 +989,7 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, v_st = anim->pFormatCtx->streams[anim->videoStream]; - frame_rate = av_q2d(av_get_r_frame_rate_compat(v_st)); + frame_rate = av_q2d(av_get_r_frame_rate_compat(anim->pFormatCtx, v_st)); st_time = anim->pFormatCtx->start_time; pts_time_base = av_q2d(v_st->time_base); diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 009258079ee..eaf4dfd84b4 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -909,7 +909,7 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, stream_size = avio_size(context->iFormatCtx->pb); - context->frame_rate = av_q2d(av_get_r_frame_rate_compat(context->iStream)); + context->frame_rate = av_q2d(av_get_r_frame_rate_compat(context->iFormatCtx, context->iStream)); context->pts_time_base = av_q2d(context->iStream->time_base); while (av_read_frame(context->iFormatCtx, &next_packet) >= 0) { diff --git a/source/gameengine/VideoTexture/VideoFFmpeg.cpp b/source/gameengine/VideoTexture/VideoFFmpeg.cpp index 10196804656..11ec97ca5f8 100644 --- a/source/gameengine/VideoTexture/VideoFFmpeg.cpp +++ b/source/gameengine/VideoTexture/VideoFFmpeg.cpp @@ -228,7 +228,7 @@ int VideoFFmpeg::openStream(const char *filename, AVInputFormat *inputFormat, AV codecCtx->frame_rate_base=1000; m_baseFrameRate = (double)codecCtx->frame_rate / (double)codecCtx->frame_rate_base; #else - m_baseFrameRate = av_q2d(av_get_r_frame_rate_compat(formatCtx->streams[videoStream])); + m_baseFrameRate = av_q2d(av_get_r_frame_rate_compat(formatCtx, formatCtx->streams[videoStream])); #endif if (m_baseFrameRate <= 0.0) m_baseFrameRate = defFrameRate;