CameraImpl.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. using System;
  2. using System.Threading;
  3. using ivf_tl_CameraHelper;
  4. namespace IvfTl.Hardware.Impl
  5. {
  6. /// <summary>
  7. /// ICamera 实现,内部包装 control 的 ivf_tl_CameraHelper.Camera(不改其内部逻辑)。
  8. /// 所有进 native 的调用统一经 ICameraGate 串行化(全进程一把锁,13 §3.5/§②)。
  9. /// 旧 Camera 内部自带的 static locker 保留(死代码隔离,01 §5);同一线程经两把锁不死锁,
  10. /// 真正跨调用者互斥由本层 _gate 保证。
  11. /// 蓝本 af Camera:GetFrameBuffer 做 null 保护;GrabStable 内置"丢残留帧 + 到位延时 + 重试"。
  12. /// </summary>
  13. public sealed class CameraImpl : ICamera
  14. {
  15. private readonly Camera _camera; // 包装的旧具体相机
  16. private readonly ICameraGate _gate; // 全进程相机锁
  17. private readonly int _index;
  18. private readonly int _width;
  19. private readonly int _height;
  20. public CameraImpl(int index, int width, int height, int exposure, ICameraGate gate)
  21. {
  22. _index = index;
  23. _width = width;
  24. _height = height;
  25. _gate = gate ?? throw new ArgumentNullException(nameof(gate));
  26. _camera = new Camera(index, width, height, exposure);
  27. }
  28. public int Index => _index;
  29. public int Width => _width;
  30. public int Height => _height;
  31. public int Exposure => _camera.Exposure;
  32. public string SerialNumber => _camera.NumBer;
  33. public bool IsInit => _camera.IsInit;
  34. public bool IsStart => _camera.IsStart;
  35. public object RawCamera => _camera;
  36. // ── 生命周期 ──
  37. public int Init(byte redGain = 25, byte greenGain = 14, byte blueGain = 25)
  38. {
  39. // 注:旧 Camera.Init() 固定增益(25/14/25),未暴露参数;此处沿用旧实现。
  40. // 增益自定义经 SetGain(旧 Camera 暂无该 API,见 SetGain 的 TODO)。
  41. return _gate.Invoke(() => _camera.Init());
  42. }
  43. public int SetOpMode(byte mode = 0, bool strobe = false)
  44. {
  45. return _gate.Invoke(() => _camera.SetOpMode());
  46. }
  47. public int ReadSerial()
  48. {
  49. return _gate.Invoke(() => _camera.GetNumbet());
  50. }
  51. public int UnInit()
  52. {
  53. return _gate.Invoke(() => _camera.UnInit());
  54. }
  55. // ── 参数 ──
  56. public int SetExposure(int exposure100us)
  57. {
  58. return _gate.Invoke(() => _camera.SetPartOfCapInfo(exposure100us));
  59. }
  60. public int SetGain(byte red, byte green, byte blue)
  61. {
  62. // TODO(M2): control 旧 Camera 未暴露独立 SetGain(增益随 Init 固定);
  63. // 自动对焦标定若需调增益,需向旧 Camera 补 native SetPartOfCapInfo(含 Gain) 包装,
  64. // 或在 HAL 内自行构造 capInfoStruct。M1 不需要,留 TODO + 待验证。
  65. return -1;
  66. }
  67. // ── 抓帧(单帧)──
  68. public int GrabRgb()
  69. {
  70. return _gate.Invoke(() => _camera.GetRgbData());
  71. }
  72. public byte[] GetFrameBuffer()
  73. {
  74. // _pDest 空保护语义(af Camera.cs:137):旧 Camera.SourceBuffer 内部直接 Marshal.Copy(pDest,...),
  75. // pDest 已释放时会抛异常——此处用 _gate 锁内取并捕获异常返回 null(按抓帧失败重试)。
  76. return _gate.Invoke(() =>
  77. {
  78. try
  79. {
  80. return _camera.SourceBuffer;
  81. }
  82. catch
  83. {
  84. return null;
  85. }
  86. });
  87. }
  88. public byte[] GrabStable(int preDelayMs = 0, bool discardStale = true, int retry = 2)
  89. {
  90. // 13 §3.5(1)(2)(4):抓前到位延时 + 丢残留帧 + 失败重试。
  91. if (preDelayMs > 0) Thread.Sleep(preDelayMs);
  92. for (int attempt = 0; attempt <= retry; attempt++)
  93. {
  94. // 丢残留帧:运动后旧帧可能滞留缓冲,先抓一帧丢弃(对应 af 双 Grab)。
  95. if (discardStale)
  96. {
  97. GrabRgb();
  98. }
  99. int r = GrabRgb();
  100. if (r == 0)
  101. {
  102. var buf = GetFrameBuffer();
  103. if (buf != null) return buf;
  104. }
  105. // 抓帧失败:短暂等待后重试
  106. Thread.Sleep(50);
  107. }
  108. return null;
  109. }
  110. // ── 实时预览 ──
  111. public int StartPreview(IntPtr hostControl, int left, int top, int width, int height)
  112. {
  113. return _gate.Invoke(() => _camera.Usb2Start(hostControl, left, top, width, height));
  114. }
  115. public int StopPreview()
  116. {
  117. return _gate.Invoke(() => _camera.Usb2Stop());
  118. }
  119. // ── operate 调试页"无图模式"两步抓帧 + 存图(M1-B2)──
  120. // 转发到底层具体 Camera 既有方法(GetRawData / RawToRgb / SaveBmpPic),全部经 _gate 串行化。
  121. public int GrabRaw()
  122. {
  123. return _gate.Invoke(() => _camera.GetRawData());
  124. }
  125. public int RawToRgb()
  126. {
  127. return _gate.Invoke(() => _camera.RawToRgb());
  128. }
  129. public bool SavePic(string fullName, int width, int height)
  130. {
  131. // 底层 control Camera 无 MVCAPI.SavePic;用其既有 SaveBmpPic(从 SourceBuffer 取 24bpp、
  132. // RotateFlipY 后按文件扩展名落盘,.jpg 即存 JPEG),与 operate 调试页存图语义等价。
  133. // 取像素 + 落盘都涉及 native 内存(SourceBuffer→Marshal.Copy),经 _gate 与抓帧/释放互斥。
  134. return _gate.Invoke(() => _camera.SaveBmpPic(fullName));
  135. }
  136. public void Dispose()
  137. {
  138. try { UnInit(); } catch { }
  139. try { _gate.Invoke(() => _camera.DisPose()); } catch { }
  140. }
  141. }
  142. }