6

I want to broadcast OpenCV images (coming from a camera) to a distant computer in real time, it has to be done via Ethernet. The images are continuously received in standard OpenCV Mat objects. The final code has to be integrated into a C++ (Qt) application.

I have found this Python script that does the job very well.

Now I'm trying to get a C++ equivalent of that code, I managed to create an HTTP server using Boost Asio and the Simple-Web-Server project. I am able to display a static blue image/webcam camera image (not refreshed).

I have written a code but it's not working. My guess is that the data are sent only at the return of the function (which never returns). How can I force sending the data after each iteration of the while loop?

#include "server_http.hpp"

#include <thread>
#include <boost/chrono.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/posix_time/posix_time_io.hpp>
#include <opencv2/opencv.hpp>
//#include <opencv/cv.h>

using namespace boost::posix_time;
typedef SimpleWeb::Server<SimpleWeb::HTTP> HttpServer;

cv::Mat image;
cv::VideoCapture cap;

int main()
{
  cap.open(0);
  if (!cap.isOpened ())
  {
    std::cerr << "Could not initialize capturing" << std::endl;
    return (-1);
  }
  cap >> image;

  HttpServer server(8080, 2);

  // Image resource is requested
  server.resource["^/cam.mjpg"]["GET"] =
      [=](HttpServer::Response& response, std::shared_ptr<HttpServer::Request> request)
      {
        time_facet *facet = new time_facet("%d-%b-%Y %H:%M:%S");
        std::cout.imbue(std::locale(std::cout.getloc(), facet));
        std::cout << second_clock::local_time() << " | " << "Camera image requested!" << std::endl;

        response <<
        "HTTP/1.1 200 OK\r\n"
        "Content-type: multipart/x-mixed-replace; boundary=--jpgboundary";
        //TODO: Send header

        while (1) // TODO: Allow exiting this
        {
          std::cout << "Send image" << std::endl;
          cap >> image;
          // Encode mat to jpg and copy it to content
          std::vector<uchar> buf;
          cv::imencode(".jpg", image, buf, std::vector<int>());
          std::string img_content(buf.begin(), buf.end());

          response << "--jpgboundary\r\n" << // Signal we start a new image
              "Content-type: image/jpeg" <<
              "Content-Length: " << img_content.length() << "\r\n" <<
              "\r\n" << img_content << "\r\n";
          std::this_thread::sleep_for(std::chrono::milliseconds(400));
        }
      };

  // Anything else is requested
  server.default_resource["GET"] = [](HttpServer::Response& response, std::shared_ptr<HttpServer::Request> request)
  {
    time_facet *facet = new time_facet("%d-%b-%Y %H:%M:%S");
    std::cout.imbue(std::locale(std::cout.getloc(), facet));
    std::cout << second_clock::local_time() << " | " << request->path << std::endl;

    std::string content =
    "<html><head></head><body>"
    "<img src=\"cam.mjpg\"/>"
    "</body></html>";
    response <<
    "HTTP/1.1 200 OK\r\n"
    "Content-Length: " << content.length() << "\r\n"
    "\r\n" << content;
  };

  std::thread server_thread([&server]()
  {
    server.start();
  });

  std::this_thread::sleep_for(std::chrono::seconds(1));
  server_thread.join();
  return 0;
}

EDIT 1

Based on Technik Empire comment I went back to boost examples; In the HTTP server example the response in sent when the callback returns so I modified the callback to allow do_write() operations on the socket.

The HTML page is correctly displayed but the image is not displayed (the broken image icon is shown instead), I tried to see what happens with Wireshark but I don't know what is wrong.

Here is my handle_request function: (request_handler.cpp):

