Set Up ONVIF Event Subscription

Learn how to subscribe to real-time events from an ONVIF device (e.g., motion detection, PTZ) using the Happytimesoft Client Library in C.

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:

C Code: Initialize Event System (from onviftest2)
// 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.

C Code: Device Setup (from onviftest2)
// 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.

C Code: Subscribe to Events (from onviftest2)
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:

C Code: Event Notification Callback (from onviftest2)
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:

C Code: Disconnect Callback with Auto Re-Subscribe (from onviftest2)
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:

C Code: Cleanup (from onviftest2)
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->next and read each NotificationMessage.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.