Open antonio-petricca opened 9 years ago
I am also having this problem...
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
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! ;)
@Stephanowicz If you guys figure out solution to problem feel free to update README.md and send me pull request - I'll gladly merge.
@Stephanowicz can you share your implementation ? thanks in advance.
Well, did You take a look at http://stackoverflow.com/a/33285565 ?
Yeah.. i tried adding a DispatcherTimer but it still crashes on stop so i figured i might have done something wrong
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
`
@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 ?
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...
@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`
@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.
Good to hear that it is working now!
Cheers, Stephan
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?