threading, optimizations, api additions, directshow features

This commit is contained in:
jim 2012-10-24 23:58:42 -07:00
parent c108d1d506
commit 42c6b401e2
25 changed files with 1141 additions and 553 deletions

View File

@ -383,8 +383,78 @@ struct FPSInfo
}; };
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 struct ConfigDialogData
{ {
CTSTR lpName;
XElement *data; XElement *data;
List<MediaOutputInfo> outputList; List<MediaOutputInfo> outputList;
List<SIZE> resolutions; List<SIZE> resolutions;
@ -522,13 +592,20 @@ INT_PTR CALLBACK ConfigureDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPA
HWND hwndFPS = GetDlgItem(hwnd, IDC_FPS); HWND hwndFPS = GetDlgItem(hwnd, IDC_FPS);
HWND hwndFlip = GetDlgItem(hwnd, IDC_FLIPIMAGE); 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")); String strDevice = configData->data->GetString(TEXT("device"));
UINT cx = configData->data->GetInt(TEXT("resolutionWidth")); UINT cx = configData->data->GetInt(TEXT("resolutionWidth"));
UINT cy = configData->data->GetInt(TEXT("resolutionHeight")); UINT cy = configData->data->GetInt(TEXT("resolutionHeight"));
UINT fps = configData->data->GetInt(TEXT("fps")); UINT fps = configData->data->GetInt(TEXT("fps"));
bool bFlipVertical = configData->data->GetInt(TEXT("flipImage")) != 0;
SendMessage(hwndFlip, BM_SETCHECK, bFlipVertical ? BST_CHECKED : BST_UNCHECKED, 0); BOOL bCustomResolution = configData->data->GetInt(TEXT("customResolution"));
SendMessage(GetDlgItem(hwnd, IDC_CUSTOMRESOLUTION), BM_SETCHECK, bCustomResolution ? BST_CHECKED : BST_UNCHECKED, 0);
LocalizeWindow(hwnd, pluginLocale); LocalizeWindow(hwnd, pluginLocale);
FillOutListOfVideoDevices(GetDlgItem(hwnd, IDC_DEVICELIST)); FillOutListOfVideoDevices(GetDlgItem(hwnd, IDC_DEVICELIST));
@ -547,21 +624,147 @@ INT_PTR CALLBACK ConfigureDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPA
SendMessage(hwndDeviceList, CB_SETCURSEL, deviceID, 0); SendMessage(hwndDeviceList, CB_SETCURSEL, deviceID, 0);
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_DEVICELIST, CBN_SELCHANGE), (LPARAM)hwndDeviceList); ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_DEVICELIST, CBN_SELCHANGE), (LPARAM)hwndDeviceList);
String strResolution; if(bCustomResolution)
strResolution << UIntString(cx) << TEXT("x") << UIntString(cy); {
String strResolution;
strResolution << UIntString(cx) << TEXT("x") << UIntString(cy);
SendMessage(hwndResolutionList, WM_SETTEXT, 0, (LPARAM)strResolution.Array()); SendMessage(hwndResolutionList, WM_SETTEXT, 0, (LPARAM)strResolution.Array());
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_RESOLUTION, CBN_EDITCHANGE), (LPARAM)hwndResolutionList); ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_RESOLUTION, CBN_EDITCHANGE), (LPARAM)hwndResolutionList);
SendMessage(hwndFPS, UDM_SETPOS32, 0, (LPARAM)fps); SendMessage(hwndFPS, UDM_SETPOS32, 0, (LPARAM)fps);
}
} }
break; 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: case WM_COMMAND:
switch(LOWORD(wParam)) 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: case IDC_REFRESH:
{ {
HWND hwndDeviceList = GetDlgItem(hwnd, IDC_DEVICELIST); HWND hwndDeviceList = GetDlgItem(hwnd, IDC_DEVICELIST);
@ -624,8 +827,26 @@ INT_PTR CALLBACK ConfigureDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPA
filter->Release(); filter->Release();
} }
SendMessage(hwndResolutions, CB_SETCURSEL, 0, 0); //-------------------------------------------------
ConfigureDialogProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_RESOLUTION, CBN_SELCHANGE), (LPARAM)hwndResolutions);
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; break;
@ -636,8 +857,8 @@ INT_PTR CALLBACK ConfigureDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPA
ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER); ConfigDialogData *configData = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
HWND hwndResolution = (HWND)lParam; HWND hwndResolution = (HWND)lParam;
HWND hwndFPS = GetDlgItem(hwnd, IDC_FPS); HWND hwndFPS = GetDlgItem(hwnd, IDC_FPS);
HWND hwndFPSEdit = GetDlgItem(hwnd, IDC_FPS_EDIT); HWND hwndFPSEdit = GetDlgItem(hwnd, IDC_FPS_EDIT);
SIZE resolution; SIZE resolution;
FPSInfo fpsInfo; FPSInfo fpsInfo;
@ -737,15 +958,52 @@ INT_PTR CALLBACK ConfigureDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPA
} }
BOOL bFlip = SendMessage(GetDlgItem(hwnd, IDC_FLIPIMAGE), BM_GETCHECK, 0, 0) == BST_CHECKED; 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->SetString(TEXT("device"), strDevice);
configData->data->SetInt(TEXT("customResolution"), bCustomResolution);
configData->data->SetInt(TEXT("resolutionWidth"), resolution.cx); configData->data->SetInt(TEXT("resolutionWidth"), resolution.cx);
configData->data->SetInt(TEXT("resolutionHeight"), resolution.cy); configData->data->SetInt(TEXT("resolutionHeight"), resolution.cy);
configData->data->SetInt(TEXT("fps"), fps); configData->data->SetInt(TEXT("fps"), fps);
configData->data->SetInt(TEXT("flipImage"), bFlip); 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: 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)); EndDialog(hwnd, LOWORD(wParam));
} }
} }
@ -767,6 +1025,7 @@ bool STDCALL ConfigureDShowSource(XElement *element, bool bCreating)
data = element->CreateElement(TEXT("data")); data = element->CreateElement(TEXT("data"));
ConfigDialogData *configData = new ConfigDialogData; ConfigDialogData *configData = new ConfigDialogData;
configData->lpName = element->GetName();
configData->data = data; configData->data = data;
configData->bGlobalSource = (scmpi(element->GetParent()->GetName(), TEXT("global sources")) == 0); configData->bGlobalSource = (scmpi(element->GetParent()->GetName(), TEXT("global sources")) == 0);
configData->bCreating = bCreating; configData->bCreating = bCreating;
@ -801,6 +1060,8 @@ bool LoadPlugin()
{ {
traceIn(DShowPluginLoadPlugin); traceIn(DShowPluginLoadPlugin);
InitColorControl(hinstMain);
pluginLocale = new LocaleStringLookup; pluginLocale = new LocaleStringLookup;
if(!pluginLocale->LoadStringFile(TEXT("plugins/DShowPlugin/locale/en.txt"))) if(!pluginLocale->LoadStringFile(TEXT("plugins/DShowPlugin/locale/en.txt")))

View File

@ -35,6 +35,7 @@ extern HINSTANCE hinstMain;
IBaseFilter* GetDeviceByName(CTSTR lpName); IBaseFilter* GetDeviceByName(CTSTR lpName);
IPin* GetOutputPin(IBaseFilter *filter); IPin* GetOutputPin(IBaseFilter *filter);
void GetOutputList(IPin *curPin, List<MediaOutputInfo> &outputInfoList); void GetOutputList(IPin *curPin, List<MediaOutputInfo> &outputInfoList);
bool GetClosestResolution(List<MediaOutputInfo> &outputList, SIZE &resolution, UINT &fps);
extern LocaleStringLookup *pluginLocale; extern LocaleStringLookup *pluginLocale;
#define PluginStr(text) pluginLocale->LookupString(TEXT2(text)) #define PluginStr(text) pluginLocale->LookupString(TEXT2(text))

View File

@ -52,7 +52,7 @@ END
// Dialog // Dialog
// //
IDD_CONFIG DIALOGEX 0, 0, 417, 92 IDD_CONFIG DIALOGEX 0, 0, 417, 235
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "DeviceSelection" CAPTION "DeviceSelection"
FONT 8, "MS Shell Dlg", 400, 0, 0x1 FONT 8, "MS Shell Dlg", 400, 0, 0x1
@ -60,15 +60,32 @@ BEGIN
RTEXT "DeviceSelection.Device",IDC_STATIC,4,9,117,8 RTEXT "DeviceSelection.Device",IDC_STATIC,4,9,117,8
COMBOBOX IDC_DEVICELIST,125,7,133,12,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP COMBOBOX IDC_DEVICELIST,125,7,133,12,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "DeviceSelection.Config",IDC_CONFIG,263,7,78,14 PUSHBUTTON "DeviceSelection.Config",IDC_CONFIG,263,7,78,14
RTEXT "DeviceSelection.Resolution",IDC_STATIC,4,27,117,8
COMBOBOX IDC_RESOLUTION,125,24,133,72,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
RTEXT "DeviceSelection.FPS",IDC_STATIC,4,44,117,8
EDITTEXT IDC_FPS_EDIT,125,41,42,14,ES_AUTOHSCROLL | ES_READONLY | ES_NUMBER
CONTROL "",IDC_FPS,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,168,41,10,14
DEFPUSHBUTTON "OK",IDOK,306,71,50,14
PUSHBUTTON "Cancel",IDCANCEL,360,71,50,14
PUSHBUTTON "DeviceSelection.Refresh",IDC_REFRESH,344,7,66,14 PUSHBUTTON "DeviceSelection.Refresh",IDC_REFRESH,344,7,66,14
CONTROL "DeviceSelection.FlipImage",IDC_FLIPIMAGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,3,59,131,10,WS_EX_RIGHT CONTROL "DeviceSelection.FlipImage",IDC_FLIPIMAGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,2,26,131,10,WS_EX_RIGHT
GROUPBOX "DeviceSelection.Resolution",IDC_STATIC,7,41,274,65
CONTROL "DeviceSelection.CustomResolution",IDC_CUSTOMRESOLUTION,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,53,130,10,WS_EX_RIGHT
RTEXT "DeviceSelection.Resolution",IDC_STATIC,18,71,117,8
COMBOBOX IDC_RESOLUTION,138,68,133,72,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
RTEXT "DeviceSelection.FPS",IDC_STATIC,18,88,117,8
EDITTEXT IDC_FPS_EDIT,138,85,42,14,ES_AUTOHSCROLL | ES_NUMBER
CONTROL "",IDC_FPS,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,180,84,10,14
GROUPBOX "DeviceSelection.ColorKey",IDC_STATIC,7,112,274,103
CONTROL "DeviceSelection.UseColorKey",IDC_USECOLORKEY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,126,130,10,WS_EX_RIGHT
RTEXT "DeviceSelection.Color",IDC_STATIC,18,144,117,8
CONTROL "",IDC_COLOR,"OBSColorControl",WS_TABSTOP,138,141,28,14
PUSHBUTTON "DeviceSelection.Select",IDC_SELECTCOLOR,170,141,50,14
RTEXT "DeviceSelection.Similarity",IDC_STATIC,18,162,117,8
EDITTEXT IDC_BASETHRESHOLD_EDIT,137,160,40,14,ES_AUTOHSCROLL | ES_NUMBER
CONTROL "",IDC_BASETHRESHOLD,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,178,160,10,14
RTEXT "DeviceSelection.Blend",IDC_STATIC,17,179,117,8
EDITTEXT IDC_BLEND_EDIT,137,177,40,14,ES_AUTOHSCROLL | ES_NUMBER
CONTROL "",IDC_BLEND,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,177,177,11,14
RTEXT "DeviceSelection.Gamma",IDC_STATIC,17,195,117,8
EDITTEXT IDC_GAMMA_EDIT,137,193,40,14,ES_AUTOHSCROLL
CONTROL "",IDC_GAMMA,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,177,193,11,14
DEFPUSHBUTTON "OK",IDOK,306,214,50,14
PUSHBUTTON "Cancel",IDCANCEL,360,214,50,14
END END
@ -85,7 +102,7 @@ BEGIN
LEFTMARGIN, 7 LEFTMARGIN, 7
RIGHTMARGIN, 410 RIGHTMARGIN, 410
TOPMARGIN, 7 TOPMARGIN, 7
BOTTOMMARGIN, 85 BOTTOMMARGIN, 228
END END
END END
#endif // APSTUDIO_INVOKED #endif // APSTUDIO_INVOKED

View File

@ -280,7 +280,7 @@
/> />
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
Optimization="2" Optimization="3"
EnableIntrinsicFunctions="true" EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="../OBSApi" AdditionalIncludeDirectories="../OBSApi"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;DSHOWPLUGIN_EXPORTS" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;DSHOWPLUGIN_EXPORTS"
@ -404,6 +404,34 @@
<Filter <Filter
Name="Shaders" Name="Shaders"
> >
<File
RelativePath="..\rundir\plugins\DShowPlugin\shaders\ColorKey_HDYCToRGB.pShader"
>
</File>
<File
RelativePath="..\rundir\plugins\DShowPlugin\shaders\ColorKey_RGB.pShader"
>
</File>
<File
RelativePath="..\rundir\plugins\DShowPlugin\shaders\ColorKey_UYVToRGB.pShader"
>
</File>
<File
RelativePath="..\rundir\plugins\DShowPlugin\shaders\ColorKey_YUVToRGB.pShader"
>
</File>
<File
RelativePath="..\rundir\plugins\DShowPlugin\shaders\ColorKey_YUXVToRGB.pShader"
>
</File>
<File
RelativePath="..\rundir\plugins\DShowPlugin\shaders\ColorKey_YVUToRGB.pShader"
>
</File>
<File
RelativePath="..\rundir\plugins\DShowPlugin\shaders\ColorKey_YVXUToRGB.pShader"
>
</File>
<File <File
RelativePath="..\rundir\plugins\DShowPlugin\shaders\HDYCToRGB.pShader" RelativePath="..\rundir\plugins\DShowPlugin\shaders\HDYCToRGB.pShader"
> >

View File

@ -48,6 +48,13 @@ bool DeviceSource::Init(XElement *data)
capture->SetFiltergraph(graph); capture->SetFiltergraph(graph);
UINT numProcessors = OSGetProcessorCount();
hConvertThreads = (HANDLE*)Allocate(sizeof(HANDLE)*numProcessors);
convertData = (ConvertData*)Allocate(sizeof(ConvertData)*numProcessors);
zero(hConvertThreads, sizeof(HANDLE)*numProcessors);
zero(convertData, sizeof(ConvertData)*numProcessors);
this->data = data; this->data = data;
UpdateSettings(); UpdateSettings();
@ -69,12 +76,44 @@ DeviceSource::~DeviceSource()
SafeRelease(capture); SafeRelease(capture);
SafeRelease(graph); SafeRelease(graph);
Free(hConvertThreads);
Free(convertData);
if(hSampleMutex) if(hSampleMutex)
OSCloseMutex(hSampleMutex); OSCloseMutex(hSampleMutex);
traceOut; traceOut;
} }
String DeviceSource::ChooseShader()
{
if(colorType == DeviceOutputType_RGB && !bUseColorKey)
return String();
String strShader;
strShader << TEXT("plugins/DShowPlugin/shaders/");
if(bUseColorKey)
strShader << TEXT("ColorKey_");
if(colorType == DeviceOutputType_I420)
strShader << TEXT("YUVToRGB.pShader");
else if(colorType == DeviceOutputType_YV12)
strShader << TEXT("YVUToRGB.pShader");
else if(colorType == DeviceOutputType_YVYU)
strShader << TEXT("YVXUToRGB.pShader");
else if(colorType == DeviceOutputType_YUY2)
strShader << TEXT("YUXVToRGB.pShader");
else if(colorType == DeviceOutputType_UYVY)
strShader << TEXT("UYVToRGB.pShader");
else if(colorType == DeviceOutputType_HDYC)
strShader << TEXT("HDYCToRGB.pShader");
else
strShader << TEXT("RGB.pShader");
return strShader;
}
bool DeviceSource::LoadFilters() bool DeviceSource::LoadFilters()
{ {
traceIn(DeviceSource::LoadFilters); traceIn(DeviceSource::LoadFilters);
@ -90,20 +129,28 @@ bool DeviceSource::LoadFilters()
VideoOutputType expectedVideoType; VideoOutputType expectedVideoType;
IPin *devicePin = NULL; IPin *devicePin = NULL;
HRESULT err; HRESULT err;
String strShader;
String strDevice = data->GetString(TEXT("device")); //------------------------------------------------
UINT cx = data->GetInt(TEXT("resolutionWidth"));
UINT cy = data->GetInt(TEXT("resolutionHeight")); bUseCustomResolution = data->GetInt(TEXT("customResolution"));
UINT fps = data->GetInt(TEXT("fps")); strDevice = data->GetString(TEXT("device"));
bFlipVertical = data->GetInt(TEXT("flipImage")) != 0; bFlipVertical = data->GetInt(TEXT("flipImage")) != 0;
renderCX = cx; //------------------------------------------------
renderCY = cy;
if(!strDevice.IsValid() || !cx || !cy || !fps) bUseColorKey = data->GetInt(TEXT("useColorKey")) != 0;
keyColor = data->GetInt(TEXT("keyColor"), 0xFFFFFFFF);
keySimilarity = data->GetInt(TEXT("keySimilarity"));
keyBlend = data->GetInt(TEXT("keyBlend"), 10);
keyGamma = data->GetInt(TEXT("keyGamma"));
//------------------------------------------------
if(!strDevice.IsValid())
{ {
AppWarning(TEXT("DShowPlugin: Invalid device/size/fps specified")); AppWarning(TEXT("DShowPlugin: Invalid device specified"));
goto cleanFinish; goto cleanFinish;
} }
@ -123,7 +170,51 @@ bool DeviceSource::LoadFilters()
GetOutputList(devicePin, outputList); GetOutputList(devicePin, outputList);
MediaOutputInfo *bestOutput = GetBestMediaOutput(outputList, cx, cy, fps); //------------------------------------------------
if(bUseCustomResolution)
{
renderCX = data->GetInt(TEXT("resolutionWidth"));
renderCY = data->GetInt(TEXT("resolutionHeight"));
fps = data->GetInt(TEXT("fps"));
}
else
{
SIZE size;
GetClosestResolution(outputList, size, fps);
renderCX = size.cx;
renderCY = size.cy;
}
if(!renderCX || !renderCY || !fps)
{
AppWarning(TEXT("DShowPlugin: Invalid size/fps specified"));
goto cleanFinish;
}
UINT numProcessors = OSGetProcessorCount();
for(UINT i=0; i<numProcessors; i++)
{
convertData[i].width = renderCX;
convertData[i].height = renderCY;
if(i == 0)
convertData[i].startY = 0;
else
convertData[i].startY = convertData[i-1].endY;
if(i == (numProcessors-1))
convertData[i].endY = renderCY;
else
convertData[i].endY = ((renderCY/numProcessors)*(i+1)) & 0xFFFFFFFE;
}
bFirstFrame = true;
prevSample = NULL;
//------------------------------------------------
MediaOutputInfo *bestOutput = GetBestMediaOutput(outputList, renderCX, renderCY, fps);
if(!bestOutput) if(!bestOutput)
{ {
AppWarning(TEXT("DShowPlugin: Could not find appropriate resolution to create device image source")); AppWarning(TEXT("DShowPlugin: Could not find appropriate resolution to create device image source"));
@ -132,41 +223,29 @@ bool DeviceSource::LoadFilters()
expectedVideoType = bestOutput->videoType; expectedVideoType = bestOutput->videoType;
colorType = DeviceOutputType_RGB;
if(bestOutput->videoType == VideoOutputType_I420) if(bestOutput->videoType == VideoOutputType_I420)
{
colorConvertShader = CreatePixelShaderFromFile(TEXT("plugins/DShowPlugin/shaders/YUVToRGB.pShader"));
colorType = DeviceOutputType_I420; colorType = DeviceOutputType_I420;
}
else if(bestOutput->videoType == VideoOutputType_YV12) else if(bestOutput->videoType == VideoOutputType_YV12)
{
colorConvertShader = CreatePixelShaderFromFile(TEXT("plugins/DShowPlugin/shaders/YVUToRGB.pShader"));
colorType = DeviceOutputType_YV12; colorType = DeviceOutputType_YV12;
}
else if(bestOutput->videoType == VideoOutputType_YVYU) else if(bestOutput->videoType == VideoOutputType_YVYU)
{
colorConvertShader = CreatePixelShaderFromFile(TEXT("plugins/DShowPlugin/shaders/YVXUToRGB.pShader"));
colorType = DeviceOutputType_YVYU; colorType = DeviceOutputType_YVYU;
}
else if(bestOutput->videoType == VideoOutputType_YUY2) else if(bestOutput->videoType == VideoOutputType_YUY2)
{
colorConvertShader = CreatePixelShaderFromFile(TEXT("plugins/DShowPlugin/shaders/YUXVToRGB.pShader"));
colorType = DeviceOutputType_YUY2; colorType = DeviceOutputType_YUY2;
}
else if(bestOutput->videoType == VideoOutputType_UYVY) else if(bestOutput->videoType == VideoOutputType_UYVY)
{
colorConvertShader = CreatePixelShaderFromFile(TEXT("plugins/DShowPlugin/shaders/UYVToRGB.pShader"));
colorType = DeviceOutputType_UYVY; colorType = DeviceOutputType_UYVY;
}
else if(bestOutput->videoType == VideoOutputType_HDYC) else if(bestOutput->videoType == VideoOutputType_HDYC)
{ colorType = DeviceOutputType_HDYC;
colorConvertShader = CreatePixelShaderFromFile(TEXT("plugins/DShowPlugin/shaders/HDYCToRGB.pShader"));
colorType = DeviceOutputType_UYVY;
}
else else
{
colorType = DeviceOutputType_RGB;
expectedVideoType = VideoOutputType_RGB32; expectedVideoType = VideoOutputType_RGB32;
}
if(colorType != DeviceOutputType_RGB)
lpImageBuffer = (LPBYTE)Allocate(renderCX*renderCY*4);
strShader = ChooseShader();
if(strShader.IsValid())
colorConvertShader = CreatePixelShaderFromFile(strShader);
if(colorType != DeviceOutputType_RGB && !colorConvertShader) if(colorType != DeviceOutputType_RGB && !colorConvertShader)
{ {
@ -260,6 +339,12 @@ cleanFinish:
colorConvertShader = NULL; colorConvertShader = NULL;
} }
if(lpImageBuffer)
{
Free(lpImageBuffer);
lpImageBuffer = NULL;
}
bReadyToDraw = true; bReadyToDraw = true;
} }
else else
@ -267,17 +352,17 @@ cleanFinish:
//----------------------------------------------------- //-----------------------------------------------------
// create the texture regardless, will just show up as red to indicate failure // create the texture regardless, will just show up as red to indicate failure
BYTE *textureData = (BYTE*)Allocate(cx*cy*4); BYTE *textureData = (BYTE*)Allocate(renderCX*renderCY*4);
if(colorType == DeviceOutputType_RGB) //you may be confused, but when directshow outputs RGB, it's actually outputting BGR if(colorType == DeviceOutputType_RGB) //you may be confused, but when directshow outputs RGB, it's actually outputting BGR
{ {
msetd(textureData, 0xFFFF0000, cx*cy*4); msetd(textureData, 0xFFFF0000, renderCX*renderCY*4);
texture = CreateTexture(cx, cy, GS_BGR, textureData, FALSE, FALSE); texture = CreateTexture(renderCX, renderCY, GS_BGR, textureData, FALSE, FALSE);
} }
else //if we're working with planar YUV, we can just use regular RGB textures instead else //if we're working with planar YUV, we can just use regular RGB textures instead
{ {
msetd(textureData, 0xFF0000FF, cx*cy*4); msetd(textureData, 0xFF0000FF, renderCX*renderCY*4);
texture = CreateTexture(cx, cy, GS_RGB, textureData, FALSE, FALSE); texture = CreateTexture(renderCX, renderCY, GS_RGB, textureData, FALSE, FALSE);
} }
Free(textureData); Free(textureData);
@ -315,6 +400,25 @@ void DeviceSource::UnloadFilters()
colorConvertShader = NULL; colorConvertShader = NULL;
} }
if(lpImageBuffer)
{
Free(lpImageBuffer);
lpImageBuffer = NULL;
}
UINT numProcessors = OSGetProcessorCount();
for(UINT i=0; i<numProcessors; i++)
{
if(hConvertThreads[i])
{
WaitForSingleObject(hConvertThreads[i], INFINITE);
CloseHandle(hConvertThreads[i]);
hConvertThreads[i] = NULL;
}
}
SafeRelease(prevSample);
SafeRelease(control); SafeRelease(control);
traceOut; traceOut;
@ -385,6 +489,13 @@ void DeviceSource::Receive(IMediaSample *sample)
} }
} }
DWORD STDCALL PackPlanarThread(ConvertData *data)
{
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
PackPlanar(data->output, data->input, data->width, data->height, data->pitch, data->startY, data->endY);
return 0;
}
void DeviceSource::Preprocess() void DeviceSource::Preprocess()
{ {
traceIn(DeviceSource::Preprocess); traceIn(DeviceSource::Preprocess);
@ -402,6 +513,8 @@ void DeviceSource::Preprocess()
} }
OSLeaveMutex(hSampleMutex); OSLeaveMutex(hSampleMutex);
UINT numProcessors = OSGetProcessorCount();
if(lastSample) if(lastSample)
{ {
BYTE *lpImage = NULL; BYTE *lpImage = NULL;
@ -412,20 +525,37 @@ void DeviceSource::Preprocess()
if(SUCCEEDED(lastSample->GetPointer(&lpImage))) if(SUCCEEDED(lastSample->GetPointer(&lpImage)))
texture->SetImage(lpImage, GS_IMAGEFORMAT_BGRX, renderCX*4); texture->SetImage(lpImage, GS_IMAGEFORMAT_BGRX, renderCX*4);
} }
lastSample->Release();
} }
else if(colorType == DeviceOutputType_I420 || colorType == DeviceOutputType_YV12) else if(colorType == DeviceOutputType_I420 || colorType == DeviceOutputType_YV12)
{ {
if(prevSample)
{
WaitForMultipleObjects(numProcessors, hConvertThreads, TRUE, INFINITE);
for(UINT i=0; i<numProcessors; i++)
{
CloseHandle(hConvertThreads[i]);
hConvertThreads[i] = NULL;
}
prevSample->Release();
prevSample = NULL;
texture->SetImage(lpImageBuffer, GS_IMAGEFORMAT_RGBX, renderCX*4);
}
if(SUCCEEDED(lastSample->GetPointer(&lpImage))) if(SUCCEEDED(lastSample->GetPointer(&lpImage)))
{ {
LPBYTE lpData; for(UINT i=0; i<numProcessors; i++)
UINT pitch;
if(texture->Map(lpData, pitch))
{ {
PackPlanar(lpData, lpImage); convertData[i].input = lpImage;
texture->Unmap(); convertData[i].pitch = renderCX*4;
convertData[i].output = lpImageBuffer;
hConvertThreads[i] = OSCreateThread((XTHREAD)PackPlanarThread, (LPVOID)(convertData+i));
} }
} }
prevSample = lastSample;
} }
else if(colorType == DeviceOutputType_YVYU || colorType == DeviceOutputType_YUY2) else if(colorType == DeviceOutputType_YVYU || colorType == DeviceOutputType_YUY2)
{ {
@ -434,17 +564,16 @@ void DeviceSource::Preprocess()
LPBYTE lpData; LPBYTE lpData;
UINT pitch; UINT pitch;
long chi1 = lastSample->GetSize();
long chi2 = lastSample->GetActualDataLength();
if(texture->Map(lpData, pitch)) if(texture->Map(lpData, pitch))
{ {
Convert422To444(lpData, lpImage, true); Convert422To444(lpData, lpImage, true);
texture->Unmap(); texture->Unmap();
} }
} }
lastSample->Release();
} }
else if(colorType == DeviceOutputType_UYVY) else if(colorType == DeviceOutputType_UYVY || colorType == DeviceOutputType_HDYC)
{ {
if(SUCCEEDED(lastSample->GetPointer(&lpImage))) if(SUCCEEDED(lastSample->GetPointer(&lpImage)))
{ {
@ -457,9 +586,9 @@ void DeviceSource::Preprocess()
texture->Unmap(); texture->Unmap();
} }
} }
}
lastSample->Release(); lastSample->Release();
}
bReadyToDraw = true; bReadyToDraw = true;
} }
@ -475,8 +604,22 @@ void DeviceSource::Render(const Vect2 &pos, const Vect2 &size)
{ {
Shader *oldShader = GetCurrentPixelShader(); Shader *oldShader = GetCurrentPixelShader();
if(colorConvertShader) if(colorConvertShader)
{
LoadPixelShader(colorConvertShader); LoadPixelShader(colorConvertShader);
if(bUseColorKey)
{
float fSimilarity = float(keySimilarity)/100.0f;
float fBlendVal = float(max(keyBlend, 1)/100.0f);
float fGammaVal = 1.0f+(float(keyGamma)/100.0f);
colorConvertShader->SetColor(colorConvertShader->GetParameterByName(TEXT("colorKey")), keyColor);
colorConvertShader->SetFloat(colorConvertShader->GetParameterByName(TEXT("similarity")), fSimilarity);
colorConvertShader->SetFloat(colorConvertShader->GetParameterByName(TEXT("blend")), fBlendVal);
colorConvertShader->SetFloat(colorConvertShader->GetParameterByName(TEXT("gamma")), fGammaVal);
}
}
bool bFlip = bFlipVertical; bool bFlip = bFlipVertical;
if(colorType != DeviceOutputType_RGB) if(colorType != DeviceOutputType_RGB)
@ -498,16 +641,72 @@ void DeviceSource::UpdateSettings()
{ {
traceIn(DeviceSource::UpdateSettings); traceIn(DeviceSource::UpdateSettings);
bool bWasCapturing = bCapturing; String strNewDevice = data->GetString(TEXT("device"));
UINT newFPS = data->GetInt(TEXT("fps"));
UINT newCX = data->GetInt(TEXT("resolutionWidth"));
UINT newCY = data->GetInt(TEXT("resolutionHeight"));
BOOL bNewCustom = data->GetInt(TEXT("customResolution"));
if(bWasCapturing) Stop(); if(renderCX != newCX || renderCY != newCY || fps != newFPS || !strDevice.CompareI(strNewDevice) || bNewCustom != bUseCustomResolution)
{
bool bWasCapturing = bCapturing;
if(bWasCapturing) Stop();
UnloadFilters(); UnloadFilters();
LoadFilters(); LoadFilters();
if(bWasCapturing) Start(); if(bWasCapturing) Start();
}
traceOut; traceOut;
} }
void DeviceSource::SetInt(CTSTR lpName, int iVal)
{
if(bCapturing)
{
if(scmpi(lpName, TEXT("useColorKey")) == 0)
{
bool bNewVal = iVal != 0;
if(bUseColorKey != bNewVal)
{
API->EnterSceneMutex();
bUseColorKey = bNewVal;
if(colorConvertShader)
{
delete colorConvertShader;
colorConvertShader = NULL;
}
String strShader;
strShader = ChooseShader();
if(strShader.IsValid())
colorConvertShader = CreatePixelShaderFromFile(strShader);
API->LeaveSceneMutex();
}
}
else if(scmpi(lpName, TEXT("flipImage")) == 0)
{
bFlipVertical = iVal != 0;
}
else if(scmpi(lpName, TEXT("keyColor")) == 0)
{
keyColor = (DWORD)iVal;
}
else if(scmpi(lpName, TEXT("keySimilarity")) == 0)
{
keySimilarity = iVal;
}
else if(scmpi(lpName, TEXT("keyBlend")) == 0)
{
keyBlend = iVal;
}
else if(scmpi(lpName, TEXT("keyGamma")) == 0)
{
keyGamma = iVal;
}
}
}

View File

@ -19,6 +19,7 @@
#pragma once #pragma once
void PackPlanar(LPBYTE convertBuffer, LPBYTE lpPlanar, UINT renderCX, UINT renderCY, UINT pitch, UINT startY, UINT endY);
enum DeviceColorType enum DeviceColorType
{ {
@ -32,6 +33,15 @@ enum DeviceColorType
DeviceOutputType_YVYU, DeviceOutputType_YVYU,
DeviceOutputType_YUY2, DeviceOutputType_YUY2,
DeviceOutputType_UYVY, DeviceOutputType_UYVY,
DeviceOutputType_HDYC,
};
struct ConvertData
{
LPBYTE input, output;
UINT width, height;
UINT pitch;
UINT startY, endY;
}; };
class DeviceSource : public ImageSource class DeviceSource : public ImageSource
@ -49,9 +59,14 @@ class DeviceSource : public ImageSource
DeviceColorType colorType; DeviceColorType colorType;
String strDevice;
bool bFlipVertical; bool bFlipVertical;
UINT fps;
UINT renderCX, renderCY; UINT renderCX, renderCY;
BOOL bUseCustomResolution;
bool bFirstFrame;
bool bUseThreadedConversion;
bool bReadyToDraw; bool bReadyToDraw;
Texture *texture; Texture *texture;
@ -59,11 +74,27 @@ class DeviceSource : public ImageSource
XElement *data; XElement *data;
bool bCapturing, bFiltersLoaded; bool bCapturing, bFiltersLoaded;
IMediaSample *curSample; IMediaSample *curSample;
IMediaSample *prevSample;
Shader *colorConvertShader; Shader *colorConvertShader;
//--------------------------------- //---------------------------------
void PackPlanar(LPBYTE convertBuffer, LPBYTE lpPlanar); LPBYTE lpImageBuffer;
ConvertData *convertData;
HANDLE *hConvertThreads;
//---------------------------------
bool bUseColorKey;
DWORD keyColor;
int keySimilarity;
int keyBlend;
int keyGamma;
//---------------------------------
String ChooseShader();
void Convert422To444(LPBYTE convertBuffer, LPBYTE lp422, bool bLeadingY); void Convert422To444(LPBYTE convertBuffer, LPBYTE lp422, bool bLeadingY);
void FlushSamples() void FlushSamples()
@ -93,6 +124,8 @@ public:
void BeginScene(); void BeginScene();
void EndScene(); void EndScene();
virtual void SetInt(CTSTR lpName, int iVal);
Vect2 GetSize() const {return Vect2(float(renderCX), float(renderCY));} Vect2 GetSize() const {return Vect2(float(renderCX), float(renderCY));}
}; };

