178 lines
5.4 KiB
C#
178 lines
5.4 KiB
C#
using System;
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Reflection;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
using FastBitmapLib;
|
|
using IPCamLib;
|
|
|
|
namespace IPCamAppBar
|
|
{
|
|
public partial class CameraView : UserControl, IRetryableCameraObserver
|
|
{
|
|
private readonly ICamera camera;
|
|
private readonly bool overlayDateTime;
|
|
private Bitmap viewBitmap;
|
|
private CancellationTokenSource streamCancellationTokenSource;
|
|
|
|
public CameraView(ICamera camera, bool overlayDateTime)
|
|
{
|
|
this.camera = camera;
|
|
this.overlayDateTime = overlayDateTime;
|
|
InitializeComponent();
|
|
|
|
StartCamera();
|
|
|
|
Disposed += (_, _) =>
|
|
{
|
|
StopCamera();
|
|
};
|
|
}
|
|
|
|
private void StartCamera()
|
|
{
|
|
streamCancellationTokenSource = new CancellationTokenSource();
|
|
|
|
Task.Run(async () =>
|
|
{
|
|
var retryableCamera = new RetryableCamera(camera);
|
|
await retryableCamera.Fetch(this, streamCancellationTokenSource.Token);
|
|
});
|
|
}
|
|
|
|
|
|
private void StopCamera()
|
|
{
|
|
streamCancellationTokenSource.Cancel();
|
|
}
|
|
|
|
|
|
private Bitmap resizedBitmap;
|
|
|
|
public Task OnFrame(Image image)
|
|
{
|
|
if (streamCancellationTokenSource.IsCancellationRequested)
|
|
return Task.CompletedTask;
|
|
|
|
if (overlayDateTime || image is not Bitmap bitmap || image.Width != Width || image.Height != Height)
|
|
{
|
|
resizedBitmap ??= new Bitmap(Width, Height);
|
|
|
|
using var graphics = Graphics.FromImage(resizedBitmap);
|
|
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
|
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
|
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
|
|
|
graphics.DrawImage(image, 0, 0, resizedBitmap.Width, resizedBitmap.Height);
|
|
|
|
if (overlayDateTime)
|
|
{
|
|
using (var path = new GraphicsPath())
|
|
{
|
|
path.AddString(
|
|
DateTime.Now.ToString("G"),
|
|
FontFamily.GenericSansSerif,
|
|
(int) FontStyle.Regular,
|
|
graphics.DpiY * 14 / 72,
|
|
Rectangle.Inflate(new Rectangle(0, 0, resizedBitmap.Width, resizedBitmap.Height), -4, -4),
|
|
new StringFormat
|
|
{
|
|
Alignment = StringAlignment.Far,
|
|
LineAlignment = StringAlignment.Far
|
|
});
|
|
|
|
graphics.DrawPath(new Pen(Color.Black, 3), path);
|
|
graphics.FillPath(Brushes.White, path);
|
|
}
|
|
}
|
|
|
|
graphics.Flush();
|
|
bitmap = resizedBitmap;
|
|
}
|
|
|
|
Invoke(new Action(() => { InvokeFrame(bitmap); }));
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
|
|
public Task OnFetch()
|
|
{
|
|
Invoke(new Action(InvokeFetch));
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
|
|
public Task OnDisconnected(Exception exception, TimeSpan retryDelay)
|
|
{
|
|
Invoke(new Action(() => { InvokeDisconnected(exception, retryDelay); }));
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
|
|
private void InvokeFrame(Bitmap bitmap)
|
|
{
|
|
if (IsDisposed)
|
|
return;
|
|
|
|
ConnectingLabel.Visible = false;
|
|
StreamView.Visible = true;
|
|
IssueLabel.Visible = false;
|
|
PausedOverlay.Visible = false;
|
|
|
|
|
|
if (viewBitmap == null)
|
|
{
|
|
viewBitmap = new Bitmap(Width, Height);
|
|
StreamView.Image = viewBitmap;
|
|
}
|
|
|
|
using (var fastViewBitmap = viewBitmap.FastLock())
|
|
{
|
|
fastViewBitmap.CopyRegion(bitmap, new Rectangle(Point.Empty, bitmap.Size), new Rectangle(Point.Empty, viewBitmap.Size));
|
|
}
|
|
|
|
StreamView.Invalidate();
|
|
}
|
|
|
|
|
|
private void InvokeFetch()
|
|
{
|
|
if (IsDisposed)
|
|
return;
|
|
|
|
IssueLabel.Text = "Connecting...";
|
|
IssueLabel.Visible = true;
|
|
IssueLabel.BringToFront();
|
|
}
|
|
|
|
|
|
private void InvokeDisconnected(Exception exception, TimeSpan retryDelay)
|
|
{
|
|
if (IsDisposed || streamCancellationTokenSource.IsCancellationRequested)
|
|
return;
|
|
|
|
IssueLabel.Text = (exception?.Message ?? "Camera disconnected") + $", retrying in {retryDelay.TotalSeconds} seconds";
|
|
IssueLabel.Visible = true;
|
|
IssueLabel.BringToFront();
|
|
}
|
|
|
|
|
|
public void SetPaused(bool pause)
|
|
{
|
|
if (pause)
|
|
{
|
|
StopCamera();
|
|
|
|
PausedOverlay.Left = (Width - PausedOverlay.Width) / 2;
|
|
PausedOverlay.Top = (Height - PausedOverlay.Height) / 2;
|
|
PausedOverlay.Visible = true;
|
|
PausedOverlay.BringToFront();
|
|
}
|
|
else
|
|
StartCamera();
|
|
}
|
|
}
|
|
}
|