text:
// ------------------------------------------------------------------
// DirectX.Capture
//
// History:
//	2003-Jan-24		BL		- created
//  2003-Aug-06		DB		- modified
//
// Copyright (c) 2003 Brian Low
// ------------------------------------------------------------------

using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using System.Data;
using System.ComponentModel;
using DShowNET;
using DShowNET.Device;

namespace DirectX.Capture
{

	///
	///  Use the Capture class to capture audio and video to AVI files.
	///
	/// <remarks>
	///  This is the core class of the Capture Class Library. The following
	///  sections introduce the Capture class and how to use this library.
	///
	///

	/// <para>Basic Usage</para>
	///
	/// <para>
	///  The Capture class only requires a video device and/or audio device
	///  to begin capturing. The <see cref="Filters"/> class provides
	///  lists of the installed video and audio devices.
	/// </para>
	///

	///  // Remember to add a reference to DirectX.Capture.dll
	///  using DirectX.Capture
	///  ...
	///  Capture capture = new Capture( Filters.VideoInputDevices[0],
	///                                 Filters.AudioInputDevices[0] );
	///  capture.Start();
	///  ...
	///  capture.Stop();
	///

	/// <para>
	///  This will capture video and audio using the first video and audio devices
	///  installed on the system. To capture video only, pass a null as the second
	///  parameter of the constructor.
	/// </para>
	/// <para>
	///  The class is initialized to a valid temporary file in the Windows temp
	///  folder. To capture to a different file, set the
	///  <see cref="Capture.Filename"/> property before you begin
	///  capturing. Remember to add DirectX.Capture.dll to
	///  your project references.
	/// </para>
	///
	///

	/// <para>Setting Common Properties</para>
	///
	/// <para>
	///  The example below shows how to change video and audio settings.
	///  Properties such as <see cref="Capture.FrameRate"/> and
	///  <see cref="AudioSampleSize"/> allow you to programmatically adjust
	///  the capture. Use <see cref="Capture.VideoCaps"/> and
	///  <see cref="Capture.AudioCaps"/> to determine valid values for these
	///  properties.
	/// </para>
	///

	///  Capture capture = new Capture( Filters.VideoInputDevices[0],
	///                                 Filters.AudioInputDevices[1] );
	///  capture.VideoCompressor = Filters.VideoCompressors[0];
	///  capture.AudioCompressor = Filters.AudioCompressors[0];
	///  capture.FrameRate = 29.997;
	///  capture.FrameSize = new Size( 640, 480 );
	///  capture.AudioSamplingRate = 44100;
	///  capture.AudioSampleSize = 16;
	///  capture.Filename = "C:\MyVideo.avi";
	///  capture.Start();
	///  ...
	///  capture.Stop();
	///

	/// <para>
	///  The example above also shows the use of video and audio compressors. In most
	///  cases you will want to use compressors. Uncompressed video can easily
	///  consume over a 1GB of disk space per minute. Whenever possible, set
	///  the <see cref="Capture.VideoCompressor"/> and <see cref="Capture.AudioCompressor"/>
	///  properties as early as possible. Changing them requires the internal filter
	///  graph to be rebuilt which often causes most of the other properties to
	///  be reset to default values.
	/// </para>
	///
	///

	/// <para>Listing Devices</para>
	///
	/// <para>
	///  Use the <see cref="Filters.VideoInputDevices"/> collection to list
	///  video capture devices installed on the system.
	/// </para>
	///

	///  foreach ( Filter f in Filters.VideoInputDevices )
	///  {
	///		Debug.WriteLine( f.Name );
	///  }
	///

	/// The <see cref="Filters"/> class also provides collections for audio
	/// capture devices, video compressors and audio compressors.
	///
	///

	/// <para>Preview</para>
	///
	/// <para>
	///  Video preview is controled with the <see cref="Capture.PreviewWindow"/>
	///  property. Setting this property to a visible control will immediately
	///  begin preview. Set to null to stop the preview.
	/// </para>
	///

	///  // Enable preview
	///  capture.PreviewWindow = myPanel;
	///  // Disable preview
	///  capture.PreviewWindow = null;
	///

	/// <para>
	///  The control used must have a window handle (HWND), good controls to
	///  use are the Panel or the form itself.
	/// </para>
	/// <para>
	///  Retrieving or changing video/audio settings such as FrameRate,
	///  FrameSize, AudioSamplingRate, and AudioSampleSize will cause
	///  the preview window to flash. This is beacuse the preview must be
	///  temporarily stopped. Disable the preview if you need to access
	///  several properties at the same time.
	/// </para>
	///
	///

	/// <para>Property Pages</para>
	///
	/// <para>
	///  Property pages exposed by the devices and compressors are
	///  available through the <see cref="Capture.PropertyPages"/>
	///  collection.
	/// </para>
	///

	///  // Display the first property page
	///  capture.PropertyPages[0].Show();
	///

	/// <para>
	///  The property pages will often expose more settings than
	///  the Capture class does directly. Some examples are brightness,
	///  color space, audio balance and bass boost. The disadvantage
	///  to using the property pages is the user's choices cannot be
	///  saved and later restored. The exception to this is the video
	///  and audio compressor property pages. Most compressors support
	///  the saving and restoring state, see the
	///  <see cref="PropertyPage.State"/> property for more information.
	/// </para>
	/// <para>
	///  Changes made in the property page will be reflected
	///  immediately in the Capture class properties (e.g. Capture.FrameSize).
	///  However, the reverse is not always true. A change made directly to
	///  FrameSize, for example, may not be reflected in the associated
	///  property page. Fortunately, the filter will use requested FrameSize
	///  even though the property page shows otherwise.
	/// </para>
	///
	///

	/// <para>Saving and Restoring Settings</para>
	///
	/// <para>
	///  To save the user's choice of devices and compressors,
	///  save <see cref="Filter.MonikerString"/> and user it later
	///  to recreate the Filter object.
	/// </para>
	/// <para>
	///  To save a user's choices from a property page use
	///  <see cref="PropertyPage.State"/>. However, only the audio
	///  and video compressor property pages support this.
	/// </para>
	/// <para>
	///  The last items to save are the video and audio settings such
	///  as FrameSize and AudioSamplingRate. When restoring, remember
	///  to restore these properties after setting the video and audio
	///  compressors.
	/// </para>
	///

	///  // Disable preview
	///  capture.PreviewWindow = null;
	///
	///  // Save settings
	///  string videoDevice = capture.VideoDevice.MonikerString;
	///  string audioDevice = capture.AudioDevice.MonikerString;
	///  string videoCompressor = capture.VideoCompressor.MonikerString;
	///  string audioCompressor = capture.AudioCompressor.MonikerString;
	///  double frameRate = capture.FrameRate;
	///  Size frameSize = capture.FrameSize;
	///  short audioChannels = capture.AudioChannels;
	///  short audioSampleSize = capture.AudioSampleSize;
	///  int audioSamplingRate = capture.AudioSamplingRate;
	///  ArrayList pages = new ArrayList();
	///  foreach ( PropertyPage p in capture.PropertyPages )
	///  {
	///		if ( p.SupportsPersisting )
	///			pages.Add( p.State );
	///	}
	///
	///
	///  // Restore settings
	///  Capture capture = new Capture( new Filter( videoDevice),
	///				new Filter( audioDevice) );
	///  capture.VideoCompressor = new Filter( videoCompressor );
	///  capture.AudioCompressor = new Filter( audioCompressor );
	///  capture.FrameRate = frameRate;
	///  capture.FrameSize = frameSize;
	///  capture.AudioChannels = audioChannels;
	///  capture.AudioSampleSize = audioSampleSize;
	///  capture.AudioSamplingRate = audioSamplingRate;
	///  foreach ( PropertyPage p in capture.PropertyPages )
	///  {
	///		if ( p.SupportsPersisting )
	///		{
	///			p.State = (byte[]) pages[0]
	///			pages.RemoveAt( 0 );
	///		}
	///	 }
	///  // Enable preview
	///  capture.PreviewWindow = myPanel;
	///

	///
	///

	/// <para>TV Tuner</para>
	///
	/// <para>
	///  To access the TV Tuner, use the <see cref="Capture.Tuner"/> property.
	///  If the device does not have a TV tuner, this property will be null.
	///  See <see cref="DirectX.Capture.Tuner.Channel"/>,
	///  <see cref="DirectX.Capture.Tuner.InputType"/> and
	///  <see cref="DirectX.Capture.Tuner.SignalPresent"/>
	///  for more information.
	/// </para>
	///

	///  // Change to channel 5
	///  capture.Tuner.Channel = 5;
	///

	///
	///

	/// <para>Troubleshooting</para>
	///
	/// <para>
	///  This class library uses COM Interop to access the full
	///  capabilities of DirectShow, so if there is another
	///  application that can successfully use a hardware device
	///  then it should be possible to modify this class library
	///  to use the device.
	/// </para>
	/// <para>
	///  Try the AMCap sample from the DirectX SDK
	///  (DX9\Samples\C++\DirectShow\Bin\AMCap.exe) or
	///  Virtual VCR from http://www.DigTV.ws
	/// </para>
	///
	///

	/// <para>Credits</para>
	///
	/// <para>
	///  This class library would not be possible without the
	///  DShowNET project by NETMaster:
	///  http://www.codeproject.com/useritems/directshownet.asp
	/// </para>
	/// <para>
	///  Documentation is generated by nDoc available at
	///  http://ndoc.sourceforge.net
	/// </para>
	///
	///

	/// <para>Feedback</para>
	///
	///  Feel free to send comments and questions to me at
	///  mportobello@hotmail.com. If the the topic may be of interest
	///  to others, post your question on the www.codeproject.com
	///  page for DirectX.Capture.
	/// </remarks>
	public class Capture : System.Windows.Forms.Form, ISampleGrabberCB
	{

		// ------------------ Private Enumerations --------------------

		///  Possible states of the interal filter graph
		protected enum GraphState
		{
			Null,			// No filter graph at all
			Created,		// Filter graph created with device filters added
			Rendered,		// Filter complete built, ready to run (possibly previewing)
			Capturing		// Filter is capturing
		}

		// ------------------ Public Properties --------------------

		///  Is the class currently capturing. Read-only.
		public bool Capturing { get { return( graphState==GraphState.Capturing ); } }

		///  Has the class been cued to begin capturing. Read-only.
		public bool Cued { get { return( isCaptureRendered && graphState==GraphState.Rendered ); } }

		///  Is the class currently stopped. Read-only.
		public bool Stopped { get { return( graphState!=GraphState.Capturing ); } }

		///
		///  Name of file to capture to. Initially set to
		///  a valid temporary file.
		///
		/// <remarks>
		///  If the file does not exist, it will be created. If it does
		///  exist, it will be overwritten. An overwritten file will
		///  not be shortened if the captured data is smaller than the
		///  original file. The file will be valid, it will just contain
		///  extra, unused, data after the audio/video data.
		///
		/// <para>
		///  A future version of this class will provide a method to copy
		///  only the valid audio/video data to a new file. </para>
		///
		/// <para>
		///  This property cannot be changed while capturing or cued. </para>
		/// </remarks>

