Merge pull request #3451 from RisingFog/libav
Use ffmpeg for Windows Video Dumping instead of VFW
This commit is contained in:
commit
bf1c53a6e8
|
@ -191,7 +191,7 @@ static std::string DoState(PointerWrap& p)
|
|||
Movie::DoState(p);
|
||||
p.DoMarker("Movie");
|
||||
|
||||
#if defined(HAVE_LIBAV) || defined (WIN32)
|
||||
#if defined(HAVE_LIBAV) || defined (_WIN32)
|
||||
AVIDump::DoState();
|
||||
#endif
|
||||
|
||||
|
|
|
@ -40,12 +40,16 @@
|
|||
<BaseAddress>0x00400000</BaseAddress>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<FixedBaseAddress>true</FixedBaseAddress>
|
||||
<AdditionalLibraryDirectories>$(ExternalsDir)OpenAL\$(PlatformName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>iphlpapi.lib;winmm.lib;setupapi.lib;vfw32.lib;opengl32.lib;glu32.lib;rpcrt4.lib;comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(ExternalsDir)ffmpeg\lib;$(ExternalsDir)OpenAL\$(PlatformName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>iphlpapi.lib;winmm.lib;setupapi.lib;opengl32.lib;glu32.lib;rpcrt4.lib;comctl32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">/NODEFAULTLIB:libcmt %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">/NODEFAULTLIB:libcmt %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
<ResourceCompile>
|
||||
<AdditionalIncludeDirectories>$(ExternalsDir)wxWidgets3\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ResourceCompile>
|
||||
<ClCompile />
|
||||
<ClCompile />
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AboutDolphin.cpp" />
|
||||
|
|
|
@ -130,7 +130,7 @@ static wxString dump_textures_desc = wxTRANSLATE("Dump decoded game textures to
|
|||
static wxString load_hires_textures_desc = wxTRANSLATE("Load custom textures from User/Load/Textures/<game_id>/.\n\nIf unsure, leave this unchecked.");
|
||||
static wxString cache_hires_textures_desc = wxTRANSLATE("Cache custom textures to system RAM on startup.\nThis can require exponentially more RAM but fixes possible stuttering.\n\nIf unsure, leave this unchecked.");
|
||||
static wxString dump_efb_desc = wxTRANSLATE("Dump the contents of EFB copies to User/Dump/Textures/.\n\nIf unsure, leave this unchecked.");
|
||||
#if !defined WIN32 && defined HAVE_LIBAV
|
||||
#if defined(HAVE_LIBAV) || defined (_WIN32)
|
||||
static wxString use_ffv1_desc = wxTRANSLATE("Encode frame dumps using the FFV1 codec.\n\nIf unsure, leave this unchecked.");
|
||||
#endif
|
||||
static wxString free_look_desc = wxTRANSLATE("This feature allows you to change the game's camera.\nMove the mouse while holding the right mouse button to pan and while holding the middle button to move.\nHold SHIFT and press one of the WASD keys to move the camera by a certain step distance (SHIFT+2 to move faster and SHIFT+1 to move slower). Press SHIFT+R to reset the camera and SHIFT+F to reset the speed.\n\nIf unsure, leave this unchecked.");
|
||||
|
@ -559,7 +559,7 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
|
|||
szr_utility->Add(cache_hires_textures);
|
||||
szr_utility->Add(CreateCheckBox(page_advanced, _("Dump EFB Target"), wxGetTranslation(dump_efb_desc), vconfig.bDumpEFBTarget));
|
||||
szr_utility->Add(CreateCheckBox(page_advanced, _("Free Look"), wxGetTranslation(free_look_desc), vconfig.bFreeLook));
|
||||
#if !defined WIN32 && defined HAVE_LIBAV
|
||||
#if defined(HAVE_LIBAV) || defined (_WIN32)
|
||||
szr_utility->Add(CreateCheckBox(page_advanced, _("Frame Dumps use FFV1"), wxGetTranslation(use_ffv1_desc), vconfig.bUseFFV1));
|
||||
#endif
|
||||
|
||||
|
|
|
@ -840,7 +840,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
|
|||
{
|
||||
s_recordWidth = source_width;
|
||||
s_recordHeight = source_height;
|
||||
bAVIDumping = AVIDump::Start(D3D::hWnd, s_recordWidth, s_recordHeight);
|
||||
bAVIDumping = AVIDump::Start(s_recordWidth, s_recordHeight);
|
||||
if (!bAVIDumping)
|
||||
{
|
||||
PanicAlert("Error dumping frames to AVI.");
|
||||
|
@ -865,6 +865,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
|
|||
h = s_recordHeight;
|
||||
}
|
||||
formatBufferDump((u8*)map.pData, &frame_data[0], source_width, source_height, map.RowPitch);
|
||||
FlipImageData(&frame_data[0], w, h);
|
||||
AVIDump::AddFrame(&frame_data[0], source_width, source_height);
|
||||
D3D::context->Unmap(s_screenshot_texture, 0);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@
|
|||
#include "VideoBackends/OGL/TextureCache.h"
|
||||
#include "VideoBackends/OGL/VertexManager.h"
|
||||
|
||||
#if defined(HAVE_LIBAV) || defined (_WIN32)
|
||||
#include "VideoCommon/AVIDump.h"
|
||||
#endif
|
||||
#include "VideoCommon/BPFunctions.h"
|
||||
#include "VideoCommon/DriverDetails.h"
|
||||
#include "VideoCommon/Fifo.h"
|
||||
|
@ -42,10 +45,6 @@
|
|||
#include "VideoCommon/VertexShaderManager.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
#if defined _WIN32 || defined HAVE_LIBAV
|
||||
#include "VideoCommon/AVIDump.h"
|
||||
#endif
|
||||
|
||||
|
||||
void VideoConfig::UpdateProjectionHack()
|
||||
{
|
||||
|
@ -1340,7 +1339,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
|
|||
// Frame dumping disabled entirely on GLES3
|
||||
if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGL)
|
||||
{
|
||||
#if defined _WIN32 || defined HAVE_LIBAV
|
||||
#if defined(HAVE_LIBAV) || defined (_WIN32)
|
||||
if (SConfig::GetInstance().m_DumpFrames)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(s_criticalScreenshot);
|
||||
|
@ -1357,11 +1356,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
|
|||
{
|
||||
if (!bLastFrameDumped)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
bAVIDumping = AVIDump::Start(nullptr, w, h);
|
||||
#else
|
||||
bAVIDumping = AVIDump::Start(w, h);
|
||||
#endif
|
||||
if (!bAVIDumping)
|
||||
{
|
||||
OSD::AddMessage("AVIDump Start failed", 2000);
|
||||
|
@ -1375,10 +1370,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
|
|||
}
|
||||
if (bAVIDumping)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
FlipImageData(&frame_data[0], w, h);
|
||||
#endif
|
||||
|
||||
AVIDump::AddFrame(&frame_data[0], w, h);
|
||||
}
|
||||
|
||||
|
@ -1401,45 +1393,6 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co
|
|||
}
|
||||
bLastFrameDumped = false;
|
||||
}
|
||||
#else
|
||||
if (SConfig::GetInstance().m_DumpFrames)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(s_criticalScreenshot);
|
||||
std::string movie_file_name;
|
||||
w = GetTargetRectangle().GetWidth();
|
||||
h = GetTargetRectangle().GetHeight();
|
||||
frame_data.resize(3 * w * h);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glReadPixels(GetTargetRectangle().left, GetTargetRectangle().bottom, w, h, GL_BGR, GL_UNSIGNED_BYTE, &frame_data[0]);
|
||||
|
||||
if (!bLastFrameDumped)
|
||||
{
|
||||
movie_file_name = File::GetUserPath(D_DUMPFRAMES_IDX) + "framedump.raw";
|
||||
File::CreateFullPath(movie_file_name);
|
||||
pFrameDump.Open(movie_file_name, "wb");
|
||||
if (!pFrameDump)
|
||||
{
|
||||
OSD::AddMessage("Error opening framedump.raw for writing.", 2000);
|
||||
}
|
||||
else
|
||||
{
|
||||
OSD::AddMessage(StringFromFormat("Dumping Frames to \"%s\" (%dx%d RGB24)", movie_file_name.c_str(), w, h), 2000);
|
||||
}
|
||||
}
|
||||
if (pFrameDump)
|
||||
{
|
||||
FlipImageData(&frame_data[0], w, h);
|
||||
pFrameDump.WriteBytes(&frame_data[0], w * 3 * h);
|
||||
pFrameDump.Flush();
|
||||
}
|
||||
bLastFrameDumped = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bLastFrameDumped)
|
||||
pFrameDump.Close();
|
||||
bLastFrameDumped = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// Finish up the current frame, print some stats
|
||||
|
@ -1687,19 +1640,6 @@ void Renderer::SetInterlacingMode()
|
|||
// TODO
|
||||
}
|
||||
|
||||
void Renderer::FlipImageData(u8 *data, int w, int h, int pixel_width)
|
||||
{
|
||||
// Flip image upside down. Damn OpenGL.
|
||||
for (int y = 0; y < h / 2; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
for (int delta = 0; delta < pixel_width; ++delta)
|
||||
std::swap(data[(y * w + x) * pixel_width + delta], data[((h - 1 - y) * w + x) * pixel_width + delta]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace OGL
|
||||
|
|
|
@ -83,7 +83,6 @@ public:
|
|||
void SetViewport() override;
|
||||
|
||||
void RenderText(const std::string& text, int left, int top, u32 color) override;
|
||||
void FlipImageData(u8 *data, int w, int h, int pixel_width = 3);
|
||||
|
||||
u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override;
|
||||
void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override;
|
||||
|
|
|
@ -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
|
||||
|
@ -403,11 +102,14 @@ bool AVIDump::CreateFile()
|
|||
|
||||
s_stream->codec->codec_id = g_Config.bUseFFV1 ? AV_CODEC_ID_FFV1
|
||||
: s_format_context->oformat->video_codec;
|
||||
if (!g_Config.bUseFFV1)
|
||||
s_stream->codec->codec_tag = MKTAG('X', 'V', 'I', 'D'); // Force XVID FourCC for better compatibility
|
||||
s_stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
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 +258,6 @@ void AVIDump::CloseFile()
|
|||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void AVIDump::DoState()
|
||||
{
|
||||
s_last_frame = CoreTiming::GetTicks();
|
||||
|
|
|
@ -4,12 +4,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class AVIDump
|
||||
|
@ -17,19 +11,9 @@ class AVIDump
|
|||
private:
|
||||
static bool CreateFile();
|
||||
static void CloseFile();
|
||||
static void SetBitmapFormat();
|
||||
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);
|
||||
#else
|
||||
static bool Start(int w, int h);
|
||||
#endif
|
||||
static void AddFrame(const u8* data, int width, int height);
|
||||
static void Stop();
|
||||
static void DoState();
|
||||
|
|
|
@ -59,8 +59,6 @@ endif()
|
|||
|
||||
add_dolphin_library(videocommon "${SRCS}" "${LIBS}")
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
if(LIBAV_FOUND)
|
||||
target_link_libraries(videocommon ${LIBS} ${LIBAV_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
@ -102,13 +102,9 @@ Renderer::~Renderer()
|
|||
prev_efb_format = PEControl::INVALID_FMT;
|
||||
|
||||
efb_scale_numeratorX = efb_scale_numeratorY = efb_scale_denominatorX = efb_scale_denominatorY = 1;
|
||||
|
||||
#if defined _WIN32 || defined HAVE_LIBAV
|
||||
if (SConfig::GetInstance().m_DumpFrames && bLastFrameDumped && bAVIDumping)
|
||||
AVIDump::Stop();
|
||||
#else
|
||||
if (pFrameDump.IsOpen())
|
||||
pFrameDump.Close();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -617,3 +613,15 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
|
|||
XFBWrited = false;
|
||||
}
|
||||
|
||||
void Renderer::FlipImageData(u8* data, int w, int h, int pixel_width)
|
||||
{
|
||||
for (int y = 0; y < h / 2; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
for (int delta = 0; delta < pixel_width; ++delta)
|
||||
std::swap(data[(y * w + x) * pixel_width + delta], data[((h - 1 - y) * w + x) * pixel_width + delta]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -121,6 +121,8 @@ public:
|
|||
virtual u16 BBoxRead(int index) = 0;
|
||||
virtual void BBoxWrite(int index, u16 value) = 0;
|
||||
|
||||
static void FlipImageData(u8* data, int w, int h, int pixel_width = 3);
|
||||
|
||||
// Finish up the current frame, print some stats
|
||||
static void Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc,float Gamma = 1.0f);
|
||||
virtual void SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc, float Gamma = 1.0f) = 0;
|
||||
|
@ -148,11 +150,8 @@ protected:
|
|||
static std::mutex s_criticalScreenshot;
|
||||
static std::string s_sScreenshotName;
|
||||
|
||||
#if defined _WIN32 || defined HAVE_LIBAV
|
||||
bool bAVIDumping;
|
||||
#else
|
||||
File::IOFile pFrameDump;
|
||||
#endif
|
||||
|
||||
std::vector<u8> frame_data;
|
||||
bool bLastFrameDumped;
|
||||
|
||||
|
|
|
@ -34,6 +34,26 @@
|
|||
<Import Project="..\..\VSProps\PCHUse.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(ExternalsDir)ffmpeg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(ExternalsDir)ffmpeg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AsyncRequests.cpp" />
|
||||
<ClCompile Include="AVIDump.cpp" />
|
||||
|
|
|
@ -48,9 +48,11 @@
|
|||
The following libs are needed since we pull in pretty much the entire
|
||||
dolphin codebase.
|
||||
-->
|
||||
<AdditionalLibraryDirectories>$(ExternalsDir)OpenAL\$(PlatformName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>iphlpapi.lib;winmm.lib;setupapi.lib;vfw32.lib;opengl32.lib;glu32.lib;rpcrt4.lib;comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(ExternalsDir)OpenAL\$(PlatformName);$(ExternalsDir)ffmpeg\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>iphlpapi.lib;winmm.lib;setupapi.lib;opengl32.lib;glu32.lib;rpcrt4.lib;comctl32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">/NODEFAULTLIB:libcmt %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">/NODEFAULTLIB:libcmt %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
@ -96,16 +98,10 @@
|
|||
<ExternalDlls Include="$(ExternalsDir)OpenAL\$(PlatformName)\*.dll" />
|
||||
</ItemGroup>
|
||||
<!--Either method of running requires the runtime deps to be copied to pwd-->
|
||||
<Target Name="CopyDeps"
|
||||
AfterTargets="AfterBuild"
|
||||
Inputs="@(ExternalDlls)"
|
||||
Outputs="@(ExternalDlls -> '$(OutDir)%(RecursiveDir)%(Filename)%(Extension)')">
|
||||
<Copy SourceFiles="@(ExternalDlls)" DestinationFolder="$(OutDir)"
|
||||
Condition="!Exists('$(OutDir)%(RecursiveDir)%(Filename)%(ExternalDlls.Extension)') OR $([System.DateTime]::Parse('%(ModifiedTime)').Ticks) > $([System.IO.File]::GetLastWriteTime('$(OutDir)%(RecursiveDir)%(Filename)%(ExternalDlls.Extension)').Ticks)" />
|
||||
<Target Name="CopyDeps" AfterTargets="AfterBuild" Inputs="@(ExternalDlls)" Outputs="@(ExternalDlls -> '$(OutDir)%(RecursiveDir)%(Filename)%(Extension)')">
|
||||
<Copy SourceFiles="@(ExternalDlls)" DestinationFolder="$(OutDir)" Condition="!Exists('$(OutDir)%(RecursiveDir)%(Filename)%(ExternalDlls.Extension)') OR $([System.DateTime]::Parse('%(ModifiedTime)').Ticks) > $([System.IO.File]::GetLastWriteTime('$(OutDir)%(RecursiveDir)%(Filename)%(ExternalDlls.Extension)').Ticks)" />
|
||||
</Target>
|
||||
<Target Name="ExecUnitTests"
|
||||
AfterTargets="AfterBuild;CopyDeps"
|
||||
Condition="'$(RunUnitTests)'=='true'">
|
||||
<Target Name="ExecUnitTests" AfterTargets="AfterBuild;CopyDeps" Condition="'$(RunUnitTests)'=='true'">
|
||||
<!--This is only executed via msbuild, VS test runner automatically does this-->
|
||||
<Exec Command="$(TargetPath)" />
|
||||
</Target>
|
||||
|
|
Loading…
Reference in New Issue