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>
1140 lines
30 KiB
C++
1140 lines
30 KiB
C++
//------------------------------------------------------------------------------
|
|
// File: PlayDMODlg.cpp
|
|
//
|
|
// Desc: DirectShow sample code - Implementation for CPlayDMODlg
|
|
//
|
|
// Copyright (c) 2000-2001 Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "stdafx.h"
|
|
#include "PlayDMO.h"
|
|
#include "PlayDMODlg.h"
|
|
|
|
#include <mtype.h>
|
|
|
|
#define DEFAULT_FILENAME TEXT("c:\\")
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CAboutDlg dialog used for App About
|
|
|
|
class CAboutDlg : public CDialog
|
|
{
|
|
public:
|
|
CAboutDlg();
|
|
|
|
// Dialog Data
|
|
//{{AFX_DATA(CAboutDlg)
|
|
enum { IDD = IDD_ABOUTBOX };
|
|
//}}AFX_DATA
|
|
|
|
// ClassWizard generated virtual function overrides
|
|
//{{AFX_VIRTUAL(CAboutDlg)
|
|
protected:
|
|
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
|
|
//}}AFX_VIRTUAL
|
|
|
|
// Implementation
|
|
protected:
|
|
//{{AFX_MSG(CAboutDlg)
|
|
//}}AFX_MSG
|
|
DECLARE_MESSAGE_MAP()
|
|
};
|
|
|
|
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
|
|
{
|
|
//{{AFX_DATA_INIT(CAboutDlg)
|
|
//}}AFX_DATA_INIT
|
|
}
|
|
|
|
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
CDialog::DoDataExchange(pDX);
|
|
//{{AFX_DATA_MAP(CAboutDlg)
|
|
//}}AFX_DATA_MAP
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
|
|
//{{AFX_MSG_MAP(CAboutDlg)
|
|
// No message handlers
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CPlayDMODlg dialog
|
|
|
|
CPlayDMODlg::CPlayDMODlg(CWnd* pParent /*=NULL*/)
|
|
: CDialog(CPlayDMODlg::IDD, pParent), m_pVW(0), m_pGB(0), m_pMC(0)
|
|
{
|
|
//{{AFX_DATA_INIT(CPlayDMODlg)
|
|
//}}AFX_DATA_INIT
|
|
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
|
|
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
|
|
}
|
|
|
|
void CPlayDMODlg::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
CDialog::DoDataExchange(pDX);
|
|
//{{AFX_DATA_MAP(CPlayDMODlg)
|
|
DDX_Control(pDX, IDC_BUTTON_PROPPAGE, m_btnProperties);
|
|
DDX_Control(pDX, IDC_STATIC_STATUS, m_strStatus);
|
|
DDX_Control(pDX, IDC_STATIC_OUT_MINBUFFERSIZE, m_nOutBufferSize);
|
|
DDX_Control(pDX, IDC_STATIC_OUT_ALIGNMENT, m_nOutAlignment);
|
|
DDX_Control(pDX, IDC_STATIC_MAXLATENCY, m_nMaxLatency);
|
|
DDX_Control(pDX, IDC_STATIC_IN_MINBUFFERSIZE, m_nInBufferSize);
|
|
DDX_Control(pDX, IDC_STATIC_IN_MAXLOOKAHEAD, m_nInLookahead);
|
|
DDX_Control(pDX, IDC_STATIC_IN_ALIGNMENT, m_nInAlignment);
|
|
DDX_Control(pDX, IDC_LIST_FILTERS, m_ListFilters);
|
|
DDX_Control(pDX, IDC_LIST_FILTER_OUTPUTS, m_ListFilterOutputs);
|
|
DDX_Control(pDX, IDC_LIST_FILTER_INPUTS, m_ListFilterInputs);
|
|
DDX_Control(pDX, IDC_LIST_AUDIO_DMOS, m_ListAudioDMO);
|
|
DDX_Control(pDX, IDC_EDIT_FILENAME, m_StrFilename);
|
|
DDX_Control(pDX, IDC_BUTTON_STOP, m_btnStop);
|
|
DDX_Control(pDX, IDC_BUTTON_PLAY, m_btnPlay);
|
|
DDX_Control(pDX, IDC_BUTTON_PAUSE, m_btnPause);
|
|
DDX_Control(pDX, IDC_SCREEN, m_Screen);
|
|
//}}AFX_DATA_MAP
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CPlayDMODlg, CDialog)
|
|
//{{AFX_MSG_MAP(CPlayDMODlg)
|
|
ON_WM_ERASEBKGND()
|
|
ON_WM_SYSCOMMAND()
|
|
ON_WM_PAINT()
|
|
ON_WM_QUERYDRAGICON()
|
|
ON_WM_CLOSE()
|
|
ON_WM_DESTROY()
|
|
ON_BN_CLICKED(IDC_BUTTON_PLAY, OnButtonPlay)
|
|
ON_BN_CLICKED(IDC_BUTTON_PAUSE, OnButtonPause)
|
|
ON_BN_CLICKED(IDC_BUTTON_STOP, OnButtonStop)
|
|
ON_BN_CLICKED(IDC_BUTTON_FILE, OnButtonFile)
|
|
ON_LBN_SELCHANGE(IDC_LIST_FILTERS, OnSelchangeListFilters)
|
|
ON_BN_CLICKED(IDC_BUTTON_ADD_DMO, OnButtonAddDmo)
|
|
ON_BN_CLICKED(IDC_BUTTON_CLEAR, OnButtonClear)
|
|
ON_BN_CLICKED(IDC_BUTTON_PROPPAGE, OnButtonProppage)
|
|
ON_LBN_DBLCLK(IDC_LIST_FILTERS, OnDblclkListFilters)
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CPlayDMODlg message handlers
|
|
|
|
void CPlayDMODlg::OnSysCommand(UINT nID, LPARAM lParam)
|
|
{
|
|
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
|
|
{
|
|
CAboutDlg dlgAbout;
|
|
dlgAbout.DoModal();
|
|
}
|
|
else
|
|
{
|
|
CDialog::OnSysCommand(nID, lParam);
|
|
}
|
|
}
|
|
|
|
// If you add a minimize button to your dialog, you will need the code below
|
|
// to draw the icon. For MFC applications using the document/view model,
|
|
// this is automatically done for you by the framework.
|
|
|
|
void CPlayDMODlg::OnPaint()
|
|
{
|
|
if (IsIconic())
|
|
{
|
|
CPaintDC dc(this); // device context for painting
|
|
|
|
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
|
|
|
|
// Center icon in client rectangle
|
|
int cxIcon = GetSystemMetrics(SM_CXICON);
|
|
int cyIcon = GetSystemMetrics(SM_CYICON);
|
|
CRect rect;
|
|
GetClientRect(&rect);
|
|
int x = (rect.Width() - cxIcon + 1) / 2;
|
|
int y = (rect.Height() - cyIcon + 1) / 2;
|
|
|
|
// Draw the icon
|
|
dc.DrawIcon(x, y, m_hIcon);
|
|
}
|
|
else
|
|
{
|
|
CDialog::OnPaint();
|
|
}
|
|
}
|
|
|
|
// The system calls this to obtain the cursor to display while the user drags
|
|
// the minimized window.
|
|
HCURSOR CPlayDMODlg::OnQueryDragIcon()
|
|
{
|
|
return (HCURSOR) m_hIcon;
|
|
}
|
|
|
|
|
|
|
|
BOOL CPlayDMODlg::OnInitDialog()
|
|
{
|
|
CDialog::OnInitDialog();
|
|
|
|
// Add "About..." menu item to system menu.
|
|
|
|
// IDM_ABOUTBOX must be in the system command range.
|
|
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
|
|
ASSERT(IDM_ABOUTBOX < 0xF000);
|
|
|
|
CMenu* pSysMenu = GetSystemMenu(FALSE);
|
|
if (pSysMenu != NULL)
|
|
{
|
|
CString strAboutMenu;
|
|
strAboutMenu.LoadString(IDS_ABOUTBOX);
|
|
if (!strAboutMenu.IsEmpty())
|
|
{
|
|
pSysMenu->AppendMenu(MF_SEPARATOR);
|
|
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
|
|
}
|
|
}
|
|
|
|
// Set the icon for this dialog. The framework does this automatically
|
|
// when the application's main window is not a dialog
|
|
SetIcon(m_hIcon, TRUE); // Set big icon
|
|
SetIcon(m_hIcon, FALSE); // Set small icon
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DirectShow-specific initialization code
|
|
|
|
CoInitialize(NULL);
|
|
SetDefaults();
|
|
|
|
// Initialize DirectShow and query for needed interfaces
|
|
HRESULT hr = InitDirectShow();
|
|
if(FAILED(hr))
|
|
{
|
|
RetailOutput(TEXT("Failed to initialize DirectShow! hr=0x%x\r\n"), hr);
|
|
return FALSE;
|
|
}
|
|
|
|
// IMPORTANT
|
|
// Since we're embedding video in a child window of a dialog,
|
|
// we must set the WS_CLIPCHILDREN style to prevent the bounding
|
|
// rectangle from drawing over our video frames.
|
|
//
|
|
// Neglecting to set this style can lead to situations when the video
|
|
// is erased and replaced with black (or the default color of the
|
|
// bounding rectangle).
|
|
m_Screen.ModifyStyle(0, WS_CLIPCHILDREN);
|
|
|
|
hr = FillLists();
|
|
if(FAILED(hr))
|
|
{
|
|
RetailOutput(TEXT("Failed to fill DMO list! hr=0x%x\r\n"), hr);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE; // return TRUE unless you set the focus to a control
|
|
}
|
|
|
|
void CPlayDMODlg::SetDefaults()
|
|
{
|
|
// Zero DirectShow interface pointers (sanity check)
|
|
m_pGB=0;
|
|
m_pMS=0;
|
|
m_pMC=0;
|
|
m_pME=0;
|
|
m_pVW=0;
|
|
|
|
// Zero DMO list
|
|
for (int i=0; i<MAX_DMOS; i++)
|
|
m_pDMOList[i] = 0;
|
|
|
|
// Set default values for controls on the dialog
|
|
m_StrFilename.SetWindowText(DEFAULT_FILENAME);
|
|
g_psCurrent = State_Stopped;
|
|
}
|
|
|
|
|
|
HRESULT CPlayDMODlg::FillLists()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Enumerate and display the audio DMOs installed in the system
|
|
hr = AddDMOsToList(&DMOCATEGORY_AUDIO_EFFECT, m_ListAudioDMO, TRUE);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Set internal variables
|
|
m_nLoadedDMOs = 0;
|
|
m_nDMOCount = m_ListAudioDMO.GetCount();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Displays a text string in a status line near the bottom of the dialog
|
|
//
|
|
void CPlayDMODlg::Say(LPTSTR szText)
|
|
{
|
|
m_strStatus.SetWindowText(szText);
|
|
}
|
|
|
|
HRESULT CPlayDMODlg::InitDirectShow(void)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
m_bAudioOnly = FALSE;
|
|
|
|
// Get interfaces
|
|
JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
|
|
IID_IGraphBuilder, (void **)&m_pGB));
|
|
|
|
JIF(m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC));
|
|
JIF(m_pGB->QueryInterface(IID_IMediaSeeking, (void **)&m_pMS));
|
|
JIF(m_pGB->QueryInterface(IID_IVideoWindow, (void **)&m_pVW));
|
|
JIF(m_pGB->QueryInterface(IID_IMediaEventEx, (void **)&m_pME));
|
|
|
|
return S_OK;
|
|
|
|
CLEANUP:
|
|
FreeDirectShow();
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT CPlayDMODlg::FreeDirectShow(void)
|
|
{
|
|
HRESULT hr=S_OK;
|
|
|
|
StopMedia();
|
|
|
|
// Disable event callbacks
|
|
if (m_pME)
|
|
hr = m_pME->SetNotifyWindow((OAHWND)NULL, 0, 0);
|
|
|
|
// Hide video window and remove owner. This is not necessary here,
|
|
// since we are about to destroy the filter graph, but it is included
|
|
// for demonstration purposes. Remember to hide the video window and
|
|
// clear its owner when destroying a window that plays video.
|
|
if(m_pVW)
|
|
{
|
|
hr = m_pVW->put_Visible(OAFALSE);
|
|
hr = m_pVW->put_Owner(NULL);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
RemoveGraphFromRot(m_dwRegister);
|
|
#endif
|
|
|
|
RemoveDMOsFromGraph();
|
|
|
|
SAFE_RELEASE(m_pMC);
|
|
SAFE_RELEASE(m_pMS);
|
|
SAFE_RELEASE(m_pVW);
|
|
SAFE_RELEASE(m_pME);
|
|
SAFE_RELEASE(m_pGB);
|
|
|
|
ClearLists();
|
|
return hr;
|
|
}
|
|
|
|
void CPlayDMODlg::ResetDirectShow(void)
|
|
{
|
|
// Destroy the current filter graph its filters.
|
|
FreeDirectShow();
|
|
|
|
// Reinitialize graph builder and query for interfaces
|
|
InitDirectShow();
|
|
}
|
|
|
|
void CPlayDMODlg::ClearAllocatedLists()
|
|
{
|
|
// Clear the lists and delete the allocated CLSID bytes
|
|
ClearFilterListWithCLSID(m_ListAudioDMO);
|
|
}
|
|
|
|
void CPlayDMODlg::ClearLists()
|
|
{
|
|
// Clear the lists that don't contain extra data
|
|
m_ListFilters.ResetContent();
|
|
m_ListFilterInputs.ResetContent();
|
|
m_ListFilterOutputs.ResetContent();
|
|
}
|
|
|
|
void CPlayDMODlg::OnClose()
|
|
{
|
|
// Release DirectShow interfaces
|
|
StopMedia();
|
|
FreeDirectShow();
|
|
ClearAllocatedLists();
|
|
|
|
// Release COM
|
|
CoUninitialize();
|
|
|
|
CDialog::OnClose();
|
|
}
|
|
|
|
void CPlayDMODlg::OnDestroy()
|
|
{
|
|
// Release DirectShow interfaces
|
|
FreeDirectShow();
|
|
ClearAllocatedLists();
|
|
|
|
CDialog::OnDestroy();
|
|
}
|
|
|
|
void CPlayDMODlg::OnButtonPlay()
|
|
{
|
|
if (g_psCurrent == State_Paused)
|
|
{
|
|
RunMedia();
|
|
}
|
|
else
|
|
{
|
|
if (SUCCEEDED(OnSelectFile()))
|
|
{
|
|
// Add any selected DMOs to the graph
|
|
OnButtonAddDmo();
|
|
|
|
// Disconnect audio renderer and insert DMOs
|
|
if (m_nLoadedDMOs != 0)
|
|
{
|
|
if (FAILED(ConnectDMOsToRenderer()))
|
|
{
|
|
MessageBeep(0);
|
|
EnableButtons(FALSE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
EnableButtons(FALSE);
|
|
RunMedia();
|
|
}
|
|
else
|
|
{
|
|
MessageBox(TEXT("Please enter a valid media file name."),
|
|
TEXT("Error opening media file!"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
Say(TEXT("Running"));
|
|
}
|
|
|
|
void CPlayDMODlg::OnButtonPause()
|
|
{
|
|
PauseMedia();
|
|
Say(TEXT("Paused"));
|
|
}
|
|
|
|
void CPlayDMODlg::OnButtonStop()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Stop playback immediately with IMediaControl::Stop().
|
|
StopMedia();
|
|
EnableButtons(TRUE);
|
|
|
|
// Reset to beginning of media clip
|
|
LONGLONG pos=0;
|
|
hr = m_pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
|
|
NULL, AM_SEEKING_NoPositioning);
|
|
if (FAILED(hr))
|
|
{
|
|
RetailOutput(TEXT("Failed to seek to beginning of media! hr=0x%x\r\n"), hr);
|
|
}
|
|
|
|
// Display the first frame of the media clip, if it contains video.
|
|
// StopWhenReady() pauses all filters internally (which allows the video
|
|
// renderer to queue and display the first video frame), after which
|
|
// it sets the filters to the stopped state. This enables easy preview
|
|
// of the video's poster frame.
|
|
hr = m_pMC->StopWhenReady();
|
|
if (FAILED(hr))
|
|
{
|
|
RetailOutput(TEXT("Failed in StopWhenReady! hr=0x%x\r\n"), hr);
|
|
}
|
|
|
|
Say(TEXT("Stopped"));
|
|
}
|
|
|
|
void CPlayDMODlg::OnButtonFile()
|
|
{
|
|
CFileDialog dlgFile(TRUE);
|
|
CString title;
|
|
CString strFilter, strDefault, strFilename;
|
|
|
|
VERIFY(title.LoadString(AFX_IDS_OPENFILE));
|
|
|
|
// Initialize the file extensions and descriptions
|
|
strFilter += "Media Files (*.avi, *.mpg, *.wav, *.mid)";
|
|
strFilter += (TCHAR)'\0';
|
|
strFilter += _T("*.avi;*.mpg;*.wav;*.mid");
|
|
strFilter += (TCHAR)'\0';
|
|
dlgFile.m_ofn.nMaxCustFilter++;
|
|
|
|
CString allFilter;
|
|
VERIFY(allFilter.LoadString(AFX_IDS_ALLFILTER));
|
|
|
|
// Append the "*.*" all files filter
|
|
strFilter += allFilter;
|
|
strFilter += (TCHAR)'\0'; // next string
|
|
strFilter += _T("*.*");
|
|
strFilter += (TCHAR)'\0\0'; // last string
|
|
dlgFile.m_ofn.nMaxCustFilter++;
|
|
|
|
dlgFile.m_ofn.lpstrFilter = strFilter;
|
|
dlgFile.m_ofn.lpstrTitle = title;
|
|
dlgFile.m_ofn.lpstrFile = strFilename.GetBuffer(_MAX_PATH);
|
|
|
|
// Display the file open dialog
|
|
INT_PTR nResult = dlgFile.DoModal();
|
|
|
|
// If a file was selected, update the main dialog
|
|
if (nResult == IDOK)
|
|
{
|
|
m_StrFilename.SetWindowText(strFilename);
|
|
|
|
// Render this file and show the first video frame, if present
|
|
OnSelectFile();
|
|
}
|
|
|
|
strFilename.ReleaseBuffer();
|
|
}
|
|
|
|
|
|
void CPlayDMODlg::OnButtonAddDmo()
|
|
{
|
|
// Release any currently loaded DMOs
|
|
RemoveDMOsFromGraph();
|
|
|
|
// Add the currently selected DMOs to the graph
|
|
AddDMOsToGraph();
|
|
|
|
// Update the graph filter display to show rendered graph
|
|
AddGraphFiltersToList(m_pGB, m_ListFilters);
|
|
}
|
|
|
|
void CPlayDMODlg::OnButtonClear()
|
|
{
|
|
// Release any currently loaded DMOs
|
|
RemoveDMOsFromGraph();
|
|
|
|
// Clear the list of active DMOs
|
|
m_ListAudioDMO.SelItemRange(FALSE, 0, m_nDMOCount);
|
|
|
|
// Update the graph filter display to show rendered graph
|
|
AddGraphFiltersToList(m_pGB, m_ListFilters);
|
|
}
|
|
|
|
HRESULT CPlayDMODlg::RemoveDMOsFromGraph(void)
|
|
{
|
|
HRESULT hr=S_OK;
|
|
|
|
// Remove and release each loaded DMO
|
|
for (int i=0; i < m_nLoadedDMOs; i++)
|
|
{
|
|
if (m_pDMOList[i])
|
|
{
|
|
// Remove filter from graph.
|
|
hr = m_pGB->RemoveFilter(m_pDMOList[i]);
|
|
|
|
// Since we're not holding a reference to the filter,
|
|
// there's no need to Release() it, but do clear the variable.
|
|
m_pDMOList[i]=0;
|
|
}
|
|
}
|
|
|
|
m_nLoadedDMOs = 0;
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CPlayDMODlg::AddDMOsToGraph(void)
|
|
{
|
|
USES_CONVERSION;
|
|
HRESULT hr=S_OK;
|
|
IBaseFilter *pDMOFilter=0;
|
|
IDMOWrapperFilter *pWrap;
|
|
CLSID *pStoredCLSID = NULL;
|
|
int rnItems[MAX_DMOS];
|
|
WCHAR wszDMOName[64];
|
|
TCHAR szItem[64];
|
|
|
|
// Clear status
|
|
m_nLoadedDMOs = 0;
|
|
|
|
// Get a list of the DMO entries selected in the multiple-select listbox
|
|
int nSelected = m_ListAudioDMO.GetSelItems(MAX_DMOS, rnItems);
|
|
|
|
// Load each selected DMO and add it to the graph
|
|
for (int i=0; i < nSelected; i++)
|
|
{
|
|
// Read this DMO's CLSID from the list box
|
|
pStoredCLSID = (CLSID *) m_ListAudioDMO.GetItemDataPtr(rnItems[i]);
|
|
|
|
// Create the DMO Wrapper filter.
|
|
hr = CoCreateInstance(CLSID_DMOWrapperFilter, NULL, CLSCTX_INPROC,
|
|
IID_IBaseFilter, (void **)&pDMOFilter);
|
|
if (FAILED(hr))
|
|
continue;
|
|
|
|
// Get its DMOWrapperFilter interface so that it can be added to
|
|
// the DirectShow filter graph
|
|
hr = pDMOFilter->QueryInterface(IID_IDMOWrapperFilter, (void **)&pWrap);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Initialize the wrapper filter with our DMO's CLSID and category
|
|
hr = pWrap->Init(*pStoredCLSID, DMOCATEGORY_AUDIO_EFFECT);
|
|
if (FAILED(hr))
|
|
{
|
|
pWrap->Release();
|
|
pDMOFilter->Release();
|
|
continue;
|
|
}
|
|
|
|
pWrap->Release();
|
|
|
|
// Read the DMO's name and convert to a wide string
|
|
m_ListAudioDMO.GetText(rnItems[i], szItem);
|
|
wcscpy(wszDMOName, T2W(szItem));
|
|
|
|
// Add the DMO to the graph
|
|
hr = m_pGB->AddFilter(pDMOFilter, wszDMOName);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// If this filter was added to the graph, save its reference
|
|
// and increment the count of loaded DMOs
|
|
m_pDMOList[i] = pDMOFilter;
|
|
m_nLoadedDMOs++;
|
|
}
|
|
|
|
pDMOFilter->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
void CPlayDMODlg::OnSelchangeListFilters()
|
|
{
|
|
IBaseFilter *pFilter = NULL;
|
|
TCHAR szNameToFind[128];
|
|
|
|
// Use a helper function to display the filter's pins in listboxes
|
|
AddFilterPinsToLists(m_pGB, m_ListFilters, m_ListFilterInputs, m_ListFilterOutputs);
|
|
|
|
// Read the current filter name from the list box
|
|
m_ListFilters.GetText(m_ListFilters.GetCurSel(), szNameToFind);
|
|
|
|
// Read the current list box name and find it in the graph.
|
|
// Display DMO-specific information if present in the graph.
|
|
pFilter = FindFilterFromName(m_pGB, szNameToFind);
|
|
if (pFilter)
|
|
{
|
|
HRESULT hr;
|
|
IMediaObject *pDMO=NULL;
|
|
|
|
// Find out if this filter supports a property page
|
|
if (SupportsPropertyPage(pFilter))
|
|
m_btnProperties.EnableWindow(TRUE);
|
|
else
|
|
m_btnProperties.EnableWindow(FALSE);
|
|
|
|
// If this is a DMO, it will support IMediaObject, so show more info.
|
|
hr = pFilter->QueryInterface(IID_IMediaObject, (void**) &pDMO);
|
|
|
|
// Show information or default values
|
|
ShowInputBufferInfo(pDMO, 0);
|
|
ShowOutputBufferInfo(pDMO, 0);
|
|
|
|
if (SUCCEEDED(hr))
|
|
pDMO->Release();
|
|
|
|
pFilter->Release();
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT CPlayDMODlg::OnSelectFile()
|
|
{
|
|
HRESULT hr;
|
|
TCHAR szFilename[MAX_PATH];
|
|
|
|
// Read file name from list box
|
|
m_StrFilename.GetWindowText(szFilename, MAX_PATH);
|
|
|
|
// First release any existing interfaces
|
|
ResetDirectShow();
|
|
|
|
// Clear filter/pin/event information listboxes
|
|
m_ListFilters.ResetContent();
|
|
m_ListFilterInputs.ResetContent();
|
|
m_ListFilterOutputs.ResetContent();
|
|
|
|
// Load the selected media file
|
|
hr = PrepareMedia(szFilename);
|
|
if (FAILED(hr))
|
|
{
|
|
MessageBeep(0);
|
|
return hr;
|
|
}
|
|
else
|
|
{
|
|
m_btnPlay.EnableWindow(TRUE);
|
|
}
|
|
|
|
// Enumerate and display filters in graph
|
|
hr = AddGraphFiltersToList(m_pGB, m_ListFilters);
|
|
|
|
// Select the first filter in the list to display pin info
|
|
m_ListFilters.SetCurSel(0);
|
|
OnSelchangeListFilters();
|
|
|
|
// Cue the first video frame
|
|
m_pMC->StopWhenReady();
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CPlayDMODlg::PrepareMedia(LPTSTR lpszMovie)
|
|
{
|
|
USES_CONVERSION;
|
|
HRESULT hr = S_OK;
|
|
|
|
Say(TEXT("Loading..."));
|
|
|
|
// Allow DirectShow to create the FilterGraph for this media file
|
|
hr = m_pGB->RenderFile(T2W(lpszMovie), NULL);
|
|
if (FAILED(hr)) {
|
|
RetailOutput(TEXT("*** Failed(%08lx) in RenderFile(%s)!\r\n"),
|
|
hr, lpszMovie);
|
|
return hr;
|
|
}
|
|
|
|
// Add our filter graph to the running object table for debugging
|
|
#ifdef DEBUG
|
|
AddGraphToRot(m_pGB, &m_dwRegister);
|
|
#endif
|
|
|
|
// Set the message drain of the video window to point to our main
|
|
// application window. If this is an audio-only or MIDI file,
|
|
// then put_MessageDrain will fail.
|
|
hr = m_pVW->put_MessageDrain((OAHWND) m_hWnd);
|
|
if (FAILED(hr))
|
|
{
|
|
m_bAudioOnly = TRUE;
|
|
}
|
|
|
|
// Have the graph signal events via window callbacks
|
|
hr = m_pME->SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY, 0);
|
|
|
|
// Configure the video window
|
|
if (!m_bAudioOnly)
|
|
{
|
|
hr = m_pVW->put_Owner((OAHWND) m_Screen.GetSafeHwnd());
|
|
hr = m_pVW->put_WindowStyle(WS_CHILD);
|
|
|
|
// We'll manually set the video to be visible
|
|
hr = m_pVW->put_Visible(OAFALSE);
|
|
|
|
// Place video window within the bounding rectangle
|
|
CenterVideo();
|
|
|
|
// Make the video window visible within the screen window.
|
|
// If this is an audio-only file, then there won't be a video interface.
|
|
hr = m_pVW->put_Visible(OATRUE);
|
|
hr = m_pVW->SetWindowForeground(-1);
|
|
}
|
|
|
|
Say(TEXT("Ready"));
|
|
return hr;
|
|
}
|
|
|
|
void CPlayDMODlg::CenterVideo(void)
|
|
{
|
|
LONG width, height;
|
|
HRESULT hr;
|
|
|
|
if ((m_bAudioOnly) || (!m_pVW))
|
|
return;
|
|
|
|
// Read coordinates of video container window
|
|
RECT rc;
|
|
m_Screen.GetClientRect(&rc);
|
|
width = rc.right - rc.left;
|
|
height = rc.bottom - rc.top;
|
|
|
|
// Ignore the video's original size and stretch to fit bounding rectangle
|
|
hr = m_pVW->SetWindowPosition(rc.left, rc.top, width, height);
|
|
if (FAILED(hr))
|
|
{
|
|
RetailOutput(TEXT("Failed to set window position! hr=0x%x\r\n"), hr);
|
|
return;
|
|
}
|
|
}
|
|
|
|
LRESULT CPlayDMODlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
// Field notifications from the DirectShow filter graph manager
|
|
// and those posted by the application
|
|
switch (message)
|
|
{
|
|
case WM_GRAPHNOTIFY:
|
|
HandleGraphEvent();
|
|
break;
|
|
}
|
|
|
|
// Pass along this message to the video window, which exists as a child
|
|
// of the m_Screen window. This method should be used by windows that
|
|
// make a renderer window a child window. It forwards significant messages
|
|
// to the child window that the child window would not otherwise receive.
|
|
if (m_pVW)
|
|
{
|
|
m_pVW->NotifyOwnerMessage((LONG_PTR) m_hWnd, message, wParam, lParam);
|
|
}
|
|
|
|
return CDialog::WindowProc(message, wParam, lParam);
|
|
}
|
|
|
|
|
|
HRESULT CPlayDMODlg::HandleGraphEvent(void)
|
|
{
|
|
LONG evCode, evParam1, evParam2;
|
|
HRESULT hr=S_OK;
|
|
|
|
// Check that event interface is still valid
|
|
if (!m_pME)
|
|
return S_OK;
|
|
|
|
// Spin through the events
|
|
while(SUCCEEDED(hr = m_pME->GetEvent(&evCode, (LONG_PTR *) &evParam1,
|
|
(LONG_PTR *) &evParam2, 0)))
|
|
{
|
|
if(EC_COMPLETE == evCode)
|
|
{
|
|
// Since we're looping, reset to beginning and continue playing
|
|
LONGLONG pos=0;
|
|
hr = m_pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
|
|
NULL, AM_SEEKING_NoPositioning);
|
|
}
|
|
|
|
// Free memory associated with this event
|
|
hr = m_pME->FreeEventParams(evCode, evParam1, evParam2);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CPlayDMODlg::RunMedia()
|
|
{
|
|
HRESULT hr=S_OK;
|
|
|
|
if (!m_pMC)
|
|
return S_OK;
|
|
|
|
// Start playback
|
|
hr = m_pMC->Run();
|
|
if (FAILED(hr)) {
|
|
RetailOutput(TEXT("\r\n*** Failed(%08lx) in Run()!\r\n"), hr);
|
|
return hr;
|
|
}
|
|
|
|
// Remember play state
|
|
g_psCurrent = State_Running;
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CPlayDMODlg::StopMedia()
|
|
{
|
|
HRESULT hr=S_OK;
|
|
|
|
if (!m_pMC)
|
|
return S_OK;
|
|
|
|
// If we're already stopped, don't check again
|
|
if (g_psCurrent == State_Stopped)
|
|
return hr;
|
|
|
|
// Stop playback
|
|
hr = m_pMC->Stop();
|
|
if (FAILED(hr)) {
|
|
RetailOutput(TEXT("\r\n*** Failed(%08lx) in Stop()!\r\n"), hr);
|
|
return hr;
|
|
}
|
|
|
|
// Wait for the stop to propagate to all filters
|
|
OAFilterState fs;
|
|
hr = m_pMC->GetState(500, &fs);
|
|
if (FAILED(hr))
|
|
{
|
|
RetailOutput(TEXT("Failed to read graph state! hr=0x%x\r\n"), hr);
|
|
}
|
|
|
|
// Remember play state
|
|
g_psCurrent = State_Stopped;
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CPlayDMODlg::PauseMedia(void)
|
|
{
|
|
HRESULT hr=S_OK;
|
|
|
|
if (!m_pMC)
|
|
return S_OK;
|
|
|
|
// Don't pause unless we're in the running state
|
|
if(g_psCurrent != State_Running)
|
|
return S_OK;
|
|
|
|
// Pause playback
|
|
hr = m_pMC->Pause();
|
|
if (FAILED(hr)) {
|
|
RetailOutput(TEXT("\r\n*** Failed(%08lx) in Pause()!\r\n"), hr);
|
|
return hr;
|
|
}
|
|
|
|
// Remember play state
|
|
g_psCurrent = State_Paused;
|
|
return hr;
|
|
}
|
|
|
|
|
|
void CPlayDMODlg::OnButtonProppage()
|
|
{
|
|
IBaseFilter *pFilter = NULL;
|
|
TCHAR szNameToFind[128];
|
|
|
|
// Read the current filter name from the list box
|
|
int nCurSel = m_ListFilters.GetCurSel();
|
|
m_ListFilters.GetText(nCurSel, szNameToFind);
|
|
|
|
// Find this filter in the graph
|
|
pFilter = FindFilterFromName(m_pGB, szNameToFind);
|
|
if (!pFilter)
|
|
return;
|
|
|
|
// Show the property page for the selected DMO or filter
|
|
ShowFilterPropertyPage(pFilter, m_hWnd);
|
|
}
|
|
|
|
void CPlayDMODlg::OnDblclkListFilters()
|
|
{
|
|
// Show the property page for the selected DMO or filter
|
|
OnButtonProppage();
|
|
}
|
|
|
|
|
|
void CPlayDMODlg::ShowInputBufferInfo(IMediaObject *pDMO, int nSel)
|
|
{
|
|
HRESULT hr;
|
|
TCHAR sz[32];
|
|
DWORD dwSize=0, dwLookahead=0, dwAlign=0;
|
|
|
|
// If the selected item is not a DMO, display default information
|
|
if (!pDMO)
|
|
{
|
|
m_nInBufferSize.SetWindowText(STR_NOTDMO);
|
|
m_nInLookahead.SetWindowText(STR_NOTDMO);
|
|
m_nInAlignment.SetWindowText(STR_NOTDMO);
|
|
m_nMaxLatency.SetWindowText(STR_NOTDMO);
|
|
return;
|
|
}
|
|
|
|
// Read/display maximum latency
|
|
REFERENCE_TIME reftime=0;
|
|
hr = pDMO->GetInputMaxLatency(nSel, &reftime);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DWORD dwLatency = (DWORD) (reftime * 100000);
|
|
wsprintf(sz, TEXT("%ld"), dwLatency);
|
|
m_nMaxLatency.SetWindowText(sz);
|
|
}
|
|
else if (hr == E_NOTIMPL)
|
|
m_nMaxLatency.SetWindowText(TEXT("0"));
|
|
else
|
|
m_nMaxLatency.SetWindowText(STR_UNKNOWN);
|
|
|
|
// Read/display input buffer size information
|
|
hr = pDMO->GetInputSizeInfo(nSel, &dwSize, &dwLookahead, &dwAlign);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
wsprintf(sz, TEXT("%ld"), dwSize);
|
|
m_nInBufferSize.SetWindowText(sz);
|
|
|
|
wsprintf(sz, TEXT("%ld"), dwLookahead);
|
|
m_nInLookahead.SetWindowText(sz);
|
|
|
|
wsprintf(sz, TEXT("%ld"), dwAlign);
|
|
m_nInAlignment.SetWindowText(sz);
|
|
}
|
|
else
|
|
{
|
|
m_nInBufferSize.SetWindowText(STR_UNKNOWN);
|
|
m_nInLookahead.SetWindowText(STR_UNKNOWN);
|
|
m_nInAlignment.SetWindowText(STR_UNKNOWN);
|
|
}
|
|
}
|
|
|
|
void CPlayDMODlg::ShowOutputBufferInfo(IMediaObject *pDMO, int nSel)
|
|
{
|
|
HRESULT hr;
|
|
TCHAR sz[32];
|
|
DWORD dwSize=0, dwAlign=0;
|
|
|
|
// If the selected item is not a DMO, display default information
|
|
if (!pDMO)
|
|
{
|
|
m_nOutBufferSize.SetWindowText(STR_NOTDMO);
|
|
m_nOutAlignment.SetWindowText(STR_NOTDMO);
|
|
return;
|
|
}
|
|
|
|
// Read/display output buffer size information
|
|
hr = pDMO->GetOutputSizeInfo(nSel, &dwSize, &dwAlign);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
wsprintf(sz, TEXT("%ld"), dwSize);
|
|
m_nOutBufferSize.SetWindowText(sz);
|
|
|
|
wsprintf(sz, TEXT("%ld"), dwAlign);
|
|
m_nOutAlignment.SetWindowText(sz);
|
|
}
|
|
else
|
|
{
|
|
m_nOutBufferSize.SetWindowText(STR_UNKNOWN);
|
|
m_nOutAlignment.SetWindowText(STR_UNKNOWN);
|
|
}
|
|
}
|
|
|
|
|
|
void CPlayDMODlg::EnableButtons(BOOL bEnable)
|
|
{
|
|
// Enable or disable buttons and other dialog items
|
|
m_StrFilename.EnableWindow(bEnable);
|
|
m_ListAudioDMO.EnableWindow(bEnable);
|
|
|
|
GetDlgItem(IDC_BUTTON_ADD_DMO)->EnableWindow(bEnable);
|
|
GetDlgItem(IDC_BUTTON_CLEAR)->EnableWindow(bEnable);
|
|
GetDlgItem(IDC_BUTTON_FILE)->EnableWindow(bEnable);
|
|
}
|
|
|
|
HRESULT CPlayDMODlg::ConnectDMOsToRenderer()
|
|
{
|
|
USES_CONVERSION;
|
|
HRESULT hr=0;
|
|
IPin *pInputPin=0, *pUpstreamOutputPin=0;
|
|
IBaseFilter *pRenderer=0;
|
|
|
|
// Make sure that the graph is stopped before manipulating its filters
|
|
hr = m_pMC->Stop();
|
|
|
|
// Find the audio renderer in the graph
|
|
#if 0
|
|
// It's not recommended to search for Microsoft-provided filters by text
|
|
// name, since the name could change in the future. Instead, use
|
|
// the more reliable method outlined below.
|
|
WCHAR wszFilter[64];
|
|
|
|
wcscpy(wszFilter, T2W(TEXT("Default DirectSound Device")));
|
|
hr = m_pGB->FindFilterByName(wszFilter, &pRenderer);
|
|
if (FAILED(hr))
|
|
{
|
|
RetailOutput(TEXT("Failed to find audio renderer!\r\n"));
|
|
goto err;
|
|
}
|
|
#else
|
|
//
|
|
// Enumerate ALL filters in the graph, find one that has one input and
|
|
// no outputs, and check to see if it is configured to receive audio data.
|
|
//
|
|
hr = FindAudioRenderer(m_pGB, &pRenderer);
|
|
if (FAILED(hr))
|
|
{
|
|
RetailOutput(TEXT("Failed to find audio renderer!\r\n"));
|
|
goto err;
|
|
}
|
|
#endif
|
|
|
|
// Verify that an audio renderer is present
|
|
if (!pRenderer)
|
|
{
|
|
MessageBox(TEXT("This file contains no audio component.\r\n\r\n")
|
|
TEXT("Please select an audio file or a video file with audio."),
|
|
TEXT("No audio renderer!"));
|
|
goto err;
|
|
}
|
|
|
|
// Get the pin interface for its input pin
|
|
hr = GetPin(pRenderer, PINDIR_INPUT, 0, &pInputPin);
|
|
if (FAILED(hr))
|
|
{
|
|
RetailOutput(TEXT("Failed to find audio input pin!\r\n"));
|
|
goto err;
|
|
}
|
|
|
|
// Find its upstream connection
|
|
hr = pInputPin->ConnectedTo(&pUpstreamOutputPin);
|
|
if (FAILED(hr))
|
|
{
|
|
RetailOutput(TEXT("Failed to find audio renderer upstream output pin!\r\n"));
|
|
goto err;
|
|
}
|
|
|
|
// Break the connection between these pins
|
|
hr = pUpstreamOutputPin->Disconnect();
|
|
if (FAILED(hr))
|
|
{
|
|
RetailOutput(TEXT("Failed to disconnect upstream output pin!\r\n"));
|
|
goto err;
|
|
}
|
|
hr = pInputPin->Disconnect();
|
|
if (FAILED(hr))
|
|
{
|
|
RetailOutput(TEXT("Failed to disconnect renderer input pin!\r\n"));
|
|
goto err;
|
|
}
|
|
|
|
// Render the upstream filter's output pin again. This will cause
|
|
// the filter graph manager to connect our newly loaded DMOs into
|
|
// the renderer's input pin.
|
|
hr = m_pGB->Render(pUpstreamOutputPin);
|
|
if (FAILED(hr))
|
|
{
|
|
RetailOutput(TEXT("Failed to render upstream output pin!\r\n"));
|
|
goto err;
|
|
}
|
|
|
|
err:
|
|
SAFE_RELEASE(pInputPin);
|
|
SAFE_RELEASE(pUpstreamOutputPin);
|
|
SAFE_RELEASE(pRenderer);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
BOOL CPlayDMODlg::OnEraseBkgnd(CDC *pDC)
|
|
{
|
|
// Intercept background erasing for the movie window, since the
|
|
// video renderer will keep the screen painted. Without this code,
|
|
// your video window might get painted over with gray (the default
|
|
// background brush) when it is obscured by another window and redrawn.
|
|
CRect rc;
|
|
|
|
// Get the bounding rectangle for the movie screen
|
|
m_Screen.GetWindowRect(&rc);
|
|
ScreenToClient(&rc);
|
|
|
|
// Exclude the clipping region occupied by our movie screen
|
|
pDC->ExcludeClipRect(&rc);
|
|
|
|
// Erase the remainder of the dialog as usual
|
|
return CDialog::OnEraseBkgnd(pDC);
|
|
}
|