CameraInterops.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. using Org.BouncyCastle.Tls;
  2. namespace SinMaiLauncher.Interops;
  3. using System.Runtime.InteropServices;
  4. using DirectShowLib;
  5. internal static class CameraInterops
  6. {
  7. public static DsDevice[] GetAllCamera() => DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);
  8. internal class CameraCaptureWrap : IDisposable
  9. {
  10. private readonly Control _container;
  11. private readonly IFilterGraph2 graphBuilder;
  12. private readonly ICaptureGraphBuilder2 captureGraphBuilder;
  13. private readonly IMediaControl mediaControl;
  14. private readonly IVideoWindow? videoWindow;
  15. private readonly IBaseFilter captureFilter;
  16. private readonly IAMStreamConfig streamConfig;
  17. public IReadOnlyList<ICameraFormat> SupportedFormats { get; private set; }
  18. public CameraCaptureWrap(DsDevice camera, Control container)
  19. {
  20. _container = container;
  21. // 创建滤镜图
  22. graphBuilder = (IFilterGraph2)new FilterGraph();
  23. mediaControl = (IMediaControl)graphBuilder;
  24. videoWindow = (IVideoWindow)graphBuilder;
  25. // 创建捕获图构建器并设置滤镜图
  26. captureGraphBuilder = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
  27. captureGraphBuilder.SetFiltergraph(graphBuilder);
  28. // 使用指定的摄像头设备
  29. var iid = typeof(IBaseFilter).GUID;
  30. camera.Mon.BindToObject(null, null, ref iid, out var source);
  31. captureFilter = (IBaseFilter)source;
  32. graphBuilder.AddFilter(captureFilter, "Video Capture");
  33. // 获取 IAMStreamConfig 接口
  34. captureGraphBuilder.FindInterface(
  35. PinCategory.Capture, MediaType.Video, captureFilter,
  36. typeof(IAMStreamConfig).GUID, out var configObj);
  37. streamConfig = configObj as IAMStreamConfig;
  38. // 枚举支持的视频格式
  39. streamConfig.GetNumberOfCapabilities(out var count, out var size);
  40. var fmtLst = new List<CameraFormat>(count);
  41. for (var i = 0; i < count; i++)
  42. {
  43. // 分配内存并获取指向 VideoStreamConfigCaps 的指针
  44. var capsPtr = Marshal.AllocHGlobal(size);
  45. try
  46. {
  47. // 调用 GetStreamCaps
  48. var hr = streamConfig.GetStreamCaps(i, out var mediaType, capsPtr);
  49. if (hr < 0)
  50. {
  51. Console.WriteLine($"GetStreamCaps failed with error: {hr}");
  52. continue;
  53. }
  54. // 解析媒体类型和 caps
  55. if (mediaType.formatType == FormatType.VideoInfo)
  56. {
  57. // 将指针转换回结构
  58. var caps = (VideoStreamConfigCaps)Marshal.PtrToStructure(capsPtr, typeof(VideoStreamConfigCaps));
  59. //TO DO: 使用caps,如果需要
  60. var fmt = new CameraFormat(mediaType);
  61. fmtLst.Add(fmt);
  62. }
  63. }
  64. finally
  65. {
  66. // 释放分配的内存
  67. Marshal.FreeHGlobal(capsPtr);
  68. }
  69. }
  70. SupportedFormats = fmtLst.ToArray();
  71. }
  72. public interface ICameraFormat
  73. {
  74. public int Width { get; }
  75. public int Height { get; }
  76. public double Fps { get; }
  77. public int Bits { get; }
  78. public string Name => $"{Width}x{Height} {Bits}Bit {Fps:N2}FPS";
  79. }
  80. public class CameraFormat : ICameraFormat
  81. {
  82. public AMMediaType MediaType { get; }
  83. public int Width { get; }
  84. public int Height { get; }
  85. public double Fps { get; }
  86. public int Bits { get; }
  87. public string Name => $"{Width}x{Height} {Bits}Bit {Fps:N2}FPS";
  88. public CameraFormat(AMMediaType mediaType)
  89. {
  90. MediaType = mediaType;
  91. var vih = (VideoInfoHeader?)Marshal.PtrToStructure(mediaType.formatPtr, typeof(VideoInfoHeader));
  92. Width = vih.BmiHeader.Width;
  93. Height = vih.BmiHeader.Height;
  94. Bits = vih.BmiHeader.BitCount;
  95. Fps = 10000000.0 / vih.AvgTimePerFrame;
  96. }
  97. }
  98. public void Dispose()
  99. {
  100. var pfs = FilterState.Stopped;
  101. mediaControl?.GetState(100, out pfs);
  102. if (pfs != FilterState.Stopped) mediaControl?.Stop();
  103. videoWindow?.put_Visible(OABool.False);
  104. _container.Resize -= UpdateWindowPosition;
  105. Marshal.ReleaseComObject(mediaControl);
  106. Marshal.ReleaseComObject(videoWindow);
  107. Marshal.ReleaseComObject(graphBuilder);
  108. Marshal.ReleaseComObject(captureGraphBuilder);
  109. Marshal.ReleaseComObject(captureFilter);
  110. Marshal.ReleaseComObject(streamConfig);
  111. // 释放 AMMediaType
  112. foreach (var format in SupportedFormats)
  113. {
  114. DsUtils.FreeAMMediaType(((CameraFormat)format).MediaType);
  115. }
  116. SupportedFormats = Array.Empty<ICameraFormat>();
  117. }
  118. public void ApplySelectedFormat(ICameraFormat selectedItem)
  119. {
  120. mediaControl.GetState(100, out var pfs);
  121. if (pfs != FilterState.Stopped) mediaControl.Stop();
  122. var fmt = (CameraFormat)selectedItem;
  123. streamConfig.SetFormat(fmt.MediaType);
  124. // 渲染预览流
  125. captureGraphBuilder.RenderStream(PinCategory.Preview, MediaType.Video, captureFilter, null, null);
  126. mediaControl.Run();
  127. // 设置视频窗口
  128. videoWindow.put_Owner(_container.Handle);
  129. videoWindow.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren);
  130. videoWindow.put_Visible(OABool.True); // 确保窗口可见
  131. videoWindow.put_MessageDrain(_container.Handle); // 将消息转发到父窗口
  132. UpdateWindowPosition();
  133. _container.Resize += UpdateWindowPosition;
  134. }
  135. public ICameraFormat? FindBestFormat(int targetWidth, int targetHeight, double targetRefreshRate)
  136. {
  137. ICameraFormat? bestMatch = null;
  138. var minScore = double.MaxValue;
  139. foreach (var format in SupportedFormats)
  140. {
  141. var fmt = (CameraFormat)format;
  142. var resolutionScore = (fmt.Width == targetWidth && fmt.Height == targetHeight) ? 0 : Math.Abs(fmt.Width - targetWidth) + Math.Abs(fmt.Height - targetHeight);
  143. var frameRateScore = Math.Abs(fmt.Fps - targetRefreshRate);
  144. var totalScore = resolutionScore * 1000 + frameRateScore; // 分辨率优先
  145. if (totalScore < minScore)
  146. {
  147. minScore = totalScore;
  148. bestMatch = format;
  149. }
  150. }
  151. return bestMatch;
  152. }
  153. private void UpdateWindowPosition(object? sender = null, EventArgs? e = null)
  154. {
  155. videoWindow?.SetWindowPosition(0, 0, _container.Width, _container.Height); // 设置初始大小
  156. }
  157. }
  158. }