Capture.cs
Upload User: wuming6209
Upload Date: 2013-06-06
Package Size: 161k
Code Size: 69k
Category:

Video Capture

Development Platform:

Visual C++

  1. // ------------------------------------------------------------------
  2. // DirectX.Capture
  3. //
  4. // History:
  5. // 2003-Jan-24 BL - created
  6. //  2003-Aug-06 DB - modified
  7. //
  8. // Copyright (c) 2003 Brian Low
  9. // ------------------------------------------------------------------
  10. using System;
  11. using System.Diagnostics;
  12. using System.Drawing;
  13. using System.Drawing.Imaging;
  14. using System.Collections;
  15. using System.IO;
  16. using System.Reflection;
  17. using System.Runtime.InteropServices; 
  18. using System.Threading;
  19. using System.Windows.Forms;
  20. using System.Data;
  21. using System.ComponentModel;
  22. using DShowNET;
  23. using DShowNET.Device;
  24. namespace DirectX.Capture
  25. {
  26. /// <summary>
  27. ///  Use the Capture class to capture audio and video to AVI files.
  28. /// </summary>
  29. /// <remarks>
  30. ///  This is the core class of the Capture Class Library. The following 
  31. ///  sections introduce the Capture class and how to use this library.
  32. ///  
  33. /// <br/><br/>
  34. /// <para><b>Basic Usage</b></para>
  35. /// 
  36. /// <para>
  37. ///  The Capture class only requires a video device and/or audio device
  38. ///  to begin capturing. The <see cref="Filters"/> class provides
  39. ///  lists of the installed video and audio devices. 
  40. /// </para>
  41. /// <code><div style="background-color:whitesmoke;">
  42. ///  // Remember to add a reference to DirectX.Capture.dll
  43. ///  using DirectX.Capture
  44. ///  ...
  45. ///  Capture capture = new Capture( Filters.VideoInputDevices[0], 
  46. ///                                 Filters.AudioInputDevices[0] );
  47. ///  capture.Start();
  48. ///  ...
  49. ///  capture.Stop();
  50. /// </div></code>
  51. /// <para>
  52. ///  This will capture video and audio using the first video and audio devices
  53. ///  installed on the system. To capture video only, pass a null as the second
  54. ///  parameter of the constructor.
  55. /// </para>
  56. /// <para> 
  57. ///  The class is initialized to a valid temporary file in the Windows temp
  58. ///  folder. To capture to a different file, set the 
  59. ///  <see cref="Capture.Filename"/> property before you begin
  60. ///  capturing. Remember to add DirectX.Capture.dll to 
  61. ///  your project references.
  62. /// </para>
  63. ///
  64. /// <br/>
  65. /// <para><b>Setting Common Properties</b></para>
  66. /// 
  67. /// <para>
  68. ///  The example below shows how to change video and audio settings. 
  69. ///  Properties such as <see cref="Capture.FrameRate"/> and 
  70. ///  <see cref="AudioSampleSize"/> allow you to programmatically adjust
  71. ///  the capture. Use <see cref="Capture.VideoCaps"/> and 
  72. ///  <see cref="Capture.AudioCaps"/> to determine valid values for these
  73. ///  properties.
  74. /// </para>
  75. /// <code><div style="background-color:whitesmoke;">
  76. ///  Capture capture = new Capture( Filters.VideoInputDevices[0], 
  77. ///                                 Filters.AudioInputDevices[1] );
  78. ///  capture.VideoCompressor = Filters.VideoCompressors[0];
  79. ///  capture.AudioCompressor = Filters.AudioCompressors[0];
  80. ///  capture.FrameRate = 29.997;
  81. ///  capture.FrameSize = new Size( 640, 480 );
  82. ///  capture.AudioSamplingRate = 44100;
  83. ///  capture.AudioSampleSize = 16;
  84. ///  capture.Filename = "C:MyVideo.avi";
  85. ///  capture.Start();
  86. ///  ...
  87. ///  capture.Stop();
  88. /// </div></code>
  89. /// <para>
  90. ///  The example above also shows the use of video and audio compressors. In most 
  91. ///  cases you will want to use compressors. Uncompressed video can easily
  92. ///  consume over a 1GB of disk space per minute. Whenever possible, set 
  93. ///  the <see cref="Capture.VideoCompressor"/> and <see cref="Capture.AudioCompressor"/>
  94. ///  properties as early as possible. Changing them requires the internal filter
  95. ///  graph to be rebuilt which often causes most of the other properties to
  96. ///  be reset to default values.
  97. /// </para>
  98. ///
  99. /// <br/>
  100. /// <para><b>Listing Devices</b></para>
  101. /// 
  102. /// <para>
  103. ///  Use the <see cref="Filters.VideoInputDevices"/> collection to list
  104. ///  video capture devices installed on the system.
  105. /// </para>
  106. /// <code><div style="background-color:whitesmoke;">
  107. ///  foreach ( Filter f in Filters.VideoInputDevices )
  108. ///  {
  109. /// Debug.WriteLine( f.Name );
  110. ///  }
  111. /// </div></code>
  112. /// The <see cref="Filters"/> class also provides collections for audio 
  113. /// capture devices, video compressors and audio compressors.
  114. ///
  115. /// <br/>
  116. /// <para><b>Preview</b></para>
  117. /// 
  118. /// <para>
  119. ///  Video preview is controled with the <see cref="Capture.PreviewWindow"/>
  120. ///  property. Setting this property to a visible control will immediately 
  121. ///  begin preview. Set to null to stop the preview. 
  122. /// </para>
  123. /// <code><div style="background-color:whitesmoke;">
  124. ///  // Enable preview
  125. ///  capture.PreviewWindow = myPanel;
  126. ///  // Disable preview
  127. ///  capture.PreviewWindow = null;
  128. /// </div></code>
  129. /// <para>
  130. ///  The control used must have a window handle (HWND), good controls to 
  131. ///  use are the Panel or the form itself.
  132. /// </para>
  133. /// <para>
  134. ///  Retrieving or changing video/audio settings such as FrameRate, 
  135. ///  FrameSize, AudioSamplingRate, and AudioSampleSize will cause
  136. ///  the preview window to flash. This is beacuse the preview must be
  137. ///  temporarily stopped. Disable the preview if you need to access
  138. ///  several properties at the same time.
  139. /// </para>
  140. /// 
  141. /// <br/>
  142. /// <para><b>Property Pages</b></para>
  143. ///  
  144. /// <para>
  145. ///  Property pages exposed by the devices and compressors are
  146. ///  available through the <see cref="Capture.PropertyPages"/> 
  147. ///  collection.
  148. /// </para>
  149. /// <code><div style="background-color:whitesmoke;">
  150. ///  // Display the first property page
  151. ///  capture.PropertyPages[0].Show();
  152. /// </div></code>
  153. /// <para>
  154. ///  The property pages will often expose more settings than
  155. ///  the Capture class does directly. Some examples are brightness, 
  156. ///  color space, audio balance and bass boost. The disadvantage
  157. ///  to using the property pages is the user's choices cannot be
  158. ///  saved and later restored. The exception to this is the video
  159. ///  and audio compressor property pages. Most compressors support
  160. ///  the saving and restoring state, see the 
  161. ///  <see cref="PropertyPage.State"/> property for more information.
  162. /// </para>
  163. /// <para>
  164. ///  Changes made in the property page will be reflected 
  165. ///  immediately in the Capture class properties (e.g. Capture.FrameSize).
  166. ///  However, the reverse is not always true. A change made directly to
  167. ///  FrameSize, for example, may not be reflected in the associated
  168. ///  property page. Fortunately, the filter will use requested FrameSize
  169. ///  even though the property page shows otherwise.
  170. /// </para>
  171. /// 
  172. /// <br/>
  173. /// <para><b>Saving and Restoring Settings</b></para>
  174. ///  
  175. /// <para>
  176. ///  To save the user's choice of devices and compressors, 
  177. ///  save <see cref="Filter.MonikerString"/> and user it later
  178. ///  to recreate the Filter object.
  179. /// </para>
  180. /// <para>
  181. ///  To save a user's choices from a property page use
  182. ///  <see cref="PropertyPage.State"/>. However, only the audio
  183. ///  and video compressor property pages support this.
  184. /// </para>
  185. /// <para>
  186. ///  The last items to save are the video and audio settings such
  187. ///  as FrameSize and AudioSamplingRate. When restoring, remember
  188. ///  to restore these properties after setting the video and audio
  189. ///  compressors.
  190. /// </para>
  191. /// <code><div style="background-color:whitesmoke;">
  192. ///  // Disable preview
  193. ///  capture.PreviewWindow = null;
  194. ///  
  195. ///  // Save settings
  196. ///  string videoDevice = capture.VideoDevice.MonikerString;
  197. ///  string audioDevice = capture.AudioDevice.MonikerString;
  198. ///  string videoCompressor = capture.VideoCompressor.MonikerString;
  199. ///  string audioCompressor = capture.AudioCompressor.MonikerString;
  200. ///  double frameRate = capture.FrameRate;
  201. ///  Size frameSize = capture.FrameSize;
  202. ///  short audioChannels = capture.AudioChannels;
  203. ///  short audioSampleSize = capture.AudioSampleSize;
  204. ///  int audioSamplingRate = capture.AudioSamplingRate;
  205. ///  ArrayList pages = new ArrayList();
  206. ///  foreach ( PropertyPage p in capture.PropertyPages )
  207. ///  {
  208. /// if ( p.SupportsPersisting )
  209. /// pages.Add( p.State );
  210. /// }
  211. ///
  212. ///  
  213. ///  // Restore settings
  214. ///  Capture capture = new Capture( new Filter( videoDevice), 
  215. /// new Filter( audioDevice) );
  216. ///  capture.VideoCompressor = new Filter( videoCompressor );
  217. ///  capture.AudioCompressor = new Filter( audioCompressor );
  218. ///  capture.FrameRate = frameRate;
  219. ///  capture.FrameSize = frameSize;
  220. ///  capture.AudioChannels = audioChannels;
  221. ///  capture.AudioSampleSize = audioSampleSize;
  222. ///  capture.AudioSamplingRate = audioSamplingRate;
  223. ///  foreach ( PropertyPage p in capture.PropertyPages )
  224. ///  {
  225. /// if ( p.SupportsPersisting )
  226. /// {
  227. /// p.State = (byte[]) pages[0]
  228. /// pages.RemoveAt( 0 );
  229. /// }
  230. ///  }
  231. ///  // Enable preview
  232. ///  capture.PreviewWindow = myPanel;
  233. /// </div></code>
  234. ///  
  235. /// <br/>
  236. /// <para><b>TV Tuner</b></para>
  237. /// 
  238. /// <para>
  239. ///  To access the TV Tuner, use the <see cref="Capture.Tuner"/> property.
  240. ///  If the device does not have a TV tuner, this property will be null.
  241. ///  See <see cref="DirectX.Capture.Tuner.Channel"/>, 
  242. ///  <see cref="DirectX.Capture.Tuner.InputType"/> and 
  243. ///  <see cref="DirectX.Capture.Tuner.SignalPresent"/> 
  244. ///  for more information.
  245. /// </para>
  246. /// <code><div style="background-color:whitesmoke;">
  247. ///  // Change to channel 5
  248. ///  capture.Tuner.Channel = 5;
  249. /// </div></code>
  250. /// 
  251. /// <br/>
  252. /// <para><b>Troubleshooting</b></para>
  253. /// 
  254. /// <para>
  255. ///  This class library uses COM Interop to access the full
  256. ///  capabilities of DirectShow, so if there is another
  257. ///  application that can successfully use a hardware device
  258. ///  then it should be possible to modify this class library
  259. ///  to use the device.
  260. /// </para>
  261. /// <para>
  262. ///  Try the <b>AMCap</b> sample from the DirectX SDK 
  263. ///  (DX9SamplesC++DirectShowBinAMCap.exe) or 
  264. ///  <b>Virtual VCR</b> from http://www.DigTV.ws 
  265. /// </para>
  266. /// 
  267. /// <br/>
  268. /// <para><b>Credits</b></para>
  269. /// 
  270. /// <para>
  271. ///  This class library would not be possible without the
  272. ///  DShowNET project by NETMaster: 
  273. ///  http://www.codeproject.com/useritems/directshownet.asp
  274. /// </para>
  275. /// <para>
  276. ///  Documentation is generated by nDoc available at
  277. ///  http://ndoc.sourceforge.net
  278. /// </para>
  279. /// 
  280. /// <br/>
  281. /// <para><b>Feedback</b></para>
  282. /// 
  283. ///  Feel free to send comments and questions to me at
  284. ///  mportobello@hotmail.com. If the the topic may be of interest
  285. ///  to others, post your question on the www.codeproject.com
  286. ///  page for DirectX.Capture.
  287. /// </remarks>
  288. public class Capture : System.Windows.Forms.Form, ISampleGrabberCB
  289. {
  290. // ------------------ Private Enumerations --------------------
  291. /// <summary> Possible states of the interal filter graph </summary>
  292. protected enum GraphState
  293. {
  294. Null, // No filter graph at all
  295. Created, // Filter graph created with device filters added
  296. Rendered, // Filter complete built, ready to run (possibly previewing)
  297. Capturing // Filter is capturing
  298. }
  299. // ------------------ Public Properties --------------------
  300. /// <summary> Is the class currently capturing. Read-only. </summary>
  301. public bool Capturing { get { return( graphState==GraphState.Capturing ); } }
  302. /// <summary> Has the class been cued to begin capturing. Read-only. </summary>
  303. public bool Cued { get { return( isCaptureRendered && graphState==GraphState.Rendered ); } }
  304. /// <summary> Is the class currently stopped. Read-only. </summary>
  305. public bool Stopped { get { return( graphState!=GraphState.Capturing ); } }
  306. /// <summary> 
  307. ///  Name of file to capture to. Initially set to
  308. ///  a valid temporary file.
  309. /// </summary>
  310. /// <remarks>
  311. ///  If the file does not exist, it will be created. If it does 
  312. ///  exist, it will be overwritten. An overwritten file will 
  313. ///  not be shortened if the captured data is smaller than the 
  314. ///  original file. The file will be valid, it will just contain 
  315. ///  extra, unused, data after the audio/video data. 
  316. /// 
  317. /// <para>
  318. ///  A future version of this class will provide a method to copy 
  319. ///  only the valid audio/video data to a new file. </para>
  320. /// 
  321. /// <para>
  322. ///  This property cannot be changed while capturing or cued. </para>
  323. /// </remarks> 
  324. public string Filename 
  325. get { return( filename ); } 
  326. set 
  327. assertStopped();
  328. if ( Cued )
  329. throw new InvalidOperationException( "The Filename cannot be changed once cued. Use Stop() before changing the filename." );
  330. filename = value; 
  331. if ( fileWriterFilter != null )
  332. {
  333. string s;
  334. AMMediaType mt = new AMMediaType(); 
  335. int hr = fileWriterFilter.GetCurFile( out s, mt );
  336. if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );
  337. if ( mt.formatSize > 0 )
  338. Marshal.FreeCoTaskMem( mt.formatPtr ); 
  339. hr = fileWriterFilter.SetFileName( filename, mt );
  340. if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );
  341. }
  342. }
  343. /// <summary>
  344. ///  The control that will host the preview window. 
  345. /// </summary>
  346. /// <remarks>
  347. ///  Setting this property will begin video preview
  348. ///  immediately. Set this property after setting all
  349. ///  other properties to avoid unnecessary changes
  350. ///  to the internal filter graph (some properties like
  351. ///  FrameSize require the internal filter graph to be 
  352. ///  stopped and disconnected before the property
  353. ///  can be retrieved or set).
  354. ///  
  355. /// <para>
  356. ///  To stop video preview, set this property to null. </para>
  357. /// </remarks>
  358. public Control PreviewWindow
  359. {
  360. get { return( previewWindow ); }
  361. set
  362. {
  363. assertStopped();
  364. derenderGraph();
  365. previewWindow = value;
  366. wantPreviewRendered = ( ( previewWindow != null ) && ( videoDevice != null ) );
  367. renderStream = false;
  368. renderGraph();
  369. startPreviewIfNeeded();
  370. }
  371. }
  372. /// <summary>
  373. ///  The capabilities of the video device.
  374. /// </summary>
  375. /// <remarks>
  376. ///  It may be required to cue the capture (see <see cref="Cue"/>) 
  377. ///  before all capabilities are correctly reported. If you 
  378. ///  have such a device, the developer would be interested to
  379. ///  hear from you.
  380. /// 
  381. /// <para>
  382. ///  The information contained in this property is retrieved and
  383. ///  cached the first time this property is accessed. Future
  384. ///  calls to this property use the cached results. This was done 
  385. ///  for performance. </para>
  386. ///  
  387. /// <para>
  388. ///  However, this means <b>you may get different results depending 
  389. ///  on when you access this property first</b>. If you are experiencing 
  390. ///  problems, try accessing the property immediately after creating 
  391. ///  the Capture class or immediately after setting the video and 
  392. ///  audio compressors. Also, inform the developer. </para>
  393. /// </remarks>
  394. public VideoCapabilities VideoCaps 
  395. get 
  396. if ( videoCaps == null )
  397. {
  398. if ( videoStreamConfig != null )
  399. {
  400. try 
  401. videoCaps = new VideoCapabilities( videoStreamConfig ); 
  402. }
  403. catch ( Exception ex ) { Debug.WriteLine( "VideoCaps: unable to create videoCaps." + ex.ToString() ); }
  404. }
  405. }
  406. return( videoCaps ); 
  407. }
  408. /// <summary>
  409. ///  The capabilities of the audio device.
  410. /// </summary>
  411. /// <remarks>
  412. ///  It may be required to cue the capture (see <see cref="Cue"/>) 
  413. ///  before all capabilities are correctly reported. If you 
  414. ///  have such a device, the developer would be interested to
  415. ///  hear from you.
  416. /// 
  417. /// <para>
  418. ///  The information contained in this property is retrieved and
  419. ///  cached the first time this property is accessed. Future
  420. ///  calls to this property use the cached results. This was done 
  421. ///  for performance. </para>
  422. ///  
  423. /// <para>
  424. ///  However, this means <b>you may get different results depending 
  425. ///  on when you access this property first</b>. If you are experiencing 
  426. ///  problems, try accessing the property immediately after creating 
  427. ///  the Capture class or immediately after setting the video and 
  428. ///  audio compressors. Also, inform the developer. </para>
  429. /// </remarks>
  430. public AudioCapabilities AudioCaps 
  431. get 
  432. if ( audioCaps == null )
  433. {
  434. if ( audioStreamConfig != null )
  435. {
  436. try 
  437. audioCaps = new AudioCapabilities( audioStreamConfig ); 
  438. }
  439. catch ( Exception ex ) { Debug.WriteLine( "AudioCaps: unable to create audioCaps." + ex.ToString() ); }
  440. }
  441. }
  442. return( audioCaps ); 
  443. }
  444. /// <summary> 
  445. ///  The video capture device filter. Read-only. To use a different 
  446. ///  device, dispose of the current Capture instance and create a new 
  447. ///  instance with the desired device. 
  448. /// </summary>
  449. public Filter VideoDevice { get { return( videoDevice ); } }
  450. /// <summary> 
  451. ///  The audio capture device filter. Read-only. To use a different 
  452. ///  device, dispose of the current Capture instance and create a new 
  453. ///  instance with the desired device. 
  454. /// </summary>
  455. public Filter AudioDevice { get { return( audioDevice ); } }
  456. /// <summary> 
  457. ///  The video compression filter. When this property is changed 
  458. ///  the internal filter graph is rebuilt. This means that some properties
  459. ///  will be reset. Set this property as early as possible to avoid losing 
  460. ///  changes. This property cannot be changed while capturing.
  461. /// </summary>
  462. public Filter VideoCompressor 
  463. get { return( videoCompressor ); } 
  464. set 
  465. assertStopped();
  466. destroyGraph();
  467. videoCompressor = value;
  468. renderGraph();
  469. startPreviewIfNeeded();
  470. }
  471. }
  472. /// <summary> 
  473. ///  The audio compression filter. 
  474. /// </summary>
  475. /// <remarks>
  476. ///  When this property is changed 
  477. ///  the internal filter graph is rebuilt. This means that some properties
  478. ///  will be reset. Set this property as early as possible to avoid losing 
  479. ///  changes. This property cannot be changed while capturing.
  480. /// </remarks>
  481. public Filter AudioCompressor 
  482. get { return( audioCompressor ); } 
  483. set 
  484. assertStopped();
  485. destroyGraph();
  486. audioCompressor = value;
  487. renderGraph();
  488. startPreviewIfNeeded();
  489. }
  490. }
  491. /// <summary> 
  492. ///  The current video source. Use Capture.VideoSources to 
  493. ///  list available sources. Set to null to disable all 
  494. ///  sources (mute).
  495. /// </summary>
  496. public Source VideoSource 
  497. get { return( VideoSources.CurrentSource ); } 
  498. set { VideoSources.CurrentSource = value; } 
  499. }
  500. /// <summary> 
  501. ///  The current audio source. Use Capture.AudioSources to 
  502. ///  list available sources. Set to null to disable all 
  503. ///  sources (mute).
  504. /// </summary>
  505. public Source AudioSource 
  506. {
  507. get { return( AudioSources.CurrentSource ); } 
  508. set { AudioSources.CurrentSource = value; }
  509. }
  510. /// <summary> 
  511. ///  Collection of available video sources/physical connectors 
  512. ///  on the current video device. 
  513. /// </summary>
  514. /// <remarks>
  515. ///  In most cases, if the device has only one source, 
  516. ///  this collection will be empty. 
  517. /// 
  518. /// <para>
  519. ///  The information contained in this property is retrieved and
  520. ///  cached the first time this property is accessed. Future
  521. ///  calls to this property use the cached results. This was done 
  522. ///  for performance. </para>
  523. ///  
  524. /// <para>
  525. ///  However, this means <b>you may get different results depending 
  526. ///  on when you access this property first</b>. If you are experiencing 
  527. ///  problems, try accessing the property immediately after creating 
  528. ///  the Capture class or immediately after setting the video and 
  529. ///  audio compressors. Also, inform the developer. </para>
  530. /// </remarks>
  531. public SourceCollection VideoSources
  532. {
  533. get 
  534. if ( videoSources == null )
  535. {
  536. try
  537. {
  538. if ( videoDevice != null )
  539. videoSources = new SourceCollection( captureGraphBuilder, videoDeviceFilter, true );
  540. else
  541. videoSources = new SourceCollection();
  542. }
  543. catch ( Exception ex ) { Debug.WriteLine( "VideoSources: unable to create VideoSources." + ex.ToString() ); }
  544. }
  545. return ( videoSources );
  546. }
  547. }
  548. /// <summary> 
  549. ///  Collection of available audio sources/physical connectors 
  550. ///  on the current audio device. 
  551. /// </summary>
  552. /// <remarks>
  553. ///  In most cases, if the device has only one source, 
  554. ///  this collection will be empty. For audio
  555. ///  there are 2 different methods for enumerating audio sources
  556. ///  an audio crossbar (usually TV tuners?) or an audio mixer 
  557. ///  (usually sound cards?). This class will first look for an 
  558. ///  audio crossbar. If no sources or only one source is available
  559. ///  on the crossbar, this class will then look for an audio mixer.
  560. ///  This class does not support both methods.
  561. /// 
  562. /// <para>
  563. ///  The information contained in this property is retrieved and
  564. ///  cached the first time this property is accessed. Future
  565. ///  calls to this property use the cached results. This was done 
  566. ///  for performance. </para>
  567. ///  
  568. /// <para>
  569. ///  However, this means <b>you may get different results depending 
  570. ///  on when you access this property first</b>. If you are experiencing 
  571. ///  problems, try accessing the property immediately after creating 
  572. ///  the Capture class or immediately after setting the video and 
  573. ///  audio compressors. Also, inform the developer. </para>
  574. ///  </remarks>
  575. public SourceCollection AudioSources
  576. {
  577. get 
  578. if ( audioSources == null )
  579. {
  580. try
  581. {
  582. if ( audioDevice != null )
  583. audioSources = new SourceCollection( captureGraphBuilder, audioDeviceFilter, false );
  584. else
  585. audioSources = new SourceCollection();
  586. }
  587. catch ( Exception ex ) { Debug.WriteLine( "AudioSources: unable to create AudioSources." + ex.ToString() ); }
  588. }
  589. return ( audioSources );
  590. }
  591. }
  592. /// <summary>
  593. ///  Available property pages. 
  594. /// </summary>
  595. /// <remarks>
  596. ///  These are property pages exposed by the DirectShow filters. 
  597. ///  These property pages allow users modify settings on the 
  598. ///  filters directly. 
  599. /// 
  600. /// <para>
  601. ///  The information contained in this property is retrieved and
  602. ///  cached the first time this property is accessed. Future
  603. ///  calls to this property use the cached results. This was done 
  604. ///  for performance. </para>
  605. ///  
  606. /// <para>
  607. ///  However, this means <b>you may get different results depending 
  608. ///  on when you access this property first</b>. If you are experiencing 
  609. ///  problems, try accessing the property immediately after creating 
  610. ///  the Capture class or immediately after setting the video and 
  611. ///  audio compressors. Also, inform the developer. </para>
  612. /// </remarks>
  613. public PropertyPageCollection PropertyPages 
  614. {
  615. get
  616. {
  617. if ( propertyPages == null )
  618. {
  619. try 
  620. propertyPages = new PropertyPageCollection( 
  621. captureGraphBuilder, 
  622. videoDeviceFilter, audioDeviceFilter, 
  623. videoCompressorFilter, audioCompressorFilter, 
  624. VideoSources, AudioSources );
  625. }
  626. catch ( Exception ex ) { Debug.WriteLine( "PropertyPages: unable to get property pages." + ex.ToString() ); }
  627. }
  628. return( propertyPages );
  629. }
  630. }
  631. /// <summary>
  632. ///  The TV Tuner or null if the current video device 
  633. ///  does not have a TV Tuner.
  634. /// </summary>
  635. public Tuner Tuner { get { return( tuner ); } }
  636. /// <summary>
  637. ///  Gets and sets the frame rate used to capture video.
  638. /// </summary>
  639. /// <remarks>
  640. ///  Common frame rates: 24 fps for film, 25 for PAL, 29.997
  641. ///  for NTSC. Not all NTSC capture cards can capture at 
  642. ///  exactly 29.997 fps. Not all frame rates are supported. 
  643. ///  When changing the frame rate, the closest supported 
  644. ///  frame rate will be used. 
  645. ///  
  646. /// <para>
  647. ///  Not all devices support getting/setting this property.
  648. ///  If this property is not supported, accessing it will
  649. ///  throw and exception. </para>
  650. ///  
  651. /// <para>
  652. ///  This property cannot be changed while capturing. Changing 
  653. ///  this property while preview is enabled will cause some 
  654. ///  fickering while the internal filter graph is partially
  655. ///  rebuilt. Changing this property while cued will cancel the
  656. ///  cue. Call Cue() again to re-cue the capture. </para>
  657. /// </remarks>
  658. public double FrameRate
  659. {
  660. get
  661. {
  662. long avgTimePerFrame = (long) getStreamConfigSetting( videoStreamConfig, "AvgTimePerFrame" );
  663. return( (double) 10000000 / avgTimePerFrame );
  664. }
  665. set
  666. {
  667. long avgTimePerFrame = (long) ( 10000000 / value );
  668. setStreamConfigSetting( videoStreamConfig, "AvgTimePerFrame", avgTimePerFrame );
  669. }
  670. }
  671. /// <summary>
  672. ///  Gets and sets the frame size used to capture video.
  673. /// </summary>
  674. /// <remarks>
  675. ///  To change the frame size, assign a new Size object 
  676. ///  to this property <code>capture.Size = new Size( w, h );</code>
  677. ///  rather than modifying the size in place 
  678. ///  (capture.Size.Width = w;). Not all frame
  679. ///  rates are supported.
  680. ///  
  681. /// <para>
  682. ///  Not all devices support getting/setting this property.
  683. ///  If this property is not supported, accessing it will
  684. ///  throw and exception. </para>
  685. /// 
  686. /// <para> 
  687. ///  This property cannot be changed while capturing. Changing 
  688. ///  this property while preview is enabled will cause some 
  689. ///  fickering while the internal filter graph is partially
  690. ///  rebuilt. Changing this property while cued will cancel the
  691. ///  cue. Call Cue() again to re-cue the capture. </para>
  692. /// </remarks>
  693. public Size FrameSize
  694. {
  695. get
  696. {
  697. BitmapInfoHeader bmiHeader;
  698. bmiHeader = (BitmapInfoHeader) getStreamConfigSetting( videoStreamConfig, "BmiHeader" );
  699. Size size = new Size( bmiHeader.Width, bmiHeader.Height );
  700. return( size );
  701. }
  702. set
  703. {
  704. BitmapInfoHeader bmiHeader;
  705. bmiHeader = (BitmapInfoHeader) getStreamConfigSetting( videoStreamConfig, "BmiHeader" );
  706. bmiHeader.Width = value.Width;
  707. bmiHeader.Height = value.Height;
  708. setStreamConfigSetting( videoStreamConfig, "BmiHeader", bmiHeader );
  709. }
  710. }
  711. /// <summary>
  712. ///  Get or set the number of channels in the waveform-audio data. 
  713. /// </summary>
  714. /// <remarks>
  715. ///  Monaural data uses one channel and stereo data uses two channels. 
  716. ///  
  717. /// <para>
  718. ///  Not all devices support getting/setting this property.
  719. ///  If this property is not supported, accessing it will
  720. ///  throw and exception. </para>
  721. ///  
  722. /// <para>
  723. ///  This property cannot be changed while capturing. Changing 
  724. ///  this property while preview is enabled will cause some 
  725. ///  fickering while the internal filter graph is partially
  726. ///  rebuilt. Changing this property while cued will cancel the
  727. ///  cue. Call Cue() again to re-cue the capture. </para>
  728. /// </remarks>
  729. public short AudioChannels
  730. {
  731. get
  732. {
  733. short audioChannels = (short) getStreamConfigSetting( audioStreamConfig, "nChannels" );
  734. return( audioChannels );
  735. }
  736. set
  737. {
  738. setStreamConfigSetting( audioStreamConfig, "nChannels", value );
  739. }
  740. }
  741. /// <summary>
  742. ///  Get or set the number of audio samples taken per second.
  743. /// </summary>
  744. /// <remarks>
  745. ///  Common sampling rates are 8.0 kHz, 11.025 kHz, 22.05 kHz, and 
  746. ///  44.1 kHz. Not all sampling rates are supported.
  747. ///  
  748. /// <para>
  749. ///  Not all devices support getting/setting this property.
  750. ///  If this property is not supported, accessing it will
  751. ///  throw and exception. </para>
  752. ///  
  753. /// <para>
  754. ///  This property cannot be changed while capturing. Changing 
  755. ///  this property while preview is enabled will cause some 
  756. ///  fickering while the internal filter graph is partially
  757. ///  rebuilt. Changing this property while cued will cancel the
  758. ///  cue. Call Cue() again to re-cue the capture. </para>
  759. /// </remarks>
  760. public int AudioSamplingRate
  761. {
  762. get
  763. {
  764. int samplingRate = (int) getStreamConfigSetting( audioStreamConfig, "nSamplesPerSec" );
  765. return( samplingRate );
  766. }
  767. set
  768. {
  769. setStreamConfigSetting( audioStreamConfig, "nSamplesPerSec", value );
  770. }
  771. }
  772. /// <summary>
  773. ///  Get or set the number of bits recorded per sample. 
  774. /// </summary>
  775. /// <remarks>
  776. ///  Common sample sizes are 8 bit and 16 bit. Not all
  777. ///  samples sizes are supported.
  778. ///  
  779. /// <para>
  780. ///  Not all devices support getting/setting this property.
  781. ///  If this property is not supported, accessing it will
  782. ///  throw and exception. </para>
  783. ///  
  784. /// <para>
  785. ///  This property cannot be changed while capturing. Changing 
  786. ///  this property while preview is enabled will cause some 
  787. ///  fickering while the internal filter graph is partially
  788. ///  rebuilt. Changing this property while cued will cancel the
  789. ///  cue. Call Cue() again to re-cue the capture. </para>
  790. /// </remarks>
  791. public short AudioSampleSize
  792. {
  793. get
  794. {
  795. short sampleSize = (short) getStreamConfigSetting( audioStreamConfig, "wBitsPerSample" );
  796. return( sampleSize );
  797. }
  798. set
  799. {
  800. setStreamConfigSetting( audioStreamConfig, "wBitsPerSample", value );
  801. }
  802. }
  803. /// <summary>
  804. /// Necesario para poder hacer una identificaci髇 cuando se lance el evento CaptureFrameCompleted
  805. /// </summary>
  806. /*public int Id
  807. {
  808. get
  809. {
  810. return id;
  811. }
  812. set
  813. {
  814. id = value;
  815. }
  816. }*/
  817. // --------------------- Events ----------------------
  818. /// <summary> Fired when a capture is completed (manually or automatically). </summary>
  819. public event EventHandler CaptureComplete;
  820. /// <summary> Fired when a frame was captured. </summary> 
  821. public delegate void FrameCapHandler(System.Windows.Forms.PictureBox Frame);
  822. //public static event FrameCapHandler FrameCaptureComplete;
  823. public event FrameCapHandler FrameCaptureComplete;
  824. // ------------- Protected/private Properties --------------
  825. protected GraphState graphState = GraphState.Null; // State of the internal filter graph
  826. protected bool isPreviewRendered = false; // When graphState==Rendered, have we rendered the preview stream?
  827. protected bool isCaptureRendered = false; // When graphState==Rendered, have we rendered the capture stream?
  828. protected bool wantPreviewRendered = false; // Do we need the preview stream rendered (VideoDevice and PreviewWindow != null)
  829. protected bool wantCaptureRendered = false; // Do we need the capture stream rendered
  830. protected bool wantCaptureFrame = false;
  831. protected int rotCookie = 0; // Cookie into the Running Object Table
  832. protected Filter videoDevice = null; // Property Backer: Video capture device filter
  833. protected Filter audioDevice = null; // Property Backer: Audio capture device filter
  834. protected Filter videoCompressor = null; // Property Backer: Video compression filter
  835. protected Filter audioCompressor = null; // Property Backer: Audio compression filter
  836. protected string filename = ""; // Property Backer: Name of file to capture to
  837. protected Control previewWindow = null; // Property Backer: Owner control for preview
  838. protected VideoCapabilities videoCaps = null; // Property Backer: capabilities of video device
  839. protected AudioCapabilities audioCaps = null; // Property Backer: capabilities of audio device
  840. protected SourceCollection videoSources = null; // Property Backer: list of physical video sources
  841. protected SourceCollection audioSources = null; // Property Backer: list of physical audio sources
  842. protected PropertyPageCollection propertyPages = null; // Property Backer: list of property pages exposed by filters
  843. protected Tuner tuner = null; // Property Backer: TV Tuner
  844. protected IGraphBuilder graphBuilder; // DShow Filter: Graph builder 
  845. protected IMediaControl mediaControl; // DShow Filter: Start/Stop the filter graph -> copy of graphBuilder
  846. protected IVideoWindow videoWindow; // DShow Filter: Control preview window -> copy of graphBuilder
  847. protected ICaptureGraphBuilder2 captureGraphBuilder = null; // DShow Filter: building graphs for capturing video
  848. protected ISampleGrabber sampGrabber = null;
  849. protected IAMStreamConfig videoStreamConfig = null; // DShow Filter: configure frame rate, size
  850. protected IAMStreamConfig audioStreamConfig = null; // DShow Filter: configure sample rate, sample size
  851. protected IBaseFilter videoDeviceFilter = null; // DShow Filter: selected video device
  852. protected IBaseFilter videoCompressorFilter = null; // DShow Filter: selected video compressor
  853. protected IBaseFilter audioDeviceFilter = null; // DShow Filter: selected audio device
  854. protected IBaseFilter audioCompressorFilter = null; // DShow Filter: selected audio compressor
  855. protected IBaseFilter muxFilter = null; // DShow Filter: multiplexor (combine video and audio streams)
  856. protected IBaseFilter baseGrabFlt = null;
  857. protected IFileSinkFilter fileWriterFilter = null; // DShow Filter: file writer
  858. protected VideoInfoHeader videoInfoHeader;
  859. protected byte[] savedArray;
  860. protected bool capturedFrame = false;
  861. protected int bufferedSize;
  862. protected bool captured = true;
  863. protected bool firstFrame = true;
  864. protected bool renderStream = false;
  865. //protected int id; //Almacena un n鷐ero que identifica a la c醡ara
  866. //protected CTee InfTee;
  867. /// <summary> event when callback has finished (ISampleGrabberCB.BufferCB). </summary>
  868. private delegate void CaptureDone();
  869. private const int WM_GRAPHNOTIFY = 0x00008001; // message from graph
  870. private IMediaEventEx mediaEvt; // event interface
  871. protected System.Windows.Forms.PictureBox ImageCaptured;
  872. // ------------- Constructors/Destructors --------------
  873. /// <summary> 
  874. ///  Create a new Capture object. 
  875. ///  videoDevice and audioDevice can be null if you do not 
  876. ///  wish to capture both audio and video. However at least
  877. ///  one must be a valid device. Use the <see cref="Filters"/> 
  878. ///  class to list available devices.
  879. ///  </summary>
  880. public Capture(Filter videoDevice, Filter audioDevice)
  881. {
  882. if ( videoDevice == null && audioDevice == null)
  883. throw new ArgumentException( "The videoDevice and/or the audioDevice parameter must be set to a valid Filter.n" );
  884. this.videoDevice = videoDevice;
  885. this.audioDevice = audioDevice;
  886. this.Filename = getTempFilename();
  887. this.ImageCaptured = new System.Windows.Forms.PictureBox();
  888. createGraph(); 
  889. }
  890. /// <summary> Destructor. Dispose of resources. </summary>
  891. ~Capture()
  892. {
  893. Dispose();
  894. }
  895. // --------------------- Public Methods -----------------------
  896. /// <summary>
  897. ///  Prepare for capturing. Use this method when capturing 
  898. ///  must begin as quickly as possible. 
  899. /// </summary>
  900. /// <remarks>
  901. ///  This will create/overwrite a zero byte file with 
  902. ///  the name set in the Filename property. 
  903. ///  
  904. /// <para>
  905. ///  This will disable preview. Preview will resume
  906. ///  once capture begins. This problem can be fixed
  907. ///  if someone is willing to make the change. </para>
  908. ///  
  909. /// <para>
  910. ///  This method is optional. If Cue() is not called, 
  911. ///  Start() will call it before capturing. This method
  912. ///  cannot be called while capturing. </para>
  913. /// </remarks>
  914. public void Cue()
  915. {
  916. assertStopped();
  917. // We want the capture stream rendered
  918. wantCaptureRendered = true;
  919. // Re-render the graph (if necessary)
  920. renderGraph();
  921. // Pause the graph
  922. int hr = mediaControl.Pause();
  923. if ( hr != 0 ) Marshal.ThrowExceptionForHR( hr ); 
  924. }
  925. /// <summary> Begin capturing. </summary>
  926. public void Start()
  927. {
  928. Stop();
  929. // Para que cuando estemos capturando un video podamos capturar frames
  930. firstFrame = false;
  931. assertStopped();
  932. // We want the capture stream rendered
  933. wantCaptureRendered = true;
  934. // Re-render the graph (if necessary)
  935. renderStream = true;
  936. renderGraph();
  937. // Start the filter graph: begin capturing
  938. int hr = mediaControl.Run();
  939. if ( hr != 0 ) Marshal.ThrowExceptionForHR( hr ); 
  940. // Update the state
  941. graphState = GraphState.Capturing;
  942. }
  943. /// <summary> 
  944. ///  Stop the current capture capture. If there is no
  945. ///  current capture, this method will succeed.
  946. /// </summary>
  947. public void Stop()
  948. {
  949. // Stop the graph if it is running
  950. // If we have a preview running we should only stop the
  951. // capture stream. However, if we have a preview stream
  952. // we need to re-render the graph anyways because we 
  953. // need to get rid of the capture stream. To re-render
  954. // we need to stop the entire graph
  955. if ( mediaControl != null )
  956. {
  957. mediaControl.Stop();
  958. }
  959. // Config is true when the parametres of the device are to be changed
  960. wantCaptureRendered = false;
  961. wantPreviewRendered = true;
  962. // Update the state
  963. if ( graphState == GraphState.Capturing )
  964. {
  965. graphState = GraphState.Rendered;
  966. if ( CaptureComplete != null )
  967. CaptureComplete( this, null );
  968. }
  969. // Para que cuando volvamos a capturar frames no haya problemas
  970. firstFrame = true;
  971. // So we destroy the capture stream IF 
  972. // we need a preview stream. If we don't
  973. // this will leave the graph as it is.
  974. renderStream = false;
  975. try { renderGraph(); } 
  976. catch {}
  977. try { startPreviewIfNeeded(); } 
  978. catch {}
  979. }
  980. /// <summary> 
  981. ///  Calls Stop, releases all references. If a capture is in progress
  982. ///  it will be stopped, but the CaptureComplete event will NOT fire.
  983. /// </summary>
  984. public void DisposeCapture()
  985. {
  986. wantPreviewRendered = false;
  987. wantCaptureRendered = false;
  988. CaptureComplete = null;
  989. try { destroyGraph(); } 
  990. catch {}
  991. if ( videoSources != null )
  992. videoSources.Dispose(); videoSources = null;
  993. if ( audioSources != null )
  994. audioSources.Dispose(); audioSources = null;
  995. }
  996. [STAThread]
  997. public void CaptureFrame()
  998. {
  999. int hr;
  1000. if(firstFrame)
  1001. {
  1002. assertStopped();
  1003. // Re-render the graph (if necessary)
  1004. renderStream = true;
  1005. renderGraph();
  1006. // Start the filter graph: begin capturing
  1007. hr = mediaControl.Run();
  1008. if ( hr != 0 ) Marshal.ThrowExceptionForHR( hr ); 
  1009. firstFrame = false;
  1010. }
  1011. captured = false;
  1012. if(savedArray == null )
  1013. {
  1014. int size = videoInfoHeader.BmiHeader.ImageSize;
  1015. if( (size<1000) || (size > 16000000) )
  1016. return;
  1017. savedArray = new byte[ size + 64000];
  1018. }
  1019. hr = sampGrabber.SetCallback( this, 1 );
  1020. }
  1021. /// <summary>
  1022. /// Esta funci髇 har