View File

@ -19,253 +19,39 @@
#include "DShowPlugin.h" #include "DShowPlugin.h"
#ifdef _WIN64 //now properly takes CPU cache into account - it's just so much faster than it was.
void PackPlanar(LPBYTE convertBuffer, LPBYTE lpPlanar, UINT renderCX, UINT renderCY, UINT pitch, UINT startY, UINT endY)
void DeviceSource::PackPlanar(LPBYTE convertBuffer, LPBYTE lpPlanar)
{
UINT halfWidth = renderCX/2;
UINT halfHeight = renderCY/2;
LPBYTE output = convertBuffer;
LPBYTE chromaPlanes[2];
chromaPlanes[0] = lpPlanar+(renderCX*renderCY);
chromaPlanes[1] = chromaPlanes[0]+(halfWidth*halfHeight);
//----------------------------------------------------------
// lum val
DWORD size = renderCX*renderCY;
DWORD dwQWSize = size>>3;
QWORD *inputQW = (QWORD*)lpPlanar;
QWORD *inputQWEnd = inputQW+dwQWSize;
while(inputQW < inputQWEnd)
{
register QWORD qw = *inputQW;
*output = BYTE(qw);
output[4] = BYTE(qw>>=8);
output[8] = BYTE(qw>>=8);
output[12] = BYTE(qw>>=8);
output[16] = BYTE(qw>>=8);
output[20] = BYTE(qw>>=8);
output[24] = BYTE(qw>>=8);
output[28] = BYTE(qw>>=8);
output += 32;
inputQW++;
}
LPBYTE input = (LPBYTE)inputQW;
size &= 7;
while(size--)
{
*output = *input;
output += 4;
input++;
}
//----------------------------------------------------------
// chroma 1
for(UINT i=0; i<2; i++)
{
output = convertBuffer+i+1;
dwQWSize = halfWidth>>3;
for(UINT y=0; y<renderCY; y++)
{
size = halfWidth;
inputQW = (QWORD*)(chromaPlanes[i]+(halfWidth*(y>>1)));
inputQWEnd = inputQW+dwQWSize;
while(inputQW < inputQWEnd)
{
register QWORD qw = *inputQW;
*output = BYTE(qw);
output[4] = BYTE(qw);
output[8] = BYTE(qw>>=8);
output[12] = BYTE(qw);
output[16] = BYTE(qw>>=8);
output[20] = BYTE(qw);
output[24] = BYTE(qw>>=8);
output[28] = BYTE(qw);
output[32] = BYTE(qw>>=8);
output[36] = BYTE(qw);
output[40] = BYTE(qw>>=8);
output[44] = BYTE(qw);
output[48] = BYTE(qw>>=8);
output[52] = BYTE(qw);
output[56] = BYTE(qw>>=8);
output[60] = BYTE(qw);
output += 64;
inputQW++;
}
input = (LPBYTE)inputQW;
size &= 7;
while(size--)
{
register BYTE byte = *input;
*output = byte;
output[4] = byte;
output += 8;
input++;
}
}
}
}
#else
void DeviceSource::PackPlanar(LPBYTE convertBuffer, LPBYTE lpPlanar)
{
UINT halfWidth = renderCX/2;
UINT halfHeight = renderCY/2;
LPBYTE output = convertBuffer;
LPBYTE chromaPlanes[2];
chromaPlanes[0] = lpPlanar+(renderCX*renderCY);
chromaPlanes[1] = chromaPlanes[0]+(halfWidth*halfHeight);
//----------------------------------------------------------
// lum val
DWORD size = renderCX*renderCY;
DWORD dwDWSize = size>>2;
LPDWORD inputDW = (LPDWORD)lpPlanar;
LPDWORD inputDWEnd = inputDW+dwDWSize;
while(inputDW < inputDWEnd)
{
register DWORD dw = *inputDW;
*output = BYTE(dw);
output[4] = BYTE(dw>>=8);
output[8] = BYTE(dw>>=8);
output[12] = BYTE(dw>>=8);
output += 16;
inputDW++;
}
LPBYTE input = (LPBYTE)inputDW;
size &= 3;
while(size--)
{
*output = *input;
output += 4;
input++;
}
//----------------------------------------------------------
// chroma 1
for(UINT i=0; i<2; i++)
{
output = convertBuffer+i+1;
dwDWSize = halfWidth>>2;
for(UINT y=0; y<renderCY; y++)
{
size = halfWidth;
inputDW = (LPDWORD)(chromaPlanes[i]+(halfWidth*(y>>1)));
inputDWEnd = inputDW+dwDWSize;
while(inputDW < inputDWEnd)
{
register DWORD dw = *inputDW;
*output = BYTE(dw);
output[4] = BYTE(dw);
output[8] = BYTE(dw>>=8);
output[12] = BYTE(dw);
output[16] = BYTE(dw>>=8);
output[20] = BYTE(dw);
output[24] = BYTE(dw>>=8);
output[28] = BYTE(dw);
output += 32;
inputDW++;
}
input = (LPBYTE)inputDW;
size &= 3;
while(size--)
{
register BYTE byte = *input;
*output = byte;
output[4] = byte;
output += 8;
input++;
}
}
}
}
#endif
/*void DeviceSource::PackPlanar(LPBYTE lpPlanar)
{ {
LPBYTE output = convertBuffer; LPBYTE output = convertBuffer;
LPBYTE input = lpPlanar; LPBYTE input = lpPlanar;
LPBYTE input2 = input+(renderCX*renderCY);
LPBYTE input3 = input2+(renderCX*renderCY/4);
//lum val UINT halfStartY = startY/2;
for(UINT y=0; y<renderCY; y++) UINT halfX = renderCX/2;
UINT halfY = endY/2;
for(UINT y=halfStartY; y<halfY; y++)
{ {
for(UINT x=0; x<renderCX; x++) LPBYTE lpLum1 = input + y*2*renderCX;
{ LPBYTE lpLum2 = lpLum1 + renderCX;
*output = *input; LPBYTE lpChroma1 = input2 + y*halfX;
LPBYTE lpChroma2 = input3 + y*halfX;
LPDWORD output1 = (LPDWORD)(output + (y*2)*pitch);
LPDWORD output2 = (LPDWORD)(((LPBYTE)output1)+pitch);
output += 4; for(UINT x=0; x<halfX; x++)
input++; {
DWORD out = (*(lpChroma1++) << 8) | (*(lpChroma2++) << 16);
*(output1++) = *(lpLum1++) | out;
*(output1++) = *(lpLum1++) | out;
*(output2++) = *(lpLum2++) | out;
*(output2++) = *(lpLum2++) | out;
} }
} }
}
//chroma 1
output = convertBuffer+1;
LPBYTE plane1 = lpPlanar+(renderCX*renderCY);
UINT halfWidth = renderCX/2;
UINT halfHeight = renderCY/2;
for(UINT y=0; y<renderCY; y++)
{
input = plane1+(halfWidth*(y/2));
for(UINT x=0; x<halfWidth; x++)
{
*output = *input;
output += 4;
*output = *input;
output += 4;
input++;
}
}
//chroma 1
output = convertBuffer+2;
LPBYTE plane2 = plane1+(halfWidth*halfHeight);
for(UINT y=0; y<renderCY; y++)
{
input = plane2+(halfWidth*(y/2));
for(UINT x=0; x<halfWidth; x++)
{
*output = *input;
output += 4;
*output = *input;
output += 4;
input++;
}
}
}*/
void DeviceSource::Convert422To444(LPBYTE convertBuffer, LPBYTE lp422, bool bLeadingY) void DeviceSource::Convert422To444(LPBYTE convertBuffer, LPBYTE lp422, bool bLeadingY)
{ {

View File

@ -12,6 +12,20 @@
#define IDC_FLIPIMAGE 1014 #define IDC_FLIPIMAGE 1014
#define IDC_BUTTON1 1015 #define IDC_BUTTON1 1015
#define IDC_CUSTOM 1015 #define IDC_CUSTOM 1015
#define IDC_SELECTCOLOR 1015
#define IDC_AUTOMATICRESOLUTION 1016
#define IDC_CUSTOMRESOLUTION 1016
#define IDC_USECHROMAKEY 1017
#define IDC_USECOLORKEY 1017
#define IDC_COLOR 1018
#define IDC_BASETHRESHOLD 1019
#define IDC_BASETHRESHOLD_EDIT 1020
#define IDC_BASETHRESHOLD_CHI 1021
#define IDC_BLEND_EDIT 1021
#define IDC_BLEND 1022
#define IDC_GAMMA_EDIT 1023
#define IDC_BASETHRESHOLD3 1024
#define IDC_GAMMA 1024
// Next default values for new objects // Next default values for new objects
// //
@ -19,7 +33,7 @@
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1016 #define _APS_NEXT_CONTROL_VALUE 1022
#define _APS_NEXT_SYMED_VALUE 101 #define _APS_NEXT_SYMED_VALUE 101
#endif #endif
#endif #endif

8
OBS.rc
View File

@ -522,8 +522,8 @@ END
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,4,2,1 FILEVERSION 0,4,3,0
PRODUCTVERSION 0,4,2,1 PRODUCTVERSION 0,4,3,0
FILEFLAGSMASK 0x17L FILEFLAGSMASK 0x17L
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -539,12 +539,12 @@ BEGIN
BLOCK "041104b0" BLOCK "041104b0"
BEGIN BEGIN
VALUE "FileDescription", "Open Broadcaster Software" VALUE "FileDescription", "Open Broadcaster Software"
VALUE "FileVersion", "0, 4, 2, 1" VALUE "FileVersion", "0, 4, 3, 0"
VALUE "InternalName", "OBS" VALUE "InternalName", "OBS"
VALUE "LegalCopyright", "Copyright (C) 2012" VALUE "LegalCopyright", "Copyright (C) 2012"
VALUE "OriginalFilename", "OBS.exe" VALUE "OriginalFilename", "OBS.exe"
VALUE "ProductName", "Open Broadcaster Software" VALUE "ProductName", "Open Broadcaster Software"
VALUE "ProductVersion", "0, 4, 2, 1" VALUE "ProductVersion", "0, 4, 3, 0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View File

@ -75,6 +75,7 @@ public:
virtual void GetBaseSize(UINT &width, UINT &height) const=0; virtual void GetBaseSize(UINT &width, UINT &height) const=0;
virtual void GetRenderFrameSize(UINT &width, UINT &height) const=0; virtual void GetRenderFrameSize(UINT &width, UINT &height) const=0;
virtual void GetOutputSize(UINT &width, UINT &height) const=0; virtual void GetOutputSize(UINT &width, UINT &height) const=0;
virtual UINT GetMaxFPS() const=0;
virtual CTSTR GetLanguage() const=0; virtual CTSTR GetLanguage() const=0;
@ -89,6 +90,22 @@ public:
virtual void RemoveStreamInfo(UINT infoID)=0; virtual void RemoveStreamInfo(UINT infoID)=0;
inline bool SSE2Available() {return bSSE2Availabe;} inline bool SSE2Available() {return bSSE2Availabe;}
inline ImageSource* GetSceneImageSource(CTSTR lpImageSource)
{
Scene *scene = GetScene();
if(scene)
{
SceneItem *item = scene->GetSceneItem(lpImageSource);
if(item)
{
if(item->GetSource())
return item->GetSource();
}
}
return NULL;
}
}; };

