1109 lines
42 KiB
C++
1109 lines
42 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"
|
|
|
|
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;
|
|
|
|
|
|
#define DSHOW_CLASSNAME TEXT("DeviceCapture")
|
|
|
|
|
|
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("device")), 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;
|
|
}
|
|
|
|
|
|
IBaseFilter* GetDeviceByName(CTSTR lpName)
|
|
{
|
|
ICreateDevEnum *deviceEnum;
|
|
IEnumMoniker *videoDeviceEnum;
|
|
|
|
HRESULT err;
|
|
err = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, IID_ICreateDevEnum, (void**)&deviceEnum);
|
|
if(FAILED(err))
|
|
{
|
|
AppWarning(TEXT("GetDeviceByName: CoCreateInstance for the device enum failed, result = %08lX"), err);
|
|
return NULL;
|
|
}
|
|
|
|
err = deviceEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &videoDeviceEnum, 0);
|
|
if(FAILED(err))
|
|
{
|
|
AppWarning(TEXT("GetDeviceByName: 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;
|
|
|
|
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;
|
|
valueThingy.vt = VT_BSTR;
|
|
|
|
err = propertyData->Read(L"FriendlyName", &valueThingy, NULL);
|
|
SafeRelease(propertyData);
|
|
|
|
if(SUCCEEDED(err))
|
|
{
|
|
String strDeviceName = (CWSTR)valueThingy.bstrVal;
|
|
if(strDeviceName == lpName)
|
|
{
|
|
IBaseFilter *filter;
|
|
err = deviceInfo->BindToObject(NULL, 0, IID_IBaseFilter, (void**)&filter);
|
|
if(FAILED(err))
|
|
{
|
|
AppWarning(TEXT("GetDeviceByName: deviceInfo->BindToObject failed, result = %08lX"), err);
|
|
return NULL;
|
|
}
|
|
|
|
SafeRelease(deviceInfo);
|
|
SafeRelease(videoDeviceEnum);
|
|
|
|
return filter;
|
|
}
|
|
}
|
|
}
|
|
|
|
SafeRelease(deviceInfo);
|
|
}
|
|
|
|
SafeRelease(videoDeviceEnum);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void FillOutListOfVideoDevices(HWND hwndCombo)
|
|
{
|
|
SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
|
|
|
|
ICreateDevEnum *deviceEnum;
|
|
IEnumMoniker *videoDeviceEnum;
|
|
|
|
HRESULT err;
|
|
err = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, IID_ICreateDevEnum, (void**)&deviceEnum);
|
|
if(FAILED(err))
|
|
{
|
|
AppWarning(TEXT("FillOutListOfVideoDevices: CoCreateInstance for the device enum failed, result = %08lX"), err);
|
|
return;
|
|
}
|
|
|
|
err = deviceEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &videoDeviceEnum, 0);
|
|
if(FAILED(err))
|
|
{
|
|
AppWarning(TEXT("FillOutListOfVideoDevices: deviceEnum->CreateClassEnumerator failed, result = %08lX"), err);
|
|
deviceEnum->Release();
|
|
return;
|
|
}
|
|
|
|
SafeRelease(deviceEnum);
|
|
|
|
if(err == S_FALSE) //no devices
|
|
return;
|
|
|
|
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;
|
|
valueThingy.vt = VT_BSTR;
|
|
|
|
err = propertyData->Read(L"FriendlyName", &valueThingy, NULL);
|
|
SafeRelease(propertyData);
|
|
|
|
if(SUCCEEDED(err))
|
|
{
|
|
IBaseFilter *filter;
|
|
err = deviceInfo->BindToObject(NULL, 0, IID_IBaseFilter, (void**)&filter);
|
|
if(SUCCEEDED(err))
|
|
{
|
|
String strDeviceName = (CWSTR)valueThingy.bstrVal;
|
|
if(SendMessage(hwndCombo, CB_FINDSTRINGEXACT, -1, (LPARAM)strDeviceName.Array()) == CB_ERR)
|
|
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)strDeviceName.Array());
|
|
|
|
SafeRelease(filter);
|
|
}
|
|
}
|
|
}
|
|
|
|
SafeRelease(deviceInfo);
|
|
}
|
|
|
|
SafeRelease(videoDeviceEnum);
|
|
}
|
|
|
|
IPin* GetOutputPin(IBaseFilter *filter)
|
|
{
|
|
IPin *foundPin = NULL;
|
|
|
|
if(filter)
|
|
{
|
|
IEnumPins *pins;
|
|
if(SUCCEEDED(filter->EnumPins(&pins)))
|
|
{
|
|
IPin *curPin;
|
|
ULONG num;
|
|
while(pins->Next(1, &curPin, &num) == S_OK)
|
|
{
|
|
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)
|
|
{
|
|
SafeRelease(propertySet);
|
|
SafeRelease(pins);
|
|
return curPin;
|
|
}
|
|
}
|
|
|
|
SafeRelease(propertySet);
|
|
}
|
|
}
|
|
}
|
|
|
|
SafeRelease(curPin);
|
|
}
|
|
|
|
SafeRelease(pins);
|
|
}
|
|
}
|
|
|
|
return foundPin;
|
|
}
|
|
|
|
void GetOutputList(IPin *curPin, List<MediaOutputInfo> &outputInfoList)
|
|
{
|
|
IAMStreamConfig *config;
|
|
if(SUCCEEDED(curPin->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;
|
|
if(SUCCEEDED(config->GetStreamCaps(i, &pMT, capsData)))
|
|
{
|
|
VideoOutputType type = GetVideoOutputTypeFromGUID(pMT->subtype);
|
|
|
|
if(pMT->formattype == FORMAT_VideoInfo)
|
|
{
|
|
VIDEO_STREAM_CONFIG_CAPS *pVSCC = reinterpret_cast<VIDEO_STREAM_CONFIG_CAPS*>(capsData);
|
|
VIDEOINFOHEADER *pVih = reinterpret_cast<VIDEOINFOHEADER*>(pMT->pbFormat);
|
|
|
|
bool bUsingFourCC = false;
|
|
if(type == VideoOutputType_None)
|
|
{
|
|
type = GetVideoOutputTypeFromFourCC(pVih->bmiHeader.biCompression);
|
|
bUsingFourCC = true;
|
|
}
|
|
|
|
if(type != VideoOutputType_None)
|
|
{
|
|
MediaOutputInfo *outputInfo = outputInfoList.CreateNew();
|
|
outputInfo->mediaType = pMT;
|
|
outputInfo->videoType = type;
|
|
outputInfo->minFPS = 1000.0/(double(pVSCC->MaxFrameInterval)/10000.0);
|
|
outputInfo->maxFPS = 1000.0/(double(pVSCC->MinFrameInterval)/10000.0);
|
|
outputInfo->minCX = pVSCC->MinOutputSize.cx;
|
|
outputInfo->maxCX = pVSCC->MaxOutputSize.cx;
|
|
outputInfo->minCY = pVSCC->MinOutputSize.cy;
|
|
outputInfo->maxCY = pVSCC->MaxOutputSize.cy;
|
|
outputInfo->bUsingFourCC = bUsingFourCC;
|
|
|
|
//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
|
|
{
|
|
FreeMediaType(*pMT);
|
|
CoTaskMemFree(pMT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FreeMediaType(*pMT);
|
|
CoTaskMemFree(pMT);
|
|
}
|
|
}
|
|
}
|
|
|
|
Free(capsData);
|
|
}
|
|
|
|
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 FPSInfo
|
|
{
|
|
int minFPS, maxFPS;
|
|
};
|
|
|
|
|
|
bool GetClosestResolution(List<MediaOutputInfo> &outputList, SIZE &resolution, UINT &fps)
|
|
{
|
|
LONG width, height;
|
|
UINT internalFPS = API->GetMaxFPS();
|
|
API->GetBaseSize((UINT&)width, (UINT&)height);
|
|
|
|
LONG bestDistance = 0x7FFFFFFF;
|
|
SIZE bestSize;
|
|
UINT minFPS = 0;
|
|
UINT bestFPS = 0;
|
|
|
|
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;
|
|
if((totalDist <= bestDistance) || (totalDist == bestDistance && outputInfo.maxFPS > bestFPS))
|
|
{
|
|
bestDistance = totalDist;
|
|
bestSize.cx = outputWidth;
|
|
bestSize.cy = outputHeight;
|
|
minFPS = (UINT)outputInfo.minFPS;
|
|
bestFPS = (UINT)outputInfo.maxFPS;
|
|
}
|
|
|
|
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(internalFPS < minFPS)
|
|
fps = minFPS;
|
|
else if(internalFPS > bestFPS)
|
|
fps = bestFPS;
|
|
else
|
|
fps = internalFPS;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
struct ConfigDialogData
|
|
{
|
|
CTSTR lpName;
|
|
XElement *data;
|
|
List<MediaOutputInfo> outputList;
|
|
List<SIZE> resolutions;
|
|
bool bGlobalSource;
|
|
bool bCreating;
|
|
|
|
~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.minFPS = -1;
|
|
fpsInfo.maxFPS = -1;
|
|
|
|
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;
|
|
|
|
int minFPS = int(outputInfo.minFPS+0.5);
|
|
int maxFPS = int(outputInfo.maxFPS+0.5);
|
|
|
|
if(fpsInfo.minFPS == -1)
|
|
{
|
|
fpsInfo.minFPS = minFPS;
|
|
fpsInfo.maxFPS = maxFPS;
|
|
}
|
|
else
|
|
{
|
|
fpsInfo.minFPS = MIN(fpsInfo.minFPS, minFPS);
|
|
fpsInfo.maxFPS = MAX(fpsInfo.maxFPS, maxFPS);
|
|
}
|
|
}
|
|
}
|
|
|
|
return fpsInfo.minFPS != -1;
|
|
}
|
|
};
|
|
|
|
|
|
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() || strCX.IsEmpty() || !ValidIntString(strCX) || !ValidIntString(strCY))
|
|
return false;
|
|
|
|
UINT cx = strCX.ToInt();
|
|
UINT cy = strCY.ToInt();
|
|
|
|
if(cx < 32 || cy < 32 || cx > 4096 || cy > 4096)
|
|
return false;
|
|
|
|
resolution.cx = cx;
|
|
resolution.cy = cy;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
INT_PTR CALLBACK ConfigureDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
|
|
|
|
ConfigDialogData *configData = (ConfigDialogData*)lParam;
|
|
|
|
HWND hwndDeviceList = GetDlgItem(hwnd, IDC_DEVICELIST);
|
|
HWND hwndResolutionList = GetDlgItem(hwnd, IDC_RESOLUTION);
|
|
HWND hwndFPS = GetDlgItem(hwnd, IDC_FPS);
|
|
HWND hwndFlip = GetDlgItem(hwnd, IDC_FLIPIMAGE);
|
|
|
|
//------------------------------------------
|
|
|
|
bool bFlipVertical = configData->data->GetInt(TEXT("flipImage")) != 0;
|
|
SendMessage(hwndFlip, BM_SETCHECK, bFlipVertical ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
|
|
//------------------------------------------
|
|
|
|
String strDevice = configData->data->GetString(TEXT("device"));
|
|
UINT cx = configData->data->GetInt(TEXT("resolutionWidth"));
|
|
UINT cy = configData->data->GetInt(TEXT("resolutionHeight"));
|
|
UINT fps = configData->data->GetInt(TEXT("fps"));
|
|
|
|
BOOL bCustomResolution = configData->data->GetInt(TEXT("customResolution"));
|
|
SendMessage(GetDlgItem(hwnd, IDC_CUSTOMRESOLUTION), BM_SETCHECK, bCustomResolution ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
|
|
LocalizeWindow(hwnd, pluginLocale);
|
|
FillOutListOfVideoDevices(GetDlgItem(hwnd, IDC_DEVICELIST));
|
|
|
|
UINT deviceID = CB_ERR;
|
|
if(strDevice.IsValid() && cx > 0 && cy > 0 && fps > 0)
|
|
deviceID = (UINT)SendMessage(hwndDeviceList, CB_FINDSTRINGEXACT, -1, (LPARAM)strDevice.Array());
|
|
|
|
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);
|
|
|
|
SendMessage(hwndFPS, UDM_SETPOS32, 0, (LPARAM)fps);
|
|
}
|
|
}
|
|
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_CUSTOMRESOLUTION, BN_CLICKED), (LPARAM)GetDlgItem(hwnd, IDC_CUSTOMRESOLUTION));
|
|
|
|
//------------------------------------------
|
|
|
|
BOOL bUseColorKey = configData->data->GetInt(TEXT("useColorKey"), 0);
|
|
DWORD keyColor = configData->data->GetInt(TEXT("keyColor"), 0xFFFFFFFF);
|
|
UINT similarity = configData->data->GetInt(TEXT("keySimilarity"), 0);
|
|
UINT blend = configData->data->GetInt(TEXT("keyBlend"), 10);
|
|
UINT gamma = configData->data->GetInt(TEXT("keyGamma"), 0);
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_USECOLORKEY), BM_SETCHECK, bUseColorKey ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
CCSetColor(GetDlgItem(hwnd, IDC_COLOR), keyColor);
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_BASETHRESHOLD), UDM_SETRANGE32, 0, 100);
|
|
SendMessage(GetDlgItem(hwnd, IDC_BASETHRESHOLD), UDM_SETPOS32, 0, similarity);
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_BLEND), UDM_SETRANGE32, 0, 100);
|
|
SendMessage(GetDlgItem(hwnd, IDC_BLEND), UDM_SETPOS32, 0, blend);
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_GAMMA), UDM_SETRANGE32, -75, 75);
|
|
SendMessage(GetDlgItem(hwnd, IDC_GAMMA), UDM_SETPOS32, 0, gamma);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_COLOR), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SELECTCOLOR), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BASETHRESHOLD_EDIT), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BASETHRESHOLD), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BLEND_EDIT), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BLEND), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_GAMMA_EDIT), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_GAMMA), bUseColorKey);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
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);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_FPS_EDIT), bCustomResolution);
|
|
break;
|
|
}
|
|
|
|
case IDC_USECOLORKEY:
|
|
{
|
|
HWND hwndUseColorKey = (HWND)lParam;
|
|
BOOL bUseColorKey = 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("useColorKey"), bUseColorKey);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_COLOR), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SELECTCOLOR), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BASETHRESHOLD_EDIT), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BASETHRESHOLD), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BLEND_EDIT), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BLEND), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_GAMMA_EDIT), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_GAMMA), bUseColorKey);
|
|
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:
|
|
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;
|
|
|
|
source->SetInt(TEXT("flipImage"), bFlipImage);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDC_BASETHRESHOLD_EDIT:
|
|
case IDC_BLEND_EDIT:
|
|
case IDC_GAMMA_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_BASETHRESHOLD_EDIT: hwndVal = GetDlgItem(hwnd, IDC_BASETHRESHOLD); break;
|
|
case IDC_BLEND_EDIT: hwndVal = GetDlgItem(hwnd, IDC_BLEND); break;
|
|
case IDC_GAMMA_EDIT: hwndVal = GetDlgItem(hwnd, IDC_GAMMA); break;
|
|
}
|
|
|
|
int val = (int)SendMessage(hwndVal, UDM_GETPOS32, 0, 0);
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDC_BASETHRESHOLD_EDIT: source->SetInt(TEXT("keySimilarity"), val); break;
|
|
case IDC_BLEND_EDIT: source->SetInt(TEXT("keyBlend"), val); break;
|
|
case IDC_GAMMA_EDIT: source->SetInt(TEXT("keyGamma"), val); break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDC_REFRESH:
|
|
{
|
|
HWND hwndDeviceList = GetDlgItem(hwnd, IDC_DEVICELIST);
|
|
|
|
FillOutListOfVideoDevices(hwndDeviceList);
|
|
SendMessage(hwndDeviceList, CB_SETCURSEL, 0, 0);
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_DEVICELIST, CBN_SELCHANGE), (LPARAM)hwndDeviceList);
|
|
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_FPS_EDIT), FALSE);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_CONFIG), FALSE);
|
|
EnableWindow(GetDlgItem(hwnd, IDOK), FALSE);
|
|
}
|
|
else
|
|
{
|
|
EnableWindow(GetDlgItem(hwnd, IDC_RESOLUTION), TRUE);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_FPS), TRUE);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_FPS_EDIT), TRUE);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_CONFIG), TRUE);
|
|
EnableWindow(GetDlgItem(hwnd, IDOK), TRUE);
|
|
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
String strSel = GetCBText(hwndDevices, id);
|
|
IBaseFilter *filter = GetDeviceByName(strSel);
|
|
if(filter)
|
|
{
|
|
IPin *outputPin = GetOutputPin(filter);
|
|
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();
|
|
}
|
|
|
|
filter->Release();
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
|
|
SIZE size;
|
|
UINT fps;
|
|
if(GetClosestResolution(configData->outputList, size, fps))
|
|
{
|
|
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);
|
|
SendMessage(hwndFPS, UDM_SETPOS32, 0, fps);
|
|
}
|
|
else
|
|
{
|
|
SendMessage(hwndResolutions, CB_SETCURSEL, 0, 0);
|
|
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_RESOLUTION, CBN_SELCHANGE), (LPARAM)hwndResolutions);
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
HWND hwndFPSEdit = GetDlgItem(hwnd, IDC_FPS_EDIT);
|
|
|
|
SIZE resolution;
|
|
FPSInfo fpsInfo;
|
|
|
|
if(!GetResolution(hwndResolution, resolution, HIWORD(wParam) == CBN_SELCHANGE) || !configData->GetResolutionFPSInfo(resolution, fpsInfo))
|
|
{
|
|
SendMessage(hwndFPS, UDM_SETRANGE32, 0, 0);
|
|
SendMessage(hwndFPS, UDM_SETPOS32, 0, 0);
|
|
SetWindowText(hwndFPSEdit, TEXT("0"));
|
|
break;
|
|
}
|
|
|
|
SendMessage(hwndFPS, UDM_SETRANGE32, fpsInfo.minFPS, fpsInfo.maxFPS);
|
|
SendMessage(hwndFPS, UDM_SETPOS32, 0, fpsInfo.maxFPS);
|
|
|
|
String strFPS = IntString(fpsInfo.maxFPS);
|
|
SetWindowText(hwndFPSEdit, strFPS.Array());
|
|
}
|
|
break;
|
|
|
|
case IDC_CONFIG:
|
|
{
|
|
UINT id = (UINT)SendMessage(GetDlgItem(hwnd, IDC_DEVICELIST), CB_GETCURSEL, 0, 0);
|
|
if(id != CB_ERR)
|
|
{
|
|
String strSel = GetCBText(GetDlgItem(hwnd, IDC_DEVICELIST), id);
|
|
IBaseFilter *filter = GetDeviceByName(strSel);
|
|
if(filter)
|
|
{
|
|
ISpecifyPropertyPages *propPages;
|
|
CAUUID cauuid;
|
|
|
|
if(SUCCEEDED(filter->QueryInterface(IID_ISpecifyPropertyPages, (void**)&propPages)))
|
|
{
|
|
if(SUCCEEDED(propPages->GetPages(&cauuid)))
|
|
{
|
|
if(cauuid.cElems)
|
|
{
|
|
OleCreatePropertyFrame(hwnd, 0, 0, NULL, 1, (LPUNKNOWN*)&filter, cauuid.cElems, cauuid.pElems, 0, 0, NULL);
|
|
CoTaskMemFree(cauuid.pElems);
|
|
}
|
|
}
|
|
propPages->Release();
|
|
}
|
|
|
|
filter->Release();
|
|
}
|
|
}
|
|
}
|
|
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))
|
|
{
|
|
MessageBox(hwnd, PluginStr("DeviceSelection.InvalidResolution"), NULL, 0);
|
|
break;
|
|
}
|
|
|
|
String strDevice = GetCBText(GetDlgItem(hwnd, IDC_DEVICELIST), deviceID);
|
|
|
|
BOOL bUDMError;
|
|
UINT fps = (UINT)SendMessage(GetDlgItem(hwnd, IDC_FPS), UDM_GETPOS32, 0, (LPARAM)&bUDMError);
|
|
if(bUDMError)
|
|
break;
|
|
|
|
if(fps == 0)
|
|
{
|
|
MessageBox(hwnd, PluginStr("DeviceSelection.UnsupportedResolution"), NULL, 0);
|
|
break;
|
|
}
|
|
|
|
if(configData->bCreating)
|
|
{
|
|
bool bFoundGlobal;
|
|
if(CurrentDeviceExists(strDevice, configData->bGlobalSource, bFoundGlobal))
|
|
{
|
|
if(bFoundGlobal)
|
|
MessageBox(hwnd, PluginStr("DeviceSelection.GlobalExists"), NULL, 0);
|
|
else
|
|
{
|
|
if(configData->bGlobalSource)
|
|
MessageBox(hwnd, PluginStr("DeviceSelection.ExistsSomewhere"), NULL, 0);
|
|
else
|
|
MessageBox(hwnd, PluginStr("DeviceSelection.ExistsInScene"), NULL, 0);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
BOOL bFlip = SendMessage(GetDlgItem(hwnd, IDC_FLIPIMAGE), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
BOOL bCustomResolution = SendMessage(GetDlgItem(hwnd, IDC_CUSTOMRESOLUTION), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
|
|
configData->data->SetString(TEXT("device"), strDevice);
|
|
configData->data->SetInt(TEXT("customResolution"), bCustomResolution);
|
|
configData->data->SetInt(TEXT("resolutionWidth"), resolution.cx);
|
|
configData->data->SetInt(TEXT("resolutionHeight"), resolution.cy);
|
|
configData->data->SetInt(TEXT("fps"), fps);
|
|
configData->data->SetInt(TEXT("flipImage"), bFlip);
|
|
|
|
BOOL bUseColorKey = SendMessage(GetDlgItem(hwnd, IDC_USECOLORKEY), 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 keyGamma = (int)SendMessage(GetDlgItem(hwnd, IDC_GAMMA), UDM_GETPOS32, 0, (LPARAM)&bUDMError);
|
|
if(bUDMError) keyGamma = 0;
|
|
|
|
configData->data->SetInt(TEXT("useColorKey"), bUseColorKey);
|
|
configData->data->SetInt(TEXT("keyColor"), color);
|
|
configData->data->SetInt(TEXT("keySimilarity"), keySimilarity);
|
|
configData->data->SetInt(TEXT("keyBlend"), keyBlend);
|
|
configData->data->SetInt(TEXT("keyGamma"), keyGamma);
|
|
}
|
|
|
|
case IDCANCEL:
|
|
if(LOWORD(wParam) == IDCANCEL)
|
|
{
|
|
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
|
|
if(source)
|
|
{
|
|
source->SetInt(TEXT("flipImage"), configData->data->GetInt(TEXT("flipImage"), 0));
|
|
|
|
source->SetInt(TEXT("useColorKey"), configData->data->GetInt(TEXT("useColorKey"), 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"), 10));
|
|
source->SetInt(TEXT("keyGamma"), configData->data->GetInt(TEXT("keyGamma"), 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 = new ConfigDialogData;
|
|
configData->lpName = element->GetName();
|
|
configData->data = data;
|
|
configData->bGlobalSource = (scmpi(element->GetParent()->GetName(), TEXT("global sources")) == 0);
|
|
configData->bCreating = bCreating;
|
|
|
|
if(DialogBoxParam(hinstMain, MAKEINTRESOURCE(IDD_CONFIG), API->GetMainWindow(), ConfigureDialogProc, (LPARAM)configData) == IDOK)
|
|
{
|
|
element->SetInt(TEXT("cx"), data->GetInt(TEXT("resolutionWidth")));
|
|
element->SetInt(TEXT("cy"), data->GetInt(TEXT("resolutionHeight")));
|
|
|
|
delete configData;
|
|
return true;
|
|
}
|
|
|
|
delete configData;
|
|
return false;
|
|
}
|
|
|
|
ImageSource* STDCALL CreateDShowSource(XElement *data)
|
|
{
|
|
DeviceSource *source = new DeviceSource;
|
|
if(!source->Init(data))
|
|
{
|
|
delete source;
|
|
return NULL;
|
|
}
|
|
|
|
return source;
|
|
}
|
|
|
|
|
|
bool LoadPlugin()
|
|
{
|
|
traceIn(DShowPluginLoadPlugin);
|
|
|
|
InitColorControl(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;
|
|
|
|
traceOut;
|
|
}
|
|
|
|
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)
|
|
hinstMain = hInst;
|
|
|
|
return TRUE;
|
|
}
|
|
|