public VideoCaptureByMJPEGInstance(VideoCaptureByMJPEGDefinition theDefinition, TestExecution testExecution) : base(theDefinition, testExecution) { mCamera = theDefinition.Camera; mConnectionTimeout = theDefinition.ConnectionTimeout; mStopConditionTimeout = theDefinition.StopConditionTimeout; mCapturedVideo = new GeneratedVideoInstance(theDefinition.ResultantVideo, testExecution); }
protected override void OnDoWork(DoWorkEventArgs e) { mHttpCameraUser.mWorkerLogMessage = "snapshot worker kicked off"; mHttpCameraUser.mWorkerLogMessageAvailable = true; VideoCaptureByMJPEGDefinition defObject = (VideoCaptureByMJPEGDefinition)mHttpCameraUser.Definition(); int readSize = 1024; int bufferSize = defObject.bufferSize; byte[] buffer = defObject.bufferForWorker; HttpWebRequest request = null; WebResponse response = null; Stream stream = null; Random randomNumberGenerator = new Random((int)DateTime.Now.Ticks); byte[] jpegMarker = new byte[] { 0xFF, 0xD8, 0xFF }; int jpegMarkerLength = 3;// jpegMarker.GetUpperBound(0) + 1; byte[] boundary = null; int boundaryLength; HttpRequestCachePolicy bypassCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.BypassCache); try { string uri = mHttpCameraUser.Camera.CompleteVideoRequestURL(null, -1, -1, -1); if (mHttpCameraUser.Camera.ProxyCacheProtection) { uri += ((uri.IndexOf('?') == -1) ? '?' : '&') + "proxyprevention=" + randomNumberGenerator.Next().ToString(); } request = (HttpWebRequest)WebRequest.Create(uri); request.CachePolicy = bypassCachePolicy; request.Timeout = mHttpCameraUser.ConnectionTimeout; if (mHttpCameraUser.Camera.Login != null && mHttpCameraUser.Camera.Login.Length > 0) { if (mHttpCameraUser.Camera.Password == null) { mHttpCameraUser.Camera.Password = String.Empty; } request.Credentials = new NetworkCredential(mHttpCameraUser.Camera.Login, mHttpCameraUser.Camera.Password); } // setting ConnectionGroupName to make sure we don't run out of connections amongst all the cameras (see http://msdn2.microsoft.com/en-us/library/ms998562.aspx section: "Connections") request.ConnectionGroupName = mHttpCameraUser.TestSequence().Name + " : " + mHttpCameraUser.Name; // a unique group for each HTTP user within each TestSequence. We don't want to create unique groups every request because that would be inefficient // TODO: check maxconnection attribute in Machine.config (limits the number of concurrent outbound calls) /////////// response = request.GetResponse(); string contentType = response.ContentType; if (contentType.IndexOf("multipart/x-mixed-replace") == -1) { mHttpCameraUser.mWorkerLogMessage = "Invalid content type from camera; type='" + contentType + "'"; mHttpCameraUser.mWorkerLogMessageAvailable = true; e.Result = ResultCodes.InvalidContentType; return; } ASCIIEncoding encoding = new ASCIIEncoding(); boundary = encoding.GetBytes(contentType.Substring(contentType.IndexOf("boundary=", 0) + 9)); boundaryLength = boundary.Length; stream = response.GetResponseStream(); int startIndex = -1; int endIndex = -1; int searchPosition = 0; int newBytesRead = 0; int bytesInBuffer = 0; int totalBytesRead = 0; bool needMoreData = true; StreamSearchMode searchMode = StreamSearchMode.SearchingForImageStart; while (true) // loop "indefinitely" grabbing frames until whomever started this worker(thread) cancels it { bool done = false; while (!done) { if (CancellationPending) { e.Cancel = true; return; } if (needMoreData) { if (bytesInBuffer > bufferSize - readSize) { e.Result = ResultCodes.BufferTooSmall; return; } newBytesRead = stream.Read(buffer, bytesInBuffer, readSize); if (newBytesRead == 0) { e.Result = ResultCodes.VideoStreamConnectionLost; return; } else { totalBytesRead += newBytesRead; bytesInBuffer += newBytesRead; } } switch (searchMode) { case StreamSearchMode.SearchingForImageStart: startIndex = FindBytePattern(buffer, jpegMarker, searchPosition, bytesInBuffer - searchPosition); if (startIndex != -1) { // found start of JPEG image within stream searchPosition = startIndex; searchMode = StreamSearchMode.SearchingForImageBoundary; needMoreData = false; // we don't need data until we can't find the image's end. We don't want more data until we verify we don't have the entire image in the buffer already (since if we overfill the buffer we will error out) } else { // image start not found searchPosition = bytesInBuffer - (jpegMarkerLength - 1); // after we add more bytes to buffer, start searching in the current tail end. (ie if marker is 3 bytes, search searching in the last 2...we can't tell if the tail is a marker until we have more data) needMoreData = true; } break; case StreamSearchMode.SearchingForImageBoundary: endIndex = FindBytePattern(buffer, boundary, searchPosition, bytesInBuffer - searchPosition); if (endIndex != -1) { // found image boundary within stream, so we know we found the end mImage = (Bitmap)Bitmap.FromStream(new MemoryStream(buffer, startIndex, endIndex - startIndex)); mHttpCameraUser.ProcessNewImage(mImage); searchPosition = endIndex + boundaryLength; // now we only care about the data in the buffer after the boundary (e.g. the start of the next image) bytesInBuffer = bytesInBuffer - searchPosition; // adjust the number of bytes in the buffer Array.Copy(buffer, searchPosition, buffer, 0, bytesInBuffer); // shift the buffer to remove the 'used' data and make room for data for the next image searchPosition = 0; // after the shift, our seachPosition is now at 0 searchMode = StreamSearchMode.SearchingForImageStart; needMoreData = false; // we don't need data until we can't find the next image's start or end. We don't want more data until we verify we don't have another entire image in the buffer } else { // didn't find the image boundary, so grab some more data and search on searchPosition = bytesInBuffer - (boundaryLength - 1); needMoreData = true; } break; default: e.Result = ResultCodes.UnexpectedError; return; } } } // we never get here since the while loop never terminates...we exit the loop on worker cancellation (above) or an exception (below) } catch (WebException exception) { switch (exception.Status) { // http://msdn2.microsoft.com/en-us/library/system.net.webexceptionstatus(vs.80).aspx case WebExceptionStatus.Timeout: e.Result = ResultCodes.Timeout; break; default: e.Result = ResultCodes.Unspecified; break; } } catch (Exception exception) { e.Result = ResultCodes.NonWebException; } finally { if (request != null) { request.Abort(); request = null; } if (stream != null) { stream.Close(); stream = null; } if (response != null) { response.Close(); response = null; } } }