View File

@ -192,7 +192,7 @@
/> />
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
Optimization="2" Optimization="3"
EnableIntrinsicFunctions="true" EnableIntrinsicFunctions="true"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;OBSAPI_EXPORTS;BASE_EXPORTING" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;OBSAPI_EXPORTS;BASE_EXPORTING"
ExceptionHandling="2" ExceptionHandling="2"
@ -270,7 +270,7 @@
/> />
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
Optimization="2" Optimization="3"
EnableIntrinsicFunctions="true" EnableIntrinsicFunctions="true"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;OBSAPI_EXPORTS;BASE_EXPORTING" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;OBSAPI_EXPORTS;BASE_EXPORTING"
ExceptionHandling="2" ExceptionHandling="2"

View File

@ -36,6 +36,34 @@ public:
virtual void BeginScene() {} virtual void BeginScene() {}
virtual void EndScene() {} virtual void EndScene() {}
virtual void SetFloat(CTSTR lpName, float fValue) {}
virtual void SetInt(CTSTR lpName, int iValue) {}
virtual void SetString(CTSTR lpName, CTSTR lpVal) {}
virtual void SetVector(CTSTR lpName, const Vect &value) {}
virtual void SetVector2(CTSTR lpName, const Vect2 &value) {}
virtual void SetVector4(CTSTR lpName, const Vect4 &value) {}
virtual void SetMatrix(CTSTR lpName, const Matrix &mat) {}
inline void SetColor(CTSTR lpName, const Color4 &value) {SetVector4(lpName, value);}
inline void SetColor(CTSTR lpName, float fR, float fB, float fG, float fA=1.0f){SetVector4(lpName, Color4(fR, fB, fG, fA));}
inline void SetColor(CTSTR lpName, DWORD color) {SetVector4(lpName, RGBA_to_Vect4(color));}
inline void SetColor3(CTSTR lpName, const Color3 &value) {SetVector(lpName, value);}
inline void SetColor3(CTSTR lpName, float fR, float fB, float fG) {SetVector(lpName, Color3(fR, fB, fG));}
inline void SetColor3(CTSTR lpName, DWORD color) {SetVector(lpName, RGB_to_Vect(color));}
inline void SetVector4(CTSTR lpName, float fX, float fY, float fZ, float fW) {SetVector4(lpName, Vect4(fX, fY, fZ, fW));}
inline void SetVector(CTSTR lpName, float fX, float fY, float fZ) {SetVector(lpName, Vect(fX, fY, fZ));}
//-------------------------------------------------------------
virtual bool GetFloat(CTSTR lpName, float &fValue) const {return false;}
virtual bool GetInt(CTSTR lpName, int &iValue) const {return false;}
virtual bool GetString(CTSTR lpName, String &strVal) const {return false;}
virtual bool GetVector(CTSTR lpName, Vect &value) const {return false;}
virtual bool GetVector2(CTSTR lpName, Vect2 &value) const {return false;}
virtual bool GetVector4(CTSTR lpName, Vect4 &value) const {return false;}
virtual bool GetMatrix(CTSTR lpName, Matrix &mat) const {return false;}
}; };

