ffmpegを使用してMPEGビデオを10分のチャンクに分割するにはどうすればよいですか?


63

多くの場合、オープンソースまたはアクティブな開発者コミュニティでは、大きなビデオセグメントをオンラインで公開する必要があります。(ミートアップビデオ、キャンプアウト、ハイテクトーク...)私はビデオグラファーではなく開発者であるため、プレミアムVimeoアカウントの余分なスクラッチをフォークしたくありません。ビデオ共有サイトに簡単にアップロードできるように、12.5 GB(1:20:00)のMPEG技術トークビデオを00:10:00のセグメントにスライスする方法を教えてください。


新しいタグに対応してくれた@StevenDに感謝します。
ガブリエル

回答:


69
$ ffmpeg -i source-file.foo -ss 0 -t 600 first-10-min.m4v
$ ffmpeg -i source-file.foo -ss 600 -t 600 second-10-min.m4v
$ ffmpeg -i source-file.foo -ss 1200 -t 600 third-10-min.m4v
...

これをスクリプトにラップしてループで実行するのは難しくありません。

引数を指定しない限り、クリップの開始時の平均ビットレートとクリップのファイルサイズから推定されるffprobe呼び出しからの継続時間出力に基づいて反復回数を計算しようとすると、操作が大幅に遅くなることに注意してください。-count_frames

もう1つ注意すべき点は-ss、コマンドラインでのオプションの位置が重要であることです。私が今持っている場所は遅いですが正確です。この回答の最初のバージョンは、高速ではあるが不正確な代替手段を提供しました。リンクされた記事では、ほとんど高速でありながら正確な代替案についても説明しますが、これは少し複雑になります。

それ以外は、クリップごとに正確に10分でカットしたいとは思わないでしょう。それは文の真ん中に言葉を含めてカットを配置します。ビデオエディターまたはプレーヤーを使用して、10分離れた自然なカットポイントを見つける必要があると思います。

ファイルがYouTubeが直接受け入れることができる形式であると仮定すると、セグメントを取得するために再エンコードする必要はありません。自然なカットポイントオフセットをffmpegに渡すだけで、「コピー」コーデックを使用して、エンコードされたA / Vをそのまま渡すように指示します。

$ ffmpeg -i source.m4v -ss 0 -t 593.3 -c copy part1.m4v
$ ffmpeg -i source.m4v -ss 593.3 -t 551.64 -c copy part2.m4v
$ ffmpeg -i source.m4v -ss 1144.94 -t 581.25 -c copy part3.m4v
...

-c copy引数はそのまま出力にすべての入力ストリーム(例えば字幕、オーディオ、ビデオ、および潜在的に他のものなど)をコピーすることを指示します。単純なA / Vプログラムの場合、より詳細なフラグ-c:v copy -c:a copyまたは古いスタイルのフラグと同等-vcodec copy -acodec copyです。一方のストリームのみをコピーし、もう一方を再エンコードする場合は、より詳細なスタイルを使用します。たとえば、何年も前に、ビデオをH.264ビデオで圧縮し、オーディオを非圧縮PCMのままにするというQuickTimeファイルの一般的な方法がありました。今日そのようなファイルに出くわした場合-c:v copy -c:a aac、オーディオストリームのみを再処理し、ビデオに手を加えることなく最新のものにすることができます。

最初のコマンドの後の上記のすべてのコマンドの開始点は、前のコマンドの開始点と前のコマンドの継続時間です。


1
「各クリップを正確に10分でカットする」ことは良い点です。
クリス

-show_packetsパラメーターを使用することで、より正確に設定できます。
rogerdpack

上記をループに入れる方法は?
kRazzy R

私は、これはそれが右に分割屋cmdをしかし、今のビデオとオーディオのSYNC上の任意のヘルプを使用していない
スニルChaudharyさん

