home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / Players / Jukebox / JukeboxDlg.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-09  |  46.3 KB  |  1,685 lines

  1. //------------------------------------------------------------------------------
  2. // File: JukeboxDlg.cpp
  3. //
  4. // Desc: DirectShow sample code - implementation of CJukeboxDlg class.
  5. //
  6. // Copyright (c) 1998-2001 Microsoft Corporation.  All rights reserved.
  7. //------------------------------------------------------------------------------
  8.  
  9. #include "stdafx.h"
  10. #include <atlbase.h>
  11. #include "Jukebox.h"
  12. #include "JukeboxDlg.h"
  13. #include "playvideo.h"
  14. #include "mediatypes.h"
  15.  
  16. #ifdef _DEBUG
  17. #define new DEBUG_NEW
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. /////////////////////////////////////////////////////////////////////////////
  23. // CAboutDlg dialog used for App About
  24.  
  25. class CAboutDlg : public CDialog
  26. {
  27. public:
  28.     CAboutDlg();
  29.  
  30. // Dialog Data
  31.     //{{AFX_DATA(CAboutDlg)
  32.     enum { IDD = IDD_ABOUTBOX };
  33.     //}}AFX_DATA
  34.  
  35.     // ClassWizard generated virtual function overrides
  36.     //{{AFX_VIRTUAL(CAboutDlg)
  37.     protected:
  38.     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
  39.     //}}AFX_VIRTUAL
  40.  
  41. // Implementation
  42. protected:
  43.     //{{AFX_MSG(CAboutDlg)
  44.     //}}AFX_MSG
  45.     DECLARE_MESSAGE_MAP()
  46. };
  47.  
  48. CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
  49. {
  50.     //{{AFX_DATA_INIT(CAboutDlg)
  51.     //}}AFX_DATA_INIT
  52. }
  53.  
  54. void CAboutDlg::DoDataExchange(CDataExchange* pDX)
  55. {
  56.     CDialog::DoDataExchange(pDX);
  57.     //{{AFX_DATA_MAP(CAboutDlg)
  58.     //}}AFX_DATA_MAP
  59. }
  60.  
  61. BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
  62.     //{{AFX_MSG_MAP(CAboutDlg)
  63.         // No message handlers
  64.     //}}AFX_MSG_MAP
  65. END_MESSAGE_MAP()
  66.  
  67. /////////////////////////////////////////////////////////////////////////////
  68. // CJukeboxDlg dialog
  69.  
  70. CJukeboxDlg::CJukeboxDlg(CWnd* pParent /*=NULL*/)
  71.     : CDialog(CJukeboxDlg::IDD, pParent)
  72. {
  73.     //{{AFX_DATA_INIT(CJukeboxDlg)
  74.     //}}AFX_DATA_INIT
  75.     // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
  76.     m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
  77. }
  78.  
  79. void CJukeboxDlg::DoDataExchange(CDataExchange* pDX)
  80. {
  81.     CDialog::DoDataExchange(pDX);
  82.     //{{AFX_DATA_MAP(CJukeboxDlg)
  83.     DDX_Control(pDX, IDC_STATIC_POSITION, m_StrPosition);
  84.     DDX_Control(pDX, IDC_SLIDER, m_Seekbar);
  85.     DDX_Control(pDX, IDC_STATIC_IMAGESIZE, m_StrImageSize);
  86.     DDX_Control(pDX, IDC_STATIC_DURATION, m_StrDuration);
  87.     DDX_Control(pDX, IDC_EDIT_MEDIADIR, m_EditMediaDir);
  88.     DDX_Control(pDX, IDC_SPIN_FILES, m_SpinFiles);
  89.     DDX_Control(pDX, IDC_BUTTON_FRAMESTEP, m_ButtonFrameStep);
  90.     DDX_Control(pDX, IDC_LIST_EVENTS, m_ListEvents);
  91.     DDX_Control(pDX, IDC_CHECK_EVENTS, m_CheckEvents);
  92.     DDX_Control(pDX, IDC_BUTTON_PROPPAGE, m_ButtonProperties);
  93.     DDX_Control(pDX, IDC_STATUS_DIRECTORY, m_StrMediaPath);
  94.     DDX_Control(pDX, IDC_CHECK_MUTE, m_CheckMute);
  95.     DDX_Control(pDX, IDC_BUTTON_STOP, m_ButtonStop);
  96.     DDX_Control(pDX, IDC_BUTTON_PLAY, m_ButtonPlay);
  97.     DDX_Control(pDX, IDC_BUTTON_PAUSE, m_ButtonPause);
  98.     DDX_Control(pDX, IDC_CHECK_PLAYTHROUGH, m_CheckPlaythrough);
  99.     DDX_Control(pDX, IDC_CHECK_LOOP, m_CheckLoop);
  100.     DDX_Control(pDX, IDC_STATIC_FILEDATE, m_StrFileDate);
  101.     DDX_Control(pDX, IDC_STATIC_FILESIZE, m_StrFileSize);
  102.     DDX_Control(pDX, IDC_LIST_PINS_OUTPUT, m_ListPinsOutput);
  103.     DDX_Control(pDX, IDC_LIST_PINS_INPUT, m_ListPinsInput);
  104.     DDX_Control(pDX, IDC_STATIC_FILELIST, m_StrFileList);
  105.     DDX_Control(pDX, IDC_STATUS, m_Status);
  106.     DDX_Control(pDX, IDC_MOVIE_SCREEN, m_Screen);
  107.     DDX_Control(pDX, IDC_LIST_FILTERS, m_ListFilters);
  108.     DDX_Control(pDX, IDC_LIST_FILES, m_ListFiles);
  109.     //}}AFX_DATA_MAP
  110. }
  111.  
  112. BEGIN_MESSAGE_MAP(CJukeboxDlg, CDialog)
  113.     //{{AFX_MSG_MAP(CJukeboxDlg)
  114.     ON_WM_ERASEBKGND()
  115.     ON_WM_SYSCOMMAND()
  116.     ON_WM_PAINT()
  117.     ON_WM_QUERYDRAGICON()
  118.     ON_WM_CLOSE()
  119.     ON_WM_DESTROY()
  120.     ON_LBN_SELCHANGE(IDC_LIST_FILES, OnSelectFile)
  121.     ON_BN_CLICKED(IDC_BUTTON_PAUSE, OnPause)
  122.     ON_BN_CLICKED(IDC_BUTTON_PLAY, OnPlay)
  123.     ON_BN_CLICKED(IDC_BUTTON_STOP, OnStop)
  124.     ON_BN_CLICKED(IDC_CHECK_MUTE, OnCheckMute)
  125.     ON_BN_CLICKED(IDC_CHECK_LOOP, OnCheckLoop)
  126.     ON_BN_CLICKED(IDC_CHECK_PLAYTHROUGH, OnCheckPlaythrough)
  127.     ON_LBN_SELCHANGE(IDC_LIST_FILTERS, OnSelchangeListFilters)
  128.     ON_LBN_DBLCLK(IDC_LIST_FILTERS, OnDblclkListFilters)
  129.     ON_BN_CLICKED(IDC_BUTTON_PROPPAGE, OnButtonProppage)
  130.     ON_BN_CLICKED(IDC_CHECK_EVENTS, OnCheckEvents)
  131.     ON_BN_CLICKED(IDC_BUTTON_FRAMESTEP, OnButtonFramestep)
  132.     ON_BN_CLICKED(IDC_BUTTON_CLEAR_EVENTS, OnButtonClearEvents)
  133.     ON_LBN_DBLCLK(IDC_LIST_FILES, OnDblclkListFiles)
  134.     ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_FILES, OnDeltaposSpinFiles)
  135.     ON_BN_CLICKED(IDC_BUTTON_SET_MEDIADIR, OnButtonSetMediadir)
  136.     ON_WM_TIMER()
  137.     ON_BN_CLICKED(IDC_BUTTON_GRAPHEDIT, OnButtonGraphedit)
  138.     //}}AFX_MSG_MAP
  139. END_MESSAGE_MAP()
  140.  
  141.  
  142. /////////////////////////////////////////////////////////////////////////////
  143. // CJukeboxDlg message handlers
  144.  
  145. void CJukeboxDlg::OnSysCommand(UINT nID, LPARAM lParam)
  146. {
  147.     if ((nID & 0xFFF0) == IDM_ABOUTBOX)
  148.     {
  149.         CAboutDlg dlgAbout;
  150.         dlgAbout.DoModal();
  151.     }
  152.     else
  153.     {
  154.         CDialog::OnSysCommand(nID, lParam);
  155.     }
  156. }
  157.  
  158. // If you add a minimize button to your dialog, you will need the code below
  159. //  to draw the icon.  For MFC applications using the document/view model,
  160. //  this is automatically done for you by the framework.
  161.  
  162. void CJukeboxDlg::OnPaint() 
  163. {
  164.     if (IsIconic())
  165.     {
  166.         CPaintDC dc(this); // device context for painting
  167.  
  168.         SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
  169.  
  170.         // Center icon in client rectangle
  171.         int cxIcon = GetSystemMetrics(SM_CXICON);
  172.         int cyIcon = GetSystemMetrics(SM_CYICON);
  173.         CRect rect;
  174.         GetClientRect(&rect);
  175.         int x = (rect.Width() - cxIcon + 1) / 2;
  176.         int y = (rect.Height() - cyIcon + 1) / 2;
  177.  
  178.         // Draw the icon
  179.         dc.DrawIcon(x, y, m_hIcon);
  180.     }
  181.     else
  182.     {
  183.         CDialog::OnPaint();
  184.     }
  185. }
  186.  
  187. // The system calls this to obtain the cursor to display while the user drags
  188. //  the minimized window.
  189. HCURSOR CJukeboxDlg::OnQueryDragIcon()
  190. {
  191.     return (HCURSOR) m_hIcon;
  192. }
  193.  
  194.  
  195. /////////////////////////////////////////////////////////////////////////////
  196. // CJukeboxDlg DirectShow code and message handlers
  197.  
  198.  
  199. BOOL CJukeboxDlg::OnInitDialog()
  200. {
  201.     CDialog::OnInitDialog();
  202.  
  203.     // Add "About..." menu item to system menu.
  204.  
  205.     // IDM_ABOUTBOX must be in the system command range.
  206.     ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
  207.     ASSERT(IDM_ABOUTBOX < 0xF000);
  208.  
  209.     CMenu* pSysMenu = GetSystemMenu(FALSE);
  210.     if (pSysMenu != NULL)
  211.     {
  212.         CString strAboutMenu;
  213.         strAboutMenu.LoadString(IDS_ABOUTBOX);
  214.         if (!strAboutMenu.IsEmpty())
  215.         {
  216.             pSysMenu->AppendMenu(MF_SEPARATOR);
  217.             pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  218.         }
  219.     }
  220.  
  221.     // Set the icon for this dialog.  The framework does this automatically
  222.     //  when the application's main window is not a dialog
  223.     SetIcon(m_hIcon, TRUE);            // Set big icon
  224.     SetIcon(m_hIcon, FALSE);        // Set small icon
  225.     
  226.     // Initialize COM
  227.     CoInitialize(NULL);
  228.     
  229.     // Initialize DirectShow and query for needed interfaces
  230.     HRESULT hr = InitDirectShow();
  231.     if(FAILED(hr))
  232.     {
  233.         RetailOutput(TEXT("Failed to initialize DirectShow!  hr=0x%x\r\n"), hr);
  234.         return FALSE;
  235.     }
  236.  
  237.     // IMPORTANT
  238.     // Since we're embedding video in a child window of a dialog,
  239.     // we must set the WS_CLIPCHILDREN style to prevent the bounding
  240.     // rectangle from drawing over our video frames.
  241.     //
  242.     // Neglecting to set this style can lead to situations when the video
  243.     // is erased and replaced with black (or the default color of the 
  244.     // bounding rectangle).
  245.     m_Screen.ModifyStyle(0, WS_CLIPCHILDREN);
  246.  
  247.     // Propagate the files list and select the first item
  248.     InitMediaDirectory();
  249.    
  250.     // Initialize seeking trackbar range
  251.     m_Seekbar.SetRange(0, 100, TRUE);
  252.     m_Seekbar.SetTicFreq(10);
  253.     
  254.     return TRUE;  // return TRUE  unless you set the focus to a control
  255. }
  256.  
  257.  
  258. void CJukeboxDlg::InitMediaDirectory(void)
  259. {
  260.     // Fill the media file list, starting with the directory passed
  261.     // on the command line.  If no directory is passed, then read the
  262.     // default media path for the DirectX SDK.
  263.     TCHAR szDir[MAX_PATH];
  264.     LONG lResult=0;
  265.  
  266.     if (theApp.m_lpCmdLine[0] == L'\0')
  267.     {
  268.         lResult = GetDXMediaPath(szDir);
  269.  
  270.         // If the DirectX SDK is not installed, use the Windows media
  271.         // directory instead.
  272.         if (lResult != 0)
  273.         {
  274.             GetWindowsDirectory(szDir, MAX_PATH);
  275.             _tcscat(szDir, _T("\\Media\\") );
  276.         }
  277.     }
  278.     else
  279.         _tcscpy(szDir, theApp.m_lpCmdLine);
  280.  
  281.     TCHAR szPathMsg[MAX_PATH];
  282.     wsprintf(szPathMsg, TEXT("Media directory: %s\0"), szDir);
  283.     m_StrMediaPath.SetWindowText(szPathMsg);
  284.  
  285.     // Save current directory name
  286.     wsprintf(m_szCurrentDir, TEXT("%s\0"), szDir);
  287.  
  288.     m_EditMediaDir.SetLimitText(MAX_PATH);
  289.     m_EditMediaDir.SetWindowText(szDir);
  290.  
  291.     // Find all files in this directory and fill the list box
  292.     FillFileList(szDir);
  293. }
  294.  
  295.  
  296. LONG CJukeboxDlg::GetDXMediaPath(TCHAR *szPath)
  297. {
  298.     HKEY  hKey;
  299.     DWORD dwType, dwSize = MAX_PATH;
  300.  
  301.     // Open the appropriate registry key
  302.     LONG lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  303.                                 _T("Software\\Microsoft\\DirectX SDK"),
  304.                                 0, KEY_READ, &hKey );
  305.     if( ERROR_SUCCESS != lResult )
  306.         return -1;
  307.  
  308.     lResult = RegQueryValueEx( hKey, _T("DX81SDK Samples Path"), NULL,
  309.                               &dwType, (BYTE*)szPath, &dwSize );
  310.     RegCloseKey( hKey );
  311.  
  312.     if( ERROR_SUCCESS != lResult )
  313.         return -1;
  314.  
  315.     _tcscat( szPath, _T("\\Media\\") );
  316.     return 0;
  317. }
  318.  
  319.  
  320. void CJukeboxDlg::FillFileList(LPTSTR pszRootDir)
  321. {
  322.     UINT attr = 0;
  323.  
  324.     m_ListFiles.ResetContent();
  325.  
  326.     ::SetCurrentDirectory(pszRootDir);
  327.     Say(TEXT("Building file list..."));
  328.  
  329.     // Add all of our known supported media types to the file list.
  330.     // Add files of each type in order.
  331.     for (int i=0; i < NUM_MEDIA_TYPES; i++)
  332.     {
  333.         m_ListFiles.Dir(attr, TypeInfo[i].pszType);
  334.     }
  335.     Say(TEXT("File list complete."));
  336.  
  337.     // Update list box title with number of items added
  338.     int nItems  = m_ListFiles.GetCount();
  339.     TCHAR szTitle[64];
  340.     wsprintf(szTitle, TEXT("Media files (%d found)"), nItems);
  341.     m_StrFileList.SetWindowText(szTitle);
  342.     
  343.     // Automatically select the first file in the list once
  344.     // the dialog is displayed.
  345.     PostMessage(WM_FIRSTFILE, 0, 0L);
  346.     m_nCurrentFileSelection = -1;     // No selection yet
  347. }
  348.  
  349.  
  350. void CJukeboxDlg::OnClose() 
  351. {
  352.     // Release DirectShow interfaces
  353.     StopMedia();
  354.     FreeDirectShow();
  355.  
  356.     // Release COM
  357.     CoUninitialize();
  358.  
  359.     CDialog::OnClose();
  360. }
  361.  
  362. void CJukeboxDlg::OnDestroy() 
  363. {
  364.     FreeDirectShow();    
  365.  
  366.     CDialog::OnDestroy();
  367. }
  368.  
  369.  
  370. void CJukeboxDlg::OnSelectFile() 
  371. {
  372.     HRESULT hr;
  373.     TCHAR szFilename[MAX_PATH];
  374.  
  375.     // If this is the currently selected file, do nothing
  376.     int nItem = m_ListFiles.GetCurSel();
  377.     if (nItem == m_nCurrentFileSelection)
  378.         return;
  379.  
  380.     // Remember the current selection to speed double-click processing
  381.     m_nCurrentFileSelection = nItem;
  382.  
  383.     // Read file name from list box
  384.     m_ListFiles.GetText(nItem, szFilename);
  385.  
  386.     // Remember current play state to restart playback
  387.     int nCurrentState = g_psCurrent;
  388.  
  389.     // First release any existing interfaces
  390.     ResetDirectShow();
  391.  
  392.     // Clear filter/pin/event information listboxes
  393.     m_ListFilters.ResetContent();
  394.     m_ListPinsInput.ResetContent();
  395.     m_ListPinsOutput.ResetContent();
  396.     m_ListEvents.ResetContent();
  397.  
  398.     // Load the selected media file
  399.     hr = PrepareMedia(szFilename);
  400.     if (FAILED(hr))
  401.     {
  402.         // Error - disable play button and give feedback
  403.         Say(TEXT("File failed to render!"));
  404.         m_ButtonPlay.EnableWindow(FALSE);
  405.         MessageBeep(0);
  406.         return;
  407.     }
  408.     else
  409.     {
  410.         m_ButtonPlay.EnableWindow(TRUE);
  411.     }
  412.  
  413.     // Display useful information about this file
  414.     DisplayFileInfo(szFilename);
  415.     DisplayImageInfo();
  416.     DisplayFileDuration();
  417.  
  418.     // Set up the seeking trackbar and read capabilities
  419.     ConfigureSeekbar();
  420.  
  421.     // Enumerate and display filters in graph
  422.     hr = EnumFilters();
  423.  
  424.     // Select the first filter in the list to display pin info
  425.     m_ListFilters.SetCurSel(0);
  426.     OnSelchangeListFilters();
  427.  
  428.     // See if the renderer supports frame stepping on this file.
  429.     // Enable/disable frame stepping button accordingly
  430.     m_ButtonFrameStep.EnableWindow(CanStep());
  431.  
  432.     // If the user has asked to mute audio then we
  433.     // need to mute this new clip before continuing.
  434.     if (g_bGlobalMute)
  435.         MuteAudio();
  436.  
  437.     // If we were running when the user changed selection,
  438.     // start running the newly selected clip
  439.     if (nCurrentState == State_Running)
  440.     {
  441.         OnPlay();
  442.     }
  443.     else
  444.     {
  445.         // Cue the first video frame
  446.         OnStop();
  447.     }
  448. }
  449.  
  450.  
  451. HRESULT CJukeboxDlg::PrepareMedia(LPTSTR lpszMovie)
  452. {
  453.     USES_CONVERSION;
  454.     HRESULT hr = S_OK;
  455.  
  456.     Say(TEXT("Loading..."));
  457.  
  458.     // Allow DirectShow to create the FilterGraph for this media file
  459.     hr = pGB->RenderFile(T2W(lpszMovie), NULL);
  460.     if (FAILED(hr)) {
  461.         RetailOutput(TEXT("*** Failed(%08lx) in RenderFile(%s)!\r\n"),
  462.                  hr, lpszMovie);
  463.         return hr;
  464.     }
  465.  
  466.     // Set the message drain of the video window to point to our main
  467.     // application window.  If this is an audio-only or MIDI file, 
  468.     // then put_MessageDrain will fail.
  469.     hr = pVW->put_MessageDrain((OAHWND) m_hWnd);
  470.     if (FAILED(hr))
  471.     {
  472.         g_bAudioOnly = TRUE;
  473.     }
  474.  
  475.     // Have the graph signal event via window callbacks
  476.     hr = pME->SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY, 0);
  477.  
  478.     // Configure the video window
  479.     if (!g_bAudioOnly)
  480.     {
  481.         // We'll manually set the video to be visible
  482.         hr = pVW->put_Visible(OAFALSE);
  483.  
  484.         hr = pVW->put_WindowStyle(WS_CHILD);
  485.         hr = pVW->put_Owner((OAHWND) m_Screen.GetSafeHwnd());
  486.  
  487.         // Place video window within the bounding rectangle
  488.         CenterVideo();
  489.  
  490.         // Make the video window visible within the screen window.
  491.         // If this is an audio-only file, then there won't be a video interface.
  492.         hr = pVW->put_Visible(OATRUE);
  493.         hr = pVW->SetWindowForeground(-1);
  494.     }
  495.  
  496.     Say(TEXT("Ready"));
  497.     return hr;
  498. }
  499.  
  500.  
  501. //
  502. //  Displays a text string in a status line near the bottom of the dialog
  503. //
  504. void CJukeboxDlg::Say(LPTSTR szText)
  505. {
  506.     m_Status.SetWindowText(szText);
  507. }
  508.  
  509. void CJukeboxDlg::OnPause() 
  510. {  
  511.     if (g_psCurrent == State_Paused)
  512.     {
  513.         RunMedia();
  514.         StartSeekTimer();
  515.         Say(TEXT("Running"));
  516.     }
  517.     else
  518.     {
  519.         StopSeekTimer();
  520.         PauseMedia();
  521.         Say(TEXT("PAUSED"));
  522.     }
  523. }
  524.  
  525. void CJukeboxDlg::OnPlay() 
  526. {
  527.       RunMedia();
  528.     StartSeekTimer();
  529.     Say(TEXT("Running"));
  530. }
  531.  
  532. void CJukeboxDlg::ShowState()
  533. {
  534.     HRESULT hr;
  535.  
  536.     OAFilterState fs;
  537.     hr = pMC->GetState(500, &fs);
  538.     if (FAILED(hr))
  539.     {
  540.         RetailOutput(TEXT("Failed to read graph state!  hr=0x%x\r\n"), hr);
  541.         return;
  542.     }
  543.  
  544.     // Show debug output for current media state
  545.     switch (fs)
  546.     {
  547.         case State_Stopped:
  548.             RetailOutput(TEXT("State_Stopped\r\n"));
  549.             break;
  550.         case State_Paused:
  551.             RetailOutput(TEXT("State_Paused\r\n"));
  552.             break;
  553.         case State_Running:
  554.             RetailOutput(TEXT("State_Running\r\n"));
  555.             break;
  556.     }
  557. }
  558.  
  559. void CJukeboxDlg::OnStop() 
  560. {
  561.     HRESULT hr;
  562.  
  563.     // Stop playback immediately with IMediaControl::Stop().
  564.     StopSeekTimer();
  565.     StopMedia();
  566.  
  567.     // Wait for the stop to propagate to all filters
  568.     OAFilterState fs;
  569.     hr = pMC->GetState(500, &fs);
  570.     if (FAILED(hr))
  571.     {
  572.         RetailOutput(TEXT("Failed to read graph state!  hr=0x%x\r\n"), hr);
  573.     }
  574.  
  575.     // Reset to beginning of media clip
  576.     LONGLONG pos=0;
  577.     hr = pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
  578.                            NULL, AM_SEEKING_NoPositioning);
  579.     if (FAILED(hr))
  580.     {
  581.         RetailOutput(TEXT("Failed to seek to beginning of media!  hr=0x%x\r\n"), hr);
  582.     }
  583.   
  584.     // Display the first frame of the media clip, if it contains video.
  585.     // StopWhenReady() pauses all filters internally (which allows the video
  586.     // renderer to queue and display the first video frame), after which
  587.     // it sets the filters to the stopped state.  This enables easy preview
  588.     // of the video's poster frame.
  589.     hr = pMC->StopWhenReady();
  590.     if (FAILED(hr))
  591.     {
  592.         RetailOutput(TEXT("Failed in StopWhenReady!  hr=0x%x\r\n"), hr);
  593.     }
  594.  
  595.     Say(TEXT("Stopped"));
  596.  
  597.     // Reset slider bar and position label back to zero
  598.     ReadMediaPosition();
  599. }
  600.  
  601. HRESULT CJukeboxDlg::InitDirectShow(void)
  602. {
  603.     HRESULT hr = S_OK;
  604.  
  605.     g_bAudioOnly = FALSE;
  606.  
  607.     // Zero interfaces (sanity check)
  608.     pVW = NULL;
  609.     pBV = NULL;
  610.  
  611.     JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&pGB));
  612.     JIF(pGB->QueryInterface(IID_IMediaControl,  (void **)&pMC));
  613.     JIF(pGB->QueryInterface(IID_IMediaSeeking,  (void **)&pMS));
  614.     JIF(pGB->QueryInterface(IID_IBasicVideo,    (void **)&pBV));
  615.     JIF(pGB->QueryInterface(IID_IVideoWindow,   (void **)&pVW));
  616.     JIF(pGB->QueryInterface(IID_IMediaEventEx,  (void **)&pME));
  617.  
  618.     return S_OK;
  619.  
  620. CLEANUP:
  621.     FreeDirectShow();
  622.     return(hr);
  623. }
  624.  
  625. HRESULT CJukeboxDlg::FreeDirectShow(void)
  626. {
  627.     HRESULT hr=S_OK;
  628.  
  629.     StopSeekTimer();
  630.     StopMedia();
  631.  
  632.     // Disable event callbacks
  633.     if (pME)
  634.         hr = pME->SetNotifyWindow((OAHWND)NULL, 0, 0);
  635.  
  636.     // Hide video window and remove owner.  This is not necessary here,
  637.     // since we are about to destroy the filter graph, but it is included
  638.     // for demonstration purposes.  Remember to hide the video window and
  639.     // clear its owner when destroying a window that plays video.
  640.     if(pVW)
  641.     {
  642.         hr = pVW->put_Visible(OAFALSE);
  643.         hr = pVW->put_Owner(NULL);
  644.     }
  645.  
  646.     SAFE_RELEASE(pMC);
  647.     SAFE_RELEASE(pMS);
  648.     SAFE_RELEASE(pVW);
  649.     SAFE_RELEASE(pBV);
  650.     SAFE_RELEASE(pME);
  651.     SAFE_RELEASE(pGB);
  652.  
  653.     return hr;
  654. }
  655.  
  656. void CJukeboxDlg::ResetDirectShow(void)
  657. {
  658.     // Destroy the current filter graph its filters.
  659.     FreeDirectShow();
  660.  
  661.     // Reinitialize graph builder and query for interfaces
  662.     InitDirectShow();
  663. }
  664.  
  665. void CJukeboxDlg::CenterVideo(void)
  666. {
  667.     LONG width, height;
  668.     HRESULT hr;
  669.  
  670.     if ((g_bAudioOnly) || (!pVW))
  671.         return;
  672.  
  673.     // Read coordinates of video container window
  674.     RECT rc;
  675.     m_Screen.GetClientRect(&rc);
  676.     width =  rc.right - rc.left;
  677.     height = rc.bottom - rc.top;
  678.  
  679.     // Ignore the video's original size and stretch to fit bounding rectangle
  680.     hr = pVW->SetWindowPosition(rc.left, rc.top, width, height);
  681.     if (FAILED(hr))
  682.     {
  683.         RetailOutput(TEXT("Failed to set window position!  hr=0x%x\r\n"), hr);
  684.         return;
  685.     }
  686. }
  687.  
  688.  
  689. LRESULT CJukeboxDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
  690. {
  691.     // Field notifications from the DirectShow filter graph manager
  692.     // and those posted by the application
  693.     switch (message)
  694.     {
  695.         case WM_GRAPHNOTIFY:
  696.             HandleGraphEvent();
  697.             break;
  698.  
  699.         case WM_HSCROLL:
  700.             HandleTrackbar(LOWORD(wParam));
  701.             break;
  702.  
  703.         case WM_PLAYFILE:
  704.             PlaySelectedFile();
  705.             break;
  706.  
  707.         case WM_NEXTFILE:
  708.             PlayNextFile();
  709.             break;
  710.  
  711.         case WM_PREVIOUSFILE:
  712.             PlayPreviousFile();
  713.             break;
  714.  
  715.         case WM_FIRSTFILE:
  716.             // Select the first item in the list
  717.             m_ListFiles.SetCurSel(0);
  718.             OnSelectFile();
  719.             break;
  720.  
  721.         case WM_CLOSE:
  722.             FreeDirectShow();
  723.             break;
  724.     }
  725.  
  726.     // Pass along this message to the video window, which exists as a child
  727.     // of the m_Screen window.  This method should be used by windows that 
  728.     // make a renderer window a child window. It forwards significant messages 
  729.     // to the child window that the child window would not otherwise receive. 
  730.     if (pVW)
  731.     {
  732.         pVW->NotifyOwnerMessage((LONG_PTR) m_hWnd, message, wParam, lParam);
  733.     }
  734.  
  735.     return CDialog::WindowProc(message, wParam, lParam);
  736. }
  737.  
  738.  
  739. HRESULT CJukeboxDlg::HandleGraphEvent(void)
  740. {
  741.     LONG evCode, evParam1, evParam2;
  742.     HRESULT hr=S_OK;
  743.  
  744.     // Since we may have a scenario where we're shutting down the application,
  745.     // but events are still being generated, make sure that the event
  746.     // interface is still valid before using it.  It's possible that
  747.     // the interface could be freed during shutdown but later referenced in
  748.     // this callback before the app completely exits.
  749.     if (!pME)
  750.         return S_OK;
  751.     
  752.     while(SUCCEEDED(pME->GetEvent(&evCode, (LONG_PTR *) &evParam1, 
  753.                     (LONG_PTR *) &evParam2, 0)))
  754.     {
  755.         // Spin through the events
  756.         hr = pME->FreeEventParams(evCode, evParam1, evParam2);
  757.  
  758.         if(EC_COMPLETE == evCode)
  759.         {
  760.             // If looping, reset to beginning and continue playing
  761.             if (g_bLooping)
  762.             {
  763.                 LONGLONG pos=0;
  764.  
  765.                 // Reset to first frame of movie
  766.                 hr = pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
  767.                                        NULL, AM_SEEKING_NoPositioning);
  768.                 if (FAILED(hr))
  769.                 {
  770.                     // Some custom filters (like the Windows CE MIDI filter) 
  771.                     // may not implement seeking interfaces (IMediaSeeking)
  772.                     // to allow seeking to the start.  In that case, just stop 
  773.                     // and restart for the same effect.  This should not be
  774.                     // necessary in most cases.
  775.                     StopMedia();
  776.                     RunMedia();
  777.                 }
  778.             }
  779.             else if (g_bPlayThrough)
  780.             {
  781.                 // Tell the app to select the next file in the list
  782.                 PostMessage(WM_NEXTFILE, 0, 0);
  783.             }
  784.             else
  785.             {
  786.                 // Stop playback and display first frame of movie
  787.                 OnStop();
  788.             }
  789.         }
  790.  
  791.         //  If requested, display DirectShow events received
  792.         if (g_bDisplayEvents)
  793.         {
  794.             DisplayECEvent(evCode, evParam1, evParam2);
  795.         }
  796.     }
  797.  
  798.     return hr;
  799. }
  800.  
  801. void CJukeboxDlg::OnCheckMute() 
  802. {
  803.     // Remember global mute status for next file.  When you destroy a
  804.     // filtergraph, you lose all of its audio settings.  Therefore, when
  805.     // we create the next graph, we will mute the audio before running
  806.     // the graph if this global variable is set.
  807.     g_bGlobalMute ^= 1; 
  808.  
  809.     if (g_bGlobalMute)
  810.         MuteAudio();
  811.     else
  812.         ResumeAudio();
  813. }
  814.  
  815. void CJukeboxDlg::OnCheckLoop() 
  816. {
  817.     g_bLooping ^= 1;
  818.  
  819.     // Looping and play-through are mutually exclusive
  820.     if ((g_bLooping) && (g_bPlayThrough))
  821.     {
  822.         // Disable play-through and uncheck button
  823.         g_bPlayThrough = 0;
  824.         m_CheckPlaythrough.SetCheck(0);
  825.     }
  826. }
  827.  
  828. void CJukeboxDlg::OnCheckPlaythrough() 
  829. {
  830.     g_bPlayThrough ^= 1;    
  831.  
  832.     // Looping and play-through are mutually exclusive
  833.     if ((g_bPlayThrough) && (g_bLooping) )
  834.     {
  835.         // Disable play-through and uncheck button
  836.         g_bLooping = 0;
  837.         m_CheckLoop.SetCheck(0);
  838.     }
  839. }
  840.  
  841. void CJukeboxDlg::OnCheckEvents() 
  842. {
  843.     g_bDisplayEvents ^= 1;    
  844. }
  845.  
  846. void CJukeboxDlg::OnButtonClearEvents() 
  847. {
  848.     m_ListEvents.ResetContent();
  849. }
  850.  
  851. void CJukeboxDlg::OnButtonSetMediadir() 
  852. {
  853.     TCHAR szEditPath[MAX_PATH];
  854.     DWORD dwAttr;
  855.  
  856.     // Make sure that we're not playing media
  857.     OnStop();
  858.  
  859.     // Read the string in the media directory edit box.
  860.     m_EditMediaDir.GetWindowText(szEditPath, MAX_PATH);
  861.  
  862.     // Is this a valid directory name?
  863.     dwAttr = GetFileAttributes(szEditPath);
  864.     if ((dwAttr == (DWORD) -1) || (! (dwAttr & FILE_ATTRIBUTE_DIRECTORY)))
  865.     {
  866.         MessageBox(TEXT("Please enter a valid directory name."), TEXT("Media error"));
  867.         return;
  868.     }
  869.  
  870.     // User has specified a valid media directory.  
  871.     // Update the current path string.
  872.     TCHAR szPathMsg[MAX_PATH];
  873.     wsprintf(szPathMsg, TEXT("Media directory: %s\0"), szEditPath);
  874.     m_StrMediaPath.SetWindowText(szPathMsg);
  875.  
  876.     // Save current directory name.  Append the trailing '\' to match
  877.     // the string created by GetDXSDKMediaPath() (if not present)
  878.     int nLength = _tcslen(szEditPath);
  879.     if (szEditPath[nLength - 1] != TEXT('\\'))
  880.         wsprintf(m_szCurrentDir, TEXT("%s\\\0"), szEditPath);
  881.  
  882.     // Propagate the files list and select the first item
  883.     FillFileList(szEditPath);
  884. }
  885.  
  886.  
  887. void CJukeboxDlg::OnDblclkListFiles() 
  888. {
  889.     // Because it might take time to render the file and display
  890.     // its first frame, it's better to post a message that tells
  891.     // the app to play the selected file when ready.
  892.     PostMessage(WM_PLAYFILE, 0, 0L);
  893. }
  894.  
  895. void CJukeboxDlg::PlaySelectedFile() 
  896. {
  897.     OnPlay();
  898. }
  899.  
  900. void CJukeboxDlg::OnDeltaposSpinFiles(NMHDR* pNMHDR, LRESULT* pResult) 
  901. {
  902.     NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;
  903.  
  904.     if (pNMUpDown->iDelta > 0)
  905.         PostMessage(WM_NEXTFILE, 0, 0L);
  906.     else
  907.         PostMessage(WM_PREVIOUSFILE, 0, 0L);
  908.  
  909.     *pResult = 0;
  910. }
  911.  
  912. void CJukeboxDlg::PlayNextFile(void)
  913. {
  914.     int nItems  = m_ListFiles.GetCount();
  915.  
  916.     // Return if the list is empty
  917.     if (!nItems)
  918.         return;
  919.         
  920.     int nCurSel = m_ListFiles.GetCurSel();
  921.     int nNewSel = (nCurSel + 1) % nItems;
  922.  
  923.     // Select the next item in the list, wrapping to top if needed
  924.     m_ListFiles.SetCurSel(nNewSel);
  925.     OnSelectFile();
  926. }
  927.  
  928.  
  929. void CJukeboxDlg::PlayPreviousFile(void)
  930. {
  931.     int nItems  = m_ListFiles.GetCount();
  932.  
  933.     // Return if the list is empty
  934.     if (!nItems)
  935.         return;
  936.         
  937.     int nCurSel = m_ListFiles.GetCurSel();
  938.     int nNewSel = nCurSel - 1;
  939.  
  940.     // If moved off top of list, select last item in list
  941.     if (nNewSel < 0)
  942.         nNewSel = nItems - 1;
  943.  
  944.     // Select the next item in the list, wrapping to top if needed
  945.     m_ListFiles.SetCurSel(nNewSel);
  946.     OnSelectFile();
  947. }
  948.  
  949.  
  950. HRESULT CJukeboxDlg::EnumFilters (void) 
  951. {
  952.     HRESULT hr;
  953.     IEnumFilters *pEnum = NULL;
  954.     IBaseFilter *pFilter = NULL;
  955.     ULONG cFetched;
  956.  
  957.     // Clear filters list box
  958.     m_ListFilters.ResetContent();
  959.     
  960.     // Get filter enumerator
  961.     hr = pGB->EnumFilters(&pEnum);
  962.     if (FAILED(hr))
  963.     {
  964.         m_ListFilters.AddString(TEXT("<ERROR>"));
  965.         return hr;
  966.     }
  967.  
  968.     // Enumerate all filters in the graph
  969.     while(pEnum->Next(1, &pFilter, &cFetched) == S_OK)
  970.     {
  971.         FILTER_INFO FilterInfo;
  972.         TCHAR szName[256];
  973.         
  974.         hr = pFilter->QueryFilterInfo(&FilterInfo);
  975.         if (FAILED(hr))
  976.         {
  977.             m_ListFilters.AddString(TEXT("<ERROR>"));
  978.         }
  979.         else
  980.         {
  981.             // Add the filter name to the filters listbox
  982.             USES_CONVERSION;
  983.  
  984.             lstrcpy(szName, W2T(FilterInfo.achName));
  985.             m_ListFilters.AddString(szName);
  986.  
  987.             FilterInfo.pGraph->Release();
  988.         }       
  989.         pFilter->Release();
  990.     }
  991.     pEnum->Release();
  992.  
  993.     return hr;
  994. }
  995.  
  996.  
  997. //
  998. // The GraphBuilder interface provides a FindFilterByName() method,
  999. // which provides similar functionality to the method below.
  1000. // This local method is provided for educational purposes.
  1001. //
  1002. IBaseFilter *CJukeboxDlg::FindFilterFromName(LPTSTR szNameToFind)
  1003. {
  1004.     USES_CONVERSION;
  1005.  
  1006.     HRESULT hr;
  1007.     IEnumFilters *pEnum = NULL;
  1008.     IBaseFilter *pFilter = NULL;
  1009.     ULONG cFetched;
  1010.     BOOL bFound = FALSE;
  1011.  
  1012.     // Get filter enumerator
  1013.     hr = pGB->EnumFilters(&pEnum);
  1014.     if (FAILED(hr))
  1015.         return NULL;
  1016.  
  1017.     // Enumerate all filters in the graph
  1018.     while((pEnum->Next(1, &pFilter, &cFetched) == S_OK) && (!bFound))
  1019.     {
  1020.         FILTER_INFO FilterInfo;
  1021.         TCHAR szName[256];
  1022.         
  1023.         hr = pFilter->QueryFilterInfo(&FilterInfo);
  1024.         if (FAILED(hr))
  1025.         {
  1026.             pFilter->Release();
  1027.             pEnum->Release();
  1028.             return NULL;
  1029.         }
  1030.  
  1031.         // Compare this filter's name with the one we want
  1032.         lstrcpy(szName, W2T(FilterInfo.achName));
  1033.         if (! lstrcmp(szName, szNameToFind))
  1034.         {
  1035.             bFound = TRUE;
  1036.         }
  1037.  
  1038.         FilterInfo.pGraph->Release();
  1039.  
  1040.         // If we found the right filter, don't release its interface.
  1041.         // The caller will use it and release it later.
  1042.         if (!bFound)
  1043.             pFilter->Release();
  1044.         else
  1045.             break;
  1046.     }
  1047.     pEnum->Release();
  1048.  
  1049.     return (bFound ? pFilter : NULL);
  1050. }
  1051.  
  1052.  
  1053. HRESULT CJukeboxDlg::EnumPins(IBaseFilter *pFilter, PIN_DIRECTION PinDir,
  1054.                               CListBox& Listbox)
  1055. {
  1056.     HRESULT hr;
  1057.     IEnumPins  *pEnum = NULL;
  1058.     IPin *pPin = NULL;
  1059.  
  1060.     // Clear the specified listbox (input or output)
  1061.     Listbox.ResetContent();
  1062.  
  1063.     // Get pin enumerator
  1064.     hr = pFilter->EnumPins(&pEnum);
  1065.     if (FAILED(hr))
  1066.     {
  1067.         Listbox.AddString(TEXT("<ERROR>"));
  1068.         return hr;
  1069.     }
  1070.  
  1071.     // Enumerate all pins on this filter
  1072.     while(pEnum->Next(1, &pPin, 0) == S_OK)
  1073.     {
  1074.         PIN_DIRECTION PinDirThis;
  1075.  
  1076.         hr = pPin->QueryDirection(&PinDirThis);
  1077.         if (FAILED(hr))
  1078.         {
  1079.             Listbox.AddString(TEXT("<ERROR>"));
  1080.             pPin->Release();
  1081.             continue;
  1082.         }
  1083.  
  1084.         // Does the pin's direction match the requested direction?
  1085.         if (PinDir == PinDirThis)
  1086.         {
  1087.             PIN_INFO pininfo={0};
  1088.  
  1089.             // Direction matches, so add pin name to listbox
  1090.             hr = pPin->QueryPinInfo(&pininfo);
  1091.             if (SUCCEEDED(hr))
  1092.             {
  1093.                 CString str(pininfo.achName);
  1094.                 Listbox.AddString(str);
  1095.             }
  1096.  
  1097.             // The pininfo structure contains a reference to an IBaseFilter,
  1098.             // so you must release its reference to prevent resource a leak.
  1099.             pininfo.pFilter->Release();
  1100.         }
  1101.         pPin->Release();
  1102.     }
  1103.     pEnum->Release();
  1104.  
  1105.     return hr;
  1106. }
  1107.  
  1108.  
  1109. void CJukeboxDlg::OnSelchangeListFilters() 
  1110. {
  1111.     HRESULT hr;
  1112.     IBaseFilter *pFilter = NULL;
  1113.     TCHAR szNameToFind[128];
  1114.  
  1115.     // Read the current filter name from the list box
  1116.     int nCurSel = m_ListFilters.GetCurSel();
  1117.     m_ListFilters.GetText(nCurSel, szNameToFind);
  1118.  
  1119.     // Read the current list box name and find it in the graph
  1120.     pFilter = FindFilterFromName(szNameToFind);
  1121.     if (!pFilter)
  1122.         return;
  1123.  
  1124.     // Now that we have filter information, enumerate pins by direction
  1125.     // and add their names to the appropriate listboxes
  1126.     hr = EnumPins(pFilter, PINDIR_INPUT,  m_ListPinsInput);
  1127.     hr = EnumPins(pFilter, PINDIR_OUTPUT, m_ListPinsOutput);
  1128.  
  1129.     // Find out if this filter supports a property page
  1130.     if (SupportsPropertyPage(pFilter))
  1131.         m_ButtonProperties.EnableWindow(TRUE);
  1132.     else
  1133.         m_ButtonProperties.EnableWindow(FALSE);
  1134.     
  1135.     // Must release the filter interface returned from FindFilterByName()
  1136.     pFilter->Release();
  1137. }
  1138.  
  1139.  
  1140. BOOL CJukeboxDlg::DisplayImageInfo(void)
  1141. {
  1142.     HRESULT hr;
  1143.     long lWidth, lHeight;
  1144.     
  1145.     // If this file has no video component, clear the text field
  1146.     if ((!pBV) || (g_bAudioOnly))
  1147.     {
  1148.         m_StrImageSize.SetWindowText(TEXT("\0"));
  1149.         return FALSE;
  1150.     }
  1151.  
  1152.     hr = pBV->GetVideoSize(&lWidth, &lHeight);
  1153.     if (SUCCEEDED(hr))
  1154.     {
  1155.         TCHAR szSize[64];
  1156.         wsprintf(szSize, TEXT("Image size: %d x %d\0"), lWidth, lHeight);
  1157.         m_StrImageSize.SetWindowText(szSize);
  1158.     }
  1159.  
  1160.     return TRUE;
  1161. }
  1162.  
  1163.  
  1164. BOOL CJukeboxDlg::DisplayFileInfo(LPTSTR szFile)
  1165. {
  1166.     HANDLE hFile;
  1167.     LONGLONG llSize=0;
  1168.     DWORD dwSizeLow=0, dwSizeHigh=0;
  1169.     TCHAR szScrap[64];
  1170.  
  1171.     // Open the specified file to read size and date information
  1172.     hFile = CreateFile(szFile,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,
  1173.                        (DWORD) 0, NULL);
  1174.  
  1175.     if (hFile == INVALID_HANDLE_VALUE)
  1176.     {
  1177.         RetailOutput(TEXT("*** Failed(0x%x) to open file (to read size)!\r\n"),
  1178.                      GetLastError());
  1179.         return FALSE;
  1180.     }
  1181.  
  1182.     dwSizeLow = GetFileSize(hFile, &dwSizeHigh);
  1183.     if ((dwSizeLow == 0xFFFFFFFF) && (GetLastError() != NO_ERROR))
  1184.     {
  1185.         RetailOutput(TEXT("*** Error(0x%x) reading file size!\r\n"),
  1186.                      GetLastError());
  1187.         CloseHandle(hFile);
  1188.         return FALSE;
  1189.     }
  1190.  
  1191.     // Large files will also use the upper DWORD to report size.
  1192.     // Add them together for the true size if necessary.
  1193.     if (dwSizeHigh)
  1194.         llSize = (dwSizeHigh << 16) + dwSizeLow;
  1195.     else
  1196.         llSize = dwSizeLow;
  1197.  
  1198.     // Read date information
  1199.     BY_HANDLE_FILE_INFORMATION fi;
  1200.     if (GetFileInformationByHandle(hFile, &fi))
  1201.     {
  1202.         CTime time(fi.ftLastWriteTime);
  1203.  
  1204.         wsprintf(szScrap, TEXT("File date: %02d/%02d/%d\0"), 
  1205.                  time.GetMonth(), time.GetDay(), time.GetYear());
  1206.         m_StrFileDate.SetWindowText(szScrap);
  1207.     }
  1208.  
  1209.     CloseHandle(hFile);
  1210.  
  1211.     // Update size/date windows
  1212.     wsprintf(szScrap, TEXT("Size: %d bytes\0"), dwSizeLow);
  1213.     m_StrFileSize.SetWindowText(szScrap);
  1214.  
  1215.     return TRUE;
  1216. }
  1217.  
  1218.  
  1219. HRESULT CJukeboxDlg::DisplayFileDuration(void)
  1220. {
  1221.     HRESULT hr;
  1222.  
  1223.     if (!pMS)
  1224.         return E_NOINTERFACE;
  1225.  
  1226.     // Initialize the display in case we can't read the duration
  1227.     m_StrDuration.SetWindowText(TEXT("<00:00.000>"));
  1228.  
  1229.     // Is media time supported for this file?
  1230.     if (S_OK != pMS->IsFormatSupported(&TIME_FORMAT_MEDIA_TIME))
  1231.         return E_NOINTERFACE;
  1232.  
  1233.     // Read the time format to restore later
  1234.     GUID guidOriginalFormat;
  1235.     hr = pMS->GetTimeFormat(&guidOriginalFormat);
  1236.     if (FAILED(hr))
  1237.         return hr;
  1238.  
  1239.     // Ensure media time format for easy display
  1240.     hr = pMS->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
  1241.     if (FAILED(hr))
  1242.         return hr;
  1243.  
  1244.     // Read the file's duration
  1245.     LONGLONG llDuration;
  1246.     hr = pMS->GetDuration(&llDuration);
  1247.     if (FAILED(hr))
  1248.         return hr;
  1249.  
  1250.     // Return to the original format
  1251.     if (guidOriginalFormat != TIME_FORMAT_MEDIA_TIME)
  1252.     {
  1253.         hr = pMS->SetTimeFormat(&guidOriginalFormat);
  1254.         if (FAILED(hr))
  1255.             return hr;
  1256.     }
  1257.  
  1258.     // Convert the LONGLONG duration into human-readable format
  1259.     unsigned long nTotalMS = (unsigned long) llDuration / 10000; // 100ns -> ms
  1260.     int nMS = nTotalMS % 1000;
  1261.     int nSeconds = nTotalMS / 1000;
  1262.     int nMinutes = nSeconds / 60;
  1263.     nSeconds %= 60;
  1264.  
  1265.     // Update the display
  1266.     TCHAR szDuration[24];
  1267.     wsprintf(szDuration, _T("%02dm:%02d.%03ds\0"), nMinutes, nSeconds, nMS);
  1268.     m_StrDuration.SetWindowText(szDuration);
  1269.  
  1270.     return hr;
  1271. }
  1272.  
  1273.  
  1274. BOOL CJukeboxDlg::SupportsPropertyPage(IBaseFilter *pFilter) 
  1275. {
  1276.     HRESULT hr;
  1277.     ISpecifyPropertyPages *pSpecify;
  1278.  
  1279.     // Discover if this filter contains a property page
  1280.     hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpecify);
  1281.     if (SUCCEEDED(hr)) 
  1282.     {
  1283.         pSpecify->Release();
  1284.         return TRUE;
  1285.     }
  1286.     else
  1287.         return FALSE;
  1288. }
  1289.  
  1290. void CJukeboxDlg::OnButtonProppage() 
  1291. {
  1292.     HRESULT hr;
  1293.     IBaseFilter *pFilter = NULL;
  1294.     TCHAR szNameToFind[128];
  1295.     ISpecifyPropertyPages *pSpecify;
  1296.  
  1297.     // Read the current filter name from the list box
  1298.     int nCurSel = m_ListFilters.GetCurSel();
  1299.     m_ListFilters.GetText(nCurSel, szNameToFind);
  1300.  
  1301.     // Read the current list box name and find it in the graph
  1302.     pFilter = FindFilterFromName(szNameToFind);
  1303.     if (!pFilter)
  1304.         return;
  1305.  
  1306.     // Discover if this filter contains a property page
  1307.     hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpecify);
  1308.     if (SUCCEEDED(hr)) 
  1309.     {
  1310.         do 
  1311.         {
  1312.             FILTER_INFO FilterInfo;
  1313.             hr = pFilter->QueryFilterInfo(&FilterInfo);
  1314.             if (FAILED(hr))
  1315.                 break;
  1316.  
  1317.             CAUUID caGUID;
  1318.             hr = pSpecify->GetPages(&caGUID);
  1319.             if (FAILED(hr))
  1320.                 break;
  1321.  
  1322.             pSpecify->Release();
  1323.         
  1324.             // Display the filter's property page
  1325.             OleCreatePropertyFrame(
  1326.                 m_hWnd,                 // Parent window
  1327.                 0,                      // x (Reserved)
  1328.                 0,                      // y (Reserved)
  1329.                 FilterInfo.achName,     // Caption for the dialog box
  1330.                 1,                      // Number of filters
  1331.                 (IUnknown **)&pFilter,  // Pointer to the filter 
  1332.                 caGUID.cElems,          // Number of property pages
  1333.                 caGUID.pElems,          // Pointer to property page CLSIDs
  1334.                 0,                      // Locale identifier
  1335.                 0,                      // Reserved
  1336.                 NULL                    // Reserved
  1337.             );
  1338.             CoTaskMemFree(caGUID.pElems);
  1339.             FilterInfo.pGraph->Release(); 
  1340.  
  1341.         } while(0);
  1342.     }
  1343.  
  1344.     pFilter->Release();
  1345. }
  1346.  
  1347. void CJukeboxDlg::OnDblclkListFilters() 
  1348. {
  1349.     OnButtonProppage();
  1350. }
  1351.  
  1352.  
  1353. void CJukeboxDlg::DisplayECEvent(long lEventCode, long lParam1, long lParam2)
  1354. {
  1355.     static TCHAR szMsg[256];
  1356.     BOOL bMatch = TRUE;
  1357.  
  1358. #define HANDLE_EC(c)                              \
  1359.     case c:                                       \
  1360.         wsprintf(szMsg, TEXT("%s\0"), TEXT(#c));  \
  1361.         break;
  1362.  
  1363.     switch (lEventCode)
  1364.     {
  1365.         HANDLE_EC(EC_ACTIVATE);
  1366.         HANDLE_EC(EC_BUFFERING_DATA);
  1367.         HANDLE_EC(EC_CLOCK_CHANGED);
  1368.         HANDLE_EC(EC_COMPLETE);
  1369.         HANDLE_EC(EC_DEVICE_LOST);
  1370.         HANDLE_EC(EC_DISPLAY_CHANGED);
  1371.         HANDLE_EC(EC_END_OF_SEGMENT);
  1372.         HANDLE_EC(EC_ERROR_STILLPLAYING);
  1373.         HANDLE_EC(EC_ERRORABORT);
  1374.         HANDLE_EC(EC_EXTDEVICE_MODE_CHANGE);
  1375.         HANDLE_EC(EC_FULLSCREEN_LOST);
  1376.         HANDLE_EC(EC_GRAPH_CHANGED);
  1377.         HANDLE_EC(EC_LENGTH_CHANGED);
  1378.         HANDLE_EC(EC_NEED_RESTART);
  1379.         HANDLE_EC(EC_NOTIFY_WINDOW);
  1380.         HANDLE_EC(EC_OLE_EVENT);
  1381.         HANDLE_EC(EC_OPENING_FILE);
  1382.         HANDLE_EC(EC_PALETTE_CHANGED);
  1383.         HANDLE_EC(EC_PAUSED);
  1384.         HANDLE_EC(EC_QUALITY_CHANGE);
  1385.         HANDLE_EC(EC_REPAINT);
  1386.         HANDLE_EC(EC_SEGMENT_STARTED);
  1387.         HANDLE_EC(EC_SHUTTING_DOWN);
  1388.         HANDLE_EC(EC_SNDDEV_IN_ERROR);
  1389.         HANDLE_EC(EC_SNDDEV_OUT_ERROR);
  1390.         HANDLE_EC(EC_STARVATION);
  1391.         HANDLE_EC(EC_STEP_COMPLETE);
  1392.         HANDLE_EC(EC_STREAM_CONTROL_STARTED);
  1393.         HANDLE_EC(EC_STREAM_CONTROL_STOPPED);
  1394.         HANDLE_EC(EC_STREAM_ERROR_STILLPLAYING);
  1395.         HANDLE_EC(EC_STREAM_ERROR_STOPPED);
  1396.         HANDLE_EC(EC_TIMECODE_AVAILABLE);
  1397.         HANDLE_EC(EC_USERABORT);
  1398.         HANDLE_EC(EC_VIDEO_SIZE_CHANGED);
  1399.         HANDLE_EC(EC_WINDOW_DESTROYED);
  1400.  
  1401.     default:
  1402.         bMatch = FALSE;
  1403.         RetailOutput(TEXT("  Received unknown event code (0x%x)\r\n"), lEventCode);
  1404.         break;
  1405.     }
  1406.  
  1407.     // If a recognized event was found, add its name to the events list box
  1408.     if (bMatch)
  1409.         m_ListEvents.AddString(szMsg);
  1410. }
  1411.  
  1412.  
  1413. void CJukeboxDlg::OnButtonFramestep() 
  1414. {
  1415.     StepFrame();
  1416. }
  1417.  
  1418.  
  1419. //
  1420. // Some hardware decoders and video renderers support stepping media
  1421. // frame by frame with the IVideoFrameStep interface.  See the interface
  1422. // documentation for more details on frame stepping.
  1423. //
  1424. BOOL CJukeboxDlg::CanStep(void)
  1425. {
  1426.     HRESULT hr;
  1427.     IVideoFrameStep* pFS;
  1428.  
  1429.     hr = pGB->QueryInterface(__uuidof(IVideoFrameStep), (PVOID *)&pFS);
  1430.     if (FAILED(hr))
  1431.         return FALSE;
  1432.  
  1433.     // Check if this decoder can step
  1434.     hr = pFS->CanStep(0L, NULL); 
  1435.  
  1436.     pFS->Release();
  1437.  
  1438.     if (hr == S_OK)
  1439.         return TRUE;
  1440.     else
  1441.         return FALSE;
  1442. }
  1443.  
  1444. HRESULT CJukeboxDlg::StepFrame(void)
  1445. {
  1446.     // Get the Frame Stepping Interface
  1447.     HRESULT hr;
  1448.     IVideoFrameStep* pFS;
  1449.  
  1450.     hr = pGB->QueryInterface(__uuidof(IVideoFrameStep), (PVOID *)&pFS);
  1451.     if (FAILED(hr))
  1452.         return hr;
  1453.  
  1454.     // The graph must be paused for frame stepping to work
  1455.     if (g_psCurrent != State_Paused)
  1456.         OnPause();
  1457.  
  1458.     // Step one frame
  1459.     hr = pFS->Step(1, NULL); 
  1460.     pFS->Release();
  1461.  
  1462.     // Since the media position has changed slightly, update the
  1463.     // slider bar and position label.
  1464.     ReadMediaPosition();
  1465.     return hr;
  1466. }
  1467.  
  1468.  
  1469. void CJukeboxDlg::ConfigureSeekbar()
  1470. {
  1471.     // Disable seekbar for new file and reset tracker/position label
  1472.     m_Seekbar.SetPos(0);
  1473.     m_StrPosition.SetWindowText(TEXT("Position: 00m:00s\0"));
  1474.     g_rtTotalTime=0;
  1475.  
  1476.     // If we can't read the file's duration, disable the seek bar
  1477.     if (pMS && SUCCEEDED(pMS->GetDuration(&g_rtTotalTime)))
  1478.         m_Seekbar.EnableWindow(TRUE);
  1479.     else
  1480.         m_Seekbar.EnableWindow(FALSE);
  1481. }
  1482.  
  1483. void CJukeboxDlg::StartSeekTimer() 
  1484. {
  1485.     // Cancel any pending timer event
  1486.     StopSeekTimer();
  1487.  
  1488.     // Create a new timer
  1489.     g_wTimerID = SetTimer(TIMERID, TICKLEN, NULL);
  1490. }
  1491.  
  1492. void CJukeboxDlg::StopSeekTimer() 
  1493. {
  1494.     // Cancel the timer
  1495.     if(g_wTimerID)        
  1496.     {                
  1497.         KillTimer(g_wTimerID);
  1498.         g_wTimerID = 0;
  1499.     }
  1500. }
  1501.  
  1502. void CJukeboxDlg::OnTimer(UINT nIDEvent) 
  1503. {
  1504.     ReadMediaPosition();
  1505.  
  1506.     CDialog::OnTimer(nIDEvent);
  1507. }
  1508.  
  1509. void CJukeboxDlg::ReadMediaPosition()
  1510. {
  1511.     HRESULT hr;
  1512.     REFERENCE_TIME rtNow;
  1513.  
  1514.     // Read the current stream position
  1515.     hr = pMS->GetCurrentPosition(&rtNow);
  1516.     if (FAILED(hr))
  1517.         return;
  1518.  
  1519.     // Convert position into a percentage value and update slider position
  1520.     if (g_rtTotalTime != 0)
  1521.     {
  1522.         long lTick = (long)((rtNow * 100) / g_rtTotalTime);
  1523.         m_Seekbar.SetPos(lTick);
  1524.     }
  1525.     else
  1526.         m_Seekbar.SetPos(0);
  1527.     
  1528.     // Update the 'current position' string on the main dialog
  1529.     UpdatePosition(rtNow);
  1530. }
  1531.  
  1532. void CJukeboxDlg::UpdatePosition(REFERENCE_TIME rtNow) 
  1533. {
  1534.     HRESULT hr;
  1535.  
  1536.     // If no reference time was passed in, read the current position
  1537.     if (rtNow == 0)
  1538.     {
  1539.         // Read the current stream position
  1540.         hr = pMS->GetCurrentPosition(&rtNow);
  1541.         if (FAILED(hr))
  1542.             return;
  1543.     }
  1544.  
  1545.     // Convert the LONGLONG duration into human-readable format
  1546.     unsigned long nTotalMS = (unsigned long) rtNow / 10000; // 100ns -> ms
  1547.     int nSeconds = nTotalMS / 1000;
  1548.     int nMinutes = nSeconds / 60;
  1549.     nSeconds %= 60;
  1550.  
  1551.     // Update the display
  1552.     TCHAR szPosition[24], szCurrentString[24];
  1553.     wsprintf(szPosition, _T("Position: %02dm:%02ds\0"), nMinutes, nSeconds);
  1554.  
  1555.     // Read current string and compare to the new string.  To prevent flicker,
  1556.     // don't update this label unless the string has changed.
  1557.     m_StrPosition.GetWindowText(szCurrentString, 24);
  1558.  
  1559.     if (_tcscmp(szCurrentString, szPosition))
  1560.         m_StrPosition.SetWindowText(szPosition);
  1561. }
  1562.  
  1563. void CJukeboxDlg::HandleTrackbar(WPARAM wReq)
  1564. {
  1565.     HRESULT hr;
  1566.     static OAFilterState state;
  1567.     static BOOL bStartOfScroll = TRUE;
  1568.  
  1569.     // If the file is not seekable, the trackbar is disabled. 
  1570.     DWORD dwPosition = m_Seekbar.GetPos();
  1571.  
  1572.     // Pause when the scroll action begins.
  1573.     if (bStartOfScroll) 
  1574.     {       
  1575.         hr = pMC->GetState(10, &state);
  1576.         bStartOfScroll = FALSE;
  1577.         hr = pMC->Pause();
  1578.     }
  1579.     
  1580.     // Update the position continuously.
  1581.     REFERENCE_TIME rtNew = (g_rtTotalTime * dwPosition) / 100;
  1582.  
  1583.     hr = pMS->SetPositions(&rtNew, AM_SEEKING_AbsolutePositioning,
  1584.                            NULL,   AM_SEEKING_NoPositioning);
  1585.  
  1586.     // Restore the state at the end.
  1587.     if (wReq == TB_ENDTRACK)
  1588.     {
  1589.         if (state == State_Stopped)
  1590.             hr = pMC->Stop();
  1591.         else if (state == State_Running) 
  1592.             hr = pMC->Run();
  1593.  
  1594.         bStartOfScroll = TRUE;
  1595.     }
  1596.  
  1597.     // Update the 'current position' string on the main dialog.
  1598.     UpdatePosition(rtNew);
  1599. }
  1600.  
  1601. LONG CJukeboxDlg::GetGraphEditPath(TCHAR *szPath)
  1602. {
  1603.     HKEY  hKey;
  1604.     DWORD dwType, dwSize = MAX_PATH;
  1605.  
  1606.     // Open the appropriate registry key
  1607.     LONG lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  1608.                                 _T("Software\\Microsoft\\Shared Tools\\Graphedit"),
  1609.                                 0, KEY_READ, &hKey );
  1610.     if( ERROR_SUCCESS != lResult )
  1611.         return -1;
  1612.  
  1613.     // Read the full path (including .exe name) for the GraphEdit tool
  1614.     lResult = RegQueryValueEx( hKey, _T("Path"), NULL,
  1615.                               &dwType, (BYTE*)szPath, &dwSize );
  1616.     RegCloseKey( hKey );
  1617.  
  1618.     if( ERROR_SUCCESS != lResult )
  1619.         return -1;
  1620.  
  1621.     return 0;
  1622. }
  1623.  
  1624. void CJukeboxDlg::OnButtonGraphedit() 
  1625. {
  1626.     TCHAR szFilename[128], szFile[MAX_PATH];
  1627.  
  1628.     // Read file name from list box
  1629.     int nItem = m_ListFiles.GetCurSel();
  1630.     m_ListFiles.GetText(nItem, szFilename);
  1631.  
  1632.     // Build the full file name with path.  The path will already have
  1633.     // a '\' appended to the end.
  1634.     wsprintf(szFile, TEXT("%s%s\0"), m_szCurrentDir, szFilename);
  1635.  
  1636.     // Launch GraphEdit for the selected file
  1637.     //    
  1638.     // First look for a registry key containing its full path
  1639.     TCHAR szPath[MAX_PATH];
  1640.  
  1641.     LONG lResult = GetGraphEditPath(szPath);
  1642.  
  1643.     // If the DirectX SDK is not installed, just look for GraphEdit
  1644.     // anywhere in the system path.
  1645.     if (lResult != 0)
  1646.     {
  1647.         wsprintf(szPath, TEXT("%s\0"), TEXT("graphedt\0"));
  1648.     }
  1649.  
  1650.     // Lauch GraphEdit using either the full tool path or just its name                               
  1651.     HINSTANCE rc = ShellExecute(m_hWnd, TEXT("open\0"), szPath, 
  1652.                                 szFile, NULL, SW_SHOWNORMAL);
  1653.  
  1654.     if (rc < (HINSTANCE) 32)
  1655.     {
  1656.         // Failed to start the app
  1657.         if ((rc == (HINSTANCE) ERROR_FILE_NOT_FOUND) || 
  1658.             (rc == (HINSTANCE) ERROR_PATH_NOT_FOUND))
  1659.         {
  1660.             MessageBox(TEXT("Couldn't find the GraphEdit application.\r\n\r\n")
  1661.                        TEXT("Please copy graphedt.exe to a directory on your path."), 
  1662.                        TEXT("Can't find GraphEdit"));   
  1663.         }
  1664.     }
  1665. }
  1666.  
  1667. BOOL CJukeboxDlg::OnEraseBkgnd(CDC *pDC)
  1668. {
  1669.     // Intercept background erasing for the movie window, since the
  1670.     // video renderer will keep the screen painted.  Without this code,
  1671.     // your video window might get painted over with gray (the default
  1672.     // background brush) when it is obscured by another window and redrawn.
  1673.     CRect rc;
  1674.  
  1675.     // Get the bounding rectangle for the movie screen
  1676.     m_Screen.GetWindowRect(&rc);
  1677.     ScreenToClient(&rc);
  1678.  
  1679.     // Exclude the clipping region occupied by our movie screen
  1680.     pDC->ExcludeClipRect(&rc);
  1681.     
  1682.     // Erase the remainder of the dialog as usual
  1683.     return CDialog::OnEraseBkgnd(pDC);
  1684. }
  1685.