⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 webcam.c

📁 video motion detection of linux base
💻 C
字号:
/* *	webcam.c *	Streaming webcam using jpeg images over a multipart/x-mixed-replace stream *	Copyright (C) 2002 Jeroen Vreeken (pe1rxq@amsat.org) * *	This program is free software; you can redistribute it and/or modify *	it under the terms of the GNU General Public License as published by *	the Free Software Foundation; either version 2 of the License, or *	(at your option) any later version. * *	This program is distributed in the hope that it will be useful, *	but WITHOUT ANY WARRANTY; without even the implied warranty of *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *	GNU General Public License for more details. * *	You should have received a copy of the GNU General Public License *	along with this program; if not, write to the Free Software *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include "picture.h"/* picture.h already includes motion.h and this one webcam.h */#include <sys/time.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <ctype.h>#include <sys/fcntl.h>#include <sys/stat.h>/* This function sets up a TCP/IP socket for incoming requests. It is called only during * initialisation of Motion from the function webcam_init * The function sets up a a socket on the port number given by _port_. * If the parameter _local_ is not zero the socket is setup to only accept connects from localhost. * Otherwise any client IP address is accepted. The function returns an integer representing the socket. */int http_bindsock(int port, int local){	int sl, optval=1;	struct sockaddr_in sin;	if ((sl=socket(PF_INET, SOCK_STREAM, 0))<0) {		motion_log(LOG_ERR, 1, "socket()");		return -1;	}	memset(&sin, 0, sizeof(struct sockaddr_in));	sin.sin_family=AF_INET;	sin.sin_port=htons(port);		if (local)		sin.sin_addr.s_addr=htonl(INADDR_LOOPBACK);	else		sin.sin_addr.s_addr=htonl(INADDR_ANY);	setsockopt(sl, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));	if (bind(sl, (struct sockaddr *)&sin, sizeof(struct sockaddr_in))==-1) {		motion_log(LOG_ERR, 1, "bind()");		close(sl);		return -1;	}	if (listen(sl, DEF_MAXWEBQUEUE)==-1) {		motion_log(LOG_ERR, 1, "listen()");		close(sl);		return -1;	}	return sl;}static int http_acceptsock(int sl){	int sc;	unsigned long i;	struct sockaddr_in sin;	socklen_t addrlen=sizeof(struct sockaddr_in);	if ((sc=accept(sl, (struct sockaddr *)&sin, &addrlen))>=0) {		i=1;		ioctl(sc, FIONBIO, &i);		return sc;	}		motion_log(LOG_ERR, 1, "accept()");	return -1;}/* Webcam flush sends any outstanding data to all connected clients. * It continuously goes through the client list until no data is able * to be sent (either because there isn't any, or because the clients * are not able to accept it). */static void webcam_flush(struct webcam *list, int *stream_count, int lim){	int written;            /* the number of bytes actually written */	struct webcam *client;  /* pointer to the client being served */	int workdone = 0;       /* flag set any time data is successfully	                           written */	client = list->next;	while (client) {			/* If data waiting for client, try to send it */		if (client->tmpbuffer) {					/* We expect that list->filepos < list->tmpbuffer->size			 * should always be true.  The check is more for safety,			 * in case of trouble is some other part of the code.			 * Note that if it is false, the following section will			 * clean up.			 */			if (client->filepos < client->tmpbuffer->size) {								/* Here we are finally ready to write out the				 * data.  Remember that (because the socket				 * has been set non-blocking) we may only				 * write out part of the buffer.  The var				 * 'filepos' contains how much of the buffer				 * has already been written.				 */				written=write(client->socket, 					      client->tmpbuffer->ptr + client->filepos,					      client->tmpbuffer->size - client->filepos);						/* If any data has been written, update the				 * data pointer and set the workdone flag				 */				if (written>0) {					client->filepos+=written;					workdone = 1;				}			} else				written = 0;						/* If we have written the entire buffer to the socket,			 * or if there was some error (other than EAGAIN, which			 * means the system couldn't take it), this request is			 * finished.			 */			if ( (client->filepos >= client->tmpbuffer->size) ||			     (written < 0 && errno!=EAGAIN)) {				/* If no other clients need this buffer, free it */				if (--client->tmpbuffer->ref <= 0) {					free(client->tmpbuffer->ptr);					free(client->tmpbuffer);				}							/* Mark this client's buffer as empty */				client->tmpbuffer=NULL;				client->nr++;			}						/* If the client is no longer connected, or the total			 * number of frames already sent to this client is			 * greater than our configuration limit, disconnect			 * the client and free the webcam struct			 */			if ( (written<0 && errno!=EAGAIN) ||			     (lim && !client->tmpbuffer && client->nr>lim) ) {				void *tmp;				close(client->socket);								if (client->next)					client->next->prev=client->prev;								client->prev->next=client->next;				tmp=client;				client=client->prev;				free(tmp);				(*stream_count)--;			}		}		/* end if (client->tmpbuffer) */				/* Step the the next client in the list.  If we get to the		 * end of the list, check if anything was written during		 * that loop; (if so) reset the 'workdone' flag and go back		 * to the beginning		 */		client = client->next;		if (!client && workdone) {			client = list->next;			workdone = 0;		}	}			/* end while (client) */}/* Routine to create a new "tmpbuffer", which is a common * object used by all clients connected to a single camera */static struct webcam_buffer *webcam_tmpbuffer(int size){	struct webcam_buffer *tmpbuffer=mymalloc(sizeof(struct webcam_buffer));	tmpbuffer->ref=0;	tmpbuffer->ptr=mymalloc(size);			return tmpbuffer;}static void webcam_add_client(struct webcam *list, int sc){	struct webcam *new = mymalloc(sizeof(struct webcam));	static const char header[] = "HTTP/1.0 200 OK\r\n"			"Server: Motion/"VERSION"\r\n"			"Connection: close\r\n"			"Max-Age: 0\r\n"			"Expires: 0\r\n"			"Cache-Control: no-cache, private\r\n"			"Pragma: no-cache\r\n"			"Content-Type: multipart/x-mixed-replace; boundary=--BoundaryString\r\n\r\n";	memset(new, 0, sizeof(struct webcam));	new->socket=sc;		if ((new->tmpbuffer = webcam_tmpbuffer(sizeof(header))) == NULL) {		motion_log(LOG_ERR, 1, "Error creating tmpbuffer in webcam_add_client");	} else {		memcpy(new->tmpbuffer->ptr, header, sizeof(header)-1);		new->tmpbuffer->size = sizeof(header)-1;	}		new->prev=list;	new->next=list->next;		if (new->next)		new->next->prev=new;		list->next=new;}static void webcam_add_write(struct webcam *list, struct webcam_buffer *tmpbuffer, unsigned int fps){	struct timeval curtimeval;	unsigned long int curtime;	gettimeofday(&curtimeval, NULL);	curtime=curtimeval.tv_usec+1000000L*curtimeval.tv_sec;		while (list->next) {		list=list->next;				if (list->tmpbuffer==NULL && ((curtime-list->last) >= 1000000L/fps)) {			list->last=curtime;			list->tmpbuffer=tmpbuffer;			tmpbuffer->ref++;			list->filepos=0;		}	}		if (tmpbuffer->ref<=0) {		free(tmpbuffer->ptr);		free(tmpbuffer);	}}/* We walk through the chain of webcam structs until we reach the end. * Here we check if the tmpbuffer points to NULL * We return 1 if it finds a list->tmpbuffer which is a NULL pointer which would * be the next client ready to be sent a new image. If not a 0 is returned. */static int webcam_check_write(struct webcam *list){	while (list->next) {		list=list->next;				if (list->tmpbuffer==NULL)			return 1;	}	return 0;}/* This function is called from motion.c for each motion thread starting up. * The function setup the incoming tcp socket that the clients connect to * The function returns an integer representing the socket. */int webcam_init(struct context *cnt){	cnt->webcam.socket=http_bindsock(cnt->conf.webcam_port, cnt->conf.webcam_localhost);	cnt->webcam.next=NULL;	cnt->webcam.prev=NULL;	return cnt->webcam.socket;}/* This function is called from the motion_loop when it ends * and motion is terminated or restarted */void webcam_stop(struct context *cnt){		struct webcam *list;	struct webcam *next = cnt->webcam.next;	if (cnt->conf.setup_mode)		motion_log(-1, 0, "Closing webcam listen socket");		close(cnt->webcam.socket);		if (cnt->conf.setup_mode)		motion_log(LOG_ERR, 1, "Closing active webcam sockets");	while (next) {		list=next;		next=list->next;				if (list->tmpbuffer) {			free(list->tmpbuffer->ptr);			free(list->tmpbuffer);		}				close(list->socket);		free(list);	}}/* webcam_put is the starting point of the webcam loop. It is called from * the motion_loop with the argument 'image' pointing to the latest frame. * If config option 'webcam_motion' is 'on' this function is called once * per second (frame 0) and when Motion is detected excl pre_capture. * If config option 'webcam_motion' is 'off' this function is called once * per captured picture frame. * It is always run in setup mode for each picture frame captured and with * the special setup image. * The function does two things: * It looks for possible waiting new clients and adds them. * It sends latest picture frame to all connected clients. * Note: Clients that have disconnected are handled in the webcam_flush() * function */void webcam_put(struct context *cnt, unsigned char *image){	struct timeval timeout; 	struct webcam_buffer *tmpbuffer;	fd_set fdread;	int sl=cnt->webcam.socket;	int sc;	/* the following string has an extra 16 chars at end for length */	const char jpeghead[] = "--BoundaryString\r\n"	                        "Content-type: image/jpeg\r\n"	                        "Content-Length:                ";	int headlength = sizeof(jpeghead) - 1;	/* don't include terminator */	char len[20];	/* will be used for sprintf, must be >= 16 */		/* timeout struct used to timeout the time we wait for a client	 * and we do not wait at all	 */	timeout.tv_sec=0;	timeout.tv_usec=0;	FD_ZERO(&fdread);	FD_SET(cnt->webcam.socket, &fdread);		/* If we have not reached the max number of allowed clients per	 * thread we will check to see if new clients are waiting to connect.	 * If this is the case we add the client as a new webcam struct and	 * add this to the end of the chain of webcam structs that are linked	 * to each other.	 */	if ((cnt->stream_count < DEF_MAXSTREAMS) &&	    (select(sl+1, &fdread, NULL, NULL, &timeout)>0)) {		sc=http_acceptsock(sl);		webcam_add_client(&cnt->webcam, sc);		cnt->stream_count++;	}		/* call flush to send any previous partial-sends which are waiting */	webcam_flush(&cnt->webcam, &cnt->stream_count, cnt->conf.webcam_limit);		/* Check if any clients have available buffers */	if (webcam_check_write(&cnt->webcam)) {		/* yes - create a new tmpbuffer for current image.		 * Note that this should create a buffer which is *much* larger		 * than necessary, but it is difficult to estimate the		 * minimum size actually required.		 */		tmpbuffer = webcam_tmpbuffer(cnt->imgs.size);				/* check if allocation went ok */		if (tmpbuffer) {			int imgsize;			/* We need a pointer that points to the picture buffer			 * just after the mjpeg header. We create a working pointer wptr			 * to be used in the call to put_picture_memory which we can change			 * and leave tmpbuffer->ptr intact.			 */			unsigned char *wptr = tmpbuffer->ptr;						/* For web protocol, our image needs to be preceded			 * with a little HTTP, so we put that into the buffer			 * first.			 */			memcpy(wptr, jpeghead, headlength);			/* update our working pointer to point past header */			wptr += headlength;			/* create a jpeg image and place into tmpbuffer */			tmpbuffer->size = put_picture_memory(cnt, wptr, cnt->imgs.size, image,			                                     cnt->conf.webcam_quality);			/* fill in the image length into the header */			imgsize = sprintf(len, "%9ld\r\n\r\n", tmpbuffer->size);			memcpy(wptr - imgsize, len, imgsize);						/* append a CRLF for good measure */			memcpy(wptr + tmpbuffer->size, "\r\n", 2);						/* now adjust tmpbuffer->size to reflect the			 * header at the beginning and the extra CRLF			 * at the end.			 */			tmpbuffer->size += headlength + 2;						/* and finally put this buffer to all clients with			 * no outstanding data from previous frames.			 */			webcam_add_write(&cnt->webcam, tmpbuffer, cnt->conf.webcam_maxrate);		} else {			motion_log(LOG_ERR, 1, "Error creating tmpbuffer");		}	}		/* Now we call flush again.  This time (assuming some clients were	 * ready for the new frame) the new data will be written out.	 */	webcam_flush(&cnt->webcam, &cnt->stream_count, cnt->conf.webcam_limit);		return;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -