I have an asynchronous method (IPCameraMethod) that handles IP camera streams. It starts and cancels tasks which manage these streams via WebSockets. The tasks are started in a separate class, IPCameraService, with its Main method.
When I attempt to cancel a task using a CancellationToken, the associated WebSocket is closed immediately, preventing me from sending a "Stop_Stream" command that should be executed before the WebSocket closes. This "Stop_Stream" message is crucial for gracefully shutting down the stream on the server side.
Here's a simplified version of my code:
internal static Dictionary<int, Tuple<int, CancellationTokenSource, Task, CameraType>> tokenSources = new Dictionary<int, Tuple<int, CancellationTokenSource, Task, CameraType>>();
In DeviceSettings class:
private async Task IPCameraMethod()
{
if (string.IsNullOrWhiteSpace(textBoxUrl.Text) ||
!int.TryParse(SourceComboBox.Name.Replace("comboBox", ""), out int cameraIndex) ||
comboBoxSavedCameras.SelectedIndex < 0)
{
return;
}
string url = textBoxUrl.Text;
int selectedItemIndex = comboBoxSavedCameras.SelectedIndex;
if (tokenSources.TryGetValue(cameraIndex, out var tuple))
{
if (selectedItemIndex != tuple.Item1)
{
tuple.Item2.Cancel();
try
{
await tuple.Item3; // Await the task's completion
}
catch (OperationCanceledException)
{
// Task was cancelled, expected behavior
}
tuple.Item2.Dispose();
tokenSources.Remove(cameraIndex);
}
else
{
return; // If selected item is the same, we do not need to create a new task
}
}
// Now that the old task has been cancelled and awaited, start the new task.
var newCts = new CancellationTokenSource();
Task newTask = null;
if (Camera != null)
{
newTask = new TaskFactory().StartNew(() => Camera.IPCameraService.Main(SourceImageControl, selectedItemIndex, newCts.Token, url));
}
else
{
newTask = new TaskFactory().StartNew(() => (BaseViewModel as AddFaceViewModel).IPCameraService.Main(SourceImageControl, selectedItemIndex, newCts.Token, url));
}
Debug.WriteLine("New Task Created!");
tokenSources[cameraIndex] = Tuple.Create(selectedItemIndex, newCts, newTask, CameraType.IP);
}
In IPCameraService class:
public async Task Main(System.Windows.Controls.Image imageControl, int cameraIndex, CancellationToken token, string cameraUrl)
{
CameraURLs[cameraIndex] = cameraUrl;
await StartSocket(cameraIndex, token);
await StartStream(cameraIndex, cameraUrl, token);
var stopStreamCompletion = new TaskCompletionSource<bool>();
EventHandler(cameraUrl, cameraIndex, token);
while (!token.IsCancellationRequested) // Added a token check to allow stopping the loop externally
{
var frame = await RunYolo(cameraIndex, token);
if (frame != null)
await UpdateDisplay(frame, imageControl, token);
}
var Stop_Server = new
{
command = "Stop_Stream"
};
var ws = CameraWebsockets[cameraIndex];
var Start_ServerJson = JsonConvert.SerializeObject(Stop_Server);
var Start_ServerBytes = Encoding.UTF8.GetBytes(Start_ServerJson);
await ws.SendAsync(new ArraySegment<byte>(Start_ServerBytes, 0, Start_ServerBytes.Length), WebSocketMessageType.Text, true, token);
Debug.WriteLine("Websocket Cleared!");
}
The issue is with tuple.Item2.Cancel();
, which is intended to cancel the ongoing task and immediately starts a clean-up sequence. I need to send the "Stop_Stream" command before this clean-up happens, but the WebSocket closes as soon as the cancellation is requested.
How can I restructure my cancellation and clean-up logic to allow sending the "Stop_Stream" message before the WebSocket is closed?
What I've tried:
- I attempted to catch the OperationCanceledException hoping the WebSocket would still be open to send the message, but it's already closed by that point.
- Moving the WebSocket sending logic to the catch block does not work either, as the WebSocket is not accessible after cancellation.
Question:
- How can I ensure the "Stop_Stream" message is sent on the WebSocket before the cancellation of the task actually causes the WebSocket to close?
Additional Context:
The IPCameraMethod and Main methods are in different classes, where in which the IPCameraMethod calls the Main method. The WebSocket connection is crucial and should remain open until the "Stop_Stream" command is sent. C# .NET Framework 4.7.2 is being used.