perf(camera): MJPEG negotiation before frame reader — camera drives at its best rate
Slice 10 from today-slices (d1126dd): UVC cameras default MediaFrameReader to their FIRST media type (YUY2 at crippled fps — C920 720p = 5fps). A 60fps container then shows a 5-20fps face cam as "every few frames cut out". Fix: VideoDeviceController.SetMediaStreamPropertiesAsync negotiated to best MJPEG >=640x360 @>=30fps (<=1280 wide) BEFORE creating the frame reader, with device-default fallback. Plus a startup.log truth line of the actual agreed media type so the next diagnosis starts from facts (no more re-deriving the fps). Build 0 warnings, 289 tests.
This commit is contained in:
@@ -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<Windows.Media.MediaProperties.VideoEncodingProperties>()
|
||||
: controller
|
||||
.GetAvailableMediaStreamProperties(MediaStreamType.VideoPreview)
|
||||
.OfType<Windows.Media.MediaProperties.VideoEncodingProperties>()
|
||||
.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)
|
||||
|
||||
Reference in New Issue
Block a user