doorbell

changeset 2:d3f2a2b19504

doorbell server under construction
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 13 Mar 2016 07:56:03 +0200
parents a5755687dd75
children 73b16c2ae6bf
files .hgignore doorbelld/Makefile doorbelld/src/dynarr.c doorbelld/src/dynarr.h doorbelld/src/main.c doorbelld/src/srv.c doorbelld/src/srv.h
diffstat 7 files changed, 460 insertions(+), 0 deletions(-) [+]
line diff
     1.1 --- a/.hgignore	Tue Mar 08 03:26:01 2016 +0200
     1.2 +++ b/.hgignore	Sun Mar 13 07:56:03 2016 +0200
     1.3 @@ -2,3 +2,5 @@
     1.4  \.d$
     1.5  \.swp$
     1.6  spycam$
     1.7 +doorbelld$
     1.8 +\.ppm$
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/doorbelld/Makefile	Sun Mar 13 07:56:03 2016 +0200
     2.3 @@ -0,0 +1,13 @@
     2.4 +src = $(wildcard src/*.c)
     2.5 +obj = $(src:.c=.o)
     2.6 +bin = doorbelld
     2.7 +
     2.8 +CFLAGS = -pedantic -Wall -g
     2.9 +LDFLAGS = -lwcam -lpthread
    2.10 +
    2.11 +$(bin): $(obj)
    2.12 +	$(CC) -o $@ $(obj) $(LDFLAGS)
    2.13 +
    2.14 +.PHONY: clean
    2.15 +clean:
    2.16 +	rm -f $(obj) $(bin)
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/doorbelld/src/dynarr.c	Sun Mar 13 07:56:03 2016 +0200
     3.3 @@ -0,0 +1,128 @@
     3.4 +/* dynarr - dynamic resizable C array data structure
     3.5 + * author: John Tsiombikas <nuclear@member.fsf.org>
     3.6 + * license: public domain
     3.7 + */
     3.8 +#include <stdio.h>
     3.9 +#include <stdlib.h>
    3.10 +#include <string.h>
    3.11 +#include "dynarr.h"
    3.12 +
    3.13 +/* The array descriptor keeps auxilliary information needed to manipulate
    3.14 + * the dynamic array. It's allocated adjacent to the array buffer.
    3.15 + */
    3.16 +struct arrdesc {
    3.17 +	int nelem, szelem;
    3.18 +	int max_elem;
    3.19 +	int bufsz;	/* not including the descriptor */
    3.20 +};
    3.21 +
    3.22 +#define DESC(x)		((struct arrdesc*)((char*)(x) - sizeof(struct arrdesc)))
    3.23 +
    3.24 +void *dynarr_alloc(int elem, int szelem)
    3.25 +{
    3.26 +	struct arrdesc *desc;
    3.27 +
    3.28 +	if(!(desc = malloc(elem * szelem + sizeof *desc))) {
    3.29 +		return 0;
    3.30 +	}
    3.31 +	desc->nelem = desc->max_elem = elem;
    3.32 +	desc->szelem = szelem;
    3.33 +	desc->bufsz = elem * szelem;
    3.34 +	return (char*)desc + sizeof *desc;
    3.35 +}
    3.36 +
    3.37 +void dynarr_free(void *da)
    3.38 +{
    3.39 +	if(da) {
    3.40 +		free(DESC(da));
    3.41 +	}
    3.42 +}
    3.43 +
    3.44 +void *dynarr_resize(void *da, int elem)
    3.45 +{
    3.46 +	int newsz;
    3.47 +	void *tmp;
    3.48 +	struct arrdesc *desc;
    3.49 +
    3.50 +	if(!da) return 0;
    3.51 +	desc = DESC(da);
    3.52 +
    3.53 +	newsz = desc->szelem * elem;
    3.54 +
    3.55 +	if(!(tmp = realloc(desc, newsz + sizeof *desc))) {
    3.56 +		return 0;
    3.57 +	}
    3.58 +	desc = tmp;
    3.59 +
    3.60 +	desc->nelem = desc->max_elem = elem;
    3.61 +	desc->bufsz = newsz;
    3.62 +	return (char*)desc + sizeof *desc;
    3.63 +}
    3.64 +
    3.65 +int dynarr_empty(void *da)
    3.66 +{
    3.67 +	return DESC(da)->nelem ? 0 : 1;
    3.68 +}
    3.69 +
    3.70 +int dynarr_size(void *da)
    3.71 +{
    3.72 +	return DESC(da)->nelem;
    3.73 +}
    3.74 +
    3.75 +
    3.76 +/* stack semantics */
    3.77 +void *dynarr_push(void *da, void *item)
    3.78 +{
    3.79 +	struct arrdesc *desc;
    3.80 +	int nelem;
    3.81 +
    3.82 +	desc = DESC(da);
    3.83 +	nelem = desc->nelem;
    3.84 +
    3.85 +	if(nelem >= desc->max_elem) {
    3.86 +		/* need to resize */
    3.87 +		struct arrdesc *tmp;
    3.88 +		int newsz = desc->max_elem ? desc->max_elem * 2 : 1;
    3.89 +
    3.90 +		if(!(tmp = dynarr_resize(da, newsz))) {
    3.91 +			fprintf(stderr, "failed to resize\n");
    3.92 +			return da;
    3.93 +		}
    3.94 +		da = tmp;
    3.95 +		desc = DESC(da);
    3.96 +		desc->nelem = nelem;
    3.97 +	}
    3.98 +
    3.99 +	if(item) {
   3.100 +		memcpy((char*)da + desc->nelem++ * desc->szelem, item, desc->szelem);
   3.101 +	}
   3.102 +	return da;
   3.103 +}
   3.104 +
   3.105 +void *dynarr_pop(void *da)
   3.106 +{
   3.107 +	struct arrdesc *desc;
   3.108 +	int nelem;
   3.109 +
   3.110 +	desc = DESC(da);
   3.111 +	nelem = desc->nelem;
   3.112 +
   3.113 +	if(!nelem) return da;
   3.114 +
   3.115 +	if(nelem <= desc->max_elem / 3) {
   3.116 +		/* reclaim space */
   3.117 +		struct arrdesc *tmp;
   3.118 +		int newsz = desc->max_elem / 2;
   3.119 +
   3.120 +		if(!(tmp = dynarr_resize(da, newsz))) {
   3.121 +			fprintf(stderr, "failed to resize\n");
   3.122 +			return da;
   3.123 +		}
   3.124 +		da = tmp;
   3.125 +		desc = DESC(da);
   3.126 +		desc->nelem = nelem;
   3.127 +	}
   3.128 +	desc->nelem--;
   3.129 +
   3.130 +	return da;
   3.131 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/doorbelld/src/dynarr.h	Sun Mar 13 07:56:03 2016 +0200
     4.3 @@ -0,0 +1,38 @@
     4.4 +/* dynarr - dynamic resizable C array data structure
     4.5 + * author: John Tsiombikas <nuclear@member.fsf.org>
     4.6 + * license: public domain
     4.7 + */
     4.8 +#ifndef DYNARR_H_
     4.9 +#define DYNARR_H_
    4.10 +
    4.11 +void *dynarr_alloc(int elem, int szelem);
    4.12 +void dynarr_free(void *da);
    4.13 +void *dynarr_resize(void *da, int elem);
    4.14 +
    4.15 +int dynarr_empty(void *da);
    4.16 +int dynarr_size(void *da);
    4.17 +
    4.18 +/* stack semantics */
    4.19 +void *dynarr_push(void *da, void *item);
    4.20 +void *dynarr_pop(void *da);
    4.21 +
    4.22 +
    4.23 +/* usage example:
    4.24 + * -------------
    4.25 + * int *arr = dynarr_alloc(0, sizeof *arr);
    4.26 + *
    4.27 + * int x = 10;
    4.28 + * arr = dynarr_push(arr, &x);
    4.29 + * x = 5;
    4.30 + * arr = dynarr_push(arr, &x);
    4.31 + * x = 42;
    4.32 + * arr = dynarr_push(arr, &x);
    4.33 + *
    4.34 + * for(i=0; i<dynarr_size(arr); i++) {
    4.35 + *     printf("%d\n", arr[i]);
    4.36 + *  }
    4.37 + *  dynarr_free(arr);
    4.38 + */
    4.39 +
    4.40 +
    4.41 +#endif	/* DYNARR_H_ */
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/doorbelld/src/main.c	Sun Mar 13 07:56:03 2016 +0200
     5.3 @@ -0,0 +1,141 @@
     5.4 +#include <stdio.h>
     5.5 +#include <stdlib.h>
     5.6 +#include <string.h>
     5.7 +#include <errno.h>
     5.8 +#include <signal.h>
     5.9 +#include <pthread.h>
    5.10 +#include <wcam.h>
    5.11 +#include "srv.h"
    5.12 +
    5.13 +void *frame_proc(void *arg);
    5.14 +int save_image(const char *fname, unsigned char *pixels, int xsz, int ysz);
    5.15 +void sighandler(int s);
    5.16 +
    5.17 +int fd;
    5.18 +int width = 640;
    5.19 +int height = 480;
    5.20 +int framerate = 30;
    5.21 +unsigned char *frame;
    5.22 +int done;
    5.23 +int frame_available;
    5.24 +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    5.25 +pthread_cond_t condvar = PTHREAD_COND_INITIALIZER;
    5.26 +
    5.27 +int main(int argc, char **argv)
    5.28 +{
    5.29 +	char *devfile = "/dev/video0";
    5.30 +	pthread_t thread;
    5.31 +
    5.32 +	if(argv[1]) {
    5.33 +		devfile = argv[1];
    5.34 +	}
    5.35 +
    5.36 +	signal(SIGINT, sighandler);
    5.37 +	signal(SIGQUIT, sighandler);
    5.38 +	signal(SIGSEGV, sighandler);
    5.39 +
    5.40 +	if(srv_init(2828) == -1) {
    5.41 +		return 1;
    5.42 +	}
    5.43 +
    5.44 +	if(!(frame = malloc(width * height * 4))) {
    5.45 +		perror("failed to allocate frame");
    5.46 +		return 1;
    5.47 +	}
    5.48 +
    5.49 +	if((fd = wcam_open(devfile, width, height, framerate)) == -1) {
    5.50 +		fprintf(stderr, "failed to open video device: %s\n", devfile);
    5.51 +		return 1;
    5.52 +	}
    5.53 +	wcam_start(fd);
    5.54 +
    5.55 +	if(pthread_create(&thread, 0, frame_proc, 0) == -1) {
    5.56 +		fprintf(stderr, "failed to create frame processing thread\n");
    5.57 +		return 1;
    5.58 +	}
    5.59 +
    5.60 +	while(!done) {
    5.61 +		int i, res;
    5.62 +		int *sock = srv_sockets();
    5.63 +		int num_sockets = srv_num_sockets();
    5.64 +		int maxfd = srv_max_socket();
    5.65 +		fd_set rdset;
    5.66 +
    5.67 +		if(fd > maxfd) maxfd = fd;
    5.68 +
    5.69 +		FD_ZERO(&rdset);
    5.70 +		FD_SET(fd, &rdset);
    5.71 +
    5.72 +		for(i=0; i<num_sockets; i++) {
    5.73 +			FD_SET(sock[i], &rdset);
    5.74 +		}
    5.75 +
    5.76 +		while((res = select(maxfd + 1, &rdset, 0, 0, 0)) == -1 && errno == EINTR);
    5.77 +		if(res == -1) {
    5.78 +			fprintf(stderr, "unexpected failure while waiting for I/O\n");
    5.79 +			break;
    5.80 +		}
    5.81 +
    5.82 +		if(FD_ISSET(fd, &rdset)) {
    5.83 +			wcam_wait(fd);
    5.84 +			pthread_mutex_lock(&mutex);
    5.85 +			frame_available = 1;
    5.86 +			pthread_mutex_unlock(&mutex);
    5.87 +			pthread_cond_signal(&condvar);
    5.88 +		}
    5.89 +
    5.90 +		for(i=0; i<num_sockets; i++) {
    5.91 +			if(FD_ISSET(sock[i], &rdset)) {
    5.92 +				srv_handle(sock[i]);
    5.93 +			}
    5.94 +		}
    5.95 +	}
    5.96 +
    5.97 +	frame_available = 1;
    5.98 +	pthread_cond_signal(&condvar);
    5.99 +	pthread_join(thread, 0);
   5.100 +	free(frame);
   5.101 +	wcam_stop(fd);
   5.102 +	wcam_close(fd);
   5.103 +	return 0;
   5.104 +}
   5.105 +
   5.106 +void *frame_proc(void *arg)
   5.107 +{
   5.108 +	for(;;) {
   5.109 +		pthread_mutex_lock(&mutex);
   5.110 +		while(!frame_available) {
   5.111 +			pthread_cond_wait(&condvar, &mutex);
   5.112 +		}
   5.113 +		frame_available = 0;
   5.114 +		pthread_mutex_unlock(&mutex);
   5.115 +
   5.116 +		if(done) break;
   5.117 +
   5.118 +		wcam_read_frame_rgb(fd, frame);
   5.119 +		save_image("output.ppm", frame, width, height);
   5.120 +	}
   5.121 +	return 0;
   5.122 +}
   5.123 +
   5.124 +int save_image(const char *fname, unsigned char *pixels, int xsz, int ysz)
   5.125 +{
   5.126 +	int i, num_pixels = xsz * ysz;
   5.127 +	FILE *fp = fopen(fname, "wb");
   5.128 +	if(!fp) return -1;
   5.129 +
   5.130 +	fprintf(fp, "P6\n%d %d\n255\n", xsz, ysz);
   5.131 +	for(i=0; i<num_pixels; i++) {
   5.132 +		fputc(*pixels++, fp);
   5.133 +		fputc(*pixels++, fp);
   5.134 +		fputc(*pixels++, fp);
   5.135 +		++pixels;
   5.136 +	}
   5.137 +	fclose(fp);
   5.138 +	return 0;
   5.139 +}
   5.140 +
   5.141 +void sighandler(int s)
   5.142 +{
   5.143 +	done = 1;
   5.144 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/doorbelld/src/srv.c	Sun Mar 13 07:56:03 2016 +0200
     6.3 @@ -0,0 +1,123 @@
     6.4 +#include <stdio.h>
     6.5 +#include <stdlib.h>
     6.6 +#include <string.h>
     6.7 +#include <errno.h>
     6.8 +#include <assert.h>
     6.9 +#include <unistd.h>
    6.10 +#include <fcntl.h>
    6.11 +#include <sys/socket.h>
    6.12 +#include <arpa/inet.h>
    6.13 +#include "srv.h"
    6.14 +#include "dynarr.h"
    6.15 +
    6.16 +static int lis_sock = -1, max_socket = -1;
    6.17 +static int *csock;
    6.18 +
    6.19 +int srv_init(int port)
    6.20 +{
    6.21 +	struct sockaddr_in addr;
    6.22 +
    6.23 +	if(lis_sock != -1) {
    6.24 +		fprintf(stderr, "%s: already running\n", __func__);
    6.25 +		return -1;
    6.26 +	}
    6.27 +
    6.28 +	if((lis_sock = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
    6.29 +		fprintf(stderr, "%s: failed to create listening socket\n", __func__);
    6.30 +		return -1;
    6.31 +	}
    6.32 +	if(!(csock = dynarr_alloc(1, sizeof *csock))) {
    6.33 +		fprintf(stderr, "%s: failed to allocate client socket array\n", __func__);
    6.34 +		return -1;
    6.35 +	}
    6.36 +	csock[0] = lis_sock;
    6.37 +	max_socket = lis_sock;
    6.38 +
    6.39 +	fcntl(lis_sock, F_SETFD, fcntl(lis_sock, F_GETFD) | O_NONBLOCK);
    6.40 +
    6.41 +	memset(&addr, 0, sizeof addr);
    6.42 +	addr.sin_family = AF_INET;
    6.43 +	addr.sin_port = htons(port);
    6.44 +	addr.sin_addr.s_addr = INADDR_ANY;
    6.45 +	if(bind(lis_sock, (struct sockaddr*)&addr, sizeof addr) == -1) {
    6.46 +		fprintf(stderr, "%s: failed to bind port %d\n", __func__, port);
    6.47 +		close(lis_sock);
    6.48 +		lis_sock = -1;
    6.49 +		return -1;
    6.50 +	}
    6.51 +
    6.52 +	return 0;
    6.53 +}
    6.54 +
    6.55 +void srv_shutdown(void)
    6.56 +{
    6.57 +	int i, sz = dynarr_size(csock);
    6.58 +	for(i=0; i<sz; i++) {
    6.59 +		close(csock[i]);
    6.60 +	}
    6.61 +	dynarr_free(csock);
    6.62 +	csock = 0;
    6.63 +	lis_sock = -1;
    6.64 +	max_socket = -1;
    6.65 +}
    6.66 +
    6.67 +int srv_num_sockets(void)
    6.68 +{
    6.69 +	return dynarr_size(csock);
    6.70 +}
    6.71 +
    6.72 +int *srv_sockets(void)
    6.73 +{
    6.74 +	return csock;
    6.75 +}
    6.76 +
    6.77 +int srv_max_socket(void)
    6.78 +{
    6.79 +	return max_socket;
    6.80 +}
    6.81 +
    6.82 +int srv_handle(int s)
    6.83 +{
    6.84 +	static char buf[1024];
    6.85 +	int sz;
    6.86 +
    6.87 +	if(s == lis_sock) {
    6.88 +		/* incoming connection */
    6.89 +		struct sockaddr_in addr;
    6.90 +		int addr_size;
    6.91 +
    6.92 +		if((s = accept(lis_sock, (struct sockaddr*)&addr, (int*)&addr_size)) == -1) {
    6.93 +			fprintf(stderr, "%s: failed to accept incoming connection\n", __func__);
    6.94 +			return -1;
    6.95 +		}
    6.96 +		printf("%s: incoming connection from %s:%d\n", __func__,
    6.97 +				inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
    6.98 +		csock = dynarr_push(csock, &s);
    6.99 +		assert(csock);
   6.100 +		return 0;
   6.101 +	}
   6.102 +
   6.103 +	/* handle client */
   6.104 +	while((sz = read(s, buf, sizeof buf - 1)) > 0) {
   6.105 +		printf("%s: got data: %s\n", __func__, buf);
   6.106 +	}
   6.107 +	if(sz < 0 && errno != EAGAIN) {
   6.108 +		/* client closed connection probably */
   6.109 +		int i, num_clients = dynarr_size(csock);
   6.110 +		printf("%s: removing client\n", __func__);
   6.111 +		close(s);
   6.112 +		for(i=0; i<num_clients; i++) {
   6.113 +			if(csock[i] == s) {
   6.114 +				csock[i] = csock[num_clients - 1];
   6.115 +				csock = dynarr_pop(csock);
   6.116 +				break;
   6.117 +			}
   6.118 +		}
   6.119 +		assert(i < num_clients);
   6.120 +	}
   6.121 +	return 0;
   6.122 +}
   6.123 +
   6.124 +void srv_send_frame(unsigned char *frame, int xsz, int ysz)
   6.125 +{
   6.126 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/doorbelld/src/srv.h	Sun Mar 13 07:56:03 2016 +0200
     7.3 @@ -0,0 +1,15 @@
     7.4 +#ifndef SRV_H_
     7.5 +#define SRV_H_
     7.6 +
     7.7 +int srv_init(int port);
     7.8 +void srv_shutdown(void);
     7.9 +
    7.10 +int srv_num_sockets(void);
    7.11 +int *srv_sockets(void);
    7.12 +int srv_max_socket(void);
    7.13 +
    7.14 +int srv_handle(int s);
    7.15 +
    7.16 +void srv_send_frame(unsigned char *frame, int xsz, int ysz);
    7.17 +
    7.18 +#endif	/* SRV_H_ */