| // Imports |
| using DirectShowLib; |
| using Microsoft.Xna.Framework; |
| using Microsoft.Xna.Framework.Graphics; |
| using System; |
| using System.Runtime.InteropServices; |
| using System.Runtime.InteropServices.ComTypes; |
| using System.Threading; |
| |
| namespace MultiNet |
| { |
| |
| /// <summary> |
| /// Manages web cam |
| /// </summary> |
| public class WebCam : ISampleGrabberCB, IDisposable |
| { |
| |
| // Public Properties: |
| public Texture2D Frame; |
| |
| // Private Properties: |
| private ICaptureGraphBuilder2 CaptureGraphBuilder; |
| private byte[] FrameBGR; |
| private byte[] FrameBytes; |
| private bool FrameReady; |
| private IGraphBuilder GraphBuilder; |
| private GraphicsDevice GraphicsDevice; |
| private int Height; |
| private IMediaControl MediaControl; |
| private ISampleGrabber SampleGrabber; |
| private Thread UpdateThread; |
| private IVideoWindow VideoWindow; |
| private int Width; |
| |
| /// <summary> |
| /// Manages cursors |
| /// </summary> |
| /// <param name="MultiNetClient">Main application</param> |
| public WebCam(GraphicsDevice GraphicsDevice) |
| { |
| |
| // Define properties |
| this.GraphicsDevice = GraphicsDevice; |
| FrameReady = false; |
| Height = 480; |
| Width = 640; |
| |
| // Set |
| Frame = new Texture2D(GraphicsDevice, Width, Height, 1, TextureUsage.None, SurfaceFormat.Color); |
| FrameBGR = new byte[(Width * Height) * 3]; |
| FrameBytes = new byte[(Width * Height) * 4]; |
| |
| // Get DirectShow interfaces |
| GraphBuilder = (IGraphBuilder)new FilterGraph(); |
| CaptureGraphBuilder = (ICaptureGraphBuilder2)new CaptureGraphBuilder2(); |
| MediaControl = (IMediaControl)GraphBuilder; |
| VideoWindow = (IVideoWindow)GraphBuilder; |
| |
| // Attach the filter graph to the capture graph |
| CaptureGraphBuilder.SetFiltergraph(GraphBuilder); |
| |
| // Create the input device |
| object VideoInputObject = null; |
| IBaseFilter VideoInput = null; |
| |
| // Get the list of input devices |
| IEnumMoniker classEnum; |
| ICreateDevEnum devEnum = (ICreateDevEnum)new CreateDevEnum(); |
| devEnum.CreateClassEnumerator(FilterCategory.VideoInputDevice, out classEnum, 0); |
| Marshal.ReleaseComObject(devEnum); |
| |
| // We've got devices |
| if (classEnum != null) |
| { |
| |
| // Find first input device |
| IMoniker[] moniker = new IMoniker[1]; |
| if (classEnum.Next(moniker.Length, moniker, IntPtr.Zero) == 0) |
| { |
| |
| // Bind moniker to a filter object |
| Guid iid = typeof(IBaseFilter).GUID; |
| moniker[0].BindToObject(null, null, ref iid, out VideoInputObject); |
| |
| } |
| |
| // Release COM objects |
| Marshal.ReleaseComObject(moniker[0]); |
| Marshal.ReleaseComObject(classEnum); |
| |
| // Case inout object to filter |
| VideoInput = (IBaseFilter)VideoInputObject; |
| |
| } |
| |
| // We've got a device |
| if (VideoInput != null) |
| { |
| |
| // Create a SampleGrabber Filter and add it to the Filter Graph |
| SampleGrabber grabber = new SampleGrabber(); |
| SampleGrabber = (ISampleGrabber)grabber; |
| GraphBuilder.AddFilter((IBaseFilter)grabber, "Grabber"); |
| |
| // Setup Media type info for the SampleGrabber |
| AMMediaType Type = new AMMediaType(); |
| Type.majorType = MediaType.Video; |
| Type.subType = MediaSubType.RGB24; |
| Type.formatType = FormatType.VideoInfo; |
| SampleGrabber.SetMediaType(Type); |
| |
| // Add Capture filter to our graph |
| GraphBuilder.AddFilter(VideoInput, "Video Capture"); |
| |
| // Set SampleGrabber Properties |
| SampleGrabber.SetBufferSamples(false); |
| SampleGrabber.SetOneShot(false); |
SampleGrabber.SetCallback((ISampleGrabberCB)this, 1); <-- Setting callback here |
| SampleGrabber.GetConnectedMediaType(new AMMediaType()); |
| |
| // Render the preview pin on the video capture filter |
| CaptureGraphBuilder.RenderStream(PinCategory.Preview, MediaType.Video, VideoInput, null, null); |
| |
| // Hide video window |
| VideoWindow.put_AutoShow(OABool.False); |
| |
| // Run update thread |
| UpdateThread = new Thread(new ThreadStart(UpdateBuffer)); |
| UpdateThread.Start(); |
| |
| // Start previewing video data |
| MediaControl.Run(); |
| |
| // Release reference to the filter |
| Marshal.ReleaseComObject(VideoInput); |
| |
| } |
| |
| } |
| |
| /// <summary> |
| /// Runs update logic |
| /// </summary> |
| public void Update() |
| { |
| |
| // Set video data into the Output Frame |
| if (Frame.GraphicsDevice.Textures[0] == Frame) Frame.GraphicsDevice.Textures[0] = null; |
| Frame.SetData<byte>(FrameBytes); |
| |
| } |
| |
| /// <summary> |
| /// Clean up |
| /// </summary> |
| public void Dispose() |
| { |
| |
| // Stop previewing data |
| if (MediaControl != null) MediaControl.StopWhenReady(); |
| |
| // Release DirectShow interfaces |
| Marshal.ReleaseComObject(MediaControl); |
| Marshal.ReleaseComObject(GraphBuilder); |
| Marshal.ReleaseComObject(CaptureGraphBuilder); |
| Marshal.ReleaseComObject(VideoWindow); |
| CaptureGraphBuilder = null; |
| GraphBuilder = null; |
| MediaControl = null; |
| VideoWindow = null; |
| |
| // Kill frame |
| Frame.Dispose(); |
| Frame = null; |
| |
| } |
vvv These aren't getting called, but they should be |
| /// <summary> |
| /// Required public callback from DirectShow SampleGrabber |
| /// </summary> |
| public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen) |
| { |
| |
| // Copy raw data into BGR byte array |
| Marshal.Copy(pBuffer, FrameBGR, 0, BufferLen); |
| |
| // Flag the new frame as available |
| FrameReady = true; |
| |
| // Return S_OK |
| return 0; |
| |
| } |
| |
| /// <summary> |
| /// Required public callback from DirectShow SampleGrabber |
| /// </summary> |
| public int SampleCB(double SampleTime, IMediaSample pSample) |
| { |
| |
| // Return S_OK |
| return 0; |
| |
| } |
| |
| /// <summary> |
| /// Worker to copy the BGR data from the video stream into the RGBA byte array for the Output Frame. |
| /// </summary> |
| protected void UpdateBuffer() |
| { |
| |
| // Initialize indexes |
| int samplePosRGBA = 0; |
| int samplePosRGB24 = 0; |
| |
| // Loop |
| while (true) |
| { |
| |
| // Iterate through rows |
| for (int y = 0, y2 = Height - 1; y < Height; y++, y2--) |
| { |
| |
| // Iterate through columns |
| for (int x = 0; x < Width; x++) |
| { |
| |
| // Update index |
| samplePosRGBA = (((y2 * Width) + x) * 4); |
| samplePosRGB24 = ((y * Width) + x) * 3; |
| |
| // Populate array |
| FrameBytes[samplePosRGBA + 0] = FrameBGR[samplePosRGB24 + 0]; |
| FrameBytes[samplePosRGBA + 1] = FrameBGR[samplePosRGB24 + 1]; |
| FrameBytes[samplePosRGBA + 2] = FrameBGR[samplePosRGB24 + 2]; |
| FrameBytes[samplePosRGBA + 3] = (byte)255; |
| |
| } |
| |
| } |
| |
| // Make frame unavailable |
| FrameReady = false; |
| while (!FrameReady) Thread.Sleep(20); |
| |
| } |
| |
| } |
| |
| } |
| |
| } |