void request_handler::handle_request(const request& req, reply& rep, connection &con)
{
  // Decode url to path.
  std::string request_path;
  if (!url_decode(req.uri, request_path))
  {
    rep = reply::stock_reply(reply::bad_request);
    return;
  }

  // Request path must be absolute and not contain "..".
  if (request_path.empty() || request_path[0] != '/'
      || request_path.find("..") != std::string::npos)
  {
    rep = reply::stock_reply(reply::bad_request);
    return;
  }

  // Determine the file extension.
  std::size_t last_slash_pos = request_path.find_last_of("/");
  std::string filename;
  if (last_slash_pos != std::string::npos)
    filename = request_path.substr(last_slash_pos + 1);

  if (filename == "cam.mjpg") // Image is requested
  {
    rep.status = reply::ok;
    rep.headers.resize(1);
    rep.headers[0].name = "Content-Type";
    rep.headers[0].value = "multipart/x-mixed-replace; boundary=--jpgboundary";
    rep.content.empty();
    con.do_write();
    rep.status = reply::none;

    while (true) // FIXME: How do I handle disconnection from the client?
    {
      cv::Mat image(200, 300, CV_8UC3);
      int random = rand() % 255 + 1;
      image = cv::Scalar(random, 0, 0); // Fill image with blue
      std::vector<uchar> buf;
      cv::imencode(".jpg", image, buf, std::vector<int>());
      std::string img_content(buf.begin(), buf.end());

      rep.headers.clear();
      rep.content.clear();

      rep.content.append("--jpgboundary\r\n");
      con.do_write();

      rep.content.clear();
      rep.headers.resize(2);
      rep.headers[0].name = "Content-Type";
      rep.headers[0].value = mime_types::extension_to_type("jpg");
      rep.headers[1].name = "Content-length";
      rep.headers[1].value = img_content.size();
      rep.content.append(img_content);
      rep.content.append("\r\n");
      con.do_write();

      boost::this_thread::sleep(boost::posix_time::milliseconds(500));
    }
  }
  else // Anything but the image is requested
  {
    std::string content =
    "<html><head></head><body>"
    "Hello :)<br>"
    "<img src=\"cam.mjpg\"/>"
    "</body></html>";

    rep.status = reply::ok;
    rep.headers.resize(2);
    rep.headers[0].name = "Content-Length";
    rep.headers[0].value = content.length();
    rep.headers[1].name = "Content-Type";
    rep.headers[1].value = mime_types::extension_to_type("html");
    rep.content.append(content);
    con.do_write();
    return;
  }
}
Victor Lamoine
  • 389
  • 1
  • 2
  • 21
  • 1
    imo you're making this infinitely more complex than it needs to be by depending on Simple-Web-Server. Should be simply a matter of a tcp listener and socket and launching async reads followed by writes in the read (request) handler. Look at the boost http server examples. Also, you shouldn't need to copy your vector into a string, it's a complete waste to do so. If that design is being forced on you by Simple-Web-Server, I'd reconsider it just for that. –  Dec 01 '15 at 20:23
  • I have updated my question, I'm now using the Boost HTTP server example. – Victor Lamoine Dec 04 '15 at 09:32
  • I think your problem is that you have some CRLF (\r\n) whitespace where they shouldn't be. You need to grab a few frames of output from your server, e.g. with curl, and make sure that it follows the MJPEG over HTTP format. Luckily you already have a working Python server, so you can compare with the output from it. – rhashimoto Dec 08 '15 at 00:59
  • Yes it was! Do you know how I can handle disconnection from the client? – Victor Lamoine Dec 11 '15 at 11:54

2 Answers2

0

I got it working by analyzing the packets thanks to the Firefox network analyzer, I replicated the Python headers/content answer and it works fine:

I added a reply::none reply type and made sure that if this reply was provided, no HTTP status is being sent. So in the reply::to_buffers() function I added this:

  if (status != none) // Don't add status to buffer if status is "none"
    buffers.push_back(status_strings::to_buffer(status));