View File

@ -120,6 +120,7 @@ BASE_EXPORT void STDCALL OSFreeLibrary(HANDLE hLibrary);
BASE_EXPORT void STDCALL OSSleep(DWORD dwMSeconds); BASE_EXPORT void STDCALL OSSleep(DWORD dwMSeconds);
BASE_EXPORT UINT STDCALL OSGetProcessorCount();
BASE_EXPORT HANDLE STDCALL OSCreateThread(XTHREAD lpThreadFunc, LPVOID param); BASE_EXPORT HANDLE STDCALL OSCreateThread(XTHREAD lpThreadFunc, LPVOID param);
BASE_EXPORT BOOL STDCALL OSWaitForThread(HANDLE hThread, LPDWORD ret); BASE_EXPORT BOOL STDCALL OSWaitForThread(HANDLE hThread, LPDWORD ret);
BASE_EXPORT BOOL STDCALL OSCloseThread(HANDLE hThread); BASE_EXPORT BOOL STDCALL OSCloseThread(HANDLE hThread);

View File

@ -40,6 +40,7 @@ DWORD startTick;
void STDCALL InputProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); void STDCALL InputProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
void STDCALL ResetCursorClip(); void STDCALL ResetCursorClip();
SYSTEM_INFO si;
BOOL bHidingCursor = 0; BOOL bHidingCursor = 0;
@ -50,6 +51,8 @@ void STDCALL OSInit()
{ {
timeBeginPeriod(1); timeBeginPeriod(1);
GetSystemInfo(&si);
QueryPerformanceFrequency(&clockFreq); QueryPerformanceFrequency(&clockFreq);
QueryPerformanceCounter(&startTime); QueryPerformanceCounter(&startTime);
startTick = GetTickCount(); startTick = GetTickCount();
@ -160,9 +163,7 @@ void STDCALL OSFindClose(HANDLE hFind)
DWORD STDCALL OSGetSysPageSize() DWORD STDCALL OSGetSysPageSize()
{ {
SYSTEM_INFO SI; return si.dwPageSize;
GetSystemInfo(&SI);
return SI.dwPageSize;
} }
LPVOID STDCALL OSVirtualAlloc(size_t dwSize) LPVOID STDCALL OSVirtualAlloc(size_t dwSize)
@ -445,6 +446,11 @@ QWORD STDCALL OSGetTimeMicroseconds()
} }
UINT STDCALL OSGetProcessorCount()
{
return si.dwNumberOfProcessors;
}
HANDLE STDCALL OSCreateThread(XTHREAD lpThreadFunc, LPVOID param) HANDLE STDCALL OSCreateThread(XTHREAD lpThreadFunc, LPVOID param)
{ {
DWORD dummy; DWORD dummy;

View File

@ -276,13 +276,17 @@ HANDLE D3D10Shader::GetParameterByName(CTSTR lpName) const
return NULL; return NULL;
} }
#define GetValidHandle() \ /*#define GetValidHandle() \
ShaderParam *param = (ShaderParam*)hObject; \ ShaderParam *param = (ShaderParam*)hObject; \
if(!hObject) \ if(!hObject) \
{ \ { \
AppWarning(TEXT("Invalid handle input as shader parameter")); \ AppWarning(TEXT("Invalid handle input as shader parameter")); \
return; \ return; \
} }*/
#define GetValidHandle() \
ShaderParam *param = (ShaderParam*)hObject; \
if(!hObject) \
return;
void D3D10Shader::GetParameterInfo(HANDLE hObject, ShaderParameterInfo &paramInfo) const void D3D10Shader::GetParameterInfo(HANDLE hObject, ShaderParameterInfo &paramInfo) const

