// Worker thread private void WorkerThread( ) { // buffer to read stream byte[] buffer = new byte[bufSize]; // JPEG magic number byte[] jpegMagic = new byte[] { 0xFF, 0xD8, 0xFF }; int jpegMagicLength = 3; UTF8Encoding encoding = new UTF8Encoding( ); while (!stopEvent.WaitOne(0)) { // 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 = null; string boudaryStr = null; // length of boundary int boundaryLen; // flag signaling if boundary was checked or not bool boundaryIsChecked = false; // read amounts and positions int read, todo = 0, total = 0, pos = 0, align = 1; int start = 0, stop = 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); } // set timeout value for the request request.Timeout(requestTimeout); // 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 if (forceBasicAuthentication) { string authInfo = string.Format("{0}:{1}", login, password); authInfo = Convert.ToBase64String(Encoding.UTF8.GetBytes(authInfo)); request.Headers["Authorization"] = "Basic " + authInfo; } // get response response = request.EndGetResponse(request.BeginGetResponse(null, null)); // check content type string contentType = response.ContentType; string[] contentTypeArray = contentType.Split('/'); // "application/octet-stream" 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)) && (!reloadEvent.WaitOne(0))) { // check total read if (total > bufSize - readSize) { total = pos = todo = 0; } // read next portion from stream 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 >= jpegMagicLength)) { start = ByteArrayUtils.Find(buffer, jpegMagic, pos, todo); if (start != -1) { // found JPEG start pos = start + jpegMagicLength; todo = total - pos; align = 2; } else { // delimiter not found todo = jpegMagicLength - 1; pos = total - todo; } } // search for image end ( boundaryLen can be 0, so need extra check ) while ((align == 2) && (todo != 0) && (todo >= boundaryLen)) { stop = ByteArrayUtils.Find(buffer, (boundaryLen != 0) ? boundary : jpegMagic, pos, todo); if (stop != -1) { pos = stop; todo = total - pos; // increment frames counter framesReceived++; // image at stop if ((NewFrame != null) && (!stopEvent.WaitOne(0))) { Bitmap bitmap = (Bitmap)Bitmap.FromStream(new MemoryStream(buffer, start, stop - start)); // 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) { request.Abort( ); request = null; } // close response stream if (stream != null) { stream.Dispose( ); stream = null; } // close response if (response != null) { response.Dispose( ); response = null; } } // need to stop ? if (stopEvent.WaitOne(0)) { break; } } if (PlayingFinished != null) { PlayingFinished(this, ReasonToFinishPlaying.StoppedByUser); } }
// Worker thread private void WorkerThread( ) { // buffer to read stream byte[] buffer = new byte[bufferSize]; // HTTP web request HttpWebRequest request = null; // web responce WebResponse response = null; // stream for JPEG downloading Stream stream = null; // random generator to add fake parameter for cache preventing Random rand = new Random((int)DateTime.Now.Ticks); // download start time and duration DateTime start; TimeSpan span; while (!stopEvent.WaitOne(0)) { int read, total = 0; try { // set dowbload start time start = DateTime.Now; // create request if (!preventCaching) { // request without cache prevention request = (HttpWebRequest)WebRequest.Create(source); } else { // request with cache prevention request = (HttpWebRequest)WebRequest.Create(source + ((source.IndexOf('?') == -1) ? '?' : '&') + "fake=" + rand.Next( ).ToString( )); } // set proxy if (proxy != null) { request.Proxy(proxy); } // set timeout value for the request request.Timeout(requestTimeout); // 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 if (forceBasicAuthentication) { string authInfo = string.Format("{0}:{1}", login, password); authInfo = Convert.ToBase64String(Encoding.UTF8.GetBytes(authInfo)); request.Headers["Authorization"] = "Basic " + authInfo; } // get response response = request.EndGetResponse(request.BeginGetResponse(null, null)); // get response stream stream = response.GetResponseStream( ); stream.ReadTimeout = requestTimeout; // loop while (!stopEvent.WaitOne(0)) { // check total read if (total > bufferSize - readSize) { total = 0; } // read next portion from stream if ((read = stream.Read(buffer, total, readSize)) == 0) { break; } total += read; // increment received bytes counter bytesReceived += read; } if (!stopEvent.WaitOne(0)) { // increment frames counter framesReceived++; // provide new image to clients if (NewFrame != null) { Bitmap bitmap = (Bitmap)Bitmap.FromStream(new MemoryStream(buffer, 0, total)); // notify client NewFrame(this, new NewFrameEventArgs(bitmap)); // release the image bitmap.Dispose( ); bitmap = null; } } // wait for a while ? if (frameInterval > 0) { // get download duration span = DateTime.Now.Subtract(start); // miliseconds to sleep int msec = frameInterval - (int)span.TotalMilliseconds; if ((msec > 0) && (stopEvent.WaitOne(msec))) { break; } } } 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) { request.Abort( ); request = null; } // close response stream if (stream != null) { stream.Dispose( ); stream = null; } // close response if (response != null) { response.Dispose( ); response = null; } } // need to stop ? if (stopEvent.WaitOne(0)) { break; } } if (PlayingFinished != null) { PlayingFinished(this, ReasonToFinishPlaying.StoppedByUser); } }