The request_handler.cpp code looks like this:

  if (filename == "cam.mjpg") // Image is requested
  {
    rep.status = reply::ok;
    rep.headers.resize(1);
    rep.headers[0].name = "Content-Type";
    rep.headers[0].value = "multipart/x-mixed-replace; boundary=--jpgboundary\r\n";
    con.do_write();

    while (true) // FIXME: How do I handle disconnection from the client?
    {
      cv::Mat image(200, 300, CV_8UC3);
      int random = rand() % 255 + 1;
      image = cv::Scalar(random, 0, 0); // Fill image with blue
      std::vector<uchar> buf;
      cv::imencode(".jpg", image, buf, std::vector<int>());
      std::string img_content(buf.begin(), buf.end());

      rep.status = reply::none;
      rep.headers.resize(0);
      rep.content.clear();
      rep.content.append("--jpgboundary");
      rep.content.append("\r\n");
      rep.content.append("Content-Type: image/jpeg");
      rep.content.append("\r\n");
      rep.content.append("Content-length: "+boost::lexical_cast<std::string>(img_content.length()));
      rep.content.append("\r\n");
      rep.content.append("\r\n");
      rep.content.append(img_content);
      rep.content.append("\r\n");
      con.do_write();
      boost::this_thread::sleep(boost::posix_time::milliseconds(100));
    }
  }
  else // Anything but the image is requested
  {
    std::string content =
    "<html><head></head><body>"
    "<img src=\"cam.mjpg\"/>"
    "</body></html>";

    rep.status = reply::ok;
    rep.headers.resize(2);
    rep.headers[0].name = "Content-Length";
    rep.headers[0].value = content.length();
    rep.headers[1].name = "Content-Type";
    rep.headers[1].value = mime_types::extension_to_type("html");
    rep.content.append(content);
    con.do_write();
    return;
  }

Improvements suggestions are welcome in the comments. I don't know how to handle a disconnection from the client (exiting the while loop) so at the moment the server will only work with 1 request; after that it's stucked in the while loop.

Victor Lamoine
  • 389
  • 1
  • 2
  • 21
0

Http_server.hpp

#ifndef HTTPSERVER_HPP_INCLUDED
#define HTTPSERVER_HPP_INCLUDED

#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <opencv2/core/core.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
typedef boost::shared_ptr<tcp::socket> socket_ptr;
class HttpServer
{
public:
  std::map<std::string, std::string> winnames;
  std::map<std::string,int> requestcounts;
  std::map<std::string,std::vector<unsigned char> > jpegbuffers;
  short port;
  HttpServer();
  void run(int portno);
  boost::shared_mutex mut;
  boost::condition_variable_any cond;
  int httpdelay;
  void IMSHOW(std::string win, cv::Mat mat);
  int compression;
  bool is_debug;

private:
  int it;
  void server(int port);
  void session(socket_ptr sock);

  void handleinfo(socket_ptr sock);
  void handlewindows(socket_ptr sock);
  void handlemjpeg(socket_ptr sock,std::string winname);
  void handlejpg(socket_ptr sock,std::string winname);
  void handle404(socket_ptr sock);
};

#endif

Http_server.cpp

#include "http_server.hpp"
#include <fstream>
#include <boost/filesystem.hpp>
#include <boost/format.hpp> 
#include <opencv2/opencv.hpp>
#include <boost/lexical_cast.hpp>

namespace bfs= boost::filesystem; 
using namespace std;
using boost::lexical_cast;

// Helper functions
#if defined(unix)        || defined(__unix)      || defined(__unix__) \
  || defined(linux)       || defined(__linux)     || defined(__linux__) \
  || defined(sun)         || defined(__sun) \
  || defined(BSD)         || defined(__OpenBSD__) || defined(__NetBSD__) \
  || defined(__FreeBSD__) || defined __DragonFly__ \
  || defined(sgi)         || defined(__sgi) \
  || defined(__MACOSX__)  || defined(__APPLE__) \
  || defined(__CYGWIN__)
#define is_nix
#endif

#if defined(_MSC_VER) || defined(WIN32)  || defined(_WIN32) || defined(__WIN32__) \
  || defined(WIN64)    || defined(_WIN64) || defined(__WIN64__)
#define is_win
#endif

#ifdef is_win 
#include <windows.h>
#define SLEEP(ms) Sleep(ms)
#endif

#ifdef is_nix 
#define SLEEP(ms) usleep(ms*1000)
#endif

