1352 lines
33 KiB
C++
1352 lines
33 KiB
C++
//------------------------------------------------------------------------------
|
|
//
|
|
// File: MainDialog.h
|
|
// Implements the main dialog.
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "MFPlayer.h"
|
|
#include "Player2.h"
|
|
|
|
// Constants
|
|
|
|
const MFTIME ONE_SECOND = 10000000; // One second in hns
|
|
const LONG ONE_MSEC = 1000; // One msec in hns
|
|
|
|
const UINT_PTR IDT_TIMER1 = 1; // Timer ID
|
|
const UINT TICK_FREQ = 250; // Timer frequency in msec
|
|
|
|
const LONG MIN_VOL = 0;
|
|
const LONG MAX_VOL = 100;
|
|
|
|
|
|
#include <initguid.h>
|
|
|
|
// CLSID of the sample video effect MFT.
|
|
// (To use this CLSID, you must build the MFT_Grayscale sample and register the DLL.)
|
|
|
|
// {2F3DBC05-C011-4a8f-B264-E42E35C67BF4}
|
|
DEFINE_GUID(CLSID_GrayscaleMFT,
|
|
0x2f3dbc05, 0xc011, 0x4a8f, 0xb2, 0x64, 0xe4, 0x2e, 0x35, 0xc6, 0x7b, 0xf4);
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// OpenUrlDialogInfo struct
|
|
//
|
|
// Contains data passed to the "Open URL" dialog proc.
|
|
//------------------------------------------------------------------------------
|
|
|
|
struct OpenUrlDialogInfo
|
|
{
|
|
WCHAR *pszURL;
|
|
DWORD cch;
|
|
};
|
|
|
|
|
|
// Function declarations
|
|
|
|
INT_PTR CALLBACK OpenUrlDialogProc(HWND, UINT, WPARAM, LPARAM);
|
|
|
|
void NotifyError(HWND hwnd, const WCHAR *sErrorMessage, HRESULT hrErr);
|
|
void ToggleMenuItemCheck(UINT bMenuItemID, HMENU hmenu);
|
|
HRESULT AllocGetWindowText(HWND hwnd, WCHAR **pszText, DWORD *pcchLen);
|
|
void EnableDialogControl(HWND hDlg, int nIDDlgItem, BOOL bEnable);
|
|
BOOL StatusBar_SetText(HWND hwnd, int iPart, const WCHAR* pszText);
|
|
|
|
|
|
inline BOOL IsMenuChecked(HMENU hmenu, UINT bMenuItemID)
|
|
{
|
|
return GetMenuState(hmenu, bMenuItemID, MF_BYCOMMAND);
|
|
}
|
|
|
|
inline float VolumeFromSlider(LONG pos)
|
|
{
|
|
return (float)pos / MAX_VOL;
|
|
}
|
|
|
|
inline LONG SliderPosFromVolume(float fLevel)
|
|
{
|
|
return (LONG)(fLevel * MAX_VOL);
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Constructor
|
|
//-----------------------------------------------------------------------------
|
|
|
|
MainDialog::MainDialog() :
|
|
m_nID(IDD_DIALOG1),
|
|
m_hDlg(0),
|
|
m_pPlayer(NULL),
|
|
m_timerID(0),
|
|
m_hSeekbar(NULL),
|
|
m_hVolume(NULL),
|
|
m_bSeeking(FALSE)
|
|
{
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Destructor
|
|
//-----------------------------------------------------------------------------
|
|
|
|
MainDialog::~MainDialog()
|
|
{
|
|
if (m_pPlayer)
|
|
{
|
|
m_pPlayer->Shutdown();
|
|
}
|
|
|
|
SafeRelease(&m_pPlayer);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// ShowDialog()
|
|
// Displays the dialog
|
|
//
|
|
// Application instance
|
|
//
|
|
// Returns TRUE if successful or FALSE otherwise
|
|
//-----------------------------------------------------------------------------
|
|
BOOL MainDialog::ShowDialog(HINSTANCE hinst)
|
|
{
|
|
// Show the dialog. Pass a pointer to ourselves as the LPARAM
|
|
INT_PTR ret = DialogBoxParam(
|
|
hinst,
|
|
MAKEINTRESOURCE(m_nID),
|
|
NULL,
|
|
DialogProc,
|
|
(LPARAM)this
|
|
);
|
|
|
|
if (ret == 0 || ret == -1)
|
|
{
|
|
MessageBox( NULL, L"Could not create dialog", L"Error", MB_OK | MB_ICONERROR );
|
|
return FALSE;
|
|
}
|
|
|
|
return (IDOK == ret);
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OnInitDialog
|
|
//
|
|
// Handler for WM_INITDIALOG message.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT MainDialog::OnInitDialog()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Create the player object.
|
|
hr = MFPlayer2::CreateInstance(
|
|
m_hDlg,
|
|
GetDlgItem(IDC_VIDEO),
|
|
&m_pPlayer
|
|
);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
InitializeControls();
|
|
|
|
// Set default UI state.
|
|
UpdateUI(MFP_MEDIAPLAYER_STATE_EMPTY);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OnCommand
|
|
//
|
|
// Handler for WM_COMMAND messages.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
INT_PTR MainDialog::OnCommand(HWND /*hControl*/, WORD idControl, WORD /*msg*/)
|
|
{
|
|
switch (idControl)
|
|
{
|
|
case ID_FILE_OPENFILE:
|
|
OnFileOpen();
|
|
break;
|
|
|
|
case ID_FILE_OPENURL:
|
|
OnOpenURL();
|
|
break;
|
|
|
|
case ID_FILE_EXIT:
|
|
PostQuitMessage(0);
|
|
break;
|
|
|
|
case IDC_MUTE:
|
|
OnMute();
|
|
break;
|
|
|
|
case IDC_PLAY:
|
|
OnPlayOrPause();
|
|
break;
|
|
|
|
case IDC_REWIND:
|
|
OnRewind();
|
|
break;
|
|
|
|
case IDC_FASTFORWARD:
|
|
OnFastForward();
|
|
break;
|
|
|
|
case ID_OPTIONS_VIDEOEFFECT:
|
|
ToggleMenuItemCheck(idControl, GetMenu(m_hDlg));
|
|
break;
|
|
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OnCommand
|
|
//
|
|
// Handler for WM_NOTIFY messages.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
LRESULT MainDialog::OnNotify(NMHDR *pHdr)
|
|
{
|
|
LRESULT result = 0; // ignored unless documented otherwise.
|
|
|
|
switch (pHdr->idFrom)
|
|
{
|
|
case IDC_SEEKBAR:
|
|
OnSeekbarNotify((NMSLIDER_INFO*)pHdr);
|
|
break;
|
|
|
|
case IDC_VOLUME:
|
|
OnVolumeNotify((NMSLIDER_INFO*)pHdr);
|
|
break;
|
|
|
|
case IDC_MUTE:
|
|
if (pHdr->code == NM_CUSTOMDRAW)
|
|
{
|
|
result = m_mute.Draw((NMCUSTOMDRAW*)pHdr);
|
|
}
|
|
break;
|
|
|
|
case IDC_PLAY:
|
|
if (pHdr->code == NM_CUSTOMDRAW)
|
|
{
|
|
result = m_play.Draw((NMCUSTOMDRAW*)pHdr);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OnReceiveMsg
|
|
//
|
|
// Handler for any other window messages.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
INT_PTR MainDialog::OnReceiveMsg(UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
PAINTSTRUCT ps;
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_TIMER: // Timer message
|
|
OnTimer();
|
|
break;
|
|
|
|
case WM_HSCROLL: // Trackbar scroll
|
|
OnScroll(LOWORD(wParam), HIWORD(wParam), (HWND)lParam);
|
|
break;
|
|
|
|
case WM_THEMECHANGED: // User changed the current theme.
|
|
m_mute.ResetTheme();
|
|
m_play.ResetTheme();
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
|
|
BeginPaint(m_hDlg, &ps);
|
|
|
|
if (m_pPlayer)
|
|
{
|
|
m_pPlayer->UpdateVideo();
|
|
}
|
|
|
|
EndPaint(m_hDlg, &ps);
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
PostQuitMessage(0);
|
|
break;
|
|
|
|
// Private window messages from the MFPlayer class.
|
|
|
|
case WM_APP_NOTIFY:
|
|
OnPlayerNotify((MFP_MEDIAPLAYER_STATE)wParam);
|
|
break;
|
|
|
|
case WM_APP_ERROR:
|
|
NotifyError(m_hDlg, L"Playback Error", (HRESULT)wParam);
|
|
UpdateUI( );
|
|
break;
|
|
|
|
case WM_AUDIO_EVENT:
|
|
{
|
|
// The audio level been changed.
|
|
|
|
float fVolume = *((float *)(&wParam));
|
|
|
|
m_mute.SetButtonImage((UINT)-1, (lParam ? 1 : 0));
|
|
InvalidateRect(m_mute.Window(), NULL, FALSE);
|
|
|
|
Slider_SetPosition(m_hVolume, SliderPosFromVolume(fVolume));
|
|
}
|
|
break;
|
|
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// InitializeControls
|
|
//
|
|
// Initializes the UI controls.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::InitializeControls()
|
|
{
|
|
m_mute.SetWindow(GetDlgItem(IDC_MUTE));
|
|
m_play.SetWindow(GetDlgItem(IDC_PLAY));
|
|
|
|
// Create a brush for the seekbar background.
|
|
// Note: Don't use GetSysColorBrush because the slider control destroys the brush.
|
|
HBRUSH hBrush = CreateSolidBrush(RGB(0,0,0));
|
|
|
|
m_hSeekbar = GetDlgItem(IDC_SEEKBAR);
|
|
|
|
Slider_SetBackground(m_hSeekbar, hBrush);
|
|
Slider_SetThumbBitmap(m_hSeekbar, IDB_SLIDER);
|
|
EnableDialogControl(m_hDlg, IDC_SEEKBAR, FALSE);
|
|
|
|
hBrush = CreateSolidBrush(RGB(12,3,127));
|
|
|
|
m_hVolume = GetDlgItem(IDC_VOLUME);
|
|
|
|
Slider_SetBackground(m_hVolume, hBrush);
|
|
Slider_SetThumbBitmap(m_hVolume, IDB_VOLUME);
|
|
Slider_SetRange(m_hVolume, MIN_VOL, MAX_VOL);
|
|
Slider_SetPosition(m_hVolume, MAX_VOL);
|
|
EnableDialogControl(m_hDlg, IDC_VOLUME, TRUE);
|
|
|
|
|
|
|
|
m_mute.LoadBitmap(IDB_MUTE, 2);
|
|
m_mute.SetButtonImage((UINT)-1, 0);
|
|
|
|
m_play.LoadBitmap(IDB_PLAY, 2);
|
|
m_play.SetButtonImage((UINT)-1, 0);
|
|
|
|
// Set the range for the "zoom" trackbar.
|
|
HWND hZoom = GetDlgItem(IDC_VIDEO_ZOOM);
|
|
|
|
SendMessage(hZoom, TBM_SETRANGEMIN, TRUE, 100);
|
|
SendMessage(hZoom, TBM_SETRANGEMAX, TRUE, 500);
|
|
SendMessage(hZoom, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)100);
|
|
|
|
InitStatusBar();
|
|
|
|
ClearMetadata();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OnPlayerNotify
|
|
//
|
|
// Called when the player object changes state.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::OnPlayerNotify(MFP_MEDIAPLAYER_STATE state)
|
|
{
|
|
UpdateSeekBar();
|
|
UpdateUI(state);
|
|
|
|
if (state == MFP_MEDIAPLAYER_STATE_PLAYING)
|
|
{
|
|
UpdateMetadata();
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OnFileOpen
|
|
//
|
|
// Open a new file for playback.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::OnFileOpen()
|
|
{
|
|
const WCHAR *lpstrFilter =
|
|
L"Media Files\0*.aac;*.asf;*.avi;*.m4a;*.mp3;*.mp4;*.wav;*.wma;*.wmv;*.3gp;*.3g2\0"
|
|
L"All files\0*.*\0";
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
OPENFILENAME ofn;
|
|
ZeroMemory(&ofn, sizeof(ofn));
|
|
|
|
WCHAR szFileName[MAX_PATH];
|
|
szFileName[0] = L'\0';
|
|
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = m_hDlg;
|
|
ofn.hInstance = GetInstance();
|
|
ofn.lpstrFilter = lpstrFilter;
|
|
ofn.lpstrFile = szFileName;
|
|
ofn.nMaxFile = MAX_PATH;
|
|
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST;
|
|
|
|
if (GetOpenFileName(&ofn))
|
|
{
|
|
ApplyOptions();
|
|
|
|
// Try to open the file.
|
|
hr = m_pPlayer->OpenURL(szFileName);
|
|
|
|
// Update the state of the UI. (Regardless of success/failure code)
|
|
UpdateUI();
|
|
|
|
// Invalidate the video window, in case there is an old video
|
|
// frame from the previous file and there is no video now. (eg, the
|
|
// new file is audio only, or we failed to open this file.)
|
|
InvalidateRect( GetDlgItem(IDC_VIDEO) , NULL, FALSE);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
NotifyError(m_hDlg, L"Cannot open this file.", hr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// GetOpenFileName can return FALSE because the user cancelled,
|
|
// or because it failed. Check for errors.
|
|
|
|
DWORD err = CommDlgExtendedError();
|
|
if (err != 0)
|
|
{
|
|
NotifyError(m_hDlg, L"GetOpenFileName failed.", E_FAIL);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OnOpenURL
|
|
//
|
|
// Opens a media file from a URL.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::OnOpenURL()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
INT_PTR result = 0;
|
|
|
|
// Pass in an OpenUrlDialogInfo structure to the dialog. The dialog
|
|
// fills in this structure with the URL. The dialog proc allocates
|
|
// the memory for the string.
|
|
|
|
OpenUrlDialogInfo url;
|
|
ZeroMemory(&url, sizeof(&url));
|
|
|
|
// Show the Open URL dialog.
|
|
result = DialogBoxParam(GetInstance(), MAKEINTRESOURCE(IDD_OPENURL), m_hDlg,
|
|
OpenUrlDialogProc, (LPARAM)&url);
|
|
|
|
if (result == IDOK)
|
|
{
|
|
ApplyOptions();
|
|
|
|
// Open the file with the playback object.
|
|
hr = m_pPlayer->OpenURL(url.pszURL);
|
|
|
|
// Update the state of the UI. (Regardless of success/failure code)
|
|
UpdateUI();
|
|
|
|
SetStatusText(L"Opening...");
|
|
|
|
// Invalidate the video window, in case there is an old video
|
|
// frame from the previous file and there is no video now. (eg, the
|
|
// new file is audio only, or we failed to open this file.)
|
|
InvalidateRect(GetDlgItem(IDC_VIDEO), NULL, FALSE);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
NotifyError(m_hDlg, L"Cannot open this URL.", hr);
|
|
}
|
|
}
|
|
|
|
// The app must release the string for the URL.
|
|
CoTaskMemFree(url.pszURL);
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OnScroll
|
|
//
|
|
// Called when user selects, drags, or releases a trackbar control.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::OnScroll(WORD request, WORD /*position*/, HWND hControl)
|
|
{
|
|
// We ignore the following trackbar requests:
|
|
switch (request)
|
|
{
|
|
case SB_ENDSCROLL:
|
|
case SB_LEFT:
|
|
case SB_RIGHT:
|
|
case SB_THUMBPOSITION:
|
|
return;
|
|
}
|
|
|
|
if (hControl == GetDlgItem(IDC_VIDEO_ZOOM))
|
|
{
|
|
if (m_pPlayer)
|
|
{
|
|
LONG pos = (LONG)SendMessage(hControl, TBM_GETPOS, 0, 0);
|
|
|
|
// Scale the video.
|
|
float fZoom = (float)pos / 100;
|
|
|
|
m_pPlayer->SetZoom(fZoom);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OnSeekbarNotify
|
|
//
|
|
// Called when user selects, drags, or releases a seek bar.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::OnSeekbarNotify(const NMSLIDER_INFO *pInfo)
|
|
{
|
|
if (m_pPlayer)
|
|
{
|
|
if (pInfo->hdr.code == SLIDER_NOTIFY_SELECT)
|
|
{
|
|
m_bSeeking = TRUE;
|
|
}
|
|
|
|
// When dragging or selecting, seek to the position.
|
|
if ((pInfo->hdr.code == SLIDER_NOTIFY_SELECT) ||
|
|
(pInfo->hdr.code == SLIDER_NOTIFY_DRAG))
|
|
{
|
|
LONGLONG pos = ONE_MSEC * (LONGLONG)pInfo->position;
|
|
|
|
(void)m_pPlayer->SetPosition(pos);
|
|
|
|
SetStatusTime(pos);
|
|
}
|
|
|
|
if (pInfo->hdr.code == SLIDER_NOTIFY_RELEASE)
|
|
{
|
|
m_bSeeking = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OnPlayOrPause
|
|
//
|
|
// Called when the user clicks the play/pause button.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::OnPlayOrPause()
|
|
{
|
|
// This button toggles between play and pause.
|
|
|
|
MFP_MEDIAPLAYER_STATE state;
|
|
|
|
m_pPlayer->GetState(&state);
|
|
|
|
if (state == MFP_MEDIAPLAYER_STATE_PLAYING)
|
|
{
|
|
m_pPlayer->Pause();
|
|
}
|
|
else
|
|
{
|
|
m_pPlayer->Play();
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OnFastForward
|
|
//
|
|
// Called when the user clicks the fast-forward button.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::OnFastForward()
|
|
{
|
|
if (m_pPlayer)
|
|
{
|
|
MFP_MEDIAPLAYER_STATE state;
|
|
|
|
m_pPlayer->GetState(&state);
|
|
|
|
if (state == MFP_MEDIAPLAYER_STATE_PAUSED)
|
|
{
|
|
m_pPlayer->FrameStep();
|
|
}
|
|
else
|
|
{
|
|
m_pPlayer->FastForward();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OnRewind
|
|
//
|
|
// Called when the user clicks the rewind button.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::OnRewind()
|
|
{
|
|
if (m_pPlayer)
|
|
{
|
|
m_pPlayer->Rewind();
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OnVolumeNotify
|
|
//
|
|
// Called when the user drags the volume slider.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::OnVolumeNotify(const NMSLIDER_INFO* pInfo)
|
|
{
|
|
if (m_pPlayer)
|
|
{
|
|
m_pPlayer->SetVolume( VolumeFromSlider(pInfo->position) );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OnVolumeChanged
|
|
//
|
|
// Called when the volume changes because of actions outside of this
|
|
// application. (For example, when the user changes the volume in the
|
|
// Volume control panel.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::OnVolumeChanged()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
float fLevel = 0;
|
|
BOOL bMute = FALSE;
|
|
|
|
if (m_pPlayer)
|
|
{
|
|
// Get the new volume level and update the slider position.
|
|
hr = m_pPlayer->GetVolume(&fLevel);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Slider_SetPosition(m_hVolume, SliderPosFromVolume(fLevel));
|
|
}
|
|
|
|
// Get the new mute state and update the image on the mute button.
|
|
hr = m_pPlayer->GetMute(&bMute);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_mute.SetButtonImage((UINT)-1, (bMute ? 1 : 0));
|
|
InvalidateRect(m_mute.Window(), NULL, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OnMute
|
|
//
|
|
// Called when the user clicks the mute/unmute button.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::OnMute()
|
|
{
|
|
BOOL bMute = FALSE;
|
|
|
|
if (m_pPlayer)
|
|
{
|
|
HRESULT hr = m_pPlayer->GetMute(&bMute);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
bMute = !bMute; // Flip the mute state.
|
|
hr = m_pPlayer->SetMute(bMute);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_mute.SetButtonImage((UINT)-1, (bMute ? 1 : 0));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OnTimer
|
|
//
|
|
// Called when the timer elapses.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::OnTimer()
|
|
{
|
|
MFTIME timeNow;
|
|
|
|
if (m_pPlayer && !m_bSeeking)
|
|
{
|
|
if (SUCCEEDED(m_pPlayer->GetCurrentPosition(&timeNow)))
|
|
{
|
|
Slider_SetPosition(m_hSeekbar, (LONG)(timeNow / ONE_MSEC));
|
|
|
|
SetStatusTime(timeNow);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// ApplyOptions
|
|
//
|
|
// Applies the user's playback options, before opening a new media file.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::ApplyOptions()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
HMENU hMenu = GetMenu(m_hDlg);
|
|
|
|
BOOL bVideoFX = IsMenuChecked(hMenu, ID_OPTIONS_VIDEOEFFECT);
|
|
|
|
if (bVideoFX)
|
|
{
|
|
IMFTransform *pMFT = NULL;
|
|
|
|
hr = CoCreateInstance(
|
|
CLSID_GrayscaleMFT,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_PPV_ARGS(&pMFT)
|
|
);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
NotifyError(m_hDlg, L"Cannot create grayscale transform. Did you register the DLL?", hr);
|
|
}
|
|
else
|
|
{
|
|
hr = m_pPlayer->SetEffect(pMFT);
|
|
pMFT->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = m_pPlayer->SetEffect(NULL);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// UpdateUI
|
|
//
|
|
// Update the dialog based on the current playback state.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::UpdateUI()
|
|
{
|
|
MFP_MEDIAPLAYER_STATE state = MFP_MEDIAPLAYER_STATE_EMPTY;
|
|
|
|
if (m_pPlayer)
|
|
{
|
|
m_pPlayer->GetState(&state);
|
|
}
|
|
|
|
UpdateUI(state);
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// UpdateUI
|
|
//
|
|
// Update the dialog based on the current playback state.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::UpdateUI(MFP_MEDIAPLAYER_STATE state)
|
|
{
|
|
BOOL bFileOpen = TRUE;
|
|
BOOL bHasVideo = FALSE;
|
|
BOOL bHasAudio = FALSE;
|
|
BOOL bEnablePlay = FALSE;
|
|
BOOL bPlay = TRUE;
|
|
|
|
// If bPlay is TRUE, the Play/Pause button shows the "play" image.
|
|
// If bEnablePlay is TRUE, the Play/Pause button is enabled.
|
|
|
|
BOOL bCanSeek = FALSE;
|
|
BOOL bEnableSeek = FALSE;
|
|
|
|
BOOL bCanFF = FALSE;
|
|
BOOL bCanRewind = FALSE;
|
|
BOOL bEnableTrickMode = FALSE;
|
|
|
|
if (m_pPlayer)
|
|
{
|
|
m_pPlayer->HasVideo(&bHasVideo);
|
|
m_pPlayer->CanSeek(&bCanSeek);
|
|
m_pPlayer->CanFastForward(&bCanFF);
|
|
m_pPlayer->CanRewind(&bCanRewind);
|
|
|
|
bHasAudio = m_pPlayer->IsAudioEnabled();
|
|
}
|
|
switch (state)
|
|
{
|
|
case MFP_MEDIAPLAYER_STATE_EMPTY:
|
|
case MFP_MEDIAPLAYER_STATE_SHUTDOWN:
|
|
bFileOpen = FALSE;
|
|
SetStatusText(L"Closed");
|
|
break;
|
|
|
|
case MFP_MEDIAPLAYER_STATE_PLAYING:
|
|
bEnablePlay = TRUE;
|
|
bPlay = FALSE;
|
|
bEnableSeek = bCanSeek;
|
|
bEnableTrickMode = TRUE;
|
|
SetStatusText(L"Playing");
|
|
UpdateMetadata();
|
|
break;
|
|
|
|
case MFP_MEDIAPLAYER_STATE_PAUSED:
|
|
bEnablePlay = TRUE;
|
|
bEnableSeek = bCanSeek;
|
|
bEnableTrickMode = bCanFF = TRUE; // Fast forward button is "frame step" while paused.
|
|
SetStatusText(L"Paused");
|
|
break;
|
|
|
|
case MFP_MEDIAPLAYER_STATE_STOPPED:
|
|
bEnablePlay = TRUE;
|
|
SetStatusText(L"Stopped");
|
|
break;
|
|
}
|
|
|
|
|
|
EnableDialogControl(m_hDlg, IDC_VIDEO_ZOOM, bHasVideo);
|
|
|
|
EnableDialogControl(m_hDlg, IDC_PLAY, bEnablePlay);
|
|
EnableDialogControl(m_hDlg, IDC_SEEKBAR, bEnablePlay);
|
|
|
|
EnableDialogControl(m_hDlg, IDC_FASTFORWARD, (bEnableTrickMode && bCanFF));
|
|
EnableDialogControl(m_hDlg, IDC_REWIND, (bEnableTrickMode && bCanRewind));
|
|
|
|
m_play.SetButtonImage((UINT)-1, (bPlay ? 0 : 1));
|
|
InvalidateRect(m_play.Window(), NULL, FALSE);
|
|
|
|
EnableDialogControl(m_hDlg, IDC_MUTE, bHasAudio);
|
|
EnableDialogControl(m_hDlg, IDC_VOLUME, bHasAudio);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// UpdateSeekBar
|
|
//
|
|
// Update the state of the seek bar.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::UpdateSeekBar()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL bCanSeek = FALSE;
|
|
MFTIME duration = 0;
|
|
|
|
// Stop the old timer, if any.
|
|
StopTimer();
|
|
|
|
if (m_pPlayer)
|
|
{
|
|
hr = m_pPlayer->CanSeek(&bCanSeek);
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
// If the player can seek, set the seekbar range and start the time.
|
|
// Otherwise, disable the seekbar.
|
|
if (bCanSeek)
|
|
{
|
|
hr = m_pPlayer->GetDuration(&duration);
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
Slider_SetRange(m_hSeekbar, 0, (LONG)(duration / ONE_MSEC));
|
|
EnableDialogControl(m_hDlg, IDC_SEEKBAR, TRUE);
|
|
|
|
// Start the timer
|
|
m_timerID = SetTimer(m_hDlg, IDT_TIMER1, TICK_FREQ, NULL);
|
|
}
|
|
else
|
|
{
|
|
EnableDialogControl(m_hDlg, IDC_SEEKBAR, FALSE);
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (FAILED(hr))
|
|
{
|
|
EnableDialogControl(m_hDlg, IDC_SEEKBAR, FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// UpdateMetadata
|
|
//
|
|
// Update the metadata text.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::UpdateMetadata()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
IPropertyStore *pProp = NULL;
|
|
|
|
PROPVARIANT var;
|
|
PropVariantInit(&var);
|
|
|
|
ClearMetadata();
|
|
|
|
if (m_pPlayer)
|
|
{
|
|
hr = m_pPlayer->GetMetadata(&pProp);
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
hr = pProp->GetValue(PKEY_Title, &var);
|
|
if (SUCCEEDED(hr) && var.vt == VT_LPWSTR)
|
|
{
|
|
SetWindowText( GetDlgItem(IDC_MEDIA_TITLE), var.pwszVal );
|
|
}
|
|
PropVariantClear(&var);
|
|
|
|
hr = pProp->GetValue(PKEY_Author, &var);
|
|
if (SUCCEEDED(hr) && var.vt == VT_LPWSTR)
|
|
{
|
|
SetWindowText( GetDlgItem(IDC_MEDIA_AUTHOR), var.pwszVal );
|
|
}
|
|
PropVariantClear(&var);
|
|
}
|
|
|
|
done:
|
|
PropVariantClear(&var);
|
|
SafeRelease(&pProp);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// ClearMetadata
|
|
//
|
|
// Clear the metadata text.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::ClearMetadata()
|
|
{
|
|
SetWindowText( GetDlgItem(IDC_MEDIA_TITLE), L"" );
|
|
SetWindowText( GetDlgItem(IDC_MEDIA_AUTHOR), L"" );
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// StopTimer
|
|
//
|
|
// Stop the timer.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::StopTimer()
|
|
{
|
|
if (m_timerID != 0)
|
|
{
|
|
KillTimer(m_hDlg, m_timerID);
|
|
m_timerID = 0;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// InitStatusBar
|
|
//
|
|
// Initialize the dialog status bar.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::InitStatusBar()
|
|
{
|
|
// NOTE: A status bar sets its own width and height.
|
|
|
|
HWND hStatus = CreateWindowEx(
|
|
0,
|
|
STATUSCLASSNAME,
|
|
NULL,
|
|
WS_CHILD | WS_VISIBLE,
|
|
0, 0, 0, 0,
|
|
m_hDlg,
|
|
(HMENU)(INT_PTR)IDC_STATUS_BAR,
|
|
GetInstance(),
|
|
NULL);
|
|
|
|
|
|
SIZE szTimeCode;
|
|
RECT rcStatusBar;
|
|
|
|
HDC hdc = GetDC(hStatus);
|
|
|
|
const WCHAR tmp[] = L"0000:00:00";
|
|
GetTextExtentPoint32(hdc, tmp, ARRAYSIZE(tmp), &szTimeCode);
|
|
|
|
ReleaseDC(GetDlgItem(IDC_STATUS_BAR), hdc);
|
|
|
|
GetClientRect(hStatus, &rcStatusBar);
|
|
|
|
int iParts[] = { rcStatusBar.right - szTimeCode.cx, -1};
|
|
|
|
SendMessage(hStatus, SB_SETPARTS, (WPARAM)2, (LPARAM)iParts);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// SetStatusTime
|
|
//
|
|
// Display the specified presentation time in the status bar.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::SetStatusTime(const MFTIME& time)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WCHAR szTimeStamp[32];
|
|
MFTIME hour = 0, min = 0, sec = 0;
|
|
|
|
sec = (time / ONE_SECOND);
|
|
min = (time / (ONE_SECOND * 60));
|
|
hour = (time / (ONE_SECOND * 360));
|
|
|
|
hr = StringCchPrintf(
|
|
szTimeStamp,
|
|
ARRAYSIZE(szTimeStamp),
|
|
L"%d:%02d:%02d",
|
|
(DWORD)hour, (DWORD)(min % 60), (DWORD)(sec % 60)
|
|
);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
HWND hStatus = GetDlgItem(IDC_STATUS_BAR);
|
|
|
|
StatusBar_SetText(hStatus, 1, szTimeStamp);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// SetStatusText
|
|
//
|
|
// Set text in the status bar.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void MainDialog::SetStatusText(const WCHAR *pszStatus)
|
|
{
|
|
HWND hStatus = GetDlgItem(IDC_STATUS_BAR);
|
|
|
|
StatusBar_SetText(hStatus, 0, pszStatus);
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: DialogProc()
|
|
// Desc: DialogProc for the dialog. This is a static class method.
|
|
//
|
|
// lParam: Pointer to the CBaseDialog object.
|
|
//
|
|
// The CBaseDialog class specifies lParam when it calls DialogBoxParam. We store the
|
|
// pointer as user data in the window.
|
|
//
|
|
// (Note: The DirectShow CBasePropertyPage class uses the same technique.)
|
|
//-----------------------------------------------------------------------------
|
|
INT_PTR CALLBACK MainDialog::DialogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
MainDialog *pDlg = 0; // Pointer to the dialog class that manages the dialog
|
|
|
|
LRESULT lresult = 0;
|
|
|
|
if (msg == WM_INITDIALOG)
|
|
{
|
|
// Get the pointer to the dialog object and store it in
|
|
// the window's user data
|
|
|
|
SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)lParam);
|
|
|
|
pDlg = (MainDialog*)lParam;
|
|
if (pDlg)
|
|
{
|
|
pDlg->m_hDlg = hDlg;
|
|
HRESULT hr = pDlg->OnInitDialog();
|
|
if (FAILED(hr))
|
|
{
|
|
pDlg->EndDialog(0);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// Get the dialog object from the window's user data
|
|
pDlg = (MainDialog*)(DWORD_PTR) GetWindowLongPtr(hDlg, DWLP_USER);
|
|
|
|
if (pDlg != NULL)
|
|
{
|
|
switch (msg)
|
|
{
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDOK:
|
|
case IDCANCEL:
|
|
pDlg->EndDialog(LOWORD(wParam));
|
|
return TRUE;
|
|
|
|
default:
|
|
return pDlg->OnCommand((HWND)lParam, LOWORD(wParam), HIWORD(wParam));
|
|
}
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
lresult = pDlg->OnNotify((NMHDR*)lParam);
|
|
|
|
// The LRESULT from WM_NOTIFY can be meaningful. Store the result in DWLP_MSGRESULT.
|
|
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)lresult);
|
|
return TRUE;
|
|
|
|
default:
|
|
return pDlg->OnReceiveMsg(msg, wParam, lParam);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// NotifyError
|
|
//
|
|
// Show a message box with an error message.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void NotifyError(
|
|
HWND hwnd,
|
|
const WCHAR *sErrorMessage, // Error message
|
|
HRESULT hrErr // Status code
|
|
)
|
|
{
|
|
const size_t MESSAGE_LEN = 512;
|
|
WCHAR message[MESSAGE_LEN];
|
|
|
|
HRESULT hr = StringCchPrintf (message, MESSAGE_LEN, L"%s (HRESULT = 0x%X)", sErrorMessage, hrErr);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
MessageBox(hwnd, message, NULL, MB_OK | MB_ICONERROR);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OpenUrlDialogProc
|
|
//
|
|
// Dialog procedure for the "Open URL" window.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
INT_PTR CALLBACK OpenUrlDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static OpenUrlDialogInfo *pUrl = NULL;
|
|
|
|
BOOL result = FALSE;
|
|
|
|
switch (message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
// The app sends a pointer to an OpenUrlDialogInfo structure as the lParam.
|
|
// We use this structure to store the URL.
|
|
pUrl = (OpenUrlDialogInfo*)lParam;
|
|
return (INT_PTR)TRUE;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDOK:
|
|
if (pUrl)
|
|
{
|
|
// Get the URL from the edit box in the dialog.
|
|
// This function allocates memory. The app must call CoTaskMemAlloc.
|
|
HRESULT hr = AllocGetWindowText(GetDlgItem(hDlg, IDC_EDIT_URL), &pUrl->pszURL, &pUrl->cch);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
result = TRUE;
|
|
}
|
|
}
|
|
EndDialog(hDlg, result ? IDOK : IDABORT);
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hDlg, LOWORD(IDCANCEL));
|
|
break;
|
|
}
|
|
return (INT_PTR)FALSE;
|
|
}
|
|
return (INT_PTR)FALSE;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// ToggleMenuItemCheck
|
|
//
|
|
// Toggle a menu item's checked state.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void ToggleMenuItemCheck(UINT bMenuItemID, HMENU hmenu)
|
|
{
|
|
BOOL bChecked = IsMenuChecked(hmenu, bMenuItemID);
|
|
|
|
BYTE fNewState = MF_CHECKED;
|
|
|
|
if (bChecked & MF_CHECKED)
|
|
{
|
|
fNewState = MF_UNCHECKED;
|
|
}
|
|
CheckMenuItem(hmenu, (UINT) bMenuItemID,
|
|
MF_BYCOMMAND | fNewState);
|
|
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: AllocGetWindowText
|
|
// Description: Helper function to get text from a window.
|
|
//
|
|
// This function allocates a buffer and returns it in pszText. The caller must
|
|
// call CoTaskMemFree on the buffer.
|
|
//
|
|
// hwnd: Handle to the window
|
|
// pszText: Receives a pointer to the string.
|
|
// pcchLen: Receives the length of the string, in characters, not including
|
|
// the terminating NULL character. This parameter can be NULL.
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT AllocGetWindowText(HWND hwnd, WCHAR **pszText, DWORD *pcchLen)
|
|
{
|
|
if (pszText == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
*pszText = NULL;
|
|
|
|
int cch = GetWindowTextLength(hwnd);
|
|
if (cch < 0)
|
|
{
|
|
return E_UNEXPECTED; // This function should not return a negative value.
|
|
}
|
|
|
|
WCHAR *szTmp = (WCHAR*)CoTaskMemAlloc(sizeof(WCHAR) * (cch + 1)); // Include room for terminating NULL character
|
|
|
|
if (!szTmp)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (cch == 0)
|
|
{
|
|
szTmp[0] = L'\0'; // No text.
|
|
}
|
|
else
|
|
{
|
|
int res = GetWindowText(hwnd, szTmp, (cch + 1)); // Size includes NULL character
|
|
|
|
// GetWindowText returns 0 if (a) there is no text or (b) it failed.
|
|
// We checked for (a) already, so 0 means failure here.
|
|
|
|
if (res == 0)
|
|
{
|
|
CoTaskMemFree(szTmp);
|
|
return __HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
|
|
// If we got here, szTmp is valid, so return it to the caller.
|
|
*pszText = szTmp;
|
|
if (pcchLen)
|
|
{
|
|
*pcchLen = static_cast<DWORD>(cch); // Return the length NOT including the '\0'
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
void EnableDialogControl(HWND hDlg, int nIDDlgItem, BOOL bEnable)
|
|
{
|
|
HWND hwnd = GetDlgItem(hDlg, nIDDlgItem);
|
|
|
|
if (!bEnable && hwnd == GetFocus())
|
|
{
|
|
// If we're being disabled and this control has focus,
|
|
// set the focus to the next control.
|
|
|
|
::SendMessage(GetParent(hwnd), WM_NEXTDLGCTL, 0, FALSE);
|
|
}
|
|
|
|
EnableWindow(hwnd, bEnable);
|
|
}
|
|
|
|
|
|
BOOL StatusBar_SetText(HWND hwnd, int iPart, const WCHAR* pszText)
|
|
{
|
|
return (BOOL)SendMessage(hwnd, SB_SETTEXT, (WPARAM)iPart, (LPARAM)pszText);
|
|
}
|