diff --git a/OBS-All.sln b/OBS-All.sln index 6d695367..b5db127e 100644 --- a/OBS-All.sln +++ b/OBS-All.sln @@ -46,6 +46,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PSVPlugin", "PSVPlugin\PSVP {11A35235-DD48-41E2-8F40-825C78024BC0} = {11A35235-DD48-41E2-8F40-825C78024BC0} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QSVHelper", "QSVHelper\QSVHelper.vcxproj", "{F1C7033A-F050-46E3-9080-E129B9CD1010}" + ProjectSection(ProjectDependencies) = postProject + {9E7B3527-11AA-46BA-A82F-C58761F9B56F} = {9E7B3527-11AA-46BA-A82F-C58761F9B56F} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -136,12 +141,12 @@ Global {5465C79D-01DF-406C-AB2D-9C0764917131}.Release|x64.Build.0 = Release|x64 {9E7B3527-11AA-46BA-A82F-C58761F9B56F}.Debug|Win32.ActiveCfg = Debug|Win32 {9E7B3527-11AA-46BA-A82F-C58761F9B56F}.Debug|Win32.Build.0 = Debug|Win32 - {9E7B3527-11AA-46BA-A82F-C58761F9B56F}.Debug|x64.ActiveCfg = Debug|x64 - {9E7B3527-11AA-46BA-A82F-C58761F9B56F}.Debug|x64.Build.0 = Debug|x64 + {9E7B3527-11AA-46BA-A82F-C58761F9B56F}.Debug|x64.ActiveCfg = Debug|Win32 + {9E7B3527-11AA-46BA-A82F-C58761F9B56F}.Debug|x64.Build.0 = Debug|Win32 {9E7B3527-11AA-46BA-A82F-C58761F9B56F}.Release|Win32.ActiveCfg = Release|Win32 {9E7B3527-11AA-46BA-A82F-C58761F9B56F}.Release|Win32.Build.0 = Release|Win32 - {9E7B3527-11AA-46BA-A82F-C58761F9B56F}.Release|x64.ActiveCfg = Release|x64 - {9E7B3527-11AA-46BA-A82F-C58761F9B56F}.Release|x64.Build.0 = Release|x64 + {9E7B3527-11AA-46BA-A82F-C58761F9B56F}.Release|x64.ActiveCfg = Release|Win32 + {9E7B3527-11AA-46BA-A82F-C58761F9B56F}.Release|x64.Build.0 = Release|Win32 {D13C4DA1-A806-49D0-9603-AF40D34D0EF3}.Debug|Win32.ActiveCfg = Debug|Win32 {D13C4DA1-A806-49D0-9603-AF40D34D0EF3}.Debug|Win32.Build.0 = Debug|Win32 {D13C4DA1-A806-49D0-9603-AF40D34D0EF3}.Debug|x64.ActiveCfg = Debug|x64 @@ -150,6 +155,14 @@ Global {D13C4DA1-A806-49D0-9603-AF40D34D0EF3}.Release|Win32.Build.0 = Release|Win32 {D13C4DA1-A806-49D0-9603-AF40D34D0EF3}.Release|x64.ActiveCfg = Release|x64 {D13C4DA1-A806-49D0-9603-AF40D34D0EF3}.Release|x64.Build.0 = Release|x64 + {F1C7033A-F050-46E3-9080-E129B9CD1010}.Debug|Win32.ActiveCfg = Debug|Win32 + {F1C7033A-F050-46E3-9080-E129B9CD1010}.Debug|Win32.Build.0 = Debug|Win32 + {F1C7033A-F050-46E3-9080-E129B9CD1010}.Debug|x64.ActiveCfg = Debug|Win32 + {F1C7033A-F050-46E3-9080-E129B9CD1010}.Debug|x64.Build.0 = Debug|Win32 + {F1C7033A-F050-46E3-9080-E129B9CD1010}.Release|Win32.ActiveCfg = Release|Win32 + {F1C7033A-F050-46E3-9080-E129B9CD1010}.Release|Win32.Build.0 = Release|Win32 + {F1C7033A-F050-46E3-9080-E129B9CD1010}.Release|x64.ActiveCfg = Release|Win32 + {F1C7033A-F050-46E3-9080-E129B9CD1010}.Release|x64.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/OBS.vcxproj b/OBS.vcxproj index 00cbbf51..f6d448ef 100644 --- a/OBS.vcxproj +++ b/OBS.vcxproj @@ -112,10 +112,10 @@ /ignore:4049 /ignore:4217 %(AdditionalOptions) - Avrt.lib;dwmapi.lib;comctl32.lib;dxgi.lib;dxguid.lib;d3d10_1.lib;d3dx10.lib;ws2_32.lib;Iphlpapi.lib;Winmm.lib;librtmp.lib;libmp3lame-static.lib;libfaac.lib;dsound.lib;obsapi.lib;shell32.lib;gdiplus.lib;mfplat.lib;Mfuuid.lib;Winhttp.lib;libx264.lib;UxTheme.lib;libmfx.lib;%(AdditionalDependencies) + Avrt.lib;dwmapi.lib;comctl32.lib;dxgi.lib;dxguid.lib;d3d10_1.lib;d3dx10.lib;ws2_32.lib;Iphlpapi.lib;Winmm.lib;librtmp.lib;libmp3lame-static.lib;libfaac.lib;dsound.lib;obsapi.lib;shell32.lib;gdiplus.lib;mfplat.lib;Mfuuid.lib;Winhttp.lib;libx264.lib;UxTheme.lib;%(AdditionalDependencies) - libmfx/$(Platform)/$(Configuration);OBSApi/Debug;x264/libs/32bit;librtmp/debug;lame/output/32bit;libfaac/debug;%(AdditionalLibraryDirectories) + OBSApi/Debug;x264/libs/32bit;librtmp/debug;lame/output/32bit;libfaac/debug;%(AdditionalLibraryDirectories) type=%27win32%27 name=%27Microsoft.Windows.Common-Controls%27 version=%276.0.0.0%27 processorArchitecture=%27X86%27 publicKeyToken=%276595b64144ccf1df%27 language=%27*%27;%(AdditionalManifestDependencies) true rundir\pdb32\$(TargetName).pdb @@ -147,10 +147,10 @@ Main.h - Avrt.lib;dwmapi.lib;comctl32.lib;dxgi.lib;dxguid.lib;d3d10_1.lib;d3dx10.lib;ws2_32.lib;Iphlpapi.lib;Winmm.lib;librtmp.lib;libmp3lame-static.lib;libfaac.lib;dsound.lib;obsapi.lib;shell32.lib;gdiplus.lib;mfplat.lib;Mfuuid.lib;Winhttp.lib;libx264.lib;UxTheme.lib;libmfx.lib;%(AdditionalDependencies) + Avrt.lib;dwmapi.lib;comctl32.lib;dxgi.lib;dxguid.lib;d3d10_1.lib;d3dx10.lib;ws2_32.lib;Iphlpapi.lib;Winmm.lib;librtmp.lib;libmp3lame-static.lib;libfaac.lib;dsound.lib;obsapi.lib;shell32.lib;gdiplus.lib;mfplat.lib;Mfuuid.lib;Winhttp.lib;libx264.lib;UxTheme.lib;%(AdditionalDependencies) - libmfx/$(Platform)/$(Configuration);OBSApi/x64/Debug;x264/libs/64bit;librtmp/x64/debug;lame/output/64bit;libfaac/x64/debug;%(AdditionalLibraryDirectories) + OBSApi/x64/Debug;x264/libs/64bit;librtmp/x64/debug;lame/output/64bit;libfaac/x64/debug;%(AdditionalLibraryDirectories) type=%27win32%27 name=%27Microsoft.Windows.Common-Controls%27 version=%276.0.0.0%27 processorArchitecture=%27amd64%27 publicKeyToken=%276595b64144ccf1df%27 language=%27*%27;%(AdditionalManifestDependencies) true rundir\pdb64\$(TargetName).pdb @@ -180,10 +180,10 @@ /ignore:4049 /ignore:4217 %(AdditionalOptions) - Avrt.lib;dwmapi.lib;comctl32.lib;dxgi.lib;dxguid.lib;d3d10_1.lib;d3dx10.lib;ws2_32.lib;Iphlpapi.lib;Winmm.lib;librtmp.lib;libmp3lame-static.lib;libfaac.lib;dsound.lib;obsapi.lib;shell32.lib;gdiplus.lib;mfplat.lib;Mfuuid.lib;Winhttp.lib;libx264.lib;UxTheme.lib;libmfx.lib;%(AdditionalDependencies) + Avrt.lib;dwmapi.lib;comctl32.lib;dxgi.lib;dxguid.lib;d3d10_1.lib;d3dx10.lib;ws2_32.lib;Iphlpapi.lib;Winmm.lib;librtmp.lib;libmp3lame-static.lib;libfaac.lib;dsound.lib;obsapi.lib;shell32.lib;gdiplus.lib;mfplat.lib;Mfuuid.lib;Winhttp.lib;libx264.lib;UxTheme.lib;%(AdditionalDependencies) - libmfx/$(Platform)/$(Configuration);OBSApi/Release;x264/libs/32bit;librtmp/release;lame/output/32bit;libfaac/release;%(AdditionalLibraryDirectories) + OBSApi/Release;x264/libs/32bit;librtmp/release;lame/output/32bit;libfaac/release;%(AdditionalLibraryDirectories) type=%27Win32%27 name=%27Microsoft.Windows.Common-Controls%27 version=%276.0.0.0%27 processorArchitecture=%27X86%27 publicKeyToken=%276595b64144ccf1df%27 language=%27*%27;%(AdditionalManifestDependencies) true rundir\pdb32\$(TargetName).pdb @@ -218,10 +218,10 @@ Main.h - Avrt.lib;dwmapi.lib;comctl32.lib;dxgi.lib;dxguid.lib;d3d10_1.lib;d3dx10.lib;ws2_32.lib;Iphlpapi.lib;Winmm.lib;librtmp.lib;libmp3lame-static.lib;libfaac.lib;dsound.lib;obsapi.lib;shell32.lib;gdiplus.lib;mfplat.lib;Mfuuid.lib;Winhttp.lib;libx264.lib;UxTheme.lib;libmfx.lib;%(AdditionalDependencies) + Avrt.lib;dwmapi.lib;comctl32.lib;dxgi.lib;dxguid.lib;d3d10_1.lib;d3dx10.lib;ws2_32.lib;Iphlpapi.lib;Winmm.lib;librtmp.lib;libmp3lame-static.lib;libfaac.lib;dsound.lib;obsapi.lib;shell32.lib;gdiplus.lib;mfplat.lib;Mfuuid.lib;Winhttp.lib;libx264.lib;UxTheme.lib;%(AdditionalDependencies) - libmfx/$(Platform)/$(Configuration);OBSApi/x64/Release;x264/libs/64bit;librtmp/x64/release;lame/output/64bit;libfaac/x64/release;%(AdditionalLibraryDirectories) + OBSApi/x64/Release;x264/libs/64bit;librtmp/x64/release;lame/output/64bit;libfaac/x64/release;%(AdditionalLibraryDirectories) type=%27win32%27 name=%27Microsoft.Windows.Common-Controls%27 version=%276.0.0.0%27 processorArchitecture=%27amd64%27 publicKeyToken=%276595b64144ccf1df%27 language=%27*%27;%(AdditionalManifestDependencies) true rundir\pdb64\$(TargetName).pdb diff --git a/QSVHelper/Encoder.h b/QSVHelper/Encoder.h new file mode 100644 index 00000000..a18526af --- /dev/null +++ b/QSVHelper/Encoder.h @@ -0,0 +1,436 @@ +/******************************************************************************** + Copyright (C) 2013 Ruwen Hahn + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +********************************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include + +#include "d3d11_allocator.h" +#include "d3d11_device.h" + +#include "IPCInfo.h" +#include "IPCStructs.h" +#include "SupportStuff.h" +#include "WindowsStuff.h" + +struct Encoder +{ + bool use_cbr; + + bool first_frame; + + unsigned frame_time_ms; + + int exit_code; + + mfxIMPL requested, actual; + mfxVersion version; + + + Parameters params; + mfxFrameAllocRequest req; + mfxFrameAllocResponse alloc_res; + + + bool using_d3d11; + CD3D11Device d3d11; + D3D11FrameAllocator d3d11_alloc; + + + MFXVideoSession session; + MFXVideoENCODE encoder; + + + std::wstring event_prefix; + + ipc_bitstream_buff bitstream; + ipc_filled_bitstream filled_bitstream; + ipc_bitstream_info bs_info; + + ipc_frame_buff frame_buff; + ipc_frame_buff_status frame_buff_status; + ipc_frame_queue frame_queue; + + ipc_sps_buff sps_buffer; + ipc_pps_buff pps_buffer; + ipc_spspps_size spspps_queried_size; + + + std::vector encode_tasks; + std::queue idle_tasks, queued_tasks, encoded_tasks; + + std::vector surfaces; + std::queue idle_surfaces; + std::vector> msdk_locked_tasks; + + std::vector frames; + + + EncodeCtrl keyframe_ctrl, sei_ctrl; + + + std::wofstream &log_file; + + + operator bool() { return static_cast(session) != nullptr; } + + Encoder(IPCSignalledType &init_req, std::wstring event_prefix, std::wofstream &log_file) + : use_cbr(init_req->use_cbr), first_frame(true), frame_time_ms(static_cast(1./init_req->fps*1000)), exit_code(0) + , using_d3d11(false), session(), encoder(session), event_prefix(event_prefix), log_file(log_file) + { + params.Init(init_req->fps, init_req->keyint, init_req->bframes, init_req->width, init_req->height, init_req->max_bitrate, init_req->buffer_size, init_req->use_cbr); + params.SetVideoSignalInfo(init_req->full_range, init_req->primaries, init_req->transfer, init_req->matrix); + } + + template + mfxStatus InitializeMFX(T& impl, bool force=false) + { + session.Close(); + + version = impl.version; + requested = impl.type | impl.intf; + auto result = session.Init(requested, &version); + if(result < 0) return result; + + session.QueryIMPL(&actual); + + if(using_d3d11 = (actual & MFX_IMPL_VIA_D3D11) == MFX_IMPL_VIA_D3D11) + { + mfxU32 device = 0; + switch(MFX_IMPL_BASETYPE(actual)) + { + case MFX_IMPL_HARDWARE: device = 0; break; + case MFX_IMPL_HARDWARE2: device = 1; break; + case MFX_IMPL_HARDWARE3: device = 2; break; + case MFX_IMPL_HARDWARE4: device = 3; break; + default: exit_code = 1000; return MFX_ERR_DEVICE_FAILED; + } + + d3d11.Init(nullptr, 1, device); + mfxHDL hdl = nullptr; + d3d11.GetHandle(MFX_HANDLE_D3D11_DEVICE, &hdl); + session.SetHandle(MFX_HANDLE_D3D11_DEVICE, hdl); + + D3D11AllocatorParams alloc_params; + alloc_params.pDevice = reinterpret_cast(hdl); + d3d11_alloc.Init(&alloc_params); + session.SetFrameAllocator(&d3d11_alloc); + params->IOPattern = MFX_IOPATTERN_IN_VIDEO_MEMORY; + } + + encoder = MFXVideoENCODE(session); + + zero(req); + result = encoder.QueryIOSurf(¶ms, &req); + return result; + } + + void InitializeBuffers(ipc_init_response &init_res) + { + using namespace std; + Parameters query = params; + encoder.GetVideoParam(query); + + unsigned num_bitstreams = max(6, req.NumFrameSuggested + query->AsyncDepth)+3, //+NUM_OUT_BUFFERS + num_surf = num_bitstreams * (using_d3d11 ? 2 : 1), + num_frames = using_d3d11 ? num_bitstreams : num_surf, + num_d3d11_frames = num_surf; + + encode_tasks.resize(num_bitstreams); + + const unsigned bs_size = (max(query->mfx.BufferSizeInKB*1000, params->mfx.BufferSizeInKB*1000)+31)/32*32; + params->mfx.BufferSizeInKB = bs_size/1000; + init_res->bitstream_size = bs_size; + + bitstream = ipc_bitstream_buff(event_prefix + BITSTREAM_BUFF, encode_tasks.size() * bs_size + 31); + mfxU8 *bs_start = (mfxU8*)(((size_t)&bitstream + 31)/32*32); + size_t index = 0; + for(auto task = begin(encode_tasks); task != end(encode_tasks); task++, index++) + { + task->Init(bs_start, bs_size); + idle_tasks.push(index); + bs_start += bs_size; + } + + filled_bitstream = ipc_filled_bitstream(event_prefix + FILLED_BITSTREAM); + { + auto lock = lock_mutex(filled_bitstream); + *filled_bitstream = -1; + } + + bs_info = ipc_bitstream_info(event_prefix + BITSTREAM_INFO, encode_tasks.size()); + + + if(using_d3d11) + { + req.NumFrameSuggested = num_d3d11_frames; + d3d11_alloc.AllocFrames(&req, &alloc_res); + } + + mfxFrameInfo &fi = params->mfx.FrameInfo; + + surfaces.resize(num_surf); + for(size_t i = 0; i < surfaces.size(); i++) + { + idle_surfaces.emplace(&surfaces[i]); + memcpy(&surfaces[i].Info, &fi, sizeof(fi)); + if(using_d3d11) + surfaces[i].Data.MemId = alloc_res.mids[i]; + } + + const unsigned lum_channel_size = fi.Width*fi.Height, + uv_channel_size = fi.Width*fi.Height, + frame_size = lum_channel_size + uv_channel_size; + init_res->frame_size = frame_size; + init_res->UV_offset = lum_channel_size; + init_res->V_offset = lum_channel_size+1; + init_res->frame_pitch = fi.Width; + + frames.resize(num_frames); + frame_queue = ipc_frame_queue(event_prefix + FRAME_QUEUE, frames.size()); + { + auto lock = lock_mutex(frame_queue); + zero(*static_cast(frame_queue), sizeof(queued_frame) * frame_queue.size); + } + + frame_buff = ipc_frame_buff(event_prefix + FRAME_BUFF, frames.size() * frame_size + 15); + mfxU8 *frame_start = (mfxU8*)(((size_t)&frame_buff + 15)/16*16); + zero(*frame_start, frame_size * frames.size()); + for(auto frame = begin(frames); frame != end(frames); frame++) + { + InitFrame(*frame, frame_start, frame_start + init_res->UV_offset, frame_start + init_res->V_offset, fi.Width); + frame_start += frame_size; + } + + frame_buff_status = ipc_frame_buff_status(event_prefix + FRAME_BUFF_STATUS, frames.size()); + { + auto lock = lock_mutex(frame_buff_status); + zero(frame_buff_status[0], frames.size() * sizeof(uint32_t)); + } + + init_res->target_usage = params->mfx.TargetUsage; + init_res->bitstream_num = encode_tasks.size(); + init_res->frame_num = frames.size(); + + keyframe_ctrl.ctrl.FrameType = MFX_FRAMETYPE_I | MFX_FRAMETYPE_REF | MFX_FRAMETYPE_IDR; + sei_ctrl.AddSEIData(EncodeCtrl::SEI_USER_DATA_UNREGISTERED, InitSEIUserData(use_cbr, query, init_res->version)); + } + + mfxStatus InitializeEncoder() + { + return encoder.Init(params); + } + + void RequestSPSPPS() + { + sps_buffer = ipc_sps_buff(event_prefix + SPS_BUFF, 100); + pps_buffer = ipc_pps_buff(event_prefix + PPS_BUFF, 100); + Parameters spspps_query; + spspps_query.SetCodingOptionSPSPPS(sps_buffer, sps_buffer.size, pps_buffer, pps_buffer.size); + encoder.GetVideoParam(spspps_query); + spspps_queried_size = ipc_spspps_size(event_prefix + SPSPPS_SIZES); + spspps_queried_size->sps_size = spspps_query.cospspps.SPSBufSize; + spspps_queried_size->pps_size = spspps_query.cospspps.PPSBufSize; + spspps_queried_size.signal(); + } + + void ProcessEncodedFrame() + { + if(encoded_tasks.size()) + { + encode_task& task = encode_tasks[encoded_tasks.front()]; + auto& sp = task.sp; + + auto result = MFXVideoCORE_SyncOperation(session, sp, 0); + if(result == MFX_WRN_IN_EXECUTION) + return; + + bitstream_info &info = bs_info[encoded_tasks.front()]; + info.time_stamp = task.bs.TimeStamp; + info.data_length = task.bs.DataLength; + info.data_offset = task.bs.DataOffset; + info.pic_struct = task.bs.PicStruct; + info.frame_type = task.bs.FrameType; + + { + auto lock = lock_mutex(filled_bitstream); + if(*filled_bitstream >= 0) + return; + *filled_bitstream = encoded_tasks.front(); + } + filled_bitstream.signal(); + + msdk_locked_tasks.emplace_back(std::make_pair(task.surf, task.frame_index)); + task.surf = nullptr; + idle_tasks.emplace(encoded_tasks.front()); + encoded_tasks.pop(); + } + } + + void UnlockSurfaces() + { + for(size_t i = 0; i < msdk_locked_tasks.size();) + { + auto pair = msdk_locked_tasks[i]; + if(pair.first->Data.Locked) + { + i += 1; + continue; + } + + msdk_locked_tasks.erase(std::begin(msdk_locked_tasks)+i); + + idle_surfaces.emplace(pair.first); + + if(!using_d3d11) + { + auto lock = lock_mutex(frame_buff_status); + frame_buff_status[pair.second] -= 1; + } + } + } + + void QueueTask() + { + using namespace std; + + for(;;) + { + if(idle_tasks.empty()) + { + log_file << "Warning: idle_tasks is empty (" << idle_tasks.size() << " idle, " << queued_tasks.size() << " queued, " + << encoded_tasks.size() << " encoded, " << msdk_locked_tasks.size() << " locked)\n"; + return; + } + + if(idle_surfaces.empty()) + { + log_file << "Warning: idle_surfaces is empty (" << idle_tasks.size() << " idle, " << queued_tasks.size() << " queued, " + << encoded_tasks.size() << " encoded, " << msdk_locked_tasks.size() << " locked)\n"; + return; + } + + auto end = static_cast(frame_queue)+frame_queue.size; + auto lock = lock_mutex(frame_queue); + auto oldest = min_element(static_cast(frame_queue), end, [](const queued_frame &f1, const queued_frame &f2) -> bool + { + if(f1.is_new) + if(f2.is_new) + return f1.timestamp < f2.timestamp; + else + return true; + return false; + }); + if(!oldest || !oldest->is_new) + return; + + oldest->is_new = false; + + auto index = idle_tasks.front(); + queued_tasks.push(index); + idle_tasks.pop(); + + encode_task &task = encode_tasks[index]; + task.bs.DataLength = 0; + task.bs.DataOffset = 0; + + task.surf = idle_surfaces.front(); + idle_surfaces.pop(); + + mfxFrameData &frame = frames[oldest->frame_index]; + if(using_d3d11) + { + d3d11_alloc.LockFrame(task.surf->Data.MemId, &task.surf->Data); + for(size_t i = 0; i < task.surf->Info.Height; i++) + memcpy(task.surf->Data.Y+i*task.surf->Data.Pitch, frame.Y+i*frame.Pitch, task.surf->Info.Width); + for(size_t i = 0; i < (task.surf->Info.Height/2u); i++) + memcpy(task.surf->Data.UV+i*task.surf->Data.Pitch, frame.UV+i*frame.Pitch, task.surf->Info.Width); + d3d11_alloc.UnlockFrame(task.surf->Data.MemId, &task.surf->Data); + auto lock = lock_mutex(frame_buff_status); + frame_buff_status[oldest->frame_index] -= 1; + } + else + { + task.surf->Data.Y = frame.Y; + task.surf->Data.UV = frame.UV; + task.surf->Data.V = frame.V; + task.surf->Data.Pitch = frame.Pitch; + } + task.surf->Data.TimeStamp = oldest->timestamp; + task.frame_index = oldest->frame_index; + + if(oldest->request_keyframe) + task.ctrl = &keyframe_ctrl; + else + task.ctrl = nullptr; + + if(first_frame) + task.ctrl = &sei_ctrl; + first_frame = false; + } + } + + void EncodeTasks() + { + while(queued_tasks.size()) + { + encode_task& task = encode_tasks[queued_tasks.front()]; + for(;;) + { + auto sts = encoder.EncodeFrameAsync(task.ctrl, task.surf, &task.bs, &task.sp); + + if(sts == MFX_ERR_NONE || (MFX_ERR_NONE < sts && task.sp)) + break; + if(sts == MFX_WRN_DEVICE_BUSY) + return; + if(sts == MFX_ERR_NOT_INITIALIZED) //returned after encoder.Init returns PARTIAL_ACCELERATION? + { + exit_code = EXIT_INCOMPATIBLE_CONFIGURATION; + return; + } + //if(!sp); //sts == MFX_ERR_MORE_DATA usually; retry the call (see MSDK examples) + //Log(TEXT("returned status %i, %u"), sts, insert); + } + encoded_tasks.push(queued_tasks.front()); + queued_tasks.pop(); + } + } + + int EncodeLoop(IPCSignal &stop, safe_handle &obs_handle) + { + IPCWaiter waiter; + waiter.push_back(stop.signal_); + waiter.push_back(obs_handle); + waiter.push_back(frame_queue.signal_); + + for(;;) + { + if(waiter.wait_for_two(0, 1, frame_time_ms/2) || exit_code) + return exit_code; + ProcessEncodedFrame(); + UnlockSurfaces(); + QueueTask(); + EncodeTasks(); + } + } +}; \ No newline at end of file diff --git a/QSVHelper/IPCInfo.h b/QSVHelper/IPCInfo.h new file mode 100644 index 00000000..4f6a5126 --- /dev/null +++ b/QSVHelper/IPCInfo.h @@ -0,0 +1,64 @@ +/******************************************************************************** + Copyright (C) 2013 Ruwen Hahn + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +********************************************************************************/ + +#pragma once + +#include + +#include "IPCStructs.h" +#include "WindowsStuff.h" + + +#define INIT_REQUEST L"init_request" +typedef IPCSignalledType ipc_init_request; + +#define INIT_RESPONSE L"init_response" +typedef IPCSignalledType ipc_init_response; + +#define BITSTREAM_BUFF L"bitstream_buff" +typedef NamedSharedMemory ipc_bitstream_buff; + +#define FILLED_BITSTREAM L"filled_bitstream" +typedef IPCLockedSignalledType ipc_filled_bitstream; + +#define BITSTREAM_INFO L"bitstream_info" +typedef IPCArray ipc_bitstream_info; + +#define FRAME_BUFF L"frame_buff" +typedef NamedSharedMemory ipc_frame_buff; + +#define FRAME_BUFF_STATUS L"frame_buff_status" +typedef IPCLockedSignalledArray ipc_frame_buff_status; + +#define FRAME_QUEUE L"frame_queue" +typedef IPCLockedSignalledArray ipc_frame_queue; + +#define SPS_BUFF L"sps_buff" +typedef IPCArray ipc_sps_buff; + +#define PPS_BUFF L"pps_buff" +typedef IPCArray ipc_pps_buff; + +#define SPSPPS_SIZES L"spspps_size" +typedef IPCSignalledType ipc_spspps_size; + +#define STOP_REQUEST L"stop" +typedef IPCSignal ipc_stop; + + +#define EXIT_INCOMPATIBLE_CONFIGURATION 10 \ No newline at end of file diff --git a/QSVHelper/IPCStructs.h b/QSVHelper/IPCStructs.h new file mode 100644 index 00000000..554c0b09 --- /dev/null +++ b/QSVHelper/IPCStructs.h @@ -0,0 +1,79 @@ +/******************************************************************************** + Copyright (C) 2013 Ruwen Hahn + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +********************************************************************************/ + +#pragma once + +#include + +#include + +#pragma pack(push) +#pragma pack(1) + +struct init_request +{ + enum { MODE_QUERY, MODE_ENCODE } mode; + uint32_t obs_process_id; + int32_t fps, keyint, bframes, width, height, max_bitrate, buffer_size; + bool use_cbr; + int32_t full_range, matrix, primaries, transfer; + bool use_custom_impl; + mfxVersion custom_version; + mfxIMPL custom_impl, custom_intf; +}; + +struct init_response +{ + mfxU16 target_usage; + mfxVersion version; + mfxIMPL requested_impl, + actual_impl; + + bool using_custom_impl; + + uint16_t bitstream_num, + frame_num; + uint32_t bitstream_size, + frame_size, + UV_offset, + V_offset, + frame_pitch; +}; + +struct spspps_size +{ + mfxU16 sps_size, + pps_size; +}; + +struct queued_frame +{ + bool is_new; + bool request_keyframe; + mfxU64 timestamp; + uint32_t frame_index; +}; + +struct bitstream_info +{ + mfxU64 time_stamp; + mfxU32 data_offset, data_length; + mfxU16 pic_struct, frame_type; +}; + +#pragma pack(pop) \ No newline at end of file diff --git a/QSVHelper/IntelSupport/include/base_allocator.h b/QSVHelper/IntelSupport/include/base_allocator.h new file mode 100644 index 00000000..19aa333d --- /dev/null +++ b/QSVHelper/IntelSupport/include/base_allocator.h @@ -0,0 +1,177 @@ +/* ****************************************************************************** *\ + +INTEL CORPORATION PROPRIETARY INFORMATION +This software is supplied under the terms of a license agreement or nondisclosure +agreement with Intel Corporation and may not be copied or disclosed except in +accordance with the terms of that agreement +Copyright(c) 2008-2012 Intel Corporation. All Rights Reserved. + +\* ****************************************************************************** */ + +#ifndef __BASE_ALLOCATOR_H__ +#define __BASE_ALLOCATOR_H__ + +#include +#include +#include +#include "mfxvideo.h" + +struct mfxAllocatorParams +{ + virtual ~mfxAllocatorParams(){}; +}; + +// this class implements methods declared in mfxFrameAllocator structure +// simply redirecting them to virtual methods which should be overridden in derived classes +class MFXFrameAllocator : public mfxFrameAllocator +{ +public: + MFXFrameAllocator(); + virtual ~MFXFrameAllocator(); + + // optional method, override if need to pass some parameters to allocator from application + virtual mfxStatus Init(mfxAllocatorParams *pParams) = 0; + virtual mfxStatus Close() = 0; + + virtual mfxStatus AllocFrames(mfxFrameAllocRequest *request, mfxFrameAllocResponse *response) = 0; + virtual mfxStatus LockFrame(mfxMemId mid, mfxFrameData *ptr) = 0; + virtual mfxStatus UnlockFrame(mfxMemId mid, mfxFrameData *ptr) = 0; + virtual mfxStatus GetFrameHDL(mfxMemId mid, mfxHDL *handle) = 0; + virtual mfxStatus FreeFrames(mfxFrameAllocResponse *response) = 0; + +private: + static mfxStatus MFX_CDECL Alloc_(mfxHDL pthis, mfxFrameAllocRequest *request, mfxFrameAllocResponse *response); + static mfxStatus MFX_CDECL Lock_(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr); + static mfxStatus MFX_CDECL Unlock_(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr); + static mfxStatus MFX_CDECL GetHDL_(mfxHDL pthis, mfxMemId mid, mfxHDL *handle); + static mfxStatus MFX_CDECL Free_(mfxHDL pthis, mfxFrameAllocResponse *response); +}; + +// This class implements basic logic of memory allocator +// Manages responses for different components according to allocation request type +// External frames of a particular component-related type are allocated in one call +// Further calls return previously allocated response. +// Ex. Preallocated frame chain with type=FROM_ENCODE | FROM_VPPIN will be returned when +// request type contains either FROM_ENCODE or FROM_VPPIN + +// This class does not allocate any actual memory +class BaseFrameAllocator: public MFXFrameAllocator +{ +public: + BaseFrameAllocator(); + virtual ~BaseFrameAllocator(); + + virtual mfxStatus Init(mfxAllocatorParams *pParams) = 0; + virtual mfxStatus Close(); + virtual mfxStatus AllocFrames(mfxFrameAllocRequest *request, mfxFrameAllocResponse *response); + virtual mfxStatus FreeFrames(mfxFrameAllocResponse *response); + +protected: + typedef std::list::iterator Iter; + static const mfxU32 MEMTYPE_FROM_MASK = MFX_MEMTYPE_FROM_ENCODE | MFX_MEMTYPE_FROM_DECODE | MFX_MEMTYPE_FROM_VPPIN | MFX_MEMTYPE_FROM_VPPOUT; + + struct UniqueResponse + : mfxFrameAllocResponse + { + mfxU16 m_cropw; + mfxU16 m_croph; + mfxU32 m_refCount; + mfxU16 m_type; + + UniqueResponse() + { + memset(this, 0, sizeof(*this)); + } + + // compare responses by actual frame size, alignment (w and h) is up to application + UniqueResponse(const mfxFrameAllocResponse & response, mfxU16 cropw, mfxU16 croph, mfxU16 type) + : mfxFrameAllocResponse(response) + , m_type(type) + , m_refCount(1) + , m_cropw(cropw) + , m_croph(croph) + { + } + //compare by resolution + bool operator () (const UniqueResponse &response)const + { + return m_cropw == response.m_cropw && m_croph == response.m_croph; + } + }; + + std::list m_responses; + std::list m_ExtResponses; + + struct IsSame + : public std::binary_function + { + bool operator () (const mfxFrameAllocResponse & l, const mfxFrameAllocResponse &r)const + { + return r.mids != 0 && l.mids != 0 && + r.mids[0] == l.mids[0] && + r.NumFrameActual == l.NumFrameActual; + } + }; + + // checks if request is supported + virtual mfxStatus CheckRequestType(mfxFrameAllocRequest *request); + + // frees memory attached to response + virtual mfxStatus ReleaseResponse(mfxFrameAllocResponse *response) = 0; + // allocates memory + virtual mfxStatus AllocImpl(mfxFrameAllocRequest *request, mfxFrameAllocResponse *response) = 0; + + template + class safe_array + { + public: + safe_array(T *ptr = 0):m_ptr(ptr) + { // construct from object pointer + }; + ~safe_array() + { + reset(0); + } + T* get() + { // return wrapped pointer + return m_ptr; + } + T* release() + { // return wrapped pointer and give up ownership + T* ptr = m_ptr; + m_ptr = 0; + return ptr; + } + void reset(T* ptr) + { // destroy designated object and store new pointer + if (m_ptr) + { + delete[] m_ptr; + } + m_ptr = ptr; + } + protected: + T* m_ptr; // the wrapped object pointer + }; +}; + +class MFXBufferAllocator : public mfxBufferAllocator +{ +public: + MFXBufferAllocator(); + virtual ~MFXBufferAllocator(); + + virtual mfxStatus AllocBuffer(mfxU32 nbytes, mfxU16 type, mfxMemId *mid) = 0; + virtual mfxStatus LockBuffer(mfxMemId mid, mfxU8 **ptr) = 0; + virtual mfxStatus UnlockBuffer(mfxMemId mid) = 0; + virtual mfxStatus FreeBuffer(mfxMemId mid) = 0; + +private: + static mfxStatus MFX_CDECL Alloc_(mfxHDL pthis, mfxU32 nbytes, mfxU16 type, mfxMemId *mid); + static mfxStatus MFX_CDECL Lock_(mfxHDL pthis, mfxMemId mid, mfxU8 **ptr); + static mfxStatus MFX_CDECL Unlock_(mfxHDL pthis, mfxMemId mid); + static mfxStatus MFX_CDECL Free_(mfxHDL pthis, mfxMemId mid); +}; + + +#endif // __BASE_ALLOCATOR_H__ \ No newline at end of file diff --git a/QSVHelper/IntelSupport/include/current_date.h b/QSVHelper/IntelSupport/include/current_date.h new file mode 100644 index 00000000..2dd2d029 --- /dev/null +++ b/QSVHelper/IntelSupport/include/current_date.h @@ -0,0 +1,18 @@ + +#define CURRENT_DATE "Build_2013/6/27" + +#define PRODUCT_NAME "Intel® Media SDK" + +#define FILE_VERSION 4,13,6,27 + +#define FILE_VERSION_STRING "4,13,6,27" + +#define FILTER_NAME_PREFIX "Intel® Media SDK" + +#define FILTER_NAME_SUFFIX "" + +#define PRODUCT_COPYRIGHT "Copyright© 2003-2013 Intel Corporation" + +#define PRODUCT_VERSION 4,0,760,0 + +#define PRODUCT_VERSION_STRING "4,0,760,0" diff --git a/QSVHelper/IntelSupport/include/d3d11_allocator.h b/QSVHelper/IntelSupport/include/d3d11_allocator.h new file mode 100644 index 00000000..2a6029da --- /dev/null +++ b/QSVHelper/IntelSupport/include/d3d11_allocator.h @@ -0,0 +1,253 @@ +/* ****************************************************************************** *\ + +INTEL CORPORATION PROPRIETARY INFORMATION +This software is supplied under the terms of a license agreement or nondisclosure +agreement with Intel Corporation and may not be copied or disclosed except in +accordance with the terms of that agreement +Copyright(c) 2011-2013 Intel Corporation. All Rights Reserved. + +\* ****************************************************************************** */ + +#ifndef __D3D11_ALLOCATOR_H__ +#define __D3D11_ALLOCATOR_H__ + +// defines MFX_D3D11_SUPPORT +#include "sample_defs.h" + +#include "base_allocator.h" +#include + +//application can provide either generic mid from surface or this wrapper +//wrapper distinguishes from generic mid by highest 1 bit +//if it set then remained pointer points to extended structure of memid +//64 bits system layout +/*----+-----------------------------------------------------------+ +|b63=1|63 bits remained for pointer to extended structure of memid| +|b63=0|63 bits from original mfxMemId | ++-----+----------------------------------------------------------*/ +//32 bits system layout +/*--+---+--------------------------------------------+ +|b31=1|31 bits remained for pointer to extended memid| +|b31=0|31 bits remained for surface pointer | ++---+---+-------------------------------------------*/ +//#pragma warning (disable:4293) +class MFXReadWriteMid +{ + static const uintptr_t bits_offset = std::numeric_limits::digits - 1; + static const uintptr_t clear_mask = ~((uintptr_t)1 << bits_offset); +public: + enum + { + //if flag not set it means that read and write + not_set = 0, + reuse = 1, + read = 2, + write = 4, + }; + //here mfxmemid might be as MFXReadWriteMid or mfxMemId memid + MFXReadWriteMid(mfxMemId mid, mfxU8 flag = not_set) + { + //setup mid + m_mid_to_report = (mfxMemId)((uintptr_t)&m_mid | ((uintptr_t)1 << bits_offset)); + if (0 != ((uintptr_t)mid >> bits_offset)) + { + //it points to extended structure + mfxMedIdEx * pMemIdExt = reinterpret_cast((uintptr_t)mid & clear_mask); + m_mid.pId = pMemIdExt->pId; + if (reuse == flag) + { + m_mid.read_write = pMemIdExt->read_write; + } + else + { + m_mid.read_write = flag; + } + } + else + { + m_mid.pId = mid; + if (reuse == flag) + m_mid.read_write = not_set; + else + m_mid.read_write = flag; + } + + } + bool isRead() const + { + return 0 != (m_mid.read_write & read) || !m_mid.read_write; + } + bool isWrite() const + { + return 0 != (m_mid.read_write & write) || !m_mid.read_write; + } + /// returns original memid without read write flags + mfxMemId raw() const + { + return m_mid.pId; + } + operator mfxMemId() const + { + return m_mid_to_report; + } + +private: + struct mfxMedIdEx + { + mfxMemId pId; + mfxU8 read_write; + }; + + mfxMedIdEx m_mid; + mfxMemId m_mid_to_report; +}; + +#if MFX_D3D11_SUPPORT + +#include +#include +#include + +struct ID3D11VideoDevice; +struct ID3D11VideoContext; + +struct D3D11AllocatorParams : mfxAllocatorParams +{ + ID3D11Device *pDevice; + bool bUseSingleTexture; + DWORD uncompressedResourceMiscFlags; + + D3D11AllocatorParams() + : pDevice() + , bUseSingleTexture() + , uncompressedResourceMiscFlags() + { + } +}; + +class D3D11FrameAllocator: public BaseFrameAllocator +{ +public: + + D3D11FrameAllocator(); + virtual ~D3D11FrameAllocator(); + + virtual mfxStatus Init(mfxAllocatorParams *pParams); + virtual mfxStatus Close(); + virtual ID3D11Device * GetD3D11Device() + { + return m_initParams.pDevice; + }; + virtual mfxStatus LockFrame(mfxMemId mid, mfxFrameData *ptr); + virtual mfxStatus UnlockFrame(mfxMemId mid, mfxFrameData *ptr); + virtual mfxStatus GetFrameHDL(mfxMemId mid, mfxHDL *handle); + +protected: + static DXGI_FORMAT ConverColortFormat(mfxU32 fourcc); + virtual mfxStatus CheckRequestType(mfxFrameAllocRequest *request); + virtual mfxStatus ReleaseResponse(mfxFrameAllocResponse *response); + virtual mfxStatus AllocImpl(mfxFrameAllocRequest *request, mfxFrameAllocResponse *response); + + D3D11AllocatorParams m_initParams; + ID3D11DeviceContext *m_pDeviceContext; + + struct TextureResource + { + std::vector outerMids; + std::vector textures; + std::vector stagingTexture; + bool bAlloc; + + TextureResource() + : bAlloc(true) + { + } + + static bool isAllocated (TextureResource & that) + { + return that.bAlloc; + } + ID3D11Texture2D* GetTexture(mfxMemId id) + { + if (outerMids.empty()) + return NULL; + + return textures[((uintptr_t)id - (uintptr_t)outerMids.front()) % textures.size()]; + } + UINT GetSubResource(mfxMemId id) + { + if (outerMids.empty()) + return NULL; + + return (UINT)(((uintptr_t)id - (uintptr_t)outerMids.front()) / textures.size()); + } + void Release() + { + size_t i = 0; + for(i = 0; i < textures.size(); i++) + { + textures[i]->Release(); + } + textures.clear(); + + for(i = 0; i < stagingTexture.size(); i++) + { + stagingTexture[i]->Release(); + } + stagingTexture.clear(); + + //marking texture as deallocated + bAlloc = false; + } + }; + class TextureSubResource + { + TextureResource * m_pTarget; + ID3D11Texture2D * m_pTexture; + ID3D11Texture2D * m_pStaging; + UINT m_subResource; + public: + TextureSubResource(TextureResource * pTarget = NULL, mfxMemId id = 0) + : m_pTarget(pTarget) + , m_pTexture() + , m_subResource() + , m_pStaging(NULL) + { + if (NULL != m_pTarget && !m_pTarget->outerMids.empty()) + { + ptrdiff_t idx = (uintptr_t)MFXReadWriteMid(id).raw() - (uintptr_t)m_pTarget->outerMids.front(); + m_pTexture = m_pTarget->textures[idx % m_pTarget->textures.size()]; + m_subResource = (UINT)(idx / m_pTarget->textures.size()); + m_pStaging = m_pTarget->stagingTexture.empty() ? NULL : m_pTarget->stagingTexture[idx]; + } + } + ID3D11Texture2D* GetStaging()const + { + return m_pStaging; + } + ID3D11Texture2D* GetTexture()const + { + return m_pTexture; + } + UINT GetSubResource()const + { + return m_subResource; + } + void Release() + { + if (NULL != m_pTarget) + m_pTarget->Release(); + } + }; + + TextureSubResource GetResourceFromMid(mfxMemId); + + std::list m_resourcesByRequest;//each alloc request generates new item in list + + typedef std::list ::iterator referenceType; + std::vector m_memIdMap; +}; + +#endif // #if MFX_D3D11_SUPPORT + +#endif // __D3D11_ALLOCATOR_H__ \ No newline at end of file diff --git a/QSVHelper/IntelSupport/include/d3d11_device.h b/QSVHelper/IntelSupport/include/d3d11_device.h new file mode 100644 index 00000000..a1675ffa --- /dev/null +++ b/QSVHelper/IntelSupport/include/d3d11_device.h @@ -0,0 +1,67 @@ +/* ****************************************************************************** *\ + +INTEL CORPORATION PROPRIETARY INFORMATION +This software is supplied under the terms of a license agreement or nondisclosure +agreement with Intel Corporation and may not be copied or disclosed except in +accordance with the terms of that agreement +Copyright(c) 2011 - 2012 Intel Corporation. All Rights Reserved. + +\* ****************************************************************************** */ + +#pragma once + +#include "sample_defs.h" // defines MFX_D3D11_SUPPORT + +#if MFX_D3D11_SUPPORT +#include "hw_device.h" +#include +#include +#include + +#include + +class CD3D11Device: public CHWDevice +{ +public: + CD3D11Device(); + virtual ~CD3D11Device(); + virtual mfxStatus Init( + mfxHDL hWindow, + mfxU16 nViews, + mfxU32 nAdapterNum); + virtual mfxStatus Reset(); + virtual mfxStatus GetHandle(mfxHandleType type, mfxHDL *pHdl); + virtual mfxStatus SetHandle(mfxHandleType type, mfxHDL hdl); + virtual mfxStatus RenderFrame(mfxFrameSurface1 * pSurface, mfxFrameAllocator * pmfxAlloc); + virtual void Close(); +protected: + virtual mfxStatus FillSCD(mfxHDL hWindow, DXGI_SWAP_CHAIN_DESC& scd); + mfxStatus CreateVideoProcessor(mfxFrameSurface1 * pSrf); + + CComPtr m_pD3D11Device; + CComPtr m_pD3D11Ctx; + CComQIPtr m_pDX11VideoDevice; + CComQIPtr m_pVideoContext; + CComPtr m_VideoProcessorEnum; + + CComQIPtr m_pDXGIDev; + CComQIPtr m_pAdapter; + + CComPtr m_pDXGIFactory; + + CComPtr m_pSwapChain; + CComPtr m_pVideoProcessor; + +private: + CComPtr m_pInputViewLeft; + CComPtr m_pInputViewRight; + CComPtr m_pOutputView; + + CComPtr m_pDXGIBackBuffer; + CComPtr m_pTempTexture; + CComPtr m_pDisplayControl; + CComPtr m_pDXGIOutput; + mfxU16 m_nViews; + BOOL m_bDefaultStereoEnabled; +}; +#endif //#if MFX_D3D11_SUPPORT diff --git a/QSVHelper/IntelSupport/include/hw_device.h b/QSVHelper/IntelSupport/include/hw_device.h new file mode 100644 index 00000000..7dc368b9 --- /dev/null +++ b/QSVHelper/IntelSupport/include/hw_device.h @@ -0,0 +1,39 @@ +/* ****************************************************************************** *\ + +INTEL CORPORATION PROPRIETARY INFORMATION +This software is supplied under the terms of a license agreement or nondisclosure +agreement with Intel Corporation and may not be copied or disclosed except in +accordance with the terms of that agreement +Copyright(c) 2011 - 2012 Intel Corporation. All Rights Reserved. + +\* ****************************************************************************** */ + +#pragma once + +#include "mfxvideo++.h" + +/// Base class for hw device +class CHWDevice +{ +public: + virtual ~CHWDevice(){} + /** Initializes device for requested processing. + @param[in] hWindow Window handle to bundle device to. + @param[in] nViews Number of views to process. + @param[in] nAdapterNum Number of adapter to use + */ + virtual mfxStatus Init( + mfxHDL hWindow, + mfxU16 nViews, + mfxU32 nAdapterNum) = 0; + /// Reset device. + virtual mfxStatus Reset() = 0; + /// Get handle can be used for MFX session SetHandle calls + virtual mfxStatus GetHandle(mfxHandleType type, mfxHDL *pHdl) = 0; + /** Set handle. + Particular device implementation may require other objects to operate. + */ + virtual mfxStatus SetHandle(mfxHandleType type, mfxHDL hdl) = 0; + virtual mfxStatus RenderFrame(mfxFrameSurface1 * pSurface, mfxFrameAllocator * pmfxAlloc) = 0; + virtual void Close() = 0; +}; \ No newline at end of file diff --git a/QSVHelper/IntelSupport/include/sample_defs.h b/QSVHelper/IntelSupport/include/sample_defs.h new file mode 100644 index 00000000..d984e374 --- /dev/null +++ b/QSVHelper/IntelSupport/include/sample_defs.h @@ -0,0 +1,92 @@ +/* ////////////////////////////////////////////////////////////////////////////// */ +/* +// +// INTEL CORPORATION PROPRIETARY INFORMATION +// This software is supplied under the terms of a license agreement or +// nondisclosure agreement with Intel Corporation and may not be copied +// or disclosed except in accordance with the terms of that agreement. +// Copyright (c) 2005-2013 Intel Corporation. All Rights Reserved. +// +// +*/ + +#ifndef __SAMPLE_DEFS_H__ +#define __SAMPLE_DEFS_H__ + +#include + +#include "mfxdefs.h" +#include "vm/strings_defs.h" +//#include "vm/file_defs.h" + +#ifndef D3D_SURFACES_SUPPORT +#define D3D_SURFACES_SUPPORT 1 +#endif + +#if defined(_WIN32) && !defined(MFX_D3D11_SUPPORT) +#include +#if (NTDDI_VERSION >= NTDDI_VERSION_FROM_WIN32_WINNT2(0x0602)) // >= _WIN32_WINNT_WIN8 + #define MFX_D3D11_SUPPORT 1 // Enable D3D11 support if SDK allows +#else + #define MFX_D3D11_SUPPORT 0 +#endif +#endif //defined(WIN32) || defined(WIN64) + +//affects win32 winnt version macro +#include "vm/time_defs.h" +//#include "sample_utils.h" + + +#define MSDK_DEC_WAIT_INTERVAL 60000 +#define MSDK_ENC_WAIT_INTERVAL 10000 +#define MSDK_VPP_WAIT_INTERVAL 60000 +#define MSDK_WAIT_INTERVAL MSDK_DEC_WAIT_INTERVAL+3*MSDK_VPP_WAIT_INTERVAL+MSDK_ENC_WAIT_INTERVAL // an estimate for the longest pipeline we have in samples + +#define MSDK_INVALID_SURF_IDX 0xFFFF + +#define MSDK_MAX_FILENAME_LEN 1024 + +#define MSDK_PRINT_RET_MSG(ERR) {msdk_printf(MSDK_STRING("\nReturn on error: error code %d,\t%s\t%d\n\n"), ERR, MSDK_STRING(__FILE__), __LINE__);} + +#define MSDK_CHECK_ERROR(P, X, ERR) {if ((X) == (P)) {MSDK_PRINT_RET_MSG(ERR); return ERR;}} +#define MSDK_CHECK_NOT_EQUAL(P, X, ERR) {if ((X) != (P)) {MSDK_PRINT_RET_MSG(ERR); return ERR;}} +#define MSDK_CHECK_RESULT(P, X, ERR) {if ((X) > (P)) {MSDK_PRINT_RET_MSG(ERR); return ERR;}} +#define MSDK_CHECK_PARSE_RESULT(P, X, ERR) {if ((X) > (P)) {return ERR;}} +#define MSDK_CHECK_RESULT_SAFE(P, X, ERR, ADD) {if ((X) > (P)) {ADD; MSDK_PRINT_RET_MSG(ERR); return ERR;}} +#define MSDK_IGNORE_MFX_STS(P, X) {if ((X) == (P)) {P = MFX_ERR_NONE;}} +#define MSDK_CHECK_POINTER(P, ...) {if (!(P)) {return __VA_ARGS__;}} +#define MSDK_CHECK_POINTER_NO_RET(P) {if (!(P)) {return;}} +#define MSDK_CHECK_POINTER_SAFE(P, ERR, ADD) {if (!(P)) {ADD; return ERR;}} +#define MSDK_BREAK_ON_ERROR(P) {if (MFX_ERR_NONE != (P)) break;} +#define MSDK_SAFE_DELETE_ARRAY(P) {if (P) {delete[] P; P = NULL;}} +#define MSDK_SAFE_RELEASE(X) {if (X) { X->Release(); X = NULL; }} + +#ifndef MSDK_SAFE_DELETE +#define MSDK_SAFE_DELETE(P) {if (P) {delete P; P = NULL;}} +#endif // MSDK_SAFE_DELETE + +#define MSDK_ZERO_MEMORY(VAR) {memset(&VAR, 0, sizeof(VAR));} +#define MSDK_MAX(A, B) (((A) > (B)) ? (A) : (B)) +#define MSDK_MIN(A, B) (((A) < (B)) ? (A) : (B)) +#define MSDK_ALIGN16(value) (((value + 15) >> 4) << 4) // round up to a multiple of 16 +#define MSDK_ALIGN32(value) (((value + 31) >> 5) << 5) // round up to a multiple of 32 +#define MSDK_ALIGN(value, alignment) (alignment) * ( (value) / (alignment) + (((value) % (alignment)) ? 1 : 0)) +#define MSDK_ARRAY_LEN(value) (sizeof(value) / sizeof(value[0])) + +#define MSDK_MEMCPY_BITSTREAM(bitstream, offset, src, count) memcpy_s((bitstream).Data + (offset), (bitstream).MaxLength - (offset), (src), (count)) + +#define MSDK_MEMCPY_BUF(bufptr, offset, maxsize, src, count) memcpy_s((bufptr)+ (offset), (maxsize) - (offset), (src), (count)) + +#define MSDK_MEMCPY_VAR(dstVarName, src, count) memcpy_s(&(dstVarName), sizeof(dstVarName), (src), (count)) + +#ifndef UNREFERENCED_PARAMETER +#define UNREFERENCED_PARAMETER(par) (par) +#endif + +#ifndef MFX_PRODUCT_VERSION +#define MFX_PRODUCT_VERSION "4.0.760.60435" +#endif + +#define MSDK_SAMPLE_VERSION MSDK_STRING(MFX_PRODUCT_VERSION) + +#endif //__SAMPLE_DEFS_H__ diff --git a/QSVHelper/IntelSupport/include/vm/strings_defs.h b/QSVHelper/IntelSupport/include/vm/strings_defs.h new file mode 100644 index 00000000..5894dc9b --- /dev/null +++ b/QSVHelper/IntelSupport/include/vm/strings_defs.h @@ -0,0 +1,45 @@ +/* ////////////////////////////////////////////////////////////////////////////// */ +/* +// +// INTEL CORPORATION PROPRIETARY INFORMATION +// This software is supplied under the terms of a license agreement or +// nondisclosure agreement with Intel Corporation and may not be copied +// or disclosed except in accordance with the terms of that agreement. +// Copyright (c) 2011-2013 Intel Corporation. All Rights Reserved. +// +// +*/ + +#ifndef __STRING_DEFS_H__ +#define __STRING_DEFS_H__ + +#include +#include +#include + +#include + +#define MSDK_STRING(x) _T(x) +#define MSDK_CHAR(x) _T(x) +typedef TCHAR msdk_char; + +#define msdk_printf _tprintf +#define msdk_fprintf _ftprintf +#define msdk_sprintf _stprintf_s +#define msdk_vprintf _vtprintf +#define msdk_strlen _tcslen +#define msdk_strcmp _tcscmp +#define msdk_strncmp _tcsnicmp +#define msdk_strstr _tcsstr +#define msdk_sscanf _stscanf_s +#define msdk_atoi _ttoi +#define msdk_strtol _tcstol +#define msdk_strtod _tcstod +#define msdk_strchr _tcschr +#define msdk_itoa_decimal(value, str) _itow_s(value, str, 4, 10) + +// msdk_strcopy is intended to be used with 2 parmeters, i.e. msdk_strcopy(dst, src) +// for _tcscpy_s that's possible if DST is declared as: TCHAR DST[n]; +#define msdk_strcopy _tcscpy_s + +#endif //__STRING_DEFS_H__ diff --git a/QSVHelper/IntelSupport/include/vm/time_defs.h b/QSVHelper/IntelSupport/include/vm/time_defs.h new file mode 100644 index 00000000..fda81cd0 --- /dev/null +++ b/QSVHelper/IntelSupport/include/vm/time_defs.h @@ -0,0 +1,19 @@ +/* ****************************************************************************** *\ + +INTEL CORPORATION PROPRIETARY INFORMATION +This software is supplied under the terms of a license agreement or nondisclosure +agreement with Intel Corporation and may not be copied or disclosed except in +accordance with the terms of that agreement +Copyright(c) 2012 Intel Corporation. All Rights Reserved. + +\* ****************************************************************************** */ + +#ifndef __TIME_DEFS_H__ +#define __TIME_DEFS_H__ + +#include "mfxdefs.h" + +#include +#define MSDK_SLEEP(msec) Sleep(msec) + +#endif // #ifndef __TIME_DEFS_H__ diff --git a/QSVHelper/IntelSupport/src/base_allocator.cpp b/QSVHelper/IntelSupport/src/base_allocator.cpp new file mode 100644 index 00000000..e50065f8 --- /dev/null +++ b/QSVHelper/IntelSupport/src/base_allocator.cpp @@ -0,0 +1,263 @@ +/* ****************************************************************************** *\ + +INTEL CORPORATION PROPRIETARY INFORMATION +This software is supplied under the terms of a license agreement or nondisclosure +agreement with Intel Corporation and may not be copied or disclosed except in +accordance with the terms of that agreement +Copyright(c) 2008-2012 Intel Corporation. All Rights Reserved. + +\* ****************************************************************************** */ + +#include +#include +#include "base_allocator.h" + +MFXFrameAllocator::MFXFrameAllocator() +{ + pthis = this; + Alloc = Alloc_; + Lock = Lock_; + Free = Free_; + Unlock = Unlock_; + GetHDL = GetHDL_; +} + +MFXFrameAllocator::~MFXFrameAllocator() +{ +} + +mfxStatus MFXFrameAllocator::Alloc_(mfxHDL pthis, mfxFrameAllocRequest *request, mfxFrameAllocResponse *response) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXFrameAllocator& self = *(MFXFrameAllocator *)pthis; + + return self.AllocFrames(request, response); +} + +mfxStatus MFXFrameAllocator::Lock_(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXFrameAllocator& self = *(MFXFrameAllocator *)pthis; + + return self.LockFrame(mid, ptr); +} + +mfxStatus MFXFrameAllocator::Unlock_(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXFrameAllocator& self = *(MFXFrameAllocator *)pthis; + + return self.UnlockFrame(mid, ptr); +} + +mfxStatus MFXFrameAllocator::Free_(mfxHDL pthis, mfxFrameAllocResponse *response) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXFrameAllocator& self = *(MFXFrameAllocator *)pthis; + + return self.FreeFrames(response); +} + +mfxStatus MFXFrameAllocator::GetHDL_(mfxHDL pthis, mfxMemId mid, mfxHDL *handle) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXFrameAllocator& self = *(MFXFrameAllocator *)pthis; + + return self.GetFrameHDL(mid, handle); +} + +BaseFrameAllocator::BaseFrameAllocator() +{ +} + +BaseFrameAllocator::~BaseFrameAllocator() +{ +} + +mfxStatus BaseFrameAllocator::CheckRequestType(mfxFrameAllocRequest *request) +{ + if (0 == request) + return MFX_ERR_NULL_PTR; + + // check that Media SDK component is specified in request + if ((request->Type & MEMTYPE_FROM_MASK) != 0) + return MFX_ERR_NONE; + else + return MFX_ERR_UNSUPPORTED; +} + +mfxStatus BaseFrameAllocator::AllocFrames(mfxFrameAllocRequest *request, mfxFrameAllocResponse *response) +{ + if (0 == request || 0 == response || 0 == request->NumFrameSuggested) + return MFX_ERR_MEMORY_ALLOC; + + if (MFX_ERR_NONE != CheckRequestType(request)) + return MFX_ERR_UNSUPPORTED; + + mfxStatus sts = MFX_ERR_NONE; + + if ( (request->Type & MFX_MEMTYPE_EXTERNAL_FRAME) && (request->Type & MFX_MEMTYPE_FROM_DECODE) ) + { + // external decoder allocations + std::list::iterator it = + std::find_if( m_ExtResponses.begin() + , m_ExtResponses.end() + , UniqueResponse (*response, request->Info.CropW, request->Info.CropH, 0)); + + if (it != m_ExtResponses.end()) + { + // check if enough frames were allocated + if (request->NumFrameMin > it->NumFrameActual) + return MFX_ERR_MEMORY_ALLOC; + + it->m_refCount++; + // return existing response + *response = (mfxFrameAllocResponse&)*it; + } + else + { + sts = AllocImpl(request, response); + if (sts == MFX_ERR_NONE) + { + m_ExtResponses.push_back(UniqueResponse(*response, request->Info.CropW, request->Info.CropH, request->Type & MEMTYPE_FROM_MASK)); + } + } + } + else + { + // internal allocations + + // reserve space before allocation to avoid memory leak + m_responses.push_back(mfxFrameAllocResponse()); + + sts = AllocImpl(request, response); + if (sts == MFX_ERR_NONE) + { + m_responses.back() = *response; + } + else + { + m_responses.pop_back(); + } + } + + return sts; +} + +mfxStatus BaseFrameAllocator::FreeFrames(mfxFrameAllocResponse *response) +{ + if (response == 0) + return MFX_ERR_INVALID_HANDLE; + + mfxStatus sts = MFX_ERR_NONE; + + // check whether response is an external decoder response + std::list::iterator i = + std::find_if( m_ExtResponses.begin(), m_ExtResponses.end(), std::bind1st(IsSame(), *response)); + + if (i != m_ExtResponses.end()) + { + if ((--i->m_refCount) == 0) + { + sts = ReleaseResponse(response); + m_ExtResponses.erase(i); + } + return sts; + } + + // if not found so far, then search in internal responses + std::list::iterator i2 = + std::find_if(m_responses.begin(), m_responses.end(), std::bind1st(IsSame(), *response)); + + if (i2 != m_responses.end()) + { + sts = ReleaseResponse(response); + m_responses.erase(i2); + return sts; + } + + // not found anywhere, report an error + return MFX_ERR_INVALID_HANDLE; +} + +mfxStatus BaseFrameAllocator::Close() +{ + std::list ::iterator i; + for (i = m_ExtResponses.begin(); i!= m_ExtResponses.end(); i++) + { + ReleaseResponse(&*i); + } + m_ExtResponses.clear(); + + std::list ::iterator i2; + for (i2 = m_responses.begin(); i2!= m_responses.end(); i2++) + { + ReleaseResponse(&*i2); + } + + return MFX_ERR_NONE; +} + +MFXBufferAllocator::MFXBufferAllocator() +{ + pthis = this; + Alloc = Alloc_; + Lock = Lock_; + Free = Free_; + Unlock = Unlock_; +} + +MFXBufferAllocator::~MFXBufferAllocator() +{ +} + +mfxStatus MFXBufferAllocator::Alloc_(mfxHDL pthis, mfxU32 nbytes, mfxU16 type, mfxMemId *mid) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXBufferAllocator& self = *(MFXBufferAllocator *)pthis; + + return self.AllocBuffer(nbytes, type, mid); +} + +mfxStatus MFXBufferAllocator::Lock_(mfxHDL pthis, mfxMemId mid, mfxU8 **ptr) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXBufferAllocator& self = *(MFXBufferAllocator *)pthis; + + return self.LockBuffer(mid, ptr); +} + +mfxStatus MFXBufferAllocator::Unlock_(mfxHDL pthis, mfxMemId mid) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXBufferAllocator& self = *(MFXBufferAllocator *)pthis; + + return self.UnlockBuffer(mid); +} + +mfxStatus MFXBufferAllocator::Free_(mfxHDL pthis, mfxMemId mid) +{ + if (0 == pthis) + return MFX_ERR_MEMORY_ALLOC; + + MFXBufferAllocator& self = *(MFXBufferAllocator *)pthis; + + return self.FreeBuffer(mid); +} + diff --git a/QSVHelper/IntelSupport/src/d3d11_allocator.cpp b/QSVHelper/IntelSupport/src/d3d11_allocator.cpp new file mode 100644 index 00000000..353a46d6 --- /dev/null +++ b/QSVHelper/IntelSupport/src/d3d11_allocator.cpp @@ -0,0 +1,450 @@ +/* ****************************************************************************** *\ + +INTEL CORPORATION PROPRIETARY INFORMATION +This software is supplied under the terms of a license agreement or nondisclosure +agreement with Intel Corporation and may not be copied or disclosed except in +accordance with the terms of that agreement +Copyright(c) 2011 - 2013 Intel Corporation. All Rights Reserved. + +\* ****************************************************************************** */ + +#include "d3d11_allocator.h" + +#if MFX_D3D11_SUPPORT + +#include +#include +#include +#include +#include +#include +//#include "thread_defs.h" + +#define D3DFMT_NV12 (DXGI_FORMAT)MAKEFOURCC('N','V','1','2') +#define D3DFMT_YV12 (DXGI_FORMAT)MAKEFOURCC('Y','V','1','2') + +//for generating sequence of mfx handles +template +struct sequence { + T x; + sequence(T seed) : x(seed) { } +}; + +template <> +struct sequence { + mfxHDL x; + sequence(mfxHDL seed) : x(seed) { } + + mfxHDL operator ()() + { + mfxHDL y = x; + x = (mfxHDL)(1 + (size_t)(x)); + return y; + } +}; + + +D3D11FrameAllocator::D3D11FrameAllocator() +{ + m_pDeviceContext = NULL; +} + +D3D11FrameAllocator::~D3D11FrameAllocator() +{ + Close(); +} + +D3D11FrameAllocator::TextureSubResource D3D11FrameAllocator::GetResourceFromMid(mfxMemId mid) +{ + size_t index = (size_t)MFXReadWriteMid(mid).raw() - 1; + + if(m_memIdMap.size() <= index) + return TextureSubResource(); + + //reverse iterator dereferencing + TextureResource * p = &(*m_memIdMap[index]); + if (!p->bAlloc) + return TextureSubResource(); + + return TextureSubResource(p, mid); +} + +mfxStatus D3D11FrameAllocator::Init(mfxAllocatorParams *pParams) +{ + D3D11AllocatorParams *pd3d11Params = 0; + pd3d11Params = dynamic_cast(pParams); + + if (NULL == pd3d11Params || + NULL == pd3d11Params->pDevice) + { + return MFX_ERR_NOT_INITIALIZED; + } + + m_initParams = *pd3d11Params; + MSDK_SAFE_RELEASE(m_pDeviceContext); + pd3d11Params->pDevice->GetImmediateContext(&m_pDeviceContext); + + return MFX_ERR_NONE; +} + +mfxStatus D3D11FrameAllocator::Close() +{ + mfxStatus sts = BaseFrameAllocator::Close(); + for(referenceType i = m_resourcesByRequest.begin(); i != m_resourcesByRequest.end(); i++) + { + i->Release(); + } + m_resourcesByRequest.clear(); + m_memIdMap.clear(); + MSDK_SAFE_RELEASE(m_pDeviceContext); + return sts; +} + +mfxStatus D3D11FrameAllocator::LockFrame(mfxMemId mid, mfxFrameData *ptr) +{ + HRESULT hRes = S_OK; + + D3D11_TEXTURE2D_DESC desc = {0}; + D3D11_MAPPED_SUBRESOURCE lockedRect = {0}; + + + //check that texture exists + TextureSubResource sr = GetResourceFromMid(mid); + if (!sr.GetTexture()) + return MFX_ERR_LOCK_MEMORY; + + D3D11_MAP mapType = D3D11_MAP_READ; + UINT mapFlags = D3D11_MAP_FLAG_DO_NOT_WAIT; + { + if (NULL == sr.GetStaging()) + { + hRes = m_pDeviceContext->Map(sr.GetTexture(), sr.GetSubResource(), D3D11_MAP_READ, D3D11_MAP_FLAG_DO_NOT_WAIT, &lockedRect); + desc.Format = DXGI_FORMAT_P8; + } + else + { + sr.GetTexture()->GetDesc(&desc); + + if (DXGI_FORMAT_NV12 != desc.Format && + DXGI_FORMAT_420_OPAQUE != desc.Format && + DXGI_FORMAT_YUY2 != desc.Format && + DXGI_FORMAT_P8 != desc.Format && + DXGI_FORMAT_B8G8R8A8_UNORM != desc.Format) + { + return MFX_ERR_LOCK_MEMORY; + } + + //coping data only in case user wants to read from stored surface + { + + if (MFXReadWriteMid(mid, MFXReadWriteMid::reuse).isRead()) + { + m_pDeviceContext->CopySubresourceRegion(sr.GetStaging(), 0, 0, 0, 0, sr.GetTexture(), sr.GetSubResource(), NULL); + } + + do + { + hRes = m_pDeviceContext->Map(sr.GetStaging(), 0, mapType, mapFlags, &lockedRect); + if (S_OK != hRes && DXGI_ERROR_WAS_STILL_DRAWING != hRes) + { + msdk_printf(MSDK_STRING("ERROR: m_pDeviceContext->Map = 0x%08lx\n"), hRes); + } + } + while (DXGI_ERROR_WAS_STILL_DRAWING == hRes); + } + + } + } + + if (FAILED(hRes)) + return MFX_ERR_LOCK_MEMORY; + + switch (desc.Format) + { + case DXGI_FORMAT_NV12: + ptr->Pitch = (mfxU16)lockedRect.RowPitch; + ptr->Y = (mfxU8 *)lockedRect.pData; + ptr->U = (mfxU8 *)lockedRect.pData + desc.Height * lockedRect.RowPitch; + ptr->V = ptr->U + 1; + + break; + + case DXGI_FORMAT_420_OPAQUE: // can be unsupported by standard ms guid + ptr->Pitch = (mfxU16)lockedRect.RowPitch; + ptr->Y = (mfxU8 *)lockedRect.pData; + ptr->V = ptr->Y + desc.Height * lockedRect.RowPitch; + ptr->U = ptr->V + (desc.Height * lockedRect.RowPitch) / 4; + + break; + + case DXGI_FORMAT_YUY2: + ptr->Pitch = (mfxU16)lockedRect.RowPitch; + ptr->Y = (mfxU8 *)lockedRect.pData; + ptr->U = ptr->Y + 1; + ptr->V = ptr->Y + 3; + + break; + + case DXGI_FORMAT_P8 : + ptr->Pitch = (mfxU16)lockedRect.RowPitch; + ptr->Y = (mfxU8 *)lockedRect.pData; + ptr->U = 0; + ptr->V = 0; + + break; + + case DXGI_FORMAT_B8G8R8A8_UNORM : + ptr->Pitch = (mfxU16)lockedRect.RowPitch; + ptr->B = (mfxU8 *)lockedRect.pData; + ptr->G = ptr->B + 1; + ptr->R = ptr->B + 2; + ptr->A = ptr->B + 3; + + break; + + default: + + return MFX_ERR_LOCK_MEMORY; + } + + return MFX_ERR_NONE; +} + +mfxStatus D3D11FrameAllocator::UnlockFrame(mfxMemId mid, mfxFrameData *ptr) +{ + //check that texture exists + TextureSubResource sr = GetResourceFromMid(mid); + if (!sr.GetTexture()) + return MFX_ERR_LOCK_MEMORY; + + if (NULL == sr.GetStaging()) + { + m_pDeviceContext->Unmap(sr.GetTexture(), sr.GetSubResource()); + } + else + { + m_pDeviceContext->Unmap(sr.GetStaging(), 0); + //only if user wrote something to texture + if (MFXReadWriteMid(mid, MFXReadWriteMid::reuse).isWrite()) + { + m_pDeviceContext->CopySubresourceRegion(sr.GetTexture(), sr.GetSubResource(), 0, 0, 0, sr.GetStaging(), 0, NULL); + } + } + + if (ptr) + { + ptr->Pitch=0; + ptr->U=ptr->V=ptr->Y=0; + ptr->A=ptr->R=ptr->G=ptr->B=0; + } + + return MFX_ERR_NONE; +} + + +mfxStatus D3D11FrameAllocator::GetFrameHDL(mfxMemId mid, mfxHDL *handle) +{ + if (NULL == handle) + return MFX_ERR_INVALID_HANDLE; + + TextureSubResource sr = GetResourceFromMid(mid); + + if (!sr.GetTexture()) + return MFX_ERR_INVALID_HANDLE; + + mfxHDLPair *pPair = (mfxHDLPair*)handle; + + pPair->first = sr.GetTexture(); + pPair->second = (mfxHDL)(UINT_PTR)sr.GetSubResource(); + + return MFX_ERR_NONE; +} + +mfxStatus D3D11FrameAllocator::CheckRequestType(mfxFrameAllocRequest *request) +{ + mfxStatus sts = BaseFrameAllocator::CheckRequestType(request); + if (MFX_ERR_NONE != sts) + return sts; + + if ((request->Type & (MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET | MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET)) != 0) + return MFX_ERR_NONE; + else + return MFX_ERR_UNSUPPORTED; +} + +mfxStatus D3D11FrameAllocator::ReleaseResponse(mfxFrameAllocResponse *response) +{ + if (NULL == response) + return MFX_ERR_NULL_PTR; + + if (response->mids && 0 != response->NumFrameActual) + { + //check whether texture exsist + TextureSubResource sr = GetResourceFromMid(response->mids[0]); + + if (!sr.GetTexture()) + return MFX_ERR_NULL_PTR; + + sr.Release(); + + //if texture is last it is possible to remove also all handles from map to reduce fragmentation + //search for allocated chunk + if (m_resourcesByRequest.end() == std::find_if(m_resourcesByRequest.begin(), m_resourcesByRequest.end(), TextureResource::isAllocated)) + { + m_resourcesByRequest.clear(); + m_memIdMap.clear(); + } + } + + return MFX_ERR_NONE; +} +mfxStatus D3D11FrameAllocator::AllocImpl(mfxFrameAllocRequest *request, mfxFrameAllocResponse *response) +{ + HRESULT hRes; + + DXGI_FORMAT colorFormat = ConverColortFormat(request->Info.FourCC); + + if (DXGI_FORMAT_UNKNOWN == colorFormat) + { + return MFX_ERR_UNSUPPORTED; + } + + TextureResource newTexture; + + if (request->Info.FourCC == MFX_FOURCC_P8) + { + D3D11_BUFFER_DESC desc = { 0 }; + + desc.ByteWidth = request->Info.Width * request->Info.Height; + desc.Usage = D3D11_USAGE_STAGING; + desc.BindFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + desc.MiscFlags = 0; + desc.StructureByteStride = 0; + + ID3D11Buffer * buffer = 0; + hRes = m_initParams.pDevice->CreateBuffer(&desc, 0, &buffer); + if (FAILED(hRes)) + return MFX_ERR_MEMORY_ALLOC; + + newTexture.textures.push_back(reinterpret_cast(buffer)); + } + else + { + D3D11_TEXTURE2D_DESC desc = {0}; + + desc.Width = request->Info.Width; + desc.Height = request->Info.Height; + + desc.MipLevels = 1; + //number of subresources is 1 in case of not single texture + desc.ArraySize = m_initParams.bUseSingleTexture ? request->NumFrameSuggested : 1; + desc.Format = ConverColortFormat(request->Info.FourCC); + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.MiscFlags = m_initParams.uncompressedResourceMiscFlags; + + desc.BindFlags = D3D11_BIND_DECODER; + + if ( (MFX_MEMTYPE_FROM_VPPIN & request->Type) && (DXGI_FORMAT_YUY2 == desc.Format) || + (DXGI_FORMAT_B8G8R8A8_UNORM == desc.Format) ) + { + desc.BindFlags = D3D11_BIND_RENDER_TARGET; + if (desc.ArraySize > 2) + return MFX_ERR_MEMORY_ALLOC; + } + + if ( (MFX_MEMTYPE_FROM_VPPOUT & request->Type) || + (MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET & request->Type)) + { + desc.BindFlags = D3D11_BIND_RENDER_TARGET; + if (desc.ArraySize > 2) + return MFX_ERR_MEMORY_ALLOC; + } + + if( DXGI_FORMAT_P8 == desc.Format ) + { + desc.BindFlags = 0; + } + + ID3D11Texture2D* pTexture2D; + + for(size_t i = 0; i < request->NumFrameSuggested / desc.ArraySize; i++) + { + hRes = m_initParams.pDevice->CreateTexture2D(&desc, NULL, &pTexture2D); + + if (FAILED(hRes)) + { + msdk_printf(MSDK_STRING("CreateTexture2D(%d) failed, hr = 0x%08lx\n"), i, hRes); + return MFX_ERR_MEMORY_ALLOC; + } + newTexture.textures.push_back(pTexture2D); + } + + desc.ArraySize = 1; + desc.Usage = D3D11_USAGE_STAGING; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + desc.BindFlags = 0; + desc.MiscFlags = 0; + + for(size_t i = 0; i < request->NumFrameSuggested; i++) + { + hRes = m_initParams.pDevice->CreateTexture2D(&desc, NULL, &pTexture2D); + + if (FAILED(hRes)) + { + printf("Create staging texture(%d) failed hr = 0x%X\n", i, hRes); + return MFX_ERR_MEMORY_ALLOC; + } + newTexture.stagingTexture.push_back(pTexture2D); + } + } + + // mapping to self created handles array, starting from zero or from last assigned handle + 1 + sequence seq_initializer(m_resourcesByRequest.empty() ? 0 : m_resourcesByRequest.back().outerMids.back()); + + //incrementing starting index + //1. 0(NULL) is invalid memid + //2. back is last index not new one + seq_initializer(); + + std::generate_n(std::back_inserter(newTexture.outerMids), request->NumFrameSuggested, seq_initializer); + + //saving texture resources + m_resourcesByRequest.push_back(newTexture); + + //providing pointer to mids externally + response->mids = &m_resourcesByRequest.back().outerMids.front(); + response->NumFrameActual = request->NumFrameSuggested; + + //iterator prior end() + std::list ::iterator it_last = m_resourcesByRequest.end(); + //fill map + std::fill_n(std::back_inserter(m_memIdMap), request->NumFrameSuggested, --it_last); + + return MFX_ERR_NONE; +} + +DXGI_FORMAT D3D11FrameAllocator::ConverColortFormat(mfxU32 fourcc) +{ + switch (fourcc) + { + case MFX_FOURCC_NV12: + return DXGI_FORMAT_NV12; + + case MFX_FOURCC_YUY2: + return DXGI_FORMAT_YUY2; + + case MFX_FOURCC_RGB4: + return DXGI_FORMAT_B8G8R8A8_UNORM; + + case MFX_FOURCC_P8: + case MFX_FOURCC_P8_TEXTURE: + return DXGI_FORMAT_P8; + + default: + return DXGI_FORMAT_UNKNOWN; + } +} + +#endif // #if MFX_D3D11_SUPPORT diff --git a/QSVHelper/IntelSupport/src/d3d11_device.cpp b/QSVHelper/IntelSupport/src/d3d11_device.cpp new file mode 100644 index 00000000..66785440 --- /dev/null +++ b/QSVHelper/IntelSupport/src/d3d11_device.cpp @@ -0,0 +1,334 @@ +/* ****************************************************************************** *\ + +INTEL CORPORATION PROPRIETARY INFORMATION +This software is supplied under the terms of a license agreement or nondisclosure +agreement with Intel Corporation and may not be copied or disclosed except in +accordance with the terms of that agreement +Copyright(c) 2011 - 2012 Intel Corporation. All Rights Reserved. + +\* ****************************************************************************** */ + +#include "d3d11_device.h" + +#if MFX_D3D11_SUPPORT + +#include "sample_defs.h" + +CD3D11Device::CD3D11Device() +{ +} + +CD3D11Device::~CD3D11Device() +{ + Close(); +} + +mfxStatus CD3D11Device::FillSCD(mfxHDL hWindow, DXGI_SWAP_CHAIN_DESC& scd) +{ + scd.Windowed = TRUE; + scd.OutputWindow = (HWND)hWindow; + scd.SampleDesc.Count = 1; + scd.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + scd.BufferCount = 1; + + return MFX_ERR_NONE; +} + +mfxStatus CD3D11Device::Init( + mfxHDL hWindow, + mfxU16 nViews, + mfxU32 nAdapterNum) +{ + mfxStatus sts = MFX_ERR_NONE; + HRESULT hres = S_OK; + m_nViews = nViews; + if (2 < nViews) + return MFX_ERR_UNSUPPORTED; + m_bDefaultStereoEnabled = FALSE; + + static D3D_FEATURE_LEVEL FeatureLevels[] = { + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0 + }; + D3D_FEATURE_LEVEL pFeatureLevelsOut; + + hres = CreateDXGIFactory(__uuidof(IDXGIFactory2), (void**)(&m_pDXGIFactory) ); + if (FAILED(hres)) + return MFX_ERR_DEVICE_FAILED; + + if (m_nViews == 2 && hWindow) + { + hres = m_pDXGIFactory->QueryInterface(__uuidof(IDXGIDisplayControl), (void **)&m_pDisplayControl); + if (FAILED(hres)) + return MFX_ERR_DEVICE_FAILED; + m_bDefaultStereoEnabled = m_pDisplayControl->IsStereoEnabled(); + if (!m_bDefaultStereoEnabled) + m_pDisplayControl->SetStereoEnabled(TRUE); + } + + hres = m_pDXGIFactory->EnumAdapters(nAdapterNum,&m_pAdapter); + if (FAILED(hres)) + return MFX_ERR_DEVICE_FAILED; + + hres = D3D11CreateDevice(m_pAdapter , + D3D_DRIVER_TYPE_UNKNOWN, + NULL, + 0, + FeatureLevels, + MSDK_ARRAY_LEN(FeatureLevels), + D3D11_SDK_VERSION, + &m_pD3D11Device, + &pFeatureLevelsOut, + &m_pD3D11Ctx); + + if (FAILED(hres)) + return MFX_ERR_DEVICE_FAILED; + + m_pDXGIDev = m_pD3D11Device; + m_pDX11VideoDevice = m_pD3D11Device; + m_pVideoContext = m_pD3D11Ctx; + + MSDK_CHECK_POINTER(m_pDXGIDev.p, MFX_ERR_NULL_PTR); + MSDK_CHECK_POINTER(m_pDX11VideoDevice.p, MFX_ERR_NULL_PTR); + MSDK_CHECK_POINTER(m_pVideoContext.p, MFX_ERR_NULL_PTR); + + // turn on multithreading for the Context + CComQIPtr p_mt(m_pVideoContext); + + if (p_mt) + p_mt->SetMultithreadProtected(true); + else + return MFX_ERR_DEVICE_FAILED; + + // create swap chain only for rendering use case (hWindow != 0) + DXGI_SWAP_CHAIN_DESC scd; + if (hWindow) + { + ZeroMemory(&scd, sizeof(scd)); + sts = FillSCD(hWindow, scd); + MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + MSDK_CHECK_POINTER (m_pDXGIFactory.p, MFX_ERR_NULL_PTR); + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0}; + swapChainDesc.Width = 0; // Use automatic sizing. + swapChainDesc.Height = 0; + swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format. + swapChainDesc.Stereo = m_nViews == 2 ? TRUE : FALSE; + swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling. + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferCount = 2; // Use double buffering to minimize latency. + swapChainDesc.Scaling = DXGI_SCALING_STRETCH; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + hres = m_pDXGIFactory->CreateSwapChainForHwnd(m_pD3D11Device, + (HWND)hWindow, + &swapChainDesc, + NULL, + NULL, + reinterpret_cast(&m_pSwapChain) ); + if (FAILED(hres)) + return MFX_ERR_DEVICE_FAILED; + } + + return sts; +} + +mfxStatus CD3D11Device::CreateVideoProcessor(mfxFrameSurface1 * pSrf) +{ + HRESULT hres = S_OK; + + if (m_VideoProcessorEnum.p || NULL == pSrf) + return MFX_ERR_NONE; + + //create video processor + D3D11_VIDEO_PROCESSOR_CONTENT_DESC ContentDesc; + MSDK_ZERO_MEMORY( ContentDesc ); + + ContentDesc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE; + ContentDesc.InputFrameRate.Numerator = 30000; + ContentDesc.InputFrameRate.Denominator = 1000; + ContentDesc.InputWidth = pSrf->Info.CropW; + ContentDesc.InputHeight = pSrf->Info.CropH; + ContentDesc.OutputWidth = pSrf->Info.CropW; + ContentDesc.OutputHeight = pSrf->Info.CropH; + ContentDesc.OutputFrameRate.Numerator = 30000; + ContentDesc.OutputFrameRate.Denominator = 1000; + + ContentDesc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL; + + hres = m_pDX11VideoDevice->CreateVideoProcessorEnumerator( &ContentDesc, &m_VideoProcessorEnum ); + if (FAILED(hres)) + return MFX_ERR_DEVICE_FAILED; + + hres = m_pDX11VideoDevice->CreateVideoProcessor( m_VideoProcessorEnum, 0, &m_pVideoProcessor ); + if (FAILED(hres)) + return MFX_ERR_DEVICE_FAILED; + + return MFX_ERR_NONE; +} + +mfxStatus CD3D11Device::Reset() +{ + // Changing video mode back to the original state + if (2 == m_nViews && !m_bDefaultStereoEnabled) + m_pDisplayControl->SetStereoEnabled(FALSE); + return MFX_ERR_NONE; +} + +mfxStatus CD3D11Device::GetHandle(mfxHandleType type, mfxHDL *pHdl) +{ + if (MFX_HANDLE_D3D11_DEVICE == type) + { + *pHdl = m_pD3D11Device.p; + return MFX_ERR_NONE; + } + return MFX_ERR_UNSUPPORTED; +} + +mfxStatus CD3D11Device::SetHandle(mfxHandleType /*type*/, mfxHDL /*hdl*/) +{ + return MFX_ERR_UNSUPPORTED; +} + +mfxStatus CD3D11Device::RenderFrame(mfxFrameSurface1 * pSrf, mfxFrameAllocator * pAlloc) +{ + HRESULT hres = S_OK; + mfxStatus sts; + + sts = CreateVideoProcessor(pSrf); + MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + hres = m_pSwapChain->GetBuffer(0, __uuidof( ID3D11Texture2D ), (void**)&m_pDXGIBackBuffer.p); + if (FAILED(hres)) + return MFX_ERR_DEVICE_FAILED; + + D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC OutputViewDesc; + if (2 == m_nViews) + { + m_pVideoContext->VideoProcessorSetStreamStereoFormat(m_pVideoProcessor, 0, TRUE,D3D11_VIDEO_PROCESSOR_STEREO_FORMAT_SEPARATE, + TRUE, TRUE, D3D11_VIDEO_PROCESSOR_STEREO_FLIP_NONE, NULL); + m_pVideoContext->VideoProcessorSetOutputStereoMode(m_pVideoProcessor,TRUE); + + OutputViewDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2DARRAY; + OutputViewDesc.Texture2DArray.ArraySize = 2; + OutputViewDesc.Texture2DArray.MipSlice = 0; + OutputViewDesc.Texture2DArray.FirstArraySlice = 0; + } + else + { + OutputViewDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D; + OutputViewDesc.Texture2D.MipSlice = 0; + } + + if (1 == m_nViews || 0 == pSrf->Info.FrameId.ViewId) + { + hres = m_pDX11VideoDevice->CreateVideoProcessorOutputView( + m_pDXGIBackBuffer, + m_VideoProcessorEnum, + &OutputViewDesc, + &m_pOutputView.p ); + if (FAILED(hres)) + return MFX_ERR_DEVICE_FAILED; + } + + D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC InputViewDesc; + InputViewDesc.FourCC = 0; + InputViewDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D; + InputViewDesc.Texture2D.MipSlice = 0; + InputViewDesc.Texture2D.ArraySlice = 0; + + mfxHDLPair pair = {NULL}; + sts = pAlloc->GetHDL(pAlloc->pthis, pSrf->Data.MemId, (mfxHDL*)&pair); + MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + ID3D11Texture2D *pRTTexture2D = reinterpret_cast(pair.first); + D3D11_TEXTURE2D_DESC RTTexture2DDesc; + + if(!m_pTempTexture && m_nViews == 2) + { + pRTTexture2D->GetDesc(&RTTexture2DDesc); + hres = m_pD3D11Device->CreateTexture2D(&RTTexture2DDesc,NULL,&m_pTempTexture.p); + if (FAILED(hres)) + return MFX_ERR_DEVICE_FAILED; + } + + // Creating input views for left and righ eyes + if (1 == m_nViews) + { + hres = m_pDX11VideoDevice->CreateVideoProcessorInputView( + pRTTexture2D, + m_VideoProcessorEnum, + &InputViewDesc, + &m_pInputViewLeft.p ); + + } + else if (2 == m_nViews && 0 == pSrf->Info.FrameId.ViewId) + { + m_pD3D11Ctx->CopyResource(m_pTempTexture,pRTTexture2D); + hres = m_pDX11VideoDevice->CreateVideoProcessorInputView( + m_pTempTexture, + m_VideoProcessorEnum, + &InputViewDesc, + &m_pInputViewLeft.p ); + } + else + { + hres = m_pDX11VideoDevice->CreateVideoProcessorInputView( + pRTTexture2D, + m_VideoProcessorEnum, + &InputViewDesc, + &m_pInputViewRight.p ); + + } + if (FAILED(hres)) + return MFX_ERR_DEVICE_FAILED; + + // NV12 surface to RGB backbuffer + RECT rect = {0}; + rect.right = pSrf->Info.CropW; + rect.bottom = pSrf->Info.CropH; + + D3D11_VIDEO_PROCESSOR_STREAM StreamData; + + if (1 == m_nViews || pSrf->Info.FrameId.ViewId == 1) + { + StreamData.Enable = TRUE; + StreamData.OutputIndex = 0; + StreamData.InputFrameOrField = 0; + StreamData.PastFrames = 0; + StreamData.FutureFrames = 0; + StreamData.ppPastSurfaces = NULL; + StreamData.ppFutureSurfaces = NULL; + StreamData.pInputSurface = m_pInputViewLeft; + StreamData.ppPastSurfacesRight = NULL; + StreamData.ppFutureSurfacesRight = NULL; + StreamData.pInputSurfaceRight = m_nViews == 2 ? m_pInputViewRight : NULL; + + m_pVideoContext->VideoProcessorSetStreamSourceRect(m_pVideoProcessor, 0, true, &rect); + m_pVideoContext->VideoProcessorSetStreamFrameFormat( m_pVideoProcessor, 0, D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE); + hres = m_pVideoContext->VideoProcessorBlt( m_pVideoProcessor, m_pOutputView, 0, 1, &StreamData ); + if (FAILED(hres)) + return MFX_ERR_DEVICE_FAILED; + } + + if (1 == m_nViews || 1 == pSrf->Info.FrameId.ViewId) + { + DXGI_PRESENT_PARAMETERS parameters = {0}; + hres = m_pSwapChain->Present1(0, 0, ¶meters); + if (FAILED(hres)) + return MFX_ERR_DEVICE_FAILED; + } + + return MFX_ERR_NONE; +} + +void CD3D11Device::Close() +{ + Reset(); +} + +#endif // #if MFX_D3D11_SUPPORT \ No newline at end of file diff --git a/QSVHelper/QSVHelper.cpp b/QSVHelper/QSVHelper.cpp new file mode 100644 index 00000000..a249a81a --- /dev/null +++ b/QSVHelper/QSVHelper.cpp @@ -0,0 +1,160 @@ +/******************************************************************************** + Copyright (C) 2013 Ruwen Hahn + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +********************************************************************************/ + + +#include +#include +#include + +#include +#include +#include + +#include + +#include "Encoder.h" +#include "IPCInfo.h" +#include "QSVStuff.h" +#include "SupportStuff.h" +#include "WindowsStuff.h" + +namespace { + const struct impl_parameters + { + mfxIMPL type, + intf; + mfxVersion version; + } valid_impl[] = { + { MFX_IMPL_HARDWARE_ANY, MFX_IMPL_VIA_D3D11, {6, 1} }, + { MFX_IMPL_HARDWARE, MFX_IMPL_VIA_D3D11, {6, 1} }, + { MFX_IMPL_HARDWARE_ANY, MFX_IMPL_VIA_D3D9, {6, 1} }, //Ivy Bridge+ with non-functional D3D11 support? + { MFX_IMPL_HARDWARE, MFX_IMPL_VIA_D3D9, {6, 1} }, + { MFX_IMPL_HARDWARE_ANY, MFX_IMPL_VIA_D3D9, {4, 1} }, //Sandy Bridge + { MFX_IMPL_HARDWARE, MFX_IMPL_VIA_D3D9, {4, 1} }, + }; + + std::wofstream log_file; +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nShowCmd) +{ + using namespace std; + + wstringstream cmdss(GetCommandLineW()); + wstring wstr; + cmdss >> wstr; + wstringstream wss; + wss << wstr << GetCurrentProcessId(); + wstring event_prefix; + wss >> event_prefix; + + + std::string log_path = GetCommandLineA(); + log_path.erase(log_path.begin(), find(log_path.begin(), log_path.end(), ' ')+1); + log_path += "/pluginData/QSVHelper.log"; + + log_file.open(log_path, ios::out | ios::trunc); + if(!log_file.is_open()) + return 200; + + ipc_init_request init_req(event_prefix + INIT_REQUEST); + + if(!init_req.is_signalled(INFINITE)) + return 1; + + if(init_req->mode == init_req->MODE_QUERY) + { + MFXVideoSession session; + for(auto impl = begin(valid_impl); impl != std::end(valid_impl); impl++) + { + auto ver = impl->version; + auto result = session.Init(impl->intf | impl->type, &ver); + if(result == MFX_ERR_NONE) + return 0; + } + return 3; + } + + safe_handle obs_handle(OpenProcess(SYNCHRONIZE, false, init_req->obs_process_id)); + if(!obs_handle) + return 2; + + ipc_init_response init_res(event_prefix + INIT_RESPONSE); + zero(*&init_res); + + Encoder encoder(init_req, event_prefix, log_file); + + init_res->using_custom_impl = false; + if(init_req->use_custom_impl) + { + impl_parameters p; + p.intf = init_req->custom_intf; + p.type = init_req->custom_impl; + p.version = init_req->custom_version; + auto result = encoder.InitializeMFX(p, true); + if(result >= MFX_ERR_NONE) + { + init_res->using_custom_impl = true; + init_res->actual_impl = p.intf | p.type; + init_res->version = p.version; + } + } + + if(!init_res->using_custom_impl || !encoder) + { + decltype(begin(valid_impl)) best = nullptr; + for(auto impl = begin(valid_impl); impl != std::end(valid_impl); impl++) + { + auto result = encoder.InitializeMFX(*impl); + if(result == MFX_WRN_PARTIAL_ACCELERATION && !best) + best = impl; + if(result == MFX_ERR_NONE) + break; + } + + if(!encoder) + { + if(!best) + return 5; + auto ver = best->version; + encoder.InitializeMFX(*best); + log_file << "No valid implementation detected, using best implementation instead\n"; + } + } + + if(!encoder) + return 6; + + init_res->version = encoder.version; + init_res->requested_impl = encoder.requested; + init_res->actual_impl = encoder.actual; + + encoder.InitializeBuffers(init_res); + + encoder.InitializeEncoder(); + + init_res.signal(); + + log_file << "Using " << encoder.encode_tasks.size() << " encode tasks and " << encoder.surfaces.size() << " internal frame buffers\n"; + + encoder.RequestSPSPPS(); + + ipc_stop stop(event_prefix + STOP_REQUEST); + + return encoder.EncodeLoop(stop, obs_handle); +} diff --git a/QSVHelper/QSVHelper.vcxproj b/QSVHelper/QSVHelper.vcxproj new file mode 100644 index 00000000..14980b64 --- /dev/null +++ b/QSVHelper/QSVHelper.vcxproj @@ -0,0 +1,126 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {F1C7033A-F050-46E3-9080-E129B9CD1010} + Win32Proj + QSVHelper + + + + Application + true + v100 + Unicode + + + Application + false + v100 + true + Unicode + + + + + + + + + + + + + false + $(WindowsSDK80Path)Include\um;$(WindowsSDK80Path)Include\shared;$(DXSDK_DIR)Include;IntelSupport\include;IntelSupport\include\vm;$(IncludePath) + $(WindowsSDK80Path)Lib\win8\um\x86;$(DXSDK_DIR)Lib\x86;$(LibraryPath) + $(Configuration)\ + + + false + $(WindowsSDK80Path)Include\um;$(WindowsSDK80Path)Include\shared;$(DXSDK_DIR)Include;IntelSupport\include;IntelSupport\include\vm;$(IncludePath) + $(Configuration)\ + + + + NotUsing + Level3 + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + ../libmfx/include/msdk/include + MultiThreadedDebug + + + Windows + true + ../libmfx/$(Platform)/$(Configuration)/libmfx.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies) + UseLinkTimeCodeGeneration + ..\rundir\pdb32\$(TargetName).pdb + ..\rundir\pdb32\stripped\$(TargetName).pdb + + + copy "$(OutDir)$(TargetName).exe" ..\rundir\ + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + ../libmfx/include/msdk/include + MultiThreaded + + + Windows + true + true + true + ../libmfx/$(Platform)/$(Configuration)/libmfx.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies) + ..\rundir\pdb32\$(TargetName).pdb + ..\rundir\pdb32\stripped\$(TargetName).pdb + + + copy "$(OutDir)$(TargetName).exe" ..\rundir\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QSVHelper/QSVHelper.vcxproj.filters b/QSVHelper/QSVHelper.vcxproj.filters new file mode 100644 index 00000000..53be3c98 --- /dev/null +++ b/QSVHelper/QSVHelper.vcxproj.filters @@ -0,0 +1,87 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {ca840a93-0bea-4a93-83c9-0476c240c3a8} + + + {fd95e3a4-45a3-40e5-a120-1e3a476e1fc0} + + + + + Source Files + + + Source Files + + + IntelSupport + + + IntelSupport + + + IntelSupport + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + IntelSupport\Headers + + + IntelSupport\Headers + + + IntelSupport\Headers + + + IntelSupport\Headers + + + IntelSupport\Headers + + + IntelSupport\Headers + + + IntelSupport\Headers + + + IntelSupport\Headers + + + \ No newline at end of file diff --git a/QSVHelper/QSVStuff.cpp b/QSVHelper/QSVStuff.cpp new file mode 100644 index 00000000..b76d73c2 --- /dev/null +++ b/QSVHelper/QSVStuff.cpp @@ -0,0 +1,172 @@ +/******************************************************************************** + Copyright (C) 2013 Ruwen Hahn + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +********************************************************************************/ + +#include "QSVStuff.h" + +#include "Utilities.h" + +using namespace std; + +namespace { + void ConvertFrameRate(mfxF64 dFrameRate, mfxU32& pnFrameRateExtN, mfxU32& pnFrameRateExtD) + { + mfxU32 fr; + + fr = (mfxU32)(dFrameRate + .5); + + if(fabs(fr - dFrameRate) < 0.0001) + { + pnFrameRateExtN = fr; + pnFrameRateExtD = 1; + return; + } + + fr = (mfxU32)(dFrameRate * 1.001 + .5); + + if(fabs(fr * 1000 - dFrameRate * 1001) < 10) + { + pnFrameRateExtN = fr * 1000; + pnFrameRateExtD = 1001; + return; + } + + pnFrameRateExtN = (mfxU32)(dFrameRate * 10000 + .5); + pnFrameRateExtD = 10000; + } +} + +Parameters::Parameters() +{ + zero(params); +} + +void Parameters::Init(int fps, int keyframe_interval_frames, int bframes, int width, int height, int max_bitrate, int buffer_size, bool use_cbr) +{ + params.mfx.CodecId = MFX_CODEC_AVC; + params.mfx.TargetUsage = MFX_TARGETUSAGE_BEST_QUALITY; + params.mfx.TargetKbps = max_bitrate; + params.mfx.MaxKbps = max_bitrate; + params.mfx.BufferSizeInKB = buffer_size/8; + params.mfx.GopOptFlag = MFX_GOP_CLOSED; + params.mfx.GopPicSize = keyframe_interval_frames; + params.mfx.GopRefDist = bframes+1; + params.mfx.NumSlice = 1; + + params.mfx.RateControlMethod = use_cbr ? MFX_RATECONTROL_CBR : MFX_RATECONTROL_VBR; + params.IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY; + + auto& fi = params.mfx.FrameInfo; + ConvertFrameRate(fps, fi.FrameRateExtN, fi.FrameRateExtD); + + fi.FourCC = MFX_FOURCC_NV12; + fi.ChromaFormat = MFX_CHROMAFORMAT_YUV420; + fi.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; + + fi.Width = align16(width); + fi.Height = align16(height); + + fi.CropX = 0; + fi.CropY = 0; + fi.CropW = width; + fi.CropH = height; +} + +void Parameters::SetCodingOptionSPSPPS(mfxU8 *sps_buff, mfxU16 sps_size, mfxU8 *pps_buff, mfxU16 pps_size) +{ + if(!FindExt(cospspps)) + { + zero(cospspps); + cospspps.Header.BufferId = MFX_EXTBUFF_CODING_OPTION_SPSPPS; + cospspps.Header.BufferSz = sizeof(cospspps); + + AddExt(cospspps); + } + cospspps.SPSBuffer = sps_buff; + cospspps.SPSBufSize = sps_size; + cospspps.PPSBuffer = pps_buff; + cospspps.PPSBufSize = pps_size; +} + +void Parameters::SetVideoSignalInfo(int full_range, int primaries, int transfer, int matrix) +{ + if(!FindExt(vsi)) + { + zero(vsi); + vsi.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO; + vsi.Header.BufferSz = sizeof(vsi); + + AddExt(vsi); + } + + vsi.ColourDescriptionPresent = 1; + vsi.VideoFullRange = full_range; + vsi.ColourPrimaries = primaries; + vsi.TransferCharacteristics = transfer; + vsi.MatrixCoefficients = matrix; + vsi.VideoFormat = 5; //unspecified +} + +void Parameters::UpdateExt() +{ + params.ExtParam = &ext_buffers.front(); + params.NumExtParam = ext_buffers.size(); +} + + + +void EncodeCtrl::AddSEIData(sei_type type, vector data) +{ + unsigned payload_size = data.size(); + vector buffer; + + mfxU16 type_ = type; + while(type_ > 255) + { + buffer.emplace_back(0xff); + type_ -= 255; + } + buffer.emplace_back((mfxU8)type_); + + while(payload_size > 255) + { + buffer.emplace_back(0xff); + payload_size -= 255; + } + buffer.emplace_back(payload_size); + + buffer.insert(end(buffer), begin(data), end(data)); + + data_buffers.emplace_back(buffer); + payloads.emplace_back(mfxPayload()); + + mfxPayload &sei_payload = payloads.back(); + + zero(sei_payload); + + sei_payload.Type = type; + sei_payload.BufSize = buffer.size(); + sei_payload.NumBit = sei_payload.BufSize*8; + sei_payload.Data = &data_buffers.back().front(); + + payload_list.clear(); + for(auto &payload = begin(payloads); payload != end(payloads); payload++) + payload_list.emplace_back(&*payload); + + ctrl.Payload = &payload_list.front(); + ctrl.NumPayload = payload_list.size(); +} diff --git a/QSVHelper/QSVStuff.h b/QSVHelper/QSVStuff.h new file mode 100644 index 00000000..180a1846 --- /dev/null +++ b/QSVHelper/QSVStuff.h @@ -0,0 +1,78 @@ +/******************************************************************************** + Copyright (C) 2013 Ruwen Hahn + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +********************************************************************************/ + +#pragma once + +#include + +#include +#include + +#include "Utilities.h" + +template +inline T align16(T t) +{ + return (((t + 15) >> 4) << 4); +} + +struct Parameters +{ +private: + mfxVideoParam params; + std::vector ext_buffers; + +public: + mfxExtCodingOptionSPSPPS cospspps; + mfxExtVideoSignalInfo vsi; + + + operator mfxVideoParam() { return params; } + operator mfxVideoParam*() { return ¶ms; } + mfxVideoParam* operator&() { return ¶ms; } + mfxVideoParam* operator->() { return ¶ms; } + + void Init(int fps, int keyframe_interval_frames, int bframes, int width, int height, int max_bitrate, int buffer_size, bool use_cbr); + void SetCodingOptionSPSPPS(mfxU8 *sps_buff, mfxU16 sps_size, mfxU8 *pps_buff, mfxU16 pps_size); + void SetVideoSignalInfo(int full_range, int primaries, int transfer, int matrix); + + Parameters(); +protected: + void UpdateExt(); + template + bool FindExt(T& t) { return std::find(std::begin(ext_buffers), std::end(ext_buffers), reinterpret_cast(&t)) != std::end(ext_buffers); } + template + void AddExt(T& t) { ext_buffers.emplace_back(reinterpret_cast(&t)); UpdateExt(); } +}; + + +struct EncodeCtrl +{ + enum sei_type {SEI_USER_DATA_UNREGISTERED=0x5}; + mfxEncodeCtrl ctrl; + std::vector payload_list; + std::vector payloads; + std::vector> data_buffers; + + operator mfxEncodeCtrl() { return ctrl; } + mfxEncodeCtrl *operator&() { return &ctrl; } + + void AddSEIData(sei_type type, std::vector payload); + + EncodeCtrl() { zero(ctrl); } +}; diff --git a/QSVHelper/SupportStuff.h b/QSVHelper/SupportStuff.h new file mode 100644 index 00000000..43368f1d --- /dev/null +++ b/QSVHelper/SupportStuff.h @@ -0,0 +1,76 @@ +/******************************************************************************** + Copyright (C) 2013 Ruwen Hahn + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +********************************************************************************/ + +#pragma once + +#include + +#include "Utilities.h" +#include "QSVStuff.h" + +struct encode_task +{ + mfxFrameSurface1 *surf; + mfxBitstream bs; + mfxSyncPoint sp; + mfxEncodeCtrl *ctrl; + size_t frame_index; + + void Init(mfxU8 *bs_start, mfxU32 bs_size) + { + sp = nullptr; + ctrl = nullptr; + surf = nullptr; + + zero(bs); + bs.Data = bs_start; + bs.MaxLength = bs_size; + } +}; + +void InitFrame(mfxFrameData &frame, mfxU8 *Y, mfxU8 *UV, mfxU8 *V, mfxU16 pitch) +{ + zero(frame); + frame.Y = Y; + frame.UV = UV; + frame.V = V; + frame.Pitch = pitch; +} + +std::vector InitSEIUserData(bool use_cbr, const mfxVideoParam& params, const mfxVersion& ver) +{ + using namespace std; + vector data; + + const mfxU8 UUID[] = { 0x6d, 0x1a, 0x26, 0xa0, 0xbd, 0xdc, 0x11, 0xe2, //ISO-11578 UUID + 0x90, 0x24, 0x00, 0x50, 0xc2, 0x49, 0x00, 0x48 }; //6d1a26a0-bddc-11e2-9024-0050c2490048 + data.insert(end(data), begin(UUID), end(UUID)); + + ostringstream str; + str << "QSV hardware encoder options:" + << " rate control: " << (use_cbr ? "cbr" : "vbr") + << "; target bitrate: " << params.mfx.TargetKbps + << "; max bitrate: " << params.mfx.MaxKbps + << "; buffersize: " << params.mfx.BufferSizeInKB*8 + << "; API level: " << ver.Major << "." << ver.Minor; + string str_(str.str()); + + data.insert(end(data), begin(str_), end(str_)); + + return data; +} \ No newline at end of file diff --git a/QSVHelper/Utilities.h b/QSVHelper/Utilities.h new file mode 100644 index 00000000..45217e36 --- /dev/null +++ b/QSVHelper/Utilities.h @@ -0,0 +1,25 @@ +/******************************************************************************** + Copyright (C) 2013 Ruwen Hahn + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +********************************************************************************/ + +#pragma once + +template +void zero(T& t, size_t size=sizeof(T)) +{ + memset(&t, 0, size); +} \ No newline at end of file diff --git a/QSVHelper/WindowsStuff.h b/QSVHelper/WindowsStuff.h new file mode 100644 index 00000000..65035e41 --- /dev/null +++ b/QSVHelper/WindowsStuff.h @@ -0,0 +1,221 @@ +/******************************************************************************** + Copyright (C) 2013 Ruwen Hahn + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +********************************************************************************/ + +#pragma once + +#include + +#include +#include + +#include +#include +#include +#include + + +struct safe_handle +{ + HANDLE h; + operator bool() const { return h != nullptr; } + operator HANDLE() { return h; } + + void reset(HANDLE h_=nullptr) { if(h) CloseHandle(h); h = h_; } + + bool operator!() const { return !h; } + + safe_handle &operator=(safe_handle &&other) { reset(other.h); other.h = nullptr; return *this; } + + safe_handle(HANDLE h=nullptr) : h(h) {} + ~safe_handle() { if(h) CloseHandle(h); } + safe_handle(safe_handle &&other) : h(other.h) { other.h = nullptr; }; +private: + safe_handle(const safe_handle&); +}; + +struct NamedSharedMemory +{ + std::wstring name; + uint64_t size; + void *memory; + safe_handle file; + + bool operator!() const { return !memory || !file; } + void *operator&() { return memory; } + + template + T &as() { return *reinterpret_cast(memory); } + + NamedSharedMemory &operator=(NamedSharedMemory &&other) + { FreeMemory(); name = other.name; size = other.size; memory = other.memory; other.memory = nullptr; file = std::move(other.file); return *this; } + + //NamedSharedMemory(const NamedSharedMemory& other) : name(other.name), size(other.size), memory(nullptr) { InitMemory(); } + NamedSharedMemory(NamedSharedMemory&& other) + : name(std::move(other.name)), size(std::move(other.size)), memory(std::move(other.memory)), file(std::move(other.file)) { other.memory = nullptr; } + ~NamedSharedMemory() { FreeMemory(); } + NamedSharedMemory(std::wstring name, uint64_t size=1) : name(name), size(size), memory(nullptr) { InitMemory(); } + NamedSharedMemory() : memory(nullptr) {} + +private: + void FreeMemory() { if(memory) UnmapViewOfFile(memory); memory = nullptr; } + void InitMemory() + { + if(!size) + size = 1; + file.reset(CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, size>>32, size & 0xffffffff, name.c_str())); + if(file) + memory = MapViewOfFile(file, FILE_MAP_ALL_ACCESS, 0, 0, 0); + } +}; + +struct IPCSignal +{ + safe_handle signal_; + + bool is_signalled(DWORD timeout=0) { return WaitForSingleObject(signal_, timeout) == WAIT_OBJECT_0; } + void signal() { SetEvent(signal_); } + + bool operator!() const { return !signal_; } + + IPCSignal &operator=(IPCSignal &&other) { signal_ = std::move(other.signal_); return *this; } + + IPCSignal(std::wstring name) { signal_.reset(CreateEvent(nullptr, false, false, name.c_str())); } + IPCSignal() {} +}; + +struct IPCWaiter +{ + std::vector list; + void push_back(const HANDLE &h) { list.push_back(h); } + bool wait(DWORD timeout=0) { if(!list.size()) return false; auto res = wait_for_multiple_objects(timeout); return WAIT_OBJECT_0 <= res && res < (WAIT_OBJECT_0+list.size()); } + bool wait_for(DWORD object, DWORD timeout=0) { if(!list.size()) return false; return wait_for_multiple_objects(timeout) == (WAIT_OBJECT_0 + object); } + bool wait_for_two(DWORD first, DWORD second, DWORD timeout=0) { if(!list.size()) return false; auto res = wait_for_multiple_objects(timeout); return res == (WAIT_OBJECT_0 + first) || res == (WAIT_OBJECT_0 + second); } + bool wait_timeout(DWORD timeout=0) { if(!list.size()) return false; return wait_for_multiple_objects(timeout) == WAIT_TIMEOUT; } + +private: + DWORD wait_for_multiple_objects(DWORD timeout) { return WaitForMultipleObjects(static_cast(list.size()), &list.front(), false, timeout); } +}; + +struct IPCMutex +{ + safe_handle mutex_; + + void lock() { if(mutex_) WaitForSingleObject(mutex_, INFINITE); } + void unlock() { if(mutex_) ReleaseMutex(mutex_); } + + bool operator!() { return !mutex_; } + + IPCMutex &operator=(IPCMutex &&other) { mutex_ = std::move(other.mutex_); return *this; } + + IPCMutex(std::wstring name) { mutex_.reset(CreateMutex(nullptr, false, name.c_str())); } + IPCMutex() {} +}; + +template +struct IPCMutexLock +{ + T& t; + bool enabled; + + IPCMutexLock(T &t) : t(t), enabled(true) { t.lock(); } + ~IPCMutexLock() { if(enabled) t.unlock(); } + IPCMutexLock(IPCMutexLock &&other) : t(other.t), enabled(other.enabled) { other.enabled = false; } +}; + +template +IPCMutexLock lock_mutex(T &t) { return IPCMutexLock(t); } + +template +void with_locked(T &t, F &f) { IPCMutexLock lock(t); f(); } + +template +struct IPCType +{ + NamedSharedMemory memory; + + bool operator!() const { return !memory; } + operator T() { return memory.as(); } + T *operator&() { return &memory.as(); } + T *operator->() { return &memory.as(); } + T &operator*() { return memory.as(); } + + IPCType &operator=(IPCType &&other) { memory = std::move(other.memory); return *this; } + + IPCType(std::wstring name) : memory(name, sizeof(T)) {} + IPCType() {} +}; + +template +struct IPCSignalledType : IPCType, IPCSignal +{ + bool operator!() const { return !memory || !signal_; } + + IPCSignalledType &operator=(IPCSignalledType &&other) { memory = std::move(other.memory); signal_ = std::move(other.signal_); return *this; } + + IPCSignalledType(std::wstring name) : IPCType(name), IPCSignal(name+L"Signal") {} + IPCSignalledType() {} +}; + +template +struct IPCLockedSignalledType : IPCSignalledType, IPCMutex +{ + bool operator!() const { return !memory || !signal_ || !mutex_; } + + IPCLockedSignalledType &operator=(IPCLockedSignalledType &&other) { memory = std::move(other.memory); signal_ = std::move(other.signal_); mutex_ = std::move(other.mutex_); return *this; } + + IPCLockedSignalledType(std::wstring name) : IPCSignalledType(name), IPCMutex(name+L"Lock") {} + IPCLockedSignalledType() {} +}; + +template +struct IPCArray +{ + NamedSharedMemory memory; + size_t size; + + bool operator!() const { return !memory; } + operator T*() { return static_cast(&memory); } + + IPCArray &operator=(IPCArray &&other) { memory = std::move(other.memory); size = other.size; return *this; } + + IPCArray(std::wstring name, size_t size) : memory(name, sizeof(T)*size), size(size) {} + IPCArray() {} +}; + +template +struct IPCSignalledArray : IPCArray, IPCSignal +{ + bool operator!() const { return !memory || !signal_; } + + IPCSignalledArray &operator=(IPCSignalledArray &&other) { memory = std::move(other.memory); signal_ = std::move(other.signal_); size = other.size; return *this; } + + IPCSignalledArray(std::wstring name, size_t size) : IPCArray(name, size), IPCSignal(name+L"Signal") {} + IPCSignalledArray() {} +}; + +template +struct IPCLockedSignalledArray : IPCSignalledArray, IPCMutex +{ + bool operator!() { return !memory || !signal_ || !mutex_; } + + IPCLockedSignalledArray &operator=(IPCLockedSignalledArray &&other) + { memory = std::move(other.memory); signal_ = std::move(other.signal_); mutex_ = std::move(other.mutex_); size = other.size; return *this; } + + IPCLockedSignalledArray(std::wstring name, size_t size) : IPCSignalledArray(name, size), IPCMutex(name+L"Lock") {} + IPCLockedSignalledArray() {} +}; \ No newline at end of file diff --git a/Source/Encoder_QSV.cpp b/Source/Encoder_QSV.cpp index d4bc6ac8..12fa5eb1 100644 --- a/Source/Encoder_QSV.cpp +++ b/Source/Encoder_QSV.cpp @@ -32,6 +32,9 @@ #include #include +#include "../QSVHelper/IPCInfo.h" +#include "../QSVHelper/WindowsStuff.h" + extern "C" { #include "../x264/x264.h" @@ -42,17 +45,11 @@ namespace #define TO_STR(a) TEXT(#a) const float baseCRF = 22.0f; - const struct impl_parameters { + const struct impl_parameters + { mfxU32 type, intf; mfxVersion version; - } validImpl[] = { - { MFX_IMPL_HARDWARE_ANY, MFX_IMPL_VIA_D3D11, {6, 1} }, - { MFX_IMPL_HARDWARE, MFX_IMPL_VIA_D3D11, {6, 1} }, - { MFX_IMPL_HARDWARE_ANY, MFX_IMPL_VIA_D3D9, {6, 1} }, //Ivy Bridge+ with non-functional D3D11 support? - { MFX_IMPL_HARDWARE, MFX_IMPL_VIA_D3D9, {6, 1} }, - { MFX_IMPL_HARDWARE_ANY, MFX_IMPL_VIA_D3D9, {4, 1} }, //Sandy Bridge - { MFX_IMPL_HARDWARE, MFX_IMPL_VIA_D3D9, {4, 1} }, }; const TCHAR* implStr[] = { TO_STR(MFX_IMPL_AUTO), @@ -89,32 +86,6 @@ namespace } }; - void ConvertFrameRate(mfxF64 dFrameRate, mfxU32& pnFrameRateExtN, mfxU32& pnFrameRateExtD) - { - mfxU32 fr; - - fr = (mfxU32)(dFrameRate + .5); - - if (fabs(fr - dFrameRate) < 0.0001) - { - pnFrameRateExtN = fr; - pnFrameRateExtD = 1; - return; - } - - fr = (mfxU32)(dFrameRate * 1.001 + .5); - - if (fabs(fr * 1000 - dFrameRate * 1001) < 10) - { - pnFrameRateExtN = fr * 1000; - pnFrameRateExtD = 1001; - return; - } - - pnFrameRateExtN = (mfxU32)(dFrameRate * 10000 + .5); - pnFrameRateExtD = 10000; - } - #define MFX_TIME_FACTOR 90 template auto timestampFromMS(T t) -> decltype(t*MFX_TIME_FACTOR) @@ -129,35 +100,73 @@ namespace } #undef MFX_TIME_FACTOR - struct MutexLock - { - HANDLE mutex; - ~MutexLock() { OSLeaveMutex(mutex); } - MutexLock(HANDLE mutex_) : mutex(mutex_) { OSEnterMutex(mutex); } - private: - MutexLock() {} - }; - template void zero(T& t, size_t size=sizeof(T)) { memset(&t, 0, size); } + + + bool spawn_helper(String &event_prefix, safe_handle &qsvhelper_process, safe_handle &qsvhelper_thread, IPCWaiter &process_waiter) + { + String helper_path; + auto dir_size = GetCurrentDirectory(0, NULL); + helper_path.SetLength(dir_size); + GetCurrentDirectory(dir_size, helper_path); + + helper_path << "/"; + + String helper = helper_path; + String helper_name = "QSVHelper.exe"; + helper << helper_name; + + PROCESS_INFORMATION pi; + STARTUPINFO si; + + zero(pi); + zero(si); + si.cb = sizeof(si); + + if(!CreateProcess(helper, helper_name+" "+OBSGetAppDataPath(), nullptr, nullptr, false, 0, nullptr, helper_path, &si, &pi)) + return false; + + qsvhelper_process.reset(pi.hProcess); + qsvhelper_thread.reset(pi.hThread); + process_waiter.push_back(pi.hProcess); + process_waiter.push_back(pi.hThread); + + event_prefix = FormattedString(TEXT("%s%u"), helper_name.Array(), pi.dwProcessId); + + return true; + } } bool CheckQSVHardwareSupport(bool log=true) { - MFXVideoSession test; - for(auto impl = std::begin(validImpl); impl != std::end(validImpl); impl++) + safe_handle helper_process, helper_thread; + IPCWaiter waiter; + String event_prefix; + if(!spawn_helper(event_prefix, helper_process, helper_thread, waiter)) + return false; + + ipc_init_request req((event_prefix + INIT_REQUEST).Array()); + req->mode = req->MODE_QUERY; + req.signal(); + + if(!waiter.wait(INFINITE)) + return false; + + DWORD code = 0; + if(!GetExitCodeProcess(helper_process.h, &code)) + return false; + + if(code == 0) { - mfxVersion ver = impl->version; - auto result = test.Init(impl->type | impl->intf, &ver); - if(result != MFX_ERR_NONE) - continue; if(log) Log(TEXT("Found QSV hardware support")); return true; } + if(log) Log(TEXT("Failed to initialize QSV hardware session")); return false; @@ -169,58 +178,50 @@ struct VideoPacket inline void FreeData() {Packet.Clear();} }; - class QSVEncoder : public VideoEncoder { mfxVersion ver; - MFXVideoSession session; + safe_handle qsvhelper_process, + qsvhelper_thread; + IPCSignal stop; - mfxVideoParam params, - query; - - std::unique_ptr enc; - - List sei_message_buffer; - - List sei_payloads; - - List sei_payload_list; - - mfxEncodeCtrl ctrl, - sei_ctrl; - - List bs_buff; + ipc_bitstream_buff bs_buff; + ipc_bitstream_info bs_info; struct encode_task { mfxFrameSurface1 surf; mfxBitstream bs; - mfxSyncPoint sp; - mfxEncodeCtrl *ctrl; mfxFrameData *frame; }; List encode_tasks; - CircularList msdk_locked_tasks, - encoded_tasks, - queued_tasks, + CircularList queued_tasks, idle_tasks; - List frame_buff; + IPCLockedSignalledArray frame_queue; + + ipc_frame_buff frame_buff; + ipc_frame_buff_status frame_buff_status; + ipc_filled_bitstream filled_bitstream; + + IPCWaiter process_waiter, + filled_bitstream_waiter; + List frames; - HANDLE frame_mutex; + + mfxU16 target_usage, + max_bitrate; + + String event_prefix; int fps; - bool bUsingDecodeTimestamp; - - bool bRequestKeyframe; + bool bRequestKeyframe, + bFirstFrameProcessed; UINT width, height; - bool bFirstFrameProcessed, - bFirstFrameQueued; - bool bUseCBR, bUseCFR; List CurrentPackets; @@ -237,79 +238,10 @@ class QSVEncoder : public VideoEncoder CurrentPackets.Clear(); } -#ifndef SEI_USER_DATA_UNREGISTERED -#define SEI_USER_DATA_UNREGISTERED 0x5 -#endif - - void AddSEIData(const List& payload, mfxU16 type) - { - unsigned offset = sei_message_buffer.Num(); - - mfxU16 type_ = type; - while(type_ > 255) - { - sei_message_buffer << 0xff; - type_ -= 255; - } - sei_message_buffer << (mfxU8)type_; - - unsigned payload_size = payload.Num(); - while(payload_size > 255) - { - sei_message_buffer << 0xff; - payload_size -= 255; - } - sei_message_buffer << payload_size; - - sei_message_buffer.AppendList(payload); - - mfxPayload& sei_payload = *sei_payloads.CreateNew(); - - zero(sei_payload); - - sei_payload.Type = type; - sei_payload.BufSize = sei_message_buffer.Num()-offset; - sei_payload.NumBit = sei_payload.BufSize*8; - sei_payload.Data = sei_message_buffer.Array()+offset; - - sei_payload_list << &sei_payload; - - sei_ctrl.Payload = sei_payload_list.Array(); - sei_ctrl.NumPayload = sei_payload_list.Num(); - } - - void InitSEIUserData() - { - List payload; - - const mfxU8 UUID[] = { 0x6d, 0x1a, 0x26, 0xa0, 0xbd, 0xdc, 0x11, 0xe2, //ISO-11578 UUID - 0x90, 0x24, 0x00, 0x50, 0xc2, 0x49, 0x00, 0x48 }; //6d1a26a0-bddc-11e2-9024-0050c2490048 - payload.AppendArray(UUID, 16); - - String str; - str << TEXT("QSV hardware encoder options:") - << TEXT(" rate control: ") << (bUseCBR ? TEXT("cbr") : TEXT("vbr")) - << TEXT("; target bitrate: ") << params.mfx.TargetKbps - << TEXT("; max bitrate: ") << query.mfx.MaxKbps - << TEXT("; buffersize: ") << query.mfx.BufferSizeInKB*8 - << TEXT("; API level: ") << ver.Major << TEXT(".") << ver.Minor; - - LPSTR info = str.CreateUTF8String(); - payload.AppendArray((LPBYTE)info, (unsigned)strlen(info)+1); - Free(info); - - AddSEIData(payload, SEI_USER_DATA_UNREGISTERED); - } - - void ForceD3D9Config() - { - GlobalConfig->SetInt(TEXT("Video Encoding"), TEXT("QSVDisableD3D11"), 1); - } - -#define ALIGN16(value) (((value + 15) >> 4) << 4) // round up to a multiple of 16 public: + QSVEncoder(int fps_, int width, int height, int quality, CTSTR preset, bool bUse444, ColorDescription &colorDesc, int maxBitrate, int bufferSize, bool bUseCFR_) - : enc(nullptr), bFirstFrameProcessed(false), bFirstFrameQueued(false) + : bFirstFrameProcessed(false), width(width), height(height), max_bitrate(maxBitrate) { fps = fps_; @@ -318,56 +250,8 @@ public: UINT keyframeInterval = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("KeyframeInterval"), 6); - zero(params); - params.mfx.CodecId = MFX_CODEC_AVC; - params.mfx.TargetUsage = MFX_TARGETUSAGE_BEST_QUALITY; - params.mfx.TargetKbps = maxBitrate; - params.mfx.MaxKbps = maxBitrate; - params.mfx.BufferSizeInKB = bufferSize/8; - params.mfx.GopOptFlag = MFX_GOP_CLOSED; - params.mfx.GopPicSize = fps*keyframeInterval; - params.mfx.GopRefDist = 8; - params.mfx.NumSlice = 1; - - params.mfx.RateControlMethod = bUseCBR ? MFX_RATECONTROL_CBR : MFX_RATECONTROL_VBR; - params.IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY; - - auto& fi = params.mfx.FrameInfo; - ConvertFrameRate(fps, fi.FrameRateExtN, fi.FrameRateExtD); - - fi.FourCC = MFX_FOURCC_NV12; - fi.ChromaFormat = bUse444 ? MFX_CHROMAFORMAT_YUV444 : MFX_CHROMAFORMAT_YUV420; - fi.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; - - fi.Width = ALIGN16(width); - fi.Height = ALIGN16(height); - - fi.CropX = 0; - fi.CropY = 0; - fi.CropW = width; - fi.CropH = height; - - this->width = width; - this->height = height; - - List ext_params; - - mfxExtVideoSignalInfo vsi; - zero(vsi); - vsi.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO; - vsi.Header.BufferSz = sizeof(vsi); - - vsi.ColourDescriptionPresent = 1; - vsi.ColourPrimaries = colorDesc.primaries; - vsi.MatrixCoefficients = colorDesc.matrix; - vsi.TransferCharacteristics = colorDesc.transfer; - vsi.VideoFullRange = colorDesc.fullRange; - vsi.VideoFormat = 5; //unspecified - - ext_params << (mfxExtBuffer*)&vsi; - - params.ExtParam = ext_params.Array(); - params.NumExtParam = ext_params.Num(); + int keyint = fps*keyframeInterval; + int bframes = 7; bool bHaveCustomImpl = false; impl_parameters custom = { 0 }; @@ -396,17 +280,17 @@ public: if(strParamName == "keyint") { - int keyint = strParamVal.ToInt(); - if(keyint < 0) + int keyint_ = strParamVal.ToInt(); + if(keyint_ < 0) continue; - params.mfx.GopPicSize = keyint; + keyint = keyint_; } else if(strParamName == "bframes") { - int bframes = strParamVal.ToInt(); - if(bframes < 0) + int bframes_ = strParamVal.ToInt(); + if(bframes_ < 0) continue; - params.mfx.GopRefDist = bframes+1; + bframes = bframes_; } else if(strParamName == "qsvimpl") { @@ -435,165 +319,395 @@ public: } } - mfxFrameAllocRequest req; - zero(req); + if(!spawn_helper(event_prefix, qsvhelper_process, qsvhelper_thread, process_waiter)) + CrashError(TEXT("Couldn't launch QSVHelper: %u"), GetLastError()); - auto log_impl = [&](mfxIMPL impl, const mfxIMPL intf) { - mfxIMPL actual; - session.QueryIMPL(&actual); - auto intf_str = qsv_intf_str(intf), - actual_intf_str = qsv_intf_str(actual); - auto length = std::distance(std::begin(implStr), std::end(implStr)); - if(impl > length) impl = static_cast(length-1); - Log(TEXT("QSV version %u.%u using %s%s (actual: %s%s)"), ver.Major, ver.Minor, - implStr[impl], intf_str, implStr[actual & (MFX_IMPL_VIA_ANY - 1)], actual_intf_str); - }; + ipc_init_request request((event_prefix + INIT_REQUEST).Array()); + + request->mode = request->MODE_ENCODE; + request->obs_process_id = GetCurrentProcessId(); + + request->fps = fps_; + request->keyint = keyint; + request->bframes = bframes; + request->width = width; + request->height = height; + request->max_bitrate = maxBitrate; + request->buffer_size = bufferSize; + request->use_cbr = bUseCBR; + request->full_range = colorDesc.fullRange; + request->matrix = colorDesc.matrix; + request->primaries = colorDesc.primaries; + request->transfer = colorDesc.transfer; + request->use_custom_impl = bHaveCustomImpl; + request->custom_impl = custom.type; + request->custom_intf = custom.intf; + request->custom_version = custom.version; + + request.signal(); + + ipc_init_response response((event_prefix + INIT_RESPONSE).Array()); + IPCWaiter response_waiter = process_waiter; + response_waiter.push_back(response.signal_); + if(response_waiter.wait_for_two(0, 1, INFINITE)) + { + DWORD code = 0; + if(!GetExitCodeProcess(qsvhelper_process.h, &code)) + CrashError(TEXT("Failed to initialize QSV session.")); + switch(code) + { + case EXIT_INCOMPATIBLE_CONFIGURATION: + CrashError(TEXT("QSVHelper.exe has exited because of an incompatible qsvimpl custom parameter (before response)")); + default: + CrashError(TEXT("QSVHelper.exe has exited with code %i (before response)"), code); + } + } Log(TEXT("------------------------------------------")); - auto try_impl = [&](impl_parameters impl_params) -> mfxStatus { - enc.reset(nullptr); - session.Close(); + if(bHaveCustomImpl && !response->using_custom_impl) + AppWarning(TEXT("Could not initialize QSV session using custom settings")); - ver = impl_params.version; - auto result = session.Init(impl_params.type | impl_params.intf, &ver); - if(result < 0) return result; + ver = response->version; + auto intf_str = qsv_intf_str(response->requested_impl), + actual_intf_str = qsv_intf_str(response->actual_impl); + auto length = std::distance(std::begin(implStr), std::end(implStr)); + auto impl = response->requested_impl & (MFX_IMPL_VIA_ANY - 1); + if(impl > length) impl = static_cast(length-1); + Log(TEXT("QSV version %u.%u using %s%s (actual: %s%s)"), ver.Major, ver.Minor, + implStr[impl], intf_str, implStr[response->actual_impl & (MFX_IMPL_VIA_ANY - 1)], actual_intf_str); - enc.reset(new MFXVideoENCODE(session)); - enc->Close(); - return enc->QueryIOSurf(¶ms, &req); - }; + target_usage = response->target_usage; - mfxStatus sts; - if(bHaveCustomImpl) - { - sts = try_impl(custom); - if(sts < 0) - AppWarning(TEXT("Could not initialize QSV session using custom settings")); - else - log_impl(custom.type, custom.intf); - } + encode_tasks.SetSize(response->bitstream_num); - - bool disable_d3d11 = GlobalConfig->GetInt(TEXT("Video Encoding"), TEXT("QSVDisableD3D11")) != 0; + bs_buff = ipc_bitstream_buff((event_prefix + BITSTREAM_BUFF).Array(), response->bitstream_size*response->bitstream_num); - if(!enc) - { - decltype(std::begin(validImpl)) best = nullptr; - for(auto impl = std::begin(validImpl); impl != std::end(validImpl); impl++) - { - if(disable_d3d11 && impl->intf == MFX_IMPL_VIA_D3D11) - continue; - sts = try_impl(*impl); - if(sts == MFX_WRN_PARTIAL_ACCELERATION && !best) - best = impl; - if(sts != MFX_ERR_NONE) - continue; - log_impl(impl->type, impl->intf); - break; - } - if(!enc) - { - if(!best) - CrashError(TEXT("Failed to initialize QSV session.")); - sts = try_impl(*best); - log_impl(best->type, best->intf); - } - } + if(!bs_buff) + CrashError(TEXT("Failed to initialize QSV bitstream buffer (%u)"), GetLastError()); - session.SetPriority(MFX_PRIORITY_HIGH); - - if(sts == MFX_WRN_PARTIAL_ACCELERATION) //This indicates software encoding for most users, while others seem to get full hardware acceleration - { - mfxIMPL impl; - session.QueryIMPL(&impl); - if(impl & MFX_IMPL_VIA_D3D11) - AppWarning(TEXT("QSV hardware acceleration seems to be unavailable. It is safe to ignore this warning\n") - TEXT("unless there are further problems like frame drops or artifacts in the video output.\n") - TEXT("If you experience any of these problems try to connect at least one monitor to your\n") - TEXT("iGPU or follow the guide at\n") - TEXT("\n") - TEXT("and enable advanced encoder settings (for qsv) with the line: qsvimpl=,d3d9,%d.%d"), ver.Major, ver.Minor); - else - AppWarning(TEXT("QSV hardware acceleration seems to be unavailable. It is safe to ignore this warning\n") - TEXT("unless there are further problems like frame drops or artifacts in the video output.\n")); - } - - enc->Init(¶ms); - - memcpy(&query, ¶ms, sizeof(params)); - enc->GetVideoParam(&query); - - unsigned num_surf = max(6, req.NumFrameSuggested + query.AsyncDepth); - - encode_tasks.SetSize(num_surf); - - const unsigned bs_size = (max(query.mfx.BufferSizeInKB*1000, bufferSize/8*1000)+31)/32*32; - bs_buff.SetSize(bs_size * encode_tasks.Num() + 31); - params.mfx.BufferSizeInKB = bs_size/1000; - - mfxU8* bs_start = (mfxU8*)(((size_t)bs_buff.Array() + 31)/32*32); + mfxU8 *bs_start = (mfxU8*)(((size_t)&bs_buff + 31)/32*32); for(unsigned i = 0; i < encode_tasks.Num(); i++) { - encode_tasks[i].sp = nullptr; - encode_tasks[i].ctrl = nullptr; + zero(encode_tasks[i].surf); - mfxFrameSurface1& surf = encode_tasks[i].surf; - zero(surf); - memcpy(&surf.Info, ¶ms.mfx.FrameInfo, sizeof(params.mfx.FrameInfo)); - - mfxBitstream& bs = encode_tasks[i].bs; + mfxBitstream &bs = encode_tasks[i].bs; zero(bs); - bs.Data = bs_start + i*bs_size; - bs.MaxLength = bs_size; + bs.Data = bs_start + i*response->bitstream_size; + bs.MaxLength = response->bitstream_size; idle_tasks << i; } - frames.SetSize(num_surf+3); //+NUM_OUT_BUFFERS + frames.SetSize(response->frame_num); - const unsigned lum_channel_size = fi.Width*fi.Height, - uv_channel_size = fi.Width*fi.Height, - frame_size = lum_channel_size + uv_channel_size; - frame_buff.SetSize(frame_size * frames.Num() + 15); + frame_buff = ipc_frame_buff((event_prefix + FRAME_BUFF).Array(), response->frame_size*response->frame_num); - mfxU8* frame_start = (mfxU8*)(((size_t)frame_buff.Array() + 15)/16*16); - zero(*frame_start, frame_size * frames.Num()); + if(!frame_buff) + CrashError(TEXT("Failed to initialize QSV frame buffer (%u)"), GetLastError()); + + mfxU8 *frame_start = (mfxU8*)(((size_t)&frame_buff + 15)/16*16); for(unsigned i = 0; i < frames.Num(); i++) { mfxFrameData& frame = frames[i]; zero(frame); - frame.Y = frame_start + i * frame_size; - frame.UV = frame_start + i * frame_size + lum_channel_size; - frame.V = frame.UV + 1; - frame.Pitch = fi.Width; + frame.Y = frame_start + i * response->frame_size; + frame.UV = frame_start + i * response->frame_size + response->UV_offset; + frame.V = frame_start + i * response->frame_size + response->V_offset; + frame.Pitch = response->frame_pitch; } - frame_mutex = OSCreateMutex(); - - Log(TEXT("Using %u encode tasks"), encode_tasks.Num()); + Log(TEXT("Using %u bitstreams and %u frame buffers"), encode_tasks.Num(), frames.Num()); Log(TEXT("------------------------------------------")); - Log(TEXT("%s"), GetInfoString().Array()); + Log(GetInfoString()); Log(TEXT("------------------------------------------")); - zero(ctrl); - ctrl.FrameType = MFX_FRAMETYPE_I | MFX_FRAMETYPE_REF | MFX_FRAMETYPE_IDR; - - zero(sei_ctrl); - - InitSEIUserData(); - - bUsingDecodeTimestamp = false && ver.Minor >= 6; - DataPacket packet; GetHeaders(packet); + + frame_queue = ipc_frame_queue((event_prefix + FRAME_QUEUE).Array(), frames.Num()); + if(!frame_queue) + CrashError(TEXT("Failed to initialize frame queue (%u)"), GetLastError()); + + frame_buff_status = ipc_frame_buff_status((event_prefix + FRAME_BUFF_STATUS).Array(), frames.Num()); + if(!frame_buff_status) + CrashError(TEXT("Failed to initialize QSV frame buffer status (%u)"), GetLastError()); + + bs_info = ipc_bitstream_info((event_prefix + BITSTREAM_INFO).Array(), response->bitstream_num); + if(!bs_info) + CrashError(TEXT("Failed to initialize QSV bitstream info (%u)"), GetLastError()); + + filled_bitstream = ipc_filled_bitstream((event_prefix + FILLED_BITSTREAM).Array()); + if(!filled_bitstream) + CrashError(TEXT("Failed to initialize bitstream signal (%u)"), GetLastError()); + + stop = ipc_stop((event_prefix + STOP_REQUEST).Array()); + if(!stop) + CrashError(TEXT("Failed to initialize QSV stop signal (%u)"), GetLastError()); + + filled_bitstream_waiter = process_waiter; + filled_bitstream_waiter.push_back(filled_bitstream.signal_); } ~QSVEncoder() { + stop.signal(); ClearPackets(); - OSCloseMutex(frame_mutex); + } + +#ifndef SEI_USER_DATA_UNREGISTERED +#define SEI_USER_DATA_UNREGISTERED 0x5 +#endif + + void ProcessEncodedFrame(List &packets, List &packetTypes, DWORD outputTimestamp, mfxU32 wait=0) + { + if(!filled_bitstream_waiter.wait_for(2, wait)) + return; + + uint32_t index = 0; + { + auto lock = lock_mutex(filled_bitstream); + index = *filled_bitstream; + *filled_bitstream = -1; + } + encode_task& task = encode_tasks[index]; + + mfxBitstream& bs = task.bs; + + List nalOut; + mfxU8 *start, *end; + { + bitstream_info &info = bs_info[index]; + bs.TimeStamp = info.time_stamp; + bs.DataLength = info.data_length; + bs.DataOffset = info.data_offset; + bs.PicStruct = info.pic_struct; + bs.FrameType = info.frame_type; + } + start = bs.Data + bs.DataOffset; + end = bs.Data + bs.DataOffset + bs.DataLength; + const static mfxU8 start_seq[] = {0, 0, 1}; + start = std::search(start, end, start_seq, start_seq+3); + while(start != end) + { + decltype(start) next = std::search(start+1, end, start_seq, start_seq+3); + x264_nal_t nal; + nal.i_ref_idc = start[3]>>5; + nal.i_type = start[3]&0x1f; + if(nal.i_type == NAL_SLICE_IDR) + nal.i_ref_idc = NAL_PRIORITY_HIGHEST; + else if(nal.i_type == NAL_SLICE) + { + switch(bs.FrameType & (MFX_FRAMETYPE_REF | (MFX_FRAMETYPE_S-1))) + { + case MFX_FRAMETYPE_REF|MFX_FRAMETYPE_I: + case MFX_FRAMETYPE_REF|MFX_FRAMETYPE_P: + nal.i_ref_idc = NAL_PRIORITY_HIGH; + break; + case MFX_FRAMETYPE_REF|MFX_FRAMETYPE_B: + nal.i_ref_idc = NAL_PRIORITY_LOW; + break; + case MFX_FRAMETYPE_B: + nal.i_ref_idc = NAL_PRIORITY_DISPOSABLE; + break; + default: + Log(TEXT("Unhandled frametype %u"), bs.FrameType); + } + } + start[3] = ((nal.i_ref_idc<<5)&0x60) | nal.i_type; + nal.p_payload = start; + nal.i_payload = int(next-start); + nalOut << nal; + start = next; + } + size_t nalNum = nalOut.Num(); + + packets.Clear(); + ClearPackets(); + + INT64 dts = outputTimestamp; + + INT64 in_pts = msFromTimestamp(task.surf.Data.TimeStamp), + out_pts = msFromTimestamp(bs.TimeStamp); + + if(!bFirstFrameProcessed && nalNum) + { + delayOffset = -dts; + bFirstFrameProcessed = true; + } + + INT64 ts = INT64(outputTimestamp); + int timeOffset; + + //if frame duplication is being used, the shift will be insignificant, so just don't bother adjusting audio + timeOffset = int(out_pts-dts); + timeOffset += frameShift; + + if(nalNum && timeOffset < 0) + { + frameShift -= timeOffset; + timeOffset = 0; + } + //Log(TEXT("inpts: %005d, dts: %005d, pts: %005d, timestamp: %005d, offset: %005d, newoffset: %005d"), task.surf.Data.TimeStamp/90, dts, bs.TimeStamp/90, outputTimestamp, timeOffset, bs.TimeStamp/90-dts); + + timeOffset = htonl(timeOffset); + + BYTE *timeOffsetAddr = ((BYTE*)&timeOffset)+1; + + VideoPacket *newPacket = NULL; + + PacketType bestType = PacketType_VideoDisposable; + bool bFoundFrame = false; + + for(unsigned i=0; iPacket); + + packetOut.OutputDword(htonl(sei_size+1)); + packetOut.Serialize(sei_start-1, sei_size+1); + } + sei_start += sei_size; + } + } + else if(nal.i_type == NAL_AUD) + { + BYTE *skip = nal.p_payload; + while(*(skip++) != 0x1); + int skipBytes = (int)(skip-nal.p_payload); + + int newPayloadSize = (nal.i_payload-skipBytes); + + if (!newPacket) + newPacket = CurrentPackets.CreateNew(); + + BufferOutputSerializer packetOut(newPacket->Packet); + + packetOut.OutputDword(htonl(newPayloadSize)); + packetOut.Serialize(nal.p_payload+skipBytes, newPayloadSize); + } + else if(nal.i_type == NAL_SLICE_IDR || nal.i_type == NAL_SLICE) + { + BYTE *skip = nal.p_payload; + while(*(skip++) != 0x1); + int skipBytes = (int)(skip-nal.p_payload); + + if (!newPacket) + newPacket = CurrentPackets.CreateNew(); + + if (!bFoundFrame) + { + newPacket->Packet.Insert(0, (nal.i_type == NAL_SLICE_IDR) ? 0x17 : 0x27); + newPacket->Packet.Insert(1, 1); + newPacket->Packet.InsertArray(2, timeOffsetAddr, 3); + + bFoundFrame = true; + } + + int newPayloadSize = (nal.i_payload-skipBytes); + BufferOutputSerializer packetOut(newPacket->Packet); + + packetOut.OutputDword(htonl(newPayloadSize)); + packetOut.Serialize(nal.p_payload+skipBytes, newPayloadSize); + + switch(nal.i_ref_idc) + { + case NAL_PRIORITY_DISPOSABLE: bestType = MAX(bestType, PacketType_VideoDisposable); break; + case NAL_PRIORITY_LOW: bestType = MAX(bestType, PacketType_VideoLow); break; + case NAL_PRIORITY_HIGH: bestType = MAX(bestType, PacketType_VideoHigh); break; + case NAL_PRIORITY_HIGHEST: bestType = MAX(bestType, PacketType_VideoHighest); break; + } + } + /*else if(nal.i_type == NAL_SPS) + { + VideoPacket *newPacket = CurrentPackets.CreateNew(); + BufferOutputSerializer headerOut(newPacket->Packet); + + headerOut.OutputByte(0x17); + headerOut.OutputByte(0); + headerOut.Serialize(timeOffsetAddr, 3); + headerOut.OutputByte(1); + headerOut.Serialize(nal.p_payload+5, 3); + headerOut.OutputByte(0xff); + headerOut.OutputByte(0xe1); + headerOut.OutputWord(htons(nal.i_payload-4)); + headerOut.Serialize(nal.p_payload+4, nal.i_payload-4); + + x264_nal_t &pps = nalOut[i+1]; //the PPS always comes after the SPS + + headerOut.OutputByte(1); + headerOut.OutputWord(htons(pps.i_payload-4)); + headerOut.Serialize(pps.p_payload+4, pps.i_payload-4); + }*/ + else + continue; + } + + packetTypes << bestType; + + packets.SetSize(CurrentPackets.Num()); + for(UINT i=0; i &packets, List &packetTypes, DWORD outputTimestamp, mfxU32 wait=0) - { - if(!encoded_tasks.Num()) - return; - - bool logged_start = false; - - for(unsigned i = 0; i < encoded_tasks.Num() && !packets.Num(); i++) - { - encode_task& task = encode_tasks[encoded_tasks[i]]; - auto& sp = task.sp; - - auto result = MFXVideoCORE_SyncOperation(session, sp, wait); - if(result == MFX_WRN_IN_EXECUTION) - continue; - - if(result == MFX_ERR_NONE) - { - mfxBitstream& bs = task.bs; - - List nalOut; - mfxU8 *start = bs.Data + bs.DataOffset, - *end = bs.Data + bs.DataOffset + bs.DataLength; - const static mfxU8 start_seq[] = {0, 0, 1}; - start = std::search(start, end, start_seq, start_seq+3); - while(start != end) - { - decltype(start) next = std::search(start+1, end, start_seq, start_seq+3); - x264_nal_t nal; - nal.i_ref_idc = start[3]>>5; - nal.i_type = start[3]&0x1f; - if(nal.i_type == NAL_SLICE_IDR) - nal.i_ref_idc = NAL_PRIORITY_HIGHEST; - else if(nal.i_type == NAL_SLICE) - { - switch(bs.FrameType & (MFX_FRAMETYPE_REF | (MFX_FRAMETYPE_S-1))) - { - case MFX_FRAMETYPE_REF|MFX_FRAMETYPE_I: - case MFX_FRAMETYPE_REF|MFX_FRAMETYPE_P: - nal.i_ref_idc = NAL_PRIORITY_HIGH; - break; - case MFX_FRAMETYPE_REF|MFX_FRAMETYPE_B: - nal.i_ref_idc = NAL_PRIORITY_LOW; - break; - case MFX_FRAMETYPE_B: - nal.i_ref_idc = NAL_PRIORITY_DISPOSABLE; - break; - default: - Log(TEXT("Unhandled frametype %u"), bs.FrameType); - } - } - start[3] = ((nal.i_ref_idc<<5)&0x60) | nal.i_type; - nal.p_payload = start; - nal.i_payload = int(next-start); - nalOut << nal; - start = next; - } - size_t nalNum = nalOut.Num(); - - packets.Clear(); - ClearPackets(); - - INT64 dts; - - if(bUsingDecodeTimestamp && bs.DecodeTimeStamp != MFX_TIMESTAMP_UNKNOWN) - { - dts = msFromTimestamp(bs.DecodeTimeStamp); - } - else - dts = outputTimestamp; - - INT64 in_pts = msFromTimestamp(task.surf.Data.TimeStamp), - out_pts = msFromTimestamp(bs.TimeStamp); - - if(!bFirstFrameProcessed && nalNum) - { - delayOffset = -dts; - bFirstFrameProcessed = true; - } - - INT64 ts = INT64(outputTimestamp); - int timeOffset; - - //if frame duplication is being used, the shift will be insignificant, so just don't bother adjusting audio - timeOffset = int(out_pts-dts); - timeOffset += frameShift; - - if(nalNum && timeOffset < 0) - { - frameShift -= timeOffset; - timeOffset = 0; - } - //Log(TEXT("inpts: %005d, dts: %005d, pts: %005d, timestamp: %005d, offset: %005d, newoffset: %005d"), task.surf.Data.TimeStamp/90, dts, bs.TimeStamp/90, outputTimestamp, timeOffset, bs.TimeStamp/90-dts); - - timeOffset = htonl(timeOffset); - - BYTE *timeOffsetAddr = ((BYTE*)&timeOffset)+1; - - VideoPacket *newPacket = NULL; - - PacketType bestType = PacketType_VideoDisposable; - bool bFoundFrame = false; - - for(unsigned i=0; iPacket); - - packetOut.OutputDword(htonl(sei_size+1)); - packetOut.Serialize(sei_start-1, sei_size+1); - } - sei_start += sei_size; - } - } - else if(nal.i_type == NAL_AUD) - { - BYTE *skip = nal.p_payload; - while(*(skip++) != 0x1); - int skipBytes = (int)(skip-nal.p_payload); - - int newPayloadSize = (nal.i_payload-skipBytes); - - if (!newPacket) - newPacket = CurrentPackets.CreateNew(); - - BufferOutputSerializer packetOut(newPacket->Packet); - - packetOut.OutputDword(htonl(newPayloadSize)); - packetOut.Serialize(nal.p_payload+skipBytes, newPayloadSize); - } - else if(nal.i_type == NAL_SLICE_IDR || nal.i_type == NAL_SLICE) - { - BYTE *skip = nal.p_payload; - while(*(skip++) != 0x1); - int skipBytes = (int)(skip-nal.p_payload); - - if (!newPacket) - newPacket = CurrentPackets.CreateNew(); - - if (!bFoundFrame) - { - newPacket->Packet.Insert(0, (nal.i_type == NAL_SLICE_IDR) ? 0x17 : 0x27); - newPacket->Packet.Insert(1, 1); - newPacket->Packet.InsertArray(2, timeOffsetAddr, 3); - - bFoundFrame = true; - } - - int newPayloadSize = (nal.i_payload-skipBytes); - BufferOutputSerializer packetOut(newPacket->Packet); - - packetOut.OutputDword(htonl(newPayloadSize)); - packetOut.Serialize(nal.p_payload+skipBytes, newPayloadSize); - - switch(nal.i_ref_idc) - { - case NAL_PRIORITY_DISPOSABLE: bestType = MAX(bestType, PacketType_VideoDisposable); break; - case NAL_PRIORITY_LOW: bestType = MAX(bestType, PacketType_VideoLow); break; - case NAL_PRIORITY_HIGH: bestType = MAX(bestType, PacketType_VideoHigh); break; - case NAL_PRIORITY_HIGHEST: bestType = MAX(bestType, PacketType_VideoHighest); break; - } - } - /*else if(nal.i_type == NAL_SPS) - { - VideoPacket *newPacket = CurrentPackets.CreateNew(); - BufferOutputSerializer headerOut(newPacket->Packet); - - headerOut.OutputByte(0x17); - headerOut.OutputByte(0); - headerOut.Serialize(timeOffsetAddr, 3); - headerOut.OutputByte(1); - headerOut.Serialize(nal.p_payload+5, 3); - headerOut.OutputByte(0xff); - headerOut.OutputByte(0xe1); - headerOut.OutputWord(htons(nal.i_payload-4)); - headerOut.Serialize(nal.p_payload+4, nal.i_payload-4); - - x264_nal_t &pps = nalOut[i+1]; //the PPS always comes after the SPS - - headerOut.OutputByte(1); - headerOut.OutputWord(htons(pps.i_payload-4)); - headerOut.Serialize(pps.p_payload+4, pps.i_payload-4); - }*/ - else - continue; - } - - packetTypes << bestType; - - packets.SetSize(CurrentPackets.Num()); - for(UINT i=0; iMemId = 0; - task.frame->Locked -= 1; - } - task.sp = nullptr; - idle_tasks << msdk_locked_tasks[i]; - msdk_locked_tasks.Remove(i); - } - } - void QueueEncodeTask(mfxFrameSurface1& pic) { encode_task& task = encode_tasks[idle_tasks[0]]; - queued_tasks << idle_tasks[0]; - idle_tasks.Remove(0); - if(bRequestKeyframe) - task.ctrl = &ctrl; - else - task.ctrl = nullptr; - bRequestKeyframe = false; + auto lock_queue = lock_mutex(frame_queue); - if(!bFirstFrameQueued) - task.ctrl = &sei_ctrl; - bFirstFrameQueued = true; + for(unsigned i = 0; i < frame_queue.size; i++) + { + queued_frame &info = frame_queue[i]; - MutexLock lock(frame_mutex); + if(info.is_new) + continue; + queued_tasks << idle_tasks[0]; + idle_tasks.Remove(0); + info.is_new = true; - mfxBitstream& bs = task.bs; - mfxFrameSurface1& surf = task.surf; - mfxFrameData& frame = frames[(unsigned)pic.Data.MemId-1]; - mfxSyncPoint& sp = task.sp; + info.request_keyframe = bRequestKeyframe; + bRequestKeyframe = false; - frame.Locked += 1; - task.frame = &frame; - - bs.DataLength = 0; - bs.DataOffset = 0; - surf.Data.Y = frame.Y; - surf.Data.UV = frame.UV; - surf.Data.V = frame.V; - surf.Data.Pitch = frame.Pitch; - surf.Data.TimeStamp = timestampFromMS(pic.Data.TimeStamp); + info.timestamp = task.surf.Data.TimeStamp = timestampFromMS(pic.Data.TimeStamp); + info.frame_index = (uint32_t)pic.Data.MemId-1; + auto lock_status = lock_mutex(frame_buff_status); + frame_buff_status[info.frame_index] += 1; + frame_queue.signal(); + return; + } + CrashError(TEXT("QSV encoder is too slow")); } bool Encode(LPVOID picInPtr, List &packets, List &packetTypes, DWORD outputTimestamp) { + if(!process_waiter.wait_timeout()) + { + int code = 0; + if(!GetExitCodeProcess(process_waiter.list[0], (LPDWORD)&code)) + CrashError(TEXT("QSVHelper.exe exited!")); + switch(code) + { + case EXIT_INCOMPATIBLE_CONFIGURATION: + CrashError(TEXT("QSVHelper.exe has exited because of an incompatible qsvimpl custom parameter")); + default: + CrashError(TEXT("QSVHelper.exe has exited with code %i"), code); + } + } + profileIn("ProcessEncodedFrame"); - mfxU32 wait = 0; - bool bMessageLogged = false; - auto timeout_ms = OSGetTime() + 1500; do { - if(!packets.Num()) - ProcessEncodedFrame(packets, packetTypes, outputTimestamp, wait); - - CleanupLockedTasks(); - - if(idle_tasks.Num()) - break; - - if(wait == INFINITE) - { - if(!bMessageLogged) - Log(TEXT("Error: encoder is taking too long, consider decreasing your FPS/increasing your bitrate")); - bMessageLogged = true; - - if(OSGetTime() >= timeout_ms) - { - mfxIMPL impl; - session.QueryIMPL(&impl); - if(impl & MFX_IMPL_VIA_D3D11) - { - ForceD3D9Config(); - CrashError(TEXT("QSV takes too long to encode frames. You are using QSV in Direct3D11 mode, possibly ") - TEXT("without any monitors connected to your iGPU. Please either connect a monitor to your ") - TEXT("iGPU or follow the guide at ") - TEXT(" ") - TEXT("(copy the url from the latest logfile). Your custom encoder settings were updated.")); - } - else - CrashError(TEXT("QSV takes too long to encode frames.")); - } - - Sleep(1); //wait for locked tasks to unlock - } - else - Log(TEXT("Error: all encode tasks in use, stalling pipeline")); - wait = INFINITE; + ProcessEncodedFrame(packets, packetTypes, outputTimestamp, idle_tasks.Num() ? 0 : INFINITE); } while(!idle_tasks.Num()); profileOut; if(picInPtr) { - mfxFrameSurface1& pic = *(mfxFrameSurface1*)picInPtr; - QueueEncodeTask(pic); + profileSegment("QueueEncodeTask"); + QueueEncodeTask(*(mfxFrameSurface1*)picInPtr); } - - profileIn("EncodeFrameAsync"); - - while(picInPtr && queued_tasks.Num()) - { - encode_task& task = encode_tasks[queued_tasks[0]]; - mfxBitstream& bs = task.bs; - mfxFrameSurface1& surf = task.surf; - mfxSyncPoint& sp = task.sp; - for(;;) - { - auto sts = enc->EncodeFrameAsync(task.ctrl, &surf, &bs, &sp); - - if(sts == MFX_ERR_NONE || (MFX_ERR_NONE < sts && sp)) - break; - if(sts == MFX_WRN_DEVICE_BUSY) - return false; - //if(!sp); //sts == MFX_ERR_MORE_DATA usually; retry the call (see MSDK examples) - //Log(TEXT("returned status %i, %u"), sts, insert); - } - encoded_tasks << queued_tasks[0]; - queued_tasks.Remove(0); - } - - while(!picInPtr && !queued_tasks.Num() && (encoded_tasks.Num() || msdk_locked_tasks.Num())) - { - if(idle_tasks.Num() <= 1) - return true; - encode_task& task = encode_tasks[idle_tasks[0]]; - task.bs.DataOffset = 0; - task.bs.DataLength = 0; - auto sts = enc->EncodeFrameAsync(nullptr, nullptr, &task.bs, &task.sp); - if(sts == MFX_ERR_MORE_DATA) - break; - if(!task.sp) - continue; - encoded_tasks << idle_tasks[0]; - idle_tasks.Remove(0); - } - - profileOut; - return true; } @@ -1039,22 +804,15 @@ public: { if(!HeaderPacket.Num()) { - mfxVideoParam header_query; - zero(header_query); - mfxU8 sps[100], pps[100]; - mfxExtCodingOptionSPSPPS headers; - zero(headers); - headers.Header.BufferId = MFX_EXTBUFF_CODING_OPTION_SPSPPS; - headers.Header.BufferSz = sizeof(mfxExtCodingOptionSPSPPS); - headers.SPSBuffer = sps; - headers.SPSBufSize = 100; - headers.PPSBuffer = pps; - headers.PPSBufSize = 100; - mfxExtBuffer *ext_buff[] = {(mfxExtBuffer*)&headers}; - header_query.ExtParam = ext_buff; - header_query.NumExtParam = 1; + IPCSignalledType spspps((event_prefix + SPSPPS_SIZES).Array()); + IPCSignalledArray sps((event_prefix + SPS_BUFF).Array(), spspps->sps_size), + pps((event_prefix + PPS_BUFF).Array(), spspps->pps_size); - auto sts = enc->GetVideoParam(&header_query); + IPCWaiter spspps_waiter = process_waiter; + spspps_waiter.push_back(spspps.signal_); + + if(!spspps_waiter.wait_for(2, INFINITE)) + return; BufferOutputSerializer headerOut(HeaderPacket); @@ -1067,13 +825,13 @@ public: headerOut.Serialize(sps+5, 3); headerOut.OutputByte(0xff); headerOut.OutputByte(0xe1); - headerOut.OutputWord(htons(headers.SPSBufSize-4)); - headerOut.Serialize(sps+4, headers.SPSBufSize-4); + headerOut.OutputWord(htons(spspps->sps_size-4)); + headerOut.Serialize(sps+4, spspps->sps_size-4); headerOut.OutputByte(1); - headerOut.OutputWord(htons(headers.PPSBufSize-4)); - headerOut.Serialize(pps+4, headers.PPSBufSize-4); + headerOut.OutputWord(htons(spspps->pps_size-4)); + headerOut.Serialize(pps+4, spspps->pps_size-4); } packet.lpPacket = HeaderPacket.Array(); @@ -1088,9 +846,7 @@ public: int GetBitRate() const { - if(params.mfx.RateControlMethod == MFX_RATECONTROL_CBR) - return params.mfx.MaxKbps; - return params.mfx.TargetKbps; + return max_bitrate; } String GetInfoString() const @@ -1100,14 +856,14 @@ public: strInfo << TEXT("Video Encoding: QSV") << TEXT("\r\n fps: ") << IntString(fps) << TEXT("\r\n width: ") << IntString(width) << TEXT(", height: ") << IntString(height) << - TEXT("\r\n target-usage: ") << usageStr[params.mfx.TargetUsage] << + TEXT("\r\n target-usage: ") << usageStr[target_usage] << TEXT("\r\n CBR: ") << CTSTR((bUseCBR) ? TEXT("yes") : TEXT("no")) << TEXT("\r\n CFR: ") << CTSTR((bUseCFR) ? TEXT("yes") : TEXT("no")) << - TEXT("\r\n max bitrate: ") << IntString(params.mfx.MaxKbps); + TEXT("\r\n max bitrate: ") << IntString(max_bitrate); if(!bUseCBR) { - strInfo << TEXT("\r\n buffer size: ") << IntString(params.mfx.BufferSizeInKB*8); + strInfo << TEXT("\r\n buffer size: ") << IntString(encode_tasks[0].bs.MaxLength*8/1000); } return strInfo; @@ -1132,7 +888,7 @@ public: virtual bool HasBufferedFrames() { - return (msdk_locked_tasks.Num() + encoded_tasks.Num() + queued_tasks.Num()) > 0; + return false; } }; diff --git a/installer/generate_binaries.bat b/installer/generate_binaries.bat index 472dbd4f..38c7ae60 100644 --- a/installer/generate_binaries.bat +++ b/installer/generate_binaries.bat @@ -43,6 +43,7 @@ copy ..\graphicscapture\graphicscapturehook\release\graphicscapturehook.dll .\32 copy ..\graphicscapture\graphicscapturehook\x64\release\graphicscapturehook64.dll .\32bit\plugins\graphicscapture copy ..\injectHelper\x64\release\injectHelper64.exe .\32bit\plugins\graphicscapture copy ..\x264\libs\32bit\libx264-136.dll .\32bit +copy ..\QSVHelper\Release\QSVHelper.exe .\32bit copy "%WindowsSDK80Path%Debuggers\x86\dbghelp.dll" .\32bit copy ..\x64\release\obs.exe .\64bit\ @@ -63,6 +64,7 @@ copy ..\graphicscapture\graphicscapturehook\release\graphicscapturehook.dll .\64 copy ..\graphicscapture\graphicscapturehook\x64\release\graphicscapturehook64.dll .\64bit\plugins\graphicscapture copy ..\injectHelper\release\injectHelper.exe .\64bit\plugins\graphicscapture copy ..\x264\libs\64bit\libx264-136.dll .\64bit +copy ..\QSVHelper\Release\QSVHelper.exe .\64bit copy "%WindowsSDK80Path%Debuggers\x64\dbghelp.dll" .\64bit copy ..\rundir\pdb32\*.pdb .\pdbs\32bit @@ -128,6 +130,7 @@ copy ..\obsapi\release\obsapi.dll .\upload\OBS\32bit\ copy ..\OBSHelp\OBSHelp.chm .\upload\OBS\32bit\ copy ..\rundir\pdb32\stripped\*.pdb .\upload\OBS\32bit\ copy ..\x264\libs\32bit\libx264-136.dll .\upload\OBS\32bit +copy ..\QSVHelper\Release\QSVHelper.exe .\upload\OBS\32bit copy "%WindowsSDK80Path%Debuggers\x86\dbghelp.dll" .\upload\OBS\32bit copy ..\x64\release\obs.exe .\upload\OBS\64bit\ @@ -135,6 +138,7 @@ copy ..\obsapi\x64\release\obsapi.dll .\upload\OBS\64bit\ copy ..\OBSHelp\OBSHelp.chm .\upload\OBS\64bit\ copy ..\rundir\pdb64\stripped\*.pdb .\upload\OBS\64bit\ copy ..\x264\libs\64bit\libx264-136.dll .\upload\OBS\64bit +copy ..\QSVHelper\Release\QSVHelper.exe .\upload\OBS\64bit copy "%WindowsSDK80Path%Debuggers\x64\dbghelp.dll" .\upload\OBS\64bit copy ..\rundir\locale\*.txt .\upload\OBS\locale\ diff --git a/installer/generate_binaries_test.bat b/installer/generate_binaries_test.bat index a69c20cf..0abee03b 100644 --- a/installer/generate_binaries_test.bat +++ b/installer/generate_binaries_test.bat @@ -44,6 +44,7 @@ copy ..\graphicscapture\graphicscapturehook\release\graphicscapturehook.dll .\32 copy ..\graphicscapture\graphicscapturehook\x64\release\graphicscapturehook64.dll .\32bit\plugins\graphicscapture copy ..\injectHelper\x64\release\injectHelper64.exe .\32bit\plugins\graphicscapture copy ..\x264\libs\32bit\libx264-136.dll .\32bit +copy ..\QSVHelper\Release\QSVHelper.exe .\32bit copy "%WindowsSDK80Path%Debuggers\x86\dbghelp.dll" .\32bit copy ..\x64\release\obs.exe .\64bit\ @@ -69,4 +70,5 @@ copy ..\graphicscapture\graphicscapturehook\release\graphicscapturehook.dll .\64 copy ..\graphicscapture\graphicscapturehook\x64\release\graphicscapturehook64.dll .\64bit\plugins\graphicscapture copy ..\injectHelper\release\injectHelper.exe .\64bit\plugins\graphicscapture copy ..\x264\libs\64bit\libx264-136.dll .\64bit +copy ..\QSVHelper\Release\QSVHelper.exe .\64bit copy "%WindowsSDK80Path%Debuggers\x64\dbghelp.dll" .\64bit diff --git a/installer/installer.nsi b/installer/installer.nsi index cb932425..1c99a549 100644 --- a/installer/installer.nsi +++ b/installer/installer.nsi @@ -113,6 +113,7 @@ Section "Open Broadcaster Software" Section1 SetOutPath "$PROGRAMFILES32\OBS" File "..\Release\OBS.exe" File "..\x264\libs\32bit\libx264-136.dll" + File "..\QSVHelper\Release\QSVHelper.exe" File "..\OBSAPI\Release\OBSApi.dll" File "..\rundir\services.xconfig" File "..\OBSHelp\OBSHelp.chm" @@ -142,6 +143,7 @@ Section "Open Broadcaster Software" Section1 SetOutPath "$PROGRAMFILES64\OBS" File "..\x64\Release\OBS.exe" File "..\x264\libs\64bit\libx264-136.dll" + File "..\QSVHelper\Release\QSVHelper.exe" File "..\OBSAPI\x64\Release\OBSApi.dll" File "..\rundir\services.xconfig" File "..\OBSHelp\OBSHelp.chm" @@ -219,6 +221,7 @@ Section Uninstall ; Clean up Open Broadcaster Software Delete "$PROGRAMFILES32\OBS\OBS.exe" Delete "$PROGRAMFILES32\OBS\libx264-136.dll" + Delete "$PROGRAMFILES32\OBS\QSVHelper.exe" Delete "$PROGRAMFILES32\OBS\OBSApi.dll" Delete "$PROGRAMFILES32\OBS\services.xconfig" Delete "$PROGRAMFILES32\OBS\*.chm" @@ -238,6 +241,7 @@ Section Uninstall ${if} ${RunningX64} Delete "$PROGRAMFILES64\OBS\OBS.exe" Delete "$PROGRAMFILES64\OBS\libx264-136.dll" + Delete "$PROGRAMFILES64\OBS\QSVHelper.exe" Delete "$PROGRAMFILES64\OBS\OBSApi.dll" Delete "$PROGRAMFILES64\OBS\services.xconfig" Delete "$PROGRAMFILES64\OBS\*.chm" @@ -282,4 +286,4 @@ Section Uninstall RMDir "$PROGRAMFILES32\OBS" SectionEnd -; eof \ No newline at end of file +; eof