mjpg_streamer.c 2.75 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "mongoose.h"

static void send_file(struct mg_connection *conn, const char *path) {
  char buf[1024];
  struct stat st;
  int n;
  FILE *fp;

  if (stat(path, &st) == 0 && (fp = fopen(path, "rb")) != NULL) {
    mg_printf(conn, "--w00t\r\nContent-Type: image/jpeg\r\n"
              "Content-Length: %lu\r\n\r\n", (unsigned long) st.st_size);
    while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
      mg_write(conn, buf, n);
    }
    fclose(fp);
    mg_write(conn, "\r\n", 2);
  }
}

struct conn_state {
  int file_index;
  time_t last_poll;
};

static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
  const char **file_names = (const char **) conn->server_param;
  struct conn_state *state;
  time_t now = time(NULL);

  switch (ev) {

    case MG_AUTH:
      return MG_TRUE;

    case MG_REQUEST:
      if (strcmp(conn->uri, "/stream") != 0) {
        mg_send_header(conn, "Content-Type", "text/html");
        mg_printf_data(conn, "%s",
                       "Go to <a href=/stream>/stream</a> for MJPG stream");
        return MG_TRUE;
      }

      mg_printf(conn, "%s",
                "HTTP/1.0 200 OK\r\n" "Cache-Control: no-cache\r\n"
                "Pragma: no-cache\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\n"
                "Connection: close\r\nContent-Type: multipart/x-mixed-replace; "
                "boundary=--w00t\r\n\r\n");

      send_file(conn, file_names[0]);

      state = (struct conn_state *) malloc(sizeof(*state));
      conn->connection_param = state;
      state->file_index = 1;  // First file is already sent
      state->last_poll = time(NULL);
      return MG_MORE;

    case MG_POLL:
      state = (struct conn_state *) conn->connection_param;

      if (state != NULL && now > state->last_poll) {
        if (file_names[state->file_index] != NULL) {
          send_file(conn, file_names[state->file_index]);
          state->file_index++;
          if (file_names[state->file_index] == NULL) {
            return MG_TRUE;  // No more images, close connection
          }
        }
        state->last_poll = now;
      }
      return MG_FALSE;

    case MG_CLOSE:
      free(conn->connection_param);
      conn->connection_param = NULL;
      return MG_FALSE;

    default:
      return MG_FALSE;
  }
}

int main(int argc, char *argv[]) {
  struct mg_server *server;

  if (argc < 3) {
    printf("Usage: %s image1.jpg image2.jpg ...\n", argv[0]);
    return 1;
  }

  server = mg_create_server(&argv[1], ev_handler);
  mg_set_option(server, "listening_port", "8080");

  printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
  for (;;) {
    mg_poll_server(server, 1000);
  }
  mg_destroy_server(&server);

  return 0;
}