//------------------------------------------------------------------------------ // File: PlayWndASF.cpp // // Desc: DirectShow sample code - a simple audio/video media file player // application for Windows Media content (ASF, WMV, WMA). // Pause, stop, mute, and fullscreen mode toggle can be performed // via keyboard commands. // // Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ #include #include #include #include #include #include #include "playwndasf.h" #include "resource.h" #include "keyprovider.h" // An application can advertise the existence of its filter graph // by registering the graph with a global Running Object Table (ROT). // The GraphEdit application can detect and remotely view the running // filter graph, allowing you to 'spy' on the graph with GraphEdit. // // To enable registration in this sample, define REGISTER_FILTERGRAPH. // #define REGISTER_FILTERGRAPH // // Global data // HWND ghApp=0; HMENU ghMenu=0; HINSTANCE ghInst=0; TCHAR g_szFileName[MAX_PATH]={0}; BOOL g_bAudioOnly=FALSE, g_bFullscreen=FALSE; LONG g_lVolume=VOLUME_FULL; DWORD g_dwGraphRegister=0; PLAYSTATE g_psCurrent=Stopped; BOOL g_bUseNewASFReader=TRUE; // DirectShow interfaces IGraphBuilder *pGB = NULL; IMediaControl *pMC = NULL; IMediaEventEx *pME = NULL; IVideoWindow *pVW = NULL; IBasicAudio *pBA = NULL; IBasicVideo *pBV = NULL; IMediaSeeking *pMS = NULL; IVideoFrameStep *pFS = NULL; // Global key provider object created/released during the // Windows Media graph-building stage. CKeyProvider prov; HRESULT PlayMovieInWindow(LPTSTR szFile) { USES_CONVERSION; WCHAR wFile[MAX_PATH]; HRESULT hr; IFileSourceFilter *pFSF=NULL; IBaseFilter *pReader=NULL; // Clear open dialog remnants before calling RenderFile() UpdateWindow(ghApp); // Convert filename to wide character string wcscpy(wFile, T2W(szFile)); // Get the interface for DirectShow's GraphBuilder JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGB)); if(SUCCEEDED(hr)) { // Use special handling for Windows Media files if (IsWindowsMediaFile(szFile)) { if (g_bUseNewASFReader) { // Load the improved ASF reader filter by CLSID hr = CreateFilter(CLSID_WMAsfReader, &pReader); if(FAILED(hr)) { Msg(TEXT("Failed to create WMAsfWriter filter! hr=0x%x\n"), hr); return hr; } // Add the ASF reader filter to the graph. For ASF/WMV/WMA content, // this filter is NOT the default and must be added explicitly. hr = pGB->AddFilter(pReader, L"ASF Reader"); if(FAILED(hr)) { Msg(TEXT("Failed to add ASF reader filter to graph! hr=0x%x\n"), hr); return hr; } // Create the key provider that will be used to unlock the WM SDK JIF(AddKeyProvider(pGB)); // Set its source filename JIF(pReader->QueryInterface(IID_IFileSourceFilter, (void **) &pFSF)); JIF(pFSF->Load(wFile, NULL)); pFSF->Release(); // Render the output pins of the ASF reader to build the // remainder of the graph automatically JIF(RenderOutputPins(pGB, pReader)); // Since the graph is built and the filters are added to the graph, // the WM ASF reader interface can be released. pReader->Release(); } // Use the legacy ASF reader filter (default reader) with RenderFile else { // Create the key provider that will be used to unlock the WM SDK JIF(AddKeyProvider(pGB)); // Have the graph builder construct the remainder of the graph JIF(pGB->RenderFile(wFile, NULL)); } } // Not a Windows Media file, so just render the standard way else { // Have the graph builder construct its the appropriate graph automatically JIF(pGB->RenderFile(wFile, NULL)); } // QueryInterface for DirectShow interfaces JIF(pGB->QueryInterface(IID_IMediaControl, (void **)&pMC)); JIF(pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME)); JIF(pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS)); // Query for video interfaces, which may not be relevant for audio files JIF(pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW)); JIF(pGB->QueryInterface(IID_IBasicVideo, (void **)&pBV)); // Query for audio interfaces, which may not be relevant for video-only files JIF(pGB->QueryInterface(IID_IBasicAudio, (void **)&pBA)); // Is this an audio-only file (no video component)? CheckVisibility(); // Have the graph signal event via window callbacks for performance JIF(pME->SetNotifyWindow((OAHWND)ghApp, WM_GRAPHNOTIFY, 0)); if (!g_bAudioOnly) { JIF(pVW->put_Owner((OAHWND)ghApp)); JIF(pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN)); JIF(InitVideoWindow(1, 1)); GetFrameStepInterface(); } else { JIF(InitPlayerWindow()); } // Let's get ready to rumble! CheckSizeMenu(ID_FILE_SIZE_NORMAL); ShowWindow(ghApp, SW_SHOWNORMAL); UpdateWindow(ghApp); SetForegroundWindow(ghApp); SetFocus(ghApp); g_bFullscreen = FALSE; UpdateMainTitle(); #ifdef REGISTER_FILTERGRAPH hr = AddGraphToRot(pGB, &g_dwGraphRegister); if (FAILED(hr)) { Msg(TEXT("Failed to register filter graph with ROT! hr=0x%x"), hr); g_dwGraphRegister = 0; } #endif // Run the graph to play the media file JIF(pMC->Run()); g_psCurrent=Running; SetFocus(ghApp); } return hr; } HRESULT RenderOutputPins(IGraphBuilder *pGB, IBaseFilter *pFilter) { HRESULT hr = S_OK; IEnumPins * pEnumPin = NULL; IPin * pConnectedPin = NULL, * pPin = NULL; PIN_DIRECTION PinDirection; ULONG ulFetched; // Enumerate all pins on the filter hr = pFilter->EnumPins(&pEnumPin); if(SUCCEEDED(hr)) { // Step through every pin, looking for the output pins while (S_OK == (hr = pEnumPin->Next(1L, &pPin, &ulFetched))) { // Is this pin connected? We're not interested in connected pins. hr = pPin->ConnectedTo(&pConnectedPin); if (pConnectedPin) { pConnectedPin->Release(); pConnectedPin = NULL; } // If this pin is not connected, render it. if (VFW_E_NOT_CONNECTED == hr) { hr = pPin->QueryDirection(&PinDirection); if ((S_OK == hr) && (PinDirection == PINDIR_OUTPUT)) { hr = pGB->Render(pPin); } } pPin->Release(); // If there was an error, stop enumerating if (FAILED(hr)) break; } } // Release pin enumerator pEnumPin->Release(); return hr; } HRESULT AddKeyProvider(IGraphBuilder *pGraph) { HRESULT hr; // Instantiate the key provider class, and AddRef it // so that COM doesn't try to free our static object. prov.AddRef(); // Give the graph an IObjectWithSite pointer to us for callbacks & QueryService. IObjectWithSite* pObjectWithSite = NULL; hr = pGraph->QueryInterface(IID_IObjectWithSite, (void**)&pObjectWithSite); if (SUCCEEDED(hr)) { // Use the IObjectWithSite pointer to specify our key provider object. // The filter graph manager will use this pointer to call // QueryService to do the unlocking. // If the unlocking succeeds, then we can build our graph. hr = pObjectWithSite->SetSite((IUnknown *) (IServiceProvider *) &prov); pObjectWithSite->Release(); } return hr; } HRESULT CreateFilter(REFCLSID clsid, IBaseFilter **ppFilter) { HRESULT hr; hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **) ppFilter); if(FAILED(hr)) { Msg(TEXT("CreateFilter: Failed to create filter! hr=0x%x\n"), hr); if (ppFilter) *ppFilter = NULL; return hr; } return S_OK; } BOOL IsWindowsMediaFile(LPTSTR lpszFile) { if (_tcsstr(lpszFile, TEXT(".asf")) || _tcsstr(lpszFile, TEXT(".ASF")) || _tcsstr(lpszFile, TEXT(".wma")) || _tcsstr(lpszFile, TEXT(".WMA")) || _tcsstr(lpszFile, TEXT(".wmv")) || _tcsstr(lpszFile, TEXT(".WMV"))) return TRUE; else return FALSE; } HRESULT InitVideoWindow(int nMultiplier, int nDivider) { LONG lHeight, lWidth; HRESULT hr = S_OK; RECT rect; if (!pBV) return S_OK; // Read the default video size hr = pBV->GetVideoSize(&lWidth, &lHeight); if (hr == E_NOINTERFACE) return S_OK; EnablePlaybackMenu(TRUE); // Account for requests of normal, half, or double size lWidth = lWidth * nMultiplier / nDivider; lHeight = lHeight * nMultiplier / nDivider; SetWindowPos(ghApp, NULL, 0, 0, lWidth, lHeight, SWP_NOMOVE | SWP_NOOWNERZORDER); int nTitleHeight = GetSystemMetrics(SM_CYCAPTION); int nBorderWidth = GetSystemMetrics(SM_CXBORDER); int nBorderHeight = GetSystemMetrics(SM_CYBORDER); // Account for size of title bar and borders for exact match // of window client area to default video size SetWindowPos(ghApp, NULL, 0, 0, lWidth + 2*nBorderWidth, lHeight + nTitleHeight + 2*nBorderHeight, SWP_NOMOVE | SWP_NOOWNERZORDER); GetClientRect(ghApp, &rect); JIF(pVW->SetWindowPosition(rect.left, rect.top, rect.right, rect.bottom)); return hr; } HRESULT InitPlayerWindow(void) { // Reset to a default size for audio and after closing a clip SetWindowPos(ghApp, NULL, 0, 0, DEFAULT_AUDIO_WIDTH, DEFAULT_AUDIO_HEIGHT, SWP_NOMOVE | SWP_NOOWNERZORDER); // Check the 'full size' menu item CheckSizeMenu(ID_FILE_SIZE_NORMAL); EnablePlaybackMenu(FALSE); return S_OK; } void MoveVideoWindow(void) { HRESULT hr; // Track the movement of the container window and resize as needed if(pVW) { RECT client; GetClientRect(ghApp, &client); hr = pVW->SetWindowPosition(client.left, client.top, client.right, client.bottom); } } void CheckVisibility(void) { long lVisible; HRESULT hr; if ((!pVW) || (!pBV)) { // Audio-only files have no video interfaces. This might also // be a file whose video component uses an unknown video codec. g_bAudioOnly = TRUE; return; } else { // Clear the global flag g_bAudioOnly = FALSE; } hr = pVW->get_Visible(&lVisible); if (FAILED(hr)) { // If this is an audio-only clip, get_Visible() won't work. // // Also, if this video is encoded with an unsupported codec, // we won't see any video, although the audio will work if it is // of a supported format. // if (hr == E_NOINTERFACE) { g_bAudioOnly = TRUE; } else { Msg(TEXT("Failed(%08lx) in pVW->get_Visible()!\r\n"), hr); } } } void PauseClip(void) { if (!pMC) return; // Toggle play/pause behavior if((g_psCurrent == Paused) || (g_psCurrent == Stopped)) { if (SUCCEEDED(pMC->Run())) g_psCurrent = Running; } else { if (SUCCEEDED(pMC->Pause())) g_psCurrent = Paused; } UpdateMainTitle(); } void StopClip(void) { HRESULT hr; if ((!pMC) || (!pMS)) return; // Stop and reset postion to beginning if((g_psCurrent == Paused) || (g_psCurrent == Running)) { LONGLONG pos = 0; hr = pMC->Stop(); g_psCurrent = Stopped; // Seek to the beginning hr = pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning , NULL, AM_SEEKING_NoPositioning); // Display the first frame to indicate the reset condition hr = pMC->Pause(); } UpdateMainTitle(); } void OpenClip() { HRESULT hr; // If no filename specified by command line, show file open dialog if(g_szFileName[0] == L'\0') { TCHAR szFilename[MAX_PATH]; UpdateMainTitle(); // If no filename was specified on the command line, then our video // window has not been created or made visible. Make our main window // visible and bring to the front to allow file selection. InitPlayerWindow(); ShowWindow(ghApp, SW_SHOWNORMAL); SetForegroundWindow(ghApp); if (! GetClipFileName(szFilename)) { DWORD dwDlgErr = CommDlgExtendedError(); // Don't show output if user cancelled the selection (no dlg error) if (dwDlgErr) { Msg(TEXT("GetClipFileName Failed! Error=0x%x\r\n"), GetLastError()); } return; } // This sample does not support playback of ASX playlists. // Since this could be confusing to a user, display a warning // message if an ASX file was opened. if (_tcsstr((_tcslwr(szFilename)), TEXT(".asx"))) { Msg(TEXT("ASX Playlists are not supported by this application.\n\n") TEXT("Please select a valid media file.\0")); return; } lstrcpy(g_szFileName, szFilename); } // Reset status variables g_psCurrent = Stopped; g_lVolume = VOLUME_FULL; // Start playing the media file hr = PlayMovieInWindow(g_szFileName); // If we couldn't play the clip, clean up if (FAILED(hr)) CloseClip(); } BOOL GetClipFileName(LPTSTR szName) { static OPENFILENAME ofn={0}; static BOOL bSetInitialDir = FALSE; // Reset filename *szName = 0; // Fill in standard structure fields ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = ghApp; ofn.lpstrFilter = NULL; ofn.lpstrFilter = FILE_FILTER_TEXT; ofn.lpstrCustomFilter = NULL; ofn.nFilterIndex = 1; ofn.lpstrFile = szName; ofn.nMaxFile = MAX_PATH; ofn.lpstrTitle = TEXT("Open Media File...\0"); ofn.lpstrFileTitle = NULL; ofn.lpstrDefExt = TEXT("*\0"); ofn.Flags = OFN_FILEMUSTEXIST | OFN_READONLY | OFN_PATHMUSTEXIST; // Remember the path of the first selected file if (bSetInitialDir == FALSE) { ofn.lpstrInitialDir = DEFAULT_MEDIA_PATH; bSetInitialDir = TRUE; } else ofn.lpstrInitialDir = NULL; // Create the standard file open dialog and return its result return GetOpenFileName((LPOPENFILENAME)&ofn); } void CloseClip() { HRESULT hr; // Stop media playback if(pMC) hr = pMC->Stop(); // Clear global flags g_psCurrent = Stopped; g_bAudioOnly = TRUE; g_bFullscreen = FALSE; // Free DirectShow interfaces CloseInterfaces(); // Clear file name to allow selection of new file with open dialog g_szFileName[0] = L'\0'; // No current media state g_psCurrent = Init; // Reset the player window RECT rect; GetClientRect(ghApp, &rect); InvalidateRect(ghApp, &rect, TRUE); UpdateMainTitle(); InitPlayerWindow(); } void CloseInterfaces(void) { HRESULT hr; // Relinquish ownership (IMPORTANT!) after hiding video window if(pVW) { hr = pVW->put_Visible(OAFALSE); hr = pVW->put_Owner(NULL); } // Disable event callbacks if (pME) hr = pME->SetNotifyWindow((OAHWND)NULL, 0, 0); #ifdef REGISTER_FILTERGRAPH if (g_dwGraphRegister) { RemoveGraphFromRot(g_dwGraphRegister); g_dwGraphRegister = 0; } #endif // Release and zero DirectShow interfaces SAFE_RELEASE(pME); SAFE_RELEASE(pMS); SAFE_RELEASE(pMC); SAFE_RELEASE(pBA); SAFE_RELEASE(pBV); SAFE_RELEASE(pVW); SAFE_RELEASE(pFS); SAFE_RELEASE(pGB); } #ifdef REGISTER_FILTERGRAPH HRESULT AddGraphToRot(IUnknown *pUnkGraph, DWORD *pdwRegister) { IMoniker * pMoniker; IRunningObjectTable *pROT; if (FAILED(GetRunningObjectTable(0, &pROT))) { return E_FAIL; } WCHAR wsz[128]; wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR)pUnkGraph, GetCurrentProcessId()); HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker); if (SUCCEEDED(hr)) { hr = pROT->Register(0, pUnkGraph, pMoniker, pdwRegister); pMoniker->Release(); } pROT->Release(); return hr; } void RemoveGraphFromRot(DWORD pdwRegister) { IRunningObjectTable *pROT; if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) { pROT->Revoke(pdwRegister); pROT->Release(); } } #endif void Msg(TCHAR *szFormat, ...) { TCHAR szBuffer[512]; // Large buffer for very long filenames (like HTTP) // Format the input string va_list pArgs; va_start(pArgs, szFormat); _vstprintf(szBuffer, szFormat, pArgs); va_end(pArgs); // Display a message box with the formatted string MessageBox(NULL, szBuffer, TEXT("PlayWndASF Sample"), MB_OK); } HRESULT ToggleMute(void) { HRESULT hr=S_OK; if ((!pGB) || (!pBA)) return S_OK; // Read current volume hr = pBA->get_Volume(&g_lVolume); if (hr == E_NOTIMPL) { // Fail quietly if this is a video-only media file return S_OK; } else if (FAILED(hr)) { Msg(TEXT("Failed to read audio volume! hr=0x%x\r\n"), hr); return hr; } // Switch volume levels if (g_lVolume == VOLUME_FULL) g_lVolume = VOLUME_SILENCE; else g_lVolume = VOLUME_FULL; // Set new volume JIF(pBA->put_Volume(g_lVolume)); UpdateMainTitle(); return hr; } void UpdateMainTitle(void) { TCHAR szTitle[MAX_PATH], szFile[MAX_PATH]; // If no file is loaded, just show the application title if (g_szFileName[0] == L'\0') { wsprintf(szTitle, TEXT("%s"), APPLICATIONNAME); } // Otherwise, show useful information else { // Get file name without full path GetFilename(g_szFileName, szFile); // Update the window title to show filename and play state wsprintf(szTitle, TEXT("%s [%s] %s%s"), szFile, g_bAudioOnly ? TEXT("Audio") : TEXT("Video"), (g_lVolume == VOLUME_SILENCE) ? TEXT("(Muted)") : TEXT(""), (g_psCurrent == Paused) ? TEXT("(Paused)") : TEXT("")); } SetWindowText(ghApp, szTitle); } void GetFilename(TCHAR *pszFull, TCHAR *pszFile) { int nLength; TCHAR szPath[MAX_PATH]={0}; BOOL bSetFilename=FALSE; // Strip path and return just the file's name _tcscpy(szPath, pszFull); nLength = _tcslen(szPath); for (int i=nLength-1; i>=0; i--) { if ((szPath[i] == '\\') || (szPath[i] == '/')) { szPath[i] = '\0'; lstrcpy(pszFile, &szPath[i+1]); bSetFilename = TRUE; break; } } // If there was no path given (just a file name), then // just copy the full path to the target path. if (!bSetFilename) _tcscpy(pszFile, pszFull); } HRESULT ToggleFullScreen(void) { HRESULT hr=S_OK; LONG lMode; static HWND hDrain=0; // Don't bother with full-screen for audio-only files if ((g_bAudioOnly) || (!pVW)) return S_OK; // Read current state JIF(pVW->get_FullScreenMode(&lMode)); if (lMode == OAFALSE) { // Save current message drain LIF(pVW->get_MessageDrain((OAHWND *) &hDrain)); // Set message drain to application main window LIF(pVW->put_MessageDrain((OAHWND) ghApp)); // Switch to full-screen mode lMode = OATRUE; JIF(pVW->put_FullScreenMode(lMode)); g_bFullscreen = TRUE; } else { // Switch back to windowed mode lMode = OAFALSE; JIF(pVW->put_FullScreenMode(lMode)); // Undo change of message drain LIF(pVW->put_MessageDrain((OAHWND) hDrain)); // Reset video window LIF(pVW->SetWindowForeground(-1)); // Reclaim keyboard focus for player application UpdateWindow(ghApp); SetForegroundWindow(ghApp); SetFocus(ghApp); g_bFullscreen = FALSE; } return hr; } // // Some video renderers support stepping media frame by frame with the // IVideoFrameStep interface. See the interface documentation for more // details on frame stepping. // BOOL GetFrameStepInterface(void) { HRESULT hr; IVideoFrameStep *pFSTest = NULL; // Get the frame step interface, if supported hr = pGB->QueryInterface(__uuidof(IVideoFrameStep), (PVOID *)&pFSTest); if (FAILED(hr)) return FALSE; // Check if this decoder can step hr = pFSTest->CanStep(0L, NULL); if (hr == S_OK) { pFS = pFSTest; // Save interface to global variable for later use return TRUE; } else { pFSTest->Release(); return FALSE; } } HRESULT StepOneFrame(void) { HRESULT hr=S_OK; // If the Frame Stepping interface exists, use it to step one frame if (pFS) { // The graph must be paused for frame stepping to work if (g_psCurrent != State_Paused) PauseClip(); // Step the requested number of frames, if supported hr = pFS->Step(1, NULL); } return hr; } HRESULT StepFrames(int nFramesToStep) { HRESULT hr=S_OK; // If the Frame Stepping interface exists, use it to step frames if (pFS) { // The renderer may not support frame stepping for more than one // frame at a time, so check for support. S_OK indicates that the // renderer can step nFramesToStep successfully. if ((hr = pFS->CanStep(nFramesToStep, NULL)) == S_OK) { // The graph must be paused for frame stepping to work if (g_psCurrent != State_Paused) PauseClip(); // Step the requested number of frames, if supported hr = pFS->Step(nFramesToStep, NULL); } } return hr; } HRESULT HandleGraphEvent(void) { LONG evCode, evParam1, evParam2; HRESULT hr=S_OK; // Make sure that we don't access the media event interface // after it has already been released. if (!pME) return S_OK; // Process all queued events while(SUCCEEDED(pME->GetEvent(&evCode, (LONG_PTR *) &evParam1, (LONG_PTR *) &evParam2, 0))) { // Free memory associated with callback, since we're not using it hr = pME->FreeEventParams(evCode, evParam1, evParam2); // If this is the end of the clip, reset to beginning if(EC_COMPLETE == evCode) { LONGLONG pos=0; // Reset to first frame of movie hr = pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning , NULL, AM_SEEKING_NoPositioning); if (FAILED(hr)) { // Some custom filters (like the Windows CE MIDI filter) // may not implement seeking interfaces (IMediaSeeking) // to allow seeking to the start. In that case, just stop // and restart for the same effect. This should not be // necessary in most cases. if (FAILED(hr = pMC->Stop())) { Msg(TEXT("Failed(0x%08lx) to stop media clip!\r\n"), hr); break; } if (FAILED(hr = pMC->Run())) { Msg(TEXT("Failed(0x%08lx) to reset media clip!\r\n"), hr); break; } } } } return hr; } void CheckSizeMenu(WPARAM wParam) { WPARAM nItems[4] = {ID_FILE_SIZE_HALF, ID_FILE_SIZE_DOUBLE, ID_FILE_SIZE_NORMAL, ID_FILE_SIZE_THREEQUARTER}; // Set/clear checkboxes that indicate the size of the video clip for (int i=0; i<4; i++) { // Check the selected item CheckMenuItem(ghMenu, (UINT) nItems[i], (UINT) (wParam == nItems[i]) ? MF_CHECKED : MF_UNCHECKED); } } void EnablePlaybackMenu(BOOL bEnable) { WPARAM nItems[9] = {ID_FILE_PAUSE, ID_FILE_STOP, ID_FILE_MUTE, ID_SINGLE_STEP, ID_FILE_SIZE_HALF, ID_FILE_SIZE_DOUBLE, ID_FILE_SIZE_NORMAL, ID_FILE_SIZE_THREEQUARTER, ID_FILE_FULLSCREEN}; // Set/clear checkboxes that indicate the size of the video clip for (int i=0; i<9; i++) { // Check the selected item EnableMenuItem(ghMenu, (UINT) nItems[i], (UINT) (bEnable) ? MF_ENABLED : MF_GRAYED); } } LRESULT CALLBACK AboutDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: if (wParam == IDOK) { EndDialog(hWnd, TRUE); return TRUE; } break; } return FALSE; } LRESULT CALLBACK WndMainProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { // Resize the video when the window changes case WM_MOVE: case WM_SIZE: if ((hWnd == ghApp) && (!g_bAudioOnly)) MoveVideoWindow(); break; // Enforce a minimum size case WM_GETMINMAXINFO: { LPMINMAXINFO lpmm = (LPMINMAXINFO) lParam; lpmm->ptMinTrackSize.x = MINIMUM_VIDEO_WIDTH; lpmm->ptMinTrackSize.y = MINIMUM_VIDEO_HEIGHT; } break; case WM_KEYDOWN: switch(toupper((int) wParam)) { // Frame stepping case VK_SPACE: case '1': StepOneFrame(); break; // Frame stepping (multiple frames) case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': StepFrames((int) wParam - '0'); break; case 'P': PauseClip(); break; case 'S': StopClip(); break; case 'M': ToggleMute(); break; case 'F': case VK_RETURN: ToggleFullScreen(); break; case 'H': InitVideoWindow(1,2); CheckSizeMenu(wParam); break; case 'N': InitVideoWindow(1,1); CheckSizeMenu(wParam); break; case 'D': InitVideoWindow(2,1); CheckSizeMenu(wParam); break; case 'T': InitVideoWindow(3,4); CheckSizeMenu(wParam); break; case VK_ESCAPE: if (g_bFullscreen) ToggleFullScreen(); else CloseClip(); break; case VK_F12: case 'Q': case 'X': CloseClip(); break; } break; case WM_COMMAND: switch(wParam) { // Menus case ID_FILE_OPENCLIP: // If we have ANY file open, close it and shut down DShow if (g_psCurrent != Init) CloseClip(); // Open the new clip OpenClip(); break; case ID_FILE_EXIT: CloseClip(); PostQuitMessage(0); break; case ID_FILE_PAUSE: PauseClip(); break; case ID_FILE_STOP: StopClip(); break; case ID_FILE_CLOSE: CloseClip(); break; case ID_FILE_MUTE: ToggleMute(); break; case ID_FILE_FULLSCREEN: ToggleFullScreen(); break; case ID_HELP_ABOUT: DialogBox(ghInst, MAKEINTRESOURCE(IDD_ABOUTBOX), ghApp, (DLGPROC) AboutDlgProc); break; case ID_FILE_SIZE_HALF: InitVideoWindow(1,2); CheckSizeMenu(wParam); break; case ID_FILE_SIZE_NORMAL: InitVideoWindow(1,1); CheckSizeMenu(wParam); break; case ID_FILE_SIZE_DOUBLE: InitVideoWindow(2,1); CheckSizeMenu(wParam); break; case ID_FILE_SIZE_THREEQUARTER: InitVideoWindow(3,4); CheckSizeMenu(wParam); break; case ID_SINGLE_STEP: StepOneFrame(); break; case ID_RENDERING_USENEWASFREADER: g_bUseNewASFReader=TRUE; CheckMenuItem(ghMenu, ID_RENDERING_USENEWASFREADER, MF_CHECKED); CheckMenuItem(ghMenu, ID_RENDERING_USELEGACYASFREADER, MF_UNCHECKED); break; case ID_RENDERING_USELEGACYASFREADER: g_bUseNewASFReader=FALSE; CheckMenuItem(ghMenu, ID_RENDERING_USENEWASFREADER, MF_UNCHECKED); CheckMenuItem(ghMenu, ID_RENDERING_USELEGACYASFREADER, MF_CHECKED); break; } // Menus break; case WM_GRAPHNOTIFY: HandleGraphEvent(); break; case WM_CLOSE: SendMessage(ghApp, WM_COMMAND, ID_FILE_EXIT, 0); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } // Window msgs handling // Pass this message to the video window for notification of system changes if (pVW) pVW->NotifyOwnerMessage((LONG_PTR) hWnd, message, wParam, lParam); return DefWindowProc(hWnd, message, wParam, lParam); } int PASCAL WinMain(HINSTANCE hInstC, HINSTANCE hInstP, LPSTR lpCmdLine, int nCmdShow) { MSG msg={0}; WNDCLASS wc; // Initialize COM if(FAILED(CoInitialize(NULL))) { Msg(TEXT("CoInitialize Failed!\r\n")); exit(1); } // Was a filename specified on the command line? if(lpCmdLine[0] != '\0') #ifdef UNICODE MultiByteToWideChar(CP_ACP, 0, lpCmdLine, -1, g_szFileName, MAX_PATH); #else lstrcpy(g_szFileName, lpCmdLine); #endif // Set initial media state g_psCurrent = Init; // Register the window class ZeroMemory(&wc, sizeof wc); wc.lpfnWndProc = WndMainProc; ghInst = wc.hInstance = hInstC; wc.lpszClassName = CLASSNAME; wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU); wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hIcon = LoadIcon(hInstC, MAKEINTRESOURCE(IDI_PLAYWND)); if(!RegisterClass(&wc)) { Msg(TEXT("RegisterClass Failed! Error=0x%x\r\n"), GetLastError()); CoUninitialize(); exit(1); } // Create the main window. The WS_CLIPCHILDREN style is required. ghApp = CreateWindow(CLASSNAME, APPLICATIONNAME, WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, ghInst, 0); if(ghApp) { // Save menu handle for later use ghMenu = GetMenu(ghApp); // Open the specified media file or prompt for a title PostMessage(ghApp, WM_COMMAND, ID_FILE_OPENCLIP, 0); // Main message loop while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } } else { Msg(TEXT("Failed to create the main window! Error=0x%x\r\n"), GetLastError()); } // Finished with COM CoUninitialize(); return (int) msg.wParam; }