2010-04-25 00:31:27 +00:00
|
|
|
/*
|
2009-02-09 21:15:56 +00:00
|
|
|
* Copyright (C) 2007-2009 Gabest
|
|
|
|
* http://www.gabest.org
|
|
|
|
*
|
|
|
|
* This Program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
* any later version.
|
2010-04-25 00:31:27 +00:00
|
|
|
*
|
2009-02-09 21:15:56 +00:00
|
|
|
* This Program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2010-04-25 00:31:27 +00:00
|
|
|
*
|
2009-02-09 21:15:56 +00:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with GNU Make; see the file COPYING. If not, write to
|
2012-09-09 18:16:11 +00:00
|
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA USA.
|
2009-02-09 21:15:56 +00:00
|
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2011-02-19 03:36:30 +00:00
|
|
|
#include "stdafx.h"
|
2009-02-09 21:15:56 +00:00
|
|
|
#include "GSCapture.h"
|
2015-05-18 09:56:20 +00:00
|
|
|
#include "GSPng.h"
|
2015-09-26 11:41:44 +00:00
|
|
|
#include "GSUtil.h"
|
2011-02-19 03:36:30 +00:00
|
|
|
|
2016-01-27 15:33:10 +00:00
|
|
|
#ifdef _WIN32
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2018-03-20 13:39:30 +00:00
|
|
|
class CPinInfo : public PIN_INFO {
|
|
|
|
public:
|
|
|
|
CPinInfo() { pFilter = NULL; }
|
|
|
|
~CPinInfo() { if (pFilter) pFilter->Release(); }
|
|
|
|
};
|
|
|
|
|
|
|
|
class CFilterInfo : public FILTER_INFO {
|
|
|
|
public:
|
|
|
|
CFilterInfo() { pGraph = NULL; }
|
|
|
|
~CFilterInfo() { if (pGraph) 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) \
|
|
|
|
{ \
|
|
|
|
|
|
|
|
#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 }}}
|
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
//
|
|
|
|
// GSSource
|
|
|
|
//
|
2010-04-25 00:31:27 +00:00
|
|
|
interface __declspec(uuid("59C193BB-C520-41F3-BC1D-E245B80A86FA"))
|
2009-02-09 21:15:56 +00:00
|
|
|
IGSSource : public IUnknown
|
|
|
|
{
|
|
|
|
STDMETHOD(DeliverNewSegment)() PURE;
|
|
|
|
STDMETHOD(DeliverFrame)(const void* bits, int pitch, bool rgba) PURE;
|
|
|
|
STDMETHOD(DeliverEOS)() PURE;
|
|
|
|
};
|
|
|
|
|
|
|
|
class __declspec(uuid("F8BB6F4F-0965-4ED4-BA74-C6A01E6E6C77"))
|
|
|
|
GSSource : public CBaseFilter, private CCritSec, public IGSSource
|
|
|
|
{
|
2009-05-14 16:41:52 +00:00
|
|
|
GSVector2i m_size;
|
2009-02-09 21:15:56 +00:00
|
|
|
REFERENCE_TIME m_atpf;
|
|
|
|
REFERENCE_TIME m_now;
|
|
|
|
|
|
|
|
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv)
|
|
|
|
{
|
2010-04-25 00:31:27 +00:00
|
|
|
return
|
2009-05-14 16:41:52 +00:00
|
|
|
riid == __uuidof(IGSSource) ? GetInterface((IGSSource*)this, ppv) :
|
2009-02-09 21:15:56 +00:00
|
|
|
__super::NonDelegatingQueryInterface(riid, ppv);
|
|
|
|
}
|
|
|
|
|
|
|
|
class GSSourceOutputPin : public CBaseOutputPin
|
|
|
|
{
|
2009-05-14 16:41:52 +00:00
|
|
|
GSVector2i m_size;
|
2017-05-26 15:57:49 +00:00
|
|
|
std::vector<CMediaType> m_mts;
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
public:
|
2011-07-25 11:16:01 +00:00
|
|
|
GSSourceOutputPin(const GSVector2i& size, REFERENCE_TIME atpf, CBaseFilter* pFilter, CCritSec* pLock, HRESULT& hr, int colorspace)
|
2009-02-09 21:15:56 +00:00
|
|
|
: CBaseOutputPin("GSSourceOutputPin", pFilter, pLock, &hr, L"Output")
|
|
|
|
, m_size(size)
|
|
|
|
{
|
|
|
|
CMediaType mt;
|
|
|
|
mt.majortype = MEDIATYPE_Video;
|
|
|
|
mt.formattype = FORMAT_VideoInfo;
|
|
|
|
|
|
|
|
VIDEOINFOHEADER vih;
|
|
|
|
memset(&vih, 0, sizeof(vih));
|
|
|
|
vih.AvgTimePerFrame = atpf;
|
|
|
|
vih.bmiHeader.biSize = sizeof(vih.bmiHeader);
|
2009-05-14 16:41:52 +00:00
|
|
|
vih.bmiHeader.biWidth = m_size.x;
|
|
|
|
vih.bmiHeader.biHeight = m_size.y;
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2011-05-02 21:04:33 +00:00
|
|
|
// YUY2
|
2011-07-25 11:16:01 +00:00
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
mt.subtype = MEDIASUBTYPE_YUY2;
|
2009-05-14 16:41:52 +00:00
|
|
|
mt.lSampleSize = m_size.x * m_size.y * 2;
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
vih.bmiHeader.biCompression = '2YUY';
|
|
|
|
vih.bmiHeader.biPlanes = 1;
|
|
|
|
vih.bmiHeader.biBitCount = 16;
|
2009-05-14 16:41:52 +00:00
|
|
|
vih.bmiHeader.biSizeImage = m_size.x * m_size.y * 2;
|
|
|
|
mt.SetFormat((uint8*)&vih, sizeof(vih));
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2009-05-11 08:18:00 +00:00
|
|
|
m_mts.push_back(mt);
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
// RGB32
|
|
|
|
|
|
|
|
mt.subtype = MEDIASUBTYPE_RGB32;
|
2009-05-14 16:41:52 +00:00
|
|
|
mt.lSampleSize = m_size.x * m_size.y * 4;
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
vih.bmiHeader.biCompression = BI_RGB;
|
|
|
|
vih.bmiHeader.biPlanes = 1;
|
|
|
|
vih.bmiHeader.biBitCount = 32;
|
2009-05-14 16:41:52 +00:00
|
|
|
vih.bmiHeader.biSizeImage = m_size.x * m_size.y * 4;
|
|
|
|
mt.SetFormat((uint8*)&vih, sizeof(vih));
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2011-07-25 11:16:01 +00:00
|
|
|
if(colorspace == 1) m_mts.insert(m_mts.begin(), mt);
|
|
|
|
else m_mts.push_back(mt);
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT GSSourceOutputPin::DecideBufferSize(IMemAllocator* pAlloc, ALLOCATOR_PROPERTIES* pProperties)
|
|
|
|
{
|
|
|
|
ASSERT(pAlloc && pProperties);
|
|
|
|
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
pProperties->cBuffers = 1;
|
|
|
|
pProperties->cbBuffer = m_mt.lSampleSize;
|
|
|
|
|
|
|
|
ALLOCATOR_PROPERTIES Actual;
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
if(FAILED(hr = pAlloc->SetProperties(pProperties, &Actual)))
|
|
|
|
{
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(Actual.cbBuffer < pProperties->cbBuffer)
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT(Actual.cBuffers == pProperties->cBuffers);
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT CheckMediaType(const CMediaType* pmt)
|
|
|
|
{
|
2017-05-26 15:57:49 +00:00
|
|
|
for(const auto &mt : m_mts)
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2017-05-26 15:57:49 +00:00
|
|
|
if(mt.majortype == pmt->majortype && mt.subtype == pmt->subtype)
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT GetMediaType(int i, CMediaType* pmt)
|
|
|
|
{
|
|
|
|
CheckPointer(pmt, E_POINTER);
|
|
|
|
|
|
|
|
if(i < 0) return E_INVALIDARG;
|
|
|
|
if(i > 1) return VFW_S_NO_MORE_ITEMS;
|
|
|
|
|
|
|
|
*pmt = m_mts[i];
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP Notify(IBaseFilter* pSender, Quality q)
|
|
|
|
{
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
const CMediaType& CurrentMediaType()
|
|
|
|
{
|
|
|
|
return m_mt;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2009-06-19 13:03:59 +00:00
|
|
|
GSSourceOutputPin* m_output;
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
|
2011-10-08 13:05:15 +00:00
|
|
|
GSSource(int w, int h, float fps, IUnknown* pUnk, HRESULT& hr, int colorspace)
|
2009-02-09 21:15:56 +00:00
|
|
|
: CBaseFilter(NAME("GSSource"), pUnk, this, __uuidof(this), &hr)
|
|
|
|
, m_output(NULL)
|
|
|
|
, m_size(w, h)
|
2013-06-17 04:11:10 +00:00
|
|
|
, m_atpf((REFERENCE_TIME)(10000000.0f / fps))
|
2009-02-09 21:15:56 +00:00
|
|
|
, m_now(0)
|
|
|
|
{
|
2011-07-25 11:16:01 +00:00
|
|
|
m_output = new GSSourceOutputPin(m_size, m_atpf, this, this, hr, colorspace);
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
2009-06-19 13:03:59 +00:00
|
|
|
virtual ~GSSource()
|
|
|
|
{
|
|
|
|
delete m_output;
|
|
|
|
}
|
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
DECLARE_IUNKNOWN;
|
|
|
|
|
|
|
|
int GetPinCount()
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-04-25 00:31:27 +00:00
|
|
|
CBasePin* GetPin(int n)
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2009-06-19 13:03:59 +00:00
|
|
|
return n == 0 ? m_output : NULL;
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// IGSSource
|
|
|
|
|
|
|
|
STDMETHODIMP DeliverNewSegment()
|
|
|
|
{
|
|
|
|
m_now = 0;
|
|
|
|
|
|
|
|
return m_output->DeliverNewSegment(0, _I64_MAX, 1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP DeliverFrame(const void* bits, int pitch, bool rgba)
|
|
|
|
{
|
|
|
|
if(!m_output || !m_output->IsConnected())
|
|
|
|
{
|
|
|
|
return E_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
CComPtr<IMediaSample> sample;
|
|
|
|
|
|
|
|
if(FAILED(m_output->GetDeliveryBuffer(&sample, NULL, NULL, 0)))
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
REFERENCE_TIME start = m_now;
|
|
|
|
REFERENCE_TIME stop = m_now + m_atpf;
|
|
|
|
|
|
|
|
sample->SetTime(&start, &stop);
|
|
|
|
sample->SetSyncPoint(TRUE);
|
|
|
|
|
|
|
|
const CMediaType& mt = m_output->CurrentMediaType();
|
|
|
|
|
2009-05-14 16:41:52 +00:00
|
|
|
uint8* src = (uint8*)bits;
|
|
|
|
uint8* dst = NULL;
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
sample->GetPointer(&dst);
|
|
|
|
|
2009-05-14 16:41:52 +00:00
|
|
|
int w = m_size.x;
|
|
|
|
int h = m_size.y;
|
2009-02-09 21:15:56 +00:00
|
|
|
int srcpitch = pitch;
|
|
|
|
|
|
|
|
if(mt.subtype == MEDIASUBTYPE_YUY2)
|
|
|
|
{
|
|
|
|
int dstpitch = ((VIDEOINFOHEADER*)mt.Format())->bmiHeader.biWidth * 2;
|
|
|
|
|
2010-07-17 14:04:38 +00:00
|
|
|
GSVector4 ys(0.257f, 0.504f, 0.098f, 0.0f);
|
|
|
|
GSVector4 us(-0.148f / 2, -0.291f / 2, 0.439f / 2, 0.0f);
|
|
|
|
GSVector4 vs(0.439f / 2, -0.368f / 2, -0.071f / 2, 0.0f);
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2011-04-04 11:05:54 +00:00
|
|
|
if(!rgba)
|
|
|
|
{
|
|
|
|
ys = ys.zyxw();
|
|
|
|
us = us.zyxw();
|
|
|
|
vs = vs.zyxw();
|
|
|
|
}
|
|
|
|
|
|
|
|
const GSVector4 offset(16, 128, 16, 128);
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2010-07-17 14:04:38 +00:00
|
|
|
for(int j = 0; j < h; j++, dst += dstpitch, src += srcpitch)
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2010-07-17 14:04:38 +00:00
|
|
|
uint32* s = (uint32*)src;
|
|
|
|
uint16* d = (uint16*)dst;
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2010-07-17 14:04:38 +00:00
|
|
|
for(int i = 0; i < w; i += 2)
|
|
|
|
{
|
2011-04-04 11:05:54 +00:00
|
|
|
GSVector4 c0 = GSVector4::rgba32(s[i + 0]);
|
|
|
|
GSVector4 c1 = GSVector4::rgba32(s[i + 1]);
|
2010-07-17 14:04:38 +00:00
|
|
|
GSVector4 c2 = c0 + c1;
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2010-07-17 14:04:38 +00:00
|
|
|
GSVector4 lo = (c0 * ys).hadd(c2 * us);
|
|
|
|
GSVector4 hi = (c1 * ys).hadd(c2 * vs);
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2010-07-17 14:04:38 +00:00
|
|
|
GSVector4 c = lo.hadd(hi) + offset;
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2010-07-17 14:04:38 +00:00
|
|
|
*((uint32*)&d[i]) = GSVector4i(c).rgba32();
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-05-14 16:41:52 +00:00
|
|
|
else if(mt.subtype == MEDIASUBTYPE_RGB32)
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
|
|
|
int dstpitch = ((VIDEOINFOHEADER*)mt.Format())->bmiHeader.biWidth * 4;
|
|
|
|
|
|
|
|
dst += dstpitch * (h - 1);
|
|
|
|
dstpitch = -dstpitch;
|
|
|
|
|
|
|
|
for(int j = 0; j < h; j++, dst += dstpitch, src += srcpitch)
|
|
|
|
{
|
|
|
|
if(rgba)
|
|
|
|
{
|
|
|
|
#if _M_SSE >= 0x301
|
|
|
|
|
|
|
|
GSVector4i* s = (GSVector4i*)src;
|
|
|
|
GSVector4i* d = (GSVector4i*)dst;
|
|
|
|
|
|
|
|
GSVector4i mask(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15);
|
|
|
|
|
|
|
|
for(int i = 0, w4 = w >> 2; i < w4; i++)
|
|
|
|
{
|
|
|
|
d[i] = s[i].shuffle8(mask);
|
|
|
|
}
|
|
|
|
|
2009-05-14 16:41:52 +00:00
|
|
|
#else
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
GSVector4i* s = (GSVector4i*)src;
|
|
|
|
GSVector4i* d = (GSVector4i*)dst;
|
|
|
|
|
|
|
|
for(int i = 0, w4 = w >> 2; i < w4; i++)
|
|
|
|
{
|
|
|
|
d[i] = ((s[i] & 0x00ff0000) >> 16) | ((s[i] & 0x000000ff) << 16) | (s[i] & 0x0000ff00);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
memcpy(dst, src, w * 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(FAILED(m_output->Deliver(sample)))
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_now = stop;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP DeliverEOS()
|
|
|
|
{
|
|
|
|
return m_output->DeliverEndOfStream();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static IPin* GetFirstPin(IBaseFilter* pBF, PIN_DIRECTION dir)
|
|
|
|
{
|
|
|
|
if(!pBF) return(NULL);
|
|
|
|
|
|
|
|
BeginEnumPins(pBF, pEP, pPin)
|
|
|
|
{
|
|
|
|
PIN_DIRECTION dir2;
|
|
|
|
pPin->QueryDirection(&dir2);
|
|
|
|
if(dir == dir2)
|
|
|
|
{
|
|
|
|
IPin* pRet = pPin.Detach();
|
|
|
|
pRet->Release();
|
|
|
|
return(pRet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EndEnumPins
|
|
|
|
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
|
2011-02-19 03:36:30 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
//
|
|
|
|
// GSCapture
|
|
|
|
//
|
|
|
|
|
|
|
|
GSCapture::GSCapture()
|
2015-05-18 09:56:20 +00:00
|
|
|
: m_capturing(false), m_frame(0)
|
|
|
|
, m_out_dir("/tmp/GSdx_Capture") // FIXME Later add an option
|
2011-02-19 03:36:30 +00:00
|
|
|
{
|
2016-05-24 19:52:06 +00:00
|
|
|
m_out_dir = theApp.GetConfigS("capture_out_dir");
|
|
|
|
m_threads = theApp.GetConfigI("capture_threads");
|
2016-05-01 09:43:18 +00:00
|
|
|
#if defined(__unix__)
|
2016-05-24 19:52:06 +00:00
|
|
|
m_compression_level = theApp.GetConfigI("png_compression_level");
|
2016-03-28 15:34:11 +00:00
|
|
|
#endif
|
2011-02-19 03:36:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GSCapture::~GSCapture()
|
|
|
|
{
|
|
|
|
EndCapture();
|
|
|
|
}
|
|
|
|
|
2017-01-13 23:29:41 +00:00
|
|
|
bool GSCapture::BeginCapture(float fps, GSVector2i recommendedResolution, float aspect)
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2017-01-13 23:29:41 +00:00
|
|
|
printf("Recommended resolution: %d x %d, DAR for muxing: %.4f\n", recommendedResolution.x, recommendedResolution.y, aspect);
|
2015-05-14 09:56:07 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_lock);
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
ASSERT(fps != 0);
|
|
|
|
|
|
|
|
EndCapture();
|
|
|
|
|
2016-01-27 15:33:10 +00:00
|
|
|
#ifdef _WIN32
|
2011-02-19 03:36:30 +00:00
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
GSCaptureDlg dlg;
|
|
|
|
|
|
|
|
if(IDOK != dlg.DoModal()) return false;
|
|
|
|
|
2009-05-14 16:41:52 +00:00
|
|
|
m_size.x = (dlg.m_width + 7) & ~7;
|
2010-04-25 00:31:27 +00:00
|
|
|
m_size.y = (dlg.m_height + 7) & ~7;
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2017-05-26 15:26:46 +00:00
|
|
|
std::wstring fn{dlg.m_filename.begin(), dlg.m_filename.end()};
|
2009-06-03 12:09:04 +00:00
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
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))
|
2009-06-03 12:09:04 +00:00
|
|
|
|| FAILED(hr = cgb->SetOutputFileName(&MEDIASUBTYPE_Avi, fn.c_str(), &mux, NULL)))
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-07-25 11:16:01 +00:00
|
|
|
m_src = new GSSource(m_size.x, m_size.y, fps, NULL, hr, dlg.m_colorspace);
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2010-03-02 12:55:26 +00:00
|
|
|
if (dlg.m_enc==0)
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2010-03-02 12:55:26 +00:00
|
|
|
if (FAILED(hr = m_graph->AddFilter(m_src, L"Source")))
|
|
|
|
return false;
|
|
|
|
if (FAILED(hr = m_graph->ConnectDirect(GetFirstPin(m_src, PINDIR_OUTPUT), GetFirstPin(mux, PINDIR_INPUT), NULL)))
|
|
|
|
return false;
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
2010-03-02 12:55:26 +00:00
|
|
|
else
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2010-03-02 12:55:26 +00:00
|
|
|
if(FAILED(hr = m_graph->AddFilter(m_src, L"Source"))
|
|
|
|
|| FAILED(hr = m_graph->AddFilter(dlg.m_enc, 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)))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
BeginEnumFilters(m_graph, pEF, pBF)
|
|
|
|
{
|
|
|
|
CFilterInfo fi;
|
|
|
|
pBF->QueryFilterInfo(&fi);
|
2017-05-26 15:26:46 +00:00
|
|
|
std::wstring s{fi.achName};
|
|
|
|
printf("Filter [%p]: %s\n", pBF.p, std::string{s.begin(), s.end()}.c_str());
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
BeginEnumPins(pBF, pEP, pPin)
|
|
|
|
{
|
|
|
|
CComPtr<IPin> pPinTo;
|
|
|
|
pPin->ConnectedTo(&pPinTo);
|
|
|
|
|
|
|
|
CPinInfo pi;
|
|
|
|
pPin->QueryPinInfo(&pi);
|
2017-05-26 15:26:46 +00:00
|
|
|
std::wstring s{pi.achName};
|
|
|
|
printf("- Pin [%p - %p]: %s (%s)\n", pPin.p, pPinTo.p, std::string{s.begin(), s.end()}.c_str(), pi.dir ? "out" : "in");
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
EndEnumPins
|
|
|
|
}
|
|
|
|
EndEnumFilters
|
|
|
|
|
|
|
|
hr = CComQIPtr<IMediaControl>(m_graph)->Run();
|
|
|
|
|
|
|
|
CComQIPtr<IGSSource>(m_src)->DeliverNewSegment();
|
|
|
|
|
2016-05-01 09:43:18 +00:00
|
|
|
#elif defined(__unix__)
|
2015-05-18 17:57:21 +00:00
|
|
|
// Note I think it doesn't support multiple depth creation
|
2015-09-26 11:41:44 +00:00
|
|
|
GSmkdir(m_out_dir.c_str());
|
2015-05-18 09:56:20 +00:00
|
|
|
|
|
|
|
// Really cheap recording
|
|
|
|
m_frame = 0;
|
|
|
|
// Add option !!!
|
2016-05-26 16:43:11 +00:00
|
|
|
m_size.x = theApp.GetConfigI("CaptureWidth");
|
|
|
|
m_size.y = theApp.GetConfigI("CaptureHeight");
|
2015-05-18 09:56:20 +00:00
|
|
|
|
2015-05-18 13:55:08 +00:00
|
|
|
for(int i = 0; i < m_threads; i++) {
|
gsdx: Make GSJobQueue non-inheritable
In the previous code, the threads were created and destroyed in the base
class constructor and destructor, so the threads could potentially be
active while the object is in a partially constructed or destroyed state.
The thread however, relies on a virtual function to process the queue
items, and the vtable might not be in the desired state when the object
is partially constructed or destroyed.
This probably only matters during object destruction - no items are in
the queue during object construction so the virtual function won't be
called, but items may still be queued up when the destructor is called,
so the virtual function can be called. It wasn't an issue because all
uses of the thread explicitly waited for the queues to be empty before
invoking the destructor.
Adjust the constructor to take a std::function parameter, which the
thread will use instead to process queue items, and avoid inheriting
from the GSJobQueue class. This will also eliminate the need to
explicitly wait for all jobs to finish (unless there are other external
factors, of course), which would probably make future code safer.
2016-11-09 01:34:48 +00:00
|
|
|
m_workers.push_back(std::unique_ptr<GSPng::Worker>(new GSPng::Worker(&GSPng::Process)));
|
2015-05-18 13:55:08 +00:00
|
|
|
}
|
2011-02-19 03:36:30 +00:00
|
|
|
#endif
|
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
m_capturing = true;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GSCapture::DeliverFrame(const void* bits, int pitch, bool rgba)
|
|
|
|
{
|
2015-05-14 09:56:07 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_lock);
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2010-04-25 00:31:27 +00:00
|
|
|
if(bits == NULL || pitch == 0)
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2010-04-25 00:31:27 +00:00
|
|
|
ASSERT(0);
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-01-27 15:33:10 +00:00
|
|
|
#ifdef _WIN32
|
2011-02-19 03:36:30 +00:00
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
if(m_src)
|
|
|
|
{
|
|
|
|
CComQIPtr<IGSSource>(m_src)->DeliverFrame(bits, pitch, rgba);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-05-01 09:43:18 +00:00
|
|
|
#elif defined(__unix__)
|
2015-05-18 09:56:20 +00:00
|
|
|
|
2015-05-18 13:40:42 +00:00
|
|
|
std::string out_file = m_out_dir + format("/frame.%010d.png", m_frame);
|
2016-02-24 21:52:17 +00:00
|
|
|
//GSPng::Save(GSPng::RGB_PNG, out_file, (uint8*)bits, m_size.x, m_size.y, pitch, m_compression_level);
|
2017-05-26 16:37:20 +00:00
|
|
|
m_workers[m_frame%m_threads]->Push(std::make_shared<GSPng::Transaction>(GSPng::RGB_PNG, out_file, static_cast<const uint8*>(bits), m_size.x, m_size.y, pitch, m_compression_level));
|
2015-05-18 09:56:20 +00:00
|
|
|
|
|
|
|
m_frame++;
|
|
|
|
|
2011-02-19 03:36:30 +00:00
|
|
|
#endif
|
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GSCapture::EndCapture()
|
|
|
|
{
|
2015-05-14 09:56:07 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lock(m_lock);
|
2011-02-19 03:36:30 +00:00
|
|
|
|
2016-01-27 15:33:10 +00:00
|
|
|
#ifdef _WIN32
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
if(m_src)
|
|
|
|
{
|
|
|
|
CComQIPtr<IGSSource>(m_src)->DeliverEOS();
|
|
|
|
|
|
|
|
m_src = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m_graph)
|
|
|
|
{
|
|
|
|
CComQIPtr<IMediaControl>(m_graph)->Stop();
|
|
|
|
|
|
|
|
m_graph = NULL;
|
|
|
|
}
|
|
|
|
|
2016-05-01 09:43:18 +00:00
|
|
|
#elif defined(__unix__)
|
2016-10-28 16:49:41 +00:00
|
|
|
m_workers.clear();
|
2015-05-18 09:56:20 +00:00
|
|
|
|
|
|
|
m_frame = 0;
|
|
|
|
|
2011-02-19 03:36:30 +00:00
|
|
|
#endif
|
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
m_capturing = false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|