楽しいハック講座(2) Windows Media Player のビデオレンダリング
こんにちは。わび~です。
今日はWindows Media Player (WMP) がビデオをレンダリング(描画)する部分を楽しくハックしていきます。対象はWindows XP SP3 + WMP11 です。今回紹介する内容も、Microsoft のデバッガ (windbg)と公開デバッグシンボルを使えばどなたでも検証できます。
Windows XP でビデオを表示するローレベル API には GDI, DirectDraw, Direct3D がありますが、
WMP はいったいどれを使用して描画しているのでしょうか。
そんなことが分かると何が嬉しいかと言えば、
- WMPのレンダリングの問題(重い、コマ落ちする、横縞が入る、縦横比がおかしい、etc. )は誰のせいなのか。
- WMPで再生中のビデオを PrintScreen キーでコピペできないのはなぜなのか。
といった疑問へのヒントになります。
今回取り上げるのは、MPEGやWMVといったファイル再生のシナリオです。DVD再生だとまた事情が変わるので今回は割愛します。
前提となる知識として、たぶん具体的には以下の API がコールされているだろう、という予想を立てておきます。
- GDI = BitBlt API
- DirectDraw (HW Overlay) = IDirectDrawSurface7::UpdateOverlay API
- Directr3D = IDirect3DDevice9::Present API
この予想が合っているかどうかを前回同様にデバッガ (windbg) で調べていきますが、デバッガで調べるには API 名に対応する実装の関数名(シンボル)が必要です。SDK上の名前とは一般に異なりますので、これを見つけるまでが最初の勝負になります。シンボルを調べる簡単な方法はサンプルコードを書いてデバッガに聞くことです。
たとえば、IDirectDrawSurface7::UpdateOverlay の場合、長年の勘からたぶんシンボルは UpdateOverlay という文字列を含むことが期待されますので(そのままじゃん)、サンプルコードにデバッガをアタッチしてMicrosoft の公開デバッグシンボルをサーチします。今回はサンプルコードとしては既製品ですみませんが、Windows SDK 内の DShowPlayer を使います。
0:017> x ddraw!*UpdateOverlay*
736bd318 DDRAW!DD_Surface_UpdateOverlay = <no type information>
736ed4d9 DDRAW!DD_Surface_UpdateOverlayDisplay = <no type information>
736ed679 DDRAW!DD_Surface_UpdateOverlayZOrder = <no type information>
DDRAW!DD_Surface_UpdateOverlay というのがいかにもなシンボルなのでこれに狙いを定め、ブレークポイントを張って実行すると、quartz (DirectShow のコア) 内の VMR7 のアロケータプレゼンタのいかにもBLTをしていそうなメソッドからコールされていますのでほぼ間違いないでしょう。
0:017> bp DDRAW!DD_Surface_UpdateOverlay
0:017> g
Breakpoint 0 hit
eax=001cbfe8 ebx=04630048 ecx=736ef020 edx=0012f034 esi=77d098fe edi=00000000
eip=736bd318 esp=0012f000 ebp=0012f04c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
DDRAW!DD_Surface_UpdateOverlay:
736bd318 68f4000000 push offset <Unloaded_ud.drv>+0xf3 (000000f4)
0:000> knL
# ChildEBP RetAddr
00 0012effc 7cfcf6ad DDRAW!DD_Surface_UpdateOverlay
01 0012f04c 7cfcf101 quartz!CAllocatorPresenter::UpdateOverlaySurface+0x1a9
02 0012f084 7cfb3549 quartz!CAllocatorPresenter::PresentImageWorker+0x148
03 0012f0a4 7d0220e2 quartz!CAllocatorPresenter::RepaintVideo+0x4a
04 0012f0bc 00432b14 quartz!CVMRFilter::RepaintVideo+0x41
05 0012f120 0043489b DShowPlayer!DShowPlayer::Repaint+0x34
06 0012f1ec 00433f29 DShowPlayer!MainWindow::OnPaint+0x5b
07 0012f254 00432244 DShowPlayer!MainWindow::OnReceiveMessage+0xf9
08 0012f2bc 77cf8734 DShowPlayer!BaseWindow::WindowProc+0x74
同様に、IDirect3D9Device::Present についても、これも既製品ですみませんが、Windows SDK 内の VMR9Player を使い、「メソッド名は Present という文字列を含むに違いない」と半分あてずっぽうでサーチします。
0:014> x d3d9!*Present
4b699230 D3D9!CSwapChain::Present = <no type information>
4b6ed930 D3D9!CSMP_PVFUNCS::Present = <no type information>
4b6ed930 D3D9!ID3DFE_PVFUNCS::Present = <no type information>
4b6ed930 D3D9!X3D_PVFUNCS::Present = <no type information>
4b66110c D3D9!_imp__IsProcessorFeaturePresent = <no type information>
4b6a0ea0 D3D9!CBaseDevice::Present = <no type information>
長年の勘から、D3D9!CSwapChain::Present は IDirect3DSwapChain9::Present の実装で、D3D9!CBaseDevice::Present は IDirect3DDevice9::Present の実装なのだろうなぁと予想できます。実際、ブレークポイントを張ってみると、VMR9 のアロケータプレゼンタオブジェクトのいかにも BLT 処理っぽいメソッドからコールされますので、たぶん合っていると推測できます。
0:014> bp D3D9!CBaseDevice::Present
0:014> g
Breakpoint 0 hit
eax=0018d020 ebx=05890f28 ecx=0018f1d0 edx=05d9fe10 esi=77d09507 edi=05d9fe30
eip=4b6a0ea0 esp=05d9fdec ebp=05d9fe48 iopl=0 nv up ei ng nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000282
D3D9!CBaseDevice::Present:
4b6a0ea0 8bff mov edi,edi
0:002> knL
# ChildEBP RetAddr
00 05d9fde8 7d047b81 D3D9!CBaseDevice::Present
01 05d9fe48 7d048623 QUARTZ!VMR9::CAllocatorPresenter::PaintBorder+0xc0
02 05d9fe7c 7d0487e4 QUARTZ!VMR9::CAllocatorPresenter::PresentImageWorker+0x166
03 05d9fe98 7cfaf058 QUARTZ!VMR9::CAllocatorPresenter::PresentImage+0xc7
04 05d9feb0 7cfaefd5 QUARTZ!CVMRFilter::CIVMRImagePresenter::PresentImage+0x2c
05 05d9fec4 7cfb25c5 QUARTZ!VMR9::CImageSync::DoRenderSample+0x1f
06 05d9fedc 7d04978b QUARTZ!CImageSync::Render+0x2c
07 05d9ff04 7d049828 QUARTZ!VMR9::CImageSync::ReceiveWorker+0xe5
08 05d9ff14 7d041e41 QUARTZ!VMR9::CImageSync::Receive+0x46
09 05d9ff8c 7d0422e9 QUARTZ!VMR9::CVideoMixer::CompositeTheStreamsTogether+0x24f
0a 05d9ffac 7d03f0b1 QUARTZ!VMR9::CVideoMixer::MixerThread+0x17c
0b 05d9ffb4 7c80b713 QUARTZ!VMR9::CVideoMixer::MixerThreadProc+0xd
0c 05d9ffec 00000000 kernel32!BaseThreadStart+0x37
GDI も同様に探すと GDI32!BitBlt であることが分かります。ここまでをまとめると、API とシンボルの対応関係はこうなります。
- GDI = GDI32!BitBlt
- DirectDraw (HW Overlay) = DDRAW!DD_Surface_UpdateOverlay
- Directr3D = D3D9!CBaseDevice::Present
これらにブレークポイントを張って WMP を実行して、ブレークすればそのAPIを使用していると判断できます。実は他にも関係するAPIがいくつか出てきますが、以下同文ですので省略します。
WMPの設定の、[ツール]→[オプション]→[パフォーマンス]→[詳細]→[ビデオアクセラレータ] を見ると、レンダリングに関係しそうな設定項目は以下の通りです。
- ビデオミキシングレンダラを使う = ON/OFF
- オーバーレイを使う = ON/OFF
- 高画質モードを使う = ON/OFF
- DirectX ビデオアクセラレーションを使う= ON/OFF
- 旧ビデオレンダラ
- YUV反転を使う= ON/OFF
- RGB反転を使う= ON/OFF
- プライマリサーフェースを使う= ON/OFF
今日は 本命であるDXVA (DirectX ビデオアクセラレーション)はスルーします。
それぞれの設定を以下、順に試していきます。
(ところで、 flipping を「反転」というのはひどい翻訳ですね。)
(1) 旧 Video Renderer によるレンダリング
(1-1) 旧 Video Renderer + Overlay
- ビデオミキシングレンダラを使う = OFF
- オーバーレイを使う = OFF
- 高画質モードを使う = OFF
- DirectX ビデオアクセラレーションを使う= OFF
- 旧ビデオレンダラ
- YUV反転を使う= ON または RGB反転を使う= ON
- プライマリサーフェースを使う= OFFまたはON
この設定をした WMP にブレークポイントを張った状態でコンテンツを再生します。そうすると、以下のように hit することが分かります。
0:023> g
Breakpoint 0 hit
eax=06678358 ebx=00004400 ecx=736ef100 edx=01ba5940 esi=01ba5518 edi=00000000
eip=736bd318 esp=055ffb34 ebp=055ffb60 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
DDRAW!DD_Surface_UpdateOverlay:
736bd318 68f4000000 push offset <Unloaded_peg2_ff.dll>+0xe3 (000000f4)
0:007> knL
# ChildEBP RetAddr
00 055ffb30 7d01b1f7 DDRAW!DD_Surface_UpdateOverlay
01 055ffb60 7d01b2fe quartz!CDirectDraw::UpdateOverlaySurface+0x8c
02 055ffb80 7d01b49e quartz!CDirectDraw::UpdateRectangles+0xa4
03 055ffbc8 7d01b565 quartz!CDirectDraw::UpdateDisplayRectangles+0x177
04 055ffbf8 7d01af87 quartz!CDirectDraw::UpdateSurface+0xbd
05 055ffc10 7d0163e8 quartz!CDirectDraw::OnPaint+0x39
06 055ffc30 7d01d0d7 quartz!CRenderer::OnPaint+0xc4
07 055ffc84 7d01d8fd quartz!CVideoWindow::OnPaint+0x3a
08 055ffc94 7cfa7810 quartz!CVideoWindow::OnReceiveMessage+0x8e
09 055ffcb8 77cf8734 quartz!WndProc+0x96
0:023> t
eax=06678358 ebx=01ba4bc8 ecx=736ef100 edx=01ba5528 esi=01ba5518 edi=0753d290
eip=736b399c esp=072df090 ebp=072df0a4 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
DDRAW!DD_Surface_Flip:
736b399c 68dc000000 push offset <Unloaded_peg2_ff.dll>+0xcb (000000dc)
0:023> kbn
# ChildEBP RetAddr Args to Child
00 072df08c 7d01a800 06678358 00000000 00000000 DDRAW!DD_Surface_Flip
01 072df0a4 7d01a88c 0753d290 01ba4bc8 01ba5518 quartz!CDirectDraw::DoFlipSurfaces+0x36
02 072df0bc 7d0162a5 0753d290 01ba4bc8 01ba4e08 quartz!CDirectDraw::DrawImage+0x31
03 072df0d0 7d0981c7 0753d290 7c9410e0 01ba4bc8 quartz!CRenderer::DoRenderSample+0x42
04 072df0e4 7d099b02 0753d290 7cf9523c 00000000 quartz!CBaseRenderer::Render+0x2e
05 072df100 7d015fa2 01ba4c44 0753d290 01ba5040 quartz!CBaseRenderer::Receive+0xf1
06 072df11c 7d099d0d 0753d290 0753d290 00000000 quartz!CRenderer::Receive+0xac
07 072df134 129e0a57 01ba5040 0753d290 01aa1610 quartz!CRendererInputPin::Receive+0x19
0:023> g
Breakpoint 11 hit
eax=736ef100 ebx=01ba4f28 ecx=072defe4 edx=01ba5528 esi=000be9e8 edi=01ba5518
eip=736b3517 esp=072defc4 ebp=072df054 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
DDRAW!DD_Surface_Lock:
736b3517 6a2c push 2Ch
0:023> k
ChildEBP RetAddr
072defc0 7d01a4bf DDRAW!DD_Surface_Lock
072df054 7d01aa52 quartz!CDirectDraw::LockSurface+0x69
072df064 7d01817f quartz!CDirectDraw::InitVideoSample+0x10
072df090 12ae9558 quartz!CVideoAllocator::GetBuffer+0x21d
Breakpoint 10 hit
eax=001013c8 ebx=00000001 ecx=736ef100 edx=0792f060 esi=075d51a0 edi=00000000
eip=736b8031 esp=0792efdc ebp=0792f064 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
DDRAW!DD_Surface_GetDC:
736b8031 6a18 push 18h
0:019> k
ChildEBP RetAddr
0792efd8 7d018a76 DDRAW!DD_Surface_GetDC
0792f064 7d019f24 quartz!CDirectDraw::GetRealKeyColour+0x1b
0792f084 7d01a5b7 quartz!CDirectDraw::ShowColourKeyOverlay+0x79
0792f098 7d01a851 quartz!CDirectDraw::ShowOverlaySurface+0xac
0792f0a4 7d01a88c quartz!CDirectDraw::DoFlipSurfaces+0x87
0792f0bc 7d0162a5 quartz!CDirectDraw::DrawImage+0x31
0792f0d0 7d0981c7 quartz!CRenderer::DoRenderSample+0x42
0792f0e4 7d099b02 quartz!CBaseRenderer::Render+0x2e
0792f100 7d015fa2 quartz!CBaseRenderer::Receive+0xf1
0792f11c 7d099d0d quartz!CRenderer::Receive+0xac
0792f134 129e0a57 quartz!CRendererInputPin::Receive+0x19
このように旧Video Renderer が HW Overlay を使用して描画しているようです。描画部分はIDirectDrawSurface7::Lock を使ってバックバッファのサーフェースをシステムメモリに持ってきているのだろうということが分かります。あとカラーキーの取得に IDirectDrawSurface7::GetDC しています。毎フレーム Lock するのは少々重そうですね。
(1-2) 旧 Video Renderer + DirectDraw (Overlayなし)
- ビデオミキシングレンダラを使う = OFF
- オーバーレイを使う = OFF
- 高画質モードを使う = OFF
- DirectX ビデオアクセラレーションを使う= OFF
- 旧ビデオレンダラ
- YUV反転を使う= OFF
- RGB反転を使う= OFF
- プライマリサーフェースを使う= ON または OFF
Breakpoint 2 hit
eax=0670fbb0 ebx=076efe20 ecx=736ef100 edx=076f0b88 esi=076f0770 edi=7c9410e0
eip=736b581b esp=0792f0a8 ebp=0792f0d0 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
DDRAW!DD_Surface_Blt:
736b581b 6818030000 push offset <Unloaded_AN32.dll>+0x2c7 (00000318)
0:019> k
ChildEBP RetAddr
0792f0a4 7d01a8d6 DDRAW!DD_Surface_Blt
0792f0d0 7d0162a5 quartz!CDirectDraw::DrawImage+0x7b
0792f0e4 7d099a99 quartz!CRenderer::DoRenderSample+0x42
0792f100 7d015fa2 quartz!CBaseRenderer::Receive+0x88
0792f11c 7d099d0d quartz!CRenderer::Receive+0xac
0792f134 129e0a57 quartz!CRendererInputPin::Receive+0x19
0:003> g
Breakpoint 11 hit
eax=736ef100 ebx=076f0180 ecx=0792efe4 edx=076f0780 esi=06725f00 edi=076f0770
eip=736b3517 esp=0792efc4 ebp=0792f054 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
DDRAW!DD_Surface_Lock:
736b3517 6a2c push 2Ch
0:019> knL
# ChildEBP RetAddr
00 0792efc0 7d01a4bf DDRAW!DD_Surface_Lock
01 0792f054 7d01aa52 quartz!CDirectDraw::LockSurface+0x69
02 0792f064 7d01817f quartz!CDirectDraw::InitVideoSample+0x10
03 0792f090 12ae9558 quartz!CVideoAllocator::GetBuffer+0x21d
DDRAW!DD_Surface_UpdateOverlay に hit しないので、Overlay は使用していません。
つまり、Overlay なしの純粋な DirectDraw を使用して描画しています。バックバッファを Lock してビデオフレームを書き込んだ後にプライマリサーフェースに BLT しているようです。これも毎フレームLock している上にプライマリサーフェースへの BLT もしなくてはいけないのでさらに重そうです。
同様に VMR の場合も見ていきます。
(2) Video Mixing Renderer 7 (VMR7) によるレンダリング
(2-1) VMR7 の非Overlay モード
- ビデオミキシングレンダラを使う = ON
- オーバーレイを使う = OFF
- 高画質モードを使う = OFF
- DirectX ビデオアクセラレーションを使う= OFF
- 旧ビデオレンダラ
- YUV反転を使う= don't care
- RGB反転を使う= don't care
- プライマリサーフェースを使う= don't care
0:026> g
Breakpoint 2 hit
eax=066cd6d0 ebx=0792ef74 ecx=736ef020 edx=736ef590 esi=01bce4e0 edi=0792ef64
eip=736b581b esp=0792ef18 ebp=0792ef44 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
DDRAW!DD_Surface_Blt:
736b581b 6818030000 push offset <Unloaded_plitter.ax>+0x2b7 (00000318)
0:017> k
ChildEBP RetAddr
0792ef14 7cfcd0db DDRAW!DD_Surface_Blt
0792ef44 7cfcd03c quartz!CAllocatorPresenter::BltImageToPrimary+0xaf
0792ef88 7cfb0439 quartz!CAllocatorPresenter::PresentImageWorker+0x18f
0792efac 129e07aa quartz!CAllocatorPresenter::PresentImage+0x106
WARNING: Stack unwind information not available. Following frames may be wrong.
0792efd8 7cfaf058 wmp!DllGetClassObject+0x6d4
0792eff0 7cfaefd5 quartz!CVMRFilter::CIVMRImagePresenter::PresentImage+0x2c
0792f004 7cfb25c5 quartz!VMR9::CImageSync::DoRenderSample+0x1f
0792f01c 7cfb0680 quartz!CImageSync::Render+0x2c
0792f044 7cfb05f7 quartz!CImageSync::ReceiveWorker+0xe5
0792f054 7cfb00bc quartz!CImageSync::Receive+0x46
0792f134 129e0a57 quartz!CVMRInputPin::Receive+0x3c3
0:017> g
Breakpoint 11 hit
eax=736ef020 ebx=00004800 ecx=0792efc4 edx=066f5448 esi=01b8a008 edi=0792f040
eip=736b3517 esp=0792efa0 ebp=0792f040 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
DDRAW!DD_Surface_Lock:
736b3517 6a2c push 2Ch
0:017> knL
# ChildEBP RetAddr
00 0792ef9c 7cfaed0e DDRAW!DD_Surface_Lock
01 0792f040 7cfb023b quartz!CVMRMediaSample::LockSurface+0x63
02 0792f06c 7cfb0148 quartz!CVMRInputPin::OnGetBuffer+0x1c3
03 0792f090 12ae9558 quartz!CVMRPinAllocator::GetBuffer+0x3c
VMR7 が Overlay を使わずに DirectDraw で描画しています。バックバッファを Lock して書き込んだ後にプライマリサーフェースに BLT しているようです。毎フレーム Lock するのでとても重そうです。
(2-2) VMR7 ミキシングモード
- ビデオミキシングレンダラを使う = ON
- オーバーレイを使う = OFF
- 高画質モードを使う = ON
- DirectX ビデオアクセラレーションを使う= OFF
- 旧ビデオレンダラ
- YUV反転を使う= don't care
- RGB反転を使う= don't care
- プライマリサーフェースを使う= don't care
まず、ファイルの読み込み時に以下のメソッドがコールされています。
0:001> g
Breakpoint 6 hit
eax=01bad01c ebx=00000000 ecx=7cf97e50 edx=01bad078 esi=01bacfb4 edi=00000000
eip=7d022347 esp=05d2efb4 ebp=05d2efdc iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
quartz!CVMRFilter::SetNumberOfStreams:
7d022347 8bff mov edi,edi
0:001> kbn
# ChildEBP RetAddr Args to Child
00 05d2efb0 12e6565d 01bad01c 00000003 01bacfb4 quartz!CVMRFilter::SetNumberOfStreams
01 05d2efdc 12b737e6 01bacfb4 01becd70 01bacfb4 wmp!Ordinal3002+0x288b24
02 05d2f034 129efc26 01bacfb4 00000001 5134e700 wmp!Ordinal3003+0xc1188
03 05d2f060 129ec089 07b26840 05d2f08c 7cfbe91f wmp!DllCanUnloadNow+0x5822
04 05d2f06c 7cfbe91f 01a5c4c4 01bacfb4 00000000 wmp!DllCanUnloadNow+0x1c85
WMP10 のヘルプには説明があったのになぜか WMP11 のヘルプには記載されていないのですが、これは主に GPU でデインターレースを行うために VMR をミキシングモードにする設定です。
実際、このように IVMRFilterConfig::SetNumberOfStreams で dwMaxStreams = 3 に設定して、VMR をミキシングモードに設定しています。
次に、再生中には以下のメソッドが繰り返しコールされます。
0:016> g
Breakpoint 9 hit
eax=736ef020 ebx=00004800 ecx=0791f0ac edx=05f139b8 esi=07bfe168 edi=0791f128
eip=736b3517 esp=0791f088 ebp=0791f128 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ddraw!DD_Surface_Lock:
736b3517 6a2c push 2Ch
0:023> k
ChildEBP RetAddr
0791f084 7cfaed0e ddraw!DD_Surface_Lock
0791f128 7cfb023b quartz!CVMRMediaSample::LockSurface+0x63
0791f154 7cfb0148 quartz!CVMRInputPin::OnGetBuffer+0x1c3
0791f178 07da9683 quartz!CVMRPinAllocator::GetBuffer+0x3c
0:023> g
Breakpoint 10 hit
eax=05f15d28 ebx=07b29310 ecx=736ef020 edx=06e9f658 esi=06e9f928 edi=00000000
eip=736b581b esp=06e9f4c4 ebp=06e9f6dc iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ddraw!DD_Surface_Blt:
736b581b 6818030000 push offset <Unloaded_peg2_ff.dll>+0x2f7 (00000318)
0:016> k
ChildEBP RetAddr
06e9f4c0 7d027fe6 ddraw!DD_Surface_Blt
06e9f6dc 7d02814f quartz!CVideoMixer::CIIVMRImageCompositor::OptimizeBackground+0x33e
06e9f8e8 7d02649b quartz!CVideoMixer::CIIVMRImageCompositor::CompositeImage+0xa6
06e9fd14 7d0279e7 quartz!CVideoMixer::CompositeStreams+0x1c5
06e9ffac 7d0259c1 quartz!CVideoMixer::MixerThread+0x516
06e9ffb4 7c80b713 quartz!CVideoMixer::MixerThreadProc+0xd
06e9ffec 00000000 kernel32!BaseThreadStart+0x37
0:016> g
Breakpoint 10 hit
eax=001642a8 ebx=06e9fc50 ecx=736ef020 edx=736ef590 esi=079f1870 edi=06e9fc40
eip=736b581b esp=06e9fbf4 ebp=06e9fc20 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
ddraw!DD_Surface_Blt:
736b581b 6818030000 push offset <Unloaded_peg2_ff.dll>+0x2f7 (00000318)
0:016> k
ChildEBP RetAddr
06e9fbf0 7cfcd0db ddraw!DD_Surface_Blt
06e9fc20 7cfcd03c quartz!CAllocatorPresenter::BltImageToPrimary+0xaf
06e9fc64 7cfb0439 quartz!CAllocatorPresenter::PresentImageWorker+0x18f
06e9fc88 129e07aa quartz!CAllocatorPresenter::PresentImage+0x106
06e9fcb4 7cfaf058 wmp!DllGetClassObject+0x6d4
06e9fccc 7cfaefd5 quartz!CVMRFilter::CIVMRImagePresenter::PresentImage+0x2c
06e9fce0 7cfb25c5 quartz!VMR9::CImageSync::DoRenderSample+0x1f
06e9fcf8 7cfb0680 quartz!CImageSync::Render+0x2c
06e9fd20 7cfb05f7 quartz!CImageSync::ReceiveWorker+0xe5
06e9fd30 7d027b1f quartz!CImageSync::Receive+0x46
06e9ffac 7d0259c1 quartz!CVideoMixer::MixerThread+0x64e
06e9ffb4 7c80b713 quartz!CVideoMixer::MixerThreadProc+0xd
06e9ffec 00000000 kernel32!BaseThreadStart+0x37
これは、VMR7 が DirectDraw を Overlay なしで使用しているようです。コールスタックの内容から想像するに、ミキシングするストリーム分のバックバッファを毎フレーム Lock して書き込み、その後ミキサースレッドが全ストリーム分のバックバッファを別のバックバッファにBLTして1枚のフレームを作ります。それをさらにプライマリサーフェースにBLTして画面に描画しているのだろうと思われます。
毎フレーム Lock しているのでとても重そうです。ただしVMRですとデインターレースを GPU で実行できますので、その分 CPU 負荷を下げることができます。そのためにはデコーダがデインターレースを VMR にまかせる実装になっている必要がありますが、手元の ffdshow ではなんだかうまくいかないようです。
(2-3) VMR7 の Overlay モード
- ビデオミキシングレンダラを使う = ON
- オーバーレイを使う = ON
- 高画質モードを使う = OFF
- DirectX ビデオアクセラレーションを使う= OFF
- 旧ビデオレンダラ
- YUV反転を使う= don't care
- RGB反転を使う= don't care
- プライマリサーフェースを使う= don't care
最後に一番普通の設定と思われる、VMR7 が Overlay を使用する例です。
0:001> g
Breakpoint 8 hit
eax=0014c538 ebx=07a629c8 ecx=736ef020 edx=0006e510 esi=77d098fe edi=07a629c8
eip=736bd318 esp=0006e4dc ebp=0006e528 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ddraw!DD_Surface_UpdateOverlay:
736bd318 68f4000000 push offset <Unloaded_player.dll>+0xe3 (000000f4)
0:000> knL
# ChildEBP RetAddr
00 0006e4d8 7cfcf6ad ddraw!DD_Surface_UpdateOverlay
01 0006e528 7cfb19b2 quartz!CAllocatorPresenter::UpdateOverlaySurface+0x1a9
02 0006e53c 129e31ad quartz!CAllocatorPresenter::SetVideoPosition+0x108
0:025> g
Breakpoint 9 hit
eax=736ef020 ebx=00004800 ecx=0734f0ac edx=05f5b610 esi=01a7e9b8 edi=0734f128
eip=736b3517 esp=0734f088 ebp=0734f128 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ddraw!DD_Surface_Lock:
736b3517 6a2c push 2Ch
0:022> knL
# ChildEBP RetAddr
00 0734f084 7cfaed0e ddraw!DD_Surface_Lock
01 0734f128 7cfb023b quartz!CVMRMediaSample::LockSurface+0x63
02 0734f154 7cfb0148 quartz!CVMRInputPin::OnGetBuffer+0x1c3
03 0734f178 07da9683 quartz!CVMRPinAllocator::GetBuffer+0x3c
このように VMR7 のアロケータプレゼンタから Overlay を使用してレンダリングしています。
Overlay サーフェースには毎フレーム Lock して書き込んでいるようで、この部分は重そうです。
しかし Overlay を使用しない場合と比較するとプライマリサーフェースへのBLTが不要なので、その分は軽いと言えます。
(3) まとめ
随分長くなりましたが、まとめるとこうなります。
- ディスプレイドライバのプロパティでアクセラレータが OFF の場合、
- 旧Video Renderer + GDI で描画されます。
- デインターレースはできません。
- PrintScreen キーでビデオをコピペ可能です
- ディスプレイドライバのプロパティ = アクセラレータを ON の場合、
- ビデオミキシングレンダラを使う = OFF の場合、
- YUV反転を使う= OFF かつ RGB反転を使う= OFF の場合、
- 旧Video Renderer + DirectDraw (Overlay なし)で描画されます。
- デインターレースはできません。
- PrintScreen キーでビデオをコピペ可能です。
- YUV反転を使う= ON または RGB反転を使う= ON の場合、
- 旧Video Renderer + DirectDraw (Overlay あり)で描画されます。
- デインターレースはできません。
- PrintScreen キーでビデオをコピペ可能です。
- YUV反転を使う= OFF かつ RGB反転を使う= OFF の場合、
- ビデオミキシングレンダラを使う = ON の場合、
- 高画質モードを使う = ON (オーバーレイを使う = OFF) の場合、
- VMR7 + DirectDraw (Overlay なし)で描画されます。
- GPU でデインターレース可能です。
- PrintScreen キーでビデオをコピペ可能です。
- オーバーレイを使う = ON (高画質モードを使う = OFF)の場合、
- VMR7 + DirectDraw (Overlay あり)で描画されます。
- GPU でデインターレース可能です。
- PrintScreen キーでビデオをコピペできません。
- 高画質モードを使う = ON (オーバーレイを使う = OFF) の場合、
- ビデオミキシングレンダラを使う = OFF の場合、
録画したTV番組や DVD など、インターレースのコンテンツを再生するケースが最近は多いと思いますが、その場合、インターレースであることはもとより、ピクセルアスペクト比が 1:1 でない(符号化されているピクセルが正方形でない)ことが殆どです。これらは GDI では処理能力が低すぎて太刀打ちできませんので、正しく再生するには GPU の支援が必要になります。旧 Video Renderer はデインターレースに対応していませんので、VMR の利用が性能・品質ともに望ましいです。また後日、さらに望ましい DXVA や VMR の後継の EVR について触れてみたいと思います。
最後に、いわゆる大人の事情から、コンテンツとビデオデバイスによってはWMP にデバッガをアタッチすること自体ができないケースがいくつかあります。そのような場合はコンテンツのコーデックを変えてみてください。一番の安全策は非圧縮 AVI です(笑
Comments
- Anonymous
February 27, 2009
PingBack from http://www.clickandsolve.com/?p=16121