Opened 5 years ago

Closed 18 months ago

#8329 closed defect (fixed)

Rotation metadata not overridden when the input video has rotation metadata set to 180

Reported by: hungryTux Owned by:
Priority: normal Component: ffmpeg
Version: unspecified Keywords:
Cc: deim@seznam.cz Blocked By:
Blocking: Reproduced by developer: no
Analyzed by developer: no

Description

Summary of the bug:

I have a MOV file that I'm trying to transcode using the -noautorotate and -metadata:s:v rotate=0 flags. I was expecting the output file to have rotation metadata set to 0, but it appears the rotation metadata is just getting copied over from the input file.

Version of FFMPEG: 3.4.5

Complete debug logs here: https://paste.ee/p/EtqKm

How to reproduce:
ffprobe output on input file:

% ./ffprobe -v quiet -print_format json -show_format -show_streams 2019-02-11_16-51-38_000.MOV 
{
    "streams": [
        {
            "index": 0,
            "codec_name": "hevc",
            "codec_long_name": "H.265 / HEVC (High Efficiency Video Coding)",
            "profile": "Main",
            "codec_type": "video",
            "codec_time_base": "67/1995",
            "codec_tag_string": "hvc1",
            "codec_tag": "0x31637668",
            "width": 1920,
            "height": 1080,
            "coded_width": 1920,
            "coded_height": 1088,
            "has_b_frames": 2,
            "sample_aspect_ratio": "0:1",
            "display_aspect_ratio": "0:1",
            "pix_fmt": "yuvj420p",
            "level": 123,
            "color_range": "pc",
            "refs": 1,
            "r_frame_rate": "30/1",
            "avg_frame_rate": "1995/67",
            "time_base": "1/600",
            "start_pts": -20,
            "start_time": "-0.033333",
            "duration_ts": 21082,
            "duration": "35.136667",
            "bit_rate": "15000382",
            "nb_frames": "1064",
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "rotate": "180",
                "creation_time": "2019-02-12T01:33:04.000000Z",
                "language": "und",
                "handler_name": "Core Media Data Handler",
                "encoder": "HEVC"
            },
            "side_data_list": [
                {
                    "side_data_type": "Display Matrix",
                    "displaymatrix": "\n00000000:       -65536           0           0\n00000001:            0      -65536           0\n00000002:            0           0  1073741824\n",
                    "rotation": -180
                }
            ]
        }
    ],
    "format": {
        "filename": "2019-02-11_16-51-38_000.MOV",
        "nb_streams": 1,
        "nb_programs": 0,
        "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
        "format_long_name": "QuickTime / MOV",
        "start_time": "-0.033333",
        "duration": "35.136667",
        "size": "67019028",
        "bit_rate": "15259051",
        "probe_score": 100,
        "tags": {
            "major_brand": "qt  ",
            "minor_version": "0",
            "compatible_brands": "qt  ",
            "creation_time": "2019-02-12T01:33:04.000000Z",
            "com.apple.quicktime.location.ISO6709": "+47.6471-122.3589+030.650/",
            "com.apple.quicktime.make": "Apple",
            "com.apple.quicktime.model": "iPhone XS",
            "com.apple.quicktime.software": "12.1.4",
            "com.apple.quicktime.creationdate": "2019-02-11T16:51:38-0800"
        }
    }
}

Command used to transcode the input

% ./ffmpeg -loglevel debug -y -noautorotate -i 2019-02-11_16-51-38_000.MOV -threads 0 -map_chapters -1 -f mp4 -movflags faststart -filter_complex '[0:v:0]yadif=deint=interlaced,scale=1280:720:flags=bicubic,setdar=1.7778[v0];[v0]concat=n=1:v=1:a=0[cat_v]' -an -sn -map [cat_v] -vcodec libx264 -profile:v main -level 3.1 -maxrate 2048k -bufsize 20480k -preset medium -crf 22 -x264opts ref=3:keyint=90 -r 24.0 -vsync 1 -metadata:s:v rotate=0 -pix_fmt yuv420p -metadata:s:v language=eng test-mov.mp4

ffprobe output on the output file. (Notice the rotation info is copied over)

