3

Context and question

I recorded a video with my MotoG3 phone Android phone and the resulting video (perhaps due to shaking the phone) plays rotated, that is, vertically, which is not something that I want. The video was saved in an MP4 container.

Upon inspecting the file that was recorded, I saw (using both ffprobe and mediainfo) that it contains a flag that indicates that the video stream is rotated 90 degrees, which explains the rotation while playing the video. Here is the output from ffprobe:

$ ffprobe -hide_banner ~/Pictures/2016/06/19/vid_20160619_170845475.mp4 
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/home/rbrito/Pictures/2016/06/19/vid_20160619_170845475.mp4':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: isommp42
    creation_time   : 2016-06-19 20:25:49
    com.android.version: 6.0
  Duration: 00:17:01.96, start: 0.000000, bitrate: 17134 kb/s
    Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080, 17000 kb/s, SAR 1:1 DAR 16:9, 29 fps, 29.42 tbr, 90k tbn, 180k tbc (default)
    Metadata:
      rotate          : 90
      creation_time   : 2016-06-19 20:25:49
      handler_name    : VideoHandle
      encoder         : MOTO
    Side data:
      displaymatrix: rotation of -90.00 degrees
    Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s (default)
    Metadata:
      creation_time   : 2016-06-19 20:25:49
      handler_name    : SoundHandle

Since this video is from one rare event with my son, I would like to minimally change the video file, preferrably, only removing the rotation metadata and nothing else.

Unfortunately, using information from other answers didn't help. I tried using:

ffmpeg -i ~/Pictures/2016/06/19/vid_20160619_170845475.mp4 -c copy -metadata:s:v:0 rotate=0 -an vid_20160619_170845475_unrotated.mp4

But other metadata, like the date etc. are not present in the resulting file. This, in particular, makes organizer programs be confused while showing the files by date, among other things.

Is there any way of minimally changing the input data (possibly remuxing it) so as to keep it as close as possible to the original data? In particular, I don't want to reencode the file.

Any tools that are available under Linux are preferred.


Extra information

Since the full output of ffmpeg was asked, here it goes. This is a pure Debian testing userland.

As a side note, ffmpeg is complaining about mismatched libraries because it is compiled two times in Debian, once without potentially problematic codecs and another with some extra codecs. I'm using the version with extra codecs, but I suppose that that doesn't matter, since only the muxing is being used here.

Metadata before remuxing

$ ffprobe  -hide_banner ~/Pictures/2016/06/19/vid_20160619_170845475.mp4
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/home/rbrito/Pictures/2016/06/19/vid_20160619_170845475.mp4':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: isommp42
    creation_time   : 2016-06-19 20:25:49
    com.android.version: 6.0
  Duration: 00:17:01.96, start: 0.000000, bitrate: 17134 kb/s
    Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080, 17000 kb/s, SAR 1:1 DAR 16:9, 29 fps, 29.42 tbr, 90k tbn, 180k tbc (default)
    Metadata:
      rotate          : 90
      creation_time   : 2016-06-19 20:25:49
      handler_name    : VideoHandle
      encoder         : MOTO
    Side data:
      displaymatrix: rotation of -90.00 degrees
    Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s (default)
    Metadata:
      creation_time   : 2016-06-19 20:25:49
      handler_name    : SoundHandle

The remuxing process

Note here that the audio metadata is not copied (which I expected, due to the -metadata:s:v:0 option) and ffmpeg tells me that it is copying the video metadata (without the rotation setting).

$ ffmpeg -hide_banner -i ~/Pictures/2016/06/19/vid_20160619_170845475.mp4 -c copy -metadata:s:v:0 rotate=0 -an vid_20160619_170845475_unrotated.mp4 
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/home/rbrito/Pictures/2016/06/19/vid_20160619_170845475.mp4':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: isommp42
    creation_time   : 2016-06-19 20:25:49
    com.android.version: 6.0
  Duration: 00:17:01.96, start: 0.000000, bitrate: 17134 kb/s
    Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080, 17000 kb/s, SAR 1:1 DAR 16:9, 29 fps, 29.42 tbr, 90k tbn, 180k tbc (default)
    Metadata:
      rotate          : 90
      creation_time   : 2016-06-19 20:25:49
      handler_name    : VideoHandle
      encoder         : MOTO
    Side data:
      displaymatrix: rotation of -90.00 degrees
    Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s (default)
    Metadata:
      creation_time   : 2016-06-19 20:25:49
      handler_name    : SoundHandle
Output #0, mp4, to 'vid_20160619_170845475_unrotated.mp4':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: isommp42
    com.android.version: 6.0
    encoder         : Lavf57.25.100
    Stream #0:0(eng): Video: h264 ([33][0][0][0] / 0x0021), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 17000 kb/s, 29 fps, 29.42 tbr, 90k tbn, 90k tbc (default)
    Metadata:
      encoder         : MOTO
      creation_time   : 2016-06-19 20:25:49
      handler_name    : VideoHandle
      rotate          : 0
Stream mapping:
  Stream #0:0 -> #0:0 (copy)
Press [q] to stop, [?] for help
frame=29640 fps=403 q=-1.0 Lsize= 2121038kB time=00:17:01.91 bitrate=17003.0kbits/s speed=13.9x    
video:2120800kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.011211%

Metadata after remuxing

Note here that there is no video metadata in the output, only container-level metadata.

