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 class StreamExceptionEventArgs : EventArgs { public Exception Exception { get; set; } } public delegate void FrameEvent(object sender, FrameEventArgs args); public delegate void StreamExceptionEvent(object sender, StreamExceptionEventArgs args); internal abstract class CameraStream : IDisposable { public event FrameEvent Frame; public event StreamExceptionEvent StreamException; 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(); try { streamTask?.Wait(); } catch (AggregateException e) { if (e.InnerExceptions.Count == 1 && e.InnerExceptions[0] is TaskCanceledException) return; throw; } catch (TaskCanceledException) { } } 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] : ""); } request.ReadWriteTimeout = 10; 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 (TaskCanceledException) { } catch (Exception e) { if (cancellationToken.IsCancellationRequested) break; OnStreamException(new StreamExceptionEventArgs { Exception = e }); await Task.Delay(5000, cancellationToken); } } } protected abstract Task ReadFrames(Stream stream, CancellationToken cancellationToken); protected virtual void OnFrame(FrameEventArgs args) { Frame?.Invoke(this, args); } protected virtual void OnStreamException(StreamExceptionEventArgs args) { StreamException?.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; } } }