This commit is contained in:
mofeng-git
2025-12-28 18:19:16 +08:00
commit d143d158e4
771 changed files with 220548 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
#ifndef CALLBACK_H
#define CALLBACK_H
#include <stdint.h>
typedef void (*EncodeCallback)(const uint8_t *data, int32_t len, int32_t key,
const void *obj, int64_t pts);
typedef void (*DecodeCallback)(void *opaque, const void *obj);
#endif // CALLBACK_H

View File

@@ -0,0 +1,57 @@
#ifndef COMMON_H
#define COMMON_H
#include <stdint.h>
#define MAX_GOP 0x7FFFFFFF // i32 max
#define TEST_TIMEOUT_MS 1000
#define ENCODE_TIMEOUT_MS 1000
#define DECODE_TIMEOUT_MS 1000
enum AdapterVendor {
ADAPTER_VENDOR_AMD = 0x1002,
ADAPTER_VENDOR_INTEL = 0x8086,
ADAPTER_VENDOR_NVIDIA = 0x10DE,
ADAPTER_VENDOR_UNKNOWN = 0,
};
enum SurfaceFormat {
SURFACE_FORMAT_BGRA,
SURFACE_FORMAT_RGBA,
SURFACE_FORMAT_NV12,
};
enum DataFormat {
H264,
H265,
VP8,
VP9,
AV1,
MJPEG,
};
// same as Driver
enum Vendor {
VENDOR_NV = 0,
VENDOR_AMD = 1,
VENDOR_INTEL = 2,
VENDOR_FFMPEG = 3
};
enum Quality { Quality_Default, Quality_High, Quality_Medium, Quality_Low };
enum RateControl {
RC_DEFAULT,
RC_CBR,
RC_VBR,
RC_CQ,
};
enum HwcodecErrno {
HWCODEC_SUCCESS = 0,
HWCODEC_ERR_COMMON = -1,
HWCODEC_ERR_HEVC_COULD_NOT_FIND_POC = -2,
};
#endif // COMMON_H

View File

@@ -0,0 +1,29 @@
#ifndef FFMPEG_H
#define FFMPEG_H
#define AV_LOG_QUIET -8
#define AV_LOG_PANIC 0
#define AV_LOG_FATAL 8
#define AV_LOG_ERROR 16
#define AV_LOG_WARNING 24
#define AV_LOG_INFO 32
#define AV_LOG_VERBOSE 40
#define AV_LOG_DEBUG 48
#define AV_LOG_TRACE 56
enum AVPixelFormat {
AV_PIX_FMT_YUV420P = 0,
AV_PIX_FMT_YUYV422 = 1,
AV_PIX_FMT_YUV422P = 4, // planar YUV 4:2:2
AV_PIX_FMT_YUVJ420P = 12, // JPEG full-range YUV420P (same layout as YUV420P)
AV_PIX_FMT_YUVJ422P = 13, // JPEG full-range YUV422P (same layout as YUV422P)
AV_PIX_FMT_NV12 = 23,
AV_PIX_FMT_NV21 = 24,
};
int av_log_get_level(void);
void av_log_set_level(int level);
void hwcodec_set_av_log_callback();
void hwcodec_set_flag_could_not_find_ref_with_poc();
#endif

View File

@@ -0,0 +1,54 @@
#include "log.h"
extern "C" {
#include <libavutil/log.h>
}
namespace gol {
enum {
LOG_LEVEL_ERROR = 0,
LOG_LEVEL_WARN = 1,
LOG_LEVEL_INFO = 2,
LOG_LEVEL_DEBUG = 3,
LOG_LEVEL_TRACE = 4,
};
extern "C" void hwcodec_log(int level, const char *message);
extern "C" void hwcodec_av_log_callback(int level, const char *message);
void log_to_rust(int level, const std::string &message) {
const char *cstr = message.c_str();
hwcodec_log(level, cstr);
}
void error(const std::string &message) {
log_to_rust(LOG_LEVEL_ERROR, message);
}
void warn(const std::string &message) { log_to_rust(LOG_LEVEL_WARN, message); }
void info(const std::string &message) { log_to_rust(LOG_LEVEL_INFO, message); }
void debug(const std::string &message) {
log_to_rust(LOG_LEVEL_DEBUG, message);
}
void trace(const std::string &message) {
log_to_rust(LOG_LEVEL_TRACE, message);
}
void av_log_callback(void *ptr, int level, const char *fmt, va_list vl) {
(void)ptr;
if (level > av_log_get_level()) {
return;
}
char line[1024] = {0};
vsnprintf(line, sizeof(line), fmt, vl);
hwcodec_av_log_callback(level, line);
};
} // namespace gol
extern "C" void hwcodec_set_av_log_callback() {
av_log_set_callback(gol::av_log_callback);
}

View File

@@ -0,0 +1,66 @@
#ifndef LOG_H
#define LOG_H
extern "C" {
#include <libavutil/attributes.h>
#include <libavutil/error.h>
}
#include <sstream>
#include <string>
#ifndef LOG_MODULE
#define LOG_MODULE "*"
#endif
namespace gol {
void error(const std::string &message);
void warn(const std::string &message);
void info(const std::string &message);
void debug(const std::string &message);
void trace(const std::string &message);
} // namespace gol
#define LOG_ERROR(message) \
gol::error(std::string("[") + LOG_MODULE + "] " + message)
#define LOG_WARN(message) \
gol::warn(std::string("[") + LOG_MODULE + "] " + message)
#define LOG_INFO(message) \
gol::info(std::string("[") + LOG_MODULE + "] " + message)
#define LOG_DEBUG(message) \
gol::debug(std::string("[") + LOG_MODULE + "] " + message)
#define LOG_TRACE(message) \
gol::trace(std::string("[") + LOG_MODULE + "] " + message)
// https://github.com/joncampbell123/composite-video-simulator/issues/5#issuecomment-611885908
#ifdef av_err2str
#undef av_err2str
av_always_inline std::string av_err2string(int errnum) {
char str[AV_ERROR_MAX_STRING_SIZE];
return av_make_error_string(str, AV_ERROR_MAX_STRING_SIZE, errnum);
}
#define av_err2str(err) av_err2string(err).c_str()
#endif // av_err2str
#ifdef _WIN32
#define HRB(f) MS_CHECK(f, return false;)
#define HRI(f) MS_CHECK(f, return -1;)
#define HRP(f) MS_CHECK(f, return nullptr;)
#define MS_CHECK(f, ...) \
do { \
HRESULT __ms_hr__ = (f); \
if (FAILED(__ms_hr__)) { \
std::stringstream ss; \
ss << "ERROR@" << __FILE__ << ":" << __LINE__ << " " << __FUNCTION__ \
<< " hr=0x" << std::hex << __ms_hr__ << std::dec << " " \
<< std::error_code(__ms_hr__, std::system_category()).message(); \
std::string result = ss.str(); \
LOG_ERROR(result); \
__VA_ARGS__ \
} \
} while (false)
#endif
#endif

View File

@@ -0,0 +1,163 @@
#include "linux.h"
#include "../../log.h"
#include <cstring>
#include <dlfcn.h>
#include <dynlink_cuda.h>
#include <dynlink_loader.h>
#include <errno.h>
#include <exception> // Include the necessary header file
#include <signal.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <fcntl.h>
namespace
{
void load_driver(CudaFunctions **pp_cuda_dl, NvencFunctions **pp_nvenc_dl,
CuvidFunctions **pp_cvdl)
{
if (cuda_load_functions(pp_cuda_dl, NULL) < 0)
{
LOG_TRACE(std::string("cuda_load_functions failed"));
throw "cuda_load_functions failed";
}
if (nvenc_load_functions(pp_nvenc_dl, NULL) < 0)
{
LOG_TRACE(std::string("nvenc_load_functions failed"));
throw "nvenc_load_functions failed";
}
if (cuvid_load_functions(pp_cvdl, NULL) < 0)
{
LOG_TRACE(std::string("cuvid_load_functions failed"));
throw "cuvid_load_functions failed";
}
}
void free_driver(CudaFunctions **pp_cuda_dl, NvencFunctions **pp_nvenc_dl,
CuvidFunctions **pp_cvdl)
{
if (*pp_cvdl)
{
cuvid_free_functions(pp_cvdl);
*pp_cvdl = NULL;
}
if (*pp_nvenc_dl)
{
nvenc_free_functions(pp_nvenc_dl);
*pp_nvenc_dl = NULL;
}
if (*pp_cuda_dl)
{
cuda_free_functions(pp_cuda_dl);
*pp_cuda_dl = NULL;
}
}
} // namespace
int linux_support_nv()
{
try
{
CudaFunctions *cuda_dl = NULL;
NvencFunctions *nvenc_dl = NULL;
CuvidFunctions *cvdl = NULL;
load_driver(&cuda_dl, &nvenc_dl, &cvdl);
free_driver(&cuda_dl, &nvenc_dl, &cvdl);
return 0;
}
catch (...)
{
LOG_TRACE(std::string("nvidia driver not support"));
}
return -1;
}
int linux_support_amd()
{
#if defined(__x86_64__) || defined(__aarch64__)
#define AMF_DLL_NAME L"libamfrt64.so.1"
#define AMF_DLL_NAMEA "libamfrt64.so.1"
#else
#define AMF_DLL_NAME L"libamfrt32.so.1"
#define AMF_DLL_NAMEA "libamfrt32.so.1"
#endif
void *handle = dlopen(AMF_DLL_NAMEA, RTLD_LAZY);
if (!handle)
{
return -1;
}
dlclose(handle);
return 0;
}
int linux_support_intel()
{
const char *libs[] =
{"libvpl.so", "libmfx.so", "libmfx-gen.so.1.2", "libmfxhw64.so.1"};
for (size_t i = 0; i < sizeof(libs) / sizeof(libs[0]); i++)
{
void *handle = dlopen(libs[i], RTLD_LAZY);
if (handle)
{
dlclose(handle);
return 0;
}
}
return -1;
}
int setup_parent_death_signal() {
// Set up parent death signal to ensure this process dies if parent dies
// This prevents orphaned processes especially when running with different
// user permissions
int ret = prctl(PR_SET_PDEATHSIG, SIGKILL);
if (ret != 0) {
LOG_ERROR(std::string("Failed to set parent death signal:") + std::to_string(errno));
return -1;
} else {
return 0;
}
}
// Check for Rockchip MPP (Media Process Platform) support
// Returns 0 if supported, -1 otherwise
int linux_support_rkmpp() {
// Check for MPP service device (primary method)
if (access("/dev/mpp_service", F_OK) == 0) {
LOG_TRACE(std::string("RKMPP: Found /dev/mpp_service"));
return 0;
}
// Fallback: check for RGA (Rockchip Graphics Acceleration) device
if (access("/dev/rga", F_OK) == 0) {
LOG_TRACE(std::string("RKMPP: Found /dev/rga"));
return 0;
}
LOG_TRACE(std::string("RKMPP: No Rockchip MPP device found"));
return -1;
}
// Check for V4L2 Memory-to-Memory (M2M) codec support
// Returns 0 if a M2M capable device is found, -1 otherwise
int linux_support_v4l2m2m() {
// Check common V4L2 M2M device paths used by various ARM SoCs
const char *m2m_devices[] = {
"/dev/video10", // Common M2M encoder device
"/dev/video11", // Common M2M decoder device
"/dev/video0", // Some SoCs use video0 for M2M
};
for (size_t i = 0; i < sizeof(m2m_devices) / sizeof(m2m_devices[0]); i++) {
if (access(m2m_devices[i], F_OK) == 0) {
// Device exists, check if it's an M2M device by trying to open it
int fd = open(m2m_devices[i], O_RDWR | O_NONBLOCK);
if (fd >= 0) {
close(fd);
LOG_TRACE(std::string("V4L2 M2M: Found device ") + m2m_devices[i]);
return 0;
}
}
}
LOG_TRACE(std::string("V4L2 M2M: No M2M device found"));
return -1;
}

View File

@@ -0,0 +1,11 @@
#ifndef LINUX_H
#define LINUX_H
extern "C" int linux_support_nv();
extern "C" int linux_support_amd();
extern "C" int linux_support_intel();
extern "C" int linux_support_rkmpp();
extern "C" int linux_support_v4l2m2m();
extern "C" int setup_parent_death_signal();
#endif

View File

