📄 netcam.c
字号:
return -1; } if (firstflag) { if ((ret = http_result_code(header)) != 200) { if (debug_level > 5) motion_log(-1, 0, "HTTP Result code %d", ret); free(header); return ret; } firstflag = 0; free(header); continue; } if (*header == 0) /* blank line received */ break; if (SETUP) motion_log(LOG_DEBUG, 0, "Received first header"); /* Check if this line is the content type */ if ((ret = netcam_check_content_type(header)) >= 0) { retval = ret; /* * We are expecting to find one of three types: * 'multipart/x-mixed-replace', 'multipart/mixed' * or 'image/jpeg'. The first two will be received * from a streaming camera, and the third from a * camera which provides a single frame only. */ switch (ret) { case 1: /* not streaming */ if (SETUP) motion_log(LOG_DEBUG, 0, "Non-streaming camera"); netcam->caps.streaming = 0; break; case 2: /* streaming */ if (SETUP) motion_log(LOG_DEBUG, 0, "Streaming camera"); netcam->caps.streaming = 1; if ((boundary = strstr(header, "boundary="))) { /* * on error recovery this * may already be set * */ if (netcam->boundary) free(netcam->boundary); netcam->boundary = strdup(boundary + 9); /* * HTTP protocol apparently permits the boundary string * to be quoted (the Lumenera does this, which caused * trouble) so we need to get rid of any surrounding * quotes */ check_quote(netcam->boundary); netcam->boundary_length = strlen(netcam->boundary); if (SETUP) { motion_log(LOG_DEBUG, 0, "Boundary string [%s]", netcam->boundary); } } break; default:{ /* error */ motion_log(LOG_ERR, 0, "Unrecognized content type"); free(header); return -1; } } } else if ((ret = (int) netcam_check_content_length(header)) >= 0) { if (SETUP) motion_log(LOG_DEBUG, 0, "Content-length present"); netcam->caps.content_length = 1; /* set flag */ netcam->receiving->content_length = ret; } free(header); } free(header); return retval;}/** * netcam_disconnect * * Disconnect from the network camera. * * Parameters: * * netcam pointer to netcam context * * Returns: Nothing * */static void netcam_disconnect(netcam_context_ptr netcam){ if (netcam->sock > 0) { if (close(netcam->sock) < 0) motion_log(LOG_ERR, 1, "netcam_disconnect"); netcam->sock = -1; }}/** * netcam_connect * * Attempt to open the network camera as a stream device. * * Parameters: * * netcam pointer to netcam_context structure * err_flag flag to suppress error printout (1 => suppress) * Note that errors which indicate something other than * a network connection problem are not suppressed. * * Returns: 0 for success, -1 for error * */static int netcam_connect(netcam_context_ptr netcam, int err_flag){ struct sockaddr_in server; /* for connect */ struct addrinfo *res; /* for getaddrinfo */ int ret; int saveflags; int back_err; socklen_t len; fd_set fd_w; struct timeval selecttime; /* Assure any previous connection has been closed */ netcam_disconnect(netcam); /* create a new socket */ if ((netcam->sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { motion_log(LOG_ERR, 1, "Attempting to create socket"); return -1; } /* lookup the hostname given in the netcam URL */ if ((ret = getaddrinfo(netcam->connect_host, NULL, NULL, &res)) != 0) { if (!err_flag) motion_log(LOG_ERR, 0, "getaddrinfo() failed (%s): %s", netcam->connect_host, gai_strerror(ret)); netcam_disconnect(netcam); return -1; } /* * Fill the hostname details into the 'server' structure and * attempt to connect to the remote server */ memset(&server, 0, sizeof(server)); memcpy(&server, res->ai_addr, sizeof(server)); freeaddrinfo(res); server.sin_family = AF_INET; server.sin_port = htons(netcam->connect_port); /* * We set the socket non-blocking and then use a 'select' * system call to control the timeout. */ if ((saveflags = fcntl(netcam->sock, F_GETFL, 0)) < 0) { motion_log(LOG_ERR, 1, "fcntl(1) on socket"); netcam_disconnect(netcam); return -1; } /* Set the socket non-blocking */ if (fcntl(netcam->sock, F_SETFL, saveflags | O_NONBLOCK) < 0) { motion_log(LOG_ERR, 1, "fcntl(2) on socket"); netcam_disconnect(netcam); return -1; } /* Now the connect call will return immediately */ ret = connect(netcam->sock, (struct sockaddr *) &server, sizeof(server)); back_err = errno; /* save the errno from connect */ /* If the connect failed with anything except EINPROGRESS, error */ if ((ret < 0) && (back_err != EINPROGRESS)) { if (!err_flag) motion_log(LOG_ERR, 1, "connect() failed (%d)", back_err); netcam_disconnect(netcam); return -1; } /* Now we do a 'select' with timeout to wait for the connect */ FD_ZERO(&fd_w); FD_SET(netcam->sock, &fd_w); selecttime.tv_sec = CONNECT_TIMEOUT; selecttime.tv_usec = 0; ret = select(FD_SETSIZE, NULL, &fd_w, NULL, &selecttime); if (ret == 0) { /* 0 means timeout */ if (!err_flag) motion_log(LOG_ERR, 0, "timeout on connect()"); netcam_disconnect(netcam); return -1; } /* * A +ve value returned from the select (actually, it must be a * '1' showing 1 fd's changed) shows the select has completed. * Now we must check the return code from the select. */ len = sizeof(ret); if (getsockopt(netcam->sock, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { motion_log(LOG_ERR, 0, "getsockopt after connect"); netcam_disconnect(netcam); return -1; } /* If the return code is anything except 0, error on connect */ if (ret) { if (!err_flag) motion_log(LOG_ERR, 1, "connect returned error"); netcam_disconnect(netcam); return -1; } /* The socket info is stored in the rbuf structure of our context */ rbuf_initialize(netcam); return 0; /* success */}/** * netcam_check_buffsize * * This routine checks whether there is enough room in a buffer to copy * some additional data. If there is not enough room, it will re-allocate * the buffer and adjust it's size. * * Parameters: * buff Pointer to a netcam_image_buffer structure * numbytes The number of bytes to be copied * * Returns: Nothing */static void netcam_check_buffsize(netcam_buff_ptr buff, size_t numbytes){ if ((buff->size - buff->used) >= numbytes) return; if (debug_level > CAMERA_INFO) motion_log(-1, 0, "expanding buffer from %d to %d bytes", (int) buff->size, (int) buff->size + NETCAM_BUFFSIZE); buff->ptr = myrealloc(buff->ptr, buff->size + NETCAM_BUFFSIZE, "netcam_check_buf_size"); buff->size += NETCAM_BUFFSIZE;}/** * netcam_read_html_jpeg * * This routine reads a jpeg image from the netcam. When it is called, * the stream is already positioned just after the image header. * * This routine is called under the four variations of two different * conditions: * 1) Streaming or non-streaming camera * 2) Header does or does not include Content-Length * Additionally, if it is a streaming camera, there must always be a * boundary-string. * * The routine will (attempt to) read the JPEG image. If a Content-Length * is present, it will be used (this will result in more efficient code, and * also code which should be better at detecting and recovering from possible * error conditions). * * If a boundary-string is present (and, if the camera is streaming, this * *must* be the case), the routine will assure that it is recognized and * acted upon. * * Our algorithm for this will be as follows: * 1) If a Content-Length is present, set the variable "remaining" * to be equal to that value, else set it to a "very large" * number. * 2) While there is more data available from the camera: * a) If there is a "boundary string" specified (from the initial * header): * i) If the amount of data in the input buffer is less than * the length of the boundary string, get more data into * the input buffer (error if failure). * ii) If the boundary string is found, check how many * characters remain in the input buffer before the start * of the boundary string. If that is less than the * variable "remaining", reset "remaining" to be equal * to that number. * b) Try to copy up to "remaining" characters from the input * buffer into our destination buffer. * c) If there are no more characters available from the camera, * exit this loop, else subtract the number of characters * actually copied from the variable "remaining". * 3) If Content-Length was present, and "remaining" is not equal * to zero, generate a warning message for logging. * * * Parameters: * netcam Pointer to netcam context * * * Returns: 0 for success, -1 for error * */static int netcam_read_html_jpeg(netcam_context_ptr netcam){ netcam_buff_ptr buffer; size_t remaining; /* # characters to read */ size_t maxflush; /* # chars before boundary */ size_t rem, rlen, ix; /* working vars */ int retval; char *ptr, *bptr, *rptr; netcam_buff *xchg; struct timeval curtime; /* * Initialisation - set our local pointers to the context * information */ buffer = netcam->receiving; /* Assure the target buffer is empty */ buffer->used = 0; /* Prepare for read loop */ if (buffer->content_length != 0) remaining = buffer->content_length; else remaining = 999999; /* Now read in the data */ while (remaining) { /* Assure data in input buffer */ if (netcam->response->buffer_left <= 0) { retval = rbuf_read_bufferful(netcam); if (retval <= 0) break; netcam->response->buffer_left = retval; netcam->response->buffer_pos = netcam->response->buffer; } /* If a boundary string is present, take it into account */ bptr = netcam->boundary; if (bptr) { rptr = netcam->response->buffer_pos; rlen = netcam->response->buffer_left; /* Loop through buffer looking for start of boundary */ while (1) { /* * Logic gets a little complicated here. The * problem is that we are reading in input * data in packets, and there is a (small) * chance that the boundary string could be * split across successive packets. * First a quick check if the string *might* * be in the current buffer. */ if (rlen > remaining) rlen = remaining; if (remaining < netcam->boundary_length) break; if ((ptr = memchr(rptr, *bptr, rlen)) == NULL) /* boundary not here (normal path) */ break; /* * At least the first char was found in the * buffer - check for the rest */ rem = rlen - (ptr - rptr); for (ix = 1; (ix < rem) && (ix < netcam->boundary_length); ix++) { if (ptr[ix] != bptr[ix]) break; } if ((ix != netcam->boundary_length) && (ix != rem)) { /* * Not pointing at a boundary string - * step along input */ ix = ptr - rptr + 1; rptr += ix; rlen -= ix; if (rlen <= 0) /* boundary not in buffer - go copy out */ break; /* * not yet decided - continue * through input */ continue; } /* * If we finish the 'for' with * ix == boundary_length, that means we found * the string, and should copy any data which * precedes it into the target buffer, then * exit the main loop. */ if (ix == netcam->boundary_length) { if ((ptr - netcam->response->buffer) < (int) remaining) remaining = ptr - netcam->response->buffer; /* go copy everything up to boundary */ break; } /* * If not, and ix == rem, that means we reached * the end of the input buffer in the middle of * our check, which is the (somewhat messy) * problem mentioned above. * * Assure there is data before potential * boundary string */ if (ptr != netcam->response->buffer) { /* * We have a boundary string crossing * packets :-(. We will copy all the * data up to the beginning of the * potential boundary, then re-position * the (partial) string to the * beginning and get some more input * data. First we flush the input * buffer up to the beginning of the * (potential) boundary string */ ix = ptr - netcam->response->buffer_pos; netcam_check_buffsize(buffer, ix); retval = rbuf_flush(netcam, buffer->ptr + buffer->used, ix); buffer->used += retval; remaining -= retval; /* * Now move the boundary fragment to * the head of the input buffer. * This is really a "hack" - ideally, * we should have a function within the * module netcam_wget.c to do this job! */ if (debug_level > CAMERA_INFO) { motion_log(-1, 0, "Potential split boundary - " "%d chars flushed, %d " "re-positioned", ix, (int) netcam->response->buffer_left); } memmove(netcam->response->buffer, ptr, netcam->response->buffer_left); } /* end of boundary split over buffer */ retval = netcam_recv(netcam, netcam->response->buffer + netcam->response->buffer_left, sizeof(netcam->response->buffer) - netcam->response->buffer_left); if (retval <= 0) { /* this is a fatal error */ motion_log(LOG_ERR, 1, "recv() fail after boundary string"); return -1; } /* Reset the input buffer pointers */ netcam->response->buffer_left = retval + netcam->response->buffer_left; netcam->response->buffer_pos = netcam->response->buffer;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -