/Gazetracker/GTHardware/Cameras/DirectShow/DirectShowCamera.cs

http://ogama.codeplex.com · C# · 1184 lines · 600 code · 185 blank · 399 comment · 68 complexity · 5ea5fe96d6e63a637e9597a402e34c45 MD5 · raw file

  1. // <copyright file="Capture.cs" company="ITU">
  2. // ******************************************************
  3. // GazeTrackingLibrary for ITU GazeTracker
  4. // Copyright (C) 2010 Martin Tall
  5. // ------------------------------------------------------------------------
  6. // This program is free software; you can redistribute it and/or modify it
  7. // under the terms of the GNU General Public License as published by the
  8. // Free Software Foundation; either version 3 of the License,
  9. // or (at your option) any later version.
  10. // This program is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. // General Public License for more details.
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program; if not, see http://www.gnu.org/licenses/.
  16. // **************************************************************
  17. // </copyright>
  18. // <author>Martin Tall</author>
  19. // <email>tall@stanford.edu</email>
  20. // <modifiedby>Adrian Voßkühler</modifiedby>
  21. using System;
  22. using System.Diagnostics;
  23. using System.Drawing;
  24. using System.Runtime.InteropServices;
  25. using System.Threading;
  26. using DirectShowLib;
  27. using Emgu.CV;
  28. using Emgu.CV.Structure;
  29. using GTHardware;
  30. //using GazeTrackingLibrary.Logging;
  31. //using GazeTrackingLibrary.Settings;
  32. namespace GTHardware.Cameras.DirectShow
  33. {
  34. using System.Windows;
  35. using Point = System.Drawing.Point;
  36. using Size = System.Drawing.Size;
  37. /// <summary>
  38. /// This is the main class for the DirectShow interop.
  39. /// It creates a graph that pushes video frames from a Video Input Device
  40. /// through the filter chain to a SampleGrabber, from which the
  41. /// frames can be catched and send into the processing tree of
  42. /// the application.
  43. /// </summary>
  44. public class DirectShowCamera : CameraBase, ISampleGrabberCB
  45. {
  46. #region Variables
  47. public static readonly GTHardware.Camera.DeviceTypeEnum deviceType = GTHardware.Camera.DeviceTypeEnum.DirectShow;
  48. private DsDevice device;
  49. private bool isRunning;
  50. //private bool skipFrameMode;
  51. private int deviceNumber;
  52. private int deviceMode;
  53. /// <summary>
  54. /// The IAMVideoControl interface controls certain video capture operations
  55. /// such as enumerating available frame rates and image orientation.
  56. /// </summary>
  57. private IAMVideoControl videoControl;
  58. /// <summary>
  59. /// The IAMCameraControl interface controls camera settings such as zoom,
  60. /// pan, aperture adjustment, or shutter speed. To obtain this interface,
  61. /// query the filter that controls the camera.
  62. /// </summary>
  63. private IAMCameraControl cameraControl;
  64. /// <summary>
  65. /// The IAMVideoProcAmp interface adjusts the qualities of an incoming
  66. /// video signal, such as brightness, contrast, hue, saturation, gamma, and sharpness.
  67. /// </summary>
  68. private IAMVideoProcAmp videoProcAmp;
  69. /// <summary>
  70. /// The IAMStreamConfig interface sets the output format on certain capture
  71. /// and compression filters, for both audio and video. Applications can use
  72. /// this interface to set format properties, such as the output dimensions and
  73. /// frame rate (for video) or the sample rate and number of channels (for audio).
  74. /// </summary>
  75. private IAMStreamConfig videoStreamConfig;
  76. /// <summary>
  77. /// This interface provides methods that enable an application to build a filter graph.
  78. /// The Filter Graph Manager implements this interface.
  79. /// </summary>
  80. private IFilterGraph2 graphBuilder;
  81. /// <summary>
  82. /// The IMediaControl interface provides methods for controlling the
  83. /// flow of data through the filter graph. It includes methods for running, pausing, and stopping the graph.
  84. /// </summary>
  85. private IMediaControl mediaControl;
  86. private ISampleGrabber sampGrabber;
  87. private IBaseFilter capFilter;
  88. private ICaptureGraphBuilder2 capGraph;
  89. private bool isSupportingROI = false;
  90. private bool isRoiSet = false;
  91. private Rectangle roi;
  92. private int width;
  93. private int height;
  94. private int stride;
  95. private int bufferLength;
  96. private int fps;
  97. private bool hasValidGraph;
  98. private IntPtr section = IntPtr.Zero;
  99. private IntPtr map = IntPtr.Zero;
  100. private Image<Bgr, byte> videoImage;
  101. private Stopwatch stopwatch;
  102. #if DEBUG
  103. /// <summary>
  104. /// Helps showing capture graph in GraphBuilder
  105. /// </summary>
  106. private DsROTEntry rotEntry;
  107. #endif
  108. #endregion //FIELDS
  109. #region Constructor
  110. /// <summary>
  111. /// Initializes a new instance of the Capture class.
  112. /// Use capture device zero, default frame rate and size
  113. /// </summary>
  114. public DirectShowCamera()
  115. {
  116. Initialize();
  117. DsDevice[] capDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);
  118. this.deviceNumber = 0;
  119. this.deviceMode = 0;
  120. if (capDevices.Length > 0)
  121. NewCamera(capDevices[deviceNumber], deviceMode, 0, 0); //force default values
  122. }
  123. /// <summary>
  124. /// Initializes a new instance of the Capture class.
  125. /// Use specified capture device, default frame rate and size
  126. /// </summary>
  127. /// <param name="device">The <see cref="DsDevice"/> to use.</param>
  128. public DirectShowCamera(DsDevice device, int deviceNumber)
  129. {
  130. Initialize();
  131. this.deviceNumber = deviceNumber;
  132. this.deviceMode = 0;
  133. NewCamera(device, deviceMode, 0, 0);
  134. }
  135. /// <summary>
  136. /// Initializes a new instance of the Capture class.
  137. /// Use specified capture device, specified frame rate and default size
  138. /// </summary>
  139. /// <param name="device">The <see cref="DsDevice"/> to use.</param>
  140. /// <param name="frameRate">The framerate for the capturing.</param>
  141. public DirectShowCamera(DsDevice device, int deviceNumber, int frameRate)
  142. {
  143. Initialize();
  144. this.deviceNumber = deviceNumber;
  145. this.deviceMode = 0;
  146. NewCamera(device, frameRate, 0, 0);
  147. }
  148. /// <summary>
  149. /// Initializes a new instance of the Capture class.
  150. /// Use specified capture device, specified frame rate and size
  151. /// </summary>
  152. /// <param name="device">The <see cref="DsDevice"/> to use.</param>
  153. /// <param name="frameRate">The framerate for the capturing.</param>
  154. /// <param name="width">The width of the video stream.</param>
  155. /// <param name="height">The height of the video stream.</param>
  156. public DirectShowCamera(DsDevice device, int number, int mode, int frameRate, int width, int height)
  157. {
  158. Initialize();
  159. this.deviceNumber = number;
  160. this.deviceMode = mode;
  161. NewCamera(device, frameRate, width, height);
  162. }
  163. #endregion //CONSTRUCTION
  164. #region Events/Delegate
  165. public delegate void FrameCapHandler();
  166. //public event FrameCapHandler FrameCaptureComplete;
  167. #endregion EVENTS
  168. #region Get/Set
  169. /// <summary>
  170. /// Gets the debug purpose performance clock that is in time with
  171. /// the capture graph.
  172. /// </summary>
  173. public Stopwatch PerformanceClock
  174. {
  175. get { return stopwatch; }
  176. }
  177. public int DeviceNumber
  178. {
  179. get { return deviceNumber; }
  180. }
  181. public int DeviceMode
  182. {
  183. get { return deviceMode; }
  184. }
  185. public override bool IsSupportingROI
  186. {
  187. get { return isSupportingROI; }
  188. set { isSupportingROI = value; }
  189. }
  190. public override bool IsROISet
  191. {
  192. get { return isRoiSet; }
  193. }
  194. public override bool IsSettingROI
  195. {
  196. get { return false; }
  197. }
  198. public Rectangle ROI
  199. {
  200. get { return roi; }
  201. set { roi = value; }
  202. }
  203. public override int Width
  204. {
  205. get { return width; }
  206. }
  207. public override int Height
  208. {
  209. get { return height; }
  210. }
  211. public override int DefaultWidth
  212. {
  213. get { return width; }
  214. }
  215. public override int DefaultHeight
  216. {
  217. get { return height; }
  218. }
  219. public override int FPS
  220. {
  221. get { return fps; }
  222. }
  223. /// <summary>
  224. /// Gets a value indicating whether the graph is
  225. /// succesfully rendered.
  226. /// </summary>
  227. public bool HasValidGraph
  228. {
  229. get { return hasValidGraph; }
  230. }
  231. public string DeviceName
  232. {
  233. get
  234. {
  235. if (device != null)
  236. return device.Name;
  237. else
  238. return "DirectShowDevice";
  239. }
  240. }
  241. #endregion //PROPERTIES
  242. #region Public methods
  243. public override bool Initialize()
  244. {
  245. stopwatch = new Stopwatch();
  246. return true;
  247. }
  248. public override bool Start()
  249. {
  250. if (isRunning || mediaControl == null) return false;
  251. int hr = mediaControl.Run();
  252. if (hr != 0)
  253. {
  254. //ErrorLogger.WriteLine("Error while starting camera. Message: " + DsError.GetErrorText(hr));
  255. isRunning = false;
  256. }
  257. // Set roi to full frame
  258. //this.ROI = new System.Drawing.Rectangle(new Point(0, 0), new Size(videoWidth, videoHeight));
  259. isRunning = true;
  260. return isRunning;
  261. }
  262. public override bool Stop()
  263. {
  264. if (!isRunning) return false;
  265. int hr = mediaControl.Pause();
  266. if (hr != 0)
  267. {
  268. //ErrorLogger.WriteLine("Error while pausing camera. Message: " + DsError.GetErrorText(hr));
  269. }
  270. isRunning = false;
  271. return isRunning;
  272. }
  273. public override Rectangle SetROI(Rectangle newRoi)
  274. {
  275. return new Rectangle(0, 0, width, height);
  276. }
  277. public override Rectangle GetROI()
  278. {
  279. return new Rectangle(0, 0, width, height);
  280. }
  281. public override void ClearROI()
  282. {
  283. }
  284. public override void Cleanup()
  285. {
  286. try
  287. {
  288. // To stop the capture filter before stopping the media control
  289. // seems to solve the problem described in the next comment.
  290. // sancta simplicitas...
  291. if (capFilter != null)
  292. {
  293. capFilter.Stop();
  294. }
  295. // The stop or stopwhenready methods sometimes hang ...
  296. // This is a multithreading issue but I don´t solved it yet
  297. // But stopping is needed, otherwise the video device
  298. // is not disposed fast enough (due to GC) so at next initialization
  299. // with other params the video device seems to be in
  300. // use and the GraphBuilder render mehtod fails.
  301. if (mediaControl != null)
  302. {
  303. // This hangs when closing the GT
  304. int hr = mediaControl.Stop();
  305. }
  306. isRunning = false;
  307. }
  308. catch (Exception)
  309. {
  310. //ErrorLogger.ProcessException(ex, false);
  311. }
  312. if (capFilter != null)
  313. {
  314. Marshal.ReleaseComObject(capFilter);
  315. capFilter = null;
  316. cameraControl = null;
  317. videoControl = null;
  318. videoStreamConfig = null;
  319. }
  320. if (videoProcAmp != null)
  321. {
  322. Marshal.ReleaseComObject(videoProcAmp);
  323. videoProcAmp = null;
  324. }
  325. if (sampGrabber != null)
  326. {
  327. Marshal.ReleaseComObject(sampGrabber);
  328. sampGrabber = null;
  329. }
  330. if (graphBuilder != null)
  331. {
  332. Marshal.ReleaseComObject(graphBuilder);
  333. graphBuilder = null;
  334. mediaControl = null;
  335. hasValidGraph = false;
  336. }
  337. if (capGraph != null)
  338. {
  339. Marshal.ReleaseComObject(capGraph);
  340. capGraph = null;
  341. }
  342. if (map != IntPtr.Zero)
  343. {
  344. UnmapViewOfFile(map);
  345. map = IntPtr.Zero;
  346. }
  347. if (section != IntPtr.Zero)
  348. {
  349. CloseHandle(section);
  350. section = IntPtr.Zero;
  351. }
  352. #if DEBUG
  353. if (this.rotEntry != null)
  354. {
  355. // This hangs when closing the GT
  356. this.rotEntry.Dispose();
  357. }
  358. #endif
  359. }
  360. /// <summary>
  361. /// This method creates a new graph for the given capture device and
  362. /// properties.
  363. /// </summary>
  364. /// <param name="device">The index of the new capture device.</param>
  365. /// <param name="frameRate">The framerate to use.</param>
  366. /// <param name="width">The width to use.</param>
  367. /// <param name="height">The height to use.</param>
  368. public void NewCamera(DsDevice device, int frameRate, int width, int height)
  369. {
  370. this.device = device;
  371. this.Cleanup();
  372. stopwatch.Start();
  373. roi = new Rectangle(new Point(0, 0), new Size(width, height));
  374. try
  375. {
  376. // Set up the capture graph
  377. if (SetupGraph(device, frameRate, width, height))
  378. {
  379. isRunning = false;
  380. SetVideoProcMinMaxRanges();
  381. SetCameraControlMinMaxRanges();
  382. }
  383. else
  384. {
  385. hasValidGraph = false;
  386. return;
  387. }
  388. }
  389. catch
  390. {
  391. Cleanup();
  392. //ErrorLogger.WriteLine("Error in Camera.Capture(), Could not initialize graphs");
  393. hasValidGraph = false;
  394. return;
  395. }
  396. hasValidGraph = true;
  397. }
  398. /// <summary>
  399. /// The <see cref="ISampleGrabberCB.SampleCB{Double,IMediaSample}"/> sample callback method.
  400. /// NOT USED.
  401. /// </summary>
  402. /// <param name="sampleTime">Starting time of the sample, in seconds.</param>
  403. /// <param name="sample">Pointer to the IMediaSample interface of the sample.</param>
  404. /// <returns>Returns S_OK if successful, or an HRESULT error code otherwise.</returns>
  405. public int SampleCB(double sampleTime, IMediaSample sample)
  406. {
  407. Marshal.ReleaseComObject(sample);
  408. return 0;
  409. }
  410. /// <summary>
  411. /// The <see cref="ISampleGrabberCB.BufferCB{Double,IntPtr, Int32}"/> buffer callback method.
  412. /// Gets called whenever a new frame arrives down the stream in the SampleGrabber.
  413. /// Updates the memory mapping of the OpenCV image and raises the
  414. /// <see cref="FrameCaptureComplete"/> event.
  415. /// </summary>
  416. /// <param name="sampleTime">Starting time of the sample, in seconds.</param>
  417. /// <param name="buffer">Pointer to a buffer that contains the sample data.</param>
  418. /// <param name="bufferLength">Length of the buffer pointed to by pBuffer, in bytes.</param>
  419. /// <returns>Returns S_OK if successful, or an HRESULT error code otherwise.</returns>
  420. public int BufferCB(double sampleTime, IntPtr buffer, int bufferLength)
  421. {
  422. if (buffer != IntPtr.Zero)
  423. {
  424. // Check mapping if it is not already released and the buffer is running
  425. if (map != IntPtr.Zero)
  426. {
  427. // This is fast and lasts less than 1 millisecond.
  428. CopyMemory(map, buffer, bufferLength);
  429. // reset roi on image and use roi (rectagle) upon get
  430. videoImage.ROI = new Rectangle();
  431. try
  432. {
  433. // Send new image to processing thread
  434. OnRaiseCustomEvent(new ImageEventArgs(videoImage.Convert<Gray, byte>()));
  435. }
  436. catch (ThreadInterruptedException)
  437. {
  438. //ErrorLogger.ProcessException(e, false);
  439. }
  440. catch (Exception)
  441. {
  442. //ErrorLogger.ProcessException(we, false);
  443. }
  444. }
  445. }
  446. return 0;
  447. }
  448. // Remove ?
  449. private Image<Gray, byte> VideoImage
  450. {
  451. get
  452. {
  453. if (roi.Width != 0)
  454. {
  455. //videoImage.ROI = roi;
  456. Image<Gray, byte> gray = videoImage.Copy().Convert<Gray, byte>();
  457. return gray;
  458. }
  459. else
  460. return videoImage.Convert<Gray, byte>();
  461. }
  462. }
  463. #endregion //PUBLICMETHODS
  464. #region Eventhandlers OnPropChange etc.
  465. /// <summary>
  466. /// The event handler for the <see cref="OnCameraControlPropertyChanged"/> event.
  467. /// Updates the video capture device with new brightness, contrast, etc.
  468. /// </summary>
  469. /// <param name="property">The <see cref="VideoProcAmpProperty"/> to be changed</param>
  470. /// <param name="value">The new value for the property</param>
  471. public void OnVideoProcAmpPropertyChanged(VideoProcAmpProperty property, int value)
  472. {
  473. if (videoProcAmp == null)
  474. return;
  475. int min, max, steppingDelta, defaultValue;
  476. VideoProcAmpFlags flags;
  477. try
  478. {
  479. videoProcAmp.GetRange(property, out min, out max, out steppingDelta, out defaultValue, out flags);
  480. if (value >= min && value <= max)
  481. videoProcAmp.Set(property, value, flags);
  482. }
  483. catch (Exception)
  484. {
  485. //ErrorLogger.ProcessException(ex, false);
  486. }
  487. }
  488. /// <summary>
  489. /// The event handler for the <see cref="OnVideoProcAmpPropertyChanged"/> event.
  490. /// Updates the video capture device with new zoom, pan, etc.
  491. /// </summary>
  492. /// <param name="property">The <see cref="CameraControlProperty"/> to be changed</param>
  493. /// <param name="value">The new value for the property</param>
  494. public void OnCameraControlPropertyChanged(CameraControlProperty property, int value)
  495. {
  496. if (cameraControl == null)
  497. return;
  498. // Todo: Disabled focus as it turns on autofocus
  499. if (property.Equals(CameraControlProperty.Focus))
  500. {
  501. return;
  502. }
  503. int min, max, steppingDelta, defaultValue;
  504. CameraControlFlags flags;
  505. try
  506. {
  507. cameraControl.GetRange(property, out min, out max, out steppingDelta, out defaultValue, out flags);
  508. if (value >= min && value <= max)
  509. cameraControl.Set(property, value, flags);
  510. }
  511. catch (Exception)
  512. {
  513. //ErrorLogger.ProcessException(ex, false);
  514. }
  515. }
  516. /// <summary>
  517. /// The event handler for the <see cref="OnVideoControlFlagsChanged"/> event.
  518. /// Updates the video capture device with new video control properties.
  519. /// </summary>
  520. /// <remarks> This method has been disabled, because it was easier to flip the incoming image
  521. /// with the CV image flip in ImageProcessing.cs.
  522. /// The direct show flipping didn't work with some webcams, e.g. the PlayStationEye3 cam or an HP Laptop Webcam</remarks>
  523. /// <param name="property">The <see cref="VideoControlFlags"/> to be changed</param>
  524. /// <param name="value">The new value for the property</param>
  525. private void OnVideoControlFlagsChanged(VideoControlFlags property, int value)
  526. {
  527. ////if (this.graphBuilder == null)
  528. ////{
  529. //// return;
  530. ////}
  531. ////if (videoControl == null)
  532. //// return;
  533. ////VideoControlFlags pCapsFlags;
  534. ////IPin pPin = DsFindPin.ByCategory(capFilter, PinCategory.Capture, 0);
  535. ////int hr = videoControl.GetCaps(pPin, out pCapsFlags);
  536. ////if (hr != 0)
  537. //// ErrorLogger.WriteLine("Error: videoControl.GetCaps in Camera.Capture. Message: " + DsError.GetErrorText(hr));
  538. ////hr = videoControl.GetMode(pPin, out pCapsFlags);
  539. ////if (hr != 0)
  540. //// ErrorLogger.WriteLine("Error while getting mode in videoControl.GetMode in Camera.Capture. Message: " + DsError.GetErrorText(hr));
  541. ////if (value == 0)
  542. ////{
  543. //// if ((pCapsFlags&VideoControlFlags.FlipVertical)==VideoControlFlags.FlipVertical)
  544. //// {
  545. //// pCapsFlags |= ~VideoControlFlags.FlipVertical;
  546. //// }
  547. ////}
  548. ////else
  549. ////{
  550. //// pCapsFlags |= VideoControlFlags.FlipVertical;
  551. ////}
  552. ////hr = videoControl.SetMode(pPin, pCapsFlags);
  553. ////if (hr != 0)
  554. //// ErrorLogger.WriteLine("Error while getting mode in videoControl.SetMode in Camera.Capture. Message: " + DsError.GetErrorText(hr));
  555. }
  556. #endregion //EVENTHANDLER
  557. #region Private methods
  558. /// <summary>
  559. /// Copies a block of memory from one location to another.
  560. /// </summary>
  561. /// <param name="destination">A pointer to the starting address of the copied block's destination.</param>
  562. /// <param name="source">A pointer to the starting address of the block of memory to copy</param>
  563. /// <param name="length">The size of the block of memory to copy, in bytes.</param>
  564. [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory")]
  565. private static extern void CopyMemory(IntPtr destination, IntPtr source, int length);
  566. /// <summary>
  567. /// Creates or opens a named or unnamed file mapping object for a specified file.
  568. /// </summary>
  569. /// <param name="file">A handle to the file from which to create a file mapping object.</param>
  570. /// <param name="fileMappingAttributes">A pointer to a SECURITY_ATTRIBUTES structure that determines whether a returned handle can be inherited by child processes.</param>
  571. /// <param name="protect">The protection for the file view, when the file is mapped.</param>
  572. /// <param name="maximumSizeHigh">The high-order DWORD of the maximum size of the file mapping object.</param>
  573. /// <param name="maximumSizeLow">The low-order DWORD of the maximum size of the file mapping object.</param>
  574. /// <param name="name">The name of the file mapping object.</param>
  575. /// <returns>If the function succeeds, the return value is a handle to the file mapping object.
  576. /// If the object exists before the function call, the function returns a handle to the existing object
  577. /// (with its Instance size, not the specified size), and GetLastError returns ERROR_ALREADY_EXISTS.
  578. /// If the function fails, the return value is NULL. To get extended error information, call GetLastError.</returns>
  579. [DllImport("kernel32.dll", SetLastError = true)]
  580. private static extern IntPtr CreateFileMapping(IntPtr file, IntPtr fileMappingAttributes, uint protect,
  581. uint maximumSizeHigh, uint maximumSizeLow, string name);
  582. /// <summary>
  583. /// Maps a view of a file mapping into the address space of a calling process.
  584. /// </summary>
  585. /// <param name="fileMappingObject">A handle to a file mapping object. The CreateFileMapping and OpenFileMapping functions return this handle.</param>
  586. /// <param name="desiredAccess">The type of access to a file mapping object, which ensures the protection of the pages.</param>
  587. /// <param name="fileOffsetHigh">A high-order DWORD of the file offset where the view begins.</param>
  588. /// <param name="fileOffsetLow">A low-order DWORD of the file offset where the view is to begin.
  589. /// The combination of the high and low offsets must specify an offset within the file mapping.
  590. /// They must also match the memory allocation granularity of the system. That is,
  591. /// the offset must be a multiple of the allocation granularity. </param>
  592. /// <param name="numberOfBytesToMap">The number of bytes of a file mapping to map to the view.</param>
  593. /// <returns>If the function succeeds, the return value is the starting address of the mapped view.
  594. /// If the function fails, the return value is NULL.</returns>
  595. [DllImport("kernel32.dll", SetLastError = true)]
  596. private static extern IntPtr MapViewOfFile(IntPtr fileMappingObject, uint desiredAccess, uint fileOffsetHigh,
  597. uint fileOffsetLow, uint numberOfBytesToMap);
  598. /// <summary>
  599. /// Unmaps a mapped view of a file from the calling process's address space.
  600. /// </summary>
  601. /// <param name="map">A pointer to the base address of the mapped view of a file that is to be unmapped. </param>
  602. /// <returns>If the function succeeds, the return value is nonzero, and all dirty pages within the specified range are written "lazily" to disk.
  603. /// If the function fails, the return value is zero. </returns>
  604. [DllImport("kernel32.dll", SetLastError = true)]
  605. private static extern bool UnmapViewOfFile(IntPtr map);
  606. /// <summary>
  607. /// Closes an open object handle.
  608. /// </summary>
  609. /// <param name="handle">A valid handle to an open object.</param>
  610. /// <returns>If the function succeeds, the return value is nonzero.
  611. /// If the function fails, the return value is zero.</returns>
  612. [DllImport("kernel32.dll", SetLastError = true)]
  613. private static extern bool CloseHandle(IntPtr handle);
  614. /// <summary>
  615. /// Connects to the property changed events of the camera settings.
  616. /// </summary>
  617. //private void Initialize()
  618. //{
  619. // //Settings.Instance.Camera.OnCameraControlPropertyChanged += OnCameraControlPropertyChanged;
  620. // //Settings.Instance.Camera.OnVideoProcAmpPropertyChanged += OnVideoProcAmpPropertyChanged;
  621. // //Settings.Instance.Camera.OnVideoControlFlagsChanged += OnVideoControlFlagsChanged;
  622. // //stopwatch = new Stopwatch();
  623. //}
  624. /// <summary>
  625. /// Build the capture graph for grabber.
  626. /// </summary>
  627. /// <param name="dev">The index of the new capture device.</param>
  628. /// <param name="frameRate">The framerate to use.</param>
  629. /// <param name="width">The width to use.</param>
  630. /// <param name="height">The height to use.</param>
  631. /// <returns>True, if succesfull, otherwise false.</returns>
  632. private bool SetupGraph(DsDevice dev, int frameRate, int width, int height)
  633. {
  634. int hr;
  635. fps = frameRate; // Not measured, only to expose FPS externally
  636. cameraControl = null;
  637. capFilter = null;
  638. // Get the graphbuilder object
  639. graphBuilder = (IFilterGraph2)new FilterGraph();
  640. mediaControl = graphBuilder as IMediaControl;
  641. try
  642. {
  643. // Create the ICaptureGraphBuilder2
  644. capGraph = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
  645. // Create the SampleGrabber interface
  646. sampGrabber = (ISampleGrabber)new SampleGrabber();
  647. // Start building the graph
  648. hr = capGraph.SetFiltergraph(graphBuilder);
  649. //if (hr != 0)
  650. // ErrorLogger.WriteLine("Error in capGraph.SetFiltergraph. Could not build graph. Message: " +
  651. // DsError.GetErrorText(hr));
  652. #if DEBUG
  653. this.rotEntry = new DsROTEntry(this.graphBuilder);
  654. #endif
  655. this.capFilter = CreateFilter(
  656. FilterCategory.VideoInputDevice,
  657. dev.Name);
  658. if (this.capFilter != null)
  659. {
  660. hr = graphBuilder.AddFilter(this.capFilter, "Video Source");
  661. DsError.ThrowExceptionForHR(hr);
  662. }
  663. //// Add the video device
  664. //hr = graphBuilder.AddSourceFilterForMoniker(dev.Mon, null, "Video input", out capFilter);
  665. //if (hr != 0)
  666. // ErrorLogger.WriteLine(
  667. // "Error in m_graphBuilder.AddSourceFilterForMoniker(). Could not add source filter. Message: " +
  668. // DsError.GetErrorText(hr));
  669. var baseGrabFlt = (IBaseFilter)sampGrabber;
  670. ConfigureSampleGrabber(sampGrabber);
  671. // Add the frame grabber to the graph
  672. hr = graphBuilder.AddFilter(baseGrabFlt, "Ds.NET Grabber");
  673. //if (hr != 0)
  674. // ErrorLogger.WriteLine("Error in m_graphBuilder.AddFilter(). Could not add filter. Message: " +
  675. // DsError.GetErrorText(hr));
  676. // turn on the infrared leds ONLY FOR THE GENIUS WEBCAM
  677. /*
  678. if (!defaultMode)
  679. {
  680. m_icc = capFilter as IAMCameraControl;
  681. CameraControlFlags CamFlags = new CameraControlFlags();
  682. int pMin, pMax, pStep, pDefault;
  683. hr = m_icc.GetRange(CameraControlProperty.Focus, out pMin, out pMax, out pStep, out pDefault, out CamFlags);
  684. m_icc.Set(CameraControlProperty.Focus, pMax, CameraControlFlags.None);
  685. }
  686. */
  687. //IBaseFilter smartTee = new SmartTee() as IBaseFilter;
  688. //// Add the smart tee filter to the graph
  689. //hr = this.graphBuilder.AddFilter(smartTee, "Smart Tee");
  690. //Marshal.ThrowExceptionForHR(hr);
  691. // Connect the video source output to the smart tee
  692. //hr = capGraph.RenderStream(null, null, capFilter, null, smartTee);
  693. hr = capGraph.RenderStream(PinCategory.Capture, MediaType.Video, capFilter, null, baseGrabFlt);
  694. var errorText = DsError.GetErrorText(hr);
  695. cameraControl = capFilter as IAMCameraControl;
  696. // Set videoProcAmp
  697. object obj;
  698. var iid_IBaseFilter = new Guid("56a86895-0ad4-11ce-b03a-0020af0ba770");
  699. DirectShowDevices.Instance.Cameras[deviceNumber].DirectshowDevice.Mon.BindToObject(
  700. null,
  701. null,
  702. ref iid_IBaseFilter,
  703. out obj);
  704. videoProcAmp = obj as IAMVideoProcAmp;
  705. // If any of the default config items are set
  706. if (frameRate + height + width > 0)
  707. SetConfigParms(capGraph, capFilter, frameRate, width, height);
  708. // Check for succesful rendering, if this failed the class cannot be used, so dispose the resources and return false.
  709. if (hr < 0)
  710. {
  711. Cleanup();
  712. return false;
  713. }
  714. else
  715. {
  716. // Otherwise update the SampleGrabber.
  717. SaveSizeInfo(sampGrabber);
  718. hr = sampGrabber.SetBufferSamples(false);
  719. if (hr == 0)
  720. {
  721. hr = sampGrabber.SetOneShot(false);
  722. hr = sampGrabber.SetCallback(this, 1);
  723. }
  724. //if (hr < 0)
  725. // ErrorLogger.WriteLine("Could not set callback function (SetupGraph) in Camera.Capture()");
  726. }
  727. }
  728. catch (Exception)
  729. {
  730. //ErrorLogger.ProcessException(ex, false);
  731. Cleanup();
  732. return false;
  733. }
  734. return true;
  735. }
  736. /// <summary>
  737. /// Enumerates all filters of the selected category and returns the IBaseFilter for the
  738. /// filter described in friendlyname
  739. /// </summary>
  740. /// <param name="category">Category of the filter</param>
  741. /// <param name="friendlyname">Friendly name of the filter</param>
  742. /// <returns>IBaseFilter for the device</returns>
  743. public static IBaseFilter CreateFilter(Guid category, string friendlyname)
  744. {
  745. object source = null;
  746. try
  747. {
  748. Guid iid = typeof(IBaseFilter).GUID;
  749. foreach (DsDevice device in DsDevice.GetDevicesOfCat(category))
  750. {
  751. if (device.Name.CompareTo(friendlyname) == 0)
  752. {
  753. device.Mon.BindToObject(null, null, ref iid, out source);
  754. break;
  755. }
  756. }
  757. }
  758. catch (Exception ex)
  759. {
  760. MessageBox.Show(ex.Message);
  761. }
  762. return (IBaseFilter)source;
  763. }
  764. /// <summary>
  765. /// Configure the sample grabber with default Video RGB24 mode.
  766. /// </summary>
  767. /// <param name="sampGrabber">The <see cref="ISampleGrabber"/> to be configured.</param>
  768. private void ConfigureSampleGrabber(ISampleGrabber sampGrabber)
  769. {
  770. AMMediaType media;
  771. int hr;
  772. // Set the media type to Video/RBG24
  773. media = new AMMediaType();
  774. media.majorType = MediaType.Video;
  775. media.subType = MediaSubType.RGB24;
  776. media.formatType = FormatType.VideoInfo;
  777. hr = this.sampGrabber.SetMediaType(media);
  778. //if (hr != 0)
  779. // ErrorLogger.WriteLine("Could not ConfigureSampleGrabber in Camera.Capture. Message: " +
  780. // DsError.GetErrorText(hr));
  781. DsUtils.FreeAMMediaType(media);
  782. media = null;
  783. // Configure the samplegrabber
  784. hr = this.sampGrabber.SetCallback(this, 1);
  785. //if (hr != 0)
  786. // ErrorLogger.WriteLine("Could not set callback method for sampleGrabber in Camera.Capture. Message: " +
  787. // DsError.GetErrorText(hr));
  788. }
  789. /// <summary>
  790. /// Set the Framerate, and video size
  791. /// </summary>
  792. /// <param name="capGraph">The <see cref="ICaptureGraphBuilder2"/> interface.</param>
  793. /// <param name="capFilter">The <see cref="IBaseFilter"/> of the capture device.</param>
  794. /// <param name="frameRate">The new framerate to be used.</param>
  795. /// <param name="width">The new video width to be used.</param>
  796. /// <param name="height">The new video height to be used.</param>
  797. private void SetConfigParms(ICaptureGraphBuilder2 capGraph, IBaseFilter capFilter, int frameRate, int width,
  798. int height)
  799. {
  800. int hr;
  801. object o;
  802. AMMediaType media = null;
  803. // Find the stream config interface
  804. hr = this.capGraph.FindInterface(PinCategory.Capture, MediaType.Video, capFilter,
  805. typeof(IAMStreamConfig).GUID, out o);
  806. videoControl = capFilter as IAMVideoControl;
  807. videoStreamConfig = o as IAMStreamConfig;
  808. //if (videoStreamConfig == null)
  809. // ErrorLogger.WriteLine("Error in Capture.SetConfigParams(). Failed to get IAMStreamConfig");
  810. // Get the existing format block
  811. if (videoStreamConfig != null) hr = videoStreamConfig.GetFormat(out media);
  812. //if (hr != 0)
  813. // ErrorLogger.WriteLine("Could not SetConfigParms in Camera.Capture. Message: " + DsError.GetErrorText(hr));
  814. // copy out the videoinfoheader
  815. var v = new VideoInfoHeader();
  816. Marshal.PtrToStructure(media.formatPtr, v);
  817. // if overriding set values
  818. if (frameRate > 0)
  819. {
  820. v.AvgTimePerFrame = 10000000 / frameRate;
  821. this.fps = frameRate;
  822. }
  823. else
  824. {
  825. this.fps = (int)(10000000 / v.AvgTimePerFrame);
  826. }
  827. if (width > 0)
  828. v.BmiHeader.Width = width;
  829. if (height > 0)
  830. v.BmiHeader.Height = height;
  831. // Copy the media structure back
  832. Marshal.StructureToPtr(v, media.formatPtr, true);
  833. // Set the new format
  834. if (videoStreamConfig != null) hr = videoStreamConfig.SetFormat(media);
  835. //if (hr != 0)
  836. // ErrorLogger.WriteLine(
  837. // "Error while setting new camera format (videoStreamConfig) in Camera.Capture. Message: " +
  838. // DsError.GetErrorText(hr));
  839. DsUtils.FreeAMMediaType(media);
  840. media = null;
  841. }
  842. /// <summary>
  843. /// Saves the video properties of the SampleGrabber into member fields
  844. /// and creates a file mapping for the captured frames.
  845. /// </summary>
  846. /// <param name="sampGrabber">The <see cref="ISampleGrabber"/>
  847. /// from which to retreive the sample information.</param>
  848. private void SaveSizeInfo(ISampleGrabber sampGrabber)
  849. {
  850. int hr;
  851. // Get the media type from the SampleGrabber
  852. var media = new AMMediaType();
  853. hr = sampGrabber.GetConnectedMediaType(media);
  854. //if (hr != 0)
  855. //{
  856. // ErrorLogger.WriteLine("Could not SaveSizeInfo in Camera.Capture. Message: " + DsError.GetErrorText(hr));
  857. //}
  858. //if ((media.formatType != FormatType.VideoInfo) || (media.formatPtr == IntPtr.Zero))
  859. //{
  860. // ErrorLogger.WriteLine("Error in Camera.Capture. Unknown Grabber Media Format");
  861. //}
  862. // Grab the size info
  863. var videoInfoHeader = (VideoInfoHeader)Marshal.PtrToStructure(media.formatPtr, typeof(VideoInfoHeader));
  864. width = videoInfoHeader.BmiHeader.Width;
  865. height = videoInfoHeader.BmiHeader.Height;
  866. stride = width * (videoInfoHeader.BmiHeader.BitCount / 8);
  867. this.fps = (int)(10000000 / videoInfoHeader.AvgTimePerFrame);
  868. bufferLength = width * height * 3; // RGB24 = 3 bytes
  869. // create memory section and map for the OpenCV Image.
  870. section = CreateFileMapping(new IntPtr(-1), IntPtr.Zero, 0x04, 0, (uint)bufferLength, null);
  871. map = MapViewOfFile(section, 0xF001F, 0, 0, (uint)bufferLength);
  872. videoImage = new Image<Bgr, byte>(width, height, stride, map);
  873. DsUtils.FreeAMMediaType(media);
  874. media = null;
  875. }
  876. /// <summary>
  877. /// This method is used to set minimum and maximum values for the
  878. /// <see cref="VideoProcAmpProperty"/>s of the capture device.
  879. /// </summary>
  880. private void SetVideoProcMinMaxRanges()
  881. {
  882. if (videoProcAmp == null)
  883. {
  884. return;
  885. }
  886. int min, max, steppingDelta, defaultValue;
  887. VideoProcAmpFlags flags;
  888. foreach (VideoProcAmpProperty item in Enum.GetValues(typeof(VideoProcAmpProperty)))
  889. {
  890. switch (item)
  891. {
  892. case VideoProcAmpProperty.Brightness:
  893. videoProcAmp.GetRange(VideoProcAmpProperty.Brightness, out min, out max, out steppingDelta,
  894. out defaultValue, out flags);
  895. //Settings.Instance.Camera.BrightnessMinimum = min;
  896. //Settings.Instance.Camera.BrightnessMaximum = max;
  897. //Settings.Instance.Camera.BrightnessStepping = steppingDelta;
  898. //Settings.Instance.Camera.BrightnessDefault = defaultValue;
  899. break;
  900. case VideoProcAmpProperty.Saturation:
  901. videoProcAmp.GetRange(VideoProcAmpProperty.Saturation, out min, out max, out steppingDelta,
  902. out defaultValue, out flags);
  903. //Settings.Instance.Camera.SaturationMinimum = min;
  904. //Settings.Instance.Camera.SaturationMaximum = max;
  905. //Settings.Instance.Camera.SaturationStepping = steppingDelta;
  906. //Settings.Instance.Camera.SaturationDefault = defaultValue;
  907. break;
  908. case VideoProcAmpProperty.Sharpness:
  909. videoProcAmp.GetRange(VideoProcAmpProperty.Sharpness, out min, out max, out steppingDelta,
  910. out defaultValue, out flags);
  911. //Settings.Instance.Camera.SharpnessMinimum = min;
  912. //Settings.Instance.Camera.SharpnessMaximum = max;
  913. //Settings.Instance.Camera.SharpnessStepping = steppingDelta;
  914. //Settings.Instance.Camera.SharpnessDefault = defaultValue;
  915. break;
  916. case VideoProcAmpProperty.Contrast:
  917. videoProcAmp.GetRange(VideoProcAmpProperty.Contrast, out min, out max, out steppingDelta,
  918. out defaultValue, out flags);
  919. //Settings.Instance.Camera.ContrastMinimum = min;
  920. //Settings.Instance.Camera.ContrastMaximum = max;
  921. //Settings.Instance.Camera.ContrastStepping = steppingDelta;
  922. //Settings.Instance.Camera.ContrastDefault = defaultValue;
  923. break;
  924. case VideoProcAmpProperty.ColorEnable:
  925. // videoProcAmp.GetRange(VideoProcAmpProperty.ColorEnable, out min, out max, out steppingDelta, out defaultValue, out flags);
  926. break;
  927. case VideoProcAmpProperty.Gain:
  928. // videoProcAmp.GetRange(VideoProcAmpProperty.Gain, out min, out max, out steppingDelta, out defaultValue, out flags);
  929. break;
  930. case VideoProcAmpProperty.Gamma:
  931. // videoProcAmp.GetRange(VideoProcAmpProperty.Gamma, out min, out max, out steppingDelta, out defaultValue, out flags);
  932. break;
  933. case VideoProcAmpProperty.Hue:
  934. // videoProcAmp.GetRange(VideoProcAmpProperty.Hue, out min, out max, out steppingDelta, out defaultValue, out flags);
  935. break;
  936. case VideoProcAmpProperty.WhiteBalance:
  937. // videoProcAmp.GetRange(VideoProcAmpProperty.WhiteBalance, out min, out max, out steppingDelta, out defaultValue, out flags);
  938. break;
  939. case VideoProcAmpProperty.BacklightCompensation:
  940. // videoProcAmp.GetRange(VideoProcAmpProperty.BacklightCompensation, out min, out max, out steppingDelta, out defaultValue, out flags);
  941. break;
  942. }
  943. }
  944. }
  945. /// <summary>
  946. /// This method is used to set minimum and maximum values for the
  947. /// <see cref="CameraControlProperty"/>s of the capture device.
  948. /// </summary>
  949. private void SetCameraControlMinMaxRanges()
  950. {
  951. if (cameraControl == null)
  952. {
  953. return;
  954. }
  955. int min, max, steppingDelta, defaultValue;
  956. CameraControlFlags flags;
  957. foreach (CameraControlProperty item in Enum.GetValues(typeof(CameraControlProperty)))
  958. {
  959. switch (item)
  960. {
  961. case CameraControlProperty.Exposure:
  962. cameraControl.GetRange(CameraControlProperty.Exposure, out min, out max, out steppingDelta,
  963. out defaultValue, out flags);
  964. //Settings.Instance.Camera.ExposureMinimum = min;
  965. //Settings.Instance.Camera.ExposureMaximum = max;
  966. //Settings.Instance.Camera.ExposureStepping = steppingDelta;
  967. //Settings.Instance.Camera.ExposureDefault = defaultValue;
  968. break;
  969. case CameraControlProperty.Focus:
  970. cameraControl.GetRange(CameraControlProperty.Focus, out min, out max, out steppingDelta,
  971. out defaultValue, out flags);
  972. //Settings.Instance.Camera.FocusMinimum = min;
  973. //Settings.Instance.Camera.FocusMaximum = max;
  974. //Settings.Instance.Camera.FocusStepping = steppingDelta;
  975. //Settings.Instance.Camera.FocusDefault = defaultValue;
  976. break;
  977. case CameraControlProperty.Zoom:
  978. cameraControl.GetRange(CameraControlProperty.Focus, out min, out max, out steppingDelta,
  979. out defaultValue, out flags);
  980. //Settings.Instance.Camera.ZoomMinimum = min;
  981. //Settings.Instance.Camera.ZoomMaximum = max;
  982. //Settings.Instance.Camera.ZoomStepping = steppingDelta;
  983. //Settings.Instance.Camera.ZoomDefault = defaultValue;
  984. break;
  985. case CameraControlProperty.Iris:
  986. break;
  987. case CameraControlProperty.Pan:
  988. break;
  989. case CameraControlProperty.Roll:
  990. break;
  991. case CameraControlProperty.Tilt:
  992. break;
  993. }
  994. }
  995. }
  996. #endregion //PRIVATEMETHODS
  997. }
  998. }