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;
}