127 lines
3.5 KiB
C#
127 lines
3.5 KiB
C#
|
using System;
|
|||
|
using System.Drawing;
|
|||
|
using System.IO;
|
|||
|
using System.Net;
|
|||
|
using System.Threading;
|
|||
|
using System.Threading.Tasks;
|
|||
|
|
|||
|
namespace IPCamAppBar
|
|||
|
{
|
|||
|
public class FrameEventArgs : EventArgs
|
|||
|
{
|
|||
|
public Image Image { get; set; }
|
|||
|
}
|
|||
|
|
|||
|
public delegate void FrameEvent(object sender, FrameEventArgs args);
|
|||
|
|
|||
|
|
|||
|
internal abstract class CameraStream : IDisposable
|
|||
|
{
|
|||
|
public event FrameEvent Frame;
|
|||
|
|
|||
|
private readonly CancellationTokenSource cancelTaskTokenSource = new CancellationTokenSource();
|
|||
|
private Task streamTask;
|
|||
|
|
|||
|
|
|||
|
protected CameraStream()
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public void Start(string url)
|
|||
|
{
|
|||
|
if (streamTask != null)
|
|||
|
throw new InvalidOperationException("CameraStream already started");
|
|||
|
|
|||
|
streamTask = Task.Run(() => Fetch(url, cancelTaskTokenSource.Token));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public void Dispose()
|
|||
|
{
|
|||
|
cancelTaskTokenSource.Cancel();
|
|||
|
streamTask?.Wait();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
private async Task Fetch(string url, CancellationToken cancellationToken)
|
|||
|
{
|
|||
|
while (!cancellationToken.IsCancellationRequested)
|
|||
|
{
|
|||
|
var uri = new Uri(url);
|
|||
|
var request = WebRequest.CreateHttp(uri);
|
|||
|
|
|||
|
if (!string.IsNullOrEmpty(uri.UserInfo))
|
|||
|
{
|
|||
|
var parts = uri.UserInfo.Split(':');
|
|||
|
request.Credentials = new NetworkCredential(parts[0], parts.Length > 1 ? parts[1] : "");
|
|||
|
}
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
HttpWebResponse response;
|
|||
|
|
|||
|
using (cancellationToken.Register(() => request.Abort(), false))
|
|||
|
{
|
|||
|
response = (HttpWebResponse)await request.GetResponseAsync();
|
|||
|
cancellationToken.ThrowIfCancellationRequested();
|
|||
|
}
|
|||
|
|
|||
|
if (response.StatusCode != HttpStatusCode.OK)
|
|||
|
throw new WebException(response.StatusDescription);
|
|||
|
|
|||
|
using (var responseStream = response.GetResponseStream())
|
|||
|
{
|
|||
|
await ReadFrames(responseStream, cancellationToken);
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Exception e)
|
|||
|
{
|
|||
|
if (cancellationToken.IsCancellationRequested)
|
|||
|
break;
|
|||
|
|
|||
|
// TODO onException
|
|||
|
await Task.Delay(5000, cancellationToken);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
protected abstract Task ReadFrames(Stream stream, CancellationToken cancellationToken);
|
|||
|
|
|||
|
|
|||
|
protected virtual void OnFrame(FrameEventArgs args)
|
|||
|
{
|
|||
|
Frame?.Invoke(this, args);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
internal static class BufferExtensions
|
|||
|
{
|
|||
|
public static int Find(this byte[] buffer, byte[] pattern, int limit = int.MaxValue, int startAt = 0)
|
|||
|
{
|
|||
|
var patternIndex = 0;
|
|||
|
var bufferIndex = 0;
|
|||
|
|
|||
|
for (bufferIndex = startAt; bufferIndex < buffer.Length && patternIndex < pattern.Length && bufferIndex < limit; bufferIndex++)
|
|||
|
{
|
|||
|
if (buffer[bufferIndex] == pattern[patternIndex])
|
|||
|
{
|
|||
|
patternIndex++;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
patternIndex = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (patternIndex == pattern.Length)
|
|||
|
return bufferIndex - pattern.Length;
|
|||
|
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|