The application attempts to provide the user with the ability to display 1 or more camera feeds in different ImageViewerWpfControls. The user can also quickly swap between camera feeds effectively disposing of the existing one and introducing a new one.
The resulting scenario can be described as the user is presented with 3 video panes, the user can display video in each pane on command, and at anytime replace that video feed with a different one. This causes the existing video feed in that pane to be disposed and the new one loaded. This can happen as fast as the user is able to issue the commands.
The problem appears to occur when video is swapped between quickly. If we slow down the changes in video feeds, there doesn’t appear to be any issue with cleanup. However, when this is done quickly, at some point the application will hang. Upon further inspection, it appears the Main thread and VideoImageManager thread are sitting on a synchronization wait call.
We have reproduced the issue in our office and attached the visual studio debugger to our application during the hang. The following thread information was available (limited).
Main Thread Main Thread WindowsBase.dll!System.Windows.Threading.DispatcherSynchronizationContext.Wait
Worker Thread VideoImageManager Thread WindowsBase.dll!System.Windows.Threading.DispatcherSynchronizationContext.Wait
Worker Thread <No Name> VideoOS.UI.Common.WPF.VideoPlayer.dll!VideoOS.UI.Common.WPF.VideoPlayer.Toolkit.ToolkitWrapper.HandleOutOfBandData
Worker Thread QueryConfigurationChanges thread VideoOS.Platform.SDK.dll!VideoOS.Platform.SDK.Config.QueryConfigurationChanges.QueryThread
Worker Thread ProtocolHandler.ReadThread Microsoft.VisualStudio.DesignTools.WpfTap.dll!Microsoft.VisualStudio.DesignTools.WpfTap.Networking.AnonymousPipe.WaitForCondition
Worker Thread ProtocolHandler.SendThread Microsoft.VisualStudio.DesignTools.WpfTap.dll!Microsoft.VisualStudio.DesignTools.WpfTap.Networking.ProtocolHandler.SendThread
It appears a deadlock is occurring, however we are having trouble pinning down why it is occurring.
The below code is used to create and destroy the camera feed.
public void Initialize(bool isMulticast = true, bool isMaintainAspectRatio = true)
{
var playbackController = ClientControl.Instance.GeneratePlaybackController();
videoControl.PlaybackControllerFQID = playbackController;
this.playbackControlInstance = ClientControl.Instance.GetPlaybackController(playbackController);
videoControl.CameraFQID = this.Camera.FQID;
videoControl.EnableVisibleLiveIndicator = false;
videoControl.EnableVisibleHeader = false;
videoControl.EnableMouseControlledPtz = false;
videoControl.EnableVisibleCameraName = false;
videoControl.EnableMouseControlledPtz = false;
videoControl.EnableMousePtzEmbeddedHandler = false;
videoControl.MaintainImageAspectRatio = isMaintainAspectRatio;
videoControl.UsingMulticast = isMulticast;
videoControl.SetVideoQuality(0, 1);
videoControl.LiveStreamInformationReceived += this.VideoControl_LiveStreamInformationReceived;
videoControl.ConnectResponseReceived += this.VideoControl_ConnectResponseReceived;
this.receiver = EnvironmentManager.Instance.RegisterReceiver(PlaybackTimeChangedHandler,
new MessageIdFilter(MessageId.SmartClient.PlaybackCurrentTimeIndication));
if (Dispatcher.CurrentDispatcher.Thread.GetApartmentState() == System.Threading.ApartmentState.STA)
{
dispatcher = Dispatcher.CurrentDispatcher;
}
else
{
throw new InvalidOperationException("Must call Initialize on UI thread.");
}
}
public void JumpToLive()
{
this.CheckInitialization();
if (this.mode != VideoMode.Live)
{
EnvironmentManager.Instance.SendMessage(new VideoOS.Platform.Messaging.Message(MessageId.System.ModeChangeCommand, Mode.ClientLive), videoControl.PlaybackControllerFQID);
this.OnVideoModeChanged(VideoMode.Live);
}
}
private void CheckInitialization()
{
lock (initializeSync)
{
if (mode == VideoMode.Uninitialized)
{
dispatcher.Invoke(() => ConnectControl());
Thread.Sleep(1000);
}
}
dispatcher.Invoke(() => videoControl.ClearOverlay(OverlayId));
}
private void ConnectControl()
{
this.videoControl.Initialize();
videoControl.Connect();
videoControl.Selected = true;
videoControl.ConfigureOverlayText(true);
this.OnVideoModeChanged(VideoMode.Live);
}
When we are finished with the viewer we then call the following:
public void EndControl()
{
dispatcher.Invoke(() =>
{
videoControl.LiveStreamInformationReceived -= this.VideoControl_LiveStreamInformationReceived;
videoControl.ConnectResponseReceived -= this.VideoControl_ConnectResponseReceived;
videoControl.ClearOverlay(OverlayId);
EnvironmentManager.Instance.UnRegisterReceiver(this.receiver);
this.videoControl.Disconnect();
this.videoControl.Close();
this.videoControl.Dispose();
this.videoControl = null;
this.dispatcher = null;
this.OnNewVideoMessage("Disposed Milestone CameraFeed");
});
}