Fix #107493, #98480: video rotation metadata is not read

Videos files recorded on most phones were coming in sideways; files
recorded on some laptop cameras were coming in upside down. In both
cases the display_matrix metadata of the video stream was not applied.

This required changing internal container format of movie proxies to
be MP4 instead of AVI, since AVI does not support rotation metadata.
File extension kept at ".avi" to not disturb existing expectations.

It might be better to "bake" rotation into proxies while building them,
but right now we do not have a trivial way of doing that.

Note that this is a potential behavior change; if someone
already had manually rotated video strips, they would have
to undo that rotation now.

Pull Request: https://projects.blender.org/blender/blender/pulls/130455
This commit is contained in:
Aras Pranckevicius
2024-11-27 17:08:37 +01:00
committed by Aras Pranckevicius
parent 0ab0f41c1e
commit 611940805e
4 changed files with 55 additions and 3 deletions

View File

@@ -17,6 +17,7 @@
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/cpu.h>
#include <libavutil/display.h>
#include <libswscale/swscale.h>
/* 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
*

View File

@@ -34,6 +34,7 @@ struct ImBufAnim {
double frs_sec_base;
double start_offset;
int x, y;
int video_rotation;
/* for number */
char filepath[1024];

View File

@@ -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;
}

View File

@@ -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) {