std::vector<std::string> &dssplit(const std::string &s, char delim, std::vector<std::string> &elems) {
  std::stringstream ss(s);
  std::string item;
  while (getline(ss, item, delim)) {
    elems.push_back(item);
  }
  return elems;
}

std::vector<std::string> dssplit(const std::string &s, char delim) {
  std::vector<std::string> elems;
  return dssplit(s, delim, elems);
}

void removeEmptyStrings(std::vector<std::string>& strings)
{
  std::vector<std::string>::iterator it = remove_if(strings.begin(), strings.end(),     mem_fun_ref(&std::string::empty));
  strings.erase(it, strings.end());
}

bool hasEnding(std::string const &fullString, std::string const &ending)
{
  if (fullString.length() >= ending.length()) {
    return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending));
  }
  else {
    return false;
  }
}

bool startswith(std::string const &src, std::string const &start)
{

  if (src.compare(0, start.length(), start) == 0)
  {
    return true;
  }
  return false;
}

std::string urldecode(std::string &src) {
  std::string ret;
  char ch;
  int ii;
  for (size_t i = 0; i<src.length(); i++) {
    if (int(src[i]) == 37) {
      sscanf(src.substr(i + 1, 2).c_str(), "%x", &ii);
      ch = static_cast<char>(ii);
      ret += ch;
      i = i + 2;
    }
    else {
      ret += src[i];
    }
  }
  return (ret);
}

// Server implementation
HttpServer::HttpServer() :compression(70), is_debug(true), httpdelay(100)
{

}

void HttpServer::IMSHOW(std::string win, cv::Mat mat)
{


  winnames[win] = lexical_cast<string>(mat.cols) + "," + lexical_cast<string>(mat.rows);

  if (is_debug)
  {
    cv::imshow(win, mat);
  }
  else
  {
    //cvDestroyWindow(win.c_str());
  }

  if (requestcounts[win] > 0)
  {
    cv::Mat towrite;
    if (mat.type() == CV_8UC1)
    {
      cvtColor(mat, towrite, CV_GRAY2BGR);
    }
    else if (mat.type() == CV_32FC3)
    {
      double minVal, maxVal;
      minMaxLoc(mat, &minVal, &maxVal);
      mat.convertTo(towrite, CV_8U, 255.0 / (maxVal - minVal), -minVal * 255.0 / (maxVal - minVal));
    }
    else{
      towrite = mat;
    }

    std::vector<uchar> buffer;
    std::vector<int> param(2);
    param[0] = CV_IMWRITE_JPEG_QUALITY;
    param[1] = compression;
    imencode(".jpg", towrite, buffer, param);
    jpegbuffers[win].swap(buffer);

  }
}
void HttpServer::run(int portno)
{
  port=portno;
  boost::thread t(boost::bind(&HttpServer::server,this,port));
}
void HttpServer::server(int port)
{
  try
  {

    boost::asio::io_service io_service;
    io_service.run();
    tcp::acceptor a(io_service, tcp::endpoint(tcp::v4(), port));
    for (;;)
    {
      socket_ptr sock(new tcp::socket(io_service));
      a.accept(*sock);
      boost::thread t(boost::bind(&HttpServer::session, this, sock));
    }
  }
  catch (boost::exception & e)
  {
    std::cout << "OMG!" << boost::diagnostic_information(e)<<endl;
  }
}
void HttpServer::session(socket_ptr sock)
{
  try
  {
    boost::system::error_code ec;
    boost::asio::streambuf sbuffer;
    boost::asio::read_until(* sock, sbuffer, "\0", ec ); 
    const char* header=boost::asio::buffer_cast<const char*>(sbuffer.data());
    std::string reqStr(header,header+sbuffer.size());   
    sbuffer.consume(sbuffer.size());
    std::vector<std::string> strs;
    strs = dssplit(reqStr,' ');
    if(strs.size()>1)
    {
      std::string requesturl = urldecode(strs[1]);
      std::vector<std::string> splited=dssplit(requesturl,'/');
      removeEmptyStrings(splited);
      if(splited.size()==1)
      {
        if(startswith(splited[0],"windows"))
        {
          handlewindows(sock);
        }else if(startswith(splited[0],"info"))
        {
          handleinfo(sock);
        }else if(hasEnding(splited[0],".mjpg"))
        {
          handlemjpeg(sock,splited[0].substr(0,splited[0].size()-5));
        }else if(hasEnding(splited[0],".jpg") || splited[0].find(".jpg?")!=string::npos)
        {
          handlejpg(sock,splited[0]);
        }else
        {
          handle404(sock);
        }
      }else
      {
        handle404(sock);
      }
      sock->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
    }

  }catch(const std::exception& ex)
  {
    boost::system::error_code ec;
    boost::asio::ip::tcp::endpoint endpoint = sock->remote_endpoint(ec);
    if(!ec)
    {
      sock->shutdown(boost::asio::ip::tcp::socket::shutdown_both);
    }
    //DPRINTERR(ex.what());
  }catch(const  std::string& ex)
  {
    boost::system::error_code ec;
    boost::asio::ip::tcp::endpoint endpoint = sock->remote_endpoint(ec);
    if(!ec)
    {
      sock->shutdown(boost::asio::ip::tcp::socket::shutdown_both);
    }
  }
}

