using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using NetLibrary.Log; using DirectShowLib; using System.Drawing; using System.Drawing.Imaging; namespace NetLibrary { public class Camera : ISampleGrabberCB, IDisposable { #region Member variables /// graph builder interface. private IFilterGraph2 m_FilterGraph = null; private IMediaControl m_mediaCtrl = null; /// so we can wait for the async job to finish private ManualResetEvent m_PictureReady = null; /// Set by async routine when it captures an image private volatile bool m_bGotOne = false; /// Indicates the status of the graph private bool m_bRunning = false; /// Dimensions of the image, calculated once in constructor. private IntPtr m_handle = IntPtr.Zero; private int m_videoWidth; private int m_videoHeight; private int m_stride; public int m_Dropped = 0; #endregion #region API [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory")] private static extern void CopyMemory(IntPtr Destination, IntPtr Source, int Length); #endregion /// Use capture device zero, default frame rate and size public Camera() { _Capture(0, 0, 0, 0); } /// Use specified capture device, default frame rate and size public Camera(int iDeviceNum) { _Capture(iDeviceNum, 0, 0, 0); } /// Use specified capture device, specified frame rate and default size public Camera(int iDeviceNum, int iFrameRate) { _Capture(iDeviceNum, iFrameRate, 0, 0); } /// Use specified capture device, specified frame rate and size public Camera(int iDeviceNum, int iFrameRate, int iWidth, int iHeight) { _Capture(iDeviceNum, iFrameRate, iWidth, iHeight); } #region 初始化线程 private AutoResetEvent AutoReset = new AutoResetEvent(false); Thread CameraThread = null; public bool IsScreenImage = false; public event Action OutImage; public void StartScreenImage() { CameraThread = new Thread(ScreenImage); CameraThread.Start(); } #endregion #region 停止录像 public void StopScreenImage() { IsScreenImage = false; if (CameraThread == null) return; while (CameraThread.IsAlive) { System.Threading.Thread.Sleep(500); } //System.Threading.Thread.Sleep(1000); //CameraThread.Abort(); //CameraThread = null; } #endregion #region 录像 void ScreenImage() { IsScreenImage = true; while (this.IsScreenImage) { try { Bitmap image = null; IntPtr ip = IntPtr.Zero; ip = this.GetBitMap(); image = new Bitmap(this.Width, this.Height, this.Stride, PixelFormat.Format24bppRgb, ip); //旋转 image.RotateFlip(RotateFlipType.RotateNoneFlipY); //释放指针ip Marshal.FreeCoTaskMem(ip); //回收垃圾 GC.Collect(); //不安全的刷新 if (this.OutImage != null) this.OutImage(image); AutoReset.WaitOne(100); } catch (Exception ex) { ErrorFollow.TraceWrite(ex.TargetSite.Name, ex.StackTrace, ex.Message); } } } #endregion #region 截图 public Bitmap GrabImage() { try { StopScreenImage(); Bitmap image = null; IntPtr ip = IntPtr.Zero; ip = this.GetBitMap(); image = new Bitmap(this.Width, this.Height, this.Stride, PixelFormat.Format24bppRgb, ip); //旋转 image.RotateFlip(RotateFlipType.RotateNoneFlipY); //释放指针ip Marshal.FreeCoTaskMem(ip); //回收垃圾 GC.Collect(); StartScreenImage(); return image; } catch (Exception ex) { ErrorFollow.TraceWrite(ex.TargetSite.Name, ex.StackTrace, ex.Message); } return null; } #endregion #region 截图 public void GrabImage(string filePath) { try { StopScreenImage(); Bitmap image = null; IntPtr ip = IntPtr.Zero; ip = this.GetBitMap(); image = new Bitmap(this.Width, this.Height, this.Stride, PixelFormat.Format24bppRgb, ip); //旋转 image.RotateFlip(RotateFlipType.RotateNoneFlipY); //释放指针ip Marshal.FreeCoTaskMem(ip); //回收垃圾 GC.Collect(); image.Save(filePath, ImageFormat.Jpeg); StartScreenImage(); } catch (Exception ex) { ErrorFollow.TraceWrite(ex.TargetSite.Name, ex.StackTrace, ex.Message); } } #endregion #region 截图并转成数组 public byte[] GrabImageToByte() { try { StopScreenImage(); Bitmap image = null; IntPtr ip = IntPtr.Zero; ip = this.GetBitMap(); image = new Bitmap(this.Width, this.Height, this.Stride, PixelFormat.Format24bppRgb, ip); //旋转 image.RotateFlip(RotateFlipType.RotateNoneFlipY); //释放指针ip Marshal.FreeCoTaskMem(ip); //回收垃圾 GC.Collect(); byte[] bytes=CustomIO.GetPhoto(image); image.Dispose(); image = null; StartScreenImage(); return bytes; } catch (Exception ex) { ErrorFollow.TraceWrite(ex.TargetSite.Name, ex.StackTrace, ex.Message); } return null; } #endregion #region 读取所有摄像头设备 public static DsDevice[] GetDevices() { DsDevice[] capDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice); return capDevices; } #endregion #region 是否存在摄像头 public static bool IsCamera() { DsDevice[] capDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice); if (capDevices.Length == 0) return false; return true; } #endregion /// release everything. public void Dispose() { this.m_bRunning = false; CloseInterfaces(); if (m_PictureReady != null) { m_PictureReady.Close(); m_PictureReady = null; } } // Destructor ~Camera() { Dispose(); } public int Width { get { return m_videoWidth; } } public int Height { get { return m_videoHeight; } } public int Stride { get { return m_stride; } } /// capture the next image public IntPtr GetBitMap() { 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 if (!m_PictureReady.WaitOne(5000, false)) { throw new Exception("Timeout waiting to get picture"); } } catch { Marshal.FreeCoTaskMem(m_handle); throw; } 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; } } // Internal capture private void _Capture(int iDeviceNum, int iFrameRate, int iWidth, int iHeight) { 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], iFrameRate, iWidth, iHeight); // tell the callback to ignore new images m_PictureReady = new ManualResetEvent(false); m_bGotOne = true; m_bRunning = false; } catch { Dispose(); throw; } } /// build the capture graph for grabber. private void SetupGraph(DsDevice dev, int iFrameRate, int iWidth, int iHeight) { 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); IBaseFilter baseGrabFlt = (IBaseFilter)sampGrabber; ConfigureSampleGrabber(sampGrabber); // Add the frame grabber to the graph hr = m_FilterGraph.AddFilter(baseGrabFlt, "Ds.NET Grabber"); DsError.ThrowExceptionForHR(hr); // If any of the default config items are set if (iFrameRate + iHeight + iWidth > 0) { SetConfigParms(capGraph, capFilter, iFrameRate, iWidth, iHeight); } 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 private void SetConfigParms(ICaptureGraphBuilder2 capGraph, IBaseFilter capFilter, int iFrameRate, int iWidth, int iHeight) { int hr; object o; AMMediaType media; // 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"); } // Get the existing format block hr = videoStreamConfig.GetFormat(out media); DsError.ThrowExceptionForHR(hr); // copy out the videoinfoheader VideoInfoHeader v = new VideoInfoHeader(); Marshal.PtrToStructure(media.formatPtr, v); // if overriding the framerate, set the frame rate if (iFrameRate > 0) { v.AvgTimePerFrame = 10000000 / iFrameRate; } // 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; } // Copy the media structure back Marshal.StructureToPtr(v, media.formatPtr, false); // Set the new format hr = videoStreamConfig.SetFormat(media); DsError.ThrowExceptionForHR(hr); DsUtils.FreeAMMediaType(media); media = null; } /// Shut down capture private void CloseInterfaces() { int hr; try { if (m_mediaCtrl != null) { // Stop the graph hr = m_mediaCtrl.Stop(); m_bRunning = false; } } catch (Exception ex) { ErrorFollow.TraceWrite(ex.TargetSite.Name, ex.StackTrace, ex.Message); } if (m_FilterGraph != null) { Marshal.ReleaseComObject(m_FilterGraph); m_FilterGraph = null; } } /// sample callback, NOT USED. 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; } /// buffer callback, COULD BE FROM FOREIGN THREAD. 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; } } }