In the case in which you just started up the dialog, the strDevice variable would be set to null, so it would not be able to check to make sure whether the device is actually an Elgato or not, so just ignore setting the use buffering checkbox if there is no strDevice set
2422 lines
102 KiB
C++
2422 lines
102 KiB
C++
/********************************************************************************
|
|
Copyright (C) 2012 Hugh Bailey <obs.jim@gmail.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
********************************************************************************/
|
|
|
|
|
|
#include "DShowPlugin.h"
|
|
#include "IVideoCaptureFilter.h" // for Elgato GameCapture
|
|
|
|
//todo: super long file. this is another one of those abominations.
|
|
//fix it jim
|
|
|
|
extern "C" __declspec(dllexport) bool LoadPlugin();
|
|
extern "C" __declspec(dllexport) void UnloadPlugin();
|
|
extern "C" __declspec(dllexport) CTSTR GetPluginName();
|
|
extern "C" __declspec(dllexport) CTSTR GetPluginDescription();
|
|
|
|
LocaleStringLookup *pluginLocale = NULL;
|
|
HINSTANCE hinstMain = NULL;
|
|
|
|
extern DeinterlacerConfig deinterlacerConfigs[DEINTERLACING_TYPE_LAST];
|
|
CTSTR deinterlacerLocalizations[DEINTERLACING_TYPE_LAST] = {
|
|
TEXT("DeinterlacingType.None"),
|
|
TEXT("DeinterlacingType.Discard"),
|
|
TEXT("DeinterlacingType.Retro"),
|
|
TEXT("DeinterlacingType.Blend"),
|
|
TEXT("DeinterlacingType.Blend2x"),
|
|
TEXT("DeinterlacingType.Linear"),
|
|
TEXT("DeinterlacingType.Linear2x"),
|
|
TEXT("DeinterlacingType.Yadif"),
|
|
TEXT("DeinterlacingType.Yadif2x"),
|
|
TEXT("DeinterlacingType.Debug")
|
|
};
|
|
|
|
#define DSHOW_CLASSNAME TEXT("DeviceCapture")
|
|
|
|
|
|
//CTSTR lpRoxioVideoCaptureGUID = TEXT("{6994AD05-93EF-11D0-A3-CC-00-A0-C9-22-31-96}");
|
|
const GUID PIN_CATEGORY_ROXIOCAPTURE = {0x6994AD05, 0x93EF, 0x11D0, {0xA3, 0xCC, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
|
|
|
|
|
|
bool SourceListHasDevice(CTSTR lpDevice, XElement *sourceList)
|
|
{
|
|
UINT numSources = sourceList->NumElements();
|
|
for(UINT i=0; i<numSources; i++)
|
|
{
|
|
XElement *sourceElement = sourceList->GetElementByID(i);
|
|
if(scmpi(sourceElement->GetString(TEXT("class")), DSHOW_CLASSNAME) == 0)
|
|
{
|
|
XElement *data = sourceElement->GetElement(TEXT("data"));
|
|
if(scmpi(data->GetString(TEXT("deviceID")), lpDevice) == 0)
|
|
return true;
|
|
if(scmpi(data->GetString(TEXT("audioDeviceID")), lpDevice) == 0)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CurrentDeviceExists(CTSTR lpDevice, bool bGlobal, bool &isGlobal)
|
|
{
|
|
isGlobal = false;
|
|
|
|
XElement *globalSources = API->GetGlobalSourceListElement();
|
|
if(globalSources)
|
|
{
|
|
if(SourceListHasDevice(lpDevice, globalSources))
|
|
{
|
|
isGlobal = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if(bGlobal)
|
|
{
|
|
XElement *sceneListElement = API->GetSceneListElement();
|
|
if(sceneListElement)
|
|
{
|
|
UINT numScenes = sceneListElement->NumElements();
|
|
for(UINT i=0; i<numScenes; i++)
|
|
{
|
|
XElement *sceneElement = sceneListElement->GetElementByID(i);
|
|
if(sceneElement)
|
|
{
|
|
XElement *sourceListElement = sceneElement->GetElement(TEXT("sources"));
|
|
if(sourceListElement)
|
|
{
|
|
if(SourceListHasDevice(lpDevice, sourceListElement))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
XElement *sceneElement = API->GetSceneElement();
|
|
if(sceneElement)
|
|
{
|
|
XElement *sourceListElement = sceneElement->GetElement(TEXT("sources"));
|
|
if(sourceListElement)
|
|
{
|
|
if(SourceListHasDevice(lpDevice, sourceListElement))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// FMB NOTE: similar functions already in OBSNVEnc\src\nvmain (guidToString(), stringToGuid())
|
|
String GUIDToString(const GUID& guid)
|
|
{
|
|
LPOLESTR resStr;
|
|
String res;
|
|
|
|
if (StringFromCLSID(guid, &resStr) == S_OK)
|
|
{
|
|
res = resStr;
|
|
CoTaskMemFree(resStr);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
bool GetGUIDFromString(CTSTR lpGUID, GUID &targetGUID)
|
|
{
|
|
String strGUID = lpGUID;
|
|
if(strGUID.Length() != 38)
|
|
return false;
|
|
|
|
strGUID = strGUID.Mid(1, strGUID.Length()-1);
|
|
|
|
StringList GUIDData;
|
|
strGUID.GetTokenList(GUIDData, '-', FALSE);
|
|
|
|
if (GUIDData.Num() != 5)
|
|
return false;
|
|
|
|
if (GUIDData[0].Length() != 8 ||
|
|
GUIDData[1].Length() != 4 ||
|
|
GUIDData[2].Length() != 4 ||
|
|
GUIDData[3].Length() != 4 ||
|
|
GUIDData[4].Length() != 12 )
|
|
{
|
|
return false;
|
|
}
|
|
targetGUID.Data1 = (UINT)tstring_base_to_uint(GUIDData[0], NULL, 16);
|
|
targetGUID.Data2 = (WORD)tstring_base_to_uint(GUIDData[1], NULL, 16);
|
|
targetGUID.Data3 = (WORD)tstring_base_to_uint(GUIDData[2], NULL, 16);
|
|
targetGUID.Data4[0] = (BYTE)tstring_base_to_uint(GUIDData[3].Left(2), NULL, 16);
|
|
targetGUID.Data4[1] = (BYTE)tstring_base_to_uint(GUIDData[3].Right(2), NULL, 16);
|
|
targetGUID.Data4[2] = (BYTE)tstring_base_to_uint(GUIDData[4].Left(2), NULL, 16);
|
|
targetGUID.Data4[3] = (BYTE)tstring_base_to_uint(GUIDData[4].Mid(2, 4), NULL, 16);
|
|
targetGUID.Data4[4] = (BYTE)tstring_base_to_uint(GUIDData[4].Mid(4, 6), NULL, 16);
|
|
targetGUID.Data4[5] = (BYTE)tstring_base_to_uint(GUIDData[4].Mid(6, 8), NULL, 16);
|
|
targetGUID.Data4[6] = (BYTE)tstring_base_to_uint(GUIDData[4].Mid(8, 10), NULL, 16);
|
|
targetGUID.Data4[7] = (BYTE)tstring_base_to_uint(GUIDData[4].Right(2), NULL, 16);
|
|
|
|
return true;
|
|
}
|
|
|
|
IBaseFilter* GetExceptionDevice(REFGUID targetGUID)
|
|
{
|
|
IBaseFilter *filter;
|
|
if(SUCCEEDED(CoCreateInstance(targetGUID, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&filter)))
|
|
return filter;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
IBaseFilter* GetExceptionDevice(CTSTR lpGUID)
|
|
{
|
|
GUID targetGUID;
|
|
if (!GetGUIDFromString(lpGUID, targetGUID))
|
|
return NULL;
|
|
|
|
return GetExceptionDevice(targetGUID);
|
|
}
|
|
|
|
IBaseFilter* GetDeviceByValue(const IID &enumType, WSTR lpType, CTSTR lpName, WSTR lpType2, CTSTR lpName2)
|
|
{
|
|
//---------------------------------
|
|
// exception devices
|
|
if(scmpi(lpType2, L"DevicePath") == 0 && lpName2 && *lpName2 == '{')
|
|
return GetExceptionDevice(lpName2);
|
|
|
|
//---------------------------------
|
|
|
|
ICreateDevEnum *deviceEnum;
|
|
IEnumMoniker *videoDeviceEnum;
|
|
|
|
HRESULT err;
|
|
err = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&deviceEnum);
|
|
if(FAILED(err))
|
|
{
|
|
AppWarning(TEXT("GetDeviceByValue: CoCreateInstance for the device enum failed, result = %08lX"), err);
|
|
return NULL;
|
|
}
|
|
|
|
err = deviceEnum->CreateClassEnumerator(enumType, &videoDeviceEnum, 0);
|
|
if(FAILED(err))
|
|
{
|
|
AppWarning(TEXT("GetDeviceByValue: deviceEnum->CreateClassEnumerator failed, result = %08lX"), err);
|
|
deviceEnum->Release();
|
|
return NULL;
|
|
}
|
|
|
|
SafeRelease(deviceEnum);
|
|
|
|
if(err == S_FALSE) //no devices, so NO ENUM FO U
|
|
return NULL;
|
|
|
|
//---------------------------------
|
|
|
|
IBaseFilter *bestFilter = NULL;
|
|
|
|
IMoniker *deviceInfo;
|
|
DWORD count;
|
|
while(videoDeviceEnum->Next(1, &deviceInfo, &count) == S_OK)
|
|
{
|
|
IPropertyBag *propertyData;
|
|
err = deviceInfo->BindToStorage(0, 0, IID_IPropertyBag, (void**)&propertyData);
|
|
if(SUCCEEDED(err))
|
|
{
|
|
VARIANT valueThingy;
|
|
VARIANT valueThingy2;
|
|
VariantInit(&valueThingy);
|
|
VariantInit(&valueThingy2);
|
|
/*valueThingy.vt = VT_BSTR;
|
|
valueThingy.pbstrVal = NULL;
|
|
|
|
valueThingy2.vt = VT_BSTR;
|
|
valueThingy2.bstrVal = NULL;*/
|
|
|
|
if(SUCCEEDED(propertyData->Read(lpType, &valueThingy, NULL)))
|
|
{
|
|
if(lpType2 && lpName2)
|
|
{
|
|
if(FAILED(propertyData->Read(lpType2, &valueThingy2, NULL)))
|
|
nop();
|
|
}
|
|
|
|
SafeRelease(propertyData);
|
|
|
|
String strVal1 = (CWSTR)valueThingy.bstrVal;
|
|
|
|
if(strVal1 == lpName)
|
|
{
|
|
IBaseFilter *filter;
|
|
err = deviceInfo->BindToObject(NULL, 0, IID_IBaseFilter, (void**)&filter);
|
|
if(FAILED(err))
|
|
{
|
|
AppWarning(TEXT("GetDeviceByValue: deviceInfo->BindToObject failed, result = %08lX"), err);
|
|
continue;
|
|
}
|
|
|
|
if(!bestFilter)
|
|
{
|
|
bestFilter = filter;
|
|
|
|
if(!lpType2 || !lpName2)
|
|
{
|
|
SafeRelease(deviceInfo);
|
|
SafeRelease(videoDeviceEnum);
|
|
|
|
return bestFilter;
|
|
}
|
|
}
|
|
else if(lpType2 && lpName2)
|
|
{
|
|
String strVal2 = (CWSTR)valueThingy2.bstrVal;
|
|
if(strVal2 == lpName2)
|
|
{
|
|
bestFilter->Release();
|
|
|
|
bestFilter = filter;
|
|
|
|
SafeRelease(deviceInfo);
|
|
SafeRelease(videoDeviceEnum);
|
|
|
|
return bestFilter;
|
|
}
|
|
}
|
|
else
|
|
filter->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
SafeRelease(deviceInfo);
|
|
}
|
|
|
|
SafeRelease(videoDeviceEnum);
|
|
|
|
return bestFilter;
|
|
}
|
|
|
|
bool PinHasMajorType(IPin *pin, const GUID *majorType)
|
|
{
|
|
HRESULT hRes;
|
|
|
|
IAMStreamConfig *config;
|
|
if(SUCCEEDED(pin->QueryInterface(IID_IAMStreamConfig, (void**)&config)))
|
|
{
|
|
int count, size;
|
|
if(SUCCEEDED(config->GetNumberOfCapabilities(&count, &size)))
|
|
{
|
|
BYTE *capsData = (BYTE*)Allocate(size);
|
|
|
|
int priority = -1;
|
|
for(int i=0; i<count; i++)
|
|
{
|
|
AM_MEDIA_TYPE *pMT = nullptr;
|
|
if(SUCCEEDED(config->GetStreamCaps(i, &pMT, capsData)))
|
|
{
|
|
BOOL bDesiredMediaType = (pMT->majortype == *majorType);
|
|
|
|
FreeMediaType(*pMT);
|
|
CoTaskMemFree(pMT);
|
|
|
|
if (bDesiredMediaType) {
|
|
Free(capsData);
|
|
SafeRelease(config);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
Free(capsData);
|
|
}
|
|
|
|
SafeRelease(config);
|
|
}
|
|
|
|
AM_MEDIA_TYPE *pinMediaType;
|
|
|
|
IEnumMediaTypes *mediaTypesEnum;
|
|
if(FAILED(pin->EnumMediaTypes(&mediaTypesEnum)))
|
|
return false;
|
|
|
|
ULONG curVal = 0;
|
|
hRes = mediaTypesEnum->Next(1, &pinMediaType, &curVal);
|
|
|
|
mediaTypesEnum->Release();
|
|
|
|
if(hRes != S_OK)
|
|
return false;
|
|
|
|
BOOL bDesiredMediaType = (pinMediaType->majortype == *majorType);
|
|
DeleteMediaType(pinMediaType);
|
|
|
|
if(!bDesiredMediaType)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
IPin* GetOutputPin(IBaseFilter *filter, const GUID *majorType)
|
|
{
|
|
IPin *foundPin = NULL;
|
|
IEnumPins *pins;
|
|
|
|
if(!filter) return NULL;
|
|
if(FAILED(filter->EnumPins(&pins))) return NULL;
|
|
|
|
IPin *curPin;
|
|
ULONG num;
|
|
while(pins->Next(1, &curPin, &num) == S_OK)
|
|
{
|
|
if(majorType)
|
|
{
|
|
if (!PinHasMajorType(curPin, majorType))
|
|
{
|
|
SafeRelease(curPin);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//------------------------------
|
|
|
|
PIN_DIRECTION pinDir;
|
|
if(SUCCEEDED(curPin->QueryDirection(&pinDir)))
|
|
{
|
|
if(pinDir == PINDIR_OUTPUT)
|
|
{
|
|
IKsPropertySet *propertySet;
|
|
if(SUCCEEDED(curPin->QueryInterface(IID_IKsPropertySet, (void**)&propertySet)))
|
|
{
|
|
GUID pinCategory;
|
|
DWORD retSize;
|
|
|
|
PIN_INFO chi;
|
|
curPin->QueryPinInfo(&chi);
|
|
|
|
if(chi.pFilter)
|
|
chi.pFilter->Release();
|
|
|
|
if(SUCCEEDED(propertySet->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0, &pinCategory, sizeof(GUID), &retSize)))
|
|
{
|
|
if(pinCategory == PIN_CATEGORY_CAPTURE || pinCategory == PIN_CATEGORY_ROXIOCAPTURE)
|
|
{
|
|
SafeRelease(propertySet);
|
|
SafeRelease(pins);
|
|
|
|
return curPin;
|
|
}
|
|
}
|
|
|
|
SafeRelease(propertySet);
|
|
}
|
|
}
|
|
}
|
|
|
|
SafeRelease(curPin);
|
|
}
|
|
|
|
SafeRelease(pins);
|
|
|
|
return foundPin;
|
|
}
|
|
|
|
// ELGATO_WORKAROUND: Not necessary anymore since Elgato Game Capture v.2.20 which implements IAMStreamConfig
|
|
// !!! Keep this enabled nonetheless for backwards compatibility !!!
|
|
#define ELGATO_WORKAROUND 1
|
|
|
|
#if ELGATO_WORKAROUND
|
|
static void AddElgatoRes(AM_MEDIA_TYPE *pMT, int cx, int cy, VideoOutputType type, List<MediaOutputInfo> &outputInfoList)
|
|
{
|
|
MediaOutputInfo *outputInfo = outputInfoList.CreateNew();
|
|
BITMAPINFOHEADER *bmiHeader = GetVideoBMIHeader(pMT);
|
|
|
|
outputInfo->minCX = outputInfo->maxCX = cx;
|
|
outputInfo->minCY = outputInfo->maxCY = cy;
|
|
outputInfo->minFrameInterval = outputInfo->maxFrameInterval = 10010000000LL/60000LL;
|
|
|
|
outputInfo->xGranularity = outputInfo->yGranularity = 1;
|
|
|
|
outputInfo->mediaType = (AM_MEDIA_TYPE*)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
|
|
memset(outputInfo->mediaType, 0, sizeof(AM_MEDIA_TYPE));
|
|
CopyMediaType(outputInfo->mediaType, pMT);
|
|
|
|
outputInfo->videoType = type;
|
|
outputInfo->bUsingFourCC = false;
|
|
}
|
|
#endif
|
|
|
|
void AddOutput(AM_MEDIA_TYPE *pMT, BYTE *capsData, bool bAllowV2, List<MediaOutputInfo> &outputInfoList)
|
|
{
|
|
VideoOutputType type = GetVideoOutputType(*pMT);
|
|
|
|
if(pMT->formattype == FORMAT_VideoInfo || (bAllowV2 && pMT->formattype == FORMAT_VideoInfo2))
|
|
{
|
|
VIDEO_STREAM_CONFIG_CAPS *pVSCC = reinterpret_cast<VIDEO_STREAM_CONFIG_CAPS*>(capsData);
|
|
VIDEOINFOHEADER *pVih = reinterpret_cast<VIDEOINFOHEADER*>(pMT->pbFormat);
|
|
BITMAPINFOHEADER *bmiHeader = GetVideoBMIHeader(pMT);
|
|
|
|
bool bUsingFourCC = false;
|
|
if(type == VideoOutputType_None)
|
|
{
|
|
type = GetVideoOutputTypeFromFourCC(bmiHeader->biCompression);
|
|
bUsingFourCC = true;
|
|
}
|
|
|
|
if(type != VideoOutputType_None)
|
|
{
|
|
#if ELGATO_WORKAROUND // FMB NOTE 18-Feb-14: Not necessary anymore since Elgato Game Capture v.2.20 which implements IAMStreamConfig
|
|
if (!pVSCC && bAllowV2)
|
|
{
|
|
AddElgatoRes(pMT, 480, 360, type, outputInfoList);
|
|
AddElgatoRes(pMT, 640, 480, type, outputInfoList);
|
|
AddElgatoRes(pMT, 1280, 720, type, outputInfoList);
|
|
AddElgatoRes(pMT, 1920, 1080, type, outputInfoList);
|
|
DeleteMediaType(pMT);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
MediaOutputInfo *outputInfo = outputInfoList.CreateNew();
|
|
|
|
if(pVSCC)
|
|
{
|
|
outputInfo->minFrameInterval = pVSCC->MinFrameInterval;
|
|
outputInfo->maxFrameInterval = pVSCC->MaxFrameInterval;
|
|
outputInfo->minCX = pVSCC->MinOutputSize.cx;
|
|
outputInfo->maxCX = pVSCC->MaxOutputSize.cx;
|
|
outputInfo->minCY = pVSCC->MinOutputSize.cy;
|
|
outputInfo->maxCY = pVSCC->MaxOutputSize.cy;
|
|
|
|
if (!outputInfo->minCX || !outputInfo->minCY || !outputInfo->maxCX || !outputInfo->maxCY) {
|
|
outputInfo->minCX = outputInfo->maxCX = bmiHeader->biWidth;
|
|
outputInfo->minCY = outputInfo->maxCY = bmiHeader->biHeight;
|
|
}
|
|
|
|
//actually due to the other code in GetResolutionFPSInfo, we can have this granularity
|
|
// back to the way it was. now, even if it's corrupted, it will always work
|
|
outputInfo->xGranularity = max(pVSCC->OutputGranularityX, 1);
|
|
outputInfo->yGranularity = max(pVSCC->OutputGranularityY, 1);
|
|
}
|
|
else
|
|
{
|
|
outputInfo->minCX = outputInfo->maxCX = bmiHeader->biWidth;
|
|
outputInfo->minCY = outputInfo->maxCY = bmiHeader->biHeight;
|
|
if(pVih->AvgTimePerFrame != 0)
|
|
outputInfo->minFrameInterval = outputInfo->maxFrameInterval = pVih->AvgTimePerFrame;
|
|
else
|
|
outputInfo->minFrameInterval = outputInfo->maxFrameInterval = 10000000/30; //elgato hack // FMB NOTE 18-Feb-14: Not necessary anymore since Elgato Game Capture v.2.20 which implements IAMStreamConfig
|
|
|
|
outputInfo->xGranularity = outputInfo->yGranularity = 1;
|
|
}
|
|
|
|
outputInfo->mediaType = pMT;
|
|
outputInfo->videoType = type;
|
|
outputInfo->bUsingFourCC = bUsingFourCC;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
DeleteMediaType(pMT);
|
|
}
|
|
|
|
void GetOutputList(IPin *curPin, List<MediaOutputInfo> &outputInfoList)
|
|
{
|
|
HRESULT hRes;
|
|
|
|
IAMStreamConfig *config;
|
|
if(SUCCEEDED(curPin->QueryInterface(IID_IAMStreamConfig, (void**)&config)))
|
|
{
|
|
int count, size;
|
|
if(SUCCEEDED(hRes = config->GetNumberOfCapabilities(&count, &size)))
|
|
{
|
|
BYTE *capsData = (BYTE*)Allocate(size);
|
|
|
|
int priority = -1;
|
|
for(int i=0; i<count; i++)
|
|
{
|
|
AM_MEDIA_TYPE *pMT = nullptr;
|
|
if(SUCCEEDED(config->GetStreamCaps(i, &pMT, capsData)))
|
|
AddOutput(pMT, capsData, false, outputInfoList);
|
|
}
|
|
|
|
Free(capsData);
|
|
}
|
|
else if(hRes == E_NOTIMPL) //...usually elgato.
|
|
{
|
|
IEnumMediaTypes *mediaTypes;
|
|
if(SUCCEEDED(curPin->EnumMediaTypes(&mediaTypes)))
|
|
{
|
|
ULONG i;
|
|
|
|
AM_MEDIA_TYPE *pMT;
|
|
if(mediaTypes->Next(1, &pMT, &i) == S_OK)
|
|
AddOutput(pMT, NULL, true, outputInfoList);
|
|
|
|
mediaTypes->Release();
|
|
}
|
|
}
|
|
|
|
SafeRelease(config);
|
|
}
|
|
}
|
|
|
|
|
|
inline bool ResolutionListHasValue(const List<SIZE> &resolutions, SIZE &size)
|
|
{
|
|
bool bHasResolution = false;
|
|
|
|
for(UINT i=0; i<resolutions.Num(); i++)
|
|
{
|
|
SIZE &testSize = resolutions[i];
|
|
if(size.cx == testSize.cx && size.cy == testSize.cy)
|
|
{
|
|
bHasResolution = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bHasResolution;
|
|
}
|
|
|
|
|
|
struct FPSInterval
|
|
{
|
|
inline FPSInterval(UINT64 minVal, UINT64 maxVal) : minFrameInterval(minVal), maxFrameInterval(maxVal) {}
|
|
UINT64 minFrameInterval, maxFrameInterval;
|
|
};
|
|
|
|
struct FPSInfo
|
|
{
|
|
List<FPSInterval> supportedIntervals;
|
|
};
|
|
|
|
static inline UINT64 GetFrameIntervalDist(UINT64 minInterval, UINT64 maxInterval, UINT64 desiredInterval)
|
|
{
|
|
INT64 minDist = INT64(minInterval)-INT64(desiredInterval);
|
|
INT64 maxDist = INT64(desiredInterval)-INT64(maxInterval);
|
|
|
|
if (minDist < 0) minDist = 0;
|
|
if (maxDist < 0) maxDist = 0;
|
|
|
|
return UINT64(MAX(minDist, maxDist));
|
|
}
|
|
|
|
bool GetClosestResolutionFPS(List<MediaOutputInfo> &outputList, SIZE &resolution, UINT64 &frameInterval, bool bPrioritizeFPS)
|
|
{
|
|
LONG width, height;
|
|
UINT64 internalFrameInterval = 10000000/UINT64(API->GetMaxFPS());
|
|
API->GetBaseSize((UINT&)width, (UINT&)height);
|
|
|
|
LONG bestDistance = 0x7FFFFFFF;
|
|
SIZE bestSize;
|
|
UINT64 maxFrameInterval = 0;
|
|
UINT64 minFrameInterval = 0;
|
|
UINT64 bestFrameIntervalDist = 0xFFFFFFFFFFFFFFFFLL;
|
|
|
|
for(UINT i=0; i<outputList.Num(); i++)
|
|
{
|
|
MediaOutputInfo &outputInfo = outputList[i];
|
|
|
|
LONG outputWidth = outputInfo.minCX;
|
|
do
|
|
{
|
|
LONG distWidth = width-outputWidth;
|
|
if(distWidth < 0)
|
|
break;
|
|
|
|
if(distWidth > bestDistance)
|
|
{
|
|
outputWidth += outputInfo.xGranularity;
|
|
continue;
|
|
}
|
|
|
|
LONG outputHeight = outputInfo.minCY;
|
|
do
|
|
{
|
|
LONG distHeight = height-outputHeight;
|
|
if(distHeight < 0)
|
|
break;
|
|
|
|
LONG totalDist = distHeight+distWidth;
|
|
|
|
UINT64 frameIntervalDist = GetFrameIntervalDist(outputInfo.minFrameInterval,
|
|
outputInfo.maxFrameInterval, internalFrameInterval);
|
|
|
|
bool bBetter;
|
|
if (bPrioritizeFPS)
|
|
bBetter = (frameIntervalDist != bestFrameIntervalDist) ?
|
|
(frameIntervalDist < bestFrameIntervalDist) :
|
|
(totalDist < bestDistance);
|
|
else
|
|
bBetter = (totalDist != bestDistance) ?
|
|
(totalDist < bestDistance) :
|
|
(frameIntervalDist < bestFrameIntervalDist);
|
|
|
|
if (bBetter) {
|
|
bestDistance = totalDist;
|
|
bestSize.cx = outputWidth;
|
|
bestSize.cy = outputHeight;
|
|
maxFrameInterval = outputInfo.maxFrameInterval;
|
|
minFrameInterval = outputInfo.minFrameInterval;
|
|
bestFrameIntervalDist = frameIntervalDist;
|
|
}
|
|
|
|
outputHeight += outputInfo.yGranularity;
|
|
}while((UINT)outputHeight <= outputInfo.maxCY);
|
|
|
|
outputWidth += outputInfo.xGranularity;
|
|
}while((UINT)outputWidth <= outputInfo.maxCX);
|
|
}
|
|
|
|
if(bestDistance != 0x7FFFFFFF)
|
|
{
|
|
resolution.cx = bestSize.cx;
|
|
resolution.cy = bestSize.cy;
|
|
|
|
if(internalFrameInterval > maxFrameInterval)
|
|
frameInterval = maxFrameInterval;
|
|
else if(internalFrameInterval < minFrameInterval)
|
|
frameInterval = minFrameInterval;
|
|
else
|
|
frameInterval = internalFrameInterval;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
struct ConfigDialogData
|
|
{
|
|
CTSTR lpName;
|
|
XElement *data;
|
|
List<MediaOutputInfo> outputList;
|
|
List<SIZE> resolutions;
|
|
StringList deviceNameList;
|
|
StringList deviceIDList;
|
|
StringList audioNameList;
|
|
StringList audioIDList;
|
|
bool bGlobalSource;
|
|
bool bCreating;
|
|
bool bDeviceHasAudio;
|
|
|
|
~ConfigDialogData()
|
|
{
|
|
ClearOutputList();
|
|
}
|
|
|
|
void ClearOutputList()
|
|
{
|
|
for(UINT i=0; i<outputList.Num(); i++)
|
|
outputList[i].FreeData();
|
|
outputList.Clear();
|
|
}
|
|
|
|
void GetResolutions(List<SIZE> &resolutions)
|
|
{
|
|
resolutions.Clear();
|
|
|
|
for(UINT i=0; i<outputList.Num(); i++)
|
|
{
|
|
MediaOutputInfo &outputInfo = outputList[i];
|
|
SIZE size;
|
|
|
|
size.cx = outputInfo.minCX;
|
|
size.cy = outputInfo.minCY;
|
|
if(!ResolutionListHasValue(resolutions, size))
|
|
resolutions << size;
|
|
|
|
size.cx = outputInfo.maxCX;
|
|
size.cy = outputInfo.maxCY;
|
|
if(!ResolutionListHasValue(resolutions, size))
|
|
resolutions << size;
|
|
}
|
|
|
|
//sort
|
|
for(UINT i=0; i<resolutions.Num(); i++)
|
|
{
|
|
SIZE &rez = resolutions[i];
|
|
|
|
for(UINT j=i+1; j<resolutions.Num(); j++)
|
|
{
|
|
SIZE &testRez = resolutions[j];
|
|
|
|
if(testRez.cy < rez.cy)
|
|
{
|
|
resolutions.SwapValues(i, j);
|
|
j = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool GetResolutionFPSInfo(SIZE &resolution, FPSInfo &fpsInfo)
|
|
{
|
|
fpsInfo.supportedIntervals.Clear();
|
|
|
|
for(UINT i=0; i<outputList.Num(); i++)
|
|
{
|
|
MediaOutputInfo &outputInfo = outputList[i];
|
|
|
|
if( UINT(resolution.cx) >= outputInfo.minCX && UINT(resolution.cx) <= outputInfo.maxCX &&
|
|
UINT(resolution.cy) >= outputInfo.minCY && UINT(resolution.cy) <= outputInfo.maxCY )
|
|
{
|
|
if((resolution.cx-outputInfo.minCX) % outputInfo.xGranularity || (resolution.cy-outputInfo.minCY) % outputInfo.yGranularity)
|
|
return false;
|
|
|
|
fpsInfo.supportedIntervals << FPSInterval(outputInfo.minFrameInterval, outputInfo.maxFrameInterval);
|
|
}
|
|
}
|
|
|
|
return fpsInfo.supportedIntervals.Num() != 0;
|
|
}
|
|
};
|
|
|
|
|
|
#define DEV_EXCEPTION_COUNT 1
|
|
CTSTR lpExceptionNames[DEV_EXCEPTION_COUNT] = {TEXT("Elgato Game Capture HD")};
|
|
const GUID lpExceptionGUIDs[DEV_EXCEPTION_COUNT] = {CLSID_ElgatoVideoCaptureFilter};
|
|
|
|
bool FillOutListOfDevices(HWND hwndCombo, GUID matchGUID, StringList *deviceList, StringList *deviceIDList)
|
|
{
|
|
deviceIDList->Clear();
|
|
deviceList->Clear();
|
|
if(hwndCombo != NULL) SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
|
|
|
|
//------------------------------------------
|
|
|
|
for(int i=0; i<DEV_EXCEPTION_COUNT; i++)
|
|
{
|
|
IBaseFilter *exceptionFilter = GetExceptionDevice(lpExceptionGUIDs[i]);
|
|
if(exceptionFilter)
|
|
{
|
|
deviceList->Add(lpExceptionNames[i]);
|
|
|
|
String exceptionGUIDString = GUIDToString(lpExceptionGUIDs[i]);
|
|
deviceIDList->Add(exceptionGUIDString);
|
|
|
|
if(hwndCombo != NULL) SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)lpExceptionNames[i]);
|
|
|
|
exceptionFilter->Release();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------
|
|
|
|
ICreateDevEnum *deviceEnum;
|
|
IEnumMoniker *videoDeviceEnum;
|
|
|
|
HRESULT err;
|
|
err = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&deviceEnum);
|
|
if(FAILED(err))
|
|
{
|
|
AppWarning(TEXT("FillOutListDevices: CoCreateInstance for the device enum failed, result = %08lX"), err);
|
|
return false;
|
|
}
|
|
|
|
err = deviceEnum->CreateClassEnumerator(matchGUID, &videoDeviceEnum, 0);
|
|
if(FAILED(err))
|
|
{
|
|
AppWarning(TEXT("FillOutListDevices: deviceEnum->CreateClassEnumerator failed, result = %08lX"), err);
|
|
deviceEnum->Release();
|
|
return false;
|
|
}
|
|
|
|
SafeRelease(deviceEnum);
|
|
|
|
if(err == S_FALSE) //no devices
|
|
return false;
|
|
|
|
//------------------------------------------
|
|
|
|
IMoniker *deviceInfo;
|
|
DWORD count;
|
|
|
|
bool bUseConfig = true;
|
|
|
|
String strConfigPath = OBSGetPluginDataPath() + "\\dshowDevices.xconfig";
|
|
|
|
XConfig dshowDevicesConfig;
|
|
XElement *devices = NULL;
|
|
|
|
if(!dshowDevicesConfig.Open(strConfigPath.Array()))
|
|
bUseConfig = false;
|
|
|
|
if(bUseConfig) {
|
|
devices = dshowDevicesConfig.GetElement(TEXT("dshowDevices"));
|
|
if(!devices)
|
|
devices = dshowDevicesConfig.CreateElement(TEXT("dshowDevices"));
|
|
}
|
|
|
|
while(videoDeviceEnum->Next(1, &deviceInfo, &count) == S_OK)
|
|
{
|
|
IPropertyBag *propertyData;
|
|
err = deviceInfo->BindToStorage(0, 0, IID_IPropertyBag, (void**)&propertyData);
|
|
if(SUCCEEDED(err))
|
|
{
|
|
VARIANT friendlyNameValue, devicePathValue;
|
|
friendlyNameValue.vt = VT_BSTR;
|
|
friendlyNameValue.bstrVal = NULL;
|
|
devicePathValue.vt = VT_BSTR;
|
|
devicePathValue.bstrVal = NULL;
|
|
|
|
err = propertyData->Read(L"FriendlyName", &friendlyNameValue, NULL);
|
|
propertyData->Read(L"DevicePath", &devicePathValue, NULL);
|
|
|
|
if(SUCCEEDED(err))
|
|
{
|
|
IBaseFilter *filter;
|
|
err = deviceInfo->BindToObject(NULL, 0, IID_IBaseFilter, (void**)&filter);
|
|
if(SUCCEEDED(err))
|
|
{
|
|
String strDeviceName = (CWSTR)friendlyNameValue.bstrVal;
|
|
deviceList->Add(strDeviceName);
|
|
|
|
UINT count2 = 0;
|
|
UINT id = INVALID;
|
|
while((id = deviceList->FindNextValueIndexI(strDeviceName, id)) != INVALID) count2++;
|
|
|
|
if(count2 > 1)
|
|
strDeviceName << TEXT(" (") << UIntString(count2) << TEXT(")");
|
|
|
|
String strDeviceID = (CWSTR)devicePathValue.bstrVal;
|
|
if(bUseConfig) {
|
|
XElement *chkDevice = devices->GetElement((CWSTR)devicePathValue.bstrVal);
|
|
if(!chkDevice) {
|
|
if(strDeviceID.Length() != 0) {
|
|
devices->CreateElement((CWSTR)devicePathValue.bstrVal);
|
|
chkDevice = devices->GetElement((CWSTR)devicePathValue.bstrVal);
|
|
chkDevice->AddString(TEXT("deviceName"), strDeviceName.Array());
|
|
}
|
|
if(hwndCombo != NULL) SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)strDeviceName.Array());
|
|
}
|
|
else {
|
|
if(hwndCombo != NULL) SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)chkDevice->GetString(TEXT("deviceName")));
|
|
}
|
|
}
|
|
else {
|
|
if(hwndCombo != NULL) SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)strDeviceName.Array());
|
|
}
|
|
deviceIDList->Add(strDeviceID);
|
|
|
|
SafeRelease(filter);
|
|
}
|
|
}
|
|
|
|
SafeRelease(propertyData);
|
|
}
|
|
|
|
SafeRelease(deviceInfo);
|
|
}
|
|
|
|
SafeRelease(videoDeviceEnum);
|
|
dshowDevicesConfig.Close(true);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool GetResolution(HWND hwndResolution, SIZE &resolution, BOOL bSelChange)
|
|
{
|
|
String strResolution;
|
|
if(bSelChange)
|
|
strResolution = GetCBText(hwndResolution);
|
|
else
|
|
strResolution = GetEditText(hwndResolution);
|
|
|
|
if(strResolution.NumTokens('x') != 2)
|
|
return false;
|
|
|
|
String strCX = strResolution.GetToken(0, 'x');
|
|
String strCY = strResolution.GetToken(1, 'x');
|
|
|
|
if(strCX.IsEmpty() || strCY.IsEmpty() || !ValidIntString(strCX) || !ValidIntString(strCY))
|
|
return false;
|
|
|
|
UINT cx = strCX.ToInt();
|
|
UINT cy = strCY.ToInt();
|
|
|
|
if(cx < 32 || cy < 32 || cx > 8192 || cy > 8192)
|
|
return false;
|
|
|
|
resolution.cx = cx;
|
|
resolution.cy = cy;
|
|
|
|
return true;
|
|
}
|
|
|
|
struct ColorSelectionData
|
|
{
|
|
HDC hdcDesktop;
|
|
HDC hdcDestination;
|
|
HBITMAP hBitmap;
|
|
bool bValid;
|
|
|
|
inline ColorSelectionData() : hdcDesktop(NULL), hdcDestination(NULL), hBitmap(NULL), bValid(false) {}
|
|
inline ~ColorSelectionData() {Clear();}
|
|
|
|
inline bool Init()
|
|
{
|
|
hdcDesktop = GetDC(NULL);
|
|
if(!hdcDesktop)
|
|
return false;
|
|
|
|
hdcDestination = CreateCompatibleDC(hdcDesktop);
|
|
if(!hdcDestination)
|
|
return false;
|
|
|
|
hBitmap = CreateCompatibleBitmap(hdcDesktop, 1, 1);
|
|
if(!hBitmap)
|
|
return false;
|
|
|
|
SelectObject(hdcDestination, hBitmap);
|
|
bValid = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
inline void Clear()
|
|
{
|
|
if(hdcDesktop)
|
|
{
|
|
ReleaseDC(NULL, hdcDesktop);
|
|
hdcDesktop = NULL;
|
|
}
|
|
|
|
if(hdcDestination)
|
|
{
|
|
DeleteDC(hdcDestination);
|
|
hdcDestination = NULL;
|
|
}
|
|
|
|
if(hBitmap)
|
|
{
|
|
DeleteObject(hBitmap);
|
|
hBitmap = NULL;
|
|
}
|
|
|
|
bValid = false;
|
|
}
|
|
|
|
inline DWORD GetColor()
|
|
{
|
|
POINT p;
|
|
if(GetCursorPos(&p))
|
|
{
|
|
BITMAPINFO data;
|
|
zero(&data, sizeof(data));
|
|
|
|
data.bmiHeader.biSize = sizeof(data.bmiHeader);
|
|
data.bmiHeader.biWidth = 1;
|
|
data.bmiHeader.biHeight = 1;
|
|
data.bmiHeader.biPlanes = 1;
|
|
data.bmiHeader.biBitCount = 24;
|
|
data.bmiHeader.biCompression = BI_RGB;
|
|
data.bmiHeader.biSizeImage = 4;
|
|
|
|
if(BitBlt(hdcDestination, 0, 0, 1, 1, hdcDesktop, p.x, p.y, SRCCOPY|CAPTUREBLT))
|
|
{
|
|
DWORD buffer;
|
|
if(GetDIBits(hdcDestination, hBitmap, 0, 1, &buffer, &data, DIB_RGB_COLORS))
|
|
return 0xFF000000|buffer;
|
|
}
|
|
else
|
|
{
|
|
int err = GetLastError();
|
|
nop();
|
|
}
|
|
}
|
|
|
|
return 0xFF000000;
|
|
}
|
|
};
|
|
|
|
static void OpenPropertyPages(HWND hwnd, IUnknown *propObject)
|
|
{
|
|
if(!propObject)
|
|
return;
|
|
|
|
ISpecifyPropertyPages *propPages;
|
|
CAUUID cauuid;
|
|
|
|
if(SUCCEEDED(propObject->QueryInterface(IID_ISpecifyPropertyPages, (void**)&propPages)))
|
|
{
|
|
if(SUCCEEDED(propPages->GetPages(&cauuid)))
|
|
{
|
|
if(cauuid.cElems)
|
|
{
|
|
OleCreatePropertyFrame(hwnd, 0, 0, NULL, 1, (LPUNKNOWN*)&propObject, cauuid.cElems, cauuid.pElems, 0, 0, NULL);
|
|
CoTaskMemFree(cauuid.pElems);
|
|
}
|
|
}
|
|
|
|
propPages->Release();
|
|
}
|
|
}
|
|
|
|
static void OpenPropertyPagesByName(HWND hwnd, String devicename, String deviceid, GUID matchGUID)
|
|
{
|
|
IBaseFilter *filter = GetDeviceByValue(matchGUID,
|
|
L"FriendlyName", devicename,
|
|
L"DevicePath", deviceid);
|
|
|
|
if(filter)
|
|
{
|
|
OpenPropertyPages(hwnd, filter);
|
|
filter->Release();
|
|
}
|
|
}
|
|
|
|
int SetSliderText(HWND hwndParent, int controlSlider, int controlText)
|
|
{
|
|
HWND hwndSlider = GetDlgItem(hwndParent, controlSlider);
|
|
HWND hwndText = GetDlgItem(hwndParent, controlText);
|
|
|
|
int sliderVal = (int)SendMessage(hwndSlider, TBM_GETPOS, 0, 0);
|
|
float floatVal = float(sliderVal)*0.01f;
|
|
|
|
SetWindowText(hwndText, FormattedString(TEXT("%.02f"), floatVal));
|
|
|
|
return sliderVal;
|
|
}
|
|
|
|
static IAMCrossbar *GetFilterCrossbar(IBaseFilter *filter)
|
|
{
|
|
IAMCrossbar *crossbar = NULL;
|
|
IGraphBuilder *graph = NULL;
|
|
ICaptureGraphBuilder2 *capture = NULL;
|
|
IAMStreamConfig *configVideo = NULL;
|
|
|
|
if (FAILED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, (REFIID)IID_IFilterGraph, (void**)&graph)))
|
|
goto crossbar_exit;
|
|
if (FAILED(CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, (REFIID)IID_ICaptureGraphBuilder2, (void**)&capture)))
|
|
goto crossbar_exit;
|
|
if (FAILED(capture->SetFiltergraph(graph)))
|
|
goto crossbar_exit;
|
|
if (FAILED(graph->AddFilter(filter, L"Capture device")))
|
|
goto crossbar_exit;
|
|
|
|
if (FAILED(capture->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, filter, IID_IAMStreamConfig, (void**)&configVideo)))
|
|
{
|
|
graph->RemoveFilter(filter);
|
|
goto crossbar_exit;
|
|
}
|
|
|
|
capture->FindInterface(NULL, NULL, filter, IID_IAMCrossbar, (void**)&crossbar);
|
|
|
|
graph->RemoveFilter(filter);
|
|
|
|
crossbar_exit:
|
|
SafeRelease(configVideo);
|
|
SafeRelease(capture);
|
|
SafeRelease(graph);
|
|
|
|
return crossbar;
|
|
}
|
|
|
|
INT_PTR CALLBACK ConfigureDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static bool bSelectingColor = false;
|
|
static bool bMouseDown = false, bAudioDevicesPresent = true;
|
|
static ColorSelectionData colorData;
|
|
static DeinterlacerConfig deinterlacingConfig;
|
|
|
|
switch(message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
|
|
|
|
ConfigDialogData *configData = (ConfigDialogData*)lParam;
|
|
|
|
HWND hwndDeviceList = GetDlgItem(hwnd, IDC_DEVICELIST);
|
|
HWND hwndAudioList = GetDlgItem(hwnd, IDC_AUDIOLIST);
|
|
HWND hwndResolutionList = GetDlgItem(hwnd, IDC_RESOLUTION);
|
|
HWND hwndFPS = GetDlgItem(hwnd, IDC_FPS);
|
|
HWND hwndFlip = GetDlgItem(hwnd, IDC_FLIPIMAGE);
|
|
HWND hwndFlipHorizontal = GetDlgItem(hwnd, IDC_FLIPIMAGEH);
|
|
|
|
#ifdef _WIN64
|
|
ShowWindow(GetDlgItem(hwnd, IDC_64BIT_WARNING), SW_SHOW);
|
|
#endif
|
|
|
|
//------------------------------------------
|
|
|
|
bool bFlipVertical = configData->data->GetInt(TEXT("flipImage")) != 0;
|
|
bool bFlipHorizontal = configData->data->GetInt(TEXT("flipImageHorizontal")) != 0;
|
|
|
|
SendMessage(hwndFlip, BM_SETCHECK, bFlipVertical ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
SendMessage(hwndFlipHorizontal, BM_SETCHECK, bFlipHorizontal ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
|
|
//------------------------------------------
|
|
|
|
UINT opacity = configData->data->GetInt(TEXT("opacity"), 100);
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_OPACITY), UDM_SETRANGE32, 0, 100);
|
|
SendMessage(GetDlgItem(hwnd, IDC_OPACITY), UDM_SETPOS32, 0, opacity);
|
|
|
|
int gammaVal = configData->data->GetInt(TEXT("gamma"), 100);
|
|
|
|
HWND hwndTemp = GetDlgItem(hwnd, IDC_GAMMA);
|
|
SendMessage(hwndTemp, TBM_CLEARTICS, FALSE, 0);
|
|
SendMessage(hwndTemp, TBM_SETRANGE, FALSE, MAKELPARAM(50, 175));
|
|
SendMessage(hwndTemp, TBM_SETTIC, 0, 100);
|
|
SendMessage(hwndTemp, TBM_SETPOS, TRUE, gammaVal);
|
|
|
|
SetSliderText(hwnd, IDC_GAMMA, IDC_GAMMAVAL);
|
|
|
|
//------------------------------------------
|
|
|
|
String strDevice = configData->data->GetString(TEXT("deviceName"));
|
|
String strDeviceID = configData->data->GetString(TEXT("deviceID"));
|
|
String strAudioDevice = configData->data->GetString(TEXT("audioDeviceName"));
|
|
String strAudioDeviceID = configData->data->GetString(TEXT("audioDeviceID"));
|
|
UINT cx = configData->data->GetInt(TEXT("resolutionWidth"));
|
|
UINT cy = configData->data->GetInt(TEXT("resolutionHeight"));
|
|
UINT64 frameInterval = configData->data->GetInt(TEXT("frameInterval"));
|
|
|
|
//------------------------------------------
|
|
|
|
deinterlacingConfig.type = configData->data->GetInt(TEXT("deinterlacingType"));
|
|
deinterlacingConfig.fieldOrder = configData->data->GetInt(TEXT("deinterlacingFieldOrder"));
|
|
deinterlacingConfig.processor = configData->data->GetInt(TEXT("deinterlacingProcessor"));
|
|
if(deinterlacingConfig.type >= DEINTERLACING_TYPE_LAST)
|
|
deinterlacingConfig.type = DEINTERLACING_NONE;
|
|
|
|
hwndTemp = GetDlgItem(hwnd, IDC_DEINTERLACELIST);
|
|
|
|
// Populate deinterlacing type list
|
|
for(size_t i = 0; i < DEINTERLACING_TYPE_LAST; i++)
|
|
{
|
|
#ifndef _DEBUG
|
|
if(i == DEINTERLACING__DEBUG)
|
|
continue;
|
|
#endif
|
|
SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)pluginLocale->LookupString(deinterlacerLocalizations[i]));
|
|
|
|
DeinterlacerConfig& config = deinterlacerConfigs[i];
|
|
if(deinterlacingConfig.type != config.type)
|
|
continue;
|
|
|
|
const int deintProcBoth = (DEINTERLACING_PROCESSOR_CPU | DEINTERLACING_PROCESSOR_GPU);
|
|
|
|
HWND checkbox = GetDlgItem(hwnd, IDC_GPUDEINT);
|
|
EnableWindow(checkbox, config.processor == deintProcBoth);
|
|
if(config.processor != deintProcBoth && deinterlacingConfig.processor != config.processor)
|
|
{
|
|
configData->data->SetInt(TEXT("deinterlacingGPU"), config.processor);
|
|
deinterlacingConfig.processor = config.processor;
|
|
}
|
|
Button_SetCheck(checkbox, deinterlacingConfig.processor == DEINTERLACING_PROCESSOR_GPU);
|
|
|
|
const int deintFieldsBoth = (FIELD_ORDER_TFF | FIELD_ORDER_BFF);
|
|
|
|
HWND tff = GetDlgItem(hwnd, IDC_TFF),
|
|
bff = GetDlgItem(hwnd, IDC_BFF);
|
|
if(config.fieldOrder != deintFieldsBoth && deinterlacingConfig.fieldOrder != config.fieldOrder)
|
|
{
|
|
configData->data->SetInt(TEXT("deinterlacingFieldOrder"), config.fieldOrder);
|
|
deinterlacingConfig.fieldOrder = config.fieldOrder;
|
|
}
|
|
Button_SetCheck(tff, deinterlacingConfig.fieldOrder == FIELD_ORDER_TFF);
|
|
Button_SetCheck(bff, deinterlacingConfig.fieldOrder == FIELD_ORDER_BFF);
|
|
EnableWindow(tff, config.fieldOrder & FIELD_ORDER_TFF);
|
|
EnableWindow(bff, config.fieldOrder & FIELD_ORDER_BFF);
|
|
}
|
|
|
|
SendMessage(hwndTemp, CB_SETCURSEL, deinterlacingConfig.type, 0);
|
|
|
|
bool fullrange = configData->data->GetInt(L"fullrange") != 0;
|
|
SendMessage(GetDlgItem(hwnd, IDC_FULLRANGE), BM_SETCHECK, fullrange ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
|
|
int colorSpace = configData->data->GetInt(L"colorspace");
|
|
hwndTemp = GetDlgItem(hwnd, IDC_COLORSPACE);
|
|
SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)PluginStr("DeviceSelection.ColorSpace.Auto"));
|
|
SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)L"709");
|
|
SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)L"601");
|
|
SendMessage(hwndTemp, CB_SETCURSEL, colorSpace, 0);
|
|
|
|
//------------------------------------------
|
|
|
|
BOOL bCustomResolution = configData->data->GetInt(TEXT("customResolution"));
|
|
SendMessage(GetDlgItem(hwnd, IDC_CUSTOMRESOLUTION), BM_SETCHECK, bCustomResolution ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
|
|
LocalizeWindow(hwnd, pluginLocale);
|
|
FillOutListOfDevices(hwndDeviceList, CLSID_VideoInputDeviceCategory, &configData->deviceNameList, &configData->deviceIDList);
|
|
|
|
UINT deviceID = CB_ERR;
|
|
|
|
if(strDevice.IsValid() && cx > 0 && cy > 0 && frameInterval > 0) {
|
|
for(UINT i=0; i < configData->deviceNameList.Num(); i++) {
|
|
if(configData->deviceNameList[i].CompareI(strDevice.Array())) {
|
|
if(configData->deviceIDList[i].CompareI(strDeviceID))
|
|
deviceID = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(deviceID == CB_ERR)
|
|
{
|
|
SendMessage(hwndDeviceList, CB_SETCURSEL, 0, 0);
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_DEVICELIST, CBN_SELCHANGE), (LPARAM)hwndDeviceList);
|
|
}
|
|
else
|
|
{
|
|
SendMessage(hwndDeviceList, CB_SETCURSEL, deviceID, 0);
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_DEVICELIST, CBN_SELCHANGE), (LPARAM)hwndDeviceList);
|
|
|
|
if(bCustomResolution)
|
|
{
|
|
String strResolution;
|
|
strResolution << UIntString(cx) << TEXT("x") << UIntString(cy);
|
|
|
|
SendMessage(hwndResolutionList, WM_SETTEXT, 0, (LPARAM)strResolution.Array());
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_RESOLUTION, CBN_EDITCHANGE), (LPARAM)hwndResolutionList);
|
|
|
|
SetWindowText(hwndFPS, FloatString(10000000.0/double(frameInterval)));
|
|
}
|
|
}
|
|
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_CUSTOMRESOLUTION, BN_CLICKED), (LPARAM)GetDlgItem(hwnd, IDC_CUSTOMRESOLUTION));
|
|
|
|
HWND hwndPreferredList = GetDlgItem(hwnd, IDC_PREFERREDOUTPUT);
|
|
|
|
BOOL bUsePreferredOutput = configData->data->GetInt(TEXT("usePreferredType"));
|
|
EnableWindow(hwndPreferredList, bUsePreferredOutput);
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_USEPREFERREDOUTPUT), BM_SETCHECK, bUsePreferredOutput ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
|
|
//------------------------------------------
|
|
|
|
bool bDefaultUseBuffering = strDevice.IsValid() && (sstri(strDevice, TEXT("Elgato")) != NULL);
|
|
|
|
bool bUseBuffering = configData->data->GetInt(TEXT("useBuffering"), bDefaultUseBuffering) != 0;
|
|
EnableWindow(GetDlgItem(hwnd, IDC_DELAY_EDIT), bUseBuffering);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_DELAY), bUseBuffering);
|
|
|
|
if (strDevice.IsValid())
|
|
SendMessage(GetDlgItem(hwnd, IDC_USEBUFFERING), BM_SETCHECK, bUseBuffering ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
|
|
DWORD bufferTime = configData->data->GetInt(TEXT("bufferTime"));
|
|
SendMessage(GetDlgItem(hwnd, IDC_DELAY), UDM_SETRANGE32, 0, 8000);
|
|
SendMessage(GetDlgItem(hwnd, IDC_DELAY), UDM_SETPOS32, 0, bufferTime);
|
|
|
|
//------------------------------------------
|
|
|
|
UINT audioDeviceID = CB_ERR;
|
|
|
|
if(strAudioDevice.IsValid() && cx > 0 && cy > 0 && frameInterval > 0) {
|
|
for(UINT i=0; i < configData->audioNameList.Num(); i++) {
|
|
if(configData->audioNameList[i].CompareI(strAudioDevice.Array())) {
|
|
if(configData->audioIDList[i].CompareI(strAudioDeviceID))
|
|
audioDeviceID = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*if(strAudioDevice.IsValid())
|
|
audioDeviceID = (UINT)SendMessage(hwndAudioList, CB_FINDSTRINGEXACT, -1, (LPARAM)strAudioDevice.Array());*/
|
|
|
|
if(audioDeviceID == CB_ERR)
|
|
SendMessage(hwndAudioList, CB_SETCURSEL, configData->bDeviceHasAudio ? 1 : 0, 0); //yes, I know, but the output is not a bool
|
|
else
|
|
SendMessage(hwndAudioList, CB_SETCURSEL, audioDeviceID, 0);
|
|
|
|
//------------------------------------------
|
|
|
|
int soundOutputType = configData->data->GetInt(TEXT("soundOutputType"), 1);
|
|
switch(soundOutputType) {
|
|
case 0:
|
|
case 1: hwndTemp = GetDlgItem(hwnd, IDC_OUTPUTSOUND); break;
|
|
case 2: hwndTemp = GetDlgItem(hwnd, IDC_PLAYDESKTOPSOUND); break;
|
|
}
|
|
|
|
SendMessage(hwndTemp, BM_SETCHECK, BST_CHECKED, 0);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_TIMEOFFSET), soundOutputType == 1);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_TIMEOFFSET_EDIT), soundOutputType == 1);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_VOLUME), soundOutputType != 0);
|
|
|
|
if (soundOutputType == 0)
|
|
SendMessage(hwndAudioList, CB_SETCURSEL, 0, 0);
|
|
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_AUDIOLIST, CBN_SELCHANGE), (LPARAM)hwndAudioList);
|
|
|
|
//------------------------------------------
|
|
|
|
float fVol = configData->data->GetFloat(TEXT("volume"), 1.0f);
|
|
SetVolumeControlValue(GetDlgItem(hwnd, IDC_VOLUME), fVol);
|
|
BOOL bUseAudioRender = configData->data->GetInt(TEXT("useAudioRender"));
|
|
SendMessage(GetDlgItem(hwnd, IDC_USEAUDIORENDER), BM_SETCHECK, bUseAudioRender ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
|
|
//------------------------------------------
|
|
|
|
int pos = configData->data->GetInt(TEXT("soundTimeOffset"));
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_TIMEOFFSET), UDM_SETRANGE32, -3000, 20000);
|
|
SendMessage(GetDlgItem(hwnd, IDC_TIMEOFFSET), UDM_SETPOS32, 0, pos);
|
|
|
|
//------------------------------------------
|
|
|
|
BOOL bUseChromaKey = configData->data->GetInt(TEXT("useChromaKey"), 0);
|
|
BOOL bUsePointFiltering = configData->data->GetInt(TEXT("usePointFiltering"), 0);
|
|
BOOL bPreserveSourceSize = configData->data->GetInt(TEXT("preserveSourceSize"), 0);
|
|
DWORD keyColor = configData->data->GetInt(TEXT("keyColor"), 0xFFFFFFFF);
|
|
UINT similarity = configData->data->GetInt(TEXT("keySimilarity"), 0);
|
|
UINT blend = configData->data->GetInt(TEXT("keyBlend"), 80);
|
|
UINT gamma = configData->data->GetInt(TEXT("keySpillReduction"), 50);
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_POINTFILTERING), BM_SETCHECK, bUsePointFiltering ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
SendMessage(GetDlgItem(hwnd, IDC_PRESERVESIZE), BM_SETCHECK, bPreserveSourceSize ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
SendMessage(GetDlgItem(hwnd, IDC_USECHROMAKEY), BM_SETCHECK, bUseChromaKey ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
CCSetColor(GetDlgItem(hwnd, IDC_COLOR), keyColor);
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_BASETHRESHOLD), UDM_SETRANGE32, 0, 1000);
|
|
SendMessage(GetDlgItem(hwnd, IDC_BASETHRESHOLD), UDM_SETPOS32, 0, similarity);
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_BLEND), UDM_SETRANGE32, 0, 1000);
|
|
SendMessage(GetDlgItem(hwnd, IDC_BLEND), UDM_SETPOS32, 0, blend);
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_SPILLREDUCTION), UDM_SETRANGE32, 0, 1000);
|
|
SendMessage(GetDlgItem(hwnd, IDC_SPILLREDUCTION), UDM_SETPOS32, 0, gamma);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_COLOR), bUseChromaKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SELECTCOLOR), bUseChromaKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BASETHRESHOLD_EDIT), bUseChromaKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BASETHRESHOLD), bUseChromaKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BLEND_EDIT), bUseChromaKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BLEND), bUseChromaKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SPILLREDUCTION_EDIT), bUseChromaKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SPILLREDUCTION), bUseChromaKey);
|
|
|
|
//------------------------------------------
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
if(colorData.bValid)
|
|
{
|
|
CCSetColor(GetDlgItem(hwnd, IDC_COLOR), colorData.GetColor());
|
|
colorData.Clear();
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
if(bSelectingColor)
|
|
{
|
|
bMouseDown = true;
|
|
CCSetColor(GetDlgItem(hwnd, IDC_COLOR), colorData.GetColor());
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_COLOR, CCN_CHANGED), (LPARAM)GetDlgItem(hwnd, IDC_COLOR));
|
|
}
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
if(bSelectingColor && bMouseDown)
|
|
{
|
|
CCSetColor(GetDlgItem(hwnd, IDC_COLOR), colorData.GetColor());
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_COLOR, CCN_CHANGED), (LPARAM)GetDlgItem(hwnd, IDC_COLOR));
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
if(bSelectingColor)
|
|
{
|
|
colorData.Clear();
|
|
ReleaseCapture();
|
|
bMouseDown = false;
|
|
bSelectingColor = false;
|
|
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
if(source)
|
|
source->SetInt(TEXT("useChromaKey"), true);
|
|
}
|
|
break;
|
|
|
|
case WM_CAPTURECHANGED:
|
|
if(bSelectingColor)
|
|
{
|
|
if(colorData.bValid)
|
|
{
|
|
CCSetColor(GetDlgItem(hwnd, IDC_COLOR), colorData.GetColor());
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_COLOR, CCN_CHANGED), (LPARAM)GetDlgItem(hwnd, IDC_COLOR));
|
|
colorData.Clear();
|
|
}
|
|
|
|
ReleaseCapture();
|
|
bMouseDown = false;
|
|
bSelectingColor = false;
|
|
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
if(source)
|
|
source->SetInt(TEXT("useChromaKey"), true);
|
|
}
|
|
break;
|
|
|
|
case WM_HSCROLL:
|
|
{
|
|
if(GetDlgCtrlID((HWND)lParam) == IDC_GAMMA)
|
|
{
|
|
int gamma = SetSliderText(hwnd, IDC_GAMMA, IDC_GAMMAVAL);
|
|
|
|
ConfigDialogData *info = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(info->lpName);
|
|
if(source)
|
|
source->SetInt(TEXT("gamma"), gamma);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDC_CUSTOMRESOLUTION:
|
|
{
|
|
HWND hwndUseCustomResolution = (HWND)lParam;
|
|
BOOL bCustomResolution = SendMessage(hwndUseCustomResolution, BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_RESOLUTION), bCustomResolution);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_FPS), bCustomResolution);
|
|
break;
|
|
}
|
|
|
|
case IDC_USEBUFFERING:
|
|
if (HIWORD(wParam) == BN_CLICKED) {
|
|
bool bUseBuffering = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_DELAY_EDIT), bUseBuffering);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_DELAY), bUseBuffering);
|
|
}
|
|
break;
|
|
|
|
case IDC_VOLUME:
|
|
if(HIWORD(wParam) == VOLN_ADJUSTING || HIWORD(wParam) == VOLN_FINALVALUE)
|
|
{
|
|
if(IsWindowEnabled((HWND)lParam))
|
|
{
|
|
float fVol = GetVolumeControlValue((HWND)lParam);
|
|
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
if(source)
|
|
source->SetFloat(TEXT("volume"), fVol);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDC_USECHROMAKEY:
|
|
{
|
|
HWND hwndUseColorKey = (HWND)lParam;
|
|
BOOL bUseChromaKey = SendMessage(hwndUseColorKey, BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
if(source)
|
|
source->SetInt(TEXT("useChromaKey"), bUseChromaKey);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_COLOR), bUseChromaKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SELECTCOLOR), bUseChromaKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BASETHRESHOLD_EDIT), bUseChromaKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BASETHRESHOLD), bUseChromaKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BLEND_EDIT), bUseChromaKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BLEND), bUseChromaKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SPILLREDUCTION_EDIT), bUseChromaKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SPILLREDUCTION), bUseChromaKey);
|
|
break;
|
|
}
|
|
|
|
case IDC_SELECTCOLOR:
|
|
{
|
|
if(!bSelectingColor)
|
|
{
|
|
if(colorData.Init())
|
|
{
|
|
bMouseDown = false;
|
|
bSelectingColor = true;
|
|
SetCapture(hwnd);
|
|
HCURSOR hCursor = (HCURSOR)LoadImage(hinstMain, MAKEINTRESOURCE(IDC_COLORPICKER), IMAGE_CURSOR, 32, 32, 0);
|
|
SetCursor(hCursor);
|
|
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
if(source)
|
|
source->SetInt(TEXT("useChromaKey"), false);
|
|
}
|
|
else
|
|
colorData.Clear();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IDC_COLOR:
|
|
{
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
|
|
if(source)
|
|
{
|
|
DWORD color = CCGetColor((HWND)lParam);
|
|
source->SetInt(TEXT("keyColor"), color);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IDC_FLIPIMAGE:
|
|
case IDC_FLIPIMAGEH:
|
|
if(HIWORD(wParam) == BN_CLICKED)
|
|
{
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
if(source)
|
|
{
|
|
HWND hwndFlip = (HWND)lParam;
|
|
BOOL bFlipImage = SendMessage(hwndFlip, BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDC_FLIPIMAGE: source->SetInt(TEXT("flipImage"), bFlipImage); break;
|
|
case IDC_FLIPIMAGEH: source->SetInt(TEXT("flipImageHorizontal"), bFlipImage); break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDC_POINTFILTERING:
|
|
if(HIWORD(wParam) == BN_CLICKED)
|
|
{
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
if(source)
|
|
{
|
|
HWND hwndPointFiltering = (HWND)lParam;
|
|
BOOL bUsePointFiltering = SendMessage(hwndPointFiltering, BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
|
|
source->SetInt(TEXT("usePointFiltering"), bUsePointFiltering);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDC_DELAY_EDIT:
|
|
case IDC_TIMEOFFSET_EDIT:
|
|
case IDC_OPACITY_EDIT:
|
|
case IDC_BASETHRESHOLD_EDIT:
|
|
case IDC_BLEND_EDIT:
|
|
case IDC_SPILLREDUCTION_EDIT:
|
|
if(HIWORD(wParam) == EN_CHANGE)
|
|
{
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
if(configData)
|
|
{
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
|
|
if(source)
|
|
{
|
|
HWND hwndVal = NULL;
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDC_DELAY_EDIT: hwndVal = GetDlgItem(hwnd, IDC_DELAY); break;
|
|
case IDC_TIMEOFFSET_EDIT: hwndVal = GetDlgItem(hwnd, IDC_TIMEOFFSET); break;
|
|
case IDC_OPACITY_EDIT: hwndVal = GetDlgItem(hwnd, IDC_OPACITY); break;
|
|
case IDC_BASETHRESHOLD_EDIT: hwndVal = GetDlgItem(hwnd, IDC_BASETHRESHOLD); break;
|
|
case IDC_BLEND_EDIT: hwndVal = GetDlgItem(hwnd, IDC_BLEND); break;
|
|
case IDC_SPILLREDUCTION_EDIT: hwndVal = GetDlgItem(hwnd, IDC_SPILLREDUCTION); break;
|
|
}
|
|
|
|
int val = (int)SendMessage(hwndVal, UDM_GETPOS32, 0, 0);
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDC_DELAY_EDIT: source->SetInt(TEXT("bufferTime"), val); break;
|
|
case IDC_TIMEOFFSET_EDIT: source->SetInt(TEXT("timeOffset"), val); break;
|
|
case IDC_OPACITY_EDIT: source->SetInt(TEXT("opacity"), val); break;
|
|
case IDC_BASETHRESHOLD_EDIT: source->SetInt(TEXT("keySimilarity"), val); break;
|
|
case IDC_BLEND_EDIT: source->SetInt(TEXT("keyBlend"), val); break;
|
|
case IDC_SPILLREDUCTION_EDIT: source->SetInt(TEXT("keySpillReduction"), val); break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDC_USEPREFERREDOUTPUT:
|
|
{
|
|
BOOL bUsePreferredOutput = SendMessage(GetDlgItem(hwnd, IDC_USEPREFERREDOUTPUT), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
EnableWindow(GetDlgItem(hwnd, IDC_PREFERREDOUTPUT), bUsePreferredOutput);
|
|
break;
|
|
}
|
|
|
|
case IDC_REFRESH:
|
|
{
|
|
HWND hwndDeviceList = GetDlgItem(hwnd, IDC_DEVICELIST);
|
|
HWND hwndAudioDeviceList = GetDlgItem(hwnd, IDC_AUDIOLIST);
|
|
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
FillOutListOfDevices(hwndDeviceList, CLSID_VideoInputDeviceCategory, &configData->deviceNameList, &configData->deviceIDList);
|
|
bAudioDevicesPresent = FillOutListOfDevices(hwndAudioDeviceList, CLSID_AudioInputDeviceCategory, &configData->audioNameList, &configData->audioIDList);
|
|
|
|
SendMessage(hwndDeviceList, CB_SETCURSEL, 0, 0);
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_DEVICELIST, CBN_SELCHANGE), (LPARAM)hwndDeviceList);
|
|
|
|
|
|
if(bAudioDevicesPresent)
|
|
{
|
|
SendMessage(hwndAudioDeviceList, CB_SETCURSEL, 0, 0);
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_AUDIOLIST, CBN_SELCHANGE), (LPARAM)hwndAudioDeviceList);
|
|
}
|
|
EnableWindow(hwndAudioDeviceList, bAudioDevicesPresent);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_CONFIGAUDIO), bAudioDevicesPresent);
|
|
|
|
break;
|
|
}
|
|
|
|
case IDC_DEINTERLACELIST:
|
|
if(HIWORD(wParam) == CBN_SELCHANGE)
|
|
{
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);;
|
|
int confId = (int)SendMessage(GetDlgItem(hwnd, IDC_DEINTERLACELIST), CB_GETCURSEL, 0, 0);
|
|
|
|
deinterlacingConfig.type = configData->data->GetInt(TEXT("deinterlacingType"));
|
|
deinterlacingConfig.fieldOrder = configData->data->GetInt(TEXT("deinterlacingFieldOrder"));
|
|
deinterlacingConfig.processor = configData->data->GetInt(TEXT("deinterlacingGPU"));
|
|
if(deinterlacingConfig.type >= DEINTERLACING_TYPE_LAST)
|
|
deinterlacingConfig.type = DEINTERLACING_NONE;
|
|
|
|
DeinterlacerConfig& config = deinterlacerConfigs[confId];
|
|
|
|
const int deintProcBoth = (DEINTERLACING_PROCESSOR_CPU | DEINTERLACING_PROCESSOR_GPU);
|
|
|
|
HWND checkbox = GetDlgItem(hwnd, IDC_GPUDEINT);
|
|
EnableWindow(checkbox, config.processor == deintProcBoth);
|
|
if(config.processor != deintProcBoth && deinterlacingConfig.processor != config.processor)
|
|
deinterlacingConfig.processor = config.processor;
|
|
Button_SetCheck(checkbox, deinterlacingConfig.processor == DEINTERLACING_PROCESSOR_GPU);
|
|
|
|
const int deintFieldsBoth = (FIELD_ORDER_TFF | FIELD_ORDER_BFF);
|
|
|
|
HWND tff = GetDlgItem(hwnd, IDC_TFF),
|
|
bff = GetDlgItem(hwnd, IDC_BFF);
|
|
if(config.fieldOrder != deintFieldsBoth && deinterlacingConfig.fieldOrder != config.fieldOrder)
|
|
deinterlacingConfig.fieldOrder = config.fieldOrder;
|
|
else if(deinterlacingConfig.fieldOrder != config.fieldOrder && (config.fieldOrder & deinterlacingConfig.fieldOrder) == 0)
|
|
deinterlacingConfig.fieldOrder = config.fieldOrder & FIELD_ORDER_TFF ? FIELD_ORDER_TFF : FIELD_ORDER_BFF;
|
|
Button_SetCheck(tff, deinterlacingConfig.fieldOrder == FIELD_ORDER_TFF);
|
|
Button_SetCheck(bff, deinterlacingConfig.fieldOrder == FIELD_ORDER_BFF);
|
|
EnableWindow(tff, config.fieldOrder & FIELD_ORDER_TFF);
|
|
EnableWindow(bff, config.fieldOrder & FIELD_ORDER_BFF);
|
|
}
|
|
break;
|
|
|
|
case IDC_DEVICELIST:
|
|
if(HIWORD(wParam) == CBN_SELCHANGE)
|
|
{
|
|
HWND hwndResolutions = GetDlgItem(hwnd, IDC_RESOLUTION);
|
|
SendMessage(hwndResolutions, CB_RESETCONTENT, 0, 0);
|
|
|
|
HWND hwndDevices = (HWND)lParam;
|
|
UINT id = (UINT)SendMessage(hwndDevices, CB_GETCURSEL, 0, 0);
|
|
if(id == CB_ERR)
|
|
{
|
|
EnableWindow(GetDlgItem(hwnd, IDC_RESOLUTION), FALSE);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_FPS), FALSE);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_CONFIG), FALSE);
|
|
EnableWindow(GetDlgItem(hwnd, IDOK), FALSE);
|
|
}
|
|
else
|
|
{
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
HWND hwndAudioList = GetDlgItem(hwnd, IDC_AUDIOLIST);
|
|
|
|
BOOL bCustomResolution = SendMessage(GetDlgItem(hwnd, IDC_CUSTOMRESOLUTION) , BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_RESOLUTION), bCustomResolution);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_FPS), bCustomResolution);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_CONFIG), TRUE);
|
|
EnableWindow(GetDlgItem(hwnd, IDOK), TRUE);
|
|
|
|
IBaseFilter *filter = GetDeviceByValue(CLSID_VideoInputDeviceCategory,
|
|
L"FriendlyName", configData->deviceNameList[id],
|
|
L"DevicePath", configData->deviceIDList[id]);
|
|
|
|
if(filter)
|
|
{
|
|
//--------------------------------
|
|
// get video info
|
|
|
|
IPin *outputPin = GetOutputPin(filter, &MEDIATYPE_Video);
|
|
if(outputPin)
|
|
{
|
|
configData->ClearOutputList();
|
|
GetOutputList(outputPin, configData->outputList);
|
|
|
|
configData->GetResolutions(configData->resolutions);
|
|
for(UINT i=0; i<configData->resolutions.Num(); i++)
|
|
{
|
|
SIZE &resolution = configData->resolutions[i];
|
|
|
|
String strResolution;
|
|
strResolution << IntString(resolution.cx) << TEXT("x") << IntString(resolution.cy);
|
|
SendMessage(hwndResolutions, CB_ADDSTRING, 0, (LPARAM)strResolution.Array());
|
|
}
|
|
|
|
outputPin->Release();
|
|
outputPin = NULL;
|
|
}
|
|
|
|
String strDeviceName = configData->deviceNameList[id];
|
|
bool bIsElworkaroundo = sstri(strDeviceName, TEXT("elgato")) != NULL; //HAHAHAHAHAHAHA HOW F***ING WONDERFUL. 自殺したい
|
|
|
|
IAMCrossbar *crossbar = bIsElworkaroundo ? NULL : GetFilterCrossbar(filter);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_CROSSBAR), crossbar != NULL);
|
|
SafeRelease(crossbar);
|
|
|
|
outputPin = GetOutputPin(filter, &MEDIATYPE_Audio);
|
|
configData->bDeviceHasAudio = (outputPin != NULL);
|
|
SafeRelease(outputPin);
|
|
|
|
filter->Release();
|
|
}
|
|
|
|
HWND hwndUseBuffering = GetDlgItem(hwnd, IDC_USEBUFFERING);
|
|
|
|
if (sstri(configData->deviceNameList[id], TEXT("Elgato")) != NULL) {
|
|
SendMessage(hwndUseBuffering, BM_SETCHECK, BST_CHECKED, 0);
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_USEBUFFERING, BN_CLICKED), (LPARAM)hwndUseBuffering);
|
|
} else {
|
|
SendMessage(hwndUseBuffering, BM_SETCHECK, BST_UNCHECKED, 0);
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_USEBUFFERING, BN_CLICKED), (LPARAM)hwndUseBuffering);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
|
|
SIZE size;
|
|
UINT64 frameInterval;
|
|
if(GetClosestResolutionFPS(configData->outputList, size, frameInterval, true))
|
|
{
|
|
String strResolution;
|
|
strResolution << UIntString(size.cx) << TEXT("x") << UIntString(size.cy);
|
|
|
|
SendMessage(hwndResolutions, WM_SETTEXT, 0, (LPARAM)strResolution.Array());
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_RESOLUTION, CBN_EDITCHANGE), (LPARAM)hwndResolutions);
|
|
|
|
HWND hwndFPS = GetDlgItem(hwnd, IDC_FPS);
|
|
SetWindowText(hwndFPS, FloatString(10000000.0/double(frameInterval)));
|
|
}
|
|
else
|
|
{
|
|
SendMessage(hwndResolutions, CB_SETCURSEL, 0, 0);
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_RESOLUTION, CBN_SELCHANGE), (LPARAM)hwndResolutions);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// get audio devices
|
|
|
|
bAudioDevicesPresent = FillOutListOfDevices(hwndAudioList, CLSID_AudioInputDeviceCategory, &configData->audioNameList, &configData->audioIDList);
|
|
|
|
if (configData->bDeviceHasAudio) {
|
|
CTSTR lpName = PluginStr("DeviceSelection.UseDeviceAudio");
|
|
|
|
SendMessage(hwndAudioList, CB_INSERTSTRING, 0, (LPARAM)lpName);
|
|
configData->audioNameList.Insert(0, lpName);
|
|
configData->audioIDList.Insert(0, NULL);
|
|
}
|
|
|
|
CTSTR pDisabled = Str("Disable");
|
|
SendMessage(hwndAudioList, CB_INSERTSTRING, 0, (LPARAM)pDisabled);
|
|
configData->audioNameList.Insert(0, pDisabled);
|
|
configData->audioIDList.Insert(0, TEXT("Disabled"));
|
|
|
|
EnableWindow(hwndAudioList, bAudioDevicesPresent);
|
|
|
|
if (!bAudioDevicesPresent)
|
|
EnableWindow(GetDlgItem(hwnd, IDC_CONFIGAUDIO), FALSE);
|
|
|
|
bool bHasAudio = configData->bDeviceHasAudio;
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_PLAYDESKTOPSOUND), bHasAudio);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_OUTPUTSOUND), bHasAudio);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_TIMEOFFSET), bHasAudio);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_TIMEOFFSET_EDIT), bHasAudio);
|
|
|
|
if (bHasAudio)
|
|
SendMessage(hwndAudioList, CB_SETCURSEL, 1, 0);
|
|
else
|
|
SendMessage(hwndAudioList, CB_SETCURSEL, 0, 0);
|
|
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_AUDIOLIST, CBN_SELCHANGE), (LPARAM)hwndAudioList);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDC_AUDIOLIST:
|
|
if(HIWORD(wParam) == CBN_SELCHANGE)
|
|
{
|
|
HWND hwndDevices = (HWND)lParam;
|
|
UINT id = (UINT)SendMessage(hwndDevices, CB_GETCURSEL, 0, 0);
|
|
|
|
if (id == CB_ERR || id == 0) {
|
|
EnableWindow(GetDlgItem(hwnd, IDC_CONFIGAUDIO), FALSE);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_PLAYDESKTOPSOUND), FALSE);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_OUTPUTSOUND), FALSE);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_TIMEOFFSET), FALSE);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_TIMEOFFSET_EDIT), FALSE);
|
|
} else {
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
IBaseFilter *filter = GetDeviceByValue(CLSID_AudioInputDeviceCategory,
|
|
L"FriendlyName", configData->audioNameList[id],
|
|
L"DevicePath", configData->audioIDList[id]);
|
|
if (filter) {
|
|
EnableWindow(GetDlgItem(hwnd, IDC_CONFIGAUDIO), TRUE);
|
|
|
|
filter->Release();
|
|
} else {
|
|
EnableWindow(GetDlgItem(hwnd, IDC_CONFIGAUDIO), FALSE);
|
|
}
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_PLAYDESKTOPSOUND), TRUE);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_OUTPUTSOUND), TRUE);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_TIMEOFFSET), TRUE);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_TIMEOFFSET_EDIT), TRUE);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDC_RESOLUTION:
|
|
if(HIWORD(wParam) == CBN_EDITCHANGE || HIWORD(wParam) == CBN_SELCHANGE)
|
|
{
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
HWND hwndResolution = (HWND)lParam;
|
|
HWND hwndFPS = GetDlgItem(hwnd, IDC_FPS);
|
|
|
|
SIZE resolution;
|
|
FPSInfo fpsInfo;
|
|
|
|
SendMessage(hwndFPS, CB_RESETCONTENT, 0, 0);
|
|
if(!GetResolution(hwndResolution, resolution, HIWORD(wParam) == CBN_SELCHANGE) || !configData->GetResolutionFPSInfo(resolution, fpsInfo))
|
|
{
|
|
SetWindowText(hwndFPS, TEXT("0"));
|
|
break;
|
|
}
|
|
|
|
//-------------------------------------------------------
|
|
|
|
double bestFPS = 0.0f;
|
|
UINT64 bestInterval = 0;
|
|
for(UINT i=0; i<fpsInfo.supportedIntervals.Num(); i++)
|
|
{
|
|
double minFPS = 10000000.0/double(fpsInfo.supportedIntervals[i].maxFrameInterval);
|
|
double maxFPS = 10000000.0/double(fpsInfo.supportedIntervals[i].minFrameInterval);
|
|
|
|
// FMB NOTE 05-Feb-15: Override some common fps values to avoid rounding errors (60.002 was displayed instead of 60 fps for the Elgato GCHD60)
|
|
if (fpsInfo.supportedIntervals[i].maxFrameInterval == 400000) minFPS = 25.0;
|
|
if (fpsInfo.supportedIntervals[i].minFrameInterval == 400000) maxFPS = 25.0;
|
|
if (fpsInfo.supportedIntervals[i].maxFrameInterval == 200000) minFPS = 50.0;
|
|
if (fpsInfo.supportedIntervals[i].minFrameInterval == 200000) maxFPS = 50.0;
|
|
if (fpsInfo.supportedIntervals[i].maxFrameInterval == 333333) minFPS = 30.0;
|
|
if (fpsInfo.supportedIntervals[i].minFrameInterval == 333333) maxFPS = 30.0;
|
|
if (fpsInfo.supportedIntervals[i].maxFrameInterval == 166666) minFPS = 60.0;
|
|
if (fpsInfo.supportedIntervals[i].minFrameInterval == 166666) maxFPS = 60.0;
|
|
|
|
String strFPS;
|
|
if(CloseDouble(minFPS, maxFPS))
|
|
strFPS << FloatString(float(minFPS));
|
|
else
|
|
strFPS << FloatString(float(minFPS)) << TEXT("-") << FloatString(float(maxFPS));
|
|
|
|
int id = (int)SendMessage(hwndFPS, CB_FINDSTRINGEXACT, -1, (LPARAM)strFPS.Array());
|
|
if(id == CB_ERR)
|
|
SendMessage(hwndFPS, CB_ADDSTRING, 0, (LPARAM)strFPS.Array());
|
|
|
|
if(bestFPS < minFPS)
|
|
{
|
|
bestFPS = minFPS;
|
|
bestInterval = fpsInfo.supportedIntervals[i].maxFrameInterval;
|
|
}
|
|
if(bestFPS < maxFPS)
|
|
{
|
|
bestFPS = maxFPS;
|
|
bestInterval = fpsInfo.supportedIntervals[i].minFrameInterval;
|
|
}
|
|
}
|
|
|
|
SetWindowText(hwndFPS, FloatString(float(bestFPS)));
|
|
|
|
//-------------------------------------------------------
|
|
|
|
PostMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDC_FPS, CBN_EDITCHANGE), (LPARAM)GetDlgItem(hwnd, IDC_FPS));
|
|
}
|
|
break;
|
|
|
|
case IDC_FPS:
|
|
if(HIWORD(wParam) == CBN_SELCHANGE || HIWORD(wParam) == CBN_EDITCHANGE)
|
|
{
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
SIZE resolution;
|
|
if(!GetResolution(GetDlgItem(hwnd, IDC_RESOLUTION), resolution, FALSE))
|
|
break;
|
|
|
|
FPSInfo fpsInfo;
|
|
if(!configData->GetResolutionFPSInfo(resolution, fpsInfo))
|
|
break;
|
|
|
|
//--------------------------------------------
|
|
|
|
String strFPSVal = GetEditText((HWND)lParam);
|
|
if(schr(strFPSVal, '-'))
|
|
{
|
|
StringList tokens;
|
|
strFPSVal.GetTokenList(tokens, '-', FALSE);
|
|
if(tokens.Num())
|
|
strFPSVal = tokens.Last();
|
|
else
|
|
break;
|
|
}
|
|
|
|
if(!ValidFloatString(strFPSVal))
|
|
break;
|
|
|
|
float fps = strFPSVal.ToFloat();
|
|
INT64 interval = INT64(10000000.0/double(fps));
|
|
|
|
UINT64 bestInterval;
|
|
UINT64 bestDist = 0xFFFFFFFFFFFFFFFFLL;
|
|
for(UINT i=0; i<fpsInfo.supportedIntervals.Num(); i++)
|
|
{
|
|
UINT64 maxDist = (UINT64)_abs64(INT64(fpsInfo.supportedIntervals[i].maxFrameInterval)-interval);
|
|
UINT64 minDist = (UINT64)_abs64(INT64(fpsInfo.supportedIntervals[i].minFrameInterval)-interval);
|
|
|
|
if(maxDist < bestDist)
|
|
{
|
|
bestDist = maxDist;
|
|
bestInterval = fpsInfo.supportedIntervals[i].maxFrameInterval;
|
|
}
|
|
if(minDist < bestDist)
|
|
{
|
|
bestDist = minDist;
|
|
bestInterval = fpsInfo.supportedIntervals[i].minFrameInterval;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------
|
|
|
|
HWND hwndPreferredList = GetDlgItem(hwnd, IDC_PREFERREDOUTPUT);
|
|
SendMessage(hwndPreferredList, CB_RESETCONTENT, 0, 0);
|
|
|
|
UINT preferredType = (UINT)configData->data->GetInt(TEXT("preferredType"), -1);
|
|
|
|
List<VideoOutputType> types;
|
|
if(GetVideoOutputTypes(configData->outputList, resolution.cx, resolution.cy, bestInterval, types))
|
|
{
|
|
int preferredID = -1;
|
|
|
|
for(UINT i=0; i<types.Num(); i++)
|
|
{
|
|
CTSTR lpName = EnumToName[(UINT)types[i]];
|
|
|
|
int id = (int)SendMessage(hwndPreferredList, CB_ADDSTRING, 0, (LPARAM)lpName);
|
|
SendMessage(hwndPreferredList, CB_SETITEMDATA, id, (LPARAM)types[i]);
|
|
|
|
if((UINT)types[i] == preferredType)
|
|
{
|
|
SendMessage(hwndPreferredList, CB_SETCURSEL, id, 0);
|
|
preferredID = id;
|
|
}
|
|
}
|
|
|
|
if(preferredID == -1)
|
|
SendMessage(hwndPreferredList, CB_SETCURSEL, 0, 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDC_CONFIG:
|
|
case IDC_CONFIGAUDIO:
|
|
case IDC_CROSSBAR:
|
|
{
|
|
UINT id;
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
IBaseFilter *filter;
|
|
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDC_CONFIG:
|
|
id = (UINT)SendMessage(GetDlgItem(hwnd, IDC_DEVICELIST), CB_GETCURSEL, 0, 0);
|
|
if(id != CB_ERR) OpenPropertyPagesByName(hwnd, configData->deviceNameList[id], configData->deviceIDList[id], CLSID_VideoInputDeviceCategory);
|
|
break;
|
|
case IDC_CONFIGAUDIO:
|
|
id = (UINT)SendMessage(GetDlgItem(hwnd, IDC_AUDIOLIST), CB_GETCURSEL, 0, 0);
|
|
if(id != CB_ERR) OpenPropertyPagesByName(hwnd, configData->audioNameList[id], configData->audioIDList[id], CLSID_AudioInputDeviceCategory);
|
|
break;
|
|
|
|
case IDC_CROSSBAR:
|
|
id = (UINT)SendMessage(GetDlgItem(hwnd, IDC_DEVICELIST), CB_GETCURSEL, 0, 0);
|
|
if (id == CB_ERR)
|
|
break;
|
|
|
|
filter = GetDeviceByValue(CLSID_VideoInputDeviceCategory,
|
|
L"FriendlyName", configData->deviceNameList[id],
|
|
L"DevicePath", configData->deviceIDList[id]);
|
|
|
|
if (filter) {
|
|
String strDeviceName = configData->deviceNameList[id];
|
|
bool bIsElworkaroundo = sstri(strDeviceName, TEXT("elgato")) != NULL; //HAHAHAHAHAHAHA HOW F***ING WONDERFUL. 自殺したい
|
|
|
|
IAMCrossbar *crossbar = bIsElworkaroundo ? NULL : GetFilterCrossbar(filter);
|
|
if (crossbar) {
|
|
OpenPropertyPages(hwnd, crossbar);
|
|
crossbar->Release();
|
|
}
|
|
|
|
filter->Release();
|
|
}
|
|
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IDOK:
|
|
{
|
|
UINT deviceID = (UINT)SendMessage(GetDlgItem(hwnd, IDC_DEVICELIST), CB_GETCURSEL, 0, 0);
|
|
if(deviceID == CB_ERR)
|
|
break;
|
|
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
SIZE resolution;
|
|
if(!GetResolution(GetDlgItem(hwnd, IDC_RESOLUTION), resolution, FALSE))
|
|
{
|
|
OBSMessageBox(hwnd, PluginStr("DeviceSelection.InvalidResolution"), NULL, 0);
|
|
break;
|
|
}
|
|
|
|
String strDevice = configData->deviceIDList[deviceID];
|
|
String strFPS = GetEditText(GetDlgItem(hwnd, IDC_FPS));
|
|
if(schr(strFPS, '-'))
|
|
{
|
|
StringList tokens;
|
|
strFPS.GetTokenList(tokens, '-', FALSE);
|
|
if(tokens.Num())
|
|
strFPS = tokens.Last();
|
|
else
|
|
break;
|
|
}
|
|
|
|
float fpsVal = 120.0f;
|
|
if(ValidFloatString(strFPS))
|
|
fpsVal = strFPS.ToFloat();
|
|
|
|
UINT64 frameInterval = UINT64(10000000.0/double(fpsVal));
|
|
|
|
if(strFPS == TEXT("0") || fpsVal == 0.0)
|
|
{
|
|
OBSMessageBox(hwnd, PluginStr("DeviceSelection.UnsupportedResolution"), NULL, 0);
|
|
break;
|
|
}
|
|
|
|
if(configData->bCreating)
|
|
{
|
|
bool bFoundGlobal;
|
|
if(CurrentDeviceExists(strDevice, configData->bGlobalSource, bFoundGlobal))
|
|
{
|
|
if(bFoundGlobal)
|
|
OBSMessageBox(hwnd, PluginStr("DeviceSelection.GlobalExists"), NULL, 0);
|
|
else
|
|
{
|
|
if(configData->bGlobalSource)
|
|
OBSMessageBox(hwnd, PluginStr("DeviceSelection.ExistsSomewhere"), NULL, 0);
|
|
else
|
|
OBSMessageBox(hwnd, PluginStr("DeviceSelection.ExistsInScene"), NULL, 0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------
|
|
|
|
BOOL bFlip = SendMessage(GetDlgItem(hwnd, IDC_FLIPIMAGE), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
BOOL bFlipHorizontal = SendMessage(GetDlgItem(hwnd, IDC_FLIPIMAGEH), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
BOOL bCustomResolution = SendMessage(GetDlgItem(hwnd, IDC_CUSTOMRESOLUTION), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
BOOL bUsePointFiltering = SendMessage(GetDlgItem(hwnd, IDC_POINTFILTERING), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
BOOL bPreserveSourceSize = SendMessage(GetDlgItem(hwnd, IDC_PRESERVESIZE), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
BOOL fullRange = SendMessage(GetDlgItem(hwnd, IDC_FULLRANGE), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
int colorSpace = SendMessage(GetDlgItem(hwnd, IDC_COLORSPACE), CB_GETCURSEL, 0, 0);
|
|
|
|
int deintId = (int)SendMessage(GetDlgItem(hwnd, IDC_DEINTERLACELIST), CB_GETCURSEL, 0, 0);
|
|
deinterlacingConfig = deinterlacerConfigs[deintId];
|
|
bool tff = SendMessage(GetDlgItem(hwnd, IDC_TFF), BM_GETCHECK, 0, 0) == BST_CHECKED,
|
|
bff = SendMessage(GetDlgItem(hwnd, IDC_BFF), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
deinterlacingConfig.fieldOrder = tff ? FIELD_ORDER_TFF : (bff ? FIELD_ORDER_BFF : FIELD_ORDER_NONE);
|
|
deinterlacingConfig.processor = SendMessage(GetDlgItem(hwnd, IDC_GPUDEINT), BM_GETCHECK, 0, 0) == BST_CHECKED ? DEINTERLACING_PROCESSOR_GPU : DEINTERLACING_PROCESSOR_CPU;
|
|
|
|
configData->data->SetString(TEXT("device"), GetCBText(GetDlgItem(hwnd, IDC_DEVICELIST), deviceID));
|
|
configData->data->SetString(TEXT("deviceName"), configData->deviceNameList[deviceID]);
|
|
configData->data->SetString(TEXT("deviceID"), configData->deviceIDList[deviceID]);
|
|
|
|
configData->data->SetInt(TEXT("customResolution"), bCustomResolution);
|
|
if(resolution.cx != 0 && resolution.cy != 0) {
|
|
configData->data->SetFloat(TEXT("scaleFactor_x"), (float)resolution.cx / configData->data->GetFloat(TEXT("resolutionWidth"), 1.0f));
|
|
configData->data->SetFloat(TEXT("scaleFactor_y"), (float)resolution.cy / configData->data->GetFloat(TEXT("resolutionHeight"), 1.0f));
|
|
}
|
|
configData->data->SetInt(TEXT("resolutionWidth"), resolution.cx);
|
|
configData->data->SetInt(TEXT("resolutionHeight"), resolution.cy);
|
|
configData->data->SetInt(TEXT("frameInterval"), UINT(frameInterval));
|
|
configData->data->SetInt(TEXT("flipImage"), bFlip);
|
|
configData->data->SetInt(TEXT("flipImageHorizontal"), bFlipHorizontal);
|
|
configData->data->SetInt(TEXT("usePointFiltering"), bUsePointFiltering);
|
|
configData->data->SetInt(TEXT("gamma"), (int)SendMessage(GetDlgItem(hwnd, IDC_GAMMA), TBM_GETPOS, 0, 0));
|
|
|
|
configData->data->SetInt(TEXT("preserveSourceSize"), bPreserveSourceSize);
|
|
|
|
configData->data->SetInt(TEXT("deinterlacingType"), deinterlacingConfig.type);
|
|
configData->data->SetInt(TEXT("deinterlacingFieldOrder"), deinterlacingConfig.fieldOrder);
|
|
configData->data->SetInt(TEXT("deinterlacingProcessor"), deinterlacingConfig.processor);
|
|
configData->data->SetInt(TEXT("deinterlacingDoublesFramerate"), deinterlacingConfig.doublesFramerate);
|
|
|
|
configData->data->SetInt(TEXT("fullrange"), fullRange ? 1 : 0);
|
|
configData->data->SetInt(TEXT("colorspace"), colorSpace);
|
|
|
|
//------------------------------------------
|
|
|
|
BOOL bUDMError;
|
|
UINT opacity = (UINT)SendMessage(GetDlgItem(hwnd, IDC_OPACITY), UDM_GETPOS32, 0, (LPARAM)&bUDMError);
|
|
if(bUDMError) opacity = 100;
|
|
|
|
configData->data->SetInt(TEXT("opacity"), opacity);
|
|
|
|
//------------------------------------------
|
|
|
|
UINT preferredType = -1;
|
|
int id = (int)SendMessage(GetDlgItem(hwnd, IDC_PREFERREDOUTPUT), CB_GETCURSEL, 0, 0);
|
|
if(id != -1)
|
|
preferredType = (UINT)SendMessage(GetDlgItem(hwnd, IDC_PREFERREDOUTPUT), CB_GETITEMDATA, id, 0);
|
|
|
|
BOOL bUsePreferredType = preferredType != -1 && SendMessage(GetDlgItem(hwnd, IDC_USEPREFERREDOUTPUT), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
configData->data->SetInt(TEXT("usePreferredType"), bUsePreferredType);
|
|
configData->data->SetInt(TEXT("preferredType"), preferredType);
|
|
|
|
bool bUseBuffering = SendMessage(GetDlgItem(hwnd, IDC_USEBUFFERING), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
configData->data->SetInt(TEXT("useBuffering"), bUseBuffering);
|
|
|
|
DWORD bufferTime = (DWORD)SendMessage(GetDlgItem(hwnd, IDC_DELAY), UDM_GETPOS32, 0, 0);
|
|
configData->data->SetInt(TEXT("bufferTime"), bufferTime);
|
|
|
|
//------------------------------------------
|
|
|
|
int soundOutputType = 0;
|
|
UINT audioDeviceID = (UINT)SendMessage(GetDlgItem(hwnd, IDC_AUDIOLIST), CB_GETCURSEL, 0, 0);
|
|
|
|
if (audioDeviceID != CB_ERR) {
|
|
if (bAudioDevicesPresent) {
|
|
String strAudioDevice = GetCBText(GetDlgItem(hwnd, IDC_AUDIOLIST), audioDeviceID);
|
|
configData->data->SetString(TEXT("audioDevice"), strAudioDevice);
|
|
configData->data->SetString(TEXT("audioDeviceName"), configData->audioNameList[audioDeviceID]);
|
|
configData->data->SetString(TEXT("audioDeviceID"), configData->audioIDList[audioDeviceID]);
|
|
}
|
|
|
|
configData->data->SetInt(TEXT("forceCustomAudioDevice"), (!configData->bDeviceHasAudio || audioDeviceID > 1));
|
|
|
|
if(SendMessage(GetDlgItem(hwnd, IDC_OUTPUTSOUND), BM_GETCHECK, 0, 0) == BST_CHECKED)
|
|
soundOutputType = 1;
|
|
else if(SendMessage(GetDlgItem(hwnd, IDC_PLAYDESKTOPSOUND), BM_GETCHECK, 0, 0) == BST_CHECKED)
|
|
soundOutputType = 2;
|
|
}
|
|
|
|
configData->data->SetInt(TEXT("soundOutputType"), soundOutputType);
|
|
|
|
int soundTimeOffset = (int)SendMessage(GetDlgItem(hwnd, IDC_TIMEOFFSET), UDM_GETPOS32, 0, 0);
|
|
configData->data->SetInt(TEXT("soundTimeOffset"), soundTimeOffset);
|
|
|
|
float fVol = GetVolumeControlValue(GetDlgItem(hwnd, IDC_VOLUME));
|
|
configData->data->SetFloat(TEXT("volume"), fVol);
|
|
BOOL bUseAudioRender = SendMessage(GetDlgItem(hwnd, IDC_USEAUDIORENDER), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
configData->data->SetInt(TEXT("useAudioRender"), bUseAudioRender);
|
|
|
|
//------------------------------------------
|
|
|
|
BOOL bUseChromaKey = SendMessage(GetDlgItem(hwnd, IDC_USECHROMAKEY), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
DWORD color = CCGetColor(GetDlgItem(hwnd, IDC_COLOR));
|
|
|
|
UINT keySimilarity = (UINT)SendMessage(GetDlgItem(hwnd, IDC_BASETHRESHOLD), UDM_GETPOS32, 0, (LPARAM)&bUDMError);
|
|
if(bUDMError) keySimilarity = 0;
|
|
|
|
UINT keyBlend = (UINT)SendMessage(GetDlgItem(hwnd, IDC_BLEND), UDM_GETPOS32, 0, (LPARAM)&bUDMError);
|
|
if(bUDMError) keyBlend = 10;
|
|
|
|
int keySpillReduction = (int)SendMessage(GetDlgItem(hwnd, IDC_SPILLREDUCTION), UDM_GETPOS32, 0, (LPARAM)&bUDMError);
|
|
if(bUDMError) keySpillReduction = 0;
|
|
|
|
configData->data->SetInt(TEXT("useChromaKey"), bUseChromaKey);
|
|
configData->data->SetInt(TEXT("keyColor"), color);
|
|
configData->data->SetInt(TEXT("keySimilarity"), keySimilarity);
|
|
configData->data->SetInt(TEXT("keyBlend"), keyBlend);
|
|
configData->data->SetInt(TEXT("keySpillReduction"), keySpillReduction);
|
|
}
|
|
|
|
case IDCANCEL:
|
|
if(LOWORD(wParam) == IDCANCEL)
|
|
{
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
|
|
if(source)
|
|
{
|
|
source->SetInt(TEXT("bufferTime"), configData->data->GetInt(TEXT("bufferTime"), 0));
|
|
|
|
source->SetInt(TEXT("timeOffset"), configData->data->GetInt(TEXT("soundTimeOffset"), 0));
|
|
source->SetFloat(TEXT("volume"), configData->data->GetFloat(TEXT("volume"), 1.0f));
|
|
|
|
source->SetInt(TEXT("flipImage"), configData->data->GetInt(TEXT("flipImage"), 0));
|
|
source->SetInt(TEXT("flipImageHorizontal"), configData->data->GetInt(TEXT("flipImageHorizontal"), 0));
|
|
source->SetInt(TEXT("usePointFiltering"), configData->data->GetInt(TEXT("usePointFiltering"), 0));
|
|
source->SetInt(TEXT("opacity"), configData->data->GetInt(TEXT("opacity"), 100));
|
|
|
|
source->SetInt(TEXT("useChromaKey"), configData->data->GetInt(TEXT("useChromaKey"), 0));
|
|
source->SetInt(TEXT("keyColor"), configData->data->GetInt(TEXT("keyColor"), 0xFFFFFFFF));
|
|
source->SetInt(TEXT("keySimilarity"), configData->data->GetInt(TEXT("keySimilarity"), 0));
|
|
source->SetInt(TEXT("keyBlend"), configData->data->GetInt(TEXT("keyBlend"), 80));
|
|
source->SetInt(TEXT("keySpillReduction"), configData->data->GetInt(TEXT("keySpillReduction"), 50));
|
|
source->SetInt(TEXT("gamma"), configData->data->GetInt(TEXT("gamma"), 100));
|
|
source->SetInt(TEXT("deinterlacingType"), configData->data->GetInt(TEXT("deinterlacingType"), 0));
|
|
source->SetInt(TEXT("deinterlacingFieldOrder"), configData->data->GetInt(TEXT("deinterlacingFieldOrder"), 0));
|
|
source->SetInt(TEXT("deinterlacingProcessor"), configData->data->GetInt(TEXT("deinterlacingProcessor"), 0));
|
|
source->SetInt(TEXT("deinterlacingDoublesFramerate"), configData->data->GetInt(TEXT("deinterlacingDoublesFramerate"), 0));
|
|
|
|
source->SetInt(TEXT("preserveSourceSize"), configData->data->GetInt(TEXT("preserveSourceSize"), 0));
|
|
source->SetInt(TEXT("useAudioRender"), configData->data->GetInt(TEXT("useAudioRender"), 0));
|
|
}
|
|
}
|
|
|
|
EndDialog(hwnd, LOWORD(wParam));
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
bool STDCALL ConfigureDShowSource(XElement *element, bool bCreating)
|
|
{
|
|
if(!element)
|
|
{
|
|
AppWarning(TEXT("ConfigureDShowSource: NULL element"));
|
|
return false;
|
|
}
|
|
|
|
XElement *data = element->GetElement(TEXT("data"));
|
|
if(!data)
|
|
data = element->CreateElement(TEXT("data"));
|
|
|
|
ConfigDialogData configData;
|
|
configData.lpName = element->GetName();
|
|
configData.data = data;
|
|
configData.bGlobalSource = (scmpi(element->GetParent()->GetName(), TEXT("global sources")) == 0);
|
|
configData.bCreating = bCreating;
|
|
|
|
if(OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_CONFIG), API->GetMainWindow(), ConfigureDialogProc, (LPARAM)&configData) == IDOK)
|
|
{
|
|
bool bPreserveSourceSize = data->GetInt(TEXT("preserveSourceSize")) != 0;
|
|
float scaleFactor_x = 1.0f, scaleFactor_y = 1.0f;
|
|
|
|
if(bPreserveSourceSize) {
|
|
scaleFactor_x = data->GetFloat(TEXT("scaleFactor_x"), 1.0f);
|
|
scaleFactor_y = data->GetFloat(TEXT("scaleFactor_y"), 1.0f);
|
|
}
|
|
|
|
element->SetFloat(TEXT("cx"), data->GetFloat(TEXT("resolutionWidth")) / scaleFactor_x);
|
|
element->SetFloat(TEXT("cy"), data->GetFloat(TEXT("resolutionHeight")) / scaleFactor_y);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
ImageSource* STDCALL CreateDShowSource(XElement *data)
|
|
{
|
|
DeviceSource *source = new DeviceSource;
|
|
if(!source->Init(data))
|
|
{
|
|
delete source;
|
|
return NULL;
|
|
}
|
|
|
|
return source;
|
|
}
|
|
|
|
|
|
bool LoadPlugin()
|
|
{
|
|
InitColorControl(hinstMain);
|
|
InitVolumeControl(hinstMain);
|
|
InitVolumeMeter(hinstMain);
|
|
|
|
pluginLocale = new LocaleStringLookup;
|
|
|
|
if(!pluginLocale->LoadStringFile(TEXT("plugins/DShowPlugin/locale/en.txt")))
|
|
AppWarning(TEXT("Could not open locale string file '%s'"), TEXT("plugins/DShowPlugin/locale/en.txt"));
|
|
|
|
if(scmpi(API->GetLanguage(), TEXT("en")) != 0)
|
|
{
|
|
String pluginStringFile;
|
|
pluginStringFile << TEXT("plugins/DShowPlugin/locale/") << API->GetLanguage() << TEXT(".txt");
|
|
if(!pluginLocale->LoadStringFile(pluginStringFile))
|
|
AppWarning(TEXT("Could not open locale string file '%s'"), pluginStringFile.Array());
|
|
}
|
|
|
|
API->RegisterImageSourceClass(DSHOW_CLASSNAME, PluginStr("ClassName"), (OBSCREATEPROC)CreateDShowSource, (OBSCONFIGPROC)ConfigureDShowSource);
|
|
|
|
return true;
|
|
}
|
|
|
|
void UnloadPlugin()
|
|
{
|
|
delete pluginLocale;
|
|
}
|
|
|
|
CTSTR GetPluginName()
|
|
{
|
|
return PluginStr("Plugin.Name");
|
|
}
|
|
|
|
CTSTR GetPluginDescription()
|
|
{
|
|
return PluginStr("Plugin.Description");
|
|
}
|
|
|
|
|
|
BOOL CALLBACK DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpBla)
|
|
{
|
|
if (dwReason == DLL_PROCESS_ATTACH)
|
|
{
|
|
#if defined _M_X64 && _MSC_VER == 1800
|
|
//workaround AVX2 bug in VS2013, http://connect.microsoft.com/VisualStudio/feedback/details/811093
|
|
_set_FMA3_enable(0);
|
|
#endif
|
|
hinstMain = hInst;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|