C# Webcam Barcode Reader: A DirectShow.NET Implementation
In today’s fast-paced digital world, the ability to quickly and accurately capture data is paramount. Barcode scanning has emerged as a reliable and efficient solution for automating data entry across various applications. This tutorial explores webcam barcode reading on Windows, demonstrating how to build a powerful barcode detection application using C#, the DirectShow.NET library, and the Dynamsoft Barcode Reader SDK. DirectShow.NET captures video streams from a webcam, while the Dynamsoft Barcode Reader SDK decodes the barcodes.
Prerequisites
- Visual Studio
- DirectShow.NET library
- Dynamsoft Barcode Reader SDK
- Dynamsoft Barcode Reader Trial License
Opening Webcam and Streaming Video with DirectShow.NET
The DirectShow.NET sample Samples\Capture\PlayCap demonstrates how to open a webcam and display the video stream in C#. Follow these steps:
-
Get connected devices.
DsDevice[] devices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);
-
Create a filter graph and a capture graph.
int hr = 0; this.graphBuilder = (IFilterGraph2)new FilterGraph(); this.captureGraphBuilder = (ICaptureGraphBuilder2)new CaptureGraphBuilder2(); this.mediaControl = (IMediaControl)this.graphBuilder; this.videoWindow = (IVideoWindow)this.graphBuilder; DsError.ThrowExceptionForHR(hr);
-
Set the filter graph to the capture graph.
hr = this.captureGraphBuilder.SetFiltergraph(this.graphBuilder); DsError.ThrowExceptionForHR(hr);
-
Bind the moniker to a filter object.
int hr = 0; IEnumMoniker classEnum = null; IMoniker[] moniker = new IMoniker[1]; object source = null; ICreateDevEnum devEnum = (ICreateDevEnum)new CreateDevEnum(); hr = devEnum.CreateClassEnumerator(FilterCategory.VideoInputDevice, out classEnum, 0); DsError.ThrowExceptionForHR(hr); Marshal.ReleaseComObject(devEnum); if (classEnum == null) { throw new ApplicationException("No video capture device was detected.\r\n\r\n" + "This sample requires a video capture device, such as a USB WebCam,\r\n" + "to be installed and working properly. The sample will now close."); } if (classEnum.Next(moniker.Length, moniker, IntPtr.Zero) == 0) { Guid iid = typeof(IBaseFilter).GUID; moniker[0].BindToObject(null, null, ref iid, out source); } else { throw new ApplicationException("Unable to access video capture device!"); } Marshal.ReleaseComObject(moniker[0]); Marshal.ReleaseComObject(classEnum); return (IBaseFilter)source;
-
Add a camera source to the graph.
hr = this.graphBuilder.AddFilter(sourceFilter, "Video Capture"); DsError.ThrowExceptionForHR(hr);
-
Configure a sample grabber and set a callback function for the video stream.
sampleGrabber = new SampleGrabber() as ISampleGrabber; { AMMediaType media; int hr; media = new AMMediaType(); media.majorType = MediaType.Video; media.subType = MediaSubType.RGB24; media.formatType = FormatType.VideoInfo; hr = sampleGrabber.SetMediaType(media); DsError.ThrowExceptionForHR(hr); DsUtils.FreeAMMediaType(media); media = null; hr = sampleGrabber.SetCallback(this, 1); DsError.ThrowExceptionForHR(hr); } hr = this.graphBuilder.AddFilter(sampleGrabber as IBaseFilter, "Frame Callback"); DsError.ThrowExceptionForHR(hr);
-
Configure the video stream format.
private void SetConfigParams(ICaptureGraphBuilder2 capGraph, IBaseFilter capFilter, int iFrameRate, int iWidth, int iHeight) { int hr; object config; AMMediaType mediaType; // Find the stream config interface hr = capGraph.FindInterface( PinCategory.Capture, MediaType.Video, capFilter, typeof(IAMStreamConfig).GUID, out config); IAMStreamConfig videoStreamConfig = config as IAMStreamConfig; if (videoStreamConfig == null) { throw new Exception("Failed to get IAMStreamConfig"); } // Get the existing format block hr = videoStreamConfig.GetFormat(out mediaType); DsError.ThrowExceptionForHR(hr); // copy out the videoinfoheader VideoInfoHeader videoInfoHeader = new VideoInfoHeader(); Marshal.PtrToStructure(mediaType.formatPtr, videoInfoHeader); // if overriding the framerate, set the frame rate if (iFrameRate > 0) { videoInfoHeader.AvgTimePerFrame = 10000000 / iFrameRate; } // if overriding the width, set the width if (iWidth > 0) { videoInfoHeader.BmiHeader.Width = iWidth; } // if overriding the Height, set the Height if (iHeight > 0) { videoInfoHeader.BmiHeader.Height = iHeight; } // Copy the media structure back Marshal.StructureToPtr(videoInfoHeader, mediaType.formatPtr, false); // Set the new format hr = videoStreamConfig.SetFormat(mediaType); DsError.ThrowExceptionForHR(hr); DsUtils.FreeAMMediaType(mediaType); mediaType = null; }
-
Render the video stream.
hr = this.captureGraphBuilder.RenderStream(PinCategory.Preview, MediaType.Video, sourceFilter, (sampleGrabber as IBaseFilter), null); DsError.ThrowExceptionForHR(hr);
-
Output the video stream to a
PictureBox
and resize the video window position.int hr = 0; hr = this.videoWindow.put_Owner(pictureBox1.Handle); DsError.ThrowExceptionForHR(hr); hr = this.videoWindow.put_WindowStyle(WindowStyle.Child); DsError.ThrowExceptionForHR(hr); hr = this.videoWindow.put_Visible(OABool.True); DsError.ThrowExceptionForHR(hr); Rectangle rc = pictureBox1.ClientRectangle; hr = videoWindow.SetWindowPosition(0, 0, _previewWidth, _previewHeight); DsError.ThrowExceptionForHR(hr);
-
Start the video stream.
rot = new DsROTEntry(this.graphBuilder); hr = this.mediaControl.Run(); DsError.ThrowExceptionForHR(hr);
Asynchronously Reading Barcodes with Dynamsoft Barcode Reader v9.x
-
Create a barcode reader instance in
Form1()
:using Dynamsoft.DBR; private Dynamsoft.DBR.BarcodeReader _barcodeReader; public Form1() { InitializeComponent(); // Initialize Dynamsoft Barcode Reader string errorMsg; EnumErrorCode errorCode = Dynamsoft.DBR.BarcodeReader.InitLicense("LICENSE-KEY", out errorMsg); if (errorCode != EnumErrorCode.DBR_SUCCESS) { Console.WriteLine(errorMsg); } _barcodeReader = new Dynamsoft.DBR.BarcodeReader(); }
-
To avoid blocking the UI thread, use asynchronous barcode reading in the callback function.
public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen) { Bitmap v = new Bitmap(_previewWidth, _previewHeight, _previewStride, PixelFormat.Format24bppRgb, pBuffer); v.RotateFlip(RotateFlipType.Rotate180FlipX); if (isFinished) { this.BeginInvoke((MethodInvoker)delegate { isFinished = false; ReadBarcode(v); isFinished = true; }); } return 0; } private void ReadBarcode(Bitmap bitmap) { Bitmap nonIndexedBitmap = EnsureNonIndexedFormat(bitmap); TextResult[] results = _barcodeReader.DecodeBitmap(nonIndexedBitmap, ""); textBox1.Clear(); if (results != null) { using (Graphics g = Graphics.FromImage(nonIndexedBitmap)) { foreach (TextResult result in results) { string output = "Text: " + result.BarcodeText + Environment.NewLine + "Format: " + result.BarcodeFormatString + Environment.NewLine; Point[] points = result.LocalizationResult.ResultPoints; // Draw lines based on four points for (int i = 0; i < points.Length; i++) { Point p1 = points[i]; Point p2 = points[(i + 1) % points.Length]; g.DrawLine(new Pen(Color.Red, 3), p1, p2); } textBox1.AppendText(output); } } if (!isCameraMode) { pictureBox1.Image = nonIndexedBitmap; } } else { textBox1.AppendText("No barcode detected!" + Environment.NewLine); } }
Using Dynamsoft Barcode Reader v10.x for Barcode Decoding
The Dynamsoft Barcode Reader SDK v10.x introduces a new architecture and API. The following steps demonstrate how to use the new version for barcode decoding:
-
Create a barcode reader instance in
Form1()
:using Dynamsoft.DBR; using Dynamsoft.Core; using Dynamsoft.CVR; using Dynamsoft.License; private CaptureVisionRouter cvr; public Form1() { InitializeComponent(); string errorMsg; int errorCode = LicenseManager.InitLicense("LICENSE-KEY", out errorMsg); if (errorCode != (int)EnumErrorCode.EC_OK) Console.WriteLine("License initialization error: " + errorMsg); cvr = new CaptureVisionRouter(); }
-
Retrieve the bitmap data and convert it to
ImageData
for barcode decoding:private void ReadBarcode(Bitmap bitmap) { Bitmap nonIndexedBitmap = EnsureNonIndexedFormat(bitmap); // Convert Bitmap to ImageData byte[] bytes; int width; int height; int stride; PixelFormat pixelFormat; GetBitmapData(nonIndexedBitmap, out bytes, out width, out height, out stride, out pixelFormat); EnumImagePixelFormat format = EnumImagePixelFormat.IPF_RGB_888; switch (pixelFormat) { case PixelFormat.Format24bppRgb: format = EnumImagePixelFormat.IPF_RGB_888; break; case PixelFormat.Format32bppArgb: format = EnumImagePixelFormat.IPF_ARGB_8888; break; default: MessageBox.Show("Unsupported pixel format."); return; } ImageData data = new ImageData(bytes, width, height, stride, format); CapturedResult result = cvr.Capture(data, PresetTemplate.PT_READ_BARCODES); textBox1.Clear(); if (result == null) { MessageBox.Show("No barcode detected."); } else if (result.GetErrorCode() != 0) { MessageBox.Show("Error: " + result.GetErrorCode() + ", " + result.GetErrorString()); } else { DecodedBarcodesResult barcodesResult = result.GetDecodedBarcodesResult(); if (barcodesResult != null) { BarcodeResultItem[] items = barcodesResult.GetItems(); foreach (BarcodeResultItem barcodeItem in items) { Console.WriteLine("Result " + (Array.IndexOf(items, barcodeItem) + 1)); Console.WriteLine("Barcode Format: " + barcodeItem.GetFormatString()); Console.WriteLine("Barcode Text: " + barcodeItem.GetText()); } using (Graphics g = Graphics.FromImage(nonIndexedBitmap)) { foreach (BarcodeResultItem barcodeItem in items) { string output = "Text: " + barcodeItem.GetFormatString() + Environment.NewLine + "Format: " + barcodeItem.GetFormatString() + Environment.NewLine; Dynamsoft.Core.Point[] points = barcodeItem.GetLocation().points; // Draw lines based on four points for (int i = 0; i < points.Length; i++) { Dynamsoft.Core.Point p1 = points[i]; Dynamsoft.Core.Point p2 = points[(i + 1) % points.Length]; g.DrawLine(new Pen(Color.Red, 3), p1[0], p1[1], p2[0], p2[1]); } textBox1.AppendText(output); } } if (!isCameraMode) { pictureBox1.Image = nonIndexedBitmap; } } else { textBox1.AppendText("No barcode detected!" + Environment.NewLine); } } }
Source Code
https://github.com/yushulx/DirectShow.NET-Webcam-Barcode-Reader