Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hang on stop grabbing during WPF form closing #4

Open
antonio-petricca opened this issue Jul 24, 2015 · 13 comments
Open

Hang on stop grabbing during WPF form closing #4

antonio-petricca opened this issue Jul 24, 2015 · 13 comments

Comments

@antonio-petricca
Copy link

Here is the scenario:

1 - I wrote a simple simple WPF form application
2 - At startup I start grabbing the webcam getting frames and displaying them insie a picture box
3 - At close time, inside the WPF form close event I call the webcam capture stop method
4 - The stop capture method never ends!
5 - If I call the stop capture method inside the Dispose method it works!

Could you help me please?

@Gakk
Copy link

Gakk commented Sep 13, 2016

I am also having this problem...

@Stephanowicz
Copy link

As far as I can see it happens in webcamlib.cpp @ void CameraMethods::StopCamera() ``g_pMediaControl->StopWhenReady(); or
g_pMediaControl->Stop();

This is calling virtual HRESULT STDMETHODCALLTYPE StopWhenReady( void) = 0;
in control.h

Dunno if this only happens in combination with wpf...?

So any help would be really appreciated!

Cheers, Stephan

@Stephanowicz
Copy link

O.k. - after ~6 hours digging and trying different suggestions I think the solution regarding WPF is to seperate threads for updating the imagesource in the window as suggested here:
[http://stackoverflow.com/a/33285565]
In short you need to define a bitmap in the main window class that is used as storage for the incoming frames. Then define a dispatcher timer with very short interval (~10 ms) wich updates the source of the image in your window with this bitmap ...
Still needs thorough testing, but @ moment it looks quite promising! ;)

@rockstardev
Copy link
Owner

@Stephanowicz If you guys figure out solution to problem feel free to update README.md and send me pull request - I'll gladly merge.

@nilsonsrjunior
Copy link

@Stephanowicz can you share your implementation ? thanks in advance.

@Stephanowicz
Copy link

Well, did You take a look at
http://stackoverflow.com/a/33285565 ?

@nilsonsrjunior
Copy link

Yeah.. i tried adding a DispatcherTimer but it still crashes on stop so i figured i might have done something wrong

@Stephanowicz
Copy link

O.k.,
I managed it as described in the thread with a Dispatcher timer.
The timer is enabled each time a new Frame is available.
I tried to extract it from my prog:

` in XAML
<Image x:Name="_camImage" ...

in CS
using Touchless.Vision.Camera;

	DispatcherTimer camTimer = new DispatcherTimer();
	BitmapSource _camImageSource;
    private CameraFrameSource _frameSource;
	
    public MainWindow()
    {
		...
	    camTimer.Tick += new EventHandler(camTimer_Tick);
        camTimer.Interval = new TimeSpan(0, 0, 0, 0, 5); // --> timer gets once started when image available
		...
	}

	
	private void camTimer_Tick(object sender, EventArgs e)
    {
        if (_camImageSource != null && _frameSource != null)
            _camImage.Source = _camImageSource;
        camTimer.IsEnabled = false;
    }

	
	
	#region cam

    private void btnCamStart_Click(object sender, RoutedEventArgs e)
    {
        if (_frameSource != null && _frameSource.Camera == comboBoxCameras.SelectedItem)
            return;

        thrashOldCamera();
        startCapturing();
    }
    private void btnCamStop_Click(object sender, RoutedEventArgs e)
    {
        thrashOldCamera();
    }
    private void btnCamSettings_Click(object sender, RoutedEventArgs e)
    {
        if (_frameSource != null && _frameSource.Camera != null)
        {
            _frameSource.Camera.ShowPropertiesDialog();
        }
    }
    private void btnCamListRefresh_Click(object sender, RoutedEventArgs e)
    {
        comboBoxCameras.Items.Clear();
        CameraService.ClearCameraList();
        foreach (Camera cam in CameraService.AvailableCameras)
            comboBoxCameras.Items.Add(cam);

        if (comboBoxCameras.Items.Count > 0)
            comboBoxCameras.SelectedIndex = 0;
        if (_frameSource != null && comboBoxCameras.Items.Count > 0 && comboBoxCameras.Items.Contains(_frameSource.Camera.ToString()))
            comboBoxCameras.SelectedItem = (Camera)_frameSource.Camera;

    }

    private Camera CurrentCamera
    {
        get
        {
            return comboBoxCameras.SelectedItem as Camera;
        }
    }
    private void startCapturing()
    {
        try
        {
            Camera c = (Camera)comboBoxCameras.SelectedItem;
            setFrameSource(new CameraFrameSource(c));
            _frameSource.Camera.CaptureWidth = 640;
            _frameSource.Camera.CaptureHeight = 480;
            _frameSource.Camera.Fps = 25;
            _frameSource.NewFrame += OnImageCaptured;
            _frameSource.StartFrameCapture();
        }
        catch (Exception ex)
        {
            comboBoxCameras.Text = "Select A Camera";
            MessageBox.Show(ex.Message);
        }
    }

    public void OnImageCaptured(Touchless.Vision.Contracts.IFrameSource frameSource, Touchless.Vision.Contracts.Frame frame, double fps)
    {
        _camImageSource = BitmapConversion.ToWpfBitmap(frame.Image);
        camTimer.IsEnabled = true;
    }
    private void setFrameSource(CameraFrameSource cameraFrameSource)
    {
        if (_frameSource == cameraFrameSource)
            return;

        _frameSource = cameraFrameSource;
    }
    private void thrashOldCamera()
    {

        // Trash the old camera
        if (_frameSource != null)
        {
            _frameSource.NewFrame -= OnImageCaptured;
            Dispatcher.Invoke(() => {
                _camImage.Source = null;
                _camImage.InvalidateVisual();
            });
            using (Dispatcher.DisableProcessing())
            {
                _frameSource.StopFrameCapture();
                setFrameSource(null);
                Thread.Sleep(10);
            }
        }
    }
    #endregion cam

`

