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;
                }
            }
        }