		public string Filename
		{
			get { return( filename ); }
			set
			{
				assertStopped();
				if ( Cued )
					throw new InvalidOperationException( "The Filename cannot be changed once cued. Use Stop() before changing the filename." );
				filename = value;
				if ( fileWriterFilter != null )
				{
					string s;
					AMMediaType mt = new AMMediaType();
					int hr = fileWriterFilter.GetCurFile( out s, mt );
					if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );
					if ( mt.formatSize > 0 )
						Marshal.FreeCoTaskMem( mt.formatPtr );
					hr = fileWriterFilter.SetFileName( filename, mt );
					if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );
				}
			}
		}

		///
		///  The control that will host the preview window.
		///
		/// <remarks>
		///  Setting this property will begin video preview
		///  immediately. Set this property after setting all
		///  other properties to avoid unnecessary changes
		///  to the internal filter graph (some properties like
		///  FrameSize require the internal filter graph to be
		///  stopped and disconnected before the property
		///  can be retrieved or set).
		///
		/// <para>
		///  To stop video preview, set this property to null. </para>
		/// </remarks>
		public Control PreviewWindow
		{
			get { return( previewWindow ); }
			set
			{
				assertStopped();
				derenderGraph();
				previewWindow = value;
				wantPreviewRendered = ( ( previewWindow != null ) && ( videoDevice != null ) );
				renderStream = false;
				renderGraph();
				startPreviewIfNeeded();
			}
		}

		///
		///  The capabilities of the video device.
		///
		/// <remarks>
		///  It may be required to cue the capture (see <see cref="Cue"/>)
		///  before all capabilities are correctly reported. If you
		///  have such a device, the developer would be interested to
		///  hear from you.
		///
		/// <para>
		///  The information contained in this property is retrieved and
		///  cached the first time this property is accessed. Future
		///  calls to this property use the cached results. This was done
		///  for performance. </para>
		///
		/// <para>
		///  However, this means you may get different results depending
		///  on when you access this property first. If you are experiencing
		///  problems, try accessing the property immediately after creating
		///  the Capture class or immediately after setting the video and
		///  audio compressors. Also, inform the developer. </para>
		/// </remarks>
		public VideoCapabilities VideoCaps
		{
			get
			{
				if ( videoCaps == null )
				{
					if ( videoStreamConfig != null )
					{
						try
						{
							videoCaps = new VideoCapabilities( videoStreamConfig );
						}
						catch ( Exception ex ) { Debug.WriteLine( "VideoCaps: unable to create videoCaps." + ex.ToString() ); }
					}
				}
				return( videoCaps );
			}
		}

		///
		///  The capabilities of the audio device.
		///
		/// <remarks>
		///  It may be required to cue the capture (see <see cref="Cue"/>)
		///  before all capabilities are correctly reported. If you
		///  have such a device, the developer would be interested to
		///  hear from you.
		///
		/// <para>
		///  The information contained in this property is retrieved and
		///  cached the first time this property is accessed. Future
		///  calls to this property use the cached results. This was done
		///  for performance. </para>
		///
		/// <para>
		///  However, this means you may get different results depending
		///  on when you access this property first. If you are experiencing
		///  problems, try accessing the property immediately after creating
		///  the Capture class or immediately after setting the video and
		///  audio compressors. Also, inform the developer. </para>
		/// </remarks>
		public AudioCapabilities AudioCaps
		{
			get
			{
				if ( audioCaps == null )
				{
					if ( audioStreamConfig != null )
					{
						try
						{
							audioCaps = new AudioCapabilities( audioStreamConfig );
						}
						catch ( Exception ex ) { Debug.WriteLine( "AudioCaps: unable to create audioCaps." + ex.ToString() ); }
					}
				}
				return( audioCaps );
			}
		}

		///
		///  The video capture device filter. Read-only. To use a different
		///  device, dispose of the current Capture instance and create a new
		///  instance with the desired device.
		///
		public Filter VideoDevice { get { return( videoDevice ); } }
		///
		///  The audio capture device filter. Read-only. To use a different
		///  device, dispose of the current Capture instance and create a new
		///  instance with the desired device.
		///
		public Filter AudioDevice { get { return( audioDevice ); } }

		///
		///  The video compression filter. When this property is changed
		///  the internal filter graph is rebuilt. This means that some properties
		///  will be reset. Set this property as early as possible to avoid losing
		///  changes. This property cannot be changed while capturing.
		///
		public Filter VideoCompressor
		{
			get { return( videoCompressor ); }
			set
			{
				assertStopped();
				destroyGraph();
				videoCompressor = value;
				renderGraph();
				startPreviewIfNeeded();
			}
		}

		///
		///  The audio compression filter.
		///
		/// <remarks>
		///  When this property is changed
		///  the internal filter graph is rebuilt. This means that some properties
		///  will be reset. Set this property as early as possible to avoid losing
		///  changes. This property cannot be changed while capturing.
		/// </remarks>
		public Filter AudioCompressor
		{
			get { return( audioCompressor ); }
			set
			{
				assertStopped();
				destroyGraph();
				audioCompressor = value;
				renderGraph();
				startPreviewIfNeeded();
			}
		}

		///
		///  The current video source. Use Capture.VideoSources to
		///  list available sources. Set to null to disable all
		///  sources (mute).
		///
		public Source VideoSource
		{
			get { return( VideoSources.CurrentSource ); }
			set { VideoSources.CurrentSource = value; }
		}

		///
		///  The current audio source. Use Capture.AudioSources to
		///  list available sources. Set to null to disable all
		///  sources (mute).
		///
		public Source AudioSource
		{
			get { return( AudioSources.CurrentSource ); }
			set { AudioSources.CurrentSource = value; }
		}

		///
		///  Collection of available video sources/physical connectors
		///  on the current video device.
		///
		/// <remarks>
		///  In most cases, if the device has only one source,
		///  this collection will be empty.
		///
		/// <para>
		///  The information contained in this property is retrieved and
		///  cached the first time this property is accessed. Future
		///  calls to this property use the cached results. This was done
		///  for performance. </para>
		///
		/// <para>
		///  However, this means you may get different results depending
		///  on when you access this property first. If you are experiencing
		///  problems, try accessing the property immediately after creating
		///  the Capture class or immediately after setting the video and
		///  audio compressors. Also, inform the developer. </para>
		/// </remarks>
		public SourceCollection VideoSources
		{
			get
			{
				if ( videoSources == null )
				{
					try
					{
						if ( videoDevice != null )
							videoSources = new SourceCollection( captureGraphBuilder, videoDeviceFilter, true );
						else
							videoSources = new SourceCollection();
					}
					catch ( Exception ex ) { Debug.WriteLine( "VideoSources: unable to create VideoSources." + ex.ToString() ); }
				}
				return ( videoSources );
			}
		}

		///
		///  Collection of available audio sources/physical connectors
		///  on the current audio device.
		///
		/// <remarks>
		///  In most cases, if the device has only one source,
		///  this collection will be empty. For audio
		///  there are 2 different methods for enumerating audio sources
		///  an audio crossbar (usually TV tuners?) or an audio mixer
		///  (usually sound cards?). This class will first look for an
		///  audio crossbar. If no sources or only one source is available
		///  on the crossbar, this class will then look for an audio mixer.
		///  This class does not support both methods.
		///
		/// <para>
		///  The information contained in this property is retrieved and
		///  cached the first time this property is accessed. Future
		///  calls to this property use the cached results. This was done
		///  for performance. </para>
		///
		/// <para>
		///  However, this means you may get different results depending
		///  on when you access this property first. If you are experiencing
		///  problems, try accessing the property immediately after creating
		///  the Capture class or immediately after setting the video and
		///  audio compressors. Also, inform the developer. </para>
		///  </remarks>
		public SourceCollection AudioSources
		{
			get
			{
				if ( audioSources == null )
				{
					try
					{
						if ( audioDevice != null )
							audioSources = new SourceCollection( captureGraphBuilder, audioDeviceFilter, false );
						else
							audioSources = new SourceCollection();
					}
					catch ( Exception ex ) { Debug.WriteLine( "AudioSources: unable to create AudioSources." + ex.ToString() ); }
				}
				return ( audioSources );
			}
		}

		///
		///  Available property pages.
		///
		/// <remarks>
		///  These are property pages exposed by the DirectShow filters.
		///  These property pages allow users modify settings on the
		///  filters directly.
		///
		/// <para>
		///  The information contained in this property is retrieved and
		///  cached the first time this property is accessed. Future
		///  calls to this property use the cached results. This was done
		///  for performance. </para>
		///
		/// <para>
		///  However, this means you may get different results depending
		///  on when you access this property first. If you are experiencing
		///  problems, try accessing the property immediately after creating
		///  the Capture class or immediately after setting the video and
		///  audio compressors. Also, inform the developer. </para>
		/// </remarks>
		public PropertyPageCollection PropertyPages
		{
			get
			{
				if ( propertyPages == null )
				{
					try
					{
						propertyPages = new PropertyPageCollection(
							captureGraphBuilder,
							videoDeviceFilter, audioDeviceFilter,
							videoCompressorFilter, audioCompressorFilter,
							VideoSources, AudioSources );
					}
					catch ( Exception ex ) { Debug.WriteLine( "PropertyPages: unable to get property pages." + ex.ToString() ); }

				}
				return( propertyPages );
			}
		}

		///
		///  The TV Tuner or null if the current video device
		///  does not have a TV Tuner.
		///
		public Tuner Tuner { get { return( tuner ); } }

		///
		///  Gets and sets the frame rate used to capture video.
		///
		/// <remarks>
		///  Common frame rates: 24 fps for film, 25 for PAL, 29.997
		///  for NTSC. Not all NTSC capture cards can capture at
		///  exactly 29.997 fps. Not all frame rates are supported.
		///  When changing the frame rate, the closest supported
		///  frame rate will be used.
		///
		/// <para>
		///  Not all devices support getting/setting this property.
		///  If this property is not supported, accessing it will
		///  throw and exception. </para>
		///
		/// <para>
		///  This property cannot be changed while capturing. Changing
		///  this property while preview is enabled will cause some
		///  fickering while the internal filter graph is partially
		///  rebuilt. Changing this property while cued will cancel the
		///  cue. Call Cue() again to re-cue the capture. </para>
		/// </remarks>
		public double FrameRate
		{
			get
			{
				long avgTimePerFrame = (long) getStreamConfigSetting( videoStreamConfig, "AvgTimePerFrame" );
				return( (double) 10000000 / avgTimePerFrame );
			}
			set
			{
				long avgTimePerFrame = (long) ( 10000000 / value );
				setStreamConfigSetting( videoStreamConfig, "AvgTimePerFrame", avgTimePerFrame );
			}
		}

		///
		///  Gets and sets the frame size used to capture video.
		///
		/// <remarks>
		///  To change the frame size, assign a new Size object
		///  to this property capture.Size = new Size( w, h );
		///  rather than modifying the size in place
		///  (capture.Size.Width = w;). Not all frame
		///  rates are supported.
		///
		/// <para>
		///  Not all devices support getting/setting this property.
		///  If this property is not supported, accessing it will
		///  throw and exception. </para>
		///
		/// <para>
		///  This property cannot be changed while capturing. Changing
		///  this property while preview is enabled will cause some
		///  fickering while the internal filter graph is partially
		///  rebuilt. Changing this property while cued will cancel the
		///  cue. Call Cue() again to re-cue the capture. </para>
		/// </remarks>
		public Size FrameSize
		{
			get
			{
				BitmapInfoHeader bmiHeader;
				bmiHeader = (BitmapInfoHeader) getStreamConfigSetting( videoStreamConfig, "BmiHeader" );
				Size size = new Size( bmiHeader.Width, bmiHeader.Height );
				return( size );
			}
			set
			{
				BitmapInfoHeader bmiHeader;
				bmiHeader = (BitmapInfoHeader) getStreamConfigSetting( videoStreamConfig, "BmiHeader" );
				bmiHeader.Width = value.Width;
				bmiHeader.Height = value.Height;
				setStreamConfigSetting( videoStreamConfig, "BmiHeader", bmiHeader );
			}
		}

		///
		///  Get or set the number of channels in the waveform-audio data.
		///
		/// <remarks>
		///  Monaural data uses one channel and stereo data uses two channels.
		///
		/// <para>
		///  Not all devices support getting/setting this property.
		///  If this property is not supported, accessing it will
		///  throw and exception. </para>
		///
		/// <para>
		///  This property cannot be changed while capturing. Changing
		///  this property while preview is enabled will cause some
		///  fickering while the internal filter graph is partially
		///  rebuilt. Changing this property while cued will cancel the
		///  cue. Call Cue() again to re-cue the capture. </para>
		/// </remarks>
		public short AudioChannels
		{
			get
			{
				short audioChannels = (short) getStreamConfigSetting( audioStreamConfig, "nChannels" );
				return( audioChannels );
			}
			set
			{
				setStreamConfigSetting( audioStreamConfig, "nChannels", value );
			}
		}

		///
		///  Get or set the number of audio samples taken per second.
		///
		/// <remarks>
		///  Common sampling rates are 8.0 kHz, 11.025 kHz, 22.05 kHz, and
		///  44.1 kHz. Not all sampling rates are supported.
		///
		/// <para>
		///  Not all devices support getting/setting this property.
		///  If this property is not supported, accessing it will
		///  throw and exception. </para>
		///
		/// <para>
		///  This property cannot be changed while capturing. Changing
		///  this property while preview is enabled will cause some
		///  fickering while the internal filter graph is partially
		///  rebuilt. Changing this property while cued will cancel the
		///  cue. Call Cue() again to re-cue the capture. </para>
		/// </remarks>
		public int AudioSamplingRate
		{
			get
			{
				int samplingRate = (int) getStreamConfigSetting( audioStreamConfig, "nSamplesPerSec" );
				return( samplingRate );
			}
			set
			{
				setStreamConfigSetting( audioStreamConfig, "nSamplesPerSec", value );
			}
		}

		///
		///  Get or set the number of bits recorded per sample.
		///
		/// <remarks>
		///  Common sample sizes are 8 bit and 16 bit. Not all
		///  samples sizes are supported.
		///
		/// <para>
		///  Not all devices support getting/setting this property.
		///  If this property is not supported, accessing it will
		///  throw and exception. </para>
		///
		/// <para>
		///  This property cannot be changed while capturing. Changing
		///  this property while preview is enabled will cause some
		///  fickering while the internal filter graph is partially
		///  rebuilt. Changing this property while cued will cancel the
		///  cue. Call Cue() again to re-cue the capture. </para>
		/// </remarks>
		public short AudioSampleSize
		{
			get
			{
				short sampleSize = (short) getStreamConfigSetting( audioStreamConfig, "wBitsPerSample" );
				return( sampleSize );
			}
			set
			{
				setStreamConfigSetting( audioStreamConfig, "wBitsPerSample", value );
			}
		}
		///
		/// Necesario para poder hacer una identificación cuando se lance el evento CaptureFrameCompleted
		///
		/*public int Id
		{
			get
			{
				return id;
			}
			set
			{
				id = value;
			}
		}*/

		// --------------------- Events ----------------------

		///  Fired when a capture is completed (manually or automatically).
		public event EventHandler CaptureComplete;
		///  Fired when a frame was captured.
		public delegate void FrameCapHandler(System.Windows.Forms.PictureBox Frame);
		//public static event FrameCapHandler FrameCaptureComplete;
		public event FrameCapHandler FrameCaptureComplete;

		// ------------- Protected/private Properties --------------

		protected GraphState		graphState = GraphState.Null;		// State of the internal filter graph
		protected bool				isPreviewRendered = false;			// When graphState==Rendered, have we rendered the preview stream?
		protected bool				isCaptureRendered = false;			// When graphState==Rendered, have we rendered the capture stream?
		protected bool				wantPreviewRendered = false;		// Do we need the preview stream rendered (VideoDevice and PreviewWindow != null)
		protected bool				wantCaptureRendered = false;		// Do we need the capture stream rendered
		protected bool				wantCaptureFrame = false;

		protected int				rotCookie = 0;						// Cookie into the Running Object Table
		protected Filter			videoDevice = null;					// Property Backer: Video capture device filter
		protected Filter			audioDevice = null;					// Property Backer: Audio capture device filter
		protected Filter			videoCompressor = null;				// Property Backer: Video compression filter
		protected Filter			audioCompressor = null;				// Property Backer: Audio compression filter
		protected string			filename = "";						// Property Backer: Name of file to capture to
		protected Control			previewWindow = null;				// Property Backer: Owner control for preview
		protected VideoCapabilities	videoCaps = null;					// Property Backer: capabilities of video device
		protected AudioCapabilities	audioCaps = null;					// Property Backer: capabilities of audio device
		protected SourceCollection	videoSources = null;				// Property Backer: list of physical video sources
		protected SourceCollection	audioSources = null;				// Property Backer: list of physical audio sources
		protected PropertyPageCollection propertyPages = null;			// Property Backer: list of property pages exposed by filters
		protected Tuner				tuner = null;						// Property Backer: TV Tuner

		protected IGraphBuilder		graphBuilder;						// DShow Filter: Graph builder
		protected IMediaControl		mediaControl;						// DShow Filter: Start/Stop the filter graph -> copy of graphBuilder
		protected IVideoWindow		videoWindow;						// DShow Filter: Control preview window -> copy of graphBuilder
		protected ICaptureGraphBuilder2		captureGraphBuilder = null;	// DShow Filter: building graphs for capturing video
		protected ISampleGrabber	sampGrabber = null;
		protected IAMStreamConfig	videoStreamConfig = null;			// DShow Filter: configure frame rate, size
		protected IAMStreamConfig	audioStreamConfig = null;			// DShow Filter: configure sample rate, sample size
		protected IBaseFilter		videoDeviceFilter = null;			// DShow Filter: selected video device
		protected IBaseFilter		videoCompressorFilter = null;		// DShow Filter: selected video compressor
		protected IBaseFilter		audioDeviceFilter = null;			// DShow Filter: selected audio device
		protected IBaseFilter		audioCompressorFilter = null;		// DShow Filter: selected audio compressor
		protected IBaseFilter		muxFilter = null;					// DShow Filter: multiplexor (combine video and audio streams)
		protected IBaseFilter		baseGrabFlt = null;
		protected IFileSinkFilter	fileWriterFilter = null;			// DShow Filter: file writer

		protected VideoInfoHeader	videoInfoHeader;
		protected byte[]			savedArray;
		protected bool				capturedFrame = false;
		protected int				bufferedSize;
		protected bool				captured = true;
		protected bool				firstFrame = true;
		protected bool				renderStream = false;
		//protected int				id;									//Almacena un número que identifica a la cámara

		//protected CTee				InfTee;

		///  event when callback has finished (ISampleGrabberCB.BufferCB).
		private delegate void CaptureDone();

		private const int WM_GRAPHNOTIFY	= 0x00008001;	// message from graph
		private IMediaEventEx		mediaEvt;				// event interface

		protected System.Windows.Forms.PictureBox ImageCaptured;

		// ------------- Constructors/Destructors --------------

		///
		///  Create a new Capture object.
		///  videoDevice and audioDevice can be null if you do not
		///  wish to capture both audio and video. However at least
		///  one must be a valid device. Use the <see cref="Filters"/>
		///  class to list available devices.
		///

		public Capture(Filter videoDevice, Filter audioDevice)
		{
			if ( videoDevice == null && audioDevice == null)
				throw new ArgumentException( "The videoDevice and/or the audioDevice parameter must be set to a valid Filter.\n" );
			this.videoDevice = videoDevice;
			this.audioDevice = audioDevice;
			this.Filename = getTempFilename();
			this.ImageCaptured = new System.Windows.Forms.PictureBox();
			createGraph();
		}

		///  Destructor. Dispose of resources.
		~Capture()
		{
			Dispose();
		}

		// --------------------- Public Methods -----------------------

		///
		///  Prepare for capturing. Use this method when capturing
		///  must begin as quickly as possible.
		///
		/// <remarks>
		///  This will create/overwrite a zero byte file with
		///  the name set in the Filename property.
		///
		/// <para>
		///  This will disable preview. Preview will resume
		///  once capture begins. This problem can be fixed
		///  if someone is willing to make the change. </para>
		///
		/// <para>
		///  This method is optional. If Cue() is not called,
		///  Start() will call it before capturing. This method
		///  cannot be called while capturing. </para>
		/// </remarks>
		public void Cue()
		{
			assertStopped();

			// We want the capture stream rendered
			wantCaptureRendered = true;

			// Re-render the graph (if necessary)
			renderGraph();

			// Pause the graph
			int hr = mediaControl.Pause();
			if ( hr != 0 ) Marshal.ThrowExceptionForHR( hr );
		}

		///  Begin capturing.
		public void Start()
		{
			Stop();

			// Para que cuando estemos capturando un video podamos capturar frames
			firstFrame = false;

			assertStopped();

			// We want the capture stream rendered
			wantCaptureRendered = true;

			// Re-render the graph (if necessary)
			renderStream = true;
			renderGraph();

			// Start the filter graph: begin capturing
			int hr = mediaControl.Run();
			if ( hr != 0 ) Marshal.ThrowExceptionForHR( hr );

			// Update the state
			graphState = GraphState.Capturing;
		}

		///
		///  Stop the current capture capture. If there is no
		///  current capture, this method will succeed.
		///
		public void Stop()
		{
			// Stop the graph if it is running
			// If we have a preview running we should only stop the
			// capture stream. However, if we have a preview stream
			// we need to re-render the graph anyways because we
			// need to get rid of the capture stream. To re-render
			// we need to stop the entire graph
			if ( mediaControl != null )
			{
				mediaControl.Stop();
			}

			// Config is true when the parametres of the device are to be changed

			wantCaptureRendered = false;
			wantPreviewRendered = true;

			// Update the state
			if ( graphState == GraphState.Capturing )
			{
				graphState = GraphState.Rendered;
				if ( CaptureComplete != null )
					CaptureComplete( this, null );
			}

			// Para que cuando volvamos a capturar frames no haya problemas
			firstFrame = true;

			// So we destroy the capture stream IF
			// we need a preview stream. If we don't
			// this will leave the graph as it is.
			renderStream = false;
			try { renderGraph(); }
			catch {}
			try { startPreviewIfNeeded(); }
			catch {}

		}

		///
		///  Calls Stop, releases all references. If a capture is in progress
		///  it will be stopped, but the CaptureComplete event will NOT fire.
		///
		public void DisposeCapture()
		{
			wantPreviewRendered = false;
			wantCaptureRendered = false;
			CaptureComplete = null;

			try { destroyGraph(); }
			catch {}

			if ( videoSources != null )
				videoSources.Dispose(); videoSources = null;
			if ( audioSources != null )
				audioSources.Dispose(); audioSources = null;

		}

		[STAThread]
		public void CaptureFrame()
		{
			int hr;

			if(firstFrame)
			{
				assertStopped();

				// Re-render the graph (if necessary)
				renderStream = true;
				renderGraph();

				// Start the filter graph: begin capturing
				hr = mediaControl.Run();
				if ( hr != 0 ) Marshal.ThrowExceptionForHR( hr );

				firstFrame = false;
			}

			captured = false;

			if(savedArray == null )
			{
				int size = videoInfoHeader.BmiHeader.ImageSize;
				if( (size<1000) || (size > 16000000) )
					return;
				savedArray = new byte[ size + 64000];

			}

			hr = sampGrabber.SetCallback( this, 1 );

		}

		///
		/// Esta función har?un preview de la cámara, ser?utilizada cuando se pare la grabación de un video
		/// o para poder configurar la cámara.
		///
		public void RenderPreview()
		{
			int hr;

			assertStopped();

			// Re-render the graph (if necessary)
			renderStream = true;
			renderGraph();

			// Start the filter graph: begin capturing
			hr = mediaControl.Run();
			if ( hr != 0 ) Marshal.ThrowExceptionForHR( hr );

		}

		// --------------------- Private Methods -----------------------

		///
		///  Create a new filter graph and add filters (devices, compressors,
		///  misc), but leave the filters unconnected. Call renderGraph()
		///  to connect the filters.
		///
		protected void createGraph()
		{
			Guid					cat;
			Guid					med;
			int						hr;
			Type comType = null;
			object comObj = null;

			// Ensure required properties are set
			if ( videoDevice == null && audioDevice == null )
				throw new ArgumentException( "The video and/or audio device have not been set. Please set one or both to valid capture devices.\n" );

			// Skip if we are already created
			if ( (int)graphState < (int)GraphState.Created )
			{
				// Garbage collect, ensure that previous filters are released
				GC.Collect();

				// Make a new filter graph
				graphBuilder = (IGraphBuilder) Activator.CreateInstance( Type.GetTypeFromCLSID( Clsid.FilterGraph, true ) );

				// Get the Capture Graph Builder
				Guid clsid = Clsid.CaptureGraphBuilder2;
				Guid riid = typeof(ICaptureGraphBuilder2).GUID;
				captureGraphBuilder = (ICaptureGraphBuilder2) DsBugWO.CreateDsInstance( ref clsid, ref riid );

				// sampGrabber, ISampleGrabber to capture frames
				comType=Type.GetTypeFromCLSID( Clsid.SampleGrabber, true );
				if(comType==null)
					throw new NotImplementedException (@"DirectShow SampleGrabber not installed/registered");
				comObj=Activator.CreateInstance( comType );
				sampGrabber = (ISampleGrabber) comObj; comObj = null;

				// Link the CaptureGraphBuilder to the filter graph
				hr = captureGraphBuilder.SetFiltergraph( graphBuilder );
				if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );

				// Begin set up of SampGrabber	<<<<<<----------------------------------------------------
				AMMediaType media = new AMMediaType();
				media.majorType= MediaType.Video;
				media.subType = MediaSubType.RGB24;
				media.formatType = FormatType.VideoInfo;
				hr = sampGrabber.SetMediaType( media );
				if( hr<0 ) Marshal.ThrowExceptionForHR( hr );
				// Finish set up of SampGrabber	<<<<<<----------------------------------------------------

				// Add the graph to the Running Object Table so it can be
				// viewed with GraphEdit
				#if DEBUG
				DsROT.AddGraphToRot( graphBuilder, out rotCookie );
				#endif

				// Get the video device and add it to the filter graph
				if ( VideoDevice != null )
				{
					videoDeviceFilter = (IBaseFilter) Marshal.BindToMoniker( VideoDevice.MonikerString );
					hr = graphBuilder.AddFilter( videoDeviceFilter, "Video Capture Device" );
					if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );

					// Add SampGrabber Filter	<<<<<<----------------------------------------------------
					mediaEvt	= (IMediaEventEx)	graphBuilder;
					baseGrabFlt = (IBaseFilter) sampGrabber;
					hr = graphBuilder.AddFilter( baseGrabFlt, "DS.NET Grabber" );
					if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );

				}

				// Get the audio device and add it to the filter graph
				if ( AudioDevice != null )
				{
					audioDeviceFilter = (IBaseFilter) Marshal.BindToMoniker( AudioDevice.MonikerString );
					hr = graphBuilder.AddFilter( audioDeviceFilter, "Audio Capture Device" );
					if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );
				}

				// Get the video compressor and add it to the filter graph
				if ( VideoCompressor != null )
				{
					videoCompressorFilter = (IBaseFilter) Marshal.BindToMoniker( VideoCompressor.MonikerString );
					hr = graphBuilder.AddFilter( videoCompressorFilter, "Video Compressor" );
					if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );
				}

				// Get the audio compressor and add it to the filter graph
				if ( AudioCompressor != null )
				{
					audioCompressorFilter = (IBaseFilter) Marshal.BindToMoniker( AudioCompressor.MonikerString );
					hr = graphBuilder.AddFilter( audioCompressorFilter, "Audio Compressor" );
					if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );
				}

				// Retrieve the stream control interface for the video device
				// FindInterface will also add any required filters
				// (WDM devices in particular may need additional
				// upstream filters to function).

				// Try looking for an interleaved media type
				object o;
				cat = PinCategory.Capture;
				med = MediaType.Interleaved;
				Guid iid = typeof(IAMStreamConfig).GUID;
				hr = captureGraphBuilder.FindInterface(
					ref cat, ref med, videoDeviceFilter, ref iid, out o );

				if ( hr != 0 )
				{
					// If not found, try looking for a video media type
					med = MediaType.Video;
					hr = captureGraphBuilder.FindInterface(
						ref cat, ref med, videoDeviceFilter, ref iid, out o );

					if ( hr != 0 )
						o = null;
				}
				videoStreamConfig = o as IAMStreamConfig;

				// Retrieve the stream control interface for the audio device
				o = null;
				cat = PinCategory.Capture;
				med = MediaType.Audio ;
				iid = typeof(IAMStreamConfig).GUID;
				hr = captureGraphBuilder.FindInterface(
					ref cat, ref med, audioDeviceFilter, ref iid, out o );
				if ( hr != 0 )
					o = null;
				audioStreamConfig = o as IAMStreamConfig;

				// Retreive the media control interface (for starting/stopping graph)
				mediaControl = (IMediaControl) graphBuilder;

				// Reload any video crossbars
				if ( videoSources != null ) videoSources.Dispose(); videoSources = null;

				// Reload any audio crossbars
				if ( audioSources != null ) audioSources.Dispose(); audioSources = null;

				// Reload any property pages exposed by filters
				if ( propertyPages != null ) propertyPages.Dispose(); propertyPages = null;

				// Reload capabilities of video device
				videoCaps = null;

				// Reload capabilities of video device
				audioCaps = null;

				// Retrieve TV Tuner if available
				o = null;
				cat = PinCategory.Capture;
				med = MediaType.Interleaved;
				iid = typeof(IAMTVTuner).GUID;
				hr = captureGraphBuilder.FindInterface(
					ref cat, ref med, videoDeviceFilter, ref iid, out o );
				if ( hr != 0 )
				{
					med = MediaType.Video ;
					hr = captureGraphBuilder.FindInterface(
						ref cat, ref med, videoDeviceFilter, ref iid, out o );
					if ( hr != 0 )
						o = null;
				}
				IAMTVTuner t = o as IAMTVTuner;
				if ( t != null )
					tuner = new Tuner( t );

				/*
							// ----------- VMR 9 -------------------
							//## check out samples\inc\vmrutil.h :: RenderFileToVMR9

							IBaseFilter vmr = null;
							if ( ( VideoDevice != null ) && ( previewWindow != null ) )
							{
								vmr = (IBaseFilter) Activator.CreateInstance( Type.GetTypeFromCLSID( Clsid.VideoMixingRenderer9, true ) );
								hr = graphBuilder.AddFilter( vmr, "VMR" );
								if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );

								IVMRFilterConfig9 vmrFilterConfig = (IVMRFilterConfig9) vmr;
								hr = vmrFilterConfig.SetRenderingMode( VMRMode9.Windowless );
								if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );

								IVMRWindowlessControl9 vmrWindowsless = (IVMRWindowlessControl9) vmr;
								hr = vmrWindowsless.SetVideoClippingWindow( previewWindow.Handle );
								if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );
							}
							//-------------------------------------------

							// ---------- SmartTee ---------------------

							IBaseFilter smartTeeFilter = (IBaseFilter) Activator.CreateInstance( Type.GetTypeFromCLSID( Clsid.SmartTee, true ) );
							hr = graphBuilder.AddFilter( smartTeeFilter, "Video Smart Tee" );
							if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );

							// Video -> SmartTee
							cat = PinCategory.Capture;
							med = MediaType.Video;
							hr = captureGraphBuilder.RenderStream( ref cat, ref med, videoDeviceFilter, null, smartTeeFilter );
							if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );

							// smarttee -> mux
							cat = PinCategory.Capture;
							med = MediaType.Video;
							hr = captureGraphBuilder.RenderStream( ref cat, ref med, smartTeeFilter, null, muxFilter );
							if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );

							// smarttee -> vmr
							cat = PinCategory.Preview;
							med = MediaType.Video;
							hr = captureGraphBuilder.RenderStream( ref cat, ref med, smartTeeFilter, null, vmr );
							if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );

							// -------------------------------------
				*/

				// Update the state now that we are done
				graphState = GraphState.Created;
			}
		}

		///
		///  Connects the filters of a previously created graph
		///  (created by createGraph()). Once rendered the graph
		///  is ready to be used. This method may also destroy
		///  streams if we have streams we no longer want.
		///
		protected void renderGraph()
		{
			Guid					cat;
			Guid					med;
			int						hr;
			bool					didSomething = false;
			const int WS_CHILD			= 0x40000000;
			const int WS_CLIPCHILDREN	= 0x02000000;
			const int WS_CLIPSIBLINGS	= 0x04000000;

			assertStopped();

			// Ensure required properties set
			if ( filename == null )
				throw new ArgumentException( "The Filename property has not been set to a file.\n" );

			// Stop the graph
			if ( mediaControl != null )
				mediaControl.Stop();

			// Create the graph if needed (group should already be created)
			createGraph();

			// Derender the graph if we have a capture or preview stream
			// that we no longer want. We can't derender the capture and
			// preview streams seperately.
			// Notice the second case will leave a capture stream intact
			// even if we no longer want it. This allows the user that is
			// not using the preview to Stop() and Start() without
			// rerendering the graph.
			if ( !wantPreviewRendered && isPreviewRendered )
				derenderGraph();
			if ( !wantCaptureRendered && isCaptureRendered )
				if ( wantPreviewRendered )
				{
					derenderGraph();
					graphState = GraphState.Null;
					createGraph();
				}

			// Video Capture
			// ===================================================================================
			if ( wantCaptureRendered && !isCaptureRendered )
			{

				// Render the file writer portion of graph (mux -> file)
				Guid mediaSubType = MediaSubType.Avi;
				hr = captureGraphBuilder.SetOutputFileName( ref mediaSubType, Filename, out muxFilter, out fileWriterFilter );
				if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );

				if ( VideoDevice != null )
				{
					// Try interleaved first, because if the device supports it,
					// it's the only way to get audio as well as video
					cat = PinCategory.Capture;
					med = MediaType.Interleaved;
					hr = captureGraphBuilder.RenderStream( ref cat, ref med, videoDeviceFilter, videoCompressorFilter, muxFilter);
					if( hr < 0 )
					{
						med = MediaType.Video;
						hr = captureGraphBuilder.RenderStream( ref cat, ref med, videoDeviceFilter, videoCompressorFilter, muxFilter);
						if ( hr == -2147220969 ) throw new DeviceInUseException( "Video device", hr );
						if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );
					}
				}
				// Render audio (audio -> mux)
				if ( AudioDevice != null )
				{
					cat = PinCategory.Capture;
					med = MediaType.Audio;
					hr = captureGraphBuilder.RenderStream( ref cat, ref med, audioDeviceFilter, audioCompressorFilter, muxFilter );
					if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );
				}
				isCaptureRendered = true;
				didSomething = true;
			}

			// Render preview stream and launch the baseGrabFlt to capture frames
			// ===================================================================================
			if ( wantPreviewRendered && renderStream && !isPreviewRendered )
			{
				/// Render preview (video.PinPreview -> baseGrabFlt -> renderer)
				/// At this point intelligent connect is used, because my webcams don't have a preview pin and
				/// a capture pin, so Smart Tee filter will be used. I have tested it using GraphEdit.
				/// I can type hr = captureGraphBuilder.RenderStream( ref cat, ref med, videoDeviceFilter, null, baseGrabFlt);
				/// because baseGrabFlt is a transform filter, like videoCompressorFilter.

				cat = PinCategory.Preview;
				med = MediaType.Video;
				hr = captureGraphBuilder.RenderStream( ref cat, ref med, videoDeviceFilter, baseGrabFlt, null );
				if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );

				// Get the IVideoWindow interface
				videoWindow = (IVideoWindow) graphBuilder;

				// Set the video window to be a child of the main window
				hr = videoWindow.put_Owner( previewWindow.Handle );
				if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );

				// Set video window style
				hr = videoWindow.put_WindowStyle( WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
				if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );

				// Position video window in client rect of owner window
				previewWindow.Resize += new EventHandler( onPreviewWindowResize );
				onPreviewWindowResize( this, null );

				// Make the video window visible, now that it is properly positioned
				hr = videoWindow.put_Visible( DsHlp.OATRUE );
				if( hr < 0 ) Marshal.ThrowExceptionForHR( hr );

				hr = mediaEvt.SetNotifyWindow( this.Handle, WM_GRAPHNOTIFY, IntPtr.Zero );
				if( hr < 0 )
					Marshal.ThrowExceptionForHR( hr );

				isPreviewRendered = true;
				didSomething = true;

				// Begin Configuration of SampGrabber	<<<<<<----------------------------------------------------

				AMMediaType media = new AMMediaType();
				hr = sampGrabber.GetConnectedMediaType( media );
				if( hr < 0 )
					Marshal.ThrowExceptionForHR( hr );
				if( (media.formatType != FormatType.VideoInfo) || (media.formatPtr == IntPtr.Zero) )
					throw new NotSupportedException( "Unknown Grabber Media Format" );

				videoInfoHeader = (VideoInfoHeader) Marshal.PtrToStructure( media.formatPtr, typeof(VideoInfoHeader) );
				Marshal.FreeCoTaskMem( media.formatPtr ); media.formatPtr = IntPtr.Zero;

				hr = sampGrabber.SetBufferSamples( false );
				if( hr == 0 )
					hr = sampGrabber.SetOneShot( false );
				if( hr == 0 )
					hr = sampGrabber.SetCallback( null, 0 );
				if( hr < 0 )
					Marshal.ThrowExceptionForHR( hr );

				// Finish Configuration of SampGrabber	<<<<<<----------------------------------------------------
			}

			if ( didSomething )
				graphState = GraphState.Rendered;
		}

		///
		///  Setup and start the preview window if the user has
		///  requested it (by setting PreviewWindow).
		///
		protected void startPreviewIfNeeded()
		{
			// Render preview
			if ( wantPreviewRendered && isPreviewRendered && !isCaptureRendered )
			{
				// Run the graph (ignore errors)
				// We can run the entire graph becuase the capture
				// stream should not be rendered (and that is enforced
				// in the if statement above)
				mediaControl.Run();
			}
		}

		///
		///  Disconnect and remove all filters except the device
		///  and compressor filters. This is the opposite of
		///  renderGraph(). Soem properties such as FrameRate
		///  can only be set when the device output pins are not
		///  connected.
		///
		protected void derenderGraph()
		{
			// Stop the graph if it is running (ignore errors)
			if ( mediaControl != null )
				mediaControl.Stop();

			// Free the preview window (ignore errors)
			if ( videoWindow != null )
			{
				videoWindow.put_Visible( DsHlp.OAFALSE );
				videoWindow.put_Owner( IntPtr.Zero );
				videoWindow = null;
			}

			//ELIMINAR RECURSOS CAPTURA <<<<<<<<<================================================
			/*if ( mediaEvt != null )
			{
				int hr = mediaEvt.SetNotifyWindow( IntPtr.Zero, WM_GRAPHNOTIFY, IntPtr.Zero );
				mediaEvt = null;
			}*/

			// Remove the Resize event handler
			if ( PreviewWindow != null )
				previewWindow.Resize -= new EventHandler( onPreviewWindowResize );

			if ( (int)graphState >= (int)GraphState.Rendered )
			{
				// Update the state
				graphState = GraphState.Created;
				isCaptureRendered = false;
				isPreviewRendered = false;

				// Disconnect all filters downstream of the
				// video and audio devices. If we have a compressor
				// then disconnect it, but don't remove it
				if ( videoDeviceFilter != null )
					removeDownstream( videoDeviceFilter, (videoCompressor==null) );
				if ( audioDeviceFilter != null )
					removeDownstream( audioDeviceFilter, (audioCompressor==null) );

				// These filters should have been removed by the
				// calls above. (Is there anyway to check?)
				muxFilter = null;
				fileWriterFilter = null;

				//ELIMINAR RECURSOS CAPTURA <<<<<<<<<================================================
				baseGrabFlt = null;
			}
		}

		///
		///  Removes all filters downstream from a filter from the graph.
		///  This is called only by derenderGraph() to remove everything
		///  from the graph except the devices and compressors. The parameter
		///  "removeFirstFilter" is used to keep a compressor (that should
		///  be immediately downstream of the device) if one is begin used.
		///
		protected void removeDownstream( IBaseFilter filter, bool removeFirstFilter )
		{
			// Get a pin enumerator off the filter
			IEnumPins pinEnum;
			int hr = filter.EnumPins( out pinEnum );
			pinEnum.Reset();
			if( (hr == 0) && (pinEnum != null) )
			{
				// Loop through each pin
				IPin[] pins = new IPin[1];
				int f;
				do
				{
					// Get the next pin
					hr = pinEnum.Next( 1, pins, out f );
					if( (hr == 0) && (pins[0] != null) )
					{
						// Get the pin it is connected to
						IPin pinTo = null;
						pins[0].ConnectedTo( out pinTo );
						if ( pinTo != null )
						{
							// Is this an input pin?
							PinInfo info = new PinInfo();
							hr = pinTo.QueryPinInfo( out info );
							if( (hr == 0) && (info.dir == (PinDirection.Input)) )
							{
								// Recurse down this branch
								removeDownstream( info.filter, true );

								// Disconnect
								graphBuilder.Disconnect( pinTo );
								graphBuilder.Disconnect( pins[0] );

								// Remove this filter
								// but don't remove the video or audio compressors
								if ( ( info.filter != videoCompressorFilter ) &&
									( info.filter != audioCompressorFilter ) )
									graphBuilder.RemoveFilter( info.filter );
							}
							Marshal.ReleaseComObject( info.filter );
							Marshal.ReleaseComObject( pinTo );
						}
						Marshal.ReleaseComObject( pins[0] );
					}
				}
				while( hr == 0 );

				Marshal.ReleaseComObject( pinEnum ); pinEnum = null;
			}
		}

		///
		///  Completely tear down a filter graph and
		///  release all associated resources.
		///
		protected void destroyGraph()
		{
			// Derender the graph (This will stop the graph
			// and release preview window. It also destroys
			// half of the graph which is unnecessary but
			// harmless here.) (ignore errors)
			try{ derenderGraph(); }
			catch {}

			// Update the state after derender because it
			// depends on correct status. But we also want to
			// update the state as early as possible in case
			// of error.
			graphState = GraphState.Null;
			isCaptureRendered = false;
			isPreviewRendered = false;

			// Remove graph from the ROT
			if ( rotCookie != 0 )
			{
				DsROT.RemoveGraphFromRot( ref rotCookie );
				rotCookie = 0;
			}

			// Remove filters from the graph
			// This should be unnecessary but the Nvidia WDM
			// video driver cannot be used by this application
			// again unless we remove it. Ideally, we should
			// simply enumerate all the filters in the graph
			// and remove them. (ignore errors)
			if ( muxFilter != null )
				graphBuilder.RemoveFilter( muxFilter );

			//ELIMINAR RECURSOS CAPTURA <<<<<<<<<================================================
			if ( baseGrabFlt != null )
				graphBuilder.RemoveFilter( baseGrabFlt );

			if ( videoCompressorFilter != null )
				graphBuilder.RemoveFilter( videoCompressorFilter  );
			if ( audioCompressorFilter != null )
				graphBuilder.RemoveFilter( audioCompressorFilter  );
			if ( videoDeviceFilter != null )
				graphBuilder.RemoveFilter( videoDeviceFilter );
			if ( audioDeviceFilter != null )
				graphBuilder.RemoveFilter( audioDeviceFilter );

			// Clean up properties
			if ( videoSources != null )
				videoSources.Dispose(); videoSources = null;
			if ( audioSources != null )
				audioSources.Dispose(); audioSources = null;
			if ( propertyPages != null )
				propertyPages.Dispose(); propertyPages = null;
			if ( tuner != null )
				tuner.Dispose(); tuner = null;

			// Cleanup
			if ( graphBuilder != null )
				Marshal.ReleaseComObject( graphBuilder );  graphBuilder = null;
			if ( captureGraphBuilder != null )
				Marshal.ReleaseComObject( captureGraphBuilder ); captureGraphBuilder = null;
			if ( muxFilter != null )
				Marshal.ReleaseComObject( muxFilter ); muxFilter = null;

			//ELIMINAR RECURSOS CAPTURA <<<<<<<<<================================================
			if ( baseGrabFlt != null )
				Marshal.ReleaseComObject( baseGrabFlt ); baseGrabFlt = null;

			if ( fileWriterFilter != null )
				Marshal.ReleaseComObject( fileWriterFilter ); fileWriterFilter = null;
			if ( videoDeviceFilter != null )
				Marshal.ReleaseComObject( videoDeviceFilter ); videoDeviceFilter = null;
			if ( audioDeviceFilter != null )
				Marshal.ReleaseComObject( audioDeviceFilter ); audioDeviceFilter = null;
			if ( videoCompressorFilter != null )
				Marshal.ReleaseComObject( videoCompressorFilter ); videoCompressorFilter = null;
			if ( audioCompressorFilter != null )
				Marshal.ReleaseComObject( audioCompressorFilter ); audioCompressorFilter = null;

			// These are copies of graphBuilder
			mediaControl = null;
			videoWindow = null;

			// For unmanaged objects we haven't released explicitly
			GC.Collect();
		}

		///  Resize the preview when the PreviewWindow is resized
		protected void onPreviewWindowResize(object sender, EventArgs e)
		{
			if ( videoWindow != null )
			{
				// Position video window in client rect of owner window
				Rectangle rc = previewWindow.ClientRectangle;
				videoWindow.SetWindowPosition( 0, 0, rc.Right, rc.Bottom );
			}
		}

		///
		///  Get a valid temporary filename (with path). We aren't using
		///  Path.GetTempFileName() because it creates a 0-byte file
		///
		protected string getTempFilename()
		{
			string s;
			try
			{
				int count = 0;
				int i;
				Random r = new Random();
				string tempPath = Path.GetTempPath();
				do
				{
					i = r.Next();
					s = Path.Combine( tempPath, i.ToString("X") + ".avi" );
					count++;
					if ( count > 100 ) throw new InvalidOperationException( "Unable to find temporary file." );
				} while ( File.Exists( s ) );
			}
			catch { s = "c:\temp.avi"; }
			return( s );
		}

		///
		///  Retrieves the value of one member of the IAMStreamConfig format block.
		///  Helper function for several properties that expose
		///  video/audio settings from IAMStreamConfig.GetFormat().
		///  IAMStreamConfig.GetFormat() returns a AMMediaType struct.
		///  AMMediaType.formatPtr points to a format block structure.
		///  This format block structure may be one of several
		///  types, the type being determined by AMMediaType.formatType.
		///
		protected object getStreamConfigSetting( IAMStreamConfig streamConfig, string fieldName)
		{
			if ( streamConfig == null )
				throw new NotSupportedException();
			assertStopped();
			derenderGraph();

			object returnValue = null;
			IntPtr pmt = IntPtr.Zero;
			AMMediaType mediaType = new AMMediaType();

			try
			{
				// Get the current format info
				int hr = streamConfig.GetFormat( out pmt );
				if ( hr != 0 )
					Marshal.ThrowExceptionForHR( hr );
				Marshal.PtrToStructure( pmt, mediaType );

				// The formatPtr member points to different structures
				// dependingon the formatType
				object formatStruct;
				if ( mediaType.formatType == FormatType.WaveEx )
					formatStruct = new WaveFormatEx();
				else if ( mediaType.formatType == FormatType.VideoInfo )
					formatStruct = new VideoInfoHeader();
				else if ( mediaType.formatType == FormatType.VideoInfo2 )
					formatStruct = new VideoInfoHeader2();
				else
					throw new NotSupportedException( "This device does not support a recognized format block." );

				// Retrieve the nested structure
				Marshal.PtrToStructure( mediaType.formatPtr, formatStruct );

				// Find the required field
				Type structType = formatStruct.GetType();
				FieldInfo fieldInfo = structType.GetField( fieldName );
				if ( fieldInfo == null )
					throw new NotSupportedException( "Unable to find the member '" + fieldName + "' in the format block." );

				// Extract the field's current value
				returnValue = fieldInfo.GetValue( formatStruct );

			}
			finally
			{
				DsUtils.FreeAMMediaType( mediaType );
				Marshal.FreeCoTaskMem( pmt );
			}
			renderStream = false;
			renderGraph();
			startPreviewIfNeeded();

			return( returnValue );
		}

		///
		///  Set the value of one member of the IAMStreamConfig format block.
		///  Helper function for several properties that expose
		///  video/audio settings from IAMStreamConfig.GetFormat().
		///  IAMStreamConfig.GetFormat() returns a AMMediaType struct.
		///  AMMediaType.formatPtr points to a format block structure.
		///  This format block structure may be one of several
		///  types, the type being determined by AMMediaType.formatType.
		///
		protected object setStreamConfigSetting( IAMStreamConfig streamConfig, string fieldName, object newValue)
		{
			if ( streamConfig == null )
				throw new NotSupportedException();
			assertStopped();
			derenderGraph();

			object returnValue = null;
			IntPtr pmt = IntPtr.Zero;
			AMMediaType mediaType = new AMMediaType();

			try
			{
				// Get the current format info
				int hr = streamConfig.GetFormat( out pmt );
				if ( hr != 0 )
					Marshal.ThrowExceptionForHR( hr );
				Marshal.PtrToStructure( pmt, mediaType );

				// The formatPtr member points to different structures
				// dependingon the formatType
				object formatStruct;
				if ( mediaType.formatType == FormatType.WaveEx )
					formatStruct = new WaveFormatEx();
				else if ( mediaType.formatType == FormatType.VideoInfo )
					formatStruct = new VideoInfoHeader();
				else if ( mediaType.formatType == FormatType.VideoInfo2 )
					formatStruct = new VideoInfoHeader2();
				else
					throw new NotSupportedException( "This device does not support a recognized format block." );

				// Retrieve the nested structure
				Marshal.PtrToStructure( mediaType.formatPtr, formatStruct );

				// Find the required field
				Type structType = formatStruct.GetType();
				FieldInfo fieldInfo = structType.GetField( fieldName );
				if ( fieldInfo == null )
					throw new NotSupportedException( "Unable to find the member '" + fieldName + "' in the format block." );

				// Update the value of the field
				fieldInfo.SetValue( formatStruct, newValue );

				// PtrToStructure copies the data so we need to copy it back
				Marshal.StructureToPtr( formatStruct, mediaType.formatPtr, false );

				// Save the changes
				hr = streamConfig.SetFormat( mediaType );
				if ( hr != 0 )
					Marshal.ThrowExceptionForHR( hr );
			}
			finally
			{
				DsUtils.FreeAMMediaType( mediaType );
				Marshal.FreeCoTaskMem( pmt );
			}
			renderStream = false;
			renderGraph();
			startPreviewIfNeeded();

			return( returnValue );
		}

		///
		///  Assert that the class is in a Stopped state.
		///
		protected void assertStopped()
		{
			if ( !Stopped )
				throw new InvalidOperationException( "This operation not allowed while Capturing. Please Stop the current capture." );
		}

		void OnCaptureDone()
		{
			int hr;
			if( sampGrabber == null )
				return;
			hr = sampGrabber.SetCallback( null, 0 );

			int w = videoInfoHeader.BmiHeader.Width;
			int h = videoInfoHeader.BmiHeader.Height;
			if( ((w & 0x03) != 0) || (w < 32) || (w > 4096) || (h < 32) || (h > 4096) )
				return;
			int stride = w * 3;

			GCHandle handle = GCHandle.Alloc( savedArray, GCHandleType.Pinned );
			int scan0 = (int) handle.AddrOfPinnedObject();
			scan0 += (h - 1) * stride;
			Bitmap b = new Bitmap( w, h, -stride, PixelFormat.Format24bppRgb, (IntPtr) scan0 );
			handle.Free();
			savedArray = null;
			ImageCaptured.Image = b;

			//Launch the event
			if (FrameCaptureComplete != null)
			{
				FrameCaptureComplete(ImageCaptured);
			}
			return;
		}

		protected override void WndProc( ref Message m )
		{
			if( m.Msg == WM_GRAPHNOTIFY )
			{
				if( mediaEvt != null )
					OnGraphNotify();
				return;
			}
			base.WndProc( ref m );
		}

		// graph event (WM_GRAPHNOTIFY) handler
		void OnGraphNotify()
		{
			DsEvCode	code;
			int p1, p2, hr = 0;
			do
			{
				hr = mediaEvt.GetEvent( out code, out p1, out p2, 0 );
				if( hr < 0 )
					break;
				hr = mediaEvt.FreeEventParams( code, p1, p2 );
			}
			while( hr == 0 );
		}

		int ISampleGrabberCB.SampleCB( double SampleTime, IMediaSample pSample )
		{
			return 0;
		}

		int ISampleGrabberCB.BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen )
		{
			if( captured || (savedArray == null) )
			{
				return 0;
			}
			captured = true;
			bufferedSize = BufferLen;
			if( (pBuffer != IntPtr.Zero) && (BufferLen > 1000) && (BufferLen <= savedArray.Length) )
				Marshal.Copy( pBuffer, savedArray, 0, BufferLen );
			try
			{
				this.BeginInvoke( new CaptureDone( this.OnCaptureDone ) );
			}
			catch (ThreadInterruptedException e)
			{
				MessageBox.Show(e.Message);
			}
			catch (Exception we)
			{
				MessageBox.Show(we.Message);
			}
			return 0;
		}
	}
}


