diff options
| author | Chris Burgener <christhecoolist@gmail.com> | 2016-01-02 13:07:08 -0500 |
|---|---|---|
| committer | Chris Burgener <christhecoolist@gmail.com> | 2016-01-07 18:37:58 -0500 |
| commit | c34fb3edf033197e6469b0b7892389402be1c9a2 (patch) | |
| tree | f5528294c88628e6515c05ff5b4d9584bd72f0a1 /Source/Core/VideoCommon/AVIDump.cpp | |
| parent | 079be1db9a93c1e39bb37f0ca9fd91bf1990aa38 (diff) | |
Use ffmpeg for Windows Video Dumping instead of VFW
Diffstat (limited to 'Source/Core/VideoCommon/AVIDump.cpp')
| -rw-r--r-- | Source/Core/VideoCommon/AVIDump.cpp | 320 |
1 files changed, 9 insertions, 311 deletions
diff --git a/Source/Core/VideoCommon/AVIDump.cpp b/Source/Core/VideoCommon/AVIDump.cpp index 1d00cf230a..1fb291a463 100644 --- a/Source/Core/VideoCommon/AVIDump.cpp +++ b/Source/Core/VideoCommon/AVIDump.cpp @@ -8,6 +8,13 @@ #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" @@ -20,314 +27,6 @@ #include "VideoCommon/AVIDump.h" #include "VideoCommon/VideoConfig.h" -#ifdef _WIN32 - -#include "tchar.h" - -#include <cstdio> -#include <cstring> -#include <vfw.h> -#include <winerror.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 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(); - - // 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 (SConfig::GetInstance().m_DumpFramesSilent || - AskYesNoT("Delete the existing file '%s'?", movie_file_name.c_str())) - { - File::Delete(movie_file_name); - } - else - { - // Stop and cancel dumping the video - return false; - } - } - - 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); - PanicAlertT("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) - { - PanicAlertT("You have resized the window while dumping frames.\n" - "Nothing can be done to handle this properly.\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 * 1 / 10) // place if 1/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 * 9 / 10) // place if 9/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; - - if (SConfig::GetInstance().m_DumpFramesSilent) - { - s_options.fccType = streamtypeVIDEO; - s_options.fccHandler = mmioFOURCC('D', 'I', 'B', ' '); // Uncompressed - return true; - } - else - { - 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 = VideoInterface::TargetRefreshRate; - 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 #define av_frame_free avcodec_free_frame @@ -407,7 +106,8 @@ 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 = (AVRational){1, static_cast<int>(VideoInterface::TargetRefreshRate)}; + s_stream->codec->time_base.num = 1; + s_stream->codec->time_base.den = VideoInterface::TargetRefreshRate; s_stream->codec->gop_size = 12; s_stream->codec->pix_fmt = g_Config.bUseFFV1 ? AV_PIX_FMT_BGRA : AV_PIX_FMT_YUV420P; @@ -556,8 +256,6 @@ void AVIDump::CloseFile() } } -#endif - void AVIDump::DoState() { s_last_frame = CoreTiming::GetTicks(); |
