Capture: Optimizations with filenames, audio, and capture mainFrame gui

*Resulting .wavs should get saved in the same location as the video file on linux
*Keep gui capture state consistent regardless of the method used to start recording
*Reworked mainFrame capture menu video options to route to a single toggleCapture_click function
*Split GS & SPU2 recording into start & end functions
This commit is contained in:
sonicfind 2020-09-20 23:26:02 -05:00 committed by refractionpcsx2
parent 1e5e742601
commit c60cdbba07
18 changed files with 125 additions and 127 deletions

View File

@ -133,10 +133,12 @@ void CALLBACK GSsetGameCRC(int crc, int gameoptions);
// controls frame skipping in the GS, if this routine isn't present, frame skipping won't be done
void CALLBACK GSsetFrameSkip(int frameskip);
// if start is 1, starts recording spu2 data, else stops
// Starts recording GS frame data
// returns a non zero value if successful
// for now, pData is not used
int CALLBACK GSsetupRecording(int start, void *pData);
int CALLBACK GSsetupRecording(std::string& filename);
// Stops recording GS frame data
void CALLBACK GSendRecording();
void CALLBACK GSreset();
//deprecated: GSgetTitleInfo was used in PCSX2 but no plugin supported it prior to r4070:
@ -183,7 +185,8 @@ typedef void(CALLBACK *_GSsetGameCRC)(int, int);
typedef void(CALLBACK *_GSsetFrameSkip)(int frameskip);
typedef void(CALLBACK *_GSsetVsync)(int enabled);
typedef void(CALLBACK *_GSsetExclusive)(int isExclusive);
typedef std::wstring*(CALLBACK *_GSsetupRecording)(int);
typedef int(CALLBACK* _GSsetupRecording)(std::string&);
typedef void(CALLBACK* _GSendRecording)();
typedef void(CALLBACK *_GSreset)();
typedef void(CALLBACK *_GSwriteCSR)(u32 value);
typedef bool(CALLBACK *_GSmakeSnapshot)(const char *path);
@ -220,6 +223,7 @@ extern _GSsetGameCRC GSsetGameCRC;
extern _GSsetFrameSkip GSsetFrameSkip;
extern _GSsetVsync GSsetVsync;
extern _GSsetupRecording GSsetupRecording;
extern _GSendRecording GSendRecording;
extern _GSreset GSreset;
extern _GSwriteCSR GSwriteCSR;
#endif

View File

@ -177,6 +177,7 @@ _GSsetFrameSkip GSsetFrameSkip;
_GSsetVsync GSsetVsync;
_GSsetExclusive GSsetExclusive;
_GSsetupRecording GSsetupRecording;
_GSendRecording GSendRecording;
_GSreset GSreset;
_GSwriteCSR GSwriteCSR;
#endif
@ -316,6 +317,7 @@ static const LegacyApi_OptMethod s_MethMessOpt_GS[] =
{ "GSopen2", (vMeth**)&GSopen2 },
{ "GSreset", (vMeth**)&GSreset },
{ "GSsetupRecording", (vMeth**)&GSsetupRecording },
{ "GSendRecording", (vMeth**)&GSendRecording },
{ "GSmakeSnapshot2", (vMeth**)&GSmakeSnapshot2 },
{ "GSgifSoftReset", (vMeth**)&GSgifSoftReset },
{ "GSreadFIFO", (vMeth**)&GSreadFIFO },

View File

@ -682,7 +682,7 @@ extern SndOutModule* mods[];
extern bool WavRecordEnabled;
extern void RecordStart(std::wstring* filename);
extern int RecordStart(const std::string* filename);
extern void RecordStop();
extern void RecordWrite(const StereoOut16& sample);

View File

@ -110,28 +110,27 @@ bool WavRecordEnabled = false;
static WavOutFile* m_wavrecord = nullptr;
static Mutex WavRecordMutex;
void RecordStart(std::wstring* filename)
int RecordStart(const std::string* filename)
{
WavRecordEnabled = false;
try
{
ScopedLock lock(WavRecordMutex);
safe_delete(m_wavrecord);
#ifdef _WIN32
if (filename)
m_wavrecord = new WavOutFile((*filename) + "wav", 48000, 16, 2);
m_wavrecord = new WavOutFile(filename->c_str(), 48000, 16, 2);
else
m_wavrecord = new WavOutFile("audio_recording.wav", 48000, 16, 2);
#elif defined(__unix__)
m_wavrecord = new WavOutFile("audio_recording.wav", 48000, 16, 2);
#endif
WavRecordEnabled = true;
return 1;
}
catch (std::runtime_error&)
{
m_wavrecord = nullptr; // not needed, but what the heck. :)
SysMessage("SPU2 couldn't open file for recording: %s.\nRecording to wavfile disabled.", "audio_recording.wav");
if (filename)
SysMessage("SPU2-X couldn't open file for recording: %s.\nWavfile capture disabled.", filename->c_str());
else
SysMessage("SPU2-X couldn't open file for recording: audio_recording.wav.\nWavfile capture disabled.");
return 0;
}
}