void HttpServer::handleinfo(socket_ptr sock)
{

  boost::system::error_code error;
  boost::asio::streambuf sbuffer;
  std::ostream response_stream(&sbuffer);
  string retstr;
  for (std::map<std::string,std::string>::iterator it=winnames.begin(); it!=winnames.end(); ++it)
  {
    string wname =it->first;
    int rcnt = 0;
    if(requestcounts.find(wname)!=requestcounts.end())
    {
      rcnt=requestcounts[wname];
    }

    retstr+=boost::str(boost::format("{"
      "\"name\":\"%s\","
      "\"reqCnt\":%d,"
      "\"size\":\"%s\""
      "},"
      )
      %wname
      %rcnt
      %it->second
      );
  }
  if(retstr.size()>0) retstr.resize(retstr.size()-1);

  retstr=boost::str(boost::format("{"
    "\"windows\":[%s],"
    "\"version\":\"%s\","
    "\"fps\":%s"
    "}"
    )
    %retstr
    %"0.0"
    % to_string(0.)
    );
  response_stream << "HTTP/1.1 200 OK\r\n";
  response_stream << "Access-Control-Allow-Origin: *\r\n";
  response_stream << "Content-Type: text/plain\r\n\r\n";
  response_stream << retstr << "\r\n\r\n";

  boost::asio::write(*sock, sbuffer);
}
void HttpServer::handlewindows(socket_ptr sock)
{

  boost::system::error_code error;
  boost::asio::streambuf sbuffer;
  std::ostream response_stream(&sbuffer);
  string retstr;
  for (std::map<std::string,std::string>::iterator it=winnames.begin(); it!=winnames.end(); ++it)
  {
    string wname =it->first;
    int rcnt = 0;
    if(requestcounts.find(wname)!=requestcounts.end())
    {
      rcnt=requestcounts[wname];
    }

    retstr+=boost::str(boost::format("{"
      "\"name\":\"%s\","
      "\"reqCnt\":%d,"
      "\"size\":\"%s\""
      "},"
      )
      %wname
      %rcnt
      %it->second
      );
  }
  if(retstr.size()>0) retstr.resize(retstr.size()-1);

  retstr="{\"windows\":["+retstr+"]}";
  response_stream<<"HTTP/1.1 200 OK\r\n"
    "Content-Type: text/plain\r\n\r\n"<<
    retstr<<"\r\n\r\n";

  boost::asio::write(*sock, sbuffer);

}
void HttpServer::handlemjpeg(socket_ptr sock,std::string winname)
{
  if(requestcounts.find(winname)==requestcounts.end())
  {
    handle404(sock);
    return;
  }
  std::string frame=winname;
  //boost::shared_lock<boost::shared_mutex> lock(mut);
  //lock.lock();
  requestcounts[frame]++;
  //lock.unlock();
  boost::system::error_code error;
  boost::asio::streambuf sbuffer;
  std::ostream response_stream(&sbuffer);
  response_stream<<"HTTP/1.1 200 OK\r\n";
  response_stream<<"Content-Type: multipart/mixed;boundary=b\r\n";
  response_stream<<"Cache-Control: no-store\r\n";
  response_stream<<"Pragma: no-cache\r\n";
  response_stream<<"Audio Mode : None\r\n";
  response_stream<<"Connection: close\r\n";
  response_stream<<"\r\n";
  boost::asio::write(*sock, sbuffer); 
  for(;;)
  {
    try
    {
      if( (jpegbuffers.count(frame)<0 ||
        jpegbuffers[frame].size()<4) || 
        (jpegbuffers[frame][0]!=0xff && jpegbuffers[frame][1]!=0xd8 &&
        jpegbuffers[frame][jpegbuffers[frame].size()-2]!=0xff && jpegbuffers[frame][jpegbuffers[frame].    size()-1]!=0xd9))
      {

        SLEEP(10);
        continue;
      }
      //boost::shared_lock<boost::shared_mutex> lock(mut);
      response_stream<<"--b\r\n"; 
      response_stream<<"Content-Type: image/jpeg\r\n";
      response_stream<<"Content-length: "<<jpegbuffers[frame].size()<<"\r\n";
      response_stream<<"\r\n";
      boost::asio::write(*sock, sbuffer);
      boost::asio::write(*sock,boost::asio::buffer(jpegbuffers[frame], jpegbuffers[frame].size()));
      //lock.unlock();
      SLEEP(httpdelay);
    }
    catch (std::exception& e)
    {
      SLEEP(50);
      //lock.lock();
      requestcounts[frame]--;
      //lock.unlock();
      return;
    }
  }
  //lock.lock();
  requestcounts[frame]--;
  //lock.unlock();
}

