1 /**
2 * netcam.c
3 *
4 * Module of common routines for handling network cameras.
5 *
6 */
7
8 #include "translate.h"
9 #include "motion.h"
10
11 #include <regex.h> /* For parsing of the URL */
12
13 #include "netcam_http.h"
14 #include "netcam_ftp.h"
15
16 /*
17 * The following three routines (netcam_url_match, netcam_url_parse and
18 * netcam_url_free are for 'parsing' (i.e. separating into the relevant
19 * components) the URL provided by the user. They make use of regular
20 * expressions (which is outside the scope of this module, so detailed
21 * comments are not provided). netcam_url_parse is called from netcam_start,
22 * and puts the "broken-up" components of the URL into the "url" element of
23 * the netcam_context structure.
24 *
25 * Note that the routines are not "very clever", but they work sufficiently
26 * well for the limited requirements of this module. The expression:
27 * (http)://(((.*):(.*))@)?([^/:]|[-.a-z0-9]+)(:([0-9]+))?($|(/[^:]*))
28 * requires
29 * 1) a string which begins with 'http', followed by '://'
30 * 2) optionally a '@' which is preceded by two strings
31 * (with 0 or more characters each) separated by a ':'
32 * [this is for an optional username:password]
33 * 3) a string comprising alpha-numerics, '-' and '.' characters
34 * [this is for the hostname]
35 * 4) optionally a ':' followed by one or more numeric characters
36 * [this is for an optional port number]
37 * 5) finally, either an end of line or a series of segments,
38 * each of which begins with a '/', and contains anything
39 * except a ':'
40 */
41
42 /**
43 * netcam_url_match
44 *
45 * Finds the matched part of a regular expression
46 *
47 * Parameters:
48 *
49 * m A structure containing the regular expression to be used
50 * input The input string
51 *
52 * Returns: The string which was matched
53 *
54 */
netcam_url_match(regmatch_t m,const char * input)55 static char *netcam_url_match(regmatch_t m, const char *input)
56 {
57 char *match = NULL;
58 int len;
59
60 if (m.rm_so != -1) {
61 len = m.rm_eo - m.rm_so;
62
63 if ((match = mymalloc(len + 1)) != NULL) {
64 strncpy(match, input + m.rm_so, len);
65 match[len] = '\0';
66 }
67 }
68
69 return match;
70 }
71
netcam_url_invalid(struct url_t * parse_url)72 static void netcam_url_invalid(struct url_t *parse_url){
73
74 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO,_("Invalid URL. Can not parse values."));
75
76 parse_url->host = malloc(5);
77 parse_url->service = malloc(5);
78 parse_url->path = malloc(10);
79 parse_url->userpass = malloc(10);
80 parse_url->port = 0;
81 sprintf(parse_url->host, "%s","????");
82 sprintf(parse_url->service, "%s","????");
83 sprintf(parse_url->path, "%s","INVALID");
84 sprintf(parse_url->userpass, "%s","INVALID");
85
86 }
87 /**
88 * netcam_url_parse
89 *
90 * parses a string containing a URL into it's components
91 *
92 * Parameters:
93 * parse_url A structure which will receive the results
94 * of the parsing
95 * text_url The input string containing the URL
96 *
97 * Returns: Nothing
98 *
99 */
netcam_url_parse(struct url_t * parse_url,const char * text_url)100 void netcam_url_parse(struct url_t *parse_url, const char *text_url)
101 {
102 char *s;
103 int i;
104
105 const char *re = "(http|ftp|mjpg|mjpeg|rtsp|rtmp)://(((.*):(.*))@)?"
106 "([^/:]|[-_.a-z0-9]+)(:([0-9]+))?($|(/[^*]*))";
107 regex_t pattbuf;
108 regmatch_t matches[10];
109
110 if (!strncmp(text_url, "file", 4))
111 re = "(file)://(((.*):(.*))@)?([/:])?(:([0-9]+))?($|(/[^*]*))";
112
113 if (!strncmp(text_url, "jpeg", 4))
114 re = "(jpeg)://(((.*):(.*))@)?([/:])?(:([0-9]+))?($|(/[^*]*))";
115
116 if (!strncmp(text_url, "v4l2", 4))
117 re = "(v4l2)://(((.*):(.*))@)?([/:])?(:([0-9]+))?($|(/[^*]*))";
118
119 /* Note that log messages are commented out to avoid leaking info related
120 * to user/host/pass etc. Keeing them in the code for easier debugging if
121 * it is needed
122 */
123
124 //MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "Entry netcam_url_parse data %s",text_url);
125
126 memset(parse_url, 0, sizeof(struct url_t));
127 /*
128 * regcomp compiles regular expressions into a form that is
129 * suitable for regexec searches
130 * regexec matches the URL string against the regular expression
131 * and returns an array of pointers to strings matching each match
132 * within (). The results that we need are finally placed in parse_url.
133 */
134 if (!regcomp(&pattbuf, re, REG_EXTENDED | REG_ICASE)) {
135 if (regexec(&pattbuf, text_url, 10, matches, 0) != REG_NOMATCH) {
136 for (i = 0; i < 10; i++) {
137 if ((s = netcam_url_match(matches[i], text_url)) != NULL) {
138 //MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "Parse case %d data %s", i, s);
139 switch (i) {
140 case 1:
141 parse_url->service = s;
142 break;
143 case 3:
144 parse_url->userpass = s;
145 break;
146 case 6:
147 parse_url->host = s;
148 break;
149 case 8:
150 parse_url->port = atoi(s);
151 free(s);
152 break;
153 case 9:
154 parse_url->path = s;
155 break;
156 /* Other components ignored */
157 default:
158 free(s);
159 break;
160 }
161 }
162 }
163 } else {
164 netcam_url_invalid(parse_url);
165 }
166 } else {
167 netcam_url_invalid(parse_url);
168 }
169 if (((!parse_url->port) && (parse_url->service)) ||
170 ((parse_url->port > 65535) && (parse_url->service))) {
171 if (!strcmp(parse_url->service, "http"))
172 parse_url->port = 80;
173 else if (!strcmp(parse_url->service, "ftp"))
174 parse_url->port = 21;
175 else if (!strcmp(parse_url->service, "rtmp"))
176 parse_url->port = 1935;
177 else if (!strcmp(parse_url->service, "rtsp"))
178 parse_url->port = 554;
179 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, _("Using port number %d"),parse_url->port);
180 }
181
182 regfree(&pattbuf);
183 }
184
185 /**
186 * netcam_url_free
187 *
188 * General cleanup of the URL structure, called from netcam_cleanup.
189 *
190 * Parameters:
191 *
192 * parse_url Structure containing the parsed data.
193 *
194 * Returns: Nothing
195 *
196 */
netcam_url_free(struct url_t * parse_url)197 void netcam_url_free(struct url_t *parse_url)
198 {
199 free(parse_url->service);
200 parse_url->service = NULL;
201
202 free(parse_url->userpass);
203 parse_url->userpass = NULL;
204
205 free(parse_url->host);
206 parse_url->host = NULL;
207
208 free(parse_url->path);
209 parse_url->path = NULL;
210 }
211
212 /**
213 * netcam_handler_loop
214 * This is the "main loop" for the handler thread. It is created
215 * in netcam_start when a streaming camera is detected.
216 *
217 * Parameters
218 *
219 * arg Pointer to the motion context for this camera.
220 *
221 * Returns: NULL pointer
222 *
223 */
netcam_handler_loop(void * arg)224 static void *netcam_handler_loop(void *arg)
225 {
226 int retval;
227 int open_error = 0;
228 netcam_context_ptr netcam = arg;
229 struct context *cnt = netcam->cnt; /* Needed for the SETUP macro :-( */
230
231 netcam->handler_finished = FALSE;
232
233 util_threadname_set("nc",netcam->threadnr,netcam->cnt->conf.camera_name);
234
235 /* Store the corresponding motion thread number in TLS also for this
236 * thread (necessary for 'MOTION_LOG' to function properly).
237 */
238 pthread_setspecific(tls_key_threadnr, (void *)((unsigned long)cnt->threadnr));
239
240 MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
241 ,_("Camera handler thread [%d] started"), netcam->threadnr);
242 /*
243 * The logic of our loop is very simple. If this is a non-
244 * streaming camera, we re-establish connection with the camera
245 * and read the header record. If it's a streaming camera, we
246 * position to the next "boundary string" in the input stream.
247 * In either case, we then read the following JPEG image into the
248 * next available buffer, updating the "next" and "latest" indices
249 * in our netcam * structure. The loop continues until netcam->finish
250 * or cnt->finish is set.
251 */
252
253 while (!netcam->finish) {
254 if (netcam->response) { /* If html input */
255 if (netcam->caps.streaming == NCS_UNSUPPORTED) {
256 /* Non-streaming ie. jpeg */
257 if (!netcam->connect_keepalive ||
258 (netcam->connect_keepalive && netcam->keepalive_timeup)) {
259 /* If keepalive flag set but time up, time to close this socket. */
260 if (netcam->connect_keepalive && netcam->keepalive_timeup) {
261 MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO
262 ,_("Closing netcam socket as Keep-Alive time is up "
263 "(camera sent Close field). A reconnect should happen."));
264 netcam_disconnect(netcam);
265 netcam->keepalive_timeup = FALSE;
266 }
267
268 /* And the netcam_connect call below will open a new one. */
269 if (netcam_connect(netcam, open_error) < 0) {
270 if (!open_error) { /* Log first error. */
271 MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO
272 ,_("re-opening camera (non-streaming)"));
273 open_error = 1;
274 }
275 /* Need to have a dynamic delay here. */
276 SLEEP(5, 0);
277 continue;
278 }
279
280 if (open_error) { /* Log re-connection */
281 MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO
282 ,_("camera re-connected"));
283 open_error = 0;
284 }
285 }
286 /* Send our request and look at the response. */
287 if ((retval = netcam_read_first_header(netcam)) != 1) {
288 if (retval > 0) {
289 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
290 ,_("Unrecognized image header (%d)"), retval);
291 } else if (retval != -1) {
292 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
293 ,_("Error in header (%d)"), retval);
294 }
295 /* Need to have a dynamic delay here. */
296 continue;
297 }
298 } else if (netcam->caps.streaming == NCS_MULTIPART) { /* Multipart Streaming */
299 if (netcam_read_next_header(netcam) < 0) {
300 if (netcam_connect(netcam, open_error) < 0) {
301 if (!open_error) { /* Log first error */
302 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
303 ,_("re-opening camera (streaming)"));
304 open_error = 1;
305 }
306 SLEEP(5, 0);
307 continue;
308 }
309
310 if ((retval = netcam_read_first_header(netcam) != 2)) {
311 if (retval > 0) {
312 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
313 ,_("Unrecognized image header (%d)"), retval);
314 } else if (retval != -1) {
315 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
316 ,_("Error in header (%d)"), retval);
317 }
318 /* FIXME need some limit. */
319 continue;
320 }
321 }
322 if (open_error) { /* Log re-connection */
323 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
324 ,_("camera re-connected"));
325 open_error = 0;
326 }
327 } else if (netcam->caps.streaming == NCS_BLOCK) { /* MJPG-Block streaming */
328 /*
329 * Since we cannot move in the stream here, because we will read past the
330 * MJPG-block-header, error handling is done while reading MJPG blocks.
331 */
332 }
333 }
334
335
336 if (netcam->get_image(netcam) < 0) {
337 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO,_("Error getting jpeg image"));
338 /* If FTP connection, attempt to re-connect to server. */
339 if (netcam->ftp) {
340 close(netcam->ftp->control_file_desc);
341 if (ftp_connect(netcam) < 0)
342 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO,_("Trying to re-connect"));
343 }
344 continue;
345 }
346
347
348 /*
349 * FIXME
350 * Need to check whether the image was received / decoded
351 * satisfactorily.
352 */
353
354 /*
355 * If non-streaming, want to synchronize our thread with the
356 * motion main-loop.
357 */
358 if (netcam->caps.streaming == NCS_UNSUPPORTED) {
359 pthread_mutex_lock(&netcam->mutex);
360
361 /* Before anything else, check for system shutdown. */
362 if (netcam->finish) {
363 pthread_mutex_unlock(&netcam->mutex);
364 break;
365 }
366
367 /*
368 * If our current loop has finished before the next
369 * request from the motion main-loop, we do a
370 * conditional wait (wait for signal). On the other
371 * hand, if the motion main-loop has already signalled
372 * us, we just continue. In either event, we clear
373 * the start_capture flag set by the main loop.
374 */
375 if (!netcam->start_capture)
376 pthread_cond_wait(&netcam->cap_cond, &netcam->mutex);
377
378 netcam->start_capture = 0;
379
380 pthread_mutex_unlock(&netcam->mutex);
381 }
382 /* The loop continues forever, or until motion shutdown. */
383 }
384
385 /* Our thread is finished - decrement motion's thread count. */
386 pthread_mutex_lock(&global_lock);
387 threads_running--;
388 pthread_mutex_unlock(&global_lock);
389
390 /* Log out a termination message. */
391 MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
392 ,_("netcam camera handler: finish set, exiting"));
393
394 netcam->handler_finished = TRUE;
395
396 /* Signal netcam_cleanup that we're all done. */
397 pthread_mutex_lock(&netcam->mutex);
398 pthread_cond_signal(&netcam->exiting);
399 pthread_mutex_unlock(&netcam->mutex);
400
401 /* Goodbye..... */
402 pthread_exit(NULL);
403 }
404
405 /**
406 * netcam_cleanup
407 *
408 * This routine releases any allocated data within the netcam context,
409 * then frees the context itself. Extreme care must be taken to assure
410 * that the multi-threading nature of the program is correctly
411 * handled.
412 * This function is also called from motion_init if first time connection
413 * fails and we start retrying until we get a valid first frame from the
414 * camera.
415 *
416 * Parameters:
417 *
418 * netcam Pointer to a netcam context
419 * init_retry_flag 1 when the function is called because we are retrying
420 * making the initial connection with a netcam and we know
421 * we do not need to kill a netcam handler thread
422 * 0 in any other case.
423 *
424 * Returns: Nothing.
425 *
426 */
netcam_cleanup(netcam_context_ptr netcam,int init_retry_flag)427 void netcam_cleanup(netcam_context_ptr netcam, int init_retry_flag){
428 struct timespec waittime;
429
430 if (!netcam) return;
431
432 /*
433 * This 'lock' is just a bit of "defensive" programming. It should
434 * only be necessary if the routine is being called from different
435 * threads, but in our Motion design, it should only be called from
436 * the motion main-loop.
437 */
438 pthread_mutex_lock(&netcam->mutex);
439
440 if (netcam->cnt->netcam == NULL)
441 return;
442
443 /*
444 * We set the netcam_context pointer in the motion main-loop context
445 * to be NULL, so that this routine won't be called a second time.
446 */
447 netcam->cnt->netcam = NULL;
448
449 /*
450 * Next we set 'finish' in order to get the camera-handler thread
451 * to stop.
452 */
453 netcam->finish = 1;
454
455 /*
456 * If the camera is non-streaming, the handler thread could be waiting
457 * for a signal, so we send it one. If it's actually waiting on the
458 * condition, it won't actually start yet because we still have
459 * netcam->mutex locked.
460 */
461
462 if (netcam->caps.streaming == NCS_UNSUPPORTED)
463 pthread_cond_signal(&netcam->cap_cond);
464
465
466 /*
467 * Once the camera-handler gets to the end of it's loop (probably as
468 * soon as we release netcam->mutex), because netcam->finish has been
469 * set it will exit it's loop, do anything it needs to do with the
470 * netcam context, and then send *us* as signal (netcam->exiting).
471 * Note that when we start our wait on netcam->exiting, our lock on
472 * netcam->mutex is automatically released, which will allow the
473 * handler to complete it's loop, notice that 'finish' is set and exit.
474 * This should always work, but again (defensive programming) we
475 * use pthread_cond_timedwait and, if our timeout (8 seconds) expires
476 * we just do the cleanup the handler would normally have done. This
477 * assures that (even if there is a bug in our code) motion will still
478 * be able to exit.
479 * If the init_retry_flag is not set the netcam_cleanup code was
480 * called while retrying the initial connection to a netcam and then
481 * there is no camera-handler started yet and thread_running must
482 * not be decremented.
483 */
484 waittime.tv_sec = time(NULL) + 8; /* Seems that 3 is too small */
485 waittime.tv_nsec = 0;
486
487 if (!init_retry_flag &&
488 pthread_cond_timedwait(&netcam->exiting, &netcam->mutex, &waittime) != 0) {
489 /*
490 * Although this shouldn't happen, if it *does* happen we will
491 * log it (just for the programmer's information).
492 */
493 MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO
494 ,_("No response from camera handler - it must have already died"));
495 pthread_mutex_lock(&global_lock);
496 threads_running--;
497 pthread_mutex_unlock(&global_lock);
498 }
499
500 /* We don't need any lock anymore, so release it. */
501 pthread_mutex_unlock(&netcam->mutex);
502
503 /* and cleanup the rest of the netcam_context structure. */
504 free(netcam->connect_host);
505 free(netcam->connect_request);
506 free(netcam->boundary);
507
508
509 if (netcam->latest != NULL) {
510 free(netcam->latest->ptr);
511 free(netcam->latest);
512 }
513
514 if (netcam->receiving != NULL) {
515 free(netcam->receiving->ptr);
516 free(netcam->receiving);
517 }
518
519 if (netcam->jpegbuf != NULL) {
520 free(netcam->jpegbuf->ptr);
521 free(netcam->jpegbuf);
522 }
523
524 if (netcam->ftp != NULL) {
525 ftp_free_context(netcam->ftp);
526 netcam->ftp = NULL;
527 } else {
528 netcam_disconnect(netcam);
529 }
530
531 free(netcam->response);
532
533 pthread_mutex_destroy(&netcam->mutex);
534 pthread_cond_destroy(&netcam->cap_cond);
535 pthread_cond_destroy(&netcam->pic_ready);
536 pthread_cond_destroy(&netcam->exiting);
537 free(netcam);
538 }
539
540 /**
541 * netcam_next
542 *
543 * This routine is called when the main 'motion' thread wants a new
544 * frame of video. It fetches the most recent frame available from
545 * the netcam, converts it to YUV420P, and returns it to motion.
546 *
547 * Parameters:
548 * cnt Pointer to the context for this thread
549 * image Pointer to a buffer for the returned image
550 *
551 * Returns: Error code
552 */
netcam_next(struct context * cnt,struct image_data * img_data)553 int netcam_next(struct context *cnt, struct image_data *img_data){
554
555 netcam_context_ptr netcam;
556
557 /*
558 * Here we have some more "defensive programming". This check should
559 * never be true, but if it is just return with a "fatal error".
560 */
561 if ((!cnt) || (!cnt->netcam))
562 return NETCAM_FATAL_ERROR;
563
564 netcam = cnt->netcam;
565
566 if (!netcam->latest->used) {
567 MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO,_("called with no data in buffer"));
568 return NETCAM_NOTHING_NEW_ERROR;
569 }
570
571 /*
572 * If we are controlling a non-streaming camera, we synchronize the
573 * motion main-loop with the camera-handling thread through a signal,
574 * together with a flag to say "start your next capture".
575 */
576 if (netcam->caps.streaming == NCS_UNSUPPORTED) {
577 pthread_mutex_lock(&netcam->mutex);
578 netcam->start_capture = 1;
579 pthread_cond_signal(&netcam->cap_cond);
580 pthread_mutex_unlock(&netcam->mutex);
581 }
582
583
584 /*
585 * If an error occurs in the JPEG decompression which follows this,
586 * jpeglib will return to the code within this 'if'. Basically, our
587 * approach is to just return a NULL (failed) to the caller (an
588 * error message has already been produced by the libjpeg routines).
589 */
590 if (setjmp(netcam->setjmp_buffer))
591 return NETCAM_GENERAL_ERROR | NETCAM_JPEG_CONV_ERROR;
592
593 /* If there was no error, process the latest image buffer. */
594 return netcam_proc_jpeg(netcam, img_data);
595 }
596
597 /**
598 * netcam_start
599 *
600 * This routine is called from the main motion thread. It's job is
601 * to open up the requested camera device and do any required
602 * initialization. If the camera is a streaming type, then this
603 * routine must also start up the camera-handling thread to take
604 * care of it.
605 *
606 * Parameters:
607 *
608 * cnt Pointer to the motion context structure for this device.
609 *
610 * Returns: 0 on success
611 * -1 on any failure
612 * -2 image dimensions are not modulo 8
613 */
netcam_start(struct context * cnt)614 int netcam_start(struct context *cnt){
615
616 netcam_context_ptr netcam; /* Local pointer to our context. */
617 pthread_attr_t handler_attribute; /* Attributes of our handler thread. */
618 int retval; /* Working var. */
619 struct url_t url; /* For parsing netcam URL. */
620 char err_service[6];
621
622 memset(&url, 0, sizeof(url));
623
624 cnt->netcam = mymalloc(sizeof(struct netcam_context));
625 netcam = cnt->netcam; /* Just for clarity in remaining code. */
626 netcam->cnt = cnt; /* Fill in the "parent" info. */
627
628 /* Our image buffers */
629 netcam->receiving = mymalloc(sizeof(netcam_buff));
630 netcam->receiving->ptr = mymalloc(NETCAM_BUFFSIZE);
631
632 netcam->latest = mymalloc(sizeof(netcam_buff));
633 netcam->latest->ptr = mymalloc(NETCAM_BUFFSIZE);
634
635 netcam->jpegbuf = mymalloc(sizeof(netcam_buff));
636 netcam->jpegbuf->ptr = mymalloc(NETCAM_BUFFSIZE);
637
638 /* Thread control structures */
639 pthread_mutex_init(&netcam->mutex, NULL);
640 pthread_cond_init(&netcam->cap_cond, NULL);
641 pthread_cond_init(&netcam->pic_ready, NULL);
642 pthread_cond_init(&netcam->exiting, NULL);
643
644 /* Initialize the average frame time to the user's value. */
645 netcam->av_frame_time = 1000000.0 / cnt->conf.framerate;
646
647 MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
648 ,_("Network Camera starting for camera (%s)"), cnt->conf.camera_name);
649
650 /* If a proxy has been specified, parse that URL. */
651 if (cnt->conf.netcam_proxy) {
652 netcam_url_parse(&url, cnt->conf.netcam_proxy);
653
654 if (!url.host) {
655 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
656 ,_("Invalid netcam_proxy (%s)"), cnt->conf.netcam_proxy);
657 netcam_url_free(&url);
658 return -1;
659 }
660
661 if (url.userpass) {
662 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
663 ,_("Username/password not allowed on a proxy URL"));
664 netcam_url_free(&url);
665 return -1;
666 }
667
668 /*
669 * A 'proxy' means that our eventual 'connect' to our
670 * camera must be sent to the proxy, and that our 'GET' must
671 * include the full path to the camera host.
672 */
673 netcam->connect_host = url.host;
674 url.host = NULL;
675 netcam->connect_port = url.port;
676 netcam_url_free(&url); /* Finished with proxy */
677 }
678
679 /* Parse the URL from the configuration data */
680 netcam_url_parse(&url, cnt->conf.netcam_url);
681
682 if (!url.service) {
683 snprintf(err_service,5,"%s",cnt->conf.netcam_url);
684 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
685 ,_("Invalid netcam service '%s' "), err_service);
686 netcam_url_free(&url);
687 return -1;
688 }
689
690 if ((!url.host) && (strcmp(url.service, "jpeg"))) {
691 MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO
692 ,_("Invalid netcam_url for camera (%s)"), cnt->conf.camera_name);
693 netcam_url_free(&url);
694 return -1;
695 }
696
697 if (cnt->conf.netcam_proxy == NULL) {
698 netcam->connect_host = url.host;
699 url.host = NULL;
700 netcam->connect_port = url.port;
701 }
702
703 /* Get HTTP Mode (1.0 default, 1.0 Keep-Alive, 1.1) flag from config
704 * and report its stata for debug reasons.
705 * The flags in the conf structure is read only and cannot be
706 * unset if the Keep-Alive needs to be switched off (ie. netcam does
707 * not turn out to support it. That is handled by unsetting the flags
708 * in the context structures (cnt->...) only.
709 */
710
711 if (!strcmp(cnt->conf.netcam_keepalive, "force")) {
712 netcam->connect_http_10 = TRUE;
713 netcam->connect_http_11 = FALSE;
714 netcam->connect_keepalive = TRUE;
715 } else if (!strcmp(cnt->conf.netcam_keepalive, "off")) {
716 netcam->connect_http_10 = TRUE;
717 netcam->connect_http_11 = FALSE;
718 netcam->connect_keepalive = FALSE;
719 } else if (!strcmp(cnt->conf.netcam_keepalive, "on")) {
720 netcam->connect_http_10 = FALSE;
721 netcam->connect_http_11 = TRUE;
722 netcam->connect_keepalive = TRUE; /* HTTP 1.1 has keepalive by default. */
723 }
724
725 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO
726 ,_("Netcam_http parameter '%s' converts to flags: HTTP/1.0: %s HTTP/1.1: %s Keep-Alive %s.")
727 ,cnt->conf.netcam_keepalive
728 ,netcam->connect_http_10 ? "1":"0", netcam->connect_http_11 ? "1":"0"
729 ,netcam->connect_keepalive ? "ON":"OFF");
730
731
732 /* Initialise the netcam socket to -1 to trigger a connection by the keep-alive logic. */
733 netcam->sock = -1;
734
735 if ((url.service) && (!strcmp(url.service, "http"))) {
736 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO,_("now calling netcam_setup_html()"));
737 retval = netcam_setup_html(netcam, &url);
738 } else if ((url.service) && (!strcmp(url.service, "ftp"))) {
739 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO,_("now calling netcam_setup_ftp"));
740 retval = netcam_setup_ftp(netcam, &url);
741 } else if ((url.service) && (!strcmp(url.service, "jpeg"))) {
742 MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO,_("now calling netcam_setup_file()"));
743 retval = netcam_setup_file(netcam, &url);
744 } else if ((url.service) && (!strcmp(url.service, "mjpg"))) {
745 retval = netcam_setup_mjpg(netcam, &url);
746 } else {
747 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
748 ,_("Invalid netcam service '%s' - must be http, ftp, mjpg, mjpeg, v4l2 or jpeg.")
749 , url.service);
750 retval = -1;
751 }
752
753 netcam_url_free(&url);
754 if (retval < 0) return -1;
755
756 /*
757 * We expect that, at this point, we should be positioned to read
758 * he first image available from the camera (directly after the
759 * applicable header). We want to decode the image in order to get
760 * the dimensions (width and height). If successful, we will use
761 * these to set the required image buffer(s) in our netcam_struct.
762 */
763 if ((retval = netcam->get_image(netcam)) != 0) {
764 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
765 ,_("Failed trying to read first image - retval:%d"), retval);
766 return -1;
767 }
768
769 /*
770 * If an error occurs in the JPEG decompression which follows this,
771 * jpeglib will return to the code within this 'if'. If such an error
772 * occurs during startup, we will just abandon this attempt.
773 */
774 if (setjmp(netcam->setjmp_buffer)) {
775 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
776 ,_("libjpeg decompression failure on first frame - giving up!"));
777 return -1;
778 }
779
780 netcam->netcam_tolerant_check = cnt->conf.netcam_tolerant_check;
781 netcam->JFIF_marker = 0;
782 netcam_get_dimensions(netcam);
783
784 /* Validate image sizes are multiple of 8 */
785 if ((netcam->width % 8) || (netcam->height % 8) ) {
786 MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO
787 ,_("Width/height(%dx%d) must be multiples of 8")
788 ,netcam->width, netcam->height);
789 return -2;
790 }
791
792 /* Fill in camera details into context structure. */
793 cnt->imgs.width = netcam->width;
794 cnt->imgs.height = netcam->height;
795 cnt->imgs.size_norm = (netcam->width * netcam->height * 3) / 2;
796 cnt->imgs.motionsize = netcam->width * netcam->height;
797
798 cnt->imgs.width_high = 0;
799 cnt->imgs.height_high = 0;
800 cnt->imgs.size_high = 0;
801
802 pthread_attr_init(&handler_attribute);
803 pthread_attr_setdetachstate(&handler_attribute, PTHREAD_CREATE_DETACHED);
804 pthread_mutex_lock(&global_lock);
805 netcam->threadnr = ++threads_running;
806 pthread_mutex_unlock(&global_lock);
807
808 retval = pthread_create(&netcam->thread_id, &handler_attribute,&netcam_handler_loop, netcam);
809 if (retval < 0) {
810 MOTION_LOG(ALR, TYPE_NETCAM, SHOW_ERRNO
811 ,_("Error starting camera handler thread [%d]"), netcam->threadnr);
812 return -1;
813 }
814
815 return 0;
816 }
817
818