View File

@ -578,17 +578,16 @@ void SPU2write(u32 rmem, u16 value)
}
}
// if start is 1, starts recording spu2 data, else stops
// returns a non zero value if successful
// for now, pData is not used
int SPU2setupRecording(int start, std::wstring* filename)
int SPU2setupRecording(const std::string* filename)
{
if (start == 0)
RecordStop();
else if (start == 1)
RecordStart(filename);
return RecordStart(filename);
}
return 0;
void SPU2endRecording()
{
if (WavRecordEnabled)
RecordStop();
}
s32 SPU2freeze(int mode, freezeData* data)

View File

@ -31,10 +31,9 @@ void SPU2write(u32 mem, u16 value);
u16 SPU2read(u32 mem);
// extended funcs
// if start is 1, starts recording spu2 data, else stops
// returns a non zero value if successful
// for now, pData is not used
int SPU2setupRecording(int start, std::wstring* filename);
int SPU2setupRecording(const std::string* filename);
void SPU2endRecording();
void SPU2setClockPtr(u32* ptr);

View File

@ -40,7 +40,7 @@ uint renderswitch_delay = 0;
extern bool switchAR;
static int g_Pcsx2Recording = 0; // true 1 if recording video and sound
static bool g_Pcsx2Recording = false; // true if recording video and sound
KeyAcceleratorCode::KeyAcceleratorCode(const wxKeyEvent& evt)
@ -450,9 +450,14 @@ namespace Implementations
ScopedCoreThreadPause paused_core;
paused_core.AllowResume();
g_Pcsx2Recording ^= 1;
if (wxGetApp().HasGUI())
{
sMainFrame.VideoCaptureToggle();
return;
}
GetMTGS().WaitGS(); // make sure GS is in sync with the audio stream when we start.
g_Pcsx2Recording = !g_Pcsx2Recording;
if (g_Pcsx2Recording)
{
// start recording
@ -469,23 +474,17 @@ namespace Implementations
if (GSsetupRecording)
{
// GSsetupRecording can be aborted/canceled by the user. Don't go on to record the audio if that happens.
std::wstring* filename = nullptr;
if (filename = GSsetupRecording(g_Pcsx2Recording))
{
SPU2setupRecording(g_Pcsx2Recording, filename);
delete filename;
std::string filename;
if (GSsetupRecording(filename))
// Note: Add a dialog box here (or in the function) that prompts the user to answer whether a failed
// SPU2 recording setup should still lead to the visuals being recorded.
SPU2setupRecording(&filename);
else // recording dialog canceled by the user. align our state
g_Pcsx2Recording = false;
}
else
{
// recording dialog canceled by the user. align our state
g_Pcsx2Recording ^= 1;
}
}
else
{
// the GS doesn't support recording
SPU2setupRecording(g_Pcsx2Recording, NULL);
}
else
g_Pcsx2Recording = SPU2setupRecording(nullptr);
if (GetMainFramePtr() && needsMainFrameEnable)
GetMainFramePtr()->Enable();
@ -493,9 +492,9 @@ namespace Implementations
else
{
// stop recording
if (GSsetupRecording)
GSsetupRecording(g_Pcsx2Recording);
SPU2setupRecording(g_Pcsx2Recording, NULL);
if (GSendRecording)
GSendRecording();
SPU2endRecording();
}
}

View File

