DirectShowSamples-2007-July\Samples\Capture\DxSnap
Capture.cs
While the underlying libraries are covered by LGPL, this sample is released
as public domain. It is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.
*****************************************************************************/
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Collections;
using System.Runtime.InteropServices;
using System.Threading;
using System.Diagnostics;
using System.Windows.Forms;
using DirectShowLib;
namespace SnapShot
{
/// <summary> Summary description for MainForm. </summary>
internal class Capture : ISampleGrabberCB, IDisposable
{
#region Member variables
/// <summary> graph builder interface. </summary>
private IFilterGraph2 m_FilterGraph = null;
// Used to snap picture on Still pin
private IAMVideoControl m_VidControl = null;
private IPin m_pinStill = null;
/// <summary> so we can wait for the async job to finish </summary>
private ManualResetEvent m_PictureReady = null;
private bool m_WantOne = false;
/// <summary> Dimensions of the image, calculated once in constructor for perf. </summary>
private int m_videoWidth;
private int m_videoHeight;
private int m_stride;
/// <summary> buffer for bitmap data. Always release by caller</summary>
private IntPtr m_ipBuffer = IntPtr.Zero;
#if DEBUG
// Allow you to "Connect to remote graph" from GraphEdit
DsROTEntry m_rot = null;
#endif
#endregion
#region APIs
[DllImport("Kernel32.dll", EntryPoint="RtlMoveMemory")]
private static extern void CopyMemory(IntPtr Destination, IntPtr Source, [MarshalAs(UnmanagedType.U4)] int Length);
#endregion
// Zero based device index and device params and output window
public Capture(int iDeviceNum, int iWidth, int iHeight, short iBPP, Control hControl)
{
DsDevice [] capDevices;
// Get the collection of video devices
capDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);
if (iDeviceNum + 1 > capDevices.Length)
{
throw new Exception("No video capture devices found at that index!");
}
try
{
// Set up the capture graph
SetupGraph( capDevices[iDeviceNum], iWidth, iHeight, iBPP, hControl);
// tell the callback to ignore new images
m_PictureReady = new ManualResetEvent(false);
}
catch
{
Dispose();
throw;
}
}
/// <summary> release everything. </summary>
public void Dispose()
{
#if DEBUG
if (m_rot != null)
{
m_rot.Dispose();
}
#endif
CloseInterfaces();
if (m_PictureReady != null)
{
m_PictureReady.Close();
}
}
// Destructor
~Capture()
{
Dispose();
}
/// <summary>
/// Get the image from the Still pin. The returned image can turned into a bitmap with
/// Bitmap b = new Bitmap(cam.Width, cam.Height, cam.Stride, PixelFormat.Format24bppRgb, m_ip);
/// If the image is upside down, you can fix it with
/// b.RotateFlip(RotateFlipType.RotateNoneFlipY);
/// </summary>
/// <returns>Returned pointer to be freed by caller with Marshal.FreeCoTaskMem</returns>
public IntPtr Click()
{
int hr;
// get ready to wait for new image
m_PictureReady.Reset();
m_ipBuffer = Marshal.AllocCoTaskMem(Math.Abs(m_stride) * m_videoHeight);
try
{
m_WantOne = true;
// If we are using a still pin, ask for a picture
if (m_VidControl != null)
{
// Tell the camera to send an image
hr = m_VidControl.SetMode(m_pinStill, VideoControlFlags.Trigger);
DsError.ThrowExceptionForHR( hr );
}
// Start waiting
if ( ! m_PictureReady.WaitOne(9000, false) )
{
throw new Exception("Timeout waiting to get picture");
}
}
catch
{
Marshal.FreeCoTaskMem(m_ipBuffer);
m_ipBuffer = IntPtr.Zero;
throw;
}
// Got one
return m_ipBuffer;
}
public int Width
{
get
{
return m_videoWidth;
}
}
public int Height
{
get
{
return m_videoHeight;
}
}
public int Stride
{
get
{
return m_stride;
}
}
/// <summary> build the capture graph for grabber. </summary>
private void SetupGraph(DsDevice dev, int iWidth, int iHeight, short iBPP, Control hControl)
{
int hr;
ISampleGrabber sampGrabber = null;
IBaseFilter capFilter = null;
IPin pCaptureOut = null;
IPin pSampleIn = null;
IPin pRenderIn = null;
// Get the graphbuilder object
m_FilterGraph = new FilterGraph() as IFilterGraph2;
try
{
#if DEBUG
m_rot = new DsROTEntry(m_FilterGraph);
#endif
// add the video input device
hr = m_FilterGraph.AddSourceFilterForMoniker(dev.Mon, null, dev.Name, out capFilter);
DsError.ThrowExceptionForHR( hr );
// Find the still pin
m_pinStill = DsFindPin.ByCategory(capFilter, PinCategory.Still, 0);
// Didn't find one. Is there a preview pin?
if (m_pinStill == null)
{
m_pinStill = DsFindPin.ByCategory(capFilter, PinCategory.Preview, 0);
}
// Still haven't found one. Need to put a splitter in so we have
// one stream to capture the bitmap from, and one to display. Ok, we
// don't *have* to do it that way, but we are going to anyway.
if (m_pinStill == null)
{
IPin pRaw = null;
IPin pSmart = null;
// There is no still pin
m_VidControl = null;
// Add a splitter
IBaseFilter iSmartTee = (IBaseFilter)new SmartTee();
try
{
hr = m_FilterGraph.AddFilter(iSmartTee, "SmartTee");
DsError.ThrowExceptionForHR( hr );
// Find the find the capture pin from the video device and the
// input pin for the splitter, and connnect them
pRaw = DsFindPin.ByCategory(capFilter, PinCategory.Capture, 0);
pSmart = DsFindPin.ByDirection(iSmartTee, PinDirection.Input, 0);
hr = m_FilterGraph.Connect(pRaw, pSmart);
DsError.ThrowExceptionForHR( hr );
// Now set the capture and still pins (from the splitter)
m_pinStill = DsFindPin.ByName(iSmartTee, "Preview");
pCaptureOut = DsFindPin.ByName(iSmartTee, "Capture");
// If any of the default config items are set, perform the config
// on the actual video device (rather than the splitter)
if (iHeight + iWidth + iBPP > 0)
{
SetConfigParms(pRaw, iWidth, iHeight, iBPP);
}
}
finally
{
if (pRaw != null)
{
Marshal.ReleaseComObject(pRaw);
}
if (pRaw != pSmart)
{
Marshal.ReleaseComObject(pSmart);
}
if (pRaw != iSmartTee)
{
Marshal.ReleaseComObject(iSmartTee);
}
}
}
else
{
// Get a control pointer (used in Click())
m_VidControl = capFilter as IAMVideoControl;
pCaptureOut = DsFindPin.ByCategory(capFilter, PinCategory.Capture, 0);
// If any of the default config items are set
if (iHeight + iWidth + iBPP > 0)
{
SetConfigParms(m_pinStill, iWidth, iHeight, iBPP);
}
}
// Get the SampleGrabber interface
sampGrabber = new SampleGrabber() as ISampleGrabber;
// Configure the sample grabber
IBaseFilter baseGrabFlt = sampGrabber as IBaseFilter;
ConfigureSampleGrabber(sampGrabber);
pSampleIn = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Input, 0);
// Get the default video renderer
IBaseFilter pRenderer = new VideoRendererDefault() as IBaseFilter;
hr = m_FilterGraph.AddFilter(pRenderer, "Renderer");
DsError.ThrowExceptionForHR( hr );
pRenderIn = DsFindPin.ByDirection(pRenderer, PinDirection.Input, 0);
// Add the sample grabber to the graph
hr = m_FilterGraph.AddFilter( baseGrabFlt, "Ds.NET Grabber" );
DsError.ThrowExceptionForHR( hr );
if (m_VidControl == null)
{
// Connect the Still pin to the sample grabber
hr = m_FilterGraph.Connect(m_pinStill, pSampleIn);
DsError.ThrowExceptionForHR( hr );
// Connect the capture pin to the renderer
hr = m_FilterGraph.Connect(pCaptureOut, pRenderIn);
DsError.ThrowExceptionForHR( hr );
}
else
{
// Connect the capture pin to the renderer
hr = m_FilterGraph.Connect(pCaptureOut, pRenderIn);
DsError.ThrowExceptionForHR( hr );
// Connect the Still pin to the sample grabber
hr = m_FilterGraph.Connect(m_pinStill, pSampleIn);
DsError.ThrowExceptionForHR( hr );
}
// Learn the video properties
SaveSizeInfo(sampGrabber);
ConfigVideoWindow(hControl);
// Start the graph
IMediaControl mediaCtrl = m_FilterGraph as IMediaControl;
hr = mediaCtrl.Run();
DsError.ThrowExceptionForHR( hr );
}
finally
{
if (sampGrabber != null)
{
Marshal.ReleaseComObject(sampGrabber);
sampGrabber = null;
}
if (pCaptureOut != null)
{
Marshal.ReleaseComObject(pCaptureOut);
pCaptureOut = null;
}
if (pRenderIn != null)
{
Marshal.ReleaseComObject(pRenderIn);
pRenderIn = null;
}
if (pSampleIn != null)
{
Marshal.ReleaseComObject(pSampleIn);
pSampleIn = null;
}
}
}
private void SaveSizeInfo(ISampleGrabber sampGrabber)
{
int hr;
// Get the media type from the SampleGrabber
AMMediaType media = new AMMediaType();
hr = sampGrabber.GetConnectedMediaType( media );
DsError.ThrowExceptionForHR( hr );
if( (media.formatType != FormatType.VideoInfo) || (media.formatPtr == IntPtr.Zero) )
{
throw new NotSupportedException( "Unknown Grabber Media Format" );
}
// Grab the size info
VideoInfoHeader videoInfoHeader = (VideoInfoHeader) Marshal.PtrToStructure( media.formatPtr, typeof(VideoInfoHeader) );
m_videoWidth = videoInfoHeader.BmiHeader.Width;
m_videoHeight = videoInfoHeader.BmiHeader.Height;
m_stride = m_videoWidth * (videoInfoHeader.BmiHeader.BitCount / 8);
DsUtils.FreeAMMediaType(media);
media = null;
}
// Set the video window within the control specified by hControl
private void ConfigVideoWindow(Control hControl)
{
int hr;
IVideoWindow ivw = m_FilterGraph as IVideoWindow;
// Set the parent
hr = ivw.put_Owner(hControl.Handle);
DsError.ThrowExceptionForHR( hr );
// Turn off captions, etc
hr = ivw.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren | WindowStyle.ClipSiblings);
DsError.ThrowExceptionForHR( hr );
// Yes, make it visible
hr = ivw.put_Visible( OABool.True );
DsError.ThrowExceptionForHR( hr );
// Move to upper left corner
Rectangle rc = hControl.ClientRectangle;
hr = ivw.SetWindowPosition( 0, 0, rc.Right, rc.Bottom );
DsError.ThrowExceptionForHR( hr );
}
private void ConfigureSampleGrabber(ISampleGrabber sampGrabber)
{
int hr;
AMMediaType media = new AMMediaType();
// Set the media type to Video/RBG24
media.majorType = MediaType.Video;
media.subType = MediaSubType.RGB24;
media.formatType = FormatType.VideoInfo;
hr = sampGrabber.SetMediaType( media );
DsError.ThrowExceptionForHR( hr );
DsUtils.FreeAMMediaType(media);
media = null;
// Configure the samplegrabber
hr = sampGrabber.SetCallback( this, 1 );
DsError.ThrowExceptionForHR( hr );
}
// Set the Framerate, and video size
private void SetConfigParms(IPin pStill, int iWidth, int iHeight, short iBPP)
{
int hr;
AMMediaType media;
VideoInfoHeader v;
IAMStreamConfig videoStreamConfig = pStill as IAMStreamConfig;
// Get the existing format block
hr = videoStreamConfig.GetFormat(out media);
DsError.ThrowExceptionForHR(hr);
try
{
// copy out the videoinfoheader
v = new VideoInfoHeader();
Marshal.PtrToStructure( media.formatPtr, v );
// if overriding the width, set the width
if (iWidth > 0)
{
v.BmiHeader.Width = iWidth;
}
// if overriding the Height, set the Height
if (iHeight > 0)
{
v.BmiHeader.Height = iHeight;
}
// if overriding the bits per pixel
if (iBPP > 0)
{
v.BmiHeader.BitCount = iBPP;
}
// Copy the media structure back
Marshal.StructureToPtr( v, media.formatPtr, false );
// Set the new format
hr = videoStreamConfig.SetFormat( media );
DsError.ThrowExceptionForHR( hr );
}
finally
{
DsUtils.FreeAMMediaType(media);
media = null;
}
}
/// <summary> Shut down capture </summary>
private void CloseInterfaces()
{
int hr;
try
{
if( m_FilterGraph != null )
{
IMediaControl mediaCtrl = m_FilterGraph as IMediaControl;
// Stop the graph
hr = mediaCtrl.Stop();
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
if (m_FilterGraph != null)
{
Marshal.ReleaseComObject(m_FilterGraph);
m_FilterGraph = null;
}
if (m_VidControl != null)
{
Marshal.ReleaseComObject(m_VidControl);
m_VidControl = null;
}
if (m_pinStill != null)
{
Marshal.ReleaseComObject(m_pinStill);
m_pinStill = null;
}
}
/// <summary> sample callback, NOT USED. </summary>
int ISampleGrabberCB.SampleCB( double SampleTime, IMediaSample pSample )
{
Marshal.ReleaseComObject(pSample);
return 0;
}
/// <summary> buffer callback, COULD BE FROM FOREIGN THREAD. </summary>
int ISampleGrabberCB.BufferCB( double SampleTime, IntPtr pBuffer, int BufferLen )
{
// Note that we depend on only being called once per call to Click. Otherwise
// a second call can overwrite the previous image.
Debug.Assert(BufferLen == Math.Abs(m_stride) * m_videoHeight, "Incorrect buffer length");
if (m_WantOne)
{
m_WantOne = false;
Debug.Assert(m_ipBuffer != IntPtr.Zero, "Unitialized buffer");
// Save the buffer
CopyMemory(m_ipBuffer, pBuffer, BufferLen);
// Picture is ready.
m_PictureReady.Set();
}
return 0;
}
}
}
Form1.cs
While the underlying libraries are covered by LGPL, this sample is released
as public domain. It is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.
*****************************************************************************/
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
namespace SnapShot
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button button1;
private System.Windows.Forms.PictureBox pictureBox1;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
private System.Windows.Forms.PictureBox pictureBox2;
private Capture cam;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
const int VIDEODEVICE = 0; // zero based index of video capture device to use
const int VIDEOWIDTH = 640; // Depends on video device caps
const int VIDEOHEIGHT = 480; // Depends on video device caps
const int VIDEOBITSPERPIXEL = 24; // BitsPerPixel values determined by device
cam = new Capture(VIDEODEVICE, VIDEOWIDTH, VIDEOHEIGHT, VIDEOBITSPERPIXEL, pictureBox2);
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
if (m_ip != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(m_ip);
m_ip = IntPtr.Zero;
}
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.pictureBox2 = new System.Windows.Forms.PictureBox();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(368, 80);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 40);
this.button1.TabIndex = 0;
this.button1.Text = "Click";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// pictureBox1
//
this.pictureBox1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.pictureBox1.Location = new System.Drawing.Point(16, 256);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(640, 480);
this.pictureBox1.TabIndex = 1;
this.pictureBox1.TabStop = false;
//
// pictureBox2
//
this.pictureBox2.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.pictureBox2.Location = new System.Drawing.Point(16, 8);
this.pictureBox2.Name = "pictureBox2";
this.pictureBox2.Size = new System.Drawing.Size(320, 240);
this.pictureBox2.TabIndex = 2;
this.pictureBox2.TabStop = false;
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(776, 794);
this.Controls.Add(this.pictureBox2);
this.Controls.Add(this.pictureBox1);
this.Controls.Add(this.button1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Name = "Form1";
this.Text = "DxSnap";
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Form1_FormClosed);
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).EndInit();
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
IntPtr m_ip = IntPtr.Zero;
private void button1_Click(object sender, System.EventArgs e)
{
Cursor.Current = Cursors.WaitCursor;
// Release any previous buffer
if (m_ip != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(m_ip);
m_ip = IntPtr.Zero;
}
// capture image
m_ip = cam.Click();
Bitmap b = new Bitmap(cam.Width, cam.Height, cam.Stride, PixelFormat.Format24bppRgb, m_ip);
// If the image is upsidedown
b.RotateFlip(RotateFlipType.RotateNoneFlipY);
pictureBox1.Image = b;
Cursor.Current = Cursors.Default;
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
cam.Dispose();
if (m_ip != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(m_ip);
m_ip = IntPtr.Zero;
}
}
}
}