View File

@ -72,7 +72,7 @@ D3D10System::D3D10System()
swapDesc.SampleDesc.Count = 1; swapDesc.SampleDesc.Count = 1;
swapDesc.Windowed = TRUE; swapDesc.Windowed = TRUE;
bDisableCompatibilityMode = AppConfig->GetInt(TEXT("Video"), TEXT("DisableD3DCompatibilityMode"), 1) != 0; bDisableCompatibilityMode = 1;//AppConfig->GetInt(TEXT("Video"), TEXT("DisableD3DCompatibilityMode"), 1) != 0;
UINT createFlags = D3D10_CREATE_DEVICE_BGRA_SUPPORT; UINT createFlags = D3D10_CREATE_DEVICE_BGRA_SUPPORT;
if(GlobalConfig->GetInt(TEXT("General"), TEXT("UseDebugD3D"))) if(GlobalConfig->GetInt(TEXT("General"), TEXT("UseDebugD3D")))

View File

@ -209,13 +209,13 @@ public:
return false; return false;
} }
if(!bFirstFrameProcessed && nalNum) /*if(!bFirstFrameProcessed && nalNum)
{ {
delayTime = -picOut.i_dts; delayTime = -picOut.i_dts;
bFirstFrameProcessed = true; bFirstFrameProcessed = true;
} }*/
int timeOffset = int(INT64(picOut.i_pts+delayTime)-INT64(outputTimestamp)); int timeOffset = int(INT64(picOut.i_pts/*+delayTime*/)-INT64(outputTimestamp));
//Log(TEXT("dts: %d, pts: %d, timestamp: %d, offset: %d"), picOut.i_dts, picOut.i_pts, outputTimestamp, timeOffset); //Log(TEXT("dts: %d, pts: %d, timestamp: %d, offset: %d"), picOut.i_dts, picOut.i_pts, outputTimestamp, timeOffset);
timeOffset = htonl(timeOffset); timeOffset = htonl(timeOffset);

View File

@ -46,6 +46,26 @@ public:
String strName = data->GetString(TEXT("name")); String strName = data->GetString(TEXT("name"));
globalSource = App->GetGlobalSource(strName); globalSource = App->GetGlobalSource(strName);
} }
//-------------------------------------------------------------
virtual void SetFloat(CTSTR lpName, float fValue) {globalSource->SetFloat(lpName, fValue);}
virtual void SetInt(CTSTR lpName, int iValue) {globalSource->SetInt(lpName, iValue);}
virtual void SetString(CTSTR lpName, CTSTR lpVal) {globalSource->SetString(lpName, lpVal);}
virtual void SetVector(CTSTR lpName, const Vect &value) {globalSource->SetVector(lpName, value);}
virtual void SetVector2(CTSTR lpName, const Vect2 &value) {globalSource->SetVector2(lpName, value);}
virtual void SetVector4(CTSTR lpName, const Vect4 &value) {globalSource->SetVector4(lpName, value);}
virtual void SetMatrix(CTSTR lpName, const Matrix &mat) {globalSource->SetMatrix(lpName, mat);}
//-------------------------------------------------------------
virtual bool GetFloat(CTSTR lpName, float &fValue) const {return globalSource->GetFloat(lpName, fValue);}
virtual bool GetInt(CTSTR lpName, int &iValue) const {return globalSource->GetInt(lpName, iValue);}
virtual bool GetString(CTSTR lpName, String &strVal) const {return globalSource->GetString(lpName, strVal);}
virtual bool GetVector(CTSTR lpName, Vect &value) const {return globalSource->GetVector(lpName, value);}
virtual bool GetVector2(CTSTR lpName, Vect2 &value) const {return globalSource->GetVector2(lpName, value);}
virtual bool GetVector4(CTSTR lpName, Vect4 &value) const {return globalSource->GetVector4(lpName, value);}
virtual bool GetMatrix(CTSTR lpName, Matrix &mat) const {return globalSource->GetMatrix(lpName, mat);}
}; };

View File

