Migrated GSCapture and GSCaptureDlg

This commit is contained in:
Silent 2021-07-06 23:51:48 +02:00 committed by Kojin
parent ed62ae124b
commit 8c6cad559e
5 changed files with 163 additions and 147 deletions

View File

@ -33,8 +33,6 @@
#include <d3dcompiler.h>
#include <d3d11_1.h>
#include <dxgi1_3.h>
#include <comutil.h>
#include <atlcomcli.h>
#else

View File

@ -21,47 +21,55 @@
#ifdef _WIN32
class CPinInfo : public PIN_INFO
static void __stdcall ClosePinInfo(_Inout_ PIN_INFO* info) WI_NOEXCEPT
{
public:
CPinInfo() { pFilter = NULL; }
~CPinInfo()
if (info->pFilter)
{
if (pFilter)
pFilter->Release();
info->pFilter->Release();
}
};
}
class CFilterInfo : public FILTER_INFO
static void __stdcall CloseFilterInfo(_Inout_ FILTER_INFO* info) WI_NOEXCEPT
{
public:
CFilterInfo() { pGraph = NULL; }
~CFilterInfo()
if (info->pGraph)
{
if (pGraph)
pGraph->Release();
info->pGraph->Release();
}
};
}
#define BeginEnumFilters(pFilterGraph, pEnumFilters, pBaseFilter) \
{ \
CComPtr<IEnumFilters> pEnumFilters; \
if(pFilterGraph && SUCCEEDED(pFilterGraph->EnumFilters(&pEnumFilters))) \
{ \
for(CComPtr<IBaseFilter> pBaseFilter; S_OK == pEnumFilters->Next(1, &pBaseFilter, 0); pBaseFilter = NULL) \
using unique_pin_info = wil::unique_struct<PIN_INFO, decltype(&::ClosePinInfo), ::ClosePinInfo>;
using unique_filter_info = wil::unique_struct<FILTER_INFO, decltype(&::CloseFilterInfo), ::CloseFilterInfo>;
template<typename Func>
static void EnumFilters(IGraphBuilder* filterGraph, Func&& f)
{
wil::com_ptr_nothrow<IEnumFilters> enumFilters;
if (SUCCEEDED(filterGraph->EnumFilters(enumFilters.put())))
{
wil::com_ptr_nothrow<IBaseFilter> baseFilter;
while (enumFilters->Next(1, baseFilter.put(), nullptr) == S_OK)
{
std::forward<Func>(f)(baseFilter.get());
}
}
}
template<typename Func>
static void EnumPins(IBaseFilter* baseFilter, Func&& f)
{
wil::com_ptr_nothrow<IEnumPins> enumPins;
if (SUCCEEDED(baseFilter->EnumPins(enumPins.put())))
{
wil::com_ptr_nothrow<IPin> pin;
while (enumPins->Next(1, pin.put(), nullptr) == S_OK)
{
if (!std::forward<Func>(f)(pin.get()))
{
#define EndEnumFilters }}}
#define BeginEnumPins(pBaseFilter, pEnumPins, pPin) \
{ \
CComPtr<IEnumPins> pEnumPins; \
if(pBaseFilter && SUCCEEDED(pBaseFilter->EnumPins(&pEnumPins))) \
{ \
for(CComPtr<IPin> pPin; S_OK == pEnumPins->Next(1, &pPin, 0); pPin = NULL) \
{
#define EndEnumPins }}}
break;
}
}
}
}
//
// GSSource
@ -249,9 +257,9 @@ public:
return E_UNEXPECTED;
}
CComPtr<IMediaSample> sample;
wil::com_ptr_nothrow<IMediaSample> sample;
if (FAILED(m_output->GetDeliveryBuffer(&sample, NULL, NULL, 0)))
if (FAILED(m_output->GetDeliveryBuffer(sample.put(), NULL, NULL, 0)))
{
return E_FAIL;
}
@ -342,7 +350,7 @@ public:
return E_FAIL;
}
if (FAILED(m_output->Deliver(sample)))
if (FAILED(m_output->Deliver(sample.get())))
{
return E_FAIL;
}
@ -358,25 +366,25 @@ public:
}
};
static IPin* GetFirstPin(IBaseFilter* pBF, PIN_DIRECTION dir)
static wil::com_ptr_nothrow<IPin> GetFirstPin(IBaseFilter* pBF, PIN_DIRECTION dir)
{
if (!pBF)
return nullptr;
BeginEnumPins(pBF, pEP, pPin)
wil::com_ptr_nothrow<IPin> result;
if (pBF)
{
PIN_DIRECTION dir2;
pPin->QueryDirection(&dir2);
if (dir == dir2)
EnumPins(pBF, [&](IPin* pin)
{
IPin* pRet = pPin.Detach();
pRet->Release();
return pRet;
}
PIN_DIRECTION dir2;
pin->QueryDirection(&dir2);
if (dir == dir2)
{
result = pin;
return false;
}
return true;
});
}
EndEnumPins
return nullptr;
return result;
}
#endif
@ -443,64 +451,73 @@ bool GSCapture::BeginCapture(float fps, GSVector2i recommendedResolution, float
m_size.y = (dlg.m_height + 7) & ~7;
//
HRESULT hr;
CComPtr<ICaptureGraphBuilder2> cgb;
CComPtr<IBaseFilter> mux;
if (FAILED(hr = m_graph.CoCreateInstance(CLSID_FilterGraph))
|| FAILED(hr = cgb.CoCreateInstance(CLSID_CaptureGraphBuilder2))
|| FAILED(hr = cgb->SetFiltergraph(m_graph))
|| FAILED(hr = cgb->SetOutputFileName(&MEDIASUBTYPE_Avi, std::wstring(dlg.m_filename.begin(), dlg.m_filename.end()).c_str(), &mux, NULL)))
auto graph = wil::CoCreateInstanceNoThrow<IGraphBuilder>(CLSID_FilterGraph);
if (!graph)
{
return false;
}
auto cgb = wil::CoCreateInstanceNoThrow<ICaptureGraphBuilder2>(CLSID_CaptureGraphBuilder2);
if (!cgb)
{
return false;
}
m_src = new GSSource(m_size.x, m_size.y, fps, NULL, hr, dlg.m_colorspace);
wil::com_ptr_nothrow<IBaseFilter> mux;
if (FAILED(cgb->SetFiltergraph(graph.get()))
|| FAILED(cgb->SetOutputFileName(&MEDIASUBTYPE_Avi, std::wstring(dlg.m_filename.begin(), dlg.m_filename.end()).c_str(), mux.put(), nullptr)))
{
return false;
}
HRESULT source_hr = S_OK;
wil::com_ptr_nothrow<IBaseFilter> src;
src.attach(new GSSource(m_size.x, m_size.y, fps, NULL, source_hr, dlg.m_colorspace));
if (dlg.m_enc == 0)
{
if (FAILED(hr = m_graph->AddFilter(m_src, L"Source")))
if (FAILED(graph->AddFilter(src.get(), L"Source")))
return false;
if (FAILED(hr = m_graph->ConnectDirect(GetFirstPin(m_src, PINDIR_OUTPUT), GetFirstPin(mux, PINDIR_INPUT), NULL)))
if (FAILED(graph->ConnectDirect(GetFirstPin(src.get(), PINDIR_OUTPUT).get(), GetFirstPin(mux.get(), PINDIR_INPUT).get(), nullptr)))
return false;
}
else
{
if (FAILED(hr = m_graph->AddFilter(m_src, L"Source")) || FAILED(hr = m_graph->AddFilter(dlg.m_enc, L"Encoder")))
if (FAILED(graph->AddFilter(src.get(), L"Source")) || FAILED(graph->AddFilter(dlg.m_enc.get(), L"Encoder")))
{
return false;
}
if (FAILED(hr = m_graph->ConnectDirect(GetFirstPin(m_src, PINDIR_OUTPUT), GetFirstPin(dlg.m_enc, PINDIR_INPUT), NULL))
|| FAILED(hr = m_graph->ConnectDirect(GetFirstPin(dlg.m_enc, PINDIR_OUTPUT), GetFirstPin(mux, PINDIR_INPUT), NULL)))
if (FAILED(graph->ConnectDirect(GetFirstPin(src.get(), PINDIR_OUTPUT).get(), GetFirstPin(dlg.m_enc.get(), PINDIR_INPUT).get(), nullptr))
|| FAILED(graph->ConnectDirect(GetFirstPin(dlg.m_enc.get(), PINDIR_OUTPUT).get(), GetFirstPin(mux.get(), PINDIR_INPUT).get(), nullptr)))
{
return false;
}
}
BeginEnumFilters(m_graph, pEF, pBF)
EnumFilters(graph.get(), [](IBaseFilter* baseFilter)
{
CFilterInfo filter;
pBF->QueryFilterInfo(&filter);
printf("Filter [%p]: %ls\n", pBF.p, &(*filter.achName));
unique_filter_info filter;
baseFilter->QueryFilterInfo(&filter);
printf("Filter [%p]: %ls\n", baseFilter, filter.achName);
BeginEnumPins(pBF, pEP, pPin)
EnumPins(baseFilter, [](IPin* pin)
{
CComPtr<IPin> pPinTo;
pPin->ConnectedTo(&pPinTo);
wil::com_ptr_nothrow<IPin> pinTo;
pin->ConnectedTo(pinTo.put());
CPinInfo pi;
pPin->QueryPinInfo(&pi);
printf("- Pin [%p - %p]: %ls (%s)\n", pPin.p, pPinTo.p, &(*filter.achName), pi.dir ? "out" : "in");
}
EndEnumPins
}
EndEnumFilters
unique_pin_info pi;
pin->QueryPinInfo(&pi);
printf("- Pin [%p - %p]: %ls (%s)\n", pin, pinTo.get(), pi.achName, pi.dir ? "out" : "in");
return true;
});
});
hr = CComQIPtr<IMediaControl>(m_graph)->Run();
// Moving forward, we want failfast semantics so "commit" these interfaces by persisting them in the class
m_graph = std::move(graph);
m_src = std::move(src);
CComQIPtr<IGSSource>(m_src)->DeliverNewSegment();
m_graph.query<IMediaControl>()->Run();
m_src.query<IGSSource>()->DeliverNewSegment();
m_capturing = true;
filename = convert_utf16_to_utf8(dlg.m_filename.erase(dlg.m_filename.length() - 3, 3) + L"wav");
@ -541,7 +558,7 @@ bool GSCapture::DeliverFrame(const void* bits, int pitch, bool rgba)
if (m_src)
{
CComQIPtr<IGSSource>(m_src)->DeliverFrame(bits, pitch, rgba);
m_src.query<IGSSource>()->DeliverFrame(bits, pitch, rgba);
return true;
}
@ -570,16 +587,14 @@ bool GSCapture::EndCapture()
if (m_src)
{
CComQIPtr<IGSSource>(m_src)->DeliverEOS();
m_src = NULL;
m_src.query<IGSSource>()->DeliverEOS();
m_src.reset();
}
if (m_graph)
{
CComQIPtr<IMediaControl>(m_graph)->Stop();
m_graph = NULL;
m_graph.query<IMediaControl>()->Stop();
m_graph.reset();;
}
#elif defined(__unix__)

