楽しいハック講座(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 キーでコピペできないのはなぜなのか。

といった疑問へのヒントになります。


 


今回取り上げるのは、MPEGWMVといったファイル再生のシナリオです。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 です(笑


 


Skip to main content