@ -19,7 +19,7 @@
#include "Main.h" #include "Main.h"
void Convert444to420(LPBYTE input, int width, int pitch, int height, LPBYTE *output, bool bSSE2Available) void Convert444to420(LPBYTE input, int width, int pitch, int height, int startY, int endY, LPBYTE *output, bool bSSE2Available)
{ {
traceIn(Convert444to420); traceIn(Convert444to420);
@ -33,7 +33,7 @@ void Convert444to420(LPBYTE input, int width, int pitch, int height, LPBYTE *out
__m128i lumMask = _mm_set1_epi32(0x0000FF00); __m128i lumMask = _mm_set1_epi32(0x0000FF00);
__m128i uvMask = _mm_set1_epi16(0x00FF); __m128i uvMask = _mm_set1_epi16(0x00FF);
for(int y=0; y<height; y+=2) for(int y=startY; y<endY; y+=2)
{ {
int yPos = y*pitch; int yPos = y*pitch;
int chrYPos = ((y>>1)*chrPitch); int chrYPos = ((y>>1)*chrPitch);
@ -77,7 +77,7 @@ void Convert444to420(LPBYTE input, int width, int pitch, int height, LPBYTE *out
else else
{ {
#ifdef _WIN64 #ifdef _WIN64
for(int y=0; y<height; y+=2) for(int y=startY; y<endY; y+=2)
{ {
int yPos = y*pitch; int yPos = y*pitch;
int chrYPos = ((y>>1)*chrPitch); int chrYPos = ((y>>1)*chrPitch);
@ -106,7 +106,7 @@ void Convert444to420(LPBYTE input, int width, int pitch, int height, LPBYTE *out
} }
} }
#else #else
for(int y=0; y<height; y+=2) for(int y=startY; y<endY; y+=2)
{ {
int yPos = y*pitch; int yPos = y*pitch;
int chrYPos = ((y>>1)*chrPitch); int chrYPos = ((y>>1)*chrPitch);

View File

@ -138,8 +138,6 @@ public:
{ {
strFile = lpFile; strFile = lpFile;
EnableMemoryTracking(TRUE, 8);
if(!fileOut.Open(lpFile, XFILE_CREATEALWAYS, 1024*1024)) if(!fileOut.Open(lpFile, XFILE_CREATEALWAYS, 1024*1024))
return false; return false;

View File

@ -61,8 +61,8 @@ extern ConfigFile *AppConfig;
extern OBS *App; extern OBS *App;
extern TCHAR lpAppDataPath[MAX_PATH]; extern TCHAR lpAppDataPath[MAX_PATH];
#define OBS_VERSION 0x000421 #define OBS_VERSION 0x000429
#define OBS_VERSION_STRING_ANSI "Open Broadcaster Software v0.421a" #define OBS_VERSION_STRING_ANSI "Open Broadcaster Software v0.43a [test version 1]"
#define OBS_VERSION_STRING TEXT(OBS_VERSION_STRING_ANSI) #define OBS_VERSION_STRING TEXT(OBS_VERSION_STRING_ANSI)
#define OBS_WINDOW_CLASS TEXT("OBSWindowClass") #define OBS_WINDOW_CLASS TEXT("OBSWindowClass")

View File

@ -66,7 +66,7 @@ VideoFileStream* CreateMP4FileStream(CTSTR lpFile);
VideoFileStream* CreateFLVFileStream(CTSTR lpFile); VideoFileStream* CreateFLVFileStream(CTSTR lpFile);
//VideoFileStream* CreateAVIFileStream(CTSTR lpFile); //VideoFileStream* CreateAVIFileStream(CTSTR lpFile);
void Convert444to420(LPBYTE input, int width, int pitch, int height, LPBYTE *output, bool bSSE2Available); void Convert444to420(LPBYTE input, int width, int pitch, int height, int startY, int endY, LPBYTE *output, bool bSSE2Available);
void STDCALL SceneHotkey(DWORD hotkey, UPARAM param, bool bDown); void STDCALL SceneHotkey(DWORD hotkey, UPARAM param, bool bDown);
@ -410,6 +410,8 @@ public:
virtual void GetRenderFrameSize(UINT &width, UINT &height) const {App->GetRenderFrameSize(width, height);} virtual void GetRenderFrameSize(UINT &width, UINT &height) const {App->GetRenderFrameSize(width, height);}
virtual void GetOutputSize(UINT &width, UINT &height) const {App->GetOutputSize(width, height);} virtual void GetOutputSize(UINT &width, UINT &height) const {App->GetOutputSize(width, height);}
virtual UINT GetMaxFPS() const {return App->bRunning ? App->fps : AppConfig->GetInt(TEXT("Video"), TEXT("FPS"), 30);}
virtual CTSTR GetLanguage() const {return App->strLanguage;} virtual CTSTR GetLanguage() const {return App->strLanguage;}
virtual CTSTR GetAppDataPath() const {return lpAppDataPath;} virtual CTSTR GetAppDataPath() const {return lpAppDataPath;}
@ -535,7 +537,7 @@ OBS::OBS()
int y = (fullscreenY/2)-(cy/2); int y = (fullscreenY/2)-(cy/2);
hwndMain = CreateWindowEx(WS_EX_CONTROLPARENT|WS_EX_WINDOWEDGE, OBS_WINDOW_CLASS, OBS_VERSION_STRING, hwndMain = CreateWindowEx(WS_EX_CONTROLPARENT|WS_EX_WINDOWEDGE, OBS_WINDOW_CLASS, OBS_VERSION_STRING,
WS_OVERLAPPED | WS_THICKFRAME | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU, WS_OVERLAPPED | WS_THICKFRAME | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU | WS_CLIPCHILDREN,
x, y, cx, cy, NULL, NULL, hinstMain, NULL); x, y, cx, cy, NULL, NULL, hinstMain, NULL);
if(!hwndMain) if(!hwndMain)
CrashError(TEXT("Could not create main window")); CrashError(TEXT("Could not create main window"));
@ -866,7 +868,7 @@ OBS::~OBS()
pluginInfo.strFile.Clear(); pluginInfo.strFile.Clear();
} }
DestroyWindow(hwndMain); //DestroyWindow(hwndMain);
AppConfig->SetInt(TEXT("General"), TEXT("Width"), clientWidth); AppConfig->SetInt(TEXT("General"), TEXT("Width"), clientWidth);
AppConfig->SetInt(TEXT("General"), TEXT("Height"), clientHeight); AppConfig->SetInt(TEXT("General"), TEXT("Height"), clientHeight);
@ -1200,11 +1202,14 @@ void OBS::Start()
td.Usage = D3D10_USAGE_STAGING; td.Usage = D3D10_USAGE_STAGING;
td.CPUAccessFlags = D3D10_CPU_ACCESS_READ; td.CPUAccessFlags = D3D10_CPU_ACCESS_READ;
HRESULT err = GetD3D()->CreateTexture2D(&td, NULL, &copyTexture); for(UINT i=0; i<2; i++)
if(FAILED(err))
{ {
CrashError(TEXT("Unable to create copy texture")); HRESULT err = GetD3D()->CreateTexture2D(&td, NULL, &copyTextures[i]);
//todo - better error handling if(FAILED(err))
{
CrashError(TEXT("Unable to create copy texture"));
//todo - better error handling
}
} }
//------------------------------------------------------------- //-------------------------------------------------------------
@ -1395,20 +1400,14 @@ void OBS::SetStatusBarData()
{ {
HWND hwndStatusBar = GetDlgItem(hwndMain, ID_STATUS); HWND hwndStatusBar = GetDlgItem(hwndMain, ID_STATUS);
String strInfo = GetMostImportantInfo(); SendMessage(hwndStatusBar, WM_SETREDRAW, 0, 0);
SendMessage(hwndStatusBar, SB_SETTEXT, 0, (LPARAM)strInfo.Array()); SendMessage(hwndStatusBar, SB_SETTEXT, 0 | SBT_OWNERDRAW, NULL);
SendMessage(hwndStatusBar, SB_SETTEXT, 1 | SBT_OWNERDRAW, NULL);
String strDroppedFrames; SendMessage(hwndStatusBar, SB_SETTEXT, 2 | SBT_OWNERDRAW, NULL);
strDroppedFrames << Str("MainWindow.DroppedFrames") << TEXT(" ") << IntString(curFramesDropped);
SendMessage(hwndStatusBar, SB_SETTEXT, 1, (LPARAM)strDroppedFrames.Array());
String strCaptureFPS;
strCaptureFPS << TEXT("FPS: ") << IntString(captureFPS);
SendMessage(hwndStatusBar, SB_SETTEXT, 2, (LPARAM)strCaptureFPS.Array());
statusBarData.bytesPerSec = bytesPerSec;
statusBarData.strain = curStrain;
SendMessage(hwndStatusBar, SB_SETTEXT, 3 | SBT_OWNERDRAW, NULL); SendMessage(hwndStatusBar, SB_SETTEXT, 3 | SBT_OWNERDRAW, NULL);
SendMessage(hwndStatusBar, WM_SETREDRAW, 1, 0);
InvalidateRect(hwndStatusBar, NULL, FALSE);
} }
void OBS::DrawStatusBar(DRAWITEMSTRUCT &dis) void OBS::DrawStatusBar(DRAWITEMSTRUCT &dis)
@ -1416,49 +1415,86 @@ void OBS::DrawStatusBar(DRAWITEMSTRUCT &dis)
if(!App->bRunning) if(!App->bRunning)
return; return;
DWORD green = 0xFF, red = 0xFF;
if(statusBarData.strain > 50.0)
green = DWORD(((50.0-(statusBarData.strain-50.0))/50.0)*255.0);
double redStrain = statusBarData.strain/50.0;
if(redStrain > 1.0)
redStrain = 1.0;
red = DWORD(redStrain*255.0);
HDC hdcTemp = CreateCompatibleDC(dis.hDC); HDC hdcTemp = CreateCompatibleDC(dis.hDC);
HBITMAP hbmpTemp = CreateCompatibleBitmap(dis.hDC, dis.rcItem.right-dis.rcItem.left, dis.rcItem.bottom-dis.rcItem.top); HBITMAP hbmpTemp = CreateCompatibleBitmap(dis.hDC, dis.rcItem.right-dis.rcItem.left, dis.rcItem.bottom-dis.rcItem.top);
SelectObject(hdcTemp, hbmpTemp); SelectObject(hdcTemp, hbmpTemp);
BitBlt(hdcTemp, 0, 0, dis.rcItem.right-dis.rcItem.left, dis.rcItem.bottom-dis.rcItem.top, dis.hDC, dis.rcItem.left, dis.rcItem.top, SRCCOPY);
SelectObject(hdcTemp, GetCurrentObject(dis.hDC, OBJ_FONT)); SelectObject(hdcTemp, GetCurrentObject(dis.hDC, OBJ_FONT));
//-------------------------------- //HBRUSH hColorBrush = CreateSolidBrush((green<<8)|red);
HBRUSH hColorBrush = CreateSolidBrush((green<<8)|red);
RECT rc = {0, 0, 20, 20};
FillRect(hdcTemp, &rc, hColorBrush);
DeleteObject(hColorBrush);
//--------------------------------
RECT rc;
mcpy(&rc, &dis.rcItem, sizeof(rc)); mcpy(&rc, &dis.rcItem, sizeof(rc));
rc.left += 22;
rc.left -= dis.rcItem.left; rc.left -= dis.rcItem.left;
rc.right -= dis.rcItem.left; rc.right -= dis.rcItem.left;
rc.top -= dis.rcItem.top; rc.top -= dis.rcItem.top;
rc.bottom -= dis.rcItem.top; rc.bottom -= dis.rcItem.top;
SetBkMode(hdcTemp, TRANSPARENT); FillRect(hdcTemp, &rc, (HBRUSH)(COLOR_BTNFACE+1));
String strKBPS; //DeleteObject(hColorBrush);
strKBPS << IntString((statusBarData.bytesPerSec*8) >> 10) << TEXT("kb/s");
DrawText(hdcTemp, strKBPS, strKBPS.Length(), &rc, DT_VCENTER|DT_SINGLELINE|DT_LEFT); //--------------------------------
if(dis.itemID == 3)
{
DWORD green = 0xFF, red = 0xFF;
statusBarData.bytesPerSec = App->bytesPerSec;
statusBarData.strain = App->curStrain;
//statusBarData.strain = rand()%101;
if(statusBarData.strain > 50.0)
green = DWORD(((50.0-(statusBarData.strain-50.0))/50.0)*255.0);
double redStrain = statusBarData.strain/50.0;
if(redStrain > 1.0)
redStrain = 1.0;
red = DWORD(redStrain*255.0);
//--------------------------------
HBRUSH hColorBrush = CreateSolidBrush((green<<8)|red);
RECT rcBox = {0, 0, 20, 20};
/*rc.left += dis.rcItem.left;
rc.right += dis.rcItem.left;
rc.top += dis.rcItem.top;
rc.bottom += dis.rcItem.top;*/
FillRect(hdcTemp, &rcBox, hColorBrush);
DeleteObject(hColorBrush);
//--------------------------------
SetBkMode(hdcTemp, TRANSPARENT);
rc.left += 22;
String strKBPS;
strKBPS << IntString((statusBarData.bytesPerSec*8) >> 10) << TEXT("kb/s");
//strKBPS << IntString(rand()) << TEXT("kb/s");
DrawText(hdcTemp, strKBPS, strKBPS.Length(), &rc, DT_VCENTER|DT_SINGLELINE|DT_LEFT);
}
else
{
String strOutString;
switch(dis.itemID)
{
case 0: strOutString << App->GetMostImportantInfo(); break;
case 1: strOutString << Str("MainWindow.DroppedFrames") << TEXT(" ") << IntString(App->curFramesDropped); break;
case 2: strOutString << TEXT("FPS: ") << IntString(App->captureFPS); break;
}
if(strOutString.IsValid())
{
SetBkMode(hdcTemp, TRANSPARENT);
DrawText(hdcTemp, strOutString, strOutString.Length(), &rc, DT_VCENTER|DT_SINGLELINE|DT_LEFT);
}
}
//-------------------------------- //--------------------------------
@ -1558,7 +1594,10 @@ void OBS::Stop()
yuvRenderTextures[i] = NULL; yuvRenderTextures[i] = NULL;
} }
SafeRelease(copyTexture); for(UINT i=0; i<2; i++)
{
SafeRelease(copyTextures[i]);
}
delete transitionTexture; delete transitionTexture;
transitionTexture = NULL; transitionTexture = NULL;
@ -1685,11 +1724,25 @@ DWORD STDCALL OBS::MainAudioThread(LPVOID lpUnused)
return 0; return 0;
} }
struct Convert444Data
{
LPBYTE input;
LPBYTE output[3];
int width, height, pitch, startY, endY;
};
DWORD STDCALL Convert444Thread(Convert444Data *data)
{
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
Convert444to420(data->input, data->width, data->pitch, data->height, data->startY, data->endY, data->output, App->SSE2Available());
return 0;
}
void OBS::MainCaptureLoop() void OBS::MainCaptureLoop()
{ {
traceIn(OBS::MainCaptureLoop); traceIn(OBS::MainCaptureLoop);
int curRenderTarget = 0, curCopyTexture = 0; int curRenderTarget = 0, curYUVTexture = 0, curCopyTexture = 0;
int copyWait = NUM_RENDER_BUFFERS-1; int copyWait = NUM_RENDER_BUFFERS-1;
UINT curStreamTime = 0, firstFrameTime = OSGetTime(), lastStreamTime = 0; UINT curStreamTime = 0, firstFrameTime = OSGetTime(), lastStreamTime = 0;
UINT lastPTSVal = 0, lastUnmodifiedPTSVal = 0; UINT lastPTSVal = 0, lastUnmodifiedPTSVal = 0;
@ -1707,16 +1760,23 @@ void OBS::MainCaptureLoop()
LPVOID nullBuff = NULL; LPVOID nullBuff = NULL;
x264_picture_t picOut; x264_picture_t outPics[2];
x264_picture_init(&picOut); x264_picture_init(&outPics[0]);
x264_picture_init(&outPics[1]);
if(bUsing444) if(bUsing444)
{ {
picOut.img.i_csp = X264_CSP_BGRA; //although the x264 input says BGR, x264 actually will expect packed UYV outPics[0].img.i_csp = X264_CSP_BGRA; //although the x264 input says BGR, x264 actually will expect packed UYV
picOut.img.i_plane = 1; outPics[0].img.i_plane = 1;
outPics[1].img.i_csp = X264_CSP_BGRA;
outPics[1].img.i_plane = 1;
} }
else else
x264_picture_alloc(&picOut, X264_CSP_I420, outputCX, outputCY); {
x264_picture_alloc(&outPics[0], X264_CSP_I420, outputCX, outputCY);
x264_picture_alloc(&outPics[1], X264_CSP_I420, outputCX, outputCY);
}
int curPTS = 0; int curPTS = 0;
@ -1736,6 +1796,29 @@ void OBS::MainCaptureLoop()
float bpsTime = 0.0f; float bpsTime = 0.0f;
double lastStrain = 0.0f; double lastStrain = 0.0f;
UINT numProcessors = OSGetProcessorCount();
HANDLE *h420Threads = (HANDLE*)Allocate(sizeof(HANDLE)*numProcessors);
Convert444Data *convertInfo = (Convert444Data*)Allocate(sizeof(Convert444Data)*numProcessors);
zero(h420Threads, sizeof(HANDLE)*numProcessors);
zero(convertInfo, sizeof(Convert444Data)*numProcessors);
for(UINT i=0; i<numProcessors; i++)
{
convertInfo[i].width = outputCX;
convertInfo[i].height = outputCY;
if(i == 0)
convertInfo[i].startY = 0;
else
convertInfo[i].startY = convertInfo[i-1].endY;
if(i == (numProcessors-1))
convertInfo[i].endY = outputCY;
else
convertInfo[i].endY = ((outputCY/numProcessors)*(i+1)) & 0xFFFFFFFE;
}
DWORD fpsTimeNumerator = 1000-(frameTime*fps); DWORD fpsTimeNumerator = 1000-(frameTime*fps);
DWORD fpsTimeDenominator = fps; DWORD fpsTimeDenominator = fps;
DWORD fpsTimeAdjust = 0; DWORD fpsTimeAdjust = 0;
@ -1743,6 +1826,8 @@ void OBS::MainCaptureLoop()
DWORD fpsCounter = 0; DWORD fpsCounter = 0;
bool bFirstFrame = true; bool bFirstFrame = true;
bool bFirst420Encode = true;
bool bUseThreaded420 = OSGetProcessorCount() > 1 && !bUsing444;
while(bRunning) while(bRunning)
{ {
@ -1999,36 +2084,75 @@ void OBS::MainCaptureLoop()
bFirstFrame = false; bFirstFrame = false;
} }
}
if(!bEncode) if(!bEncode)
{ {
if(curCopyTexture == (NUM_RENDER_BUFFERS-1)) if(curYUVTexture == (NUM_RENDER_BUFFERS-1))
curCopyTexture = 0; curYUVTexture = 0;
else else
curCopyTexture++; curYUVTexture++;
}
} }
} }
if(bEncode) if(bEncode)
{ {
UINT prevCopyTexture = (curCopyTexture+1) & 1;
ID3D10Texture2D *copyTexture = copyTextures[curCopyTexture];
profileIn("CopyResource"); profileIn("CopyResource");
D3D10Texture *d3dYUV = static_cast<D3D10Texture*>(yuvRenderTextures[curCopyTexture]);
if(!bFirst420Encode && bUseThreaded420)
{
WaitForMultipleObjects(numProcessors, h420Threads, TRUE, INFINITE);
for(UINT i=0; i<numProcessors; i++)
{
CloseHandle(h420Threads[i]);
h420Threads[i] = NULL;
}
copyTexture->Unmap(0);
}
D3D10Texture *d3dYUV = static_cast<D3D10Texture*>(yuvRenderTextures[curYUVTexture]);
GetD3D()->CopyResource(copyTexture, d3dYUV->texture); GetD3D()->CopyResource(copyTexture, d3dYUV->texture);
profileOut; profileOut;
ID3D10Texture2D *prevTexture = copyTextures[prevCopyTexture];
D3D10_MAPPED_TEXTURE2D map; D3D10_MAPPED_TEXTURE2D map;
if(SUCCEEDED(copyTexture->Map(0, D3D10_MAP_READ, 0, &map))) if(SUCCEEDED(prevTexture->Map(0, D3D10_MAP_READ, 0, &map)))
{ {
List<DataPacket> videoPackets; List<DataPacket> videoPackets;
List<PacketType> videoPacketTypes; List<PacketType> videoPacketTypes;
x264_picture_t &picOut = outPics[prevCopyTexture];
if(!bUsing444) if(!bUsing444)
{ {
profileIn("conversion to 4:2:0"); profileIn("conversion to 4:2:0");
Convert444to420((LPBYTE)map.pData, outputCX, map.RowPitch, outputCY, picOut.img.plane, SSE2Available()); if(bUseThreaded420)
copyTexture->Unmap(0); {
x264_picture_t &newPicOut = outPics[curCopyTexture];
for(UINT i=0; i<numProcessors; i++)
{
convertInfo[i].input = (LPBYTE)map.pData;
convertInfo[i].pitch = map.RowPitch;
convertInfo[i].output[0] = newPicOut.img.plane[0];
convertInfo[i].output[1] = newPicOut.img.plane[1];
convertInfo[i].output[2] = newPicOut.img.plane[2];
h420Threads[i] = OSCreateThread((XTHREAD)Convert444Thread, (LPVOID)(convertInfo+i));
}
}
else
{
Convert444to420((LPBYTE)map.pData, outputCX, map.RowPitch, outputCY, 0, outputCY, picOut.img.plane, SSE2Available());
prevTexture->Unmap(0);
}
if(bFirst420Encode)
bFirst420Encode = bEncode = false;
profileOut; profileOut;
} }
@ -2038,127 +2162,132 @@ void OBS::MainCaptureLoop()
picOut.img.plane[0] = (uint8_t*)map.pData; picOut.img.plane[0] = (uint8_t*)map.pData;
} }
//------------------------------------ if(bEncode)
// get timestamps
DWORD curTimeStamp = 0;
DWORD curPTSVal = 0;
curTimeStamp = bufferedTimes[0];
curPTSVal = bufferedTimes[curPTS++];
if(bUseSyncFix)
{ {
DWORD savedPTSVal = curPTSVal; //------------------------------------
// get timestamps
if(curPTSVal != 0) DWORD curTimeStamp = 0;
DWORD curPTSVal = 0;
curTimeStamp = bufferedTimes[0];
curPTSVal = bufferedTimes[curPTS++];
if(bUseSyncFix)
{ {
/*int toleranceVal = int(lastPTSVal+frameTime); DWORD savedPTSVal = curPTSVal;
int toleranceOffset = (int(curPTSVal)-toleranceVal);
int halfFrameTime = int(frameTime/2);
if(toleranceOffset > halfFrameTime) if(curPTSVal != 0)
curPTSVal = DWORD(toleranceVal+(toleranceOffset-halfFrameTime));
else if(toleranceOffset < -halfFrameTime)
curPTSVal = DWORD(toleranceVal+(toleranceOffset+halfFrameTime));
else
curPTSVal = DWORD(toleranceVal);*/
//this turned out to be much better than the previous way I was doing it.
//if the FPS is set to about the same as the capture FPS, this works pretty much flawlessly.
//100% calculated timestamps that are almost fully accurate with no CPU timers involved,
//while still fully allowing any potential unexpected frame variability.
curPTSVal = lastPTSVal+frameTimeAdjust;
if(curPTSVal < lastUnmodifiedPTSVal)
curPTSVal = lastUnmodifiedPTSVal;
bufferedTimes[curPTS-1] = curPTSVal;
}
lastUnmodifiedPTSVal = savedPTSVal;
lastPTSVal = curPTSVal;
//Log(TEXT("val: %u - adjusted: %u"), savedPTSVal, curPTSVal);
}
picOut.i_pts = curPTSVal;
//------------------------------------
// encode
profileIn("call to encoder");
videoEncoder->Encode(&picOut, videoPackets, videoPacketTypes, curTimeStamp);
if(bUsing444) copyTexture->Unmap(0);
profileOut;
//------------------------------------
// upload
bool bSendingVideo = videoPackets.Num() > 0;
//send headers before the first frame if not yet sent
if(bSendingVideo)
{
if(!bSentHeaders)
{
network->BeginPublishing();
bSentHeaders = true;
DataPacket seiPacket;
videoEncoder->GetSEI(seiPacket);
network->SendPacket(seiPacket.lpPacket, seiPacket.size, 0, PacketType_VideoHighest);
}
OSEnterMutex(hSoundDataMutex);
if(pendingAudioFrames.Num())
{
//Log(TEXT("pending frames %u, (in milliseconds): %u"), pendingAudioFrames.Num(), pendingAudioFrames.Last().timestamp-pendingAudioFrames[0].timestamp);
while(pendingAudioFrames.Num() && pendingAudioFrames[0].timestamp < curTimeStamp)
{ {
List<BYTE> &audioData = pendingAudioFrames[0].audioData; /*int toleranceVal = int(lastPTSVal+frameTime);
int toleranceOffset = (int(curPTSVal)-toleranceVal);
int halfFrameTime = int(frameTime/2);
if(audioData.Num()) if(toleranceOffset > halfFrameTime)
{ curPTSVal = DWORD(toleranceVal+(toleranceOffset-halfFrameTime));
network->SendPacket(audioData.Array(), audioData.Num(), pendingAudioFrames[0].timestamp, PacketType_Audio); else if(toleranceOffset < -halfFrameTime)
if(fileStream) curPTSVal = DWORD(toleranceVal+(toleranceOffset+halfFrameTime));
fileStream->AddPacket(audioData.Array(), audioData.Num(), pendingAudioFrames[0].timestamp, PacketType_Audio); else
curPTSVal = DWORD(toleranceVal);*/
audioData.Clear(); //this turned out to be much better than the previous way I was doing it.
} //if the FPS is set to about the same as the capture FPS, this works pretty much flawlessly.
//100% calculated timestamps that are almost fully accurate with no CPU timers involved,
//while still fully allowing any potential unexpected frame variability.
curPTSVal = lastPTSVal+frameTimeAdjust;
if(curPTSVal < lastUnmodifiedPTSVal)
curPTSVal = lastUnmodifiedPTSVal;
//Log(TEXT("audio packet timestamp: %u"), pendingAudioFrames[0].timestamp); bufferedTimes[curPTS-1] = curPTSVal;
pendingAudioFrames[0].audioData.Clear();
pendingAudioFrames.Remove(0);
} }
lastUnmodifiedPTSVal = savedPTSVal;
lastPTSVal = curPTSVal;
//Log(TEXT("val: %u - adjusted: %u"), savedPTSVal, curPTSVal);
} }
OSLeaveMutex(hSoundDataMutex); picOut.i_pts = curPTSVal;
for(UINT i=0; i<videoPackets.Num(); i++) //------------------------------------
// encode
profileIn("call to encoder");
videoEncoder->Encode(&picOut, videoPackets, videoPacketTypes, curTimeStamp);
if(bUsing444) prevTexture->Unmap(0);
profileOut;
//------------------------------------
// upload
bool bSendingVideo = videoPackets.Num() > 0;
//send headers before the first frame if not yet sent
if(bSendingVideo)
{ {
DataPacket &packet = videoPackets[i]; if(!bSentHeaders)
PacketType type = videoPacketTypes[i]; {
network->BeginPublishing();
bSentHeaders = true;
network->SendPacket(packet.lpPacket, packet.size, curTimeStamp, type); DataPacket seiPacket;
if(fileStream) videoEncoder->GetSEI(seiPacket);
fileStream->AddPacket(packet.lpPacket, packet.size, curTimeStamp, type);
network->SendPacket(seiPacket.lpPacket, seiPacket.size, 0, PacketType_VideoHighest);
}
OSEnterMutex(hSoundDataMutex);
if(pendingAudioFrames.Num())
{
//Log(TEXT("pending frames %u, (in milliseconds): %u"), pendingAudioFrames.Num(), pendingAudioFrames.Last().timestamp-pendingAudioFrames[0].timestamp);
while(pendingAudioFrames.Num() && pendingAudioFrames[0].timestamp < curTimeStamp)
{
List<BYTE> &audioData = pendingAudioFrames[0].audioData;
if(audioData.Num())
{
network->SendPacket(audioData.Array(), audioData.Num(), pendingAudioFrames[0].timestamp, PacketType_Audio);
if(fileStream)
fileStream->AddPacket(audioData.Array(), audioData.Num(), pendingAudioFrames[0].timestamp, PacketType_Audio);
audioData.Clear();
}
//Log(TEXT("audio packet timestamp: %u"), pendingAudioFrames[0].timestamp);
pendingAudioFrames[0].audioData.Clear();
pendingAudioFrames.Remove(0);
}
}
OSLeaveMutex(hSoundDataMutex);
for(UINT i=0; i<videoPackets.Num(); i++)
{
DataPacket &packet = videoPackets[i];
PacketType type = videoPacketTypes[i];
network->SendPacket(packet.lpPacket, packet.size, curTimeStamp, type);
if(fileStream)
fileStream->AddPacket(packet.lpPacket, packet.size, curTimeStamp, type);
}
curPTS--;
bufferedTimes.Remove(0);
} }
curPTS--;
bufferedTimes.Remove(0);
} }
} }
if(curCopyTexture == (NUM_RENDER_BUFFERS-1)) curCopyTexture = prevCopyTexture;
curCopyTexture = 0;
if(curYUVTexture == (NUM_RENDER_BUFFERS-1))
curYUVTexture = 0;
else else
curCopyTexture++; curYUVTexture++;
} }
lastRenderTarget = curRenderTarget; lastRenderTarget = curRenderTarget;
@ -2201,7 +2330,26 @@ void OBS::MainCaptureLoop()
} }
if(!bUsing444) if(!bUsing444)
x264_picture_clean(&picOut); {
if(!bFirst420Encode && bUseThreaded420)
{
WaitForMultipleObjects(numProcessors, h420Threads, TRUE, INFINITE);
for(UINT i=0; i<numProcessors; i++)
{
if(h420Threads)
CloseHandle(h420Threads[i]);
h420Threads[i] = NULL;
}
ID3D10Texture2D *copyTexture = copyTextures[curCopyTexture];
copyTexture->Unmap(0);
}
x264_picture_clean(&outPics[0]);
x264_picture_clean(&outPics[1]);
}
Free(h420Threads);
Free(convertInfo);
Log(TEXT("Total frames rendered: %d, number of frames that lagged: %d (%0.2f%%) (it's okay for some frames to lag)"), numTotalFrames, numLongFrames, (double(numLongFrames)/double(numTotalFrames))*100.0); Log(TEXT("Total frames rendered: %d, number of frames that lagged: %d (%0.2f%%) (it's okay for some frames to lag)"), numTotalFrames, numLongFrames, (double(numLongFrames)/double(numTotalFrames))*100.0);

