motion_detects_fifo for reading all motion detects.

V 4.2.0
~/pikrellcam/www/motion_detects_fifo can be read to get all motion
detects regardless of motion videos enabled state.

Moved Setup->Config->Times/* and Setup->Settings->Startup_Motion
to Setup->Config->Motion
This commit is contained in:
Bill Wilson 2019-05-30 14:36:19 -05:00
parent 0172d45cce
commit 1e970587b8
17 changed files with 803 additions and 184 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
scripts
www/.htpasswd
www/FIFO
www/motion_events_FIFO
www/audio_FIFO
www/media
www/config-user*

Binary file not shown.

View File

@ -0,0 +1,100 @@
#!/usr/bin/env python
# tab-width: 4
# This is an example of an external script that will read all motion detects
# from PiKrellCam whether or not motion videos are enabled.
#
# Use it a starting point for a motion detect front end application that is
# independent of motion video enabled state.
#
# Run this script from a terminal on a Pi running PiKrellCam and watch for
# motion detects to be printed.
#
# This script can be terminated with a terminal ^C or by sending an "off"
# command to PiKrellCam:
# echo "motion_detects_fifo_enable off" > /home/pi/pikrellcam/www/FIFO
#
import os
import sys
import signal
import fcntl
import atexit
import select
import StringIO
import time
# Turn on pikrellcam motion detects writing to the motion_detects_FIFO
#
os.system('echo "motion_detects_fifo_enable on" > /home/pi/pikrellcam/www/FIFO')
# If ^C interrupted, send "off" to the command FIFO
# If external program sends "off", then this script will get an <off> tag
# read from the motion_detects_FIFO and exit.
#
def signal_handler(signum, frame):
print ' Turning off motion_detects_fifo_enable'
os.system('echo "motion_detects_fifo_enable off" > /home/pi/pikrellcam/www/FIFO')
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
motion_detects_FIFO = '/home/pi/pikrellcam/www/motion_detects_FIFO'
detects_fifo = os.open(motion_detects_FIFO, os.O_RDONLY | os.O_NONBLOCK)
bufferSize = 1024
state = 'wait'
while True:
# Wait until there is data to read from the fifo. May be multiple lines.
#
select.select([detects_fifo],[],[detects_fifo])
data = os.read(detects_fifo, bufferSize)
# Split the data into lines and process motion type lines between
# <motion>...</motion> tags
# If a motion detect has external or audio, there may not be a frame vector.
# A motion detect with actual motion always has an overall 'f' frame vector.
# If there is an 'f' frame vector, there can also be one or more region
# vectors only, or a burst vector only, or both burst and one or more
# region vectors. If you care only about overall frame motion, look
# for 'f' frame vectors.
#
lines = StringIO.StringIO(data)
for line in lines.readlines():
line = line.strip('\n')
print "FIFO read: " + line
# If some program sends a "motion_detects_fifo_enable off" to the
# command FIFO, an <off> tag will be written to the motion_detects_FIFO.
#
if (line.find('<off>') == 0):
print " detected motion_detects_fifo_enable off - exiting."
sys.exit(1)
elif (line.find('<motion') == 0):
state = 'motion'
# <motion t > where t is system time with .1 second precision
m, t, b = line.split()
print " motion detected - system time " + t + " => " + time.asctime( time.localtime( int(float(t)) ) )
elif (line.find('</motion>') == 0):
print ""
state = 'wait'
elif (state == 'motion'):
if (line[0] == "f"):
r, x, y, dx, dy, mag, count = line.split()
print " motion vector - frame:" + " mag=" + mag + " count=" + count
elif (line[0].isdigit()):
r, x, y, dx, dy, mag, count = line.split()
print " motion vector - region " + line[0] + ": mag=" + mag + " count=" + count
elif (line[0] == "b"):
b, count = line.split()
print " burst detect - count=" + count
elif (line[0] == "e"):
e, code = line.split()
print " external trigger - code: " + code
elif (line[0] == "a"):
a, level = line.split()
print " audio trigger - level=" + level

View File

@ -1,6 +1,6 @@
/* PiKrellCam
|
| Copyright (C) 2015-2017 Bill Wilson billw@gkrellm.net
| Copyright (C) 2015-2019 Bill Wilson billw@gkrellm.net
|
| PiKrellCam is free software: you can redistribute it and/or modify it
| under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/* PiKrellCam
|
| Copyright (C) 2015-2017 Bill Wilson billw@gkrellm.net
| Copyright (C) 2015-2019 Bill Wilson billw@gkrellm.net
|
| PiKrellCam is free software: you can redistribute it and/or modify it
| under the terms of the GNU General Public License as published by
@ -303,6 +303,7 @@ rotation_control_set(char *option, char *setting)
MMAL_STATUS_T status = MMAL_EINVAL;
value = ((value % 360 ) / 90) * 90;
pikrellcam.rotation_value = value;
for (i = 0; i < MAX_CAMERA_PORTS; ++i)
{
status = mmal_port_parameter_set_int32(camera.component->output[i],
@ -604,6 +605,18 @@ static Config config[] =
"#",
"loop_diskusage_percent", "30", FALSE, {.value = &pikrellcam.loop_diskusage_percent}, config_value_int_set},
#ifdef MOTION_STILLS
{ "\n# -------------------- Motion Still Recording -----------------------\n"
"# Take still images instead of recording videos when motion is enabled.\n"
"#",
"motion_stills_enable", "off", FALSE, {.value = &pikrellcam.motion_stills_enable}, config_value_bool_set},
{ "# Motion still images per minute range from 1 to 60 which gives a\n"
"# max rate range of 1 per minute to 1 per second.\n"
"# Stills are taken at motion detects separated by at least this rate.\n"
"#",
"motion_stills_per_minute", "30", TRUE, {.value = &pikrellcam.motion_stills_per_minute}, config_value_int_set },
#endif
{ "\n# -------------------- Motion Detect Options -----------------------\n"
"# PiKrellCam V3.0 stores some motion detect settings in preset-xxx.conf\n"
@ -657,7 +670,7 @@ static Config config[] =
"motion_pre_capture", "5", TRUE, {.value = &pikrellcam.motion_times.pre_capture}, config_value_int_set },
{ "# Seconds of video that will be recorded after the last motion event.\n"
"# motion_post_caputure must be <= motion_event_gap.\n"
"# motion_post_capture must be <= motion_event_gap.\n"
"#",
"motion_post_capture", "5", TRUE, {.value = &pikrellcam.motion_times.post_capture}, config_value_int_set },
@ -745,6 +758,12 @@ static Config config[] =
"#",
"motion_stats", "off", FALSE, {.value = &pikrellcam.motion_stats}, config_value_bool_set },
{ "# Enable writing all motion detects to ~/pikrellcam/www/motion_detects_fifo\n"
"# Motion detects are written regardless of motion videos enabled state,\n"
"# so this provides a front end motion detect function for another app.\n"
"#",
"motion_detects_fifo_enable", "off", FALSE, {.value = &pikrellcam.motion_detects_fifo_enable}, config_value_bool_set },
{ "# Command/script to run when receiving a user defined multicast\n"
"# pkc-message sent by other PiKrellCams or separate scripts on your LAN.\n"
"# Use this to run a script needing PiKrellCam variables passed to it,\n"
@ -1203,6 +1222,8 @@ config_set_defaults(char *home_dir)
pikrellcam.version = strdup(PIKRELLCAM_VERSION);
pikrellcam.timelapse_format = strdup("tl_$n_$N.jpg");
pikrellcam.motion_stills_name_format = strdup("motion_%F_%H.%M.%S_$N.jpg");
pikrellcam.preview_pathname = strdup("");
pikrellcam.thumb_name = strdup("");
pikrellcam.multicast_group_IP = "225.0.0.55";
@ -1290,6 +1311,11 @@ config_load(char *config_file)
)
pikrellcam.motion_record_time_limit = 10;
if (pikrellcam.motion_stills_per_minute > 60)
pikrellcam.motion_stills_per_minute = 60;
else if (pikrellcam.motion_stills_per_minute < 1)
pikrellcam.motion_stills_per_minute = 1;
if (pikrellcam.diskfree_percent < 5)
pikrellcam.diskfree_percent = 5;
if (pikrellcam.loop_diskusage_percent < 5)
@ -1357,6 +1383,21 @@ config_load(char *config_file)
if (pikrellcam.config_sequence_new != pikrellcam.config_sequence)
{
#if 0
if ( pikellcam.config_sequence <= 45
&& ( pikrellcam.rotation_value == 90
|| pikrellcam.rotation_value == 270
)
)
{
n = pikrellcam.video_width;
pikrellcam.video_width = pikrellcam.video_height;
pikrellcam.video_height = n;
n = pikrellcam.still_width;
pikrellcam.still_width = pikrellcam.still_height;
pikrellcam.still_height = n;
}
#endif
pikrellcam.config_sequence = pikrellcam.config_sequence_new;
pikrellcam.config_modified = TRUE;
}

View File

@ -1,6 +1,6 @@
/* PiKrellCam
|
| Copyright (C) 2015-2016 Bill Wilson billw@gkrellm.net
| Copyright (C) 2015-2019 Bill Wilson billw@gkrellm.net
|
| PiKrellCam is free software: you can redistribute it and/or modify it
| under the terms of the GNU General Public License as published by
@ -50,7 +50,7 @@ MotionTimes motion_times_temp;
#define IMAGE_EFFECT 16
#define MOTION_LIMIT 17
#define SETTINGS 18
#define MOTION_TIME 19
#define MOTION_SETTINGS 19
#define SERVO_SETTINGS 20
#define LOOP_SETTINGS 21
#define AUDIO_SETTINGS 22
@ -70,7 +70,7 @@ static DisplayCommand display_commands[] =
{ ">", RIGHT_ARROW },
{ ">>", REPEAT_RIGHT_ARROW },
{ "back", BACK },
{ "motion_time", MOTION_TIME },
{ "motion_settings", MOTION_SETTINGS },
{ "motion_limit", MOTION_LIMIT },
{ "video_presets", VIDEO_PRESET },
{ "still_presets", STILL_PRESET },
@ -607,7 +607,7 @@ motion_draw(uint8_t *i420)
MotionRegion *mreg;
CompositeVector *vec;
SList *mrlist;
char *msg, info[100], status[100];
char *msg, *fifo_msg, info[100], status[100];
int16_t color; /* just B&W */
int i, x, y, dx, dy, r, r_unit;
int t_record, t_hold;
@ -627,6 +627,7 @@ motion_draw(uint8_t *i420)
JUSTIFY_RIGHT(0), "Vectors: ON");
}
fifo_msg = "";
if (!inform_shown && (mf->show_preset || pikrellcam.preset_notify))
{
for (mrlist = mf->motion_region_list; mrlist; mrlist = mrlist->next)
@ -707,6 +708,9 @@ motion_draw(uint8_t *i420)
if (pikrellcam.on_preset && !pikrellcam.preset_notify)
{
if (pikrellcam.motion_detects_fifo_enable)
fifo_msg = "Motion FIFO ON ";
if (mf->frame_window > 0 && mf->motion_status == MOTION_NONE)
{
snprintf(status, sizeof(status), "confirming[%d]", mf->frame_window);
@ -735,16 +739,19 @@ motion_draw(uint8_t *i420)
if (pikrellcam.motion_show_counts)
{
snprintf(info, sizeof(info),
"any:%-3d(%.1f) rej:%-3d spkl:%-3d(%.1f) %s",
mf->any_count, mf->any_count_expma,
"%sany:%-3d(%.1f) rej:%-3d spkl:%-3d(%.1f) %s",
fifo_msg, mf->any_count, mf->any_count_expma,
mf->reject_count, mf->sparkle_count, mf->sparkle_expma,
status);
i420_print(&bottom_status_area, normal_font, 0xff, 0, 0, 0,
JUSTIFY_LEFT, info);
}
else
else
{
snprintf(info, sizeof(info), "%s%s", fifo_msg, status);
i420_print(&bottom_status_area, normal_font, 0xff, 0, 0, 0,
JUSTIFY_LEFT, status);
JUSTIFY_LEFT, info);
}
}
}
else
@ -779,7 +786,7 @@ motion_draw(uint8_t *i420)
else
{
t_hold = pikrellcam.motion_times.event_gap -
(pikrellcam.t_now - vcb->motion_last_detect_time);
(pikrellcam.t_now - pikrellcam.motion_last_detect_time);
snprintf(info, sizeof(info), "REC (%s) %d:%02d hold %d:%02d",
pikrellcam.external_motion ?
(mf->fifo_detects ? mf->fifo_trigger_code : "Audio")
@ -825,6 +832,10 @@ motion_draw(uint8_t *i420)
i420_print(&bottom_status_area, normal_font, 0xff, 1, 1, 0,
JUSTIFY_LEFT, msg);
if (pikrellcam.motion_detects_fifo_enable && !*fifo_msg)
i420_print(&bottom_status_area, normal_font, 0xff, 0, 1, 0,
JUSTIFY_LEFT, "Motion FIFO ON");
if (time_lapse.show_status)
{
int p = time_lapse.period,
@ -923,8 +934,8 @@ static int menu_white_balance_index;
static SList *menu_image_effect_list;
static int menu_image_effect_index;
static SList *menu_motion_time_list;
static int menu_motion_time_index;
static SList *menu_motion_settings_list;
static int menu_motion_settings_index;
static SList *menu_motion_limit_list;
static int menu_motion_limit_index;
@ -1092,17 +1103,24 @@ static Adjustment picture_adjustment[] =
/* Adjustment changes made to a temp struct to avoid thrashing malloc/free
| of huge circular buffer. Final change is applied if/when SEL is clicked.
*/
Adjustment motion_time_adjustment[] =
Adjustment motion_settings_adjustment[] =
{
{ "Startup_Motion", 0, 1, 1, 0, 0, 0, "", NULL, &pikrellcam.startup_motion_enable },
{ "Confirm_Gap", 0, 30, 1, 0, 0, 0, "", NULL, &motion_times_temp.confirm_gap },
{ "Pre_Capture", 1, 180, 1, 0, 0, 0, "", NULL, &motion_times_temp.pre_capture },
{ "Event_Gap", 1, 300, 1, 0, 0, 0, "", NULL, &motion_times_temp.event_gap },
{ "Post_Capture", 1, 180, 1, 0, 0, 0, "", NULL, &motion_times_temp.post_capture },
{ "Motion_Time_Limit", 0, 1800, 10, 0, 0, 0, "sec", NULL, &pikrellcam.motion_record_time_limit }
{ "Video_Time_Limit", 0, 1800, 10, 0, 0, 0, "sec", NULL, &pikrellcam.motion_record_time_limit }
// XXX
#ifdef MOTION_STILLS
{ "Motion_Stills_(no_videos)", 0, 1, 1, 0, 0, 0, "", NULL, &pikrellcam.motion_stills_enable },
{ "Max_Stills_per_Minute", 1, 60, 1, 0, 0, 0, "", NULL, &pikrellcam.motion_stills_per_minute}
#endif
};
#define N_MOTION_TIME_ADJUSTMENTS \
(sizeof(motion_time_adjustment) / sizeof(Adjustment))
#define N_MOTION_SETTINGS_ADJUSTMENTS \
(sizeof(motion_settings_adjustment) / sizeof(Adjustment))
Adjustment motion_limit_adjustment[] =
{
@ -1121,7 +1139,6 @@ Adjustment motion_limit_adjustment[] =
*/
Adjustment settings_adjustment[] =
{
{ "Startup_Motion", 0, 1, 1, 0, 0, 0, "", NULL, &pikrellcam.startup_motion_enable },
// { "Vertical_Filter", 0, 1, 1, 0, 0, 0, "", NULL, &pikrellcam.motion_vertical_filter },
{ "Check_Media_Diskfree", 0, 1, 1, 0, 0, 0, "", NULL, &pikrellcam.check_media_diskfree },
{ "Check_Archive_Diskfree", 0, 1, 1, 0, 0, 0, "", NULL, &pikrellcam.check_archive_diskfree },
@ -1241,7 +1258,7 @@ apply_adjustment(void)
if (!adjustments || !cur_adj)
return FALSE;
if (adjustments == &motion_time_adjustment[0])
if (adjustments == &motion_settings_adjustment[0])
{
pikrellcam.motion_times.confirm_gap = motion_times_temp.confirm_gap;
pikrellcam.motion_times.post_capture = motion_times_temp.post_capture;
@ -1549,10 +1566,14 @@ display_adjustment(uint8_t *i420)
if (boolean_flag)
snprintf(buf, sizeof(buf), "%s", cur_adj->value ? "ON" : "OFF");
else if ( adjustments == &motion_settings_adjustment[0]
&& cur_adj->value == 0
)
snprintf(buf, sizeof(buf), "OFF");
else
snprintf(buf, sizeof(buf), "%d", cur_adj->value);
i420_print(da, font, 0xff, 0, adj_x, 0, JUSTIFY_CENTER_AT_X, buf);
i420_print(da, font, 0xff, 0, adj_x, 0, JUSTIFY_CENTER_AT_X, buf);
i420_print(da, font, 0xff, 2, 0, 0, JUSTIFY_CENTER, cur_adj->name);
}
@ -1579,9 +1600,9 @@ display_draw_menu(uint8_t *i420)
adjustments = &motion_limit_adjustment[0];
cur_adj_start = TRUE;
break;
case MOTION_TIME:
case MOTION_SETTINGS:
display_state = DISPLAY_ADJUSTMENT;
adjustments = &motion_time_adjustment[0];
adjustments = &motion_settings_adjustment[0];
cur_adj_start = TRUE;
break;
case SETTINGS:
@ -1783,11 +1804,11 @@ display_command(char *cmd)
display_menu = DISPLAY_MENU_NONE;
}
break;
case MOTION_TIME:
display_menu_list = menu_motion_time_list;
display_menu_index = &menu_motion_time_index;
case MOTION_SETTINGS:
display_menu_list = menu_motion_settings_list;
display_menu_index = &menu_motion_settings_index;
display_state = DISPLAY_MENU;
display_menu = MOTION_TIME;
display_menu = MOTION_SETTINGS;
break;
case MOTION_LIMIT:
display_menu_list = menu_motion_limit_list;
@ -2117,17 +2138,17 @@ display_init(void)
bottom_status_area.x0, bottom_status_area.y0,
bottom_status_area.width, bottom_status_area.height);
}
if (!menu_motion_time_list)
if (!menu_motion_settings_list)
{
for (i = 0, position = 0; i < N_MOTION_TIME_ADJUSTMENTS; ++i)
for (i = 0, position = 0; i < N_MOTION_SETTINGS_ADJUSTMENTS; ++i)
{
adj = &motion_time_adjustment[i];
adj = &motion_settings_adjustment[i];
entry = calloc(1, sizeof(MenuEntry));
entry->name = adj->name;
entry->length = strlen(entry->name);
entry->line_position = position;
position += entry->length + 1;
menu_motion_time_list = slist_append(menu_motion_time_list, entry);
menu_motion_settings_list = slist_append(menu_motion_settings_list, entry);
}
}
if (!menu_motion_limit_list)

View File

@ -1,6 +1,6 @@
/* PiKrellCam
|
| Copyright (C) 2015-2017 Bill Wilson billw@gkrellm.net
| Copyright (C) 2015-2019 Bill Wilson billw@gkrellm.net
|
| PiKrellCam is free software: you can redistribute it and/or modify it
| under the terms of the GNU General Public License as published by
@ -123,6 +123,9 @@ expand_command(char *command, char *arg)
case 't':
fmt_arg = pikrellcam.thumb_dir;
break;
case 'r':
fmt_arg = pikrellcam.motion_record_mode;
break;
case 'T':
snprintf(buf, sizeof(buf), "%d", time_lapse.period);
@ -398,6 +401,20 @@ event_still_capture_cmd(char *cmd)
exec_wait(cmd, NULL);
}
void
event_motion_still_capture(void *t)
{
time_t motion_time = (time_t) t;
char *path, buf[50];
snprintf(buf, sizeof(buf), "%d", pikrellcam.motion_stills_sequence++);
path = media_pathname(pikrellcam.still_dir,
pikrellcam.motion_stills_name_format, motion_time,
'N', buf, 'H', pikrellcam.hostname);
still_capture(path, motion_time);
free(path);
}
static boolean
diskfree_is_low(char *dir)
{
@ -772,6 +789,12 @@ state_file_write(void)
f = fopen(fname_part, "w");
fprintf(f, "motion_enable %s\n", mf->motion_enable ? "on" : "off");
#ifdef MOTION_STILLS
fprintf(f, "motion_stills_enable %s\n",
pikrellcam.motion_stills_enable ? "on" : "off");
#endif
fprintf(f, "show_preset %s\n", mf->show_preset ? "on" : "off");
fprintf(f, "show_vectors %s\n", mf->show_vectors ? "on" : "off");
@ -814,6 +837,15 @@ state_file_write(void)
state = "stop";
fprintf(f, "video_record_state %s\n", state);
if (vcb->state & VCB_STATE_MOTION_RECORD)
state = "video";
else if (pikrellcam.motion_stills_record)
state = "stills";
else
state = "none";
fprintf(f, "motion_record_state %s\n", state);
fprintf(f, "video_last %s\n",
pikrellcam.video_last ? pikrellcam.video_last : "none");
fprintf(f, "video_last_frame_count %d\n", pikrellcam.video_last_frame_count);
@ -840,6 +872,9 @@ state_file_write(void)
fprintf(f, "timelapse_video_last %s\n",
pikrellcam.timelapse_video_last ? pikrellcam.timelapse_video_last : "none");
fprintf(f, "motion_detects_fifo_enable %s\n",
pikrellcam.motion_detects_fifo_enable ? "on" : "off");
fprintf(f, "current_minute %d\n",
pikrellcam.tm_local.tm_hour * 60 + pikrellcam.tm_local.tm_min);
fprintf(f, "dawn %d\n", sun.dawn);
@ -1332,6 +1367,9 @@ at_commands_config_save(char *config_file)
"# renamed after the video ends.\n"
"# $S - still files directory full path\n"
"# $s - last still saved full path filename\n"
#ifdef MOTION_STILLS
"# $r - motion record mode: stills or videos\n"
#endif
"# $L - timelapse files directory full path\n"
"# $l - timelapse current series filename format: tl_sssss_%%05d.jpg\n"
"# in timelapse sub directory. If used in any script\n"

View File

@ -1,6 +1,6 @@
/* PiKrellCam
|
| Copyright (C) 2015-2016 Bill Wilson billw@gkrellm.net
| Copyright (C) 2015-2019 Bill Wilson billw@gkrellm.net
|
| PiKrellCam is free software: you can redistribute it and/or modify it
| under the terms of the GNU General Public License as published by
@ -306,6 +306,12 @@ still_jpeg_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
if (buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END)
{
fclose(still_jpeg_encoder.file);
#ifdef MOTION_STILLS
if (pikrellcam.motion_stills_capture_time > 0)
motion_events_write(&motion_frame, MOTION_EVENTS_STILL, 0);
#endif
if (pikrellcam.still_capture_event)
{
event_add("still capture command", pikrellcam.t_now, 0,
@ -330,6 +336,7 @@ still_jpeg_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
PIKRELLCAM_TIMELAPSE_SUBDIR);
}
pikrellcam.motion_stills_capture_time = 0;
pikrellcam.still_capture_event = FALSE;
pikrellcam.timelapse_capture_event = FALSE;
bytes_written = 0;
@ -750,12 +757,14 @@ video_h264_encoder_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *mmalbuf)
if (mf->fifo_trigger_time_limit > 0)
{
vcb->motion_sync_time = vcb->t_cur + mf->fifo_trigger_time_limit;
vcb->motion_sync_time = vcb->t_cur
+ mf->fifo_trigger_time_limit;
vcb->max_record_time = mf->fifo_trigger_time_limit;
}
else
{
vcb->motion_sync_time = vcb->t_cur + pikrellcam.motion_times.post_capture;
vcb->motion_sync_time = vcb->t_cur
+ pikrellcam.motion_times.post_capture;
vcb->max_record_time = pikrellcam.motion_record_time_limit;
}
}
@ -915,7 +924,8 @@ video_h264_encoder_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *mmalbuf)
if ( force_stop
|| ( mf->fifo_trigger_time_limit == 0
&& vcb->t_cur >= vcb->motion_last_detect_time + pikrellcam.motion_times.event_gap
&& vcb->t_cur >= pikrellcam.motion_last_detect_time
+ pikrellcam.motion_times.event_gap
)
)
{

View File

@ -1,6 +1,6 @@
/* PiKrellCam
|
| Copyright (C) 2015-2017 Bill Wilson billw@gkrellm.net
| Copyright (C) 2015-2019 Bill Wilson billw@gkrellm.net
|
| PiKrellCam is free software: you can redistribute it and/or modify it
| under the terms of the GNU General Public License as published by
@ -354,7 +354,7 @@ motion_stats_write(VideoCircularBuffer *vcb, MotionFrame *mf)
}
void
motion_event_write(VideoCircularBuffer *vcb, MotionFrame *mf, boolean start)
motion_events_write(MotionFrame *mf, int type, float detect_time)
{
static FILE *f;
CompositeVector *cvec, *frame_vec = &mf->frame_vector;
@ -365,7 +365,7 @@ motion_event_write(VideoCircularBuffer *vcb, MotionFrame *mf, boolean start)
int burst, i, pan, tilt;
boolean dir_motion;
if (start)
if (type & MOTION_EVENTS_HEADER)
{
f = fopen(pikrellcam.motion_events_filename, "w");
fprintf(f, "<header>\n");
@ -394,10 +394,11 @@ motion_event_write(VideoCircularBuffer *vcb, MotionFrame *mf, boolean start)
}
fprintf(f, "</header>\n");
}
if (f && ((vcb->state & VCB_STATE_MOTION_RECORD) || start))
// if (f && ((vcb->state & VCB_STATE_MOTION_RECORD) || start))
if (f && (type & MOTION_EVENTS_DETECT))
{
fprintf(f, "<motion %6.3f>\n",
(float) vcb->frame_count / (float) pikrellcam.camera_adjust.video_fps);
fprintf(f, "<motion %6.3f >\n", detect_time);
// (float) vcb->frame_count / (float) pikrellcam.camera_adjust.video_fps);
fprintf(f, "f %3d %3d %3d %3d %3.0f %4d\n",
frame_vec->x, frame_vec->y, -frame_vec->vx, -frame_vec->vy,
sqrt((float)frame_vec->mag2), frame_vec->mag2_count);
@ -431,7 +432,9 @@ motion_event_write(VideoCircularBuffer *vcb, MotionFrame *mf, boolean start)
fprintf(f, "</motion>\n");
fflush(f);
}
else if (f && (vcb->state == VCB_STATE_NONE))
else if (f && (type & MOTION_EVENTS_STILL))
fprintf(f, "<still %s>\n", pikrellcam.still_last);
else if (f && (type & MOTION_EVENTS_END))
{
fprintf(f, "<end>\n");
fclose(f);
@ -440,6 +443,132 @@ motion_event_write(VideoCircularBuffer *vcb, MotionFrame *mf, boolean start)
/* else a MANUAL record state */
}
void
motion_detects_fifo_write(MotionFrame *mf)
{
CompositeVector *cvec, *frame_vec = &mf->frame_vector;
MotionRegion *mreg;
SList *mrlist;
char buf[256];
int burst, i;
boolean dir_motion;
static int fd = -1;
static boolean warned = FALSE;
if (!mf)
{
if (!pikrellcam.motion_detects_fifo_enable && fd >= 0)
{
write(fd, "<off>\n", 6);
close(fd);
fd = -1;
}
else if (pikrellcam.motion_detects_fifo_enable && fd < 0)
fd = open(pikrellcam.motion_detects_fifo, O_WRONLY | O_NONBLOCK);
return;
}
if (!pikrellcam.motion_detects_fifo_enable)
return;
if (fd < 0)
fd = open(pikrellcam.motion_detects_fifo, O_WRONLY | O_NONBLOCK);
if (fd < 0)
{
if (!warned)
log_printf("Open failed: %s. %m\n", pikrellcam.motion_detects_fifo);
warned = TRUE;
return;
}
else
warned = FALSE;
i = snprintf(buf, sizeof(buf), "<motion %d.%d >\n",
(int) pikrellcam.tv_now.tv_sec,
(int) pikrellcam.tv_now.tv_usec / 100000);
write(fd, buf, i);
/* Write same data as motion_events_write() but to fd for the nonblocking
| fifo instead of a buffered FILE.
*/
snprintf(buf, sizeof(buf), "f %3d %3d %3d %3d %3.0f %4d\n",
frame_vec->x, frame_vec->y, -frame_vec->vx, -frame_vec->vy,
sqrt((float)frame_vec->mag2), frame_vec->mag2_count);
write(fd, buf, strlen(buf));
if (mf->motion_status & MOTION_BURST)
{
burst = frame_vec->mag2_count + mf->reject_count;
snprintf(buf, sizeof(buf), "b %3d\n", burst);
write(fd, buf, strlen(buf));
}
if (mf->motion_status & MOTION_AUDIO)
{
snprintf(buf, sizeof(buf), "a %3d\n", pikrellcam.audio_level_event);
write(fd, buf, strlen(buf));
}
if (mf->motion_status & MOTION_FIFO)
{
snprintf(buf, sizeof(buf), "e %s\n", mf->fifo_trigger_code);
write(fd, buf, strlen(buf));
}
if (mf->motion_status & MOTION_DIRECTION)
{
for (i = 0, mrlist = mf->motion_region_list; mrlist;
mrlist = mrlist->next, ++i)
{
mreg = (MotionRegion *)mrlist->data;
dir_motion = mreg->motion
& (MOTION_TYPE_DIR_SMALL | MOTION_TYPE_DIR_NORMAL);
if (!dir_motion)
continue;
cvec = &mreg->vector;
snprintf(buf, sizeof(buf), "%d %3d %3d %3d %3d %3.0f %4d\n",
i, cvec->x, cvec->y, -cvec->vx, -cvec->vy,
sqrt((float)cvec->mag2), cvec->mag2_count);
write(fd, buf, strlen(buf));
}
}
write(fd, "</motion>\n", 10);
}
void
motion_stills_record(MotionFrame *mf)
{
PiKrellCam *pkc = &pikrellcam;
float period_usec, capture_diff_usec;
struct timeval tv_diff;
static struct timeval tv_motion_still;
if (!pkc->motion_stills_record) /* stills record start */
{
motion_events_write(mf, MOTION_EVENTS_HEADER, 0);
pkc->motion_stills_start_time = pkc->t_now;
tv_motion_still = pkc->tv_now;
if (mf->fifo_trigger_time_limit > 0)
pkc->motion_stills_max_time = mf->fifo_trigger_time_limit;
else
pkc->motion_stills_max_time = pkc->motion_record_time_limit;
pkc->motion_stills_sequence = 1;
}
timersub(&pkc->tv_now, &tv_motion_still, &tv_diff);
capture_diff_usec = (float)(tv_diff.tv_sec * 1e6) + (float)tv_diff.tv_usec;
period_usec = 60.0 / (float) pkc->motion_stills_per_minute * 1e6;
if ( !pkc->motion_stills_record /* First capture */
|| capture_diff_usec > period_usec
)
{
pkc->motion_stills_record = TRUE;
tv_motion_still = pkc->tv_now;
event_add("event_motion_still_capture", pkc->t_now, 0,
event_motion_still_capture, (void *) pkc->t_now);
}
}
void
motion_frame_process(VideoCircularBuffer *vcb, MotionFrame *mf)
{
@ -450,6 +579,7 @@ motion_frame_process(VideoCircularBuffer *vcb, MotionFrame *mf)
boolean burst_density_pass, motion_enabled;
char tbuf[50], *msg;
int x0, y0, x1, y1, t;
float detect_time;
static int mfp_number, motion_burst_frame;
/* Allow some startup camera settle time before motion detecting.
@ -593,6 +723,7 @@ motion_frame_process(VideoCircularBuffer *vcb, MotionFrame *mf)
)
{
if ( !(vcb->state & VCB_STATE_MOTION_RECORD)
&& !pikrellcam.motion_stills_record
&& mf->frame_window == 0
&& pikrellcam.motion_times.confirm_gap > 0
)
@ -692,11 +823,15 @@ motion_frame_process(VideoCircularBuffer *vcb, MotionFrame *mf)
|| ( (mf->motion_status & MOTION_FIFO) && motion_enabled)
)
{
vcb->motion_last_detect_time = pikrellcam.t_now;
pikrellcam.motion_last_detect_time = pikrellcam.t_now;
/* Motion detection will be ignored if a manual record is in progress.
/* Motion recording ignored if a manual record is in progress.
| If loop recording, no motion stills recording and turn the
| loop video into a motion video.
*/
if ( vcb->state == VCB_STATE_NONE
if ( ( vcb->state == VCB_STATE_NONE
&& !pikrellcam.motion_stills_record
)
|| ( (vcb->state & VCB_STATE_LOOP_RECORD)
&& !(vcb->state & VCB_STATE_MOTION_RECORD)
)
@ -707,17 +842,34 @@ motion_frame_process(VideoCircularBuffer *vcb, MotionFrame *mf)
| callback can immediately schedule the on_preview_save command
| so there is no wait to execute it.
*/
if (!(vcb->state & VCB_STATE_LOOP_RECORD))
if ( !(vcb->state & VCB_STATE_LOOP_RECORD)
&& !pikrellcam.motion_stills_enable
)
video_record_start(vcb, VCB_STATE_MOTION_RECORD_START);
else
{
/* Either a current loop video to be turned into a motion
| video or start motion stills recording.
*/
pikrellcam.do_preview_save = TRUE;
motion_event_write(vcb, &motion_frame, TRUE);
vcb->state |= VCB_STATE_MOTION_RECORD;
if (vcb->state & VCB_STATE_LOOP_RECORD)
{
vcb->state |= VCB_STATE_MOTION_RECORD;
motion_events_write(&motion_frame, MOTION_EVENTS_START,
(float) vcb->frame_count
/ (float) pikrellcam.camera_adjust.video_fps);
}
else if (pikrellcam.motion_stills_enable)
motion_stills_record(mf);
if (*pikrellcam.on_motion_begin_cmd)
{
pikrellcam.motion_record_mode = pikrellcam.motion_stills_enable
? "stills" : "videos";
event_add("motion begin", pikrellcam.t_now, 0,
event_motion_begin_cmd,
pikrellcam.on_motion_begin_cmd);
}
}
if (!strcmp(pikrellcam.motion_preview_save_mode, "first"))
@ -743,13 +895,16 @@ motion_frame_process(VideoCircularBuffer *vcb, MotionFrame *mf)
if (pikrellcam.verbose_motion && !pikrellcam.verbose)
printf("***Motion record start: %s\n\n", pikrellcam.video_pathname);
}
else if (vcb->state & VCB_STATE_MOTION_RECORD)
else if ( vcb->state & VCB_STATE_MOTION_RECORD
|| pikrellcam.motion_stills_record
)
{
/* Already recording, so each motion trigger bumps up the record
| time to now + post capture time.
| If mode "best" and better composite vector, save a new preview.
*/
vcb->motion_sync_time = pikrellcam.t_now + pikrellcam.motion_times.post_capture;
vcb->motion_sync_time = pikrellcam.t_now
+ pikrellcam.motion_times.post_capture;
if (mf->motion_status & (MOTION_DIRECTION | MOTION_BURST))
pikrellcam.external_motion = FALSE;
@ -778,15 +933,30 @@ motion_frame_process(VideoCircularBuffer *vcb, MotionFrame *mf)
++mf->audio_detects;
if (mf->motion_status & MOTION_FIFO)
++mf->fifo_detects;
motion_event_write(vcb, mf, FALSE);
if (vcb->state & VCB_STATE_MOTION_RECORD)
detect_time = (float) vcb->frame_count
/ (float) pikrellcam.camera_adjust.video_fps;
else
detect_time = (float) (pikrellcam.motion_stills_capture_time
- pikrellcam.motion_stills_start_time);
motion_events_write(mf, MOTION_EVENTS_DETECT, detect_time);
if (pikrellcam.verbose_motion)
printf("==>Motion record bump: %s\n\n", pikrellcam.video_pathname);
}
if (pikrellcam.motion_stills_enable)
motion_stills_record(mf);
if (pikrellcam.motion_stats)
motion_stats_write(vcb, mf);
}
if ( mf->motion_status & MOTION_DETECTED
&& !pikrellcam.servo_moving
&& (pikrellcam.on_preset || pikrellcam.motion_off_preset)
)
motion_detects_fifo_write(mf);
}

View File

@ -1,6 +1,6 @@
/* PiKrellCam
|
| Copyright (C) 2015-2016 Bill Wilson billw@gkrellm.net
| Copyright (C) 2015-2019 Bill Wilson billw@gkrellm.net
|
| PiKrellCam is free software: you can redistribute it and/or modify it
| under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/* PiKrellCam
|
| Copyright (C) 2015-2017 Bill Wilson billw@gkrellm.net
| Copyright (C) 2015-2019 Bill Wilson billw@gkrellm.net
|
| PiKrellCam is free software: you can redistribute it and/or modify it
| under the terms of the GNU General Public License as published by
@ -296,7 +296,7 @@ camera_restart(void)
}
boolean
still_capture(char *fname)
still_capture(char *fname, time_t motion_time)
{
Event *event;
int n;
@ -348,6 +348,7 @@ still_capture(char *fname)
event_notify_expire, &pikrellcam.still_notify);
pikrellcam.still_capture_event = TRUE;
pikrellcam.still_notify = TRUE;
pikrellcam.motion_stills_capture_time = motion_time;
}
}
return result;
@ -600,7 +601,9 @@ video_record_start(VideoCircularBuffer *vcb, int start_state)
&& (vcb->motion_stats_file = fopen(stats_path, "w")) != NULL
)
vcb->motion_stats_do_header = TRUE;
motion_event_write(vcb, &motion_frame, TRUE);
motion_events_write(&motion_frame, MOTION_EVENTS_START,
(float) vcb->frame_count
/ (float) pikrellcam.camera_adjust.video_fps);
if (*pikrellcam.on_motion_begin_cmd != '\0')
event_add("motion begin", pikrellcam.t_now, 0,
event_motion_begin_cmd, pikrellcam.on_motion_begin_cmd);
@ -927,7 +930,7 @@ video_record_stop(VideoCircularBuffer *vcb)
pikrellcam.external_motion = FALSE;
vcb->state = VCB_STATE_NONE;
motion_event_write(vcb, mf, FALSE);
motion_events_write(mf, MOTION_EVENTS_END, 0);
pikrellcam.state_modified = TRUE;
vcb->pause = FALSE;
vcb->record_hold =FALSE;
@ -971,6 +974,26 @@ loop_record(void)
}
}
#ifdef MOTION_STILLS
void
motion_stills_stop_check(void)
{
PiKrellCam *pkc = &pikrellcam;
time_t expire_time;
expire_time = pkc->motion_stills_max_time > 0
? pkc->motion_stills_start_time + pkc->motion_stills_max_time
: pkc->motion_last_detect_time + pkc->motion_times.event_gap;
if (pkc->motion_stills_enable && pkc->t_now < expire_time)
return;
pikrellcam.motion_stills_record = FALSE;
pikrellcam.motion_stills_sequence = 1;
motion_events_write(&motion_frame, MOTION_EVENTS_END, 0);
}
#endif
static int
get_arg_pass1(char *opt, char *arg)
{
@ -1085,6 +1108,12 @@ typedef enum
motion_cmd,
motion_enable,
#ifdef MOTION_STILLS
motion_stills_enable,
#endif
motion_detects_fifo_enable,
display_cmd, /* Placement above here can affect OSD. If menu */
/* or adjustment is showing, above commands redirect */
/* to cancel the menu or adjustment. */
@ -1138,6 +1167,12 @@ static Command commands[] =
{ "motion", motion_cmd, 1, FALSE },
{ "motion_enable", motion_enable, 1, TRUE },
#ifdef MOTION_STILLS
{ "motion_stills_enable", motion_stills_enable, 1, TRUE },
#endif
{ "motion_detects_fifo_enable", motion_detects_fifo_enable, 1, TRUE },
/* Above commands are redirected to abort a menu or adjustment display
*/
{ "display", display_cmd, 1, FALSE },
@ -1277,12 +1312,23 @@ command_process(char *command_line)
break;
case loop:
pthread_mutex_lock(&vcb->mutex);
n = pikrellcam.loop_enable;
config_set_boolean(&pikrellcam.loop_enable, args);
config_set_boolean(&n, args);
if (n && pikrellcam.motion_stills_enable)
{
display_inform("\"Cannot enable loop videos\" 3 3 1");
display_inform("\"while motion_stills are enabled.\" 4 3 1");
display_inform("timeout 2");
break;
}
if (n != pikrellcam.loop_enable)
{
pthread_mutex_lock(&vcb->mutex);
video_record_stop(vcb); // override motion & manual
pthread_mutex_unlock(&vcb->mutex);
pthread_mutex_unlock(&vcb->mutex);
}
pikrellcam.loop_enable = n;
pikrellcam.state_modified = TRUE;
break;
case still:
@ -1292,7 +1338,7 @@ command_process(char *command_line)
'N', buf,
'H', pikrellcam.hostname);
pikrellcam.still_sequence += 1;
still_capture(path);
still_capture(path, 0);
free(path);
break;
@ -1440,6 +1486,37 @@ command_process(char *command_line)
event_motion_enable_cmd, pikrellcam.on_motion_enable_cmd);
break;
case motion_detects_fifo_enable:
config_set_boolean(&pikrellcam.motion_detects_fifo_enable, args);
motion_detects_fifo_write(NULL); /* Open or close fifo */
pikrellcam.config_modified = TRUE;
pikrellcam.state_modified = TRUE;
break;
#ifdef MOTION_STILLS
case motion_stills_enable:
n = pikrellcam.motion_stills_enable;
config_set_boolean(&n, args);
if (n && pikrellcam.loop_enable)
{
display_inform("\"Cannot enable motion stills\" 3 3 1");
display_inform("\"while loop videos are enabled.\" 4 3 1");
display_inform("timeout 2");
break;
}
if (n && !pikrellcam.motion_stills_enable)
{
pthread_mutex_lock(&vcb->mutex);
if (vcb->state == VCB_STATE_MOTION_RECORD)
video_record_stop(vcb);
pthread_mutex_unlock(&vcb->mutex);
}
pikrellcam.motion_stills_enable = n;
pikrellcam.config_modified = TRUE;
pikrellcam.state_modified = TRUE;
break;
#endif
case video_fps:
if ((n = atoi(args)) < 1)
n = 1;
@ -1842,7 +1919,7 @@ main(int argc, char *argv[])
char *opt, *arg, *equal_arg, *user;
char *line, *eol, buf[4096];
int t_usleep;
struct timeval tv;
// struct timeval tv;
pgm_name = argv[0];
setlocale(LC_TIME, "");
@ -1997,6 +2074,7 @@ main(int argc, char *argv[])
check_modes(buf, 0775);
asprintf(&pikrellcam.command_fifo, "%s/www/FIFO", pikrellcam.install_dir);
asprintf(&pikrellcam.motion_detects_fifo, "%s/www/motion_detects_FIFO", pikrellcam.install_dir);
asprintf(&pikrellcam.audio_fifo, "%s/www/audio_FIFO", pikrellcam.install_dir);
asprintf(&pikrellcam.scripts_dir, "%s/scripts", pikrellcam.install_dir);
asprintf(&pikrellcam.scripts_dist_dir, "%s/scripts-dist", pikrellcam.install_dir);
@ -2007,6 +2085,7 @@ main(int argc, char *argv[])
asprintf(&pikrellcam.loop_converting, "%s/loop_converting", pikrellcam.tmpfs_dir);
log_printf_no_timestamp("command FIFO: %s\n", pikrellcam.command_fifo);
log_printf_no_timestamp("motion_detects FIFO : %s\n", pikrellcam.motion_detects_fifo);
log_printf_no_timestamp("audio FIFO : %s\n", pikrellcam.audio_fifo);
log_printf_no_timestamp("mjpeg stream: %s\n", pikrellcam.mjpeg_filename);
@ -2073,9 +2152,11 @@ main(int argc, char *argv[])
"Failed to create archive directory, continuing anyway.\n");
if (!make_fifo(pikrellcam.motion_detects_fifo))
log_printf_no_timestamp("Failed to create motion_detects FIFO.\n");
if (!make_fifo(pikrellcam.audio_fifo))
log_printf_no_timestamp("Failed to create audio FIFO.\n");
if ((fifo = open(pikrellcam.command_fifo, O_RDONLY | O_NONBLOCK)) < 0)
{
log_printf("Failed to open FIFO: %s. %m\n", pikrellcam.command_fifo);
@ -2111,18 +2192,24 @@ main(int argc, char *argv[])
while (1)
{
if (gettimeofday(&tv, NULL) < 0)
if (gettimeofday(&pikrellcam.tv_now, NULL) < 0)
{
log_printf(" XXX gettimeofday error: %m\n");
usleep(100000);
}
else
{
t_usleep = (int) (100000 - (tv.tv_usec % 100000)); /* EVENT_LOOP_FREQUENCY!! */
/* EVENT_LOOP_FREQUENCY!! */
t_usleep = (int) (100000 - (pikrellcam.tv_now.tv_usec % 100000));
usleep(t_usleep + 1);
}
time(&pikrellcam.t_now);
#ifdef MOTION_STILLS
if (pikrellcam.motion_stills_record)
motion_stills_stop_check();
#endif
event_process();
multicast_recv();
tcp_poll_connect();

View File

@ -1,6 +1,6 @@
/* PiKrellCam
|
| Copyright (C) 2015-2017 Bill Wilson billw@gkrellm.net
| Copyright (C) 2015-2019 Bill Wilson billw@gkrellm.net
|
| PiKrellCam is free software: you can redistribute it and/or modify it
| under the terms of the GNU General Public License as published by
@ -57,7 +57,7 @@
#include "utils.h"
#define PIKRELLCAM_VERSION "4.1.6"
#define PIKRELLCAM_VERSION "4.2.0"
//TCP Stream Server
@ -257,7 +257,16 @@ typedef struct
}
MotionRegion;
/* Motion types for writing the /run/pikrellcam/motion-events file
*/
#define MOTION_EVENTS_HEADER 1
#define MOTION_EVENTS_DETECT 2
#define MOTION_EVENTS_START (MOTION_EVENTS_HEADER | MOTION_EVENTS_DETECT)
#define MOTION_EVENTS_STILL 4
#define MOTION_EVENTS_END 8
/* Motion detect state/types for the MotionFrame
*/
#define MOTION_NONE 0
#define MOTION_DETECTED 1
#define MOTION_DIRECTION 2
@ -267,7 +276,7 @@ typedef struct
#define MOTION_PENDING_BURST 0x20
#define MOTION_AUDIO 0x40
/* Possible motion types for a region
/* Additional motion types for motion in regions.
*/
#define MOTION_TYPE_DIR_SMALL 1
#define MOTION_TYPE_DIR_NORMAL 2
@ -405,8 +414,7 @@ typedef struct
int record_start_frame_index;
float actual_fps;
time_t motion_last_detect_time,
motion_sync_time;
time_t motion_sync_time;
}
VideoCircularBuffer;
@ -523,7 +531,12 @@ typedef struct
{
time_t t_now,
t_start;
t_start,
motion_last_detect_time;
struct timeval
tv_now;
struct tm tm_local;
int second_tick;
int pi_model;
@ -542,6 +555,7 @@ typedef struct
*command_fifo,
*state_filename,
*motion_events_filename,
*motion_detects_fifo,
*video_converting,
*loop_converting;
@ -570,6 +584,7 @@ typedef struct
*longitude;
boolean startup_motion_enable,
motion_detects_fifo_enable,
external_motion,
state_modified,
preset_state_modified,
@ -581,22 +596,35 @@ typedef struct
MotionTimes
motion_times;
int motion_vectors_dimming,
motion_magnitude_limit,
motion_magnitude_limit_count,
motion_burst_count,
motion_burst_frames,
motion_record_time_limit;
char *motion_stills_name_format;
int motion_stills_sequence,
motion_stills_per_minute,
motion_stills_max_time;
time_t motion_stills_start_time,
motion_stills_capture_time;
boolean motion_stills_enable,
motion_stills_record;
char *on_motion_begin_cmd,
*on_motion_end_cmd,
*on_motion_enable_cmd,
*on_manual_end_cmd,
*on_loop_end_cmd,
*motion_regions_name;
*motion_regions_name,
*motion_record_mode;
char *preview_pathname,
*thumb_name;
char *motion_preview_save_mode,
*on_motion_preview_save_cmd;
boolean motion_preview_clean,
motion_vertical_filter,
motion_stats,
@ -731,7 +759,8 @@ typedef struct
boolean video_notify,
still_notify,
timelapse_notify;
int notify_duration;
int notify_duration,
rotation_value;
char *sharpness,
*contrast,
@ -857,7 +886,7 @@ boolean resizer_create(char *name, CameraObject *resizer,
void mjpeg_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
void I420_video_callback(MMAL_PORT_T *port,
MMAL_BUFFER_HEADER_T *buffer);
boolean still_capture(char *fname);
boolean still_capture(char *fname, time_t motion_time);
void still_jpeg_callback(MMAL_PORT_T *port,
MMAL_BUFFER_HEADER_T *buffer);
void video_h264_encoder_callback(MMAL_PORT_T *port,
@ -904,8 +933,8 @@ boolean motion_regions_config_load(char *config_file, boolean inform);
void motion_preview_file_event(void);
void motion_preview_area_fixup(void);
void print_cvec(char *str, CompositeVector *cvec);
void motion_event_write(VideoCircularBuffer *vcb, MotionFrame *mf,
boolean start);
void motion_events_write(MotionFrame *mf, int type, float detect_time);
void motion_detects_fifo_write(MotionFrame *mf);
/* On Screen Display
*/
@ -935,6 +964,7 @@ void event_process(void);
void event_motion_enable_cmd(char *cmd);
void event_motion_begin_cmd(char *cmd);
void event_still_capture_cmd(char *cmd);
void event_motion_still_capture(void *arg);
void event_video_diskfree_percent(char *type);
void event_archive_diskfree_percent(char *type);

View File

@ -1,7 +1,7 @@
/* PiKrellCam
|
| Copyright (C) 2015-2016 Bill Wilson billw@gkrellm.net
| Copyright (C) 2015-2019 Bill Wilson billw@gkrellm.net
|
| PiKrellCam is free software: you can redistribute it and/or modify it
| under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,7 @@
/* PiKrellCam
|
| Copyright (C) 2015-2016 Bill Wilson billw@gkrellm.net
| Copyright (C) 2015-2019 Bill Wilson billw@gkrellm.net
|
| PiKrellCam is free software: you can redistribute it and/or modify it
| under the terms of the GNU General Public License as published by

View File

@ -68,6 +68,13 @@ And there is a Raspberry Pi
<span style='font-size: 1.5em; font-weight: 650;'>Release Notes</span><hr>
<div class='indent0'>
Version 4.2.0
<div class='indent1'>
<a href="help.php#MOTION_EVENTS_FIFO">motion_detects_FIFO</a> can be read
to get all motion detects regardless of motion videos enabled state.<br>
Moved Setup->Config->Times/* and Setup->Settings->Startup_Motion to Setup->Config->Motion.
</div>
Version 4.1.5
<div class='indent1'>
Archive directory <a href="help.php#ARCHIVING">NFS mount examples.</a><br>
@ -368,7 +375,7 @@ event will cause the video to be tagged as having a motion event and the web
page thumb will be labeled to show that.
<ul>
<li>
Loop videos with no motion event will end with
Loop videos with no motion detect will end with
<span style='font-weight:700'>_0.mp4</span>.
</li>
<li>
@ -812,6 +819,67 @@ Preset group and there will be no Servo button in the Config group.
<p>
<span style='font-size: 1.2em; font-weight: 680;'>Config</span>
<ul>
<li><span style='font-weight:700'>Motion</span>
<ul>
<li><span style='font-weight:700'>Startup_Motion</span> - set to
<span style='font-weight:700'>ON</span> for motion detection to be enabled each time
PiKrellCam starts. Motion detection can be
enabled from the web page or a script.
</li>
<li><span style='font-weight:700'>Confirm_Gap</span> - for motion direction detects,
require a second motion detect within this period of seconds before triggering a
motion record event. This adds a level of noise rejection to motion direction detecting
but may be set to zero to disable a second detect requirement if fast detection is desired.
This setting does not apply to motion burst detects because the Burst_Frames setting
provides a confirm time for that method.
</li>
<li><span style='font-weight:700'>Pre_Capture</span> - seconds of video to record prior
to the first motion detect event. This value should be greater than or equal to the
Confirm_Gap. Pre capture does not apply when motion stills recording.
</li>
<li><span style='font-weight:700'>Event_Gap</span> - number of seconds that must pass
since the last motion detect before a motion event recording can end.
When an Event_Gap period does expire without a new motion detect occurring,
videos will end with an end time of the last motion detect time plus the
Post_Capture time (but see Post_Capture).
Set this higher for animals or walking people that may pause for
periods of time before resuming motion. Set lower for active scenes where events
are frequent and you want to bias towards shorter videos
that capture more events separately.
</li>
<li><span style='font-weight:700'>Post_Capture</span> - seconds of video
that will be recorded after the last occurring motion event. This time must be
less than or equal to the Event_Gap time because post capture time is accumulated
in the circular buffer while the video is recording. An expiring Event_Gap time
ends the video immediately and no more Post_Capture time can be accumulated.
</li>
<li><span style='font-weight:700'>Video_Time_Limit</span> - range is 0 - 1800
seconds (30 minutes) and is the maximum seconds of motion video
that will be recorded after the first occurring motion detect. So the total
video length max will be the Pre_Capture time + the Time_Limit.
If this is set to zero, there will be no time limit enforced. This limit
does not apply to manual recordings - see FIFO examples for that.
</li>
</ul>
<p>
<div class='indent1'>
<span style='font-weight:700'>Note:</span>
Videos can be started only on key frame boundaries. When waiting for a motion
event to start, PiKrellCam requests key frames from the camera once per second,
so a Pre_Capture time setting of T will actually record somewhere between
T and T+1 seconds.
Also, if there is a new motion event immediately following a previous motion
video end, there may not be the configured Pre_Capture time accumulated in the
circular buffer and so the actual pre capture time will be less than what
is configured.
</div>
</li>
<li><span style='font-weight:700'>Video Res</span> - selects the video resolution for
motion and manual videos. Different resolutions may have different fields of view. So
one reason for selecting
@ -819,17 +887,14 @@ Preset group and there will be no Servo button in the Config group.
<span style='font-weight:700'>1080p</span> would be to get a wider field of view. Resolutions
will have either 16:9 or 4:3 aspect ratio.
</li>
<li><span style='font-weight:700'>Still Res</span> - selecting different resolutions
gives different fields of view and aspect ratios.
</li>
<a name="DISKFREE">
<li><span style='font-weight:700'>Settings</span>
<ul>
<li><span style='font-weight:700'>Startup_Motion</span> - set to
<span style='font-weight:700'>ON</span> for motion detection to be enabled each time
PiKrellCam starts. Motion detection can be
enabled from the web page or a script.
</li>
<li><span style='font-weight:700'>Check_Media_Diskfree</span>
- if set <span style='font-weight:700'>ON</span>, when new motion
@ -850,6 +915,7 @@ Preset group and there will be no Servo button in the Config group.
moving media videos and except for some directory structure overhead
is not increasing disk usage.
</li>
<li><span style='font-weight:700'>Diskfree_Percent</span>
- maintain this minimum free percent on media and archive
file systems when checking is enabled for those file systems
@ -862,31 +928,38 @@ Preset group and there will be no Servo button in the Config group.
and its quality. Adjust up if it improves video quality. Adjust down if you want
to reduce the size of the videos.
</li>
<li><span style='font-weight:700'>video_fps</span> - typically this should be no higher
than 24 or the motion detecting preview jpeg camera stream may start dropping frames.
(I have no data on the effect GPU overclocking might have on this limitation).
</li>
<li><span style='font-weight:700'>video_mp4box_fps</span> - keep this value set to zero
unless you want to create fast or slow motion videos. When zero, mp4 boxing fps will be
the same as video_fps which is normally what you want. But this value can be set to a
non zero value different from video_fps if you want fast or slow motion videos.
</li>
<li><span style='font-weight:700'>mjpeg_divider</span> - this value is divided into
the video_fps value to get the preview jpeg rate. The preview is updated at this rate
and it is the rate that motion vector frames are checked for motion.
</li>
<li><span style='font-weight:700'>still_quality</span> - adjust up if it improves
still jpeg quality. Adjust down if you want to reduce the size of still jpegs.
</li>
<li><span style='font-weight:700'>Vector_Counts</span> - enable showing of vector count
statistics when showing a Preset. This information may help when setting motion detect
limits.
</li>
<li><span style='font-weight:700'>Vector_Dimming</span> - sets a percentage dimming
of the preview jpeg image when the
<span style='font-weight:700'>Vectors</span> display is enabled. This is to improve
the contrast of the drawn motion vectors.
</li>
<li><span style='font-weight:700'>Preview_Clean</span> - if set to
<span style='font-weight:700'>OFF</span>, whatever text or graphics that happen to be
drawn on the preview jpeg at the time a motion preview save or thumb save occurs will
@ -896,56 +969,6 @@ Preset group and there will be no Servo button in the Config group.
</li>
</ul>
</li>
<li><span style='font-weight:700'>Times</span>
<ul>
<li><span style='font-weight:700'>Confirm_Gap</span> - for motion direction detects,
require a second motion detect within this period of seconds before triggering a
motion detect event. This adds a level of noise rejection to motion direction detecting
but may be set to zero to disable a second detect requirement if fast detection is desired.
This setting does not apply to motion burst detects because the Burst_Frames setting
provides a confirm time for that method.
</li>
<li><span style='font-weight:700'>Pre_Capture</span> - seconds of video to record prior
to the first motion detect event. This value should be greater than or equal to the
Confirm_Gap.
</li>
<li><span style='font-weight:700'>Event_Gap</span> - number of seconds that must pass
since the last motion detect event before a motion video record can end.
When an Event_Gap period does expire without a new motion event occurring,
the video will end with an end time of the last motion detect time plus the
Post_Capture time (but see Post_Capture).
Set this higher for animals or walking people that may pause for
periods of time before resuming motion. Set lower for active scenes where events
are frequent and you want to bias towards shorter videos
that capture more events separately.
</li>
<li><span style='font-weight:700'>Post_Capture</span> - seconds of video
that will be recorded after the last occurring motion event. This time must be
less than or equal to the Event_Gap time because post capture time is accumulated
in the circular buffer while the video is recording. An expiring Event_Gap time
ends the video immediately and no more Post_Capture time can be accumulated.
</li>
<li><span style='font-weight:700'>Time_Limit</span> - range is 10 - 1800
seconds (30 minutes) and is the maximum seconds of motion video
that will be recorded after the first occurring motion event. So the total
video length max will be the Pre_Capture time + the Time_Limit.
If this is set to zero, there will be no time limit enforced. This limit
does not apply to manual recordings - see FIFO examples for that.
</li>
</ul>
<p>
<div class='indent1'>
<span style='font-weight:700'>Note:</span>
Videos can be started only on key frame boundaries. When waiting for a motion
event to start, PiKrellCam requests key frames from the camera once per second,
so a Pre_Capture time setting of T will actually record somewhere between
T and T+1 seconds.
Also, if there is a new motion event immediately following a previous motion
video end, there may not be the configured Pre_Capture time accumulated in the
circular buffer and so the actual pre capture time will be less than what
is configured.
</div>
</li>
<a name="DISKUSAGE">
<li><span style='font-weight:700'>Loop</span><br>
<ul>
@ -954,9 +977,11 @@ Preset group and there will be no Servo button in the Config group.
be enabled each time PiKrellCam starts. Loop recording can be
enabled from the web page or a script or an at-command.
</li>
<li><span style='font-weight:700'>Time_Limit</span>
- loop video length in seconds.
</li>
<li><span style='font-weight:700'>Diskusage_Percent</span>
- Limit disk space used by loop videos to this percent
by deleting oldest loop videos as new ones are recorded.
@ -972,7 +997,7 @@ Preset group and there will be no Servo button in the Config group.
<ul>
<li><span style='font-weight:700'>Audio_Trigger_Video</span>
- set to <span style='font-weight:700'>ON</span>
to enable audio events to be treated as motion events to trigger
to enable audio detects to be treated as motion detects to trigger
motion videos.
</li>
<li><span style='font-weight:700'>Audio_Trigger_Level</span>
@ -1829,6 +1854,7 @@ tl_end
tl_hold [on|off|toggle]
tl_show_status [on|off|toggle]
motion_enable [on|off|toggle]
motion_detects_fifo_enable [on|off|toggle]
motion limits magnitude count
motion burst count frames
motion trigger code # code is digit N or N:ID N is 0 or 1 and ID is a string (see Examples)
@ -2081,7 +2107,7 @@ frequency time "command"
time period (say started during the day versus started at night) when
PiKrellCam is started, you need to have your own script that checks the time.
Such a script can parse the PiKrellCam state file
<span style='font-weight:700'>/var/run/pikrellcam/state</span>
<span style='font-weight:700'>/run/pikrellcam/state</span>
to get the
<span style='font-weight:700'>current_minute</span>
and compare it to todays sun times from the state file:
@ -2229,27 +2255,28 @@ motion_state=${line#motion_enable}
<a name="MOTION_EVENTS">
<span style='font-size: 1.2em; font-weight: 700;'>/run/pikrellcam/motion-events</span>
<div class='indent1'>
This is new in PiKrellCam 3.1.0 and it's possible there will be small
changes in its format in coming versions.
This file is for processing motion detects during a motion video
or stills record event
in real time as they occur while recording is in progress. It is intended
to be processed by an on_motion_begin command configured in
pikrellcam.conf.
<p>
Motion detect events are written to this file during each motion video
recording. An on_motion_begin command configured to run in pikrellcam.conf
can read this file to get
a reasonably fast notification of motion
events as they occur while the video is recording. Scripts can determine
where motion is in the video frame (by x,y position or motion region
number) and then take some action such as sending multicast alarms and/or
moving servos.
See the motion_detects_FIFO below if you want to process a continuous
stream of motion detects whether or not a motion event is recording.
<p>
This file is overwritten with new event data for each new motion video
recording so
it is intended for use by on_motion_begin commands which are run
This file is overwritten with new detect data for each new motion
recording and the on_motion_begin command is run
immediately after the motion-events file writing begins.
The output to the file is
flushed after data for each motion detect is written.
See script-dist/example-motion-send-alarm2 for an example reading of this
file in an on_motion_begin script.
<p>
Scripts can determine
where motion is in the video frame (by x,y position or motion region
number) and then take some action such as sending multicast alarms and/or
moving servos.
<p>
The format of the file is a header block followed by one or more
motion blocks and a final end tag.
Inside of motion blocks are the data
@ -2262,50 +2289,144 @@ to be documented...
&lt;/header&gt;
...
&lt;motion 3.667&gt;
b 0
f 49 43 57 -2 57 263
1 44 42 53 -4 53 144
2 55 44 61 0 61 119
a 45
e 0
&lt;/motion&gt;
&lt;motion 4.100&gt;
f 10 36 69 52 86 290
b 949
&lt;/motion&gt;
&lt;motion 5.120&gt;
f 0 0 0 0 0 0
e PIR
&lt;/motion&gt;
&lt;motion 6.000&gt;
f 0 0 0 0 0 0
a 45
&lt;/motion&gt;
...
&lt;end&gt;
</pre>
shows data for a detect at 3.667 seconds into the video
shows data for a first detect at 3.667 seconds into the video
(including precapture).
Each line inside the motion block begins with a single character code:
Each line inside a motion block begins with a single character code:
<ul>
<li> <span style='font-weight:700'>b</span> - this line shows burst counts.
For this detect the burst count did not exceed the burst count limit, so zero
is reported.
<li> <span style='font-weight:700'>f x y dx dy magnitude count</span>
- where the first character code is 'f'.<br>
This line is the data for
the total frame vector (composite of all the motion region vectors).
There is always a frame vector reported but may be a zero vector for
audio or external only detects. A non-zero frame vector passes
the configured magnitude and count limits and there can be a passing
frame vector without any passing region vectors.
</li>
<li> <span style='font-weight:700'>f</span> - this line is the data for
the total frame vector (composite of all the motion region vectors)
and has the format:
<pre>
code x y dx dy magnitude count
</pre>
</li>
<li> <span style='font-weight:700'>n</span> - where n is a digit. These
lines are for motion vectors for a motion region and have the same
format as the frame vector.
There is one
line for each region having motion and no line for regions not having
motion. Just like the overall frame vector, for a motion region to have
motion, the configured magnitude and count limits must be met.
<li> <span style='font-weight:700'>n x y dx dy magnitude count</span>
- where the first character code n is a digit.<br>
These lines are for motion vectors for a motion region detects.
There will be a line for each region having motion and no line for
regions not having motion. Just like the overall frame vector, for a
motion region to have motion, the configured magnitude and count limits
must be met.
For this detect there was motion in regions 1 and 2.
</li>
<li> <span style='font-weight:700'>a|e</span>
- shows audio or external triggers. If an audio level exceeded the
audio_trigger_level value it is printed, otherwise 0 is shown. If there
was an external trigger (motion trigger command into the FIFO), then
the e line will show 1, otherwise 0.
<li> <span style='font-weight:700'>b</span> - this line shows burst counts.
If no regions individually passed detection magnitude and count limits,
the overall frame vector must pass for a burst count detect so a burst
detect with no region detects still always has a frame vector.
</li>
<li> <span style='font-weight:700'>a level</span>
- shows an audio trigger if an audio level exceeded the configured
audio_trigger_level value.
If there was only an audio trigger, then the overall
motion frame vector 'f' line will show a zero vector.
</li>
<li> <span style='font-weight:700'>e code</span>
- shows there was an external trigger (motion trigger command into the
FIFO). The code will either be "FIFO" or a custom code supplied in
the external trigger command.
If there was only an external trigger, then the overall
motion frame vector 'f' line will show a zero vector.
</li>
</div>
The end tag is written when the motion video ends.
</div
<a name="MULTICAST_INTERFACE">
</div>
<a name="MOTION_EVENTS_FIFO">
<span style='font-size: 1.5em; font-weight: 650;'>Motion Detects FIFO</span><hr>
<div class='indent0'>
&nbsp;&nbsp;<span style='font-size: 1.2em; font-weight:700'>~/pikrellcam/www/motion_detects_FIFO</span>
<p>
This named pipe fifo is for near real time processing of all PiKrellCam
motion detects regardless of motion recording enabled state and so
can be a general purpose motion detect front end interface for a user
application with its own motion detect policy. If motion video recordings are
enabled and there is a configured non-zero confirm gap, then this fifo will
report motion events that do not trigger a motion video if the second
confirming motion detect required for video recordings did not happen.
<p>
This fifo is not intended for use by an on_motion_begin command.
See the
<nobr><span style='font-weight:700'>/run/pikrellcam/motion-events</span></nobr>
file section above for motion detect processing on a per motion record event
basis via an on_motion_begin command.
<p>
To enable or disable writing all motion detects to the motion_detects_FIFO,
send to the command FIFO:
<pre>
echo "motion_detects_fifo_enable [on|off|toggle]" > ~/pikrellcam/www/FIFO
</pre>
Motion detect data blocks are written to the motion_detects_FIFO in the same
format as is written into the
<nobr><span style='font-weight:700'>/run/pikrellcam/motion-events</span></nobr>
file as described above except that the time in the motion tag will be
the current system time in seconds (with .1 second precision) instead of
the time elapsed into a video.
Also this is a continuous stream of motion blocks with no header blocks
written.
If the motion_detects_FIFO reading app needs any information other than
motion detects, it should read the
<nobr><span style='font-weight:700'>/run/pikrellcam/state</span></nobr>
file.
<p>
If the enable is "on" it will stay enabled across pikrellcam
restarts until an "off" command or pikrellcam.conf is edited. Once enabled,
pikrellcam tries to write all motion detects into the motion_detects_FIFO
and an external app can read the detects from the motion_detects_FIFO.
The external app can be started by hand, by a command in the
at-commands.conf file, or by cron.
<br>
When a "motion_detects_fifo_enable off" command is sent to the command FIFO,
an &lt;off&gt; tag is written to the motion_detects_FIFO so the user app can
know the motion_detects_fifo_enable has been turned off.
<p>
Read the example script
<span style='font-weight:700'>~/pikrellcam/scripts-dist/example-motion-detects-fifo</span>
for more information. If this script is run by hand from a terminal,
a stream of all motion detects is printed.
<p>
To test the example script on a Pi running pikrellcam, open a terminal
(SSH terminal if the Pi is headless) and run the example script:<br>
<pre>
$ ~/pikrellcam/scripts-dist/example-motion-detects-fifo
# The script enables motion_detects_FIFO and motion detects are printed here.
# Terminate the script with ^C or send an off command in another termial:
# $ echo "motion_detects_fifo_enable off" > ~/pikrellcam/www/FIFO
</pre>
Commands in at-commands.conf can coordinate times when you want
PiKrellCam to have motion recordings and times when you might want to run
a custom motion detect app that reads from the motion_detects_FIFO.
As is done in the example-motion-detects-fifo script, your app can enable
the motion_detects_FIFO or it can be enabled with an at-commands.conf command
before or after your script starts.
And an at-command can turn the motion_detects_FIFO off at a certain time
if you want to signal your app to self terminate.
</pre>
</div>
<a name="MULTICAST_INTERFACE">
<span style='font-size: 1.5em; font-weight: 650;'>Multicast Interface</span><hr>
<div class='indent0'>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -421,9 +421,13 @@ echo "<span style=\"color: $default_text_color\"> Enable:</span>";
<td>
<?php echo "<span style=\"font-weight:600; color: $default_text_color\">Config</span>"; ?>
<div>
<input type="button" value="Video Res"
<input type="button" value="Motion"
class="btn-menu"
style="margin-left:40px"
onclick="fifo_command('display motion_settings');"
>
<input type="button" value="Video Res"
class="btn-menu"
onclick="fifo_command('display video_presets');"
>
<input type="button" value="Still Res"
@ -434,10 +438,6 @@ echo "<span style=\"color: $default_text_color\"> Enable:</span>";
class="btn-menu"
onclick="fifo_command('display settings');"
>
<input type="button" value="Times"
class="btn-menu"
onclick="fifo_command('display motion_time');"
>
<input type="button" value="Loop"
class="btn-menu"
onclick="fifo_command('display loop_settings');"