diff --git a/intern/ffmpeg/ffmpeg_compat.h b/intern/ffmpeg/ffmpeg_compat.h index 482ec1e6a3d..4d5b30eef37 100644 --- a/intern/ffmpeg/ffmpeg_compat.h +++ b/intern/ffmpeg/ffmpeg_compat.h @@ -17,6 +17,7 @@ #include #include #include +#include #include /* Check if our ffmpeg is new enough, avoids user complaints. @@ -168,6 +169,44 @@ FFMPEG_INLINE size_t ffmpeg_get_buffer_alignment() return align; } +FFMPEG_INLINE void ffmpeg_copy_display_matrix(const AVStream *src, AVStream *dst) +{ +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(60, 29, 100) + const AVPacketSideData *src_matrix = av_packet_side_data_get(src->codecpar->coded_side_data, + src->codecpar->nb_coded_side_data, + AV_PKT_DATA_DISPLAYMATRIX); + if (src_matrix != nullptr) { + uint8_t *dst_matrix = (uint8_t *)av_memdup(src_matrix->data, src_matrix->size); + av_packet_side_data_add(&dst->codecpar->coded_side_data, + &dst->codecpar->nb_coded_side_data, + AV_PKT_DATA_DISPLAYMATRIX, + dst_matrix, + src_matrix->size, + 0); + } +#endif +} + +FFMPEG_INLINE int ffmpeg_get_video_rotation(const AVStream *stream) +{ +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(60, 29, 100) + const AVPacketSideData *src_matrix = av_packet_side_data_get( + stream->codecpar->coded_side_data, + stream->codecpar->nb_coded_side_data, + AV_PKT_DATA_DISPLAYMATRIX); + if (src_matrix != nullptr) { + /* ffmpeg reports rotation in [-180..+180] range; our image rotation + * uses different direction and [0..360] range. */ + double theta = -av_display_rotation_get((const int32_t *)src_matrix->data); + if (theta < 0.0) { + theta += 360.0; + } + return int(theta); + } +#endif + return 0; +} + /* -------------------------------------------------------------------- */ /** \name Deinterlace code block * diff --git a/source/blender/imbuf/intern/IMB_anim.hh b/source/blender/imbuf/intern/IMB_anim.hh index 7419256d6e2..48fff879974 100644 --- a/source/blender/imbuf/intern/IMB_anim.hh +++ b/source/blender/imbuf/intern/IMB_anim.hh @@ -34,6 +34,7 @@ struct ImBufAnim { double frs_sec_base; double start_offset; int x, y; + int video_rotation; /* for number */ char filepath[1024]; diff --git a/source/blender/imbuf/intern/anim_movie.cc b/source/blender/imbuf/intern/anim_movie.cc index d8a8af75db3..125713a7652 100644 --- a/source/blender/imbuf/intern/anim_movie.cc +++ b/source/blender/imbuf/intern/anim_movie.cc @@ -356,6 +356,8 @@ static int startffmpeg(ImBufAnim *anim) anim->x = pCodecCtx->width; anim->y = pCodecCtx->height; + anim->video_rotation = ffmpeg_get_video_rotation(video_stream); + /* Decode >8bit videos into floating point image. */ anim->is_float = calc_pix_fmt_max_component_bits(pCodecCtx->pix_fmt) > 8; @@ -652,6 +654,11 @@ static void ffmpeg_postprocess(ImBufAnim *anim, AVFrame *input, ImBuf *ibuf) if (filter_y) { IMB_filtery(ibuf); } + + /* Rotate video if display matrix is multiple of 90 degrees. */ + if (ELEM(anim->video_rotation, 90, 180, 270)) { + IMB_rotate_orthogonal(ibuf, anim->video_rotation); + } } static void final_frame_log(ImBufAnim *anim, @@ -1355,10 +1362,10 @@ bool IMB_anim_get_fps(const ImBufAnim *anim, int IMB_anim_get_image_width(ImBufAnim *anim) { - return anim->x; + return ELEM(anim->video_rotation, 90, 270) ? anim->y : anim->x; } int IMB_anim_get_image_height(ImBufAnim *anim) { - return anim->y; + return ELEM(anim->video_rotation, 90, 270) ? anim->x : anim->y; } diff --git a/source/blender/imbuf/intern/indexer.cc b/source/blender/imbuf/intern/indexer.cc index 30416de47bd..a3c4ea165d1 100644 --- a/source/blender/imbuf/intern/indexer.cc +++ b/source/blender/imbuf/intern/indexer.cc @@ -470,7 +470,10 @@ static proxy_output_ctx *alloc_proxy_output_ffmpeg(ImBufAnim *anim, } rv->of = avformat_alloc_context(); - rv->of->oformat = av_guess_format("avi", nullptr, nullptr); + /* Note: we keep on using .avi extension for proxies, + * but actual container can not be AVI, since it does not support + * video rotation metadata. */ + rv->of->oformat = av_guess_format("mp4", nullptr, nullptr); rv->of->url = av_strdup(filepath); @@ -551,6 +554,8 @@ static proxy_output_ctx *alloc_proxy_output_ffmpeg(ImBufAnim *anim, avcodec_parameters_from_context(rv->st->codecpar, rv->c); + ffmpeg_copy_display_matrix(st, rv->st); + int ret = avio_open(&rv->of->pb, filepath, AVIO_FLAG_WRITE); if (ret < 0) {