「C言語/C++/TestPlayer」の編集履歴(バックアップ)一覧はこちら

C言語/C++/TestPlayer - (2012/10/22 (月) 05:53:51) の1つ前との変更点

追加された行は緑色になります。

削除された行は赤色になります。

|開発環境|Microsoft Visual C++ 2010 Express (SP1)| |実行環境|Microsoft Windows XP Home Edition (SP3)| |プロジェクトの種類|Win32 プロジェクト| |プロジェクト名|TestPlayer| |アプリケーションの種類|Windows アプリケーション| |追加のオプション|空のプロジェクト| |文字セット|Unicode| TestPlayer.cpp #highlight(cpp){{ // Unicode文字セット #pragma comment(lib, "strmiids.lib") #include <DShow.h> #include <evr.h> #include <CommCtrl.h> #include "resource.h" #define SAFE_RELEASE(x) { if (x) { x->Release(); x = NULL; } } #define LTRB(rect) rect.left, rect.top, rect.right, rect.bottom #define CLASS_NAME TEXT("TestPlayer") #define WINDOW_NAME TEXT("TestPlayer") #define PANEL_H 32 #define THUMB_W 16 #define TIMER1 1 #define TIMER2 2 #define WM_GRAPHNOTIFY (WM_APP + 1) enum State {NO_FILE, STOPPED, PAUSED, RUNNING}; enum Ctrl {VIDEO, PLAY, STOP, SEEK, MESSAGE, VOLUME, PANEL, CTRL_NUM}; // 関数プロトタイプ宣言 HWND CreateWnd(HINSTANCE hInstance, int nCmdShow); void Trace(LPCTSTR format, ...); LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void OnTimer(HWND hWnd, WPARAM wParam); void UpdateSeek(HWND hWnd); void DrawSeek(HDC hdc); BOOL OnCommand(HWND hWnd, WPARAM wParam); void SeekFrame(HWND hWnd, LONGLONG llFrame); void OnGraphNotify(void); void OnLButtonDown(HWND hWnd, LPARAM lParam); void OnLButtonUp(HWND hWnd, LPARAM lParam); int HitTest(POINT pt); void OnPlay(HWND hWnd); void OnStop(HWND hWnd); void StartTimer(HWND hWnd); void StopTimer(HWND hWnd); void OnPaint(HWND hWnd); void OnSize(HWND hWnd, WPARAM wParam, LPARAM lParam); void OnDropFiles(HWND hWnd, WPARAM wParam); void OnCreate(HWND hWnd); void InitGraph(HWND hWnd); void ReleaseGraph(HWND hWnd); HRESULT OpenFile(HWND hWnd, LPCWSTR pszFileName); HRESULT InitEvr(HWND hWnd); void AdjustWnd(HWND hWnd, int nMode); // 外部変数構造体 static struct { TCHAR szPath[MAX_PATH]; // 動画ファイルのフルパス TCHAR szFile[MAX_PATH]; // 動画ファイル名 RECT rc[CTRL_NUM]; // コントロール領域 BOOL bMouseCap; // マウスキャプチャフラグ int nCtrl; // クリックされたコントロール State state; // 状態フラグ UINT_PTR uIDEvent; // タイマID int nRange; // シーク範囲 LONGLONG llCurr; // 動画の現在時間 LONGLONG llEnd; // 動画の終了時間 TCHAR szEnd[11+1]; // 動画の終了時間 00:00:00.00 SIZE size; // 動画の幅と高さ IGraphBuilder *pGraph; IMediaControl *pControl; IMediaSeeking *pSeek; IMediaEventEx *pEvent; IMFVideoDisplayControl *pVideo; } g; //============================================================================== int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG msg; HACCEL hAccTable; LPTSTR pszCmdLine; LPTSTR *argv; int argc; HRESULT hr; // COMライブラリの初期化 hr = CoInitialize(NULL); if (FAILED(hr)) { return 0; } msg.wParam = 0; // ウィンドウ作成失敗時の戻り値 g.size.cx = 640; g.size.cy = 480; // コマンド引数の取得 pszCmdLine = GetCommandLine(); argv = CommandLineToArgvW(pszCmdLine, &argc); if (2 <= argc) { wcscpy_s(g.szPath, argv[1]); } // ウィンドウの作成 hWnd = CreateWnd(hInstance, nCmdShow); if (hWnd == NULL) { goto Exit; } // メッセージループ hAccTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_MAIN)); while (GetMessage(&msg, NULL, 0, 0)) { if (TranslateAccelerator(msg.hwnd, hAccTable, &msg) == 0) { TranslateMessage(&msg); DispatchMessage(&msg); } } Exit: CoUninitialize(); return msg.wParam; } //------------------------------------------------------------------------------ HWND CreateWnd(HINSTANCE hInstance, int nCmdShow) { WNDCLASSEX wcx; HWND hWnd; // ウィンドウクラスの登録 ZeroMemory(&wcx, sizeof wcx); wcx.cbSize = sizeof wcx; wcx.style = CS_HREDRAW | CS_VREDRAW; wcx.lpfnWndProc = MainWndProc; wcx.hInstance = hInstance; wcx.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)); wcx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); wcx.lpszClassName = CLASS_NAME; if (RegisterClassEx(&wcx) == 0) { return NULL; } // ウィンドウの作成 hWnd = CreateWindowEx( WS_EX_ACCEPTFILES, CLASS_NAME, WINDOW_NAME, WS_OVERLAPPEDWINDOW, // CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 100, 100, g.size.cx, g.size.cy, NULL, NULL, hInstance, NULL); if (hWnd) { ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); } return hWnd; } //------------------------------------------------------------------------------ void Trace(LPCTSTR format, ...) { va_list arg_ptr; TCHAR buffer[256]; int size; va_start(arg_ptr, format); size = _vsnwprintf_s(buffer, _TRUNCATE, format, arg_ptr); va_end(arg_ptr); OutputDebugString(buffer); if (size < 0) { OutputDebugString(L"...\n"); } } //============================================================================== LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_TIMER: OnTimer(hWnd, wParam); break; case WM_COMMAND: if (OnCommand(hWnd, wParam) == FALSE) { return DefWindowProc(hWnd, uMsg, wParam, lParam); } break; case WM_GRAPHNOTIFY: OnGraphNotify(); break; case WM_LBUTTONDOWN: OnLButtonDown(hWnd, lParam); break; case WM_LBUTTONUP: OnLButtonUp(hWnd, lParam); break; case WM_PAINT: OnPaint(hWnd); break; case WM_SIZE: OnSize(hWnd, wParam, lParam); break; case WM_DROPFILES: OnDropFiles(hWnd, wParam); DragFinish((HDROP)wParam); break; case WM_CREATE: OnCreate(hWnd); break; case WM_DESTROY: ReleaseGraph(hWnd); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return 0; } //------------------------------------------------------------------------------ void OnTimer(HWND hWnd, WPARAM wParam) { switch (wParam) { case TIMER1: if (g.pSeek) { g.pSeek->GetCurrentPosition(&g.llCurr); UpdateSeek(hWnd); } break; case TIMER2: InvalidateRect(hWnd, &g.rc[PANEL], TRUE); KillTimer(hWnd, wParam); break; } } //------------------------------------------------------------------------------ void UpdateSeek(HWND hWnd) { HDC hdc; hdc = GetDC(hWnd); DrawSeek(hdc); ReleaseDC(hWnd, hdc); } //------------------------------------------------------------------------------ void DrawSeek(HDC hdc) { HBRUSH hBrush; RECT rc; int nPos; hBrush = GetSysColorBrush(COLOR_BTNFACE); SelectObject(hdc, hBrush); SetBkMode(hdc, TRANSPARENT); Rectangle(hdc, LTRB(g.rc[SEEK])); Rectangle(hdc, LTRB(g.rc[MESSAGE])); if (g.state == NO_FILE) { return; } // シークバー nPos = (int)(g.nRange * g.llCurr / g.llEnd); nPos += g.rc[SEEK].left; SetRect(&rc, nPos, g.rc[SEEK].top, nPos + THUMB_W, g.rc[SEEK].bottom); Rectangle(hdc, LTRB(rc)); // メッセージ TCHAR szBuf[26+1]; int nCurr; nCurr = (int)(g.llCurr / 100000LL); swprintf_s(szBuf, L" %02d:%02d:%02d.%02d / %s", nCurr / 360000, (nCurr / 6000) % 60, (nCurr / 100) % 60, nCurr % 100, g.szEnd); DrawText(hdc, szBuf, -1, &g.rc[MESSAGE], DT_SINGLELINE | DT_VCENTER); } //------------------------------------------------------------------------------ BOOL OnCommand(HWND hWnd, WPARAM wParam) { WORD wID = LOWORD(wParam); switch (wID) { case ID_ENTER: ShowWindow(hWnd, IsZoomed(hWnd) ? SW_SHOWNOACTIVATE : SW_MAXIMIZE); break; case ID_ESCAPE: DestroyWindow(hWnd); break; case ID_PLAY: OnPlay(hWnd); break; case ID_LEFT: SeekFrame(hWnd, -333667LL); // 1s / 29.97f = 0.0333667s break; case ID_LEFT_CTRL: SeekFrame(hWnd, -1001001LL); // 3f = 0.1001001s break; case ID_RIGHT: SeekFrame(hWnd, 333667LL); break; case ID_RIGHT_CTRL: SeekFrame(hWnd, 1001001LL); break; case ID_NUM1: case ID_NUM2: case ID_NUM3: case ID_NUM4: AdjustWnd(hWnd, (wID - ID_NUM1) + 1); break; case ID_STOP: OnStop(hWnd); break; default: return FALSE; } return TRUE; } //------------------------------------------------------------------------------ void SeekFrame(HWND hWnd, LONGLONG llFrame) { if (g.state != PAUSED) { return; } g.llCurr += llFrame; g.llCurr = max(g.llCurr, 0LL); g.llCurr = min(g.llCurr, g.llEnd); g.pSeek->SetPositions(&g.llCurr, AM_SEEKING_AbsolutePositioning, NULL, 0); UpdateSeek(hWnd); } //------------------------------------------------------------------------------ void OnGraphNotify(void) { long lEvCode, lParam1, lParam2; Trace(L"OnGraphNotify\n"); if (g.pEvent == NULL) { return; } while (SUCCEEDED(g.pEvent->GetEvent(&lEvCode, &lParam1, &lParam2, 0))) { g.pEvent->FreeEventParams(lEvCode, lParam1, lParam2); Trace(L"lEvCode=%#x\n", lEvCode); switch (lEvCode) { case EC_COMPLETE: break; } } } //------------------------------------------------------------------------------ void OnLButtonDown(HWND hWnd, LPARAM lParam) { POINT pt; SetCapture(hWnd); g.bMouseCap = TRUE; POINTSTOPOINT(pt, lParam); g.nCtrl = HitTest(pt); } //------------------------------------------------------------------------------ void OnLButtonUp(HWND hWnd, LPARAM lParam) { POINT pt; if (g.bMouseCap == FALSE) { return; } ReleaseCapture(); g.bMouseCap = FALSE; POINTSTOPOINT(pt, lParam); if (HitTest(pt) != g.nCtrl) { return; } switch (g.nCtrl) { case PLAY: OnPlay(hWnd); break; case STOP: OnStop(hWnd); break; case SEEK: { int nPos; if (g.state <= STOPPED) { break; } nPos = (pt.x - g.rc[SEEK].left) - THUMB_W / 2; nPos = max(nPos, 0); nPos = min(nPos, g.nRange); g.llCurr = g.llEnd * nPos / g.nRange; g.pSeek->SetPositions(&g.llCurr, AM_SEEKING_AbsolutePositioning, NULL, 0); UpdateSeek(hWnd); } break; } } //------------------------------------------------------------------------------ int HitTest(POINT pt) { int n; for (n = 0; n < CTRL_NUM; n++) { if (PtInRect(g.rc + n, pt)) { return n; } } return -1; } //------------------------------------------------------------------------------ void OnPlay(HWND hWnd) { switch (g.state) { case NO_FILE: break; case STOPPED: case PAUSED: g.pControl->Run(); g.state = RUNNING; StartTimer(hWnd); break; case RUNNING: g.pControl->Pause(); g.state = PAUSED; StopTimer(hWnd); g.pSeek->GetCurrentPosition(&g.llCurr); break; } } //------------------------------------------------------------------------------ void OnStop(HWND hWnd) { if (g.state <= STOPPED) { return; } g.pControl->StopWhenReady(); g.state = STOPPED; StopTimer(hWnd); g.llCurr = 0LL; g.pSeek->SetPositions(&g.llCurr, AM_SEEKING_AbsolutePositioning, NULL, 0); UpdateSeek(hWnd); } //------------------------------------------------------------------------------ void StartTimer(HWND hWnd) { g.uIDEvent = SetTimer(hWnd, TIMER1, 1000, NULL); } //------------------------------------------------------------------------------ void StopTimer(HWND hWnd) { if (g.uIDEvent == 0) { return; } KillTimer(hWnd, TIMER1); g.uIDEvent = 0; } //------------------------------------------------------------------------------ void OnPaint(HWND hWnd) { HDC hdc; PAINTSTRUCT ps; Trace(L"OnPaint\n"); if (g.pVideo) { g.pVideo->RepaintVideo(); } hdc = BeginPaint(hWnd, &ps); DrawSeek(hdc); Rectangle(hdc, LTRB(g.rc[PLAY])); DrawText(hdc, L"Play", -1, &g.rc[PLAY], DT_SINGLELINE | DT_VCENTER | DT_CENTER); Rectangle(hdc, LTRB(g.rc[STOP])); DrawText(hdc, L"Stop", -1, &g.rc[STOP], DT_SINGLELINE | DT_VCENTER | DT_CENTER); Rectangle(hdc, LTRB(g.rc[VOLUME])); DrawText(hdc, L"Volume", -1, &g.rc[VOLUME], DT_SINGLELINE | DT_VCENTER | DT_CENTER); EndPaint(hWnd, &ps); } //------------------------------------------------------------------------------ void OnSize(HWND hWnd, WPARAM wParam, LPARAM lParam) { POINTS pts; RECT rc; LONG lPanelT; LONG lSeekB; pts = MAKEPOINTS(lParam); Trace(L"OnSize(%u, %d, %d)\n", wParam, pts.x, pts.y); if (wParam == SIZE_MINIMIZED) { return; } lPanelT = pts.y - PANEL_H; SetRect(&g.rc[VIDEO], 0, 0, pts.x, lPanelT); SetRect(&rc, 0, lPanelT, pts.x, pts.y); lSeekB = rc.top + 12; g.rc[PANEL] = rc; SetRect(&g.rc[PLAY], 0, rc.top, 40, rc.bottom); SetRect(&g.rc[STOP], 40, rc.top, 80, rc.bottom); SetRect(&g.rc[SEEK], 80, rc.top, rc.right, lSeekB); SetRect(&g.rc[MESSAGE], 80, lSeekB, rc.right - 80, rc.bottom); SetRect(&g.rc[VOLUME], rc.right - 80, lSeekB, rc.right, rc.bottom); g.nRange = (g.rc[SEEK].right - g.rc[SEEK].left) - THUMB_W; // ビデオ出力位置のセット if (g.pVideo) { MFVideoNormalizedRect mvnr = {0.0f, 0.0f, 1.0f, 1.0f}; g.pVideo->SetVideoPosition(&mvnr, &g.rc[VIDEO]); } if (g.state == RUNNING) { SetTimer(hWnd, TIMER2, 100, NULL); } } //------------------------------------------------------------------------------ void OnDropFiles(HWND hWnd, WPARAM wParam) { HDROP hDrop = (HDROP)wParam; ReleaseGraph(hWnd); DragQueryFile(hDrop, 0, g.szPath, _countof(g.szPath)); InitGraph(hWnd); } //------------------------------------------------------------------------------ void OnCreate(HWND hWnd) { Trace(L"OnCreate\n"); if (g.szPath[0]) { InitGraph(hWnd); } } //------------------------------------------------------------------------------ void InitGraph(HWND hWnd) { TCHAR szBuf[280]; TCHAR szDrive [_MAX_DRIVE]; TCHAR szDir [_MAX_DIR]; TCHAR szFName [_MAX_FNAME]; TCHAR szExt [_MAX_EXT]; int nEnd; HRESULT hr; // DirectShowフィルタの準備 hr = OpenFile(hWnd, g.szPath); if (FAILED(hr)) { return; } g.state = STOPPED; // タイトルバー _wsplitpath_s(g.szPath, szDrive, szDir, szFName, szExt); _wmakepath_s(g.szFile, NULL, NULL, szFName, szExt); swprintf_s(szBuf, L"%s - %s", g.szFile, WINDOW_NAME); SetWindowText(hWnd, szBuf); // 停止タイムの取得 hr = g.pSeek->GetStopPosition(&g.llEnd); nEnd = (int)(g.llEnd / 100000LL); swprintf_s(g.szEnd, L"%02d:%02d:%02d.%02d", nEnd / 360000, (nEnd / 6000) % 60, (nEnd / 100) % 60, nEnd % 100); // 動画再生 OnPlay(hWnd); } //------------------------------------------------------------------------------ void ReleaseGraph(HWND hWnd) { StopTimer(hWnd); if (g.pControl) { g.pControl->Stop(); g.state = NO_FILE; Sleep(500); } if (g.pEvent) { g.pEvent->SetNotifyWindow(NULL, 0, 0); } SAFE_RELEASE(g.pVideo); SAFE_RELEASE(g.pEvent); SAFE_RELEASE(g.pSeek); SAFE_RELEASE(g.pControl); SAFE_RELEASE(g.pGraph); } //============================================================================== HRESULT OpenFile(HWND hWnd, LPCWSTR pszFile) { // フィルタグラフの作成 HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&g.pGraph)); // メディアコントロールインターフェイスの取得 if (SUCCEEDED(hr)) { hr = g.pGraph->QueryInterface(IID_PPV_ARGS(&g.pControl)); } // ビデオの作成 if (SUCCEEDED(hr)) { hr = InitEvr(hWnd); } // グラフを作成する if (SUCCEEDED(hr)) { hr = g.pGraph->RenderFile(pszFile, NULL); } // シークインターフェイス DWORD dwCaps; if (SUCCEEDED(hr)) { hr = g.pGraph->QueryInterface(IID_PPV_ARGS(&g.pSeek)); } if (SUCCEEDED(hr)) { hr = g.pSeek->GetCapabilities(&dwCaps); // 55 0x37 0011.0111 AM_SEEKING_CanSeekAbsolute; } if (SUCCEEDED(hr)) { hr = g.pSeek->IsFormatSupported(&TIME_FORMAT_MEDIA_TIME); // frame=false } // イベント if (SUCCEEDED(hr)) { hr = g.pGraph->QueryInterface(IID_PPV_ARGS(&g.pEvent)); } if (SUCCEEDED(hr)) { hr = g.pEvent->SetNotifyWindow((OAHWND)hWnd, WM_GRAPHNOTIFY, 0); } // 描画領域の設定 if (SUCCEEDED(hr)) { g.pVideo->GetNativeVideoSize(&g.size, NULL); } if (SUCCEEDED(hr)) { AdjustWnd(hWnd, 2); } return hr; } //------------------------------------------------------------------------------ HRESULT InitEvr(HWND hWnd) { IBaseFilter *pEvr = NULL; // EVRの作成 HRESULT hr = CoCreateInstance(CLSID_EnhancedVideoRenderer, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pEvr)); // フィルタグラフにEVRを追加 if (SUCCEEDED(hr)) { hr = g.pGraph->AddFilter(pEvr, L"EVR"); } IMFGetService *pService = NULL; if (SUCCEEDED(hr)) { hr = pEvr->QueryInterface(IID_PPV_ARGS(&pService)); } if (SUCCEEDED(hr)) { hr = pService->GetService(MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&g.pVideo)); } SAFE_RELEASE(pService); if (SUCCEEDED(hr)) { hr = g.pVideo->SetVideoWindow(hWnd); } SAFE_RELEASE(pEvr); return hr; } //------------------------------------------------------------------------------ void AdjustWnd(HWND hWnd, int nMode) { RECT rc; SetRect(&rc, 0, 0, g.size.cx * nMode / 2, g.size.cy * nMode / 2 + PANEL_H); AdjustWindowRectEx(&rc, WS_OVERLAPPEDWINDOW, FALSE, 0); SetWindowPos(hWnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOMOVE); } }} resource.h #highlight(c){{ #define IDR_MAIN 128 #define ID_ENTER 129 #define ID_ESCAPE 130 #define ID_PLAY 131 #define ID_LEFT 132 #define ID_LEFT_CTRL 133 #define ID_RIGHT 134 #define ID_RIGHT_CTRL 135 #define ID_NUM1 136 #define ID_NUM2 137 #define ID_NUM3 138 #define ID_NUM4 139 #define ID_STOP 140 }} TestPlayer.rc #highlight(c){{ // resource script #include <windows.h> #include "resource.h" //------------------------------------------------------------------------------ // アクセラレータ IDR_MAIN ACCELERATORS BEGIN VK_RETURN, ID_ENTER, VIRTKEY // 0x0D VK_ESCAPE, ID_ESCAPE, VIRTKEY // 0x1B VK_SPACE, ID_PLAY, VIRTKEY // 0x20 VK_LEFT, ID_LEFT, VIRTKEY // 0x25 VK_LEFT, ID_LEFT_CTRL, VIRTKEY, CONTROL VK_RIGHT, ID_RIGHT, VIRTKEY // 0x27 VK_RIGHT, ID_RIGHT_CTRL, VIRTKEY, CONTROL "1", ID_NUM1, VIRTKEY "2", ID_NUM2, VIRTKEY "3", ID_NUM3, VIRTKEY "4", ID_NUM4, VIRTKEY "S", ID_STOP, VIRTKEY END }}
|開発環境|Microsoft Visual C++ 2010 Express (SP1)| |実行環境|Microsoft Windows XP Home Edition (SP3)| |プロジェクトの種類|Win32 プロジェクト| |プロジェクト名|TestPlayer| |アプリケーションの種類|Windows アプリケーション| |追加のオプション|空のプロジェクト| |文字セット|Unicode| 参考 -[[イベントへの応答>http://msdn.microsoft.com/ja-jp/library/cc370589.aspx]] TestPlayer.cpp #highlight(cpp){{ // Unicode文字セット #pragma comment(lib, "strmiids.lib") #include <DShow.h> #include <evr.h> #include <CommCtrl.h> #include "resource.h" #define SAFE_RELEASE(x) { if (x) { x->Release(); x = NULL; } } #define LTRB(rect) rect.left, rect.top, rect.right, rect.bottom #define CLASS_NAME TEXT("TestPlayer") #define WINDOW_NAME TEXT("TestPlayer") #define PANEL_H 32 // パネルの高さ #define THUMB_W 16 // つまみの幅 #define TIMER1 1 // シーク時間定期描画 #define TIMER2 2 // コントロール遅延描画 #define WM_GRAPHNOTIFY (WM_APP + 1) enum Ctrl {VIDEO, PLAY, STOP, SEEK, MESSAGE, VOLUME, PANEL, CTRL_NUM}; enum State {NO_FILE, STOPPED, PAUSED, RUNNING}; // 関数プロトタイプ宣言 HWND CreateWnd(HINSTANCE hInstance, int nCmdShow); void Trace(LPCTSTR format, ...); LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void OnTimer(HWND hWnd, WPARAM wParam); void UpdateSeek(HWND hWnd); void DrawSeek(HDC hdc); BOOL OnCommand(HWND hWnd, WPARAM wParam); void SeekFrame(HWND hWnd, WORD wID); void OnGraphNotify(HWND hWnd); void OnLButtonDown(HWND hWnd, LPARAM lParam); void OnLButtonUp(HWND hWnd, LPARAM lParam); int HitTest(POINT pt); void OnPlay(HWND hWnd); void OnStop(HWND hWnd); void StartTimer(HWND hWnd); void StopTimer(HWND hWnd); void OnPaint(HWND hWnd); void OnSize(HWND hWnd, WPARAM wParam, LPARAM lParam); void OnDropFiles(HWND hWnd, WPARAM wParam); void OnCreate(HWND hWnd); void InitGraph(HWND hWnd); void ReleaseGraph(HWND hWnd); HRESULT OpenFile(HWND hWnd, LPCWSTR pszFileName); HRESULT InitEvr(HWND hWnd); void AdjustWnd(HWND hWnd, int nMode); // 外部変数構造体 static struct { TCHAR szPath[MAX_PATH]; // 動画ファイルのフルパス TCHAR szFile[MAX_PATH]; // 動画ファイル名 RECT rc[CTRL_NUM]; // コントロール領域 BOOL bMouseCap; // マウスキャプチャフラグ int nCtrl; // クリックされたコントロール State state; // 状態フラグ UINT_PTR uIDEvent; // タイマID int nRange; // シーク範囲 LONGLONG llCurr; // 動画の現在時間 LONGLONG llEnd; // 動画の終了時間 TCHAR szEnd[11+1]; // 動画の終了時間 00:00:00.00 SIZE size; // 動画の幅と高さ IGraphBuilder *pGraph; IMediaControl *pControl; IMediaSeeking *pSeek; IMediaEventEx *pEvent; IMFVideoDisplayControl *pVideo; } g; //============================================================================== int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG msg; HACCEL hAccTable; LPTSTR pszCmdLine; LPTSTR *argv; int argc; HRESULT hr; // COMライブラリの初期化 hr = CoInitialize(NULL); if (FAILED(hr)) { return 0; } msg.wParam = 0; // ウィンドウ作成失敗時の戻り値 g.size.cx = 640; g.size.cy = 480; // コマンド引数の取得 pszCmdLine = GetCommandLine(); argv = CommandLineToArgvW(pszCmdLine, &argc); if (2 <= argc) { wcscpy_s(g.szPath, argv[1]); } // ウィンドウの作成 hWnd = CreateWnd(hInstance, nCmdShow); if (hWnd == NULL) { goto Exit; } // メッセージループ hAccTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_MAIN)); while (GetMessage(&msg, NULL, 0, 0)) { if (TranslateAccelerator(msg.hwnd, hAccTable, &msg) == 0) { TranslateMessage(&msg); DispatchMessage(&msg); } } Exit: CoUninitialize(); return msg.wParam; } //------------------------------------------------------------------------------ HWND CreateWnd(HINSTANCE hInstance, int nCmdShow) { WNDCLASSEX wcx; HWND hWnd; // ウィンドウクラスの登録 ZeroMemory(&wcx, sizeof wcx); wcx.cbSize = sizeof wcx; wcx.style = CS_HREDRAW | CS_VREDRAW; wcx.lpfnWndProc = MainWndProc; wcx.hInstance = hInstance; wcx.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)); wcx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); wcx.lpszClassName = CLASS_NAME; if (RegisterClassEx(&wcx) == 0) { return NULL; } // ウィンドウの作成 hWnd = CreateWindowEx( WS_EX_ACCEPTFILES, CLASS_NAME, WINDOW_NAME, WS_OVERLAPPEDWINDOW, // CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 100, 100, g.size.cx, g.size.cy, NULL, NULL, hInstance, NULL); if (hWnd) { ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); } return hWnd; } //------------------------------------------------------------------------------ void Trace(LPCTSTR format, ...) { va_list arg_ptr; TCHAR buffer[256]; int size; va_start(arg_ptr, format); size = _vsnwprintf_s(buffer, _TRUNCATE, format, arg_ptr); va_end(arg_ptr); OutputDebugString(buffer); if (size < 0) { OutputDebugString(L"...\n"); } } //============================================================================== LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_TIMER: OnTimer(hWnd, wParam); break; case WM_COMMAND: if (OnCommand(hWnd, wParam) == FALSE) { return DefWindowProc(hWnd, uMsg, wParam, lParam); } break; case WM_GRAPHNOTIFY: OnGraphNotify(hWnd); break; case WM_LBUTTONDOWN: OnLButtonDown(hWnd, lParam); break; case WM_LBUTTONUP: OnLButtonUp(hWnd, lParam); break; case WM_PAINT: OnPaint(hWnd); break; case WM_SIZE: OnSize(hWnd, wParam, lParam); break; case WM_DROPFILES: OnDropFiles(hWnd, wParam); DragFinish((HDROP)wParam); break; case WM_CREATE: OnCreate(hWnd); break; case WM_DESTROY: ReleaseGraph(hWnd); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return 0; } //------------------------------------------------------------------------------ void OnTimer(HWND hWnd, WPARAM wParam) { switch (wParam) { case TIMER1: if (g.pSeek) { g.pSeek->GetCurrentPosition(&g.llCurr); UpdateSeek(hWnd); } break; case TIMER2: InvalidateRect(hWnd, &g.rc[PANEL], TRUE); KillTimer(hWnd, wParam); break; } } //------------------------------------------------------------------------------ void UpdateSeek(HWND hWnd) { HDC hdc; hdc = GetDC(hWnd); DrawSeek(hdc); ReleaseDC(hWnd, hdc); } //------------------------------------------------------------------------------ void DrawSeek(HDC hdc) { HBRUSH hBrush; RECT rc; int nPos; hBrush = GetSysColorBrush(COLOR_BTNFACE); SelectObject(hdc, hBrush); SetBkMode(hdc, TRANSPARENT); Rectangle(hdc, LTRB(g.rc[SEEK])); Rectangle(hdc, LTRB(g.rc[MESSAGE])); if (g.state == NO_FILE) { return; } // シークバー nPos = (int)(g.nRange * g.llCurr / g.llEnd); nPos += g.rc[SEEK].left; SetRect(&rc, nPos, g.rc[SEEK].top, nPos + THUMB_W, g.rc[SEEK].bottom); Rectangle(hdc, LTRB(rc)); // メッセージ TCHAR szBuf[26+1]; int nCurr; nCurr = (int)(g.llCurr / 100000LL); swprintf_s(szBuf, L" %02d:%02d:%02d.%02d / %s", nCurr / 360000, (nCurr / 6000) % 60, (nCurr / 100) % 60, nCurr % 100, g.szEnd); DrawText(hdc, szBuf, -1, &g.rc[MESSAGE], DT_SINGLELINE | DT_VCENTER); } //------------------------------------------------------------------------------ BOOL OnCommand(HWND hWnd, WPARAM wParam) { WORD wID = LOWORD(wParam); switch (wID) { case ID_ENTER: ShowWindow(hWnd, IsZoomed(hWnd) ? SW_SHOWNOACTIVATE : SW_MAXIMIZE); break; case ID_ESCAPE: DestroyWindow(hWnd); break; case ID_PLAY: OnPlay(hWnd); break; case ID_LEFT: case ID_LEFT_CTRL: case ID_LEFT_SHIFT: case ID_RIGHT: case ID_RIGHT_CTRL: case ID_RIGHT_SHIFT: SeekFrame(hWnd, wID); break; case ID_NUM1: case ID_NUM2: case ID_NUM3: case ID_NUM4: AdjustWnd(hWnd, (wID - ID_NUM1) + 1); break; case ID_STOP: OnStop(hWnd); break; default: return FALSE; } return TRUE; } //------------------------------------------------------------------------------ // ポーズ: 3f Ctrl:1f Shift: 1s // 再生時:10s Ctrl:5s Shift:60s void SeekFrame(HWND hWnd, WORD wID) { static const LONGLONG llRunning[] = { -100000000LL, -50000000LL, -600000000LL, 100000000LL, 50000000LL, 600000000LL }; LONGLONG llFrame; switch (g.state) { case PAUSED: switch (wID) { case ID_LEFT: llFrame = -3000000000LL / 2997LL; // 3f = 0.1001001s break; case ID_LEFT_CTRL: llFrame = -1000000000LL / 2997LL; // 1s / 29.97f = 0.0333667s break; case ID_LEFT_SHIFT: llFrame = -10000000LL; break; case ID_RIGHT: llFrame = 3000000000LL / 2997LL; break; case ID_RIGHT_CTRL: llFrame = 1000000000LL / 2997LL; break; case ID_RIGHT_SHIFT: llFrame = 10000000LL; break; } break; case RUNNING: llFrame = llRunning[wID - ID_LEFT]; break; default: return; } g.llCurr += llFrame; g.llCurr = max(g.llCurr, 0LL); g.llCurr = min(g.llCurr, g.llEnd); g.pSeek->SetPositions(&g.llCurr, AM_SEEKING_AbsolutePositioning, NULL, 0); UpdateSeek(hWnd); } //------------------------------------------------------------------------------ void OnGraphNotify(HWND hWnd) { long lEvCode, lParam1, lParam2; Trace(L"OnGraphNotify\n"); if (g.pEvent == NULL) { return; } while (SUCCEEDED(g.pEvent->GetEvent(&lEvCode, &lParam1, &lParam2, 0))) { g.pEvent->FreeEventParams(lEvCode, lParam1, lParam2); Trace(L"lEvCode=%#x\n", lEvCode); switch (lEvCode) { case EC_COMPLETE: OnPlay(hWnd); break; } } } //------------------------------------------------------------------------------ void OnLButtonDown(HWND hWnd, LPARAM lParam) { POINT pt; SetCapture(hWnd); g.bMouseCap = TRUE; POINTSTOPOINT(pt, lParam); g.nCtrl = HitTest(pt); } //------------------------------------------------------------------------------ void OnLButtonUp(HWND hWnd, LPARAM lParam) { POINT pt; if (g.bMouseCap == FALSE) { return; } ReleaseCapture(); g.bMouseCap = FALSE; POINTSTOPOINT(pt, lParam); if (HitTest(pt) != g.nCtrl) { return; } switch (g.nCtrl) { case PLAY: OnPlay(hWnd); break; case STOP: OnStop(hWnd); break; case SEEK: { int nPos; if (g.state <= STOPPED) { break; } nPos = (pt.x - g.rc[SEEK].left) - THUMB_W / 2; nPos = max(nPos, 0); nPos = min(nPos, g.nRange); g.llCurr = g.llEnd * nPos / g.nRange; g.pSeek->SetPositions(&g.llCurr, AM_SEEKING_AbsolutePositioning, NULL, 0); UpdateSeek(hWnd); } break; } } //------------------------------------------------------------------------------ int HitTest(POINT pt) { int n; for (n = 0; n < CTRL_NUM; n++) { if (PtInRect(g.rc + n, pt)) { return n; } } return -1; } //------------------------------------------------------------------------------ void OnPlay(HWND hWnd) { switch (g.state) { case NO_FILE: break; case STOPPED: case PAUSED: g.pControl->Run(); g.state = RUNNING; StartTimer(hWnd); break; case RUNNING: g.pControl->Pause(); g.state = PAUSED; StopTimer(hWnd); g.pSeek->GetCurrentPosition(&g.llCurr); break; } } //------------------------------------------------------------------------------ void OnStop(HWND hWnd) { if (g.state <= STOPPED) { return; } g.pControl->Stop(); g.state = STOPPED; StopTimer(hWnd); g.llCurr = 0LL; g.pSeek->SetPositions(&g.llCurr, AM_SEEKING_AbsolutePositioning, NULL, 0); UpdateSeek(hWnd); } //------------------------------------------------------------------------------ void StartTimer(HWND hWnd) { g.uIDEvent = SetTimer(hWnd, TIMER1, 1000, NULL); } //------------------------------------------------------------------------------ void StopTimer(HWND hWnd) { if (g.uIDEvent == 0) { return; } KillTimer(hWnd, TIMER1); g.uIDEvent = 0; } //------------------------------------------------------------------------------ void OnPaint(HWND hWnd) { HDC hdc; PAINTSTRUCT ps; Trace(L"OnPaint\n"); if (g.pVideo) { g.pVideo->RepaintVideo(); } hdc = BeginPaint(hWnd, &ps); DrawSeek(hdc); Rectangle(hdc, LTRB(g.rc[PLAY])); DrawText(hdc, L"Play", -1, &g.rc[PLAY], DT_SINGLELINE | DT_VCENTER | DT_CENTER); Rectangle(hdc, LTRB(g.rc[STOP])); DrawText(hdc, L"Stop", -1, &g.rc[STOP], DT_SINGLELINE | DT_VCENTER | DT_CENTER); Rectangle(hdc, LTRB(g.rc[VOLUME])); DrawText(hdc, L"Volume", -1, &g.rc[VOLUME], DT_SINGLELINE | DT_VCENTER | DT_CENTER); EndPaint(hWnd, &ps); } //------------------------------------------------------------------------------ void OnSize(HWND hWnd, WPARAM wParam, LPARAM lParam) { POINTS pts; RECT rc; LONG lPanelT; LONG lSeekB; pts = MAKEPOINTS(lParam); Trace(L"OnSize(%u, %d, %d)\n", wParam, pts.x, pts.y); if (wParam == SIZE_MINIMIZED) { return; } lPanelT = pts.y - PANEL_H; SetRect(&g.rc[VIDEO], 0, 0, pts.x, lPanelT); SetRect(&rc, 0, lPanelT, pts.x, pts.y); lSeekB = rc.top + 12; g.rc[PANEL] = rc; SetRect(&g.rc[PLAY], 0, rc.top, 40, rc.bottom); SetRect(&g.rc[STOP], 40, rc.top, 80, rc.bottom); SetRect(&g.rc[SEEK], 80, rc.top, rc.right, lSeekB); SetRect(&g.rc[MESSAGE], 80, lSeekB, rc.right - 80, rc.bottom); SetRect(&g.rc[VOLUME], rc.right - 80, lSeekB, rc.right, rc.bottom); g.nRange = (g.rc[SEEK].right - g.rc[SEEK].left) - THUMB_W; // ビデオ出力位置のセット if (g.pVideo) { MFVideoNormalizedRect mvnr = {0.0f, 0.0f, 1.0f, 1.0f}; g.pVideo->SetVideoPosition(&mvnr, &g.rc[VIDEO]); } if (g.state == RUNNING) { SetTimer(hWnd, TIMER2, 100, NULL); } } //------------------------------------------------------------------------------ void OnDropFiles(HWND hWnd, WPARAM wParam) { HDROP hDrop = (HDROP)wParam; ReleaseGraph(hWnd); DragQueryFile(hDrop, 0, g.szPath, _countof(g.szPath)); InitGraph(hWnd); } //------------------------------------------------------------------------------ void OnCreate(HWND hWnd) { Trace(L"OnCreate\n"); if (g.szPath[0]) { InitGraph(hWnd); } } //------------------------------------------------------------------------------ void InitGraph(HWND hWnd) { TCHAR szBuf[280]; TCHAR szDrive [_MAX_DRIVE]; TCHAR szDir [_MAX_DIR]; TCHAR szFName [_MAX_FNAME]; TCHAR szExt [_MAX_EXT]; int nEnd; HRESULT hr; // DirectShowフィルタの準備 hr = OpenFile(hWnd, g.szPath); if (FAILED(hr)) { return; } g.state = STOPPED; // タイトルバー _wsplitpath_s(g.szPath, szDrive, szDir, szFName, szExt); _wmakepath_s(g.szFile, NULL, NULL, szFName, szExt); swprintf_s(szBuf, L"%s - %s", g.szFile, WINDOW_NAME); SetWindowText(hWnd, szBuf); // 停止タイムの取得 hr = g.pSeek->GetStopPosition(&g.llEnd); nEnd = (int)(g.llEnd / 100000LL); swprintf_s(g.szEnd, L"%02d:%02d:%02d.%02d", nEnd / 360000, (nEnd / 6000) % 60, (nEnd / 100) % 60, nEnd % 100); // 動画再生 OnPlay(hWnd); } //------------------------------------------------------------------------------ void ReleaseGraph(HWND hWnd) { StopTimer(hWnd); if (g.pControl) { g.pControl->Stop(); g.state = NO_FILE; Sleep(500); } if (g.pEvent) { g.pEvent->SetNotifyWindow(NULL, 0, 0); } SAFE_RELEASE(g.pVideo); SAFE_RELEASE(g.pEvent); SAFE_RELEASE(g.pSeek); SAFE_RELEASE(g.pControl); SAFE_RELEASE(g.pGraph); } //============================================================================== HRESULT OpenFile(HWND hWnd, LPCWSTR pszFile) { // フィルタグラフの作成 HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&g.pGraph)); // メディアコントロールインターフェイスの取得 if (SUCCEEDED(hr)) { hr = g.pGraph->QueryInterface(IID_PPV_ARGS(&g.pControl)); } // ビデオの作成 if (SUCCEEDED(hr)) { hr = InitEvr(hWnd); } // グラフを作成する if (SUCCEEDED(hr)) { hr = g.pGraph->RenderFile(pszFile, NULL); } // シークインターフェイス DWORD dwCaps; if (SUCCEEDED(hr)) { hr = g.pGraph->QueryInterface(IID_PPV_ARGS(&g.pSeek)); } if (SUCCEEDED(hr)) { hr = g.pSeek->GetCapabilities(&dwCaps); // 55 0x37 0011.0111 AM_SEEKING_CanSeekAbsolute; } if (SUCCEEDED(hr)) { hr = g.pSeek->IsFormatSupported(&TIME_FORMAT_MEDIA_TIME); // frame=false } // イベント if (SUCCEEDED(hr)) { hr = g.pGraph->QueryInterface(IID_PPV_ARGS(&g.pEvent)); } if (SUCCEEDED(hr)) { hr = g.pEvent->SetNotifyWindow((OAHWND)hWnd, WM_GRAPHNOTIFY, 0); } // 描画領域の設定 if (SUCCEEDED(hr)) { g.pVideo->GetNativeVideoSize(&g.size, NULL); } if (SUCCEEDED(hr)) { AdjustWnd(hWnd, 2); } return hr; } //------------------------------------------------------------------------------ HRESULT InitEvr(HWND hWnd) { IBaseFilter *pEvr = NULL; // EVRの作成 HRESULT hr = CoCreateInstance(CLSID_EnhancedVideoRenderer, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pEvr)); // フィルタグラフにEVRを追加 if (SUCCEEDED(hr)) { hr = g.pGraph->AddFilter(pEvr, L"EVR"); } IMFGetService *pService = NULL; if (SUCCEEDED(hr)) { hr = pEvr->QueryInterface(IID_PPV_ARGS(&pService)); } if (SUCCEEDED(hr)) { hr = pService->GetService(MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&g.pVideo)); } SAFE_RELEASE(pService); if (SUCCEEDED(hr)) { hr = g.pVideo->SetVideoWindow(hWnd); } SAFE_RELEASE(pEvr); return hr; } //------------------------------------------------------------------------------ void AdjustWnd(HWND hWnd, int nMode) { RECT rc; SetRect(&rc, 0, 0, g.size.cx * nMode / 2, g.size.cy * nMode / 2 + PANEL_H); AdjustWindowRectEx(&rc, WS_OVERLAPPEDWINDOW, FALSE, 0); SetWindowPos(hWnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOMOVE); } }} resource.h #highlight(c){{ #define IDR_MAIN 128 #define ID_ENTER 129 #define ID_ESCAPE 130 #define ID_PLAY 131 #define ID_LEFT 132 #define ID_LEFT_CTRL 133 #define ID_LEFT_SHIFT 134 #define ID_RIGHT 135 #define ID_RIGHT_CTRL 136 #define ID_RIGHT_SHIFT 137 #define ID_NUM1 138 #define ID_NUM2 139 #define ID_NUM3 140 #define ID_NUM4 141 #define ID_STOP 142 }} TestPlayer.rc #highlight(c){{ // resource script #include <windows.h> #include "resource.h" //------------------------------------------------------------------------------ // アクセラレータ IDR_MAIN ACCELERATORS BEGIN VK_RETURN, ID_ENTER, VIRTKEY // 0x0D VK_ESCAPE, ID_ESCAPE, VIRTKEY // 0x1B VK_SPACE, ID_PLAY, VIRTKEY // 0x20 VK_LEFT, ID_LEFT, VIRTKEY // 0x25 VK_LEFT, ID_LEFT_CTRL, VIRTKEY, CONTROL VK_LEFT, ID_LEFT_SHIFT, VIRTKEY, SHIFT VK_RIGHT, ID_RIGHT, VIRTKEY // 0x27 VK_RIGHT, ID_RIGHT_CTRL, VIRTKEY, CONTROL VK_RIGHT, ID_RIGHT_SHIFT, VIRTKEY, SHIFT "1", ID_NUM1, VIRTKEY "2", ID_NUM2, VIRTKEY "3", ID_NUM3, VIRTKEY "4", ID_NUM4, VIRTKEY "S", ID_STOP, VIRTKEY END }}

表示オプション

横に並べて表示:
変化行の前後のみ表示: