📄 netcam.c
字号:
/* * We copy the strings out of the url structure into the ftp_context * structure. By setting url->{string} to NULL we effectively "take * ownership" of the string away from the URL (i.e. it won't be freed * when we cleanup the url structure later). */ netcam->ftp->path = url->path; url->path = NULL; if (cnt->conf.netcam_userpass != NULL) ptr = cnt->conf.netcam_userpass; else { ptr = url->userpass; /* don't set this one NULL, gets freed */ } if (ptr != NULL) { char *cptr; if ((cptr = strchr(ptr, ':')) == NULL) netcam->ftp->user = strdup(ptr); else { netcam->ftp->user = mymalloc((cptr - ptr)); memcpy(netcam->ftp->user,ptr,(cptr - ptr)); netcam->ftp->passwd = strdup(cptr + 1); } } netcam_url_free(url); /* * The ftp context should be all ready to attempt a connection with * the server, so we try .... */ if (ftp_connect(netcam) < 0) { ftp_free_context(netcam->ftp); return -1; } if (ftp_send_type(netcam->ftp, 'I') < 0) { motion_log(LOG_ERR, 0, "Error sending TYPE I to ftp server"); return -1; } netcam->get_image = netcam_read_ftp_jpeg; return 0;}/** * netcam_recv * * This routine receives the next block from the netcam. It takes care * of the potential timeouts and interrupt which may occur because of * the settings from setsockopt. * * Parameters: * * netcam Pointer to a netcam context * buffptr Pointer to the receive buffer * buffsize Length of the buffer * * Returns: * If successful, the length of the message received, otherwise the * error reply from the system call. * */ssize_t netcam_recv(netcam_context_ptr netcam, void *buffptr, size_t buffsize) { ssize_t retval; fd_set fd_r; struct timeval selecttime; FD_ZERO(&fd_r); FD_SET(netcam->sock, &fd_r); selecttime = netcam->timeout; retval = select(FD_SETSIZE, &fd_r, NULL, NULL, &selecttime); if (retval == 0) { /* 0 means timeout */ return -1; } return recv(netcam->sock, buffptr, buffsize, 0);}/** * netcam_cleanup * * This routine releases any allocated data within the netcam context, * then frees the context itself. Extreme care must be taken to assure * that the multi-threading nature of the program is correctly * handled. * This function is also called from motion_init if first time connection * fails and we start retrying until we get a valid first frame from the * camera. * * Parameters: * * netcam Pointer to a netcam context * init_retry_flag 1 when the function is called because we are retrying * making the initial connection with a netcam and we know * we do not need to kill a netcam handler thread * 0 in any other case. * * Returns: Nothing. * */void netcam_cleanup(netcam_context_ptr netcam, int init_retry_flag){ struct timespec waittime; if (!netcam) return; /* * This 'lock' is just a bit of "defensive" programming. It should * only be necessary if the routine is being called from different * threads, but in our Motion design, it should only be called from * the motion main-loop. */ pthread_mutex_lock(&netcam->mutex); if (netcam->cnt->netcam == NULL) { return; } /* * We set the netcam_context pointer in the motion main-loop context * to be NULL, so that this routine won't be called a second time */ netcam->cnt->netcam = NULL; /* * Next we set 'finish' in order to get the camera-handler thread * to stop. */ netcam->finish = 1; /* * If the camera is non-streaming, the handler thread could be waiting * for a signal, so we send it one. If it's actually waiting on the * condition, it won't actually start yet because we still have * netcam->mutex locked. */ if (!netcam->caps.streaming) { pthread_cond_signal(&netcam->cap_cond); } /* * Once the camera-handler gets to the end of it's loop (probably as * soon as we release netcam->mutex), because netcam->finish has been * set it will exit it's loop, do anything it needs to do with the * netcam context, and then send *us* as signal (netcam->exiting). * Note that when we start our wait on netcam->exiting, our lock on * netcam->mutex is automatically released, which will allow the * handler to complete it's loop, notice that 'finish' is set and exit. * This should always work, but again (defensive programming) we * use pthread_cond_timedwait and, if our timeout (8 seconds) expires * we just do the cleanup the handler would normally have done. This * assures that (even if there is a bug in our code) motion will still * be able to exit. * If the init_retry_flag is not set the netcam_cleanup code was * called while retrying the initial connection to a netcam and then * there is no camera-handler started yet and thread_running must * not be decremented. */ waittime.tv_sec = time(NULL) + 8; /* Seems that 3 is too small */ waittime.tv_nsec = 0; if (!init_retry_flag && pthread_cond_timedwait(&netcam->exiting, &netcam->mutex, &waittime) != 0) { /* * Although this shouldn't happen, if it *does* happen we will * log it (just for the programmer's information) */ motion_log(-1, 0, "No response from camera " "handler - it must have already died"); pthread_mutex_lock(&global_lock); threads_running--; pthread_mutex_unlock(&global_lock); } /* we don't need any lock anymore, so release it */ pthread_mutex_unlock(&netcam->mutex); /* and cleanup the rest of the netcam_context structure */ if (netcam->connect_host != NULL) { free(netcam->connect_host); } if (netcam->connect_request != NULL) { free(netcam->connect_request); } if (netcam->boundary != NULL) { free(netcam->boundary); } if (netcam->latest != NULL) { if (netcam->latest->ptr != NULL) { free(netcam->latest->ptr); } free(netcam->latest); } if (netcam->receiving != NULL) { if (netcam->receiving->ptr != NULL) { free(netcam->receiving->ptr); } free(netcam->receiving); } if (netcam->jpegbuf != NULL) { if (netcam->jpegbuf->ptr != NULL) { free(netcam->jpegbuf->ptr); } free(netcam->jpegbuf); } if (netcam->ftp != NULL) { ftp_free_context(netcam->ftp); } else { netcam_disconnect(netcam); } if (netcam->response != NULL) { free(netcam->response); } pthread_mutex_destroy(&netcam->mutex); pthread_cond_destroy(&netcam->cap_cond); pthread_cond_destroy(&netcam->pic_ready); pthread_cond_destroy(&netcam->exiting); free(netcam);}/** * netcam_next * * This routine is called when the main 'motion' thread wants a new * frame of video. It fetches the most recent frame available from * the netcam, converts it to YUV420P, and returns it to motion. * * Parameters: * cnt Pointer to the context for this thread * image Pointer to a buffer for the returned image * * Returns: Error code */int netcam_next(struct context *cnt, unsigned char *image){ netcam_context_ptr netcam; /* * Here we have some more "defensive programming". This check should * never be true, but if it is just return with a "fatal error". */ if ((!cnt) || (!cnt->netcam)) return NETCAM_FATAL_ERROR; netcam = cnt->netcam; if (!netcam->latest->used) { if (debug_level) { motion_log(LOG_INFO, 0, "netcam_next called with no data in buffer"); } return NETCAM_NOTHING_NEW_ERROR; } /* * If we are controlling a non-streaming camera, we synchronize the * motion main-loop with the camera-handling thread through a signal, * together with a flag to say "start your next capture". */ if (!netcam->caps.streaming) { pthread_mutex_lock(&netcam->mutex); netcam->start_capture = 1; pthread_cond_signal(&netcam->cap_cond); pthread_mutex_unlock(&netcam->mutex); } /* * If an error occurs in the JPEG decompression which follows this, * jpeglib will return to the code within this 'if'. Basically, our * approach is to just return a NULL (failed) to the caller (an * error message has already been produced by the libjpeg routines) */ if (setjmp(netcam->setjmp_buffer)) { return NETCAM_GENERAL_ERROR | NETCAM_JPEG_CONV_ERROR; } /* If there was no error, process the latest image buffer */ return netcam_proc_jpeg(netcam, image);}/** * netcam_start * * This routine is called from the main motion thread. It's job is * to open up the requested camera device and do any required * initialisation. If the camera is a streaming type, then this * routine must also start up the camera-handling thread to take * care of it. * * Parameters: * * cnt Pointer to the motion context structure for this device * * Returns: 0 on success, -1 on any failure */int netcam_start(struct context *cnt){ netcam_context_ptr netcam; /* local pointer to our context */ pthread_attr_t handler_attribute; /* attributes of our handler thread */ int retval; /* working var */ struct url_t url; /* for parsing netcam URL */ if (debug_level > CAMERA_INFO) motion_log(-1, 0, "entered netcam_start()"); memset(&url, 0, sizeof(url)); if (SETUP) motion_log(LOG_INFO, 0, "Camera thread starting..."); /* * Create a new netcam_context for this camera * and clear all the entries. */ cnt->netcam = (struct netcam_context *) mymalloc(sizeof(struct netcam_context)); memset(cnt->netcam, 0, sizeof(struct netcam_context)); netcam = cnt->netcam; /* Just for clarity in remaining code */ netcam->cnt = cnt; /* Fill in the "parent" info */ /* * Fill in our new netcam context with all known initial * values. */ /* Our image buffers */ netcam->receiving = mymalloc(sizeof(netcam_buff)); memset(netcam->receiving, 0, sizeof(netcam_buff)); netcam->receiving->ptr = mymalloc(NETCAM_BUFFSIZE); netcam->jpegbuf = mymalloc(sizeof(netcam_buff)); memset(netcam->jpegbuf, 0, sizeof(netcam_buff)); netcam->jpegbuf->ptr = mymalloc(NETCAM_BUFFSIZE); netcam->latest = mymalloc(sizeof(netcam_buff)); memset(netcam->latest, 0, sizeof(netcam_buff)); netcam->latest->ptr = mymalloc(NETCAM_BUFFSIZE); netcam->timeout.tv_sec = READ_TIMEOUT; /* Thread control structures */ pthread_mutex_init(&netcam->mutex, NULL); pthread_cond_init(&netcam->cap_cond, NULL); pthread_cond_init(&netcam->pic_ready, NULL); pthread_cond_init(&netcam->exiting, NULL); /* Initialise the average frame time to the user's value */ netcam->av_frame_time = 1000000.0 / cnt->conf.frame_limit; /* * If a proxy has been specified, parse that URL. */ if (cnt->conf.netcam_proxy) { netcam_url_parse(&url, cnt->conf.netcam_proxy); if (!url.host) { motion_log(LOG_ERR, 0, "Invalid netcam_proxy (%s)", cnt->conf.netcam_proxy); netcam_url_free(&url); return -1; } if (url.userpass) { motion_log(LOG_ERR, 0, "Username/password not allowed on a proxy URL"); netcam_url_free(&url); return -1; } /* * A 'proxy' means that our eventual 'connect' to our * camera must be sent to the proxy, and that our 'GET' must * include the full path to the camera host. */ netcam->connect_host = url.host; url.host = NULL; netcam->connect_port = url.port; netcam_url_free(&url); /* Finished with proxy */ } /* * Parse the URL from the configuration data */ netcam_url_parse(&url, cnt->conf.netcam_url); if (!url.host) { motion_log(LOG_ERR, 0, "Invalid netcam_url (%s)", cnt->conf.netcam_url); netcam_url_free(&url); return -1; } if (cnt->conf.netcam_proxy == NULL) { netcam->connect_host = url.host; url.host = NULL; netcam->connect_port = url.port; } if ((url.service) && (!strcmp(url.service, "http")) ){ retval = netcam_setup_html(netcam, &url); } else if ((url.service) && (!strcmp(url.service, "ftp")) ){ retval = netcam_setup_ftp(netcam, &url); } else { motion_log(LOG_ERR, 0, "Invalid netcam service '%s' - " "must be http or ftp", url.service); netcam_url_free(&url); return -1; } if (retval < 0) return -1; /* * We expect that, at this point, we should be positioned to read * the first image available from the camera (directly after the * applicable header). We want to decode the image in order to get * the dimensions (width and height). If successful, we will use * these to set the required image buffer(s) in our netcam_struct. */ if ((retval = netcam->get_image(netcam)) != 0) { motion_log(LOG_ERR, 0, "Failed trying to read first image"); return -1; } /* * If an error occurs in the JPEG decompression which follows this, * jpeglib will return to the code within this 'if'. If such an error * occurs during startup, we will just abandon this attempt. */ if (setjmp(netcam->setjmp_buffer)) { motion_log(LOG_ERR, 0, "libjpeg decompression failure " "on first frame - giving up!"); return -1; } netcam_get_dimensions(netcam); /* Motion currently requires that image height and width is a * multiple of 16. So we check for this. */ if (netcam->width % 16) { motion_log(LOG_ERR, 0, "netcam image width (%d) is not modulo 16", netcam->width); return -1; } if (netcam->height % 16) { motion_log(LOG_ERR, 0, "netcam image height (%d) is not modulo 16", netcam->height); return -1; } /* Fill in camera details into context structure */ cnt->imgs.width = netcam->width; cnt->imgs.height = netcam->height; cnt->imgs.size = (netcam->width * netcam->height * 3) / 2; cnt->imgs.motionsize = netcam->width * netcam->height; cnt->imgs.type = VIDEO_PALETTE_YUV420P; /* * Everything is now ready - start up the * "handler thread". */ pthread_attr_init(&handler_attribute); pthread_attr_setdetachstate(&handler_attribute, PTHREAD_CREATE_DETACHED); pthread_mutex_lock(&global_lock); netcam->threadnr = ++threads_running; pthread_mutex_unlock(&global_lock); if ((retval = pthread_create(&netcam->thread_id, &handler_attribute, &netcam_handler_loop, netcam)) < 0) { motion_log(LOG_ERR, 1, "Starting camera handler thread [%d]", netcam->threadnr); return -1; } return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -