FFmpegを用いて音入り動画を作成できる無料アプリ


9VAeきゅうべえ Windows / Linux /Android 版の欠点

フリーソフト9VAeきゅうべえは、下のような動画が簡単につくれるベクトルアニメーションアプリです。2Dグラフィックス作成機能があり、音も入れられます。

しかし音入りの MP4動画を作ることは、Windows / Linux /Android 版ではできませんでした。(Mac / iPhone / iPad版は可能。上はアニメGIF)

今回、FFmpegで音入りアニメーション動画を作成する方法を見つけましたので紹介します。この方法をつかって、9VAeきゅうべえは、どのOSでも動画出力できるようになりました。

9VAe きゅうべえの音声の仕様

  • 9VAeには、効果音、音楽の2トラックがあり、2つの音を同時に鳴らすことができます。
  • 音が鳴っているトラックで別の音を鳴らすと前の音は停止します。
  • 音の開始時間はキーフレームからわかります。
  • 9VAeは音のファイルを指定時刻に鳴らすだけで、音を修正する機能はありません。

FFmpeg の機能

  • FFmpeg は連番画像から動画を作成する機能があります。
  • 動画に音を入れる機能があります。
  • 動画や音をカットしたり、つないだりする機能があります。
  • オプションの説明

ただ、その記述方法がわかりにくく、試行錯誤を繰り返しました。結局、理解したこと。

  • 動画に音声を合成するときに、時間を指定して、そこに音を入れる機能はないみたい。途中から音を鳴らしたい場合は、音の前に無音をいれる。短い音をいれる場合、音の後ろに無音をいれなければならない。
  • そのため、鳴らす音の長さを正確にはかる必要がある。
  • 映像に2トラックの音をいれることもできるはずだが、うまくならなかった。結局、効果音トラック、音楽トラックの音を合成してひとつの音にした。

今回、FFmpegで使用した機能は以下です。いろんなOSで同じように動作します。

機能 命令
連番画像から映像作成 -vcodec mpeg4 -pix_fmt yuv420p
指定した時間の無音作成 -f lavfi -i aevalsrc
指定した時間で音をカット -t 秒
2つの音をつなぐ -filter_complex concat
作成した音の時間を取得 関数を自作
2トラックの音を重ねる -filter_complex
映像に音を追加する -c:v copy -c:a copy

これらの機能をつかって、効果音トラック、音楽トラックの音を無音をはさんで順番につなげていき、最後に、2つのトラックを合成して1トラックにし、映像に入れます。具体例を示します。

連番画像から映像作成

ffmpeg -y -r 1秒のフレーム数 -i %04d.jpg -b 6000k -vcodec mpeg4 -pix_fmt yuv420p video.mp4 

%04d.jpg は連番画像(%04d は、0001,0002,0003...)9VAeから指定したフレームレート、画像サイズで出力します。なお、画像サイズを奇数にするとスマホで再生できないようです。
video.mp4 は出力ファイル名
-y は同名ファイルがあったときの上書き指定

指定した時間(秒)の無音作成

ffmpeg -f lavfi -i aevalsrc=0:d=-ac 2 -strict -2  -c:a aac -ab 256k  out.m4a

秒は小数可能
-strict -2 がないとエラーになるときがある

指定した時間(秒)で音をカット

ffmpeg -i in.wav -t-c:a aac -ab 256k out.m4a

in.wav が 指定した時間より短い場合、out.m4a は、指定した時間より短い音になる。out.m4a の時間を調べ、必要なら、無音を追加しなければならない。
-ab 256k はビットレート指定で CD音質

2つの音をつなぎ、指定した時間(秒)で音をカット

ffmpeg -i in.m4a -i in.wav -t-filter_complex concat=n=2:v=0:a=1   -ab 256k out.m4a

in.m4a の後ろに in.wav をつなぐ

2トラックの音を重ねる

ffmpeg -y -i trk1.m4a -i trk2.m4a -filter_complex "[0:a][1:a]amix=inputs=2[a]" -map "[a]" -ab 256k out.m4a

2つの音、trk1.m4a trk2.m4a を重ねて1トラックの音 out.m4a にする

映像に音をつける

ffmpeg -y -i video.mp4 -i add.m4a -c:v copy -c:a copy out.mp4

映像 video.mp4 に 音 add.m4a をつける

音 m4a の時間(msec)を取得

以下のようなプログラムを自作しました。エラーの場合、負の値が返ります。

#include <stdio.h>

#define CHis(a,b,c,d) ((a)==hd[4] && (b)==hd[5] && (c)==hd[6] && (d)==hd[7])
#define Int3(a) *(a)*0x10000   + *((a)+1)*0x100   + *((a)+2)
#define Int4(a) *(a)*0x1000000 + *((a)+1)*0x10000 + *((a)+2)*0x100 + *((a)+3)
//m4a 形式の音の再生時間(msec)を取得する
long AcGetM4aMsec(char *fileName)
{
    FILE *fp; int bb,cc; long ret=-1;
    unsigned char hd[8],work[1024]; 

    // ファイルオープン
    fp = fopen(fileName, "rb");
    for(;fp;){
        // 先頭フレームのヘッダ8Byteを読み込む
        if(fread(hd, 1, 8, fp) != 8) break;
        bb = Int4(hd);//チャンクサイズ
        bb-=8;
        if(CHis('m','o','o','v')){//メタデータ発見
            for(;bb>0;){
                if(fread(hd, 1, 8, fp) != 8) break; 
                bb-=8;
                cc = Int4(hd);//チャンクサイズ
                bb-=cc;
                for(;cc>0;cc-=1024){
                    if(fread(work, 1, cc>1024?1024:cc, fp) < 1024) break;
                }
                if(CHis('m','v','h','d')){//時間情報が入っている
                    int version=work[0];
                    int flags = Int3(work+1);
                    int creatTime = Int4(work+4);
                    int modifTime = Int4(work+8);
                    int timeScale = Int4(work+12);//秒60 の場合60
                    int duration  = Int4(work+16);//duration/timeScale=秒
                    ret = (long)((double)duration*1000/timeScale);
                    goto exit;
                }
            }
            continue;
        }
        for(;bb>0;bb-=1024){//データ読み飛ばし
            if(fread(work, 1, bb>1024?1024:bb, fp) < 1024) break;
        }
    }
exit:
    if(fp) fclose(fp);
    return ret;
}