mirror of
https://github.com/LizardByte/Sunshine.git
synced 2025-02-11 15:40:11 +00:00
Encode with nvenc smoothly
This commit is contained in:
parent
c21038af88
commit
7edaa0cce0
@ -67,12 +67,10 @@ public:
|
||||
struct hwdevice_ctx_t {
|
||||
void *hwdevice {};
|
||||
|
||||
// Could be nullptr, depends on the encoder
|
||||
std::shared_ptr<std::recursive_mutex> lock;
|
||||
|
||||
virtual const platf::img_t*const convert(platf::img_t &img) {
|
||||
return nullptr;
|
||||
}
|
||||
virtual void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) {};
|
||||
|
||||
virtual ~hwdevice_ctx_t() = default;
|
||||
};
|
||||
@ -86,18 +84,10 @@ enum class capture_e : int {
|
||||
|
||||
class display_t {
|
||||
public:
|
||||
virtual capture_e snapshot(img_t *img, bool cursor) = 0;
|
||||
virtual capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor) = 0;
|
||||
virtual std::shared_ptr<img_t> alloc_img() = 0;
|
||||
|
||||
virtual int dummy_img(img_t *img, int &dummy_data_p) {
|
||||
img->row_pitch = 4;
|
||||
img->height = 1;
|
||||
img->width = 1;
|
||||
img->pixel_pitch = 4;
|
||||
img->data = (std::uint8_t*)&dummy_data_p;
|
||||
|
||||
return 0;
|
||||
}
|
||||
virtual int dummy_img(img_t *img) = 0;
|
||||
|
||||
virtual std::shared_ptr<hwdevice_ctx_t> make_hwdevice_ctx(int width, int height, pix_fmt_e pix_fmt) {
|
||||
return std::make_shared<hwdevice_ctx_t>();
|
||||
@ -137,6 +127,8 @@ int alloc_gamepad(input_t &input, int nr);
|
||||
void free_gamepad(input_t &input, int nr);
|
||||
|
||||
[[nodiscard]] std::unique_ptr<deinit_t> init();
|
||||
|
||||
int thread_priority();
|
||||
}
|
||||
|
||||
#endif //SUNSHINE_COMMON_H
|
||||
|
@ -331,6 +331,10 @@ void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
}
|
||||
}
|
||||
|
||||
int thread_priority() {
|
||||
return SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) ? 0 : 1;
|
||||
}
|
||||
|
||||
void freeInput(void *p) {
|
||||
auto vigem = (vigem_t*)p;
|
||||
|
||||
|
@ -8,6 +8,7 @@ extern "C" {
|
||||
|
||||
#include <dxgi.h>
|
||||
#include <d3d11.h>
|
||||
#include <d3d11_4.h>
|
||||
#include <d3dcommon.h>
|
||||
#include <dxgi1_2.h>
|
||||
|
||||
@ -26,17 +27,17 @@ void Release(T *dxgi) {
|
||||
dxgi->Release();
|
||||
}
|
||||
|
||||
using factory1_t = util::safe_ptr<IDXGIFactory1, Release<IDXGIFactory1>>;
|
||||
using dxgi_t = util::safe_ptr<IDXGIDevice, Release<IDXGIDevice>>;
|
||||
using dxgi1_t = util::safe_ptr<IDXGIDevice1, Release<IDXGIDevice1>>;
|
||||
using device_t = util::safe_ptr<ID3D11Device, Release<ID3D11Device>>;
|
||||
using device_ctx_t = util::safe_ptr<ID3D11DeviceContext, Release<ID3D11DeviceContext>>;
|
||||
using adapter_t = util::safe_ptr<IDXGIAdapter1, Release<IDXGIAdapter1>>;
|
||||
using output_t = util::safe_ptr<IDXGIOutput, Release<IDXGIOutput>>;
|
||||
using output1_t = util::safe_ptr<IDXGIOutput1, Release<IDXGIOutput1>>;
|
||||
using dup_t = util::safe_ptr<IDXGIOutputDuplication, Release<IDXGIOutputDuplication>>;
|
||||
using texture2d_t = util::safe_ptr<ID3D11Texture2D, Release<ID3D11Texture2D>>;
|
||||
using resource_t = util::safe_ptr<IDXGIResource, Release<IDXGIResource>>;
|
||||
using factory1_t = util::safe_ptr<IDXGIFactory1, Release<IDXGIFactory1>>;
|
||||
using dxgi_t = util::safe_ptr<IDXGIDevice, Release<IDXGIDevice>>;
|
||||
using dxgi1_t = util::safe_ptr<IDXGIDevice1, Release<IDXGIDevice1>>;
|
||||
using device_t = util::safe_ptr<ID3D11Device, Release<ID3D11Device>>;
|
||||
using device_ctx_t = util::safe_ptr<ID3D11DeviceContext, Release<ID3D11DeviceContext>>;
|
||||
using adapter_t = util::safe_ptr<IDXGIAdapter1, Release<IDXGIAdapter1>>;
|
||||
using output_t = util::safe_ptr<IDXGIOutput, Release<IDXGIOutput>>;
|
||||
using output1_t = util::safe_ptr<IDXGIOutput1, Release<IDXGIOutput1>>;
|
||||
using dup_t = util::safe_ptr<IDXGIOutputDuplication, Release<IDXGIOutputDuplication>>;
|
||||
using texture2d_t = util::safe_ptr<ID3D11Texture2D, Release<ID3D11Texture2D>>;
|
||||
using resource_t = util::safe_ptr<IDXGIResource, Release<IDXGIResource>>;
|
||||
|
||||
namespace video {
|
||||
using device_t = util::safe_ptr<ID3D11VideoDevice, Release<ID3D11VideoDevice>>;
|
||||
@ -54,13 +55,13 @@ public:
|
||||
dup_t dup;
|
||||
bool has_frame {};
|
||||
|
||||
capture_e next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, resource_t::pointer *res_p) {
|
||||
capture_e next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p) {
|
||||
auto capture_status = release_frame();
|
||||
if(capture_status != capture_e::ok) {
|
||||
return capture_status;
|
||||
}
|
||||
|
||||
auto status = dup->AcquireNextFrame(1000, &frame_info, res_p);
|
||||
auto status = dup->AcquireNextFrame(timeout.count(), &frame_info, res_p);
|
||||
|
||||
switch(status) {
|
||||
case S_OK:
|
||||
@ -300,7 +301,8 @@ public:
|
||||
video::processor_in_t::pointer processor_in_p;
|
||||
auto status = device->CreateVideoProcessorInputView(img.texture.get(), processor_e.get(), &input_desc, &processor_in_p);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create VideoProcessorInputView [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
BOOST_LOG(error) << "Failed to create VideoProcessorInputView [0x"sv
|
||||
<< util::hex(status).to_string_view() << ']';
|
||||
return nullptr;
|
||||
}
|
||||
it = texture_to_processor_in.emplace(img.texture.get(), processor_in_p).first;
|
||||
@ -308,7 +310,7 @@ public:
|
||||
auto &processor_in = it->second;
|
||||
|
||||
D3D11_VIDEO_PROCESSOR_STREAM stream { TRUE, 0, 0, 0, 0, nullptr, processor_in.get(), nullptr };
|
||||
std::lock_guard lg { *lock };
|
||||
|
||||
auto status = ctx->VideoProcessorBlt(processor.get(), processor_out.get(), 0, 1, &stream);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed size and color conversion [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
@ -318,11 +320,13 @@ public:
|
||||
return &this->img;
|
||||
}
|
||||
|
||||
int init(std::shared_ptr<std::recursive_mutex> &lock, std::shared_ptr<platf::display_t> display, device_t::pointer device_p, device_ctx_t::pointer device_ctx_p, int in_width, int in_height, int out_width, int out_height) {
|
||||
HRESULT status;
|
||||
void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) override {
|
||||
colorspace |= (color_range >> 4);
|
||||
ctx->VideoProcessorSetOutputColorSpace(processor.get(), (D3D11_VIDEO_PROCESSOR_COLOR_SPACE*)&colorspace);
|
||||
}
|
||||
|
||||
this->lock = lock;
|
||||
std::lock_guard lg { *lock };
|
||||
int init(std::shared_ptr<platf::display_t> display, device_t::pointer device_p, device_ctx_t::pointer device_ctx_p, int in_width, int in_height, int out_width, int out_height) {
|
||||
HRESULT status;
|
||||
|
||||
video::device_t::pointer vdevice_p;
|
||||
status = device_p->QueryInterface(IID_ID3D11VideoDevice, (void**)&vdevice_p);
|
||||
@ -344,7 +348,7 @@ public:
|
||||
D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE,
|
||||
{ 1, 1 }, (UINT)in_width, (UINT)in_height,
|
||||
{ 1, 1 }, (UINT)out_width, (UINT)out_height,
|
||||
D3D11_VIDEO_USAGE_PLAYBACK_NORMAL
|
||||
D3D11_VIDEO_USAGE_OPTIMAL_QUALITY
|
||||
};
|
||||
|
||||
video::processor_enum_t::pointer vp_e_p;
|
||||
@ -632,7 +636,7 @@ public:
|
||||
|
||||
class display_cpu_t : public display_base_t {
|
||||
public:
|
||||
capture_e snapshot(::platf::img_t *img_base, bool cursor_visible) override {
|
||||
capture_e snapshot(::platf::img_t *img_base, std::chrono::milliseconds timeout, bool cursor_visible) override {
|
||||
auto img = (img_t*)img_base;
|
||||
|
||||
HRESULT status;
|
||||
@ -640,7 +644,7 @@ public:
|
||||
DXGI_OUTDUPL_FRAME_INFO frame_info;
|
||||
|
||||
resource_t::pointer res_p {};
|
||||
auto capture_status = dup.next_frame(frame_info, &res_p);
|
||||
auto capture_status = dup.next_frame(frame_info, timeout, &res_p);
|
||||
resource_t res{res_p};
|
||||
|
||||
if (capture_status != capture_e::ok) {
|
||||
@ -736,10 +740,14 @@ public:
|
||||
return img;
|
||||
}
|
||||
|
||||
int dummy_img(platf::img_t *img, int &) override {
|
||||
auto dummy_data_p = new int[1];
|
||||
int dummy_img(platf::img_t *img) override {
|
||||
img->data = new std::uint8_t[4];
|
||||
img->row_pitch = 4;
|
||||
img->pixel_pitch = 4;
|
||||
img->width = 1;
|
||||
img->height = 1;
|
||||
|
||||
return platf::display_t::dummy_img(img, *dummy_data_p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init() {
|
||||
@ -784,23 +792,22 @@ public:
|
||||
|
||||
class display_gpu_t : public display_base_t, public std::enable_shared_from_this<display_gpu_t> {
|
||||
public:
|
||||
capture_e snapshot(::platf::img_t *img_base, bool cursor_visible) override {
|
||||
capture_e snapshot(::platf::img_t *img_base, std::chrono::milliseconds timeout, bool cursor_visible) override {
|
||||
auto img = (img_d3d_t*)img_base;
|
||||
|
||||
HRESULT status;
|
||||
|
||||
DXGI_OUTDUPL_FRAME_INFO frame_info;
|
||||
|
||||
std::lock_guard lg { *lock };
|
||||
resource_t::pointer res_p {};
|
||||
auto capture_status = dup.next_frame(frame_info, &res_p);
|
||||
auto capture_status = dup.next_frame(frame_info, timeout, &res_p);
|
||||
resource_t res{res_p};
|
||||
|
||||
if (capture_status != capture_e::ok) {
|
||||
return capture_status;
|
||||
}
|
||||
|
||||
const bool update_flag = frame_info.LastPresentTime.QuadPart != 0;
|
||||
const bool update_flag = frame_info.AccumulatedFrames != 0 || frame_info.LastPresentTime.QuadPart != 0;
|
||||
if(!update_flag) {
|
||||
return capture_e::timeout;
|
||||
}
|
||||
@ -814,7 +821,6 @@ public:
|
||||
}
|
||||
|
||||
texture2d_t src { src_p };
|
||||
|
||||
device_ctx->CopyResource(img->texture.get(), src.get());
|
||||
|
||||
return capture_e::ok;
|
||||
@ -850,7 +856,7 @@ public:
|
||||
return img;
|
||||
}
|
||||
|
||||
int dummy_img(platf::img_t *img_base, int &dummy_data_p) override {
|
||||
int dummy_img(platf::img_t *img_base) override {
|
||||
auto img = (img_d3d_t*)img_base;
|
||||
|
||||
img->row_pitch = width * 4;
|
||||
@ -887,17 +893,10 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init() {
|
||||
lock = std::make_shared<std::recursive_mutex>();
|
||||
std::lock_guard lg { *lock };
|
||||
return display_base_t::init();
|
||||
}
|
||||
|
||||
std::shared_ptr<platf::hwdevice_ctx_t> make_hwdevice_ctx(int width, int height, pix_fmt_e pix_fmt) override {
|
||||
auto hwdevice = std::make_shared<hwdevice_ctx_t>();
|
||||
|
||||
auto ret = hwdevice->init(
|
||||
lock,
|
||||
shared_from_this(),
|
||||
device.get(),
|
||||
device_ctx.get(),
|
||||
@ -910,8 +909,6 @@ public:
|
||||
|
||||
return hwdevice;
|
||||
}
|
||||
|
||||
std::shared_ptr<std::recursive_mutex> lock;
|
||||
};
|
||||
|
||||
const char *format_str[] = {
|
||||
|
@ -118,6 +118,14 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void dec() {
|
||||
if(_pos == _begin) {
|
||||
_pos = _end;
|
||||
}
|
||||
|
||||
--_pos;
|
||||
}
|
||||
|
||||
bool eq(const round_robin_t &other) const {
|
||||
return *_pos == *other._pos;
|
||||
}
|
||||
|
@ -604,21 +604,21 @@ void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, vid
|
||||
|
||||
payload = {(char *) payload_new.data(), payload_new.size()};
|
||||
|
||||
// make sure moonlight recognizes the nalu code for IDR frames
|
||||
if (packet->flags & AV_PKT_FLAG_KEY) {
|
||||
// TODO: Not all encoders encode their IDR frames with the 4 byte NALU prefix
|
||||
std::string_view frame_old = "\000\000\001e"sv;
|
||||
std::string_view frame_new = "\000\000\000\001e"sv;
|
||||
if(session->config.monitor.videoFormat != 0) {
|
||||
frame_old = "\000\000\001("sv;
|
||||
frame_new = "\000\000\000\001("sv;
|
||||
}
|
||||
// // make sure moonlight recognizes the nalu code for IDR frames
|
||||
// if (packet->flags & AV_PKT_FLAG_KEY) {
|
||||
// // TODO: Not all encoders encode their IDR frames with the 4 byte NALU prefix
|
||||
// std::string_view frame_old = "\000\000\001e"sv;
|
||||
// std::string_view frame_new = "\000\000\000\001e"sv;
|
||||
// if(session->config.monitor.videoFormat != 0) {
|
||||
// frame_old = "\000\000\001("sv;
|
||||
// frame_new = "\000\000\000\001("sv;
|
||||
// }
|
||||
|
||||
assert(std::search(std::begin(payload), std::end(payload), std::begin(frame_new), std::end(frame_new)) ==
|
||||
std::end(payload));
|
||||
payload_new = replace(payload, frame_old, frame_new);
|
||||
payload = {(char *) payload_new.data(), payload_new.size()};
|
||||
}
|
||||
// assert(std::search(std::begin(payload), std::end(payload), std::begin(frame_new), std::end(frame_new)) ==
|
||||
// std::end(payload));
|
||||
// payload_new = replace(payload, frame_old, frame_new);
|
||||
// payload = {(char *) payload_new.data(), payload_new.size()};
|
||||
// }
|
||||
|
||||
// insert packet headers
|
||||
auto blocksize = session->config.packetsize + MAX_RTP_HEADER_SIZE;
|
||||
|
@ -113,7 +113,12 @@ static encoder_t nvenc {
|
||||
{ {"forced-idr"s, 1} }, "hevc_nvenc"s
|
||||
},
|
||||
{
|
||||
{ {"forced-idr"s, 1}, { "preset"s , 9} }, "h264_nvenc"s
|
||||
{
|
||||
{ "forced-idr"s, 1},
|
||||
{ "profile"s, "high"s },
|
||||
{ "preset"s , "llhp" },
|
||||
{ "rc"s, "cbr_ld_hq"s },
|
||||
}, "h264_nvenc"s
|
||||
},
|
||||
false,
|
||||
|
||||
@ -209,9 +214,8 @@ void captureThread(
|
||||
display_wp = disp;
|
||||
|
||||
std::vector<std::shared_ptr<platf::img_t>> imgs(12);
|
||||
auto round_robin = util::make_round_robin<std::shared_ptr<platf::img_t>>(std::begin(imgs) +1, std::end(imgs));
|
||||
auto round_robin = util::make_round_robin<std::shared_ptr<platf::img_t>>(std::begin(imgs), std::end(imgs));
|
||||
|
||||
int dummy_data = 0;
|
||||
for(auto &img : imgs) {
|
||||
img = disp->alloc_img();
|
||||
if(!img) {
|
||||
@ -219,9 +223,11 @@ void captureThread(
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto &dummy_img = imgs.front();
|
||||
if(disp->dummy_img(dummy_img.get(), dummy_data)) {
|
||||
return;
|
||||
|
||||
if(auto capture_ctx = capture_ctx_queue->pop()) {
|
||||
capture_ctxs.emplace_back(std::move(*capture_ctx));
|
||||
|
||||
delay = capture_ctxs.back().delay;
|
||||
}
|
||||
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
@ -229,22 +235,15 @@ void captureThread(
|
||||
while(capture_ctx_queue->peek()) {
|
||||
capture_ctxs.emplace_back(std::move(*capture_ctx_queue->pop()));
|
||||
|
||||
// Temporary image to ensure something is send to Moonlight even if no frame has been captured yet.
|
||||
capture_ctxs.back().images->raise(dummy_img);
|
||||
|
||||
delay = std::min(delay, capture_ctxs.back().delay);
|
||||
}
|
||||
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if(next_frame > now) {
|
||||
std::this_thread::sleep_until(next_frame);
|
||||
}
|
||||
next_frame += delay;
|
||||
|
||||
auto &img = *round_robin++;
|
||||
while(img.use_count() > 1) {}
|
||||
|
||||
auto status = disp->snapshot(img.get(), display_cursor);
|
||||
auto status = disp->snapshot(img.get(), 1000ms, display_cursor);
|
||||
switch (status) {
|
||||
case platf::capture_e::reinit: {
|
||||
reinit_event.raise(true);
|
||||
@ -276,16 +275,14 @@ void captureThread(
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(disp->dummy_img(dummy_img.get(), dummy_data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
reinit_event.reset();
|
||||
continue;
|
||||
}
|
||||
case platf::capture_e::error:
|
||||
return;
|
||||
return;
|
||||
case platf::capture_e::timeout:
|
||||
std::this_thread::sleep_for(1ms);
|
||||
continue;
|
||||
case platf::capture_e::ok:
|
||||
break;
|
||||
@ -310,9 +307,36 @@ void captureThread(
|
||||
capture_ctx->images->raise(img);
|
||||
++capture_ctx;
|
||||
})
|
||||
|
||||
if(next_frame > now) {
|
||||
std::this_thread::sleep_until(next_frame);
|
||||
}
|
||||
next_frame += delay;
|
||||
}
|
||||
}
|
||||
|
||||
int start_capture(capture_thread_ctx_t &capture_thread_ctx) {
|
||||
capture_thread_ctx.encoder_p = &encoders.front();
|
||||
capture_thread_ctx.reinit_event.reset();
|
||||
|
||||
capture_thread_ctx.capture_ctx_queue = std::make_shared<safe::queue_t<capture_ctx_t>>();
|
||||
|
||||
capture_thread_ctx.capture_thread = std::thread {
|
||||
captureThread,
|
||||
capture_thread_ctx.capture_ctx_queue,
|
||||
std::ref(capture_thread_ctx.display_wp),
|
||||
std::ref(capture_thread_ctx.reinit_event),
|
||||
std::ref(*capture_thread_ctx.encoder_p)
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
void end_capture(capture_thread_ctx_t &capture_thread_ctx) {
|
||||
capture_thread_ctx.capture_ctx_queue->stop();
|
||||
|
||||
capture_thread_ctx.capture_thread.join();
|
||||
}
|
||||
|
||||
util::Either<buffer_t, int> hwdevice_ctx(AVHWDeviceType type, void *hwdevice_ctx) {
|
||||
buffer_t ctx;
|
||||
|
||||
@ -385,28 +409,6 @@ int encode(int64_t frame_nr, ctx_t &ctx, frame_t &frame, packet_queue_t &packets
|
||||
return 0;
|
||||
}
|
||||
|
||||
int start_capture(capture_thread_ctx_t &capture_thread_ctx) {
|
||||
capture_thread_ctx.encoder_p = &encoders.front();
|
||||
capture_thread_ctx.reinit_event.reset();
|
||||
|
||||
capture_thread_ctx.capture_ctx_queue = std::make_shared<safe::queue_t<capture_ctx_t>>();
|
||||
|
||||
capture_thread_ctx.capture_thread = std::thread {
|
||||
captureThread,
|
||||
capture_thread_ctx.capture_ctx_queue,
|
||||
std::ref(capture_thread_ctx.display_wp),
|
||||
std::ref(capture_thread_ctx.reinit_event),
|
||||
std::ref(*capture_thread_ctx.encoder_p)
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
void end_capture(capture_thread_ctx_t &capture_thread_ctx) {
|
||||
capture_thread_ctx.capture_ctx_queue->stop();
|
||||
|
||||
capture_thread_ctx.capture_thread.join();
|
||||
}
|
||||
|
||||
std::optional<session_t> make_session(const encoder_t &encoder, const config_t &config, platf::hwdevice_ctx_t *device_ctx) {
|
||||
bool hardware = encoder.dev_type != AV_HWDEVICE_TYPE_NONE;
|
||||
|
||||
@ -505,6 +507,8 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t
|
||||
if(hwframe_ctx(ctx, hwdevice, sw_fmt)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ctx->slices = config.slicesPerFrame;
|
||||
}
|
||||
else /* software */ {
|
||||
ctx->pix_fmt = sw_fmt;
|
||||
@ -530,7 +534,7 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t
|
||||
if(config.bitrate > 500) {
|
||||
auto bitrate = config.bitrate * 1000;
|
||||
ctx->rc_max_rate = bitrate;
|
||||
ctx->rc_buffer_size = bitrate / 100;
|
||||
ctx->rc_buffer_size = bitrate / config.framerate;
|
||||
ctx->bit_rate = bitrate;
|
||||
ctx->rc_min_rate = bitrate;
|
||||
}
|
||||
@ -582,6 +586,8 @@ void encode_run(
|
||||
return;
|
||||
}
|
||||
|
||||
hwdevice_ctx->set_colorspace(session->sws_color_format, session->ctx->color_range);
|
||||
|
||||
auto delay = std::chrono::floor<std::chrono::nanoseconds>(1s) / config.framerate;
|
||||
|
||||
auto img_width = 0;
|
||||
@ -654,17 +660,8 @@ void encode_run(
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int err;
|
||||
if(hwdevice_ctx && hwdevice_ctx->lock) {
|
||||
std::lock_guard lg { *hwdevice_ctx->lock };
|
||||
err = encode(frame_nr++, session->ctx, session->frame, packets, channel_data);
|
||||
}
|
||||
else {
|
||||
err = encode(frame_nr++, session->ctx, session->frame, packets, channel_data);
|
||||
}
|
||||
|
||||
if(err) {
|
||||
if(encode(frame_nr++, session->ctx, session->frame, packets, channel_data)) {
|
||||
BOOST_LOG(fatal) << "Could not encode video packet"sv;
|
||||
log_flush();
|
||||
std::abort();
|
||||
@ -681,6 +678,110 @@ void capture(
|
||||
config_t config,
|
||||
void *channel_data) {
|
||||
|
||||
auto lg = util::fail_guard([&]() {
|
||||
shutdown_event->raise(true);
|
||||
});
|
||||
|
||||
const auto &encoder = encoders.front();
|
||||
auto disp = platf::display(encoder.dev_type);
|
||||
if(!disp) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto pix_fmt = config.dynamicRange == 0 ? platf::pix_fmt_e::yuv420p : platf::pix_fmt_e::yuv420p10;
|
||||
auto hwdevice_ctx = disp->make_hwdevice_ctx(config.width, config.height, pix_fmt);
|
||||
if(!hwdevice_ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto session = make_session(encoder, config, hwdevice_ctx.get());
|
||||
if(!session) {
|
||||
return;
|
||||
}
|
||||
hwdevice_ctx->set_colorspace(session->sws_color_format, session->ctx->color_range);
|
||||
|
||||
auto img = disp->alloc_img();
|
||||
if(disp->dummy_img(img.get())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const platf::img_t* img_p = hwdevice_ctx->convert(*img);
|
||||
if(!img_p) {
|
||||
return;
|
||||
}
|
||||
|
||||
sws_t sws;
|
||||
encoder.img_to_frame(sws, *img_p, session->frame);
|
||||
|
||||
std::vector<std::shared_ptr<platf::img_t>> imgs(12);
|
||||
for(auto &img : imgs) {
|
||||
img = disp->alloc_img();
|
||||
}
|
||||
|
||||
auto round_robin = util::make_round_robin<std::shared_ptr<platf::img_t>>(std::begin(imgs), std::end(imgs));
|
||||
|
||||
int frame_nr = 1;
|
||||
int key_frame_nr = 1;
|
||||
|
||||
auto max_delay = 1000ms / config.framerate;
|
||||
|
||||
std::shared_ptr<platf::img_t> img_tmp;
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
while(!shutdown_event->peek()) {
|
||||
if(idr_events->peek()) {
|
||||
session->frame->pict_type = AV_PICTURE_TYPE_I;
|
||||
|
||||
auto event = idr_events->pop();
|
||||
TUPLE_2D_REF(_, end, *event);
|
||||
|
||||
frame_nr = end;
|
||||
key_frame_nr = end + config.framerate;
|
||||
}
|
||||
else if(frame_nr == key_frame_nr) {
|
||||
session->frame->pict_type = AV_PICTURE_TYPE_I;
|
||||
}
|
||||
|
||||
auto delay = std::max(0ms, std::chrono::duration_cast<std::chrono::milliseconds>(next_frame - std::chrono::steady_clock::now()));
|
||||
|
||||
auto status = disp->snapshot(round_robin->get(), delay, display_cursor);
|
||||
switch(status) {
|
||||
case platf::capture_e::reinit:
|
||||
return;
|
||||
case platf::capture_e::error:
|
||||
return;
|
||||
case platf::capture_e::timeout:
|
||||
next_frame += max_delay;
|
||||
if(!img_tmp && frame_nr > (key_frame_nr + config.framerate)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
case platf::capture_e::ok:
|
||||
img_tmp = *round_robin++;
|
||||
break;
|
||||
}
|
||||
|
||||
if(img_tmp) {
|
||||
img_p = hwdevice_ctx->convert(*img_tmp);
|
||||
img_tmp.reset();
|
||||
}
|
||||
|
||||
if(encode(frame_nr++, session->ctx, session->frame, packets, channel_data)) {
|
||||
BOOST_LOG(fatal) << "Could not encode video packet"sv;
|
||||
log_flush();
|
||||
std::abort();
|
||||
}
|
||||
|
||||
session->frame->pict_type = AV_PICTURE_TYPE_NONE;
|
||||
}
|
||||
}
|
||||
void capture_async(
|
||||
safe::signal_t *shutdown_event,
|
||||
packet_queue_t packets,
|
||||
idr_event_t idr_events,
|
||||
config_t config,
|
||||
void *channel_data) {
|
||||
|
||||
auto images = std::make_shared<img_event_t::element_type>();
|
||||
auto lg = util::fail_guard([&]() {
|
||||
images->stop();
|
||||
@ -723,6 +824,12 @@ void capture(
|
||||
return;
|
||||
}
|
||||
|
||||
auto dummy_img = display->alloc_img();
|
||||
if(display->dummy_img(dummy_img.get())) {
|
||||
return;
|
||||
}
|
||||
images->raise(std::move(dummy_img));
|
||||
|
||||
encode_run(frame_nr, key_frame_nr, shutdown_event, packets, idr_events, images, config, hwdevice_ctx.get(), ref->reinit_event, *ref->encoder_p, channel_data);
|
||||
}
|
||||
}
|
||||
@ -733,7 +840,6 @@ bool validate_config(std::shared_ptr<platf::display_t> &disp, const encoder_t &e
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
auto pix_fmt = config.dynamicRange == 0 ? platf::pix_fmt_e::yuv420p : platf::pix_fmt_e::yuv420p10;
|
||||
auto hwdevice_ctx = disp->make_hwdevice_ctx(config.width, config.height, pix_fmt);
|
||||
if(!hwdevice_ctx) {
|
||||
@ -744,10 +850,10 @@ bool validate_config(std::shared_ptr<platf::display_t> &disp, const encoder_t &e
|
||||
if(!session) {
|
||||
return false;
|
||||
}
|
||||
hwdevice_ctx->set_colorspace(session->sws_color_format, session->ctx->color_range);
|
||||
|
||||
int dummy_data;
|
||||
auto img = disp->alloc_img();
|
||||
if(disp->dummy_img(img.get(), dummy_data)) {
|
||||
if(disp->dummy_img(img.get())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -900,10 +1006,8 @@ void nv_d3d_img_to_frame(sws_t &sws, const platf::img_t &img, frame_t &frame) {
|
||||
}
|
||||
|
||||
void nvenc_lock(void *lock_p) {
|
||||
((std::recursive_mutex*)lock_p)->lock();
|
||||
}
|
||||
void nvenc_unlock(void *lock_p) {
|
||||
((std::recursive_mutex*)lock_p)->unlock();
|
||||
}
|
||||
|
||||
util::Either<buffer_t, int> nv_d3d_make_hwdevice_ctx(platf::hwdevice_ctx_t *hwdevice_ctx) {
|
||||
@ -913,10 +1017,6 @@ util::Either<buffer_t, int> nv_d3d_make_hwdevice_ctx(platf::hwdevice_ctx_t *hwde
|
||||
std::fill_n((std::uint8_t*)ctx, sizeof(AVD3D11VADeviceContext), 0);
|
||||
std::swap(ctx->device, *(ID3D11Device**)&hwdevice_ctx->hwdevice);
|
||||
|
||||
ctx->lock_ctx = hwdevice_ctx->lock.get();
|
||||
ctx->lock = nvenc_lock;
|
||||
ctx->unlock = nvenc_unlock;
|
||||
|
||||
auto err = av_hwdevice_ctx_init(ctx_buf.get());
|
||||
if(err) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] {0};
|
||||
|
Loading…
x
Reference in New Issue
Block a user