94a934ffa9
A browser grabbing the C920 kills our reader startup: the camera stays in MJPG 1920x1080@30 (another app's format) and our OBS-refusal to re-negotiate under SharedReadOnly contention (`SetMediaStreamPropertiesAsync` throws 'file in use / CaptureMode is SharedReadOnly') leaves it there. CreateFrameReaderAsync(.., Bgra8) + StartAsync then refuses with OutputFormatNotSupported — the MJPG-active source only exposes NV12 at the reader level (clue: startup.log 19:01/19:05 sessions, same hardware that started YUY2 640x480 fine at 08:52). Fresh launches showed no webcam and 'Add Webcam' failed. Reader creation is now a per-candidate ladder (ReaderSubtypeCandidates): Bgra8 for uncompressed cameras (unchanged fast path); NV12 then the source-default for MJPG cameras — converted in OnFrameArrived like any non-BGRA frame. Each candidate is allocated AND started under its OWN catch: WinRT answers an unsupported subtype with a throw (E_INVALIDARG), not a status, so a single rejected format must degrade to the next candidate instead of aborting acquisition (creator rule — see MyMistakes WINRT resource-allocation recipe). Rejections are logged and collected into the final error. Good Dog: 4 unit tests lock the candidate ordering (MJPG never Bgra8, case-insensitive, uncompressed keeps Bgra8 first, unknown/null -> Bgra8). 301/301 green, 0 warnings. [no push]
50 lines
2.0 KiB
C#
50 lines
2.0 KiB
C#
using Windows.Media.MediaProperties;
|
|
using Xunit;
|
|
using ytLive.Services;
|
|
|
|
namespace ytLive.Tests;
|
|
|
|
/// <summary>
|
|
/// Take-2026-09-15 fix: a camera that another app has left in MJPG mode can't be
|
|
/// read as Bgra8 by a MediaFrameReader (StartAsync answers OutputFormatNotSupported) —
|
|
/// exactly what a browser grabbing the webcam causes. The reader-subtype ladder in
|
|
/// MediaCaptureFrameSource picks an output the source can actually produce; these
|
|
/// tests lock the ordering so the fix can't regress into the silent empty webcam box.
|
|
/// </summary>
|
|
public sealed class MediaCaptureFrameSourceTests
|
|
{
|
|
[Fact]
|
|
public void ReaderSubtypeCandidates_MjpgActive_NeverAsksBgra8()
|
|
{
|
|
var candidates = MediaCaptureFrameSource.ReaderSubtypeCandidates("MJPG");
|
|
Assert.DoesNotContain(candidates, s => s == MediaEncodingSubtypes.Bgra8);
|
|
Assert.Equal(MediaEncodingSubtypes.Nv12, candidates[0]);
|
|
Assert.Null(candidates[^1]);
|
|
}
|
|
|
|
[Fact]
|
|
public void ReaderSubtypeCandidates_MjpgActive_IsCaseInsensitive()
|
|
{
|
|
var candidates = MediaCaptureFrameSource.ReaderSubtypeCandidates("mjpg");
|
|
Assert.Equal(MediaEncodingSubtypes.Nv12, candidates[0]);
|
|
Assert.DoesNotContain(candidates, s => s == MediaEncodingSubtypes.Bgra8);
|
|
}
|
|
|
|
[Fact]
|
|
public void ReaderSubtypeCandidates_UncompressedActive_KeepsBgra8FastPath()
|
|
{
|
|
var yuy2 = MediaCaptureFrameSource.ReaderSubtypeCandidates("YUY2");
|
|
var nv12 = MediaCaptureFrameSource.ReaderSubtypeCandidates("NV12");
|
|
Assert.Equal(MediaEncodingSubtypes.Bgra8, yuy2[0]);
|
|
Assert.Equal(MediaEncodingSubtypes.Bgra8, nv12[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void ReaderSubtypeCandidates_UnknownActive_DefaultsToBgra8()
|
|
{
|
|
Assert.Equal(MediaEncodingSubtypes.Bgra8,
|
|
MediaCaptureFrameSource.ReaderSubtypeCandidates(null)[0]);
|
|
Assert.Equal(MediaEncodingSubtypes.Bgra8,
|
|
MediaCaptureFrameSource.ReaderSubtypeCandidates("")[0]);
|
|
}
|
|
} |