Commit 1fec5574 authored by Sven Gestegård Robertz's avatar Sven Gestegård Robertz
Browse files

rewrite for 2017

See merge request !1
parents f5d2a8a4 02002927
......@@ -20,23 +20,27 @@ LD=${COMPILER_DIR}/bin/mipsisa32r2el-axis-linux-gnu-ld
# Tell the compiler where to look for header files used in the auxilliary native methods for the camera classes
#CPPFLAGS+=-I${AXIS_PATH}/include -I${AXIS_PATH}/usr/include
CPPFLAGS+=-I${AXIS_PATH}/usr/include -I${COMPILER_DIR}/mipsisa32r2el-axis-linux-gnu/sys-include
CPPFLAGS+=-DFORMAT_FOR_SIZE_T=\"%u\"
AXIS_LIBCAP=-lcapture # This is needed for the native camera classes to fetch images
AXIS_LIBCAP_DEP=-ldbus-1 -lgobject-2.0 -ldbus-glib-1 -lgthread-2.0 -lglib-2.0 -lrapp -lpthread # Dependencies of libcapture
LDFLAGS+=${AXIS_LIBCAP} ${AXIS_LIBCAP_DEP} ${AXIS_LIBCAP} -L${AXIS_PATH}/lib -L${AXIS_PATH}/usr/lib # Specify which libs are used and set search path for them
CFLAGS+=-O3 -g -Wall
LDLIBS+=-lpthread
all: http_server
all: http_server motion_server
http_server: http_server.o camera.o
http_server: http_server.o camera.o server_common.o
http_server.o: http_server.c camera.h
motion_server: motion_server.o server_common.o
motion_server.o: motion_server.c
server_common.o: server_common.c server_common.h
camera.o: camera.c camera.h fakecapture.h
clean:
-rm http_server.o camera.o
-rm http_server.o motion_server.o server_common.o camera.o
distclean: clean
-rm http_server
-rm http_server motion_server
.PHONY: all clean
......
# To run: make -f Makefile.fake
#
# For compiling on PC
CC=gcc
.PHONY: all clean
all: fake_server
.PHONY: all clean distclean
all: fake_server motion_server_host motion_client
CPPFLAGS+=-DFAKE -DDISABLE_SANITY_CHECKS
CFLAGS+=-DFAKE -DDISABLE_SANITY_CHECKS
CFLAGS+=-Wall
CPPFLAGS+=-DUSE_POSIX_FUNCTION
CPPFLAGS+=-DINFO
# CPPFLAGS+=-DDEBUG
# CPPFLAGS+=-DSHORT_NUMBER_FRAME_SEQUENCE
CFLAGS+=-g -Wall
LDLIBS=-lpthread
fake_server: http_server.c fakecapture.h fakecapture.c camera.h camera.c
$(CC) -o $@ $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) http_server.c fakecapture.c camera.c $(LDLIBS)
fake_server: http_server.c fakecapture.h fakecapture.c camera.h camera.c server_common.c
$(CC) -o $@ $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) http_server.c fakecapture.c camera.c server_common.c $(LDLIBS)
motion_server_host: motion_server.c server_common.c
$(CC) -o $@ $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) motion_server.c server_common.c $(LDLIBS)
motion_client: motion_client.c server_common.c
$(CC) -o $@ $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) motion_client.c server_common.c $(LDLIBS)
clean:
-rm fake_server
distclean: clean
-rm fake_server motion_server_host motion_client
http_server.c: a simple example of socket communication, interfacing
to the Axis capture API and pthread programming.
A brief overview of the files:
camera.c : An interface towards the (fake or real) camera
camera.h
fakecapture.c : Fake motion capture for use on the PC while testing
fakecapture.h
http_server.c : A http server getting images from the camera. Allows
testing of the capture code from a web browser.
Makefile : makefile for cross-compiling for the Axis camera
Makefile.fake : makefile for building the fake versions for testing
motion_client.c : a minimal example of a client connecting to the
motion server. Provided for illustration of the protocol.
motion_server.c : A proxy server written to decouple the push notification
interface of the Axis camera from the student code, by
providing a pull interface.
server_common.c : common routines and config
server_common.h
About the Makefile
Makefile is the file to use for cross-compiling to the Axis camera.
It is written to work in the computer labs, where the tools and
libraries are found under /usr/local/cs/rtp/ in the directories
under tools.
If compiling elsewhere, if you have the same directory structure as
in /usr/local/cs/rtp, you can use the variable COMMON_DIR to the
location of the compiler and libs.
Makefile.fake is the file to use for building the http server
with fake capture.
When building, you can set options to change its behaviour.
To use a short frame sequence of images of numbers, define
SHORT_NUMBER_FRAME_SEQUENCE, for instance with the command line
> CPPFLAGS="-DSHORT_NUMBER_FRAME_SEQUENCE" make -f Makefile.fake
To turn off fake motion, define NO_FAKE_MOTION
Example: CPPFLAGS="-DSHORT_NUMBER_FRAME_SEQUENCE -DNO_FAKE_MOTION" make ...
(note that you need separate-D options for each macro)
To use the film sequence (of man in corridor) but skip the first part
without motion, give START_FRAME a value>1. See fakecapture.c for details.
Running
On host, start the two server processes
> ./fake_server
and
> ./motion_server_host
The servers have a simple user interface.
For instance, if you type s (and press enter) it will print some status
information. See the code for details.
To close down the servers cleanly, type q (and press enter) in the
terminal.
For this reason, it is recommended to run the servers in separate
terminals.
Then, you can browse to localhost:9999 to get an image
and to localhost:9091 to get motion information
Note: if you run just the fake_server without running the motion_server
on the same computer, you will get the output "ERROR connecting:
Connection refused" when motion occurs, as the fake_server
also sends motion notifications to localhost:9090.
The motion client (which polls a motion server) is run
(with the default parameters of polling localhost every second) as
> ./motion_client
......@@ -8,6 +8,10 @@ camera* camera_open(){
return NULL;
}
#ifdef FAKE
#ifdef DEBUG
printf("fake camera: camera_open\n");
#endif
cam->frame_nbr = 0;
#endif
cam->stream = capture_open_stream(IMAGE_JPEG, "fps=25&resolution=640x480");
......@@ -22,50 +26,15 @@ void camera_close(camera* cam){
frame* camera_get_frame(camera* cam){
frame *f = malloc(sizeof(frame));
f->fr = capture_get_frame(cam->stream);
#ifdef FAKE
f->motion = (cam->frame_nbr >= 86 && cam->frame_nbr <= 240);
cam->frame_nbr = (cam->frame_nbr % 247) + 1;
#else
//TODO
//f->motion = analyseframes(capture_get_frame(cam->stream, */prev_frame*/);
++cam->frame_nbr;
#endif
return f;
}
//From axis example code
static int analyse_frames(media_frame *cur_frame, media_frame *prev_frame){
int i, j;
char *prev_data = NULL;
int prev_width;
int prev_height;
//int prev_stride;
char *cur_data = NULL;
int cur_width;
int cur_height;
//int cur_stride;
int result = 0;
prev_data = (char *)capture_frame_data(prev_frame);
prev_width = capture_frame_width(prev_frame);
prev_height = capture_frame_height(prev_frame);
//prev_stride = capture_frame_stride(prev_frame);
cur_data = (char *)capture_frame_data(cur_frame);
cur_width = capture_frame_width(cur_frame);
cur_height = capture_frame_height(cur_frame);
//cur_stride = capture_frame_stride(cur_frame);;
/* simple pixel diff */
for (i = 0; i < cur_height; i++) {
for (j = 0; j < cur_width; j++) {
int c_pos = i /* * cur_stride */ + j;
int p_pos = i /* * prev_stride */ + j;
result += abs(cur_data[c_pos] - prev_data[p_pos]);
}
}
result /= (cur_width * cur_height);
return result > 20; //Arbitrary value used in the axis example
#ifdef DEBUG
printf("camera_get_frame: ts=%llu\n", get_frame_timestamp(f));
#endif
return f;
}
byte* get_frame_bytes(frame* f){
......@@ -80,9 +49,11 @@ capture_time get_frame_timestamp(frame* f){
return capture_frame_timestamp(f->fr);
}
#ifdef MOTION_FLAG_IN_FRAMES
int get_frame_motion(frame* f){
return f->motion;
}
#endif
size_t get_frame_height(frame* f){
return capture_frame_height(f->fr);
......
......@@ -6,6 +6,7 @@
#define IMAGE_JPEG "image/jpeg"
#undef MOTION_FLAG_IN_FRAMES
/* A wrapper for the Axis capture API
*/
......@@ -23,14 +24,12 @@ typedef struct camera camera;
typedef struct frame frame;
typedef char byte;
#ifdef FRAME_NOT_NEEDED
typedef frame media_frame;
#else
struct frame{
media_frame* fr;
#ifdef MOTION_FLAG_IN_FRAMES
int motion;
};
#endif
};
struct camera* camera_open();
void camera_close(struct camera*);
......@@ -43,7 +42,9 @@ size_t get_frame_size(struct frame*);
capture_time get_frame_timestamp(struct frame*);
#ifdef MOTION_FLAG_IN_FRAMES
int get_frame_motion(struct frame*);
#endif
size_t get_frame_height(struct frame*);
......
#include <math.h>
#ifndef NO_FAKE_MOTION
#include "server_common.h"
#endif
#include <time.h>
#include "fakecapture.h"
#define DEBUG
// compile-time options:
// prefereably, set using -D<MACRONAME> in the Makefile
// or on the command line
//
// define to enable debug output,
// #define DEBUG
//
// define to use a short sequence of frames with numbers 1 -- 5,
// undefine to use an actual film sequence (man in corridor)
// #define SHORT_NUMBER_FRAME_SEQUENCE
//
// we need sockets for fake motion detection,
// define to turn fake motion detection off
// #define NO_FAKE_MOTION
#ifdef SHORT_NUMBER_FRAME_SEQUENCE
#define MEDIA_FRAME_STR "media/%d.jpg"
#define START_FRAME 1
#define NUM_FRAMES 5
#else
#define MEDIA_FRAME_STR "media/film%03d.jpg"
#ifndef START_FRAME
// to skip directly to the parts with motion,
// define START_FRAME to (a bit less than) 86
#define START_FRAME 1
#endif
#define NUM_FRAMES 247
#endif
#define FILEPATH_LENGTH 100
static int frame_nr = START_FRAME;
#ifndef NO_FAKE_MOTION
// we need sockets for fake motion detection
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
// #include <stdlib.h>
// #include <unistd.h>
#include <errno.h>
// #include <string.h> // for memcpy, memset, strlen
static void fake_motion_detect(int frame_nr);
static void fake_motion_init();
static void fake_motion_free();
int motionfd;
struct hostent *motion_server;
struct sockaddr_in motion_serv_addr;
#endif
// for portability with MacOs
#ifdef __MACH__
#define UNUSED_VALUE 1
#define CLOCK_MONOTONIC UNUSED_VALUE
......@@ -27,6 +77,10 @@ media_stream *
capture_open_stream(const char *media_type, const char *media_props)
{
media_stream *res = malloc(sizeof(media_stream));
res->frame_nr = START_FRAME;
#ifndef NO_FAKE_MOTION
fake_motion_init();
#endif
return res;
}
......@@ -36,11 +90,13 @@ capture_get_frame(media_stream *stream)
int fd;
char fname[FILEPATH_LENGTH];
fname[FILEPATH_LENGTH - 1]='\0';
snprintf(fname, FILEPATH_LENGTH - 1, "media/film%03d.jpg", frame_nr++);
if(frame_nr > NUM_FRAMES) {
frame_nr = START_FRAME;
snprintf(fname, FILEPATH_LENGTH - 1, MEDIA_FRAME_STR, stream->frame_nr++);
if(stream->frame_nr > NUM_FRAMES) {
stream->frame_nr = START_FRAME;
};
#ifdef INFO
printf("trying to open %s\n", fname);
#endif
fd = open(fname, O_RDONLY);
if(fd < 0) {
perror("fakecapture:capture_get_frame:open");
......@@ -62,6 +118,10 @@ capture_get_frame(media_stream *stream)
perror("Warning! fakecapture:capture_get_frame:close");
}
#ifndef NO_FAKE_MOTION
fake_motion_detect(stream->frame_nr);
#endif
return res;
}
......@@ -141,4 +201,70 @@ void
capture_close_stream(media_stream *stream)
{
free(stream);
#ifndef NO_FAKE_MOTION
fake_motion_free();
#endif
}
#ifndef NO_FAKE_MOTION
#ifndef MOTION_PORT
#define MOTION_PORT 9090
#endif
static void fake_motion_notify()
{
char msg[100];
snprintf(msg, 100, "GET /motion?Message=%ld\n",time(0));
#ifdef DEBUG
printf("fake_motion_notify: %s\n", msg);
#endif
fake_motion_init();
if (connect(motionfd, (struct sockaddr*)&motion_serv_addr, sizeof(motion_serv_addr)) < 0) {
perror("ERROR connecting");
} else {
if(write_string(motionfd, msg) < 0){
perror("ERROR writing to motion server");
}
close(motionfd);
}
}
static void fake_motion_detect(int frame_nr)
{
#ifdef SHORT_NUMBER_FRAME_SEQUENCE
// START_FRAME 1
// NUM_FRAMES 5
fake_motion_notify();
#else
// START_FRAME 55
// NUM_FRAMES 247
if(frame_nr > 86 && frame_nr < 219 && (frame_nr % 5 == 0)){
fake_motion_notify();
}
#endif
}
static void fake_motion_init()
{
motionfd = socket(AF_INET, SOCK_STREAM, 0);
if (motionfd < 0) {
perror("creating motion socket");
//TODO: set error flag to avoid using motion
}
motion_server = gethostbyname("localhost");
if (motion_server == NULL) {
fprintf(stderr,"ERROR, motion_server name not found\n");
}
bzero((char *) &motion_serv_addr, sizeof(motion_serv_addr));
motion_serv_addr.sin_family = AF_INET;
bcopy((char *)motion_server->h_addr, (char *)&motion_serv_addr.sin_addr.s_addr, motion_server->h_length);
motion_serv_addr.sin_port = htons(MOTION_PORT);
}
static void fake_motion_free()
{
//TODO: any more cleanup needed?
if (close(motionfd)) {
perror("closing motion socket");
}
}
#endif
......@@ -16,6 +16,7 @@
typedef unsigned long long capture_time;
struct _media_stream {
int frame_nr;
};
struct _media_frame {
......
#include "server_common.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
......@@ -9,16 +10,24 @@
#include <pthread.h>
#define USE_CAMERA
#define INFO
#undef DEBUG
// some diagnostic printouts
// #define INFO
// more diagnostic printouts
// #undef DEBUG
// the strncasecmp function is not in the ISO C standard
#define USE_POSIX_FUNCTION
// #define USE_POSIX_FUNCTION
#ifdef USE_CAMERA
#include "camera.h"
#endif
#ifndef MOTION_PORT
#define MOTION_PORT 9090
#endif
#define BUFSIZE 50000
struct client{
int connfd;
......@@ -28,8 +37,29 @@ struct client{
byte* frame_data;
#endif
};
struct global_state {
int listenfd;
int running;
int quit;
pthread_t main_thread;
pthread_t client_thread;
pthread_t ui_thread;
pthread_t bg_thread;
struct pollfd pfd;
int motionfd;
#ifdef USE_CAMERA
camera* cam;
#endif
};
int client_write_string(struct client* client);
int client_write_n(struct client* client, size_t n);
void* serve_client(void *ctxt);
void server_quit(struct global_state* s);
void signal_to_bg_task();
int is_running(struct global_state* state);
/////////////// camera stuff
......@@ -38,21 +68,29 @@ int client_write_n(struct client* client, size_t n);
struct client;
pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t global_cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t global_cond = PTHREAD_COND_INITIALIZER;
#ifdef USE_CAMERA
int try_open_camera(struct client* client)
int try_open_camera(struct global_state* state)
{
client->cam = camera_open();
if (!client->cam){ // Check if null
state->cam = camera_open();
if (!state->cam){ // Check if null
printf("axism3006v: Stream is null, can't connect to camera");
return ERR_OPEN_STREAM;
}
return 0;
}
void close_camera(struct global_state* state)
{
if(state->cam) {
camera_close(state->cam);
state->cam = NULL;
}
}
/* Sets up the packet structure in client->sendBuff.
* This is a minimal HTTP header for an image/jpeg
......@@ -65,7 +103,7 @@ int try_open_camera(struct client* client)
ssize_t setup_packet(struct client* client, uint32_t frame_sz)
{
size_t header_size;
snprintf(client->sendBuff, sizeof(client->sendBuff),
snprintf(client->sendBuff, sizeof(client->sendBuff),
"HTTP/1.0 200 OK\nContent-Length: %d\nContent-Type: image/jpeg\n\n", frame_sz);
header_size = strlen(client->sendBuff);
if(header_size + frame_sz > sizeof(client->sendBuff)) {
......@@ -73,16 +111,17 @@ ssize_t setup_packet(struct client* client, uint32_t frame_sz)
}
client->frame_data = client->sendBuff + header_size;
#ifdef DEBUG
printf("Header size = %d\n", header_size);
printf("Header size = " FORMAT_FOR_SIZE_T "\n", header_size);
#endif
return header_size+frame_sz;
}
/* send packet with frame
* returns 0 on success
*/
int client_send_frame(struct client* client, frame* fr)
{
// this should be a compile-time check
// this should really be a compile-time check, but that is complicated
#ifndef DISABLE_SANITY_CHECKS
if(sizeof(size_t) != sizeof(uint32_t)) {
printf("sizeof(size_t)=%d, sizeof(uint32_t)=%d\n", sizeof(size_t), sizeof(uint32_t));
......@@ -98,21 +137,19 @@ int client_send_frame(struct client* client, frame* fr)
ssize_t packet_sz = setup_packet(client, frame_sz);
if(packet_sz < 0) {
printf("Frame too big for send buffer(%d > %d), skipping.\n", frame_sz, sizeof(client->sendBuff));
printf("Frame too big for send buffer(" FORMAT_FOR_SIZE_T " > " FORMAT_FOR_SIZE_T "), skipping.\n", frame_sz, sizeof(client->sendBuff));
result = 1;
} else {
int written;
#ifdef INFO
printf("encode size (%X=%d) + data\n", frame_sz, frame_sz);
#endif
#ifdef DEBUG
printf("sizeof(size_t)=%d, sizeof(uint32_t)=%d\n", sizeof(size_t), sizeof(uint32_t));
printf("encode size:" FORMAT_FOR_SIZE_T "\n", frame_sz);
printf("sizeof(size_t)=" FORMAT_FOR_SIZE_T ", sizeof(uint32_t)=" FORMAT_FOR_SIZE_T "\n", sizeof(size_t), sizeof(uint32_t));
#endif
memcpy(client->frame_data, data, frame_sz);
written=client_write_n(client, packet_sz);
if(written != packet_sz) {
printf("WARNING! packet_sz=%d, written=%d\n", packet_sz, written);
printf("WARNING! packet_sz=" FORMAT_FOR_SIZE_T ", written=%d\n", packet_sz, written);
result = 3;
} else {
result = 0;
......@@ -142,17 +179,7 @@ int try_get_frame(struct client* client)
int client_write_string(struct client* client)
{
size_t n = strlen(client->sendBuff);
size_t written = 0;
while (written < n) {
ssize_t tmp = write(client->connfd, client->sendBuff, n-written);
if (tmp < 0) {
perror("write_n ERROR");
return tmp;
}
written += tmp;
}
return written;
return write_string(client->connfd, client->sendBuff);
}
/*
......@@ -163,16 +190,7 @@ int client_write_string(struct client* client)
*/
int client_write_n(struct client* client, size_t n)
{
size_t written = 0;
while (written < n) {
ssize_t tmp = write(client->connfd, client->sendBuff, n-written);
if (tmp < 0) {
perror("write_n ERROR");
return tmp;
}
written += tmp;
}