2025-11-28 00:35:46 +09:00

367 lines
9.8 KiB
C++

//------------------------------------------------------------------------------
// File: CutScene.cpp
//
// Desc: DirectShow sample code - simple interactive movie player. Plays
// a movie or game cutscene in fullscreen mode. Supports simple user
// input to enable ESC, spacebar, or ENTER to quit.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------
#define _WIN32_WINNT 0x0500
#include <dshow.h>
#include <strsafe.h>
#include "cutscene.h"
//
// Constants
//
#define KEYBOARD_SAMPLE_FREQ 100 // Sample user input on an interval
#define CUTSCENE_NAME TEXT("Cutscene Player Sample\0")
//
// Globals
//
static IGraphBuilder *pGB = NULL;
static IMediaControl *pMC = NULL;
static IVideoWindow *pVW = NULL;
static IMediaEvent *pME = NULL;
static HWND g_hwndMain=0;
static BOOL g_bContinue=TRUE, g_bUserInterruptedPlayback=FALSE;
//
// Function prototypes
//
static HRESULT PlayMedia(LPTSTR lpszMovie, HINSTANCE hInstance);
static HRESULT GetInterfaces(void);
static HRESULT SetFullscreen(void);
static void CloseApp();
static void CleanupInterfaces(void);
static void Msg(TCHAR *szFormat, ...);
static BOOL CreateHiddenWindow( HINSTANCE hInstance, TCHAR *szFile );
static LONG WINAPI WindowProc(HWND, UINT, WPARAM, LPARAM);
//
// Helper Macros (Jump-If-Failed, Log-If-Failed)
//
#define RELEASE(i) {if (i) i->Release(); i = NULL;}
#define JIF(x) if (FAILED(hr=(x))) \
{Msg(TEXT("FAILED(hr=0x%x) in ") TEXT(#x) TEXT("\n\0"), hr); goto CLEANUP;}
#define LIF(x) if (FAILED(hr=(x))) \
{Msg(TEXT("FAILED(hr=0x%x) in ") TEXT(#x) TEXT("\n\0"), hr); return hr;}
HRESULT PlayCutscene(LPTSTR szMovie, HINSTANCE hInstance)
{
HRESULT hr;
if (!szMovie)
return E_POINTER;
// Create the main hidden window to field keyboard input
if (!CreateHiddenWindow(hInstance, szMovie))
return E_FAIL;
// Initialize COM
if (FAILED(hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)))
return hr;
// Get DirectShow interfaces
if (FAILED(hr = GetInterfaces()))
{
CoUninitialize();
return hr;
}
// Play the movie / cutscene
hr = PlayMedia(szMovie, hInstance);
// If the user interrupted playback and there was no other error,
// return S_FALSE.
if ((hr == S_OK) && g_bUserInterruptedPlayback)
hr = S_FALSE;
// Release DirectShow interfaces
CleanupInterfaces();
CoUninitialize();
return hr;
}
BOOL CreateHiddenWindow( HINSTANCE hInstance, TCHAR *szFile )
{
TCHAR szTitle[MAX_PATH + sizeof(CUTSCENE_NAME) + 5];
// Set up and register window class
WNDCLASS wc = {0};
wc.lpfnWndProc = (WNDPROC) WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CUTSCENE_NAME;
if (!RegisterClass(&wc))
return FALSE;
// Prevent buffer overrun by restricting size of title to MAX_PATH
(void)StringCchPrintf(szTitle, NUMELMS(szTitle), TEXT("%s: \0"), CUTSCENE_NAME);
StringCchCatN(szTitle, NUMELMS(szTitle), szFile, MAX_PATH);
// Create a window of zero size that will serve as the sink for
// keyboard input. If this media file has a video component, then
// a second ActiveMovie window will be displayed in which the video
// will be rendered. Setting keyboard focus on this application window
// will allow the user to move the video window around the screen, make
// it full screen, resize, center, etc. independent of the application
// window. If the media file has only an audio component, then this will
// be the only window created.
g_hwndMain = CreateWindowEx(
0, CUTSCENE_NAME, szTitle,
0, // not visible
0, 0, 0, 0,
NULL, NULL, hInstance, NULL );
return (g_hwndMain != NULL);
}
LONG WINAPI WindowProc( HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam )
{
switch( message )
{
// Monitor keystrokes for manipulating video window
// and program options
case WM_KEYDOWN:
switch( wParam )
{
case VK_ESCAPE:
case VK_SPACE:
case VK_RETURN:
g_bUserInterruptedPlayback = TRUE;
CloseApp();
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
// Pass this message to the video window for notification of system changes
if (pVW)
pVW->NotifyOwnerMessage((LONG_PTR) hWnd, message, wParam, lParam);
return (LONG) DefWindowProc(hWnd, message, wParam, lParam);
}
HRESULT GetInterfaces(void)
{
HRESULT hr = S_OK;
// Instantiate filter graph interface
JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
IID_IGraphBuilder, (void **)&pGB));
// Get interfaces to control playback & screensize
JIF(pGB->QueryInterface(IID_IMediaControl, (void **)&pMC));
JIF(pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW));
// Get interface to allow the app to wait for completion of playback
JIF(pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME));
return S_OK;
// In case of failure, the helper macro jumps here
CLEANUP:
CleanupInterfaces();
return(hr);
}
void CleanupInterfaces(void)
{
// Release the DirectShow interfaces
RELEASE(pGB);
RELEASE(pMC);
RELEASE(pVW);
RELEASE(pME);
DestroyWindow(g_hwndMain);
}
void CloseApp()
{
// Stop playback and exit
if (pMC)
pMC->Stop();
g_bContinue = FALSE;
PostMessage(g_hwndMain, WM_CLOSE, 0, 0);
}
HRESULT PlayMedia(LPTSTR lpszMovie, HINSTANCE hInstance)
{
HRESULT hr = S_OK;
BOOL bSleep=TRUE;
if (!lpszMovie)
return E_POINTER;
// Allow DirectShow to create the FilterGraph for this media file
hr = pGB->RenderFile(lpszMovie, NULL);
if (FAILED(hr)) {
Msg(TEXT("Failed(0x%08lx) in RenderFile(%s)!\r\n"), hr, lpszMovie);
return hr;
}
// Set the message drain of the video window to point to our hidden
// application window. This allows keyboard input to be transferred
// to our main window for processing.
//
// If this is an audio-only or MIDI file, then put_MessageDrain will fail.
//
hr = pVW->put_MessageDrain((OAHWND) g_hwndMain);
if (FAILED(hr))
{
Msg(TEXT("Failed(0x%08lx) to set message drain for %s.\r\n\r\n")
TEXT("This sample is designed to play videos, but the file selected ")
TEXT("has no video component."), hr, lpszMovie);
return hr;
}
// Set fullscreen
hr = SetFullscreen();
if (FAILED(hr)) {
Msg(TEXT("Failed(%08lx) to set fullscreen!\r\n"), hr);
return hr;
}
// Display first frame of the movie
hr = pMC->Pause();
if (FAILED(hr)) {
Msg(TEXT("Failed(%08lx) in Pause()!\r\n"), hr);
return hr;
}
// Start playback
hr = pMC->Run();
if (FAILED(hr)) {
Msg(TEXT("Failed(%08lx) in Run()!\r\n"), hr);
return hr;
}
// Update state variables
g_bContinue = TRUE;
// Enter a loop of checking for events and sampling keyboard input
while (g_bContinue)
{
MSG msg;
long lEventCode;
LONG_PTR lpParam1, lpParam2;
// Reset sleep flag
bSleep = TRUE;
// Has there been a media event? Look for end of stream condition.
if(E_ABORT != pME->GetEvent(&lEventCode, &lpParam1,
&lpParam2, 0))
{
// Is this the end of the movie?
if (lEventCode == EC_COMPLETE)
{
g_bContinue = FALSE;
bSleep = FALSE;
}
// Free the media event resources
hr = pME->FreeEventParams(lEventCode, lpParam1, lpParam2);
if (FAILED(hr))
{
Msg(TEXT("Failed(%08lx) to free event params (%s)!\r\n"),
hr, lpszMovie);
}
}
// Give system threads time to run (and don't sample user input madly)
if (bSleep)
Sleep(KEYBOARD_SAMPLE_FREQ);
// Check and process window messages (like our keystrokes)
while (PeekMessage (&msg, g_hwndMain, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return hr;
}
HRESULT SetFullscreen(void)
{
HRESULT hr=S_OK;
LONG lMode;
static HWND hDrain=0;
if (!pVW)
return S_FALSE;
// Read current state
LIF(pVW->get_FullScreenMode(&lMode));
if (lMode == 0) /* OAFALSE */
{
// Save current message drain
LIF(pVW->get_MessageDrain((OAHWND *) &hDrain));
// Set message drain to application main window
LIF(pVW->put_MessageDrain((OAHWND) g_hwndMain));
// Switch to full-screen mode
lMode = -1; /* OATRUE */
LIF(pVW->put_FullScreenMode(lMode));
}
return hr;
}
void Msg(TCHAR *szFormat, ...)
{
TCHAR szBuffer[1024]; // Large buffer for long filenames or URLs
const size_t NUMCHARS = sizeof(szBuffer) / sizeof(szBuffer[0]);
const int LASTCHAR = NUMCHARS - 1;
// Format the input string
va_list pArgs;
va_start(pArgs, szFormat);
// Use a bounded buffer size to prevent buffer overruns. Limit count to
// character size minus one to allow for a NULL terminating character.
(void)StringCchVPrintf(szBuffer, NUMCHARS - 1, szFormat, pArgs);
va_end(pArgs);
// Ensure that the formatted string is NULL-terminated
szBuffer[LASTCHAR] = TEXT('\0');
// This sample uses a simple message box to convey warning and error
// messages. You may want to display a debug string or suppress messages
// altogether, depending on your application.
MessageBox(NULL, szBuffer, TEXT("PlayCutscene Error"), MB_OK);
}