Browse Source

[WIP] Native AVI palyers [skip ci]

Lou Yihua 7 years ago
parent
commit
f4859fcf4b
8 changed files with 203 additions and 17 deletions
  1. 11 2
      aviplay.c
  2. 15 0
      video.c
  3. 6 0
      video.h
  4. 1 1
      win32/Makefile
  5. 1 1
      win32/Makefile.mingw
  6. 1 1
      win32/pal_config.h
  7. 6 6
      win32/sdlpal.vcxproj
  8. 162 6
      win32/win32.cpp

+ 11 - 2
aviplay.c

@@ -60,6 +60,12 @@
 #include "riff.h"
 #include "palcfg.h"
 
+#if PAL_HAS_NATIVEAVI
+BOOL PAL_PlayAVI_Native(const char *lpszPath);
+#else
+static BOOL PAL_PlayAVI_Native(const char *lpszPath) { return FALSE; }
+#endif
+
 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
 
 # define SwapStruct32(v, s) \
@@ -634,7 +640,7 @@ PAL_RenderAVIFrameToSurface(
 
 BOOL
 PAL_PlayAVI(
-    LPCSTR     lpszPath
+	const char *lpszPath
 )
 {
 	if (!gConfig.fEnableAviPlay) return FALSE;
@@ -654,7 +660,10 @@ PAL_PlayAVI(
 	{
 		UTIL_LogOutput(LOGLEVEL_WARNING, "Failed to parse AVI file or its format not supported!\n");
 		fclose(fp);
-		return FALSE;
+		//
+		// Try play AVI through OS-native interface
+		//
+		return PAL_PlayAVI_Native(lpszPath);
 	}
 
     PAL_ClearKeyState();

+ 15 - 0
video.c

@@ -1243,3 +1243,18 @@ VIDEO_DrawSurfaceToScreen(
    SDL_FreeSurface(pCompatSurface);
 #endif
 }
+
+BOOL
+VIDEO_GetWindowInfo(
+	SDL_SysWMinfo  *pInfo
+)
+{
+	if (NULL == pInfo) return FALSE;
+
+	SDL_VERSION(&pInfo->version);
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+	return SDL_GetWindowWMInfo(gpWindow, pInfo);
+#else
+	return SDL_GetWMInfo(pInfo);
+#endif
+}

+ 6 - 0
video.h

@@ -24,6 +24,7 @@
 #define VIDEO_H
 
 #include "common.h"
+#include <SDL_syswm.h>
 
 #define VIDEO_CopySurface(s, sr, t, tr) SDL_BlitSurface((s), (sr), (t), (tr))
 #define VIDEO_CopyEntireSurface(s, t)   SDL_BlitSurface((s), NULL, (t), NULL)
@@ -126,6 +127,11 @@ VIDEO_DrawSurfaceToScreen(
     SDL_Surface    *pSurface
 );
 
+BOOL
+VIDEO_GetWindowInfo(
+	SDL_SysWMinfo  *pInfo
+);
+
 PAL_C_LINKAGE_END
 
 #endif

+ 1 - 1
win32/Makefile

@@ -26,7 +26,7 @@ SDL_CONFIG = sdl2-config
 override CCFLAGS += `$(SDL_CONFIG) --cflags` -g -msse2 -Wall -O2 -fno-strict-aliasing -I. -I../ -I../liboggvorbis/include -I../liboggvorbis/src -DPAL_HAS_PLATFORM_SPECIFIC_UTILS $(TEST_CCFLAGS) $(GENERATED)
 CXXFLAGS = $(CCFLAGS) -std=c++11
 CFLAGS = $(CCFLAGS) -std=gnu99
-LDFLAGS = `$(SDL_CONFIG) --libs` -lm -lwinmm -lole32 -loleaut32 -limm32 -lcomctl32 -luuid -ldxguid -lversion -static -static-libgcc -static-libstdc++
+LDFLAGS = `$(SDL_CONFIG) --libs` -lm -lwinmm -lole32 -loleaut32 -limm32 -lcomctl32 -luuid -ldxguid -lstrmiids -lversion -static -static-libgcc -static-libstdc++
 TEST_CXXFLAGS += -isystem $(GTEST_DIR)/include -I $(GTEST_DIR) -g -Wall -Wextra -pthread
 
 POSTCOMPILE = @mv -f $*$(INTER).Td $*$(INTER).d && touch $@

+ 1 - 1
win32/Makefile.mingw

@@ -25,7 +25,7 @@ TEST_OBJFILES = $(TEST_CPPFILES:.cpp=.o)
 override CCFLAGS += -g -msse2 -Wall -O2 -fno-strict-aliasing -I../3rd/mingw-std-threads -I../3rd/SDL/include -I. -I../ -I../liboggvorbis/include -I../liboggvorbis/src -DPAL_HAS_PLATFORM_SPECIFIC_UTILS $(TEST_CCFLAGS) $(GENERATED)
 CXXFLAGS = $(CCFLAGS) -std=c++11
 CFLAGS = $(CCFLAGS) -std=gnu99
-LDFLAGS = -lmingw32 -lSDL2main -lSDL2 -mwindows -lm -lwinmm -lole32 -loleaut32 -limm32 -lcomctl32 -luuid -ldxguid -lversion -static -static-libgcc -static-libstdc++
+LDFLAGS = -lmingw32 -lSDL2main -lSDL2 -mwindows -lm -lwinmm -lole32 -loleaut32 -limm32 -lcomctl32 -luuid -lstrmiids -ldxguid -lversion -static -static-libgcc -static-libstdc++
 TEST_CXXFLAGS += -isystem $(GTEST_DIR)/include -I $(GTEST_DIR) -g -Wall -Wextra -pthread
 
 POSTCOMPILE = @move /Y $(subst /,\,$*)$(INTER).Td $(subst /,\,$*)$(INTER).d > NUL 2>&1 && type NUL >> $(subst /,\,$@)

+ 1 - 1
win32/pal_config.h

@@ -52,7 +52,7 @@
 #define PAL_PORTYEAR         NULL
 
 #define PAL_HAS_NATIVEMIDI  1
-
+#define PAL_HAS_NATIVEAVI   1
 #define PAL_HAS_CONFIG_PAGE 1
 
 #define PAL_FILESYSTEM_IGNORE_CASE 1

+ 6 - 6
win32/sdlpal.vcxproj

@@ -132,7 +132,7 @@
       <Culture>0x0804</Culture>
     </ResourceCompile>
     <Link>
-      <AdditionalDependencies>imm32.lib;version.lib;comctl32.lib;winmm.lib;sdl2.lib;sdl2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>strmiids.lib;imm32.lib;version.lib;comctl32.lib;winmm.lib;sdl2.lib;sdl2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <SuppressStartupBanner>true</SuppressStartupBanner>
       <SubSystem>Windows</SubSystem>
       <TargetMachine>MachineX86</TargetMachine>
@@ -157,7 +157,7 @@
       <Culture>0x0804</Culture>
     </ResourceCompile>
     <Link>
-      <AdditionalDependencies>imm32.lib;version.lib;comctl32.lib;winmm.lib;sdl2.lib;sdl2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>strmiids.lib;imm32.lib;version.lib;comctl32.lib;winmm.lib;sdl2.lib;sdl2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <SuppressStartupBanner>true</SuppressStartupBanner>
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
@@ -184,7 +184,7 @@
       <Culture>0x0804</Culture>
     </ResourceCompile>
     <Link>
-      <AdditionalDependencies>imm32.lib;version.lib;comctl32.lib;winmm.lib;sdl2.lib;sdl2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>strmiids.lib;imm32.lib;version.lib;comctl32.lib;winmm.lib;sdl2.lib;sdl2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <SuppressStartupBanner>true</SuppressStartupBanner>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <SubSystem>Windows</SubSystem>
@@ -212,7 +212,7 @@
       <Culture>0x0804</Culture>
     </ResourceCompile>
     <Link>
-      <AdditionalDependencies>imm32.lib;version.lib;comctl32.lib;winmm.lib;sdl2.lib;sdl2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>strmiids.lib;imm32.lib;version.lib;comctl32.lib;winmm.lib;sdl2.lib;sdl2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <SuppressStartupBanner>true</SuppressStartupBanner>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalLibraryDirectories>
@@ -239,7 +239,7 @@
       <Culture>0x0804</Culture>
     </ResourceCompile>
     <Link>
-      <AdditionalDependencies>imm32.lib;version.lib;comctl32.lib;winmm.lib;sdl2.lib;sdl2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>strmiids.lib;imm32.lib;version.lib;comctl32.lib;winmm.lib;sdl2.lib;sdl2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <SuppressStartupBanner>true</SuppressStartupBanner>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <SubSystem>Windows</SubSystem>
@@ -264,7 +264,7 @@
       <Culture>0x0804</Culture>
     </ResourceCompile>
     <Link>
-      <AdditionalDependencies>imm32.lib;version.lib;comctl32.lib;winmm.lib;sdl2.lib;sdl2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>strmiids.lib;imm32.lib;version.lib;comctl32.lib;winmm.lib;sdl2.lib;sdl2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <SuppressStartupBanner>true</SuppressStartupBanner>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <SubSystem>Console</SubSystem>

+ 162 - 6
win32/win32.cpp

@@ -26,17 +26,30 @@
 #define UNICODE
 #define _UNICODE
 #define _CRT_SECURE_NO_WARNINGS
+#define NO_DSHOW_STRSAFE
 
+#include <inttypes.h>
+#include <string>
 #include <tchar.h>
 #include <windows.h>
+#include <MMSystem.h>
 #include <commctrl.h>
 #include <shlobj.h>
-#include <string>
+#include <SDL_syswm.h>
+#include <d3d9.h>
+#include <dshow.h>
+#include <vmr9.h>
 #include "resource.h"
-#include "../global.h"
-#include "../util.h"
-#include "../palcfg.h"
-#include "../resampler.h"
+#include "global.h"
+#include "util.h"
+#include "palcfg.h"
+#include "resampler.h"
+#include "input.h"
+#include "video.h"
+
+#undef ComboBox_AddString
+#undef ComboBox_SetCurSel
+#undef ComboBox_GetCurSel
 
 #define ComboBox_AddString(hwndDlg, idCtrl, lpsz) \
             (BOOL)SNDMSG(GetDlgItem((hwndDlg), (idCtrl)), CB_ADDSTRING, (WPARAM)(0), (LPARAM)(lpsz))
@@ -296,7 +309,7 @@ INT_PTR ButtonProc(HWND hwndDlg, WORD idControl, HWND hwndCtrl)
 		if (pidl)
 		{
 			SHGetPathFromIDList(pidl, szName);
-			int n = _tcslen(szName);
+			size_t n = _tcslen(szName);
 			if (szName[n - 1] != '\\') _tcscat(szName, L"\\");
 			SetDlgItemText(hwndDlg, IDC_GAMEPATH, szName);
 		}
@@ -427,3 +440,146 @@ BOOL UTIL_IsAbsolutePath(LPCSTR  lpszFileName)
 	else
 		return FALSE;
 }
+
+// DirectShow-based AVI player for Windows
+
+template<class T>
+class com_ptr
+{
+public:
+	com_ptr() : _obj(nullptr) {}
+	com_ptr(T* obj) : _obj(obj) {}
+	~com_ptr() { _obj && _obj->Release(); }
+
+	operator T*() { return _obj; }
+	T* operator->() { return _obj; }
+	T** operator&() { return &_obj; }
+
+	com_ptr& operator=(T* other)
+	{
+		if (_obj != other)
+		{
+			_obj && _obj->Release();
+			(_obj = other) && _obj->AddRef();
+		}
+		return *this;
+	}
+
+private:
+	T* _obj;
+};
+
+extern "C"
+BOOL PAL_PlayAVI_Native(const char* lpszPath)
+{
+	SDL_SysWMinfo info;
+	if (!VIDEO_GetWindowInfo(&info)) return FALSE;
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+	HWND hwnd = info.info.win.window;
+#else
+	HWND hwnd = info.window;
+#endif
+
+	RECT rc;
+	if (!GetClientRect(hwnd, &rc)) return FALSE;
+
+	int buf_len = MultiByteToWideChar(CP_ACP, 0, lpszPath, -1, nullptr, 0);
+	wchar_t* szPath = (wchar_t*)alloca(sizeof(wchar_t) * buf_len);
+	MultiByteToWideChar(CP_ACP, 0, lpszPath, -1, szPath, buf_len);
+
+	HANDLE hAviPlayEvent;
+	HRESULT hr;
+	
+	com_ptr<IGraphBuilder> pGraph;
+	com_ptr<IMediaControl> pControl;
+	com_ptr<IMediaEvent> pEvent;
+	com_ptr<IEnumFilters> pEnum;
+	com_ptr<IBaseFilter> pFilter;
+	com_ptr<IVMRWindowlessControl9> pWindow;
+
+	if (FAILED(hr = CoCreateInstance(CLSID_FilterGraph, nullptr, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph))) return FALSE;
+	if (FAILED(hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl))) return FALSE;
+	if (FAILED(hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent))) return FALSE;
+	if (FAILED(hr = pGraph->RenderFile(szPath, nullptr))) return FALSE;
+	if (FAILED(hr = pGraph->EnumFilters(&pEnum))) return FALSE;
+	while (S_OK == pEnum->Next(1, &pFilter, nullptr))
+	{
+		FILTER_INFO fi;
+		pFilter->QueryFilterInfo(&fi);
+		if (lstrcmpW(fi.achName, L"Video Renderer") == 0)
+		{
+			com_ptr<IEnumPins> pEnumPins;
+			com_ptr<IPin> pOutputPin, pPin;
+			if (FAILED(hr = pFilter->EnumPins(&pEnumPins))) return FALSE;
+			while (S_OK == pEnumPins->Next(1, &pPin, nullptr))
+			{
+				PIN_DIRECTION dir;
+				if (SUCCEEDED(pPin->QueryDirection(&dir)) && dir == PINDIR_INPUT &&
+					SUCCEEDED(pPin->ConnectedTo(&pOutputPin)) && pOutputPin)
+					break;
+				else
+					pPin = nullptr;
+			}
+			if (!pOutputPin || FAILED(hr = pGraph->RemoveFilter(pFilter))) return FALSE;
+
+			pPin = nullptr;
+			pEnumPins = nullptr;
+			pFilter = nullptr;
+
+			com_ptr<IVMRFilterConfig9> pConfig;
+			if (FAILED(hr = CoCreateInstance(CLSID_VideoMixingRenderer9, nullptr, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&pFilter))) return FALSE;
+			if (FAILED(hr = pFilter->QueryInterface(&pConfig))) return FALSE;
+			if (FAILED(hr = pConfig->SetRenderingMode(VMR9Mode_Windowless))) return FALSE;
+			if (FAILED(hr = pConfig->QueryInterface(&pWindow))) return FALSE;
+			if (FAILED(hr = pWindow->SetVideoClippingWindow(hwnd))) return FALSE;
+			if (FAILED(hr = pWindow->SetVideoPosition(nullptr, &rc))) return FALSE;
+			if (FAILED(hr = pGraph->AddFilter(pFilter, L"Video Renderer"))) return FALSE;
+			if (FAILED(hr = pFilter->EnumPins(&pEnumPins))) return FALSE;
+			while (S_OK == pEnumPins->Next(1, &pPin, nullptr))
+			{
+				PIN_DIRECTION dir;
+				if (SUCCEEDED(pPin->QueryDirection(&dir)) && dir == PINDIR_INPUT)
+					break;
+				else
+					pPin = nullptr;
+			}
+			if (!pPin || FAILED(hr = pGraph->Connect(pOutputPin, pPin))) return FALSE;
+
+			break;
+		}
+		pFilter = nullptr;
+	}
+	RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE);
+	PAL_ClearKeyState();
+
+	if (FAILED(hr = pEvent->GetEventHandle((OAEVENT*)&hAviPlayEvent))) return FALSE;
+	if (FAILED(hr = pControl->Run())) return FALSE;
+
+	BOOL looping = TRUE;
+	while (looping && !(g_InputState.dwKeyPress & (kKeyMenu | kKeySearch)))
+	{
+		if (WaitForSingleObject(hAviPlayEvent, 50) == WAIT_OBJECT_0)
+		{
+			long evCode;
+			LONG_PTR param1, param2;
+			while (S_OK == pEvent->GetEvent(&evCode, &param1, &param2, 0))
+			{
+				pEvent->FreeEventParams(evCode, param1, param2);
+				if (evCode == EC_COMPLETE)
+					looping = FALSE;
+			}
+		}
+		UTIL_Delay(1);
+	}
+	hr = pControl->Stop();
+
+	if (looping)
+	{
+		//
+		// Simulate a short delay (like the original game)
+		//
+		UTIL_Delay(500);
+	}
+
+	return TRUE;
+}