@@ -0,0 +1,167 @@
#include <AVFoundation/AVFoundation.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreMedia/CoreMedia.h>
#include <MacTypes.h>
#include <VideoToolbox/VideoToolbox.h>
#include <cstdlib>
#include <pthread.h>
#include <ratio>
#include <sys/_types/_int32_t.h>
#include <sys/event.h>
#include <unistd.h>
#include "../../log.h"
#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif
// ---------------------- Core: More Robust Hardware Encoder Detection ----------------------
static int32_t hasHardwareEncoder(bool h265) {
CMVideoCodecType codecType = h265 ? kCMVideoCodecType_HEVC : kCMVideoCodecType_H264;
// ---------- Path A: Quick Query with Enable + Require ----------
// Note: Require implies Enable, but setting both here makes it easier to bypass the strategy on some models that default to a software encoder.
CFMutableDictionaryRef spec = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(spec, kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
CFDictionarySetValue(spec, kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
CFDictionaryRef properties = NULL;
CFStringRef outID = NULL;
// Use 1280x720 for capability detection to reduce the probability of "no hardware encoding" due to resolution/level issues.
OSStatus result = VTCopySupportedPropertyDictionaryForEncoder(1280, 720, codecType, spec, &outID, &properties);
if (properties) CFRelease(properties);
if (outID) CFRelease(outID);
if (spec) CFRelease(spec);
if (result == noErr) {
// Explicitly found an encoder that meets the "hardware-only" specification.
return 1;
}
// Reaching here means either no encoder satisfying Require was found (common), or another error occurred.
// For all failure cases, continue with the safer "session-level confirmation" path to avoid misjudgment.
// ---------- Path B: Create Session and Read UsingHardwareAcceleratedVideoEncoder ----------
CFMutableDictionaryRef enableOnly = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(enableOnly, kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
VTCompressionSessionRef session = NULL;
// Also use 1280x720 to reduce profile/level interference
OSStatus st = VTCompressionSessionCreate(kCFAllocatorDefault,
1280, 720, codecType,
enableOnly, /* encoderSpecification */
NULL, /* sourceImageBufferAttributes */
NULL, /* compressedDataAllocator */
NULL, /* outputCallback */
NULL, /* outputRefCon */
&session);
if (enableOnly) CFRelease(enableOnly);
if (st != noErr || !session) {
// Creation failed, considered no hardware available.
return 0;
}
// First, explicitly prepare the encoding process to give VideoToolbox a chance to choose between software/hardware.
OSStatus prepareStatus = VTCompressionSessionPrepareToEncodeFrames(session);
if (prepareStatus != noErr) {
VTCompressionSessionInvalidate(session);
CFRelease(session);
return 0;
}
// Query the session's read-only property: whether it is using a hardware encoder.
CFBooleanRef usingHW = NULL;
st = VTSessionCopyProperty(session,
kVTCompressionPropertyKey_UsingHardwareAcceleratedVideoEncoder,
kCFAllocatorDefault,
(void **)&usingHW);
Boolean isHW = (st == noErr && usingHW && CFBooleanGetValue(usingHW));
if (usingHW) CFRelease(usingHW);
VTCompressionSessionInvalidate(session);
CFRelease(session);
return isHW ? 1 : 0;
}
// -------------- Your Public Interface: Unchanged ------------------
extern "C" void checkVideoToolboxSupport(int32_t *h264Encoder, int32_t *h265Encoder, int32_t *h264Decoder, int32_t *h265Decoder) {
// https://stackoverflow.com/questions/50956097/determine-if-ios-device-can-support-hevc-encoding
*h264Encoder = 0; // H.264 encoder support is disabled due to frequent reliability issues (see encode.rs)
*h265Encoder = hasHardwareEncoder(true);
*h264Decoder = VTIsHardwareDecodeSupported(kCMVideoCodecType_H264);
*h265Decoder = VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC);
return;
}
extern "C" uint64_t GetHwcodecGpuSignature() {
int32_t h264Encoder = 0;
int32_t h265Encoder = 0;
int32_t h264Decoder = 0;
int32_t h265Decoder = 0;
checkVideoToolboxSupport(&h264Encoder, &h265Encoder, &h264Decoder, &h265Decoder);
return (uint64_t)h264Encoder << 24 | (uint64_t)h265Encoder << 16 | (uint64_t)h264Decoder << 8 | (uint64_t)h265Decoder;
}
static void *parent_death_monitor_thread(void *arg) {
int kq = (intptr_t)arg;
struct kevent events[1];
int ret = kevent(kq, NULL, 0, events, 1, NULL);
if (ret > 0) {
// Parent process died, terminate this process
LOG_INFO("Parent process died, terminating hwcodec check process");
exit(1);
}
return NULL;
}
extern "C" int setup_parent_death_signal() {
// On macOS, use kqueue to monitor parent process death
pid_t parent_pid = getppid();
int kq = kqueue();
if (kq == -1) {
LOG_DEBUG("Failed to create kqueue for parent monitoring");
return -1;
}
struct kevent event;
EV_SET(&event, parent_pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0,
NULL);
int ret = kevent(kq, &event, 1, NULL, 0, NULL);
if (ret == -1) {
LOG_ERROR("Failed to register parent death monitoring on macOS\n");
close(kq);
return -1;
} else {
// Spawn a thread to monitor parent death
pthread_t monitor_thread;
ret = pthread_create(&monitor_thread, NULL, parent_death_monitor_thread,
(void *)(intptr_t)kq);
if (ret != 0) {
LOG_ERROR("Failed to create parent death monitor thread");
close(kq);
return -1;
}
// Detach the thread so it can run independently
pthread_detach(monitor_thread);
return 0;
}
}

View File

@@ -0,0 +1,187 @@
#include <atomic>
#include <chrono>
#include <cstdio>
#include <list>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include <d3d11.h>
#include <dxgi.h>
#include <wrl/client.h>
#include "../../common.h"
#include "win.h"
#define IF_FAILED_THROW(X) \
if (FAILED(hr = (X))) { \
throw hr; \
}
using Microsoft::WRL::ComPtr;
static HRESULT CreateBmpFile(LPCWSTR wszBmpFile, BYTE *pData,
const UINT uiFrameSize, const UINT uiWidth,
const UINT uiHeight) {
HRESULT hr = S_OK;
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD dwWritten;
UINT uiStride;
BYTE header24[54] = {0x42, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
DWORD dwSizeFile = uiWidth * uiHeight * 3;
dwSizeFile += 54;
header24[2] = dwSizeFile & 0x000000ff;
header24[3] = static_cast<BYTE>((dwSizeFile & 0x0000ff00) >> 8);
header24[4] = static_cast<BYTE>((dwSizeFile & 0x00ff0000) >> 16);
header24[5] = (dwSizeFile & 0xff000000) >> 24;
dwSizeFile -= 54;
header24[18] = uiWidth & 0x000000ff;
header24[19] = (uiWidth & 0x0000ff00) >> 8;
header24[20] = static_cast<BYTE>((uiWidth & 0x00ff0000) >> 16);
header24[21] = (uiWidth & 0xff000000) >> 24;
header24[22] = uiHeight & 0x000000ff;
header24[23] = (uiHeight & 0x0000ff00) >> 8;
header24[24] = static_cast<BYTE>((uiHeight & 0x00ff0000) >> 16);
header24[25] = (uiHeight & 0xff000000) >> 24;
header24[34] = dwSizeFile & 0x000000ff;
header24[35] = (dwSizeFile & 0x0000ff00) >> 8;
header24[36] = static_cast<BYTE>((dwSizeFile & 0x00ff0000) >> 16);
header24[37] = static_cast<BYTE>((dwSizeFile & 0xff000000) >> 24);
try {
hFile = CreateFileW(wszBmpFile, GENERIC_WRITE, FILE_SHARE_READ, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
IF_FAILED_THROW(hFile == INVALID_HANDLE_VALUE ? E_FAIL : S_OK);
IF_FAILED_THROW(WriteFile(hFile, (LPCVOID)header24, 54, &dwWritten, 0) ==
FALSE);
IF_FAILED_THROW(dwWritten == 0 ? E_FAIL : S_OK);
uiStride = uiWidth * 3;
BYTE *Tmpbufsrc = pData + (uiFrameSize - uiStride);
for (UINT i = 0; i < uiHeight; i++) {
IF_FAILED_THROW(WriteFile(hFile, (LPCVOID)Tmpbufsrc, uiStride, &dwWritten,
0) == FALSE);
IF_FAILED_THROW(dwWritten == 0 ? E_FAIL : S_OK);
Tmpbufsrc -= uiStride;
}
} catch (HRESULT) {
}
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
return hr;
}
static std::string GetDirectoryFromFilename(const std::string &filename) {
size_t lastSeparator = filename.find_last_of("/\\");
if (lastSeparator != std::string::npos) {
return filename.substr(0, lastSeparator);
}
return "";
}
static bool createBgraBmpFile(ID3D11Device *device, ID3D11Texture2D *texture,
const std::string &filename) {
D3D11_TEXTURE2D_DESC desc = {};
ComPtr<ID3D11DeviceContext> deviceContext;
HRESULT hr;
texture->GetDesc(&desc);
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.BindFlags = 0;
ComPtr<ID3D11Texture2D> bgraStagingTexture;
hr = device->CreateTexture2D(&desc, nullptr,
bgraStagingTexture.GetAddressOf());
IF_FAILED_THROW(hr);
device->GetImmediateContext(deviceContext.ReleaseAndGetAddressOf());
deviceContext->CopyResource(bgraStagingTexture.Get(), texture);
D3D11_MAPPED_SUBRESOURCE ResourceDesc = {};
deviceContext->Map(bgraStagingTexture.Get(), 0, D3D11_MAP_READ, 0,
&ResourceDesc);
UINT uiImageSize = desc.Width * desc.Height * 3;
BYTE *pDataRgb = new (std::nothrow) BYTE[uiImageSize];
BYTE *pDataRgbaColor = (BYTE *)ResourceDesc.pData;
BYTE *pDataRgbColor = pDataRgb;
for (UINT i = 0; i < desc.Height; i++) {
for (UINT j = 0; j < desc.Width; j++) {
if (desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM) {
// bgr bgra
*pDataRgbColor++ = *pDataRgbaColor++;
*pDataRgbColor++ = *pDataRgbaColor++;
*pDataRgbColor++ = *pDataRgbaColor++;
pDataRgbaColor++;
} else {
// bgr rgba
pDataRgbColor[0] = pDataRgbaColor[2];
pDataRgbColor[1] = pDataRgbaColor[1];
pDataRgbColor[2] = pDataRgbaColor[0];
pDataRgbColor += 3;
pDataRgbaColor += 4;
}
}
}
auto dir = GetDirectoryFromFilename(filename);
DWORD attrib = GetFileAttributesA(dir.c_str());
if (attrib == INVALID_FILE_ATTRIBUTES ||
!(attrib & FILE_ATTRIBUTE_DIRECTORY)) {
if (!CreateDirectoryA(dir.c_str(), NULL)) {
std::cout << "Failed to create directory: " << dir << std::endl;
return false;
} else {
std::cout << "Directory created: " << dir << std::endl;
}
} else {
// already exists
}
int size = MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, nullptr, 0);
wchar_t *wszBmpFile = new wchar_t[size];
MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, wszBmpFile, size);
hr =
CreateBmpFile(wszBmpFile, pDataRgb, uiImageSize, desc.Width, desc.Height);
delete[] pDataRgb;
delete[] wszBmpFile;
IF_FAILED_THROW(hr);
deviceContext->Unmap(bgraStagingTexture.Get(), 0);
}
void SaveBgraBmps(ID3D11Device *device, void *texture, int cycle) {
if (!texture)
return;
static int index = 0;
if (index++ % cycle == 0) {
auto now = std::chrono::system_clock::now();
auto time_t_now = std::chrono::system_clock::to_time_t(now);
std::tm local_tm;
localtime_s(&local_tm, &time_t_now);
char buffer[80];
std::strftime(buffer, 80, "%H_%M_%S", &local_tm);
std::string filename = std::string("bmps") + "/" + std::to_string(index) +
"_" + buffer + ".bmp";
createBgraBmpFile(device, (ID3D11Texture2D *)texture, filename);
}
}

View File

@@ -0,0 +1,58 @@
#include "win.h"
#include <fstream>
bool dumpTexture(ID3D11Device *device, ID3D11Texture2D *texture, int cropW,
int cropH, const string &filename) {
const char *dir = "texture";
DWORD attrib = GetFileAttributesA(dir);
if (attrib == INVALID_FILE_ATTRIBUTES ||
!(attrib & FILE_ATTRIBUTE_DIRECTORY)) {
if (!CreateDirectoryA(dir, NULL)) {
std::cout << "Failed to create directory: " << dir << std::endl;
return false;
} else {
std::cout << "Directory created: " << dir << std::endl;
}
} else {
// already exists
}
D3D11_TEXTURE2D_DESC desc = {};
ComPtr<ID3D11DeviceContext> deviceContext;
HRESULT hr;
texture->GetDesc(&desc);
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.BindFlags = 0;
ComPtr<ID3D11Texture2D> stagingTexture;
hr = device->CreateTexture2D(&desc, nullptr, stagingTexture.GetAddressOf());
if (FAILED(hr)) {
return false;
}
device->GetImmediateContext(deviceContext.ReleaseAndGetAddressOf());
deviceContext->CopyResource(stagingTexture.Get(), texture);
D3D11_MAPPED_SUBRESOURCE mappedResource = {};
deviceContext->Map(stagingTexture.Get(), 0, D3D11_MAP_READ, 0,
&mappedResource);
string path = string(dir) + "/" + filename;
std::ofstream file(path, std::ios::binary | std::ios::app);
if (desc.Format == DXGI_FORMAT_NV12) {
int Pitch = mappedResource.RowPitch;
uint8_t *Y = (uint8_t *)mappedResource.pData;
uint8_t *U =
(uint8_t *)mappedResource.pData + desc.Height * mappedResource.RowPitch;
uint8_t *V = (desc.Format == DXGI_FORMAT_P010) ? U + 2 : U + 1;
for (int i = 0; i < cropH; i++) {
file.write((const char *)(Y + i * Pitch), cropW);
}
int ChromaH = cropH / 2;
int ChromaW = cropW;
for (int i = 0; i < ChromaH; i++) {
file.write((const char *)(U + i * Pitch), ChromaW);
}
}
deviceContext->Unmap(stagingTexture.Get(), 0);
file.close();
return true;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,919 @@
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 10.1
//
//
//
// Input signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// POSITION 0 xyzw 0 NONE float xyzw
// TEXCOORD 0 xy 1 NONE float xy
//
//
// Output signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_POSITION 0 xyzw 0 POS float xyzw
// TEXCOORD 0 xy 1 NONE float xy
//
//
// Runtime generated constant mappings:
//
// Target Reg Constant Description
// ---------- --------------------------------------------------
// c0 Vertex Shader position offset
//
//
// Level9 shader bytecode:
//
vs_2_x
dcl_texcoord v0 // input<0,1,2,3>
dcl_texcoord1 v1 // input<4,5>
#line 12 "D:\rustdesk\gpu_video_codec\vs\ShaderCompileTool\nv_vertex_shader.hlsl"
mad oPos.xy, v0.w, c0, v0 // ::VS<0,1>
mov oPos.zw, v0 // ::VS<2,3>
mov oT0.xy, v1 // ::VS<4,5>
// approximately 3 instruction slots used
vs_4_0
dcl_input v0.xyzw
dcl_input v1.xy
dcl_output_siv o0.xyzw, position
dcl_output o1.xy
//
// Initial variable locations:
// v0.x <- input.Pos.x; v0.y <- input.Pos.y; v0.z <- input.Pos.z; v0.w <- input.Pos.w;
// v1.x <- input.Tex.x; v1.y <- input.Tex.y;
// o1.x <- <VS return value>.Tex.x; o1.y <- <VS return value>.Tex.y;
// o0.x <- <VS return value>.Pos.x; o0.y <- <VS return value>.Pos.y; o0.z <- <VS return value>.Pos.z; o0.w <- <VS return value>.Pos.w
//
#line 14 "D:\rustdesk\gpu_video_codec\vs\ShaderCompileTool\nv_vertex_shader.hlsl"
mov o0.xyzw, v0.xyzw
mov o1.xy, v1.xyxx
ret
// Approximately 3 instruction slots used
#endif
const BYTE g_VS[] = {
68, 88, 66, 67, 17, 156, 6, 174, 73, 86, 39, 50, 168, 176, 148,
24, 52, 224, 94, 107, 1, 0, 0, 0, 56, 50, 0, 0, 7, 0,
0, 0, 60, 0, 0, 0, 76, 2, 0, 0, 188, 2, 0, 0, 196,
48, 0, 0, 64, 49, 0, 0, 140, 49, 0, 0, 224, 49, 0, 0,
65, 111, 110, 57, 8, 2, 0, 0, 8, 2, 0, 0, 0, 2, 254,
255, 224, 1, 0, 0, 40, 0, 0, 0, 0, 0, 36, 0, 0, 0,
36, 0, 0, 0, 36, 0, 0, 0, 36, 0, 1, 0, 36, 0, 0,
0, 0, 0, 1, 2, 254, 255, 254, 255, 100, 0, 68, 66, 85, 71,
40, 0, 0, 0, 100, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 112, 0, 0, 0, 5, 0, 0, 0, 116, 0, 0, 0, 2, 0,
0, 0, 60, 1, 0, 0, 156, 0, 0, 0, 68, 58, 92, 114, 117,
115, 116, 100, 101, 115, 107, 92, 103, 112, 117, 95, 118, 105, 100, 101,
111, 95, 99, 111, 100, 101, 99, 92, 118, 115, 92, 83, 104, 97, 100,
101, 114, 67, 111, 109, 112, 105, 108, 101, 84, 111, 111, 108, 92, 110,
118, 95, 118, 101, 114, 116, 101, 120, 95, 115, 104, 97, 100, 101, 114,
46, 104, 108, 115, 108, 0, 171, 40, 0, 0, 0, 0, 0, 255, 255,
152, 1, 0, 0, 0, 0, 255, 255, 164, 1, 0, 0, 12, 0, 0,
0, 176, 1, 0, 0, 12, 0, 0, 0, 196, 1, 0, 0, 14, 0,
0, 0, 208, 1, 0, 0, 86, 83, 0, 80, 111, 115, 0, 171, 1,
0, 3, 0, 1, 0, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0,
84, 101, 120, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 0,
0, 0, 0, 0, 0, 159, 0, 0, 0, 164, 0, 0, 0, 180, 0,
0, 0, 184, 0, 0, 0, 5, 0, 0, 0, 1, 0, 6, 0, 1,
0, 2, 0, 200, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0,
255, 255, 255, 255, 3, 0, 0, 0, 255, 255, 255, 255, 2, 0, 3,
0, 4, 0, 0, 0, 4, 0, 5, 0, 255, 255, 255, 255, 105, 110,
112, 117, 116, 0, 171, 171, 5, 0, 0, 0, 1, 0, 6, 0, 1,
0, 2, 0, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
2, 0, 3, 0, 1, 0, 0, 0, 4, 0, 5, 0, 255, 255, 255,
255, 0, 0, 0, 0, 156, 0, 0, 0, 216, 0, 0, 0, 3, 0,
0, 0, 232, 0, 0, 0, 156, 0, 0, 0, 12, 1, 0, 0, 20,
1, 0, 0, 2, 0, 0, 0, 36, 1, 0, 0, 77, 105, 99, 114,
111, 115, 111, 102, 116, 32, 40, 82, 41, 32, 72, 76, 83, 76, 32,
83, 104, 97, 100, 101, 114, 32, 67, 111, 109, 112, 105, 108, 101, 114,
32, 49, 48, 46, 49, 0, 31, 0, 0, 2, 5, 0, 0, 128, 0,
0, 15, 144, 31, 0, 0, 2, 5, 0, 1, 128, 1, 0, 15, 144,
4, 0, 0, 4, 0, 0, 3, 192, 0, 0, 255, 144, 0, 0, 228,
160, 0, 0, 228, 144, 1, 0, 0, 2, 0, 0, 12, 192, 0, 0,
228, 144, 1, 0, 0, 2, 0, 0, 3, 224, 1, 0, 228, 144, 255,
255, 0, 0, 83, 72, 68, 82, 104, 0, 0, 0, 64, 0, 1, 0,
26, 0, 0, 0, 95, 0, 0, 3, 242, 16, 16, 0, 0, 0, 0,
0, 95, 0, 0, 3, 50, 16, 16, 0, 1, 0, 0, 0, 103, 0,
0, 4, 242, 32, 16, 0, 0, 0, 0, 0, 1, 0, 0, 0, 101,
0, 0, 3, 50, 32, 16, 0, 1, 0, 0, 0, 54, 0, 0, 5,
242, 32, 16, 0, 0, 0, 0, 0, 70, 30, 16, 0, 0, 0, 0,
0, 54, 0, 0, 5, 50, 32, 16, 0, 1, 0, 0, 0, 70, 16,
16, 0, 1, 0, 0, 0, 62, 0, 0, 1, 83, 80, 68, 66, 0,
46, 0, 0, 77, 105, 99, 114, 111, 115, 111, 102, 116, 32, 67, 47,
67, 43, 43, 32, 77, 83, 70, 32, 55, 46, 48, 48, 13, 10, 26,
68, 83, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 23, 0,
0, 0, 132, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 56, 0, 128, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 5, 0, 0, 0, 32, 0,
0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
0, 0, 0, 6, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 46,
49, 1, 118, 98, 147, 101, 1, 0, 0, 0, 201, 198, 0, 59, 60,
109, 111, 65, 179, 15, 4, 145, 38, 240, 113, 148, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 81, 51, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
68, 51, 68, 83, 72, 68, 82, 0, 104, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 32, 0, 0, 96, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 198, 90, 0, 0, 117, 131, 1, 0, 156, 39, 3, 0, 156,
202, 1, 0, 38, 247, 2, 0, 69, 103, 0, 0, 109, 24, 1, 0,
248, 34, 2, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 115, 116, 114, 117, 99, 116, 32, 86, 83, 95, 73,
78, 80, 85, 84, 13, 10, 123, 13, 10, 32, 32, 32, 32, 102, 108,
111, 97, 116, 52, 32, 80, 111, 115, 32, 58, 32, 80, 79, 83, 73,
84, 73, 79, 78, 59, 13, 10, 32, 32, 32, 32, 102, 108, 111, 97,
116, 50, 32, 84, 101, 120, 32, 58, 32, 84, 69, 88, 67, 79, 79,
82, 68, 59, 13, 10, 125, 59, 13, 10, 13, 10, 115, 116, 114, 117,
99, 116, 32, 86, 83, 95, 79, 85, 84, 80, 85, 84, 13, 10, 123,
13, 10, 32, 32, 32, 32, 102, 108, 111, 97, 116, 52, 32, 80, 111,
115, 32, 58, 32, 83, 86, 95, 80, 79, 83, 73, 84, 73, 79, 78,
59, 13, 10, 32, 32, 32, 32, 102, 108, 111, 97, 116, 50, 32, 84,
101, 120, 32, 58, 32, 84, 69, 88, 67, 79, 79, 82, 68, 59, 13,
10, 125, 59, 13, 10, 86, 83, 95, 79, 85, 84, 80, 85, 84, 32,
86, 83, 40, 86, 83, 95, 73, 78, 80, 85, 84, 32, 105, 110, 112,
117, 116, 41, 13, 10, 123, 13, 10, 32, 32, 32, 32, 114, 101, 116,
117, 114, 110, 32, 105, 110, 112, 117, 116, 59, 13, 10, 125, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 254, 239, 254, 239, 1, 0, 0, 0, 108,
1, 0, 0, 0, 68, 58, 92, 114, 117, 115, 116, 100, 101, 115, 107,
92, 103, 112, 117, 95, 118, 105, 100, 101, 111, 95, 99, 111, 100, 101,
99, 92, 118, 115, 92, 83, 104, 97, 100, 101, 114, 67, 111, 109, 112,
105, 108, 101, 84, 111, 111, 108, 92, 110, 118, 95, 118, 101, 114, 116,
101, 120, 95, 115, 104, 97, 100, 101, 114, 46, 104, 108, 115, 108, 0,
0, 100, 58, 92, 114, 117, 115, 116, 100, 101, 115, 107, 92, 103, 112,
117, 95, 118, 105, 100, 101, 111, 95, 99, 111, 100, 101, 99, 92, 118,
115, 92, 115, 104, 97, 100, 101, 114, 99, 111, 109, 112, 105, 108, 101,
116, 111, 111, 108, 92, 110, 118, 95, 118, 101, 114, 116, 101, 120, 95,
115, 104, 97, 100, 101, 114, 46, 104, 108, 115, 108, 0, 115, 116, 114,
117, 99, 116, 32, 86, 83, 95, 73, 78, 80, 85, 84, 13, 10, 123,
13, 10, 32, 32, 32, 32, 102, 108, 111, 97, 116, 52, 32, 80, 111,
115, 32, 58, 32, 80, 79, 83, 73, 84, 73, 79, 78, 59, 13, 10,
32, 32, 32, 32, 102, 108, 111, 97, 116, 50, 32, 84, 101, 120, 32,
58, 32, 84, 69, 88, 67, 79, 79, 82, 68, 59, 13, 10, 125, 59,
13, 10, 13, 10, 115, 116, 114, 117, 99, 116, 32, 86, 83, 95, 79,
85, 84, 80, 85, 84, 13, 10, 123, 13, 10, 32, 32, 32, 32, 102,
108, 111, 97, 116, 52, 32, 80, 111, 115, 32, 58, 32, 83, 86, 95,
80, 79, 83, 73, 84, 73, 79, 78, 59, 13, 10, 32, 32, 32, 32,
102, 108, 111, 97, 116, 50, 32, 84, 101, 120, 32, 58, 32, 84, 69,
88, 67, 79, 79, 82, 68, 59, 13, 10, 125, 59, 13, 10, 86, 83,
95, 79, 85, 84, 80, 85, 84, 32, 86, 83, 40, 86, 83, 95, 73,
78, 80, 85, 84, 32, 105, 110, 112, 117, 116, 41, 13, 10, 123, 13,
10, 32, 32, 32, 32, 114, 101, 116, 117, 114, 110, 32, 105, 110, 112,
117, 116, 59, 13, 10, 125, 0, 7, 0, 0, 0, 0, 0, 0, 0,
72, 0, 0, 0, 144, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 73, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 27, 226, 48, 1, 128, 0, 0,
0, 11, 23, 208, 112, 24, 61, 218, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0,
0, 0, 73, 0, 0, 0, 40, 0, 0, 0, 27, 226, 48, 1, 78,
31, 42, 3, 219, 0, 0, 0, 1, 0, 0, 0, 72, 0, 0, 0,
73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 66,
0, 60, 17, 16, 1, 0, 0, 0, 1, 10, 0, 1, 0, 243, 2,
93, 88, 10, 0, 1, 0, 243, 2, 93, 88, 77, 105, 99, 114, 111,
115, 111, 102, 116, 32, 40, 82, 41, 32, 72, 76, 83, 76, 32, 83,
104, 97, 100, 101, 114, 32, 67, 111, 109, 112, 105, 108, 101, 114, 32,
49, 48, 46, 49, 0, 0, 0, 62, 0, 61, 17, 1, 104, 108, 115,
108, 70, 108, 97, 103, 115, 0, 48, 120, 49, 0, 104, 108, 115, 108,
84, 97, 114, 103, 101, 116, 0, 118, 115, 95, 52, 95, 48, 95, 108,
101, 118, 101, 108, 95, 57, 95, 51, 0, 104, 108, 115, 108, 69, 110,
116, 114, 121, 0, 86, 83, 0, 0, 0, 0, 0, 42, 0, 16, 17,
0, 0, 0, 0, 64, 2, 0, 0, 0, 0, 0, 0, 44, 0, 0,
0, 0, 0, 0, 0, 44, 0, 0, 0, 7, 16, 0, 0, 60, 0,
0, 0, 1, 0, 160, 86, 83, 0, 0, 0, 46, 0, 62, 17, 3,
16, 0, 0, 9, 0, 105, 110, 112, 117, 116, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0,
80, 17, 1, 0, 5, 0, 0, 0, 4, 0, 60, 0, 0, 0, 1,
0, 44, 0, 0, 0, 0, 0, 22, 0, 80, 17, 1, 0, 5, 0,
4, 0, 4, 0, 60, 0, 0, 0, 1, 0, 44, 0, 4, 0, 0,
0, 22, 0, 80, 17, 1, 0, 5, 0, 8, 0, 4, 0, 60, 0,
0, 0, 1, 0, 44, 0, 8, 0, 0, 0, 22, 0, 80, 17, 1,
0, 5, 0, 12, 0, 4, 0, 60, 0, 0, 0, 1, 0, 44, 0,
12, 0, 0, 0, 22, 0, 80, 17, 1, 0, 5, 0, 16, 0, 4,
0, 60, 0, 0, 0, 1, 0, 44, 0, 16, 0, 0, 0, 22, 0,
80, 17, 1, 0, 5, 0, 20, 0, 4, 0, 60, 0, 0, 0, 1,
0, 44, 0, 20, 0, 0, 0, 58, 0, 62, 17, 6, 16, 0, 0,
136, 0, 60, 86, 83, 32, 114, 101, 116, 117, 114, 110, 32, 118, 97,
108, 117, 101, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 22, 0, 80, 17, 2, 0, 5, 0,
16, 0, 4, 0, 60, 0, 0, 0, 1, 0, 44, 0, 16, 0, 0,
0, 22, 0, 80, 17, 2, 0, 5, 0, 20, 0, 4, 0, 60, 0,
0, 0, 1, 0, 44, 0, 20, 0, 0, 0, 22, 0, 80, 17, 2,
0, 5, 0, 0, 0, 4, 0, 60, 0, 0, 0, 1, 0, 44, 0,
0, 0, 0, 0, 22, 0, 80, 17, 2, 0, 5, 0, 4, 0, 4,
0, 60, 0, 0, 0, 1, 0, 44, 0, 4, 0, 0, 0, 22, 0,
80, 17, 2, 0, 5, 0, 8, 0, 4, 0, 60, 0, 0, 0, 1,
0, 44, 0, 8, 0, 0, 0, 22, 0, 80, 17, 2, 0, 5, 0,
12, 0, 4, 0, 60, 0, 0, 0, 1, 0, 44, 0, 12, 0, 0,
0, 2, 0, 6, 0, 244, 0, 0, 0, 24, 0, 0, 0, 1, 0,
0, 0, 16, 1, 132, 23, 125, 135, 58, 127, 163, 121, 164, 136, 47,
191, 133, 155, 215, 75, 0, 0, 242, 0, 0, 0, 96, 0, 0, 0,
0, 0, 0, 0, 1, 0, 1, 0, 104, 0, 0, 0, 0, 0, 0,
0, 6, 0, 0, 0, 84, 0, 0, 0, 60, 0, 0, 0, 14, 0,
0, 128, 60, 0, 0, 0, 14, 0, 0, 0, 80, 0, 0, 0, 14,
0, 0, 128, 80, 0, 0, 0, 14, 0, 0, 0, 100, 0, 0, 0,
14, 0, 0, 128, 100, 0, 0, 0, 14, 0, 0, 0, 5, 0, 17,
0, 5, 0, 17, 0, 5, 0, 17, 0, 5, 0, 17, 0, 5, 0,
17, 0, 5, 0, 17, 0, 246, 0, 0, 0, 4, 0, 0, 0, 0,
0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11,
202, 49, 1, 56, 0, 0, 0, 0, 16, 0, 0, 8, 16, 0, 0,
188, 0, 0, 0, 10, 0, 255, 255, 4, 0, 0, 0, 255, 255, 3,
0, 0, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 8, 0,
0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 22, 0, 27, 21, 64,
0, 0, 0, 4, 0, 0, 0, 16, 0, 102, 108, 111, 97, 116, 52,
0, 243, 242, 241, 22, 0, 27, 21, 64, 0, 0, 0, 2, 0, 0,
0, 8, 0, 102, 108, 111, 97, 116, 50, 0, 243, 242, 241, 34, 0,
3, 18, 13, 21, 3, 0, 0, 16, 0, 0, 0, 0, 80, 111, 115,
0, 242, 241, 13, 21, 3, 0, 1, 16, 0, 0, 16, 0, 84, 101,
120, 0, 242, 241, 30, 0, 5, 21, 2, 0, 0, 0, 2, 16, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 86, 83, 95, 73,
78, 80, 85, 84, 0, 241, 10, 0, 1, 18, 1, 0, 0, 0, 3,
16, 0, 0, 30, 0, 5, 21, 2, 0, 0, 0, 2, 16, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 86, 83, 95, 79, 85,
84, 80, 85, 84, 0, 10, 0, 24, 21, 5, 16, 0, 0, 1, 0,
1, 0, 14, 0, 8, 16, 6, 16, 0, 0, 23, 0, 1, 0, 4,
16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 11, 202, 49, 1, 56, 0, 0, 0, 0, 16, 0, 0, 0, 16,
0, 0, 0, 0, 0, 0, 11, 0, 255, 255, 4, 0, 0, 0, 255,
255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 255, 255, 255, 255, 26, 9, 47, 241, 8, 0, 0, 0,
8, 2, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 18, 0, 37, 17, 0, 0, 0, 0,
136, 0, 0, 0, 1, 0, 86, 83, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 255, 255, 255, 255, 26, 9, 47, 241, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 26, 9, 47, 241,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
119, 9, 49, 1, 1, 0, 0, 0, 13, 0, 20, 142, 14, 0, 20,
107, 15, 0, 1, 0, 72, 0, 0, 0, 32, 0, 0, 0, 44, 0,
0, 0, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22,
0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 104, 0, 0,
0, 32, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 9, 0, 68, 2, 0, 0, 0, 0, 0, 0, 148,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 86, 83, 0, 110, 111, 110, 101, 0, 45, 186, 46,
241, 1, 0, 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, 32, 0,
0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
0, 2, 0, 7, 0, 0, 0, 0, 0, 1, 0, 255, 255, 255, 255,
0, 0, 0, 0, 104, 0, 0, 0, 8, 2, 0, 0, 0, 0, 0,
0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 1, 0,
1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 68, 58, 92, 114, 117,
115, 116, 100, 101, 115, 107, 92, 103, 112, 117, 95, 118, 105, 100, 101,
111, 95, 99, 111, 100, 101, 99, 92, 118, 115, 92, 83, 104, 97, 100,
101, 114, 67, 111, 109, 112, 105, 108, 101, 84, 111, 111, 108, 92, 110,
118, 95, 118, 101, 114, 116, 101, 120, 95, 115, 104, 97, 100, 101, 114,
46, 104, 108, 115, 108, 0, 0, 254, 239, 254, 239, 1, 0, 0, 0,
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 12, 0, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 46,
49, 1, 118, 98, 147, 101, 1, 0, 0, 0, 201, 198, 0, 59, 60,
109, 111, 65, 179, 15, 4, 145, 38, 240, 113, 148, 116, 0, 0, 0,
47, 76, 105, 110, 107, 73, 110, 102, 111, 0, 47, 110, 97, 109, 101,
115, 0, 47, 115, 114, 99, 47, 104, 101, 97, 100, 101, 114, 98, 108,
111, 99, 107, 0, 47, 115, 114, 99, 47, 102, 105, 108, 101, 115, 47,
100, 58, 92, 114, 117, 115, 116, 100, 101, 115, 107, 92, 103, 112, 117,
95, 118, 105, 100, 101, 111, 95, 99, 111, 100, 101, 99, 92, 118, 115,
92, 115, 104, 97, 100, 101, 114, 99, 111, 109, 112, 105, 108, 101, 116,
111, 111, 108, 92, 110, 118, 95, 118, 101, 114, 116, 101, 120, 95, 115,
104, 97, 100, 101, 114, 46, 104, 108, 115, 108, 0, 4, 0, 0, 0,
6, 0, 0, 0, 1, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0,
0, 17, 0, 0, 0, 7, 0, 0, 0, 34, 0, 0, 0, 8, 0,
0, 0, 10, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 5,
0, 0, 0, 0, 0, 0, 0, 220, 81, 51, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16, 0, 0, 0, 32, 0, 0, 0, 208, 0, 0, 0, 244, 0, 0,
0, 87, 1, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 156, 1,
0, 0, 128, 0, 0, 0, 219, 0, 0, 0, 224, 2, 0, 0, 40,
0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 32, 2, 0, 0,
44, 0, 0, 0, 20, 0, 0, 0, 3, 0, 0, 0, 20, 0, 0,
0, 13, 0, 0, 0, 19, 0, 0, 0, 14, 0, 0, 0, 9, 0,
0, 0, 10, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 12,
0, 0, 0, 7, 0, 0, 0, 6, 0, 0, 0, 15, 0, 0, 0,
16, 0, 0, 0, 18, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 83, 84, 65, 84, 116, 0, 0, 0, 3, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 82, 68, 69, 70, 68, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0,
0, 0, 0, 4, 254, 255, 1, 1, 0, 0, 28, 0, 0, 0, 77,
105, 99, 114, 111, 115, 111, 102, 116, 32, 40, 82, 41, 32, 72, 76,
83, 76, 32, 83, 104, 97, 100, 101, 114, 32, 67, 111, 109, 112, 105,
108, 101, 114, 32, 49, 48, 46, 49, 0, 73, 83, 71, 78, 76, 0,
0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 56, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0,
15, 15, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 3, 0, 0, 0, 1, 0, 0, 0, 3, 3, 0, 0, 80, 79,
83, 73, 84, 73, 79, 78, 0, 84, 69, 88, 67, 79, 79, 82, 68,
0, 171, 171, 79, 83, 71, 78, 80, 0, 0, 0, 2, 0, 0, 0,
8, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 3, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 68, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1,
0, 0, 0, 3, 12, 0, 0, 83, 86, 95, 80, 79, 83, 73, 84,
73, 79, 78, 0, 84, 69, 88, 67, 79, 79, 82, 68, 0, 171, 171,
171};

View File

@@ -0,0 +1,793 @@
#include <array>
#include <atomic>
#include <chrono>
#include <cstdio>
#include <list>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include <d3d11.h>
#include <dxgi.h>
#include "win.h"
#define LOG_MODULE "WIN"
#include "log.h"
#define NUMVERTICES 6
typedef struct _VERTEX {
DirectX::XMFLOAT3 Pos;
DirectX::XMFLOAT2 TexCoord;
} VERTEX;
bool NativeDevice::Init(int64_t luid, ID3D11Device *device, int pool_size) {
if (device) {
if (!InitFromDevice(device))
return false;
} else {
if (!InitFromLuid(luid))
return false;
}
if (!SetMultithreadProtected())
return false;
if (!InitQuery())
return false;
if (!InitVideoDevice())
return false;
count_ = pool_size;
texture_.resize(count_);
std::fill(texture_.begin(), texture_.end(), nullptr);
return true;
}
bool NativeDevice::InitFromLuid(int64_t luid) {
HRESULT hr = S_OK;
HRB(CreateDXGIFactory1(IID_IDXGIFactory1,
(void **)factory1_.ReleaseAndGetAddressOf()));
ComPtr<IDXGIAdapter1> tmpAdapter = nullptr;
UINT i = 0;
while (!FAILED(
factory1_->EnumAdapters1(i, tmpAdapter.ReleaseAndGetAddressOf()))) {
i++;
DXGI_ADAPTER_DESC1 desc = DXGI_ADAPTER_DESC1();
tmpAdapter->GetDesc1(&desc);
if (LUID(desc) == luid) {
adapter1_.Swap(tmpAdapter);
break;
}
}
if (!adapter1_) {
LOG_ERROR(std::string("Failed to find adapter1_"));
return false;
}
HRB(adapter1_.As(&adapter_));
UINT createDeviceFlags =
D3D11_CREATE_DEVICE_VIDEO_SUPPORT | D3D11_CREATE_DEVICE_BGRA_SUPPORT;
D3D_FEATURE_LEVEL featureLevels[] = {
D3D_FEATURE_LEVEL_11_0,
};
UINT numFeatureLevels = ARRAYSIZE(featureLevels);
D3D_FEATURE_LEVEL featureLevel;
D3D_DRIVER_TYPE d3dDriverType =
adapter1_ ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE;
HRB(D3D11CreateDevice(adapter1_.Get(), d3dDriverType, nullptr,
createDeviceFlags, featureLevels, numFeatureLevels,
D3D11_SDK_VERSION, device_.ReleaseAndGetAddressOf(),
&featureLevel, context_.ReleaseAndGetAddressOf()));
if (featureLevel != D3D_FEATURE_LEVEL_11_0) {
LOG_ERROR(std::string("Direct3D Feature Level 11 unsupported."));
return false;
}
return true;
}
bool NativeDevice::InitFromDevice(ID3D11Device *device) {
device_ = device;
device_->GetImmediateContext(context_.ReleaseAndGetAddressOf());
ComPtr<IDXGIDevice> dxgiDevice = nullptr;
HRB(device_.As(&dxgiDevice));
HRB(dxgiDevice->GetAdapter(adapter_.ReleaseAndGetAddressOf()));
HRB(adapter_.As(&adapter1_));
HRB(adapter1_->GetParent(IID_PPV_ARGS(&factory1_)));
return true;
}
bool NativeDevice::SetMultithreadProtected() {
ComPtr<ID3D10Multithread> hmt = nullptr;
HRB(context_.As(&hmt));
if (!hmt->SetMultithreadProtected(TRUE)) {
if (!hmt->GetMultithreadProtected()) {
LOG_ERROR(std::string("Failed to SetMultithreadProtected"));
return false;
}
}
return true;
}
bool NativeDevice::InitQuery() {
D3D11_QUERY_DESC queryDesc;
ZeroMemory(&queryDesc, sizeof(queryDesc));
queryDesc.Query = D3D11_QUERY_EVENT;
queryDesc.MiscFlags = 0;
HRB(device_->CreateQuery(&queryDesc, query_.ReleaseAndGetAddressOf()));
return true;
}
bool NativeDevice::InitVideoDevice() {
HRB(device_.As(&video_device_));
HRB(context_.As(&video_context_));
HRB(video_context_.As(&video_context1_));
return true;
}
bool NativeDevice::Nv12ToBgra(int width, int height,
ID3D11Texture2D *nv12Texture,
ID3D11Texture2D *bgraTexture,
int nv12ArrayIndex) {
if (width != last_nv12_to_bgra_width_ ||
height != last_nv12_to_bgra_height_) {
if (!nv12_to_bgra_set_srv(nv12Texture, width, height))
return false;
if (!nv12_to_bgra_set_view_port(width, height))
return false;
if (!nv12_to_bgra_set_sample())
return false;
if (!nv12_to_bgra_set_shader())
return false;
if (!nv12_to_bgra_set_vertex_buffer())
return false;
}
last_nv12_to_bgra_width_ = width;
last_nv12_to_bgra_height_ = height;
if (!nv12_to_bgra_set_rtv(bgraTexture, width, height))
return false;
D3D11_BOX srcBox;
srcBox.left = 0;
srcBox.top = 0;
srcBox.right = width;
srcBox.bottom = height;
srcBox.front = 0;
srcBox.back = 1;
context_->CopySubresourceRegion(nv12SrvTexture_.Get(), 0, 0, 0, 0,
nv12Texture, nv12ArrayIndex, &srcBox);
if (!nv12_to_bgra_draw())
return false;
return true;
}
bool NativeDevice::nv12_to_bgra_set_srv(ID3D11Texture2D *nv12Texture, int width,
int height) {
SRV_[0].Reset();
SRV_[1].Reset();
D3D11_TEXTURE2D_DESC texDesc = {};
nv12Texture->GetDesc(&texDesc);
texDesc.MipLevels = 1;
texDesc.ArraySize = 1;
texDesc.Format = DXGI_FORMAT_NV12;
texDesc.SampleDesc.Quality = 0;
texDesc.SampleDesc.Count = 1;
texDesc.Usage = D3D11_USAGE_DEFAULT;
texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
texDesc.CPUAccessFlags = 0;
texDesc.MiscFlags = 0;
texDesc.Width = width;
texDesc.Height = height;
HRB(device_->CreateTexture2D(&texDesc, nullptr,
nv12SrvTexture_.ReleaseAndGetAddressOf()));
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
srvDesc = CD3D11_SHADER_RESOURCE_VIEW_DESC(nv12SrvTexture_.Get(),
D3D11_SRV_DIMENSION_TEXTURE2D,
DXGI_FORMAT_R8_UNORM);
HRB(device_->CreateShaderResourceView(nv12SrvTexture_.Get(), &srvDesc,
SRV_[0].ReleaseAndGetAddressOf()));
srvDesc = CD3D11_SHADER_RESOURCE_VIEW_DESC(nv12SrvTexture_.Get(),
D3D11_SRV_DIMENSION_TEXTURE2D,
DXGI_FORMAT_R8G8_UNORM);
HRB(device_->CreateShaderResourceView(nv12SrvTexture_.Get(), &srvDesc,
SRV_[1].ReleaseAndGetAddressOf()));
// set SRV
std::array<ID3D11ShaderResourceView *, 2> const textureViews = {
SRV_[0].Get(), SRV_[1].Get()};
context_->PSSetShaderResources(0, textureViews.size(), textureViews.data());
return true;
}
bool NativeDevice::nv12_to_bgra_set_rtv(ID3D11Texture2D *bgraTexture, int width,
int height) {
RTV_.Reset();
D3D11_RENDER_TARGET_VIEW_DESC rtDesc;
rtDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtDesc.Texture2D.MipSlice = 0;
HRB(device_->CreateRenderTargetView(bgraTexture, &rtDesc,
RTV_.ReleaseAndGetAddressOf()));
const float clearColor[4] = {0.0f, 0.0f, 0.0f, 0.0f}; // clear as black
context_->ClearRenderTargetView(RTV_.Get(), clearColor);
context_->OMSetRenderTargets(1, RTV_.GetAddressOf(), NULL);
return true;
}
bool NativeDevice::nv12_to_bgra_set_view_port(int width, int height) {
D3D11_VIEWPORT vp;
vp.Width = (FLOAT)(width);
vp.Height = (FLOAT)(height);
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
context_->RSSetViewports(1, &vp);
return true;
}
bool NativeDevice::nv12_to_bgra_set_sample() {
samplerLinear_.Reset();
D3D11_SAMPLER_DESC sampleDesc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT());
HRB(device_->CreateSamplerState(&sampleDesc,
samplerLinear_.ReleaseAndGetAddressOf()));
context_->PSSetSamplers(0, 1, samplerLinear_.GetAddressOf());
return true;
}
bool NativeDevice::nv12_to_bgra_set_shader() {
vertexShader_.Reset();
pixelShader_.Reset();
// https://gist.github.com/RomiTT/9c05d36fe339b899793a3252297a5624
#include "pixel_shader_601.h"
#include "vertex_shader.h"
device_->CreateVertexShader(g_VS, ARRAYSIZE(g_VS), nullptr,
vertexShader_.ReleaseAndGetAddressOf());
device_->CreatePixelShader(g_PS, ARRAYSIZE(g_PS), nullptr,
pixelShader_.ReleaseAndGetAddressOf());
// set InputLayout
constexpr std::array<D3D11_INPUT_ELEMENT_DESC, 2> Layout = {{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
D3D11_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12,
D3D11_INPUT_PER_VERTEX_DATA, 0},
}};
ComPtr<ID3D11InputLayout> inputLayout = NULL;
HRB(device_->CreateInputLayout(Layout.data(), Layout.size(), g_VS,
ARRAYSIZE(g_VS), inputLayout.GetAddressOf()));
context_->IASetInputLayout(inputLayout.Get());
context_->VSSetShader(vertexShader_.Get(), NULL, 0);
context_->PSSetShader(pixelShader_.Get(), NULL, 0);
return true;
}
bool NativeDevice::nv12_to_bgra_set_vertex_buffer() {
UINT Stride = sizeof(VERTEX);
UINT Offset = 0;
FLOAT blendFactor[4] = {0.f, 0.f, 0.f, 0.f};
context_->OMSetBlendState(nullptr, blendFactor, 0xffffffff);
context_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// set VertexBuffers
VERTEX Vertices[NUMVERTICES] = {
{XMFLOAT3(-1.0f, -1.0f, 0), XMFLOAT2(0.0f, 1.0f)},
{XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)},
{XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)},
{XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)},
{XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)},
{XMFLOAT3(1.0f, 1.0f, 0), XMFLOAT2(1.0f, 0.0f)},
};
D3D11_BUFFER_DESC BufferDesc;
RtlZeroMemory(&BufferDesc, sizeof(BufferDesc));
BufferDesc.Usage = D3D11_USAGE_DEFAULT;
BufferDesc.ByteWidth = sizeof(VERTEX) * NUMVERTICES;
BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
BufferDesc.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
RtlZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = Vertices;
ComPtr<ID3D11Buffer> VertexBuffer = nullptr;
// Create vertex buffer
HRB(device_->CreateBuffer(&BufferDesc, &InitData, &VertexBuffer));
context_->IASetVertexBuffers(0, 1, VertexBuffer.GetAddressOf(), &Stride,
&Offset);
return true;
}
bool NativeDevice::nv12_to_bgra_draw() {
context_->Draw(NUMVERTICES, 0);
context_->Flush();
return true;
}
bool NativeDevice::EnsureTexture(int width, int height) {
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
if (texture_[0]) {
texture_[0]->GetDesc(&desc);
if ((int)desc.Width == width && (int)desc.Height == height &&
desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM &&
desc.MiscFlags == D3D11_RESOURCE_MISC_SHARED &&
desc.Usage == D3D11_USAGE_DEFAULT) {
return true;
}
}
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
desc.CPUAccessFlags = 0;
for (int i = 0; i < texture_.size(); i++) {
HRB(device_->CreateTexture2D(&desc, nullptr,
texture_[i].ReleaseAndGetAddressOf()));
}
return true;
}
bool NativeDevice::SetTexture(ID3D11Texture2D *texture) {
texture_[index_].Reset();
texture_[index_] = texture;
return true;
}
HANDLE NativeDevice::GetSharedHandle() {
ComPtr<IDXGIResource> resource = nullptr;
HRP(texture_[index_].As(&resource));
HANDLE sharedHandle = nullptr;
HRP(resource->GetSharedHandle(&sharedHandle));
return sharedHandle;
}
ID3D11Texture2D *NativeDevice::GetCurrentTexture() {
return texture_[index_].Get();
}
int NativeDevice::next() {
index_++;
index_ = index_ % count_;
return index_;
}
void NativeDevice::BeginQuery() { context_->Begin(query_.Get()); }
void NativeDevice::EndQuery() { context_->End(query_.Get()); }
bool NativeDevice::Query() {
BOOL bResult = FALSE;
int attempts = 0;
while (!bResult) {
HRESULT hr = context_->GetData(query_.Get(), &bResult, sizeof(BOOL), 0);
if (SUCCEEDED(hr)) {
if (bResult) {
break;
}
}
attempts++;
if (attempts > 100)
Sleep(1);
if (attempts > 1000)
break;
}
return bResult == TRUE;
}
bool NativeDevice::Process(ID3D11Texture2D *in, ID3D11Texture2D *out, int width,
int height,
D3D11_VIDEO_PROCESSOR_CONTENT_DESC content_desc,
DXGI_COLOR_SPACE_TYPE colorSpace_in,
DXGI_COLOR_SPACE_TYPE colorSpace_out,
int arraySlice) {
D3D11_TEXTURE2D_DESC inDesc = {0};
D3D11_TEXTURE2D_DESC outDesc = {0};
in->GetDesc(&inDesc);
out->GetDesc(&outDesc);
if (memcmp(&last_content_desc_, &content_desc, sizeof(content_desc)) != 0) {
if (video_processor_enumerator_) {
video_processor_enumerator_.Reset();
}
if (video_processor_) {
video_processor_.Reset();
}
}
memcpy(&last_content_desc_, &content_desc, sizeof(content_desc));
if (!video_processor_enumerator_ || !video_processor_) {
HRB(video_device_->CreateVideoProcessorEnumerator(
&content_desc, video_processor_enumerator_.ReleaseAndGetAddressOf()));
HRB(video_device_->CreateVideoProcessor(
video_processor_enumerator_.Get(), 0,
video_processor_.ReleaseAndGetAddressOf()));
// This fix too dark or too light, and also make in/out colorspace work
video_context_->VideoProcessorSetStreamAutoProcessingMode(
video_processor_.Get(), 0, FALSE);
video_context_->VideoProcessorSetStreamFrameFormat(
video_processor_.Get(), 0, D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE);
}
// https://chromium.googlesource.com/chromium/src/media/+/refs/heads/main/gpu/windows/d3d11_video_processor_proxy.cc#138
// https://chromium.googlesource.com/chromium/src/+/a30440e4cfc7016d4f75a4e108025667e130b78b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
video_context1_->VideoProcessorSetStreamColorSpace1(video_processor_.Get(), 0,
colorSpace_in);
video_context1_->VideoProcessorSetOutputColorSpace1(video_processor_.Get(),
colorSpace_out);
RECT rect = {0};
rect.right = width;
rect.bottom = height;
video_context_->VideoProcessorSetStreamSourceRect(video_processor_.Get(), 0,
true, &rect);
video_context1_->VideoProcessorSetStreamDestRect(video_processor_.Get(), 0,
true, &rect);
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC InputViewDesc;
ZeroMemory(&InputViewDesc, sizeof(InputViewDesc));
InputViewDesc.FourCC = 0;
InputViewDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
InputViewDesc.Texture2D.MipSlice = 0;
InputViewDesc.Texture2D.ArraySlice = arraySlice;
ComPtr<ID3D11VideoProcessorInputView> inputView = nullptr;
HRB(video_device_->CreateVideoProcessorInputView(
in, video_processor_enumerator_.Get(), &InputViewDesc,
inputView.ReleaseAndGetAddressOf()));
D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC OutputViewDesc;
ZeroMemory(&OutputViewDesc, sizeof(OutputViewDesc));
OutputViewDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
OutputViewDesc.Texture2D.MipSlice = 0;
ComPtr<ID3D11VideoProcessorOutputView> outputView = nullptr;
video_device_->CreateVideoProcessorOutputView(
out, video_processor_enumerator_.Get(), &OutputViewDesc,
outputView.ReleaseAndGetAddressOf());
D3D11_VIDEO_PROCESSOR_STREAM StreamData;
ZeroMemory(&StreamData, sizeof(StreamData));
StreamData.Enable = TRUE;
StreamData.pInputSurface = inputView.Get();
HRB(video_context_->VideoProcessorBlt(video_processor_.Get(),
outputView.Get(), 0, 1, &StreamData));
return true;
}
bool NativeDevice::BgraToNv12(ID3D11Texture2D *bgraTexture,
ID3D11Texture2D *nv12Texture, int width,
int height, DXGI_COLOR_SPACE_TYPE colorSpace_in,
DXGI_COLOR_SPACE_TYPE colorSpace_out) {
D3D11_TEXTURE2D_DESC bgraDesc = {0};
D3D11_TEXTURE2D_DESC nv12Desc = {0};
bgraTexture->GetDesc(&bgraDesc);
nv12Texture->GetDesc(&nv12Desc);
if (bgraDesc.Width < width || bgraDesc.Height < height) {
LOG_ERROR(std::string("bgraTexture size is smaller than width and height, ") +
std::to_string(bgraDesc.Width) + "x" +
std::to_string(bgraDesc.Height) + " < " + std::to_string(width) +
"x" + std::to_string(height));
return false;
}
if (nv12Desc.Width < width || nv12Desc.Height < height) {
LOG_ERROR(std::string("nv12Texture size is smaller than width and height,") +
std::to_string(nv12Desc.Width) + "x" +
std::to_string(nv12Desc.Height) + " < " + std::to_string(width) +
"x" + std::to_string(height));
return false;
}
D3D11_VIDEO_PROCESSOR_CONTENT_DESC contentDesc;
ZeroMemory(&contentDesc, sizeof(contentDesc));
contentDesc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
contentDesc.InputFrameRate.Numerator = 30;
contentDesc.InputFrameRate.Denominator = 1;
// TODO: width height always same with desc.Width and desc.Height in test,
// need test for decide to use which one
// https://chromium.googlesource.com/chromium/src/media/+/refs/heads/main/gpu/windows/d3d11_video_processor_proxy.cc#72
// https://chromium.googlesource.com/chromium/src/media/+/refs/heads/main/gpu/windows/media_foundation_video_encode_accelerator_win.cc#2170
contentDesc.InputWidth = width;
contentDesc.InputHeight = height;
contentDesc.OutputWidth = width;
contentDesc.OutputHeight = height;
contentDesc.OutputFrameRate.Numerator = 30;
contentDesc.OutputFrameRate.Denominator = 1;
return Process(bgraTexture, nv12Texture, width, height, contentDesc,
colorSpace_in, colorSpace_out, 0);
}
AdapterVendor NativeDevice::GetVendor() {
DXGI_ADAPTER_DESC1 desc1 = DXGI_ADAPTER_DESC1();
adapter1_->GetDesc1(&desc1);
if (desc1.VendorId == ADAPTER_VENDOR_NVIDIA) {
return ADAPTER_VENDOR_NVIDIA;
} else if (desc1.VendorId == ADAPTER_VENDOR_AMD) {
return ADAPTER_VENDOR_AMD;
} else if (desc1.VendorId == ADAPTER_VENDOR_INTEL) {
return ADAPTER_VENDOR_INTEL;
} else {
return ADAPTER_VENDOR_UNKNOWN;
}
}
bool NativeDevice::support_decode(DataFormat format) {
const GUID *guid = nullptr;
switch (format) {
case H264:
guid = &D3D11_DECODER_PROFILE_H264_VLD_NOFGT;
break;
case H265:
guid = &D3D11_DECODER_PROFILE_HEVC_VLD_MAIN;
break;
default:
return false;
}
BOOL supported = FALSE;
if (S_OK != video_device_->CheckVideoDecoderFormat(guid, DXGI_FORMAT_NV12,
&supported)) {
return false;
}
if (supported) {
DXGI_ADAPTER_DESC1 desc1 = DXGI_ADAPTER_DESC1();
if (FAILED(adapter1_->GetDesc1(&desc1))) {
return false;
}
bool partial =
isFormatHybridDecodedByHardware(format, desc1.VendorId, desc1.DeviceId);
return partial == false;
}
return false;
}
// https://github.com/moonlight-stream/moonlight-qt/blob/9117f6565e4b2a6ba5417282de6bf9360b681f1a/app/streaming/video/ffmpeg-renderers/dxutil.h#L8
bool NativeDevice::isFormatHybridDecodedByHardware(DataFormat format,
unsigned int vendorId,
unsigned int deviceId) {
if (vendorId == ADAPTER_VENDOR_INTEL) {
// Intel seems to encode the series in the high byte of
// the device ID. We want to avoid the "Partial" acceleration
// support explicitly. Those will claim to have HW acceleration
// but perform badly.
// https://en.wikipedia.org/wiki/Intel_Graphics_Technology#Capabilities_(GPU_video_acceleration)
// https://raw.githubusercontent.com/GameTechDev/gpudetect/master/IntelGfx.cfg
switch (deviceId & 0xFF00) {
case 0x0400: // Haswell
case 0x0A00: // Haswell
case 0x0D00: // Haswell
case 0x1600: // Broadwell
case 0x2200: // Cherry Trail and Braswell
// Block these for HEVC to avoid hybrid decode
return format == H265;
default:
break;
}
} else if (vendorId == ADAPTER_VENDOR_NVIDIA) {
// For NVIDIA, we wait to avoid those GPUs with Feature Set E
// for HEVC decoding, since that's hybrid. It appears that Kepler GPUs
// also had some hybrid decode support (per DXVA2 Checker) so we'll
// blacklist those too.
// https://en.wikipedia.org/wiki/Nvidia_PureVideo
// https://bluesky23.yukishigure.com/en/dxvac/deviceInfo/decoder.html
// http://envytools.readthedocs.io/en/latest/hw/pciid.html (missing GM200)
if ((deviceId >= 0x1180 && deviceId <= 0x11BF) || // GK104
(deviceId >= 0x11C0 && deviceId <= 0x11FF) || // GK106
(deviceId >= 0x0FC0 && deviceId <= 0x0FFF) || // GK107
(deviceId >= 0x1000 && deviceId <= 0x103F) || // GK110/GK110B
(deviceId >= 0x1280 && deviceId <= 0x12BF) || // GK208
(deviceId >= 0x1340 && deviceId <= 0x137F) || // GM108
(deviceId >= 0x1380 && deviceId <= 0x13BF) || // GM107
(deviceId >= 0x13C0 && deviceId <= 0x13FF) || // GM204
(deviceId >= 0x1617 && deviceId <= 0x161A) || // GM204
(deviceId == 0x1667) || // GM204
(deviceId >= 0x17C0 && deviceId <= 0x17FF)) { // GM200
// Avoid HEVC on Feature Set E GPUs
return format == H265;
}
}
return false;
}
bool Adapter::Init(IDXGIAdapter1 *adapter1) {
HRESULT hr = S_OK;
adapter1_ = adapter1;
HRB(adapter1_.As(&adapter_));
UINT createDeviceFlags = 0;
D3D_FEATURE_LEVEL featureLevels[] = {
D3D_FEATURE_LEVEL_11_0,
};
UINT numFeatureLevels = ARRAYSIZE(featureLevels);
D3D_FEATURE_LEVEL featureLevel;
D3D_DRIVER_TYPE d3dDriverType =
adapter1_ ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE;
hr = D3D11CreateDevice(adapter1_.Get(), d3dDriverType, nullptr,
createDeviceFlags, featureLevels, numFeatureLevels,
D3D11_SDK_VERSION, device_.ReleaseAndGetAddressOf(),
&featureLevel, context_.ReleaseAndGetAddressOf());
if (FAILED(hr)) {
return false;
}
if (featureLevel != D3D_FEATURE_LEVEL_11_0) {
std::cerr << "Direct3D Feature Level 11 unsupported." << std::endl;
return false;
}
HRB(adapter1->GetDesc1(&desc1_));
if (desc1_.VendorId == ADAPTER_VENDOR_INTEL) {
if (!SetMultithreadProtected())
return false;
}
return true;
}
bool Adapter::SetMultithreadProtected() {
ComPtr<ID3D10Multithread> hmt = nullptr;
HRB(context_.As(&hmt));
if (!hmt->SetMultithreadProtected(TRUE)) {
if (!hmt->GetMultithreadProtected()) {
std::cerr << "Failed to SetMultithreadProtected" << std::endl;
return false;
}
}
return true;
}
bool Adapters::Init(AdapterVendor vendor) {
HRB(CreateDXGIFactory1(IID_IDXGIFactory1,
(void **)factory1_.ReleaseAndGetAddressOf()));
ComPtr<IDXGIAdapter1> tmpAdapter = nullptr;
UINT i = 0;
while (!FAILED(
factory1_->EnumAdapters1(i, tmpAdapter.ReleaseAndGetAddressOf()))) {
i++;
DXGI_ADAPTER_DESC1 desc = DXGI_ADAPTER_DESC1();
tmpAdapter->GetDesc1(&desc);
if (desc.VendorId == static_cast<UINT>(vendor)) {
auto adapter = std::make_unique<Adapter>();
if (adapter->Init(tmpAdapter.Get())) {
adapters_.push_back(std::move(adapter));
}
}
}
return true;
}
int Adapters::GetFirstAdapterIndex(AdapterVendor vendor) {
ComPtr<IDXGIFactory1> factory1 = nullptr;
HRI(CreateDXGIFactory1(IID_IDXGIFactory1,
(void **)factory1.ReleaseAndGetAddressOf()));
ComPtr<IDXGIAdapter1> tmpAdapter = nullptr;
UINT i = 0;
while (!FAILED(
factory1->EnumAdapters1(i, tmpAdapter.ReleaseAndGetAddressOf()))) {
i++;
DXGI_ADAPTER_DESC1 desc = DXGI_ADAPTER_DESC1();
tmpAdapter->GetDesc1(&desc);
if (desc.VendorId == static_cast<UINT>(vendor)) {
return i - 1;
}
}
return -1;
}
// https://asawicki.info/news_1773_how_to_programmatically_check_graphics_driver_version
// https://github.com/citizenfx/fivem/issues/1121
uint64_t GetHwcodecGpuSignature() {
uint64_t signature = 0;
ComPtr<IDXGIFactory1> factory1 = nullptr;
HRI(CreateDXGIFactory1(IID_IDXGIFactory1,
(void **)factory1.ReleaseAndGetAddressOf()));
ComPtr<IDXGIAdapter1> tmpAdapter = nullptr;
UINT i = 0;
while (!FAILED(
factory1->EnumAdapters1(i, tmpAdapter.ReleaseAndGetAddressOf()))) {
i++;
DXGI_ADAPTER_DESC1 desc = {0};
if (SUCCEEDED(tmpAdapter->GetDesc1(&desc))) {
if (desc.VendorId == ADAPTER_VENDOR_NVIDIA ||
desc.VendorId == ADAPTER_VENDOR_AMD ||
desc.VendorId == ADAPTER_VENDOR_INTEL) {
// hardware
signature += desc.VendorId;
signature += desc.DeviceId;
signature += desc.SubSysId;
signature += desc.Revision;
// software
LARGE_INTEGER umd_version;
if SUCCEEDED (tmpAdapter->CheckInterfaceSupport(__uuidof(IDXGIDevice),
&umd_version)) {
signature += umd_version.QuadPart;
}
}
}
}
return signature;
}
void hwcodec_get_d3d11_texture_width_height(ID3D11Texture2D *texture, int *w,
int *h) {
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
*w = desc.Width;
*h = desc.Height;
}
int32_t add_process_to_new_job(DWORD process_id) {
HANDLE job_handle = CreateJobObjectW(nullptr, nullptr);
if (job_handle == nullptr) {
LOG_ERROR(std::string("Failed to create job object"));
return -1;
}
JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = {0};
job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
BOOL result = SetInformationJobObject(
job_handle,
JobObjectExtendedLimitInformation,
&job_info,
sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)
);
if (result == FALSE) {
CloseHandle(job_handle);
LOG_ERROR(std::string("Failed to set job information"));
return -1;
}
// Open the existing process by ID
HANDLE process_handle = OpenProcess(PROCESS_SET_QUOTA | PROCESS_TERMINATE, FALSE, process_id);
if (process_handle == nullptr) {
CloseHandle(job_handle);
LOG_ERROR(std::string("Failed to open process with ID: ") + std::to_string(process_id));
return -1;
}
// Assign the child process to the Job object
BOOL assign_result = AssignProcessToJobObject(job_handle, process_handle);
if (assign_result == FALSE) {
CloseHandle(process_handle);
CloseHandle(job_handle);
LOG_ERROR(std::string("Failed to assign process to job"));
return -1;
}
// Close process handle (but keep job handle)
CloseHandle(process_handle);
return 0;
}

