How to change metadata with ffmpeg/avconv without creating a new file?
Question:
I am writing a python script for producing audio and video podcasts. There are a bunch of recorded media files (audio and video) and text files containing the meta information.
Now I want to program a function which shall add the information from the meta data text files to all media files (the original and the converted ones). Because I have to handle many different file formats (wav
, flac
, mp3
, mp4
, ogg
, ogv
…) it would be great to have a tool which add meta data to arbitrary formats.
My Question:
How can I change the metadata of a file with ffmpeg/avconv
without changing the audio or video of it and without creating a new file? Is there another commandline/python tool which would do the job for me?
What I tried so far:
I thought ffmpeg/avconv
could be such a tool, because it can handle nearly all media formats. I hoped, that if I set -i input_file
and the output_file
to the same file, ffmpeg/avconv
will be smart enough to leave the file unchanged. Then I could set -metadata key=value
and just the metadata will be changed.
But I noticed, that if I type avconv -i test.mp3 -metadata title='Test title' test.mp3
the audio test.mp3
will be reconverted in another bitrate.
So I thought to use -c copy
to copy all video and audio information. Unfortunately also this does not work:
:~$ du -h test.wav # test.wav is 303 MB big
303M test.wav
:~$ avconv -i test.wav -c copy -metadata title='Test title' test.wav
avconv version 0.8.3-4:0.8.3-0ubuntu0.12.04.1, Copyright (c) 2000-2012 the
Libav developers
built on Jun 12 2012 16:37:58 with gcc 4.6.3
[wav @ 0x846b260] max_analyze_duration reached
Input #0, wav, from 'test.wav':
Duration: 00:29:58.74, bitrate: 1411 kb/s
Stream #0.0: Audio: pcm_s16le, 44100 Hz, 2 channels, s16, 1411 kb/s
File 'test.wav' already exists. Overwrite ? [y/N] y
Output #0, wav, to 'test.wav':
Metadata:
title : Test title
encoder : Lavf53.21.0
Stream #0.0: Audio: pcm_s16le, 44100 Hz, 2 channels, 1411 kb/s
Stream mapping:
Stream #0:0 -> #0:0 (copy)
Press ctrl-c to stop encoding
size= 896kB time=5.20 bitrate=1411.3kbits/s
video:0kB audio:896kB global headers:0kB muxing overhead 0.005014%
:~$ du -h test.wav # file size of test.wav changed dramatically
900K test.wav
You see, that I cannot use -c copy
if input_file
and output_file
are the same. Of course I could produce a temporarily file:
:-$ avconv -i test.wav -c copy -metadata title='Test title' test_temp.mp3
:-$ mv test_tmp.mp3 test.mp3
But this solution would create (temporarily) a new file on the filesystem and is therefore not preferable.
Answers:
You can do this with FFmpeg like so:
ffmpeg -i input.avi -metadata key=value -codec copy output.avi
Example:
$ du -h test.mov
27M test.mov
$ ffprobe -loglevel quiet -show_format out.mov | grep title # nothing found
$ ffmpeg -loglevel quiet -i test.mov -codec copy -metadata title="My title" out.mov
$ du -h out.mov
27M out.mov
$ ffprobe -loglevel quiet -show_format out.mov | grep title
TAG_title=My title
See the documentation for -metadata
and on stream copying for more information.
Note also that not all formats allow setting arbitrary metadata, for, e.g., Quicktime doing -metadata title="my title"
does what you’d expect, but -metadata foo=bux
does nothing.
I asked on the mailing list of avconv and got the following answer:
„No, it’s not possible [to change the metadata without creating a new file], neither libavformat API nor avconv design allows for in-place editing of files.“
Since changing the metadata will change the length of the file, and I expect the metadata is near the beginning of the file, the audio and video would start at a different offset from the beginning of the file. So you cannot alter the metadata without first creating a temporary file, then renaming files after.
If the new metadata were exactly the same size, and you know where the metadata was located in the container (file) you might be able to use a hex editor of some kind to simply replace characters. Good luck with that.
Also. you might be able to put shorter data in place directly if you paded with nulls, but this might be problematic for some players.
With ffmpeg
, you will always have to copy to a new file. But it has the advantage that it can handle many different formats.
The alternative, without copying the whole file, depends on the format. For many, there are tools wich will allow you to edit metadata "in-place".
exiftool
for many formats
metaflac
for FLAC
MP4Box
for .mp4
eyeD3
, mid3v2
, etc. for MP3
- etc.
So you would really have to check for what works for you for each specific file format.
You can also make something like this that deletes the file on success and rename the output
ffmpeg -i default.mp4 -metadata title="my title" -codec copy output.mp4 && mv output.mp4 default.mp4
If like me you have a lot of files with long name (and spaces) to rename, you can save file name and title as variable before and execute always same ffmpeg
command after:
input="My default video.mp4" && title="My title video"
ffmpeg -i "$input" -metadata title="$title" -codec copy output.mp4 && mv output.mp4 "$input"
If you have ffmpeg
< v.3.2, you can prevent Codec for stream 0 does not use global headers but container format requires global headers
warning by adding -flags +global_header
flag:
ffmpeg -i "$input" -metadata title="$title" -codec copy -flags +global_header output.mp4 && mv output.mp4 "$input"
Note: I also have to change rights to retrieve same file as before (sudo
may be needed):
chmod 774 "$input" && chown admin:users "$input"
This depends on the physical location of the metadata in the file. If the data is at the head of the file, you might be able to ‘slice’ that section off and edit the data then ‘splice’ the sections back together. This would only be useful if you had a lot of files to do in a batch. If only one is to be edited, use one of the two-copy methods.
I am writing a python script for producing audio and video podcasts. There are a bunch of recorded media files (audio and video) and text files containing the meta information.
Now I want to program a function which shall add the information from the meta data text files to all media files (the original and the converted ones). Because I have to handle many different file formats (wav
, flac
, mp3
, mp4
, ogg
, ogv
…) it would be great to have a tool which add meta data to arbitrary formats.
My Question:
How can I change the metadata of a file with ffmpeg/avconv
without changing the audio or video of it and without creating a new file? Is there another commandline/python tool which would do the job for me?
What I tried so far:
I thought ffmpeg/avconv
could be such a tool, because it can handle nearly all media formats. I hoped, that if I set -i input_file
and the output_file
to the same file, ffmpeg/avconv
will be smart enough to leave the file unchanged. Then I could set -metadata key=value
and just the metadata will be changed.
But I noticed, that if I type avconv -i test.mp3 -metadata title='Test title' test.mp3
the audio test.mp3
will be reconverted in another bitrate.
So I thought to use -c copy
to copy all video and audio information. Unfortunately also this does not work:
:~$ du -h test.wav # test.wav is 303 MB big
303M test.wav
:~$ avconv -i test.wav -c copy -metadata title='Test title' test.wav
avconv version 0.8.3-4:0.8.3-0ubuntu0.12.04.1, Copyright (c) 2000-2012 the
Libav developers
built on Jun 12 2012 16:37:58 with gcc 4.6.3
[wav @ 0x846b260] max_analyze_duration reached
Input #0, wav, from 'test.wav':
Duration: 00:29:58.74, bitrate: 1411 kb/s
Stream #0.0: Audio: pcm_s16le, 44100 Hz, 2 channels, s16, 1411 kb/s
File 'test.wav' already exists. Overwrite ? [y/N] y
Output #0, wav, to 'test.wav':
Metadata:
title : Test title
encoder : Lavf53.21.0
Stream #0.0: Audio: pcm_s16le, 44100 Hz, 2 channels, 1411 kb/s
Stream mapping:
Stream #0:0 -> #0:0 (copy)
Press ctrl-c to stop encoding
size= 896kB time=5.20 bitrate=1411.3kbits/s
video:0kB audio:896kB global headers:0kB muxing overhead 0.005014%
:~$ du -h test.wav # file size of test.wav changed dramatically
900K test.wav
You see, that I cannot use -c copy
if input_file
and output_file
are the same. Of course I could produce a temporarily file:
:-$ avconv -i test.wav -c copy -metadata title='Test title' test_temp.mp3
:-$ mv test_tmp.mp3 test.mp3
But this solution would create (temporarily) a new file on the filesystem and is therefore not preferable.
You can do this with FFmpeg like so:
ffmpeg -i input.avi -metadata key=value -codec copy output.avi
Example:
$ du -h test.mov
27M test.mov
$ ffprobe -loglevel quiet -show_format out.mov | grep title # nothing found
$ ffmpeg -loglevel quiet -i test.mov -codec copy -metadata title="My title" out.mov
$ du -h out.mov
27M out.mov
$ ffprobe -loglevel quiet -show_format out.mov | grep title
TAG_title=My title
See the documentation for -metadata
and on stream copying for more information.
Note also that not all formats allow setting arbitrary metadata, for, e.g., Quicktime doing -metadata title="my title"
does what you’d expect, but -metadata foo=bux
does nothing.
I asked on the mailing list of avconv and got the following answer:
„No, it’s not possible [to change the metadata without creating a new file], neither libavformat API nor avconv design allows for in-place editing of files.“
Since changing the metadata will change the length of the file, and I expect the metadata is near the beginning of the file, the audio and video would start at a different offset from the beginning of the file. So you cannot alter the metadata without first creating a temporary file, then renaming files after.
If the new metadata were exactly the same size, and you know where the metadata was located in the container (file) you might be able to use a hex editor of some kind to simply replace characters. Good luck with that.
Also. you might be able to put shorter data in place directly if you paded with nulls, but this might be problematic for some players.
With ffmpeg
, you will always have to copy to a new file. But it has the advantage that it can handle many different formats.
The alternative, without copying the whole file, depends on the format. For many, there are tools wich will allow you to edit metadata "in-place".
exiftool
for many formatsmetaflac
for FLACMP4Box
for .mp4eyeD3
,mid3v2
, etc. for MP3- etc.
So you would really have to check for what works for you for each specific file format.
You can also make something like this that deletes the file on success and rename the output
ffmpeg -i default.mp4 -metadata title="my title" -codec copy output.mp4 && mv output.mp4 default.mp4
If like me you have a lot of files with long name (and spaces) to rename, you can save file name and title as variable before and execute always same ffmpeg
command after:
input="My default video.mp4" && title="My title video"
ffmpeg -i "$input" -metadata title="$title" -codec copy output.mp4 && mv output.mp4 "$input"
If you have ffmpeg
< v.3.2, you can prevent Codec for stream 0 does not use global headers but container format requires global headers
warning by adding -flags +global_header
flag:
ffmpeg -i "$input" -metadata title="$title" -codec copy -flags +global_header output.mp4 && mv output.mp4 "$input"
Note: I also have to change rights to retrieve same file as before (sudo
may be needed):
chmod 774 "$input" && chown admin:users "$input"
This depends on the physical location of the metadata in the file. If the data is at the head of the file, you might be able to ‘slice’ that section off and edit the data then ‘splice’ the sections back together. This would only be useful if you had a lot of files to do in a batch. If only one is to be edited, use one of the two-copy methods.