View File

@ -20,6 +20,7 @@
#ifdef _WIN32
#include "Window/GSCaptureDlg.h"
#include <wil/com.h>
#endif
class GSCapture
@ -33,8 +34,8 @@ class GSCapture
#ifdef _WIN32
CComPtr<IGraphBuilder> m_graph;
CComPtr<IBaseFilter> m_src;
wil::com_ptr_failfast<IGraphBuilder> m_graph;
wil::com_ptr_failfast<IBaseFilter> m_src;
#elif defined(__unix__)

View File

@ -17,17 +17,32 @@
#include "GS.h"
#include "GSCaptureDlg.h"
#define BeginEnumSysDev(clsid, pMoniker) \
{ \
CComPtr<ICreateDevEnum> pDevEnum4$##clsid; \
pDevEnum4$##clsid.CoCreateInstance(CLSID_SystemDeviceEnum); \
CComPtr<IEnumMoniker> pClassEnum4$##clsid; \
if (SUCCEEDED(pDevEnum4$##clsid->CreateClassEnumerator(clsid, &pClassEnum4$##clsid, 0)) && pClassEnum4$##clsid) \
{ \
for (CComPtr<IMoniker> pMoniker; pClassEnum4$##clsid->Next(1, &pMoniker, 0) == S_OK; pMoniker = NULL) \
{
// Ideally this belongs in WIL, but CAUUID is used by a *single* COM function in WinAPI.
// That's presumably why it's omitted and is unlikely to make it to upstream WIL.
static void __stdcall CloseCAUUID(_Inout_ CAUUID* cauuid) WI_NOEXCEPT
{
::CoTaskMemFree(cauuid->pElems);
}
#define EndEnumSysDev }}}
using unique_cauuid = wil::unique_struct<CAUUID, decltype(&::CloseCAUUID), ::CloseCAUUID>;
using unique_olestr = wil::unique_any<LPOLESTR, decltype(&::CoTaskMemFree), ::CoTaskMemFree>;
template<typename Func>
static void EnumSysDev(const GUID& clsid, Func&& f)
{
if (auto devEnum = wil::CoCreateInstanceNoThrow<ICreateDevEnum>(CLSID_SystemDeviceEnum))
{
wil::com_ptr_nothrow<IEnumMoniker> classEnum;
if (SUCCEEDED(devEnum->CreateClassEnumerator(clsid, classEnum.put(), 0)))
{
wil::com_ptr_nothrow<IMoniker> moniker;
while (classEnum->Next(1, moniker.put(), nullptr) == S_OK)
{
std::forward<Func>(f)(moniker.get());
}
}
}
}
void GSCaptureDlg::InvalidFile()
{
@ -56,7 +71,7 @@ int GSCaptureDlg::GetSelCodec(Codec& c)
if (!c.filter)
{
c.moniker->BindToObject(NULL, NULL, __uuidof(IBaseFilter), (void**)&c.filter);
c.moniker->BindToObject(NULL, NULL, IID_PPV_ARGS(c.filter.put()));
if (!c.filter)
return 0;
@ -79,13 +94,12 @@ void GSCaptureDlg::UpdateConfigureButton()
return;
}
if (CComQIPtr<ISpecifyPropertyPages> pSPP = c.filter)
if (auto pSPP = c.filter.try_query<ISpecifyPropertyPages>())
{
CAUUID caGUID;
memset(&caGUID, 0, sizeof(caGUID));
unique_cauuid caGUID;
enable = SUCCEEDED(pSPP->GetPages(&caGUID));
}
else if (CComQIPtr<IAMVfwCompressDialogs> pAMVfWCD = c.filter)
else if (auto pAMVfWCD = c.filter.try_query<IAMVfwCompressDialogs>())
{
enable = pAMVfWCD->ShowDialog(VfwCompressDialog_QueryConfig, nullptr) == S_OK;
}
@ -102,7 +116,7 @@ void GSCaptureDlg::OnInit()
m_codecs.clear();
_bstr_t selected = theApp.GetConfigS("CaptureVideoCodecDisplayName").c_str();
const std::wstring selected = convert_utf8_to_utf16(theApp.GetConfigS("CaptureVideoCodecDisplayName"));
ComboBoxAppend(IDC_CODECS, "Uncompressed", 0, true);
ComboBoxAppend(IDC_COLORSPACE, "YUY2", 0, true);
@ -110,44 +124,38 @@ void GSCaptureDlg::OnInit()
CoInitialize(0); // this is obviously wrong here, each thread should call this on start, and where is CoUninitalize?
BeginEnumSysDev(CLSID_VideoCompressorCategory, moniker)
EnumSysDev(CLSID_VideoCompressorCategory, [&](IMoniker* moniker)
{
Codec c;
c.moniker = moniker;
unique_olestr str;
if (FAILED(moniker->GetDisplayName(NULL, NULL, str.put())))
return;
std::wstring prefix;
if (wcsstr(str.get(), L"@device:dmo:")) prefix = L"(DMO) ";
else if (wcsstr(str.get(), L"@device:sw:")) prefix = L"(DS) ";
else if (wcsstr(str.get(), L"@device:cm:")) prefix = L"(VfW) ";
LPOLESTR str = NULL;
c.DisplayName = str.get();
if (FAILED(moniker->GetDisplayName(NULL, NULL, &str)))
continue;
wil::com_ptr_nothrow<IPropertyBag> pPB;
if (FAILED(moniker->BindToStorage(0, 0, IID_PPV_ARGS(pPB.put()))))
return;
if (wcsstr(str, L"@device:dmo:")) prefix = L"(DMO) ";
else if (wcsstr(str, L"@device:sw:")) prefix = L"(DS) ";
else if (wcsstr(str, L"@device:cm:")) prefix = L"(VfW) ";
c.DisplayName = str;
CoTaskMemFree(str);
CComPtr<IPropertyBag> pPB;
if (FAILED(moniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pPB)))
continue;
_variant_t var;
if (FAILED(pPB->Read(_bstr_t(_T("FriendlyName")), &var, NULL)))
continue;
wil::unique_variant var;
if (FAILED(pPB->Read(L"FriendlyName", &var, nullptr)))
return;
c.FriendlyName = prefix + var.bstrVal;
m_codecs.push_back(c);
ComboBoxAppend(IDC_CODECS, c.FriendlyName.c_str(), (LPARAM)&m_codecs.back(), c.DisplayName == selected);
}
EndEnumSysDev
});
UpdateConfigureButton();
}
@ -194,23 +202,16 @@ bool GSCaptureDlg::OnCommand(HWND hWnd, UINT id, UINT code)
Codec c;
if (GetSelCodec(c) == 1)
{
if (CComQIPtr<ISpecifyPropertyPages> pSPP = c.filter)
if (auto pSPP = c.filter.try_query<ISpecifyPropertyPages>())
{
CAUUID caGUID;
memset(&caGUID, 0, sizeof(caGUID));
unique_cauuid caGUID;
if (SUCCEEDED(pSPP->GetPages(&caGUID)))
{
IUnknown* lpUnk = NULL;
pSPP.QueryInterface(&lpUnk);
OleCreatePropertyFrame(m_hWnd, 0, 0, c.FriendlyName.c_str(), 1, (IUnknown**)&lpUnk, caGUID.cElems, caGUID.pElems, 0, 0, NULL);
lpUnk->Release();
if (caGUID.pElems)
CoTaskMemFree(caGUID.pElems);
auto lpUnk = pSPP.try_query<IUnknown>();
OleCreatePropertyFrame(m_hWnd, 0, 0, c.FriendlyName.c_str(), 1, lpUnk.addressof(), caGUID.cElems, caGUID.pElems, 0, 0, NULL);
}
}
else if (CComQIPtr<IAMVfwCompressDialogs> pAMVfWCD = c.filter)
else if (auto pAMVfWCD = c.filter.try_query<IAMVfwCompressDialogs>())
{
if (pAMVfWCD->ShowDialog(VfwCompressDialog_QueryConfig, NULL) == S_OK)
pAMVfWCD->ShowDialog(VfwCompressDialog_Config, m_hWnd);
@ -244,7 +245,7 @@ bool GSCaptureDlg::OnCommand(HWND hWnd, UINT id, UINT code)
theApp.SetConfig("CaptureFileName", convert_utf16_to_utf8(m_filename).c_str());
if (ris != 2)
theApp.SetConfig("CaptureVideoCodecDisplayName", c.DisplayName);
theApp.SetConfig("CaptureVideoCodecDisplayName", convert_utf16_to_utf8(c.DisplayName).c_str());
else
theApp.SetConfig("CaptureVideoCodecDisplayName", "");
break;

View File

@ -18,15 +18,16 @@
#include "GSDialog.h"
#include "GS/resource.h"
#include <streams.h>
#include <wil/com.h>
class GSCaptureDlg : public GSDialog
{
struct Codec
{
CComPtr<IMoniker> moniker;
CComPtr<IBaseFilter> filter;
wil::com_ptr_nothrow<IMoniker> moniker;
wil::com_ptr_nothrow<IBaseFilter> filter;
std::wstring FriendlyName;
_bstr_t DisplayName;
std::wstring DisplayName;
};
std::list<Codec> m_codecs;
@ -46,5 +47,5 @@ public:
int m_height;
std::wstring m_filename;
INT_PTR m_colorspace;
CComPtr<IBaseFilter> m_enc;
wil::com_ptr_nothrow<IBaseFilter> m_enc;
};