@SunilChaudhary:A / Vファイル形式は複雑であり、すべてのソフトウェアが同じ方法でそれらを実装するffmpegわけではないため、ソースファイルにオーディオストリームとビデオストリーム間の同期を維持するための情報が存在しない場合があります。これがあなたに起こっている場合、問題を回避する分割のより複雑な方法があります。
ウォーレンヤング

53

これが1行のソリューションです。

ffmpeg -i input.mp4 -c copy -map 0 -segment_time 00:20:00 -f segment output%03d.mp4

これは正確な分割を提供するものではありませんが、ニーズに合っている必要があることに注意してください。代わりに、指定された時間の後の最初のフレームでカットしsegment_timeます。上記のコードでは、20分後です。

最初のチャンクのみが再生可能であることがわかった場合-reset_timestamps 1は、コメントに記載されているとおりに追加してみてください。

ffmpeg -i input.mp4 -c copy -map 0 -segment_time 00:20:00 -f segment -reset_timestamps 1 output%03d.mp4

2
ビデオ品質を重視する場合、実際には非常に正確な分割が可能です。特定の時間に基づいて分割するのではなく、要求された時間に続く最も近いキーフレームで分割するため、新しい各セグメントは常にキーフレームで始まります。
悪意のある

3
ユニットは何ですか?8秒?8分?8時間?
user1133275

1
@ user1133275その二
ジョン

2
Macでは、これによりN個の出力ビデオチャンクが生成されることがわかりましたが、そのうちの最初の1つだけが有効で表示可能なMP4でした。他のN-1チャンクは、音声のない空のビデオ(すべて黒)でした。動作させるには、次のようにreset_timestampsフラグを追加する必要がありました。ffmpeg-i input.mp4 -c copy -map 0 -segment_time 8 -f segment -reset_timestamps 1 output%03d.mp4。
jarmod

3
追加-reset_timestamps 1することで問題が解決することがわかりました
jlarsch

6

以前に同じ問題に直面し、それを行うためのシンプルなPythonスクリプトを作成しました(FFMpegを使用)。ここから入手可能:https : //github.com/c0decracker/video-splitter、および以下に貼り付け:

#!/usr/bin/env python
import subprocess
import re
import math
from optparse import OptionParser
length_regexp = 'Duration: (\d{2}):(\d{2}):(\d{2})\.\d+,'
re_length = re.compile(length_regexp)
def main():
    (filename, split_length) = parse_options()
    if split_length <= 0:
        print "Split length can't be 0"
        raise SystemExit
    output = subprocess.Popen("ffmpeg -i '"+filename+"' 2>&1 | grep 'Duration'",
                              shell = True,
                              stdout = subprocess.PIPE
    ).stdout.read()
    print output
    matches = re_length.search(output)
    if matches:
        video_length = int(matches.group(1)) * 3600 + \
                       int(matches.group(2)) * 60 + \
                       int(matches.group(3))
        print "Video length in seconds: "+str(video_length)
    else:
        print "Can't determine video length."
        raise SystemExit
    split_count = int(math.ceil(video_length/float(split_length)))
    if(split_count == 1):
        print "Video length is less then the target split length."
        raise SystemExit
    split_cmd = "ffmpeg -i '"+filename+"' -vcodec copy "
    for n in range(0, split_count):
        split_str = ""
        if n == 0:
            split_start = 0
        else:
            split_start = split_length * n
            split_str += " -ss "+str(split_start)+" -t "+str(split_length) + \
                         " '"+filename[:-4] + "-" + str(n) + "." + filename[-3:] + \
                         "'"
    print "About to run: "+split_cmd+split_str
    output = subprocess.Popen(split_cmd+split_str, shell = True, stdout =
                              subprocess.PIPE).stdout.read()
def parse_options():
    parser = OptionParser()
    parser.add_option("-f", "--file",
                      dest = "filename",
                      help = "file to split, for example sample.avi",
                      type = "string",
                      action = "store"
    )
    parser.add_option("-s", "--split-size",
                      dest = "split_size",
                      help = "split or chunk size in seconds, for example 10",
                      type = "int",
                      action = "store"
    )
    (options, args) = parser.parse_args()
    if options.filename and options.split_size:
        return (options.filename, options.split_size)
    else:
        parser.print_help()
        raise SystemExit
