obs/QSVHelper/QSVHelper.cpp

278 lines
8.8 KiB
C++

/********************************************************************************
Copyright (C) 2013 Ruwen Hahn <palana@stunned.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
********************************************************************************/
#include <stdio.h>
#include <windows.h>
#include <shellapi.h>
#include <fstream>
#include <sstream>
#include <utility>
#include <mfxvideo++.h>
#include "Encoder.h"
#include "IPCInfo.h"
#include "QSVStuff.h"
#include "SupportStuff.h"
#include "WindowsStuff.h"
namespace {
enum qsv_cpu_platform
{
QSV_CPU_PLATFORM_UNKNOWN,
QSV_CPU_PLATFORM_SNB_BNL = 1 << 0,
QSV_CPU_PLATFORM_IVB_SLM_CHT = 1 << 1,
QSV_CPU_PLATFORM_HSW = 1 << 2,
QSV_CPU_PLATFORM_INTEL = 1 << 3
};
qsv_cpu_platform qsv_get_cpu_platform()
{
using std::string;
int cpuInfo[4];
__cpuid(cpuInfo, 0);
string vendor;
vendor += string((char*)&cpuInfo[1], 4);
vendor += string((char*)&cpuInfo[3], 4);
vendor += string((char*)&cpuInfo[2], 4);
if (vendor != "GenuineIntel")
return QSV_CPU_PLATFORM_UNKNOWN;
__cpuid(cpuInfo, 1);
BYTE model = ((cpuInfo[0] >> 4) & 0xF) + ((cpuInfo[0] >> 12) & 0xF0);
BYTE family = ((cpuInfo[0] >> 8) & 0xF) + ((cpuInfo[0] >> 20) & 0xFF);
// See Intel 64 and IA-32 Architectures Software Developer's Manual, Vol 3C Table 35-1
if (family != 6)
return QSV_CPU_PLATFORM_UNKNOWN;
switch (model)
{
case 0x1C:
case 0x26:
case 0x27:
case 0x35:
case 0x36:
//BNL
case 0x2a:
case 0x2d:
//SNB
return QSV_CPU_PLATFORM_SNB_BNL;
case 0x3a:
case 0x3e:
//IVB
case 0x37:
case 0x4A:
case 0x4D:
case 0x5A:
case 0x5D:
//SLM
case 0x4C:
//CHT
return QSV_CPU_PLATFORM_IVB_SLM_CHT;
case 0x3c:
case 0x3f:
case 0x45:
case 0x46:
return QSV_CPU_PLATFORM_HSW;
}
return QSV_CPU_PLATFORM_INTEL; //assume newer revisions are at least as capable as haswell
}
const struct impl_parameters
{
mfxIMPL type,
intf;
mfxVersion version;
int platforms;
} valid_impl[] = {
{ MFX_IMPL_HARDWARE_ANY, MFX_IMPL_VIA_D3D11, {8, 1}, QSV_CPU_PLATFORM_INTEL | QSV_CPU_PLATFORM_IVB_SLM_CHT | QSV_CPU_PLATFORM_HSW },
{ MFX_IMPL_HARDWARE_ANY, MFX_IMPL_VIA_D3D9, {8, 1}, QSV_CPU_PLATFORM_INTEL | QSV_CPU_PLATFORM_IVB_SLM_CHT | QSV_CPU_PLATFORM_HSW }, //Ivy Bridge+ with non-functional D3D11 support?
{ MFX_IMPL_HARDWARE_ANY, MFX_IMPL_VIA_D3D11, {7, 1}, QSV_CPU_PLATFORM_INTEL | QSV_CPU_PLATFORM_IVB_SLM_CHT | QSV_CPU_PLATFORM_HSW },
{ MFX_IMPL_HARDWARE_ANY, MFX_IMPL_VIA_D3D9, {7, 1}, QSV_CPU_PLATFORM_INTEL | QSV_CPU_PLATFORM_IVB_SLM_CHT | QSV_CPU_PLATFORM_HSW }, //Ivy Bridge+ with non-functional D3D11 support?
{ MFX_IMPL_HARDWARE_ANY, MFX_IMPL_VIA_D3D11, {6, 1}, QSV_CPU_PLATFORM_INTEL | QSV_CPU_PLATFORM_IVB_SLM_CHT | QSV_CPU_PLATFORM_HSW },
{ MFX_IMPL_HARDWARE_ANY, MFX_IMPL_VIA_D3D9, {6, 1}, QSV_CPU_PLATFORM_INTEL | QSV_CPU_PLATFORM_IVB_SLM_CHT | QSV_CPU_PLATFORM_HSW }, //Ivy Bridge+ with non-functional D3D11 support?
{ MFX_IMPL_HARDWARE_ANY, MFX_IMPL_VIA_D3D11, {4, 1}, QSV_CPU_PLATFORM_INTEL | QSV_CPU_PLATFORM_IVB_SLM_CHT | QSV_CPU_PLATFORM_HSW | QSV_CPU_PLATFORM_SNB_BNL }, //Sandy Bridge + D3D11?
{ MFX_IMPL_HARDWARE_ANY, MFX_IMPL_VIA_D3D9, {4, 1}, QSV_CPU_PLATFORM_INTEL | QSV_CPU_PLATFORM_IVB_SLM_CHT | QSV_CPU_PLATFORM_HSW | QSV_CPU_PLATFORM_SNB_BNL }, //Sandy Bridge
};
std::wofstream log_file;
bool HasIntelGraphics()
{
REFIID iidVal = __uuidof(IDXGIFactory1);
ComPtr<IDXGIFactory1> factory;
if (!SUCCEEDED(CreateDXGIFactory1(iidVal, (void**)factory.Assign())))
return false;
UINT i = 0;
ComPtr<IDXGIAdapter1> adapter;
while (factory->EnumAdapters1(i++, adapter.Assign()) == S_OK)
{
DXGI_ADAPTER_DESC adapterDesc;
if (!SUCCEEDED(adapter->GetDesc(&adapterDesc)))
continue;
if (adapterDesc.VendorId == 0x8086)
return true;
}
return false;
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nShowCmd)
{
using namespace std;
#if defined _M_X64 && _MSC_VER == 1800
//workaround AVX2 bug in VS2013, http://connect.microsoft.com/VisualStudio/feedback/details/811093
_set_FMA3_enable(0);
#endif
wstringstream cmdss(GetCommandLineW());
wstring wstr;
cmdss >> wstr;
wstringstream wss;
wss << wstr << GetCurrentProcessId();
wstring event_prefix;
wss >> event_prefix;
std::string log_path = GetCommandLineA();
log_path.erase(log_path.begin(), find(log_path.begin(), log_path.end(), ' ')+1);
log_path += "/pluginData/QSVHelper.log";
log_file.open(log_path, ios::out | ios::trunc);
if(!log_file.is_open())
return EXIT_LOG_FILE_OPEN_FAILED;
ipc_init_request init_req(event_prefix + INIT_REQUEST);
if(!init_req.is_signalled(INFINITE))
return EXIT_INIT_IPC_FAILED;
if (!HasIntelGraphics())
return EXIT_NO_INTEL_GRAPHICS;
if(init_req->mode == init_req->MODE_QUERY)
{
MFXVideoSession session;
int platform = qsv_get_cpu_platform();
for(auto impl = begin(valid_impl); impl != std::end(valid_impl); impl++)
{
if (platform != QSV_CPU_PLATFORM_UNKNOWN && !(impl->platforms & platform))
continue;
auto ver = impl->version;
auto result = session.Init(impl->intf | impl->type, &ver);
if(result == MFX_ERR_NONE)
return 0;
}
return EXIT_INIT_QUERY_FAILED;
}
safe_handle obs_handle(OpenProcess(SYNCHRONIZE, false, init_req->obs_process_id));
if(!obs_handle)
return EXIT_IPC_OBS_HANDLE_FAILED;
ipc_init_response init_res(event_prefix + INIT_RESPONSE);
zero(*&init_res);
Encoder encoder(init_req, event_prefix, log_file);
init_res->using_custom_impl = false;
if(init_req->use_custom_impl)
{
impl_parameters p;
p.intf = init_req->custom_intf;
p.type = init_req->custom_impl;
p.version = init_req->custom_version;
auto result = encoder.InitializeMFX(p, true);
if(result >= MFX_ERR_NONE)
{
init_res->using_custom_impl = true;
init_res->actual_impl = p.intf | p.type;
init_res->version = p.version;
}
}
if(!init_res->using_custom_impl || !encoder)
{
init_res->using_custom_impl = false;
decltype(&valid_impl[0]) best = nullptr;
int platform = qsv_get_cpu_platform();
for(auto &impl : valid_impl)
{
if (platform != QSV_CPU_PLATFORM_UNKNOWN && !(impl.platforms & platform))
continue;
auto result = encoder.InitializeMFX(impl);
if(result == MFX_WRN_PARTIAL_ACCELERATION && !best)
best = &impl;
if(result == MFX_ERR_NONE)
break;
}
if(!encoder)
{
if(!best)
return EXIT_NO_VALID_CONFIGURATION;
auto ver = best->version;
encoder.InitializeMFX(*best);
log_file << "No valid implementation detected, using best implementation instead\n";
}
}
if(!encoder)
return EXIT_ENCODER_INIT_FAILED;
init_res->version = encoder.version;
init_res->requested_impl = encoder.requested;
init_res->actual_impl = encoder.actual;
if (encoder.InitializeEncoder() < MFX_ERR_NONE)
return EXIT_ENCODER_INIT_FAILED;
encoder.InitializeBuffers(init_res);
init_res.signal();
log_file << "Using " << encoder.encode_tasks.size() << " encode tasks and " << encoder.surfaces.size() << " internal frame buffers\n";
encoder.RequestSPSPPS();
ipc_stop stop(event_prefix + STOP_REQUEST);
return encoder.EncodeLoop(stop, obs_handle);
}