使用ffmpeg按大小分割视频文件

21
我正在尝试使用ffmpeg编写批处理文件,自动化工作中每天冗余的任务,该任务是将以4GB块记录在大多数DSLR相机和GoPro中的镜头拆分为2GB文件以进行流媒体传输。 我的想法是让脚本检查外部驱动器FOOTAGE的@import文件夹,并在2GB后拆分文件(由于最大大小为4GB,因此这将减轻需要拆分的文件数量)。
我还尝试修改拆分文件的文件名,使得如果FILE1为4GB,则将其拆分成分别为2GB的FILE1_1和FILE1_2。 但是,我尝试过的所有方法都只是将原始文件复制到两个新的完全相同的文件中 - 没有拆分或任何其他操作。
在进行了一些谷歌搜索并阅读了这里的一些答案之后,我发现了这篇文章,但它基于持续时间而不是大小(在不同质量级别下记录视频镜头使其无意义):Split into equal parts and convert many mp4 videos using ffmpeg

有人能帮我解决这个问题吗?我还没有找到可用的解决方案,利用我所理解的方法,使用-fs limit_size,我真的想了解这是如何工作的。

更新:我也发现了这个,但它已经四年没有更新了,我没有看到任何关于分割的内容,这将证明有用:

https://github.com/kcm1700/VideoSplitter/blob/master/

3个回答

33

我刚遇到了同样的问题,在这里没有找到解决方案后,我为此写了一个快速的bash脚本。

功能:

  • 使用ffmpeg的-fs标志来限制文件大小
  • 检查结果视频部分的长度并计算从哪里开始下一部分
  • 枚举视频部分
  • 处理尽可能多的部分以包含整个源文件。
  • 允许自定义ffmpeg参数以在一次转换中减小分辨率和质量。

它只需要三个参数:源文件名、每个部分的所需文件大小和ffmpeg的所需参数。

将文件拆分为64MB部分的示例调用:

./split-video.sh huge-video.mov 64000000 "-c:v libx264 -crf 23 -c:a copy -vf scale=960:-1"

最后,脚本的源代码:

#!/bin/bash
# Short script to split videos by filesize using ffmpeg by LukeLR

if [ $# -ne 3 ]; then
    echo 'Illegal number of parameters. Needs 3 parameters:'
    echo 'Usage:'
    echo './split-video.sh FILE SIZELIMIT "FFMPEG_ARGS'
    echo 
    echo 'Parameters:'
    echo '    - FILE:        Name of the video file to split'
    echo '    - SIZELIMIT:   Maximum file size of each part (in bytes)'
    echo '    - FFMPEG_ARGS: Additional arguments to pass to each ffmpeg-call'
    echo '                   (video format and quality options etc.)'
    exit 1
fi

FILE="$1"
SIZELIMIT="$2"
FFMPEG_ARGS="$3"

# Duration of the source video
DURATION=$(ffprobe -i "$FILE" -show_entries format=duration -v quiet -of default=noprint_wrappers=1:nokey=1|cut -d. -f1)

# Duration that has been encoded so far
CUR_DURATION=0

# Filename of the source video (without extension)
BASENAME="${FILE%.*}"

# Extension for the video parts
#EXTENSION="${FILE##*.}"
EXTENSION="mp4"

# Number of the current video part
i=1

# Filename of the next video part
NEXTFILENAME="$BASENAME-$i.$EXTENSION"

echo "Duration of source video: $DURATION"

# Until the duration of all partial videos has reached the duration of the source video
while [[ $CUR_DURATION -lt $DURATION ]]; do
    # Encode next part
    echo ffmpeg -i "$FILE" -ss "$CUR_DURATION" -fs "$SIZELIMIT" $FFMPEG_ARGS "$NEXTFILENAME"
    ffmpeg -ss "$CUR_DURATION" -i "$FILE" -fs "$SIZELIMIT" $FFMPEG_ARGS "$NEXTFILENAME"

    # Duration of the new part
    NEW_DURATION=$(ffprobe -i "$NEXTFILENAME" -show_entries format=duration -v quiet -of default=noprint_wrappers=1:nokey=1|cut -d. -f1)

    # Total duration encoded so far
    CUR_DURATION=$((CUR_DURATION + NEW_DURATION))

    i=$((i + 1))

    echo "Duration of $NEXTFILENAME: $NEW_DURATION"
    echo "Part No. $i starts at $CUR_DURATION"

    NEXTFILENAME="$BASENAME-$i.$EXTENSION"
done

希望对您有所帮助 :)


2
我可以知道您提供的解决方案是否会降低视频块的质量吗?我是FFmpeg新手。 - Giorgi Aptsiauri
1
这完全取决于您传递的FFMPEG_ARGS(第三个参数)。如果不传递任何内容,ffmpeg将根据输出容器自动选择设置,这很可能意味着它将重新压缩内容,导致不必要的CPU负载和质量损失。因此,建议将-c copy至少作为该脚本的第三个参数传递,因为这将指示ffmpeg复制视频和音频数据而不是重新编码它。这使转换速度更快(唯一的限制是硬盘驱动器速度),同时保持原始质量。 - LukeLR
谢谢。自从我发表了这条评论以来,我对FFMPEG有了更深入的了解。当时我使用过这个脚本,但是遇到了一些问题,我不记得了,我会再次查看它。 - Giorgi Aptsiauri
2
你的脚本很棒!我认为默认行为应该是未修改的复制,不需要重新编码。 - peterh
这个脚本只完成了第一部分并失败了。ffprobe 命令获取的持续时间对于源文件和第一部分文件是相同的。因此,while 循环只执行了一次。 - rusty

20

您可以使用mp4box一条命令来完成此操作。

mp4box -splits 2000000 filename.mp4

splits参数的值以千字节为单位。由于关键帧的存在,分段大小可能不完全为2GB。


1
我以为它是以字节为单位?那不是会变成2MB吗? - Benubird
不,它是以kB为单位的。 - Gyan
1
当我尝试使用这个工具时,它将视频分成了3部分,但实际上只需要2部分。最后一部分大小为120kb。之前的那一部分还剩下100MB以上的可用空间。 - Daniel Kaplan
这个命令根本不起作用。它只会创建一个文件,大小设置为参数上的值。 - Natan Lotério
取决于关键帧的位置。 - Gyan

2

一个思路是使用带有 -fs 选项的 ffmpeg。这将限制文件大小。输出文件的大小略大于所请求的文件大小。

这只会创建一个文件。但是你可以循环构建,直到整个文件被拆分。

首先创建文件的一部分,然后使用以下命令检查其长度:

ffprobe -i input.file -show_format -v quiet | sed -n 's/duration=//p'

然后使用 -ss 参数以偏移量开始另一个文件。

之后再进行一轮编码,直到整个文件被分割。对于音频和视频,您也可以使用流复制。


3
您能否提供一个使用-fs和-ss的脚本示例? - user2548469
谢谢这个命令。比上面脚本中使用的那个更短。tldr.sh建议使用的标准命令比这个长。 - GNUSupporter 8964民主女神 地下教會

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接