if __name__ == '__main__':
    try:
        main()
    except Exception, e:
        print "Exception occured running main():"
        print str(e)

1
次回はこれをコメントでお願いします。ここではリンクのみの回答はあまり好まれておらず、サイトを宣伝する場合も同じです。ソースコードを使用したオープンソースプロジェクトの場合は例外かもしれませんが、回答の削除に投票しないことで、レビュー権限を危険にさらしました。そして、はい、コメントを投稿することはできませんが、5回のアップ投票を収集した後(これは非常に速いようです)。
user259412

2
こんにちは、サイトへようこそ。リンクのみの回答を投稿しないでください。あなたのスクリプト自体はおそらく素晴らしい答えになるでしょうが、それへのリンクは答えではありません。それは答えを指す道しるべです。詳細はこちら。あなたが親切にリンクを与えたので、私は先に進み、あなたの答えの本文にスクリプトを含めました。それに反対する場合は、回答をすべて削除してください。
テルドン

4

本当に同じチャンクを作成する場合は、ffmpegですべてのチャンクの最初のフレームにiフレームを作成する必要があるため、このコマンドを使用して0.5秒のチャンクを作成できます。

ffmpeg -hide_banner  -err_detect ignore_err -i input.mp4 -r 24 -codec:v libx264  -vsync 1  -codec:a aac  -ac 2  -ar 48k  -f segment   -preset fast  -segment_format mpegts  -segment_time 0.5 -force_key_frames  "expr: gte(t, n_forced * 0.5)" out%d.mkv

これは受け入れられた答えでなければなりません。仲間を共有してくれてありがとう!
ステファンアールフ

4

代替より読みやすい方法は次のようになります

ffmpeg -i input.mp4 -ss 00:00:00 -to 00:10:00 -c copy output1.mp4
ffmpeg -i input.mp4 -ss 00:10:00 -to 00:20:00 -c copy output2.mp4

/**
* -i  input file
* -ss start time in seconds or in hh:mm:ss
* -to end time in seconds or in hh:mm:ss
* -c codec to use
*/

一般的に使用されるFFmpegコマンドのソースとリストを次に示します。


3

代替形式の正確な句読点は-ss mm:ss.xxxです。私は何時間も直観的だが間違ったものmm:ss:xxを利用しようとして苦労した。

$ man ffmpeg | grep -C1 position

-ss position
指定された時間位置を秒単位でシークします。「hh:mm:ss [.xxx]」構文もサポートされています。

参考資料ここここ


0
#!/bin/bash

if [ "X$1" == "X" ]; then
    echo "No file name for split, exiting ..."
    exit 1
fi

if [ ! -f "$1" ]; then
    echo "The file '$1' doesn't exist. exiting ..."
    exit 1
fi

duration=$(ffmpeg -i "$1" 2>&1 | grep Duration | sed 's/^.*Duration: \(.*\)\..., start.*$/\1/' | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')      #'
split_time=${split_time:-55}
time=0
part=1

filename=${file%%.*}
postfix=${file##*.}

while [ ${time} -le ${duration} ]; do

echo    ffmpeg -i "$1" -vcodec copy -ss ${time} -t ${split_time} "${filename}-${part}.${postfix}"
    (( part++ ))
    (( time = time + split_time ))

done

0

まさにこれを行うには、ffmpegに組み込まれているものを使用します。

ffmpeg -i invid.mp4 -threads 3 -vcodec copy -f segment -segment_time 2 cam_out_h264%04d.mp4

これにより、約2秒のチャックに分割され、関連するキーフレームで分割され、cam_out_h2640001.mp4、cam_out_h2640002.mp4などのファイルに出力されます。


この答えがなぜダウン票されたのか、私にはわかりません。それはうまくいくようです。
コスティン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.