Doom9 / MPEG-4のB- frameを AVI/VFW に入れるハッキング


Doom9 / MPEG-4のB- frameを AVI/VFW に入れるハッキング
2010-03-15 (月) 04:52:41更新
カテゴリ[[:]]

はじめに

【原文】http://forum.doom9.org/showthread.php?s=&threadid=80430
【概要】

ここでは主にXviDのBフレームを、AVIに入れるためにWin界で編み出された技法が紹介されています。
手許では、Mac OSX 上でx264.mp4を作成する際、MEncoder,ffmpeg,x264cliでBフレームを使ったものは、いずれもQuickTime Player で再生すると冒頭に白紙フレームが入ります。
これは以下で言及されている"Delay Frame"である可能性があります。
再生に関しては問題ありませんが、以下のような症状が発生します。

  1. 冒頭で(白紙フレーム)を表示させた状態でシークするとムービー終端に飛ぶ。
  2. QuickTime Playerで映像情報が一部表示できない。
  3. QuickTime Player Proで.mp4へ「そのまま」書き出しできない。

MPEG-4のB- frameを AVI/VFW に入れるハッキング(裏技)について

2004/07/28:bond

b-frameがどのように .aviに収められ、vfwでのエンコード/デコード過程で使われるかについて、一部の人たちには説明して来たが、一度簡単な説明を書いて公開しておいた方が良いと思った。

内容は以下の通り。

  • packed bitstream とは何か
  • delay frames とは何か
  • xvidの デコーダ・ラグメッセージの意味
  • 時代遅れの .avi と vfw を使い続ける為にやらなければ行けない事。

第一の問題

"video for windows" (VFW) コデック・インターフェイス (virtualdub(mod)が使う) とそのコンテナ (AVI) は b-frameを扱えない、それどころか、このタイプのフレームの存在すら認識出来ないのだ!
b-frameのようなモダンテクノロジーを扱えないという事は、これらは時代遅れのテクノロジーと言う事ができる。

b-frameを使いたければ可能性は二つある:

1)b-frameを扱えないような 時代遅れの技術を捨てる(そして DirectShow や .MP4 のような新しいものを使う)。
2) 時代遅れの技術を開発と裏技でなんとかする。

原文執筆時点(2004/07/28)のQT6もBフレームをサポートしていません。QT6のMPEG-4サポートはSP(Simple Profile、Bなし)のみであり、Bを使ったASP(Advanced Simple Profile)を再生・作成するには3ivxコンポーネントの追加インストールが必要でした。QT7(2005年前半)では「フレーム並べ替え」の名称でBがサポートされましたが、指定できるのは.mov作成時のみです。

2種類の裏技(ハッキング)

現在、 2) のb-frames をavi と vfwで扱う方法は2種類ある。
a) エンコーダ側で扱う (xvid と divx5がデフォルトで使うpacked bitstream)
b) デコーダ側で扱う (xvidが packed bitstream をオフにした時に使う)

基本

aviとvfwでb-frameを使う裏技を理解するには、まず次の理解が必要だ。

通常、コンテナの中のフレームは次のように配置されている:

I P B B

ディスプレイは次の順番だ:

I B B P

VFW と AVI が使うのは " 1 フレーム イン・1 フレーム アウト " 方式 (それがこのテクノロジーの基礎)、これは(バッファメモリに)フレームが1枚入って来る度に、フレームを1枚出力しなければならない事を意味する(エンコードでもデコードでも)。
この原則はb-frameと互換性がない。b-frame は 前後のi/pフレームとセットで使うべく作られているからだ。しかし vfw/avi は " 2 フレーム イン・1 フレーム アウト" といった事を許さない。 (モダンな directshow や .mp4はできる。)

通常 (モダンなテクノロジーを使った場合)デコーダはデコード中に次のような事を行う:
1) I-フレームを表示
2) 次に B-frameを表示するが、Bフレームは前後の I/P-frameが必要なので、既にデコード済みのIフレームをキープしておき、さらにPも読み込む。これで B もデコードできるわけだ (これは "3 フレーム イン・1 フレーム アウト" の場合)
3) 2番目のBフレームも同様に処理
4) 次に P-フレームの表示


デコードの裏技(ハッキング)

では裏技に行こう:

a)packed bitstream

AVI and VFW は" 1 フレーム イン・1 フレーム アウト " しかできないので、以下の処理をエンコーダで行う方法 ( packed bitstream と呼ばれる):~

最初のBフレームは1フレームとしてPフレームとパックされる。

I P B B

これは次のようになる

I PB B N 

(*訳注:半角スペースに注意*)
(N フレームナンバー維持の為の、符号化されていない代替フレーム)

次に、デコーダは以下の処理をする:
1) I をデコード
2) 最初の B-フレームをデコードするには (前述のように) I と P が要る。最初の B は Pとパックされているので、彼は "公式には" 1フレームとカウントされる ( PB が1フレームしか無いように見える)が、実際にはP+Bの2フレームだ。これで最初のBはデコードできる。
3) デコーダは既に I と P を持っているので2番目の B もデコードできる。
4) 次に、 Pをディスプレイ。

まとめると、avi/vfwは" 1 フレーム イン・1 フレーム アウト " 方式しか使えないので、2フレームをパックして、これは1フレームですよと偽って処理に流し込むというものだ。
開発者の中には(ffmpegのミヒャエル=ニーダーマイヤーのように)2フレームを1フレームにパックするとMPEG-4規格との互換性が無くなるとする人もいます!また、MPEG-4規格を遵守する通常のMPEG-4デコーダはpacked bitstreamなんか知らないのでデコードもできない。

