summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorFog <christhecoolist@gmail.com>2014-10-04 03:28:01 -0400
committerFog <christhecoolist@gmail.com>2014-10-09 00:06:04 -0400
commitfc4125cdd15c6119d75f6667cd44dcbd4df4a8b3 (patch)
treea7ce8b2e4c918ea08db6d9841befc13b27c7250e /Source
parent47bf698b70668f72f8495da209ffe68091d89bcf (diff)
Proper Audio/Video Dumping
Diffstat (limited to 'Source')
-rw-r--r--Source/Core/AudioCommon/AudioCommon.cpp14
-rw-r--r--Source/Core/AudioCommon/AudioCommon.vcxproj2
-rw-r--r--Source/Core/AudioCommon/AudioCommon.vcxproj.filters2
-rw-r--r--Source/Core/AudioCommon/Mixer.cpp6
-rw-r--r--Source/Core/AudioCommon/Mixer.h64
-rw-r--r--Source/Core/Core/State.cpp4
-rw-r--r--Source/Core/VideoCommon/AVIDump.cpp238
-rw-r--r--Source/Core/VideoCommon/AVIDump.h5
8 files changed, 247 insertions, 88 deletions
diff --git a/Source/Core/AudioCommon/AudioCommon.cpp b/Source/Core/AudioCommon/AudioCommon.cpp
index 88fc7b892f..8afbc1478e 100644
--- a/Source/Core/AudioCommon/AudioCommon.cpp
+++ b/Source/Core/AudioCommon/AudioCommon.cpp
@@ -68,9 +68,12 @@ namespace AudioCommon
{
if (SConfig::GetInstance().m_DumpAudio)
{
- std::string audio_file_name = File::GetUserPath(D_DUMPAUDIO_IDX) + "audiodump.wav";
- File::CreateFullPath(audio_file_name);
- mixer->StartLogAudio(audio_file_name);
+ std::string audio_file_name_dtk = File::GetUserPath(D_DUMPAUDIO_IDX) + "dtkdump.wav";
+ std::string audio_file_name_dsp = File::GetUserPath(D_DUMPAUDIO_IDX) + "dspdump.wav";
+ File::CreateFullPath(audio_file_name_dtk);
+ File::CreateFullPath(audio_file_name_dsp);
+ mixer->StartLogDTKAudio(audio_file_name_dtk);
+ mixer->StartLogDSPAudio(audio_file_name_dsp);
}
return g_sound_stream;
@@ -93,8 +96,11 @@ namespace AudioCommon
{
g_sound_stream->Stop();
if (SConfig::GetInstance().m_DumpAudio)
- g_sound_stream->GetMixer()->StopLogAudio();
+ {
+ g_sound_stream->GetMixer()->StopLogDTKAudio();
+ g_sound_stream->GetMixer()->StopLogDSPAudio();
//g_sound_stream->StopLogAudio();
+ }
delete g_sound_stream;
g_sound_stream = nullptr;
}
diff --git a/Source/Core/AudioCommon/AudioCommon.vcxproj b/Source/Core/AudioCommon/AudioCommon.vcxproj
index c626ffd77d..13aa887e32 100644
--- a/Source/Core/AudioCommon/AudioCommon.vcxproj
+++ b/Source/Core/AudioCommon/AudioCommon.vcxproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
diff --git a/Source/Core/AudioCommon/AudioCommon.vcxproj.filters b/Source/Core/AudioCommon/AudioCommon.vcxproj.filters
index 2832422073..fee0799dbb 100644
--- a/Source/Core/AudioCommon/AudioCommon.vcxproj.filters
+++ b/Source/Core/AudioCommon/AudioCommon.vcxproj.filters
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="SoundStreams">
diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp
index a7984579ca..60203b2f37 100644
--- a/Source/Core/AudioCommon/Mixer.cpp
+++ b/Source/Core/AudioCommon/Mixer.cpp
@@ -122,8 +122,6 @@ unsigned int CMixer::Mix(short* samples, unsigned int num_samples, bool consider
m_dma_mixer.Mix(samples, num_samples, consider_framelimit);
m_streaming_mixer.Mix(samples, num_samples, consider_framelimit);
m_wiimote_speaker_mixer.Mix(samples, num_samples, consider_framelimit);
- if (m_logAudio)
- g_wave_writer.AddStereoSamples(samples, num_samples);
return num_samples;
}
@@ -161,11 +159,15 @@ void CMixer::MixerFifo::PushSamples(const short *samples, unsigned int num_sampl
void CMixer::PushSamples(const short *samples, unsigned int num_samples)
{
m_dma_mixer.PushSamples(samples, num_samples);
+ if (m_log_dsp_audio)
+ g_wave_writer_dsp.AddStereoSamplesBE(samples, num_samples);
}
void CMixer::PushStreamingSamples(const short *samples, unsigned int num_samples)
{
m_streaming_mixer.PushSamples(samples, num_samples);
+ if (m_log_dtk_audio)
+ g_wave_writer_dtk.AddStereoSamplesBE(samples, num_samples);
}
void CMixer::PushWiimoteSpeakerSamples(const short *samples, unsigned int num_samples, unsigned int sample_rate)
diff --git a/Source/Core/AudioCommon/Mixer.h b/Source/Core/AudioCommon/Mixer.h
index e3272af070..ba9809d5bc 100644
--- a/Source/Core/AudioCommon/Mixer.h
+++ b/Source/Core/AudioCommon/Mixer.h
@@ -26,7 +26,8 @@ public:
, m_streaming_mixer(this, 48000)
, m_wiimote_speaker_mixer(this, 3000)
, m_sampleRate(BackendSampleRate)
- , m_logAudio(0)
+ , m_log_dtk_audio(0)
+ , m_log_dsp_audio(0)
, m_speed(0)
{
INFO_LOG(AUDIO_INTERFACE, "Mixer is initialized");
@@ -48,32 +49,61 @@ public:
void SetStreamingVolume(unsigned int lvolume, unsigned int rvolume);
void SetWiimoteSpeakerVolume(unsigned int lvolume, unsigned int rvolume);
- virtual void StartLogAudio(const std::string& filename)
+ virtual void StartLogDTKAudio(const std::string& filename)
{
- if (! m_logAudio)
+ if (!m_log_dtk_audio)
{
- m_logAudio = true;
- g_wave_writer.Start(filename, GetSampleRate());
- g_wave_writer.SetSkipSilence(false);
- NOTICE_LOG(DSPHLE, "Starting Audio logging");
+ m_log_dtk_audio = true;
+ g_wave_writer_dtk.Start(filename, 48000);
+ g_wave_writer_dtk.SetSkipSilence(false);
+ NOTICE_LOG(DSPHLE, "Starting DTK Audio logging");
}
else
{
- WARN_LOG(DSPHLE, "Audio logging has already been started");
+ WARN_LOG(DSPHLE, "DTK Audio logging has already been started");
}
}
- virtual void StopLogAudio()
+ virtual void StopLogDTKAudio()
{
- if (m_logAudio)
+ if (m_log_dtk_audio)
{
- m_logAudio = false;
- g_wave_writer.Stop();
- NOTICE_LOG(DSPHLE, "Stopping Audio logging");
+ m_log_dtk_audio = false;
+ g_wave_writer_dtk.Stop();
+ NOTICE_LOG(DSPHLE, "Stopping DTK Audio logging");
}
else
{
- WARN_LOG(DSPHLE, "Audio logging has already been stopped");
+ WARN_LOG(DSPHLE, "DTK Audio logging has already been stopped");
+ }
+ }
+
+ virtual void StartLogDSPAudio(const std::string& filename)
+ {
+ if (!m_log_dsp_audio)
+ {
+ m_log_dsp_audio = true;
+ g_wave_writer_dsp.Start(filename, 32000);
+ g_wave_writer_dsp.SetSkipSilence(false);
+ NOTICE_LOG(DSPHLE, "Starting DSP Audio logging");
+ }
+ else
+ {
+ WARN_LOG(DSPHLE, "DSP Audio logging has already been started");
+ }
+ }
+
+ virtual void StopLogDSPAudio()
+ {
+ if (m_log_dsp_audio)
+ {
+ m_log_dsp_audio = false;
+ g_wave_writer_dsp.Stop();
+ NOTICE_LOG(DSPHLE, "Stopping DSP Audio logging");
+ }
+ else
+ {
+ WARN_LOG(DSPHLE, "DSP Audio logging has already been stopped");
}
}
@@ -118,9 +148,11 @@ protected:
MixerFifo m_wiimote_speaker_mixer;
unsigned int m_sampleRate;
- WaveFileWriter g_wave_writer;
+ WaveFileWriter g_wave_writer_dtk;
+ WaveFileWriter g_wave_writer_dsp;
- bool m_logAudio;
+ bool m_log_dtk_audio;
+ bool m_log_dsp_audio;
std::mutex m_csMixing;
diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp
index 8d05d01fbc..b1cb54afd0 100644
--- a/Source/Core/Core/State.cpp
+++ b/Source/Core/Core/State.cpp
@@ -25,6 +25,7 @@
#include "Core/HW/Wiimote.h"
#include "Core/PowerPC/JitCommon/JitBase.h"
+#include "VideoCommon/AVIDump.h"
#include "VideoCommon/VideoBackendBase.h"
namespace State
@@ -116,6 +117,9 @@ static void DoState(PointerWrap &p)
p.DoMarker("CoreTiming");
Movie::DoState(p);
p.DoMarker("Movie");
+#if defined(HAVE_LIBAV) || defined (WIN32)
+ AVIDump::DoState();
+#endif
}
void LoadFromBuffer(std::vector<u8>& buffer)
diff --git a/Source/Core/VideoCommon/AVIDump.cpp b/Source/Core/VideoCommon/AVIDump.cpp
index 850868d54e..2bd655b9d0 100644
--- a/Source/Core/VideoCommon/AVIDump.cpp
+++ b/Source/Core/VideoCommon/AVIDump.cpp
@@ -26,38 +26,60 @@
#include <vfw.h>
#include <winerror.h>
-HWND m_emuWnd;
-LONG m_byteBuffer;
-LONG m_frameCount;
-LONG m_totalBytes;
-PAVIFILE m_file;
-int m_width;
-int m_height;
-int m_fileCount;
-PAVISTREAM m_stream;
-PAVISTREAM m_streamCompressed;
-AVISTREAMINFO m_header;
-AVICOMPRESSOPTIONS m_options;
-AVICOMPRESSOPTIONS *m_arrayOptions[1];
-BITMAPINFOHEADER m_bitmap;
+#include "Core/ConfigManager.h" // for EuRGB60
+#include "Core/CoreTiming.h"
+#include "Core/HW/SystemTimers.h"
+
+static HWND s_emu_wnd;
+static LONG s_byte_buffer;
+static LONG s_frame_count;
+static LONG s_total_bytes;
+static PAVIFILE s_file;
+static int s_width;
+static int s_height;
+static int s_file_count;
+static u64 s_last_frame;
+static PAVISTREAM s_stream;
+static PAVISTREAM s_stream_compressed;
+static int s_frame_rate;
+static AVISTREAMINFO s_header;
+static AVICOMPRESSOPTIONS s_options;
+static AVICOMPRESSOPTIONS* s_array_options[1];
+static BITMAPINFOHEADER s_bitmap;
+// the CFR dump design doesn't let you dump until you know the NEXT timecode.
+// so we have to save a frame and always be behind
+static void* s_stored_frame = nullptr;
+static u64 s_stored_frame_size = 0;
+bool b_start_dumping = false;
bool AVIDump::Start(HWND hWnd, int w, int h)
{
- m_emuWnd = hWnd;
- m_fileCount = 0;
+ s_emu_wnd = hWnd;
+ s_file_count = 0;
- m_width = w;
- m_height = h;
+ s_width = w;
+ s_height = h;
+
+ s_last_frame = CoreTiming::GetTicks();
+
+ if (SConfig::GetInstance().m_SYSCONF->GetData<u8>("IPL.E60"))
+ s_frame_rate = 60; // always 60, for either pal60 or ntsc
+ else
+ s_frame_rate = VideoInterface::TargetRefreshRate; // 50 or 60, depending on region
+
+ // clear CFR frame cache on start, not on file create (which is also segment switch)
+ SetBitmapFormat();
+ StoreFrame(nullptr);
return CreateFile();
}
bool AVIDump::CreateFile()
{
- m_totalBytes = 0;
- m_frameCount = 0;
+ s_total_bytes = 0;
+ s_frame_count = 0;
- std::string movie_file_name = StringFromFormat("%sframedump%d.avi", File::GetUserPath(D_DUMPFRAMES_IDX).c_str(), m_fileCount);
+ std::string movie_file_name = StringFromFormat("%sframedump%d.avi", File::GetUserPath(D_DUMPFRAMES_IDX).c_str(), s_file_count);
// Create path
File::CreateFullPath(movie_file_name);
@@ -72,7 +94,7 @@ bool AVIDump::CreateFile()
AVIFileInit();
NOTICE_LOG(VIDEO, "Opening AVI file (%s) for dumping", movie_file_name.c_str());
// TODO: Make this work with AVIFileOpenW without it throwing REGDB_E_CLASSNOTREG
- HRESULT hr = AVIFileOpenA(&m_file, movie_file_name.c_str(), OF_WRITE | OF_CREATE, nullptr);
+ HRESULT hr = AVIFileOpenA(&s_file, movie_file_name.c_str(), OF_WRITE | OF_CREATE, nullptr);
if (FAILED(hr))
{
if (hr == AVIERR_BADFORMAT) NOTICE_LOG(VIDEO, "The file couldn't be read, indicating a corrupt file or an unrecognized format.");
@@ -93,7 +115,7 @@ bool AVIDump::CreateFile()
return false;
}
- if (!m_fileCount)
+ if (!s_file_count)
{
if (!SetCompressionOptions())
{
@@ -103,14 +125,14 @@ bool AVIDump::CreateFile()
}
}
- if (FAILED(AVIMakeCompressedStream(&m_streamCompressed, m_stream, &m_options, nullptr)))
+ if (FAILED(AVIMakeCompressedStream(&s_stream_compressed, s_stream, &s_options, nullptr)))
{
NOTICE_LOG(VIDEO, "AVIMakeCompressedStream failed");
Stop();
return false;
}
- if (FAILED(AVIStreamSetFormat(m_streamCompressed, 0, &m_bitmap, m_bitmap.biSize)))
+ if (FAILED(AVIStreamSetFormat(s_stream_compressed, 0, &s_bitmap, s_bitmap.biSize)))
{
NOTICE_LOG(VIDEO, "AVIStreamSetFormat failed");
Stop();
@@ -122,22 +144,22 @@ bool AVIDump::CreateFile()
void AVIDump::CloseFile()
{
- if (m_streamCompressed)
+ if (s_stream_compressed)
{
- AVIStreamClose(m_streamCompressed);
- m_streamCompressed = nullptr;
+ AVIStreamClose(s_stream_compressed);
+ s_stream_compressed = nullptr;
}
- if (m_stream)
+ if (s_stream)
{
- AVIStreamClose(m_stream);
- m_stream = nullptr;
+ AVIStreamClose(s_stream);
+ s_stream = nullptr;
}
- if (m_file)
+ if (s_file)
{
- AVIFileRelease(m_file);
- m_file = nullptr;
+ AVIFileRelease(s_file);
+ s_file = nullptr;
}
AVIFileExit();
@@ -145,69 +167,150 @@ void AVIDump::CloseFile()
void AVIDump::Stop()
{
+ // store one copy of the last video frame, CFR case
+ if (s_stream_compressed)
+ AVIStreamWrite(s_stream_compressed, s_frame_count++, 1, GetFrame(), s_bitmap.biSizeImage, AVIIF_KEYFRAME, nullptr, &s_byte_buffer);
+ b_start_dumping = false;
CloseFile();
- m_fileCount = 0;
+ s_file_count = 0;
NOTICE_LOG(VIDEO, "Stop");
}
+void AVIDump::StoreFrame(const void* data)
+{
+ if (s_bitmap.biSizeImage > s_stored_frame_size)
+ {
+ void* temp_stored_frame = realloc(s_stored_frame, s_bitmap.biSizeImage);
+ if (temp_stored_frame)
+ {
+ s_stored_frame = temp_stored_frame;
+ }
+ else
+ {
+ free(s_stored_frame);
+ PanicAlert("Something has gone seriously wrong.\n"
+ "Stopping video recording.\n"
+ "Your video will likely be broken.");
+ Stop();
+ }
+ s_stored_frame_size = s_bitmap.biSizeImage;
+ memset(s_stored_frame, 0, s_bitmap.biSizeImage);
+ }
+ if (s_stored_frame)
+ {
+ if (data)
+ memcpy(s_stored_frame, data, s_bitmap.biSizeImage);
+ else // pitch black frame
+ memset(s_stored_frame, 0, s_bitmap.biSizeImage);
+ }
+}
+
+void* AVIDump::GetFrame()
+{
+ return s_stored_frame;
+}
+
void AVIDump::AddFrame(const u8* data, int w, int h)
{
static bool shown_error = false;
- if ((w != m_bitmap.biWidth || h != m_bitmap.biHeight) && !shown_error)
+ if ((w != s_bitmap.biWidth || h != s_bitmap.biHeight) && !shown_error)
{
PanicAlert("You have resized the window while dumping frames.\n"
"Nothing sane can be done to handle this.\n"
"Your video will likely be broken.");
shown_error = true;
- m_bitmap.biWidth = w;
- m_bitmap.biHeight = h;
+ s_bitmap.biWidth = w;
+ s_bitmap.biHeight = h;
}
-
- AVIStreamWrite(m_streamCompressed, ++m_frameCount, 1, const_cast<u8*>(data), m_bitmap.biSizeImage, AVIIF_KEYFRAME, nullptr, &m_byteBuffer);
- m_totalBytes += m_byteBuffer;
- // Close the recording if the file is more than 2gb
- // VfW can't properly save files over 2gb in size, but can keep writing to them up to 4gb.
- if (m_totalBytes >= 2000000000)
+ // no timecodes, instead dump each frame as many/few times as needed to keep sync
+ u64 one_cfr = SystemTimers::GetTicksPerSecond() / VideoInterface::TargetRefreshRate;
+ int nplay = 0;
+ s64 delta;
+ if (!b_start_dumping && s_last_frame <= SystemTimers::GetTicksPerSecond())
{
- CloseFile();
- m_fileCount++;
- CreateFile();
+ delta = CoreTiming::GetTicks();
+ b_start_dumping = true;
+ }
+ else
+ {
+ delta = CoreTiming::GetTicks() - s_last_frame;
}
+ bool b_frame_dumped = false;
+ // try really hard to place one copy of frame in stream (otherwise it's dropped)
+ if (delta > (s64)one_cfr * 3 / 10) // place if 3/10th of a frame space
+ {
+ delta -= one_cfr;
+ nplay++;
+ }
+ // try not nearly so hard to place additional copies of the frame
+ while (delta > (s64)one_cfr * 8 / 10) // place if 8/10th of a frame space
+ {
+ delta -= one_cfr;
+ nplay++;
+ }
+ while (nplay--)
+ {
+ if (!b_frame_dumped)
+ {
+ AVIStreamWrite(s_stream_compressed, s_frame_count++, 1, GetFrame(), s_bitmap.biSizeImage, AVIIF_KEYFRAME, nullptr, &s_byte_buffer);
+ b_frame_dumped = true;
+ }
+ else
+ {
+ AVIStreamWrite(s_stream, s_frame_count++, 1, nullptr, 0, 0, nullptr, nullptr);
+ }
+ s_total_bytes += s_byte_buffer;
+ // Close the recording if the file is larger than 2gb
+ // VfW can't properly save files over 2gb in size, but can keep writing to them up to 4gb.
+ if (s_total_bytes >= 2000000000)
+ {
+ CloseFile();
+ s_file_count++;
+ CreateFile();
+ }
+ }
+ StoreFrame(data);
+ s_last_frame = CoreTiming::GetTicks();
}
void AVIDump::SetBitmapFormat()
{
- memset(&m_bitmap, 0, sizeof(m_bitmap));
- m_bitmap.biSize = 0x28;
- m_bitmap.biPlanes = 1;
- m_bitmap.biBitCount = 24;
- m_bitmap.biWidth = m_width;
- m_bitmap.biHeight = m_height;
- m_bitmap.biSizeImage = 3 * m_width * m_height;
+ memset(&s_bitmap, 0, sizeof(s_bitmap));
+ s_bitmap.biSize = 0x28;
+ s_bitmap.biPlanes = 1;
+ s_bitmap.biBitCount = 24;
+ s_bitmap.biWidth = s_width;
+ s_bitmap.biHeight = s_height;
+ s_bitmap.biSizeImage = 3 * s_width * s_height;
}
bool AVIDump::SetCompressionOptions()
{
- memset(&m_options, 0, sizeof(m_options));
- m_arrayOptions[0] = &m_options;
+ memset(&s_options, 0, sizeof(s_options));
+ s_array_options[0] = &s_options;
- return (AVISaveOptions(m_emuWnd, 0, 1, &m_stream, m_arrayOptions) != 0);
+ return (AVISaveOptions(s_emu_wnd, 0, 1, &s_stream, s_array_options) != 0);
}
bool AVIDump::SetVideoFormat()
{
- memset(&m_header, 0, sizeof(m_header));
- m_header.fccType = streamtypeVIDEO;
- m_header.dwScale = 1;
- m_header.dwRate = VideoInterface::TargetRefreshRate;
- m_header.dwSuggestedBufferSize = m_bitmap.biSizeImage;
+ memset(&s_header, 0, sizeof(s_header));
+ s_header.fccType = streamtypeVIDEO;
+ s_header.dwScale = 1;
+ s_header.dwRate = s_frame_rate;
+ s_header.dwSuggestedBufferSize = s_bitmap.biSizeImage;
- return SUCCEEDED(AVIFileCreateStream(m_file, &m_stream, &m_header));
+ return SUCCEEDED(AVIFileCreateStream(s_file, &s_stream, &s_header));
}
#else
+#include "Common/CommonPaths.h"
+#include "Common/FileUtil.h"
+#include "Common/StringUtil.h"
+#include "Common/Logging/Log.h"
+
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
@@ -229,6 +332,8 @@ static SwsContext* s_sws_context = nullptr;
static int s_width;
static int s_height;
static int s_size;
+static u64 s_last_frame;
+bool b_start_dumping = false;
static void InitAVCodec()
{
@@ -245,6 +350,8 @@ bool AVIDump::Start(int w, int h)
s_width = w;
s_height = h;
+ s_last_frame = CoreTiming::GetTicks();
+
InitAVCodec();
bool success = CreateFile();
if (!success)
@@ -397,3 +504,8 @@ void AVIDump::CloseFile()
}
#endif
+
+void AVIDump::DoState()
+{
+ s_last_frame = CoreTiming::GetTicks();
+}
diff --git a/Source/Core/VideoCommon/AVIDump.h b/Source/Core/VideoCommon/AVIDump.h
index df17ae409f..be6d4eed95 100644
--- a/Source/Core/VideoCommon/AVIDump.h
+++ b/Source/Core/VideoCommon/AVIDump.h
@@ -21,6 +21,9 @@ private:
static bool SetCompressionOptions();
static bool SetVideoFormat();
+ static void StoreFrame(const void* data);
+ static void* GetFrame();
+
public:
#ifdef _WIN32
static bool Start(HWND hWnd, int w, int h);
@@ -28,6 +31,6 @@ public:
static bool Start(int w, int h);
#endif
static void AddFrame(const u8* data, int width, int height);
-
static void Stop();
+ static void DoState();
};