我想使用 lame 来编码 mp3 文件,但不是直接将其写入文件,而是将其导入另一个程序,最终将其写入某个地方。问题是,如果 lame 检测到其输出是流,则它不会写入 VBR 标头,播放器会错误地计算出文件的长度。例如,如果我这样做,则生成的文件将是错误的:
lame infile.wav - >outfile.mp3
我尝试了以下技巧:
lame infile.wav /dev/stdout >outfile.mp3
乍一看似乎可行,但前提是 stdout 直接重定向到文件。以下情况不起作用:
lame infile.wav /dev/stdout | cat >outfile.mp3
如果我使用 ffmpeg(或 avconv)作为 lame 的前端,我会遇到完全相同的问题。
有什么方法可以告诉 lame 我希望它写入 VBR 标头,即使它认为它正在写入流?
答案1
似乎无法通过这种方式完成。问题是 VBR 标头写入文件开头,但仅在编码结束时计算。这需要在文件中进行查找,如果输出是管道,则这是不可能的。
我对所有上述变体都进行了测试strace
。在第二个版本中(写入普通文件时),我最后得到了以下结果:
lseek(4, 0, SEEK_SET) = 0
write(4, "\377\373\220d\0\0\0"..., 417) = 417
close(4) = 0
在第一个版本中,我将其用作-
输出参数,lame 甚至没有尝试写入标头。然而,在第三个版本中,它尝试了但失败了,因为输出是一个管道。
lseek(4, 0, SEEK_SET) = -1 ESPIPE (Illegal seek)
它还会在最后写一条错误消息,这条消息很容易被忽视,因为它通常会将其他输出打印到 stderr,除非我使用该--silent
选项运行它(我这样做了,以使 strace 输出更清晰):
fatal error: can't update LAME-tag frame!
解决这个问题的方法是写入一个临时文件,然后进一步传输,或者使用恒定比特率编码(在这种情况下,编码后不会在文件开头写入任何额外的头)。