Files
Client/Library/dxx8/samples/Multimedia/DirectShow/Players/PlayWndASF/playwndasf.cpp
LGram16 e067522598 Initial commit: ROW Client source code
Game client codebase including:
- CharacterActionControl: Character and creature management
- GlobalScript: Network, items, skills, quests, utilities
- RYLClient: Main client application with GUI and event handlers
- Engine: 3D rendering engine (RYLGL)
- MemoryManager: Custom memory allocation
- Library: Third-party dependencies (DirectX, boost, etc.)
- Tools: Development utilities

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 16:24:34 +09:00

1262 lines
33 KiB
C++

//------------------------------------------------------------------------------
// 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 <dshow.h>
#include <commctrl.h>
#include <commdlg.h>
#include <stdio.h>
#include <tchar.h>
#include <atlbase.h>
#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;
}