diff --git a/Services/MediaCaptureFrameSource.cs b/Services/MediaCaptureFrameSource.cs index 67a057c..8b7d646 100644 --- a/Services/MediaCaptureFrameSource.cs +++ b/Services/MediaCaptureFrameSource.cs @@ -102,6 +102,43 @@ public sealed class MediaCaptureFrameSource : ICameraFrameSource }; await capture.InitializeAsync(settings); + // MediaFrameReader defaults to the camera's FIRST media type — on UVC webcams + // (Logitech C920 et al.) that is often YUY2 at a crippled rate (720p5!), and a + // real-time 60fps container then shows a 5-20fps face cam as "every few frames + // cut out". OBS's mechanism, available via VideoDeviceController (VideoProfile + // is NOT projected in the 19041 SDK): negotiate the stream property. Pick the + // best MJPEG >=640x360 @>=30fps (largest, fastest), set it BEFORE the frame + // reader is created, fall back to device default on refusal, and log the live + // media type so the next diagnosis starts from facts. + try + { + var controller = capture.VideoDeviceController; + var candidates = controller == null + ? new System.Collections.Generic.List() + : controller + .GetAvailableMediaStreamProperties(MediaStreamType.VideoPreview) + .OfType() + .Where(v => string.Equals(v.Subtype, "MJPG", StringComparison.OrdinalIgnoreCase) + && v.Width >= 640 && v.Height >= 360 + && v.FrameRate.Denominator > 0 + && v.FrameRate.Numerator / v.FrameRate.Denominator >= 30.0) + .OrderByDescending(v => v.FrameRate.Numerator / (double)Math.Max(1, v.FrameRate.Denominator)) + .ThenByDescending(v => (long)v.Width * v.Height) + .ToList(); + if (candidates.Count > 0) + { + var pick = candidates + .OrderBy(v => (long)v.Width * v.Height) + .LastOrDefault(v => v.Width <= 1280) ?? candidates[0]; + await controller!.SetMediaStreamPropertiesAsync(MediaStreamType.VideoPreview, pick); + } + } + catch (Exception fmtEx) + { + AppLog.Write($"MediaCaptureFrameSource: MJPG negotiation refused ({fmtEx.Message}); " + + "keeping device-default media type"); + } + // Post-init resource validation: the capture must actually be bound to // the device we asked for and must expose a live video controller. if (!string.Equals(capture.MediaCaptureSettings?.VideoDeviceId, _deviceId, @@ -147,6 +184,17 @@ public sealed class MediaCaptureFrameSource : ICameraFrameSource if (reader == null) throw new InvalidOperationException($"Camera '{_deviceId}' created no frame reader."); + // One-line truth of what the camera actually agreed to (format/size/fps). + try + { + var active = capture.VideoDeviceController? + .GetMediaStreamProperties(colorSource.Info.MediaStreamType); + if (active is Windows.Media.MediaProperties.VideoEncodingProperties vep) + AppLog.Write($"Camera '{_deviceId}' media type: {vep.Subtype} " + + $"{vep.Width}x{vep.Height} @ {vep.FrameRate.Numerator}/{vep.FrameRate.Denominator}fps"); + } + catch { /* logging must never kill acquisition */ } + reader.FrameArrived += OnFrameArrived; var status = await reader.StartAsync(); if (status != MediaFrameReaderStartStatus.Success)