Introduction
ONVIF Event Subscription allows your application to receive real-time notifications from a camera or device when specific events occur, such as motion detection, door opening, or PTZ movement. The Happytimesoft ONVIF Client Library runs an internal HTTP server to receive these push notifications. The code examples below are taken from the official onviftest2.cpp test program.
This guide walks you through initializing the event system, subscribing, and handling incoming events with the notification and disconnect callbacks.
Step 1: Initialize the Event System
Before subscribing, initialize the event handling system. This sets up an internal HTTP server to receive event notifications pushed by the device, and registers the callbacks that will be invoked:
// Initialize the event handler. Parameters:
// http_enable=1, bind HTTP server to 0.0.0.0:30100
// https_enable=0 (no HTTPS server)
onvif_event_init(1, NULL, 30100, 0, NULL, 0, NULL, NULL, 1, MAX_DEV_NUMS);
// Set event notification callback
onvif_set_event_notify_cb(eventNotifyCallback, 0);
// Set subscription disconnect callback
onvif_set_subscribe_disconnect_cb(subscribeDisconnectCallback, 0);
Note: The internal HTTP server (port 30100) listens for callbacks from the ONVIF device. Ensure this port is reachable from the device. To also enable an HTTPS listener, pass the HTTPS flag, port, and certificate files.
Step 2: Configure and Connect to the Device
Set up the ONVIF device connection with IP, port, credentials, and request timeout (same as other tutorials), then load its capabilities to check whether the event service is supported.
// Define an ONVIF_DEVICE variable
ONVIF_DEVICE g_device;
// Open log file and initialize buffers
log_init("onviftest2.log");
log_set_level(HT_LOG_DBG);
sys_buf_init(10 * MAX_DEV_NUMS);
http_msg_buf_init(10 * MAX_DEV_NUMS);
// Initialize the device structure
memset(&g_device, 0, sizeof(g_device));
// Set device address, credentials, auth method, timeout
onvif_initDevice(&g_device, argv[1], atoi(argv[2]), atoi(argv[3]));
onvif_SetAuthInfo(&g_device, argv[4], argv[5]);
onvif_SetAuthMethod(&g_device, AuthMethod_UsernameToken);
onvif_SetReqTimeout(&g_device, 5000);
// Load capabilities to check event support
if (!GetCapabilities(&g_device))
{
errorHandler(&g_device);
printf("%s, GetCapabilities failed\r\n", g_device.binfo.XAddr.host);
}
Step 3: Subscribe to Events
After connecting, subscribe to the device's event service if it is supported. The Subscribe() function takes the device handle and an index used to route notifications back to the correct device.
if (g_device.Capabilities.events.support == 1)
{
if (Subscribe(&g_device, getDeviceIndex(&g_device)))
{
printf("Subscribe successful!\r\n");
}
else
{
errorHandler(&g_device);
printf("Subscribe failed!\r\n");
}
}
Step 4: Handle Incoming Events
When an event occurs, the device pushes a notification to your internal HTTP server, which triggers the notification callback. The event data is delivered as a linked list of NotificationMessageList nodes; iterate the list and read each Topic:
void eventNotifyCallback(Notify_REQ * p_req, void * p_data)
{
NotificationMessageList * p_notify = p_req->notify;
NotificationMessageList * p_tmp = p_notify;
printf("receive event : \r\n");
printf("\tposturl : %s\r\n", p_req->PostUrl);
// Iterate the notification message list and print each topic
while (p_tmp)
{
printf("\tTopic : %s\r\n", p_tmp->NotificationMessage.Topic);
p_tmp = p_tmp->next;
}
// Route to the device using the index embedded in the PostUrl
int index = -1;
ONVIF_DEVICE * p_dev = NULL;
sscanf(p_req->PostUrl, "/subscription%d", &index);
p_dev = getDeviceByIndex(index);
if (NULL == p_dev)
{
onvif_free_NotificationMessages(&p_req->notify);
return;
}
// Store notifications and keep at most 100 per device
onvif_device_add_NotificationMessages(p_dev, p_notify);
p_dev->events.notify_nums += onvif_get_NotificationMessages_nums(p_notify);
if (p_dev->events.notify_nums > 100)
{
int nums = onvif_device_free_NotificationMessages(p_dev,
p_dev->events.notify_nums - 100);
p_dev->events.notify_nums -= nums;
}
}
Step 5: Handle Subscription Disconnect and Re-Subscribe
If the subscription is lost (e.g., network interruption or server restart), the disconnect callback is invoked. In a robust client, automatically re-subscribe here:
void subscribeDisconnectCallback(ONVIF_DEVICE * p_dev, void * p_data)
{
printf("\r\nsubscribeDisconnectCallback, %s\r\n", p_dev->binfo.XAddr.host);
// Automatically re-subscribe to restore the event feed
BOOL ret = Subscribe(p_dev, getDeviceIndex(p_dev));
printf("Subscribe, ret = %d\r\n", ret);
}
Step 6: Clean Up
Before exiting, release the device resources and de-initialize the event system and buffers:
onvif_free_device(&g_device); // free device resources
onvif_event_deinit(); // stop the event handler / HTTP server
http_msg_buf_deinit(); // free HTTP message buffer
sys_buf_deinit(); // free system buffer
log_close(); // close the log file
Key Concepts
- Push Notification: The device pushes notifications to your internal HTTP server, which invokes your
eventNotifyCallback. - NotificationMessageList: Events arrive as a linked list; iterate with
p_tmp = p_tmp->nextand read eachNotificationMessage.Topic. - PostUrl routing: The subscription index is embedded in
p_req->PostUrl(e.g.,/subscription0) and is used to identify which device the event belongs to. - Automatic re-subscribe: Re-call
Subscribe()inside the disconnect callback to restore the event feed automatically. - Port 30100: Must be open and accessible so the device can send notifications.
- Common events: Motion detection, PTZ control, audio level, door sensors.