mirror of https://github.com/PCSX2/pcsx2.git
310 lines
7.6 KiB
C++
310 lines
7.6 KiB
C++
/*
|
|
* Copyright (C) 2003-2005 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.
|
|
*
|
|
* This Program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with GNU Make; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*
|
|
*/
|
|
|
|
#include "StdAfx.h"
|
|
#include "GSCapture.h"
|
|
#include "GSCaptureDlg.h"
|
|
#include <initguid.h>
|
|
#include <uuids.h>
|
|
//
|
|
// GSSource
|
|
//
|
|
|
|
#ifdef __INTEL_COMPILER
|
|
interface __declspec(uuid("59C193BB-C520-41F3-BC1D-E245B80A86FA"))
|
|
#else
|
|
[uuid("59C193BB-C520-41F3-BC1D-E245B80A86FA")] interface
|
|
#endif
|
|
IGSSource : public IUnknown
|
|
{
|
|
STDMETHOD(DeliverNewSegment)() PURE;
|
|
STDMETHOD(DeliverFrame)(D3DLOCKED_RECT& r) PURE;
|
|
STDMETHOD(DeliverEOS)() PURE;
|
|
};
|
|
|
|
#ifdef __INTEL_COMPILER
|
|
class __declspec(uuid("F8BB6F4F-0965-4ED4-BA74-C6A01E6E6C77"))
|
|
#else
|
|
[uuid("F8BB6F4F-0965-4ED4-BA74-C6A01E6E6C77")] class
|
|
#endif
|
|
GSSource : public CBaseFilter, public IGSSource
|
|
{
|
|
CCritSec m_csLock;
|
|
CAutoPtr<CBaseOutputPin> m_pOutput;
|
|
CSize m_size;
|
|
REFERENCE_TIME m_rtAvgTimePerFrame, m_rtNow;
|
|
|
|
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv)
|
|
{
|
|
return
|
|
QI(IGSSource)
|
|
__super::NonDelegatingQueryInterface(riid, ppv);
|
|
}
|
|
|
|
class GSSourceOutputPin : public CBaseOutputPin
|
|
{
|
|
CMediaType m_mt;
|
|
|
|
public:
|
|
GSSourceOutputPin(CMediaType& mt, CBaseFilter* pFilter, CCritSec* pLock, HRESULT& hr)
|
|
: CBaseOutputPin("GSSourceOutputPin", pFilter, pLock, &hr, L"Output")
|
|
, m_mt(mt)
|
|
{
|
|
}
|
|
|
|
HRESULT GSSourceOutputPin::DecideBufferSize(IMemAllocator* pAlloc, ALLOCATOR_PROPERTIES* pProperties)
|
|
{
|
|
ASSERT(pAlloc && pProperties);
|
|
|
|
HRESULT hr;
|
|
|
|
pProperties->cBuffers = 1;
|
|
pProperties->cbBuffer = m_mt.lSampleSize;
|
|
|
|
ALLOCATOR_PROPERTIES Actual;
|
|
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)
|
|
{
|
|
return pmt->majortype == MEDIATYPE_Video && pmt->subtype == MEDIASUBTYPE_RGB32 ? S_OK : E_FAIL;
|
|
}
|
|
|
|
HRESULT GetMediaType(int iPosition, CMediaType* pmt)
|
|
{
|
|
CheckPointer(pmt, E_POINTER);
|
|
if(iPosition < 0) return E_INVALIDARG;
|
|
if(iPosition > 0) return VFW_S_NO_MORE_ITEMS;
|
|
*pmt = m_mt;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP Notify(IBaseFilter* pSender, Quality q)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
};
|
|
|
|
public:
|
|
GSSource(int w, int h, int fps, IUnknown* pUnk, HRESULT& hr)
|
|
: CBaseFilter(NAME("GSSource"), pUnk, &m_csLock, __uuidof(this), &hr)
|
|
, m_pOutput(NULL)
|
|
, m_size(w, h)
|
|
, m_rtAvgTimePerFrame(10000000i64/fps)
|
|
, m_rtNow(0)
|
|
{
|
|
CMediaType mt;
|
|
|
|
mt.majortype = MEDIATYPE_Video;
|
|
mt.subtype = MEDIASUBTYPE_RGB32;
|
|
mt.formattype = FORMAT_VideoInfo;
|
|
mt.lSampleSize = w*h*4;
|
|
|
|
VIDEOINFOHEADER vih;
|
|
memset(&vih, 0, sizeof(vih));
|
|
vih.AvgTimePerFrame = m_rtAvgTimePerFrame;
|
|
vih.bmiHeader.biSize = sizeof(vih.bmiHeader);
|
|
vih.bmiHeader.biCompression = BI_RGB;
|
|
vih.bmiHeader.biPlanes = 1;
|
|
vih.bmiHeader.biWidth = w;
|
|
vih.bmiHeader.biHeight = -h;
|
|
vih.bmiHeader.biBitCount = 32;
|
|
vih.bmiHeader.biSizeImage = w*h*4;
|
|
mt.SetFormat((BYTE*)&vih, sizeof(vih));
|
|
|
|
m_pOutput.Attach(new GSSourceOutputPin(mt, this, &m_csLock, hr));
|
|
}
|
|
|
|
DECLARE_IUNKNOWN;
|
|
|
|
int GetPinCount() {return 1;}
|
|
CBasePin* GetPin(int n) {return n == 0 ? m_pOutput.m_p : NULL;}
|
|
|
|
// IGSSource
|
|
|
|
STDMETHODIMP DeliverNewSegment()
|
|
{
|
|
return m_pOutput->DeliverNewSegment(m_rtNow = 0, _I64_MAX, 1.0);
|
|
}
|
|
|
|
STDMETHODIMP DeliverFrame(D3DLOCKED_RECT& r)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if(!m_pOutput || !m_pOutput->IsConnected()) return E_UNEXPECTED;
|
|
|
|
CComPtr<IMediaSample> pSample;
|
|
if(FAILED(hr = m_pOutput->GetDeliveryBuffer(&pSample, NULL, NULL, 0)))
|
|
return hr;
|
|
|
|
REFERENCE_TIME rtStart = m_rtNow, rtStop = m_rtNow + m_rtAvgTimePerFrame;
|
|
|
|
pSample->SetTime(&rtStart, &rtStop);
|
|
pSample->SetSyncPoint(TRUE);
|
|
|
|
BYTE* pSrc = (BYTE*)r.pBits;
|
|
int srcpitch = r.Pitch;
|
|
|
|
BYTE* pDst = NULL;
|
|
pSample->GetPointer(&pDst);
|
|
CMediaType mt;
|
|
m_pOutput->GetMediaType(0, &mt);
|
|
int dstpitch = ((VIDEOINFOHEADER*)mt.Format())->bmiHeader.biWidth*4;
|
|
|
|
int minpitch = min(srcpitch, dstpitch);
|
|
|
|
for(int y = 0; y < m_size.cy; y++, pDst += dstpitch, pSrc += srcpitch)
|
|
memcpy(pDst, pSrc, minpitch);
|
|
|
|
if(FAILED(hr = m_pOutput->Deliver(pSample)))
|
|
return hr;
|
|
|
|
m_rtNow = rtStop;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP DeliverEOS()
|
|
{
|
|
return m_pOutput->DeliverEndOfStream();
|
|
}
|
|
};
|
|
|
|
//
|
|
// GSCapture
|
|
//
|
|
|
|
GSCapture::GSCapture()
|
|
{
|
|
}
|
|
|
|
bool GSCapture::BeginCapture(IDirect3DDevice9* pD3Dev, int fps)
|
|
{
|
|
ASSERT(pD3Dev != NULL && fps != 0);
|
|
|
|
EndCapture();
|
|
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
|
|
GSCaptureDlg dlg;
|
|
dlg.DoModal();
|
|
//if(IDOK != dlg.DoModal())
|
|
// return false;
|
|
|
|
HRESULT hr;
|
|
|
|
int w, h;
|
|
w = 640, h = 480; // TODO
|
|
|
|
CComPtr<IDirect3DSurface9> pRTSurf;
|
|
if(FAILED(hr = pD3Dev->CreateRenderTarget(w, h, D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0, TRUE, &pRTSurf, NULL)))
|
|
return false;
|
|
|
|
if(FAILED(hr = pD3Dev->CreateOffscreenPlainSurface(w, h, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &m_pSysMemSurf, NULL)))
|
|
return false;
|
|
|
|
//
|
|
|
|
m_pGB.CoCreateInstance(CLSID_FilterGraph);
|
|
if(!m_pGB) return false;
|
|
|
|
CComPtr<ICaptureGraphBuilder2> pCGB;
|
|
pCGB.CoCreateInstance(CLSID_CaptureGraphBuilder2);
|
|
if(!pCGB) return false;
|
|
|
|
pCGB->SetFiltergraph(m_pGB);
|
|
|
|
CComPtr<IBaseFilter> pMux;
|
|
if(FAILED(hr = pCGB->SetOutputFileName(&MEDIASUBTYPE_Avi, CStringW(dlg.m_filename), &pMux, NULL)))
|
|
return false;
|
|
|
|
m_pSrc = new GSSource(w, h, fps, NULL, hr);
|
|
if(FAILED(hr = m_pGB->AddFilter(m_pSrc, L"Source")))
|
|
return false;
|
|
|
|
if(FAILED(hr = m_pGB->AddFilter(dlg.m_pVidEnc, L"Encoder")))
|
|
return false;
|
|
|
|
if(FAILED(hr = pCGB->RenderStream(NULL, NULL, m_pSrc, dlg.m_pVidEnc, pMux)))
|
|
return false;
|
|
|
|
hr = CComQIPtr<IMediaControl>(m_pGB)->Run();
|
|
|
|
hr = CComQIPtr<IGSSource>(m_pSrc)->DeliverNewSegment();
|
|
|
|
m_pRTSurf = pRTSurf;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GSCapture::BeginFrame(int& w, int& h, IDirect3DSurface9** pRTSurf)
|
|
{
|
|
if(!m_pRTSurf || !pRTSurf) return false;
|
|
|
|
// FIXME: remember w, h from BeginCapture
|
|
D3DSURFACE_DESC desc;
|
|
if(FAILED(m_pRTSurf->GetDesc(&desc))) return false;
|
|
w = desc.Width;
|
|
h = desc.Height;
|
|
|
|
(*pRTSurf = m_pRTSurf)->AddRef();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GSCapture::EndFrame()
|
|
{
|
|
if(m_pRTSurf == NULL || m_pSysMemSurf == NULL) {ASSERT(0); return false;}
|
|
|
|
CComPtr<IDirect3DDevice9> pD3DDev;
|
|
D3DLOCKED_RECT r;
|
|
|
|
if(FAILED(m_pRTSurf->GetDevice(&pD3DDev))
|
|
|| FAILED(pD3DDev->GetRenderTargetData(m_pRTSurf, m_pSysMemSurf))
|
|
|| FAILED(m_pSysMemSurf->LockRect(&r, NULL, D3DLOCK_READONLY)))
|
|
return false;
|
|
|
|
CComQIPtr<IGSSource>(m_pSrc)->DeliverFrame(r);
|
|
|
|
m_pSysMemSurf->UnlockRect();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GSCapture::EndCapture()
|
|
{
|
|
m_pRTSurf = NULL;
|
|
m_pSysMemSurf = NULL;
|
|
|
|
if(m_pSrc) CComQIPtr<IGSSource>(m_pSrc)->DeliverEOS();
|
|
if(m_pGB) CComQIPtr<IMediaControl>(m_pGB)->Stop();
|
|
|
|
m_pSrc = NULL;
|
|
m_pGB = NULL;
|
|
|
|
return true;
|
|
}
|