View File

@ -329,7 +329,7 @@ class OBS
//--------------------------------------------------- //---------------------------------------------------
// graphics stuff // graphics stuff
ID3D10Texture2D *copyTexture; ID3D10Texture2D *copyTextures[2];
Texture *mainRenderTextures[NUM_RENDER_BUFFERS]; Texture *mainRenderTextures[NUM_RENDER_BUFFERS];
Texture *yuvRenderTextures[NUM_RENDER_BUFFERS]; Texture *yuvRenderTextures[NUM_RENDER_BUFFERS];

View File

@ -42,7 +42,7 @@ struct NetworkPacket
}; };
//max latency in milliseconds allowed when using the send buffer //max latency in milliseconds allowed when using the send buffer
const DWORD maxBufferTime = 500; const DWORD maxBufferTime = 600;
class RTMPPublisher : public NetworkStream class RTMPPublisher : public NetworkStream
{ {
@ -57,7 +57,7 @@ class RTMPPublisher : public NetworkStream
int packetWaitType; int packetWaitType;
List<NetworkPacket> Packets; List<NetworkPacket> Packets;
UINT numVideoPackets; UINT numVideoPackets;
UINT maxVideoPackets, BFrameThreshold; UINT maxVideoPackets, BFrameThreshold, revertThreshold;
QWORD bytesSent; QWORD bytesSent;
@ -67,12 +67,14 @@ class RTMPPublisher : public NetworkStream
UINT numVideoPacketsBuffered; UINT numVideoPacketsBuffered;
DWORD firstBufferedVideoFrameTimestamp; DWORD firstBufferedVideoFrameTimestamp;
bool bPacketDumpMode;
BOOL bUseSendBuffer; BOOL bUseSendBuffer;
//all data sending is done in yet another separate thread to prevent blocking in the main capture thread //all data sending is done in yet another separate thread to prevent blocking in the main capture thread
void SendLoop() void SendLoop()
{ {
traceIn(RTMPPublisher::SendLoop); //traceIn(RTMPPublisher::SendLoop);
while(WaitForSingleObject(hSendSempahore, INFINITE) == WAIT_OBJECT_0 && !bStopping && RTMP_IsConnected(rtmp)) while(WaitForSingleObject(hSendSempahore, INFINITE) == WAIT_OBJECT_0 && !bStopping && RTMP_IsConnected(rtmp))
{ {
@ -168,7 +170,7 @@ class RTMPPublisher : public NetworkStream
bytesSent += packetData.Num(); bytesSent += packetData.Num();
} }
traceOut; //traceOut;
} }
static DWORD SendThread(RTMPPublisher *publisher) static DWORD SendThread(RTMPPublisher *publisher)
@ -289,7 +291,7 @@ class RTMPPublisher : public NetworkStream
{ {
if(packet.type >= packetWaitType) if(packet.type >= packetWaitType)
{ {
packetWaitType = PacketType_VideoDisposable; packetWaitType = (bPacketDumpMode) ? PacketType_VideoLow : PacketType_VideoDisposable;
break; break;
} }
else //clear out following dependent packets of lower priority else //clear out following dependent packets of lower priority
@ -353,30 +355,26 @@ class RTMPPublisher : public NetworkStream
static int BufferedSend(RTMPSockBuf *sb, const char *buf, int len, RTMPPublisher *network) static int BufferedSend(RTMPSockBuf *sb, const char *buf, int len, RTMPPublisher *network)
{ {
bool bComplete = false;
int fullLen = len; int fullLen = len;
bool bSentData = false;
int newTotal = network->curSendBufferLen+len; do
//would exceed buffer size, send instead.
if(newTotal >= int(network->sendBuffer.Num()))
{ {
int nBytes; int newTotal = network->curSendBufferLen+len;
//flush existing packets
nBytes = network->FlushSendBuffer();
if(nBytes < 0)
return nBytes;
if(nBytes == 0)
return 0;
//if packet is bigger than the send buffer, just send it straight up instead of buffering //buffer full, send
if((UINT)len > network->sendBuffer.Num()) if(newTotal >= int(network->sendBuffer.Num()))
{ {
const char *lpTemp = buf; int pendingBytes = newTotal-network->sendBuffer.Num();
int totalBytesSent = len; int copyCount = network->sendBuffer.Num()-network->curSendBufferLen;
mcpy(network->sendBuffer.Array()+network->curSendBufferLen, buf, copyCount);
BYTE *lpTemp = network->sendBuffer.Array();
int totalBytesSent = network->sendBuffer.Num();
while(totalBytesSent > 0) while(totalBytesSent > 0)
{ {
nBytes = send(sb->sb_socket, lpTemp, totalBytesSent, 0); int nBytes = send(sb->sb_socket, (const char*)lpTemp, totalBytesSent, 0);
if(nBytes < 0) if(nBytes < 0)
return nBytes; return nBytes;
if(nBytes == 0) if(nBytes == 0)
@ -386,17 +384,27 @@ class RTMPPublisher : public NetworkStream
lpTemp += nBytes; lpTemp += nBytes;
} }
len = 0; network->curSendBufferLen = 0;
if(pendingBytes)
{
buf += copyCount;
len -= copyCount;
}
else
bComplete = true;
} }
else
{
if(len)
{
mcpy(network->sendBuffer.Array()+network->curSendBufferLen, buf, len);
network->curSendBufferLen = newTotal;
}
network->numVideoPacketsBuffered = 0; bComplete = true;
} }
} while(!bComplete);
if(len > 0)
{
mcpy(network->sendBuffer.Array()+network->curSendBufferLen, buf, len);
network->curSendBufferLen += len;
}
return fullLen; return fullLen;
} }
@ -404,7 +412,7 @@ class RTMPPublisher : public NetworkStream
public: public:
RTMPPublisher(RTMP *rtmpIn, BOOL bUseSendBuffer, UINT sendBufferSize) RTMPPublisher(RTMP *rtmpIn, BOOL bUseSendBuffer, UINT sendBufferSize)
{ {
traceIn(RTMPPublisher::RTMPPublisher); //traceIn(RTMPPublisher::RTMPPublisher);
rtmp = rtmpIn; rtmp = rtmpIn;
@ -440,13 +448,14 @@ public:
BFrameThreshold = 30; //when it starts cutting out b frames BFrameThreshold = 30; //when it starts cutting out b frames
maxVideoPackets = 70; //when it starts cutting out p frames maxVideoPackets = 70; //when it starts cutting out p frames
revertThreshold = 2; //when it reverts to normal
traceOut; //traceOut;
} }
~RTMPPublisher() ~RTMPPublisher()
{ {
traceIn(RTMPPublisher::~RTMPPublisher); //traceIn(RTMPPublisher::~RTMPPublisher);
bStopping = true; bStopping = true;
ReleaseSemaphore(hSendSempahore, 1, NULL); ReleaseSemaphore(hSendSempahore, 1, NULL);
@ -475,12 +484,12 @@ public:
RTMP_Free(rtmp); RTMP_Free(rtmp);
} }
traceOut; //traceOut;
} }
void SendPacket(BYTE *data, UINT size, DWORD timestamp, PacketType type) void SendPacket(BYTE *data, UINT size, DWORD timestamp, PacketType type)
{ {
traceIn(RTMPPublisher::SendPacket); //traceIn(RTMPPublisher::SendPacket);
if(!bStopping) if(!bStopping)
{ {
@ -488,18 +497,25 @@ public:
paddedData.SetSize(size+RTMP_MAX_HEADER_SIZE); paddedData.SetSize(size+RTMP_MAX_HEADER_SIZE);
mcpy(paddedData.Array()+RTMP_MAX_HEADER_SIZE, data, size); mcpy(paddedData.Array()+RTMP_MAX_HEADER_SIZE, data, size);
if(bPacketDumpMode && Packets.Num() <= revertThreshold)
bPacketDumpMode = false;
bool bAddPacket = false;
if(type >= packetWaitType) if(type >= packetWaitType)
{ {
if(type != PacketType_Audio) if(type != PacketType_Audio)
{ {
packetWaitType = PacketType_VideoDisposable; packetWaitType = (bPacketDumpMode) ? PacketType_VideoLow : PacketType_VideoDisposable;
if(type <= PacketType_VideoHigh) if(type <= PacketType_VideoHigh)
numVideoPackets++; numVideoPackets++;
} }
//----------------- bAddPacket = true;
}
if(bAddPacket)
{
OSEnterMutex(hDataMutex); OSEnterMutex(hDataMutex);
NetworkPacket &netPacket = *Packets.CreateNew(); NetworkPacket &netPacket = *Packets.CreateNew();
@ -509,7 +525,16 @@ public:
//begin dumping b frames if there's signs of lag //begin dumping b frames if there's signs of lag
if(numVideoPackets > BFrameThreshold) if(numVideoPackets > BFrameThreshold)
{
if(!bPacketDumpMode)
{
bPacketDumpMode = true;
if(packetWaitType == PacketType_VideoDisposable)
packetWaitType = PacketType_VideoLow;
}
DumpBFrame(); DumpBFrame();
}
//begin dumping p frames if b frames aren't enough //begin dumping p frames if b frames aren't enough
if(numVideoPackets > maxVideoPackets) if(numVideoPackets > maxVideoPackets)
@ -546,12 +571,12 @@ public:
}*/ }*/
} }
traceOut; //traceOut;
} }
void BeginPublishing() void BeginPublishing()
{ {
traceIn(RTMPPublisher::BeginPublishing); //traceIn(RTMPPublisher::BeginPublishing);
RTMPPacket packet; RTMPPacket packet;
@ -619,7 +644,7 @@ public:
return; return;
} }
traceOut; //traceOut;
} }
double GetPacketStrain() const double GetPacketStrain() const
@ -640,7 +665,7 @@ public:
NetworkStream* CreateRTMPPublisher(String &failReason, bool &bCanRetry) NetworkStream* CreateRTMPPublisher(String &failReason, bool &bCanRetry)
{ {
traceIn(CreateRTMPPublisher); //traceIn(CreateRTMPPublisher);
//------------------------------------------------------ //------------------------------------------------------
// set up URL // set up URL
@ -763,5 +788,5 @@ NetworkStream* CreateRTMPPublisher(String &failReason, bool &bCanRetry)
return new RTMPPublisher(rtmp, bUseSendBuffer, sendBufferSize); return new RTMPPublisher(rtmp, bUseSendBuffer, sendBufferSize);
traceOut; //traceOut;
} }

View File

@ -258,6 +258,8 @@ LRESULT CALLBACK OBS::ListboxHook(HWND hwnd, UINT message, WPARAM wParam, LPARAM
{ {
if(message == WM_RBUTTONDOWN) if(message == WM_RBUTTONDOWN)
{ {
CallWindowProc(listboxProc, hwnd, WM_LBUTTONDOWN, wParam, lParam);
UINT id = (UINT)GetWindowLongPtr(hwnd, GWL_ID); UINT id = (UINT)GetWindowLongPtr(hwnd, GWL_ID);
HMENU hMenu = CreatePopupMenu(); HMENU hMenu = CreatePopupMenu();