// Worker thread private void WorkerThread() { // buffer to read stream var buffer = new byte[BufSize]; // JPEG magic number var jpegMagic = new byte[] { 0xFF, 0xD8, 0xFF }; var encoding = new ASCIIEncoding(); var res = ReasonToFinishPlaying.StoppedByUser; while (!_stopEvent.WaitOne(0, false)) { // reset reload event _reloadEvent.Reset(); // HTTP web request HttpWebRequest request = null; // web response WebResponse response = null; // stream for MJPEG downloading Stream stream = null; // boundary between images (string and binary versions) byte[] boundary; string boudaryStr = null; // length of boundary // flag signaling if boundary was checked or not bool boundaryIsChecked = false; // read amounts and positions int todo = 0, total = 0, pos = 0, align = 1; int start = 0; // align // 1 = searching for image start // 2 = searching for image end try { // create request request = GenerateRequest(_source); // get response response = request.GetResponse(); // check content type string contentType = response.ContentType; string[] contentTypeArray = contentType.Split('/'); // "application/octet-stream" int boundaryLen; if ((contentTypeArray[0] == "application") && (contentTypeArray[1] == "octet-stream")) { boundaryLen = 0; boundary = new byte[0]; } else if ((contentTypeArray[0] == "multipart") && (contentType.Contains("mixed"))) { // get boundary int boundaryIndex = contentType.IndexOf("boundary", 0, StringComparison.Ordinal); if (boundaryIndex != -1) { boundaryIndex = contentType.IndexOf("=", boundaryIndex + 8, StringComparison.Ordinal); } if (boundaryIndex == -1) { // try same scenario as with octet-stream, i.e. without boundaries boundaryLen = 0; boundary = new byte[0]; } else { boudaryStr = contentType.Substring(boundaryIndex + 1); // remove spaces and double quotes, which may be added by some IP cameras boudaryStr = boudaryStr.Trim(' ', '"'); boundary = encoding.GetBytes(boudaryStr); boundaryLen = boundary.Length; boundaryIsChecked = false; } } else { if (contentType == "text/html") { try { //read body var sr = new StreamReader(response.GetResponseStream()); var html = sr.ReadToEnd(); if (html.IndexOf("setup_kakulens.html", StringComparison.Ordinal) != -1) { //hack for panasonic cameras that redirect on reboot in privacy mode - POST a command to disable privacy if (DisablePrivacy(request)) { _needsPrivacyEnabledTarget = Helper.Now.AddSeconds(4); _needsPrivacyEnabled = true; } } } catch (Exception ex) { MainForm.LogExceptionToFile(ex); } //continue to throw the invalid content type error as the next retry should connect } throw new Exception("Invalid content type."); } // get response stream stream = response.GetResponseStream(); stream.ReadTimeout = _requestTimeout; // loop while ((!_stopEvent.WaitOne(0, false)) && (!_reloadEvent.WaitOne(0, false))) { // check total read if (total > BufSize - ReadSize) { total = pos = todo = 0; } // read next portion from stream int read; if ((read = stream.Read(buffer, total, ReadSize)) == 0) { throw new ApplicationException(); } total += read; todo += read; // increment received bytes counter _bytesReceived += read; // do we need to check boundary ? if ((boundaryLen != 0) && (!boundaryIsChecked)) { // some IP cameras, like AirLink, claim that boundary is "myboundary", // when it is really "--myboundary". this needs to be corrected. pos = ByteArrayUtils.Find(buffer, boundary, 0, todo); // continue reading if boudary was not found if (pos == -1) { continue; } for (int i = pos - 1; i >= 0; i--) { byte ch = buffer[i]; if ((ch == (byte)'\n') || (ch == (byte)'\r')) { break; } boudaryStr = (char)ch + boudaryStr; } boundary = encoding.GetBytes(boudaryStr); boundaryLen = boundary.Length; boundaryIsChecked = true; } // search for image start if ((align == 1) && (todo >= jpegMagic.Length)) { start = ByteArrayUtils.Find(buffer, jpegMagic, pos, todo); if (start != -1) { // found JPEG start pos = start + jpegMagic.Length; todo = total - pos; align = 2; } else { // delimiter not found todo = jpegMagic.Length - 1; pos = total - todo; } } // search for image end ( boundaryLen can be 0, so need extra check ) while ((align == 2) && (todo != 0) && (todo >= boundaryLen)) { int stop = ByteArrayUtils.Find(buffer, (boundaryLen != 0) ? boundary : jpegMagic, pos, todo); if (stop != -1) { // increment frames counter _framesReceived++; // image at stop if ((NewFrame != null) && (!_stopEvent.WaitOne(0, false))) { if (!String.IsNullOrEmpty(DecodeKey)) { byte[] marker = Encoding.ASCII.GetBytes(DecodeKey); using (var ms = new MemoryStream(buffer, start + jpegMagic.Length, jpegMagic.Length + marker.Length)) { var key = new byte[marker.Length]; ms.Read(key, 0, marker.Length); if (!ByteArrayUtils.UnsafeCompare(marker, key)) { throw (new Exception("Image Decode Failed - Check the decode key matches the encode key on ispy server")); } } using (var ms = new MemoryStream(buffer, start + marker.Length, stop - start - marker.Length)) { ms.Seek(0, SeekOrigin.Begin); ms.WriteByte(jpegMagic[0]); ms.WriteByte(jpegMagic[1]); ms.WriteByte(jpegMagic[2]); ms.Seek(0, SeekOrigin.Begin); using (var bitmap = (Bitmap)Image.FromStream(ms)) { NewFrame(this, new NewFrameEventArgs(bitmap)); } } } else { using (var ms = new MemoryStream(buffer, start, stop - start)) { using (var bitmap = (Bitmap)Image.FromStream(ms)) { NewFrame(this, new NewFrameEventArgs(bitmap)); } } } if (_needsPrivacyEnabled && _needsPrivacyEnabledTarget < Helper.Now) { if (EnablePrivacy(request)) { _needsPrivacyEnabled = false; } } } // shift array pos = stop + boundaryLen; todo = total - pos; System.Array.Copy(buffer, pos, buffer, 0, todo); total = todo; pos = 0; align = 1; } else { // boundary not found if (boundaryLen != 0) { todo = boundaryLen - 1; pos = total - todo; } else { todo = 0; pos = total; } } } } } catch (ApplicationException) { // do nothing for Application Exception, which we raised on our own // wait for a while before the next try Thread.Sleep(250); } catch (ThreadAbortException) { break; } catch (Exception ex) { // provide information to clients MainForm.LogExceptionToFile(ex); res = ReasonToFinishPlaying.DeviceLost; break; // wait for a while before the next try //Thread.Sleep(250); } finally { // abort request if (request != null) { try { request.Abort(); } catch {} request = null; } // close response stream if (stream != null) { try { stream.Flush(); } catch { } try { stream.Close(); } catch { } try { stream.Dispose(); } catch {} stream = null; } // close response if (response != null) { try { response.Close(); } catch { } response = null; } } // need to stop ? if (_stopEvent.WaitOne(0, false)) { break; } } if (PlayingFinished != null) { PlayingFinished(this, res); } }
// Worker thread private void WorkerThread() { // buffer to read stream var buffer = new byte[BufSize]; // JPEG magic number var jpegMagic = new byte[] { 0xFF, 0xD8, 0xFF }; var encoding = new ASCIIEncoding(); while (!_stopEvent.WaitOne(0, false)) { // reset reload event _reloadEvent.Reset(); // HTTP web request HttpWebRequest request = null; // web responce WebResponse response = null; // stream for MJPEG downloading Stream stream = null; // boundary betweeen images (string and binary versions) byte[] boundary; string boudaryStr = null; // length of boundary // flag signaling if boundary was checked or not bool boundaryIsChecked = false; // read amounts and positions int todo = 0, total = 0, pos = 0, align = 1; int start = 0; // align // 1 = searching for image start // 2 = searching for image end try { // create request request = (HttpWebRequest)WebRequest.Create(_source); // set user agent if (_userAgent != null) { request.UserAgent = _userAgent; } // set proxy if (_proxy != null) { request.Proxy = _proxy; } if (_usehttp10) { request.ProtocolVersion = HttpVersion.Version10; } // set timeout value for the request request.Timeout = _requestTimeout; request.AllowAutoRedirect = true; ServicePointManager.ServerCertificateValidationCallback += Certificates.ValidateRemoteCertificate; // set login and password if ((_login != null) && (_password != null) && (_login != string.Empty)) { request.Credentials = new NetworkCredential(_login, _password); } // set connection group name if (_useSeparateConnectionGroup) { request.ConnectionGroupName = GetHashCode().ToString(); } // force basic authentication through extra headers if required var authInfo = ""; if (!String.IsNullOrEmpty(_login)) { authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(_login + ":" + _password)); request.Headers["Authorization"] = "Basic " + authInfo; } if (!String.IsNullOrEmpty(_cookies)) { _cookies = _cookies.Replace("[AUTH]", authInfo); var myContainer = new CookieContainer(); string[] coll = _cookies.Split(';'); foreach (var ckie in coll) { if (!String.IsNullOrEmpty(ckie)) { string[] nv = ckie.Split('='); if (nv.Length == 2) { var cookie = new Cookie(nv[0].Trim(), nv[1].Trim()); myContainer.Add(new Uri(request.RequestUri.ToString()), cookie); } } } request.CookieContainer = myContainer; } // get response response = request.GetResponse(); // check content type string contentType = response.ContentType; string[] contentTypeArray = contentType.Split('/'); // "application/octet-stream" int boundaryLen; if ((contentTypeArray[0] == "application") && (contentTypeArray[1] == "octet-stream")) { boundaryLen = 0; boundary = new byte[0]; } else if ((contentTypeArray[0] == "multipart") && (contentType.Contains("mixed"))) { // get boundary int boundaryIndex = contentType.IndexOf("boundary", 0); if (boundaryIndex != -1) { boundaryIndex = contentType.IndexOf("=", boundaryIndex + 8); } if (boundaryIndex == -1) { // try same scenario as with octet-stream, i.e. without boundaries boundaryLen = 0; boundary = new byte[0]; } else { boudaryStr = contentType.Substring(boundaryIndex + 1); // remove spaces and double quotes, which may be added by some IP cameras boudaryStr = boudaryStr.Trim(' ', '"'); boundary = encoding.GetBytes(boudaryStr); boundaryLen = boundary.Length; boundaryIsChecked = false; } } else { throw new Exception("Invalid content type."); } // get response stream stream = response.GetResponseStream(); stream.ReadTimeout = _requestTimeout; // loop while ((!_stopEvent.WaitOne(0, false)) && (!_reloadEvent.WaitOne(0, false))) { // check total read if (total > BufSize - ReadSize) { total = pos = todo = 0; } // read next portion from stream int read; if ((read = stream.Read(buffer, total, ReadSize)) == 0) { throw new ApplicationException(); } total += read; todo += read; // increment received bytes counter _bytesReceived += read; // do we need to check boundary ? if ((boundaryLen != 0) && (!boundaryIsChecked)) { // some IP cameras, like AirLink, claim that boundary is "myboundary", // when it is really "--myboundary". this needs to be corrected. pos = ByteArrayUtils.Find(buffer, boundary, 0, todo); // continue reading if boudary was not found if (pos == -1) { continue; } for (int i = pos - 1; i >= 0; i--) { byte ch = buffer[i]; if ((ch == (byte)'\n') || (ch == (byte)'\r')) { break; } boudaryStr = (char)ch + boudaryStr; } boundary = encoding.GetBytes(boudaryStr); boundaryLen = boundary.Length; boundaryIsChecked = true; } // search for image start if ((align == 1) && (todo >= jpegMagic.Length)) { start = ByteArrayUtils.Find(buffer, jpegMagic, pos, todo); if (start != -1) { // found JPEG start pos = start + jpegMagic.Length; todo = total - pos; align = 2; } else { // delimiter not found todo = jpegMagic.Length - 1; pos = total - todo; } } // search for image end ( boundaryLen can be 0, so need extra check ) while ((align == 2) && (todo != 0) && (todo >= boundaryLen)) { int stop = ByteArrayUtils.Find(buffer, (boundaryLen != 0) ? boundary : jpegMagic, pos, todo); if (stop != -1) { // increment frames counter _framesReceived++; // image at stop if ((NewFrame != null) && (!_stopEvent.WaitOne(0, false))) { Bitmap bitmap; if (!String.IsNullOrEmpty(DecodeKey)) { byte[] marker = Encoding.ASCII.GetBytes(DecodeKey); using (var ms = new MemoryStream(buffer, start + jpegMagic.Length, jpegMagic.Length + marker.Length)) { var key = new byte[marker.Length]; ms.Read(key, 0, marker.Length); if (!ByteArrayUtils.UnsafeCompare(marker, key)) { throw (new Exception("Image Decode Failed - Check the decode key matches the encode key on ispy server")); } } using (var ms = new MemoryStream(buffer, start + marker.Length, stop - start - marker.Length)) { ms.Seek(0, SeekOrigin.Begin); ms.WriteByte(jpegMagic[0]); ms.WriteByte(jpegMagic[1]); ms.WriteByte(jpegMagic[2]); ms.Seek(0, SeekOrigin.Begin); bitmap = (Bitmap)Image.FromStream(ms); } } else { using (var ms = new MemoryStream(buffer, start, stop - start)) { bitmap = (Bitmap)Image.FromStream(ms); } } // notify client NewFrame(this, new NewFrameEventArgs(bitmap)); // release the image bitmap.Dispose(); bitmap = null; } // shift array pos = stop + boundaryLen; todo = total - pos; Array.Copy(buffer, pos, buffer, 0, todo); total = todo; pos = 0; align = 1; } else { // boundary not found if (boundaryLen != 0) { todo = boundaryLen - 1; pos = total - todo; } else { todo = 0; pos = total; } } } } } catch (ApplicationException) { // do nothing for Application Exception, which we raised on our own // wait for a while before the next try Thread.Sleep(250); } catch (ThreadAbortException) { break; } catch (Exception exception) { // provide information to clients if (VideoSourceError != null) { VideoSourceError(this, new VideoSourceErrorEventArgs(exception.Message)); } // wait for a while before the next try Thread.Sleep(250); } finally { // abort request if (request != null) { try { request.Abort(); } catch {} request = null; } // close response stream if (stream != null) { stream.Close(); stream = null; } // close response if (response != null) { response.Close(); response = null; } } // need to stop ? if (_stopEvent.WaitOne(0, false)) { break; } } if (PlayingFinished != null) { PlayingFinished(this, ReasonToFinishPlaying.StoppedByUser); } }