%./ffprobe -v quiet -print_format json -show_format -show_streams test-mov.mp4 
{
    "streams": [
        {
            "index": 0,
            "codec_name": "h264",
            "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
            "profile": "Main",
            "codec_type": "video",
            "codec_time_base": "1/48",
            "codec_tag_string": "avc1",
            "codec_tag": "0x31637661",
            "width": 1280,
            "height": 720,
            "coded_width": 1280,
            "coded_height": 720,
            "has_b_frames": 2,
            "sample_aspect_ratio": "1:1",
            "display_aspect_ratio": "16:9",
            "pix_fmt": "yuv420p",
            "level": 31,
            "chroma_location": "left",
            "refs": 1,
            "is_avc": "true",
            "nal_length_size": "4",
            "r_frame_rate": "24/1",
            "avg_frame_rate": "24/1",
            "time_base": "1/12288",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 433152,
            "duration": "35.250000",
            "bit_rate": "2278083",
            "bits_per_raw_sample": "8",
            "nb_frames": "846",
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "rotate": "180",
                "language": "eng",
                "handler_name": "VideoHandler"
            },
            "side_data_list": [
                {
                    "side_data_type": "Display Matrix",
                    "displaymatrix": "\n00000000:       -65536           0           0\n00000001:            0      -65536           0\n00000002:            0           0  1073741824\n",
                    "rotation": -180
                }
            ]
        }
    ],
    "format": {
        "filename": "test-mov.mp4",
        "nb_streams": 1,
        "nb_programs": 0,
        "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
        "format_long_name": "QuickTime / MOV",
        "start_time": "0.000000",
        "duration": "35.250000",
        "size": "10048917",
        "bit_rate": "2280605",
        "probe_score": 100,
        "tags": {
            "major_brand": "isom",
            "minor_version": "512",
            "compatible_brands": "isomiso2avc1mp41",
            "encoder": "Lavf57.83.100"
        }
    }
}

ffmpeg version:

% ./ffmpeg -version
ffmpeg version 3.4.5 Copyright (c) 2000-2018 the FFmpeg developers
built with gcc 4.1.2 (GCC) 20070626 (Red Hat 4.1.2-14)
configuration: --prefix=/local/p4clients/pkgbuild-z8TTk/workspace/src/FFmpeg/build/private/install --pkg-config-flags=--static --enable-gpl --enable-nonfree --enable-libx264 --enable-libfdk-aac --enable-libvpx --enable-libspeex --enable-encoder=vorbis --enable-libvorbis --enable-libmp3lame --enable-static --disable-protocol=hls --disable-demuxer=hls --disable-decoder=prores --disable-decoder=prores_lgpl --disable-ffplay --disable-ffserver --disable-shared
libavutil      55. 78.100 / 55. 78.100
libavcodec     57.107.100 / 57.107.100
libavformat    57. 83.100 / 57. 83.100
libavdevice    57. 10.100 / 57. 10.100
libavfilter     6.107.100 /  6.107.100
libswscale      4.  8.100 /  4.  8.100
libswresample   2.  9.100 /  2.  9.100
libpostproc    54.  7.100 / 54.  7.100

This is not reproducible on all input files with rotation info set. I tried the same command out with a file containing rotation metadata set to 90 degrees, and in that case the rotation metadata wasn't copied over.

Change History (8)

comment:1 by hungryTux, 5 years ago

I couldn't attach the input file due to file size limitation. It can be downloaded from here: https://drive.google.com/open?id=1RDqXApLmB_gPPIFij00w6r0Qv0gLqa0t

comment:2 by Balling, 5 years ago

Please recheck with latest version. https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-20191022-0b8956b-win64-static.zip
Only it is supported on bug tracker. Thanks.

comment:3 by hungryTux, 5 years ago

It is also reproducible with the latest version. I made use of the Mac OS binary, since I do not have access to a Windows machine.

Complete logs: https://paste.ee/p/ds9MM

ffmpeg version:

%./ffmpeg -version
ffmpeg version git-2019-10-23-1f327f5 Copyright (c) 2000-2019 the FFmpeg developers
built with Apple clang version 11.0.0 (clang-1100.0.33.8)
configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-appkit --enable-avfoundation --enable-coreimage --enable-audiotoolbox
libavutil      56. 35.101 / 56. 35.101
libavcodec     58. 59.102 / 58. 59.102
libavformat    58. 33.100 / 58. 33.100
libavdevice    58.  9.100 / 58.  9.100
libavfilter     7. 64.100 /  7. 64.100
libswscale      5.  6.100 /  5.  6.100
libswresample   3.  6.100 /  3.  6.100
libpostproc    55.  6.100 / 55.  6.100

ffmpeg command:

%./ffmpeg -loglevel debug -y -noautorotate -i 2019-02-11_16-51-38_000.MOV -threads 0 -map_chapters -1 -f mp4 -movflags faststart -filter_complex '[0:v:0]yadif=deint=interlaced,scale=1280:720:flags=bicubic,setdar=1.7778[v0];[v0]concat=n=1:v=1:a=0[cat_v]' -an -sn -map [cat_v] -vcodec libx264 -profile:v main -level 3.1 -maxrate 2048k -bufsize 20480k -preset medium -crf 22 -x264opts ref=3:keyint=90 -r 24.0 -vsync 1 -metadata:s:v rotate=0 -pix_fmt yuv420p -metadata:s:v language=eng test-mov.mp4

