Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 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
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
5 changes: 5 additions & 0 deletions divinus.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ night_mode:
adc_device: /dev/sar_adc_drv
adc_threshold: 128

motion_detect:
enable: false
sensitivity: 5
hold_s: 5

http_post:
enable: false
host: <your host>
Expand Down
9 changes: 9 additions & 0 deletions doc/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ This document describes the fields that can be found within a configuration file
- **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.

## 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

- **mirror**: Boolean to turn on image mirroring (default: `false`).
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/>
17 changes: 17 additions & 0 deletions src/app_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ int app_config_save(void) {
fprintf(file, " adc_device: %s\n", app_config.adc_device);
fprintf(file, " adc_threshold: %d\n", app_config.adc_threshold);

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");
fprintf(file, " flip: %s\n", app_config.flip ? "true" : "false");
Expand Down Expand Up @@ -281,6 +286,10 @@ enum ConfigError app_config_parse(void) {
app_config.adc_device[0] = 0;
app_config.adc_threshold = 128;

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 @@ -370,6 +379,14 @@ enum ConfigError app_config_parse(void) {
&app_config.adc_threshold);
}

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);
if (err != CONFIG_OK)
goto RET_ERR;
Expand Down
5 changes: 5 additions & 0 deletions src/app_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ struct AppConfig {
char adc_device[128];
int adc_threshold;

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

// [isp]
bool mirror;
bool flip;
Expand Down
63 changes: 63 additions & 0 deletions src/hal/star/i6_hal.c
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,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
8 changes: 8 additions & 0 deletions src/hal/star/i6_hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ 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);
Expand Down
38 changes: 38 additions & 0 deletions src/hal/star/i6_sys.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,31 @@ typedef struct {
unsigned char version[128];
} i6_sys_ver;

typedef struct {
unsigned long long pts;
int type;
unsigned int flags;
union {
// Layout measured on SSC323 (infinity6), the first five words are unknown
struct {
int tileMode;
unsigned int unknown[4];
i6_common_pixfmt pixFmt;
i6_common_compr compress;
int scanMode;
int fieldType;
int ispInfoType;
unsigned short width;
unsigned short height;
void *virtAddr[3];
unsigned long long physAddr[3];
unsigned int stride[3];
unsigned int bufSize;
} frame;
unsigned char reserved[240];
};
} i6_sys_bufinfo;

typedef struct {
void *handle, *handleCamOsWrapper;

Expand All @@ -81,6 +106,10 @@ typedef struct {
unsigned int dstFps, i6_sys_link link, unsigned int linkParam);
int (*fnSetOutputDepth)(i6_sys_bind *bind, unsigned int usrDepth, unsigned int bufDepth);
int (*fnUnbind)(i6_sys_bind *source, i6_sys_bind *dest);

int (*fnFlushInvCache)(void *address, unsigned int length);
int (*fnGetBuf)(i6_sys_bind *bind, i6_sys_bufinfo *info, int *handle);
int (*fnPutBuf)(int handle);
} i6_sys_impl;

static int i6_sys_load(i6_sys_impl *sys_lib) {
Expand Down Expand Up @@ -119,6 +148,15 @@ static int i6_sys_load(i6_sys_impl *sys_lib) {
hal_symbol_load("i6_sys", sys_lib->handle, "MI_SYS_UnBindChnPort")))
return EXIT_FAILURE;

sys_lib->fnFlushInvCache = (int(*)(void *address, unsigned int length))
hal_symbol_load("i6_sys", sys_lib->handle, "MI_SYS_FlushInvCache");

sys_lib->fnGetBuf = (int(*)(i6_sys_bind *bind, i6_sys_bufinfo *info, int *handle))
hal_symbol_load("i6_sys", sys_lib->handle, "MI_SYS_ChnOutputPortGetBuf");

sys_lib->fnPutBuf = (int(*)(int handle))
hal_symbol_load("i6_sys", sys_lib->handle, "MI_SYS_ChnOutputPortPutBuf");

return EXIT_SUCCESS;
}

Expand Down
6 changes: 3 additions & 3 deletions src/hal/tools.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ int base64_decode(char *decoded, const char *string, int maxLen) {
}
}

if (buflen > 0 && i + buflen <= maxLen) {
memcpy(decoded + i, buf, buflen);
i += buflen;
if (buflen > 1 && i + buflen - 1 <= maxLen) {
memcpy(decoded + i, buf, buflen - 1);
i += buflen - 1;
}

return i;
Expand Down
7 changes: 7 additions & 0 deletions src/hal/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ typedef struct {
hal_vidcodec payload;
} hal_chnstate;

typedef struct {
unsigned char *luma;
int stride;
short width, height;
int handle;
} hal_rawframe;

typedef struct {
unsigned short width, height;
} hal_dim;
Expand Down
Loading