This commit is contained in:
KingKDot
2026-08-04 00:03:01 -05:00
parent dd06b573d0
commit fd2c2fdbfa
4 changed files with 81 additions and 21 deletions
@@ -37,7 +37,8 @@ const (
mftMessageNotifyEndOfStream = 0x10000002 mftMessageNotifyEndOfStream = 0x10000002
mftMessageNotifyEndStreaming = 0x10000001 mftMessageNotifyEndStreaming = 0x10000001
mftOutputStreamProvidesSamples = 0x00000100 mftOutputStreamProvidesSamples = 0x00000100
mftOutputStreamCanProvideSamples = 0x00000200
mfETransformNeedMoreInput = 0xC00D6D72 mfETransformNeedMoreInput = 0xC00D6D72
mfETransformStreamChange = 0xC00D6D61 mfETransformStreamChange = 0xC00D6D61
@@ -1067,7 +1068,7 @@ func (e *mfH264Encoder) processOutput() ([]byte, error) {
} }
var outSample *mfSample var outSample *mfSample
if info.flags&mftOutputStreamProvidesSamples == 0 { if !mftCanAllocateOutputSample(info.flags) {
size := int(info.cbSize) size := int(info.cbSize)
minSize := e.width * e.height * 3 / 2 minSize := e.width * e.height * 3 / 2
if size < minSize { if size < minSize {
@@ -1098,7 +1099,11 @@ func (e *mfH264Encoder) processOutput() ([]byte, error) {
return nil, nil return nil, nil
default: default:
if failedHR(hr) { if failedHR(hr) {
return nil, fmt.Errorf("mf h264: ProcessOutput failed 0x%x", hr) allocation := "transform"
if outSample != nil {
allocation = "caller"
}
return nil, fmt.Errorf("mf h264: ProcessOutput failed 0x%x stream_flags=0x%x output_sample=%s", hr, info.flags, allocation)
} }
} }
if output.sample == nil { if output.sample == nil {
@@ -1110,6 +1115,10 @@ func (e *mfH264Encoder) processOutput() ([]byte, error) {
return sampleBytes(output.sample) return sampleBytes(output.sample)
} }
func mftCanAllocateOutputSample(flags uint32) bool {
return flags&(mftOutputStreamProvidesSamples|mftOutputStreamCanProvideSamples) != 0
}
func createVideoType(subtype windows.GUID, width, height, fps int) (*mfMediaType, error) { func createVideoType(subtype windows.GUID, width, height, fps int) (*mfMediaType, error) {
var mt *mfMediaType var mt *mfMediaType
hr, _, _ := procMFCreateMediaType.Call(uintptr(unsafe.Pointer(&mt))) hr, _, _ := procMFCreateMediaType.Call(uintptr(unsafe.Pointer(&mt)))
@@ -0,0 +1,25 @@
//go:build windows
package capture
import "testing"
func TestMFTCanAllocateOutputSample(t *testing.T) {
tests := []struct {
name string
flags uint32
want bool
}{
{name: "caller must allocate", flags: 0, want: false},
{name: "transform provides samples", flags: mftOutputStreamProvidesSamples, want: true},
{name: "transform can provide samples", flags: mftOutputStreamCanProvideSamples, want: true},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if got := mftCanAllocateOutputSample(test.flags); got != test.want {
t.Fatalf("mftCanAllocateOutputSample(0x%x) = %t, want %t", test.flags, got, test.want)
}
})
}
}
@@ -1365,31 +1365,47 @@ func findOutput(factory *idxgiFactory1, desiredName string, desiredBounds image.
} }
func createD3DDevice(adapter *idxgiAdapter1) (*d3d11Device, *d3d11DeviceContext, error) { func createD3DDevice(adapter *idxgiAdapter1) (*d3d11Device, *d3d11DeviceContext, error) {
var device *d3d11Device flags := d3d11DeviceCreationFlagCandidates()
var context *d3d11DeviceContext var firstHR uintptr
var featureLevel uint32 for attempt, creationFlags := range flags {
hr, _, _ := procD3D11CreateDevice.Call( var device *d3d11Device
uintptr(unsafe.Pointer(adapter)), var context *d3d11DeviceContext
uintptr(d3dDriverTypeUnknown), var featureLevel uint32
0, hr, _, _ := procD3D11CreateDevice.Call(
uintptr(d3d11CreateDeviceBgraSupport|d3d11CreateDeviceVideoSupport), uintptr(unsafe.Pointer(adapter)),
0, uintptr(d3dDriverTypeUnknown),
0, 0,
uintptr(d3d11SdkVersion), uintptr(creationFlags),
uintptr(unsafe.Pointer(&device)), 0,
uintptr(unsafe.Pointer(&featureLevel)), 0,
uintptr(unsafe.Pointer(&context)), uintptr(d3d11SdkVersion),
) uintptr(unsafe.Pointer(&device)),
if hr != S_OK || device == nil || context == nil { uintptr(unsafe.Pointer(&featureLevel)),
uintptr(unsafe.Pointer(&context)),
)
if hr == S_OK && device != nil && context != nil {
return device, context, nil
}
if context != nil { if context != nil {
context.Release() context.Release()
} }
if device != nil { if device != nil {
device.Release() device.Release()
} }
return nil, nil, fmt.Errorf("dxgi: D3D11CreateDevice failed 0x%x", hr) if attempt == 0 {
firstHR = hr
continue
}
return nil, nil, fmt.Errorf("dxgi: D3D11CreateDevice failed video=0x%x fallback=0x%x", firstHR, hr)
}
return nil, nil, errors.New("dxgi: D3D11CreateDevice had no creation modes")
}
func d3d11DeviceCreationFlagCandidates() [2]uint32 {
return [2]uint32{
d3d11CreateDeviceBgraSupport | d3d11CreateDeviceVideoSupport,
d3d11CreateDeviceBgraSupport,
} }
return device, context, nil
} }
func convertBGRA(src []byte, pitch, width, height int, rotation uint32) *image.RGBA { func convertBGRA(src []byte, pitch, width, height int, rotation uint32) *image.RGBA {
@@ -49,6 +49,16 @@ func TestDesktopDuplicationToggleResetsState(t *testing.T) {
} }
} }
func TestD3D11DeviceCreationFlagsFallBackWithoutVideoSupport(t *testing.T) {
flags := d3d11DeviceCreationFlagCandidates()
if flags[0] != d3d11CreateDeviceBgraSupport|d3d11CreateDeviceVideoSupport {
t.Fatalf("first D3D11 creation flags = 0x%x, want BGRA and video support", flags[0])
}
if flags[1] != d3d11CreateDeviceBgraSupport {
t.Fatalf("fallback D3D11 creation flags = 0x%x, want BGRA only", flags[1])
}
}
func TestCaptureDisplayDXGI(t *testing.T) { func TestCaptureDisplayDXGI(t *testing.T) {
if displayCount() == 0 { if displayCount() == 0 {
t.Skip("no displays detected") t.Skip("no displays detected")