(*訳注:MEncoder -xvidencopts の(no)closed_gop, divx5bvopあたりが相当すると思われる*)

b)Delay Frame

2番目の案はストリームはaviの中に正しく配置(I P B B)しておいて、デコーダ側に裏技を用意するというものだ。

1) デコーダは先ずI-フレームを取得する。しかしディスプレイしない。代わりに例えば、有名なxvidの"decoder lag"メッセージを表示する!
2) 次に、デコーダはPを入力として取得する。そして、最初のIフレームだけを表示--" 1 フレーム イン・1 フレーム アウト " は遵守されるが、1フレームぶんのタイムラグがある。
3) 3番目、デコーダは最初のB-フレームを取得する。既にデコーダの手許には必要なIとPがあるので、このBもデコードできる。
4) 既にデコーダの手許には必要なIとPがあるので、2番目のBもデコードできる。
5) Pをディスプレイ

この裏技は、avi/vfwは" 1 フレーム イン・1 フレーム アウト " 方式しか使えないので、ラグを作って、デコーダがこの方式に従いつつも、取得したフレームを吐き出さないようにするもの。
packed bitstreamとの違いは、ストリームがMPEG-4規格準拠の方式で記述される事。

(*訳注:そういえばXvid+mp3.avi再生時、冒頭の一瞬は、素材と異なる真っ白のフレームだった記憶が…*)

エンコードの裏技(ハッキング)

ここまでが、デコード側で時代遅れのvfw/aviでB-フレームを使うための2個の裏技。
さらに、エンコード側にも裏技がある(もちろん、ここでも" 1 フレーム イン・1 フレーム アウト " 方式に従う必要がある)。
B-フレーム エンコードの為には、エンコーダに2フレームを送り込ま無ければならない:

エンコーダは以下の作業をする:
1) 第一フレームをエンコーダに送り込む -> これはI-フレームとして符合化される。
2) 第二フレームを入力 -> これはB-フレームに符号化するべきだが => その為にはP-フレームも見る必要があるのでできない。
3) 第三フレームの入力 -> B => これも不可能。Pがまだ無い。
4) 第四フレームの入力 -> P-フレームに符号化
5) (IとPが準備完了している)=> 最初のBが符号化
6) (IとPが準備完了している) => 2番目の Bが符号化

さてvfw/aviの" 1 フレーム イン・1 フレーム アウト " 方式で、どうやって2) と 3) を行うか?
1 フレーム イン・1 フレーム アウトなので、いわゆるディレイ・フレーム (1-byte 0x7f frames)を書き出す。
VFWはなにか書き出せば良いのでディレイ・フレームでも良い訳だ!しかし、それは入力の中に存在するものではないし、しかも、MPEG-4規格との互換性は破壊される!

ではどうしよう?
AVI と VFW がトラブルを起こした時、我々がいつもしている事は、 -> 裏技を探す、だ。:
=> virtualdub(mod)にこうした"ディレイ・フレーム"をドロップさせればいい。これで最終的な出力ストリームの中には"ディレイ・フレーム" はなくなる。
しかし、vfwコデックを使う他のツールでは"ディレイ・フレーム"が残ってしまう可能性がある。注意してくれ。

AVI と VFW って、いいもんだろ?

Macにおける補足

Xvid.aviは広く普及した為、上記のハッキングは安定した枯れたテクニック。恐らくlinuxでも同様。すなわち、MEncoder, ffmpeg, x264cliのどこに潜んでいるか解らない。
また、Delay Frameを使ったエンコードでは、終端フレームがエンコーダバッファ内に留まったまま消失する。枚数はBの指定枚数に拠る?


MEncoder -x264encoptsでrawvideo.264を吐く場合、上記Delay Frameが発生する。

1フレーム分(23.976fpsなら約0.0417sec、29.97fpsなら約0.0334sec)映像にDelayがかかる。
これを除去せずに音声とmuxした場合、音声が映像1フレーム分先行し、冒頭に白紙フレームが1枚入る。この白紙フレームがあってもQT7で再生はできるが、MP4規格外。

Mac上で「Delay Frameをドロップ」する手段:

QuickTime Player Proで冒頭フレームを除去(2chより。手許未成功)。
QTcoffeeを使わなくても、一瞬だけ再生させて、情報を表示、←キーで一コマづつ戻す、現在の時間が0.03or0.04になったら「O」キーを押す。この状態で最初の一コマだけが選択されてるから、deleteキーを押して削除。これでMP4でそのまま書き出すが選択出来るよ。
QTCoffee/muxmovieの-startAtで冒頭1フレーム除去(成功)。
除去前の.mp4から音声を抽出
除去後の.mp4から映像を抽出
新規QTムービーに両者を貼付けて「そのまま」書き出しなど。

但し、これらの方法でも冒頭の白紙フレームにPTS(Presentation Time Stamp)が付いてしまっているため、本来の先頭フレームは0.03or0.04sec遅れたままとなる。
最善は、virtualdub(mod)同様にrawvideo.264の段階で「ディレイ・フレーム (1-byte 0x7f frames)」を直接除去する事だが、未解明。



FrontPage
MPlayer
Manuals
Documents
カテゴリ

■GENERAL
MEMO
LINK
雑談所
最近の更新
popular

■Other Tools
ffmpeg
mkvmerge
mp4box
MPEG Streamclip
QTCoffee
x264cli

■About
About Wiki

edit


blog


本日1
昨日0
累積6130