text:
class BetterSet {
    constructor(_values) {
        let values = _values;

        if (values instanceof BetterSet) {
            values = values._set;
        }

        this._set = new Set(values);

        Object.defineProperty(this, 'size', {
            get: this.getSize.bind(this)
        });
    }

    /**
     * Adds the specified value to the set.
     *
     * @param {*} value
     *        The value which is to be added.
     *
     * @return {BetterSet}
     *          The set itself.
     */
    add(value) {
        this._set.add(value);
        return this;
    }

    /**
     * Adds all the specified values to the set.
     *
     * @param {Iterator} values
     *        The values which are to be added to the set.
     *
     * @return {BetterSet}
     *          The set itself.
     */
    addAll(values) {
        for (let value of values) {
            this._set.add(value);
        }

        return this;
    }

    /**
     * Removes all the elements from the set.
     *
     * @return {BetterSet}
     *         The set itself.
     */
    clear() {
        this._set.clear();
        return this;
    }

    /**
     * Deletes the sepcified value from the set.
     *
     * @param {*} value
     *        The value which is to be deleted.
     *
     * @return {BetterSet}
     *         Returns true if an element in the Set object has been removed
     *         successfully; otherwise false.
     */
    delete(value) {
        return this._set.delete(value);
    }

    /**
     * Gets all the entries in the set as an iterator object.
     *
     * @return {Iterator}
     *         A new Iterator object that contains an array of [value, value]
     *         for each element in the Set object, in insertion order. This is
     *         kept similar to the Map object, so that each entry has the same
     *         value for its key and value here.
     */
    entries() {
        return this._set.entries();
    }

