diff options
| author | Pierre Bourdon <delroth@gmail.com> | 2015-01-26 02:35:29 +0100 |
|---|---|---|
| committer | Pierre Bourdon <delroth@gmail.com> | 2015-01-26 02:35:29 +0100 |
| commit | 1e809d9c11079be3ed8985dae42f36595767dcb5 (patch) | |
| tree | 485b5470882b50245dc444eb502d470dadc31c49 /Source/Core/VideoCommon/AVIDump.cpp | |
| parent | 34079a00379d8fb773e570c2d6fbf8750c0cd7b2 (diff) | |
Revert "Merge pull request #1903 from RisingFog/libav"
This reverts commit 34079a00379d8fb773e570c2d6fbf8750c0cd7b2, reversing
changes made to 3274df71588640b4648628e513cc59d6bdbdae26.
Diffstat (limited to 'Source/Core/VideoCommon/AVIDump.cpp')
| -rw-r--r-- | Source/Core/VideoCommon/AVIDump.cpp | 322 |
1 files changed, 299 insertions, 23 deletions
diff --git a/Source/Core/VideoCommon/AVIDump.cpp b/Source/Core/VideoCommon/AVIDump.cpp index e70189eb94..1dcbbe4d9c 100644 --- a/Source/Core/VideoCommon/AVIDump.cpp +++ b/Source/Core/VideoCommon/AVIDump.cpp @@ -8,19 +8,11 @@ #include <string> -extern "C" { -#include <libavcodec/avcodec.h> -#include <libavformat/avformat.h> -#include <libavutil/mathematics.h> -#include <libswscale/swscale.h> -} - #include "Common/CommonPaths.h" #include "Common/FileUtil.h" #include "Common/StringUtil.h" #include "Common/Logging/Log.h" -#include "Core/ConfigManager.h" // for PAL60 #include "Core/CoreTiming.h" #include "Core/HW/SystemTimers.h" #include "Core/HW/VideoInterface.h" //for TargetRefreshRate @@ -33,9 +25,299 @@ extern "C" { #include <cstdio> #include <cstring> +#include <vfw.h> #include <winerror.h> -#endif +#include "Core/ConfigManager.h" // for PAL60 +#include "Core/CoreTiming.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; +static bool s_start_dumping = false; + +bool AVIDump::Start(HWND hWnd, int w, int h) +{ + s_emu_wnd = hWnd; + s_file_count = 0; + + 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() +{ + s_total_bytes = 0; + s_frame_count = 0; + + 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); + + // Ask to delete file + if (File::Exists(movie_file_name)) + { + if (AskYesNoT("Delete the existing file '%s'?", movie_file_name.c_str())) + File::Delete(movie_file_name); + } + + 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(&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."); + if (hr == AVIERR_MEMORY) NOTICE_LOG(VIDEO, "The file could not be opened because of insufficient memory."); + if (hr == AVIERR_FILEREAD) NOTICE_LOG(VIDEO, "A disk error occurred while reading the file."); + if (hr == AVIERR_FILEOPEN) NOTICE_LOG(VIDEO, "A disk error occurred while opening the file."); + if (hr == REGDB_E_CLASSNOTREG) NOTICE_LOG(VIDEO, "AVI class not registered"); + Stop(); + return false; + } + + SetBitmapFormat(); + NOTICE_LOG(VIDEO, "Setting video format..."); + if (!SetVideoFormat()) + { + NOTICE_LOG(VIDEO, "Setting video format failed"); + Stop(); + return false; + } + + if (!s_file_count) + { + if (!SetCompressionOptions()) + { + NOTICE_LOG(VIDEO, "SetCompressionOptions failed"); + Stop(); + return false; + } + } + + if (FAILED(AVIMakeCompressedStream(&s_stream_compressed, s_stream, &s_options, nullptr))) + { + NOTICE_LOG(VIDEO, "AVIMakeCompressedStream failed"); + Stop(); + return false; + } + + if (FAILED(AVIStreamSetFormat(s_stream_compressed, 0, &s_bitmap, s_bitmap.biSize))) + { + NOTICE_LOG(VIDEO, "AVIStreamSetFormat failed"); + Stop(); + return false; + } + + return true; +} + +void AVIDump::CloseFile() +{ + if (s_stream_compressed) + { + AVIStreamClose(s_stream_compressed); + s_stream_compressed = nullptr; + } + + if (s_stream) + { + AVIStreamClose(s_stream); + s_stream = nullptr; + } + + if (s_file) + { + AVIFileRelease(s_file); + s_file = nullptr; + } + + AVIFileExit(); +} + +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); + s_start_dumping = false; + CloseFile(); + 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 != 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; + + s_bitmap.biWidth = w; + s_bitmap.biHeight = h; + } + // 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 (!s_start_dumping && s_last_frame <= SystemTimers::GetTicksPerSecond()) + { + delta = CoreTiming::GetTicks(); + s_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(&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(&s_options, 0, sizeof(s_options)); + s_array_options[0] = &s_options; + + return (AVISaveOptions(s_emu_wnd, 0, 1, &s_stream, s_array_options) != 0); +} + +bool AVIDump::SetVideoFormat() +{ + 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(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> +#include <libswscale/swscale.h> +#include <libavutil/mathematics.h> +} #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 28, 1) #define av_frame_alloc avcodec_alloc_frame @@ -54,7 +336,6 @@ static int s_size; static u64 s_last_frame; static bool s_start_dumping = false; static u64 s_last_pts; -static int s_frame_rate; static void InitAVCodec() { @@ -74,11 +355,6 @@ bool AVIDump::Start(int w, int h) s_last_frame = CoreTiming::GetTicks(); s_last_pts = 0; - 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 - InitAVCodec(); bool success = CreateFile(); if (!success) @@ -107,8 +383,7 @@ bool AVIDump::CreateFile() s_stream->codec->bit_rate = 400000; s_stream->codec->width = s_width; s_stream->codec->height = s_height; - s_stream->codec->time_base.num = 1; - s_stream->codec->time_base.den = s_frame_rate; + s_stream->codec->time_base = (AVRational){1, static_cast<int>(VideoInterface::TargetRefreshRate)}; s_stream->codec->gop_size = 12; s_stream->codec->pix_fmt = g_Config.bUseFFV1 ? AV_PIX_FMT_BGRA : AV_PIX_FMT_YUV420P; @@ -256,9 +531,10 @@ void AVIDump::CloseFile() s_sws_context = nullptr; } } -#if defined(HAVE_LIBAV) || defined (WIN32) - void AVIDump::DoState() - { - s_last_frame = CoreTiming::GetTicks(); - } -#endif
\ No newline at end of file + +#endif + +void AVIDump::DoState() +{ + s_last_frame = CoreTiming::GetTicks(); +} |
