2

I have a c++ dll that connects to a webcam. I am trying to call these frames into a c# wpf application for display.

I have a thread that runs in the dll and sends the data back via a callback, as below: The Updated function is called every new frame, and updates the zedframe data;

// class A
//callback from a c++ dll.
        public IntPtr zedFrame;
        public Mutex mutex = new Mutex();
        public bool bGotFrame = false;

 public event EventHandler Updated;

        protected virtual void Updated(EventArgs e, IntPtr frame)
        {

            mutex.WaitOne();   // Wait until it is safe to enter.  
            zedFrame = frame;
            mutex.ReleaseMutex();    // Release the Mutex.

            bGotFrame = true;

        }

In the same class, i have this function that 'should' grab the frame data in a thread safe way.

public IntPtr getFrame()
        {
            IntPtr tmp;
            mutex.WaitOne();   // Wait until it is safe to enter.  
            tmp = zedFrame;
            mutex.ReleaseMutex();    // Release the Mutex.

            return tmp;
        }

In a wpf form, I have an image:

 <Grid>
        <Image Name="ImageCameraFrame" Margin="5,5,5,5" MouseDown="GetImageCoordsAt"/>       
    </Grid>

and in the cs for the form, I have a DispatcherTimer

//in the image form .xaml.cs

 var timer = new DispatcherTimer
                {
                    Interval = TimeSpan.FromMilliseconds(100)
                };

                timer.Tick += Timer_Tick;

                 private void Timer_Tick(object sender, EventArgs e)
        {
            if (ClassA.bGotFrame == true)
            {
                var data = ClassA.getFrame();
                var width = 1280;
                var height = 720;
                var stride = 3 * width;
                var bitmap = BitmapSource.Create(width, height, 96, 96,
                    PixelFormats.Rgb24, null, data, stride * height, stride);
                bitmap.Freeze();


                ImageCameraFrame.Source = bitmap;

            }

        }

When I run this, it connects, shows a single frame, and then crashes the application. I am freezing the bitmap, and using a mutex. What am i doing wrong?

Is it possible to bind the Image source to a variable in another class, updated in another thread? Or do I need the timer?

anti
  • 3,011
  • 7
  • 36
  • 86
  • Instead of handling a mutex you could use a simple `lock(){}`. Freezing means it is now thread safe, what thread is the call back on, is it UI or background? – XAMlMAX Sep 18 '19 at 16:30
  • The callback in background. – anti Sep 18 '19 at 16:31
  • 3
    Could it be that the timer ticks more than once per image frame and sees a `ClassA.bGotFrame` as `true` for the same frame - and then tries to make two or more images with the same memory image? Maybe try to set `bGotFrame = false;`in `getFrame()`? –  Sep 18 '19 at 18:26
  • 1
    I would also suggest not to create a new BitmapSource for each frame. Instead, assign a single WriteableBitmap once to the Image's Source property, then repeatedly update its BackBuffer property: https://stackoverflow.com/a/12587470/1136211 – Clemens Sep 19 '19 at 16:16

1 Answers1

-1

I had a somewhat similar situation and ended up with the following approach. Perhaps doing the ImageSource conversion with GDI cleanup will help you.

Xaml:

<Image DockPanel.Dock="Left" Source="{Binding AssistantLogo}"
       RenderOptions.BitmapScalingMode="NearestNeighbor" UseLayoutRounding="True"
       Width="{Binding RelativeSource={RelativeSource Self}, Path=Source.PixelWidth}" 
       Height="{Binding RelativeSource={RelativeSource Self}, Path=Source.PixelHeight}"/>

ViewModel:

if (Patient.ContainsMeaningfulData(applicationState.Patient))
  _viewModel.AssistantLogo = bitmap1.ToImageSource();
else
  _viewModel.AssistantLogo = bitmap2.ToImageSource();

Extension method:

using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;

    /// <summary>
    /// Convert a winforms image to a wpf-usable imageSource
    /// </summary>
    /// <param name="bitmap">winforms image</param>
    /// <returns>wpf imageSource object</returns>
    public static ImageSource ToImageSource(this System.Drawing.Bitmap bitmap)
    {
      var hbitmap = bitmap.GetHbitmap();
      try
      {
        var imageSource = Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight(bitmap.Width, bitmap.Height));

        return imageSource;
      }
      finally
      {
        Gdi32.DeleteObject(hbitmap);
      }
    }
Bill Tarbell
  • 4,933
  • 2
  • 32
  • 52