# VIF Custom Event Listener API Reference VIF delivers detection results to event listeners which are Java classes that implement `IVifEventListener`. This reference documents that interface and the `DetectionResponse` class hierarchy passed to it. For a guided walkthrough with a worked example, see [Create a custom VIF event listener](https://www.wowza.com/docs/create-a-video-intelligence-event-listener). ## Interface `IVifEventListener` ```java package com.wowza.wms.plugin.videointelligence.api; public interface IVifEventListener { static String getVersion(); default void onInit(IApplicationInstance appInstance, IMediaStream stream, Set methods, HashMap properties); default void onShutdown(); default boolean immediate(DetectionResponse response); default boolean batch(ArrayList responses); default boolean rollup(DetectionResponse response); } ``` All five instance methods are **`default`**. An implementation only needs to override the ones relevant to it. A listener that only uses `immediate` delivery, for example, doesn't need to provide `batch` or `rollup` overrides at all; the interface's defaults cover the rest. ### Method summary | Modifier and type | Method | Description | | --- | --- | --- | | `static String` | [`getVersion()`](#getversion) | Returns the plugin API version. | | `void` | [`onInit(...)`](#oninit) | Called once when the listener is attached to a stream. | | `void` | [`onShutdown()`](#onshutdown) | Called once when the listener is detached or the stream stops. | | `boolean` | [`immediate(DetectionResponse)`](#immediate) | Called per detection response, if `methods` includes `immediate`. | | `boolean` | [`batch(ArrayList<DetectionResponse>)`](#batch) | Called periodically with a batch of accumulated responses, if `methods` includes `batch`. | | `boolean` | [`rollup(DetectionResponse)`](#rollup) | Called on a rollup interval with a response, if `methods` includes `rollup`. | ### Method detail #### `getVersion` ```java static String getVersion() ``` Returns the version string of the `IVifEventListener` API your listener was compiled against — useful for logging or diagnostics when multiple listener versions may be in play. #### `onInit` ```java default void onInit(IApplicationInstance appInstance, IMediaStream stream, Set methods, HashMap properties) ``` Called once, when VIF attaches this listener to a stream. **Parameters:** - `appInstance` — the WSE application instance the stream belongs to. - `stream` — the media stream this listener instance is attached to. - `methods` — the delivery modes configured for this listener (any combination of `"immediate"`, `"batch"`, `"rollup"` — see [`EventMethodType`](#enum-eventmethodtype)). - `properties` — the listener's configured `properties` map from `video-intelligence.json`, passed through as a raw `HashMap`. There's no schema enforcement at this layer — implementations read and type-convert each expected key themselves. Before calling `onInit`, VIF also writes a set of standard keys into this same map (see below). **Standard properties injected by VIF:** Before `onInit` is called, VIF adds the following keys to the `properties` map, on top of whatever you configured. They're written *after* your configured entries, so these names are reserved — a configured property that reuses one of these names is overwritten by VIF's value. | Key | Value type in map | Description | | --- | --- | --- | | `stream_name` | `String` | Source stream name (`stream.getName()`). | | `width` | `String` | Source video width in pixels (from the stream's codec config). | | `height` | `String` | Source video height in pixels. | | `vif_width` | `String` | Width of the frame VIF sends to the detector — the inference resolution, derived from `inference_video_height` and the source aspect ratio. | | `vif_height` | `String` | Height of the frame VIF sends to the detector. | | `frame_rate` | `String` | Source frame rate in fps (rounded to a whole number). | | `detector_type` | `String` | The detector running on the stream: `object`, `scene`, `vlm`, or `synthetic`. | | `confidence_threshold` | `Double` | This listener's configured `confidence_threshold` (default `0.0`, meaning no filtering) — the per-listener value, not the detector's `object_analysis` / `scene_analysis` threshold. | Every value is inserted as a `String` **except** `confidence_threshold`, which is a `Double`. Because the map is typed ``, read the values defensively: ```java String streamName = (String) properties.get("stream_name"); int srcWidth = Integer.parseInt(properties.get("width").toString()); int srcHeight = Integer.parseInt(properties.get("height").toString()); int vifWidth = Integer.parseInt(properties.get("vif_width").toString()); int vifHeight = Integer.parseInt(properties.get("vif_height").toString()); int frameRate = Integer.parseInt(properties.get("frame_rate").toString()); String detector = (String) properties.get("detector_type"); double confidence = ((Number) properties.get("confidence_threshold")).doubleValue(); ``` **Typical use:** parse `properties` into typed fields, store `appInstance`/`stream` references, and set up any external resources (for example, obtaining a shared singleton like `WebhookListener`). #### `onShutdown` ```java default void onShutdown() ``` Called once, when the listener is detached from its stream or the stream stops. Release anything acquired in `onInit` here — timers, connections, in-progress work tied to this listener instance. #### `immediate` ```java default boolean immediate(DetectionResponse response) ``` Called for each individual detection response, only if `"immediate"` is in this listener's configured `methods`. Lowest-latency delivery path. **Returns:** `true` if the response was consumed/acted on; `false` otherwise. #### `batch` ```java default boolean batch(ArrayList responses) ``` Called periodically with a group of accumulated responses, only if `"batch"` is in this listener's configured `methods`. **Returns:** `true` if the batch was consumed/acted on; `false` otherwise. #### `rollup` ```java default boolean rollup(DetectionResponse response) ``` Called on a rollup interval with a response, only if `"rollup"` is in this listener's configured `methods`. For object detections, the rollup response carries statistical summaries (average, min, max, standard deviation) rather than raw per-frame values — see [`ObjectDetectionRollupData`](#class-objectdetectionrollupdata) below. **Returns:** `true` if the rollup was consumed/acted on; `false` otherwise. ## Enum `EventMethodType` ```java package com.wowza.wms.plugin.videointelligence.event; public enum EventMethodType { DISABLED, IMMEDIATE, BATCH, ROLLUP } ``` The four values a listener's `methods` configuration maps to. ## Class `DetectionResponse` ```java package com.wowza.wms.plugin.videointelligence.message; public class DetectionResponse extends Message { public String createdAt; public DetectionWindowData detectionWindow; public Integer preprocessTimeMs; public Integer inferenceTimeMs; public Integer postprocessTimeMs; public Integer totalProcessingTimeMs; public String detectionsType; public Long sequenceId; public ArrayList getDetections(); public void setDetections(ArrayList detections); public DetectionResponse clone(); } ``` The base class delivered to `immediate`, `batch`, and `rollup`. Fields are **public**, not getter-accessed — there's no `getCreatedAt()`/`getSequenceId()`, you read the fields directly. At runtime, the object passed to your listener is always one of the concrete subclasses below, never a bare `DetectionResponse` — check `detectionsType` or use `instanceof` to determine which. | Field | Type | Description | | --- | --- | --- | | `createdAt` | `String` | Timestamp the response was created. | | `detectionWindow` | `DetectionWindowData` | The frame/time range this response covers — see below. | | `preprocessTimeMs` / `inferenceTimeMs` / `postprocessTimeMs` / `totalProcessingTimeMs` | `Integer` | Per-stage processing time in milliseconds. | | `detectionsType` | `String` | Discriminates which concrete subclass this is. | | `sequenceId` | `Long` | Monotonically increasing sequence number for ordering responses. | ### Subclass `ObjectDetectionResponse` ```java public class ObjectDetectionResponse extends DetectionResponse { public ArrayList detections; } ``` ### Subclass `SceneDetectionResponse` ```java public class SceneDetectionResponse extends DetectionResponse { // detections field is package-private; use getDetections() public ArrayList getDetections(); } ``` ### Subclass `VlmDetectionResponse` ```java public class VlmDetectionResponse extends DetectionResponse { public boolean isStructured; public String modelName; public String content; public Integer tokenCount; public Boolean truncated; public static final String FREEFORM_CLASS_NAME; public void parseContent(); public ArrayList getDetections(); } ``` `isStructured` distinguishes **Custom** mode (a JSON schema response, `isStructured = true`) from **Detect**/**Describe** mode (free-text, `isStructured = false`). `content` holds the raw model output before parsing; `parseContent()` populates `detections` from it. `FREEFORM_CLASS_NAME` is the sentinel class name used for **Describe** mode results, which have no real class name. ## Class `DetectionData` (and subclasses) ```java package com.wowza.wms.plugin.videointelligence.message; public class DetectionData { public String className; public Double confidence; public String reasoning; public String toJsonStr(); } ``` The per-item entries inside `getDetections()`. `reasoning` is populated for VLM results (the model's stated justification for the class) and generally null for object/scene detections. ### Subclass `ObjectDetectionData` ```java public class ObjectDetectionData extends DetectionData { public Integer trackId; public int frameId; public BoundingBoxData bbox; } ``` `trackId` is null for detections without a confirmed tracking ID yet (VIF interleaves these with tracked detections — see the wall-clock-not-response-cadence guidance in the how-to article). ### Subclass `SceneDetectionData` / `VlmDetectionData` Both currently add no additional public fields beyond the `DetectionData` base (`className`, `confidence`, `reasoning`). ## Class `BoundingBoxData` ```java public class BoundingBoxData { public int x; public int y; public int w; public int h; public int getX2(); // x + w public int getY2(); // y + h } ``` Pixel coordinates in the analyzed frame. Not present at all for scene or VLM detections, since those aren't spatially localized. ## Class `DetectionWindowData` ```java public class DetectionWindowData { public Long fromFrameId; public Long toFrameId; public Long frameCount; public String fromFrame; public String toFrame; public Long fromTimeCode; public Long toTimeCode; public Long behindLiveMs; public Boolean caughtUp; public Long skippedFrames; public Long skippedMs; } ``` `behindLiveMs`, `caughtUp`, `skippedFrames`, and `skippedMs` relate to VIF's catch-up-to-live behavior (see the `catch_up_to_live` / `catch_up_max_behind_seconds` config fields) — useful if your listener needs to know whether a given response reflects real-time analysis or catch-up-skipped frames. ## Class `ObjectDetectionRollupData` / `ObjectDetectionRollupValue` Delivered inside `rollup()` responses for object detections — statistical summaries instead of raw per-frame values. ```java public class ObjectDetectionRollupData { public Integer trackId; public String className; public ObjectDetectionRollupValue confidence, x, y, x2, y2, h, w; public Integer firstFrameId; public Integer lastFrameId; public Double duration; public Long samples; } public class ObjectDetectionRollupValue { public Double avg, stdDev, min, max, first, last, sum; public Long sampleSize; } ``` Each spatial/confidence value is summarized (average, min, max, standard deviation, first, last) over the rollup interval rather than delivered as a single point-in-time value. ## See also - [Create a custom VIF event listener](https://www.wowza.com/docs/create-a-video-intelligence-event-listener) - [Access VIF logs in WSE](https://www.wowza.com/docs/access-vif-logs-in-wowza-streaming-engine)