int RemoteCameraHttp::Capture( Image &image )
{
    int content_length = GetResponse();
    if ( content_length == 0 )
    {
        Warning( "Unable to capture image, retrying" );
        return( 1 );
    }
    if ( content_length < 0 )
    {
        Error( "Unable to get response" );
        Disconnect();
        return( -1 );
    }
    switch( format )
    {
        case JPEG :
        {
            if ( !image.DecodeJpeg( buffer.extract( content_length ), content_length, colours, subpixelorder ) )
            {
                Error( "Unable to decode jpeg" );
                Disconnect();
                return( -1 );
            }
            break;
        }
        case X_RGB :
        {
            if ( content_length != image.Size() )
            {
                Error( "Image length mismatch, expected %d bytes, content length was %d", image.Size(), content_length );
                Disconnect();
                return( -1 );
            }
            image.Assign( width, height, colours, subpixelorder, buffer, imagesize );
            break;
        }
        case X_RGBZ :
        {
            if ( !image.Unzip( buffer.extract( content_length ), content_length ) )
            {
                Error( "Unable to unzip RGB image" );
                Disconnect();
                return( -1 );
            }
            image.Assign( width, height, colours, subpixelorder, buffer, imagesize );
            break;
        }
        default :
        {
            Error( "Unexpected image format encountered" );
            Disconnect();
            return( -1 );
        }
    }
    return( 0 );
}
int cURLCamera::Capture( Image &image ) {
  bool frameComplete = false;

  /* MODE_STREAM specific variables */
  bool SubHeadersParsingComplete = false;
  unsigned int frame_content_length = 0;
  std::string frame_content_type;
  bool need_more_data = false;
  int nRet;

  /* Grab the mutex to ensure exclusive access to the shared data */
  lock();

  while ( !frameComplete ) {

    /* If the work thread did a reset, reset our local variables */
    if ( bReset ) {
      SubHeadersParsingComplete = false;
      frame_content_length = 0;
      frame_content_type.clear();
      need_more_data = false;
      bReset = false;
    }

    if ( mode == MODE_UNSET ) {
      /* Don't have a mode yet. Sleep while waiting for data */
      nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex);
      if ( nRet != 0 ) {
        Error("Failed waiting for available data condition variable: %s",strerror(nRet));
        return -20;
      }
    }

    if ( mode == MODE_STREAM ) {

      /* Subheader parsing */
      while( !SubHeadersParsingComplete && !need_more_data ) {

        size_t crlf_start, crlf_end, crlf_size;
        std::string subheader;

        /* Check if the buffer contains something */
        if ( databuffer.empty() ) {
          /* Empty buffer, wait for data */
          need_more_data = true;
          break;
        }
     
        /* Find crlf start */
        crlf_start = memcspn(databuffer,"\r\n",databuffer.size());
        if ( crlf_start == databuffer.size() ) {
          /* Not found, wait for more data */
          need_more_data = true;
          break;
        }

        /* See if we have enough data for determining crlf length */
        if ( databuffer.size() < crlf_start+5 ) {
          /* Need more data */
          need_more_data = true;
          break;
        }

        /* Find crlf end and calculate crlf size */
        crlf_end = memspn(((const char*)databuffer.head())+crlf_start,"\r\n",5);
        crlf_size = (crlf_start + crlf_end) - crlf_start;

        /* Is this the end of a previous stream? (This is just before the boundary) */
        if ( crlf_start == 0 ) {
          databuffer.consume(crlf_size);
          continue;        
        }

        /* Check for invalid CRLF size */
        if ( crlf_size > 4 ) {
          Error("Invalid CRLF length");
        }

        /* Check if the crlf is \n\n or \r\n\r\n (marks end of headers, this is the last header) */
        if( (crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0) || (crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0) ) {
          /* This is the last header */
          SubHeadersParsingComplete = true;
        }

        /* Copy the subheader, excluding the crlf */
        subheader.assign(databuffer, crlf_start);

        /* Advance the buffer past this one */
        databuffer.consume(crlf_start+crlf_size);

        Debug(7,"Got subheader: %s",subheader.c_str());

        /* Find where the data in this header starts */
        size_t subheader_data_start = subheader.rfind(' ');
        if ( subheader_data_start == std::string::npos ) {
          subheader_data_start = subheader.find(':');
        }

        /* Extract the data into a string */
        std::string subheader_data = subheader.substr(subheader_data_start+1, std::string::npos);

        Debug(8,"Got subheader data: %s",subheader_data.c_str());

        /* Check the header */
        if(strncasecmp(subheader.c_str(),content_length_match,content_length_match_len) == 0) {  
          /* Found the content-length header */
          frame_content_length = atoi(subheader_data.c_str());
          Debug(6,"Got content-length subheader: %d",frame_content_length);
        } else if(strncasecmp(subheader.c_str(),content_type_match,content_type_match_len) == 0) { 
          /* Found the content-type header */
          frame_content_type = subheader_data;
          Debug(6,"Got content-type subheader: %s",frame_content_type.c_str());
        }

      }

      /* Attempt to extract the frame */
      if(!need_more_data) {
        if(!SubHeadersParsingComplete) {
          /* We haven't parsed all headers yet */
          need_more_data = true;
        } else if ( ! frame_content_length ) {
          /* Invalid frame */
          Error("Invalid frame: invalid content length");
        } else if ( frame_content_type != "image/jpeg" ) {
          /* Unsupported frame type */
          Error("Unsupported frame: %s",frame_content_type.c_str());
        } else if(frame_content_length > databuffer.size()) {
          /* Incomplete frame, wait for more data */
          need_more_data = true;
        } else {
          /* All good. decode the image */
          image.DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder);
          frameComplete = true;
        }
      }

      /* Attempt to get more data */
      if(need_more_data) {
        nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex);
        if(nRet != 0) {
          Error("Failed waiting for available data condition variable: %s",strerror(nRet));
          return -18;
        }
        need_more_data = false;
      }

    } else if(mode == MODE_SINGLE) { 
      /* Check if we have anything */
      if (!single_offsets.empty()) {
        if( (single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front()) ) {
          /* Extract frame */
          image.DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder);
          single_offsets.pop_front();
          frameComplete = true;
        } else {
          /* This shouldn't happen */
          Error("Internal error. Attempting recovery");
          databuffer.consume(single_offsets.front());
          single_offsets.pop_front();
        }
      } else {
        /* Don't have a frame yet, wait for the request complete condition variable */
        nRet = pthread_cond_wait(&request_complete_cond,&shareddata_mutex);
        if(nRet != 0) {
          Error("Failed waiting for request complete condition variable: %s",strerror(nRet));
          return -19;
        }
      }
    } else {
      /* Failed to match content-type */
      Fatal("Unable to match Content-Type. Check URL, username and password");
    } /* mode */

  } /* frameComplete loop */

  /* Release the mutex */
  unlock();

  if(!frameComplete)
    return -1;

  return 1;
}