AForgeCapturer.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. using AForge.Video;
  2. using AForge.Video.DirectShow;
  3. using Lennox.LibYuvSharp;
  4. using System;
  5. using System.Drawing;
  6. using System.Drawing.Imaging;
  7. using System.Linq;
  8. using System.Threading;
  9. using Vinno.FIS.TRTCClient.Common.Enum;
  10. using Vinno.FIS.TRTCClient.Common.Log;
  11. namespace Vinno.FIS.TRTCClient.Captures
  12. {
  13. internal class AForgeCapturer
  14. {
  15. private readonly string _id;
  16. private readonly int _outputWidth;
  17. private readonly int _outputHeight;
  18. private readonly int _deviceWidth;
  19. private readonly int _deviceHeight;
  20. private int _actualWidthBeforeResize;
  21. private int _actualHeightBeforeResize;
  22. private TRTCImageFrameData _imageFrameData;
  23. private VideoCaptureDevice _videoCaptureDevice;
  24. private readonly ManualResetEvent _handlingImageEvent = new ManualResetEvent(true);
  25. private EnumLiveChannelCategory _liveChannelCategory;
  26. private Rectangle _imageRectangle;
  27. private bool _isCapturing;
  28. private Bitmap _cameraBitmap;
  29. private int _lastCaptureTime = 0;
  30. private int _fps;
  31. private EnumCaptureMode _captureMode;
  32. public int OutputWidth => _outputWidth;
  33. public int OutputHeight => _outputHeight;
  34. public string Id => _id;
  35. public bool IsCapturing => _isCapturing;
  36. public event EventHandler<TRTCImageFrameData> ImageFrameReceived;
  37. public AForgeCapturer(string id, int width, int height, int fps, EnumLiveChannelCategory liveChannelCategory, int deviceWidth, int deviceHeight)
  38. {
  39. _id = id;
  40. _outputWidth = width;
  41. _outputHeight = height;
  42. _deviceWidth = deviceWidth;
  43. _deviceHeight = deviceHeight;
  44. _fps = fps;
  45. _liveChannelCategory = liveChannelCategory;
  46. _captureMode = EnumCaptureMode.Normal;
  47. _imageFrameData = new TRTCImageFrameData(OutputWidth, OutputHeight);
  48. _imageRectangle = new Rectangle(0, 0, OutputWidth, OutputHeight);
  49. Logger.WriteLineInfo($"AForgeCapturer {_id}: Resolution:{width} {height} {liveChannelCategory}");
  50. }
  51. public void StartCapture()
  52. {
  53. if (_isCapturing)
  54. {
  55. Logger.WriteLineWarn($"AForgeCapturer {_id}: Has Been In Capturing!");
  56. return;
  57. }
  58. var videoFilterInfos = new FilterInfoCollection(FilterCategory.VideoInputDevice).OfType<FilterInfo>();
  59. var filterInfo = videoFilterInfos.FirstOrDefault(i => Id.Contains(i.MonikerString) || i.MonikerString.Contains(Id));
  60. if (filterInfo != null)
  61. {
  62. _videoCaptureDevice = new VideoCaptureDevice(filterInfo.MonikerString);
  63. if (_outputHeight == _deviceHeight && _outputWidth == _deviceWidth)
  64. {
  65. var capbility = _videoCaptureDevice.VideoCapabilities.FirstOrDefault(x => x.FrameSize.Width == OutputWidth && x.FrameSize.Height == OutputHeight);
  66. if (capbility != null)
  67. {
  68. _videoCaptureDevice.VideoResolution = capbility;
  69. _actualWidthBeforeResize = OutputWidth;
  70. _actualHeightBeforeResize = OutputHeight;
  71. _captureMode = EnumCaptureMode.Normal;
  72. }
  73. else
  74. {
  75. capbility = _videoCaptureDevice.VideoCapabilities.Where(x => x.FrameSize.Width >= OutputWidth && x.FrameSize.Height >= OutputHeight).OrderBy(x => x.FrameSize.Width).FirstOrDefault();
  76. if (capbility != null)
  77. {
  78. _videoCaptureDevice.VideoResolution = capbility;
  79. _imageRectangle.Width = capbility.FrameSize.Width; ;
  80. _imageRectangle.Height = capbility.FrameSize.Height;
  81. _actualWidthBeforeResize = capbility.FrameSize.Width;
  82. _actualHeightBeforeResize = capbility.FrameSize.Height;
  83. _captureMode = EnumCaptureMode.Unspecify;
  84. }
  85. else
  86. {
  87. Logger.WriteLineError($"AForgeCapturer {_id}:Start Capture Error,The camera can't meet the Output{OutputWidth}X{OutputHeight} and Device{_deviceWidth}X{_deviceHeight}!");
  88. return;
  89. }
  90. }
  91. }
  92. else
  93. {
  94. var capbility = _videoCaptureDevice.VideoCapabilities.FirstOrDefault(x => x.FrameSize.Width == _deviceWidth && x.FrameSize.Height == _deviceHeight);
  95. if (capbility != null)
  96. {
  97. _videoCaptureDevice.VideoResolution = capbility;
  98. _imageRectangle.Width = capbility.FrameSize.Width; ;
  99. _imageRectangle.Height = capbility.FrameSize.Height;
  100. _actualWidthBeforeResize = capbility.FrameSize.Width;
  101. _actualHeightBeforeResize = capbility.FrameSize.Height;
  102. _captureMode = EnumCaptureMode.Specify;
  103. }
  104. else
  105. {
  106. capbility = _videoCaptureDevice.VideoCapabilities.Where(x => x.FrameSize.Width >= OutputWidth && x.FrameSize.Height >= OutputHeight).OrderBy(x => x.FrameSize.Width).FirstOrDefault();
  107. if (capbility != null)
  108. {
  109. _videoCaptureDevice.VideoResolution = capbility;
  110. _imageRectangle.Width = capbility.FrameSize.Width; ;
  111. _imageRectangle.Height = capbility.FrameSize.Height;
  112. _actualWidthBeforeResize = capbility.FrameSize.Width;
  113. _actualHeightBeforeResize = capbility.FrameSize.Height;
  114. _captureMode = EnumCaptureMode.Unspecify;
  115. }
  116. else
  117. {
  118. Logger.WriteLineError($"AForgeCapturer {_id}:Start Capture Error,The camera can't meet the Output{OutputWidth}X{OutputHeight} and Device{_deviceWidth}X{_deviceHeight}!");
  119. return;
  120. }
  121. }
  122. }
  123. Logger.WriteLineInfo($"AForgeCapturer CaptureMode:{_captureMode},OutputWidth:{OutputWidth},OutputHeight:{OutputHeight},ActualWidthBeforeResize:{_actualWidthBeforeResize},ActualHeightBeforeResize:{_actualHeightBeforeResize}");
  124. _videoCaptureDevice.NewFrame += OnNewFrame;
  125. _videoCaptureDevice.Start();
  126. _isCapturing = true;
  127. Logger.WriteLineInfo($"AForgeCapturer {_id}: Start Capture!");
  128. return;
  129. }
  130. Logger.WriteLineError($"AForgeCapturer {_id}:Can not find device {_id},Start Capture Error!");
  131. }
  132. private void OnNewFrame(object sender, NewFrameEventArgs eventArgs)
  133. {
  134. try
  135. {
  136. _handlingImageEvent.Reset();
  137. if (Math.Abs(_lastCaptureTime - DateTime.Now.Millisecond) < 30)
  138. {
  139. return;
  140. }
  141. _lastCaptureTime = DateTime.Now.Millisecond;
  142. switch (_captureMode)
  143. {
  144. case EnumCaptureMode.Normal:
  145. using (var bitmap = eventArgs.Frame)
  146. {
  147. var bitmapData = bitmap.LockBits(_imageRectangle, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
  148. unsafe
  149. {
  150. Buffer.MemoryCopy(bitmapData.Scan0.ToPointer(), _imageFrameData.Data.ToPointer(), _imageFrameData.Size, _imageFrameData.Size);
  151. }
  152. bitmap.UnlockBits(bitmapData);
  153. }
  154. ImageFrameReceived?.Invoke(this, _imageFrameData);
  155. break;
  156. case EnumCaptureMode.Specify:
  157. ResizeBitmap(ref _cameraBitmap, _deviceWidth, _deviceHeight);
  158. using (eventArgs.Frame)
  159. {
  160. using (Graphics g = Graphics.FromImage(_cameraBitmap))
  161. {
  162. g.DrawImage(eventArgs.Frame, 0, 0, _deviceWidth, _deviceHeight);
  163. }
  164. var bitmapData = _cameraBitmap.LockBits(_imageRectangle, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
  165. unsafe
  166. {
  167. var captureData = (byte*)bitmapData.Scan0.ToPointer();
  168. var tempData = (byte*)_imageFrameData.Data.ToPointer();
  169. LibYuv.ARGBScale(captureData, _deviceWidth * 4, _deviceWidth, _deviceHeight, tempData, OutputWidth * 4, OutputWidth, OutputHeight, FilterMode.None);
  170. }
  171. _cameraBitmap.UnlockBits(bitmapData);
  172. ImageFrameReceived?.Invoke(this, _imageFrameData);
  173. }
  174. break;
  175. case EnumCaptureMode.Unspecify:
  176. ResizeBitmap(ref _cameraBitmap, _actualWidthBeforeResize, _actualHeightBeforeResize);
  177. using (eventArgs.Frame)
  178. {
  179. using (Graphics g = Graphics.FromImage(_cameraBitmap))
  180. {
  181. g.DrawImage(eventArgs.Frame, 0, 0, _actualWidthBeforeResize, _actualHeightBeforeResize);
  182. }
  183. var bitmapData = _cameraBitmap.LockBits(_imageRectangle, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
  184. unsafe
  185. {
  186. var captureData = (byte*)bitmapData.Scan0.ToPointer();
  187. var tempData = (byte*)_imageFrameData.Data.ToPointer();
  188. LibYuv.ARGBScale(captureData, _actualWidthBeforeResize * 4, _actualWidthBeforeResize, _actualHeightBeforeResize, tempData, OutputWidth * 4, OutputWidth, OutputHeight, FilterMode.None);
  189. }
  190. _cameraBitmap.UnlockBits(bitmapData);
  191. ImageFrameReceived?.Invoke(this, _imageFrameData);
  192. }
  193. break;
  194. }
  195. }
  196. catch (Exception ex)
  197. {
  198. Logger.WriteLineError($"AForgeCapturer {Id} receive data error:{ex}");
  199. }
  200. finally
  201. {
  202. _handlingImageEvent.Set();
  203. }
  204. }
  205. public void StopCapture()
  206. {
  207. try
  208. {
  209. if (_isCapturing)
  210. {
  211. if (_videoCaptureDevice != null)
  212. {
  213. _videoCaptureDevice.NewFrame -= OnNewFrame;
  214. if (_videoCaptureDevice.IsRunning)
  215. {
  216. var stopThread = new Thread(() =>
  217. {
  218. try
  219. {
  220. _videoCaptureDevice.SignalToStop();
  221. _videoCaptureDevice.WaitForStop();
  222. }
  223. catch
  224. {
  225. }
  226. });
  227. stopThread.Start();
  228. try
  229. {
  230. if (!stopThread.Join(2000))
  231. {
  232. stopThread.Abort();
  233. }
  234. }
  235. catch
  236. {
  237. }
  238. }
  239. _videoCaptureDevice = null;
  240. }
  241. _isCapturing = false;
  242. Logger.WriteLineInfo($"AForgeCapturer {_id}: Stop Capture!");
  243. }
  244. }
  245. catch (Exception e)
  246. {
  247. Logger.WriteLineError($"AForgeCapturer {_id}:Stop Capture Error:{e}");
  248. }
  249. }
  250. public void Dispose()
  251. {
  252. StopCapture();
  253. _handlingImageEvent.WaitOne();
  254. _imageFrameData?.Dispose();
  255. _cameraBitmap?.Dispose();
  256. }
  257. private void ResizeBitmap(ref Bitmap bitmap, int width, int height)
  258. {
  259. if (bitmap == null)
  260. {
  261. bitmap = new Bitmap(width, height, PixelFormat.Format32bppRgb);
  262. }
  263. else if (bitmap.Width != width || bitmap.Height != height)
  264. {
  265. bitmap = new Bitmap(width, height, PixelFormat.Format32bppRgb);
  266. }
  267. }
  268. }
  269. }