void HttpServer::handlejpg(socket_ptr sock,std::string winname)
{
  if(winname.find("?")!=string::npos)
  {
    winname = winname.substr(0,winname.find("?"));
  }
  winname =winname.substr(0,winname.size()-4);
  std::string frame=winname;
  requestcounts[frame]++;
  boost::system::error_code error;
  boost::asio::streambuf sbuffer;
  std::ostream response_stream(&sbuffer);

  jpegbuffers[frame].clear();
  for(;;)
  {
    try
    {
      if( (jpegbuffers.count(frame)<0 ||
        jpegbuffers[frame].size()<4) || 
        (jpegbuffers[frame][0]!=0xff && jpegbuffers[frame][1]!=0xd8 &&
        jpegbuffers[frame][jpegbuffers[frame].size()-2]!=0xff && jpegbuffers[frame][jpegbuffers[frame].    size()-1]!=0xd9))
      {

        SLEEP(10);
        continue;
      }
      response_stream<<"HTTP/1.1 200 OK\r\n";
      response_stream<<"Content-Type:  image/jpeg\r\n";
      response_stream<<"Cache-Control: no-store\r\n";
      response_stream<<"Access-Control-Allow-Origin: *\r\n";
      response_stream<<"Pragma: no-cache\r\n";
      response_stream<<"Content-length: "<<jpegbuffers[frame].size()<<"\r\n";
      response_stream<<"Connection: close\r\n";

      response_stream<<"\r\n";
      boost::asio::write(*sock, sbuffer);
      boost::asio::write(*sock,boost::asio::buffer(jpegbuffers[frame], jpegbuffers[frame].size()));

      break;
    }
    catch (std::exception& e)
    {

      //DPRINTERR( "net exceptoin:"+std::string(e.what()));
      SLEEP(50);
      requestcounts[frame]--;
      return;
    }
  }
  requestcounts[frame]--;
}
void HttpServer::handle404(socket_ptr sock)
{


  boost::system::error_code error;
  boost::asio::streambuf sbuffer;
  std::ostream response_stream(&sbuffer);
  response_stream<<"HTTP/1.1 404 Not Found\r\n"
    "Content-Type: text/html\r\n"
    "Connection: close\r\n"
    "Content-Length: 132\r\n\r\n"
    "<html>\r\n"
    "<head><title>404 Not Found</title></head>\r\n"
    "<body bgcolor=\"white\">\r\n"
    "<center><h1>404 Not Found</h1></center>\r\n"   
    "</body>\r\n"
    "</html>\r\n";
  boost::asio::write(*sock, sbuffer);
}

