楽しいハック講座(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 がコールされているだろう、という予想を立てておきます。

 

この予想が合っているかどうかを前回同様にデバッガ (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 キーでビデオをコピペ可能です。
    • ビデオミキシングレンダラを使う = ON の場合、
      • 高画質モードを使う = ON (オーバーレイを使う = OFF) の場合、
        • VMR7 + DirectDraw (Overlay なし)で描画されます。
        • GPU でデインターレース可能です。
        • PrintScreen キーでビデオをコピペ可能です。
      • オーバーレイを使う = ON (高画質モードを使う = OFF)の場合、
        • VMR7 + DirectDraw (Overlay あり)で描画されます。
        • GPU でデインターレース可能です。
        • PrintScreen キーでビデオをコピペできません。

 

録画したTV番組や DVD など、インターレースのコンテンツを再生するケースが最近は多いと思いますが、その場合、インターレースであることはもとより、ピクセルアスペクト比が 1:1 でない(符号化されているピクセルが正方形でない)ことが殆どです。これらは GDI では処理能力が低すぎて太刀打ちできませんので、正しく再生するには GPU の支援が必要になります。旧 Video Renderer はデインターレースに対応していませんので、VMR の利用が性能・品質ともに望ましいです。また後日、さらに望ましい DXVA や VMR の後継の EVR について触れてみたいと思います。

 

最後に、いわゆる大人の事情から、コンテンツとビデオデバイスによってはWMP にデバッガをアタッチすること自体ができないケースがいくつかあります。そのような場合はコンテンツのコーデックを変えてみてください。一番の安全策は非圧縮 AVI です(笑