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.

DirectShow.NET Webcam barcode reader

Prerequisites

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:

  1. Get connected devices.

     DsDevice[] devices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);
    
  2. 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);
    
  3. Set the filter graph to the capture graph.

     hr = this.captureGraphBuilder.SetFiltergraph(this.graphBuilder);
     DsError.ThrowExceptionForHR(hr);
    
  4. 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;
    
  5. Add a camera source to the graph.

     hr = this.graphBuilder.AddFilter(sourceFilter, "Video Capture");
     DsError.ThrowExceptionForHR(hr);
    
  6. 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);
    
  7. 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;
     }
    
  8. Render the video stream.

     hr = this.captureGraphBuilder.RenderStream(PinCategory.Preview, MediaType.Video, sourceFilter, (sampleGrabber as IBaseFilter), null);
     DsError.ThrowExceptionForHR(hr);
    
  9. 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);
    
  10. 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

  1. 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();
     }
    
  2. 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:

  1. 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();
     }
    
  2. 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