Main.cpp

#include <opencv2/opencv.hpp>
#include "http_server.hpp"
#include <iostream>
#include <fstream>

using namespace cv;
#define MJPGFILE_BUFFER_SIZE 10240
class MjpgFileCapture{
public:
  static double lastframeseen;
  MjpgFileCapture() {};
  MjpgFileCapture(std::string filepath)
  {
    filepath_ = filepath;
    is_inited_ = false;
    skip_ = true;
    imgready_ = false;
    ff_ = false;
    readbytes_ = -2;
    i_ = 0;
  };

  void init();
  MjpgFileCapture& operator >>(cv::Mat& out);
private:
  std::string filepath_;
  bool is_inited_;
  std::ifstream ifstream_;
  std::vector<char> data_;
  bool skip_;
  bool imgready_;
  bool ff_;//have we seen ff byte?
  long long readbytes_;
  char ca_[MJPGFILE_BUFFER_SIZE];
  int i_;//loop index
};

void MjpgFileCapture::init()
{
  is_inited_ = true;
  ifstream_ = std::ifstream(filepath_.c_str(), std::ios::binary);
}

MjpgFileCapture& MjpgFileCapture::operator >> (cv::Mat& out)
{
  out = Mat();
  if (!is_inited_)
  {
    init();
  }


  while (1)
  {
    uchar c;
    if (readbytes_ != 0 && readbytes_ != -1)
    {
      if (i_ >= readbytes_)
      {
        ifstream_.read(ca_, MJPGFILE_BUFFER_SIZE);
        readbytes_ = ifstream_.gcount();
        i_ = 0;
      }
      for (; i_ < readbytes_; i_++)
      {
        c = ca_[i_];
        if (ff_ && c == 0xd8)
        {
          skip_ = false;
          data_.push_back((uchar)0xff);
        }
        if (ff_ && c == 0xd9)
        {
          imgready_ = true;
          data_.push_back((uchar)0xd9);
          skip_ = true;
        }
        ff_ = c == 0xff;
        if (!skip_)
        {
          data_.push_back(c);
        }
        if (imgready_)
        {
          if (data_.size() != 0)
          {
            cv::Mat data_mat(data_);
            cv::Mat frame(imdecode(data_mat, 1));
            out = frame;
          }
          else
          {
            printf("warning:image is ready and data is empty. Likely bug.");
          }
          imgready_ = false;
          skip_ = true;
          data_.clear();
          return *this;
        }
      }
    }
    else
    {
      //own exception class
      throw std::string("zero byte read:probably end of file.");
    }
  }
  return *this;
}
HttpServer* server = 0;
void file_loop()
{

  MjpgFileCapture cap("C:/v/frame.mjpg");
  while (true)
  {
    Mat im;
    cap >> im;
    server->IMSHOW("im", im);
    imshow("im", im);
    if (waitKey(1) == 27)
      exit(0);
  }
}
int main(int argc, char** argv)
{
  server = new HttpServer;
  //server->port = 8080;
  server->run(8080);
  while (true)
  {
    try{
      file_loop();
    }
    catch (...)
    {

    }
  }
  return 0;
}    

Usage

Zaw Lin
  • 5,629
  • 1
  • 23
  • 41
  • just a note on content type, if the data stream is to be consumed from chrome, maybe through java applet or flash, it cannot be x-mixed-replace. chrome will immediately terminate connection as soon as it sees it in header. this will probably save you a few hours(or more..) – Zaw Lin Dec 13 '15 at 15:59