2011-09-08 22:31:32 -03:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
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.Collections.Generic ;
using DirectShowLib ;
namespace WebCamService
{
public delegate void CamImage ( Image camimage ) ;
/// <summary> Summary description for MainForm. </summary>
public class Capture : ISampleGrabberCB , IDisposable
{
#region Member variables
/// <summary> graph builder interface. </summary>
private IFilterGraph2 m_FilterGraph = null ;
private IMediaControl m_mediaCtrl = null ;
/// <summary> so we can wait for the async job to finish </summary>
private ManualResetEvent m_PictureReady = null ;
/// <summary> Set by async routine when it captures an image </summary>
private volatile bool m_bGotOne = false ;
/// <summary> Indicates the status of the graph </summary>
private bool m_bRunning = false ;
/// <summary> Dimensions of the image, calculated once in constructor. </summary>
private IntPtr m_handle = IntPtr . Zero ;
private int m_videoWidth ;
private int m_videoHeight ;
private int m_stride ;
public int m_Dropped = 0 ;
public Image image = null ;
IntPtr ip = IntPtr . Zero ;
public event CamImage camimage ;
System . Windows . Forms . Timer timer1 = new System . Windows . Forms . Timer ( ) ;
#endregion
#region API
[DllImport("Kernel32.dll", EntryPoint="RtlMoveMemory")]
private static extern void CopyMemory ( IntPtr Destination , IntPtr Source , int Length ) ;
#endregion
public Capture ( )
{
}
2011-10-07 05:43:11 -03:00
/// <summary> Use capture with selected media caps</summary>
public Capture ( int iDeviceNum , AMMediaType media )
2011-09-08 22:31:32 -03:00
{
2011-10-07 05:43:11 -03:00
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 ] , media ) ;
// tell the callback to ignore new images
m_PictureReady = new ManualResetEvent ( false ) ;
m_bGotOne = true ;
m_bRunning = false ;
timer1 . Interval = 1000 / 15 ; // 15 fps
timer1 . Tick + = new EventHandler ( timer1_Tick ) ;
timer1 . Start ( ) ;
}
catch
{
Dispose ( ) ;
throw ;
}
2011-09-08 22:31:32 -03:00
}
/// <summary> release everything. </summary>
public void Dispose ( )
{
timer1 . Stop ( ) ;
if ( camimage ! = null )
{
camimage ( null ) ; // clear last pic
}
CloseInterfaces ( ) ;
if ( m_PictureReady ! = null )
{
m_PictureReady . Close ( ) ;
m_PictureReady = null ;
}
}
// Destructor
~ Capture ( )
{
Dispose ( ) ;
}
public int Width
{
get
{
return m_videoWidth ;
}
}
public int Height
{
get
{
return m_videoHeight ;
}
}
public int Stride
{
get
{
return m_stride ;
}
}
/// <summary> capture the next image </summary>
public IntPtr GetBitMap ( )
{
if ( m_handle = = IntPtr . Zero )
m_handle = Marshal . AllocCoTaskMem ( m_stride * m_videoHeight ) ;
try
{
// get ready to wait for new image
m_PictureReady . Reset ( ) ;
m_bGotOne = false ;
// If the graph hasn't been started, start it.
Start ( ) ;
// Start waiting
2012-03-12 20:15:08 -03:00
if ( ! m_PictureReady . WaitOne ( 5000 , false ) )
2011-09-08 22:31:32 -03:00
{
throw new Exception ( "Timeout waiting to get picture" ) ;
}
//Pause(); //- we are effectivly pulling at 15 fps, so no need to pause
}
catch
{
Marshal . FreeCoTaskMem ( m_handle ) ;
throw ;
}
// Got one
return m_handle ;
}
// Start the capture graph
public void Start ( )
{
if ( ! m_bRunning )
{
int hr = m_mediaCtrl . Run ( ) ;
DsError . ThrowExceptionForHR ( hr ) ;
m_bRunning = true ;
}
}
// Pause the capture graph.
// Running the graph takes up a lot of resources. Pause it when it
// isn't needed.
public void Pause ( )
{
if ( m_bRunning )
{
int hr = m_mediaCtrl . Pause ( ) ;
DsError . ThrowExceptionForHR ( hr ) ;
m_bRunning = false ;
}
}
public static List < string > getDevices ( )
{
List < string > list = new List < string > ( ) ;
DsDevice [ ] capDevices ;
// Get the collection of video devices
capDevices = DsDevice . GetDevicesOfCat ( FilterCategory . VideoInputDevice ) ;
foreach ( DsDevice dev in capDevices )
{
list . Add ( dev . Name ) ;
}
return list ;
}
public bool showhud = true ;
void timer1_Tick ( object sender , EventArgs e )
{
try
{
ip = this . GetBitMap ( ) ;
image = new Bitmap ( this . Width , this . Height , this . Stride , PixelFormat . Format24bppRgb , ip ) ;
image . RotateFlip ( RotateFlipType . RotateNoneFlipY ) ;
if ( camimage ! = null )
{
camimage ( image ) ;
}
}
2012-03-09 11:18:12 -04:00
catch { Console . WriteLine ( "Grab bmp failed" ) ; timer1 . Enabled = false ; this . CloseInterfaces ( ) ; System . Windows . Forms . CustomMessageBox . Show ( "Problem with capture device, grabbing frame took longer than 5 sec" ) ; }
2011-09-08 22:31:32 -03:00
}
/// <summary> build the capture graph for grabber. </summary>
2011-10-07 05:43:11 -03:00
private void SetupGraph ( DsDevice dev , AMMediaType media )
2011-09-08 22:31:32 -03:00
{
int hr ;
ISampleGrabber sampGrabber = null ;
IBaseFilter capFilter = null ;
ICaptureGraphBuilder2 capGraph = null ;
// Get the graphbuilder object
m_FilterGraph = ( IFilterGraph2 ) new FilterGraph ( ) ;
m_mediaCtrl = m_FilterGraph as IMediaControl ;
try
{
// Get the ICaptureGraphBuilder2
capGraph = ( ICaptureGraphBuilder2 ) new CaptureGraphBuilder2 ( ) ;
// Get the SampleGrabber interface
sampGrabber = ( ISampleGrabber ) new SampleGrabber ( ) ;
// Start building the graph
hr = capGraph . SetFiltergraph ( m_FilterGraph ) ;
DsError . ThrowExceptionForHR ( hr ) ;
// Add the video device
hr = m_FilterGraph . AddSourceFilterForMoniker ( dev . Mon , null , "Video input" , out capFilter ) ;
DsError . ThrowExceptionForHR ( hr ) ;
// add video crossbar
// thanks to Andrew Fernie - this is to get tv tuner cards working
IAMCrossbar crossbar = null ;
object o ;
hr = capGraph . FindInterface ( PinCategory . Capture , MediaType . Video , capFilter , typeof ( IAMCrossbar ) . GUID , out o ) ;
if ( hr > = 0 )
{
crossbar = ( IAMCrossbar ) o ;
int oPin , iPin ;
int ovLink , ivLink ;
ovLink = ivLink = 0 ;
crossbar . get_PinCounts ( out oPin , out iPin ) ;
int pIdxRel ;
PhysicalConnectorType tp ;
for ( int i = 0 ; i < iPin ; i + + )
{
crossbar . get_CrossbarPinInfo ( true , i , out pIdxRel , out tp ) ;
if ( tp = = PhysicalConnectorType . Video_Composite ) ivLink = i ;
}
for ( int i = 0 ; i < oPin ; i + + )
{
crossbar . get_CrossbarPinInfo ( false , i , out pIdxRel , out tp ) ;
if ( tp = = PhysicalConnectorType . Video_VideoDecoder ) ovLink = i ;
}
try
{
crossbar . Route ( ovLink , ivLink ) ;
o = null ;
}
catch
{
throw new Exception ( "Failed to get IAMCrossbar" ) ;
}
}
//add AVI Decompressor
IBaseFilter pAVIDecompressor = ( IBaseFilter ) new AVIDec ( ) ;
hr = m_FilterGraph . AddFilter ( pAVIDecompressor , "AVI Decompressor" ) ;
DsError . ThrowExceptionForHR ( hr ) ;
//
IBaseFilter baseGrabFlt = ( IBaseFilter ) sampGrabber ;
ConfigureSampleGrabber ( sampGrabber ) ;
// Add the frame grabber to the graph
hr = m_FilterGraph . AddFilter ( baseGrabFlt , "Ds.NET Grabber" ) ;
DsError . ThrowExceptionForHR ( hr ) ;
2011-10-07 05:43:11 -03:00
SetConfigParms ( capGraph , capFilter , media ) ;
2011-09-08 22:31:32 -03:00
hr = capGraph . RenderStream ( PinCategory . Capture , MediaType . Video , capFilter , pAVIDecompressor , baseGrabFlt ) ;
if ( hr < 0 )
{
hr = capGraph . RenderStream ( PinCategory . Capture , MediaType . Video , capFilter , null , baseGrabFlt ) ;
}
DsError . ThrowExceptionForHR ( hr ) ;
SaveSizeInfo ( sampGrabber ) ;
}
finally
{
if ( capFilter ! = null )
{
Marshal . ReleaseComObject ( capFilter ) ;
capFilter = null ;
}
if ( sampGrabber ! = null )
{
Marshal . ReleaseComObject ( sampGrabber ) ;
sampGrabber = null ;
}
if ( capGraph ! = null )
{
Marshal . ReleaseComObject ( capGraph ) ;
capGraph = 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 ;
}
private void ConfigureSampleGrabber ( ISampleGrabber sampGrabber )
{
AMMediaType media ;
int hr ;
// Set the media type to Video/RBG24
media = new AMMediaType ( ) ;
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
2011-10-07 05:43:11 -03:00
private void SetConfigParms ( ICaptureGraphBuilder2 capGraph , IBaseFilter capFilter , AMMediaType media )
2011-09-08 22:31:32 -03:00
{
int hr ;
object o ;
// Find the stream config interface
hr = capGraph . FindInterface (
PinCategory . Capture , MediaType . Video , capFilter , typeof ( IAMStreamConfig ) . GUID , out o ) ;
IAMStreamConfig videoStreamConfig = o as IAMStreamConfig ;
if ( videoStreamConfig = = null )
{
throw new Exception ( "Failed to get IAMStreamConfig" ) ;
2011-10-07 05:43:11 -03:00
}
2011-09-08 22:31:32 -03:00
// Set the new format
hr = videoStreamConfig . SetFormat ( media ) ;
DsError . ThrowExceptionForHR ( hr ) ;
DsUtils . FreeAMMediaType ( media ) ;
media = null ;
}
/// <summary> Shut down capture </summary>
private void CloseInterfaces ( )
{
int hr ;
try
{
if ( m_mediaCtrl ! = null )
{
// Stop the graph
hr = m_mediaCtrl . Stop ( ) ;
m_bRunning = false ;
}
}
catch ( Exception ex )
{
Debug . WriteLine ( ex ) ;
}
if ( m_FilterGraph ! = null )
{
Marshal . ReleaseComObject ( m_FilterGraph ) ;
m_FilterGraph = null ;
}
}
/// <summary> sample callback, NOT USED. </summary>
int ISampleGrabberCB . SampleCB ( double SampleTime , IMediaSample pSample )
{
if ( ! m_bGotOne )
{
// Set bGotOne to prevent further calls until we
// request a new bitmap.
m_bGotOne = true ;
IntPtr pBuffer ;
pSample . GetPointer ( out pBuffer ) ;
int iBufferLen = pSample . GetSize ( ) ;
if ( pSample . GetSize ( ) > m_stride * m_videoHeight )
{
throw new Exception ( "Buffer is wrong size" ) ;
}
CopyMemory ( m_handle , pBuffer , m_stride * m_videoHeight ) ;
// Picture is ready.
m_PictureReady . Set ( ) ;
}
Marshal . ReleaseComObject ( pSample ) ;
return 0 ;
}
/// <summary> buffer callback, COULD BE FROM FOREIGN THREAD. </summary>
int ISampleGrabberCB . BufferCB ( double SampleTime , IntPtr pBuffer , int BufferLen )
{
if ( ! m_bGotOne )
{
// The buffer should be long enought
if ( BufferLen < = m_stride * m_videoHeight )
{
// Copy the frame to the buffer
CopyMemory ( m_handle , pBuffer , m_stride * m_videoHeight ) ;
}
else
{
throw new Exception ( "Buffer is wrong size" ) ;
}
// Set bGotOne to prevent further calls until we
// request a new bitmap.
m_bGotOne = true ;
// Picture is ready.
m_PictureReady . Set ( ) ;
}
else
{
m_Dropped + + ;
}
return 0 ;
}
}
}