    /**
     * Calls callbackFn once for each value present in the Set object, in
     * insertion order. If a thisArg parameter is provided to forEach, it will
     * be used as the this value for each callback.
     *
     * @param {Function} callbackFn
     *        Function to execute for each element.
     * @param {Object} [thisArg]
     *        Value to use as this when executing callbackFn.
     */
    forEach(callbackFn, thisArg) {
        this._set.forEach(callbackFn, thisArg);
    }

    /**
     * Gets the number of values that are in the set.
     *
     * @return {Number}
     */
    getSize() {
        return this._set.size;
    }

    /**
     * Gets whether the set contains an element with the specified value.
     *
     * @param {*} value
     *        The value which is to be checked.
     *
     * @return {Boolean}
     */
    has(value) {
        return this._set.has(value);
    }

    /**
     * Gets all the values in the set as an iterator object.
     *
     * @return {Iterator}
     *         A new Iterator object that contains the values for each element
     *         in the Set object in insertion order.
     */
    values() {
        return this._set.values();
    }

    /**
     * The same function as values().
     *
     * @return {Iterator}
     *         A new Iterator object that contains the values for each element
     *         in the Set object in insertion order.
     */
    keys() {
        return this._set.keys();
    }

    /**
     * Removes all the values contained in the argument from the current set.
     *
     * @param {Set|BetterSet} set
     *        The set whose values are to be removed.
     *
     * @return {BetterSet}
     *         The set itself.
     */
    difference(set) {
        set.forEach((value) => {
            this.delete(value);
        });

        return this;
    }

