mirror of https://github.com/billw2/pikrellcam.git
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:
parent
0172d45cce
commit
1e970587b8
|
@ -1,6 +1,7 @@
|
|||
scripts
|
||||
www/.htpasswd
|
||||
www/FIFO
|
||||
www/motion_events_FIFO
|
||||
www/audio_FIFO
|
||||
www/media
|
||||
www/config-user*
|
||||
|
|
BIN
pikrellcam
BIN
pikrellcam
Binary file not shown.
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
45
src/config.c
45
src/config.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
40
src/event.c
40
src/event.c
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
)
|
||||
)
|
||||
{
|
||||
|
|
204
src/motion.c
204
src/motion.c
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
111
src/pikrellcam.c
111
src/pikrellcam.c
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
321
www/help.php
321
www/help.php
|
@ -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...
|
|||
</header>
|
||||
...
|
||||
<motion 3.667>
|
||||
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
|
||||
</motion>
|
||||
<motion 4.100>
|
||||
f 10 36 69 52 86 290
|
||||
b 949
|
||||
</motion>
|
||||
<motion 5.120>
|
||||
f 0 0 0 0 0 0
|
||||
e PIR
|
||||
</motion>
|
||||
<motion 6.000>
|
||||
f 0 0 0 0 0 0
|
||||
a 45
|
||||
</motion>
|
||||
...
|
||||
<end>
|
||||
</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'>
|
||||
<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 <off> 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 |
|
@ -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');"
|
||||
|
|
Loading…
Reference in New Issue