83 lines
2.7 KiB
C#
83 lines
2.7 KiB
C#
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
// ReSharper disable UnusedMember.Global - public API
|
|
|
|
namespace IPCamLib
|
|
{
|
|
/// <summary>
|
|
/// Receives frames from an ICamera as well as status updates.
|
|
/// </summary>
|
|
public interface IRetryableCameraObserver : ICameraObserver
|
|
{
|
|
/// <summary>
|
|
/// Called when a new Fetch attempt has started.
|
|
/// </summary>
|
|
Task OnFetch();
|
|
|
|
/// <summary>
|
|
/// Called when the stream was disconnected.
|
|
/// </summary>
|
|
/// <param name="exception">Contains the exception, if any occured. Null for graceful disconnects and timeouts.</param>
|
|
/// <param name="retryDelay">The delay until the next Fetch is attempted.</param>
|
|
Task OnDisconnected(Exception exception, TimeSpan retryDelay);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Implements retry logic for an ICamera instance.
|
|
/// </summary>
|
|
public class RetryableCamera
|
|
{
|
|
private readonly ICamera camera;
|
|
|
|
|
|
/// <summary>
|
|
/// Creates a new instance of a RetryableCamera.
|
|
/// </summary>
|
|
/// <param name="camera">The camera instance to fetch from.</param>
|
|
public RetryableCamera(ICamera camera)
|
|
{
|
|
this.camera = camera;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Starts receiving frames and will retry when disconnected.
|
|
/// </summary>
|
|
/// <param name="observer">The observer implementation to receive frames and status updates.</param>
|
|
/// <param name="cancellationToken">A CancellationToken which will stop the camera stream when cancelled.</param>
|
|
public async Task Fetch(IRetryableCameraObserver observer, CancellationToken cancellationToken)
|
|
{
|
|
while (!cancellationToken.IsCancellationRequested)
|
|
{
|
|
// TODO incremental back-off?
|
|
var retryDelay = TimeSpan.FromSeconds(5);
|
|
var exception = false;
|
|
|
|
try
|
|
{
|
|
await observer.OnFetch();
|
|
await camera.Fetch(observer, cancellationToken);
|
|
}
|
|
catch (TaskCanceledException)
|
|
{
|
|
// Empty by design
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
await observer.OnDisconnected(e, retryDelay);
|
|
exception = true;
|
|
}
|
|
|
|
|
|
if (!exception && !cancellationToken.IsCancellationRequested)
|
|
await observer.OnDisconnected(null, retryDelay);
|
|
|
|
await Task.Delay(retryDelay, cancellationToken);
|
|
}
|
|
}
|
|
}
|
|
}
|