View File

@@ -0,0 +1,135 @@
#ifndef WIN_H
#define WIN_H
#include <DirectXMath.h>
#include <d3d11.h>
#include <d3d11_1.h>
#include <directxcolors.h>
#include <iostream>
#include <vector>
#include <wrl/client.h>
#include "../../common.h"
using Microsoft::WRL::ComPtr;
using namespace std;
using namespace DirectX;
#define SAFE_RELEASE(p) \
{ \
if ((p)) { \
(p)->Release(); \
(p) = nullptr; \
} \
}
#define LUID(desc) \
(((int64_t)desc.AdapterLuid.HighPart << 32) | desc.AdapterLuid.LowPart)
class NativeDevice {
public:
bool Init(int64_t luid, ID3D11Device *device, int pool_size = 1);
bool EnsureTexture(int width, int height);
bool SetTexture(ID3D11Texture2D *texture);
HANDLE GetSharedHandle();
ID3D11Texture2D *GetCurrentTexture();
int next();
void BeginQuery();
void EndQuery();
bool Query();
bool Process(ID3D11Texture2D *in, ID3D11Texture2D *out, int width, int height,
D3D11_VIDEO_PROCESSOR_CONTENT_DESC content_desc,
DXGI_COLOR_SPACE_TYPE colorSpace_in,
DXGI_COLOR_SPACE_TYPE colorSpace_out, int arraySlice);
bool BgraToNv12(ID3D11Texture2D *bgraTexture, ID3D11Texture2D *nv12Texture,
int width, int height, DXGI_COLOR_SPACE_TYPE colorSpace_in,
DXGI_COLOR_SPACE_TYPE colorSpace_outt);
bool Nv12ToBgra(int width, int height, ID3D11Texture2D *nv12Texture,
ID3D11Texture2D *bgraTexture, int nv12ArrayIndex);
AdapterVendor GetVendor();
bool support_decode(DataFormat format);
private:
bool InitFromLuid(int64_t luid);
bool InitFromDevice(ID3D11Device *device);
bool SetMultithreadProtected();
bool InitQuery();
bool InitVideoDevice();
bool isFormatHybridDecodedByHardware(DataFormat format, unsigned int vendorId,
unsigned int deviceId);
// nv12 to bgra
bool nv12_to_bgra_set_srv(ID3D11Texture2D *nv12Texture, int width,
int height);
bool nv12_to_bgra_set_rtv(ID3D11Texture2D *bgraTexture, int width,
int height);
bool nv12_to_bgra_set_view_port(int width, int height);
bool nv12_to_bgra_set_sample();
bool nv12_to_bgra_set_shader();
bool nv12_to_bgra_set_vertex_buffer();
bool nv12_to_bgra_draw();
public:
// Direct3D 11
ComPtr<IDXGIFactory1> factory1_ = nullptr;
ComPtr<IDXGIAdapter> adapter_ = nullptr;
ComPtr<IDXGIAdapter1> adapter1_ = nullptr;
ComPtr<ID3D11Device> device_ = nullptr;
ComPtr<ID3D11DeviceContext> context_ = nullptr;
ComPtr<ID3D11Query> query_ = nullptr;
ComPtr<ID3D11VideoDevice> video_device_ = nullptr;
ComPtr<ID3D11VideoContext> video_context_ = nullptr;
ComPtr<ID3D11VideoContext1> video_context1_ = nullptr;
ComPtr<ID3D11VideoProcessorEnumerator> video_processor_enumerator_ = nullptr;
ComPtr<ID3D11VideoProcessor> video_processor_ = nullptr;
D3D11_VIDEO_PROCESSOR_CONTENT_DESC last_content_desc_ = {};
ComPtr<ID3D11RenderTargetView> RTV_ = NULL;
ComPtr<ID3D11ShaderResourceView> SRV_[2] = {NULL, NULL};
ComPtr<ID3D11VertexShader> vertexShader_ = NULL;
ComPtr<ID3D11PixelShader> pixelShader_ = NULL;
ComPtr<ID3D11SamplerState> samplerLinear_ = NULL;
ComPtr<ID3D11Texture2D> nv12SrvTexture_ = nullptr;
int count_;
int index_ = 0;
int last_nv12_to_bgra_width_ = 0;
int last_nv12_to_bgra_height_ = 0;
private:
std::vector<ComPtr<ID3D11Texture2D>> texture_;
};
class Adapter {
public:
bool Init(IDXGIAdapter1 *adapter1);
private:
bool SetMultithreadProtected();
public:
ComPtr<IDXGIAdapter> adapter_ = nullptr;
ComPtr<IDXGIAdapter1> adapter1_ = nullptr;
ComPtr<ID3D11Device> device_ = nullptr;
ComPtr<ID3D11DeviceContext> context_ = nullptr;
DXGI_ADAPTER_DESC1 desc1_;
};
class Adapters {
public:
bool Init(AdapterVendor vendor);
static int GetFirstAdapterIndex(AdapterVendor vendor);
public:
ComPtr<IDXGIFactory1> factory1_ = nullptr;
std::vector<std::unique_ptr<Adapter>> adapters_;
};
extern "C" uint64_t GetHwcodecGpuSignature();
extern "C" void hwcodec_get_d3d11_texture_width_height(ID3D11Texture2D *texture, int *w,
int *h);
extern "C" int32_t add_process_to_new_job(DWORD process_id);
#endif

