はじめに
VapourSynth を使った基本的な動画エンコードの手順は前回説明したので、ここでは、実際の音声も合わせた動画エンコード全体の手順を説明していきます。
以下では、例として、変換元の動画を MKV or MP4 (H.264 + AAC) とし、
シーンカットと映像のリサイズを行って、MP4 (H.264 + AAC) に再エンコードするものとします。
ノイズ除去や逆テレシネなどの処理は行っていませんが、必要な場合はスクリプトに記述してください。
以下では、例として、変換元の動画を MKV or MP4 (H.264 + AAC) とし、
シーンカットと映像のリサイズを行って、MP4 (H.264 + AAC) に再エンコードするものとします。
ノイズ除去や逆テレシネなどの処理は行っていませんが、必要な場合はスクリプトに記述してください。
エンコード用スクリプトを用意
エンコード用のスクリプトを用意します。
以下では、シーンカットとリサイズを行っています。
<例: enc.vpy>
以下では、シーンカットとリサイズを行っています。
<例: enc.vpy>
#!/usr/bin/python3 import vapoursynth as vs core = vs.core core.std.LoadPlugin('libdamb.so') clip = core.ffms2.Source(source='source.mkv') clip = core.damb.Read(clip, 'src.wav') clip = clip[0:100] + clip[200:300] + clip[500:] clip = core.resize.Spline36(clip,width=1280,height=720,format=vs.YUV420P8) clip = core.damb.Write(clip, 'cut.wav') clip.set_output()
カット位置の確認
シーンカットをしたい場合は、VapourSynth ビューアなどで、カットするフレームの位置を確認します。
動画ファイル名の部分は書き換えてください。
<prev.vpy>
ビューア上で映像を確認しながら、カットするフレーム位置を見つけてください。
なお、スクリプトに書く際には、カットする部分の範囲ではなく、映像として残す部分の範囲が必要なので、「残す部分の先頭位置」と「残す部分の終端位置+1」のフレーム位置を確認します。
位置が確認できたら、エンコード用のスクリプトファイルに、カット処理を記述します。
例えば、フレーム位置 「0〜99、200〜299」 をカットして、「100〜199、300〜終端」 の範囲を残したい場合、以下のようになります。
また、再エンコードする必要が出た場合などは、フレーム位置をもう一度確認するのは面倒なので、カット部分のコードを別のテキストに保存しておくと、再エンコード時に楽になります。
プレビュー用のスクリプトを用意
まずは、映像を読み込むだけの、プレビュー用のスクリプトファイルを用意します。動画ファイル名の部分は書き換えてください。
<prev.vpy>
#!/usr/bin/python3 import vapoursynth as vs core = vs.core c = core.ffms2.Source(source='source.mkv') c.set_output()
フレーム位置を確認
フレーム位置は、ビューア上でも VapourSynth スクリプト側でも、「0 〜 (最大フレーム数 - 1)」の数値で指定します。ビューア上で映像を確認しながら、カットするフレーム位置を見つけてください。
なお、スクリプトに書く際には、カットする部分の範囲ではなく、映像として残す部分の範囲が必要なので、「残す部分の先頭位置」と「残す部分の終端位置+1」のフレーム位置を確認します。
位置が確認できたら、エンコード用のスクリプトファイルに、カット処理を記述します。
例えば、フレーム位置 「0〜99、200〜299」 をカットして、「100〜199、300〜終端」 の範囲を残したい場合、以下のようになります。
clip = clip[100:200] + clip[300:]
また、再エンコードする必要が出た場合などは、フレーム位置をもう一度確認するのは面倒なので、カット部分のコードを別のテキストに保存しておくと、再エンコード時に楽になります。
元動画から音声を抽出
音声は、映像とは別にエンコードする必要があるので、元動画から音声のみを抽出して、処理します。
映像も同時に抽出することはできますが、MP4Box コマンドで mp4 から映像を抽出した場合、元動画をそのまま読み込んだ場合と、抽出した映像を読み込んだ場合とで、フレーム位置が 1 ずれる場合があったので、映像は、元動画から直接読み込むことにします。
※動画内に音声が複数格納されている場合は、動画の情報を確認した上で、必要なトラックだけ抽出してください。
※以下は、音声が AAC の場合として扱っています。
映像も同時に抽出することはできますが、MP4Box コマンドで mp4 から映像を抽出した場合、元動画をそのまま読み込んだ場合と、抽出した映像を読み込んだ場合とで、フレーム位置が 1 ずれる場合があったので、映像は、元動画から直接読み込むことにします。
※動画内に音声が複数格納されている場合は、動画の情報を確認した上で、必要なトラックだけ抽出してください。
※以下は、音声が AAC の場合として扱っています。
ffmpeg を使う
ffmpeg を使えば、大抵の動画は処理できます。
## 動画情報表示 $ ffprobe source.mkv ## 音声のみコピーして出力 $ ffmpeg -i source.mkv -vn -acodec copy src.aac ## 1番目の音声のみコピーして出力 ## -map <input_file_no>:a:<audio_no> $ ffmpeg -i source.mp4 -map 0:a:0 -acodec copy src.aac
MKV 動画の場合
MKV のツール類 (mkv* コマンド) を使います。
Arch Linux: mkvtoolnix-cli パッケージ。
Ubuntu: mkvtoolnix パッケージ。
Arch Linux: mkvtoolnix-cli パッケージ。
Ubuntu: mkvtoolnix パッケージ。
## 各トラック抽出 ## mkvextract <mkv_file> tracks <TrackID>:<output_name> ## (ID:0 が映像、ID:1 が音声の場合) $ mkvextract source.mkv tracks 1:src.aac
MP4 動画の場合
MP4Box コマンドで抽出します。
Arch Linux/Ubuntu: gpac パッケージ。
Arch Linux/Ubuntu: gpac パッケージ。
## MP4Box -raw <TrackNo>[:output=<filename>] <mp4file> ## ':output=...' を省略すると、適当な名前で出力されます $ MP4Box -raw 2:output=src.aac source.mp4
音声を wav 変換
シーンカットをする場合は、Damb プラグインで WAV ファイルを読み込む必要があるので、ソースの音声が WAV ではない場合は、WAV に変換します。
ただし、シーンカットや音声編集を行わない場合は、WAV 変換と音声エンコードのコマンドをパイプでつないで、直接エンコードしても構いません。
ただし、シーンカットや音声編集を行わない場合は、WAV 変換と音声エンコードのコマンドをパイプでつないで、直接エンコードしても構いません。
エンコーダディレイについて
MP3 や AAC でエンコードされたファイルには、音声データの先頭に、「エンコーダディレイ」 と呼ばれるデータが付加されます。
エンコーダディレイの部分は、デコードの際に無音として変換されるため、そのデータの長さ分、先頭に数十 ms 程度の余分な無音が追加されてしまいます。
例えば、動画の音声を AAC → WAV → AAC というように再エンコードする場合、最初の AAC → WAV の部分で、エンコーダディレイの無音部分が追加されると、音声が少し遅れてしまうので、音ズレの原因となります。
正確に音ズレを回避するためには、変換後の WAV ファイルのエンコーダディレイ部分を、手動でカットする必要があります。
ただし、エンコーダディレイの長さは、AAC エンコーダやエンコード形式によって異なります。
エンコーダディレイの部分は、デコードの際に無音として変換されるため、そのデータの長さ分、先頭に数十 ms 程度の余分な無音が追加されてしまいます。
例えば、動画の音声を AAC → WAV → AAC というように再エンコードする場合、最初の AAC → WAV の部分で、エンコーダディレイの無音部分が追加されると、音声が少し遅れてしまうので、音ズレの原因となります。
正確に音ズレを回避するためには、変換後の WAV ファイルのエンコーダディレイ部分を、手動でカットする必要があります。
ただし、エンコーダディレイの長さは、AAC エンコーダやエンコード形式によって異なります。
AAC を WAV 変換
faad コマンドか、ffmpeg などを使います。
Arch Linux: faad2 パッケージ。
Ubuntu: faad パッケージ。
※詳しくは後述しますが、faad コマンドの場合、エンコーダディレイを常に 1024 サンプルとして、その分を自動で削除して出力されます(エンコーダディレイを完全に取り除くわけではない)。
ffmpeg の場合、エンコーダディレイはすべて無音として含まれます。
先頭のエンコーダディレイがどれくらいかわからない、または確かめるのが面倒だという場合は、とりあえず faad でデコードしておけば良いでしょう。
エンコーダディレイが 1024 サンプルではなかった場合、先頭の 1024 サンプルを削除しても、まだ無音部分が残る場合がありますが、ffmpeg よりは短くなるので、再生してもそこまで気になることはありません。
ffmpeg で、エンコーダディレイ部分を直接カットして出力したい場合は、atrim フィルタを使います。
「start_sample=」 の所に、エンコーダディレイの先頭サンプル数を指定してください。
Arch Linux: faad2 パッケージ。
Ubuntu: faad パッケージ。
## faad $ faad -o out.wav src.aac ## ffmpeg $ ffmpeg -i src.aac out.wav ## ffmpeg (2048 サンプル分先頭カット) $ ffmpeg -i src.aac -filter_complex atrim=start_sample=2048,asetpts=PTS-STARTPTS out.wav
※詳しくは後述しますが、faad コマンドの場合、エンコーダディレイを常に 1024 サンプルとして、その分を自動で削除して出力されます(エンコーダディレイを完全に取り除くわけではない)。
ffmpeg の場合、エンコーダディレイはすべて無音として含まれます。
先頭のエンコーダディレイがどれくらいかわからない、または確かめるのが面倒だという場合は、とりあえず faad でデコードしておけば良いでしょう。
エンコーダディレイが 1024 サンプルではなかった場合、先頭の 1024 サンプルを削除しても、まだ無音部分が残る場合がありますが、ffmpeg よりは短くなるので、再生してもそこまで気になることはありません。
ffmpeg で、エンコーダディレイ部分を直接カットして出力したい場合は、atrim フィルタを使います。
「start_sample=」 の所に、エンコーダディレイの先頭サンプル数を指定してください。
シーンカットと音声の編集を行う場合
シーンカットに加えて、音量変更などの編集も行う場合は、シーンカットして出力した音声データに対して行う方が、後々面倒がなくて良いです。
その場合、以下のような流れになります。
「カット前の音声 → 映像エンコードと同時に、カット後の音声出力 → (音声編集) → カット後の音声をエンコード」
この時、カット前の音声に対して、先に音声編集をしてしまうと、映像のエンコード後に音声の編集をやり直したいとなった場合、もう一度映像のエンコードからやり直さなければならなくなります(カット後の音声は、すでに音声編集された状態のため)。
無編集の状態でカットして出力しておけば、音声編集をやり直したい場合は、カット後の音声を再編集・再エンコードするだけで済みます。
その場合、以下のような流れになります。
「カット前の音声 → 映像エンコードと同時に、カット後の音声出力 → (音声編集) → カット後の音声をエンコード」
この時、カット前の音声に対して、先に音声編集をしてしまうと、映像のエンコード後に音声の編集をやり直したいとなった場合、もう一度映像のエンコードからやり直さなければならなくなります(カット後の音声は、すでに音声編集された状態のため)。
無編集の状態でカットして出力しておけば、音声編集をやり直したい場合は、カット後の音声を再編集・再エンコードするだけで済みます。
映像のエンコードと音声の出力
今回は H.264 でエンコードします。
Arch Linux/Ubuntu: x264 パッケージ。
シーンカットをする場合は、映像のエンコードと同時に、カット後の音声ファイル (VapourSynth スクリプトに記述したファイル名) が出力されます。
上記は、アニメ用 (23.976 fps) で、中〜高画質、高負荷、高圧縮で、どちらかというと圧縮重視の設定です。
ある程度時間はかかってもいいので、できるだけ容量を抑えつつ、画質をそれなりに保つようにしました。
これよりもう少し容量を小さくしたい場合は、--qcomp 0.5 を追加したり、--crf を +0.5 するなどすると良いです。
画質重視にする場合は、また別の設定となります。
ただし、.264 ファイルの場合は、再生時にシークができないので、映像を確認する時に不便かもしれません。
MP4Box などで結合する場合は、.mp4 で構いません。
Arch Linux/Ubuntu: x264 パッケージ。
シーンカットをする場合は、映像のエンコードと同時に、カット後の音声ファイル (VapourSynth スクリプトに記述したファイル名) が出力されます。
$ vspipe -c y4m enc.vpy - | x264 --demuxer y4m \ --profile high --fps 24000/1001 -I 240 --sar 1:1 \ --crf 23 --bframes 3 --ref 5 --b-adapt 2 --direct auto --rc-lookahead 40 \ --me umh --subme 8 --trellis 2 \ --aq-mode 2 --aq-strength 0.8 --psy-rd 0.0:0.0 - -o out.264
上記は、アニメ用 (23.976 fps) で、中〜高画質、高負荷、高圧縮で、どちらかというと圧縮重視の設定です。
ある程度時間はかかってもいいので、できるだけ容量を抑えつつ、画質をそれなりに保つようにしました。
これよりもう少し容量を小さくしたい場合は、--qcomp 0.5 を追加したり、--crf を +0.5 するなどすると良いです。
画質重視にする場合は、また別の設定となります。
出力形式について
なお、L-SMASH ツール (muxer コマンド) で MP4 結合を行った場合、ここで出力ファイルを .mp4 にすると、fps が 0 になるなどして、うまく結合できなかったので、.264 で出力しています。ただし、.264 ファイルの場合は、再生時にシークができないので、映像を確認する時に不便かもしれません。
MP4Box などで結合する場合は、.mp4 で構いません。
音声の編集とエンコード
シーンカットした場合は、映像エンコード後に、カットされた音声ファイルが出力されるので、そのファイルに対して、音声の編集とエンコードを行います。
音声の編集
GUI で行うなら audacity、CUI で行うなら sox コマンドを使います。
Arch Linux の場合、パッケージ名は、上記のコマンド名と同じです。
-n で、変更後の音量の最大値が、指定した dB になるようにします。
ここでは、-1 dB を指定して、MAX (0 dB) より少し音量を下げて、音割れを防いでいます。
Arch Linux の場合、パッケージ名は、上記のコマンド名と同じです。
sox コマンドで音量正規化
$ sox cut.wav cut_out.wav gain -n -1
-n で、変更後の音量の最大値が、指定した dB になるようにします。
ここでは、-1 dB を指定して、MAX (0 dB) より少し音量を下げて、音割れを防いでいます。
AAC エンコード
音声をエンコードします。
ここでは、fdkaac コマンドで、AAC にエンコードします。
Arch Linux/Ubuntu: fdkacc パッケージ。
音声編集しなかった場合は、カット後の音声ファイルを、音声編集した場合は、編集後のファイルを元にエンコードします。
音質重視なら、LC-AAC : 128 kbps〜。
圧縮重視なら、HE-AAC : 48〜80 kbps。
超低ビットレートなら、HE-AAC v2 : 32 kbps 以下。
ここでは、fdkaac コマンドで、AAC にエンコードします。
Arch Linux/Ubuntu: fdkacc パッケージ。
音声編集しなかった場合は、カット後の音声ファイルを、音声編集した場合は、編集後のファイルを元にエンコードします。
## HE-AAC 64 kbps $ fdkaac -p 5 -b 64 -o out.m4a cut.wav
-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)
映像と音声を MP4 に結合するには、「L-SMASH ツールの muxer コマンド」 か 「MP4Box」 を使います。
ffmpeg でも出来ますが、一応 MP4 に特化したツールを使った方が良いでしょう。
音ズレ調整に関しては、L-SMASH の方が使いやすいです。
ただし、うまく結合しない場合もあるので、気に入った方を使ってください。
Arch Linux の場合、L-SMASH は l-smash パッケージ、MP4Box は gpac パッケージ。
L-SMASH: https://github.com/l-smash/l-smash
ffmpeg でも出来ますが、一応 MP4 に特化したツールを使った方が良いでしょう。
音ズレ調整に関しては、L-SMASH の方が使いやすいです。
ただし、うまく結合しない場合もあるので、気に入った方を使ってください。
Arch Linux の場合、L-SMASH は l-smash パッケージ、MP4Box は gpac パッケージ。
L-SMASH: https://github.com/l-smash/l-smash
結合
x264 でエンコードした映像を out.264、AAC エンコードした音声を out.m4a として、この2つを MP4 コンテナに格納します。
※音ズレ調整の数値は、音声ごとに適切な値があるので、後述します。
以下は、fdkaac でエンコード、HE-AAC、44100 Hz の場合の数値です。
--language jpn は、すべてのトラックの言語を日本語にします。
なくても構いません。
encoder-delay または delay オプションは、音ズレ調整のための設定です。
エンコーダディレイの分、音声を先に読み込んで、映像と合わせます。
出力された動画ファイルを再生してみて、問題なければ完成です。
※音ズレ調整の数値は、音声ごとに適切な値があるので、後述します。
以下は、fdkaac でエンコード、HE-AAC、44100 Hz の場合の数値です。
## L-SMASH の場合 $ muxer -i out.264 -i out.m4a?encoder-delay=2048 --language jpn -o res.mp4 ## MP4Box の場合 $ MP4Box -add out.mp4 -add out.m4a:delay=-46:lang=jpn -new res.mp4
--language jpn は、すべてのトラックの言語を日本語にします。
なくても構いません。
encoder-delay または delay オプションは、音ズレ調整のための設定です。
エンコーダディレイの分、音声を先に読み込んで、映像と合わせます。
出力された動画ファイルを再生してみて、問題なければ完成です。
MP4 タグ
MP4 にタイトルなどのタグを付けたい場合は、mp4tags コマンドを使います。
Arch Linux: libmp4v2 パッケージ。
Ubuntu: mp4v2-utils パッケージ。
Arch Linux: libmp4v2 パッケージ。
Ubuntu: mp4v2-utils パッケージ。
## タイトルを付加 $ mp4tags -s "タイトル" res.mp4 ## WEB 上でダウンロードしながら再生できるようにする $ mp4file --optimize res.mp4
音ズレについて
元動画やエンコード後の音声が MP3 や AAC の場合、WAV 変換時や映像・音声結合時に注意しておかないと、再生時に音ズレしてしまいます。
エンコーダディレイ
MP3 や AAC の場合、音声データの先頭と終端に、「エンコーダディレイ」と呼ばれる部分が追加されています。
その部分は、デコード時に無音として扱われるため、再生時間もその分少しだけ長くなります。
通常、デコーダは、エンコーダディレイをそのまま音声データとして変換するので、動画の音声として使う場合は、エンコーダディレイを正しく処理しないと、音ズレの原因となります。
元の音声を WAV に変換する際は、変換後の音声データからエンコーダディレイを除去し、また、音声をエンコードして動画と結合する時は、エンコーダディレイ分の音声をスキップして、映像と合わせるようにする必要があります。
その部分は、デコード時に無音として扱われるため、再生時間もその分少しだけ長くなります。
通常、デコーダは、エンコーダディレイをそのまま音声データとして変換するので、動画の音声として使う場合は、エンコーダディレイを正しく処理しないと、音ズレの原因となります。
元の音声を WAV に変換する際は、変換後の音声データからエンコーダディレイを除去し、また、音声をエンコードして動画と結合する時は、エンコーダディレイ分の音声をスキップして、映像と合わせるようにする必要があります。
AAC → WAV にデコード
動画から抽出した音声をデコードして WAV に変換する場合、エンコーダディレイは自動で除去できません。
なぜなら、エンコーダディレイがどれだけ含まれているかという情報は、生の音声ファイルには記述されていないからです。
しかし、*.aac の AAC ファイルの場合は、ファイル内に AAC の生の音声データしか含まれていないため、エンコーダディレイの情報はありません。
そのため、デコーダは、エンコーダディレイも含めてすべて音声データとして処理します。
動画の結合時には、生の音声データのみが書き込まれるため、エンコード後のファイルにエンコーダディレイの情報が含まれていても、それらは動画内には格納されません。
faad の場合は特殊で、エンコーダディレイの除去は、中途半端な形で対応しています。
faac でエンコードされた AAC ファイルの場合、先頭のエンコーダディレイは 1024 サンプルなので、常に先頭の 1024 サンプル分を削除して出力します。
実際はエンコーダや形式ごとにサンプル数が違うので、これだけでは完全にエンコーダディレイを取り除けない場合がありますが、先頭のエンコーダディレイが 1024 サンプルを下回ることはないので、切り取りすぎる心配はないし、全く除去しないよりはマシという形になります。
ffmpeg など、通常のデコーダの場合、エンコーダディレイはすべて音声データとしてデコードするので、エンコーダディレイ部分は無音となります。
正確にエンコーダディレイを取り除きたい場合は、ffmpeg を使ってデコードし、先頭や終端のエンコーダディレイ部分を手動でカットしてください。
※ fdkaac はデフォルトで m4a 形式で出力されるので、"-f 2" オプションで ADTS の生 AAC データにします。
>> 比較画像
※ fdkaac LC-AAC の場合、先頭のエンコーダディレイのサンプル数は 2048 です。
※ 下2つの先頭部分に、ソースにない波形が出ていますが、その部分はエンコーダディレイなので、無視します。
ffmpeg でデコードした場合は、2048 サンプル (44100Hz で 46ms) 分、先頭に余分な部分があります。
faad でデコードした場合は、2048 - 1024 = 1024 サンプル分、先頭に余分な部分があります。
「小数点以下の時間 × samplerate(Hz)」で、サンプル数を逆算できます。
例えば、時間が「00:00:00.021333333」となっていて、音声が 48000Hz の場合、"0.021333 * 48000 = 1023.984" で、1024 サンプルとなります。
(0.2133... は、1秒を 1.0 とした時の、1秒未満の時間を表しています)
元動画が MP4 の場合は、エンコーダディレイ対策として、音声遅延が設定されていれば、その秒数から取得できます。
ただ、エンコーダディレイ対策ではなく、単純に音ズレ調整のために使われている場合もあるので、注意してください。
元動画にエンコーダディレイの情報がない場合、使われた AAC エンコーダがわかっている場合は、以下の表にある値でサンプル数を特定できます。
動画内に情報がなく、エンコーダもわからない場合は、映像の長さと音声の長さを比べたり、WAV 変換して波形を確認したりして、なんとなくこのくらいかなという当たりをつけるしかありません。
ただ、元の音声が LC-AAC で、高サンプリングレートの場合、エンコーダディレイはそれほど長くないので、最低でも 1024 サンプル削っておけば、それほど気にならないかもしれません。
なぜなら、エンコーダディレイがどれだけ含まれているかという情報は、生の音声ファイルには記述されていないからです。
AAC について
MP4 コンテナ (*.m4a) で出力された AAC の場合は、エンコーダが、MP4 コンテナ内にエンコーダディレイの情報を書き込むので、デコーダはそれを元にして、エンコーダディレイを除去できます。しかし、*.aac の AAC ファイルの場合は、ファイル内に AAC の生の音声データしか含まれていないため、エンコーダディレイの情報はありません。
そのため、デコーダは、エンコーダディレイも含めてすべて音声データとして処理します。
動画の結合時には、生の音声データのみが書き込まれるため、エンコード後のファイルにエンコーダディレイの情報が含まれていても、それらは動画内には格納されません。
AAC デコーダ
AAC をデコードする場合、faad や ffmpeg などが使えます。faad の場合は特殊で、エンコーダディレイの除去は、中途半端な形で対応しています。
faac でエンコードされた AAC ファイルの場合、先頭のエンコーダディレイは 1024 サンプルなので、常に先頭の 1024 サンプル分を削除して出力します。
実際はエンコーダや形式ごとにサンプル数が違うので、これだけでは完全にエンコーダディレイを取り除けない場合がありますが、先頭のエンコーダディレイが 1024 サンプルを下回ることはないので、切り取りすぎる心配はないし、全く除去しないよりはマシという形になります。
ffmpeg など、通常のデコーダの場合、エンコーダディレイはすべて音声データとしてデコードするので、エンコーダディレイ部分は無音となります。
正確にエンコーダディレイを取り除きたい場合は、ffmpeg を使ってデコードし、先頭や終端のエンコーダディレイ部分を手動でカットしてください。
デコードの状態を確認してみる
fdkaac で LC-AAC 128 kbps にエンコードした *.aac ファイル (fdkaac -b 128 -f 2) を、ffmpeg と faad でデコードしたものを、元の WAV ファイルと比較してみました。※ fdkaac はデフォルトで m4a 形式で出力されるので、"-f 2" オプションで ADTS の生 AAC データにします。
>> 比較画像
※ fdkaac LC-AAC の場合、先頭のエンコーダディレイのサンプル数は 2048 です。
※ 下2つの先頭部分に、ソースにない波形が出ていますが、その部分はエンコーダディレイなので、無視します。
ffmpeg でデコードした場合は、2048 サンプル (44100Hz で 46ms) 分、先頭に余分な部分があります。
faad でデコードした場合は、2048 - 1024 = 1024 サンプル分、先頭に余分な部分があります。
結論
元動画が MKV の場合は、mkvinfo コマンドで、音声の 「デフォルトのデュレーション」 を見れば、それが先頭のエンコーダディレイの長さになっていると思います。「小数点以下の時間 × samplerate(Hz)」で、サンプル数を逆算できます。
例えば、時間が「00:00:00.021333333」となっていて、音声が 48000Hz の場合、"0.021333 * 48000 = 1023.984" で、1024 サンプルとなります。
(0.2133... は、1秒を 1.0 とした時の、1秒未満の時間を表しています)
元動画が MP4 の場合は、エンコーダディレイ対策として、音声遅延が設定されていれば、その秒数から取得できます。
ただ、エンコーダディレイ対策ではなく、単純に音ズレ調整のために使われている場合もあるので、注意してください。
元動画にエンコーダディレイの情報がない場合、使われた AAC エンコーダがわかっている場合は、以下の表にある値でサンプル数を特定できます。
動画内に情報がなく、エンコーダもわからない場合は、映像の長さと音声の長さを比べたり、WAV 変換して波形を確認したりして、なんとなくこのくらいかなという当たりをつけるしかありません。
ただ、元の音声が LC-AAC で、高サンプリングレートの場合、エンコーダディレイはそれほど長くないので、最低でも 1024 サンプル削っておけば、それほど気にならないかもしれません。
各 AAC エンコーダのエンコーダディレイの長さ
※ HE-AAC v2 は載せていません。
終端のサンプル数は、エンコーダのバージョンによって変わることが多かったり、正確な値がわからない場合があるので、割愛しています。
終端のサンプル数は、エンコーダのバージョンによって変わることが多かったり、正確な値がわからない場合があるので、割愛しています。
形式 | 先頭サンプル数 |
---|---|
fdkaac (ver 1.0.2) | |
LC | 2048 |
HE | 2048 |
NeroAACEnc (ver 1.5.4) | |
LC | 2624 |
HE | 4672 |
qaac (ver 2.64) | |
LC | 2112 |
HE | 2112 |
ffmpeg (ver 3.4.1, 内蔵エンコーダ) | |
LC | 1024 |
エンコーダディレイの長さを調べる
エンコーダディレイは、使ったエンコーダや出力形式などによって、長さが変わります。
HE-AAC/HE-AAC v2 の場合や、サンプリングレートが低い場合、エンコーダディレイは長くなるので、何も対策をしていない場合は、音ズレが気になるかもしれません。
qaac か fdkaac を使って、*.m4a (MP4 コンテナ) で出力した AAC の場合は、ファイル内に iTunSMPB というデータがあるので、その情報からエンコーダディレイの長さ (サンプル数) を取得できます。
※ *.aac で出力した場合、iTunSMPB は書き込まれません。
iTunSMPB が書き込まれないエンコーダの場合は、サイン波などを生成してエンコードし、その波形から確認する方法があります。
バイナリエディタがない場合、fdkaac は終端にデータがあるので、以下のコマンドで確認できます。
※ qaac の場合は先頭から少し進んだ所にあります。
iTunSMPB のデータは 140 byte なので、ファイルサイズから 140 byte を引いた位置から、バイナリデータとその文字を表示しています。
データがファイルの終端に無い場合は、このままでは見えないかもしれません。
先頭の数値部分は無視して、">iTunSMPB....data" 以降のデータを見てください。
以下は、fdkaac / HE-AAC でエンコードした場合のデータです。
データは、16進数の数値を文字列にして空白で区切ったものとなっています。
16進数の文字列を10進数に変換する場合は、以下のようにします。
"16#" の後に変換したい値を入れてください。
fdkaac / HE-AAC の場合、エンコーダディレイの先頭のサンプル数は 2048 であることが分かりました。
HE-AAC/HE-AAC v2 の場合や、サンプリングレートが低い場合、エンコーダディレイは長くなるので、何も対策をしていない場合は、音ズレが気になるかもしれません。
qaac か fdkaac を使って、*.m4a (MP4 コンテナ) で出力した AAC の場合は、ファイル内に iTunSMPB というデータがあるので、その情報からエンコーダディレイの長さ (サンプル数) を取得できます。
※ *.aac で出力した場合、iTunSMPB は書き込まれません。
iTunSMPB が書き込まれないエンコーダの場合は、サイン波などを生成してエンコードし、その波形から確認する方法があります。
iTunSMPB 確認方法
エンコードした *.m4a ファイルをバイナリエディタで開き、先頭か終端部分で "iTunSMPB" という文字列がある部分を探します。バイナリエディタがない場合、fdkaac は終端にデータがあるので、以下のコマンドで確認できます。
※ qaac の場合は先頭から少し進んだ所にあります。
$ od -A n -t x1z -w140 -j $(($(wc -c out.m4a | cut -d ' ' -f1) - 140)) out.m4a※ 2箇所の "out.m4a" のファイル名部分は置き換えてください。
iTunSMPB のデータは 140 byte なので、ファイルサイズから 140 byte を引いた位置から、バイナリデータとその文字を表示しています。
データがファイルの終端に無い場合は、このままでは見えないかもしれません。
先頭の数値部分は無視して、">iTunSMPB....data" 以降のデータを見てください。
以下は、fdkaac / HE-AAC でエンコードした場合のデータです。
データは、16進数の数値を文字列にして空白で区切ったものとなっています。
iTunSMPB....data........ 00000000 00000800 000002AC 0000000000035D54 (以下略)
00000000 | - |
---|---|
00000800 | 先頭のエンコーダディレイのサンプル数 (=2048) |
000002AC | 終端のエンコーダディレイのサンプル数 (=684) |
0000000000035D54 | エンコード前の音声の全サンプル数 (=220500) |
16進数の文字列を10進数に変換する場合は、以下のようにします。
"16#" の後に変換したい値を入れてください。
$ echo $((16#00000800)) 2048
fdkaac / HE-AAC の場合、エンコーダディレイの先頭のサンプル数は 2048 であることが分かりました。
サンプル数を秒数に変換
ところで、「サンプル数」とは何だろうと思うかもしれません。
音声データでよく目にする、44100 Hz や 22050 Hz などのサンプリングレートの値は、「音声データの一秒間のサンプル数」 を表しています。
48000 Hz なら、一秒間に 48000 のサンプルデータがあります。
ということは、音声のサンプリングレート値と、エンコーダディレイのサンプル数を使えば、エンコーダディレイの音声としての時間が計算できることになります。
samplerate は、44100 などの音声のサンプリングレート値。
delay_samples は、エンコーダディレイのサンプル数。
結果は、ミリセカンド (1/1000 秒) です。
44100 Hz で 2048 サンプルなら、46.4399..
22050 Hz で 2048 サンプルなら、92.8798..
MP4 の結合に MP4Box などを使う場合、音声遅延は ms 単位で指定しなければならないので、その場合は、サンプル数を秒数に変換した値を使います。
音声データでよく目にする、44100 Hz や 22050 Hz などのサンプリングレートの値は、「音声データの一秒間のサンプル数」 を表しています。
48000 Hz なら、一秒間に 48000 のサンプルデータがあります。
ということは、音声のサンプリングレート値と、エンコーダディレイのサンプル数を使えば、エンコーダディレイの音声としての時間が計算できることになります。
ms = delay_samples * 1000 / samplerate(Hz)
samplerate は、44100 などの音声のサンプリングレート値。
delay_samples は、エンコーダディレイのサンプル数。
結果は、ミリセカンド (1/1000 秒) です。
44100 Hz で 2048 サンプルなら、46.4399..
22050 Hz で 2048 サンプルなら、92.8798..
MP4 の結合に MP4Box などを使う場合、音声遅延は ms 単位で指定しなければならないので、その場合は、サンプル数を秒数に変換した値を使います。
動画の結合時にエンコーダディレイ対策をする
動画の再生側では、音声のエンコーダディレイ部分はそのまま音声として無音に変換されるので、何も対策をせずに結合すると、エンコーダディレイ分、音声が遅れることになります。
音声に AAC などを使う場合、結合時にエンコーダディレイの対策をする必要があります。
MKV で結合する場合は、おそらく自動で音声ファイルからエンコーダディレイの長さが取得されて、動画内に情報を書き込んでくれるので、基本的には何もする必要はありません(音声ファイルにエンコーダディレイの情報がある場合)。
MP4 で結合する場合は、エンコーダディレイを自動で処理してくれないので、動画に音声遅延の情報を設定して、エンコーダディレイの秒数をずらして再生させる必要があります。
音声に AAC などを使う場合、結合時にエンコーダディレイの対策をする必要があります。
MKV で結合する場合は、おそらく自動で音声ファイルからエンコーダディレイの長さが取得されて、動画内に情報を書き込んでくれるので、基本的には何もする必要はありません(音声ファイルにエンコーダディレイの情報がある場合)。
MP4 で結合する場合は、エンコーダディレイを自動で処理してくれないので、動画に音声遅延の情報を設定して、エンコーダディレイの秒数をずらして再生させる必要があります。
MP4 結合時に音声遅延の設定を行う
MP4 結合時に音声遅延の設定を行う方法を説明します。
結合ツールによって、指定方法が異なります。
ツール側で、サンプル数と音声のサンプリングレート値を元に秒数に変換してくれるので、便利です。
MP4Box コマンドを使う場合は、音声のソース指定時に 「:delay=-<ms 秒数>」 を追加します。
※この時、数値はマイナスを付けて負の値にしてください。
音声の開始を早めて、先頭の無音部分をスキップする必要があるので、開始位置はマイナスにしなければなりません。
正の値にすると、逆に音が遅れてしまいます。
結合ツールによって、指定方法が異なります。
L-SMASH ツール
muxer コマンドで、音声のソース指定時に、「?encoder-delay=<サンプル数>」 を追加します。ツール側で、サンプル数と音声のサンプリングレート値を元に秒数に変換してくれるので、便利です。
$ muxer -i out.mp4 -i out.m4a?encoder-delay=2048 -o res.mp4
MP4Box
L-SMASH 以外のツールでは、基本的に秒数で指定するので、自分でエンコーダディレイの秒数を計算する必要があります。MP4Box コマンドを使う場合は、音声のソース指定時に 「:delay=-<ms 秒数>」 を追加します。
※この時、数値はマイナスを付けて負の値にしてください。
音声の開始を早めて、先頭の無音部分をスキップする必要があるので、開始位置はマイナスにしなければなりません。
正の値にすると、逆に音が遅れてしまいます。
$ MP4Box -add out.mp4 -add out.m4a:delay=-46 -new res.mp4