diff options
| author | Fog <christhecoolist@gmail.com> | 2014-10-04 03:28:01 -0400 |
|---|---|---|
| committer | Fog <christhecoolist@gmail.com> | 2014-10-09 00:06:04 -0400 |
| commit | fc4125cdd15c6119d75f6667cd44dcbd4df4a8b3 (patch) | |
| tree | a7ce8b2e4c918ea08db6d9841befc13b27c7250e /Source/Core/VideoCommon/AVIDump.cpp | |
| parent | 47bf698b70668f72f8495da209ffe68091d89bcf (diff) | |
Proper Audio/Video Dumping
Diffstat (limited to 'Source/Core/VideoCommon/AVIDump.cpp')
| -rw-r--r-- | Source/Core/VideoCommon/AVIDump.cpp | 238 |
1 files changed, 175 insertions, 63 deletions
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(); +} |
