はじめに
ここでは、VapourSynth などを使って、MP4 (H.264 + AAC) の動画を作成する手順を説明していきます。
VapourSynth でソースから動画を読み込み、シーンカットとリサイズを行って H.264 で映像をエンコードした後、音声ファイル単独で映像の位置に合わせて範囲をカットし、AAC でエンコードします。
その後、映像と音声を結合して、MP4 ファイルを作成します。
VapourSynth でソースから動画を読み込み、シーンカットとリサイズを行って H.264 で映像をエンコードした後、音声ファイル単独で映像の位置に合わせて範囲をカットし、AAC でエンコードします。
その後、映像と音声を結合して、MP4 ファイルを作成します。
映像エンコード用のスクリプトを用意
まず、映像エンコード用のスクリプトを用意します。
以下のスクリプトを参考にして、作成してください。
<enc.vpy>
以下のスクリプトを参考にして、作成してください。
<enc.vpy>
#!/usr/bin/python3 import vapoursynth as vs core = vs.core # プラグイン読み込み #core.std.LoadPlugin('/usr/local/lib/vapoursynth/libsavfsource.so') # ffms2 でソースを読み込み clip = core.ffms2.Source(source='src.mp4') # 編集用にフォーマットを YUV444 に変換したい場合 #clip = core.ffms2.Source(source='src.mp4',format=vs.YUV444P8) # savfsource でソースを読み込み (WebM:VP9 の場合など) #clip = core.savf.Source('src.webm') # フォーマットを YUV444 に変換したい場合 (savf.Source でフォーマット変換はできない) #clip = core.resize.Point(clip,format=vs.YUV444P8) # フレームのカット clip = clip[0:100] + clip[200:300] + clip[500:] # 画像の左右8pxを切り取り # [!] YUV420 の状態だと、奇数位置からの切り取りや、奇数サイズでの切り取りはできない #clip = core.std.Crop(clip,left=8,right=8) # 画像のリサイズ clip = core.resize.Spline36(clip,width=1280,height=720,format=vs.YUV420P8) # 映像を出力 clip.set_output()
シーンカット
映像のシーンカットを行いたい場合は、まず、VapourSynth ビューアなどを使って、カットするフレームの位置を確認します。
プレビュー用のスクリプトを用意
まずは、映像を読み込むだけの、プレビュー用スクリプトファイルを用意します。
<prev.vpy>
<prev.vpy>
#!/usr/bin/python3 import vapoursynth as vs core = vs.core c = core.ffms2.Source(source='src.mp4') c.set_output()
フレーム位置を確認
ビューア上でスクリプトを読み込み、表示される画像を確認しながら、カットするフレーム位置を見つけてください。
映像のフレーム位置は、0が先頭になります。
スクリプトに書く際には、カットする部分の範囲ではなく、映像として残す部分の範囲を指定するため、「先頭のフレーム位置」と「終端のフレーム位置 + 1」の数値を確認します。
位置が確認できたら、エンコード用のスクリプトファイルに記述していきます。
なお、一度エンコードした後に、映像もしくは音声を再エンコードすることになった場合、フレーム位置をまた一から確認するのは面倒なので、カット部分のコードを別のテキストに保存しておくと、再エンコードする時に楽になります。
映像のフレーム位置は、0が先頭になります。
スクリプトに書く際には、カットする部分の範囲ではなく、映像として残す部分の範囲を指定するため、「先頭のフレーム位置」と「終端のフレーム位置 + 1」の数値を確認します。
位置が確認できたら、エンコード用のスクリプトファイルに記述していきます。
# 100〜199 + 300〜500 clip = clip[100:200] + clip[300:501]
なお、一度エンコードした後に、映像もしくは音声を再エンコードすることになった場合、フレーム位置をまた一から確認するのは面倒なので、カット部分のコードを別のテキストに保存しておくと、再エンコードする時に楽になります。
映像のエンコード
vspipe でエンコードスクリプトを読み込んで、標準出力に y4m 形式 (無圧縮) で出力し、パイプで繋げた x264 コマンドを使って、H.264 でエンコードします。
Arch Linux/Ubuntu のパッケージ名は、x264 です。
Arch Linux/Ubuntu のパッケージ名は、x264 です。
$ vspipe -c y4m enc.vpy - | x264 --demuxer y4m --sar 1:1 --crf 23 - -o out.264
x264 オプション (一部抜粋)
--demuxer y4m | 入力のフォーマットを y4m に指定 |
---|---|
--profile STR | H.264 プロファイル。 再生機器によっては対応プロファイルが制限されているため、PC 以外で再生したいや、8bit 以上のビット数にしたい場合は適切なものを選んでください。 main : 携帯機器など用 high : PC 用 high10 : 8〜10bit をサポート high422 : high10 + YUV422 サポート high444 : high10 + YUV444 サポート |
--fps N | 映像のフレームレートを指定します。 整数または分数表記で指定できます。 入力の y4m にフレームレートの情報があるので、指定しなくても構いません。 |
-I N | キーフレーム間隔の最大サイズ。 fps * 10 の値が妥当。 |
--sar N:N | 再生時のピクセルのアスペクト比。 通常は 1:1 です。 |
--crf FLOAT | 品質ベースの VBR。 0.0〜51.0 の数値で品質を指定する。値が小さいほど高画質になる。 20 あたりで、ほぼ劣化は気にならない。 23 (デフォルト) で、画質とサイズのバランスが良い。動きのある部分は少し劣化が気になる。 |
--bframes N | Bフレーム (差分) の最大連続数。 値を大きくすると容量を下げることができるが、再生時とエンコード時の負荷が増える。 デフォルトの3にしておいた方が、多くの機器で再生できるかも。 |
--ref N | 参照するフレームの距離の最大数。 値を大きくすると圧縮率と画質が上がるが、再生とエンコードの負荷が増える。 |
--me STR | 動き探索モード。 hex でデフォルト。 umh はそれより一段階負荷が重く、高品質。 |
音声を WAV に変換
音声は、映像とは別にエンコードしていきます。
ソースの音声が AAC などでエンコードされている場合は、まず動画から音声を抽出した後、無圧縮の WAV に変換します。
※動画から直接音声を WAV 変換することも出来ますが、動画に設定されている音声の遅延分がスキップされてデコードされてしまうので、エンコーダディレイは手動で除去します。
※動画内に音声が複数格納されている場合、動画の情報を確認した上で、必要なトラックだけを抽出してください。
※ソースが 5.1ch などの場合、必要であれば 2ch に変換してください。
詳しくは、音声のエンコーダディレイ - デコード を参考にしてください。
音声が複数あって、2番目以降の音声を指定したい場合は、
"-map [入力ファイルの番号(通常 0)]:a:[何番目の音声か(0〜)]" で指定します。
この状態では、WAV にエンコーダディレイの無音部分が含まれています。
ソースの音声が AAC などでエンコードされている場合は、まず動画から音声を抽出した後、無圧縮の WAV に変換します。
※動画から直接音声を WAV 変換することも出来ますが、動画に設定されている音声の遅延分がスキップされてデコードされてしまうので、エンコーダディレイは手動で除去します。
※動画内に音声が複数格納されている場合、動画の情報を確認した上で、必要なトラックだけを抽出してください。
※ソースが 5.1ch などの場合、必要であれば 2ch に変換してください。
詳しくは、音声のエンコーダディレイ - デコード を参考にしてください。
ffmpeg を使って、音声を抽出
## 最初の音声を抽出 (MP4->AAC) $ ffmpeg -i src.mp4 -c:a copy asrc.aac ## 2番目の音声を抽出 $ ffmpeg -i src.mp4 -map 0:a:1 -c:a copy asrc.aac
音声が複数あって、2番目以降の音声を指定したい場合は、
"-map [入力ファイルの番号(通常 0)]:a:[何番目の音声か(0〜)]" で指定します。
ffmpeg を使って、抽出した音声を WAV 変換
## AAC->WAV $ ffmpeg -i asrc.aac src.wav ## 5.1chなど -> 2ch $ ffmpeg -i asrc.aac -ac 2 src.wav
この状態では、WAV にエンコーダディレイの無音部分が含まれています。
音声の編集を行う
音量の変更などを行いたい場合は、出力した WAV ファイルを編集してください。
音声を AAC でエンコード
WAV ファイルを元に、音声を AAC でエンコードします。
ここでは、fdkaac コマンドを使います。
Arch Linux/Ubuntu でのパッケージ名は、fdkacc です。
音質重視なら、LC-AAC : 128 kbps〜。
圧縮重視なら、HE-AAC : 48〜80 kbps。
超低ビットレートなら、HE-AAC v2 : 32 kbps 以下。
ここでは、fdkaac コマンドを使います。
Arch Linux/Ubuntu でのパッケージ名は、fdkacc です。
vasynccut
https://gitlab.com/azelpg/vasynccut
音声ファイル単独で、映像のフレーム位置に合わせてカットを行うソフトを自作しました。
これを使って音声をカットし、出力した WAV をエンコードします。
オーディオのサンプル位置は、「映像のフレーム位置 * 音声のサンプルレート(Hz) / fps」で計算できるので、VapourSynth のスクリプトで音声を処理しなくても、音声ファイルだけで、映像に合わせたカットが行えます。
映像は映像、音声は音声で別々に処理した方が効率が良いので、ここでは vasynccut を使います。
音声ファイル単独で、映像のフレーム位置に合わせてカットを行うソフトを自作しました。
これを使って音声をカットし、出力した WAV をエンコードします。
オーディオのサンプル位置は、「映像のフレーム位置 * 音声のサンプルレート(Hz) / fps」で計算できるので、VapourSynth のスクリプトで音声を処理しなくても、音声ファイルだけで、映像に合わせたカットが行えます。
映像は映像、音声は音声で別々に処理した方が効率が良いので、ここでは vasynccut を使います。
カットとエンコード
以下のコマンドで、WAV の音声を映像のフレーム単位でカットし、出力した WAV を fdkaac でエンコードします。
"0:100 200:300" は、フレーム位置の指定です。
映像エンコード用のスクリプトで記述した、シーンカットの clip[A:B] の [] 内の文字列をそのまま指定します。
※終端位置は省略できません。
フレーム位置を複数指定した場合は、先頭から順番に結合されます。
※Opus の場合は、デコード時に余分な部分は自動で除外されるため、問題ありません。
エンコーダディレイについての詳細は、音声のエンコーダディレイ - 基本 をご覧ください。
例えば、fdkaac でエンコードされた LC-AAC をデコードした場合、デコード後の音声データの先頭に 2048 サンプル分の余分な無音部分があるので、それをスキップする必要があります。
vasynccut では、エンコーダディレイを除外するために、開始位置のサンプル数を指定できるので、ソースの音声が fdkaac でエンコードされた LC-AAC であれば、"-s 2048" で、先頭の 2048 サンプルをスキップします。
$ vasynccut -i src.wav -f 24000/1001 0:100 200:300 | fdkaac -b 128 -o out.m4a -
"0:100 200:300" は、フレーム位置の指定です。
映像エンコード用のスクリプトで記述した、シーンカットの clip[A:B] の [] 内の文字列をそのまま指定します。
※終端位置は省略できません。
フレーム位置を複数指定した場合は、先頭から順番に結合されます。
clip = clip[0:100] + clip[200:300] => "0:100 200:300" で指定
vasynccut オプション
-f <N or NUM/DEN> | 映像のフレームレートの指定。 23.976 fps なら、24000/1001。 29.97 fps なら、30000/1001。 |
---|---|
-s <INT> | 先頭の指定サンプル数を除外する。 入力の音声にエンコーダディレイがある場合に使います。 |
-v <FLOAT> | 音量を変更する場合、デシベル単位で指定。 |
エンコーダディレイ
ソースの音声が AAC などの場合、WAV に変換した時に、先頭にエンコーダディレイの余分な無音部分が含まれています。※Opus の場合は、デコード時に余分な部分は自動で除外されるため、問題ありません。
エンコーダディレイについての詳細は、音声のエンコーダディレイ - 基本 をご覧ください。
例えば、fdkaac でエンコードされた LC-AAC をデコードした場合、デコード後の音声データの先頭に 2048 サンプル分の余分な無音部分があるので、それをスキップする必要があります。
vasynccut では、エンコーダディレイを除外するために、開始位置のサンプル数を指定できるので、ソースの音声が fdkaac でエンコードされた LC-AAC であれば、"-s 2048" で、先頭の 2048 サンプルをスキップします。
$ vasynccut -i src.wav -f 24000/1001 -s 2048 0:100 | fdkaac ...
fdkaac コマンド
-o <FILE> | 出力ファイル名 |
---|---|
-p <N> | プロファイル。 2 : LC-AAC 5 : HE-AAC 29 : HE-AAC v2 |
-b <N> | CBR ビットレート (kbps) |
音質重視なら、LC-AAC : 128 kbps〜。
圧縮重視なら、HE-AAC : 48〜80 kbps。
超低ビットレートなら、HE-AAC v2 : 32 kbps 以下。
映像と音声の結合
エンコードした映像と音声を、最終的に MP4 に結合するために、MP4Box コマンドを使います。
Arch Linux の場合、MP4Box は gpac パッケージ内にあります。
Arch Linux の場合、MP4Box は gpac パッケージ内にあります。
結合
x264 でエンコードした映像は out.264、AAC でエンコードした音声は out.m4a なので、この2つを MP4 コンテナに格納します。
-add で、コンテナに追加するトラックのファイルを指定します。
ファイル名の後に ":" を付けると、そのトラックにオプションを指定することができます。
-new は、常に出力ファイルを新規作成の状態にします。既存のファイルがある場合、このオプションがないと、トラックが追加されます。
AAC にはエンコーダディレイがあるので、先頭の余分な無音部分の遅延情報を delay オプションで指定します。
これを設定しないと、エンコーダディレイのサンプル数分、音声が少し遅れて再生されます。
(LC-AAC 44100 Hz であれば、0.046 秒ほどなので、視聴する分にはそこまで差は感じませんが)
$ MP4Box -add out.264 -add out.m4a:delay=-2048/44100 -new res.mp4
-add で、コンテナに追加するトラックのファイルを指定します。
ファイル名の後に ":" を付けると、そのトラックにオプションを指定することができます。
-new は、常に出力ファイルを新規作成の状態にします。既存のファイルがある場合、このオプションがないと、トラックが追加されます。
AAC にはエンコーダディレイがあるので、先頭の余分な無音部分の遅延情報を delay オプションで指定します。
これを設定しないと、エンコーダディレイのサンプル数分、音声が少し遅れて再生されます。
(LC-AAC 44100 Hz であれば、0.046 秒ほどなので、視聴する分にはそこまで差は感じませんが)
delay
詳しくは、音声のエンコーダディレイ - 遅延のセット をご覧ください。
「delay=-<エンコーダディレイのサンプル数>/<サンプルレート>」で、分数表記による秒単位で指定します。
音声の開始位置を早めたいので、ここでは負の値にします。
整数で指定した場合は、ミリセカンド単位になります。
fdkaac でエンコードした AAC の場合、エンコーダディレイのサンプル数は、LC-AAC なら 2048、HE-AAC なら 5058、HE-AAC v2 なら 7106 になります。
Opus の場合は自動で設定されるので、指定する必要はありません。
「delay=-<エンコーダディレイのサンプル数>/<サンプルレート>」で、分数表記による秒単位で指定します。
音声の開始位置を早めたいので、ここでは負の値にします。
整数で指定した場合は、ミリセカンド単位になります。
fdkaac でエンコードした AAC の場合、エンコーダディレイのサンプル数は、LC-AAC なら 2048、HE-AAC なら 5058、HE-AAC v2 なら 7106 になります。
Opus の場合は自動で設定されるので、指定する必要はありません。
音声を再エンコードする
音声だけ再エンコードしたい場合は、vasynccut などを使って音声を再エンコードした後、MP4Box で結合する時に、作成済みの mp4 ファイルから映像を読み込むことができます。
res.mp4 からエンコード済みの映像を取得したい場合、"-add res.mp4#video" として、ファイル名の後に #video を付けると、res.mp4 の最初の映像トラックを入力に指定できます。
トラックIDで指定したい場合は、"-add res.mp4#1" というように、番号の数字 (1〜) を指定します。
$ MP4Box -add res.mp4#video -add out.m4a:delay=-2048/44100 -new res2.mp4
res.mp4 からエンコード済みの映像を取得したい場合、"-add res.mp4#video" として、ファイル名の後に #video を付けると、res.mp4 の最初の映像トラックを入力に指定できます。
トラックIDで指定したい場合は、"-add res.mp4#1" というように、番号の数字 (1〜) を指定します。
遅延の再設定
これは、音声の遅延を再設定したい場合などでも使えます。
res.mp4 の最初の映像と音声を読み込んで、新しい動画を作成することができます。
なお、delay を指定しなかった場合、元の mp4 ファイルの遅延が維持されます。
$ MP4Box -add res.mp4#video -add res.mp4#audio:delay=-2048/44100 -new res2.mp4
res.mp4 の最初の映像と音声を読み込んで、新しい動画を作成することができます。
なお、delay を指定しなかった場合、元の mp4 ファイルの遅延が維持されます。