Skip to content

Commit 01d010a

Browse files
committed
获取到了摄像头和分辨率
1 parent a89086f commit 01d010a

4 files changed

Lines changed: 324 additions & 29 deletions

File tree

SecRandom.Core/Helpers/Media/CameraResolutionHelper.cs

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,287 @@ public readonly record struct CameraResolution(int Width, int Height)
1212
public override string ToString() => $"{Width}x{Height}";
1313
}
1414

15+
public static IReadOnlyList<CameraResolution> GetSuggestedResolutionsByIndex(int deviceIndex)
16+
{
17+
var max = GetMaximumResolutionByIndex(deviceIndex);
18+
if (max.Width <= 0 || max.Height <= 0)
19+
{
20+
max = new CameraResolution(1920, 1080);
21+
}
22+
23+
var result = new HashSet<CameraResolution>();
24+
result.Add(NormalizeEven(max));
25+
26+
var scalePercents = new[] { 100, 75, 67, 50, 40, 33, 25 };
27+
foreach (var percent in scalePercents)
28+
{
29+
if (percent == 100)
30+
{
31+
continue;
32+
}
33+
34+
var w = (int)Math.Round(max.Width * (percent / 100.0));
35+
var h = (int)Math.Round(max.Height * (percent / 100.0));
36+
var scaled = NormalizeEven(new CameraResolution(w, h));
37+
if (scaled.Width >= 320 && scaled.Height >= 240)
38+
{
39+
result.Add(scaled);
40+
}
41+
}
42+
43+
foreach (var targetHeight in new[] { 2160, 1440, 1080, 720, 540, 480, 360, 240 })
44+
{
45+
if (targetHeight >= max.Height)
46+
{
47+
continue;
48+
}
49+
50+
var w = (int)Math.Round(max.Width * (targetHeight / (double)max.Height));
51+
var h = targetHeight;
52+
var scaled = NormalizeEven(new CameraResolution(w, h));
53+
if (scaled.Width >= 320 && scaled.Height >= 240)
54+
{
55+
result.Add(scaled);
56+
}
57+
}
58+
59+
return result
60+
.OrderByDescending(x => (long)x.Width * x.Height)
61+
.ThenByDescending(x => x.Width)
62+
.ThenByDescending(x => x.Height)
63+
.ToList();
64+
}
65+
66+
public static CameraResolution GetMaximumResolutionByIndex(int deviceIndex)
67+
{
68+
using var _ = new ComInitScope();
69+
70+
var list = GetResolutionsByIndex(deviceIndex);
71+
if (list.Count == 0)
72+
{
73+
var current = GetCurrentResolutionByIndex(deviceIndex);
74+
if (current.Width > 0 && current.Height > 0)
75+
{
76+
return current;
77+
}
78+
79+
return default;
80+
}
81+
82+
CameraResolution best = default;
83+
long bestArea = -1;
84+
foreach (var r in list)
85+
{
86+
if (r.Width <= 0 || r.Height <= 0)
87+
{
88+
continue;
89+
}
90+
91+
var area = (long)r.Width * r.Height;
92+
if (area > bestArea)
93+
{
94+
best = r;
95+
bestArea = area;
96+
}
97+
}
98+
99+
return best;
100+
}
101+
102+
private static CameraResolution NormalizeEven(CameraResolution resolution)
103+
{
104+
var w = resolution.Width;
105+
var h = resolution.Height;
106+
if (w <= 0 || h <= 0)
107+
{
108+
return default;
109+
}
110+
111+
if ((w & 1) == 1)
112+
{
113+
w--;
114+
}
115+
116+
if ((h & 1) == 1)
117+
{
118+
h--;
119+
}
120+
121+
if (w <= 0 || h <= 0)
122+
{
123+
return default;
124+
}
125+
126+
return new CameraResolution(w, h);
127+
}
128+
129+
private static CameraResolution GetCurrentResolutionByIndex(int deviceIndex)
130+
{
131+
if (!OperatingSystem.IsWindows())
132+
{
133+
return default;
134+
}
135+
136+
if (deviceIndex < 0)
137+
{
138+
return default;
139+
}
140+
141+
var devEnumType = Type.GetTypeFromCLSID(Clsid.SystemDeviceEnum);
142+
if (devEnumType is null)
143+
{
144+
return default;
145+
}
146+
147+
var createDevEnum = (ICreateDevEnum?)Activator.CreateInstance(devEnumType);
148+
if (createDevEnum is null)
149+
{
150+
return default;
151+
}
152+
153+
IEnumMoniker? enumMoniker = null;
154+
IBaseFilter? filter = null;
155+
IEnumPins? enumPins = null;
156+
IPin? pin = null;
157+
158+
try
159+
{
160+
var category = FilterCategory.VideoInputDevice;
161+
var hr = createDevEnum.CreateClassEnumerator(in category, out enumMoniker, 0);
162+
if (hr != 0 || enumMoniker is null)
163+
{
164+
return default;
165+
}
166+
167+
var targetMoniker = GetMonikerByIndex(enumMoniker, deviceIndex);
168+
if (targetMoniker is null)
169+
{
170+
return default;
171+
}
172+
173+
try
174+
{
175+
var filterGuid = typeof(IBaseFilter).GUID;
176+
targetMoniker.BindToObject(null, null, in filterGuid, out var filterObj);
177+
filter = filterObj as IBaseFilter;
178+
if (filter is null)
179+
{
180+
if (filterObj is not null)
181+
{
182+
Marshal.ReleaseComObject(filterObj);
183+
}
184+
return default;
185+
}
186+
187+
filter.EnumPins(out enumPins);
188+
if (enumPins is null)
189+
{
190+
return default;
191+
}
192+
193+
var pins = new IPin[1];
194+
while (enumPins.Next(1, pins, IntPtr.Zero) == 0)
195+
{
196+
pin = pins[0];
197+
try
198+
{
199+
pin.QueryDirection(out var direction);
200+
if (direction != PinDirection.Output)
201+
{
202+
continue;
203+
}
204+
205+
if (!TryGetStreamConfig(pin, out var streamConfig) || streamConfig is null)
206+
{
207+
continue;
208+
}
209+
210+
try
211+
{
212+
var fmtHr = streamConfig.GetFormat(out var pmtPtr);
213+
if (fmtHr != 0 || pmtPtr == IntPtr.Zero)
214+
{
215+
continue;
216+
}
217+
218+
try
219+
{
220+
var mt = Marshal.PtrToStructure<AMMediaType>(pmtPtr);
221+
try
222+
{
223+
if (TryParseResolution(mt, out var resolution) &&
224+
resolution.Width > 0 &&
225+
resolution.Height > 0)
226+
{
227+
return resolution;
228+
}
229+
}
230+
finally
231+
{
232+
FreeMediaType(ref mt);
233+
}
234+
}
235+
finally
236+
{
237+
Marshal.FreeCoTaskMem(pmtPtr);
238+
}
239+
}
240+
finally
241+
{
242+
Marshal.ReleaseComObject(streamConfig);
243+
}
244+
}
245+
finally
246+
{
247+
Marshal.ReleaseComObject(pin);
248+
pin = null;
249+
}
250+
251+
pins = new IPin[1];
252+
}
253+
254+
return default;
255+
}
256+
finally
257+
{
258+
Marshal.ReleaseComObject(targetMoniker);
259+
}
260+
}
261+
catch
262+
{
263+
return default;
264+
}
265+
finally
266+
{
267+
if (pin is not null)
268+
{
269+
Marshal.ReleaseComObject(pin);
270+
}
271+
if (enumPins is not null)
272+
{
273+
Marshal.ReleaseComObject(enumPins);
274+
}
275+
if (filter is not null)
276+
{
277+
Marshal.ReleaseComObject(filter);
278+
}
279+
if (enumMoniker is not null)
280+
{
281+
Marshal.ReleaseComObject(enumMoniker);
282+
}
283+
Marshal.ReleaseComObject(createDevEnum);
284+
}
285+
}
286+
15287
public static IReadOnlyList<string> GetCameraNames()
16288
{
17289
if (!OperatingSystem.IsWindows())
18290
{
19291
return [];
20292
}
21293

294+
using var _ = new ComInitScope();
295+
22296
var devEnumType = Type.GetTypeFromCLSID(Clsid.SystemDeviceEnum);
23297
if (devEnumType is null)
24298
{
@@ -96,6 +370,8 @@ public static IReadOnlyList<CameraResolution> GetResolutionsByIndex(int deviceIn
96370
return [];
97371
}
98372

373+
using var _ = new ComInitScope();
374+
99375
try
100376
{
101377
return EnumerateDirectShowResolutions(deviceIndex);
@@ -281,6 +557,12 @@ private static IReadOnlyList<CameraResolution> EnumerateDirectShowResolutions(in
281557

282558
private static bool TryGetStreamConfig(IPin pin, out IAMStreamConfig? streamConfig)
283559
{
560+
if (pin is IAMStreamConfig directCast)
561+
{
562+
streamConfig = directCast;
563+
return true;
564+
}
565+
284566
streamConfig = null;
285567
var iid = typeof(IAMStreamConfig).GUID;
286568
var unk = Marshal.GetIUnknownForObject(pin);
@@ -310,6 +592,43 @@ private static bool TryGetStreamConfig(IPin pin, out IAMStreamConfig? streamConf
310592
}
311593
}
312594

595+
private sealed class ComInitScope : IDisposable
596+
{
597+
private readonly bool _initialized;
598+
599+
public ComInitScope()
600+
{
601+
if (!OperatingSystem.IsWindows())
602+
{
603+
_initialized = false;
604+
return;
605+
}
606+
607+
var hr = CoInitializeEx(IntPtr.Zero, CoInit.MultiThreaded);
608+
_initialized = hr == 0 || hr == 1;
609+
}
610+
611+
public void Dispose()
612+
{
613+
if (_initialized)
614+
{
615+
CoUninitialize();
616+
}
617+
}
618+
}
619+
620+
[Flags]
621+
private enum CoInit : uint
622+
{
623+
MultiThreaded = 0x0
624+
}
625+
626+
[DllImport("ole32.dll")]
627+
private static extern int CoInitializeEx(IntPtr pvReserved, CoInit dwCoInit);
628+
629+
[DllImport("ole32.dll")]
630+
private static extern void CoUninitialize();
631+
313632
private static string? TryGetMonikerFriendlyName(IMoniker moniker)
314633
{
315634
IPropertyBag? bag = null;

SecRandom/Langs/SettingsPages/DrawSettingsPage/Resources.en-us.resx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,10 @@
8686
<value>Camera Resolution</value>
8787
</data>
8888
<data name="CameraDisplayResolutionDesc" xml:space="preserve">
89-
<value>Set camera preview resolution (empty = highest)</value>
89+
<value>Auto uses the maximum resolution</value>
9090
</data>
9191
<data name="CameraDisplayResolutionPlaceholder" xml:space="preserve">
92-
<value>Auto (highest)</value>
92+
<value>Auto</value>
9393
</data>
9494
<data name="CameraPreviewMode" xml:space="preserve">
9595
<value>Mode</value>

SecRandom/Langs/SettingsPages/DrawSettingsPage/Resources.zh-hans.resx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,10 @@
8686
<value>摄像头显示分辨率</value>
8787
</data>
8888
<data name="CameraDisplayResolutionDesc" xml:space="preserve">
89-
<value>设置摄像头预览采集分辨率(留空=最高)</value>
89+
<value>自动使用最高分辨率</value>
9090
</data>
9191
<data name="CameraDisplayResolutionPlaceholder" xml:space="preserve">
92-
<value>自动(最高)</value>
92+
<value>自动</value>
9393
</data>
9494
<data name="CameraPreviewMode" xml:space="preserve">
9595
<value>模式</value>

0 commit comments

Comments
 (0)