home *** CD-ROM | disk | FTP | other *** search
/ Beginning Direct3D Game Programming / Direct3D.iso / directx / dxf / samples / multimedia / directshow / dvd / dvdsample / dvdcore.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-11-04  |  81.1 KB  |  2,436 lines

  1. //------------------------------------------------------------------------------
  2. // File: DvdCore.cpp
  3. //
  4. // Desc: Actual implementation of DVD Playback capabilities
  5. //
  6. // Copyright (c) 1999-2000, Microsoft Corporation. All rights reserved.
  7. //------------------------------------------------------------------------------
  8.  
  9. #include <dshow.h>
  10. #include <IL21Dec.h>
  11. #include "resource.h"
  12. #include "DvdCore.h"
  13. #include <TCHAR.H>
  14. #include "StringUtil.h"
  15. #include <sstream> // STL String library
  16. using namespace std;
  17.  
  18. // Error message displayed if no DVD decoder is installed
  19. #define MSG_NO_DECODER TEXT \
  20.         ("The DVD sample could not locate a DVD decoder on your system.\r\n\r\n"     \
  21.         "You must install a third-party DirectShow-compatible MPEG-2 decoder,\r\n"   \
  22.         "either software or hardware-based. Microsoft does not supply an MPEG2\r\n"  \
  23.         "decoder with DirectShow or as an operating system component.\r\n\r\n"       \
  24.         "Please contact your DVD or PC manufacturer for a DVD decoder.\r\n\r\n"      \
  25.         "This sample will now exit.")
  26.  
  27.  
  28.  
  29. //------------------------------------------------------------------------------
  30. // File-specific data
  31. //------------------------------------------------------------------------------
  32.  
  33. namespace // empty namespace means these are only available within this .cpp file
  34. {
  35.     const WCHAR g_szBookmarkFilename[] = L"c:\\DVDSample.bmk";
  36.     const WCHAR g_szBookmarkName[] = L"DVDSampleBookmarks";
  37.     HHOOK g_hMouse = 0;
  38.     const UINT MOUSETIMER = 1; // the number is arbitrary, it just can't be 0.
  39.     const DWORD MOUSE_TIMEOUT = 5000; // the mouse should disappear after 5 seconds
  40. }
  41.  
  42.  
  43.  
  44.  
  45. //------------------------------------------------------------------------------
  46. // Name: CDvdCore::CDvdCore()
  47. // Desc: This is our constructor.  It initializes all of our variables and initializes
  48. //       the COM subsystem.
  49. //------------------------------------------------------------------------------
  50.  
  51. CDvdCore::CDvdCore(HINSTANCE hInstance, IDvdCallback * pIDC) : 
  52.                        m_dwRenderFlags(AM_DVD_HWDEC_PREFER),
  53.                        m_eState(Uninitialized),
  54.                        m_pIDvdGB(NULL),
  55.                        m_pIDvdC2(NULL),
  56.                        m_pIDvdI2(NULL),
  57.                        m_pGraph(NULL),
  58.                        m_pIVW(NULL),
  59.                        m_pIME(NULL),
  60.                        m_pIMC(NULL),
  61.                        m_hInstance(hInstance),
  62.                        m_pIL21Dec(NULL),
  63.                        m_hWnd(NULL),
  64.                        m_dwMouseMoveTime(0),
  65.                        m_bFirstPlay(true),
  66.                        m_bFullScreenOn(false),
  67.                        m_ulCurTitle(0),
  68.                        m_ulCurChapter(0),
  69.                        m_bMessageSink(false)
  70. {
  71.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::CDvdCore()"))) ;
  72.  
  73.     CoInitialize(NULL);
  74.     ZeroMemory(m_szDiscPath, sizeof(m_szDiscPath)) ;
  75.     ZeroMemory(&m_CurTime, sizeof(m_CurTime));
  76.  
  77.     // if we are given a valid pointer we will use it, otherwise we point to ourself.
  78.     if (NULL != pIDC)
  79.         m_pCallback = pIDC;
  80.     else
  81.         m_pCallback = this;
  82. }
  83.  
  84.  
  85. //------------------------------------------------------------------------------
  86. // Name: CDvdCore::~CDvdCore()
  87. // Desc: This is our destructor.  It release all of our COM interfaces and closes
  88. //       down the COM subsystem.
  89. //------------------------------------------------------------------------------
  90.  
  91. CDvdCore::~CDvdCore()
  92. {
  93.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::~CDvdCore()"))) ;
  94.  
  95.     if (m_bMessageSink) // we set up the message sink and so we need to tear it down
  96.         UnInitMessageSink();
  97.  
  98.     ReleaseInterfaces();
  99.     CoUninitialize();
  100. }
  101.  
  102.  
  103. //------------------------------------------------------------------------------
  104. // Name: CDvdCore::BuildGraph()
  105. // Desc: This method builds the DVD graph and initializes all of the interface pointers 
  106. //       we will need.
  107. //------------------------------------------------------------------------------
  108.  
  109. bool CDvdCore::BuildGraph()
  110. {
  111.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::BuildGraph()"))) ;
  112.     
  113.     // First release any existing interface pointer(s)
  114.     ReleaseInterfaces() ;
  115.  
  116.     HRESULT hr;
  117.     hr = CoCreateInstance(    CLSID_DvdGraphBuilder,
  118.                             NULL,
  119.                             CLSCTX_INPROC_SERVER,
  120.                             IID_IDvdGraphBuilder,
  121.                             reinterpret_cast<void**>(&m_pIDvdGB));
  122.     if (FAILED(hr))    // IDvdGraphBuilder not successfully instantiated
  123.     {
  124.         DbgLog((LOG_ERROR, 0, 
  125.             TEXT("ERROR: CoCreateInstance() for CLSID_DvdGraphBuilder failed (Error 0x%x)"), 
  126.             hr)) ;
  127.         return false; 
  128.     }
  129.  
  130.     SetState(Uninitialized) ;
  131.  
  132.     // Check if a DVD-Video volume name has been specified; if so, use that
  133.     WCHAR    szwDiscPath[MAX_PATH] ;
  134.     PCWSTR   pszwDiscPath = NULL ;  // by default
  135.     if (lstrlen(m_szDiscPath) > 0)  // if something was specified before
  136.     {
  137. #ifdef UNICODE
  138.         lstrcpy(szwDiscPath, m_szDiscPath) ;
  139. #else
  140.         MultiByteToWideChar(CP_ACP, 0, m_szDiscPath, -1, szwDiscPath, MAX_PATH) ;
  141. #endif // UNICODE
  142.         
  143.         pszwDiscPath = szwDiscPath ;
  144.     }
  145.  
  146.     // Build the Graph
  147.     AM_DVD_RENDERSTATUS    buildStatus;
  148.     DbgLog((LOG_TRACE, 5, TEXT("Calling RenderDvdVideoVolume(<%s>, 0x%lx, 0x%lx)"), 
  149.         m_szDiscPath, m_dwRenderFlags, &buildStatus)) ;
  150.     hr = m_pIDvdGB->RenderDvdVideoVolume(pszwDiscPath, m_dwRenderFlags, &buildStatus);
  151.  
  152.     if (FAILED(hr)) // total failure
  153.     {
  154.         // If there is no DVD decoder, give a user-friendly message
  155.         if (hr == 0x8004027b)
  156.         {
  157.             MessageBox(NULL, MSG_NO_DECODER, TEXT("Can't initialize DVD sample"), MB_OK);
  158.         }
  159.         else
  160.         {
  161.             TCHAR szBuffer[100];
  162.             AMGetErrorText(hr, szBuffer, sizeof(szBuffer));
  163.             MessageBox(NULL, szBuffer, TEXT("RenderDvdVideoVolume Failed!"), MB_OK);
  164.         }
  165.         DbgLog((LOG_ERROR, 0, 
  166.             TEXT("ERROR: RenderDvdVideoVolume() completely failed (Error 0x%x - %s)"), 
  167.             hr, szBuffer)) ;
  168.         return false;
  169.     }
  170.  
  171.     if (S_FALSE == hr) // partial success
  172.     {
  173.         TCHAR szStatusText[1000] ;
  174.         if (0 == GetStatusText(&buildStatus, szStatusText, sizeof(szStatusText)))
  175.         {
  176.             lstrcpy(szStatusText, TEXT("An unknown error has occurred")) ;
  177.         }
  178.         lstrcat(szStatusText, TEXT("\n\nDo you still want to continue?")) ;
  179.         if (IDNO == MessageBox(NULL, szStatusText, TEXT("Warning"), MB_YESNO))
  180.         {
  181.             return false ;
  182.         }
  183.     }
  184.  
  185.     // The graph was successfully rendered in some form if we get this far
  186.     // We will now instantiate all of the necessary interfaces
  187.  
  188.     hr = m_pIDvdGB->GetDvdInterface(IID_IDvdInfo2, reinterpret_cast<void**>(&m_pIDvdI2));
  189.     if (FAILED(hr))
  190.     {
  191.         IDvdInfo  *pDvdI ;
  192.         hr = m_pIDvdGB->GetDvdInterface(IID_IDvdInfo, reinterpret_cast<void**>(&pDvdI)) ;
  193.         if (SUCCEEDED(hr))
  194.         {
  195.             MessageBox(NULL, 
  196.                 TEXT("This version of the application cannot run using\n")
  197.                 TEXT("the current DirectShow components. Please use the\n")
  198.                 TEXT("DVDSampl.exe application."), 
  199.                 TEXT("DVD Error!"), MB_OK) ;
  200.             pDvdI->Release() ;
  201.         }
  202.         else
  203.         {
  204.             MessageBox(NULL, 
  205.                 TEXT("Cannot get the IDvdInfo/IDvdInfo2 interface.")
  206.                 TEXT("Sorry, can't play DVD-Video titles."), 
  207.                 TEXT("DVD Error!"), MB_OK) ;
  208.         }
  209.  
  210.         return false ;
  211.     }
  212.  
  213.     hr = m_pIDvdGB->GetDvdInterface(IID_IDvdControl2, reinterpret_cast<void**>(&m_pIDvdC2));
  214.     if (FAILED(hr))
  215.     {
  216.         IDvdControl  *pDvdC ;
  217.         hr = m_pIDvdGB->GetDvdInterface(IID_IDvdControl, reinterpret_cast<void**>(&pDvdC)) ;
  218.         if (SUCCEEDED(hr))
  219.         {
  220.             MessageBox(NULL, 
  221.                 TEXT("This version of the application cannot run using\n")
  222.                 TEXT("the current DirectShow components. Please use the\n")
  223.                 TEXT("DVDSampl.exe application."), 
  224.                 TEXT("DVD Error!"), MB_OK) ;
  225.             pDvdC->Release() ;
  226.         }
  227.         else
  228.         {
  229.             MessageBox(NULL, 
  230.                 TEXT("Cannot get the IDvdControl/IDvdControl2 interface.")
  231.                 TEXT("Sorry, can't play DVD-Video titles."), 
  232.                 TEXT("DVD Error!"), MB_OK) ;
  233.         }
  234.  
  235.         return false ;
  236.     }
  237.  
  238.     hr = m_pIDvdGB->GetFiltergraph(&m_pGraph);
  239.     if (FAILED(hr))
  240.     {
  241.         MessageBox(NULL, 
  242.             TEXT("Cannot get the IGraphBuilder interface.")
  243.             TEXT("Sorry, can't play DVD-Video titles."), 
  244.             TEXT("DVD Error!"), MB_OK) ;
  245.  
  246.         return false ;
  247.     }
  248.  
  249.     hr = m_pGraph->QueryInterface(IID_IMediaControl, reinterpret_cast<void**>(&m_pIMC));
  250.     if (FAILED(hr))
  251.     {
  252.         MessageBox(NULL, 
  253.             TEXT("Cannot get the IMediaControl interface.")
  254.             TEXT("Sorry, can't play DVD-Video titles."), 
  255.             TEXT("DVD Error!"), MB_OK) ;
  256.  
  257.         return false ;
  258.     }
  259.  
  260.     hr = m_pGraph->QueryInterface(IID_IMediaEventEx, reinterpret_cast<void**>(&m_pIME)) ;
  261.     if (FAILED(hr))
  262.     {
  263.         MessageBox(NULL, 
  264.             TEXT("Cannot get the MediaEventEx interface.")
  265.             TEXT("Sorry, can't play DVD-Video titles."), 
  266.             TEXT("DVD Error!"), MB_OK) ;
  267.  
  268.         return false ;
  269.     }
  270.  
  271.     hr = m_pIDvdGB->GetDvdInterface(IID_IVideoWindow, reinterpret_cast<void**>(&m_pIVW));
  272.     if (FAILED(hr))
  273.     {
  274.         MessageBox(NULL, 
  275.             TEXT("Cannot get the IVideoWindow interface.")
  276.             TEXT("Sorry, can't play DVD-Video titles."), 
  277.             TEXT("DVD Error!"), MB_OK) ;
  278.  
  279.         return false ;
  280.     }
  281.     
  282.     // this one may or may not be present
  283.     m_pIDvdGB->GetDvdInterface(IID_IAMLine21Decoder, reinterpret_cast<void**>(&m_pIL21Dec));
  284.     
  285.  
  286.     SetState(Graph_Stopped2);
  287.     return true;
  288. }
  289.  
  290.  
  291. //------------------------------------------------------------------------------
  292. // Name: CDvdCore::ReleaseInterfaces()
  293. // Desc: This method releases all of our COM interfaces
  294. //       This will be called before building a graph and during destruction of the class
  295. //------------------------------------------------------------------------------
  296.  
  297. void CDvdCore::ReleaseInterfaces()
  298. {
  299.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::ReleaseInterfaces()"))) ;
  300.  
  301.     if (NULL != m_pGraph)
  302.     {
  303.         m_pGraph->Release();
  304.         m_pGraph = NULL;
  305.     }
  306.  
  307.     if (NULL != m_pIDvdC2)
  308.     {
  309.         m_pIDvdC2->Release();
  310.         m_pIDvdC2 = NULL;
  311.     }
  312.  
  313.     if (NULL != m_pIDvdGB)
  314.     {
  315.         m_pIDvdGB->Release();
  316.         m_pIDvdGB = NULL;
  317.     }
  318.  
  319.     if (NULL != m_pIDvdI2)
  320.     {
  321.         m_pIDvdI2->Release();
  322.         m_pIDvdI2 = NULL;
  323.     }
  324.  
  325.     if (NULL != m_pIL21Dec)
  326.     {
  327.         m_pIL21Dec->Release();
  328.         m_pIL21Dec = NULL;
  329.     }
  330.  
  331.     if (NULL != m_pIMC)
  332.     {
  333.         m_pIMC->Release();
  334.         m_pIMC = NULL;
  335.     }
  336.  
  337.     if (NULL != m_pIME)
  338.     {
  339.         m_pIME->SetNotifyWindow(NULL, WM_DVD_EVENT, 0); // we don't want messages any more
  340.         m_pIME->Release();
  341.         m_pIME = NULL;
  342.     }
  343.  
  344.     if (NULL != m_pIVW)
  345.     {
  346.         m_pIVW->put_Owner(NULL); // make sure we send messages back to the right place
  347.         m_pIVW->Release();
  348.         m_pIVW = NULL;
  349.     }
  350.  
  351.     SetState(Uninitialized);
  352. }
  353.  
  354.  
  355. //------------------------------------------------------------------------------
  356. // Name: CDvdCore::Init()
  357. // Desc: This method initializes the DVD Playback Core.  It provides a means to 
  358. //       divide the work among several functions.
  359. //------------------------------------------------------------------------------
  360.  
  361. bool CDvdCore::Init()
  362. {
  363.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::Init()"))) ;
  364.  
  365.     if (false == BuildGraph())
  366.     {
  367.         ReleaseInterfaces();
  368.         return false;
  369.     }
  370.     if (false == InitMessageSink())
  371.         return false;
  372.     return SetPlaybackOptions();
  373. }
  374.  
  375.  
  376. //------------------------------------------------------------------------------
  377. // Name: CDvdCore::GetStatusText()
  378. // Desc: This method parses AM_DVD_RENDERSTATUS and returns a text description of the error
  379. //------------------------------------------------------------------------------
  380.  
  381. DWORD CDvdCore::GetStatusText(AM_DVD_RENDERSTATUS *pStatus, PTSTR pszStatusText, DWORD dwMaxText)
  382. {
  383.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::GetStatusText()"))) ;
  384.     
  385.     TCHAR szBuffer[1000] ;
  386.     
  387.     if (IsBadWritePtr(pszStatusText, sizeof(*pszStatusText) * dwMaxText))
  388.     {
  389.         DbgLog((LOG_ERROR, 0, TEXT("GetStatusText(): bad text buffer param"))) ;
  390.         return 0 ;
  391.     }
  392.     
  393.     int iChars ;
  394.     PTSTR pszBuff = szBuffer ;
  395.     ZeroMemory(szBuffer, sizeof(TCHAR) * 1000) ;
  396.     if (pStatus->iNumStreamsFailed > 0)
  397.     {
  398.         iChars = wsprintf(pszBuff, 
  399.             TEXT("* %d out of %d DVD-Video streams failed to render properly\n"), 
  400.             pStatus->iNumStreamsFailed, pStatus->iNumStreams) ;
  401.         pszBuff += iChars ;
  402.         
  403.         if (pStatus->dwFailedStreamsFlag & AM_DVD_STREAM_VIDEO)
  404.         {
  405.             iChars = wsprintf(pszBuff, TEXT("    - video stream\n")) ;
  406.             pszBuff += iChars ;
  407.         }
  408.         if (pStatus->dwFailedStreamsFlag & AM_DVD_STREAM_AUDIO)
  409.         {
  410.             iChars = wsprintf(pszBuff, TEXT("    - audio stream\n")) ;
  411.             pszBuff += iChars ;
  412.         }
  413.         if (pStatus->dwFailedStreamsFlag & AM_DVD_STREAM_SUBPIC)
  414.         {
  415.             iChars = wsprintf(pszBuff, TEXT("    - subpicture stream\n")) ;
  416.             pszBuff += iChars ;
  417.         }
  418.     }
  419.     
  420.     if (FAILED(pStatus->hrVPEStatus))
  421.     {
  422.         lstrcat(pszBuff, TEXT("* ")) ;
  423.         pszBuff += lstrlen(TEXT("* ")) ;
  424.         iChars = AMGetErrorText(pStatus->hrVPEStatus, pszBuff, 200) ;
  425.         pszBuff += iChars ;
  426.         lstrcat(pszBuff, TEXT("\n")) ;
  427.         pszBuff += lstrlen(TEXT("\n")) ;
  428.     }
  429.     
  430.     if (pStatus->bDvdVolInvalid)
  431.     {
  432.         iChars = wsprintf(pszBuff, TEXT("* Specified DVD-Video volume was invalid\n")) ;
  433.         pszBuff += iChars ;
  434.     }
  435.     else if (pStatus->bDvdVolUnknown)
  436.     {
  437.         iChars = wsprintf(pszBuff, TEXT("* No valid DVD-Video volume could be located\n")) ;
  438.         pszBuff += iChars ;
  439.     }
  440.     
  441.     if (pStatus->bNoLine21In)
  442.     {
  443.         iChars = wsprintf(pszBuff, TEXT("* The video decoder doesn't produce closed caption data\n")) ;
  444.         pszBuff += iChars ;
  445.     }
  446.     if (pStatus->bNoLine21Out)
  447.     {
  448.         iChars = wsprintf(pszBuff, TEXT("* Decoded closed caption data not rendered properly\n")) ;
  449.         pszBuff += iChars ;
  450.     }
  451.     
  452.     DWORD dwLength = (pszBuff - szBuffer) * sizeof(*pszBuff) ;
  453.     dwLength = min(dwLength, dwMaxText) ;
  454.     lstrcpyn(pszStatusText, szBuffer, dwLength) ;
  455.     
  456.     return dwLength ;
  457.  
  458. }
  459.  
  460.  
  461. //------------------------------------------------------------------------------
  462. // Name: CDvdCore::Play()
  463. // Desc: This method begins DVD Playback
  464. //------------------------------------------------------------------------------
  465.  
  466. HRESULT CDvdCore::Play()
  467. {
  468.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::Play()"))) ;
  469.  
  470.     if (true == m_bFirstPlay)
  471.     {
  472.         // now show the playback window
  473.         ShowWindow(m_hWnd, SW_SHOWNORMAL);
  474.         UpdateWindow(m_hWnd) ;
  475.         m_bFirstPlay = false;
  476.     }
  477.  
  478.     HRESULT hr;
  479.  
  480.     switch (m_eState)
  481.     {
  482.     case Uninitialized:
  483.         return E_FAIL;
  484.  
  485.     case Graph_Stopped2:
  486.         hr = m_pIMC->Run();
  487.         m_pIDvdC2->PlayForwards(1.0, 0, NULL); // this call will likely fail due to timing issues.  Ignore it.
  488.         if (SUCCEEDED(hr))
  489.             SetState(Playing);
  490.         return hr;
  491.  
  492.     case Nav_Stopped:
  493.     case Graph_Stopped1:
  494.         //hr = m_pIDvdC2->PlayTitle(1, DVD_CMD_FLAG_None, NULL); // play title 1
  495.         hr = m_pIMC->Run();
  496.         if (SUCCEEDED(hr))
  497.             SetState(Playing);
  498.         return hr; 
  499.  
  500.     case Graph_Paused:
  501.         m_pIDvdC2->PlayForwards(1.0, 0, NULL); 
  502.         hr = m_pIMC->Run();
  503.         if (SUCCEEDED(hr))
  504.             SetState(Playing);
  505.         return hr;
  506.  
  507.     case Nav_Paused:
  508.         m_pIDvdC2->PlayForwards(1.0, 0, NULL); 
  509.         hr = m_pIDvdC2->Pause(FALSE);
  510.         if (SUCCEEDED(hr))
  511.             SetState(Playing);
  512.         return hr;
  513.  
  514.     case Playing:
  515.         if (true == m_bStillOn) // we are at a still without buttons
  516.         {
  517.             hr = m_pIDvdC2->StillOff();
  518.             if (SUCCEEDED(hr))
  519.                 m_bStillOn = false;
  520.         }
  521.         return hr;
  522.         
  523.     case Scanning:
  524.         hr = m_pIDvdC2->PlayForwards(1.0, 0, NULL);
  525.         if (SUCCEEDED(hr))
  526.             SetState(Playing);
  527.         return hr;
  528.     }
  529.  
  530.     return S_OK;
  531. }
  532.  
  533.  
  534. //------------------------------------------------------------------------------
  535. // Name: CDvdCore::InitMessageSink()
  536. // Desc: This method creates a parent window and initializes sinks for mouse 
  537. //       messages and DVD events.
  538. //       We create a new window instead of just using the IVideoWindow window is
  539. //       that we lose the ability to resize the Video Window once we set up a 
  540. //       message drain.  The message drain is necessary to handle mouse events for
  541. //       the menus but causes the automatic resizing and movement of the original 
  542. //       window to stop working.
  543. //------------------------------------------------------------------------------
  544.  
  545. bool CDvdCore::InitMessageSink()
  546. {
  547.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::InitMessageSink()"))) ;
  548.  
  549.     // register playback window
  550.     WNDCLASSEX wndclass = {
  551.         sizeof(WNDCLASSEX),
  552.         CS_HREDRAW | CS_VREDRAW,
  553.         CDvdCore::WndProc,
  554.         0,
  555.         0,
  556.         m_hInstance,
  557.         LoadIcon(m_hInstance, MAKEINTRESOURCE(IDI_APPICON)),
  558.         LoadCursor(NULL, IDC_ARROW),
  559.         HBRUSH(COLOR_WINDOW+1),
  560.         NULL,
  561.         TEXT("EventWindow"),
  562.         NULL
  563.     };
  564.  
  565.     RegisterClassEx(&wndclass);
  566.  
  567.     // create the playback window
  568.  
  569.     RECT rPlayback = GetPlaybackWindowRect();
  570.  
  571.     m_hWnd = CreateWindow(
  572.         TEXT("EventWindow"),
  573.         TEXT("Video Window"),
  574.         WS_OVERLAPPEDWINDOW,
  575.         rPlayback.left, 
  576.         rPlayback.top, 
  577.         rPlayback.right - rPlayback.left, 
  578.         rPlayback.bottom - rPlayback.top,
  579.         NULL,
  580.         NULL,
  581.         m_hInstance,
  582.         this); // we send the this pointer here so we can retrieve it from WM_CREATE later
  583.  
  584.     // store the pointer to this instance of CDvdCore.
  585.     // this is so we can refer to it in the OnDvdEvent message loop
  586.     // For win64 compatibily, use SetWindowLongPtr...
  587.     //SetWindowLongPtr(m_hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
  588.     SetWindowLong(m_hWnd, GWL_USERDATA, reinterpret_cast<long>(this));
  589.  
  590.     // set up the event notification so that the dummy window gets
  591.     // informed about DVD events about during playback.
  592.     HRESULT hr;
  593.     hr = m_pIME->SetNotifyWindow(reinterpret_cast<OAHWND>(m_hWnd), WM_DVD_EVENT, 0);
  594.     ASSERT(SUCCEEDED(hr));
  595.  
  596.     // Make the video window a child of the playback window.  We do this so we can trap
  597.     // mouse events and still be able to manipulate the window.
  598.     long lStyle;
  599.     m_pIVW->get_WindowStyle(&lStyle);
  600.     m_pIVW->put_WindowStyle( (lStyle | WS_CHILD) & ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME) );
  601.     m_pIVW->put_Owner(reinterpret_cast<OAHWND>(m_hWnd));
  602.     RECT rect;
  603.     GetClientRect(m_hWnd, &rect);
  604.     m_pIVW->SetWindowPosition(0, 0, rect.right, rect.bottom);
  605.  
  606.     // This will cause our playback window to get mouse and keyboard events from the
  607.     // Video Window
  608.     hr = m_pIVW->put_MessageDrain(reinterpret_cast<OAHWND>(m_hWnd)) ;
  609.     ASSERT(SUCCEEDED(hr)) ;
  610.  
  611.     m_bMessageSink = true; // flag so we will turn this off later
  612.  
  613.     return true;
  614. }
  615.  
  616.  
  617. //------------------------------------------------------------------------------
  618. // Name: CDvdCore::WndProc()
  619. // Desc: This method is a message loop designed to handle mouse, key, and Dvd event messages
  620. //       Because there is no 'this' pointer in a static method, we do the actual work
  621. //       in CDvdCore non-static methods.
  622. //------------------------------------------------------------------------------
  623.  
  624. LRESULT CALLBACK CDvdCore::WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
  625. {
  626.     // retrieve the pointer to the instance of CDvdCore that this window belongs to
  627.     // because OnDvdEvent is a static method, it doesn't have a this pointer
  628.     CDvdCore * pCore = NULL;
  629.     // For Win64 compatibility, use GetWindowLongPtr (Platform SDK)
  630.     //pCore = reinterpret_cast<CDvdCore *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
  631.     pCore = reinterpret_cast<CDvdCore *>(GetWindowLong(hWnd, GWL_USERDATA));
  632.  
  633.     if (NULL == pCore)
  634.     // this is really only necessary if you want to react to WM_CREATE messages
  635.     {
  636.         if (WM_CREATE == uMessage)
  637.             // WM_CREATE messages return the last parameter to CreateWindow()
  638.         {
  639.             CREATESTRUCT * pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
  640.             pCore = reinterpret_cast<CDvdCore *>(pCreate->lpCreateParams);
  641.         }
  642.         else return DefWindowProc(hWnd, uMessage, wParam, lParam);
  643.     }
  644.             
  645.  
  646.     switch (uMessage)
  647.     {
  648.     
  649.     case WM_TIMER:
  650.         return pCore->OnMouseTimer(wParam, lParam);
  651.  
  652.     case WM_SIZE:
  653.         return pCore->OnSize(wParam, lParam);
  654.  
  655.     case WM_KEYUP:
  656.         return pCore->OnKeyEvent(wParam, lParam);
  657.  
  658.     case WM_DVD_EVENT:
  659.         return pCore->OnDvdEvent(uMessage, wParam, lParam);
  660.  
  661.     case WM_MOUSEMOVE:
  662.     case WM_LBUTTONUP:
  663.     case WM_RBUTTONUP:
  664.     case WM_MBUTTONUP:
  665.     //case WM_MOUSEWHEEL:
  666.         return pCore->OnMouseEvent(uMessage, wParam, lParam);
  667.  
  668.     case WM_CLOSE:
  669.         return pCore->OnClose();
  670.     }
  671.     return DefWindowProc(hWnd, uMessage, wParam, lParam);
  672. }
  673.  
  674.  
  675. //------------------------------------------------------------------------------
  676. // Name: CDvdCore::SetPlaybackOptions()
  677. // Desc: This method sets our default line21, ResetOnStop, Parental controls, etc.
  678. //------------------------------------------------------------------------------
  679.  
  680. bool CDvdCore::SetPlaybackOptions()
  681. {
  682.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::SetPlaybackOptions()"))) ;
  683.  
  684.     HRESULT hr;
  685.  
  686.     if (m_pIL21Dec)
  687.     {
  688.         hr = m_pIL21Dec->SetServiceState(AM_L21_CCSTATE_Off) ; // Line21 off by default
  689.         if (FAILED(hr))
  690.             return false;
  691.     }
  692.     else
  693.     {
  694.         DbgLog((LOG_TRACE, 2, TEXT("Line21 Decoder interface not found. Can't CC"))) ;
  695.     }
  696.  
  697.     hr = m_pIDvdC2->SetOption(DVD_ResetOnStop, FALSE); // don't reset on stop
  698.     if (FAILED(hr))
  699.         return false;
  700.  
  701.     hr = m_pIDvdC2->SetOption(DVD_NotifyParentalLevelChange, TRUE); // notify us when parental level changes
  702.     if (FAILED(hr))
  703.         return false;
  704.  
  705.     hr = m_pIDvdC2->SetOption(DVD_HMSF_TimeCodeEvents, TRUE); // use new HMSF timecode format
  706.     if (FAILED(hr))
  707.         return false;
  708.  
  709.     return true;
  710. }
  711.  
  712.  
  713. //------------------------------------------------------------------------------
  714. // Name: CDvdCore::NextChapter()
  715. // Desc: This method is a wrapper for the PlayNextChapter method.
  716. //       It is not necessary to use the IDvdCmd->WaitForEnd() method here.  It is 
  717. //       done merely to demonstrate how to use this blocking method.
  718. //------------------------------------------------------------------------------
  719.  
  720. bool CDvdCore::NextChapter()
  721. {
  722.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::NextChapter()"))) ;
  723.  
  724.     if (Uninitialized == m_eState)
  725.         return false;
  726.  
  727.     if (Playing == m_eState)
  728.     {
  729.         HRESULT hr;
  730.         IDvdCmd * cmdObj;
  731.         hr = m_pIDvdC2->PlayNextChapter(DVD_CMD_FLAG_None, &cmdObj);
  732.         if (FAILED(hr))
  733.             return false;
  734.         cmdObj->WaitForEnd();
  735.         cmdObj->Release();
  736.         if (SUCCEEDED(hr))
  737.             return true;
  738.     }
  739.     return false;
  740. }
  741.  
  742.  
  743. //------------------------------------------------------------------------------
  744. // Name: CDvdCore::PrevChapter()
  745. // Desc: This method is a wrapper for the PlayPrevChapter method.
  746. //       It is not necessary to use the DVD_CMD_FLAG_Block flag.  It is done
  747. //       merely to demonstrate how to use this blocking method.
  748. //------------------------------------------------------------------------------
  749.  
  750. bool CDvdCore::PrevChapter()
  751. {
  752.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::PrevChapter()"))) ;
  753.  
  754.     if (Uninitialized == m_eState)
  755.         return false;
  756.  
  757.     if (Playing == m_eState)
  758.     {
  759.         HRESULT hr;
  760.         hr = m_pIDvdC2->PlayPrevChapter(DVD_CMD_FLAG_Block, NULL);
  761.         if (SUCCEEDED(hr))
  762.             return true;
  763.     }
  764.     return false;
  765.  
  766. }
  767.  
  768.  
  769. //------------------------------------------------------------------------------
  770. // Name: CDvdCore::Stop()
  771. // Desc: This method merely stops playback.  We use IMediaControl->Stop rather than
  772. //       IDvdControl2->Stop because it it easier to recover from.
  773. //------------------------------------------------------------------------------
  774.  
  775. bool CDvdCore::Stop()
  776. {
  777.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::Stop()"))) ;
  778.     HRESULT hr;
  779.  
  780.     switch (m_eState)
  781.     {
  782.     case Uninitialized:
  783.         return false;
  784.  
  785.     case Graph_Stopped2:
  786.         hr = m_pIDvdC2->SetOption(DVD_ResetOnStop, TRUE); // do a complete reset
  787.         hr = m_pIMC->Stop(); // just to be sure;
  788.         hr = m_pIDvdC2->SetOption(DVD_ResetOnStop, FALSE); // restore previous settings
  789.         break;
  790.  
  791.     case Playing:
  792.     case Scanning:
  793.     case Nav_Paused:
  794.     case Graph_Paused:
  795.         //hr = m_pIDvdC2->Stop(); // put us in stop domain
  796.         hr = m_pIMC->Pause();
  797.         SetState(Graph_Stopped1);
  798.         break;
  799.  
  800.     case Nav_Stopped:
  801.     case Graph_Stopped1:
  802.         hr = m_pIDvdC2->SetOption(DVD_ResetOnStop, TRUE); // do a complete reset
  803.         hr = m_pIMC->Stop(); // stop the entire graph
  804.         hr = m_pIDvdC2->SetOption(DVD_ResetOnStop, FALSE); // restore previous settings
  805.         SetState(Graph_Stopped2);
  806.         break;
  807.     }
  808.     return true;
  809. }
  810.  
  811.  
  812. //------------------------------------------------------------------------------
  813. // Name: CDvdCore::Rewind()
  814. // Desc: This method begins fast forwarding the disc.  It only tries if the disc
  815. //       is playing or FF/RW'ing already.
  816. //------------------------------------------------------------------------------
  817.  
  818. bool CDvdCore::Rewind()
  819. {
  820.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::Rewind()"))) ;
  821.  
  822.     if (Uninitialized == m_eState)
  823.         return false;
  824.  
  825.     if (Playing == m_eState || Scanning == m_eState)
  826.     {
  827.         if (FAILED(m_pIDvdC2->PlayBackwards(8.0, 0, NULL)))
  828.             return false;
  829.         SetState(Scanning);
  830.         return true;
  831.     }
  832.     return false;
  833. }
  834.  
  835. //------------------------------------------------------------------------------
  836. // Name: CDvdCore::FastForward()
  837. // Desc: This method begins rewinding the disc.  It only tries if the disc
  838. //       is playing or FF/RW'ing already.
  839. //------------------------------------------------------------------------------
  840.  
  841. bool CDvdCore::FastForward()
  842. {
  843.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::FastForward()"))) ;
  844.  
  845.     if (Uninitialized == m_eState)
  846.         return false;
  847.  
  848.     if (Playing == m_eState || Scanning == m_eState)
  849.     {
  850.         if (FAILED(m_pIDvdC2->PlayForwards(8.0, 0, NULL)))
  851.             return false;
  852.         SetState(Scanning);
  853.         return true;
  854.     }
  855.     return false;
  856. }
  857.  
  858.  
  859. //------------------------------------------------------------------------------
  860. // Name: CDvdCore::Pause()
  861. // Desc: This method pauses if we aren't already paused and unpauses if we are.
  862. //------------------------------------------------------------------------------
  863.  
  864. bool CDvdCore::Pause(void)
  865. {
  866.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::Pause()"))) ;
  867.  
  868.     if (Uninitialized == m_eState)
  869.         return false;
  870.  
  871.     // if we are paused at the graph level, call run on the graph
  872.     if (Graph_Paused == m_eState)
  873.     {
  874.         if (FAILED(m_pIMC->Run()))
  875.             return false;
  876.         else
  877.         {
  878.             SetState(Playing);
  879.             return true;
  880.         }
  881.     }
  882.     // if we are paused at the Navigator level, call Pause(false)
  883.     if (Nav_Paused == m_eState)
  884.     {
  885.         if (FAILED(m_pIDvdC2->Pause(false)))
  886.             return false;
  887.         else
  888.         {
  889.             SetState(Playing);
  890.             return true;
  891.         }
  892.     }
  893.     // Anything else, we try to pause at the Nav Level
  894.     // this will fail if we are stopped.
  895.     if (FAILED(m_pIDvdC2->Pause(TRUE)))
  896.         return false;
  897.     else
  898.     {
  899.         SetState(Nav_Paused);
  900.         return true;
  901.     }
  902. }
  903.  
  904.  
  905. //------------------------------------------------------------------------------
  906. // Name: CDvdCore::RootMenu()
  907. // Desc: This method will either bring us into the root menu or call resume if 
  908. //       we are already in a menu.  The one caveat is that if we are in the title menu
  909. //       we will resume instead of going to the root menu.
  910. //------------------------------------------------------------------------------
  911.  
  912. bool CDvdCore::RootMenu()
  913. {
  914.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::RootMenu()"))) ;
  915.  
  916.     if (Uninitialized == m_eState)
  917.         return false;
  918.  
  919.     if (true == m_bMenuOn)
  920.     {
  921.         // we use flush here merely to show how it might be used.  You can call Resume without it.
  922.         if (FAILED(m_pIDvdC2->Resume(DVD_CMD_FLAG_Flush, NULL)))
  923.             return false;
  924.         else
  925.         {
  926.             m_bMenuOn = false;
  927.             SetState(Playing);
  928.             return true;
  929.         }
  930.     }
  931.     else if (Playing == m_eState || Scanning == m_eState)
  932.     {
  933.         IDvdCmd * pObj;
  934.         // it is a good idea to block on Menu commands.  This is just one way to do so.
  935.         if (FAILED(m_pIDvdC2->ShowMenu(DVD_MENU_Root, DVD_CMD_FLAG_Flush, &pObj)))
  936.         {
  937.             return false;
  938.         }
  939.         else
  940.         {
  941.             pObj->WaitForEnd();
  942.             pObj->Release();
  943.             m_bMenuOn = true;
  944.             SetState(Playing);
  945.             return true;
  946.         }
  947.     }
  948.     return false; // we can't go to a menu from a stop or paused state
  949. }
  950.  
  951.  
  952. //------------------------------------------------------------------------------
  953. // Name: CDvdCore::EnableFullScreen()
  954. // Desc: This method will enter and exit fullscreen mode depending on the bool 
  955. //       value.  We don't use IVideoWindow->putFullScreenMode because
  956. //       it won't handle the mouse correctly.
  957. //------------------------------------------------------------------------------
  958.  
  959. bool CDvdCore::ToggleFullScreen()
  960. {
  961.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::ToggleFullScreen()"))) ;
  962.  
  963.     if (false == m_bFullScreenOn) 
  964.     {
  965.         return StartFullScreen();
  966.     }
  967.  
  968.     else 
  969.         return StopFullScreen();
  970. }
  971.  
  972.  
  973. //------------------------------------------------------------------------------
  974. // Name: CDvdCore::TitleMenu()
  975. // Desc: This method will either bring us into the root menu or call resume if 
  976. //       we are already in a menu.  The one caveat is that if we are in the root menu
  977. //       we will resume instead of going to the title menu.
  978. //------------------------------------------------------------------------------
  979.  
  980. bool CDvdCore::TitleMenu()
  981. {
  982.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::TitleMenu()"))) ;
  983.  
  984.     if (Uninitialized == m_eState)
  985.         return false;
  986.  
  987.     if (true == m_bMenuOn)
  988.     {
  989.         // we use flush here merely to show how it might be used.  You can call Resume without it.
  990.         if (FAILED(m_pIDvdC2->Resume(DVD_CMD_FLAG_Flush, NULL)))
  991.             return false;
  992.         else
  993.         {
  994.             SetState(Playing);
  995.             m_bMenuOn = false;
  996.             return true;
  997.         }
  998.     }
  999.     else if (Playing == m_eState || Scanning == m_eState)
  1000.     {
  1001.         IDvdCmd * pObj;
  1002.         // it is a good idea to block on Menu commands.  This is just one way to do so.
  1003.         HRESULT hr = m_pIDvdC2->ShowMenu(DVD_MENU_Title, DVD_CMD_FLAG_Flush, &pObj);
  1004.         if (FAILED(hr))
  1005.         {
  1006.             if (VFW_E_DVD_OPERATION_INHIBITED == hr)
  1007.                 m_pCallback->Prohibited();
  1008.             DbgLog((LOG_ERROR, 1, TEXT("CDvdCore::TitleMenu() failed: %#x"), hr)) ;
  1009.             return false;
  1010.         }
  1011.         else
  1012.         {
  1013.             pObj->WaitForEnd();
  1014.             pObj->Release();
  1015.             SetState(Playing);
  1016.             m_bMenuOn = true;
  1017.             return true;
  1018.         }
  1019.     }
  1020.     return false; // we can't go to a menu from a stop or paused state
  1021. }
  1022.  
  1023.  
  1024. //------------------------------------------------------------------------------
  1025. // Name: CDvdCore::EnableCaptions()
  1026. // Desc: This method turns on or off closed captions (Line 21)
  1027. //------------------------------------------------------------------------------
  1028.  
  1029. bool CDvdCore::EnableCaptions(bool bOn)
  1030. {
  1031.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::EnableCaptions(%s)"), bOn ? TEXT("True") : 
  1032.         TEXT("False"))) ;
  1033.  
  1034.     if (NULL == m_pIL21Dec) // if this decoder doesn't support Line 21
  1035.         return false;
  1036.  
  1037.     if (SUCCEEDED(m_pIL21Dec->SetServiceState(bOn ? AM_L21_CCSTATE_On : AM_L21_CCSTATE_Off)))
  1038.         return true;
  1039.     else return false;
  1040. }
  1041.  
  1042.  
  1043. //------------------------------------------------------------------------------
  1044. // Name: CDvdCore::SetVideoWindowTitle()
  1045. // Desc: This method sets the Title of the IVideoWindow window
  1046. //------------------------------------------------------------------------------
  1047.  
  1048. bool CDvdCore::SetVideoWindowTitle(TCHAR *pszTitle)
  1049. {
  1050.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::SetVideoWindowTitle()"))) ;
  1051.  
  1052.         if (TRUE == SetWindowText(m_hWnd, pszTitle))
  1053.         return true;
  1054.     else return false;
  1055. }
  1056.  
  1057.  
  1058. //------------------------------------------------------------------------------
  1059. // Name: CDvdCore::SaveBookmark()
  1060. // Desc: This method saves a bookmark on the DVD to the hard drive.
  1061. //------------------------------------------------------------------------------
  1062.  
  1063. bool CDvdCore::SaveBookmark()
  1064. {
  1065.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::SaveBookmark()"))) ;
  1066.  
  1067.     // we begin by getting a copy of the DVD Navigator's internal state
  1068.     HRESULT hr;
  1069.     IDvdState * pBookmark;
  1070.     hr = m_pIDvdI2->GetState (&pBookmark);
  1071.     if (FAILED(hr))
  1072.         return false;
  1073.  
  1074.     // next we serialize (persist) the data to the disc.  
  1075.     // This always saves to the same bookmark file.  It could just as easily 
  1076.     // save to a different one each time.
  1077.     // 
  1078.     // We could also serialize the data to memory using IPersistMemory if we
  1079.     // don't want to persist across different executions of the program.
  1080.     IStorage * pStorage;
  1081.     IStream * pStream;
  1082.     bool bCreateStream = false;
  1083.     // first try to open an existing file
  1084.     hr = StgOpenStorage(g_szBookmarkFilename, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 
  1085.         NULL, 0, &pStorage);
  1086.     if (SUCCEEDED(hr)) 
  1087.     {
  1088.         // we then open a stream object within that file
  1089.         hr = pStorage->OpenStream(g_szBookmarkName, NULL, STGM_WRITE |
  1090.             STGM_SHARE_EXCLUSIVE, 0, &pStream);
  1091.         if (FAILED(hr))
  1092.         {
  1093.             pBookmark->Release();
  1094.             pStorage->Release();
  1095.             return false;
  1096.         }
  1097.     }
  1098.     else // if that fails, try to create a new file
  1099.     {   
  1100.         hr = StgCreateDocfile(g_szBookmarkFilename, STGM_CREATE | STGM_WRITE | 
  1101.             STGM_SHARE_EXCLUSIVE, 0, &pStorage);
  1102.         if (FAILED(hr))
  1103.         {
  1104.             pBookmark->Release();
  1105.             return false;
  1106.         }
  1107.         // we then create a stream object within that file
  1108.         hr = pStorage->CreateStream(g_szBookmarkName, STGM_CREATE | STGM_WRITE |
  1109.             STGM_SHARE_EXCLUSIVE, 0, 0, &pStream);
  1110.         if (FAILED(hr))
  1111.         {
  1112.             pBookmark->Release();
  1113.             pStorage->Release();
  1114.             return false;
  1115.         }
  1116.     }
  1117.    
  1118.     // now we tell the bookmark to persist itself into the stream we just created
  1119.     IPersistStream * pPersistStream;
  1120.     hr = pBookmark->QueryInterface(IID_IPersistStream, reinterpret_cast<void**>(&pPersistStream));
  1121.     if (SUCCEEDED(hr))
  1122.     {
  1123.         hr = OleSaveToStream(pPersistStream, pStream);
  1124.         pPersistStream->Release();
  1125.         pStorage->Release();
  1126.         pStream->Release();
  1127.         pBookmark->Release();
  1128.  
  1129.         if (SUCCEEDED(hr))
  1130.             return true;
  1131.         else 
  1132.             return false;
  1133.     }
  1134.     else
  1135.     {
  1136.         pBookmark->Release();
  1137.         pStorage->Release();
  1138.         pStream->Release();
  1139.         return false;
  1140.     }
  1141. }
  1142.  
  1143.  
  1144. //------------------------------------------------------------------------------
  1145. // Name: CDvdCore::RestoreBookmark()
  1146. // Desc: This method reads a bookmark file from the disc and restores playback
  1147. //       at that location.
  1148. //------------------------------------------------------------------------------
  1149.  
  1150. bool CDvdCore::RestoreBookmark()
  1151. {
  1152.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::RestoreBookmark()"))) ;
  1153.  
  1154.     // Begin by opening the bookmark file on the drive.
  1155.     IStorage * pStorage;
  1156.     HRESULT hr = StgOpenStorage(g_szBookmarkFilename, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE,
  1157.         NULL, 0, &pStorage);
  1158.     if (FAILED(hr))
  1159.         return false;
  1160.  
  1161.     // open a stream object within that file
  1162.     IStream * pStream;
  1163.     hr = pStorage->OpenStream(g_szBookmarkName, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0,
  1164.         &pStream);
  1165.     if (FAILED(hr))
  1166.     {
  1167.         pStorage->Release();
  1168.         return false;
  1169.     }
  1170.  
  1171.     // load the bookmark in from the stream
  1172.     IDvdState * pBookmark;
  1173.     hr = OleLoadFromStream(pStream, IID_IDvdState, reinterpret_cast<void**>(&pBookmark));
  1174.     pStream->Release();
  1175.     pStorage->Release();
  1176.     if (FAILED(hr))
  1177.         return false;
  1178.  
  1179.     // restore bookmark's state in the Navigator
  1180.     hr = m_pIDvdC2->SetState(pBookmark, DVD_CMD_FLAG_Block, NULL);
  1181.     pBookmark->Release();
  1182.  
  1183.     if (SUCCEEDED(hr))
  1184.         return true;
  1185.     else
  1186.         return false;
  1187. }
  1188.  
  1189.  
  1190. //------------------------------------------------------------------------------
  1191. // Name: CDvdCore::StartFullScreen()
  1192. // Desc: This method begins fullscreen playback.  It saves the original window
  1193. //       information, then stretches the window full screen.  We use this rather
  1194. //       than IVideoWindow::put_FullScreen() because we have greater control over
  1195. //       the mouse this way and can show and hide it at will.
  1196. //------------------------------------------------------------------------------
  1197.  
  1198. bool CDvdCore::StartFullScreen()
  1199. {
  1200.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::StartFullScreen()"))) ;
  1201.     
  1202.     if (NULL == m_pIVW)
  1203.     {
  1204.         DbgLog((LOG_ERROR, 0, 
  1205.             TEXT("CDvdCore::StartFullScreen() -- no IVideoWindow pointer yet"))) ;
  1206.         return false ;
  1207.     }
  1208.     
  1209.     HRESULT   hr ;
  1210.  
  1211.     // store the original window position
  1212.     LONG  lLeft, lTop, lWidth, lHeight ;
  1213.     hr = m_pIVW->GetWindowPosition(&lLeft, &lTop, &lWidth, &lHeight) ;
  1214.     ASSERT(SUCCEEDED(hr)) ;
  1215.     SetRect(&m_RectOrigVideo, lLeft, lTop, lLeft + lWidth, lTop + lHeight) ;
  1216.     
  1217.     // save the original window style
  1218.     hr = m_pIVW->get_WindowStyle(&m_lOrigStyle) ;
  1219.     ASSERT(SUCCEEDED(hr)) ;
  1220.     hr = m_pIVW->get_WindowStyleEx(&m_lOrigStyleEx) ;
  1221.     ASSERT(SUCCEEDED(hr)) ;
  1222.  
  1223.     // the window can't be a child while it is full screen or it can't be stretched beyond the 
  1224.     // parent window's borders
  1225.     hr = m_pIVW->put_Owner(NULL);
  1226.     ASSERT(SUCCEEDED(hr)) ;
  1227.  
  1228.     // modify the window's style
  1229.     hr = m_pIVW->put_WindowStyle(m_lOrigStyle & 
  1230.         ~(WS_BORDER | WS_CAPTION | WS_THICKFRAME)) ;  // remove these styles
  1231.     ASSERT(SUCCEEDED(hr)) ;
  1232.     hr = m_pIVW->put_WindowStyleEx(m_lOrigStyleEx & 
  1233.         ~(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_WINDOWEDGE | 
  1234.                     WS_EX_DLGMODALFRAME)  // remove these extended styles
  1235.                     | WS_EX_TOPMOST) ;
  1236.     ASSERT(SUCCEEDED(hr)) ;
  1237.     
  1238.     // stretch the window to the full size of the screen
  1239.     LONG  lScrnWidth  = GetSystemMetrics(SM_CXSCREEN) ;
  1240.     LONG  lScrnHeight = GetSystemMetrics(SM_CYSCREEN) ;
  1241.     hr = m_pIVW->SetWindowPosition(0, 0, lScrnWidth, lScrnHeight) ;
  1242.     ASSERT(SUCCEEDED(hr)) ;
  1243.     
  1244.     // start a timer for mouse blanking
  1245.     m_dwMouseMoveTime = timeGetTime();
  1246.     SetTimer(m_hWnd, MOUSETIMER, 2500, NULL);
  1247.  
  1248.     m_bFullScreenOn = true;
  1249.  
  1250.     return true ;
  1251. }
  1252.  
  1253.  
  1254. //------------------------------------------------------------------------------
  1255. // Name: CDvdCore::StopFullScreen()
  1256. // Desc: This method restores the Video Window to its original size and style.
  1257. //------------------------------------------------------------------------------
  1258.  
  1259. bool CDvdCore::StopFullScreen()
  1260. {
  1261.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::StopFullScreen()"))) ;   
  1262.     
  1263.     if (NULL == m_pIVW)
  1264.     {
  1265.         DbgLog((LOG_ERROR, 0, 
  1266.             TEXT("CDvdCore::StopFullScreen() -- no IVideoWindow pointer yet"))) ;
  1267.         return false ;
  1268.     }
  1269.  
  1270.     HRESULT hr ;
  1271.  
  1272.     // restore the window's position
  1273.     RECT rect;
  1274.     GetClientRect(m_hWnd, &rect);
  1275.     hr = m_pIVW->SetWindowPosition(0, 0, rect.right, rect.bottom);
  1276.     ASSERT(SUCCEEDED(hr)) ;    
  1277.  
  1278.     // make it a child window again
  1279.     hr = m_pIVW->put_Owner(reinterpret_cast<OAHWND>(m_hWnd)) ;  
  1280.     ASSERT(SUCCEEDED(hr)) ;    
  1281.  
  1282.     // restore the window's styles
  1283.     hr = m_pIVW->put_WindowStyle(m_lOrigStyle) ;      // restore the styles
  1284.     ASSERT(SUCCEEDED(hr)) ;
  1285.     hr = m_pIVW->put_WindowStyleEx(m_lOrigStyleEx) ;  // restore the extended styles
  1286.     ASSERT(SUCCEEDED(hr)) ;
  1287.     
  1288.     KillTimer(m_hWnd, MOUSETIMER); // remove the mouse blanking timer
  1289.     ShowMouseCursor(true) ;  // make sure to show mouse now
  1290.  
  1291.     m_bFullScreenOn = false;
  1292.     
  1293.     return true ;
  1294. }
  1295.  
  1296.  
  1297. //------------------------------------------------------------------------------
  1298. // Name: CDvdCore::OnDvdEvent()
  1299. // Desc: This method receives DVD messages from our WndProc and acts on them
  1300. //------------------------------------------------------------------------------
  1301. LRESULT CDvdCore::OnDvdEvent(UINT uMessage, WPARAM wParam, LPARAM lParam)
  1302. {
  1303.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::OnDvdEvent()"))) ;
  1304.  
  1305.     long lEvent, lParam1, lParam2;
  1306.     long lTimeOut = 0;
  1307.  
  1308.     while (SUCCEEDED(m_pIME->GetEvent(&lEvent, &lParam1, &lParam2, lTimeOut)))
  1309.     {
  1310.         DbgLog((LOG_TRACE, 5, TEXT("Event: %#x"), lEvent)) ;
  1311.  
  1312.         HRESULT hr;
  1313.         switch(lEvent)
  1314.         {
  1315.  
  1316.         case EC_DVD_CURRENT_HMSF_TIME:
  1317.         {
  1318.             DVD_HMSF_TIMECODE * pTC = reinterpret_cast<DVD_HMSF_TIMECODE *>(&lParam1);
  1319.             m_CurTime = *pTC;
  1320.             m_pCallback->UpdateStatus(); // inform our client that something changed
  1321.         }
  1322.         break;
  1323.  
  1324.         case EC_DVD_CHAPTER_START:
  1325.             m_ulCurChapter = lParam1;
  1326.             break;
  1327.  
  1328.         case EC_DVD_TITLE_CHANGE:
  1329.             m_ulCurTitle = lParam1;
  1330.             break;
  1331.  
  1332.         case EC_DVD_NO_FP_PGC:
  1333.             hr = m_pIDvdC2->PlayTitle(1, DVD_CMD_FLAG_None, NULL);
  1334.             ASSERT(SUCCEEDED(hr));
  1335.             break;
  1336.  
  1337.         case EC_DVD_DOMAIN_CHANGE:
  1338.             switch (lParam1)
  1339.             {
  1340.             case DVD_DOMAIN_FirstPlay:  // = 1
  1341.             case DVD_DOMAIN_Stop:       // = 5
  1342.                 break ;
  1343.             
  1344.             case DVD_DOMAIN_VideoManagerMenu:  // = 2
  1345.             case DVD_DOMAIN_VideoTitleSetMenu: // = 3
  1346.                 // Inform the app to update the menu option to show "Resume" now
  1347.                 DbgLog((LOG_TRACE, 3, TEXT("DVD Event: Domain changed to VMGM(2)/VTSM(3) (%ld)"), lParam1)) ;
  1348.                 //PostMessage(m_hWnd, WM_COMMAND, IDM_USER_MENUSTATUS, (LPARAM) 1) ;
  1349.                 m_bMenuOn = true;  // now menu is "On"
  1350.                 break ;
  1351.             
  1352.             case DVD_DOMAIN_Title:      // = 4
  1353.                 // Inform the app to update the menu option to show "Menu" again
  1354.                 DbgLog((LOG_TRACE, 3, TEXT("DVD Event: Title Domain (%ld)"), lParam1)) ;
  1355.                 //PostMessage(m_hWnd, WM_COMMAND, IDM_USER_MENUSTATUS, (LPARAM) 0) ;
  1356.                 m_bMenuOn = false ; // we are no longer in a menu
  1357.                 break ;
  1358.             } // end of domain change switch
  1359.             break;
  1360.  
  1361.  
  1362.         case EC_DVD_PARENTAL_LEVEL_CHANGE:
  1363.             if (IDYES == MessageBox(m_hWnd, TEXT("Accept Parental Level Change Request?"), 
  1364.                 TEXT("Accept Change?"), MB_YESNO))
  1365.             {
  1366.                 m_pIDvdC2->AcceptParentalLevelChange(TRUE);
  1367.             }
  1368.             else
  1369.             {
  1370.                 m_pIDvdC2->AcceptParentalLevelChange(FALSE);
  1371.             }
  1372.             break;
  1373.  
  1374.         case EC_DVD_ERROR:
  1375.             DbgLog((LOG_TRACE, 3, TEXT("DVD Event: Error event received (code %ld)"), lParam1)) ;
  1376.             switch (lParam1)
  1377.             {
  1378.             case DVD_ERROR_Unexpected:
  1379.                 MessageBox(m_hWnd, 
  1380.                     TEXT("An unexpected error (possibly incorrectly authored content)")
  1381.                     TEXT("\nwas encountered.")
  1382.                     TEXT("\nCan't playback this DVD-Video disc."),
  1383.                     TEXT("Error"), MB_OK | MB_ICONINFORMATION) ;
  1384.                 m_pIMC->Stop() ;
  1385.                 break ;
  1386.                 
  1387.             case DVD_ERROR_CopyProtectFail:
  1388.                 MessageBox(m_hWnd, 
  1389.                     TEXT("Key exchange for DVD copy protection failed.")
  1390.                     TEXT("\nCan't playback this DVD-Video disc."),
  1391.                     TEXT("Error"), MB_OK | MB_ICONINFORMATION) ;
  1392.                 m_pIMC->Stop() ;
  1393.                 break ;
  1394.                 
  1395.             case DVD_ERROR_InvalidDVD1_0Disc:
  1396.                 MessageBox(m_hWnd, 
  1397.                     TEXT("This DVD-Video disc is incorrectly authored for v1.0  of the spec.")
  1398.                     TEXT("\nCan't playback this disc."),
  1399.                     TEXT("Error"), MB_OK | MB_ICONINFORMATION) ;
  1400.                 m_pIMC->Stop() ;
  1401.                 break ;
  1402.                 
  1403.             case DVD_ERROR_InvalidDiscRegion:
  1404.                 MessageBox(m_hWnd, 
  1405.                     TEXT("This DVD-Video disc cannot be played, because it is not")
  1406.                     TEXT("\nauthored to play in the current system region.")
  1407.                     TEXT("\nThe region mismatch may be fixed by changing the")
  1408.                     TEXT("\nsystem region (with DVDRgn.exe)."),
  1409.                     TEXT("Error"), MB_OK | MB_ICONINFORMATION) ;
  1410.                 m_pIMC->Stop() ;
  1411.                 ChangeDvdRegion();
  1412.                 break ;
  1413.                 
  1414.             case DVD_ERROR_LowParentalLevel:
  1415.                 MessageBox(m_hWnd, 
  1416.                     TEXT("Player parental level is set lower than the lowest parental")
  1417.                     TEXT("\nlevel available in this DVD-Video content.")
  1418.                     TEXT("\nCannot playback this DVD-Video disc."),
  1419.                     TEXT("Error"), MB_OK | MB_ICONINFORMATION) ;
  1420.                 m_pIMC->Stop() ;
  1421.                 break ;
  1422.                 
  1423.             case DVD_ERROR_MacrovisionFail:
  1424.                 MessageBox(m_hWnd, 
  1425.                     TEXT("This DVD-Video content is protected by Macrovision.")
  1426.                     TEXT("\nThe system does not satisfy Macrovision requirement.")
  1427.                     TEXT("\nCan't continue playing this disc."),
  1428.                     TEXT("Error"), MB_OK | MB_ICONINFORMATION) ;
  1429.                 m_pIMC->Stop() ;
  1430.                 break ;
  1431.                 
  1432.             case DVD_ERROR_IncompatibleSystemAndDecoderRegions:
  1433.                 MessageBox(m_hWnd, 
  1434.                     TEXT("No DVD-Video disc can be played on this system, because ")
  1435.                     TEXT("\nthe system region does not match the decoder region.")
  1436.                     TEXT("\nPlease contact the manufacturer of this system."),
  1437.                     TEXT("Error"), MB_OK | MB_ICONINFORMATION) ;
  1438.                 m_pIMC->Stop() ;
  1439.                 break ;
  1440.                 
  1441.             case DVD_ERROR_IncompatibleDiscAndDecoderRegions:
  1442.                 MessageBox(m_hWnd, 
  1443.                     TEXT("This DVD-Video disc cannot be played on this system, because it is")
  1444.                     TEXT("\nnot authored to be played in the installed decoder's region."),
  1445.                     TEXT("Error"), MB_OK | MB_ICONINFORMATION) ;
  1446.                 m_pIMC->Stop() ;
  1447.                 break ;
  1448.             }  // end of switch (lParam1)
  1449.             break ;
  1450.             
  1451.         // Next is warning
  1452.         case EC_DVD_WARNING:
  1453.             switch (lParam1)
  1454.             {
  1455.             case DVD_WARNING_InvalidDVD1_0Disc:
  1456.                 DbgLog((LOG_ERROR, 1, TEXT("DVD Warning: Current disc is not v1.0 spec compliant"))) ;
  1457.                 break ;
  1458.  
  1459.             case DVD_WARNING_FormatNotSupported:
  1460.                 DbgLog((LOG_ERROR, 1, TEXT("DVD Warning: The decoder does not support the new format."))) ;
  1461.                 break ;
  1462.  
  1463.             case DVD_WARNING_IllegalNavCommand:
  1464.                 DbgLog((LOG_ERROR, 1, TEXT("DVD Warning: An illegal navigation command was encountered."))) ;
  1465.                 break ;
  1466.  
  1467.             case DVD_WARNING_Open:
  1468.                 DbgLog((LOG_ERROR, 1, TEXT("DVD Warning: Could not open a file on the DVD disc."))) ;
  1469.                 MessageBox(m_hWnd, 
  1470.                     TEXT("A file on the DVD disc could not be opened. Playback may not continue."), 
  1471.                     TEXT("Warning"), MB_OK | MB_ICONINFORMATION) ;
  1472.                 break ;
  1473.  
  1474.             case DVD_WARNING_Seek:
  1475.                 DbgLog((LOG_ERROR, 1, TEXT("DVD Warning: Could not seek in a file on the DVD disc."))) ;
  1476.                 MessageBox(m_hWnd, 
  1477.                     TEXT("Could not move to a different part of a file on the DVD disc. Playback may not continue."), 
  1478.                     TEXT("Warning"), MB_OK | MB_ICONINFORMATION) ;
  1479.                 break ;
  1480.  
  1481.             case DVD_WARNING_Read:
  1482.                 DbgLog((LOG_ERROR, 1, TEXT("DVD Warning: Could not read a file on the DVD disc."))) ;
  1483.                 MessageBox(m_hWnd, 
  1484.                     TEXT("Could not read part of a file on the DVD disc. Playback may not continue."), 
  1485.                     TEXT("Warning"), MB_OK | MB_ICONINFORMATION) ;
  1486.                 break ;
  1487.  
  1488.             default:
  1489.                 DbgLog((LOG_ERROR, 1, TEXT("DVD Warning: An unknown (%ld) warning received."), lParam1)) ;
  1490.                 break ;
  1491.             }
  1492.             break ;
  1493.  
  1494.         case EC_DVD_BUTTON_CHANGE:
  1495.            break;
  1496.  
  1497.         case EC_DVD_STILL_ON:
  1498.             if (TRUE == lParam1) // if there is a still without buttons, we can call StillOff
  1499.                 m_bStillOn = true;
  1500.             break;
  1501.  
  1502.         case EC_DVD_STILL_OFF:
  1503.             m_bStillOn = false; // we are no longer in a still
  1504.             break;
  1505.  
  1506.         } // end of switch(lEvent)
  1507.         m_pIME->FreeEventParams(lEvent, lParam1, lParam2) ;
  1508.         } // end of while(GetEvent())
  1509.  
  1510.     return 0;
  1511. }
  1512.  
  1513.  
  1514. //------------------------------------------------------------------------------
  1515. // Name: CDvdCore::OnMouseEvent()
  1516. // Desc: This method acts on mouse events sent to it from the WndProc method
  1517. //------------------------------------------------------------------------------
  1518.  
  1519. LRESULT CDvdCore::OnMouseEvent(UINT uMessage, WPARAM wParam, LPARAM lParam)
  1520. {
  1521.     if (true == m_bFullScreenOn) // if any mouse activity happens while in full screen mode
  1522.     {
  1523.         ShowMouseCursor(true);
  1524.     }
  1525.  
  1526.     switch (uMessage)
  1527.     {
  1528.     case WM_MOUSEMOVE:
  1529.         {
  1530.             POINT pt;
  1531.             pt.x = GET_X_LPARAM(lParam); 
  1532.             pt.y = GET_Y_LPARAM(lParam); 
  1533.             m_pIDvdC2->SelectAtPosition(pt);
  1534.             return 0;
  1535.         }
  1536.  
  1537.     case WM_LBUTTONUP:
  1538.         {
  1539.             POINT pt;
  1540.             pt.x = GET_X_LPARAM(lParam); 
  1541.             pt.y = GET_Y_LPARAM(lParam); 
  1542.             m_pIDvdC2->ActivateAtPosition(pt);
  1543.             return 0;
  1544.         }
  1545.     }
  1546.     return DefWindowProc(m_hWnd, uMessage, wParam, lParam);
  1547. }
  1548.  
  1549.  
  1550. //------------------------------------------------------------------------------
  1551. // Name: CDvdCore::OnKeyEvent()
  1552. // Desc: This method processes keyboard events forwarded to it by the WndProc.
  1553. //------------------------------------------------------------------------------
  1554.  
  1555. LRESULT CDvdCore::OnKeyEvent(WPARAM wParam, LPARAM lParam)
  1556. {
  1557.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::OnKeyEvent()"))) ;
  1558.  
  1559.     switch (wParam)
  1560.     {
  1561.     case VK_ESCAPE: // exit full screen
  1562.         if (true == m_bFullScreenOn) // only do this if we are in fullscreen mode
  1563.             StopFullScreen();
  1564.         return 0;
  1565.  
  1566.     case VK_RETURN: // activate the currently selected button
  1567.         m_pIDvdC2->ActivateButton();
  1568.         return 0;
  1569.  
  1570.     case VK_LEFT: // select the left button
  1571.         m_pIDvdC2->SelectRelativeButton(DVD_Relative_Left);
  1572.         return 0;
  1573.  
  1574.     case VK_RIGHT: // select the right button
  1575.         m_pIDvdC2->SelectRelativeButton(DVD_Relative_Right);
  1576.         return 0;
  1577.  
  1578.     case VK_UP: // select the upper button
  1579.         m_pIDvdC2->SelectRelativeButton(DVD_Relative_Upper);
  1580.         return 0;
  1581.  
  1582.     case VK_DOWN: // select the lower button
  1583.         m_pIDvdC2->SelectRelativeButton(DVD_Relative_Lower);
  1584.         return 0;
  1585.     }
  1586.     return DefWindowProc(m_hWnd, WM_KEYUP, wParam, lParam);
  1587. }
  1588.  
  1589.  
  1590. //------------------------------------------------------------------------------
  1591. // Name: CDvdCore::ShowMouseCursor()
  1592. // Desc: This method is used to show or hide the cursor during fullscreen mode
  1593. //------------------------------------------------------------------------------
  1594.  
  1595. void CDvdCore::ShowMouseCursor(bool bShow)
  1596. {
  1597.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::ShowMouseCursor(%s)"), bShow ? TEXT("true") : 
  1598.         TEXT("false"))) ;
  1599.  
  1600.     if (true == bShow)   // display cursor
  1601.     {
  1602.         while (m_iMouseShowCount < 0)
  1603.         {
  1604.             m_iMouseShowCount = ::ShowCursor(TRUE) ; // keep adding until the count is 0 or higher
  1605.         } 
  1606.         m_dwMouseMoveTime = timeGetTime() ;  // it has just been moved
  1607.     }
  1608.     else // hide cursor
  1609.     {
  1610.         while (m_iMouseShowCount >= 0)
  1611.         {
  1612.             m_iMouseShowCount = ::ShowCursor(FALSE) ; // keep subtracting until the count is below 0
  1613.         }
  1614.     }
  1615. }
  1616.  
  1617.  
  1618. //------------------------------------------------------------------------------
  1619. // Name: CDvdCore::OnMouseTimer()
  1620. // Desc: This method will hide the pointer if the mouse hasn't moved for 
  1621. //       MOUSE_TIMEOUT milliseconds.
  1622. //------------------------------------------------------------------------------
  1623.  
  1624. LRESULT CDvdCore::OnMouseTimer(WPARAM wParam, LPARAM lParam)
  1625. {
  1626.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::OnMouseTimer()"))) ;
  1627.  
  1628.     if (MOUSETIMER != wParam ) // if it isn't our timer we won't do anything with it
  1629.         return (DefWindowProc(m_hWnd, WM_TIMER, wParam, lParam));
  1630.  
  1631.     // If we are in fullscreen mode, check if mouse should be hidden now
  1632.     if (true == m_bFullScreenOn) // we should be but just to make sure
  1633.     {
  1634.         if (timeGetTime() - m_dwMouseMoveTime > MOUSE_TIMEOUT)
  1635.         {
  1636.             ShowMouseCursor(false) ;
  1637.         }
  1638.     }
  1639.     
  1640.     return 0 ;
  1641. }
  1642.  
  1643.  
  1644. //------------------------------------------------------------------------------
  1645. // Name: CDvdCore::OnSize()
  1646. // Desc: This method is called whenever we receive WM_SIZE.  It will resize the 
  1647. //       IVideoWindow window to match our new client space.
  1648. //------------------------------------------------------------------------------
  1649.  
  1650. LRESULT CDvdCore::OnSize(WPARAM wParam, LPARAM lParam)
  1651. {
  1652.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::OnSize()"))) ;
  1653.  
  1654.     // set the video window to fill the client space
  1655.     RECT rect;
  1656.     GetClientRect(m_hWnd, &rect);
  1657.     HRESULT hr = m_pIVW->SetWindowPosition(0, 0, rect.right, rect.bottom);
  1658.     ASSERT(SUCCEEDED(hr)) ;    
  1659.     return 0;
  1660. }
  1661.  
  1662.  
  1663. //------------------------------------------------------------------------------
  1664. // Name: CDvdCore::GetParentalLevel()
  1665. // Desc: This method returns the current parental level setting of the DVD Navigator
  1666. //------------------------------------------------------------------------------
  1667.  
  1668. ULONG CDvdCore::GetParentalLevel()
  1669. {
  1670.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::GetParentalLevel()"))) ;
  1671.  
  1672.     ULONG ulLevel = 0;
  1673.     UCHAR cCountry[2];
  1674.     HRESULT hr;
  1675.     hr = m_pIDvdI2->GetPlayerParentalLevel(&ulLevel, cCountry);
  1676.     ASSERT(SUCCEEDED(hr));
  1677.     return ulLevel;
  1678. }
  1679.  
  1680.  
  1681. //------------------------------------------------------------------------------
  1682. // Name: CDvdCore::SetParentalLevel()
  1683. // Desc: This method sets the Navigator's parental level
  1684. //------------------------------------------------------------------------------
  1685.  
  1686. bool CDvdCore::SetParentalLevel(ULONG ulLevel)
  1687. {
  1688.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::SetParentalLevel() - Level: %u"), ulLevel)) ;
  1689.  
  1690.     if (Graph_Stopped2 != GetState())
  1691.     {
  1692.         // we need to be in the stop state to change parental levels.
  1693.         // we can either stop from IMediaControl or stop from IDvdControl2
  1694.         // we choose to stop from the IMediaControl because it makes the play code more clean
  1695.         m_pIDvdC2->SetOption(DVD_ResetOnStop, TRUE); // do a complete reset
  1696.         m_pIMC->Stop(); // stop the entire graph
  1697.         m_pIDvdC2->SetOption(DVD_ResetOnStop, FALSE); // restore previous settings
  1698.         SetState(Graph_Stopped2);
  1699.     }
  1700.  
  1701.     HRESULT hr = m_pIDvdC2->SelectParentalLevel(ulLevel);
  1702.     if (SUCCEEDED(hr))
  1703.         return true;
  1704.     else
  1705.         return false;
  1706. }
  1707.  
  1708.  
  1709. //------------------------------------------------------------------------------
  1710. // Name: CDvdCore::UpdateStatus()
  1711. // Desc: This method is just a stub to make our work with the callback cleaner.
  1712. //       We will point our m_pCallback to this unless given a different pointer by
  1713. //       the calling function
  1714. //------------------------------------------------------------------------------
  1715.  
  1716. void CDvdCore::UpdateStatus(void)
  1717. {
  1718.     //intentionally left blank
  1719. }
  1720.  
  1721.  
  1722. //------------------------------------------------------------------------------
  1723. // Name: CDvdCore::SetDirectory()
  1724. // Desc: This method is a wrapper for SetDVDDirectory.  It changes the current
  1725. //       directory used for video playback.  It is called by the Select Disc 
  1726. //       menu function.
  1727. //------------------------------------------------------------------------------
  1728.  
  1729. bool CDvdCore::SetDirectory(TCHAR *szDirectory)
  1730. {
  1731.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::SetDirectory()"))) ;
  1732.  
  1733.     HRESULT hr = m_pIDvdC2->Stop() ;  // first ask player to stop the playback.
  1734.     if (SUCCEEDED(hr))
  1735.         SetState(Nav_Stopped);
  1736.     
  1737.     // Convert TCHAR filename to WCHAR for COM
  1738.     WCHAR   szwFileName[MAX_PATH] ;
  1739. #ifdef UNICODE
  1740.     lstrcpy(szwFileName, szDirectory) ;
  1741. #else
  1742.     MultiByteToWideChar(CP_ACP, 0, szDirectory, -1, szwFileName, MAX_PATH) ;
  1743. #endif // UNICODE            
  1744.     hr = m_pIDvdC2->SetDVDDirectory(szwFileName) ;
  1745.     if (SUCCEEDED(hr))  // if the new file name is valid DVD-video volume
  1746.     {
  1747.         return true;
  1748.     }
  1749.     else
  1750.     {
  1751.         DbgLog((LOG_ERROR, 2, TEXT("WARNING: SetDVDDirectory(%s) failed (Error 0x%lx)"), 
  1752.             szDirectory, hr)) ;
  1753.         return false;
  1754.     }
  1755. }
  1756.  
  1757.  
  1758. //------------------------------------------------------------------------------
  1759. // Name: CDvdCore::FrameStep()
  1760. // Desc: This method will pause the graph and step forward one step at a time
  1761. //------------------------------------------------------------------------------
  1762.  
  1763. bool CDvdCore::FrameStep()
  1764. {
  1765.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::FrameStep()"))) ;
  1766.  
  1767.     if (Uninitialized == GetState())
  1768.     {        
  1769.         DbgLog((LOG_ERROR, 2, TEXT("WARNING: DVDCore is not initialized!"), 
  1770.             szDirectory, hr)) ;
  1771.         return false;
  1772.     }
  1773.  
  1774.     // Get the Frame Stepping Interface
  1775.     IVideoFrameStep* pFS;
  1776.     HRESULT hr;
  1777.     hr = m_pGraph->QueryInterface(__uuidof(IVideoFrameStep), (PVOID *)&pFS);
  1778.  
  1779.     if (FAILED(hr))
  1780.     {
  1781.         DbgLog((LOG_ERROR, 2, TEXT("WARNING: Can't get IVideoFrameStep interface!"), 
  1782.             szDirectory, hr)) ;
  1783.         return false;
  1784.     }
  1785.  
  1786.     // check if this decoder can step
  1787.     hr = pFS->CanStep(0L, NULL); 
  1788.  
  1789.     if (S_FALSE == hr)
  1790.     {
  1791.         DbgLog((LOG_ERROR, 2, TEXT("WARNING: Decoder can't step"), 
  1792.             szDirectory, hr)) ;
  1793.         pFS->Release();
  1794.         return false;
  1795.     }
  1796.  
  1797.     // this will only work while the graph is paused
  1798.     m_pIMC->Pause();
  1799.     SetState(Graph_Paused);
  1800.  
  1801.     // step one frame
  1802.     hr = pFS->Step(1, NULL); 
  1803.  
  1804.     bool retVal = SUCCEEDED(hr) ? true : false;
  1805.  
  1806.     // clean up after ourselves
  1807.     pFS->Release();
  1808.  
  1809.     return retVal;
  1810. }
  1811.  
  1812.  
  1813. //------------------------------------------------------------------------------
  1814. // Name: CDvdCore::PlayChapter()
  1815. // Desc: This method plays a chapter in the current title
  1816. //------------------------------------------------------------------------------
  1817.  
  1818. bool CDvdCore::PlayChapter(ULONG ulChap)
  1819. {
  1820.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::PlayChapter() %u"), ulChap)) ;
  1821.  
  1822.     HRESULT hr = m_pIDvdC2->PlayChapter(ulChap, DVD_CMD_FLAG_Block, NULL);
  1823.     if (SUCCEEDED(hr))
  1824.         return true;
  1825.     else
  1826.     {
  1827.         if (VFW_E_DVD_OPERATION_INHIBITED == hr)
  1828.             m_pCallback->Prohibited();
  1829.         DbgLog((LOG_ERROR, 1, TEXT("CDvdCore::PlayChapter() failed: %#x"), hr)) ;
  1830.         return false;
  1831.     }
  1832. }
  1833.  
  1834.  
  1835. //------------------------------------------------------------------------------
  1836. // Name: CDvdCore::PlayChapterInTitle()
  1837. // Desc: This method plays a chapter in the specified title
  1838. //------------------------------------------------------------------------------
  1839.  
  1840. bool CDvdCore::PlayChapterInTitle(ULONG ulTitle, ULONG ulChapter)
  1841. {
  1842.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::PlayChapterInTitle() Title: %u, "
  1843.         "Chapter: %u"), ulTitle, ulChapter)) ;
  1844.  
  1845.     HRESULT hr = m_pIDvdC2->PlayChapterInTitle(ulTitle, ulChapter, DVD_CMD_FLAG_Block, NULL);
  1846.     if (SUCCEEDED(hr))
  1847.         return true;
  1848.     else
  1849.     {
  1850.         if (VFW_E_DVD_OPERATION_INHIBITED == hr)
  1851.             m_pCallback->Prohibited();
  1852.         DbgLog((LOG_ERROR, 1, TEXT("CDvdCore::PlayChapterInTitle() failed: %#x"), hr)) ;
  1853.         return false;
  1854.     }
  1855. }
  1856.  
  1857.  
  1858. //------------------------------------------------------------------------------
  1859. // Name: CDvdCore::PlayTime()
  1860. // Desc: This method plays a time in the current title
  1861. //------------------------------------------------------------------------------
  1862.  
  1863. bool CDvdCore::PlayTime(DVD_HMSF_TIMECODE time)
  1864. {
  1865.     DbgLog((LOG_TRACE, 5, TEXT("CDvdCore::PlayTime() hh:mm:ss %u:%u:%u"),
  1866.         time.bHours, time.bMinutes, time.bSeconds)) ;
  1867.  
  1868.     /*
  1869.     time.bFrames = 1;
  1870.  
  1871.     DVD_HMSF_TIMECODE tempTime;
  1872.     tempTime.bHours = 0;
  1873.     tempTime.bMinutes = 5;
  1874.     tempTime.bSeconds = 5;
  1875.     tempTime.bFrames = 1;
  1876.     */
  1877.  
  1878.     HRESULT hr = m_pIDvdC2->PlayAtTime(&time, DVD_CMD_FLAG_Block, NULL);
  1879.     if (SUCCEEDED(hr))
  1880.         return true;
  1881.     else
  1882.     {
  1883.         DbgLog((LOG_ERROR, 1, TEXT("CDvdCore::PlayTime() failed: %#x"), hr)) ;
  1884.         return false;
  1885.     }
  1886. }
  1887.  
  1888.  
  1889. //------------------------------------------------------------------------------
  1890. // Name: CDvdCore::ChangeDvdRegion()
  1891. // Desc: This method changes the DVD Region
  1892. //
  1893. // Returns:
  1894. //         true:     if the drive region change is successful
  1895. //      false:     if drive region change fails (probably 
  1896. //            because no drive was found with a valid 
  1897. //            DVD-V disc).
  1898. //
  1899. //------------------------------------------------------------------------------
  1900.  
  1901. bool CDvdCore::ChangeDvdRegion()
  1902. {
  1903.     typedef  BOOL (APIENTRY * DVDPPLAUNCHER) (HWND hWnd, CHAR DriveLetter);
  1904.  
  1905.     bool     bRegionChanged = false;
  1906.     TCHAR     szCmdLine[MAX_PATH];
  1907.  
  1908.     //
  1909.     // First find out which drive is a DVD drive with a 
  1910.     // valid DVD-V disc.
  1911.     //
  1912.     TCHAR         szDVDDrive[4];
  1913.     if (! GetDriveLetter (szDVDDrive) )
  1914.     {
  1915.         DbgLog((LOG_ERROR, 1, TEXT("No DVD drive was found with DVD-Video disc.")
  1916.         TEXT("Cannot change DVD region of the drive."))); 
  1917.         return false;
  1918.     }
  1919.  
  1920.     //
  1921.     // Detect which OS we are running on.  For Windows NT,
  1922.     // use the storprop.dll, while for Windows 9x platform
  1923.     // use the DVDRgn.exe application to change the region.
  1924.     //
  1925.     OSVERSIONINFO     ver;
  1926.     ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  1927.     GetVersionEx(&ver);
  1928.     if (VER_PLATFORM_WIN32_NT  == ver.dwPlatformId)
  1929.     {
  1930.         // Windows NT platform
  1931.         HINSTANCE         hInstDLL;
  1932.         DVDPPLAUNCHER    dvdPPLauncher;
  1933.         CHAR             szDVDDriveA[4];
  1934.  
  1935.     #ifdef UNICODE
  1936.         WideCharToMultiByte(0, 0, szDVDDrive, -1,
  1937.                                     szDVDDriveA, sizeof(szDVDDriveA),
  1938.                                     NULL, NULL );
  1939.     #else
  1940.         strcpy(szDVDDriveA, szDVDDrive);
  1941.     #endif  // UNICODE
  1942.  
  1943.         GetSystemDirectory(szCmdLine, MAX_PATH);
  1944.         lstrcat(szCmdLine, TEXT("\\storprop.dll"));
  1945.     
  1946.         hInstDLL = LoadLibrary (szCmdLine);
  1947.         if (hInstDLL)
  1948.         {
  1949.             dvdPPLauncher = (DVDPPLAUNCHER) GetProcAddress(hInstDLL, "DvdLauncher");
  1950.             if (dvdPPLauncher)
  1951.             {
  1952.                 if (TRUE == dvdPPLauncher(m_hWnd, szDVDDriveA[0]))
  1953.                     bRegionChanged = true;
  1954.                 else
  1955.                     bRegionChanged = false;
  1956.             }
  1957.  
  1958.             FreeLibrary(hInstDLL);
  1959.         }
  1960.     }  // end of region change code for Windows NT platform
  1961.     else   // Windows 98 platform
  1962.     {
  1963.         // Get path of \windows\dvdrgn.exe for command line
  1964.         // string
  1965.         GetWindowsDirectory(szCmdLine, MAX_PATH);
  1966.         lstrcat(szCmdLine, TEXT("\\DVDRgn.exe "));
  1967.  
  1968.         // Add only the drive letter as command line parameter
  1969.         _tcsncat(szCmdLine, szDVDDrive, 1);
  1970.     
  1971.         // Prepare to execute DVDRgn.exe 
  1972.         STARTUPINFO                 StartupInfo;
  1973.         PROCESS_INFORMATION            ProcessInfo;
  1974.         StartupInfo.cb                = sizeof(StartupInfo);
  1975.         StartupInfo.dwFlags            = STARTF_USESHOWWINDOW;
  1976.         StartupInfo.wShowWindow        = SW_SHOWNORMAL;
  1977.         StartupInfo.lpReserved        = NULL;
  1978.         StartupInfo.lpDesktop        = NULL;
  1979.         StartupInfo.lpTitle            = NULL;
  1980.         StartupInfo.cbReserved2        = 0;
  1981.         StartupInfo.lpReserved2        = NULL;
  1982.         if (CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 
  1983.                     NORMAL_PRIORITY_CLASS, NULL, NULL, &StartupInfo, &ProcessInfo) )
  1984.         {
  1985.             // Wait until DVDRgn.exe finishes
  1986.             WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
  1987.             DWORD dwRet = 1;
  1988.             BOOL bRet = GetExitCodeProcess(ProcessInfo.hProcess,
  1989.                                                      &dwRet);
  1990.             // If the user changed the drive region 
  1991.             // successfully, the exit code of DVDRgn.exe is 0.
  1992.             if (bRet && 0 == dwRet)    
  1993.             {
  1994.                 bRegionChanged = true;
  1995.             }
  1996.         }
  1997.     }  // end of region change code for Windows 9x platform
  1998.  
  1999.     if (bRegionChanged)   // if region changed successfully
  2000.     {
  2001.         // We now cycle through stop.  We have to turn on ResetOnStop or we won't reset
  2002.         // the internals of the navigator when we stop.  After we have stopped, we reset
  2003.         // this option to its original state.  
  2004.         HRESULT hr;
  2005.         hr = m_pIMC->Pause();
  2006.         hr = m_pIDvdC2->SetOption(DVD_ResetOnStop, TRUE);
  2007.         hr = m_pIMC->Stop(); // come to a complete stop.
  2008.         hr = m_pIDvdC2->SetOption(DVD_ResetOnStop, FALSE);
  2009.         hr = m_pIMC->Run();
  2010.         return true;
  2011.     }
  2012.     else   // DVD region didn't happen
  2013.     {
  2014.         DbgLog((LOG_ERROR, 1, TEXT("DVD drive region could not be changed.")) );
  2015.         return false;
  2016.     }
  2017. }
  2018.  
  2019.  
  2020. //------------------------------------------------------------------------------
  2021. // Name: CDvdCore::GetDriveLetter()
  2022. // Desc: This method gets the drive letter of the currently active DVD drive
  2023. //
  2024. // Returns:
  2025. //         true:     if a DVD drive is found (with valid disc)
  2026. //      false:     if no DVD drive was found with valid DVD-V
  2027. //            disc.
  2028. //
  2029. //------------------------------------------------------------------------------
  2030.  
  2031. bool CDvdCore::GetDriveLetter(TCHAR *pszDrive)
  2032. {
  2033.     WCHAR     szwPath[MAX_PATH];
  2034.     ULONG     ulActualSize;
  2035.  
  2036.     pszDrive[0] = pszDrive[3] = 0;
  2037.     //
  2038.     // Get the current root directory
  2039.     //
  2040.     if (SUCCEEDED(m_pIDvdI2->GetDVDDirectory(szwPath, MAX_PATH, &ulActualSize)))
  2041.     {
  2042. #ifdef UNICODE
  2043.         lstrcpynW(pszDrive, szwPath, 3);
  2044. #else
  2045.         CHAR szDrive[MAX_PATH];
  2046.         WideCharToMultiByte(CP_ACP, NULL, szwPath, -1, szDrive, MAX_PATH, NULL, NULL);
  2047.         lstrcpyn(pszDrive, szDrive, 3);
  2048. #endif;
  2049.         return true;
  2050.     }
  2051.     return false; // couldn't find a directory
  2052. }
  2053.  
  2054.  
  2055. //------------------------------------------------------------------------------
  2056. // Name: CDvdCore::DoesFileExist()
  2057. // Desc: This method determines if the given filename is an existing file
  2058. //------------------------------------------------------------------------------
  2059.  
  2060. bool CDvdCore::DoesFileExist(PTSTR pszFile)
  2061. {
  2062.     HANDLE        hFile = NULL ;
  2063.     //
  2064.     // We don't want any error message box to pop up when
  2065.     // we try to test if the required file is available to
  2066.     // open for read.
  2067.     //
  2068.     UINT uErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | 
  2069.      SEM_NOOPENFILEERRORBOX);
  2070.     hFile = CreateFile(pszFile, GENERIC_READ, 
  2071.                         FILE_SHARE_READ, NULL, 
  2072.     OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN,
  2073.     NULL);
  2074.     SetErrorMode(uErrorMode);  // restore error mode
  2075.     if (INVALID_HANDLE_VALUE  == hFile) 
  2076.     return FALSE ;        
  2077.  
  2078.     CloseHandle(hFile);
  2079.     return TRUE;
  2080. }
  2081.  
  2082.  
  2083. //------------------------------------------------------------------------------
  2084. // Name: CDvdCore::UnInitMessageSink()
  2085. // Desc: This method undoes what InitMessageSink does.  It is important that we remove
  2086. //       the child window bit from the video window before we close the parent window.
  2087. //------------------------------------------------------------------------------
  2088.  
  2089. void CDvdCore::UnInitMessageSink()
  2090. {
  2091.     // Make the sure the video window is no longer child of the playback window.
  2092.     if (m_pIVW)
  2093.     {
  2094.         long lStyle;
  2095.         m_pIVW->get_WindowStyle(&lStyle);
  2096.         m_pIVW->put_WindowStyle(lStyle & ~WS_CHILD);
  2097.         m_pIVW->put_Owner(NULL);
  2098.     }
  2099.     DestroyWindow(m_hWnd);
  2100.  
  2101. }
  2102.  
  2103.  
  2104. //------------------------------------------------------------------------------
  2105. // Name: CDvdCore::OnClose()
  2106. // Desc: This method is called when the playback window is closed.  In response it
  2107. // calls the currently registered IDvdCallback object's Exit method.  
  2108. //------------------------------------------------------------------------------
  2109.  
  2110. LRESULT CDvdCore::OnClose()
  2111. {
  2112.     m_pCallback->Exit();
  2113.     return 0;
  2114. }
  2115.  
  2116.  
  2117. //------------------------------------------------------------------------------
  2118. // Name: CDvdCore::GetPlaybackWindowRect()
  2119. // Desc: This method will check the size of the desktop and scale the size of 
  2120. //       the playback window's accordingly. 
  2121. //------------------------------------------------------------------------------
  2122.  
  2123. RECT CDvdCore::GetPlaybackWindowRect()
  2124. {
  2125.     RECT rDesktop, rWindow, rApp;
  2126.     SystemParametersInfo(SPI_GETWORKAREA, NULL, &rDesktop, NULL);
  2127.     rApp = m_pCallback->GetAppWindow();
  2128.  
  2129.     rWindow.left = rDesktop.left + 5; // leave a 5 pixel gap
  2130.     rWindow.top = rDesktop.top + 5; // leave a 5 pixel gap
  2131.     rWindow.bottom = rApp.top; // place this window above the top of player window
  2132.     rWindow.right = rDesktop.left + ((rWindow.bottom - rWindow.top) * 4)/3; //maintain 4:3 aspect ratio
  2133.     return rWindow;
  2134. }
  2135.  
  2136.  
  2137. //------------------------------------------------------------------------------
  2138. // Name: CDvdCore::GetDvdText()
  2139. // Desc: This method will read the Dvd Text Info off of a disc and output it to 
  2140. //       a messagebox.  We will be using GetDvdTextStringAsUnicode.  There is 
  2141. //       also a "native" version but we don't use it here.  The Unicode version
  2142. //       will work better with non-English text.
  2143. //
  2144. //       This is done only to show how to read the text info and
  2145. //       display it.  In a real application, you would probably
  2146. //         create your own data structure to hold the text information.
  2147. //------------------------------------------------------------------------------
  2148.  
  2149. bool CDvdCore::GetDvdText()
  2150. {
  2151.     ULONG ulNumStrings, ulNumLang;
  2152.     LCID LangCode;
  2153.     DVD_TextCharSet CharSet;
  2154.  
  2155.     HRESULT hr;
  2156.  
  2157.     // first, we check if there is any text on this disc
  2158.     hr = m_pIDvdI2->GetDVDTextNumberOfLanguages(&ulNumLang);
  2159.     if (0 == ulNumLang || FAILED(hr))
  2160.     {
  2161.         MessageBox(m_hWnd, TEXT("There is no text on this disc"), TEXT("No Text Present"), 
  2162.             MB_OK);
  2163.         return false;
  2164.     }
  2165.  
  2166.     ULONG ulLangIndex = 0; // We will just grab the first language.  You might want to 
  2167.                            //ask the user which language they want to see or try to 
  2168.                            //choose one that closest matches the locale of the machine
  2169.  
  2170.     // next we get the number of strings available
  2171.     hr = m_pIDvdI2->GetDVDTextLanguageInfo(ulLangIndex,
  2172.         &ulNumStrings, &LangCode, &CharSet); 
  2173.  
  2174.     if (FAILED(hr))
  2175.     {
  2176.         MessageBox(m_hWnd, TEXT("There was an error getting the number of Strings"), 
  2177.             TEXT("Error!"), MB_OK);
  2178.         return false;
  2179.     }
  2180.  
  2181. #ifdef UNICODE
  2182.     wstringstream aString;
  2183. #else
  2184.     stringstream aString;
  2185. #endif
  2186.  
  2187.        WCHAR buffer[1024] = {L"\0"}; // should be ample space
  2188.  
  2189.     for (ULONG ulStringIndex = 0; ulStringIndex < ulNumStrings; ulStringIndex++)
  2190.     {
  2191.  
  2192.         ULONG ulActualSize;
  2193.         DVD_TextStringType type;
  2194.  
  2195.         hr = m_pIDvdI2->GetDVDTextStringAsUnicode(ulLangIndex,
  2196.             ulStringIndex, buffer, sizeof(buffer)/sizeof(*buffer), &ulActualSize, &type);
  2197.  
  2198.         if (SUCCEEDED(hr))
  2199.         {
  2200. #ifdef UNICODE
  2201.  
  2202.             // Any string type less than 0x20 is an empty string used 
  2203.             // only to identify the logical structure or stream.
  2204.             // Here, we display the string types to illustrate how
  2205.             // they are used to organize text strings on the disc.
  2206.             if(type > DVD_Channel_Audio) //0x20
  2207.                 aString << AsStr << buffer << endl;
  2208.             else
  2209.                 aString << AsStr <<  endl;
  2210. #else
  2211.             // we need to convert the Unicode to ANSI so we can output it
  2212.             // stringstream can't handle unicode characters
  2213.             CHAR myBuffer[1024];
  2214.             WideCharToMultiByte(CP_ACP, 0, buffer, -1, myBuffer, 1024, NULL, NULL);
  2215.  
  2216.             // Any string type less than 0x20 is an empty string used 
  2217.             // only to identify the logical structure or stream.
  2218.             // Here, we display the string types to illustrate how
  2219.             // they are used to organize text strings on the disc.
  2220.             if(type > DVD_Channel_Audio) //0x20
  2221.                 aString << AsStr(type) << " " << myBuffer << endl;
  2222.             else
  2223.                 aString << AsStr(type) << " " << endl;
  2224. #endif
  2225.         }
  2226.     }
  2227.  
  2228.     MessageBox(m_hWnd, aString.str().c_str(), TEXT("Dvd Text Info"), MB_OK);
  2229.     return true; // once everything is over, if we haven't failed, we must have passed
  2230. }
  2231.  
  2232.  
  2233. //------------------------------------------------------------------------------
  2234. // Name: CDvdCore::GetAudioAttributes()
  2235. // Desc: This method will parse the audio attributes of the current audio stream
  2236. //       and output it to a messagebox.
  2237. // 
  2238. //       You probably don't want to dump raw data like this on the user.  This is
  2239. //       merely to demonstrate how to parse the information.  The same information
  2240. //       found here can be used to describe the audio in a more user-friendly
  2241. //       manner.  In a real application, this function should probably return
  2242. //       a string to CApp rather than displaying it itself.
  2243. //------------------------------------------------------------------------------
  2244.  
  2245. bool CDvdCore::GetAudioAttributes()
  2246. {
  2247.     HRESULT hr;
  2248.  
  2249.     DVD_AudioAttributes audioAtr;
  2250.     hr = m_pIDvdI2->GetAudioAttributes(DVD_STREAM_DATA_CURRENT, &audioAtr);
  2251.  
  2252.     if (FAILED(hr))
  2253.     {
  2254.         MessageBox(m_hWnd, TEXT("GetAudioAttributes Failed"), TEXT("Error!"), MB_OK);
  2255.         return false;
  2256.     }
  2257.  
  2258. #ifdef UNICODE
  2259.     wstringstream aString;
  2260. #else
  2261.     stringstream aString;
  2262. #endif
  2263.  
  2264.     aString << TEXT("Audio Attributes :") << endl << endl << TEXT("Audio Application Mode =")
  2265.         << AsStr(audioAtr.AppMode) << endl;
  2266.  
  2267.     aString << TEXT("Audio Format = ") << AsStr(audioAtr.AudioFormat) << endl;
  2268.  
  2269.     aString << TEXT("Multichannel Extension = ")
  2270.         << AsStr( audioAtr.fHasMultichannelInfo != FALSE ) << endl;
  2271.  
  2272.     aString << TEXT("Number of Audio Channels = ") << 
  2273.         static_cast<ULONG>(audioAtr.bNumberOfChannels) << endl; // the cast is only necessary to
  2274.                                                                 // allow IOStream to understand it
  2275.  
  2276.     aString << TEXT("Sampling Frequency = ") << audioAtr.dwFrequency << endl;
  2277.  
  2278.     aString << TEXT("Sampling Quantization = ") << static_cast<ULONG>(audioAtr.bQuantization) << endl;
  2279.  
  2280.     aString << TEXT("Language Code LCID = ") << audioAtr.Language << endl;
  2281.  
  2282.     aString << TEXT("Language Code Extension = ") << AsStr(audioAtr.LanguageExtension) << endl;
  2283.  
  2284.     if( DVD_AudioMode_Karaoke == audioAtr.AppMode ) 
  2285.     {
  2286.         DVD_KaraokeAttributes karaokeAtr;
  2287.         hr = m_pIDvdI2->GetKaraokeAttributes(DVD_STREAM_DATA_CURRENT, &karaokeAtr );
  2288.         if (SUCCEEDED(hr)) 
  2289.         {
  2290.             aString << endl << TEXT("Karaoke Attributes :") << endl << endl;
  2291.             aString << TEXT("Version = ") << static_cast<ULONG>(karaokeAtr.bVersion) << endl;
  2292.  
  2293.             aString << TEXT("MasterOfCeremoniesInGuideVocal1 = ") <<
  2294.                 AsStr( karaokeAtr.fMasterOfCeremoniesInGuideVocal1 != FALSE ) << endl;
  2295.  
  2296.             aString << TEXT("Duet = ") << AsStr( karaokeAtr.fDuet != FALSE ) << endl;
  2297.  
  2298.             aString << TEXT("ChannelAssignment = ") << AsStr( karaokeAtr.ChannelAssignment ) << endl;
  2299.  
  2300.             
  2301.             if( VFW_S_DVD_CHANNEL_CONTENTS_NOT_AVAILABLE != hr) 
  2302.             {
  2303.                 aString << endl << TEXT("Channel contents:") << endl << endl;
  2304.                 for( UINT i=0; i < audioAtr.bNumberOfChannels; i++) 
  2305.                 {
  2306.                     aString << TEXT("Channel ") << i << TEXT(":");
  2307.                     WORD c = karaokeAtr.wChannelContents[i];
  2308.                     if( c & DVD_Karaoke_GuideVocal1 ) aString << TEXT(" GuideVocal1");
  2309.                     if( c & DVD_Karaoke_GuideVocal2 ) aString << TEXT(" GuideVocal2");
  2310.                     if( c & DVD_Karaoke_GuideMelody1 ) aString << TEXT(" GuideMelody1");
  2311.                     if( c & DVD_Karaoke_GuideMelody2 ) aString << TEXT(" GuideMelody2");
  2312.                     if( c & DVD_Karaoke_GuideMelodyA ) aString << TEXT(" GuideMelodyA");
  2313.                     if( c & DVD_Karaoke_GuideMelodyB ) aString << TEXT(" GuideMelodyB");
  2314.                     if( c & DVD_Karaoke_SoundEffectA ) aString << TEXT(" SoundEffectA");
  2315.                     if( c & DVD_Karaoke_SoundEffectB ) aString << TEXT(" SoundEffectB");
  2316.                     aString << endl;
  2317.                 }
  2318.             }
  2319.         }
  2320.     }
  2321.     MessageBox(m_hWnd, aString.str().c_str(), TEXT("Audio Attributes"), MB_OK);
  2322.     return true;
  2323. }
  2324.  
  2325.  
  2326. //------------------------------------------------------------------------------
  2327. // Name: CDvdCore::GetVideoAttributes()
  2328. // Desc: This method will parse the video attributes of the current video stream
  2329. //       and output it to a messagebox.
  2330. // 
  2331. //       You probably don't want to dump raw data like this on the user.  This is
  2332. //       merely to demonstrate how to parse the information.  The same information
  2333. //       found here can be used to describe the video in a more user-friendly
  2334. //       manner.  In a real application, this function should probably return
  2335. //       a string to CApp rather than displaying it itself.
  2336. //------------------------------------------------------------------------------
  2337.  
  2338. bool CDvdCore::GetVideoAttributes()
  2339. {
  2340.     DVD_VideoAttributes atrVideo;
  2341.     HRESULT hr;
  2342.     hr = m_pIDvdI2->GetCurrentVideoAttributes(&atrVideo);
  2343.     if (FAILED(hr))
  2344.     {
  2345.         MessageBox(m_hWnd, TEXT("GetCurrentVideoAttributes Failed"), TEXT("Error!"), MB_OK);
  2346.         return false;
  2347.     }
  2348.  
  2349. #ifdef UNICODE
  2350.     wstringstream aString;
  2351. #else
  2352.     stringstream aString;
  2353. #endif
  2354.  
  2355.     aString << TEXT("Video Attributes:") << endl << endl;
  2356.  
  2357.     aString << TEXT("Pan-Scan permitted: ") << AsStr(atrVideo.fPanscanPermitted) << endl;
  2358.  
  2359.     aString << TEXT("Letterbox permitted: ") << AsStr(atrVideo.fLetterboxPermitted) << endl;
  2360.  
  2361.     aString << TEXT("Aspect: " << atrVideo.ulAspectX << TEXT(" x ") << atrVideo.ulAspectY << endl;
  2362.  
  2363.     aString << TEXT("Frame Rate: ") << atrVideo.ulFrameRate << endl;
  2364.  
  2365.     aString << TEXT("Frame Height: ") << atrVideo.ulFrameHeight << endl;
  2366.  
  2367.     aString << TEXT("Compression Mode: ") << AsStr(atrVideo.Compression) << endl;
  2368.  
  2369.     aString << TEXT("Line21 Field 1: ") << AsStr(atrVideo.fLine21Field1InGOP) << endl;
  2370.  
  2371.     aString << TEXT("Line21 Field 2: ") << AsStr(atrVideo.fLine21Field2InGOP) << endl;
  2372.  
  2373.     aString << TEXT("Source Resolution: ") << atrVideo.ulSourceResolutionX << TEXT(" x ") << 
  2374.        atrVideo.ulSourceResolutionY) << endl;
  2375.  
  2376.     aString << TEXT("Is Source Letterboxed? ") << AsStr(atrVideo.fIsSourceLetterboxed) << endl;
  2377.  
  2378.     aString << TEXT("Is Film Mode? ") << AsStr(atrVideo.fIsFilmMode) << endl;
  2379.  
  2380.     MessageBox(m_hWnd, aString.str().c_str(), TEXT("Video Attributes"), MB_OK);
  2381.     return true;
  2382. }
  2383.  
  2384.  
  2385. //------------------------------------------------------------------------------
  2386. // Name: CDvdCore::GetSPAttributes()
  2387. // Desc: This method will parse the subpicture attributes of the current video stream
  2388. //       and output it to a messagebox.
  2389. // 
  2390. //       You probably don't want to dump raw data like this on the user.  This is
  2391. //       merely to demonstrate how to parse the information.  The same information
  2392. //       found here can be used to describe the subpicture in a more user-friendly
  2393. //       manner.  In a real application, this function should probably return
  2394. //       a string to CApp rather than displaying it itself.
  2395. //------------------------------------------------------------------------------
  2396.  
  2397. bool CDvdCore::GetSPAttributes()
  2398. {
  2399.     DVD_SubpictureAttributes atrSP;
  2400.     HRESULT hr;
  2401.     hr = m_pIDvdI2->GetSubpictureAttributes(DVD_STREAM_DATA_CURRENT, &atrSP);
  2402.     if (FAILED(hr))
  2403.     {
  2404.         MessageBox(m_hWnd, TEXT("GetSubpictureAttributes Failed"), TEXT("Error!"), MB_OK);
  2405.         return false;
  2406.     }
  2407.  
  2408. #ifdef UNICODE
  2409.     wstringstream aString;
  2410. #else
  2411.     stringstream aString;
  2412. #endif
  2413.  
  2414.     aString << TEXT("SubPicture Attributes:") << endl << endl;
  2415.     aString << TEXT("Subpicture Type = ") << AsStr(atrSP.Type) << endl;
  2416.  
  2417.     aString << TEXT("Subpicture Coding Mode = ") << AsStr(atrSP.CodingMode) << endl;
  2418.  
  2419.     TCHAR pszCode[100];
  2420.     if (0 == GetLocaleInfo(atrSP.Language, LOCALE_SLANGUAGE, pszCode, 100))
  2421.     {
  2422.         aString << TEXT("GetLocaleInfo Failed !!!") << endl;
  2423.     }
  2424.     else
  2425.     {
  2426.         aString << TEXT("Language Code = ") << pszCode << endl;
  2427.     }
  2428.  
  2429.     aString << TEXT("Language Code Extension = ") << AsStr(atrSP.LanguageExtension) << endl;
  2430.  
  2431.     MessageBox(m_hWnd, aString.str().c_str(), TEXT("Video Attributes"), MB_OK);
  2432.     return true;
  2433. }
  2434.  
  2435.  
  2436.