📄 netcam.c
字号:
if (debug_level > CAMERA_INFO ) motion_log(LOG_DEBUG, 0, "netcam_connect with keepalive set, invalid socket." "This could be first time, created a new one with fd %d", netcam->sock); /* Record that this connection has not yet received a Keep-Alive header */ netcam->keepalive_thisconn = FALSE; /* Check the socket status for the keepalive option */ if (getsockopt(netcam->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, &optlen) < 0) { motion_log(LOG_ERR, 1, "netcam_connect : getsockopt()"); return -1; } if (debug_level > CAMERA_INFO) { if (optval==1) motion_log(LOG_DEBUG, 0, "netcam_connect: SO_KEEPALIVE is ON"); else motion_log(LOG_DEBUG, 0, "netcam_connect: SO_KEEPALIVE is OFF"); } /* Set the option active */ optval = 1; optlen = sizeof(optval); if(setsockopt(netcam->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) { motion_log(LOG_ERR, 1, "netcam_connect : setsockopt()"); return -1; } if (debug_level > CAMERA_INFO ) motion_log(LOG_DEBUG, 0, "netcam_connect: SO_KEEPALIVE set on socket."); } else if (debug_level > CAMERA_INFO ) { motion_log(LOG_DEBUG, 0, "netcam_connect re-using socket %d since keepalive is set.", netcam->sock); } } /* 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)); if (debug_level > CAMERA_INFO) motion_log(LOG_DEBUG, 0, "netcam_connect disconnecting netcam (1)"); 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); if (debug_level > CAMERA_INFO) motion_log(LOG_DEBUG, 0, "netcam_connect disconnecting netcam (4)"); 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()"); if (debug_level > CAMERA_INFO) motion_log(LOG_DEBUG, 0, "netcam_connect disconnecting netcam (2)"); 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"); if (debug_level > CAMERA_INFO) motion_log(LOG_DEBUG, 0, "netcam_connect disconnecting netcam (3)"); 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 * Note: Keep-Alive is supported for non-streaming cameras, * if enabled in the netcam's config structure. * 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; /* This will cause a 'continue' of the main loop */ bptr = NULL; /* Return to do the boundary compare from the start */ break; } /* end of while(1) input buffer search */ /* !bptr shows we're processing split boundary */ if (!bptr) continue; } /* end of if(bptr) */ /* boundary string not present, so just write out as much data as possible */ if (remaining) { maxflush = MINVAL(netcam->response->buffer_left, remaining); netcam_check_buffsize(buffer, maxflush); retval = rbuf_flush(netcam, buffer->ptr + buffer->used, maxflush); buffer->used += retval; remaining -= retval; } } /* * read is complete - set the current 'receiving' buffer atomically * as 'latest', and make the buffer previously in 'latest' become * the new 'receiving' */ if (gettimeofday(&curtime, NULL) < 0) { motion_log(LOG_ERR, 1, "gettimeofday in netcam_read_jpeg"); } netcam->receiving->image_time = curtime; /* * Calculate our "running average" time for this netcam's * frame transmissions (except for the first time).
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -