save current
This commit is contained in:
@@ -166,6 +166,7 @@ COPY --from=builder /app/dist-clients ./dist-clients
|
|||||||
|
|
||||||
# Go agent source needed at every agent build.
|
# Go agent source needed at every agent build.
|
||||||
COPY Overlord-Client/ ./Overlord-Client/
|
COPY Overlord-Client/ ./Overlord-Client/
|
||||||
|
RUN test -s ./Overlord-Client/third_party/nvcodec/nvEncodeAPI.h
|
||||||
|
|
||||||
RUN mkdir -p certs data
|
RUN mkdir -p certs data
|
||||||
|
|
||||||
|
|||||||
@@ -493,6 +493,7 @@ var (
|
|||||||
statKeepaliveFrames atomic.Int64
|
statKeepaliveFrames atomic.Int64
|
||||||
statBlockRegions atomic.Int64
|
statBlockRegions atomic.Int64
|
||||||
statBlockFallbacks atomic.Int64
|
statBlockFallbacks atomic.Int64
|
||||||
|
statFrameSlotSkips atomic.Int64
|
||||||
|
|
||||||
overrideQuality atomic.Int64
|
overrideQuality atomic.Int64
|
||||||
overrideCodec atomic.Value
|
overrideCodec atomic.Value
|
||||||
@@ -700,6 +701,7 @@ func resetStats() {
|
|||||||
statKeepaliveFrames.Store(0)
|
statKeepaliveFrames.Store(0)
|
||||||
statBlockRegions.Store(0)
|
statBlockRegions.Store(0)
|
||||||
statBlockFallbacks.Store(0)
|
statBlockFallbacks.Store(0)
|
||||||
|
statFrameSlotSkips.Store(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func avgMs(ns int64, frames int64) float64 {
|
func avgMs(ns int64, frames int64) float64 {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ func AcquireFrameSlot() bool {
|
|||||||
inFlightFrames.Store(0)
|
inFlightFrames.Store(0)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
statFrameSlotSkips.Add(1)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if inFlightFrames.CompareAndSwap(cur, cur+1) {
|
if inFlightFrames.CompareAndSwap(cur, cur+1) {
|
||||||
@@ -81,3 +82,13 @@ func activeFrameSlotLimit() int64 {
|
|||||||
}
|
}
|
||||||
return defaultMaxInFlightFrames
|
return defaultMaxInFlightFrames
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func frameFlowSnapshot() (inFlight, limit int64, ackSeen bool, ackAge time.Duration) {
|
||||||
|
inFlight = inFlightFrames.Load()
|
||||||
|
limit = activeFrameSlotLimit()
|
||||||
|
ackSeen = frameAckSeen.Load()
|
||||||
|
if lastAck := lastAckNano.Load(); lastAck > 0 {
|
||||||
|
ackAge = time.Since(time.Unix(0, lastAck))
|
||||||
|
}
|
||||||
|
return inFlight, limit, ackSeen, ackAge
|
||||||
|
}
|
||||||
|
|||||||
@@ -530,6 +530,7 @@ func resetH264Encoder() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RequestDesktopH264Keyframe() {
|
func RequestDesktopH264Keyframe() {
|
||||||
|
requestNativeH264D3D11TextureKeyframe()
|
||||||
resetH264Encoder()
|
resetH264Encoder()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,4 +17,6 @@ func encodeNativeH264D3D11Texture(device, texture unsafe.Pointer, inputWidth, in
|
|||||||
return nil, fmt.Errorf("native NVENC D3D11 texture encode requires cgo for input=%dx%d output=%dx%d@%dfps", inputWidth, inputHeight, encodeWidth, encodeHeight, fps)
|
return nil, fmt.Errorf("native NVENC D3D11 texture encode requires cgo for input=%dx%d output=%dx%d@%dfps", inputWidth, inputHeight, encodeWidth, encodeHeight, fps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func requestNativeH264D3D11TextureKeyframe() {}
|
||||||
|
|
||||||
func resetNativeH264D3D11TextureEncoder() {}
|
func resetNativeH264D3D11TextureEncoder() {}
|
||||||
|
|||||||
@@ -414,70 +414,75 @@ static nvenc_d3d11_texture_create_result nvenc_create_d3d11_texture_encoder(ID3D
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT hr = enc->device->lpVtbl->QueryInterface(enc->device, &nvenc_iid_id3d11_video_device, (void**)&enc->video_device);
|
|
||||||
if (FAILED(hr) || !enc->video_device) {
|
|
||||||
out.stage = 11;
|
|
||||||
out.hr = hr;
|
|
||||||
strncpy(out.message, "ID3D11VideoDevice unavailable for GPU BGRA->NV12 conversion", sizeof(out.message)-1);
|
|
||||||
nvenc_release_texture_encoder(enc);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
hr = enc->context->lpVtbl->QueryInterface(enc->context, &nvenc_iid_id3d11_video_context, (void**)&enc->video_context);
|
|
||||||
if (FAILED(hr) || !enc->video_context) {
|
|
||||||
out.stage = 12;
|
|
||||||
out.hr = hr;
|
|
||||||
strncpy(out.message, "ID3D11VideoContext unavailable for GPU BGRA->NV12 conversion", sizeof(out.message)-1);
|
|
||||||
nvenc_release_texture_encoder(enc);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
D3D11_VIDEO_PROCESSOR_CONTENT_DESC content_desc;
|
|
||||||
memset(&content_desc, 0, sizeof(content_desc));
|
|
||||||
content_desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
|
|
||||||
content_desc.InputFrameRate.Numerator = (UINT)fps;
|
|
||||||
content_desc.InputFrameRate.Denominator = 1;
|
|
||||||
content_desc.InputWidth = (UINT)input_width;
|
|
||||||
content_desc.InputHeight = (UINT)input_height;
|
|
||||||
content_desc.OutputFrameRate.Numerator = (UINT)fps;
|
|
||||||
content_desc.OutputFrameRate.Denominator = 1;
|
|
||||||
content_desc.OutputWidth = (UINT)encode_width;
|
|
||||||
content_desc.OutputHeight = (UINT)encode_height;
|
|
||||||
content_desc.Usage = D3D11_VIDEO_USAGE_OPTIMAL_SPEED;
|
|
||||||
hr = enc->video_device->lpVtbl->CreateVideoProcessorEnumerator(enc->video_device, &content_desc, &enc->video_enum);
|
|
||||||
if (FAILED(hr) || !enc->video_enum) {
|
|
||||||
out.stage = 13;
|
|
||||||
out.hr = hr;
|
|
||||||
strncpy(out.message, "CreateVideoProcessorEnumerator failed for GPU BGRA->NV12 conversion", sizeof(out.message)-1);
|
|
||||||
nvenc_release_texture_encoder(enc);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
UINT format_flags = 0;
|
|
||||||
hr = enc->video_enum->lpVtbl->CheckVideoProcessorFormat(enc->video_enum, (DXGI_FORMAT)dxgi_format, &format_flags);
|
|
||||||
if (FAILED(hr) || !(format_flags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT)) {
|
|
||||||
out.stage = 14;
|
|
||||||
out.hr = FAILED(hr) ? hr : E_FAIL;
|
|
||||||
strncpy(out.message, "desktop DXGI format is not supported as D3D11 video processor input", sizeof(out.message)-1);
|
|
||||||
nvenc_release_texture_encoder(enc);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
DXGI_FORMAT output_dxgi_format = output_mode == 1 ? (DXGI_FORMAT)dxgi_format : DXGI_FORMAT_NV12;
|
DXGI_FORMAT output_dxgi_format = output_mode == 1 ? (DXGI_FORMAT)dxgi_format : DXGI_FORMAT_NV12;
|
||||||
NV_ENC_BUFFER_FORMAT output_buffer_format = output_mode == 1 ? buffer_format : NV_ENC_BUFFER_FORMAT_NV12;
|
NV_ENC_BUFFER_FORMAT output_buffer_format = output_mode == 1 ? buffer_format : NV_ENC_BUFFER_FORMAT_NV12;
|
||||||
format_flags = 0;
|
int direct_copy = output_mode == 1 && input_width == encode_width && input_height == encode_height;
|
||||||
hr = enc->video_enum->lpVtbl->CheckVideoProcessorFormat(enc->video_enum, output_dxgi_format, &format_flags);
|
HRESULT hr = S_OK;
|
||||||
if (FAILED(hr) || !(format_flags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT)) {
|
|
||||||
out.stage = 15;
|
if (!direct_copy) {
|
||||||
out.hr = FAILED(hr) ? hr : E_FAIL;
|
hr = enc->device->lpVtbl->QueryInterface(enc->device, &nvenc_iid_id3d11_video_device, (void**)&enc->video_device);
|
||||||
strncpy(out.message, "requested D3D11 video processor output format is not supported", sizeof(out.message)-1);
|
if (FAILED(hr) || !enc->video_device) {
|
||||||
nvenc_release_texture_encoder(enc);
|
out.stage = 11;
|
||||||
return out;
|
out.hr = hr;
|
||||||
}
|
strncpy(out.message, "ID3D11VideoDevice unavailable for GPU conversion/scaling", sizeof(out.message)-1);
|
||||||
hr = enc->video_device->lpVtbl->CreateVideoProcessor(enc->video_device, enc->video_enum, 0, &enc->video_processor);
|
nvenc_release_texture_encoder(enc);
|
||||||
if (FAILED(hr) || !enc->video_processor) {
|
return out;
|
||||||
out.stage = 16;
|
}
|
||||||
out.hr = hr;
|
hr = enc->context->lpVtbl->QueryInterface(enc->context, &nvenc_iid_id3d11_video_context, (void**)&enc->video_context);
|
||||||
strncpy(out.message, "CreateVideoProcessor failed for GPU BGRA->NV12 conversion", sizeof(out.message)-1);
|
if (FAILED(hr) || !enc->video_context) {
|
||||||
nvenc_release_texture_encoder(enc);
|
out.stage = 12;
|
||||||
return out;
|
out.hr = hr;
|
||||||
|
strncpy(out.message, "ID3D11VideoContext unavailable for GPU conversion/scaling", sizeof(out.message)-1);
|
||||||
|
nvenc_release_texture_encoder(enc);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D11_VIDEO_PROCESSOR_CONTENT_DESC content_desc;
|
||||||
|
memset(&content_desc, 0, sizeof(content_desc));
|
||||||
|
content_desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
|
||||||
|
content_desc.InputFrameRate.Numerator = (UINT)fps;
|
||||||
|
content_desc.InputFrameRate.Denominator = 1;
|
||||||
|
content_desc.InputWidth = (UINT)input_width;
|
||||||
|
content_desc.InputHeight = (UINT)input_height;
|
||||||
|
content_desc.OutputFrameRate.Numerator = (UINT)fps;
|
||||||
|
content_desc.OutputFrameRate.Denominator = 1;
|
||||||
|
content_desc.OutputWidth = (UINT)encode_width;
|
||||||
|
content_desc.OutputHeight = (UINT)encode_height;
|
||||||
|
content_desc.Usage = D3D11_VIDEO_USAGE_OPTIMAL_SPEED;
|
||||||
|
hr = enc->video_device->lpVtbl->CreateVideoProcessorEnumerator(enc->video_device, &content_desc, &enc->video_enum);
|
||||||
|
if (FAILED(hr) || !enc->video_enum) {
|
||||||
|
out.stage = 13;
|
||||||
|
out.hr = hr;
|
||||||
|
strncpy(out.message, "CreateVideoProcessorEnumerator failed for GPU conversion/scaling", sizeof(out.message)-1);
|
||||||
|
nvenc_release_texture_encoder(enc);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
UINT format_flags = 0;
|
||||||
|
hr = enc->video_enum->lpVtbl->CheckVideoProcessorFormat(enc->video_enum, (DXGI_FORMAT)dxgi_format, &format_flags);
|
||||||
|
if (FAILED(hr) || !(format_flags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT)) {
|
||||||
|
out.stage = 14;
|
||||||
|
out.hr = FAILED(hr) ? hr : E_FAIL;
|
||||||
|
strncpy(out.message, "desktop DXGI format is not supported as D3D11 video processor input", sizeof(out.message)-1);
|
||||||
|
nvenc_release_texture_encoder(enc);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
format_flags = 0;
|
||||||
|
hr = enc->video_enum->lpVtbl->CheckVideoProcessorFormat(enc->video_enum, output_dxgi_format, &format_flags);
|
||||||
|
if (FAILED(hr) || !(format_flags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT)) {
|
||||||
|
out.stage = 15;
|
||||||
|
out.hr = FAILED(hr) ? hr : E_FAIL;
|
||||||
|
strncpy(out.message, "requested D3D11 video processor output format is not supported", sizeof(out.message)-1);
|
||||||
|
nvenc_release_texture_encoder(enc);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
hr = enc->video_device->lpVtbl->CreateVideoProcessor(enc->video_device, enc->video_enum, 0, &enc->video_processor);
|
||||||
|
if (FAILED(hr) || !enc->video_processor) {
|
||||||
|
out.stage = 16;
|
||||||
|
out.hr = hr;
|
||||||
|
strncpy(out.message, "CreateVideoProcessor failed for GPU conversion/scaling", sizeof(out.message)-1);
|
||||||
|
nvenc_release_texture_encoder(enc);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
D3D11_TEXTURE2D_DESC source_desc;
|
D3D11_TEXTURE2D_DESC source_desc;
|
||||||
@@ -514,37 +519,41 @@ static nvenc_d3d11_texture_create_result nvenc_create_d3d11_texture_encoder(ID3D
|
|||||||
output_view_desc.Texture2D.MipSlice = 0;
|
output_view_desc.Texture2D.MipSlice = 0;
|
||||||
for (int i = 0; i < NVENC_D3D11_TEXTURE_PIPELINE_DEPTH; i++) {
|
for (int i = 0; i < NVENC_D3D11_TEXTURE_PIPELINE_DEPTH; i++) {
|
||||||
nvenc_d3d11_texture_slot *slot = &enc->slots[i];
|
nvenc_d3d11_texture_slot *slot = &enc->slots[i];
|
||||||
hr = enc->device->lpVtbl->CreateTexture2D(enc->device, &source_desc, NULL, &slot->source_texture);
|
if (!direct_copy) {
|
||||||
if (FAILED(hr) || !slot->source_texture) {
|
hr = enc->device->lpVtbl->CreateTexture2D(enc->device, &source_desc, NULL, &slot->source_texture);
|
||||||
out.stage = 17;
|
if (FAILED(hr) || !slot->source_texture) {
|
||||||
out.hr = hr;
|
out.stage = 17;
|
||||||
strncpy(out.message, "CreateTexture2D desktop video-processor input failed", sizeof(out.message)-1);
|
out.hr = hr;
|
||||||
nvenc_release_texture_encoder(enc);
|
strncpy(out.message, "CreateTexture2D desktop video-processor input failed", sizeof(out.message)-1);
|
||||||
return out;
|
nvenc_release_texture_encoder(enc);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
hr = enc->device->lpVtbl->CreateTexture2D(enc->device, &tex_desc, NULL, &slot->input_texture);
|
hr = enc->device->lpVtbl->CreateTexture2D(enc->device, &tex_desc, NULL, &slot->input_texture);
|
||||||
if (FAILED(hr) || !slot->input_texture) {
|
if (FAILED(hr) || !slot->input_texture) {
|
||||||
out.stage = 18;
|
out.stage = 18;
|
||||||
out.hr = hr;
|
out.hr = hr;
|
||||||
strncpy(out.message, "CreateTexture2D video-processor output failed", sizeof(out.message)-1);
|
strncpy(out.message, direct_copy ? "CreateTexture2D direct NVENC input failed" : "CreateTexture2D video-processor output failed", sizeof(out.message)-1);
|
||||||
nvenc_release_texture_encoder(enc);
|
nvenc_release_texture_encoder(enc);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
hr = enc->video_device->lpVtbl->CreateVideoProcessorInputView(enc->video_device, (ID3D11Resource*)slot->source_texture, enc->video_enum, &input_view_desc, &slot->video_input_view);
|
if (!direct_copy) {
|
||||||
if (FAILED(hr) || !slot->video_input_view) {
|
hr = enc->video_device->lpVtbl->CreateVideoProcessorInputView(enc->video_device, (ID3D11Resource*)slot->source_texture, enc->video_enum, &input_view_desc, &slot->video_input_view);
|
||||||
out.stage = 19;
|
if (FAILED(hr) || !slot->video_input_view) {
|
||||||
out.hr = hr;
|
out.stage = 19;
|
||||||
strncpy(out.message, "CreateVideoProcessorInputView failed", sizeof(out.message)-1);
|
out.hr = hr;
|
||||||
nvenc_release_texture_encoder(enc);
|
strncpy(out.message, "CreateVideoProcessorInputView failed", sizeof(out.message)-1);
|
||||||
return out;
|
nvenc_release_texture_encoder(enc);
|
||||||
}
|
return out;
|
||||||
hr = enc->video_device->lpVtbl->CreateVideoProcessorOutputView(enc->video_device, (ID3D11Resource*)slot->input_texture, enc->video_enum, &output_view_desc, &slot->video_output_view);
|
}
|
||||||
if (FAILED(hr) || !slot->video_output_view) {
|
hr = enc->video_device->lpVtbl->CreateVideoProcessorOutputView(enc->video_device, (ID3D11Resource*)slot->input_texture, enc->video_enum, &output_view_desc, &slot->video_output_view);
|
||||||
out.stage = 20;
|
if (FAILED(hr) || !slot->video_output_view) {
|
||||||
out.hr = hr;
|
out.stage = 20;
|
||||||
strncpy(out.message, "CreateVideoProcessorOutputView failed", sizeof(out.message)-1);
|
out.hr = hr;
|
||||||
nvenc_release_texture_encoder(enc);
|
strncpy(out.message, "CreateVideoProcessorOutputView failed", sizeof(out.message)-1);
|
||||||
return out;
|
nvenc_release_texture_encoder(enc);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NV_ENC_REGISTER_RESOURCE reg;
|
NV_ENC_REGISTER_RESOURCE reg;
|
||||||
@@ -580,7 +589,7 @@ static nvenc_d3d11_texture_create_result nvenc_create_d3d11_texture_encoder(ID3D
|
|||||||
}
|
}
|
||||||
slot->bitstream = bs.bitstreamBuffer;
|
slot->bitstream = bs.bitstreamBuffer;
|
||||||
}
|
}
|
||||||
enc->use_video_processor = 1;
|
enc->use_video_processor = direct_copy ? 0 : 1;
|
||||||
out.encoder = enc;
|
out.encoder = enc;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -866,7 +875,7 @@ static nvenc_d3d11_encode_result nvenc_encode_d3d11_texture(nvenc_d3d11_texture_
|
|||||||
strncpy(out.message, "nil encoder or texture", sizeof(out.message)-1);
|
strncpy(out.message, "nil encoder or texture", sizeof(out.message)-1);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
if (!enc->use_video_processor || !enc->video_context || !enc->video_processor) {
|
if (enc->use_video_processor && (!enc->video_context || !enc->video_processor)) {
|
||||||
out.stage = 3;
|
out.stage = 3;
|
||||||
out.nvstatus = NV_ENC_ERR_INVALID_PTR;
|
out.nvstatus = NV_ENC_ERR_INVALID_PTR;
|
||||||
strncpy(out.message, "GPU BGRA->NV12 video processor is missing", sizeof(out.message)-1);
|
strncpy(out.message, "GPU BGRA->NV12 video processor is missing", sizeof(out.message)-1);
|
||||||
@@ -925,48 +934,56 @@ static nvenc_d3d11_encode_result nvenc_encode_d3d11_texture(nvenc_d3d11_texture_
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (slot->in_flight || !slot->source_texture || !slot->input_texture || !slot->video_input_view || !slot->video_output_view || !slot->registered_resource || !slot->bitstream) {
|
if (slot->in_flight || !slot->input_texture || !slot->registered_resource || !slot->bitstream) {
|
||||||
out.stage = 2;
|
out.stage = 2;
|
||||||
out.nvstatus = NV_ENC_ERR_INVALID_PTR;
|
out.nvstatus = NV_ENC_ERR_INVALID_PTR;
|
||||||
strncpy(out.message, "pipeline slot is not ready for encode", sizeof(out.message)-1);
|
strncpy(out.message, "pipeline slot is not ready for encode", sizeof(out.message)-1);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
if (enc->use_video_processor && (!slot->source_texture || !slot->video_input_view || !slot->video_output_view)) {
|
||||||
|
out.stage = 2;
|
||||||
|
out.nvstatus = NV_ENC_ERR_INVALID_PTR;
|
||||||
|
strncpy(out.message, "video-processor pipeline slot is not ready for encode", sizeof(out.message)-1);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
QueryPerformanceCounter(&t0);
|
QueryPerformanceCounter(&t0);
|
||||||
enc->context->lpVtbl->CopyResource(enc->context, (ID3D11Resource*)slot->source_texture, (ID3D11Resource*)texture);
|
enc->context->lpVtbl->CopyResource(enc->context, enc->use_video_processor ? (ID3D11Resource*)slot->source_texture : (ID3D11Resource*)slot->input_texture, (ID3D11Resource*)texture);
|
||||||
QueryPerformanceCounter(&t1);
|
QueryPerformanceCounter(&t1);
|
||||||
out.copy_ms = nvenc_qpc_ms(t0, t1, qpc_freq);
|
out.copy_ms = nvenc_qpc_ms(t0, t1, qpc_freq);
|
||||||
|
|
||||||
RECT src_rect;
|
if (enc->use_video_processor) {
|
||||||
src_rect.left = 0;
|
RECT src_rect;
|
||||||
src_rect.top = 0;
|
src_rect.left = 0;
|
||||||
src_rect.right = enc->input_width;
|
src_rect.top = 0;
|
||||||
src_rect.bottom = enc->input_height;
|
src_rect.right = enc->input_width;
|
||||||
RECT dst_rect;
|
src_rect.bottom = enc->input_height;
|
||||||
dst_rect.left = 0;
|
RECT dst_rect;
|
||||||
dst_rect.top = 0;
|
dst_rect.left = 0;
|
||||||
dst_rect.right = enc->encode_width;
|
dst_rect.top = 0;
|
||||||
dst_rect.bottom = enc->encode_height;
|
dst_rect.right = enc->encode_width;
|
||||||
enc->video_context->lpVtbl->VideoProcessorSetStreamFrameFormat(enc->video_context, enc->video_processor, 0, D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE);
|
dst_rect.bottom = enc->encode_height;
|
||||||
nvenc_set_video_processor_color_space(enc);
|
enc->video_context->lpVtbl->VideoProcessorSetStreamFrameFormat(enc->video_context, enc->video_processor, 0, D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE);
|
||||||
enc->video_context->lpVtbl->VideoProcessorSetStreamSourceRect(enc->video_context, enc->video_processor, 0, TRUE, &src_rect);
|
nvenc_set_video_processor_color_space(enc);
|
||||||
enc->video_context->lpVtbl->VideoProcessorSetStreamDestRect(enc->video_context, enc->video_processor, 0, TRUE, &dst_rect);
|
enc->video_context->lpVtbl->VideoProcessorSetStreamSourceRect(enc->video_context, enc->video_processor, 0, TRUE, &src_rect);
|
||||||
enc->video_context->lpVtbl->VideoProcessorSetOutputTargetRect(enc->video_context, enc->video_processor, TRUE, &dst_rect);
|
enc->video_context->lpVtbl->VideoProcessorSetStreamDestRect(enc->video_context, enc->video_processor, 0, TRUE, &dst_rect);
|
||||||
enc->video_context->lpVtbl->VideoProcessorSetStreamAutoProcessingMode(enc->video_context, enc->video_processor, 0, FALSE);
|
enc->video_context->lpVtbl->VideoProcessorSetOutputTargetRect(enc->video_context, enc->video_processor, TRUE, &dst_rect);
|
||||||
D3D11_VIDEO_PROCESSOR_STREAM stream;
|
enc->video_context->lpVtbl->VideoProcessorSetStreamAutoProcessingMode(enc->video_context, enc->video_processor, 0, FALSE);
|
||||||
memset(&stream, 0, sizeof(stream));
|
D3D11_VIDEO_PROCESSOR_STREAM stream;
|
||||||
stream.Enable = TRUE;
|
memset(&stream, 0, sizeof(stream));
|
||||||
stream.OutputIndex = 0;
|
stream.Enable = TRUE;
|
||||||
stream.InputFrameOrField = 0;
|
stream.OutputIndex = 0;
|
||||||
stream.pInputSurface = slot->video_input_view;
|
stream.InputFrameOrField = 0;
|
||||||
QueryPerformanceCounter(&t0);
|
stream.pInputSurface = slot->video_input_view;
|
||||||
HRESULT hr = enc->video_context->lpVtbl->VideoProcessorBlt(enc->video_context, enc->video_processor, slot->video_output_view, enc->frame, 1, &stream);
|
QueryPerformanceCounter(&t0);
|
||||||
QueryPerformanceCounter(&t1);
|
HRESULT hr = enc->video_context->lpVtbl->VideoProcessorBlt(enc->video_context, enc->video_processor, slot->video_output_view, enc->frame, 1, &stream);
|
||||||
out.blt_ms = nvenc_qpc_ms(t0, t1, qpc_freq);
|
QueryPerformanceCounter(&t1);
|
||||||
if (FAILED(hr)) {
|
out.blt_ms = nvenc_qpc_ms(t0, t1, qpc_freq);
|
||||||
out.stage = 4;
|
if (FAILED(hr)) {
|
||||||
out.nvstatus = NV_ENC_ERR_GENERIC;
|
out.stage = 4;
|
||||||
snprintf(out.message, sizeof(out.message), "VideoProcessorBlt BGRA->NV12 failed hr=0x%08lx", (unsigned long)hr);
|
out.nvstatus = NV_ENC_ERR_GENERIC;
|
||||||
return out;
|
snprintf(out.message, sizeof(out.message), "VideoProcessorBlt failed hr=0x%08lx", (unsigned long)hr);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NV_ENC_MAP_INPUT_RESOURCE map;
|
NV_ENC_MAP_INPUT_RESOURCE map;
|
||||||
@@ -1444,6 +1461,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1668,6 +1686,10 @@ func newNativeD3D11TextureH264Encoder(device unsafe.Pointer, inputWidth, inputHe
|
|||||||
}
|
}
|
||||||
return nil, fmt.Errorf("NVENC D3D11 texture encoder create failed")
|
return nil, fmt.Errorf("NVENC D3D11 texture encoder create failed")
|
||||||
}
|
}
|
||||||
|
pipeline := "d3d11_video_processor"
|
||||||
|
if outputMode == 1 && inputWidth == encodeWidth && inputHeight == encodeHeight {
|
||||||
|
pipeline = "d3d11_direct_copy"
|
||||||
|
}
|
||||||
enc := &nativeD3D11TextureH264Encoder{
|
enc := &nativeD3D11TextureH264Encoder{
|
||||||
enc: raw.encoder,
|
enc: raw.encoder,
|
||||||
device: device,
|
device: device,
|
||||||
@@ -1682,9 +1704,9 @@ func newNativeD3D11TextureH264Encoder(device unsafe.Pointer, inputWidth, inputHe
|
|||||||
outputName: outputName,
|
outputName: outputName,
|
||||||
}
|
}
|
||||||
if inputWidth != encodeWidth || inputHeight != encodeHeight {
|
if inputWidth != encodeWidth || inputHeight != encodeHeight {
|
||||||
log.Printf("capture: native NVENC D3D11 desktop texture encoder active provider=NVIDIA NVENC input=%dx%d output=%dx%d fps=%d source_format=%s(dxgi=%d) nvenc_input=%s gpu_scale=d3d11_video_processor bitrate=%d", inputWidth, inputHeight, encodeWidth, encodeHeight, fps, name, dxgiFormat, outputName, bitrate)
|
log.Printf("capture: native NVENC D3D11 desktop texture encoder active provider=NVIDIA NVENC input=%dx%d output=%dx%d fps=%d source_format=%s(dxgi=%d) nvenc_input=%s pipeline=%s bitrate=%d", inputWidth, inputHeight, encodeWidth, encodeHeight, fps, name, dxgiFormat, outputName, pipeline, bitrate)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("capture: native NVENC D3D11 desktop texture encoder active provider=NVIDIA NVENC size=%dx%d fps=%d source_format=%s(dxgi=%d) nvenc_input=%s gpu_scale=d3d11_video_processor bitrate=%d", encodeWidth, encodeHeight, fps, name, dxgiFormat, outputName, bitrate)
|
log.Printf("capture: native NVENC D3D11 desktop texture encoder active provider=NVIDIA NVENC size=%dx%d fps=%d source_format=%s(dxgi=%d) nvenc_input=%s pipeline=%s bitrate=%d", encodeWidth, encodeHeight, fps, name, dxgiFormat, outputName, pipeline, bitrate)
|
||||||
}
|
}
|
||||||
return enc, nil
|
return enc, nil
|
||||||
}
|
}
|
||||||
@@ -1782,7 +1804,7 @@ func preferredNVENCD3D11OutputModes() []int {
|
|||||||
case "bgra", "argb":
|
case "bgra", "argb":
|
||||||
return []int{1}
|
return []int{1}
|
||||||
default:
|
default:
|
||||||
return []int{0, 1}
|
return []int{1, 0}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1812,8 +1834,9 @@ func envBool(name string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
nativeTextureH264Mu sync.Mutex
|
nativeTextureH264Mu sync.Mutex
|
||||||
nativeTextureH264Enc *nativeD3D11TextureH264Encoder
|
nativeTextureH264Enc *nativeD3D11TextureH264Encoder
|
||||||
|
nativeTextureH264ForceIDR atomic.Bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func encodeNativeH264D3D11Texture(device, texture unsafe.Pointer, inputWidth, inputHeight, encodeWidth, encodeHeight, fps int, dxgiFormat uint32, forceIDR bool) ([]byte, error) {
|
func encodeNativeH264D3D11Texture(device, texture unsafe.Pointer, inputWidth, inputHeight, encodeWidth, encodeHeight, fps int, dxgiFormat uint32, forceIDR bool) ([]byte, error) {
|
||||||
@@ -1834,7 +1857,11 @@ func encodeNativeH264D3D11Texture(device, texture unsafe.Pointer, inputWidth, in
|
|||||||
}
|
}
|
||||||
nativeTextureH264Enc = enc
|
nativeTextureH264Enc = enc
|
||||||
}
|
}
|
||||||
return nativeTextureH264Enc.EncodeTexture(texture, forceIDR)
|
return nativeTextureH264Enc.EncodeTexture(texture, forceIDR || nativeTextureH264ForceIDR.Swap(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestNativeH264D3D11TextureKeyframe() {
|
||||||
|
nativeTextureH264ForceIDR.Store(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resetNativeH264D3D11TextureEncoder() {
|
func resetNativeH264D3D11TextureEncoder() {
|
||||||
@@ -1844,4 +1871,5 @@ func resetNativeH264D3D11TextureEncoder() {
|
|||||||
nativeTextureH264Enc.Close()
|
nativeTextureH264Enc.Close()
|
||||||
nativeTextureH264Enc = nil
|
nativeTextureH264Enc = nil
|
||||||
}
|
}
|
||||||
|
nativeTextureH264ForceIDR.Store(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -617,6 +617,8 @@ type duplicationState struct {
|
|||||||
context *d3d11DeviceContext
|
context *d3d11DeviceContext
|
||||||
staging *d3d11Texture2D
|
staging *d3d11Texture2D
|
||||||
stagingDesc d3d11Texture2DDesc
|
stagingDesc d3d11Texture2DDesc
|
||||||
|
h264LastTex *d3d11Texture2D
|
||||||
|
h264LastDesc d3d11Texture2DDesc
|
||||||
desc dxgiOutDuplDesc
|
desc dxgiOutDuplDesc
|
||||||
lastBase *image.RGBA
|
lastBase *image.RGBA
|
||||||
lastFrame *image.RGBA
|
lastFrame *image.RGBA
|
||||||
@@ -658,6 +660,11 @@ func (s *duplicationState) closeLocked() {
|
|||||||
s.staging.Release()
|
s.staging.Release()
|
||||||
s.staging = nil
|
s.staging = nil
|
||||||
}
|
}
|
||||||
|
if s.h264LastTex != nil {
|
||||||
|
s.h264LastTex.Release()
|
||||||
|
s.h264LastTex = nil
|
||||||
|
s.h264LastDesc = d3d11Texture2DDesc{}
|
||||||
|
}
|
||||||
resetNativeH264D3D11TextureEncoder()
|
resetNativeH264D3D11TextureEncoder()
|
||||||
if s.context != nil {
|
if s.context != nil {
|
||||||
s.context.Release()
|
s.context.Release()
|
||||||
@@ -888,6 +895,23 @@ func (s *duplicationState) captureH264(display int, forceKeyframe bool) ([]byte,
|
|||||||
var resource *iunknown
|
var resource *iunknown
|
||||||
hr := s.dup.AcquireNextFrame(5, &info, &resource)
|
hr := s.dup.AcquireNextFrame(5, &info, &resource)
|
||||||
if hr == dxgiErrorWaitTimeout {
|
if hr == dxgiErrorWaitTimeout {
|
||||||
|
if s.h264LastTex != nil {
|
||||||
|
captureDur := time.Since(capStart)
|
||||||
|
fps := activeH264FPS()
|
||||||
|
encStart := time.Now()
|
||||||
|
out, err := encodeNativeH264D3D11Texture(unsafe.Pointer(s.device), unsafe.Pointer(s.h264LastTex), width, height, encodeW, encodeH, fps, s.h264LastDesc.Format, forceKeyframe)
|
||||||
|
encodeDur := time.Since(encStart)
|
||||||
|
if err != nil {
|
||||||
|
directH264WarnOnce.Do(func() {
|
||||||
|
log.Printf("capture: direct DXGI->NVENC h264 cached-frame encode failed for %dx%d@%dfps format=%d: %v; using readback path", width, height, fps, s.h264LastDesc.Format, err)
|
||||||
|
})
|
||||||
|
return nil, 0, 0, 0, 0, false, err
|
||||||
|
}
|
||||||
|
if len(out) == 0 {
|
||||||
|
return nil, encodeW, encodeH, captureDur, encodeDur, true, nil
|
||||||
|
}
|
||||||
|
return out, encodeW, encodeH, captureDur, encodeDur, true, nil
|
||||||
|
}
|
||||||
return nil, encodeW, encodeH, time.Since(capStart), 0, true, nil
|
return nil, encodeW, encodeH, time.Since(capStart), 0, true, nil
|
||||||
}
|
}
|
||||||
if hr == dxgiErrorAccessLost {
|
if hr == dxgiErrorAccessLost {
|
||||||
@@ -907,7 +931,12 @@ func (s *duplicationState) captureH264(display int, forceKeyframe bool) ([]byte,
|
|||||||
if resource == nil {
|
if resource == nil {
|
||||||
return nil, 0, 0, 0, 0, false, errors.New("dxgi: acquired nil resource")
|
return nil, 0, 0, 0, 0, false, errors.New("dxgi: acquired nil resource")
|
||||||
}
|
}
|
||||||
defer s.dup.ReleaseFrame()
|
frameReleased := false
|
||||||
|
defer func() {
|
||||||
|
if !frameReleased {
|
||||||
|
_ = s.dup.ReleaseFrame()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
var tex *d3d11Texture2D
|
var tex *d3d11Texture2D
|
||||||
hr = resource.QueryInterface(&IID_ID3D11Texture2D, unsafe.Pointer(&tex))
|
hr = resource.QueryInterface(&IID_ID3D11Texture2D, unsafe.Pointer(&tex))
|
||||||
@@ -930,15 +959,24 @@ func (s *duplicationState) captureH264(display int, forceKeyframe bool) ([]byte,
|
|||||||
})
|
})
|
||||||
return nil, 0, 0, 0, 0, false, fmt.Errorf("direct h264 unsupported DXGI texture format %d", srcDesc.Format)
|
return nil, 0, 0, 0, 0, false, fmt.Errorf("direct h264 unsupported DXGI texture format %d", srcDesc.Format)
|
||||||
}
|
}
|
||||||
|
if err := s.cacheH264TextureLocked(tex, srcDesc); err != nil {
|
||||||
|
directH264WarnOnce.Do(func() {
|
||||||
|
log.Printf("capture: direct DXGI->NVENC h264 cache setup failed for %dx%d format=%d: %v; using readback path", width, height, srcDesc.Format, err)
|
||||||
|
})
|
||||||
|
return nil, 0, 0, 0, 0, false, err
|
||||||
|
}
|
||||||
|
|
||||||
captureDur := time.Since(capStart)
|
captureDur := time.Since(capStart)
|
||||||
|
_ = s.dup.ReleaseFrame()
|
||||||
|
frameReleased = true
|
||||||
|
|
||||||
fps := activeH264FPS()
|
fps := activeH264FPS()
|
||||||
encStart := time.Now()
|
encStart := time.Now()
|
||||||
out, err := encodeNativeH264D3D11Texture(unsafe.Pointer(s.device), unsafe.Pointer(tex), width, height, encodeW, encodeH, fps, srcDesc.Format, forceKeyframe)
|
out, err := encodeNativeH264D3D11Texture(unsafe.Pointer(s.device), unsafe.Pointer(s.h264LastTex), width, height, encodeW, encodeH, fps, s.h264LastDesc.Format, forceKeyframe)
|
||||||
encodeDur := time.Since(encStart)
|
encodeDur := time.Since(encStart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
directH264WarnOnce.Do(func() {
|
directH264WarnOnce.Do(func() {
|
||||||
log.Printf("capture: direct DXGI->NVENC h264 failed for %dx%d@%dfps format=%d: %v; using readback path", width, height, fps, srcDesc.Format, err)
|
log.Printf("capture: direct DXGI->NVENC h264 failed for %dx%d@%dfps format=%d: %v; using readback path", width, height, fps, s.h264LastDesc.Format, err)
|
||||||
})
|
})
|
||||||
return nil, 0, 0, 0, 0, false, err
|
return nil, 0, 0, 0, 0, false, err
|
||||||
}
|
}
|
||||||
@@ -951,6 +989,40 @@ func (s *duplicationState) captureH264(display int, forceKeyframe bool) ([]byte,
|
|||||||
return out, encodeW, encodeH, captureDur, encodeDur, true, nil
|
return out, encodeW, encodeH, captureDur, encodeDur, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *duplicationState) cacheH264TextureLocked(src *d3d11Texture2D, srcDesc d3d11Texture2DDesc) error {
|
||||||
|
if src == nil || s.device == nil || s.context == nil {
|
||||||
|
return errors.New("nil D3D11 source/device/context")
|
||||||
|
}
|
||||||
|
recreate := s.h264LastTex == nil ||
|
||||||
|
s.h264LastDesc.Width != srcDesc.Width ||
|
||||||
|
s.h264LastDesc.Height != srcDesc.Height ||
|
||||||
|
s.h264LastDesc.Format != srcDesc.Format
|
||||||
|
if recreate {
|
||||||
|
if s.h264LastTex != nil {
|
||||||
|
s.h264LastTex.Release()
|
||||||
|
s.h264LastTex = nil
|
||||||
|
}
|
||||||
|
cacheDesc := srcDesc
|
||||||
|
cacheDesc.MipLevels = 1
|
||||||
|
cacheDesc.ArraySize = 1
|
||||||
|
cacheDesc.SampleDesc.Count = 1
|
||||||
|
cacheDesc.SampleDesc.Quality = 0
|
||||||
|
cacheDesc.Usage = 0 // D3D11_USAGE_DEFAULT
|
||||||
|
cacheDesc.BindFlags = 0
|
||||||
|
cacheDesc.CPUAccessFlags = 0
|
||||||
|
cacheDesc.MiscFlags = 0
|
||||||
|
var tex *d3d11Texture2D
|
||||||
|
hr := s.device.CreateTexture2D(&cacheDesc, nil, &tex)
|
||||||
|
if hr != S_OK || tex == nil {
|
||||||
|
return fmt.Errorf("CreateTexture2D h264 cache failed 0x%x", hr)
|
||||||
|
}
|
||||||
|
s.h264LastTex = tex
|
||||||
|
s.h264LastDesc = cacheDesc
|
||||||
|
}
|
||||||
|
s.context.CopyResource(s.h264LastTex, src)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *duplicationState) composeFrame(base *image.RGBA, nativeW, nativeH int) *image.RGBA {
|
func (s *duplicationState) composeFrame(base *image.RGBA, nativeW, nativeH int) *image.RGBA {
|
||||||
if base == nil {
|
if base == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func HandleScreenshot(ctx context.Context, env *rt.Env, cmdID string, allDisplay
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
img, displayIndex, bounds, err := captureScreenshotImage(allDisplays)
|
img, _, bounds, err := captureScreenshotImage(allDisplays)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("screenshot: capture failed: %v", err)
|
log.Printf("screenshot: capture failed: %v", err)
|
||||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||||
@@ -86,20 +86,7 @@ func HandleScreenshot(ctx context.Context, env *rt.Env, cmdID string, allDisplay
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
frame := wire.Frame{
|
capture.RequestDesktopH264Keyframe()
|
||||||
Type: "frame",
|
|
||||||
Header: wire.FrameHeader{
|
|
||||||
Monitor: displayIndex,
|
|
||||||
FPS: 0,
|
|
||||||
Format: "jpeg",
|
|
||||||
},
|
|
||||||
Data: jpegData,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := wire.WriteMsg(ctx, env.Conn, frame); err != nil {
|
|
||||||
log.Printf("screenshot: failed to send frame: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{
|
||||||
Type: "command_result",
|
Type: "command_result",
|
||||||
|
|||||||
@@ -3,9 +3,15 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
rt "overlord-client/cmd/agent/runtime"
|
||||||
|
"overlord-client/cmd/agent/wire"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCaptureScreenshotImageWindows_PrimaryOnly(t *testing.T) {
|
func TestCaptureScreenshotImageWindows_PrimaryOnly(t *testing.T) {
|
||||||
@@ -105,3 +111,44 @@ func TestCaptureScreenshotImageWindows_NoDisplays(t *testing.T) {
|
|||||||
t.Fatal("expected nil image on error")
|
t.Fatal("expected nil image on error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHandleScreenshot_DoesNotEmitLiveFrame(t *testing.T) {
|
||||||
|
origMonitorCountFn := monitorCountFn
|
||||||
|
origCaptureFn := captureDisplayRGBABitBltFn
|
||||||
|
t.Cleanup(func() {
|
||||||
|
monitorCountFn = origMonitorCountFn
|
||||||
|
captureDisplayRGBABitBltFn = origCaptureFn
|
||||||
|
})
|
||||||
|
|
||||||
|
monitorCountFn = func() int { return 1 }
|
||||||
|
captureDisplayRGBABitBltFn = func(display int) (*image.RGBA, error) {
|
||||||
|
img := image.NewRGBA(image.Rect(0, 0, 2, 2))
|
||||||
|
img.SetRGBA(0, 0, color.RGBA{B: 255, A: 255})
|
||||||
|
return img, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
writer := &testWriter{}
|
||||||
|
if err := HandleScreenshot(ctx, &rt.Env{Conn: writer}, "cmd-1", false); err != nil {
|
||||||
|
t.Fatalf("HandleScreenshot failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(writer.msgs) != 2 {
|
||||||
|
t.Fatalf("expected 2 messages without live frame emission, got %d", len(writer.msgs))
|
||||||
|
}
|
||||||
|
|
||||||
|
var first wire.ScreenshotResult
|
||||||
|
if err := msgpack.Unmarshal(writer.msgs[0], &first); err != nil {
|
||||||
|
t.Fatalf("unmarshal screenshot_result: %v", err)
|
||||||
|
}
|
||||||
|
if first.Type != "screenshot_result" {
|
||||||
|
t.Fatalf("expected screenshot_result, got %q", first.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
var second wire.CommandResult
|
||||||
|
if err := msgpack.Unmarshal(writer.msgs[1], &second); err != nil {
|
||||||
|
t.Fatalf("unmarshal command_result: %v", err)
|
||||||
|
}
|
||||||
|
if second.Type != "command_result" || !second.OK {
|
||||||
|
t.Fatalf("expected successful command_result, got %+v", second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -393,6 +393,28 @@ function detectAgentVersion(clientDir: string): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function nvcodecHeaderPath(clientDir: string): string {
|
||||||
|
return path.join(clientDir, "third_party", "nvcodec", "nvEncodeAPI.h");
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureNVCodecHeaderForWindowsCgo(
|
||||||
|
clientDir: string,
|
||||||
|
sendToStream: (data: any) => void,
|
||||||
|
): void {
|
||||||
|
const headerPath = nvcodecHeaderPath(clientDir);
|
||||||
|
if (fs.existsSync(headerPath)) {
|
||||||
|
sendToStream({
|
||||||
|
type: "output",
|
||||||
|
text: `Native NVENC header: ${headerPath}\n`,
|
||||||
|
level: "info",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
"Native NVENC streaming requires Overlord-Client/third_party/nvcodec/nvEncodeAPI.h for Windows CGO builds. Restore the vendored header or run scripts/vendor-nvcodec-headers from the repo root.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function writeSgnTextArtifact(
|
function writeSgnTextArtifact(
|
||||||
sgnPath: string,
|
sgnPath: string,
|
||||||
txtPath: string,
|
txtPath: string,
|
||||||
@@ -1032,6 +1054,10 @@ func runBoundFiles() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (effectiveOs === "windows" && env.CGO_ENABLED === "1") {
|
||||||
|
ensureNVCodecHeaderForWindowsCgo(clientDir, sendToStream);
|
||||||
|
}
|
||||||
|
|
||||||
let ldflags = config.stripDebug !== false ? "-s -w -buildid=" : "";
|
let ldflags = config.stripDebug !== false ? "-s -w -buildid=" : "";
|
||||||
|
|
||||||
if (config.serverUrl) {
|
if (config.serverUrl) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ if not defined ENABLE_PERSISTENCE set ENABLE_PERSISTENCE=false
|
|||||||
if not defined OBFUSCATE set OBFUSCATE=false
|
if not defined OBFUSCATE set OBFUSCATE=false
|
||||||
if not defined HIDE_CONSOLE set HIDE_CONSOLE=false
|
if not defined HIDE_CONSOLE set HIDE_CONSOLE=false
|
||||||
if not defined NO_PRINTING set NO_PRINTING=false
|
if not defined NO_PRINTING set NO_PRINTING=false
|
||||||
|
if not defined DISABLE_CGO set DISABLE_CGO=false
|
||||||
|
|
||||||
set "GARBLE_FLAGS="
|
set "GARBLE_FLAGS="
|
||||||
|
|
||||||
@@ -83,7 +84,16 @@ pushd "%CLIENT_DIR%"
|
|||||||
echo == Building agent for windows amd64 ==
|
echo == Building agent for windows amd64 ==
|
||||||
set GOOS=windows
|
set GOOS=windows
|
||||||
set GOARCH=amd64
|
set GOARCH=amd64
|
||||||
set CGO_ENABLED=0
|
if /I "%DISABLE_CGO%"=="true" (
|
||||||
|
set CGO_ENABLED=0
|
||||||
|
) else (
|
||||||
|
set CGO_ENABLED=1
|
||||||
|
if not exist "%CLIENT_DIR%\third_party\nvcodec\nvEncodeAPI.h" (
|
||||||
|
echo Missing NVENC header: "%CLIENT_DIR%\third_party\nvcodec\nvEncodeAPI.h"
|
||||||
|
echo Run scripts\vendor-nvcodec-headers.bat from the repo root or set DISABLE_CGO=true.
|
||||||
|
goto :err
|
||||||
|
)
|
||||||
|
)
|
||||||
%BUILD_CMD% %BUILD_TAGS% -ldflags="%LDFLAGS% %WIN_LDFLAGS%" -o "%OUT_DIR%\agent-windows-amd64.exe" ./cmd/agent
|
%BUILD_CMD% %BUILD_TAGS% -ldflags="%LDFLAGS% %WIN_LDFLAGS%" -o "%OUT_DIR%\agent-windows-amd64.exe" ./cmd/agent
|
||||||
if errorlevel 1 goto :err
|
if errorlevel 1 goto :err
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
set "SCRIPT_DIR=%~dp0"
|
||||||
|
for %%I in ("%SCRIPT_DIR%..") do set "ROOT_DIR=%%~fI"
|
||||||
|
set "DEST_DIR=%ROOT_DIR%\Overlord-Client\third_party\nvcodec"
|
||||||
|
set "DEST_HEADER=%DEST_DIR%\nvEncodeAPI.h"
|
||||||
|
if "%NV_CODEC_HEADERS_REPO%"=="" set "NV_CODEC_HEADERS_REPO=https://github.com/FFmpeg/nv-codec-headers.git"
|
||||||
|
if "%NV_CODEC_HEADERS_REF%"=="" set "NV_CODEC_HEADERS_REF=master"
|
||||||
|
|
||||||
|
set "FORCE=%FORCE_NV_CODEC_HEADERS%"
|
||||||
|
if /I "%~1"=="--force" set "FORCE=1"
|
||||||
|
|
||||||
|
if exist "%DEST_HEADER%" if not "%FORCE%"=="1" (
|
||||||
|
echo nvEncodeAPI.h already exists: "%DEST_HEADER%"
|
||||||
|
echo Use --force or set FORCE_NV_CODEC_HEADERS=1 to refresh it.
|
||||||
|
exit /b 0
|
||||||
|
)
|
||||||
|
|
||||||
|
where git >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo git is required to vendor nv-codec-headers
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
set "TMP_DIR=%DEST_DIR%\.nv-codec-headers.tmp"
|
||||||
|
if exist "%TMP_DIR%" rd /s /q "%TMP_DIR%"
|
||||||
|
if not exist "%DEST_DIR%" mkdir "%DEST_DIR%"
|
||||||
|
|
||||||
|
echo Cloning %NV_CODEC_HEADERS_REPO% (%NV_CODEC_HEADERS_REF%)...
|
||||||
|
git clone --depth 1 --branch "%NV_CODEC_HEADERS_REF%" "%NV_CODEC_HEADERS_REPO%" "%TMP_DIR%"
|
||||||
|
if errorlevel 1 goto :err
|
||||||
|
|
||||||
|
set "SRC_HEADER=%TMP_DIR%\include\ffnvcodec\nvEncodeAPI.h"
|
||||||
|
if not exist "%SRC_HEADER%" (
|
||||||
|
echo Expected header not found: "%SRC_HEADER%"
|
||||||
|
goto :err
|
||||||
|
)
|
||||||
|
|
||||||
|
copy /y "%SRC_HEADER%" "%DEST_HEADER%" >nul
|
||||||
|
if errorlevel 1 goto :err
|
||||||
|
rd /s /q "%TMP_DIR%"
|
||||||
|
|
||||||
|
echo Vendored "%DEST_HEADER%"
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:err
|
||||||
|
if exist "%TMP_DIR%" rd /s /q "%TMP_DIR%"
|
||||||
|
echo Failed to vendor nv-codec-headers
|
||||||
|
exit /b 1
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||||
|
DEST_DIR="${ROOT_DIR}/Overlord-Client/third_party/nvcodec"
|
||||||
|
DEST_HEADER="${DEST_DIR}/nvEncodeAPI.h"
|
||||||
|
REPO_URL="${NV_CODEC_HEADERS_REPO:-https://github.com/FFmpeg/nv-codec-headers.git}"
|
||||||
|
REF="${NV_CODEC_HEADERS_REF:-master}"
|
||||||
|
FORCE="${FORCE_NV_CODEC_HEADERS:-0}"
|
||||||
|
|
||||||
|
if [[ "${1:-}" == "--force" ]]; then
|
||||||
|
FORCE=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -s "${DEST_HEADER}" && "${FORCE}" != "1" ]]; then
|
||||||
|
echo "nvEncodeAPI.h already exists: ${DEST_HEADER}"
|
||||||
|
echo "Use --force or FORCE_NV_CODEC_HEADERS=1 to refresh it."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
command -v git >/dev/null 2>&1 || {
|
||||||
|
echo "git is required to vendor nv-codec-headers" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
TMP_DIR="${DEST_DIR}/.nv-codec-headers.tmp"
|
||||||
|
rm -rf "${TMP_DIR}"
|
||||||
|
mkdir -p "${DEST_DIR}"
|
||||||
|
|
||||||
|
echo "Cloning ${REPO_URL} (${REF})..."
|
||||||
|
git clone --depth 1 --branch "${REF}" "${REPO_URL}" "${TMP_DIR}"
|
||||||
|
|
||||||
|
SRC_HEADER="${TMP_DIR}/include/ffnvcodec/nvEncodeAPI.h"
|
||||||
|
if [[ ! -s "${SRC_HEADER}" ]]; then
|
||||||
|
echo "Expected header not found: ${SRC_HEADER}" >&2
|
||||||
|
rm -rf "${TMP_DIR}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp "${SRC_HEADER}" "${DEST_HEADER}"
|
||||||
|
rm -rf "${TMP_DIR}"
|
||||||
|
|
||||||
|
echo "Vendored ${DEST_HEADER}"
|
||||||
Reference in New Issue
Block a user