View File

@@ -0,0 +1,11 @@
#ifndef SYSTEM_H
#define SYSTEM_H
#ifdef _WIN32
#include "platform/win/win.h"
#endif
#ifdef __linux__
#include "platform/linux/linux.h"
#endif
#endif // SYSTEM_H

View File

@@ -0,0 +1,357 @@
extern "C" {
#include <libavutil/opt.h>
}
#include "util.h"
#include <limits>
#include <map>
#include <string.h>
#include <vector>
#include "common.h"
#include "common.h"
#define LOG_MODULE "UTIL"
#include "log.h"
namespace util_encode {
void set_av_codec_ctx(AVCodecContext *c, const std::string &name, int kbs,
int gop, int fps) {
c->has_b_frames = 0;
c->max_b_frames = 0;
if (gop > 0 && gop < std::numeric_limits<int16_t>::max()) {
c->gop_size = gop;
c->keyint_min = gop; // Match keyint_min to gop for consistent keyframe interval
} else if (name.find("vaapi") != std::string::npos) {
c->gop_size = fps > 0 ? fps : 30; // Default to 1 second keyframe interval
c->keyint_min = c->gop_size;
} else if (name.find("qsv") != std::string::npos) {
c->gop_size = fps > 0 ? fps : 30;
c->keyint_min = c->gop_size;
} else {
c->gop_size = fps > 0 ? fps : 30;
c->keyint_min = c->gop_size;
}
/* put sample parameters */
// https://github.com/FFmpeg/FFmpeg/blob/415f012359364a77e8394436f222b74a8641a3ee/libavcodec/encode.c#L581
if (kbs > 0) {
c->bit_rate = kbs * 1000;
if (name.find("qsv") != std::string::npos) {
c->rc_max_rate = c->bit_rate;
c->bit_rate--; // cbr with vbr
}
}
/* frames per second */
c->time_base = av_make_q(1, 1000);
c->framerate = av_make_q(fps, 1);
c->flags |= AV_CODEC_FLAG2_LOCAL_HEADER;
c->flags |= AV_CODEC_FLAG_LOW_DELAY;
c->slices = 1;
c->thread_type = FF_THREAD_SLICE;
c->thread_count = c->slices;
// https://github.com/obsproject/obs-studio/blob/3cc7dc0e7cf8b01081dc23e432115f7efd0c8877/plugins/obs-ffmpeg/obs-ffmpeg-mux.c#L160
c->color_range = AVCOL_RANGE_MPEG;
c->colorspace = AVCOL_SPC_SMPTE170M;
c->color_primaries = AVCOL_PRI_SMPTE170M;
c->color_trc = AVCOL_TRC_SMPTE170M;
if (name.find("h264") != std::string::npos) {
c->profile = FF_PROFILE_H264_HIGH;
} else if (name.find("hevc") != std::string::npos) {
c->profile = FF_PROFILE_HEVC_MAIN;
}
}
bool set_lantency_free(void *priv_data, const std::string &name) {
int ret;
if (name.find("nvenc") != std::string::npos) {
if ((ret = av_opt_set(priv_data, "delay", "0", 0)) < 0) {
LOG_ERROR(std::string("nvenc set_lantency_free failed, ret = ") + av_err2str(ret));
return false;
}
}
if (name.find("amf") != std::string::npos) {
if ((ret = av_opt_set(priv_data, "query_timeout", "1000", 0)) < 0) {
LOG_ERROR(std::string("amf set_lantency_free failed, ret = ") + av_err2str(ret));
return false;
}
}
if (name.find("qsv") != std::string::npos) {
if ((ret = av_opt_set(priv_data, "async_depth", "1", 0)) < 0) {
LOG_ERROR(std::string("qsv set_lantency_free failed, ret = ") + av_err2str(ret));
return false;
}
}
if (name.find("vaapi") != std::string::npos) {
if ((ret = av_opt_set(priv_data, "async_depth", "1", 0)) < 0) {
LOG_ERROR(std::string("vaapi set_lantency_free failed, ret = ") + av_err2str(ret));
return false;
}
}
if (name.find("videotoolbox") != std::string::npos) {
if ((ret = av_opt_set_int(priv_data, "realtime", 1, 0)) < 0) {
LOG_ERROR(std::string("videotoolbox set realtime failed, ret = ") + av_err2str(ret));
return false;
}
if ((ret = av_opt_set_int(priv_data, "prio_speed", 1, 0)) < 0) {
LOG_ERROR(std::string("videotoolbox set prio_speed failed, ret = ") + av_err2str(ret));
return false;
}
}
// libvpx (VP8/VP9) - set realtime mode to avoid frame lag
if (name.find("libvpx") != std::string::npos) {
// deadline: realtime for low-latency streaming
if ((ret = av_opt_set(priv_data, "deadline", "realtime", 0)) < 0) {
LOG_ERROR(std::string("libvpx set deadline realtime failed, ret = ") + av_err2str(ret));
return false;
}
// cpu-used: 6 is good balance for real-time (0-8, higher = faster but lower quality)
if ((ret = av_opt_set_int(priv_data, "cpu-used", 6, 0)) < 0) {
LOG_ERROR(std::string("libvpx set cpu-used failed, ret = ") + av_err2str(ret));
return false;
}
// lag-in-frames: 0 disables frame lag (important for real-time)
if ((ret = av_opt_set_int(priv_data, "lag-in-frames", 0, 0)) < 0) {
LOG_ERROR(std::string("libvpx set lag-in-frames failed, ret = ") + av_err2str(ret));
return false;
}
// row-mt: enable row-based multithreading for VP9
if (name.find("vp9") != std::string::npos) {
if ((ret = av_opt_set_int(priv_data, "row-mt", 1, 0)) < 0) {
LOG_ERROR(std::string("libvpx-vp9 set row-mt failed, ret = ") + av_err2str(ret));
// row-mt failure is not fatal
}
}
}
return true;
}
bool set_quality(void *priv_data, const std::string &name, int quality) {
int ret = -1;
if (name.find("nvenc") != std::string::npos) {
switch (quality) {
// p7 isn't zero lantency
case Quality_Medium:
if ((ret = av_opt_set(priv_data, "preset", "p4", 0)) < 0) {
LOG_ERROR(std::string("nvenc set opt preset p4 failed, ret = ") + av_err2str(ret));
return false;
}
break;
case Quality_Low:
if ((ret = av_opt_set(priv_data, "preset", "p1", 0)) < 0) {
LOG_ERROR(std::string("nvenc set opt preset p1 failed, ret = ") + av_err2str(ret));
return false;
}
break;
default:
break;
}
}
if (name.find("amf") != std::string::npos) {
switch (quality) {
case Quality_High:
if ((ret = av_opt_set(priv_data, "quality", "quality", 0)) < 0) {
LOG_ERROR(std::string("amf set opt quality quality failed, ret = ") +
av_err2str(ret));
return false;
}
break;
case Quality_Medium:
if ((ret = av_opt_set(priv_data, "quality", "balanced", 0)) < 0) {
LOG_ERROR(std::string("amf set opt quality balanced failed, ret = ") +
av_err2str(ret));
return false;
}
break;
case Quality_Low:
if ((ret = av_opt_set(priv_data, "quality", "speed", 0)) < 0) {
LOG_ERROR(std::string("amf set opt quality speed failed, ret = ") + av_err2str(ret));
return false;
}
break;
default:
break;
}
}
if (name.find("qsv") != std::string::npos) {
switch (quality) {
case Quality_High:
if ((ret = av_opt_set(priv_data, "preset", "veryslow", 0)) < 0) {
LOG_ERROR(std::string("qsv set opt preset veryslow failed, ret = ") +
av_err2str(ret));
return false;
}
break;
case Quality_Medium:
if ((ret = av_opt_set(priv_data, "preset", "medium", 0)) < 0) {
LOG_ERROR(std::string("qsv set opt preset medium failed, ret = ") + av_err2str(ret));
return false;
}
break;
case Quality_Low:
if ((ret = av_opt_set(priv_data, "preset", "veryfast", 0)) < 0) {
LOG_ERROR(std::string("qsv set opt preset veryfast failed, ret = ") +
av_err2str(ret));
return false;
}
break;
default:
break;
}
}
if (name.find("mediacodec") != std::string::npos) {
if (name.find("h264") != std::string::npos) {
if ((ret = av_opt_set(priv_data, "level", "5.1", 0)) < 0) {
LOG_ERROR(std::string("mediacodec set opt level 5.1 failed, ret = ") +
av_err2str(ret));
return false;
}
}
if (name.find("hevc") != std::string::npos) {
// https:en.wikipedia.org/wiki/High_Efficiency_Video_Coding_tiers_and_levels
if ((ret = av_opt_set(priv_data, "level", "h5.1", 0)) < 0) {
LOG_ERROR(std::string("mediacodec set opt level h5.1 failed, ret = ") +
av_err2str(ret));
return false;
}
}
}
return true;
}
struct CodecOptions {
std::string codec_name;
std::string option_name;
std::map<int, std::string> rc_values;
};
bool set_rate_control(AVCodecContext *c, const std::string &name, int rc,
int q) {
if (name.find("qsv") != std::string::npos) {
// https://github.com/LizardByte/Sunshine/blob/3e47cd3cc8fd37a7a88be82444ff4f3c0022856b/src/video.cpp#L1635
c->strict_std_compliance = FF_COMPLIANCE_UNOFFICIAL;
}
std::vector<CodecOptions> codecs = {
{"nvenc", "rc", {{RC_CBR, "cbr"}, {RC_VBR, "vbr"}}},
{"amf", "rc", {{RC_CBR, "cbr"}, {RC_VBR, "vbr_latency"}}},
{"mediacodec",
"bitrate_mode",
{{RC_CBR, "cbr"}, {RC_VBR, "vbr"}, {RC_CQ, "cq"}}},
// {"videotoolbox", "constant_bit_rate", {{RC_CBR, "1"}}},
};
for (const auto &codec : codecs) {
if (name.find(codec.codec_name) != std::string::npos) {
auto it = codec.rc_values.find(rc);
if (it != codec.rc_values.end()) {
int ret = av_opt_set(c->priv_data, codec.option_name.c_str(),
it->second.c_str(), 0);
if (ret < 0) {
LOG_ERROR(codec.codec_name + " set opt " + codec.option_name + " " +
it->second + " failed, ret = " + av_err2str(ret));
return false;
}
if (name.find("mediacodec") != std::string::npos) {
if (rc == RC_CQ) {
if (q >= 0 && q <= 51) {
c->global_quality = q;
}
}
}
}
break;
}
}
return true;
}
bool set_gpu(void *priv_data, const std::string &name, int gpu) {
int ret;
if (gpu < 0)
return -1;
if (name.find("nvenc") != std::string::npos) {
if ((ret = av_opt_set_int(priv_data, "gpu", gpu, 0)) < 0) {
LOG_ERROR(std::string("nvenc set gpu failed, ret = ") + av_err2str(ret));
return false;
}
}
return true;
}
bool force_hw(void *priv_data, const std::string &name) {
int ret;
if (name.find("_mf") != std::string::npos) {
if ((ret = av_opt_set_int(priv_data, "hw_encoding", 1, 0)) < 0) {
LOG_ERROR(std::string("mediafoundation set hw_encoding failed, ret = ") +
av_err2str(ret));
return false;
}
}
if (name.find("videotoolbox") != std::string::npos) {
if ((ret = av_opt_set_int(priv_data, "allow_sw", 0, 0)) < 0) {
LOG_ERROR(std::string("mediafoundation set allow_sw failed, ret = ") +
av_err2str(ret));
return false;
}
}
return true;
}
bool set_others(void *priv_data, const std::string &name) {
int ret;
if (name.find("_mf") != std::string::npos) {
// ff_eAVScenarioInfo_DisplayRemoting = 1
if ((ret = av_opt_set_int(priv_data, "scenario", 1, 0)) < 0) {
LOG_ERROR(std::string("mediafoundation set scenario failed, ret = ") +
av_err2str(ret));
return false;
}
}
// NOTE: Removed idr_interval = INT_MAX for VAAPI.
// This was disabling automatic keyframe generation.
// The encoder should respect c->gop_size for keyframe interval.
return true;
}
bool change_bit_rate(AVCodecContext *c, const std::string &name, int kbs) {
if (kbs > 0) {
c->bit_rate = kbs * 1000;
if (name.find("qsv") != std::string::npos) {
c->rc_max_rate = c->bit_rate;
}
}
return true;
}
void vram_encode_test_callback(const uint8_t *data, int32_t len, int32_t key, const void *obj, int64_t pts) {
(void)data;
(void)len;
(void)pts;
if (obj) {
int32_t *pkey = (int32_t *)obj;
*pkey = key;
}
}
} // namespace util_encode
namespace util_decode {
static bool g_flag_could_not_find_ref_with_poc = false;
bool has_flag_could_not_find_ref_with_poc() {
bool v = g_flag_could_not_find_ref_with_poc;
g_flag_could_not_find_ref_with_poc = false;
return v;
}
} // namespace util_decode
extern "C" void hwcodec_set_flag_could_not_find_ref_with_poc() {
util_decode::g_flag_could_not_find_ref_with_poc = true;
}

