DirectShow
8.0 이전 버전에서 다이렉트쇼를 배포하려면 별도의 다이렉트X 미디어 배포 버전을 설치
WAVE 오디오 재생
오디오
비디오
기본적으로 그래프에디터에서 동작하는 모든 멀티미디어는 다시 파일로 저장할 수 있다.
//#define _WIN32_WINNT 0x400
#include <dshow.h>
#pragma comment(lib, "strmiids")
#pragma comment(lib, "quartz")
필터 그래프 매니저
필터 그래프 컴포넌트
비디오 캡처 필터(Video Capture Sources 범주)
소스 필터
윈도우 미디어 소스 필터
오디오 CD 소스(WMP CD) 필터(DirectShow Filters 범주) // push 모드
DVD 네비게이터 필터
변환 필터(?)
미디 파서 필터
MPEG-1 파서(MPEG-I Stream Splitter) 필터
MPEG-2 파서(MPEG-2 Stream Splitter) 필터
AVI 파서(AVI Splitter) 필터
MP3 디코더(MPEG Layer-3 Decoder)
MPEG-1 오디오 디코더 필터
색상 공간 변환기(Color Space Converter) 필터
AVI 디코더(AVI Decompressor) 필터(DirectShow Filters 범주)
MPEG-1 비디오 디코더 필터(비디오 CD)
MPEG-4 디코더(MPEG-4 Decoder DMO) 필터
MP3 ACM(MPEG Layer-3)(Audio Compressors 범주)
MPEG 비디오 인코더
MPEG 오디오 인코더
오디오 장치 필터
오버레이 믹서 필터(DirectShow Filters 범주)
VMR(Video Mixing Renderer) 필터
AVI Mux 필터(DirectShow Filters 범주)
MPEG 먹스 필터
WAV 생성 필터
MP3 생성 필터
Null Renderer(DirectShow Filters 범주)
자동 연결하기
[Options | AutoArrange filters]
연결 시 중간에 필터가 자동 추가
안정성
반드시 필터 그래프 매니저에 인터페이스 요청
애플리케이션 개발자가 힘들게 필터를 검색하지 않고 필터 그래프 매니저에만 인터페이스 요청
.AddSourceFilter() // 소스 필터 자동 추가
.Render() // 핀 렌더링(?)
.Connect() // 자동 연결하기
.Pause()
.GetState()
.StopWhenReady()
.CheckCapabilities()
.GetPositions()
.SetPositions()
.GetDuration()
.SetRate()
DWORD dwCap = 0;
HRESULT hr = pSeek->GetCapabilities(&dwCap);
if (AM_SEEKING_CanSeekAbsolute & dwCap)
{
// Graph can seek to absolute positions.
}
/*
// Determine if the source is seekable.
BOOL bCanSeek = FALSE;
DWORD caps = AM_SEEKING_CanSeekAbsolute | AM_SEEKING_CanGetDuration;
bCanSeek = (S_OK == pSeek->CheckCapabilities(&caps));
if (bCanSeek)
{
// Enable the trackbar.
EnableWindow(hScroll, TRUE);
// Find the file duration.
pSeek->GetDuration(&g_rtTotalTime);
}
*/
#define ONE_SECOND 10000000
REFERENCE_TIME rtNow = 2 * ONE_SECOND,
rtStop = 5 * ONE_SECOND;
hr = pSeek->SetPositions(
&rtNow, AM_SEEKING_AbsolutePositioning,
&rtStop, AM_SEEKING_AbsolutePositioning
);
/*
REFERENCE_TIME rtNow = 10 * ONE_SECOND;
hr = pSeek->SetPositions(
&rtNow, AM_SEEKING_RelativePositioning,
NULL, AM_SEEKING_NoPositioning
);
*/
.put_WindowStyle()
.SetWindowPosition()
.get_Visible()
.put_Visible()
.put_FullScreenMode()
.get_MessageDrain()
.put_MessageDrain()
.SetWindowForeground()
.put_Rate()
#define WM_GRAPHNOTIFY WM_APP + 1
interface IGraphBuilder;
//
// define CMPlayer1App class
//
class CMPlayer1App
{
friend int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
// Construction
public:
CMPlayer1App() ;
virtual ~CMPlayer1App() ;
// Attributes
public:
void SetAppValues(HINSTANCE hInst, PTSTR szAppName, int iAppTitleResId) ;
public:
inline const TCHAR * GetAppName(void) const { return m_szAppName ; } ;
// Operations
public:
bool InitInstance(int nCmdShow) ;
void OnDraw(HDC hDC) ;
public:
// File Menu
HRESULT OnFileOpen() ;
HRESULT OnFileClose() ;
// Control Menu
HRESULT OnPlayClip() ;
HRESULT OnPauseClip() ;
HRESULT OnStopClip() ;
HRESULT OnRewindClip() ;
HRESULT OnFastForwardClip() ;
HRESULT OnPreviousClip() ;
HRESULT OnNextClip() ;
// Volume/Balance Menu
HRESULT OnVolume(bool bUp);
HRESULT OnBalance(bool bRight);
// Procedures
public:
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) ;
private:
LRESULT MenuProc(HWND hWnd, WPARAM wParam, LPARAM lParam) ;
LRESULT KeyProc(WPARAM wParam, LPARAM lParam) ;
LRESULT ToolTipProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) ;
// WM_SIZE
HRESULT OnSize() ;
// Video Window related
HRESULT ToggleFullScreen() ;
HRESULT StartFullScreen() ;
HRESULT StopFullScreen() ;
// Filter Graph Events
HRESULT HandleGraphEvent(LPARAM lParam) ;
private:
TCHAR m_szAppName[50] ; // internal name of the app
TCHAR m_szAppTitle[50] ; // title bar text
HINSTANCE m_hInstance ;
HWND m_hWnd ;
HWND m_hwndToolBar ;
private:
TCHAR m_szFileName[MAX_PATH] ;
#ifdef REGISTER_FILTERGRAPH
DWORD m_dwGraphRegister ;
#endif
// Video window related
bool m_bFullScreenOn ;
RECT m_rectOrgVideo ; // used to store the original video rectangle during fullscreen playback
long m_lOrgStyle ; // original video window style bits (before fullscreen)
long m_lOrgStyleEx ; // original video window extended style bits (.....)
// Volume/Balance related
float m_fVolumePos; // Volume Position
float m_fBalancePos; // Balance Position
private:
// DirectShow interfaces
IGraphBuilder * m_pGB ;
} ;
#include "MPlayer1App.h"
#include "resource.h"
#include <commctrl.h>
#pragma comment(lib, TEXT("comctl32.lib"))
#include <commdlg.h>
#include <math.h>
//------------------------------------------------------------------------------
// Type Definitions
//------------------------------------------------------------------------------
typedef UINT (CALLBACK* PFNDLL_STES)(UINT);
// File filter for OpenFile dialog
#define FILE_FILTER_TEXT \
TEXT("Video Files (AVI,ASF,MPEG,Indeo,QuickTime)\0*.avi;*.asf;*.wmv;*.mpg;*.mpe*;*.mp1*;*.mp2*;*.mpv*;*.ivf;*.qt\0")\
TEXT("Audio files (WAV,WMA,MPEG,MIDI,AIFF,AU,SND)\0*.wav;*.wma;*.mp3;*.mpa;*.mid*;*.rmi;*.aif*;*.au;*.snd\0")\
TEXT("All Files (*.*)\0*.*;\0\0")
// Begin default media search at root directory
#define DEFAULT_MEDIA_PATH TEXT("\\\0")
//------------------------------------------------------------------------------
// Name: CMPlayer1App::CMPlayer1App()
// Desc: This is the constructor for CMPlayer1App. It sets default values and disables
// power saving.
//------------------------------------------------------------------------------
CMPlayer1App::CMPlayer1App() : m_hInstance(NULL),
m_hWnd(NULL),
m_hwndToolBar(NULL),
#ifdef REGISTER_FILTERGRAH
m_dwGraphRegister(0),
#endif
m_bFullScreenOn(false), m_lOrgStyle(0), m_lOrgStyleEx(0),
m_fVolumePos(0.5), m_fBalancePos(0),
m_pGB(NULL)
{
ZeroMemory(m_szAppTitle, sizeof(m_szAppTitle)) ;
ZeroMemory(m_szAppName, sizeof(m_szAppName)) ;
ZeroMemory(m_szFileName, sizeof(m_szFileName)) ;
ZeroMemory(&m_rectOrgVideo, sizeof(m_rectOrgVideo)) ;
// Initialize COM
if (FAILED(CoInitialize(NULL)))
{
TRACE(TEXT("CoInitialize Failed!\r\n")) ;
exit(1) ;
}
// notify the power manager that the display is in use so it won't go to sleep
// SetThreadExecutionState() is a Win98/Win2k API.
// The following code lets it fail gracefully on Win95
PFNDLL_STES pfn ;
pfn = (PFNDLL_STES) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
"SetThreadExecutionState") ;
if (pfn)
{
pfn(ES_CONTINUOUS | ES_DISPLAY_REQUIRED) ;
}
}
//------------------------------------------------------------------------------
// Name: CMPlayer1App::~CMPlayer1App()
// Desc: This is the destructor for CMPlayer1App.
//------------------------------------------------------------------------------
CMPlayer1App::~CMPlayer1App()
{
if (m_pGB != NULL)
{
OnFileClose() ;
}
// Finished with COM
CoUninitialize() ;
// notify the power manager that the display is no longer in use and it can go to sleep
// SetThreadExecutionState() is a Win98/Win2k API.
// The following code lets it fail gracefully on Win95
PFNDLL_STES pfn;
pfn = (PFNDLL_STES) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
"SetThreadExecutionState") ;
if (pfn)
{
pfn(ES_CONTINUOUS) ;
}
}
//------------------------------------------------------------------------------
// Name: CMPlayer1App::SetAppValues()
// Desc: This method sets the basic application values like szAppName and hInstance
//------------------------------------------------------------------------------
void CMPlayer1App::SetAppValues(HINSTANCE hInst, PTSTR szAppName, int iAppTitleResId)
{
// The Windows stuff
m_hInstance = hInst ;
lstrcpy(m_szAppName, szAppName) ;
LoadString(m_hInstance, IDS_APP_TITLE, m_szAppTitle, 100) ;
}
//------------------------------------------------------------------------------
// Name: CMPlayer1App::InitInstance()
// Desc: This method registers and creates our window and toolbar.
//------------------------------------------------------------------------------
bool CMPlayer1App::InitInstance(int nCmdShow)
{
// Win32 will always set hPrevInstance to NULL, So lets check
// things a little closer. This is because we only want a single
// version of this MPlayer1App to run at a time
m_hWnd = FindWindow (m_szAppName, m_szAppTitle) ;
if (m_hWnd)
{
// We found another instance of ourself. Lets use that one:
if (IsIconic(m_hWnd))
{
ShowWindow(m_hWnd, SW_RESTORE) ;
}
SetForegroundWindow(m_hWnd) ;
// If this MPlayer1App actually had any methodality, we would
// also want to communicate any action that our 'twin'
// should now perform based on how the user tried to
// execute us.
return false;
}
// Register the MPlayer1App main window class
WNDCLASSEX wc ;
wc.cbSize = sizeof(wc) ;
wc.style = CS_HREDRAW | CS_VREDRAW ;
wc.lpfnWndProc = (WNDPROC) WndProc ;
wc.cbClsExtra = 0 ;
wc.cbWndExtra = 0 ;
wc.hInstance = m_hInstance ;
wc.hIcon = LoadIcon(m_hInstance, MAKEINTRESOURCE(IDI_APPICON)) ;
wc.hCursor = LoadCursor(NULL, IDC_ARROW) ;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1) ;
wc.lpszMenuName = (LPCSTR)IDR_APPMENU ;
wc.lpszClassName = m_szAppName ;
wc.hIconSm = LoadIcon(m_hInstance, MAKEINTRESOURCE(IDI_SMALL)) ;
if (0 == RegisterClassEx(&wc))
{
TRACE("ERROR: RegisterClassEx() for MPlayer1App class failed (Error %ld)\n", GetLastError()) ;
return false ;
}
// Determine where to put the Application Window
RECT rDesktop ;
SystemParametersInfo(SPI_GETWORKAREA, NULL, &rDesktop, NULL) ;
// Create an instance of the window we just registered
// locate it at the center of the screen
m_hWnd = CreateWindowEx(0, m_szAppName, m_szAppTitle, WS_OVERLAPPEDWINDOW,
(rDesktop.right - rDesktop.left) / 2 - 150,
(rDesktop.bottom - rDesktop.top) / 2 - 80,
300, 160, NULL, NULL, m_hInstance, NULL) ;
if (! m_hWnd)
{
TRACE("ERROR: CreateWindowEx() failed (Error %ld)\n", GetLastError()) ;
return false ;
}
// We now create the toolbar
INITCOMMONCONTROLSEX cc ;
cc.dwSize = sizeof(INITCOMMONCONTROLSEX) ;
cc.dwICC = ICC_BAR_CLASSES ; // register only the toolbar control
InitCommonControlsEx(&cc) ;
TBBUTTON tbb[] =
{
0, IDM_PLAYBACK_PREVIOUS, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0, 0,
1, IDM_PLAYBACK_REWIND, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0, 0,
2, IDM_PLAYBACK_PLAY, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0, 0,
3, IDM_PLAYBACK_PAUSE, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0, 0,
4, IDM_PLAYBACK_STOP, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0, 0,
5, IDM_PLAYBACK_FASTFORWARD, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0, 0,
6, IDM_PLAYBACK_NEXT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0, 0,
// TODO: Place code here.
} ;
m_hwndToolBar = CreateToolbarEx(m_hWnd, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | CCS_TOP
| TBSTYLE_TOOLTIPS, 1, 10, m_hInstance, IDR_APPTOOLBAR, tbb, sizeof(tbb)/sizeof(tbb[0]), 0, 0, 0, 0,
sizeof(TBBUTTON)) ;
if (! m_hwndToolBar)
{
TRACE("ERROR: CreateToolbarEx() failed (Error %ld)\n", GetLastError()) ;
return false ;
}
// and finally, we make the window visible
ShowWindow(m_hWnd, nCmdShow) ;
UpdateWindow(m_hWnd) ;
return true ;
}
//------------------------------------------------------------------------------
// Name: CMPlayer1App::OnDraw()
// Desc: This method draws something on the screen
//------------------------------------------------------------------------------
void CMPlayer1App::OnDraw(HDC hDC)
{
// TODO: Place code here.
}
// render filter (downstream)
HRESULT RenderFilter(IGraphBuilder* pGB, IBaseFilter* pFilter)
{
if (pGB == NULL || pFilter == NULL)
return E_ABORT;
HRESULT hr;
// Get the first output pin of the new source filter. Audio sources
// typically have only one output pin, so for most audio cases finding
// any output pin is sufficient.
IEnumPins *pEnumPins = NULL;
IPin *pPin = NULL, *pDownstreamPin;
PIN_DIRECTION direction;
bool bIsAnySuccess = false;
JIF(pFilter->EnumPins(&pEnumPins));
while (pEnumPins->Next(1, &pPin, 0) == S_OK)
{
JIF(pPin->QueryDirection(&direction));
if (direction == PINDIR_OUTPUT)
{
pPin->ConnectedTo(&pDownstreamPin);
if (pDownstreamPin == NULL)
{
// We have the new ouput pin. Render it
hr = pGB->Render(pPin);
if (SUCCEEDED(hr))
bIsAnySuccess = true;
}
SAFE_RELEASE(pDownstreamPin);
}
SAFE_RELEASE(pPin);
}
SAFE_RELEASE(pEnumPins);
return (bIsAnySuccess == false ? E_FAIL : S_OK);
}
HRESULT ConnectFilters(IGraphBuilder* pGB, IBaseFilter* pUpFilter, IBaseFilter* pDownFilter, int nMAX)
{
HRESULT hr;
// Get the first output pin of the new source filter. Audio sources
// typically have only one output pin, so for most audio cases finding
// any output pin is sufficient.
IEnumPins *pUpEnumPins = NULL, *pDownEnumPins = NULL;
IPin *ppinOut = NULL, *pDownstreamPin;
IPin *ppinIn = NULL, *pUpstreamPin;
PIN_DIRECTION direction;
bool bIsAnySuccess = false;
JIF(pUpFilter->EnumPins(&pUpEnumPins));
while (pUpEnumPins->Next(1, &ppinOut, 0) == S_OK)
{
JIF(ppinOut->QueryDirection(&direction));
if (direction == PINDIR_OUTPUT)
{
ppinOut->ConnectedTo(&pDownstreamPin);
if (pDownstreamPin == NULL)
{
// Downstream filter: pin enumeration
JIF(pDownFilter->EnumPins(&pDownEnumPins));
while (pDownEnumPins->Next(1, &ppinIn, 0) == S_OK)
{
JIF(ppinIn->QueryDirection(&direction));
if (direction == PINDIR_INPUT)
{
ppinIn->ConnectedTo(&pUpstreamPin);
if (pUpstreamPin == NULL)
{
//
// We have two pins. Connect it
//
// 동적 생성 필터 둘이 결합할 때
// 발생하는 무한 루프를 막는다!
if (nMAX-- > 0)
{
hr = pGB->Connect(ppinOut, ppinIn);
}
if (hr == S_OK)
bIsAnySuccess |= true;
else if (hr == VFW_S_PARTIAL_RENDER)
bIsAnySuccess |= false;
else if (hr == E_ABORT)
bIsAnySuccess |= false;
else if (hr == E_POINTER)
bIsAnySuccess |= false;
else if (hr == VFW_E_CANNOT_CONNECT)
bIsAnySuccess |= false;
else if (hr == VFW_E_NOT_IN_GRAPH)
bIsAnySuccess |= false;
}
SAFE_RELEASE(pUpstreamPin);
}
SAFE_RELEASE(ppinIn);
}
SAFE_RELEASE(pDownEnumPins);
}
// end of downstream
SAFE_RELEASE(pDownstreamPin);
}
SAFE_RELEASE(ppinOut);
}
SAFE_RELEASE(pUpEnumPins);
// end of upstream
return (bIsAnySuccess == false ? E_FAIL : S_OK);
}
#define CONNECTPINS(x, y, z) \
{ \
if ((y != NULL) && (z != NULL)) \
{ \
IPin *pPin; \
z->ConnectedTo(&pPin); \
\
if (pPin == NULL) \
x->Connect(y, z); \
else \
pPin->Release(); \
} \
}
//------------------------------------------------------------------------------
// Name: CMPlayer1App::OnFileOpen()
// Desc: This method open a file dialog and render the selected file.
//------------------------------------------------------------------------------
#include "myuuids.h"
HRESULT CMPlayer1App::OnFileOpen()
{
HRESULT hr ;
if (m_pGB != NULL)
{
JIF(OnFileClose()) ;
}
// 미디어 파일 불러오기
// Open File Dialog ...
static OPENFILENAME ofn={0} ;
static BOOL bSetInitialDir = FALSE ;
ofn.lStructSize = sizeof(OPENFILENAME) ;
ofn.hwndOwner = m_hWnd ;
ofn.lpstrFilter = FILE_FILTER_TEXT ;
ofn.lpstrCustomFilter = NULL ;
ofn.nFilterIndex = 1 ;
ofn.lpstrFile = m_szFileName ;
ofn.nMaxFile = MAX_PATH ;
ofn.lpstrTitle = TEXT("Open Media File...\0") ;
ofn.lpstrFileTitle = NULL ;
ofn.lpstrDefExt = TEXT("*\0") ;
ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST ;
// Remember the path of the first selected file
if (bSetInitialDir == FALSE)
{
ofn.lpstrInitialDir = DEFAULT_MEDIA_PATH ;
bSetInitialDir = TRUE ;
}
else
ofn.lpstrInitialDir = NULL ;
// Retrieve the name of the selected file
WCHAR wFileName[MAX_PATH] ;
if (GetOpenFileName((LPOPENFILENAME)&ofn))
{
#ifndef UNICODE
MultiByteToWideChar(CP_ACP, 0, m_szFileName, -1, wFileName, MAX_PATH) ;
#else
lstrcpy(wFileName, m_szFileName);
#endif
}
else
return E_ABORT ;
// Clear open dialog remnants before calling RenderFile()
SetWindowText(m_hWnd, m_szFileName) ;
UpdateWindow(m_hWnd) ;
// Get the interface for DirectShow's GraphBuilder
JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&m_pGB)) ;
// AddSourceFilter and Render Pin...
CComPtr<IBaseFilter> pFilterFileSourceAsync;
LIF(CoCreateInstance(CLSID_L544_FileSourceAsync, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&pFilterFileSourceAsync)) ;
LIF(m_pGB->AddFilter(pFilterFileSourceAsync, L"File Source (Async.)"));
// Setting File Source
{
CComQIPtr<IFileSourceFilter> pFSF(pFilterFileSourceAsync);
if (pFSF != NULL)
{
LIF(pFSF->Load(wFileName, NULL));
// LIF(pFSF->Load(L"I:\\work\\Data\\movie.avi", NULL));
}
}
// Render Pin ...
// Have the graph construct its the appropriate graph automatically
LIF(RenderFilter(m_pGB, pFilterFileSourceAsync));
/*
IBaseFilter *pSourceFilter ;
JIF(m_pGB->AddSourceFilter(wFileName, wFileName, &pSourceFilter)) ;
if (pSourceFilter != NULL)
{
LIF(RenderFilter(m_pGB, pSourceFilter)) ;
}
// Release a interface
SAFE_RELEASE(pSourceFilter) ;*/
/*
// Render File ...
// Have the graph construct its the appropriate graph automatically
JIF(m_pGB->RenderFile(wFileName, NULL)) ;
*/
// {
// Query for video interfaces
IVideoWindow * pVW ;
JIF(m_pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW)) ;
if (pVW != NULL)
{
LIF(pVW->put_Owner((OAHWND)m_hWnd)) ;
LIF(pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN)) ;
// resizing
LIF(OnSize());
}
// Release a interface
SAFE_RELEASE(pVW) ;
// }
// {
// Get the video size
IBasicVideo * pBV ;
JIF(m_pGB->QueryInterface(IID_IBasicVideo, (void **)&pBV)) ;
if (pBV != NULL)
{
long lWidth, lHeight ;
hr = pBV->GetVideoSize(&lWidth, &lHeight) ;
if (SUCCEEDED(hr))
{
// Track the movement of the container window and resize as needed
RECT rcToolbar ;
GetWindowRect(m_hwndToolBar, &rcToolbar) ;
int nToolbarHeight = rcToolbar.bottom - rcToolbar.top ;
SetWindowPos(m_hWnd, HWND_NOTOPMOST, 0, 0,
lWidth, lHeight + nToolbarHeight,
SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE) ;
}
}
// Release a interface
SAFE_RELEASE(pBV) ;
// }
// {
// Set the owner window to receive event notices
IMediaEventEx * pME ;
JIF(m_pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME)) ;
if (pME != NULL)
{
LIF(pME->SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY, 0)) ;
}
// Release a interface
SAFE_RELEASE(pME) ;
// }
#ifdef REGISTER_FILTERGRAPH
hr = AddGraphToRot(m_pGB, &m_dwGraphRegister) ;
if (FAILED(hr))
{
TRACE(TEXT("Failed to register filter graph with ROT! hr=0x%x"), hr) ;
m_dwGraphRegister = 0 ;
}
#endif
return S_OK ;
}
//------------------------------------------------------------------------------
// Name: CMPlayer1App::OnFileClose()
// Desc: This method release the filter graph manager.
//------------------------------------------------------------------------------
HRESULT CMPlayer1App::OnFileClose()
{
if (m_pGB == NULL)
return E_ABORT ;
HRESULT hr ;
// Query for media control interfaces
IMediaControl * pMC ;
JIF(m_pGB->QueryInterface(IID_IMediaControl, (void **)&pMC)) ;
if (pMC != NULL)
{
// Stop the graph to stop the media file
LIF(pMC->Stop()) ;
}
// Release a interface
SAFE_RELEASE(pMC) ;
// {
// Query for media event interfaces
IMediaEventEx * pME;
JIF(m_pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME));
if (pME != NULL)
{
// Relinquish ownership (IMPORTANT!)
LIF(pME->SetNotifyWindow(NULL, WM_GRAPHNOTIFY, 0));
}
// Release a interface
SAFE_RELEASE(pME);
// }
// {
// Query for video interfaces
IVideoWindow * pVW;
JIF(m_pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW));
if (pVW != NULL)
{
// Relinquish ownership (IMPORTANT!) after hiding video window
LIF(pVW->put_Visible(OAFALSE));
LIF(pVW->put_Owner(NULL));
}
// Release a interface
SAFE_RELEASE(pVW);
// }
#ifdef REGISTER_FILTERGRAPH
if (m_dwGraphRegister)
{
RemoveGraphFromRot(m_dwGraphRegister) ;
m_dwGraphRegister = 0 ;
}
#endif
// Release Filter Graph Manager
SAFE_RELEASE(m_pGB) ;
// 기본 윈도우 타이틀로 변경
SetWindowText(m_hWnd, m_szAppTitle) ;
return S_OK ;
}
//------------------------------------------------------------------------------
// Name: CMPlayer1App::OnXXXClip()
// Desc: These methods manipulate playback features.
//------------------------------------------------------------------------------
HRESULT CMPlayer1App::OnPlayClip()
{
HRESULT hr ;
if (m_pGB == NULL)
JIF(OnFileOpen()) ;
// Let's get ready to rumble!
ShowWindow(m_hWnd, SW_SHOW) ;
UpdateWindow(m_hWnd) ;
SetForegroundWindow(m_hWnd) ;
SetFocus(m_hWnd) ;
// Query for media control interfaces
IMediaControl * pMC ;
JIF(m_pGB->QueryInterface(IID_IMediaControl, (void **)&pMC)) ;
if (pMC != NULL)
{
// Run the graph to play the media file
LIF(pMC->Run()) ;
}
// Release a interface
SAFE_RELEASE(pMC) ;
// give a focus to the Video window, again!
SetFocus(m_hWnd) ;
return hr ;
}
HRESULT CMPlayer1App::OnPauseClip()
{
HRESULT hr ;
if (m_pGB == NULL)
JIF(OnFileOpen()) ;
// Query for media control interfaces
IMediaControl * pMC ;
JIF(m_pGB->QueryInterface(IID_IMediaControl, (void **)&pMC)) ;
if (pMC != NULL)
{
// Pause the graph to pause the media file
LIF(pMC->Pause()) ;
}
// Release a interface
SAFE_RELEASE(pMC) ;
return hr ;
}
HRESULT CMPlayer1App::OnStopClip()
{
HRESULT hr ;
if (m_pGB == NULL)
JIF(OnFileOpen()) ;
// Query for media control interfaces
IMediaControl * pMC ;
JIF(m_pGB->QueryInterface(IID_IMediaControl, (void **)&pMC)) ;
if (pMC != NULL)
{
// Stop the graph to stop the media file
LIF(pMC->Stop()) ;
}
// Query for media seeking interfaces
IMediaSeeking * pMS ;
JIF(m_pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS)) ;
if (pMS != NULL)
{
// Go to the previous of the graph, usually the very first.
LONGLONG pos = 0 ;
LIF(pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning)) ;
// Display the first frame to indicate the reset condition
LIF(pMC->Pause()) ;
}
// Release a interface
SAFE_RELEASE(pMS) ;
// Release a interface
SAFE_RELEASE(pMC) ;
return hr ;
}
HRESULT CMPlayer1App::OnRewindClip()
{
HRESULT hr ;
if (m_pGB == NULL)
JIF(OnFileOpen()) ;
// Query for media seeking interfaces
IMediaSeeking * pMS ;
JIF(m_pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS)) ;
if (pMS != NULL)
{
// Rewind the graph to rewind the media file
LONGLONG pos;
LIF(pMS->GetPositions(&pos, NULL)) ;
pos -= ONE_SECOND;
LIF(pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning)) ;
}
// Release a interface
SAFE_RELEASE(pMS) ;
return hr ;
}
HRESULT CMPlayer1App::OnFastForwardClip()
{
HRESULT hr ;
if (m_pGB == NULL)
JIF(OnFileOpen()) ;
// Query for media seeking interfaces
IMediaSeeking * pMS ;
JIF(m_pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS)) ;
if (pMS != NULL)
{
// Fastforward the graph to fastforward the media file
LONGLONG pos;
LIF(pMS->GetPositions(&pos, NULL)) ;
pos += ONE_SECOND;
LIF(pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning)) ;
}
// Release a interface
SAFE_RELEASE(pMS) ;
return hr ;
}
HRESULT CMPlayer1App::OnPreviousClip()
{
HRESULT hr ;
if (m_pGB == NULL)
JIF(OnFileOpen()) ;
// Query for media seeking interfaces
IMediaSeeking * pMS ;
JIF(m_pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS)) ;
if (pMS != NULL)
{
// Go to the previous of the graph, usually the very first.
LONGLONG pos = 0;
LIF(pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning)) ;
}
// Release a interface
SAFE_RELEASE(pMS) ;
return hr ;
}
HRESULT CMPlayer1App::OnNextClip()
{
HRESULT hr ;
if (m_pGB == NULL)
JIF(OnFileOpen()) ;
// Query for media seeking interfaces
IMediaSeeking * pMS ;
JIF(m_pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS)) ;
if (pMS != NULL)
{
// Go to the next of the graph, usually the very end.
LONGLONG pos;
LIF(pMS->GetPositions(NULL, &pos)) ;
LIF(pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning)) ;
}
// Release a interface
SAFE_RELEASE(pMS) ;
return hr ;
}
//------------------------------------------------------------------------------
// Name: CSampleApp::OnVolume/Balance()
// Desc: This method manipulate Volume/Balance
//------------------------------------------------------------------------------
HRESULT CMPlayer1App::OnVolume(bool bUp)
{
if (m_pGB == NULL)
return E_ABORT;
HRESULT hr ;
// Query audio interface
IBasicAudio * pBA;
JIF(m_pGB->QueryInterface(IID_IBasicAudio, (void **)&pBA)) ;
if (!pBA)
return E_FAIL ;
//----------------------------------
// fPos Range: (0 ~ 1)
// lfVolume Range: (-10,000 ~ 0)
//----------------------------------
float& fPos = m_fVolumePos ;
double lfVolume ;
fPos += (bUp ? 0.1f : -0.1f) ;
// Saturation
if (fPos < 0)
fPos = 0 ;
else if (fPos > 1)
fPos = 1 ;
// 단순 처리의 경우
// lfVolume = (fPos - 1) * 10000 ;
// Scaling (fPos => lfVolume)
double lfPower10 = pow(10, -10) ; // 10 ^ (-10)
lfVolume = (1 - lfPower10) * fPos + lfPower10 ;
lfVolume = 10 * log10(lfVolume) ; // decibel (-100dB ~ 0dB)
lfVolume *= 100 ; // DirectShow resolution (-10,000 ~ 0)
// Set volume
JIF(pBA->put_Volume((long)lfVolume)) ;
// Release a interface
SAFE_RELEASE(pBA);
return hr ;
}
HRESULT CMPlayer1App::OnBalance(bool bRight)
{
if (m_pGB == NULL)
return E_ABORT ;
HRESULT hr ;
// Query audio interface
IBasicAudio * pBA;
JIF(m_pGB->QueryInterface(IID_IBasicAudio, (void **)&pBA)) ;
if (!pBA)
return E_FAIL ;
//----------------------------------
// fPos Range: (0 ~ 1)
// lfBalance Range: (-10,000 ~ 0)
//----------------------------------
float& fPos = m_fVolumePos ;
double lfBalance ;
fPos += (bRight ? 0.1f : -0.1f) ;
// Saturation
if (fPos < 0)
fPos = 0 ;
else if (fPos > 1)
fPos = 1 ;
// Position
float fRightPos = fPos;
float fLeftPos = 1 - fPos;
// decibel values
double lfRightdB, lfLeftdB;
double lfPower10 = pow(10, -10) ; // 10 ^ (-10)
// Right dB
lfRightdB = (1 - lfPower10) * fRightPos + lfPower10 ; // Scaling
lfRightdB = 10 * log10(lfRightdB) ; // decibel (-100dB ~ 0dB)
// Left dB
lfLeftdB = (1 - lfPower10) * fLeftPos + lfPower10 ; // Scaling
lfLeftdB = 10 * log10(lfLeftdB) ; // decibel (-100dB ~ 0dB)
// Total value
lfBalance = lfRightdB - lfLeftdB; // decibel (-100dB ~ 100dB)
lfBalance *= 100 ; // DirectShow resolution (-10,000 ~ 10,000)
// Set balance
JIF(pBA->put_Balance((long)lfBalance)) ;
// Release a interface
SAFE_RELEASE(pBA);
return hr;
}
//------------------------------------------------------------------------------
// Name: CMPlayer1App::OnSize()
// Desc: This method manipulate WM_SIZE events
//------------------------------------------------------------------------------
HRESULT CMPlayer1App::OnSize()
{
if (m_pGB == NULL)
return E_ABORT ;
HRESULT hr ;
IVideoWindow * pVW;
JIF(m_pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW));
if (pVW != NULL)
{
// Track the movement of the container window and resize as needed
RECT rcToolbar;
GetWindowRect(m_hwndToolBar, &rcToolbar);
int nToolbarHeight = rcToolbar.bottom - rcToolbar.top;
RECT rect;
GetClientRect(m_hWnd, &rect);
LIF(pVW->SetWindowPosition(rect.left, rect.top + nToolbarHeight,
rect.right - rect.left, rect.bottom -rect.top - nToolbarHeight));
}
// Release a interface
SAFE_RELEASE(pVW);
return hr;
}
//------------------------------------------------------------------------------
// Name: CMPlayer1App::XXXFullScreen()
// Desc: This method will enter and exit fullscreen mode depending on the bool
// value. We don't use IVideoWindow->putFullScreenMode because
// it won't handle the mouse correctly.
//------------------------------------------------------------------------------
HRESULT CMPlayer1App::ToggleFullScreen()
{
return false == m_bFullScreenOn ? StartFullScreen() : StopFullScreen() ;
}
HRESULT CMPlayer1App::StartFullScreen()
{
if (m_pGB == NULL)
return E_ABORT ;
HRESULT hr ;
// Query for video interfaces
IVideoWindow * pVW ;
JIF(m_pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW)) ;
if (!pVW)
return E_FAIL ;
// store the original window position
long lLeft, lTop, lWidth, lHeight ;
JIF(pVW->GetWindowPosition(&lLeft, &lTop, &lWidth, &lHeight)) ;
SetRect(&m_rectOrgVideo, lLeft, lTop, lLeft + lWidth, lTop + lHeight) ;
// save the original window style
JIF(pVW->get_WindowStyle(&m_lOrgStyle)) ;
JIF(pVW->get_WindowStyleEx(&m_lOrgStyleEx)) ;
// the window can't be a child while it is full screen
// or it can't be stretched beyond the parent window's borders
JIF(pVW->put_Owner(NULL)) ;
JIF(pVW->put_MessageDrain((OAHWND)m_hWnd)) ;
// modify the window's style
// ... remove these styles
JIF(pVW->put_WindowStyle(m_lOrgStyle & ~(WS_BORDER|WS_CAPTION|WS_THICKFRAME))) ;
// ... remove these extended styles
JIF(pVW->put_WindowStyleEx(m_lOrgStyleEx \
& ~(WS_EX_CLIENTEDGE|WS_EX_STATICEDGE|WS_EX_WINDOWEDGE|WS_EX_DLGMODALFRAME) \
| WS_EX_TOPMOST)) ;
// stretch the window to the full size of the screen
long lScrnWidth = GetSystemMetrics(SM_CXSCREEN) ;
long lScrnHeight = GetSystemMetrics(SM_CYSCREEN) ;
JIF(pVW->SetWindowPosition(0, 0, lScrnWidth, lScrnHeight)) ;
::ShowCursor(FALSE) ; // make sure to show mouse now
m_bFullScreenOn = true;
// Release a interface
SAFE_RELEASE(pVW) ;
return true ;
}
HRESULT CMPlayer1App::StopFullScreen()
{
if (m_pGB == NULL)
return E_ABORT ;
HRESULT hr ;
// Query for video interfaces
IVideoWindow * pVW ;
JIF(m_pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW)) ;
if (!(pVW != NULL))
return E_FAIL ;
// Track the movement of the container window and resize as needed
RECT rcToolbar;
GetWindowRect(m_hwndToolBar, &rcToolbar);
int nToolbarHeight = rcToolbar.bottom - rcToolbar.top ;
// restore the window's position
RECT rect;
GetClientRect(m_hWnd, &rect);
JIF(pVW->SetWindowPosition(0, nToolbarHeight, rect.right, rect.bottom-nToolbarHeight)) ;
// make it a child window again
JIF(pVW->put_Owner(reinterpret_cast<OAHWND>(m_hWnd))) ;
// restore the window's styles
// ... restore the styles
JIF(pVW->put_WindowStyle(m_lOrgStyle)) ;
// ... restore the extended styles
JIF(pVW->put_WindowStyleEx(m_lOrgStyleEx)) ;
::ShowCursor(TRUE) ; // make sure to show mouse now
m_bFullScreenOn = false;
SetForegroundWindow(m_hWnd) ;
SetFocus(m_hWnd) ;
// Release a interface
SAFE_RELEASE(pVW) ;
return true ;
}
//------------------------------------------------------------------------------
// Name: CMPlayer1App::HandleGraphEvent()
// Desc: This method manipulate filter graph events.
//------------------------------------------------------------------------------
HRESULT CMPlayer1App::HandleGraphEvent(LPARAM lParam)
{
if (m_pGB == NULL)
return E_ABORT ;
HRESULT hr ;
long lEvent, lParam1, lParam2;
long lTimeOut = 0;
IMediaEventEx * pME;
JIF(m_pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME));
// 이 루프에서 그냥 리턴하면 Release()할 수 없으므로 조심한다!
while (SUCCEEDED(pME->GetEvent(&lEvent, &lParam1, &lParam2, lTimeOut)))
{
switch (lEvent)
{
case EC_USERABORT:
OnStopClip();
break;
case EC_COMPLETE:
break;
// TODO: Place code here.
}
hr = pME->FreeEventParams(lEvent, lParam1, lParam2);
}
// Release a interface
SAFE_RELEASE(pME);
return hr;
}
#include "MPlayer1App.h"
#include "resource.h"
#include <commctrl.h>
//------------------------------------------------------------------------------
// Global data
//------------------------------------------------------------------------------
const PTSTR APPNAME = TEXT("MPlayer1") ;
CMPlayer1App g_App ;
//------------------------------------------------------------------------------
// Name: WinMain()
// Desc: This method is the standard Windows program entry point.
//------------------------------------------------------------------------------
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
g_App.SetAppValues(hInstance, APPNAME, IDS_APP_TITLE) ;
// Perform application initialization:
if (! g_App.InitInstance(nCmdShow) )
{
return (FALSE) ;
}
HACCEL hAccelTable ;
hAccelTable = LoadAccelerators(hInstance, g_App.GetAppName()) ;
// Main message loop:
MSG msg ;
while (GetMessage(&msg, NULL, 0, 0))
{
if (! TranslateAccelerator(msg.hwnd, hAccelTable, &msg) )
{
TranslateMessage(&msg) ;
DispatchMessage(&msg) ;
}
}
return (msg.wParam) ;
}
//------------------------------------------------------------------------------
// Name: WndProc()
// Desc: This method is our main window procedure. It parses the messages
// and farms out the work to other procedures.
// Note: It's a static function. We should use g_App to access members.
//------------------------------------------------------------------------------
LRESULT CMPlayer1App::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HDC hDC ;
static PAINTSTRUCT ps ;
switch (message)
{
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps) ;
assert(hDC) ;
g_App.OnDraw(hDC) ;
EndPaint(hWnd, &ps) ;
break ;
case WM_KEYUP:
g_App.KeyProc(wParam, lParam) ;
break ;
case WM_COMMAND:
g_App.MenuProc(hWnd, wParam, lParam) ;
break ;
case WM_NOTIFY:
return g_App.ToolTipProc(hWnd, message, wParam, lParam) ;
break ;
case WM_GRAPHNOTIFY:
g_App.HandleGraphEvent(lParam) ;
break ;
case WM_DESTROY:
PostQuitMessage(0) ;
break;
case WM_SIZE:
// we do this to cause the toolbar to resize correctly
SendMessage(g_App.m_hwndToolBar, WM_SIZE, wParam, lParam) ;
g_App.OnSize() ;
return (DefWindowProc(hWnd, message, wParam, lParam)) ;
break ;
default:
return (DefWindowProc(hWnd, message, wParam, lParam)) ;
}
return (0) ; // let Windows know we handled the message
}
//------------------------------------------------------------------------------
// Name: ToolTipProc()
// Desc: This method is a MessageProc for our toolbar tooltips.
// This checks whether the WM_NOTIFY message is really a tooltip.
// If it is, we compare it to the buttons on the toolbar and send the right text back.
// Note that older code would use TTN_NEEDTEXT instead of TTN_GETDISPINFO.
//------------------------------------------------------------------------------
LRESULT CMPlayer1App::ToolTipProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
LPNMTTDISPINFO TText ;
TText = (LPNMTTDISPINFO) lParam ;
if (TTN_GETDISPINFO == TText->hdr.code)
{
switch (TText->hdr.idFrom)
{
case IDM_PLAYBACK_PLAY:
TText->lpszText = TEXT("Play") ;
break ;
case IDM_PLAYBACK_PAUSE:
TText->lpszText = TEXT("Pause") ;
break ;
case IDM_PLAYBACK_STOP:
TText->lpszText = TEXT("Stop") ;
break ;
case IDM_PLAYBACK_REWIND:
TText->lpszText = TEXT("Rewind") ;
break ;
case IDM_PLAYBACK_FASTFORWARD:
TText->lpszText = TEXT("FastForward") ;
break ;
case IDM_PLAYBACK_PREVIOUS:
TText->lpszText = TEXT("Previous") ;
break ;
case IDM_PLAYBACK_NEXT:
TText->lpszText = TEXT("Next") ;
break ;
// TODO: Place code here.
}
return (0) ;
}
else
return (DefWindowProc(hWnd, message, wParam, lParam)) ; // it wasn't a tooltip message
}
//------------------------------------------------------------------------------
// Name: CMPlayer1App::MenuProc()
// Desc: This method handles all of the menu messages for our application. It is
// passed these messages by the main message proc.
//------------------------------------------------------------------------------
LRESULT CMPlayer1App::MenuProc(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
HMENU hMenu = GetMenu(hWnd) ;
// Parse the menu selections:
switch (LOWORD(wParam))
{
case IDM_EXIT:
DestroyWindow(m_hWnd) ;
break ;
case IDM_FILE_OPEN:
OnFileOpen() ;
break ;
case IDM_FILE_CLOSE:
OnFileClose() ;
break ;
case IDM_PLAYBACK_PLAY:
OnPlayClip() ;
break ;
case IDM_PLAYBACK_PAUSE:
OnPauseClip() ;
break ;
case IDM_PLAYBACK_STOP:
OnStopClip() ;
break ;
case IDM_PLAYBACK_REWIND:
OnRewindClip() ;
break ;
case IDM_PLAYBACK_FASTFORWARD:
OnFastForwardClip() ;
break ;
case IDM_PLAYBACK_PREVIOUS:
OnPreviousClip() ;
break ;
case IDM_PLAYBACK_NEXT:
OnNextClip() ;
break ;
// TODO: Place code here.
default:
break ;
}
return (0);
}
//------------------------------------------------------------------------------
// Name: CMPlayer1App::KeyProc()
// Desc: This method will process all key presses sent to our application window.
// At present it passes all of the keys along to the DvdCore but this is where
// you would implement shortcut keys, etc.
//------------------------------------------------------------------------------
LRESULT CMPlayer1App::KeyProc(WPARAM wParam, LPARAM lParam)
{
switch (wParam)
{
case VK_ESCAPE: // exit full screen
DestroyWindow(m_hWnd);
break ;
case VK_RETURN: // activate the currently selected button
break ;
case VK_LEFT: // select the left button
OnBalance(false);
break ;
case VK_RIGHT: // select the right button
OnBalance(true);
break ;
case VK_DOWN: // select the lower button
OnVolume(false);
break ;
case VK_UP: // select the upper button
OnVolume(true);
break ;
// TODO: Place code here.
case 'F':
ToggleFullScreen();
break ;
}
return (DefWindowProc(m_hWnd, WM_KEYUP, wParam, lParam)) ;
}