    /**
     * Removes all the values that are not also in the provided set.
     *
     * @param {BetterSet} set
     *        The set which is to be intersected against the current set.
     *
     * @return {BetterSet}
     *         The set itself.
     */
    intersection(set) {
        this.forEach((value) => {
            if (!set.has(value)) {
                this.delete(value);
            }
        });
        return this;
    }

    static intersection(set1, set2) {
        if (set1.size < set2.size) {
            return new BetterSet(set1).intersection(set2);
        } else {
            return new BetterSet(set2).intersection(set1);
        }
    }

    /**
     * Adds all the values in the argument to the current set.
     *
     * @param {BetterSet} set
     *        The set which is to be united with the current set.
     *
     * @return {BetterSet}
     *         The set itself.
     */
    union(set) {
        set.forEach((value) => {
            this.add(value);
        });
        return this;
    }

    static union(set1, set2) {
        return new BetterSet(set1).union(set2);
    }

    /**
     * Pushes all the values in the set into the specified array.
     *
     * @param {Array} array
     *        The array into which the values are to be pushed.
     *
     * @return {Array}
     *         The array into which the values have been pushed.
     */
    pushTo(array) {
        this.forEach(function (value) {
            array.push(value);
        });
        return array;
    }

    static pushTo(array, set) {
        return set.pushInto(array)
    }

