.NET Barcode Scanner & Reader with C# and Webcam: 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);
    

Scanning 1D/2D Barcodes with Dynamsoft Barcode Reader SDK

The following steps demonstrate how to use the Dynamsoft Barcode Reader SDK for barcode detection:

  1. Set the license key and instantiate a CaptureVisionRouter object 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 detection:

     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