@nilsonsrjunior
Copy link

nilsonsrjunior commented Aug 23, 2017

@Stephanowicz thanks for sharing it. Well, i tried your implementation but it still crashes when _frameSource.StopFrameCapture(); or _frameSource.Camera.Dispose(); is executed (i tried both).

The exception:

InnerException: null
Message: Referência de objeto não definida para uma instância de um objeto.
StackTracke: em Touchless.Vision.Camera.CameraFrameSource.OnImageCaptured(Object sender, CameraEventArgs e) em System.EventHandler1.Invoke(Object sender, TEventArgs e) em Touchless.Vision.Camera.Camera.ImageCaptured(Bitmap bitmap) em Touchless.Vision.Camera.Camera.CaptureCallbackProc(Int32 dataSize, Byte[] data) em WebCamLib.CameraMethods.CaptureCallbackDelegate.Invoke(Int32 dwSize, Byte[] abData) em WebCamLib.SampleGrabberCB.BufferCB(SampleGrabberCB* , Double SampleTime, Byte* pBuffer, Int32 BufferLen)

Any ideas ?

@Stephanowicz
Copy link

Sorry, not really...

The idea of the workaround is to put the image into a local resource in onImageCaptured : _camImageSource = BitmapConversion.ToWpfBitmap(frame.Image);
and then in an other thread (thedispatcherTimer thread) to update the wpf image with this local resource:
_camImage.Source = _camImageSource;
So You have 3 resources - the display image, the temporary image and the frame.image

If You set up everything like this, it ought to work...

@nilsonsrjunior
Copy link

nilsonsrjunior commented Aug 23, 2017

@Stephanowicz I really appreciate the effort in trying to help me.

I'll just leave my code below so you can take a look, but i've done pretty much what you did.

`
private readonly UltrasoundSettings _ultrasoundSettings;
private CameraFrameSource _frameSource;
private BitmapSource _camImageSource;
private static bool _running;
private DispatcherTimer _videoTimer = new DispatcherTimer();

    public Ultrassom()
    {
        InitializeComponent();

        _ultrasoundSettings = SimpleIoc.Default.GetInstance<UltrasoundSettings>();

        _videoTimer.Tick += new EventHandler(VideoTimer_Tick);
        _videoTimer.Interval = new TimeSpan(0, 0, 0, 0, 5); // --> timer gets once started when image available
    }

    #region Video
    private void buttonStartCapture_Click(object sender, RoutedEventArgs e)
    {
        Cnv.Children.Clear();

        StartCapturing();
    }

    private void buttonCaptureImage_Click(object sender, RoutedEventArgs e)
    {
        DeleteOldVideo();
    }

    private void StartCapturing()
    {
        if (!_running)
        {
            try
            {
                var video = CameraService.AvailableCameras.FirstOrDefault(x => x.Name == _ultrasoundSettings.Device);
                SetFrameSource(new CameraFrameSource(video));
                _frameSource.NewFrame += OnImageCaptured;
                _frameSource.StartFrameCapture();
                _running = true;
            }
            catch (Exception exception)
            {
                _running = false;
                MessageBox.Show(
                      string.Format(Messages.MSG_BOX_ERR_FalhaNaConexaoPortaComunicacao, exception.Message),
                      Messages.MSG_BOX_TITLE, MessageBoxButton.OK, MessageBoxImage.Stop);
            }
        }
    }

    private void DeleteOldVideo()
    {
        if (_running && _frameSource != null)
        {
            _frameSource.NewFrame -= OnImageCaptured;
            _running = false;

            Dispatcher.Invoke(() =>
            {
                CameraImage.Source = null;
                //CameraImage.Source = _camImageSource.ToBitmapSource();
                //CameraImage.InvalidateVisual();
            });
            using (Dispatcher.DisableProcessing())
            {
                try
                {
                    _frameSource.StopFrameCapture();
                }
                catch (Exception)
                {

                }
                finally
                {
                    SetFrameSource(null);
                    Thread.Sleep(10);
                }
            }
        }
    }

    public void OnImageCaptured(Touchless.Vision.Contracts.IFrameSource frameSource, Touchless.Vision.Contracts.Frame frame, double fps)
    {
        _camImageSource = frame.Image.ToWpfBitmap();
        _videoTimer.IsEnabled = true;
    }

    private void VideoTimer_Tick(object sender, EventArgs e)
    {
        if (_camImageSource != null && _frameSource != null)
            CameraImage.Source = _camImageSource;

        _videoTimer.IsEnabled = false;
    }

    private void SetFrameSource(CameraFrameSource videoFrameSource)
    {
        if (_frameSource == videoFrameSource)
            return;

        _frameSource = videoFrameSource;
    }
    #endregion`

@nilsonsrjunior
Copy link

@Stephanowicz i found the problem... it was my mistake.
I first found this project on CodeProject and i assumed it was the same version as the one here in github, so i updated the lib and it started working.

So once again: thanks for your time & help and i'm sorry for my fault.

@Stephanowicz
Copy link

Good to hear that it is working now!

Cheers,
Stephan

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants