![使用 FFMPEG 自动连接(合并)DVD 中的 VOB 文件时出现问题](https://linux22.com/image/1627472/%E4%BD%BF%E7%94%A8%20FFMPEG%20%E8%87%AA%E5%8A%A8%E8%BF%9E%E6%8E%A5%EF%BC%88%E5%90%88%E5%B9%B6%EF%BC%89DVD%20%E4%B8%AD%E7%9A%84%20VOB%20%E6%96%87%E4%BB%B6%E6%97%B6%E5%87%BA%E7%8E%B0%E9%97%AE%E9%A2%98.png)
我有一个解密 DVD 库,这些 DVD 的视频都包含在通常的 VIDEO_TS 文件夹中的一系列 VOB 文件中。我想知道我是否可以使用 FFMPEG 将它们合并为一个 MPEG 文件。所以我首先从一个旧的非堆栈交换帖子中找到了这个(示例假设有 2 个 VOB 文件),它有效...
ffmpeg -i "concat:VTS_01_1.VOB|VTS_01_2.VOB" -f DVD -c copy output.mpg
ffmpeg 有时会抱怨可能缺少时间戳,但我没有看到视频、音频或同步方面出现任何问题。对于略有不同的投诉,这也有效
ffmpeg -i "concat:VTS_01_1.VOB|VTS_01_2.VOB" -f mpeg -c copy output.mpeg
所以我编写了一个批处理文件(Windows 10),用于收集我想要处理的 VOB 文件数量。我原本希望做的是创建“concat”字符串供 FFMPEG 处理。但我手动做了一个,只是为了像这样尝试...
set concat="concat: VTS_01_1.VOB|VTS_01_2.VOB|VTS_01_3.VOB|VTS_01_4.VOB|VTS_01_5.VOB"
然后我尝试在批处理文件中将该字符串传递给 FFMPEG
ffmpeg -i %concat% -f DVD -c 复制输出.mpg
嗯,这根本不起作用。我猜 FFMPEG 无法识别字符串变量作为替代。所以我查阅了关于“concat”主题的 ffmpeg 文档,他们建议使用存储在文本文件中的文件列表,如下所示...
file 'VTS_01_1.VOB'
file 'VTS_01_2.VOB' (etc...)
然后,将文件保存为“mylist.txt”,并像这样使用对 FFMPEG 的调用...
ffmpeg -f concat -safe 0 -i mylist.txt -c copy 输出.mpg
我试过了,但效果不太好。在第一个 VOB 文件之后,我不断收到有关缓冲区下溢和时间戳的警告,通常快速的连接过程变得缓慢。警告通常如下所示...
[mpeg @ 00000185e32b4200] buffer underflow st=1 bufi=1466 size=1998
[mpeg @ 00000185e32b4200] Non-monotonous DTS in output stream 0:1; previous: 328415794, current: 9265348; changing to 328415795. This may result in incorrect timestamps in the output file.
那么,有人可以建议一种与我的第一个例子一样有效的方法,但是从输入文本文件中获取文件列表吗?
答案1
经过大量的实验和研究,我发现最好的办法是制作一个 BAT 文件脚本,在变量中构建第二个 BAT 文件脚本并将其保存为临时 BAT 文件。然后可以使用自定义构造的“concat:”参数构建构造的 BAT 文件,该参数基于所需的 DVD 标题集和标题集中发现的文件数量。唯一美中不足的是,几乎不可能在包含管道 (|) 字符的变量中构建字符串。我通过在构建过程中替换“@”字符解决了这个问题,后来使用开源文件搜索和替换实用程序将“@”实例更改为“|”。它工作正常。注意我使用的是 Windows 10 和 FFMPEG 版本 git-2020-06-26-7447045,于 2020 年 6 月下载并安装。下面是我创建的带注释的批处理文件,所以如果有人想做类似的事情,他们可以从这个开始。我知道它可以改进,但它确实有效(总是一个很好的开始)!
::
:: Batch file to automate conversion of a series of VOB files in a DVD
:: titleset into a single MPG file, via FFMPEG.
:: Pass a name you'd like for the final output movie file in param 1
:: This script needs a a file search and replace utility, and I'm useing
:: SKF (Swiss Army Knife), which is open source and free ...
:: https://sourceforge.net/projects/swissfileknife/
:: Whatever search/replace util you use, you'll have to put it in a
:: 'utilities' folder somewhere in your C: drive, and make sure
:: your system (or USER) PATH variable includes it.
:: Also, unless you have unencrytpted DVD copies to work with, OR a
:: Decryptor that automatically works in the background (like DVD43),
:: you'll have to start with a program like "1-Click DVD Copy (Pro)".
:: In that case you can put the Resulting VIDEO_TS folder in a temp
:: area on your HD. This will make this batch process run faster,
:: and eliminate the needs to waste a blank DVD.
echo off
SETLOCAL ENABLEDELAYEDEXPANSION
:: Set a path where converted ouptput videos will go
:: Temporary files created by this script will also go here.
set outPath="C:\Users\Public\Videos\Temp"
:: set a path to your DVD. Playable DVDs always have aVIDEO_TS folder
set inPathDVD="Q:\VIDEO_TS"
:: In the event you are making unencrypted copies (maybe with 1-Click),
:: set that software to put its output files here. In that case,
:: the parameter you pass this script should match the folder name
:: created by that software!
set inPathHD="C:\Users\Public\Videos\Temp\%1\Video_TS"
:: some variables to construct expecteed files to seach for and gather
set inpTitleSet=VTS_
set inpExt=.VOB
set output=%outPath%\%1.mpg
set outTemp=%outPath%\%1%inpExt%
:: choose whether files come directly from DVD (must be unencrypted!)
:: or defined location on HD
choice /M "Choose Source 1 = DVD 2 = HD Temp Area" /C 12
if ERRORLEVEL 1 set inPath=%inPathDVD%
if ERRORLEVEL 2 set inPath=%inPathHD%
echo input path is: %inPath%
cd /D %inPath%
echo .
echo VOB files from %inpath%...
echo .
:: create filespec for search "VTS_01_*.vob"
set inputFileSpec=%inpTitleSet%*%inpExt%
dir %inputFileSpec%
echo .
:: select desired titleset. Usually the group with the
:: biggest file sizes is the main movie titleset.
:: You'll be entering the digit after "VTS_0"
choice /M "Select Desired Titleset (digit after VTS_): " /C 12345678
echo you picked %ERRORLEVEL%
set inpTitleSet=%inpTitleSet%0%ERRORLEVEL%_
set inputFileSpec=%inpTitleSet%*%inpExt%
:: use a brute force loop to calculate number of
:: files matching %inputFileSpec% and display them.
:: Note that the '0' file is usually title/menu information,
:: and is skipped by making the %start% var '1'
echo files selected...
echo .
set start=1
:loop0
set tmp=%inpTitleSet%%start%%inpExt%
IF EXIST %tmp% (
echo %tmp%
set /A start=start+1
GOTO loop0
)
set /A count=%start%-1
echo .
:: confirm total files
echo Total Files = %count%
:: safety exit, in odd event where no movie files found
if %count% NEQ 0 GOTO continue1
echo .
echo Must Exit... no Files Found.
goto exit
:continue1
:: Build string containing of file names for FFMPEG "concat:" parameter.
:: Another Brute force loop. Again file *0.VOB will be skipped,
:: by making loop start=1::
:: set an 'empty' variable
set cc=
set start=1
:: create a temp substitute for the pipe "|" character,
:: because | can't be cant be used in a script (^| or similar
:: constructs just won't work here). So I'll use '@' and change it
:: later with a file search/replace utility.
set mypipe=@
:loop1
set tmp=%inpTitleSet%%start%%inpExt%
if %start% EQU %count% set mypipe=
IF EXIST %tmp% (
set cc=%cc%%inp%%tmp%%mypipe%
set /A start=start+1
GOTO loop1
)
:: create whole FFMPEG command as a string
set ffm=ffmpeg -i "concat:%cc%" -f DVD -c copy %output%
:: put it in a tmp batch file
set cmdfile=tmp.bat
echo %ffm% > %outPath%\%cmdfile%
:: now use SKF to replace the @ chars with |
:: if you have another search/replace utility, feel free to use it.
sfk replace %outPath%\%cmdfile% "/@/|/" -yes -quiet
:: prove string in batch file looks right. You can delete
:: these two lines if you want.
echo Final command...
type %outPath%\%cmdfile%
:: run the new batch file... Give user chance to start process
@pause
call %outPath%\%cmdfile%
:: delete batch file?
del %outPath%\%cmdfile%
echo .
echo .
echo %output% converted file should be ready
ENDLOCAL
:exit