首页 > 解决方案 > 如何在 FFmpeg 的参数中使用六十进制语法,或者如果不可能,如何在 Windows CMD shell 中转换它

问题描述

问题于 2020 年 12 月 6 日更新,以扩大范围,同时不放弃适用于先前案例和更大案例的先前答案。

我很难为HH:MM:SS.mm不是表达式的过滤器选项提供六十进制时间 ( )。例如trim过滤器。 碰巧有一个转义规则,当我第一次询问时,我还不知道,并且在@Gyan 的第一条评论中得到了解决。

这个问题是普遍的,但如果我们走脚本路线,解决方案可能取决于外壳......我目前被Windows的 CMD.exe卡住了。

例如,以下内容会尽可能准确地在所有流中跳过一分零四秒,然后调用修剪过滤器以将片段保持在剩余持续时间的一到两分钟之间,并使用两种不同的语法来执行此操作。此示例恰好与 CMD 和 BASH shell 兼容,因此无法逃脱地狱。

ffmpeg -ss "1:00.4" -i INPUT -vf "trim=start='1\:00':end='120'" OUTPUT

那么我们如何在过滤器中的表达式中实现相同的效果呢?如果我们无法避免使用 shell 脚本,我正在寻找 Windows CMD 解决方案。 我用一段脚本发布了一个答案,以秒为单位将文本六十进制时间转换为文本十进制小数时间,这对原始情况没有用,但可能适用于更一般的情况,特别是表达式。

过滤器中使用一个表达式的失败尝试示例。select

ffmpeg -ss "1:00.4" -i INPUT -vf "select='between(t,1\:00',120)'" OUTPUT

ffmpeg 过滤器表达式似乎不支持六十进制表示法,因为我在文档和 SO/web.xml 中都没有找到它的引用。

我浏览了 ffmpeg 表达式库中定义的函数列表,但没有找到任何方法来解析那里的六十进制输入,也没有任何方法可以在其语义中使用文本。

但是,我发现了一些不相关的示例,这些示例对一些算术表达式进行了硬编码,以提供与预期等效的秒数十进制数,例如2*60+2表示2:02.

上面用于计算秒数的多项式可能会使用 shell 变量的预处理,无论它是哪个 shell,但我们需要先解析 的组件HH:MM:SS.mm以将它们放入这些变量中。你知道,使用 bash$var或 cmd %var%/%~1样式。否则,我们可能会完全在 shell 中计算多项式而不是表达式,但这样做很麻烦,收效甚微。

因此,虽然 CMD 仍然存在 ~~就像一个不死生物并变得真的死了~~,虽然我还没有机会替换它,但我希望得到一个答案:

提醒和澄清一下,用例假设我们得到一个文本的六十进制时间作为输入,并打算在 ffmpeg 过滤器的表达式中使用它,尽可能少地依赖 shell 或满足 Windows CMD。

标签: batch-fileffmpeg

解决方案


这是我编写的批处理脚本(用于 CMD.exe),用于将字符串输入时间从六十进制转换为十进制,尽管外壳非常有限且过时。我认为它可能对像我一样没有使用其他 shell 的技能或权限的 Windows 用户有用。

    REM May be inserted in another script to call :second or called as a file batch such as "hms-to-second.cmd".
    :second
    REM Convert sexagesimal time plus decimal period into decimal signed number as a string with possible period by parsing that string.
    REM param 1 is name of result destination
    REM param 2 is name of source env-var
    REM param 3 is prefix character of source
        setlocal enableDelayedExpansion
        set "inp=!%~2!"
        set "t=0"
        set "neg="
        if "!inp:~0,0!"=="-" (
            set "inp=!inp:~1!"
            set "neg=-"
        )
        for /f "tokens=1-3 delims=:" %%G in ( "!inp!" ) do (
            call :stepparse t "%%G"
            call :stepparse t "%%H"
            call :stepparse t "%%I"
        )
        (
            endlocal
            set "%~1=%neg%%t%"
        )
    exit/b
    
    :stepparse
    REM Call this repetitively from high weight to low weight, assuming the higher weight never are empty. Assume there is at most one dot for a decimal part which is appended (with the dot) because CMD SET/A cannot compute decimals.
    REM param 1 is name of accumulator
    REM param 2 is input
        setlocal enableDelayedExpansion
        set "x=!%~1!"
        for /f "tokens=1-2 delims=." %%E in ( "%~2" ) do (
            if not [%%E]==[] set /a "x=x*60+%%E"
            if not [%%F]==[] set "x=!x!.%%F"
        )
        (
            endlocal
            set "%~1=%x%"
        )
    exit/b

像这样使用它。

set "begin=1:2"
set "end=2:3"
call hms-to-second.cmd begin %begin%
call hms-to-second.cmd end %end%
ffmpeg -i sample-counter.mp4 -vf "trim=start='%begin%':end='%end%'" sample-counter-trimmed.mp4

推荐阅读