    /**
     * Returns an array which contains the results of passing each value in the
     * set to the specified callback.
     *
     * @param {Function} callback
     *        Function to execute for each element.
     *
     * @return {Array}
     *         An array with the results of each invocation of callback.
     */
    map(callback) {
        let result = [];

        this.forEach(function (value) {
            result.push(callback(value));
        });

        return result;
    }

    static map(set, callback) {
        return set.map(callback);
    }

    /**
     *  Creates a new set which contains all the values of the current set that
     *  pass the specified predicate.
     *
     * @param {Function} predicate
     *        The predicate which is to be applied to all the values of the set.
     *
     * @return {BetterSet}
     */
    filter(predicate) {
        let result = new BetterSet();

        this.forEach(function (value) {
            if (predicate(value)) {
                result.add(value);
            }
        });

        return result;
    }

    static filter(set, predicate) {
        return set.filter(predicate);
    }

    /**
     * Creates a new set which contains all the values of the current set that
     * do not pass the specified predicate.
     *
     * @param {Function} predicate
     *        The predicate which is to be applied to all the values of the set.
     *
     * @return {BetterSet}
     */
    reject(predicate) {
        return this.filter(function (value) {
            return !predicate(value);
        });
    }

    static reject(set, predicate) {
        return set.reject(predicate);
    }

