FFmpegの個人的な使い方まとめ

2020/05/08

【プログラミング】 ツール

t f B! P L

FFmpegとは?


動画と音声を記録・変換・再生するためのフリーソフトウェアで、いろんなプラットフォームで使用することができる。


本格的な動画の編集は専門のソフトが良いけれど、単純な変換や切り出しなどはコマンドラインでできる方が断然ラクではやい。

以下、個人的によく使っている内容を紹介していきますが、ほかにもできることはたくさんあるので、ぜひ調べてみてください。

FFmpegでできること

  • ファイル形式の変換
  • 動画の分割
  • 動画の結合
  • 静止画から動画の作成
  • などなど。

    ファイル形式の変換

    たとえば、mkv形式をmp4形式に変換したいときは、

    ffmpeg -i <input.mkv> -vcodec copy <output.mp4>
    

    codecをcopyにしておくと、無劣化になるそうな。

    ちなみに、.mkvファイルの変換はyoutube-dlと合わせてよく使うことができる。

    動画の分割

    1つの動画を分割、切り出して、複数の動画にしたいとき。
    この「分割」という作業が他のツールではとてもやりにくかったり、時間を要したので、FFmpegがありがたい。


    ffmpeg -ss <start time> -i <input.mp4> -ss 0 -t <cut time> -c:v copy -c:a copy -async 1 -strict -2 <output.mp4>
    
    • start time : 開始位置
    • cut time : 開始位置から切り出したい時間

    を決めてあげる。

    「動画編集ソフトで元ファイルを読み込んで、クリップを分割して、書き出す」
    よりもだいぶ早かった。

    動画の結合

    まず結合元となる複数の動画について、ファイルリストを作成する。
    ファイルがおいてあるパスを指定する。

    file 1.mp4
    file 2.mp4
    file 3.mp4
    

    ファイルリストを元に、結合

    ffmpeg -f concat -safe 0 -i mylist.txt -c copy <output.mp4>
    

    ファイルリストもファイルの名前に規則性があればPythonで作ってしまう。

    import os
    
    basepath = "/dir/path""
    filepath1 = basepath + "short_movie"
    filepath2 = basepath + "movie"
    list_file_name1 = os.listdir(filepath1)
    list_file_name2 = os.listdir(filepath2)
    # カレントディレクトリの変更
    os.chdir(basepath + "concat") 
    for file in list_file_name1:
        name = file.split(".")[0]
        name = name.replace(" ", "") # スペースを消す
        file = open('{}.txt'.format(name), 'w')
        # ファイルリストに書き込む
        # 今回は2つのファイルの場合
        file.write('file ../short_movie/{}.mp4\n'.format(name))
        file.write('file ../movie/{}.mp4'.format(name))
        file.close()
        print(name)
    

    静止画から動画の作成

    ffmpeg -loop 1 -i <input image> -t 2 -r 30 <output.mp4>
    
    • -t : 動画の時間

    output動画のサイズの指定もできる。

    ffmpeg -loop 1 -i <input image> -s 1920x1080 -t 2 -r 30 <output.mp4>
    

    FFmpegの処理の前に、画像自体のサイズを変更したいときは、

    • imagemagick
    • sips

    などで編集しておくのも一手。

    # 縦横比を無視して強制リサイズ
     convert -resize 1920x1080! <input image> <output image>
    
     # sips
     sips -z 1080 1920 <input image>
    

    繰り返す処理はシェルスクリプトで

    FFmpegに限った話ではないけれど、コマンドラインのツールで、同じ処理を繰り返す場合はシェルスクリプトでループ処理を書いてあげると便利。


    一例として、

    フォルダ内のすべての.mkvファイルを.mp4形式に変換する

     # ディレクトリ内のファイル名を出力
     for file in *.mkv; do
         name=$(basename $file .mkv)
         echo $name
         # 変換
         ffmpeg -i $file -vcodec copy $name.mp4
         # mkvを削除
         rm $file
     done
    

    複数の画像ごとに、動画を作成する

    for file in *.jpg; do
        name=$(basename $file .jpg)
        echo $name
        ffmpeg -loop 1 -i $file -t 2 -r 30 $name.mp4
    done
    

    動画の分割

    1つの動画ファイルに対して、複数の分割をしたいとき

    分割したい時間ごとに、
    開始時間、終了時間、終了-開始の時間
    をタブ区切りのテキストにまとめます。

    シェルスクリプトでまとめて実行したかったので、

    • テキストファイルを読み込む
    • タブごとの時間を別々の変数に入れる
    • 変数に基づいて、FFmpegコマンドを実行

    のような設計を考えたのですが、タブごとの時間を別々の変数に入れるという処理がどうもうまくいかなかったので、Pythonを使いました。
    が、それもsubprocess等でPythonからコマンドを実行できたらよかったのですが、複数の引数の渡し方がややこしく、結局変数に基づいた、実行コマンドをprintするだけにとどまってしまいました。


    import pandas as pd
    
    txt = "time_1.txt"
    input = "input.mp4"
    output = "title"
    
    df = pd.read_csv(txt, header=None)
    for index, row in df.iterrows():
        start = row[0]
        time = row[2]
        number = index + 1
        output_name = output + "_" + str(number) + ".mp4"
        print("ffmpeg -ss ", end="")
        print(start, end="")
        print(" -i ", end="")
        print(input, end="")
        print(" -ss 0 -t ", end="")
        print(time, end="")
        print(" -c:v copy -c:a copy -async 1 ", end="")
        print(output_name)
    

    これを実行すると…

    ffmpeg -ss 0:02:40 -i input.mp4 -ss 0 -t 1:09:36 -c:v copy -c:a copy -async 1 title_1.mp4
    ffmpeg -ss 1:12:16 -i input.mp4 -ss 0 -t 1:09:36 -c:v copy -c:a copy -async 1 title_2.mp4
    ffmpeg -ss 2:23:29 -i input.mp4 -ss 0 -t 1:09:38 -c:v copy -c:a copy -async 1 title_3.mp4
    ffmpeg -ss 3:33:07 -i input.mp4 -ss 0 -t 1:09:36 -c:v copy -c:a copy -async 1 title_4.mp4
    ffmpeg -ss 4:44:20 -i input.mp4 -ss 0 -t 1:09:37 -c:v copy -c:a copy -async 1 title_5.mp4
    ffmpeg -ss 5:53:57 -i input.mp4 -ss 0 -t 1:11:14 -c:v copy -c:a copy -async 1 title_6.mp4
    ffmpeg -ss 7:05:11 -i input.mp4 -ss 0 -t 0:46:24 -c:v copy -c:a copy -async 1 title_7.mp4
    ffmpeg -ss 7:51:35 -i input.mp4 -ss 0 -t 0:46:05 -c:v copy -c:a copy -async 1 title_8.mp4
    

    このような感じで、コマンドラインに貼り付けて実行できる形にはなりました。

    もっときれいな書き方がありそうですが、個人の利用の範囲内では速度的にも問題ありません。

    トラブル

    concatで音が無くなる問題

    上記の動画の結合のところで、
    1つ目の動画が無音声のファイルの場合、2つ目以降が音声つきでも、出力ファイルに音声がついていませんでした。


    Concatenating videos with ffmpeg produces silent video when the first video has no audio track - Super User
    php - Why ffmpeg concat with 2 videos is losing the 2nd audio? - Stack Overflow
    これは質問サイトでもけっこう見つかるトラブルで、フォーマットの違いから1つ目にそろえられてしまう、と解釈しました。


    対処法としては、

    • 1つ目のファイルに音声をつける
    • 無音声の出力ファイルに、後付けで音声をつける

    などが考えられます。
    動画と音声の結合は音ずれが生じることもあり、修正するのも大変なので、難しいです。


    動画に音声をつけたいときは、

    ffmpeg -y -i <input.mp4> -itsoffset 00:00:02 -i <input.mp3> -vcodec copy -bsf:a aac_adtstoasc -async 1 <output.mp4>
    
    などでできます。

    まとめ

    動画や音声を扱いたいとき、カンタンなことならさくっとできてしまいます。
    各機能、いろいろなオプションがあるけれど、音声や動画のファイル形式についてあんまり知らないからエラーを読むのも苦戦します…


    記事の感想をリアクションでお願いします!

    QooQ