@ -289,8 +289,8 @@ void MainEmuFrame::ConnectMenus()
Bind(wxEVT_MENU, &MainEmuFrame::Menu_Debug_Open_Click, this, MenuId_Debug_Open);
// Capture
Bind(wxEVT_MENU, &MainEmuFrame::Menu_Capture_Video_Record_Click, this, MenuId_Capture_Video_Record);
Bind(wxEVT_MENU, &MainEmuFrame::Menu_Capture_Video_Stop_Click, this, MenuId_Capture_Video_Stop);
Bind(wxEVT_MENU, &MainEmuFrame::Menu_Capture_Video_ToggleCapture_Click, this, MenuId_Capture_Video_Record);
Bind(wxEVT_MENU, &MainEmuFrame::Menu_Capture_Video_ToggleCapture_Click, this, MenuId_Capture_Video_Stop);
Bind(wxEVT_MENU, &MainEmuFrame::Menu_Capture_Screenshot_Screenshot_Click, this, MenuId_Capture_Screenshot_Screenshot);
Bind(wxEVT_MENU, &MainEmuFrame::Menu_Capture_Screenshot_Screenshot_As_Click, this, MenuId_Capture_Screenshot_Screenshot_As);
@ -573,6 +573,7 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title)
{
m_RestartEmuOnDelete = false;
m_capturingVideo = false;
for (int i = 0; i < PluginId_Count; ++i)
m_PluginMenuPacks[i].Populate((PluginsEnum_t)i);

View File

@ -172,6 +172,7 @@ public:
void CommitPreset_noTrigger();
void AppendShortcutToMenuOption(wxMenuItem& item, wxString keyCodeStr);
void UpdateStatusBar();
void VideoCaptureToggle();
#ifndef DISABLE_RECORDING
void initializeRecordingMenuItem(MenuIdentifiers menuId, wxString keyCodeStr, bool enable = true);
void enableRecordingMenuItem(MenuIdentifiers menuId, bool enable);
@ -252,9 +253,7 @@ protected:
void Menu_Wiki(wxCommandEvent& event);
void Menu_ShowAboutBox(wxCommandEvent& event);
void Menu_Capture_Video_Record_Click(wxCommandEvent& event);
void Menu_Capture_Video_Stop_Click(wxCommandEvent& event);
void VideoCaptureUpdate();
void Menu_Capture_Video_ToggleCapture_Click(wxCommandEvent& event);
void Menu_Capture_Screenshot_Screenshot_Click(wxCommandEvent& event);
void Menu_Capture_Screenshot_Screenshot_As_Click(wxCommandEvent& event);

View File

@ -848,27 +848,17 @@ void MainEmuFrame::Menu_ShowAboutBox(wxCommandEvent& event)
AppOpenDialog<AboutBoxDialog>(this);
}
void MainEmuFrame::Menu_Capture_Video_Record_Click(wxCommandEvent& event)
void MainEmuFrame::Menu_Capture_Video_ToggleCapture_Click(wxCommandEvent& event)
{
ScopedCoreThreadPause paused_core;
paused_core.AllowResume();
m_capturingVideo = true;
VideoCaptureUpdate();
VideoCaptureToggle();
}
void MainEmuFrame::Menu_Capture_Video_Stop_Click(wxCommandEvent& event)
{
ScopedCoreThreadPause paused_core;
paused_core.AllowResume();
m_capturingVideo = false;
VideoCaptureUpdate();
}
void MainEmuFrame::VideoCaptureUpdate()
void MainEmuFrame::VideoCaptureToggle()
{
GetMTGS().WaitGS(); // make sure GS is in sync with the audio stream when we start.
m_capturingVideo = !m_capturingVideo;
if (m_capturingVideo)
{
// start recording
@ -876,20 +866,23 @@ void MainEmuFrame::VideoCaptureUpdate()
// make the recording setup dialog[s] pseudo-modal also for the main PCSX2 window
// (the GSdx dialog is already properly modal for the GS window)
bool needsMainFrameEnable = false;
if (GetMainFramePtr() && GetMainFramePtr()->IsEnabled())
if (IsEnabled())
{
needsMainFrameEnable = true;
GetMainFramePtr()->Disable();
Disable();
}
if (GSsetupRecording)
{
// GSsetupRecording can be aborted/canceled by the user. Don't go on to record the audio if that happens
std::wstring* filename = nullptr;
if (filename = GSsetupRecording(m_capturingVideo))
std::string filename;
if (GSsetupRecording(filename))
{
SPU2setupRecording(m_capturingVideo, filename);
delete filename;
// Note: Add a dialog box here (or in the function) that prompts the user to answer whether a failed
// SPU2 recording setup should still lead to the visuals being recorded.
SPU2setupRecording(&filename);
m_submenuVideoCapture.Enable(MenuId_Capture_Video_Record, false);
m_submenuVideoCapture.Enable(MenuId_Capture_Video_Stop, true);
}
else
{
@ -900,29 +893,26 @@ void MainEmuFrame::VideoCaptureUpdate()
else
{
// the GS doesn't support recording.
SPU2setupRecording(m_capturingVideo, nullptr);
if (SPU2setupRecording(nullptr))
{
m_submenuVideoCapture.Enable(MenuId_Capture_Video_Record, false);
m_submenuVideoCapture.Enable(MenuId_Capture_Video_Stop, true);
}
else
m_capturingVideo = false;
}
if (GetMainFramePtr() && needsMainFrameEnable)
GetMainFramePtr()->Enable();
if (needsMainFrameEnable)
Enable();
}
else
{
// stop recording
if (GSsetupRecording)
GSsetupRecording(m_capturingVideo);
SPU2setupRecording(m_capturingVideo, nullptr);
}
if (m_capturingVideo)
{
m_submenuVideoCapture.FindItem(MenuId_Capture_Video_Record)->Enable(false);
m_submenuVideoCapture.FindItem(MenuId_Capture_Video_Stop)->Enable(true);
}
else
{
m_submenuVideoCapture.FindItem(MenuId_Capture_Video_Record)->Enable(true);
m_submenuVideoCapture.FindItem(MenuId_Capture_Video_Stop)->Enable(false);
if (GSendRecording)
GSendRecording();
SPU2endRecording();
m_submenuVideoCapture.Enable(MenuId_Capture_Video_Record, true);
m_submenuVideoCapture.Enable(MenuId_Capture_Video_Stop, false);
}
}

View File

@ -677,7 +677,11 @@ EXPORT_C_(uint32) GSmakeSnapshot(char* path)
{
// Allows for providing a complete path
std::string extension = s.substr(s.size() - 4, 4);
#ifdef _WIN32
std::transform(extension.begin(), extension.end(), extension.begin(), (char(_cdecl*)(int))tolower);
#else
std::transform(extension.begin(), extension.end(), extension.begin(), tolower);
#endif
if (extension == ".png")
return s_gs->MakeSnapshot(s);
else if (s[s.length() - 1] != DIRECTORY_SEPARATOR)
@ -807,41 +811,36 @@ void pt(const char* str){
printf("%02i:%02i:%02i%s", current->tm_hour, current->tm_min, current->tm_sec, str);
}
EXPORT_C_(std::wstring*) GSsetupRecording(int start)
EXPORT_C_(int) GSsetupRecording(std::string& filename)
{
if (s_gs == NULL) {
printf("GSdx: no s_gs for recording\n");
return nullptr;
return 0;
}
#if defined(__unix__) || defined(__APPLE__)
if (!theApp.GetConfigB("capture_enabled")) {
printf("GSdx: Recording is disabled\n");
return nullptr;
return 0;
}
#endif
std::wstring* filename = nullptr;
if(start & 1)
{
printf("GSdx: Recording start command\n");
filename = s_gs->BeginCapture();
if (filename)
if (s_gs->BeginCapture(filename))
{
pt(" - Capture started\n");
return 1;
}
else
{
pt(" - Capture cancelled\n");
return nullptr;
return 0;
}
}
else
{
}
EXPORT_C_(void) GSendRecording()
{
printf("GSdx: Recording end command\n");
s_gs->EndCapture();
pt(" - Capture ended\n");
}
return filename;
}
EXPORT_C GSsetGameCRC(uint32 crc, int options)

View File

@ -404,7 +404,7 @@ GSCapture::~GSCapture()
EndCapture();
}
std::wstring* GSCapture::BeginCapture(float fps, GSVector2i recommendedResolution, float aspect)
int GSCapture::BeginCapture(float fps, GSVector2i recommendedResolution, float aspect, std::string& filename)
{
printf("Recommended resolution: %d x %d, DAR for muxing: %.4f\n", recommendedResolution.x, recommendedResolution.y, aspect);
std::lock_guard<std::recursive_mutex> lock(m_lock);
@ -418,10 +418,10 @@ std::wstring* GSCapture::BeginCapture(float fps, GSVector2i recommendedResolutio
GSCaptureDlg dlg;
if (IDOK != dlg.DoModal())
return nullptr;
return 0;
{
int start = dlg.m_filename.length() - 4;
const int start = dlg.m_filename.length() - 4;
if (start > 0)
{
std::string test = dlg.m_filename.substr(start);
@ -438,11 +438,11 @@ std::wstring* GSCapture::BeginCapture(float fps, GSVector2i recommendedResolutio
else
{
dlg.InvalidFile();
return nullptr;
return 0;
}
}
std::wstring fn{dlg.m_filename.begin(), dlg.m_filename.end()};
;
m_size.x = (dlg.m_width + 7) & ~7;
m_size.y = (dlg.m_height + 7) & ~7;
@ -456,9 +456,9 @@ std::wstring* GSCapture::BeginCapture(float fps, GSVector2i recommendedResolutio
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, fn.c_str(), &mux, NULL)))
|| FAILED(hr = cgb->SetOutputFileName(&MEDIASUBTYPE_Avi, std::wstring(dlg.m_filename.begin(), dlg.m_filename.end()).c_str(), &mux, NULL)))
{
return nullptr;
return 0;
}
m_src = new GSSource(m_size.x, m_size.y, fps, NULL, hr, dlg.m_colorspace);
@ -466,22 +466,22 @@ std::wstring* GSCapture::BeginCapture(float fps, GSVector2i recommendedResolutio
if (dlg.m_enc==0)
{
if (FAILED(hr = m_graph->AddFilter(m_src, L"Source")))
return nullptr;
return 0;
if (FAILED(hr = m_graph->ConnectDirect(GetFirstPin(m_src, PINDIR_OUTPUT), GetFirstPin(mux, PINDIR_INPUT), NULL)))
return nullptr;
return 0;
}
else
{
if(FAILED(hr = m_graph->AddFilter(m_src, L"Source"))
|| FAILED(hr = m_graph->AddFilter(dlg.m_enc, L"Encoder")))
{
return nullptr;
return 0;
}
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 nullptr;
return 0;
}
}
@ -509,7 +509,8 @@ std::wstring* GSCapture::BeginCapture(float fps, GSVector2i recommendedResolutio
CComQIPtr<IGSSource>(m_src)->DeliverNewSegment();
m_capturing = true;
return new std::wstring(dlg.m_filename.begin(), dlg.m_filename.end() - 3);
filename = dlg.m_filename.erase(dlg.m_filename.length() - 3, 3) + "wav";
return 1;
#elif defined(__unix__)
// Note I think it doesn't support multiple depth creation
GSmkdir(m_out_dir.c_str());
@ -525,7 +526,8 @@ std::wstring* GSCapture::BeginCapture(float fps, GSVector2i recommendedResolutio
}
m_capturing = true;
return new std::wstring();
filename = m_out_dir + "/audio_recording.wav";
return 1;
#endif
}
@ -564,6 +566,9 @@ bool GSCapture::DeliverFrame(const void* bits, int pitch, bool rgba)
bool GSCapture::EndCapture()
{
if (!m_capturing)
return false;
std::lock_guard<std::recursive_mutex> lock(m_lock);
#ifdef _WIN32

View File

@ -53,7 +53,7 @@ public:
GSCapture();
virtual ~GSCapture();
std::wstring* BeginCapture(float fps, GSVector2i recommendedResolution, float aspect);
int BeginCapture(float fps, GSVector2i recommendedResolution, float aspect, std::string& filename);
bool DeliverFrame(const void* bits, int pitch, bool rgba);
bool EndCapture();

View File

@ -336,7 +336,7 @@ void GSmkdir(const wchar_t* dir)
if (!CreateDirectory(dir, nullptr)) {
DWORD errorID = ::GetLastError();
if (errorID != ERROR_ALREADY_EXISTS) {
fprintf(stderr, "Failed to create directory: %s error %u\n", dir, errorID);
fprintf(stderr, "Failed to create directory: %ls error %u\n", dir, errorID);
}
}
#else

View File

@ -34,6 +34,7 @@ EXPORTS
GSreadFIFO2
GSirqCallback
GSsetupRecording
GSendRecording
GSsetGameCRC
GSsetFrameSkip
GSsetVsync

View File

@ -533,12 +533,12 @@ bool GSRenderer::MakeSnapshot(const std::string& path)
return true;
}
std::wstring* GSRenderer::BeginCapture()
int GSRenderer::BeginCapture(std::string& filename)
{
GSVector4i disp = m_wnd->GetClientRect().fit(m_aspectratio);
float aspect = (float)disp.width() / std::max(1, disp.height());
return m_capture.BeginCapture(GetTvRefreshRate(), GetInternalResolution(), aspect);
return m_capture.BeginCapture(GetTvRefreshRate(), GetInternalResolution(), aspect, filename);
}
void GSRenderer::EndCapture()

View File

@ -72,7 +72,7 @@ public:
void SetAspectRatio(int aspect) {m_aspectratio = aspect;}
void SetVSync(int vsync);
virtual std::wstring* BeginCapture();
virtual int BeginCapture(std::string& filename);
virtual void EndCapture();
void PurgePool();

View File

@ -40,7 +40,8 @@
void GSCaptureDlg::InvalidFile()
{
wchar_t tmp[512];
swprintf_s(tmp, L"GSdx couldn't open file for capturing: %s.\nCapture aborted.", m_filename.c_str());
std::wstring tmpstr(m_filename.begin(), m_filename.end());
swprintf_s(tmp, L"GSdx couldn't open file for capturing: %ls.\nCapture aborted.", tmpstr.c_str());
MessageBox(GetActiveWindow(), tmp, L"GSdx System Message", MB_OK | MB_SETFOREGROUND);
}