如何在不创建新文件的情况下使用 ffmpeg/avconv 更改元数据?

Posted

技术标签:

【中文标题】如何在不创建新文件的情况下使用 ffmpeg/avconv 更改元数据?【英文标题】:How to change metadata with ffmpeg/avconv without creating a new file? 【发布时间】:2012-07-13 13:02:01 【问题描述】:

我正在编写一个用于制作音频和视频播客的 python 脚本。有一堆录制的媒体文件(音频和视频)和包含元信息的文本文件。

现在我想编写一个函数,它将元数据文本文件中的信息添加到所有媒体文件(原始文件和转换后的文件)。因为我必须处理许多不同的文件格式(wavflacmp3mp4oggogv...)如果有一个工具可以将元数据添加到任意格式。

我的问题:

如何更改带有ffmpeg/avconv 的文件的元数据而不更改其音频或视频且不创建新文件?是否有另一个命令行/python 工具可以为我完成这项工作?

到目前为止我尝试了什么:

我认为ffmpeg/avconv 可能是这样一个工具,因为它可以处理几乎所有的媒体格式。我希望,如果我将-i input_fileoutput_file 设置为同一个文件,ffmpeg/avconv 将足够聪明以保持文件不变。然后我可以设置-metadata key=value,只更改元数据。

但我注意到,如果我输入 avconv -i test.mp3 -metadata title='Test title' test.mp3,音频 test.mp3 将被重新转换为另一种比特率。

所以我想用-c copy 来复制所有的视频和音频信息。不幸的是,这也不起作用:

:~$ 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

你看,如果input_fileoutput_file 相同,我不能使用-c copy。当然我可以生成一个临时文件:

:-$ avconv -i test.wav -c copy -metadata title='Test title' test_temp.mp3
:-$ mv test_tmp.mp3 test.mp3

但是这种解决方案会(临时)在文件系统上创建一个新文件,因此并不可取。

【问题讨论】:

【参考方案1】:

你可以像这样用 FFmpeg 做到这一点:

ffmpeg -i input.avi -metadata key=value -codec copy output.avi

例子:

$ 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

请参阅documentation for -metadata 和stream copying 了解更多信息。

另请注意,并非所有格式都允许设置任意元数据,例如,Quicktime 执行 -metadata title="my title" 会达到您的预期,但 -metadata foo=bux 不会执行任何操作。

【讨论】:

在您的解决方案中创建一个新文件。还有不创建新文件的方法吗? 澄清一点,ffmpeg 需要创建一个输出文件。您有一个输入文件,而ffmpeg 不允许直接操作输入文件。我相信这种做法是由于多媒体文件有多少,因为动态更改输入文件需要缓存整个文件。与后一个概念相关,请注意ffmpeg 不对输入文件执行任何健全性检查。 经过自己的反复试验,我建议使用 ExifTool (exiftool.org) 而不是 FFmpeg 来更改元数据。它易于使用、速度更快,并且可以选择覆盖原始文件。【参考方案2】:

我在mailing list of avconv 上询问并得到the following answer:

“不,不可能 [在不创建新文件的情况下更改元数据],libavformat API 和 avconv 设计都不允许就地编辑文件。”

【讨论】:

@NikosBaxevanis 很可能是通过输出到临时文件,删除原始文件,然后重命名新文件。在读取文件时覆盖文件可能会导致许多问题。例如,如果你这样做,ffmpeg 会破坏你的视频文件ffmpeg -i video.mkv -c:a copy -c:v copy video.mkv @ColeJohnson 这可能就是它的作用,而且它做得非常好!这对我来说很有趣,因为即使您正在播放媒体文件,您也可以即时更改其 ID3 标签!不确定它是否/如何通过在后台复制来做到这一点,但 Winamp 做得非常好! MP3 文件中的(原始)ID3 元数据格式是文件末尾的固定大小的数据结构。这使得就地编辑变得微不足道。另一方面,较新的 ID3v2 是文件开头的可变长度结构(以支持流式传输),因此通常无法就地编辑。 还有其他的音视频格式。有些不会在文件末尾附加数据(它包含在文件的其他地方)。某些格式可以快速轻松地修改元数据,而其他格式则需要更长时间,因为数据已被重写。【参考方案3】:

你也可以做这样的事情,在成功时删除文件并重命名输出

ffmpeg -i default.mp4 -metadata title="my title" -codec copy output.mp4 && mv output.mp4 default.mp4

【讨论】:

ffmpeg -i default.mp4 -metadata title="my title" -codec copy output.mp4 && mv output.mp4 default.mp4 应该也能正常工作【参考方案4】:

由于更改元数据会更改文件的长度,并且我希望元数据位于文件开头附近,因此音频和视频将从文件开头的不同偏移量开始。因此,如果不先创建临时文件,然后重命名文件,就无法更改元数据。

如果新元数据完全相同大小相同,并且您知道元数据在您可能的容器(文件)中的位置能够使用某种十六进制编辑器来简单地替换字符。祝你好运。

还有。如果你用空值填充,你也许可以直接放置较短的数据,但这对某些玩家来说可能是个问题。

【讨论】:

您提到的新旧文件版本之间的不一致是为什么如果文件的艺术作品标签已被编辑,但内容可以正常播放,VLC 等程序经常报告音乐文件中的曲目长度不正确。跨度> 【参考方案5】:

使用ffmpeg,您将始终需要复制到新文件。但它的优点是可以处理多种不同的格式。

不复制整个文件的替代方法取决于格式。对于许多人来说,有一些工具可以让您“就地”编辑元数据。

exiftool 适用于多种格式 metaflac FLAC MP4Box 为.mp4 用于 MP3 的eyeD3mid3v2 等 等

因此,您确实必须检查每种特定文件格式适合您的内容。

【讨论】:

【参考方案6】:

这取决于文件中元数据的物理位置。如果数据位于文件的开头,您可以“切分”该部分并编辑数据,然后将这些部分“拼接”在一起。仅当您有很多文件要批量处理时,这才有用。如果只编辑一个,则使用两份复制方法之一。

【讨论】:

【参考方案7】:

如果你像我一样有很多长名称(和空格)的文件要重命名,你可以在之前将文件名和标题保存为变量并在之后执行相同的ffmpeg 命令:

input="My default video.mp4" && title="My title video"
ffmpeg -i "$input" -metadata title="$title" -codec copy output.mp4 && mv output.mp4 "$input"

如果您有ffmpeg -flags +global_header 标志来防止Codec for stream 0 does not use global headers but container format requires global headers 警告:

ffmpeg -i "$input" -metadata title="$title" -codec copy -flags +global_header output.mp4 && mv output.mp4 "$input"

注意:我还必须更改权限才能检索与以前相同的文件(可能需要sudo):

chmod 774 "$input" && chown admin:users "$input"

【讨论】:

对不起,你没有回答我的问题。我要求更改元数据而不创建新文件..

以上是关于如何在不创建新文件的情况下使用 ffmpeg/avconv 更改元数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不创建另一个新标签或新浏览器窗口的情况下在同一网页中播放 MPG 视频/音频文件?

如何在不创建新的 dart 项目的情况下运行与我的颤振项目隔离的 dart 代码?

如何在不退出的情况下使用 Firebase API 创建新用户? [复制]

如何在不创建新片段的情况下显示片段?

如何在不打开新命令行窗口的情况下使用“schtasks”执行计划任务?

需要在不使用管理工作室的情况下创建新数据库