View File

@@ -0,0 +1,52 @@
#ifndef UTIL_H
#define UTIL_H
#include <string>
#include <chrono>
extern "C" {
#include <libavcodec/avcodec.h>
}
namespace util_encode {
void set_av_codec_ctx(AVCodecContext *c, const std::string &name, int kbs,
int gop, int fps);
bool set_lantency_free(void *priv_data, const std::string &name);
bool set_quality(void *priv_data, const std::string &name, int quality);
bool set_rate_control(AVCodecContext *c, const std::string &name, int rc,
int q);
bool set_gpu(void *priv_data, const std::string &name, int gpu);
bool force_hw(void *priv_data, const std::string &name);
bool set_others(void *priv_data, const std::string &name);
bool change_bit_rate(AVCodecContext *c, const std::string &name, int kbs);
void vram_encode_test_callback(const uint8_t *data, int32_t len, int32_t key, const void *obj, int64_t pts);
} // namespace util
namespace util_decode {
bool has_flag_could_not_find_ref_with_poc();
}
namespace util {
inline std::chrono::steady_clock::time_point now() {
return std::chrono::steady_clock::now();
}
inline int64_t elapsed_ms(std::chrono::steady_clock::time_point start) {
return std::chrono::duration_cast<std::chrono::milliseconds>(now() - start).count();
}
inline bool skip_test(const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount, int64_t currentLuid, int32_t dataFormat) {
for (int32_t i = 0; i < excludeCount; i++) {
if (excludedLuids[i] == currentLuid && excludeFormats[i] == dataFormat) {
return true;
}
}
return false;
}
}
#endif