    /**
     * Deletes all the values which do not pass the specified predicate.
     *
     * @param {Function} predicate
     *        The predicate which is to be applied to all the values of the set.
     *
     * @return {BetterSet}
     */
    deleteAll(predicate) {
        this.forEach((value) => {
            if (predicate(value)) {
                this.delete(value);
            }
        });

        return this;
    }

    static deleteAll(set, predicate) {
        return set.deleteAll(predicate);
    }

    /**
     *  Keeps all the values which pass the specified predicate and removes all
     *  the other ones.
     *
     * @param {Function} predicate
     *        The predicate which is to be applied to all the values of the set.
     *
     * @return {BetterSet}
     */
    keepAll(predicate) {
        return this.deleteAll(function (value) {
            return !predicate(value);
        });
    }

    static keepAll(set, predicate) {
        return set.keepAll(predicate);
    }

    /**
     * Gets whether every value in the set passes the specified predicate.
     *
     * @param {Function} predicate
     *        The predicate which is to be applied to all the values of the set.
     *
     * @return {Boolean}
     */
    every(predicate) {
        for (let value of this) {
            if (!predicate(value)) {
                return false;
            }
        }

        return true;
    }

    static every(set, predicate) {
        return set.every(predicate);
    }

    /**
     * Gets whether no in the set passes the specified predicate.
     *
     * @param {Function} predicate
     *        The predicate which is to be applied to all the values of the set.
     *
     * @return {Boolean}
     */
    none(predicate) {
        return this.every(function (value) {
            return !predicate(value);
        });
    }

    static none(set, predicate) {
        return set.none(predicate);
    }

    /**
     * Gets whether any value in the set passes the specified predicate.
     *
     * @param {Function} predicate
     *        The predicate which is to be applied to all the values of the set.
     *
     * @return {Boolean}
     */
    any(predicate) {
        for (let value of this) {
            if (predicate(value)) {
                return true;
            }
        }

        return false;
    }

    static any(set, predicate) {
        return set.any(predicate);
    }

    /**
     * Gets whether all the values in the current set are also in the specified
     * set, that is, get whether the intersection of the two sets corresponds to
     * the set itself.
     *
     * @param {BetterSet} set
     *        et where all the values from the current set are to be found.
     *
     * @return {Boolean}
     */
    areAllIn(set) {
        return this.every(function (value) {
            set.has(value);
        });
    }

    static areAllIn(set1, set2) {
        return set1.areAllIn(set2);
    }

    fromArray(array) {
        this.clear();

        for (let i = 0; i < array.length; ++i) {
            this.add(array[i]);
        }

        return this;
    }

    static fromArray(array) {
        return new BetterSet().fromArray(array);
    }

    /**
     *  Creates a new array which contains all the elements that are in the
     *  current set.
     *
     * @return {Array}
     */
    toArray() {
        let result = [];

        this.forEach(function (value) {
            result.push(value);
        });

        return result;
    }

    static toArray(set) {
        return set.toArray();
    }
};

export default BetterSet;

redpajama_set_name: RedPajamaGithub


text:
module RubyHoldem
  class Round
    class MoveHistoryComputations
      attr_reader :players, :move_history

      def initialize(players, move_history)
        @players = players
        @move_history = move_history
      end

      def highest_bet_placed
        get_current_bet_amount_for_player(
          players_still_in_round.max_by(&:current_bet_amount)
        )
      end

      def ready_for_next_stage?
        every_player_has_checked? && turns_played_in_stage > 0
      end

      def has_winner?
        (stage == 'show_down' || players_still_in_round.count == 1)
      end

      # TODO: Compare the hands of the two players
      def winner
        return players_still_in_round[0] if players_still_in_round.count == 1
      end

      def turns_played_in_stage
        moves.select { |move| move[:stage] == stage }.length
      end

      def player_in_turn  #The player whose turn it is to make a move
        return players[0] if moves.length == 0

        last_player = moves.last[:player]
        i = (players.index(last_player) + 1) % players.length

        player_found = false
        until player_found
          at_end_of_array = (i == (players.length - 1))

          if player_is_folded?(players[i]) && !at_end_of_array
            i += 1
          elsif player_is_folded?(players[i]) && at_end_of_array
            i = 0
          else
            player_found = true
          end
        end
        players[i]
      end

      def every_player_has_checked?
        players_num_checks = players_still_in_round.map do |round_player|
          checks = moves.select do |move|
            move[:stage] == stage &&
              move[:move_type] == 'check' &&
              move[:player] == round_player
          end
          checks.length
        end

        players_num_checks.map { |num_checks| num_checks >= 1 }.all?
      end

      def players_still_in_round
        players.select do |round_player|
          folds = moves.select do |move|
            move[:move_type] == 'fold' && move[:player] == round_player
          end

          folds.length == 0
        end
      end

      private

      def player_is_folded?(player)
        moves.select do |move|
          move[:move_type] == 'fold' && move[:player] == player
        end.any?
      end

      #
      # Dependencies on MoveHistory class
      #
      def moves
        move_history.moves
      end

      def stage
        move_history.stage
      end

      #
      # Dependencies on Player class
      #
      def get_current_bet_amount_for_player(player)
        player.current_bet_amount
      end
    end
  end
end


text:
/***********************************************************************
 * Based on MAGMA library http://icl.cs.utk.edu/magma/
 * Below is the original copyright.
 *
 *   -- MAGMA (version 0.1) --
 *      Univ. of Tennessee, Knoxville
 *      Univ. of California, Berkeley
 *      Univ. of Colorado, Denver
 *      @date
 *
 *      @precisions normal z -> s d c
 *
 * -- Innovative Computing Laboratory
 * -- Electrical Engineering and Computer Science Department
 * -- University of Tennessee
 * -- (C) Copyright 2009-2013
 *
 * Redistribution  and  use  in  source and binary forms, with or without
 * modification,  are  permitted  provided  that the following conditions
 * are met:
 *
 * * Redistributions  of  source  code  must  retain  the above copyright
 *   notice,  this  list  of  conditions  and  the  following  disclaimer.
 * * Redistributions  in  binary  form must reproduce the above copyright
 *   notice,  this list of conditions and the following disclaimer in the
 *   documentation  and/or other materials provided with the distribution.
 * * Neither  the  name of the University of Tennessee, Knoxville nor the
 *   names of its contributors may be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS  SOFTWARE  IS  PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS''  AND  ANY  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL,  EXEMPLARY,  OR  CONSEQUENTIAL  DAMAGES  (INCLUDING,  BUT NOT
 * LIMITED  TO,  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA,  OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY  OF  LIABILITY,  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF  THIS  SOFTWARE,  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 **********************************************************************/

#include "magma.h"
#include "magma_blas.h"
#include "magma_data.h"
#include "magma_cpu_lapack.h"
#include "magma_helper.h"
#include "magma_sync.h"

#include <algorithm>

