text.cpp
Upload User: aya088
Upload Date: 2021-10-23
Package Size: 42k
Code Size: 22k
Category:

DirextX

Development Platform:

Visual C++

  1. //------------------------------------------------------------------------------
  2. // File: Text.cpp
  3. //
  4. // Desc: DirectShow sample code - a simple text-over-video app
  5. //       Using the DirectX 9 Video Mixing Renderer, a generated bitmap
  6. //       containing app-specified text is blended with a running video.
  7. //
  8. // Copyright (c) Microsoft Corporation.  All rights reserved.
  9. //------------------------------------------------------------------------------
  10. #include <dshow.h>
  11. #include <commctrl.h>
  12. #include <commdlg.h>
  13. #include <stdio.h>
  14. #include <tchar.h>
  15. #include <atlbase.h>
  16. #include "text.h"
  17. #include "bitmap.h"
  18. #include "vmrutil.h"
  19. // An application can advertise the existence of its filter graph
  20. // by registering the graph with a global Running Object Table (ROT).
  21. // The GraphEdit application can detect and remotely view the running
  22. // filter graph, allowing you to 'spy' on the graph with GraphEdit.
  23. //
  24. // To enable registration in this sample, define REGISTER_FILTERGRAPH.
  25. //
  26. #define REGISTER_FILTERGRAPH
  27. //
  28. // Global data
  29. //
  30. HWND      ghApp=0;
  31. HMENU     ghMenu=0;
  32. HINSTANCE ghInst=0;
  33. TCHAR     g_szFileName[MAX_PATH]={0};
  34. DWORD     g_dwGraphRegister=0;
  35. RECT      g_rcDest={0};
  36. // DirectShow interfaces
  37. IGraphBuilder *pGB = NULL;
  38. IMediaControl *pMC = NULL;
  39. IMediaEventEx *pME = NULL;
  40. IMediaSeeking *pMS = NULL;
  41. IVMRWindowlessControl9 *pWC = NULL;
  42. HRESULT PlayMovieInWindow(LPTSTR szFile)
  43. {
  44.     USES_CONVERSION;
  45.     WCHAR wFile[MAX_PATH];
  46.     HRESULT hr;
  47.     // Check input string
  48.     if (szFile == NULL)
  49.         return E_POINTER;
  50.     // Clear open dialog remnants before calling RenderFile()
  51.     UpdateWindow(ghApp);
  52.     // Convert filename to wide character string
  53.     wcsncpy(wFile, T2W(szFile), NUMELMS(wFile)-1);
  54.     wFile[MAX_PATH-1] = 0;
  55.     // Get the interface for DirectShow's GraphBuilder
  56.     JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, 
  57.                          IID_IGraphBuilder, (void **)&pGB));
  58.     if(SUCCEEDED(hr))
  59.     {
  60.         CComPtr <IBaseFilter> pVmr;
  61.         // Create the Video Mixing Renderer and add it to the graph
  62.         JIF(InitializeWindowlessVMR(&pVmr));
  63.         // Render the file programmatically to use the VMR9 as renderer.
  64.         // We pass a pointer to the VMR9 so that it will be used as the 
  65.         // video renderer.  Pass TRUE to create an audio renderer also.
  66.         if (FAILED(hr = RenderFileToVMR9(pGB, wFile, pVmr, TRUE)))
  67.             return hr;
  68.         // QueryInterface for DirectShow interfaces
  69.         JIF(pGB->QueryInterface(IID_IMediaControl, (void **)&pMC));
  70.         JIF(pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME));
  71.         JIF(pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS));
  72.         // Is this an audio-only file (no video component)?
  73.         if (CheckVideoVisibility())
  74.         {
  75.             JIF(InitVideoWindow(1, 1));
  76.         }
  77.         else
  78.         {
  79.             // This sample requires a video clip to be loaded
  80.             Msg(TEXT("This sample requires media with a video component.  ")
  81.                 TEXT("Please select another file."));
  82.             return E_FAIL;
  83.         }
  84.         // Have the graph signal event via window callbacks for performance
  85.         JIF(pME->SetNotifyWindow((OAHWND)ghApp, WM_GRAPHNOTIFY, 0));
  86.         // Select a text font if not already set
  87.         if (!g_hFont)
  88.             g_hFont = SetTextFont(FALSE);  // Don't display the Font Select dialog
  89.         // Add the dynamic text bitmap to the VMR's input
  90.         // If the initial blend fails, post a close message to exit the app
  91.         hr = BlendText(ghApp, g_szAppText);
  92.         if (FAILED(hr))
  93.             PostMessage(ghApp, WM_CLOSE, 0, 0);
  94.         // Complete the window setup
  95.         ShowWindow(ghApp, SW_SHOWNORMAL);
  96.         UpdateWindow(ghApp);
  97.         SetForegroundWindow(ghApp);
  98.         SetFocus(ghApp);
  99. #ifdef REGISTER_FILTERGRAPH
  100.         if (FAILED(AddGraphToRot(pGB, &g_dwGraphRegister)))
  101.         {
  102.             Msg(TEXT("Failed to register filter graph with ROT!"));
  103.             g_dwGraphRegister = 0;
  104.         }
  105. #endif
  106.         // Run the graph to play the media file
  107.         JIF(pMC->Run());
  108.         // Start the text update timer
  109.         StartTimer();
  110.     }
  111.     return hr;
  112. }
  113. HRESULT InitVideoWindow(int nMultiplier, int nDivider)
  114. {
  115.     LONG lHeight, lWidth;
  116.     HRESULT hr = S_OK;
  117.     if (!pWC)
  118.         return S_OK;
  119.     // Read the default video size
  120.     hr = pWC->GetNativeVideoSize(&lWidth, &lHeight, NULL, NULL);
  121.     if (hr == E_NOINTERFACE)
  122.         return S_OK;
  123.     // Account for requests of normal, half, or double size
  124.     lWidth  = lWidth  * nMultiplier / nDivider;
  125.     lHeight = lHeight * nMultiplier / nDivider;
  126.     int nTitleHeight  = GetSystemMetrics(SM_CYCAPTION);
  127.     int nBorderWidth  = GetSystemMetrics(SM_CXBORDER);
  128.     int nBorderHeight = GetSystemMetrics(SM_CYBORDER);
  129.     // Account for size of title bar and borders for exact match
  130.     // of window client area to default video size
  131.     SetWindowPos(ghApp, NULL, 0, 0, lWidth + 2*nBorderWidth,
  132.                  lHeight + nTitleHeight + 2*nBorderHeight,
  133.                  SWP_NOMOVE | SWP_NOOWNERZORDER);
  134.     GetClientRect(ghApp, &g_rcDest);
  135.     hr = pWC->SetVideoPosition(NULL, &g_rcDest);
  136.     if (FAILED(hr))
  137.         Msg(TEXT("SetVideoPosition FAILED!  hr=0x%xrn"), hr);
  138.     return hr;
  139. }
  140. HRESULT InitPlayerWindow(void)
  141. {
  142.     // Reset to a default size for audio and after closing a clip
  143.     SetWindowPos(ghApp, NULL, 0, 0,
  144.                  DEFAULT_PLAYER_WIDTH, DEFAULT_PLAYER_HEIGHT,
  145.                  SWP_NOMOVE | SWP_NOOWNERZORDER);
  146.     return S_OK;
  147. }
  148. void MoveVideoWindow(void)
  149. {
  150.     HRESULT hr;
  151.     // Track the movement of the container window and resize as needed
  152.     if(pWC)
  153.     {
  154.         GetClientRect(ghApp, &g_rcDest);
  155.         hr = pWC->SetVideoPosition(NULL, &g_rcDest);
  156.         if (FAILED(hr))
  157.             Msg(TEXT("SetVideoPosition FAILED!  hr=0x%xrn"), hr);
  158. }
  159. }
  160. BOOL CheckVideoVisibility(void)
  161. {
  162.     HRESULT hr;
  163.     LONG lWidth=0, lHeight=0;
  164.     //
  165.     // Because this sample explicitly loads the VMR9 into the filter graph
  166.     // before rendering a file, the IVMRWindowlessControl interface will exist
  167.     // for all properly rendered files.  As a result, we can't depend on the
  168.     // existence of the pWC interface to determine whether the media file has
  169.     // a video component.  Instead, check the width and height values.
  170.     //
  171.     if (!pWC)
  172.     {
  173.         // Audio-only files have no video interfaces.  This might also
  174.         // be a file whose video component uses an unknown video codec.
  175.         return FALSE;
  176.     }
  177.     hr = pWC->GetNativeVideoSize(&lWidth, &lHeight, 0, 0);
  178.     if (FAILED(hr))
  179.     {
  180.         // If this video is encoded with an unsupported codec,
  181.         // we won't see any video, although the audio will work if it is
  182.         // of a supported format.
  183.         return FALSE;
  184.     }
  185.     // If this is an audio-only clip, width and height will be 0.
  186.     if ((lWidth == 0) && (lHeight == 0))
  187.         return FALSE;
  188.     // Assume that this media file contains a video component
  189.     return TRUE;
  190. }
  191. void OpenClip()
  192. {
  193.     HRESULT hr;
  194.     // If no filename specified by command line, show file open dialog
  195.     if(g_szFileName[0] == L'')
  196.     {
  197.         TCHAR szFilename[MAX_PATH];
  198.         InitPlayerWindow();
  199.         SetForegroundWindow(ghApp);
  200.         if (! GetClipFileName(szFilename))
  201.         {
  202.             DWORD dwDlgErr = CommDlgExtendedError();
  203.             // Don't show output if user cancelled the selection (no dlg error)
  204.             if (dwDlgErr)
  205.             {
  206.                 Msg(TEXT("GetClipFileName Failed! Error=0x%xrn"), GetLastError());
  207.             }
  208.             return;
  209.         }
  210.         // This sample does not support playback of ASX playlists.
  211.         // Since this could be confusing to a user, display a warning
  212.         // message if an ASX file was opened.
  213.         if (_tcsstr((_tcslwr(szFilename)), TEXT(".asx")))
  214.         {
  215.             Msg(TEXT("ASX Playlists are not supported by this application.nn")
  216.                 TEXT("Please select a valid media file."));
  217.             return;
  218.         }
  219.         lstrcpyn(g_szFileName, szFilename, NUMELMS(g_szFileName));
  220.     }
  221.     // Start playing the media file
  222.     hr = PlayMovieInWindow(g_szFileName);
  223.     // If we couldn't play the clip, clean up
  224.     if (FAILED(hr))
  225.         CloseClip();
  226. }
  227. BOOL GetClipFileName(LPTSTR szName)
  228. {
  229.     static OPENFILENAME ofn={0};
  230.     static BOOL bSetInitialDir = FALSE;
  231.     // Reset filename
  232.     *szName = 0;
  233.     // Fill in standard structure fields
  234.     ofn.lStructSize       = sizeof(OPENFILENAME);
  235.     ofn.hwndOwner         = ghApp;
  236.     ofn.lpstrFilter       = NULL;
  237.     ofn.lpstrFilter       = FILE_FILTER_TEXT;
  238.     ofn.lpstrCustomFilter = NULL;
  239.     ofn.nFilterIndex      = 1;
  240.     ofn.lpstrFile         = szName;
  241.     ofn.nMaxFile          = MAX_PATH;
  242.     ofn.lpstrTitle        = TEXT("Open Image File...");
  243.     ofn.lpstrFileTitle    = NULL;
  244.     ofn.lpstrDefExt       = TEXT("*");
  245.     ofn.Flags             = OFN_FILEMUSTEXIST | OFN_READONLY | OFN_PATHMUSTEXIST;
  246.     // Remember the path of the first selected file
  247.     if (bSetInitialDir == FALSE)
  248.     {
  249.         ofn.lpstrInitialDir = DEFAULT_MEDIA_PATH;
  250.         bSetInitialDir = TRUE;
  251.     }
  252.     else
  253.         ofn.lpstrInitialDir = NULL;
  254.     // Create the standard file open dialog and return its result
  255.     return GetOpenFileName((LPOPENFILENAME)&ofn);
  256. }
  257. void CloseClip()
  258. {
  259.     HRESULT hr;
  260.     // Stop the text update timer
  261.     StopTimer();
  262.     // Stop media playback
  263.     if(pMC)
  264.         hr = pMC->Stop();
  265.     // Free DirectShow interfaces
  266.     CloseInterfaces();
  267.     // Clear file name to allow selection of new file with open dialog
  268.     g_szFileName[0] = L'';
  269.     // Reset the player window
  270.     RECT rect;
  271.     GetClientRect(ghApp, &rect);
  272.     InvalidateRect(ghApp, &rect, TRUE);
  273.     InitPlayerWindow();
  274. }
  275. void CloseInterfaces(void)
  276. {
  277. #ifdef REGISTER_FILTERGRAPH
  278.     if (g_dwGraphRegister)
  279.     {
  280.         RemoveGraphFromRot(g_dwGraphRegister);
  281.         g_dwGraphRegister = 0;
  282.     }
  283. #endif
  284.     // Release and zero DirectShow interfaces
  285.     SAFE_RELEASE(pME);
  286.     SAFE_RELEASE(pMS);
  287.     SAFE_RELEASE(pMC);
  288.     SAFE_RELEASE(pWC);
  289.     SAFE_RELEASE(pBMP);
  290.     SAFE_RELEASE(pGB);
  291. }
  292. HRESULT HandleGraphEvent(void)
  293. {
  294.     LONG evCode, evParam1, evParam2;
  295.     HRESULT hr=S_OK;
  296.     // Make sure that we don't access the media event interface
  297.     // after it has already been released.
  298.     if (!pME)
  299.         return S_OK;
  300.     // Process all queued events
  301.     while(SUCCEEDED(pME->GetEvent(&evCode, (LONG_PTR *) &evParam1,
  302.                    (LONG_PTR *) &evParam2, 0)))
  303.     {
  304.         // Free memory associated with callback, since we're not using it
  305.         hr = pME->FreeEventParams(evCode, evParam1, evParam2);
  306.         // If this is the end of the clip, reset to beginning
  307.         if(EC_COMPLETE == evCode)
  308.         {
  309.             LONGLONG pos=0;
  310.             // Reset to first frame of movie
  311.             hr = pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
  312.                                    NULL, AM_SEEKING_NoPositioning);
  313.             if (FAILED(hr))
  314.             {
  315.                 // If seeking failed, just stop and restart playback
  316.                 StopTimer();
  317.                 hr = pMC->Stop();
  318.                 // Wait for the state to propagate to all filters
  319.                 OAFilterState fs;
  320.                 hr = pMC->GetState(500, &fs);
  321.                 hr = pMC->Run();
  322.                 StartTimer();
  323.             }
  324.         }
  325.     }
  326.     return hr;
  327. }
  328. #ifdef REGISTER_FILTERGRAPH
  329. HRESULT AddGraphToRot(IUnknown *pUnkGraph, DWORD *pdwRegister) 
  330. {
  331.     IMoniker * pMoniker;
  332.     IRunningObjectTable *pROT;
  333.     if (FAILED(GetRunningObjectTable(0, &pROT))) 
  334.     {
  335.         return E_FAIL;
  336.     }
  337.     WCHAR wsz[128];
  338.     wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR)pUnkGraph, 
  339.               GetCurrentProcessId());
  340.     HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker);
  341.     if (SUCCEEDED(hr)) 
  342.     {
  343.         // Use the ROTFLAGS_REGISTRATIONKEEPSALIVE to ensure a strong reference
  344.         // to the object.  Using this flag will cause the object to remain
  345.         // registered until it is explicitly revoked with the Revoke() method.
  346.         //
  347.         // Not using this flag means that if GraphEdit remotely connects
  348.         // to this graph and then GraphEdit exits, this object registration 
  349.         // will be deleted, causing future attempts by GraphEdit to fail until
  350.         // this application is restarted or until the graph is registered again.
  351.         hr = pROT->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, pUnkGraph, 
  352.                             pMoniker, pdwRegister);
  353.         pMoniker->Release();
  354.     }
  355.     pROT->Release();
  356.     return hr;
  357. }
  358. void RemoveGraphFromRot(DWORD pdwRegister)
  359. {
  360.     IRunningObjectTable *pROT;
  361.     if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) 
  362.     {
  363.         pROT->Revoke(pdwRegister);
  364.         pROT->Release();
  365.     }
  366. }
  367. #endif
  368. void Msg(TCHAR *szFormat, ...)
  369. {
  370.     TCHAR szBuffer[1024];  // Large buffer for long filenames or URLs
  371.     const size_t NUMCHARS = sizeof(szBuffer) / sizeof(szBuffer[0]);
  372.     const int LASTCHAR = NUMCHARS - 1;
  373.     // Format the input string
  374.     va_list pArgs;
  375.     va_start(pArgs, szFormat);
  376.     // Use a bounded buffer size to prevent buffer overruns.  Limit count to
  377.     // character size minus one to allow for a NULL terminating character.
  378.     _vsntprintf(szBuffer, NUMCHARS - 1, szFormat, pArgs);
  379.     va_end(pArgs);
  380.     // Ensure that the formatted string is NULL-terminated
  381.     szBuffer[LASTCHAR] = TEXT('');
  382.     // Display a message box with the formatted string
  383.     MessageBox(NULL, szBuffer, TEXT("VMR Text Sample"), MB_OK);
  384. }
  385. LRESULT CALLBACK AboutDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  386. {
  387.     switch (message)
  388.     {
  389.         case WM_INITDIALOG:
  390.             return TRUE;
  391.         case WM_COMMAND:
  392.             if (wParam == IDOK)
  393.             {
  394.                 EndDialog(hWnd, TRUE);
  395.                 return TRUE;
  396.             }
  397.             break;
  398.     }
  399.     return FALSE;
  400. }
  401. LRESULT CALLBACK WndMainProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  402. {
  403.     switch(message)
  404.     {
  405.         case WM_PAINT:
  406.             OnPaint(hWnd);
  407.             break;
  408.         case WM_DISPLAYCHANGE:
  409.             if (pWC)
  410.                 pWC->DisplayModeChanged();
  411.             break;
  412.         
  413.         // Resize the video when the window changes
  414.         case WM_MOVE:
  415.         case WM_SIZE:
  416.             if (hWnd == ghApp)
  417.                 MoveVideoWindow();
  418.             break;
  419.         // Enforce a minimum size
  420.         case WM_GETMINMAXINFO:
  421.             {
  422.                 LPMINMAXINFO lpmm = (LPMINMAXINFO) lParam;
  423.                 if (lpmm)
  424.                 {
  425.                     lpmm->ptMinTrackSize.x = MINIMUM_VIDEO_WIDTH;
  426.                     lpmm->ptMinTrackSize.y = MINIMUM_VIDEO_HEIGHT;
  427.                 }
  428.             }
  429.             break;
  430.         case WM_KEYDOWN:
  431.             switch(toupper((int) wParam))
  432.             {
  433.                 case VK_ESCAPE:
  434.                 case VK_F12:
  435.                     CloseClip();
  436.                     break;
  437.                 // When spacebar is presssed, advance to the next text string
  438.                 case VK_SPACE:
  439.                     StopTimer();
  440.                     UpdateText();
  441.                     StartTimer();
  442.                     break;
  443.             }
  444.             break;
  445.         case WM_COMMAND:
  446.             switch(wParam)
  447.             { // Menus
  448.                 case ID_FILE_OPENCLIP:
  449.                     // If we have ANY file open, close it and shut down DirectShow
  450.                     CloseClip();                   
  451.                     OpenClip();   // Open the new clip
  452.                     break;
  453.                 case ID_FILE_INITCLIP:
  454.                     OpenClip();
  455.                     break;
  456.                 case ID_FILE_EXIT:
  457.                     CloseClip();
  458.                     PostQuitMessage(0);
  459.                     break;
  460.                 case ID_FILE_CLOSE:
  461.                     CloseClip();
  462.                     break;
  463.                 case ID_SET_FONT:
  464.                     g_hFont = UserSelectFont();   // Change the current font
  465.                     break;
  466.                 case ID_HELP_ABOUT:
  467.                     DialogBox(ghInst, MAKEINTRESOURCE(IDD_HELP_ABOUT),
  468.                               ghApp,  (DLGPROC) AboutDlgProc);
  469.                     break;
  470.             } // Menus
  471.             break;
  472.         case WM_GRAPHNOTIFY:
  473.             HandleGraphEvent();
  474.             break;
  475.         case WM_CLOSE:
  476.             SendMessage(ghApp, WM_COMMAND, ID_FILE_EXIT, 0);
  477.             break;
  478.         case WM_DESTROY:
  479.             PostQuitMessage(0);
  480.             break;
  481.         default:
  482.             return DefWindowProc(hWnd, message, wParam, lParam);
  483.     } // Window msgs handling
  484.     return DefWindowProc(hWnd, message, wParam, lParam);
  485. }
  486. int PASCAL WinMain(HINSTANCE hInstC, HINSTANCE hInstP, LPSTR lpCmdLine, int nCmdShow)
  487. {
  488.     MSG msg={0};
  489.     WNDCLASS wc;
  490.     USES_CONVERSION;
  491.     // Initialize COM
  492.     if(FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)))
  493.     {
  494.         Msg(TEXT("CoInitialize Failed!rn"));
  495.         return FALSE;
  496.     }
  497.     // Verify that the VMR is present on this system
  498.     if(!VerifyVMR9())
  499.         return FALSE;
  500.     // Was a filename specified on the command line?
  501.     if(lpCmdLine[0] != '')
  502.     {
  503.         USES_CONVERSION;
  504.         _tcsncpy(g_szFileName, A2T(lpCmdLine), NUMELMS(g_szFileName));        
  505.     }
  506.     // Register the window class
  507.     ZeroMemory(&wc, sizeof wc);
  508.     ghInst = wc.hInstance = hInstC;
  509.     wc.lpfnWndProc   = WndMainProc;
  510.     wc.lpszClassName = CLASSNAME;
  511.     wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU);
  512.     wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  513.     wc.hIcon         = LoadIcon(hInstC, MAKEINTRESOURCE(IDI_TEXT));
  514.     if(!RegisterClass(&wc))
  515.     {
  516.         Msg(TEXT("RegisterClass Failed! Error=0x%xrn"), GetLastError());
  517.         CoUninitialize();
  518.         exit(1);
  519.     }
  520.     // Create the main window.  The WS_CLIPCHILDREN style is required.
  521.     ghApp = CreateWindow(CLASSNAME, APPLICATIONNAME,
  522.                          WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_CLIPCHILDREN | WS_VISIBLE,
  523.                          CW_USEDEFAULT, CW_USEDEFAULT,
  524.                          DEFAULT_PLAYER_WIDTH, DEFAULT_PLAYER_HEIGHT,
  525.                          0, 0, ghInst, 0);
  526.     if(ghApp)
  527.     {
  528.         // Save menu handle for later use
  529.         ghMenu = GetMenu(ghApp);
  530.         // Set default dynamic text
  531.         _tcsncpy(g_szAppText, BLEND_TEXT, NUMELMS(g_szAppText));
  532.         // If a media file was specified on the command line, open it now.
  533.         // (If the first character in the string isn't NULL, post an open clip message.)
  534.         if (g_szFileName[0] != 0)
  535.             PostMessage(ghApp, WM_COMMAND, ID_FILE_INITCLIP, 0);
  536.         // Main message loop
  537.         while(GetMessage(&msg,NULL,0,0))
  538.         {
  539.             TranslateMessage(&msg);
  540.             DispatchMessage(&msg);
  541.         }
  542.     }
  543.     else
  544.     {
  545.         Msg(TEXT("Failed to create the main window! Error=0x%xrn"), GetLastError());
  546.     }
  547.     // Finished with COM
  548.     CoUninitialize();
  549.     return (int) msg.wParam;
  550. }
  551. HRESULT InitializeWindowlessVMR(IBaseFilter **ppVmr9)
  552. {
  553.     IBaseFilter* pVmr = NULL;
  554.     if (!ppVmr9)
  555.         return E_POINTER;
  556.     *ppVmr9 = NULL;
  557.     // Create the VMR and add it to the filter graph.
  558.     HRESULT hr = CoCreateInstance(CLSID_VideoMixingRenderer9, NULL,
  559.                      CLSCTX_INPROC, IID_IBaseFilter, (void**)&pVmr);
  560.     if (SUCCEEDED(hr)) 
  561.     {
  562.         hr = pGB->AddFilter(pVmr, L"Video Mixing Renderer 9");
  563.         if (SUCCEEDED(hr)) 
  564.         {
  565.             // Set the rendering mode and number of streams
  566.             CComPtr <IVMRFilterConfig9> pConfig;
  567.             JIF(pVmr->QueryInterface(IID_IVMRFilterConfig9, (void**)&pConfig));
  568.             JIF(pConfig->SetRenderingMode(VMR9Mode_Windowless));
  569.             hr = pVmr->QueryInterface(IID_IVMRWindowlessControl9, (void**)&pWC);
  570.             if( SUCCEEDED(hr)) 
  571.             {
  572.                 hr = pWC->SetVideoClippingWindow(ghApp);
  573.                 hr = pWC->SetBorderColor(RGB(0,0,0));
  574.             }
  575. #ifndef BILINEAR_FILTERING
  576.             // Request point filtering (instead of bilinear filtering)
  577.             // to improve the text quality.  In general, if you are 
  578.             // not scaling the app image, you should use point filtering.
  579.             // This is very important if you are doing source color keying.
  580.             IVMRMixerControl9 *pMix;
  581.             hr = pVmr->QueryInterface(IID_IVMRMixerControl9, (void**)&pMix);
  582.             if( SUCCEEDED(hr)) 
  583.             {
  584.                 DWORD dwPrefs=0;
  585.                 hr = pMix->GetMixingPrefs(&dwPrefs);
  586.                 if (SUCCEEDED(hr))
  587.                 {
  588.                     dwPrefs |= MixerPref_PointFiltering;
  589.                     dwPrefs &= ~(MixerPref_BiLinearFiltering);
  590.                     hr = pMix->SetMixingPrefs(dwPrefs);
  591.                 }
  592.                 pMix->Release();
  593.             }
  594. #endif
  595.             // Get alpha-blended bitmap interface
  596.             hr = pVmr->QueryInterface(IID_IVMRMixerBitmap9, (void**)&pBMP);
  597.         }
  598.         else
  599.             Msg(TEXT("Failed to add VMR to graph!  hr=0x%xrn"), hr);
  600.         // Don't release the pVmr interface because we are copying it into
  601.         // the caller's ppVmr9 pointer
  602.         *ppVmr9 = pVmr;
  603.     }
  604.     else
  605.         Msg(TEXT("Failed to create VMR!  hr=0x%xrn"), hr);
  606.     return hr;
  607. }
  608. void OnPaint(HWND hwnd) 
  609. {
  610.     HRESULT hr;
  611.     PAINTSTRUCT ps; 
  612.     HDC         hdc; 
  613.     RECT        rcClient; 
  614.     GetClientRect(hwnd, &rcClient); 
  615.     hdc = BeginPaint(hwnd, &ps); 
  616.     if(pWC) 
  617.     { 
  618.         // When using VMR Windowless mode, you must explicitly tell the
  619.         // renderer when to repaint the video in response to WM_PAINT
  620.         // messages.  This is most important when the video is stopped
  621.         // or paused, since the VMR won't be automatically updating the
  622.         // window as the video plays.
  623.         hr = pWC->RepaintVideo(hwnd, hdc);  
  624.     } 
  625.     else  // No video image. Just paint the whole client area. 
  626.     { 
  627.         FillRect(hdc, &rcClient, (HBRUSH)(COLOR_BTNFACE + 1)); 
  628.     } 
  629.     EndPaint(hwnd, &ps);