$ ffprobe -hide_banner vid_20160619_170845475_unrotated.mp4 
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'vid_20160619_170845475_unrotated.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf57.25.100
  Duration: 00:17:01.95, start: 0.000000, bitrate: 17002 kb/s
    Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080, 17000 kb/s, 29 fps, 29.42 tbr, 90k tbn, 180k tbc (default)
    Metadata:
      handler_name    : VideoHandler
  • Have you seen this answer that explains how to copy just the metadata from one file to another using FFmpeg? – JakeGould Jun 24 '16 at 1:57
  • Please show the complete console output from your ffmpeg command. – llogan Jun 24 '16 at 3:52
  • @LordNeckbeard, I added all the information that you asked me to. If there's any other information that I should provide, please let me know. – rbrito Jun 24 '16 at 20:41
  • 1
    When you say only partially copy, I assume you mean copy all metadata except rotation. If so, try ffmpeg -i in.mp4 -c copy -map_metadata 0 -metadata:s:v:0 rotate=0 -an out.mp4 – Gyan Jun 25 '16 at 5:39
  • 1
    @Marc.2377, I agree with you and I removed some noisy information (like the banner from ffmpeg) from the question. It should be more readable now (just curating the question for posterity). – rbrito May 3 at 20:07
3

FFmpeg by default doesn't transfer most user metadata over, but it does transfer fields like rotation tag.

The command below instructs FFmpeg to transfer all metadata over but then overrides the rotation tag value.

ffmpeg -i in.mp4 -c copy -map_metadata 0 -metadata:s:v:0 rotate=0 -an out.mp4
0
  • Install mutagen python library:

    python3.6 -m pip install --user mutagen
    

    or:

    python2.7 -m pip install --user mutagen
    

    Mutagen is a Python module to handle audio metadata.

  • Save the below content as metadata_cleaner.py but you can name it anything.

  • Change the VIDEO_FOLDER = 'your_full_path_for_your_videos_folder' variable.

  • Modify the attributes you want to overwrite between the lines where it says "Your changes".

  • Run python metadata_cleaner.py anywhere.

Here is the script:

import os
from mutagen.mp4 import MP4
## Can import MP3 as well if you want to modify MP3 files

## Folder full path to the files you want to modify:

FILES_FOLDER = "/media/removable/64GB-Micro/FilesToClean"

def get_all_files(path):
    """Returns all the files in the path as a list
    """
    return os.listdir(path)

def clean_metadata(videos_dir, files):
    """
    Receives two variables videos_dir (full file folder path) and files (list)
    Iterates over all the files in the directory and creates full paths for 
    every file using the videos_dir path and os.path.join function.
    """
    for file_name in files:
        file_path = os.path.join(videos_dir, file_name)

        ## create MP4 video_file instance

        video_file = MP4(file_path)

        ## This line will print out all key-value for the video_file-s:
        ## Useful if you don't know which attributes you want to modify yet

        print(video_file)

        ## In this example I wanted to clean the ART, aArt = Artist metadata
        ## and nam = Song Name metadata
        ## These prints will show artists and titles for each file before clean
        ## -------------------------- Your changes ---------------------------
        print("file_name: {}".format(file_name))
        print("Old ART: {}".format(video_file.get('\xa9ART', "Key not found")))
        print("Old aArt: {}".format(video_file.get('aART', "Key not found")))
        print("Old nam: {}".format(video_file.get('\xa9nam', "Key not found")))

        ## clean artist and name
        ## This part will overwrite the artist and song name attributes

        video_file['\xa9ART'] = ''
        video_file['aART'] = ''
        video_file['\xa9nam'] = ''
        ## -------------------------- Your changes ---------------------------
        ## save new metadata

        video_file.save()

        ## show video_file artist and title after clean
        ## Just to see if our actions were successful print out song metadata
        ## again after the changes

        print("New ART: {}".format(video_file.get('\xa9ART', "Key not found")))
        print("New aArt: {}".format(video_file.get('aART', "Key not found")))
        print("New nam: {}".format(video_file.get('\xa9nam', "Key not found")))


if __name__ == '__main__':
    ## load all the files into files variable as a list

    files = get_all_files(FILES_FOLDER)

    ## run clean_metadata function to clean all the files in the VIDEO_FOLDER
    ## path
    clean_metadata(videos_dir=FILES_FOLDER, files=files)
  • Some explanation as to what this does and how to implement it will make it more of an answer. Right now a) the question already has an answer, and b) its not immediately clear what this does. – music2myear Jan 15 at 22:15
  • The question asks for a solution using a tool, not a program.  And it has an accepted answer using a command. (1) What does your program do that is different from the ffmpeg command?  Specifically, how/why is it better? (2) How does one run your program?  OK, I see a couple of (what look like) comments near the beginning that look like commands, but they aren’t very clear.  You should have at least a couple of sentences outside of the code that answer these questions.  … (Cont’d) – Scott Jan 15 at 22:29
  • (Cont’d) … (3) And why would the user need sudo to read a file that he has read access to, and create an output file in a directory where he has write access? … … … … … … Please do not respond in comments; edit your answer to make it clearer and more complete. – Scott Jan 15 at 22:29
  • Good points everyone. I modified my answer. The first solution was not working for me. Question asked for "How to delete a single metadata item", in my script you can list all attributes, choose and delete single metadata attribute, or any number of attributes, even replace the values to your liking. Mutagen is much lightweight than ffmpeg. Sudo is not used anywhere to open files, only used for mutagen installation. – jturi Feb 13 at 10:10
  • I separated the instructions from the script, which should make it easier to read. Also note that you never need sudo (and it's not recommended) to install packages with Python. – slhck Feb 13 at 10:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.