template<typename Ty>
magma_int_t magma_potrf_gpu(
    magma_uplo_t   uplo, magma_int_t    n,
    cl_mem dA, size_t dA_offset, magma_int_t ldda,
    magma_queue_t queue,
    magma_int_t*   info)
{
/*  -- clMAGMA (version 0.1) --
    Univ. of Tennessee, Knoxville
    Univ. of California, Berkeley
    Univ. of Colorado, Denver
    @date

    Purpose
    =======
    ZPOTRF computes the Cholesky factorization of a complex Hermitian
    positive definite matrix dA.

    The factorization has the form
    dA = U\*\*H * U,  if UPLO = 'U', or
    dA = L  * L\*\*H,  if UPLO = 'L',
    where U is an upper triangular matrix and L is lower triangular.

    This is the block version of the algorithm, calling Level 3 BLAS.

    Arguments
    =========
    UPLO    (input) INTEGER
    = MagmaUpper:  Upper triangle of dA is stored;
    = MagmaLower:  Lower triangle of dA is stored.

    N       (input) INTEGER
    The order of the matrix dA.  N >= 0.

    dA      (input/output) COMPLEX_16 array on the GPU, dimension (LDDA,N)
    On entry, the Hermitian matrix dA.  If UPLO = 'U', the leading
    N-by-N upper triangular part of dA contains the upper
    triangular part of the matrix dA, and the strictly lower
    triangular part of dA is not referenced.  If UPLO = 'L', the
    leading N-by-N lower triangular part of dA contains the lower
    triangular part of the matrix dA, and the strictly upper
    triangular part of dA is not referenced.

    On exit, if INFO = 0, the factor U or L from the Cholesky
    factorization dA = U\*\*H*U or dA = L*L\*\*H.

    LDDA    (input) INTEGER
    The leading dimension of the array dA.  LDDA >= max(1,N).
    To benefit from coalescent memory accesses LDDA must be
    divisible by 16.

    INFO    (output) INTEGER
    = 0:  successful exit
    < 0:  if INFO = -i, the i-th argument had an illegal value
    > 0:  if INFO = i, the leading minor of order i is not
    positive definite, and the factorization could not be
    completed.
    =====================================================================   */

// produces pointer and offset as two args to magmaBLAS routines
#define dA(i,j)  dA, ((dA_offset) + (i) + (j)*ldda)

// produces pointer as single arg to BLAS routines
#define A(i,j)  &A[ (i) + (j)*lda ]

    magma_int_t j, jb, nb;
    static const Ty  z_one = magma_one<Ty>();
    static const Ty mz_one = magma_neg_one<Ty>();
    static const double    one =  1.0;
    static const double  m_one = -1.0;

    static const clblasTranspose transType = magma_is_real<Ty>() ? clblasTrans : clblasConjTrans;

    Ty* work;
    magma_int_t err;

    *info = 0;
    if (uplo != MagmaUpper && uplo != MagmaLower) {
        *info = -1;
    } else if (n < 0) {
        *info = -2;
    } else if (ldda < std::max(1,n)) {
        *info = -4;
    }
    if (*info != 0) {
        //magma_xerbla(__func__, -(*info));
        return *info;
    }

    nb = magma_get_potrf_nb<Ty>(n);

    gpu_blas_gemm_func<Ty> gpu_blas_gemm;
    gpu_blas_trsm_func<Ty> gpu_blas_trsm;
    gpu_blas_herk_func<Ty> gpu_blas_herk;
    cpu_lapack_potrf_func<Ty> cpu_lapack_potrf;

    err = magma_malloc_cpu<Ty>( &work, nb*nb);
    if (err != MAGMA_SUCCESS) {
        *info = MAGMA_ERR_HOST_ALLOC;
        return *info;
    }

    magma_event_t event = NULL;
    cl_event blas_event = NULL;

    if ((nb <= 1) || (nb >= n)) {
        // use unblocked code
        magma_getmatrix<Ty>(n, n, dA, dA_offset, ldda, work, n, queue);

        LAPACKE_CHECK(cpu_lapack_potrf(
                          uplo == MagmaUpper ? *MagmaUpperStr : *MagmaLowerStr,
                          n, work, n));

        magma_setmatrix<Ty>(n, n, work, n, dA, dA_offset, ldda, queue);
    }
    else {
        if (uplo == MagmaUpper) {
            // --------------------
            // compute Cholesky factorization A = U'*U
            // using the left looking algorithm
            for(j = 0; j < n; j += nb) {
                // apply all previous updates to diagonal block
                jb = std::min(nb, n-j);
                if (j > 0) {
                    CLBLAS_CHECK(gpu_blas_herk(
                                     clblasUpper, transType,
                                     jb, j,
                                     m_one,
                                     dA(0,j), ldda,
                                     one,
                                     dA(j,j), ldda,
                                     1, &queue, 0, nullptr, &blas_event));
                }

                // start asynchronous data transfer
                magma_getmatrix_async<Ty>(jb, jb, dA(j,j), ldda, work, jb, queue, &event);

                // apply all previous updates to block row right of diagonal block
                if (j+jb < n) {
                    CLBLAS_CHECK(gpu_blas_gemm(
                                     transType, clblasNoTrans,
                                     jb, n-j-jb, j,
                                     mz_one,
                                     dA(0, j   ), ldda,
                                     dA(0, j+jb), ldda,
                                     z_one,
                                     dA(j, j+jb), ldda,
                                     1, &queue, 0, nullptr, &blas_event));
                }

                // simultaneous with above zgemm, transfer data, factor
                // diagonal block on CPU, and test for positive definiteness
                magma_event_sync(event);
                LAPACKE_CHECK(cpu_lapack_potrf( *MagmaUpperStr, jb, work, jb));

                if (*info != 0) {
                    assert(*info > 0);
                    *info += j;
                    break;
                }

                magma_setmatrix_async<Ty>(jb, jb, work, jb, dA(j,j), ldda, queue, &event);

                // apply diagonal block to block row right of diagonal block
                if (j+jb < n) {
                    magma_event_sync(event);
                    CLBLAS_CHECK(gpu_blas_trsm(
                                     clblasLeft, clblasUpper,
                                     transType, clblasNonUnit,
                                     jb, n-j-jb,
                                     z_one,
                                     dA(j, j   ), ldda,
                                     dA(j, j+jb), ldda,
                                     1, &queue, 0, nullptr, &blas_event));
                }
            }
        }
        else {
            // --------------------
            // compute Cholesky factorization A = L*L'
            // using the left looking algorithm
            for(j = 0; j < n; j += nb) {
                // apply all previous updates to diagonal block
                jb = std::min(nb, n-j);
                if (j>0) {
                    CLBLAS_CHECK(gpu_blas_herk(
                                     clblasLower, clblasNoTrans, jb, j,
                                     m_one,
                                     dA(j, 0), ldda,
                                     one,
                                     dA(j, j), ldda,
                                     1, &queue, 0, nullptr, &blas_event));
                }

                // start asynchronous data transfer
                magma_getmatrix_async<Ty>(jb, jb, dA(j,j), ldda, work, jb, queue, &event);

                // apply all previous updates to block column below diagonal block
                if (j+jb < n) {
                    CLBLAS_CHECK(gpu_blas_gemm(
                                     clblasNoTrans, transType,
                                     n-j-jb, jb, j,
                                     mz_one,
                                     dA(j+jb, 0), ldda,
                                     dA(j,    0), ldda,
                                     z_one,
                                     dA(j+jb, j), ldda,
                                     1, &queue, 0, nullptr, &blas_event));
                }

                // simultaneous with above zgemm, transfer data, factor
                // diagonal block on CPU, and test for positive definiteness
                magma_event_sync(event);
                LAPACKE_CHECK(cpu_lapack_potrf(
                                  *MagmaLowerStr, jb, work, jb));
                if (*info != 0) {
                    assert(*info > 0);
                    *info += j;
                    break;
                }
                magma_setmatrix_async<Ty>(jb, jb, work, jb, dA(j,j), ldda, queue, &event);

                // apply diagonal block to block column below diagonal
                if (j+jb < n) {
                    magma_event_sync(event);
                    CLBLAS_CHECK(gpu_blas_trsm(
                                     clblasRight, clblasLower, transType, clblasNonUnit,
                                     n-j-jb, jb,
                                     z_one,
                                     dA(j   , j), ldda,
                                     dA(j+jb, j), ldda,
                                     1, &queue, 0, nullptr, &blas_event));
                }
            }
        }
    }

    magma_queue_sync(queue);
    magma_free_cpu(work);

    return *info;
}

#define INSTANTIATE(T)                                  \
    template magma_int_t magma_potrf_gpu<T>(            \
        magma_uplo_t   uplo, magma_int_t    n,          \
        cl_mem dA, size_t dA_offset, magma_int_t ldda,  \
        magma_queue_t queue,                            \
        magma_int_t*   info);                           \

INSTANTIATE(float)
INSTANTIATE(double)
INSTANTIATE(magmaFloatComplex)
INSTANTIATE(magmaDoubleComplex)


text: Tests that console does not log deprecated warning messages while dir-dumping objects.


text:
<?php

namespace Elcodi\Bundle\LanguageBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;

use Elcodi\Bundle\CoreBundle\DependencyInjection\Abstracts\AbstractConfiguration;

/**
 * Class Configuration.
 */
class Configuration extends AbstractConfiguration
{
    /**
     * {@inheritdoc}
     */
    protected function setupTree(ArrayNodeDefinition $rootNode)
    {
        $rootNode
            ->children()
                ->arrayNode('mapping')
                    ->addDefaultsIfNotSet()
                    ->children()
                        ->append($this->addMappingNode(
                            'language',
                            'Elcodi\Component\Language\Entity\Language',
                            '@ElcodiLanguageBundle/Resources/config/doctrine/Language.orm.yml',
                            'default',
                            true
                        ))
                    ->end()
                ->end()
            ->end();
    }
}


text:
<?php

namespace Magento\Payment\Model\Method;

class FactoryTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @var \Magento\Framework\ObjectManagerInterface|PHPUnit_Framework_MockObject_MockObject
     */
    protected $_objectManagerMock;

    /**
     * @var \Magento\Payment\Model\Method\Factory
     */
    protected $_factory;

    protected function setUp()
    {
        $objectManagerHelper = new \Magento\TestFramework\Helper\ObjectManager($this);

        $this->_objectManagerMock = $this->getMock('Magento\Framework\ObjectManagerInterface');
        $this->_factory = $objectManagerHelper->getObject(
            'Magento\Payment\Model\Method\Factory',
            ['objectManager' => $this->_objectManagerMock]
        );
    }

    public function testCreateMethod()
    {
        $className = 'Magento\Payment\Model\Method\AbstractMethod';
        $methodMock = $this->getMock($className, [], [], '', false);
        $this->_objectManagerMock->expects(
            $this->once()
        )->method(
            'create'
        )->with(
            $className,
            []
        )->will(
            $this->returnValue($methodMock)
        );

        $this->assertEquals($methodMock, $this->_factory->create($className));
    }

    public function testCreateMethodWithArguments()
    {
        $className = 'Magento\Payment\Model\Method\AbstractMethod';
        $data = ['param1', 'param2'];
        $methodMock = $this->getMock($className, [], [], '', false);
        $this->_objectManagerMock->expects(
            $this->once()
        )->method(
            'create'
        )->with(
            $className,
            $data
        )->will(
            $this->returnValue($methodMock)
        );

        $this->assertEquals($methodMock, $this->_factory->create($className, $data));
    }

    /**
     * @expectedException \Magento\Framework\Model\Exception
     * @expectedExceptionMessage WrongClass class doesn't implement \Magento\Payment\Model\MethodInterface
     */
    public function testWrongTypeException()
    {
        $className = 'WrongClass';
        $methodMock = $this->getMock($className, [], [], '', false);
        $this->_objectManagerMock->expects(
            $this->once()
        )->method(
            'create'
        )->with(
            $className,
            []
        )->will(
            $this->returnValue($methodMock)
        );

        $this->_factory->create($className);
    }
}


text:
import React from 'react';
import ReactDOM from 'react-dom';
import Main from './component/Main';
import TabsSwipeable from './component/TabsSwipeable';
import Buttons from './component/Buttons';
import QuestionBank from './component/QuestionBank';
import { Router, Route, hashHistory, Link } from 'react-router';

import injectTapEventPlugin from 'react-tap-event-plugin';
injectTapEventPlugin();

const App = props => (

React Router Tutorial

<Link to="/bt">Buttons</Link>

<Link to="/ts">TabsSwipeable</Link>

<Link to="/qb">QuestionBank</Link>

);

ReactDOM.render(

      <Router history={hashHistory}>
        <Route path="/" component={App} />
        <Route path="/bt" component={Buttons} />
        <Route path="/ts" component={TabsSwipeable} />
        <Route path="/qb" component={QuestionBank} />
      </Router>

,
  document.getElementById('root'),
);


text:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    app:titleTextAppearance="@style/ToolbarTitleTextStyle"
    app:layout_scrollFlags="scroll|enterAlways"/>


text:
using System.Net.Http;
using Digipost.Signature.Api.Client.Core.Tests.Utilities;

namespace Digipost.Signature.Api.Client.Core.Tests.Fakes
{
    internal class FakeHttpClientForPadesResponse : FakeHttpClientHandlerResponse
    {
        public override HttpContent GetContent()
        {
            return new StreamContent(CoreResponseUtility.GetPades());
        }
    }
}