ffprobe output on the transcoded file (rotation metadata copied over):

%./ffprobe -v quiet -print_format json -show_format -show_streams test-mov.mp4
{
    "streams": [
        {
            "index": 0,
            "codec_name": "h264",
            "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
            "profile": "Main",
            "codec_type": "video",
            "codec_time_base": "1/48",
            "codec_tag_string": "avc1",
            "codec_tag": "0x31637661",
            "width": 1280,
            "height": 720,
            "coded_width": 1280,
            "coded_height": 720,
            "has_b_frames": 2,
            "sample_aspect_ratio": "1:1",
            "display_aspect_ratio": "16:9",
            "pix_fmt": "yuv420p",
            "level": 31,
            "chroma_location": "left",
            "refs": 1,
            "is_avc": "true",
            "nal_length_size": "4",
            "r_frame_rate": "24/1",
            "avg_frame_rate": "24/1",
            "time_base": "1/12288",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 432640,
            "duration": "35.208333",
            "bit_rate": "2271865",
            "bits_per_raw_sample": "8",
            "nb_frames": "845",
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0
            },
            "tags": {
                "rotate": "180",
                "language": "eng",
                "handler_name": "VideoHandler"
            },
            "side_data_list": [
                {
                    "side_data_type": "Display Matrix",
                    "displaymatrix": "\n00000000:       -65536           0           0\n00000001:            0      -65536           0\n00000002:            0           0  1073741824\n",
                    "rotation": -180
                }
            ]
        }
    ],
    "format": {
        "filename": "test-mov.mp4",
        "nb_streams": 1,
        "nb_programs": 0,
        "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
        "format_long_name": "QuickTime / MOV",
        "start_time": "0.000000",
        "duration": "35.209000",
        "size": "10009672",
        "bit_rate": "2274343",
        "probe_score": 100,
        "tags": {
            "major_brand": "isom",
            "minor_version": "512",
            "compatible_brands": "isomiso2avc1mp41",
            "encoder": "Lavf58.33.100"
        }
    }
}

comment:4 by d3im, 4 years ago

Cc: deim@seznam.cz added
Priority: normalimportant
Version: unspecified4.2

I have exactly the same problem - source video is rotated by 180 deg. Autorotate is correct but there may be scenarios when I wanna rotate myself. In current version (4.2.2) manual rotate of this file is broken and source rotation metadata is copied to destination file.

comment:5 by Carl Eugen Hoyos, 4 years ago

Priority: importantnormal
Version: 4.2unspecified

comment:6 by d3im, 4 years ago

Priority: normalimportant
Version: unspecified4.2

The problem code is here: (in ffmpeg.c)
It only solves rotation when autorotate is set (its easy - autorotate rotates to correct rotation and sets rotate metadata to 0 deg.).

/*
         * Add global input side data. For now this is naive, and copies it
         * from the input stream's global side data. All side data should
         * really be funneled over AVFrame and libavfilter, then added back to
         * packet side data, and then potentially using the first packet for
         * global side data.
         */
        if (ist) {
            int i;
            for (i = 0; i < ist->st->nb_side_data; i++) {
                AVPacketSideData *sd = &ist->st->side_data[i];
                uint8_t *dst = av_stream_new_side_data(ost->st, sd->type, sd->size);
                if (!dst)
                    return AVERROR(ENOMEM);
                memcpy(dst, sd->data, sd->size);

                if (ist->autorotate && sd->type == AV_PKT_DATA_DISPLAYMATRIX)
                    av_display_rotation_set((uint32_t *)dst, 0);
            }
        }

This helps a little:
Set rotation to 0 autorotate and manual also.

        if (ist) {
            int i;
            for (i = 0; i < ist->st->nb_side_data; i++) {
                AVPacketSideData *sd = &ist->st->side_data[i];
                uint8_t *dst = av_stream_new_side_data(ost->st, sd->type, sd->size);
                if (!dst)
                    return AVERROR(ENOMEM);

                if (sd->type == AV_PKT_DATA_DISPLAYMATRIX) {
                	av_display_rotation_set((uint32_t *)dst, 0);
                } else {
                	memcpy(dst, sd->data, sd->size);
                }
            }
        }   

Anyway the original code should be corrected not to copy all side data. This is just a quick fix not for all situations.

comment:7 by d3im, 4 years ago

Priority: importantnormal
Version: 4.2unspecified

comment:8 by Thilo Borgmann, 18 months ago

Resolution: fixed
Status: newclosed
Note: See TracTickets for help on using tickets.