/* vcb should be locked before calling video_record_stop() */ void video_record_stop(VideoCircularBuffer *vcb) { Event *event = NULL; char *cmd; if (!vcb->file) return; fclose(vcb->file); vcb->file = NULL; log_printf("Video %s record stopped.\n", (vcb->state & VCB_STATE_MOTION_RECORD) ? "motion" : "manual"); if (pikrellcam.video_mp4box) { asprintf(&cmd, "(MP4Box %s -fps %d -add %s %s %s ; rm %s)", pikrellcam.verbose ? "" : "-quiet", pikrellcam.camera_adjust.mp4_box_fps, pikrellcam.video_h264, pikrellcam.video_pathname, pikrellcam.verbose ? "" : "2> /dev/null", pikrellcam.video_h264); if ( (vcb->state & VCB_STATE_MOTION_RECORD) && *pikrellcam.on_motion_end_cmd ) event = exec_child_event("motion end command", cmd, NULL); else exec_no_wait(cmd, NULL); free(cmd); } dup_string(&pikrellcam.video_last_save, pikrellcam.video_pathname); pikrellcam.video_notify = TRUE; event_count_down_add("video saved notify", pikrellcam.notify_duration * EVENT_LOOP_FREQUENCY, event_notify_expire, &pikrellcam.video_notify); if (vcb->state & VCB_STATE_MOTION_RECORD) { if (!strcmp(pikrellcam.motion_preview_save_mode, "best")) event_add("preview save command", pikrellcam.t_now, 0, event_preview_save_cmd, pikrellcam.on_motion_preview_save_cmd); if (event) /* a mp4 video save event needs a MP4Box child exit */ { event->data = pikrellcam.on_motion_end_cmd; event->func = event_motion_end_cmd; } else if (*pikrellcam.on_motion_end_cmd) /* a h264 video save */ event_add("motion end command", pikrellcam.t_now, 0, event_motion_end_cmd, pikrellcam.on_motion_end_cmd); } event_add("preview dispose", pikrellcam.t_now, 0, event_preview_dispose, NULL); vcb->state = VCB_STATE_NONE; vcb->pause = FALSE; }
void multicast_recv(void) { socklen_t addrlen; int msg_id, n, nbytes = 0; char *line, *eol, *s, recv_buf[1025]; char from[128], to[256], message[256]; char msg_type[32], action[256]; boolean repeat, match; if (fd_recv < 0 || !pikrellcam.multicast_enable) return; ioctl(fd_recv, FIONREAD, &nbytes); if (nbytes <= 0) return; addrlen = sizeof(addr_recv); if ((nbytes = recvfrom(fd_recv, recv_buf, sizeof(recv_buf) - 1, 0, (struct sockaddr *) &addr_recv, &addrlen)) <= 0) return; if (recv_buf[nbytes - 1] != '\n') recv_buf[nbytes++] = '\n'; recv_buf[nbytes] = '\0'; line = recv_buf; while (*line) { eol = strchr(line, '\n'); if (!eol) break; *eol++ = '\0'; msg_id = 0; repeat = FALSE; from[0] = to[0] = message[0] = '\0'; n = sscanf(line, "%127s %255s %255[^\n]", from, to, message); if (pikrellcam.verbose_multicast) printf("multicast recv: <%s> <%s> <%s>\n", from, to, message); if ((s = strchr(from, ':')) != NULL) { *s++ = '\0'; msg_id = atoi(s); repeat = multicast_message_id_repeat(from, msg_id); } if (n == 3) { if ((match = hostname_match(from, to)) && !repeat) { if (pikrellcam.verbose_multicast) printf(" message accepted for %s\n", pikrellcam.hostname); if (sscanf(message, "%31s %255[^\n]", msg_type, action) == 2) { dup_string(&pikrellcam.multicast_from_hostname, from); if (!strcmp(msg_type, "command")) exec_no_wait(action, NULL); else if (!strcmp(msg_type, "pkc-message")) /* user defined */ exec_no_wait(pikrellcam.on_multicast_message_cmd, action); /* ack and other message types are ignored */ } else if (pikrellcam.verbose_multicast) printf(" msg_type action parse fail.\n"); } else if (pikrellcam.verbose_multicast) printf(" message rejected: %s\n", !match ? "hostname match failed" : "repeated message"); if (match && msg_id > 0) multicast_ack(from, msg_id); } line = eol; } }
void event_process(void) { Event *event; AtCommand *at; SList *list, *prev_link = NULL, *next_link, *expired_link, tmp_link; int minute_tick, five_minute_tick, ten_minute_tick, fifteen_minute_tick, thirty_minute_tick, hour_tick, day_tick; struct tm *tm_now; static struct tm tm_prev; static time_t t_prev; time(&pikrellcam.t_now); pikrellcam.second_tick = (pikrellcam.t_now == t_prev) ? FALSE : TRUE; t_prev = pikrellcam.t_now; if (pikrellcam.second_tick) { tm_prev = pikrellcam.tm_local; tm_now = localtime(&pikrellcam.t_now); minute_tick = (tm_now->tm_min != tm_prev.tm_min) ? TRUE : FALSE; pikrellcam.tm_local = *tm_now; } else minute_tick = FALSE; if (pikrellcam.state_modified || minute_tick) { pikrellcam.state_modified = FALSE; state_file_write(); } tmp_link.next = NULL; for (list = event_list; list; prev_link = list, list = list->next) { /* Event loop processing is done with the list unlocked until we | have an expired link that must be removed from the list so that | called event functions cad add events to the list. | If another thread adds an event, only the last list->next | will be modified and this loop will catch it or it won't. */ event = (Event *) list->data; if ( (event->time == 0 && event->count == 0) || event->func == NULL ) /* not activated */ continue; if (event->time > 0 && event->time > pikrellcam.t_now) continue; else if (event->count > 0 && --event->count > 0) continue; if (pikrellcam.verbose) printf("Event func -> [%s] period=%d\n", event->name, (int) event->period); if (event->func) (*event->func)(event->data); if (event->period > 0) event->time += event->period; else { pthread_mutex_lock(&event_mutex); expired_link = list; if (list == event_list) { event_list = list->next; tmp_link.next = list->next; list = &tmp_link; } else { next_link = list->next; list = prev_link; list->next = next_link; } free(expired_link->data); free(expired_link); pthread_mutex_unlock(&event_mutex); } } if (minute_tick) { char *p, buf[IBUF_LEN]; int i, n, minute_now, minute_at, minute_offset = 0; static int start = TRUE; static int at_notify_fd, at_notify_wd; struct inotify_event *event; if (at_notify_fd == 0) { at_notify_fd = inotify_init(); if (at_notify_fd > 0) { fcntl(at_notify_fd, F_SETFL, fcntl(at_notify_fd, F_GETFL) | O_NONBLOCK); at_notify_wd = inotify_add_watch(at_notify_fd, pikrellcam.config_dir, IN_CREATE | IN_MODIFY); } } else if (at_notify_wd > 0) { n = read(at_notify_fd, buf, IBUF_LEN); if (n > 0) { for (i = 0; i < n; i += sizeof(*event) + event->len) { event = (struct inotify_event *) &buf[i]; if ( event->len > 0 && !strcmp(event->name, PIKRELLCAM_AT_COMMANDS_CONFIG) ) at_commands_config_load(pikrellcam.at_commands_config_file); } } } tm_now = &pikrellcam.tm_local; minute_now = tm_now->tm_hour * 60 + tm_now->tm_min; five_minute_tick = ((tm_now->tm_min % 5) == 0) ? TRUE : FALSE; ten_minute_tick = ((tm_now->tm_min % 10) == 0) ? TRUE : FALSE; fifteen_minute_tick = ((tm_now->tm_min % 15) == 0) ? TRUE : FALSE; thirty_minute_tick = ((tm_now->tm_min % 10) == 0) ? TRUE : FALSE; hour_tick = (tm_now->tm_hour != tm_prev.tm_hour) ? TRUE : FALSE; day_tick = (tm_now->tm_mday != tm_prev.tm_mday) ? TRUE : FALSE; if (day_tick || !sun.initialized) { if (sun.initialized) { char tbuf[32]; log_printf_no_timestamp("\n========================================================\n"); strftime(tbuf, sizeof(tbuf), "%F", localtime(&pikrellcam.t_now)); log_printf_no_timestamp("%s ================== New Day ==================\n", tbuf); log_printf_no_timestamp("========================================================\n"); strftime(tbuf, sizeof(tbuf), "%F", localtime(&pikrellcam.t_now)); } sun_times_init(); sun.initialized = TRUE; log_lines(); state_file_write(); } for (list = at_command_list; list; list = list->next) { at = (AtCommand *) list->data; if ((p = strchr(at->at_time, '+')) != NULL) minute_offset = atoi(p + 1); else if ((p = strchr(at->at_time, '-')) != NULL) minute_offset = -atoi(p + 1); if (!strcmp(at->at_time, "start")) minute_at = start ? minute_now : 0; else if (!strcmp(at->at_time, "minute")) minute_at = minute_now; else if (!strcmp(at->at_time, "5minute")) minute_at = five_minute_tick ? minute_now : 0; else if (!strcmp(at->at_time, "10minute")) minute_at = ten_minute_tick ? minute_now : 0; else if (!strcmp(at->at_time, "15minute")) minute_at = fifteen_minute_tick ? minute_now : 0; else if (!strcmp(at->at_time, "30minute")) minute_at = thirty_minute_tick ? minute_now : 0; else if (!strcmp(at->at_time, "hour")) minute_at = hour_tick ? minute_now : 0; else if (!strncmp(at->at_time, "dawn", 4)) minute_at = sun.dawn + minute_offset; else if (!strncmp(at->at_time, "dusk", 4)) minute_at = sun.dusk + minute_offset; else if (!strncmp(at->at_time, "sunrise", 7)) minute_at = sun.sunrise + minute_offset; else if (!strncmp(at->at_time, "sunset", 6)) minute_at = sun.sunset + minute_offset; else if (!strncmp(at->at_time, "nautical_dawn", 13)) minute_at = sun.nautical_dawn + minute_offset; else if (!strncmp(at->at_time, "nautical_dusk", 13)) minute_at = sun.nautical_dusk + minute_offset; else { minute_at = (int) strtol(at->at_time, &p, 10) * 60; if (*p == ':') minute_at += strtol(p + 1, NULL, 10); else { minute_at = 0; /* error in at_time string */ log_printf("Error in at_command: [%s] bad at_time: [%s]\n", at->command, at->at_time); } } if (minute_now != minute_at) continue; /* Have a time match so check frequency. */ if ( !strcmp(at->frequency, "daily") || ( !strcmp(at->frequency, "Sat-Sun") && (tm_now->tm_wday == 0 || tm_now->tm_wday == 6) ) || ( !strcmp(at->frequency, "Mon-Fri") && tm_now->tm_wday > 0 && tm_now->tm_wday < 6 ) || ( (p = strstr(weekdays, at->frequency)) != NULL && (p - weekdays) / 3 == tm_now->tm_wday ) ) { if (*(at->command) == '@') command_process(at->command + 1); else exec_no_wait(at->command, NULL); } } start = FALSE; if (pikrellcam.config_modified) config_save(pikrellcam.config_file); } }
/* Do something with a motion video, eg scp to archive, etc. */ void event_motion_end_cmd(char *cmd) { log_printf("event_motion_end_cmd(); running %s\n", cmd); exec_no_wait(cmd, NULL); }
void command_process(char *command_line) { VideoCircularBuffer *vcb = &video_circular_buffer; Command *cmd; char command[64], args[128], buf[32], *path; int i, n; if (!command_line || *command_line == '\0') return; n = sscanf(command_line, "%63s %[^\n]", command, args); if (n < 1 || command[0] == '#') return; for (cmd = NULL, i = 0; i < COMMAND_SIZE; cmd = NULL, ++i) { cmd = &commands[i]; if (!strcmp(command, cmd->name)) { if (cmd->n_args != n - 1) { log_printf("Wrong number of args for command: %s\n", command); return; } break; } } if (!cmd) { if ( !config_set_option(command, args, FALSE) && !mmalcam_config_parameter_set(command, args, TRUE) ) log_printf("Bad command: [%s] [%s]\n", command, args); return; } if (cmd->code != display_cmd) log_printf("command_process: %s\n", command_line); if (cmd->code < display_cmd && !display_is_default()) { display_set_default(); return; } switch (cmd->code) { case record: pthread_mutex_lock(&vcb->mutex); if (config_boolean_value(args) == TRUE) { if (vcb->pause) vcb->pause = FALSE; else { if (vcb->state == VCB_STATE_MOTION_RECORD) video_record_stop(vcb); video_record_start(vcb, VCB_STATE_MANUAL_RECORD_START); } } else video_record_stop(vcb); pthread_mutex_unlock(&vcb->mutex); break; case record_pause: /* Can pause manual record only. Because of event_gap/capture | times, I'm not even sure what it would mean to pause a | motion record. */ pthread_mutex_lock(&vcb->mutex); if (vcb->state == VCB_STATE_MANUAL_RECORD) vcb->pause = vcb->pause ? FALSE : TRUE; else vcb->pause = FALSE; pthread_mutex_unlock(&vcb->mutex); break; case still: snprintf(buf, sizeof(buf), "%d", pikrellcam.still_sequence); path = media_pathname(pikrellcam.still_dir, pikrellcam.still_filename, 'N', buf, '\0', NULL); pikrellcam.still_sequence += 1; still_capture(path); free(path); break; case tl_start: if ((n = atoi(args)) < 1) n = 0; time_lapse.activated = TRUE; time_lapse.on_hold = FALSE; if (!time_lapse.event && n > 0) { time_lapse.sequence = 0; ++time_lapse.series; time_lapse.event = event_add("timelapse", pikrellcam.t_now, n, timelapse_capture, NULL); } else if (n > 0) /* Change the period */ { time_lapse.event->time += (n - time_lapse.period); time_lapse.event->period = n; } if (n > 0) time_lapse.period = n; /* n == 0 just sets on_hold FALSE */ config_timelapse_save_status(); break; case tl_hold: config_set_boolean(&time_lapse.on_hold, args); config_timelapse_save_status(); break; case tl_end: if (time_lapse.activated) { event_remove(time_lapse.event); time_lapse.event = NULL; time_lapse.activated = FALSE; time_lapse.on_hold = FALSE; config_timelapse_save_status(); exec_no_wait(pikrellcam.on_timelapse_end_cmd, NULL); } break; case tl_inform_convert: if (!strcmp(args, "done")) { event_remove(time_lapse.inform_event); dup_string(&time_lapse.convert_name, ""); time_lapse.convert_size = 0; } else { dup_string(&time_lapse.convert_name, args); time_lapse.inform_event = event_add("tl_inform_convert", pikrellcam.t_now, 5, timelapse_inform_convert, NULL); } break; case tl_show_status: config_set_boolean(&time_lapse.show_status, args); break; case display_cmd: display_command(args); break; case motion_cmd: motion_command(args); break; case motion_enable: n = motion_frame.motion_enable; config_set_boolean(&motion_frame.motion_enable, args); if (n && !motion_frame.motion_enable) { pthread_mutex_lock(&vcb->mutex); if (vcb->state == VCB_STATE_MOTION_RECORD) video_record_stop(vcb); pthread_mutex_unlock(&vcb->mutex); } break; case save_config: config_save(pikrellcam.config_file); break; case quit: config_timelapse_save_status(); if (pikrellcam.config_modified) config_save(pikrellcam.config_file); display_quit(); exit(0); break; default: log_printf("command in table with no action!\n"); break; } }