Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
aad9e32
Adding a small host test harness
Jamp Sep 29, 2026
8f66c80
Adding namespace-agnostic SOAP helpers, fixing base64 padding
Jamp Sep 29, 2026
5728858
Parsing ONVIF actions and credentials by local names
Jamp Sep 29, 2026
6d62338
Answering unknown ONVIF actions with SOAP faults, reporting real resp…
Jamp Sep 29, 2026
c379aa7
Adding a PullPoint subscription store for ONVIF events
Jamp Sep 29, 2026
1a5b530
Adding the ONVIF event service responses and GetServices
Jamp Sep 29, 2026
faa8245
Serving ONVIF PullPoint subscriptions with long polling
Jamp Sep 29, 2026
892be4a
Replacing the longest idle ONVIF subscription when all slots are taken
Jamp Sep 29, 2026
6c98581
Timing ONVIF subscriptions on a monotonic clock
Jamp Sep 29, 2026
c2c323f
Hardening the ONVIF SOAP parsing and PullMessages allocation
Jamp Sep 29, 2026
2cab9c3
Rejecting ONVIF durations that overflow before scaling them
Jamp Sep 30, 2026
eadbde3
Applying time zone offsets to ONVIF termination times
Jamp Sep 30, 2026
18dfbdd
Keeping ONVIF subscriptions with a waiting pull out of replacement
Jamp Sep 30, 2026
de493b1
Timing PullMessages waits on the monotonic clock
Jamp Sep 30, 2026
e3eb967
Allowing GetServices and GetServiceCapabilities before ONVIF authenti…
Jamp Sep 30, 2026
d226bd7
Adding a block-based motion detector core
Jamp Sep 29, 2026
74c58b9
Adding the motion detection settings
Jamp Sep 29, 2026
478a9be
Reading raw frames from a free VPE port on infinity6
Jamp Sep 29, 2026
1e691c6
Detecting motion from raw VPE frames, raising ONVIF events
Jamp Sep 29, 2026
163dc83
Reserving the raw VPE port, hardening the motion detector input
Jamp Sep 29, 2026
7109f2c
Reopening the raw port on every frame stall, as a second stall stoppe…
Jamp Oct 5, 2026
fd3a24a
Letting the night mode thread stop, reading one byte in sysfs gpio_read
Jamp Sep 29, 2026
3b5159c
Passing a 4-byte boolean to MI_ISP_IQ_SetColorToGray
Jamp Sep 29, 2026
89e6f95
Switching night mode from the ISP gain when there is no light sensor
Jamp Sep 29, 2026
2f2fe2f
Hardening the night mode thread start and the ISP gain fallback
Jamp Sep 29, 2026
0121e9f
Pulsing the IR cut filter again every 10 minutes, as a missed pulse l…
Jamp Oct 4, 2026
4876b37
Retrying a failed ISP gain read instead of ending the night mode thre…
Jamp Oct 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
/toolchain
compile_flags.txt
divinus
/test/*_test
9 changes: 9 additions & 0 deletions divinus.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ night_mode:
pin_switch_delay_us: 150
adc_device: /dev/sar_adc_drv
adc_threshold: 128
night_gain: 8
day_gain: 2
night_hold_s: 15
day_hold_s: 60

motion_detect:
enable: false
sensitivity: 5
hold_s: 5

http_post:
enable: false
Expand Down
13 changes: 13 additions & 0 deletions doc/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ This document describes the fields that can be found within a configuration file
- **pin_switch_delay_us**: Delay in microseconds before switching GPIO pins, must be used to protect cut filter coils from burning.
- **adc_device**: Path to the ADC device used for night mode.
- **adc_threshold**: Threshold raw value to trigger night mode, depends on the bitness of the given ADC device.
- **night_gain**: Without a light sensor or ADC, total ISP gain (×) that switches to night mode (default: `8`).
- **day_gain**: Without a light sensor or ADC, total ISP gain (×) below which night mode switches back to day; keep it under the gain reached with the IR LED on (default: `2`).
- **night_hold_s**: Seconds the gain must stay above `night_gain` before switching to night (default: `15`).
- **day_hold_s**: Seconds the gain must stay below `day_gain` before switching back to day (default: `60`).

## Motion detection section

Software motion detection, published as ONVIF `tns1:VideoSource/MotionAlarm` events
(PullPoint). Only available on infinity6 for now.

- **enable**: Boolean to activate motion detection (default: `false`).
- **sensitivity**: From 1 (only large movements) to 10 (small movements) (default: `5`).
- **hold_s**: Seconds without movement before the motion state goes back to false (default: `5`).

## ISP section

Expand Down
2 changes: 1 addition & 1 deletion res/onvif/capabilities.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
<tt:Events>
<tt:XAddr>http://%s:%d/onvif/event_service</tt:XAddr>
<tt:WSSubscriptionPolicySupport>false</tt:WSSubscriptionPolicySupport>
<tt:WSPullPointSupport>false</tt:WSPullPointSupport>
<tt:WSPullPointSupport>true</tt:WSPullPointSupport>
<tt:WSPausableSubscriptionManagerInterfaceSupport>false</tt:WSPausableSubscriptionManagerInterfaceSupport>
</tt:Events>
<tt:Imaging>
Expand Down
9 changes: 9 additions & 0 deletions res/onvif/event.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2" xmlns:wstop="http://docs.oasis-open.org/wsn/t-1" xmlns:tev="http://www.onvif.org/ver10/events/wsdl" xmlns:tt="http://www.onvif.org/ver10/schema" xmlns:tns1="http://www.onvif.org/ver10/topics" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Header>
<wsa:Action>%s</wsa:Action>%s
</SOAP-ENV:Header>
<SOAP-ENV:Body>
%s
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
3 changes: 3 additions & 0 deletions res/onvif/eventcaps.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<tev:GetServiceCapabilitiesResponse>
<tev:Capabilities WSSubscriptionPolicySupport="false" WSPullPointSupport="true" WSPausableSubscriptionManagerInterfaceSupport="false" MaxNotificationProducers="0" MaxPullPoints="4" PersistentNotificationStorage="false"/>
</tev:GetServiceCapabilitiesResponse>
22 changes: 22 additions & 0 deletions res/onvif/eventprops.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<tev:GetEventPropertiesResponse>
<tev:TopicNamespaceLocation>http://www.onvif.org/onvif/ver10/topics/topicns.xml</tev:TopicNamespaceLocation>
<wsnt:FixedTopicSet>true</wsnt:FixedTopicSet>
<wstop:TopicSet>
<tns1:VideoSource>
<MotionAlarm wstop:topic="true">
<tt:MessageDescription IsProperty="true">
<tt:Source>
<tt:SimpleItemDescription Name="Source" Type="tt:ReferenceToken"/>
</tt:Source>
<tt:Data>
<tt:SimpleItemDescription Name="State" Type="xs:boolean"/>
</tt:Data>
</tt:MessageDescription>
</MotionAlarm>
</tns1:VideoSource>
</wstop:TopicSet>
<wsnt:TopicExpressionDialect>http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet</wsnt:TopicExpressionDialect>
<wsnt:TopicExpressionDialect>http://docs.oasis-open.org/wsn/t-1/TopicExpression/Concrete</wsnt:TopicExpressionDialect>
<tev:MessageContentFilterDialect>http://www.onvif.org/ver10/tev/messageContentFilter/ItemFilter</tev:MessageContentFilterDialect>
<tev:MessageContentSchemaLocation>http://www.onvif.org/onvif/ver10/schema/onvif.xsd</tev:MessageContentSchemaLocation>
</tev:GetEventPropertiesResponse>
16 changes: 16 additions & 0 deletions res/onvif/fault.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:ter="http://www.onvif.org/ver10/error" xmlns:wsrf-rw="http://docs.oasis-open.org/wsrf/rw-2">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<SOAP-ENV:Code>
<SOAP-ENV:Value>SOAP-ENV:%s</SOAP-ENV:Value>
<SOAP-ENV:Subcode>
<SOAP-ENV:Value>%s</SOAP-ENV:Value>
</SOAP-ENV:Subcode>
</SOAP-ENV:Code>
<SOAP-ENV:Reason>
<SOAP-ENV:Text xml:lang="en">%s</SOAP-ENV:Text>
</SOAP-ENV:Reason>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
13 changes: 13 additions & 0 deletions res/onvif/notification.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<wsnt:NotificationMessage>
<wsnt:Topic Dialect="http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet">tns1:VideoSource/MotionAlarm</wsnt:Topic>
<wsnt:Message>
<tt:Message UtcTime="%s" PropertyOperation="%s">
<tt:Source>
<tt:SimpleItem Name="Source" Value="%s"/>
</tt:Source>
<tt:Data>
<tt:SimpleItem Name="State" Value="%s"/>
</tt:Data>
</tt:Message>
</wsnt:Message>
</wsnt:NotificationMessage>
4 changes: 4 additions & 0 deletions res/onvif/pullmessages.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<tev:PullMessagesResponse>
<tev:CurrentTime>%s</tev:CurrentTime>
<tev:TerminationTime>%s</tev:TerminationTime>%s
</tev:PullMessagesResponse>
7 changes: 7 additions & 0 deletions res/onvif/pullpoint.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<tev:CreatePullPointSubscriptionResponse>
<tev:SubscriptionReference>
<wsa:Address>http://%s:%d/onvif/subscription?id=%d</wsa:Address>
</tev:SubscriptionReference>
<wsnt:CurrentTime>%s</wsnt:CurrentTime>
<wsnt:TerminationTime>%s</wsnt:TerminationTime>
</tev:CreatePullPointSubscriptionResponse>
4 changes: 4 additions & 0 deletions res/onvif/renew.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<wsnt:RenewResponse>
<wsnt:TerminationTime>%s</wsnt:TerminationTime>
<wsnt:CurrentTime>%s</wsnt:CurrentTime>
</wsnt:RenewResponse>
22 changes: 22 additions & 0 deletions res/onvif/services.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:tds="http://www.onvif.org/ver10/device/wsdl" xmlns:tt="http://www.onvif.org/ver10/schema">
<SOAP-ENV:Body>
<tds:GetServicesResponse>
<tds:Service>
<tds:Namespace>http://www.onvif.org/ver10/device/wsdl</tds:Namespace>
<tds:XAddr>http://%s:%d/onvif/device_service</tds:XAddr>
<tds:Version><tt:Major>2</tt:Major><tt:Minor>40</tt:Minor></tds:Version>
</tds:Service>
<tds:Service>
<tds:Namespace>http://www.onvif.org/ver10/media/wsdl</tds:Namespace>
<tds:XAddr>http://%s:%d/onvif/media_service</tds:XAddr>
<tds:Version><tt:Major>2</tt:Major><tt:Minor>40</tt:Minor></tds:Version>
</tds:Service>
<tds:Service>
<tds:Namespace>http://www.onvif.org/ver10/events/wsdl</tds:Namespace>
<tds:XAddr>http://%s:%d/onvif/event_service</tds:XAddr>
<tds:Version><tt:Major>2</tt:Major><tt:Minor>40</tt:Minor></tds:Version>
</tds:Service>
</tds:GetServicesResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
1 change: 1 addition & 0 deletions res/onvif/syncpoint.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<tev:SetSynchronizationPointResponse/>
1 change: 1 addition & 0 deletions res/onvif/unsubscribe.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<wsnt:UnsubscribeResponse/>
35 changes: 35 additions & 0 deletions src/app_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ int app_config_save(void) {
fprintf(file, " pin_switch_delay_us: %d\n", app_config.pin_switch_delay_us);
fprintf(file, " adc_device: %s\n", app_config.adc_device);
fprintf(file, " adc_threshold: %d\n", app_config.adc_threshold);
fprintf(file, " night_gain: %d\n", app_config.night_gain);
fprintf(file, " day_gain: %d\n", app_config.day_gain);
fprintf(file, " night_hold_s: %d\n", app_config.night_hold_s);
fprintf(file, " day_hold_s: %d\n", app_config.day_hold_s);

fprintf(file, "motion_detect:\n");
fprintf(file, " enable: %s\n", app_config.motion_detect_enable ? "true" : "false");
fprintf(file, " sensitivity: %d\n", app_config.motion_detect_sensitivity);
fprintf(file, " hold_s: %d\n", app_config.motion_detect_hold_s);

fprintf(file, "isp:\n");
fprintf(file, " mirror: %s\n", app_config.mirror ? "true" : "false");
Expand Down Expand Up @@ -280,6 +289,14 @@ enum ConfigError app_config_parse(void) {
app_config.check_interval_s = 10;
app_config.adc_device[0] = 0;
app_config.adc_threshold = 128;
app_config.night_gain = 8;
app_config.day_gain = 2;
app_config.night_hold_s = 15;
app_config.day_hold_s = 60;

app_config.motion_detect_enable = false;
app_config.motion_detect_sensitivity = 5;
app_config.motion_detect_hold_s = 5;

struct IniConfig ini;
memset(&ini, 0, sizeof(struct IniConfig));
Expand Down Expand Up @@ -368,6 +385,24 @@ enum ConfigError app_config_parse(void) {
parse_int(
&ini, "night_mode", "adc_threshold", INT_MIN, INT_MAX,
&app_config.adc_threshold);
parse_int(&ini, "night_mode", "night_gain", 1, 1024, &app_config.night_gain);
parse_int(&ini, "night_mode", "day_gain", 1, 1024, &app_config.day_gain);
parse_int(&ini, "night_mode", "night_hold_s", 1, 3600, &app_config.night_hold_s);
parse_int(&ini, "night_mode", "day_hold_s", 1, 3600, &app_config.day_hold_s);
if (!app_config.adc_device[0] && app_config.ir_sensor_pin == 999 &&
app_config.day_gain >= app_config.night_gain) {
HAL_DANGER("app_config", "night_mode.day_gain must be lower than night_gain!\n");
err = CONFIG_PARAM_ISNT_IN_RANGE;
goto RET_ERR;
}
}

parse_bool(&ini, "motion_detect", "enable", &app_config.motion_detect_enable);
if (app_config.motion_detect_enable) {
parse_int(&ini, "motion_detect", "sensitivity", 1, 10,
&app_config.motion_detect_sensitivity);
parse_int(&ini, "motion_detect", "hold_s", 1, 300,
&app_config.motion_detect_hold_s);
}

err = parse_bool(&ini, "isp", "mirror", &app_config.mirror);
Expand Down
9 changes: 9 additions & 0 deletions src/app_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ struct AppConfig {
unsigned int pin_switch_delay_us;
char adc_device[128];
int adc_threshold;
unsigned int night_gain;
unsigned int day_gain;
unsigned int night_hold_s;
unsigned int day_hold_s;

// [motion_detect]
bool motion_detect_enable;
int motion_detect_sensitivity;
int motion_detect_hold_s;

// [isp]
bool mirror;
Expand Down
4 changes: 1 addition & 3 deletions src/gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,13 @@ int gpio_read(char pin, bool *value) {

char val = 0;
lseek(fd, 0, SEEK_SET);
read(fd, &val, 0);
if (!val) {
if (read(fd, &val, 1) != 1) {
close(fd);
HAL_ERROR("gpio", "Unable to read from GPIO pin %d!\n", pin);
}
*value = val - 0x30;
close(fd);

if (gpio_direction(pin, "out")) return EXIT_FAILURE;
if (gpio_export(pin, false)) return EXIT_FAILURE;

return EXIT_SUCCESS;
Expand Down
80 changes: 79 additions & 1 deletion src/hal/star/i6_hal.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,8 @@ int i6_channel_create(char index, short width, short height, char jpeg)

int i6_channel_grayscale(char enable)
{
return i6_isp.fnSetColorToGray(0, &enable);
int active = enable;
return i6_isp.fnSetColorToGray(0, &active);
}

int i6_channel_unbind(char index)
Expand Down Expand Up @@ -379,6 +380,69 @@ void i6_pipeline_destroy(void)
i6_snr.fnDisable(_i6_snr_index);
}

int i6_raw_create(short width, short height)
{
int ret;

if (!i6_sys.fnGetBuf || !i6_sys.fnPutBuf)
HAL_ERROR("i6_raw", "libmi_sys does not export the buffer functions!\n");
if (i6_state[I6_RAW_PORT].enable)
HAL_ERROR("i6_raw", "VPE port %d is already used by an encoder!\n", I6_RAW_PORT);

i6_vpe_port port;
port.output.width = width;
port.output.height = height;
port.mirror = 0;
port.flip = 0;
port.compress = I6_COMPR_NONE;
port.pixFmt = I6_PIXFMT_YUV420SP;
if (ret = i6_vpe.fnSetPortConfig(_i6_vpe_chn, I6_RAW_PORT, &port))
return ret;

i6_sys_bind bind = { .module = I6_SYS_MOD_VPE,
.device = _i6_vpe_dev, .channel = _i6_vpe_chn, .port = I6_RAW_PORT };
if (ret = i6_sys.fnSetOutputDepth(&bind, 1, 3))
return ret;

return i6_vpe.fnEnablePort(_i6_vpe_chn, I6_RAW_PORT);
}

int i6_raw_get(hal_rawframe *frame)
{
i6_sys_bind bind = { .module = I6_SYS_MOD_VPE,
.device = _i6_vpe_dev, .channel = _i6_vpe_chn, .port = I6_RAW_PORT };
i6_sys_bufinfo info;
int handle;

if (i6_sys.fnGetBuf(&bind, &info, &handle))
return EXIT_FAILURE;

if (info.frame.pixFmt != I6_PIXFMT_YUV420SP) {
i6_sys.fnPutBuf(handle);
HAL_ERROR("i6_raw", "Unexpected frame format %d!\n", info.frame.pixFmt);
}

if (i6_sys.fnFlushInvCache)
i6_sys.fnFlushInvCache(info.frame.virtAddr[0], info.frame.stride[0] * info.frame.height);

frame->luma = info.frame.virtAddr[0];
frame->stride = info.frame.stride[0];
frame->width = info.frame.width;
frame->height = info.frame.height;
frame->handle = handle;
return EXIT_SUCCESS;
}

int i6_raw_release(hal_rawframe *frame)
{
return i6_sys.fnPutBuf(frame->handle);
}

void i6_raw_destroy(void)
{
i6_vpe.fnDisablePort(_i6_vpe_chn, I6_RAW_PORT);
}

int i6_region_create(char handle, hal_rect rect, short opacity)
{
int ret;
Expand Down Expand Up @@ -492,6 +556,20 @@ int i6_sensor_exposure(unsigned int micros)
return ret;
}

int i6_isp_gain(unsigned int *gain)
{
i6_isp_expinfo exp;
int ret;

if (!i6_isp.fnQueryExposureInfo)
return EXIT_FAILURE;
if (ret = i6_isp.fnQueryExposureInfo(_i6_isp_chn, &exp))
return ret;

*gain = (unsigned long long)exp.info.longExp.sensorGain * exp.info.longExp.ispGain / 1024;
return EXIT_SUCCESS;
}

int i6_video_create(char index, hal_vidconfig *config)
{
int ret;
Expand Down
9 changes: 9 additions & 0 deletions src/hal/star/i6_hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,21 @@ int i6_config_load(char *path);
int i6_pipeline_create(char index, short width, short height, char mirror, char flip, char framerate);
void i6_pipeline_destroy(void);

// Port 3 of the SSC323 VPE hands out buffers that are never written
#define I6_RAW_PORT 2

int i6_raw_create(short width, short height);
int i6_raw_get(hal_rawframe *frame);
int i6_raw_release(hal_rawframe *frame);
void i6_raw_destroy(void);

int i6_region_create(char handle, hal_rect rect, short opacity);
void i6_region_deinit(void);
void i6_region_destroy(char handle);
void i6_region_init(void);
int i6_region_setbitmap(int handle, hal_bitmap *bitmap);

int i6_isp_gain(unsigned int *gain);
int i6_sensor_exposure(unsigned int micros);

int i6_video_create(char index, hal_vidconfig *config);
Expand Down
Loading