📄 netcam.c
字号:
/* 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). * Note that the average frame time is held in microseconds. */ if (netcam->last_image.tv_sec) { netcam->av_frame_time = (9.0 * netcam->av_frame_time + 1000000.0 * (curtime.tv_sec - netcam->last_image.tv_sec) + (curtime.tv_usec- netcam->last_image.tv_usec)) / 10.0; if (debug_level > 5) motion_log(-1, 0, "Calculated frame time %f", netcam->av_frame_time); } netcam->last_image = curtime; pthread_mutex_lock(&netcam->mutex); xchg = netcam->latest; netcam->latest = netcam->receiving; netcam->receiving = xchg; netcam->imgcnt++; /* * We have a new frame ready. We send a signal so that * any thread (e.g. the motion main loop) waiting for the * next frame to become available may proceed. */ pthread_cond_signal(&netcam->pic_ready); pthread_mutex_unlock(&netcam->mutex); if (!netcam->caps.streaming) netcam_disconnect(netcam); return 0;}/** * netcam_read_ftp_jpeg * * This routine reads from a netcam using the FTP protocol. * The current implementation is still a little experimental, * and needs some additional code for error detection and * recovery. */static int netcam_read_ftp_jpeg(netcam_context_ptr netcam){ netcam_buff_ptr buffer; int len; netcam_buff *xchg; struct timeval curtime; /* Point to our working buffer */ buffer = netcam->receiving; buffer->used = 0; /* Request the image from the remote server */ if (ftp_get_socket(netcam->ftp) <= 0) { motion_log(LOG_ERR, 0, "ftp_get_socket failed in netcam_read_jpeg"); return -1; } /* Now fetch the image using ftp_read. Note this is a blocking call */ do { /* Assure there's enough room in the buffer */ netcam_check_buffsize(buffer, FTP_BUF_SIZE); /* Do the read */ if ((len = ftp_read(netcam->ftp, buffer->ptr + buffer->used, FTP_BUF_SIZE)) < 0) return -1; buffer->used += len; } while (len > 0); 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). * Note that the average frame time is held in microseconds. */ if (netcam->last_image.tv_sec) { netcam->av_frame_time = ((9.0 * netcam->av_frame_time) + 1000000.0 * (curtime.tv_sec - netcam->last_image.tv_sec) + (curtime.tv_usec- netcam->last_image.tv_usec)) / 10.0; if (debug_level > 5) motion_log(-1, 0, "Calculated frame time %f", netcam->av_frame_time); } netcam->last_image = curtime; /* * read is complete - set the current 'receiving' buffer atomically * as 'latest', and make the buffer previously in 'latest' become * the new 'receiving' */ pthread_mutex_lock(&netcam->mutex); xchg = netcam->latest; netcam->latest = netcam->receiving; netcam->receiving = xchg; netcam->imgcnt++; /* * We have a new frame ready. We send a signal so that * any thread (e.g. the motion main loop) waiting for the * next frame to become available may proceed. */ pthread_cond_signal(&netcam->pic_ready); pthread_mutex_unlock(&netcam->mutex); return 0;}/** * netcam_handler_loop * This is the "main loop" for the handler thread. It is created * in netcam_start when a streaming camera is detected. * * Parameters * * arg Pointer to the motion context for this camera * * Returns: NULL pointer * */static void *netcam_handler_loop(void *arg){ int retval; int open_error = 0; netcam_context_ptr netcam = arg; struct context *cnt = netcam->cnt; /* needed for the SETUP macro :-( */ /* Store the corresponding motion thread number in TLS also for this * thread (necessary for 'motion_log' to function properly). */ pthread_setspecific(tls_key_threadnr, (void *)((unsigned long)cnt->threadnr)); if (SETUP) motion_log(LOG_INFO, 0, "Camera handler thread [%d] started", netcam->threadnr); /* * The logic of our loop is very simple. If this is a non- * streaming camera, we re-establish connection with the camera * and read the header record. If it's a streaming camera, we * position to the next "boundary string" in the input stream. * In either case, we then read the following JPEG image into the * next available buffer, updating the "next" and "latest" indices * in our netcam * structure. The loop continues until netcam->finish * or cnt->finish is set. */ while (!netcam->finish) { if (netcam->response) { /* if html input */ if (!netcam->caps.streaming) { if (netcam_connect(netcam, open_error) < 0) { if (!open_error) { /* log first error */ motion_log(LOG_ERR, 0, "re-opening camera (non-streaming)"); open_error = 1; } /* need to have a dynamic delay here */ SLEEP(5,0); continue; } if (open_error) { /* log re-connection */ motion_log(LOG_ERR, 0, "camera re-connected"); open_error = 0; } /* Send our request and look at the response */ if ((retval = netcam_read_first_header(netcam)) != 1) { if (retval > 0) { motion_log(LOG_ERR, 0, "Unrecognized image header (%d)", retval); } else if (retval != -1) { motion_log(LOG_ERR, 0, "Error in header (%d)", retval); } /* need to have a dynamic delay here */ continue; } } else { if (netcam_read_next_header(netcam) < 0) { if (netcam_connect(netcam, open_error) < 0) { if (!open_error) { /* log first error */ motion_log(LOG_ERR, 0, "re-opening camera (streaming)"); open_error = 1; } SLEEP(5,0); continue; } if ((retval = netcam_read_first_header(netcam) != 2)) { if (retval > 0) { motion_log(LOG_ERR, 0, "Unrecognized image header (%d)", retval); } else if (retval != -1) { motion_log(LOG_ERR, 0, "Error in header (%d)", retval); } /* FIXME need some limit */ continue; } } if (open_error) { /* log re-connection */ motion_log(LOG_ERR, 0, "camera re-connected"); open_error = 0; } } } if (netcam->get_image(netcam) < 0) { motion_log(LOG_ERR, 0, "Error getting jpeg image"); /* if FTP connection, attempt to re-connect to server */ if (netcam->ftp) { close(netcam->ftp->control_file_desc); if (ftp_connect(netcam) < 0) { motion_log(LOG_ERR, 0, "Trying to re-connect"); } } continue; } /* * FIXME * Need to check whether the image was received / decoded * satisfactorily */ /* * If non-streaming, want to synchronize our thread with the * motion main-loop. */ if (!netcam->caps.streaming) { pthread_mutex_lock(&netcam->mutex); /* before anything else, check for system shutdown */ if (netcam->finish) { pthread_mutex_unlock(&netcam->mutex); break; } /* * If our current loop has finished before the next * request from the motion main-loop, we do a * conditional wait (wait for signal). On the other * hand, if the motion main-loop has already signalled * us, we just continue. In either event, we clear * the start_capture flag set by the main loop. */ if (!netcam->start_capture) pthread_cond_wait(&netcam->cap_cond, &netcam->mutex); netcam->start_capture = 0; pthread_mutex_unlock(&netcam->mutex); } /* the loop continues forever, or until motion shutdown */ } /* our thread is finished - decrement motion's thread count */ pthread_mutex_lock(&global_lock); threads_running--; pthread_mutex_unlock(&global_lock); /* log out a termination message */ motion_log(LOG_INFO, 0, "netcam camera handler: finish set, exiting"); /* setting netcam->thread_id to zero shows netcam_cleanup we're done */ netcam->thread_id = 0; /* signal netcam_cleanup that we're all done */ pthread_mutex_lock(&netcam->mutex); pthread_cond_signal(&netcam->exiting); pthread_mutex_unlock(&netcam->mutex); /* Goodbye..... */ pthread_exit(NULL);}static int netcam_setup_html(netcam_context_ptr netcam, struct url_t *url) { struct context *cnt = netcam->cnt; const char *ptr; /* working var */ char *userpass; /* temp pointer to config value */ char *encuserpass; /* temp storage for encoded ver */ char *request_pass = NULL; /* temp storage for base64 conv */ int ix; /* First the http context structure */ netcam->response = (struct rbuf *) mymalloc(sizeof(struct rbuf)); memset(netcam->response, 0, sizeof(struct rbuf)); /* * The network camera may require a username and password. If * so, the information can come from two different places in the * motion configuration file. Either it can be present in * the netcam_userpass, or it can be present as a part of the URL * for the camera. We assume the first of these has a higher * relevance. */ if (cnt->conf.netcam_userpass) ptr = cnt->conf.netcam_userpass; else ptr = url->userpass; /* base64_encode needs up to 3 additional chars */ if (ptr) { userpass = mymalloc(strlen(ptr) + 3); strcpy(userpass, ptr); } else userpass = NULL; /* * Now we want to create the actual string which will be used to * connect to the camera. It may or may not contain a username / * password. We first compose a basic connect message, then check * whether a username / password is required and, if so, just * concatenate it with the request. * */ /* space for final \r\n plus string terminator */ ix = 3; /* See if username / password is required */ if (userpass) { /* if either of the above are non-NULL */ /* Allocate space for the base64-encoded string */ encuserpass = mymalloc(BASE64_LENGTH(strlen(userpass)) + 1); /* Fill in the value */ base64_encode(userpass, encuserpass, strlen(userpass)); /* Now create the last part (authorization) of the request */ request_pass = mymalloc(strlen(connect_auth_req) + strlen(encuserpass) + 1); ix += sprintf(request_pass, connect_auth_req, encuserpass); /* free the working variables */ free(encuserpass); } /* * We are now ready to set up the netcam's "connect request". Most of * this comes from the (preset) string 'connect_req', but additional * characters are required if there is a proxy server, or if there is * a username / password for the camera. The variable 'ix' currently * has the number of characters required for username/password (which * could be zero) and for the \r\n and string terminator. We will also * always need space for the netcam path, and if a proxy is being used * we also need space for a preceding 'http://{hostname}' for the * netcam path. */ if (cnt->conf.netcam_proxy) { /* * Allocate space for a working string to contain the path. * The extra 4 is for "://" and string terminator. */ ptr = mymalloc(strlen(url->service) + strlen(url->host) + strlen(url->path) + 4); sprintf((char *)ptr, "http://%s%s", url->host, url->path); } else { /* if no proxy, set as netcam_url path */ ptr = url->path; /* * after generating the connect message the string * will be freed, so we don't want netcam_url_free * to free it as well. */ url->path = NULL; } ix += strlen(ptr); /* * Now that we know how much space we need, we can allocate space * for the connect-request string. */ netcam->connect_request = mymalloc(strlen(connect_req) + ix + strlen(netcam->connect_host)); /* Now create the request string with an sprintf */ sprintf(netcam->connect_request, connect_req, ptr, netcam->connect_host); if (userpass) { strcat(netcam->connect_request, request_pass); free(request_pass); free(userpass); } /* put on the final CRLF onto the request */ strcat(netcam->connect_request, "\r\n"); free((void *)ptr); netcam_url_free(url); /* Cleanup the url data */ /* * Our basic initialisation has been completed. Now we will attempt * to connect with the camera so that we can then get a "header" * in order to find out what kind of camera we are dealing with, * as well as what are the picture dimensions. Note that for * this initial connection, any failure will cause an error * return from netcam_start (unlike later possible attempts at * re-connecting, if the network connection is later interrupted). */ for (ix = 0; ix < MAX_HEADER_RETRIES; ix++) { /* * netcam_connect does an automatic netcam_close, so it's * safe to include it as part of this loop */ if (netcam_connect(netcam, 0) != 0) { motion_log(LOG_ERR, 0,"Failed to open camera - check your config and that netcamera is online"); /* Fatal error on startup */ ix = MAX_HEADER_RETRIES; break;; } if (netcam_read_first_header(netcam) >= 0) break; motion_log(LOG_ERR, 0, "Error reading first header - re-trying"); } if (ix == MAX_HEADER_RETRIES) { motion_log(LOG_ERR, 0, "Failed to read first camera header - giving up for now"); return -1; } /* * If this is a streaming camera, we need to position just * past the boundary string and read the image header */ if (netcam->caps.streaming) { if (netcam_read_next_header(netcam) < 0) { motion_log(LOG_ERR, 0, "Failed to read first stream header - giving up for now"); return -1; } } netcam->get_image = netcam_read_html_jpeg; return 0;}static int netcam_setup_ftp(netcam_context_ptr netcam, struct url_t *url) { struct context *cnt = netcam->cnt; const char *ptr; if ((netcam->ftp = ftp_new_context()) == NULL) return -1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -