Files
Client/Library/dxx8/samples/Multimedia/DirectShow/Players/JukeboxASF/JukeboxASFDlg.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

1844 lines
49 KiB
C++

//------------------------------------------------------------------------------
// File: JukeboxASFDlg.cpp
//
// Desc: DirectShow sample code - implementation of CJukeboxDlg class.
//
// Copyright (c) 1998-2001 Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------
#include "stdafx.h"
#include <atlbase.h>
#include "JukeboxASF.h"
#include "JukeboxASFDlg.h"
#include "playvideo.h"
#include "mediatypes.h"
#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()
/////////////////////////////////////////////////////////////////////////////
// CJukeboxDlg dialog
CJukeboxDlg::CJukeboxDlg(CWnd* pParent /*=NULL*/)
: CDialog(CJukeboxDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CJukeboxDlg)
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CJukeboxDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CJukeboxDlg)
DDX_Control(pDX, IDC_STATIC_POSITION, m_StrPosition);
DDX_Control(pDX, IDC_SLIDER, m_Seekbar);
DDX_Control(pDX, IDC_STATIC_IMAGESIZE, m_StrImageSize);
DDX_Control(pDX, IDC_STATIC_DURATION, m_StrDuration);
DDX_Control(pDX, IDC_EDIT_MEDIADIR, m_EditMediaDir);
DDX_Control(pDX, IDC_SPIN_FILES, m_SpinFiles);
DDX_Control(pDX, IDC_BUTTON_FRAMESTEP, m_ButtonFrameStep);
DDX_Control(pDX, IDC_LIST_EVENTS, m_ListEvents);
DDX_Control(pDX, IDC_CHECK_EVENTS, m_CheckEvents);
DDX_Control(pDX, IDC_BUTTON_PROPPAGE, m_ButtonProperties);
DDX_Control(pDX, IDC_STATUS_DIRECTORY, m_StrMediaPath);
DDX_Control(pDX, IDC_CHECK_MUTE, m_CheckMute);
DDX_Control(pDX, IDC_BUTTON_STOP, m_ButtonStop);
DDX_Control(pDX, IDC_BUTTON_PLAY, m_ButtonPlay);
DDX_Control(pDX, IDC_BUTTON_PAUSE, m_ButtonPause);
DDX_Control(pDX, IDC_CHECK_PLAYTHROUGH, m_CheckPlaythrough);
DDX_Control(pDX, IDC_CHECK_LOOP, m_CheckLoop);
DDX_Control(pDX, IDC_STATIC_FILEDATE, m_StrFileDate);
DDX_Control(pDX, IDC_STATIC_FILESIZE, m_StrFileSize);
DDX_Control(pDX, IDC_LIST_PINS_OUTPUT, m_ListPinsOutput);
DDX_Control(pDX, IDC_LIST_PINS_INPUT, m_ListPinsInput);
DDX_Control(pDX, IDC_STATIC_FILELIST, m_StrFileList);
DDX_Control(pDX, IDC_STATUS, m_Status);
DDX_Control(pDX, IDC_MOVIE_SCREEN, m_Screen);
DDX_Control(pDX, IDC_LIST_FILTERS, m_ListFilters);
DDX_Control(pDX, IDC_LIST_FILES, m_ListFiles);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CJukeboxDlg, CDialog)
//{{AFX_MSG_MAP(CJukeboxDlg)
ON_WM_ERASEBKGND()
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_CLOSE()
ON_WM_DESTROY()
ON_LBN_SELCHANGE(IDC_LIST_FILES, OnSelectFile)
ON_BN_CLICKED(IDC_BUTTON_PAUSE, OnPause)
ON_BN_CLICKED(IDC_BUTTON_PLAY, OnPlay)
ON_BN_CLICKED(IDC_BUTTON_STOP, OnStop)
ON_BN_CLICKED(IDC_CHECK_MUTE, OnCheckMute)
ON_BN_CLICKED(IDC_CHECK_LOOP, OnCheckLoop)
ON_BN_CLICKED(IDC_CHECK_PLAYTHROUGH, OnCheckPlaythrough)
ON_LBN_SELCHANGE(IDC_LIST_FILTERS, OnSelchangeListFilters)
ON_LBN_DBLCLK(IDC_LIST_FILTERS, OnDblclkListFilters)
ON_BN_CLICKED(IDC_BUTTON_PROPPAGE, OnButtonProppage)
ON_BN_CLICKED(IDC_CHECK_EVENTS, OnCheckEvents)
ON_BN_CLICKED(IDC_BUTTON_FRAMESTEP, OnButtonFramestep)
ON_BN_CLICKED(IDC_BUTTON_CLEAR_EVENTS, OnButtonClearEvents)
ON_LBN_DBLCLK(IDC_LIST_FILES, OnDblclkListFiles)
ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_FILES, OnDeltaposSpinFiles)
ON_BN_CLICKED(IDC_BUTTON_SET_MEDIADIR, OnButtonSetMediadir)
ON_WM_TIMER()
ON_BN_CLICKED(IDC_BUTTON_GRAPHEDIT, OnButtonGraphedit)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CJukeboxDlg message handlers
void CJukeboxDlg::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 CJukeboxDlg::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 CJukeboxDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
/////////////////////////////////////////////////////////////////////////////
// CJukeboxDlg DirectShow code and message handlers
BOOL CJukeboxDlg::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
// Initialize COM
CoInitialize(NULL);
// 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);
// Propagate the files list and select the first item
InitMediaDirectory();
// Initialize seeking trackbar range
m_Seekbar.SetRange(0, 100, TRUE);
m_Seekbar.SetTicFreq(10);
return TRUE; // return TRUE unless you set the focus to a control
}
void CJukeboxDlg::InitMediaDirectory(void)
{
// Fill the media file list, starting with the directory passed
// on the command line. If no directory is passed, then read the
// default media path for the DirectX SDK.
TCHAR szDir[MAX_PATH];
LONG lResult=0;
if (theApp.m_lpCmdLine[0] == L'\0')
{
lResult = GetDXMediaPath(szDir);
// If the DirectX SDK is not installed, use the Windows media
// directory instead.
if (lResult != 0)
{
GetWindowsDirectory(szDir, MAX_PATH);
_tcscat(szDir, _T("\\Media\\") );
}
}
else
_tcscpy(szDir, theApp.m_lpCmdLine);
TCHAR szPathMsg[MAX_PATH];
wsprintf(szPathMsg, TEXT("Media directory: %s\0"), szDir);
m_StrMediaPath.SetWindowText(szPathMsg);
// Save current directory name
wsprintf(m_szCurrentDir, TEXT("%s\0"), szDir);
m_EditMediaDir.SetLimitText(MAX_PATH);
m_EditMediaDir.SetWindowText(szDir);
FillFileList(szDir);
}
LONG CJukeboxDlg::GetDXMediaPath(TCHAR *szPath)
{
HKEY hKey;
DWORD dwType, dwSize = MAX_PATH;
// Open the appropriate registry key
LONG lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
_T("Software\\Microsoft\\DirectX SDK"),
0, KEY_READ, &hKey );
if( ERROR_SUCCESS != lResult )
return -1;
lResult = RegQueryValueEx( hKey, _T("DX81SDK Samples Path"), NULL,
&dwType, (BYTE*)szPath, &dwSize );
RegCloseKey( hKey );
if( ERROR_SUCCESS != lResult )
return -1;
_tcscat( szPath, _T("\\Media\\") );
return 0;
}
void CJukeboxDlg::FillFileList(LPTSTR pszRootDir)
{
UINT attr = 0;
m_ListFiles.ResetContent();
::SetCurrentDirectory(pszRootDir);
Say(TEXT("Building file list..."));
// Add all of our known supported media types to the file list.
// Add files of each type in order.
for (int i=0; i < NUM_MEDIA_TYPES; i++)
{
m_ListFiles.Dir(attr, TypeInfo[i].pszType);
}
Say(TEXT("File list complete."));
// Update list box title with number of items added
int nItems = m_ListFiles.GetCount();
TCHAR szTitle[64];
wsprintf(szTitle, TEXT("Media files (%d found)"), nItems);
m_StrFileList.SetWindowText(szTitle);
// Automatically select the first file in the list once
// the dialog is displayed.
PostMessage(WM_FIRSTFILE, 0, 0L);
m_nCurrentFileSelection = -1; // No selection yet
}
void CJukeboxDlg::OnClose()
{
// Release DirectShow interfaces
StopMedia();
FreeDirectShow();
// Release COM
CoUninitialize();
CDialog::OnClose();
}
void CJukeboxDlg::OnDestroy()
{
FreeDirectShow();
CDialog::OnDestroy();
}
void CJukeboxDlg::OnSelectFile()
{
HRESULT hr;
TCHAR szFilename[MAX_PATH];
// If this is the currently selected file, do nothing
int nItem = m_ListFiles.GetCurSel();
if (nItem == m_nCurrentFileSelection)
return;
// Remember the current selection to speed double-click processing
m_nCurrentFileSelection = nItem;
// Read file name from list box
m_ListFiles.GetText(nItem, szFilename);
// Remember current play state to restart playback
int nCurrentState = g_psCurrent;
// First release any existing interfaces
ResetDirectShow();
// Clear filter/pin/event information listboxes
m_ListFilters.ResetContent();
m_ListPinsInput.ResetContent();
m_ListPinsOutput.ResetContent();
m_ListEvents.ResetContent();
// Load the selected media file
hr = PrepareMedia(szFilename);
if (FAILED(hr))
{
// Error - disable play button and give feedback
Say(TEXT("File failed to render!"));
m_ButtonPlay.EnableWindow(FALSE);
MessageBeep(0);
return;
}
else
{
m_ButtonPlay.EnableWindow(TRUE);
}
// Display useful information about this file
DisplayFileInfo(szFilename);
DisplayImageInfo();
DisplayFileDuration();
// Set up the seeking trackbar and read capabilities
ConfigureSeekbar();
// Enumerate and display filters in graph
hr = EnumFilters();
// Select the first filter in the list to display pin info
m_ListFilters.SetCurSel(0);
OnSelchangeListFilters();
// See if the renderer supports frame stepping on this file.
// Enable/disable frame stepping button accordingly
m_ButtonFrameStep.EnableWindow(CanStep());
// If the user has asked to mute audio then we
// need to mute this new clip before continuing.
if (g_bGlobalMute)
MuteAudio();
// If we were running when the user changed selection,
// start running the newly selected clip
if (nCurrentState == State_Running)
{
OnPlay();
}
else
{
// Cue the first video frame
OnStop();
}
}
BOOL CJukeboxDlg::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 CJukeboxDlg::PrepareMedia(LPTSTR lpszMovie)
{
USES_CONVERSION;
HRESULT hr = S_OK;
Say(TEXT("Loading..."));
// Is this a Windows Media file (ASF, WMA, WMV)? If so, use the new
// ASF Reader filter, which is faster and much better at seeking than
// the default ASF Reader filter used by default with RenderFile.
if (IsWindowsMediaFile(lpszMovie))
{
hr = RenderWMFile(T2W(lpszMovie));
if (FAILED(hr)) {
RetailOutput(TEXT("*** Failed(%08lx) to Render WM File(%s)!\r\n"),
hr, lpszMovie);
return hr;
}
}
else
{
// Allow DirectShow to create the FilterGraph for this media file
hr = pGB->RenderFile(T2W(lpszMovie), NULL);
if (FAILED(hr)) {
RetailOutput(TEXT("*** Failed(%08lx) in RenderFile(%s)!\r\n"),
hr, lpszMovie);
return hr;
}
}
// 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 = pVW->put_MessageDrain((OAHWND) m_hWnd);
if (FAILED(hr))
{
g_bAudioOnly = TRUE;
}
// Have the graph signal event via window callbacks
hr = pME->SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY, 0);
// Configure the video window
if (!g_bAudioOnly)
{
// We'll manually set the video to be visible
hr = pVW->put_Visible(OAFALSE);
hr = pVW->put_WindowStyle(WS_CHILD);
hr = pVW->put_Owner((OAHWND) m_Screen.GetSafeHwnd());
// 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 = pVW->put_Visible(OATRUE);
hr = pVW->SetWindowForeground(-1);
}
Say(TEXT("Ready"));
return hr;
}
HRESULT CJukeboxDlg::RenderWMFile(LPCWSTR wFile)
{
HRESULT hr=S_OK;
IFileSourceFilter *pFS=NULL;
IBaseFilter *pReader=NULL;
// Load the improved ASF reader filter by CLSID
hr = CreateFilter(CLSID_WMAsfReader, &pReader);
if(FAILED(hr))
{
RetailOutput(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))
{
RetailOutput(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 **) &pFS));
JIF(pFS->Load(wFile, NULL));
pFS->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();
CLEANUP:
return hr;
}
HRESULT CJukeboxDlg::CreateFilter(REFCLSID clsid, IBaseFilter **ppFilter)
{
HRESULT hr;
hr = CoCreateInstance(clsid,
NULL,
CLSCTX_INPROC_SERVER,
IID_IBaseFilter,
(void **) ppFilter);
if(FAILED(hr))
{
RetailOutput(TEXT("CreateFilter: Failed to create filter! hr=0x%x\n"), hr);
*ppFilter = NULL;
return hr;
}
return S_OK;
}
HRESULT CJukeboxDlg::RenderOutputPins(IGraphBuilder *pGB, IBaseFilter *pFilter)
{
HRESULT hr = S_OK;
IEnumPins * pEnumPin = NULL;
IPin * pConnectedPin = NULL, * pPin;
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 CJukeboxDlg::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(); // Don't let COM try to free our static object.
// 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;
}
//
// Displays a text string in a status line near the bottom of the dialog
//
void CJukeboxDlg::Say(LPTSTR szText)
{
m_Status.SetWindowText(szText);
}
void CJukeboxDlg::OnPause()
{
if (g_psCurrent == State_Paused)
{
RunMedia();
StartSeekTimer();
Say(TEXT("Running"));
}
else
{
StopSeekTimer();
PauseMedia();
Say(TEXT("PAUSED"));
}
}
void CJukeboxDlg::OnPlay()
{
RunMedia();
StartSeekTimer();
Say(TEXT("Running"));
}
void CJukeboxDlg::ShowState()
{
HRESULT hr;
OAFilterState fs;
hr = pMC->GetState(500, &fs);
if (FAILED(hr))
{
RetailOutput(TEXT("Failed to read graph state! hr=0x%x\r\n"), hr);
return;
}
// Show debug output for current media state
switch (fs)
{
case State_Stopped:
RetailOutput(TEXT("State_Stopped\r\n"));
break;
case State_Paused:
RetailOutput(TEXT("State_Paused\r\n"));
break;
case State_Running:
RetailOutput(TEXT("State_Running\r\n"));
break;
}
}
void CJukeboxDlg::OnStop()
{
HRESULT hr;
// Stop playback immediately with IMediaControl::Stop().
StopSeekTimer();
StopMedia();
// Wait for the stop to propagate to all filters
OAFilterState fs;
hr = pMC->GetState(500, &fs);
if (FAILED(hr))
{
RetailOutput(TEXT("Failed to read graph state! hr=0x%x\r\n"), hr);
}
// Reset to beginning of media clip
LONGLONG pos=0;
hr = 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 = pMC->StopWhenReady();
if (FAILED(hr))
{
RetailOutput(TEXT("Failed in StopWhenReady! hr=0x%x\r\n"), hr);
}
Say(TEXT("Stopped"));
// Reset slider bar and position label back to zero
ReadMediaPosition();
}
HRESULT CJukeboxDlg::InitDirectShow(void)
{
HRESULT hr = S_OK;
g_bAudioOnly = FALSE;
// Zero interfaces (sanity check)
pVW = NULL;
pBV = NULL;
JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&pGB));
JIF(pGB->QueryInterface(IID_IMediaControl, (void **)&pMC));
JIF(pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS));
JIF(pGB->QueryInterface(IID_IBasicVideo, (void **)&pBV));
JIF(pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW));
JIF(pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME));
return S_OK;
CLEANUP:
FreeDirectShow();
return(hr);
}
HRESULT CJukeboxDlg::FreeDirectShow(void)
{
HRESULT hr=S_OK;
StopSeekTimer();
StopMedia();
// Disable event callbacks
if (pME)
hr = 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(pVW)
{
hr = pVW->put_Visible(OAFALSE);
hr = pVW->put_Owner(NULL);
}
SAFE_RELEASE(pMC);
SAFE_RELEASE(pMS);
SAFE_RELEASE(pVW);
SAFE_RELEASE(pBV);
SAFE_RELEASE(pME);
SAFE_RELEASE(pGB);
return hr;
}
void CJukeboxDlg::ResetDirectShow(void)
{
// Destroy the current filter graph its filters.
FreeDirectShow();
// Reinitialize graph builder and query for interfaces
InitDirectShow();
}
void CJukeboxDlg::CenterVideo(void)
{
LONG width, height;
HRESULT hr;
if ((g_bAudioOnly) || (!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 = 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 CJukeboxDlg::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;
case WM_HSCROLL:
HandleTrackbar(LOWORD(wParam));
break;
case WM_PLAYFILE:
PlaySelectedFile();
break;
case WM_NEXTFILE:
PlayNextFile();
break;
case WM_PREVIOUSFILE:
PlayPreviousFile();
break;
case WM_FIRSTFILE:
// Select the first item in the list
m_ListFiles.SetCurSel(0);
OnSelectFile();
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 (pVW)
{
pVW->NotifyOwnerMessage((LONG_PTR) m_hWnd, message, wParam, lParam);
}
return CDialog::WindowProc(message, wParam, lParam);
}
HRESULT CJukeboxDlg::HandleGraphEvent(void)
{
LONG evCode, evParam1, evParam2;
HRESULT hr=S_OK;
// Since we may have a scenario where we're shutting down the application,
// but events are still being generated, make sure that the event
// interface is still valid before using it. It's possible that
// the interface could be freed during shutdown but later referenced in
// this callback before the app completely exits.
if (!pME)
return S_OK;
while(SUCCEEDED(pME->GetEvent(&evCode, (LONG_PTR *) &evParam1,
(LONG_PTR *) &evParam2, 0)))
{
// Spin through the events
hr = pME->FreeEventParams(evCode, evParam1, evParam2);
if(EC_COMPLETE == evCode)
{
// If looping, reset to beginning and continue playing
if (g_bLooping)
{
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.
StopMedia();
RunMedia();
}
}
else if (g_bPlayThrough)
{
// Tell the app to select the next file in the list
PostMessage(WM_NEXTFILE, 0, 0);
}
else
{
// Stop playback and display first frame of movie
OnStop();
}
}
// If requested, display DirectShow events received
if (g_bDisplayEvents)
{
DisplayECEvent(evCode, evParam1, evParam2);
}
}
return hr;
}
void CJukeboxDlg::OnCheckMute()
{
// Remember global mute status for next file. When you destroy a
// filtergraph, you lose all of its audio settings. Therefore, when
// we create the next graph, we will mute the audio before running
// the graph if this global variable is set.
g_bGlobalMute ^= 1;
if (g_bGlobalMute)
MuteAudio();
else
ResumeAudio();
}
void CJukeboxDlg::OnCheckLoop()
{
g_bLooping ^= 1;
// Looping and play-through are mutually exclusive
if ((g_bLooping) && (g_bPlayThrough))
{
// Disable play-through and uncheck button
g_bPlayThrough = 0;
m_CheckPlaythrough.SetCheck(0);
}
}
void CJukeboxDlg::OnCheckPlaythrough()
{
g_bPlayThrough ^= 1;
// Looping and play-through are mutually exclusive
if ((g_bPlayThrough) && (g_bLooping) )
{
// Disable play-through and uncheck button
g_bLooping = 0;
m_CheckLoop.SetCheck(0);
}
}
void CJukeboxDlg::OnCheckEvents()
{
g_bDisplayEvents ^= 1;
}
void CJukeboxDlg::OnButtonClearEvents()
{
m_ListEvents.ResetContent();
}
void CJukeboxDlg::OnButtonSetMediadir()
{
// Make sure that we're not playing media
OnStop();
// Read the string in the media directory edit box.
TCHAR szEditPath[MAX_PATH];
DWORD dwAttr;
m_EditMediaDir.GetWindowText(szEditPath, MAX_PATH);
// Is this a valid directory name?
dwAttr = GetFileAttributes(szEditPath);
if ((dwAttr == (DWORD) -1) || (! (dwAttr & FILE_ATTRIBUTE_DIRECTORY)))
{
MessageBox(TEXT("Please enter a valid directory name."), TEXT("Media error"));
return;
}
// User has specified a valid media directory.
// Update the current path string.
TCHAR szPathMsg[MAX_PATH];
wsprintf(szPathMsg, TEXT("Media directory: %s\0"), szEditPath);
m_StrMediaPath.SetWindowText(szPathMsg);
// Save current directory name. Append the trailing '\' to match
// the string created by GetDXSDKMediaPath() (if not present)
int nLength = _tcslen(szEditPath);
if (szEditPath[nLength - 1] != TEXT('\\'))
wsprintf(m_szCurrentDir, TEXT("%s\\\0"), szEditPath);
// Propagate the files list and select the first item
FillFileList(szEditPath);
}
void CJukeboxDlg::OnDblclkListFiles()
{
// Because it might take time to render the file and display
// its first frame, it's better to post a message that tells
// the app to play the selected file when ready.
PostMessage(WM_PLAYFILE, 0, 0L);
}
void CJukeboxDlg::PlaySelectedFile()
{
OnPlay();
}
void CJukeboxDlg::OnDeltaposSpinFiles(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;
if (pNMUpDown->iDelta > 0)
PostMessage(WM_NEXTFILE, 0, 0L);
else
PostMessage(WM_PREVIOUSFILE, 0, 0L);
*pResult = 0;
}
void CJukeboxDlg::PlayNextFile(void)
{
int nItems = m_ListFiles.GetCount();
// Return if the list is empty
if (!nItems)
return;
int nCurSel = m_ListFiles.GetCurSel();
int nNewSel = (nCurSel + 1) % nItems;
// Select the next item in the list, wrapping to top if needed
m_ListFiles.SetCurSel(nNewSel);
OnSelectFile();
}
void CJukeboxDlg::PlayPreviousFile(void)
{
int nItems = m_ListFiles.GetCount();
// Return if the list is empty
if (!nItems)
return;
int nCurSel = m_ListFiles.GetCurSel();
int nNewSel = nCurSel - 1;
// If moved off top of list, select last item in list
if (nNewSel < 0)
nNewSel = nItems - 1;
// Select the next item in the list, wrapping to top if needed
m_ListFiles.SetCurSel(nNewSel);
OnSelectFile();
}
HRESULT CJukeboxDlg::EnumFilters (void)
{
HRESULT hr;
IEnumFilters *pEnum = NULL;
IBaseFilter *pFilter = NULL;
ULONG cFetched;
// Clear filters list box
m_ListFilters.ResetContent();
// Get filter enumerator
hr = pGB->EnumFilters(&pEnum);
if (FAILED(hr))
{
m_ListFilters.AddString(TEXT("<ERROR>"));
return hr;
}
// Enumerate all filters in the graph
while(pEnum->Next(1, &pFilter, &cFetched) == S_OK)
{
FILTER_INFO FilterInfo;
TCHAR szName[256];
hr = pFilter->QueryFilterInfo(&FilterInfo);
if (FAILED(hr))
{
m_ListFilters.AddString(TEXT("<ERROR>"));
}
else
{
// Add the filter name to the filters listbox
USES_CONVERSION;
lstrcpy(szName, W2T(FilterInfo.achName));
m_ListFilters.AddString(szName);
FilterInfo.pGraph->Release();
}
pFilter->Release();
}
pEnum->Release();
return hr;
}
//
// The GraphBuilder interface provides a FindFilterByName() method,
// which provides similar functionality to the method below.
// This local method is provided for educational purposes.
//
IBaseFilter *CJukeboxDlg::FindFilterFromName(LPTSTR szNameToFind)
{
USES_CONVERSION;
HRESULT hr;
IEnumFilters *pEnum = NULL;
IBaseFilter *pFilter = NULL;
ULONG cFetched;
BOOL bFound = FALSE;
// Get filter enumerator
hr = pGB->EnumFilters(&pEnum);
if (FAILED(hr))
return NULL;
// Enumerate all filters in the graph
while((pEnum->Next(1, &pFilter, &cFetched) == S_OK) && (!bFound))
{
FILTER_INFO FilterInfo;
TCHAR szName[256];
hr = pFilter->QueryFilterInfo(&FilterInfo);
if (FAILED(hr))
{
pFilter->Release();
pEnum->Release();
return NULL;
}
// Compare this filter's name with the one we want
lstrcpy(szName, W2T(FilterInfo.achName));
if (! lstrcmp(szName, szNameToFind))
{
bFound = TRUE;
}
FilterInfo.pGraph->Release();
// If we found the right filter, don't release its interface.
// The caller will use it and release it later.
if (!bFound)
pFilter->Release();
else
break;
}
pEnum->Release();
return (bFound ? pFilter : NULL);
}
HRESULT CJukeboxDlg::EnumPins(IBaseFilter *pFilter, PIN_DIRECTION PinDir,
CListBox& Listbox)
{
HRESULT hr;
IEnumPins *pEnum = NULL;
IPin *pPin = NULL;
// Clear the specified listbox (input or output)
Listbox.ResetContent();
// Get pin enumerator
hr = pFilter->EnumPins(&pEnum);
if (FAILED(hr))
{
Listbox.AddString(TEXT("<ERROR>"));
return hr;
}
// Enumerate all pins on this filter
while(pEnum->Next(1, &pPin, 0) == S_OK)
{
PIN_DIRECTION PinDirThis;
hr = pPin->QueryDirection(&PinDirThis);
if (FAILED(hr))
{
Listbox.AddString(TEXT("<ERROR>"));
pPin->Release();
continue;
}
// Does the pin's direction match the requested direction?
if (PinDir == PinDirThis)
{
PIN_INFO pininfo={0};
// Direction matches, so add pin name to listbox
hr = pPin->QueryPinInfo(&pininfo);
if (SUCCEEDED(hr))
{
CString str(pininfo.achName);
Listbox.AddString(str);
}
// The pininfo structure contains a reference to an IBaseFilter,
// so you must release its reference to prevent resource a leak.
pininfo.pFilter->Release();
}
pPin->Release();
}
pEnum->Release();
return hr;
}
void CJukeboxDlg::OnSelchangeListFilters()
{
HRESULT hr;
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);
// Read the current list box name and find it in the graph
pFilter = FindFilterFromName(szNameToFind);
if (!pFilter)
return;
// Now that we have filter information, enumerate pins by direction
// and add their names to the appropriate listboxes
hr = EnumPins(pFilter, PINDIR_INPUT, m_ListPinsInput);
hr = EnumPins(pFilter, PINDIR_OUTPUT, m_ListPinsOutput);
// Find out if this filter supports a property page
if (SupportsPropertyPage(pFilter))
m_ButtonProperties.EnableWindow(TRUE);
else
m_ButtonProperties.EnableWindow(FALSE);
// Must release the filter interface returned from FindFilterByName()
pFilter->Release();
}
BOOL CJukeboxDlg::DisplayImageInfo(void)
{
HRESULT hr;
long lWidth, lHeight;
// If this file has no video component, clear the text field
if ((!pBV) || (g_bAudioOnly))
{
m_StrImageSize.SetWindowText(TEXT("\0"));
return FALSE;
}
hr = pBV->GetVideoSize(&lWidth, &lHeight);
if (SUCCEEDED(hr))
{
TCHAR szSize[64];
wsprintf(szSize, TEXT("Image size: %d x %d\0"), lWidth, lHeight);
m_StrImageSize.SetWindowText(szSize);
}
return TRUE;
}
BOOL CJukeboxDlg::DisplayFileInfo(LPTSTR szFile)
{
HANDLE hFile;
LONGLONG llSize=0;
DWORD dwSizeLow=0, dwSizeHigh=0;
TCHAR szScrap[64];
// Open the specified file to read size and date information
hFile = CreateFile(szFile,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,
(DWORD) 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
RetailOutput(TEXT("*** Failed(0x%x) to open file (to read size)!\r\n"),
GetLastError());
return FALSE;
}
dwSizeLow = GetFileSize(hFile, &dwSizeHigh);
if ((dwSizeLow == 0xFFFFFFFF) && (GetLastError() != NO_ERROR))
{
RetailOutput(TEXT("*** Error(0x%x) reading file size!\r\n"),
GetLastError());
CloseHandle(hFile);
return FALSE;
}
// Large files will also use the upper DWORD to report size.
// Add them together for the true size if necessary.
if (dwSizeHigh)
llSize = (dwSizeHigh << 16) + dwSizeLow;
else
llSize = dwSizeLow;
// Read date information
BY_HANDLE_FILE_INFORMATION fi;
if (GetFileInformationByHandle(hFile, &fi))
{
CTime time(fi.ftLastWriteTime);
wsprintf(szScrap, TEXT("File date: %02d/%02d/%d\0"),
time.GetMonth(), time.GetDay(), time.GetYear());
m_StrFileDate.SetWindowText(szScrap);
}
CloseHandle(hFile);
// Update size/date windows
wsprintf(szScrap, TEXT("Size: %d bytes\0"), dwSizeLow);
m_StrFileSize.SetWindowText(szScrap);
return TRUE;
}
HRESULT CJukeboxDlg::DisplayFileDuration(void)
{
HRESULT hr;
if (!pMS)
return E_NOINTERFACE;
// Initialize the display in case we can't read the duration
m_StrDuration.SetWindowText(TEXT("<00:00.000>"));
// Is media time supported for this file?
if (S_OK != pMS->IsFormatSupported(&TIME_FORMAT_MEDIA_TIME))
return E_NOINTERFACE;
// Read the time format to restore later
GUID guidOriginalFormat;
hr = pMS->GetTimeFormat(&guidOriginalFormat);
if (FAILED(hr))
return hr;
// Ensure media time format for easy display
hr = pMS->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
if (FAILED(hr))
return hr;
// Read the file's duration
LONGLONG llDuration;
hr = pMS->GetDuration(&llDuration);
if (FAILED(hr))
return hr;
// Return to the original format
if (guidOriginalFormat != TIME_FORMAT_MEDIA_TIME)
{
hr = pMS->SetTimeFormat(&guidOriginalFormat);
if (FAILED(hr))
return hr;
}
// Convert the LONGLONG duration into human-readable format
unsigned long nTotalMS = (unsigned long) llDuration / 10000; // 100ns -> ms
int nMS = nTotalMS % 1000;
int nSeconds = nTotalMS / 1000;
int nMinutes = nSeconds / 60;
nSeconds %= 60;
// Update the display
TCHAR szDuration[24];
wsprintf(szDuration, _T("%02dm:%02d.%03ds\0"), nMinutes, nSeconds, nMS);
m_StrDuration.SetWindowText(szDuration);
return hr;
}
BOOL CJukeboxDlg::SupportsPropertyPage(IBaseFilter *pFilter)
{
HRESULT hr;
ISpecifyPropertyPages *pSpecify;
// Discover if this filter contains a property page
hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpecify);
if (SUCCEEDED(hr))
{
pSpecify->Release();
return TRUE;
}
else
return FALSE;
}
void CJukeboxDlg::OnButtonProppage()
{
HRESULT hr;
IBaseFilter *pFilter = NULL;
TCHAR szNameToFind[128];
ISpecifyPropertyPages *pSpecify;
// Read the current filter name from the list box
int nCurSel = m_ListFilters.GetCurSel();
m_ListFilters.GetText(nCurSel, szNameToFind);
// Read the current list box name and find it in the graph
pFilter = FindFilterFromName(szNameToFind);
if (!pFilter)
return;
// Discover if this filter contains a property page
hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpecify);
if (SUCCEEDED(hr))
{
do
{
FILTER_INFO FilterInfo;
hr = pFilter->QueryFilterInfo(&FilterInfo);
if (FAILED(hr))
break;
CAUUID caGUID;
hr = pSpecify->GetPages(&caGUID);
if (FAILED(hr))
break;
pSpecify->Release();
// Display the filter's property page
OleCreatePropertyFrame(
m_hWnd, // Parent window
0, // x (Reserved)
0, // y (Reserved)
FilterInfo.achName, // Caption for the dialog box
1, // Number of filters
(IUnknown **)&pFilter, // Pointer to the filter
caGUID.cElems, // Number of property pages
caGUID.pElems, // Pointer to property page CLSIDs
0, // Locale identifier
0, // Reserved
NULL // Reserved
);
CoTaskMemFree(caGUID.pElems);
FilterInfo.pGraph->Release();
} while(0);
}
pFilter->Release();
}
void CJukeboxDlg::OnDblclkListFilters()
{
OnButtonProppage();
}
void CJukeboxDlg::DisplayECEvent(long lEventCode, long lParam1, long lParam2)
{
static TCHAR szMsg[256];
BOOL bMatch = TRUE;
#define HANDLE_EC(c) \
case c: \
wsprintf(szMsg, TEXT("%s\0"), TEXT(#c)); \
break;
switch (lEventCode)
{
HANDLE_EC(EC_ACTIVATE);
HANDLE_EC(EC_BUFFERING_DATA);
HANDLE_EC(EC_CLOCK_CHANGED);
HANDLE_EC(EC_COMPLETE);
HANDLE_EC(EC_DEVICE_LOST);
HANDLE_EC(EC_DISPLAY_CHANGED);
HANDLE_EC(EC_END_OF_SEGMENT);
HANDLE_EC(EC_ERROR_STILLPLAYING);
HANDLE_EC(EC_ERRORABORT);
HANDLE_EC(EC_EXTDEVICE_MODE_CHANGE);
HANDLE_EC(EC_FULLSCREEN_LOST);
HANDLE_EC(EC_GRAPH_CHANGED);
HANDLE_EC(EC_LENGTH_CHANGED);
HANDLE_EC(EC_NEED_RESTART);
HANDLE_EC(EC_NOTIFY_WINDOW);
HANDLE_EC(EC_OLE_EVENT);
HANDLE_EC(EC_OPENING_FILE);
HANDLE_EC(EC_PALETTE_CHANGED);
HANDLE_EC(EC_PAUSED);
HANDLE_EC(EC_QUALITY_CHANGE);
HANDLE_EC(EC_REPAINT);
HANDLE_EC(EC_SEGMENT_STARTED);
HANDLE_EC(EC_SHUTTING_DOWN);
HANDLE_EC(EC_SNDDEV_IN_ERROR);
HANDLE_EC(EC_SNDDEV_OUT_ERROR);
HANDLE_EC(EC_STARVATION);
HANDLE_EC(EC_STEP_COMPLETE);
HANDLE_EC(EC_STREAM_CONTROL_STARTED);
HANDLE_EC(EC_STREAM_CONTROL_STOPPED);
HANDLE_EC(EC_STREAM_ERROR_STILLPLAYING);
HANDLE_EC(EC_STREAM_ERROR_STOPPED);
HANDLE_EC(EC_TIMECODE_AVAILABLE);
HANDLE_EC(EC_USERABORT);
HANDLE_EC(EC_VIDEO_SIZE_CHANGED);
HANDLE_EC(EC_WINDOW_DESTROYED);
default:
bMatch = FALSE;
RetailOutput(TEXT(" Received unknown event code (0x%x)\r\n"), lEventCode);
break;
}
// If a recognized event was found, add its name to the events list box
if (bMatch)
m_ListEvents.AddString(szMsg);
}
void CJukeboxDlg::OnButtonFramestep()
{
StepFrame();
}
//
// Some hardware decoders and video renderers support stepping media
// frame by frame with the IVideoFrameStep interface. See the interface
// documentation for more details on frame stepping.
//
BOOL CJukeboxDlg::CanStep(void)
{
HRESULT hr;
IVideoFrameStep* pFS;
hr = pGB->QueryInterface(__uuidof(IVideoFrameStep), (PVOID *)&pFS);
if (FAILED(hr))
return FALSE;
// Check if this decoder can step
hr = pFS->CanStep(0L, NULL);
pFS->Release();
if (hr == S_OK)
return TRUE;
else
return FALSE;
}
HRESULT CJukeboxDlg::StepFrame(void)
{
// Get the Frame Stepping Interface
HRESULT hr;
IVideoFrameStep* pFS;
hr = pGB->QueryInterface(__uuidof(IVideoFrameStep), (PVOID *)&pFS);
if (FAILED(hr))
return hr;
// The graph must be paused for frame stepping to work
if (g_psCurrent != State_Paused)
OnPause();
// Step one frame
hr = pFS->Step(1, NULL);
pFS->Release();
// Since the media position has changed slightly, update the
// slider bar and position label.
ReadMediaPosition();
return hr;
}
void CJukeboxDlg::ConfigureSeekbar()
{
// Disable seekbar for new file and reset tracker/position label
m_Seekbar.SetPos(0);
m_StrPosition.SetWindowText(TEXT("Position: 00m:00s\0"));
g_rtTotalTime=0;
// If we can't read the file's duration, disable the seek bar
if (pMS && SUCCEEDED(pMS->GetDuration(&g_rtTotalTime)))
m_Seekbar.EnableWindow(TRUE);
else
m_Seekbar.EnableWindow(FALSE);
}
void CJukeboxDlg::StartSeekTimer()
{
// Cancel any pending timer event
StopSeekTimer();
// Create a new timer
g_wTimerID = SetTimer(TIMERID, TICKLEN, NULL);
}
void CJukeboxDlg::StopSeekTimer()
{
// Cancel the timer
if(g_wTimerID)
{
KillTimer(g_wTimerID);
g_wTimerID = 0;
}
}
void CJukeboxDlg::OnTimer(UINT nIDEvent)
{
ReadMediaPosition();
CDialog::OnTimer(nIDEvent);
}
void CJukeboxDlg::ReadMediaPosition()
{
HRESULT hr;
REFERENCE_TIME rtNow;
// Read the current stream position
hr = pMS->GetCurrentPosition(&rtNow);
if (FAILED(hr))
return;
// Convert position into a percentage value and update slider position
if (g_rtTotalTime != 0)
{
long lTick = (long)((rtNow * 100) / g_rtTotalTime);
m_Seekbar.SetPos(lTick);
}
else
m_Seekbar.SetPos(0);
// Update the 'current position' string on the main dialog
UpdatePosition(rtNow);
}
void CJukeboxDlg::UpdatePosition(REFERENCE_TIME rtNow)
{
HRESULT hr;
// If no reference time was passed in, read the current position
if (rtNow == 0)
{
// Read the current stream position
hr = pMS->GetCurrentPosition(&rtNow);
if (FAILED(hr))
return;
}
// Convert the LONGLONG duration into human-readable format
unsigned long nTotalMS = (unsigned long) rtNow / 10000; // 100ns -> ms
int nSeconds = nTotalMS / 1000;
int nMinutes = nSeconds / 60;
nSeconds %= 60;
// Update the display
TCHAR szPosition[24], szCurrentString[24];
wsprintf(szPosition, _T("Position: %02dm:%02ds\0"), nMinutes, nSeconds);
// Read current string and compare to the new string. To prevent flicker,
// don't update this label unless the string has changed.
m_StrPosition.GetWindowText(szCurrentString, 24);
if (_tcscmp(szCurrentString, szPosition))
m_StrPosition.SetWindowText(szPosition);
}
void CJukeboxDlg::HandleTrackbar(WPARAM wReq)
{
HRESULT hr;
static OAFilterState state;
static BOOL bStartOfScroll = TRUE;
// If the file is not seekable, the trackbar is disabled.
DWORD dwPosition = m_Seekbar.GetPos();
// Pause when the scroll action begins.
if (bStartOfScroll)
{
hr = pMC->GetState(10, &state);
bStartOfScroll = FALSE;
hr = pMC->Pause();
}
// Update the position continuously.
REFERENCE_TIME rtNew = (g_rtTotalTime * dwPosition) / 100;
hr = pMS->SetPositions(&rtNew, AM_SEEKING_AbsolutePositioning,
NULL, AM_SEEKING_NoPositioning);
// Restore the state at the end.
if (wReq == TB_ENDTRACK)
{
if (state == State_Stopped)
hr = pMC->Stop();
else if (state == State_Running)
hr = pMC->Run();
bStartOfScroll = TRUE;
}
// Update the 'current position' string on the main dialog.
UpdatePosition(rtNew);
}
LONG CJukeboxDlg::GetGraphEditPath(TCHAR *szPath)
{
HKEY hKey;
DWORD dwType, dwSize = MAX_PATH;
// Open the appropriate registry key
LONG lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
_T("Software\\Microsoft\\Shared Tools\\Graphedit"),
0, KEY_READ, &hKey );
if( ERROR_SUCCESS != lResult )
return -1;
// Read the full path (including .exe name) for the GraphEdit tool
lResult = RegQueryValueEx( hKey, _T("Path"), NULL,
&dwType, (BYTE*)szPath, &dwSize );
RegCloseKey( hKey );
if( ERROR_SUCCESS != lResult )
return -1;
return 0;
}
void CJukeboxDlg::OnButtonGraphedit()
{
TCHAR szFilename[128], szFile[MAX_PATH];
// Read file name from list box
int nItem = m_ListFiles.GetCurSel();
m_ListFiles.GetText(nItem, szFilename);
// Build the full file name with path. The path will already have
// a '\' appended to the end.
wsprintf(szFile, TEXT("%s%s\0"), m_szCurrentDir, szFilename);
// Launch GraphEdit for the selected file
//
// First look for a registry key containing its full path
TCHAR szPath[MAX_PATH];
LONG lResult = GetGraphEditPath(szPath);
// If the DirectX SDK is not installed, just look for GraphEdit
// anywhere in the system path.
if (lResult != 0)
{
wsprintf(szPath, TEXT("%s\0"), TEXT("graphedt\0"));
}
// Lauch GraphEdit using either the full tool path or just its name
HINSTANCE rc = ShellExecute(m_hWnd, TEXT("open\0"), szPath,
szFile, NULL, SW_SHOWNORMAL);
if (rc < (HINSTANCE) 32)
{
// Failed to start the app
if ((rc == (HINSTANCE) ERROR_FILE_NOT_FOUND) ||
(rc == (HINSTANCE) ERROR_PATH_NOT_FOUND))
{
MessageBox(TEXT("Couldn't find the GraphEdit application.\r\n\r\n")
TEXT("Please copy graphedt.exe to a directory on your path."),
TEXT("Can't find GraphEdit"));
}
}
}
BOOL CJukeboxDlg::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);
}