Chapter 4. Event Handling
Prev |  | Next

## Chapter 4. Event Handling

The Video Library (VL) provides several ways of handling data stream events, such as completion or failure of data transfer, vertical retrace event, loss of the path to another client, lack of detectable sync, or dropped fields or frames. The method you use depends on the kind of application you're writing:

- For a strictly VL application, use

  - vlSelectEvents() to choose the events to which you want the application to respond

  - vlAddCallback() to specify the function called when the event occurs

  - your own event loop or a main loop (vlMainLoop()) to dispatch the events

- For an application that also accesses another program or device driver, or if you're adding video capability to an existing X or OpenGL application, set up an event loop in the main part of the application and use the IRIX file descriptor (FD) of the event(s) you want to add.

This chapter explains

- Indigo2 IMPACT Video VL events

- querying VL events

- creating a VL event loop

- creating a main loop with callbacks

It concludes with an example illustrating a main loop and event loops.

## Indigo2 IMPACT Video VL Events

This section describes the events that the Indigo2 IMPACT Video device generates. Each event has a standard header, which can be followed by additional data. The additional data can be accessed through the appropriate structure member of the VLEvent union, specified for each of the events listed below.

The VLEvent union and its structures are found in `/usr/include/dmediavl.h`.

The standard header for a VL event contains

- int reason: the event ID, such as VLControlChanged

- VLServer server: the server from which the event originated

- VLDev device: the device from which the event originated

- VLPath path: the path on which the event originated

- uint serial: the serial number of the last request read from the server connection

- uint time: the time at which the event was generated

| Note: Hardware-generated events, such as vertical retrace, are not available on pure video source-to-video drain paths. To receive these events, a path must make use of the screen, blender, framebuffer, or memory nodes. A path receives a VLBadPath error from `vlSelectEvents()` if it attempts to register for events it cannot receive.

Table 4-1 summarizes the VL events for Indigo2 IMPACT Video.

Table 4-1. VL Events for Indigo2 IMPACT Video

Event | Structure | Description
--- | --- | ---
VLStreamPreempted | vlstreampreempted | Generated when a path is preempted by another path that requires some resource that the first path also requires. The paths may be contending over a node (such as a video drain), a part of a node (such as a dual-link input node or one of the single-link nodes that comprise it), or other resource (such as a connector required to route a path). The preempted path is indicated by the path member of the vlstreampreempted structure. Once preempted, the path has a stream usage of VL_READ_ONLY. When the stream becomes available again, the path is downgraded to a control usage of VL_SHARE, unless control usage was at VL_READ_ONLY before the stream was preempted. In this case, the level remains at VL_READ_ONLY. A VLStreamAvailable event is delivered when the path can be set up again to a stream usage of VL_SHARE or VL_LOCK.
VLStreamAvailable | vlstreamavailable | Generated when all nodes required by a path become available for setup with a stream usage of VL_SHARE or VL_LOCK. Typically, such a path becomes available when another path that was using the nodes is set up with stream usage VL_READ_ONLY or VL_DONE_USING, or is destroyed. The path in question is indicated by the path member of the vlstreamavailable structure. VLStreamAvailable is delivered to all registered paths with a stream usage of VL_READ_ONLY. Consequently, a rare condition can occur in which several paths are set up when they receive this event, so that the last path that was set up “wins.”
VLSyncLost | vlsynclost | Generated when a node on a path detects invalid timing. The path on which the timing error occurred is specified by the path member of the vlsynclost structure. Some memory nodes, such as the VGI1 memory nodes, have controls to abort a transfer when they detect invalid timing. In that case, a VLTransferFailed event is generated in its place.
VLSequenceLost | vlsequencelost | Generated when a video unit (field or frame, depending on the capture type) is dropped. The path on which the unit was dropped is specified by the path member of the vlsequencelost structure. If a group of contiguous units is dropped, only one VLSequenceLost event is generated. The client can register for VLTransferComplete events to determine when capture or playback resumes. Note that VLSequenceLost represents a “soft” error and video transfer continues on the path. This event is in contrast to VLTransferFailed, which signals a “hard” error that causes the transfer to abort. The event is delivered as soon as the missed unit is detected. Note that for VGI1 memory nodes; this event may not be generated until a valid unit is transferred.
VLControlChanged | vlcontrolchanged | Generated when a control's value changes. In order for a path to receive this event, it must contain the node on which the control resides. The node is specified in the node member of the vlcontrolchanged structure, and the control's ID is specified by the type member. Use vlGetControl to retrieve the new value of the control. This event is never delivered to the path causing the event, that is, the path on which vlSetControl was called. Note that the vlcontrolchanged structure contains a value member. This member is not currently used and does not contain the new value of the control.
VLTransferComplete | vltransfercomplete | Generated each time a video unit is captured or played back on a path. The video unit is a field or a frame, depending on the capture type. The path on which the event occurred is specified in the path member of the vltransfercomplete structure. This event is generated by paths containing memory nodes only. VLTransferComplete is not sent on “jack-to-jack” paths, for example, a video input to video output path.
VLTransferFailed | vltransferfailed | Generated when a catastrophic error occurs while a path is capturing or playing back a video unit. The memory transfer is halted. The path on which the failure occurred is specified by the path member of the vltransferfailed structure. Note that this event is in contrast to the VLSyncLost or VLSequenceLost events, which are generated when noncatastrophic errors are detected. This event is generated by paths containing memory nodes only. VLTransferFailed is not sent on “jack-to-jack” paths, for example, a video input to video output path.
VLEvenVerticalRetrace | vlevenverticalretrace | Generated at the vertical retrace for each even field in the video stream. The path on which the event occurred is specified by the path member of the vlevenverticalretrace structure. A path must contain a memory, screen, or blender node to receive VLEvenVerticalRetrace events.
VLOddVerticalRetrace | vloddverticalretrace | Generated at the vertical retrace for each odd field in the video stream. The path on which the event occurred is specified by the path member of the vloddverticalretrace structure. A path must contain a memory, screen, or blender node to receive VLOddVerticalRetrace events.
VLFrameVerticalRetrace | vlframeverticalretrace | Generated at the vertical retrace for each frame. The path to which the event is delivered is specified by the path member of the vlframeverticalretrace structure. A path must contain a memory, screen, or blender node to receive VLFrameVerticalRetrace events.
VLDeviceEvent | vldeviceevent | Generated when the external trigger fires. The event is delivered to all paths registered for it. The path to which an event record is delivered is specified by the path member of the vldeviceevent structure. Trigger polarity, trigger line, and other parameters controlling the trigger are specified by controls on the device node.
VLDefaultSource | vldefaultsource | Generated when a `vlSetControl()` on the VL_DEFAULT_SOURCE control changes the default video source. The new source is specified by the node member of the vldefaultsource structure. In order for a path to receive this event, it must contain the new default source node.
VLControlRangeChanged | vlcontrolrangechanged | Generated when the range for a control changes. In order for a path to receive this event, it must contain the node on which the control resides. The node is specified in the node member of the vlcontrolrangechanged structure, and the control's ID is specified by the type member.
VLControlPreempted | vlcontrolpreempted | Delivered to a path that has acquired a node with VL_SHARE control usage (the preempted path) when a path with VL_LOCK control usage (the preempting path) is set up. The preempted path retains VL_SHARE control usage, but is prevented from changing any controls while the preempting path is set up with control usage VL_LOCK. A VLControlAvailable event is sent when the controls are unlocked. The node whose controls have been locked is specified by the node member of the vlcontrolpreempted structure. The path containing the node is identified by the path member.
VLControlAvailable | vlcontrolavailable | Delivered to a path whose controls were previously preempted (see VLControlPreempted), when controls are unlocked, that is, when the control usage of the locking path is dropped to VL_SHARE, VL_READ_ONLY, or VL_DONE_USING. The node whose controls have been unlocked is specified by the node member of the vlcontrolavailable structure. The path containing the node is identified by the path member.
VLDefaultDrain | vldefaultdrain | Generated when a `vlSetControl()` changes the default video drain to VL_DEFAULT_DRAIN control. The new drain is specified by the node member of the vldefaultdrain structure. In order to receive this event, the path must contain the new default drain node.
VLStreamChanged | vlstreamchanged | Generated when the connectivity of the device is changed by `vlSetConnection()`, or by connections generated by the Indigo2 IMPACT Video device on a path's behalf. This event is sent to all paths containing the drain node whose input has changed. Paths containing only the source node do not receive this event, nor does the path causing the connectivity change. The affected path is specified by the path member of the vlstreamchanged structure. The affected drain (node, port) pair are specified by the drnnode and drnport members. The new source (node, port) pair is specified by the srcnode and srcport members. If the source or drain (node, port) pair cannot be represented on the path because it does not contain the node in question, then the (node, port) pair has the value (VLUnknownNode, VLUnknownPort).

## Querying VL Events

General VL event handling routines are summarized in Table 4-2.

Table 4-2. VL Event Handling Routines

Routine | Use
--- | ---
vlGetFD() | Retrieves a file descriptor for a VL server
vlNextEvent() | Obtains the next event; blocks until the next event from the queue is obtained
vlCheckEvent() | Like a nonblocking vlNextEvent(), checks to see if you have an event waiting of the type you specify and reads it off the queue without blocking
vlPeekEvent() | Copies the next event from the queue but, unlike vlNextEvent(), does not update the queue, so that you can see the event without processing it
vlSelectEvents() | Selects video events of interest
vlPending() | Queries whether there is an event waiting for the application
vlEventToName() | Retrieves the character string with the name of the event; for example, to use in messages
vlAddCallback() | Adds a callback; use for VL events
vlRemoveCallback() | Removes a callback for the events specified if the client data matches that supplied when adding the callback
vlRemoveAllCallbacks() | Removes all callbacks for the specified path and events
vlCallCallbacks() | Creates a handler; used when creating a main loop or using a supplied, non-VL main loop
vlRegisterHandler() | Registers an event handler; use for non-VL events
vlRemoveHandler() | Removes an event handler

The event type is an integer. vlEventToName() allows you to get the character string with the name of the event, so that you can use the event name, for example, in messages.

Table 4-3 summarizes VL event masks.

Table 4-3. VL Event Masks

Symbol | Meaning
--- | ---
VLStreamBusyMask | Stream is locked
VLStreamPreemptedMask | Stream was grabbed by another application
VLStreamChangedMask | Stream topology has changed
VLAdvanceMissedMask | Time was already reached
VLSyncLostMask | Irregular or interrupted signal
VLSequenceLostMask | Field or frame dropped
VLControlChangedMask | A control has changed
VLControlRangeChangedMask | A control range has changed
VLControlPreemptedMask | Control of a node has been preempted, typically by another user setting VL_LOCK on a path that was previously set with VL_SHARE
VLControlAvailableMask | Access is now available
VLTransferCompleteMask | Transfer of field or frame complete
VLTransferFailedMask | Error; transfer terminated; perform cleanup at this point, including vlEndTransfer()
VLEvenVerticalRetraceMask | Vertical retrace event, even field
VLOddVerticalRetraceMask | Vertical retrace event, odd field
VLFrameVerticalRetraceMask | Frame vertical retrace event
VLDeviceEventMask | Device-specific event, such as a timing change on a node
VLDefaultSourceMask | Default source changed

Call vlGetFD() to get a file descriptor usable from `select`(2) or `poll`(2).

Call vlSelectEvents() to express interest in one or more event. For example:

    vlSelectEvents(svr, path, VLTransferCompleteMask);

The VLEvent structure returned by vlNextEvent or vlCheckEvent identifies the type of event that occurred and provides additional information on the event; for example, the VLControlChanged event, accompanied by the node on which the control resides and by the new value of the control. These additional pieces of information can be obtained through the members of the VLEvent union corresponding to each event.

Event masks can be Or'ed together. For example:

    vlSelectEvents(svr, path, VLTransferCompleteMask |
                   VLTransferFailedMask);

Depending on whether you want to block processing or not, use vlNextEvent() (blocking) or vlCheckEvent() (nonblocking) to get the next event.

Use vlPeekEvent() to see what the next event in the queue is without removing it from the queue. For example, the part of the code that actually gets the event from the event loop uses vlNextEvent(), whereas another part of the code that just wants to know about it, for example, for priority purposes, uses vlPeekEvent().

## Creating a VL Event Loop

You can set an event loop to run until a specific condition is fulfilled. The routine vlSelectEvents() allows you to specify which event the application will receive.

Using an event loop requires creating an event mask to specify the events you want. The VL event mask symbols are combined with the bitwise OR operator. For example, to set an event mask to express interest in either transfer complete or control changed events, use

    VLTransferCompleteMask | VLControlChangedMask

To create an event loop, follow these steps:

1. Define the event; for example:

       VLEvent ev;

2. Set the event mask; for example:

       vlSelectEvents(vlServer, path, VLTransferCompleteMask | VLControlChangedMask)

3. Block on the transfer process until at least one event is waiting:

       for(;;){
       vlNextEvent(vlServer, &ev);

4. Create the loop and define the choices; for example:

       switch(ev.reason){
               case VLTransferComplete:
               …
               break;
           case VLControlChanged:
               …
               break;
           }
       }

## Creating a Main Loop With Callbacks

vlMainLoop() is provided as a convenience routine and constitutes the main loop of VL applications. This routine first reads the next incoming video event; it then dispatches the event to the appropriate registered procedure. Note that the application does not return from this call.

Applications are expected to exit in response to some user action. There is nothing special about vlMainLoop(); it is simply an infinite loop that calls the next event and then dispatches it. An application can provide its own version of this loop, for example, to test a global termination flag or to test that the number of top-level widgets is larger than zero before circling back to the call to the next event.

To specify callbacks, that is, routines that are called when a particular VL event arrives, use vlAddCallback(). Its function prototype is

    int vlAddCallback(VLServer vlServer, VLEvent * event,
         void * clientdata, VLEventMask events,
         VLCallbackProc callback, void *clientData)

Example 4-1 illustrates the use of vlAddCallback().

Example 4-1. Using VL Callbacks

    main()
    {
      …
          /* Set up the mask for control changed events and Stream preempted events */
       if (vlSelectEvents(vlSvr, vlPath, VLTransferComplete | VLStreamPreemptedMask))
             doErrorExit(“select events”);

       /* Set ProcessEvent() as the callback for VL events */
       vlAddCallback(vlSvr, vlPath, VLTransferCompleteMask | VLStreamPreemptedMask,
                     ProcessEvent, NULL);

       /* Start the data transfer immediately (i.e. don't wait for trigger) */
       if (vlBeginTransfer(vlSvr, vlPath, 0, NULL))
            doErrorExit(“begin transfer”);

       /* Get and dispatch events */
       vlMainLoop();
    }

    /* Handle VL events */
    void
    ProcessEvent(VLServer svr, VLEvent *ev, void *data)
    {
       switch (ev->reason)
       {
          case VLTransferComplete:
           /* Get the valid video data from that frame */
               dataPtr = vlGetActiveRegion(vlSvr, transferBuf, info);
           /* Done with that frame, free the memory used by it */
                 vlPutFree(vlSvr, transferBuf);
                 frameCount++;
        break;

        case VLStreamPreempted:
            fprintf(stderr, “%s: Stream was preempted by another Program\n”,
            _progname);
            docleanup(1);
        break;

        default:
        break;
       }
    }

Delete a callback with vlRemoveCallback() or vlRemoveAllCallbacks(). Their function prototypes are

    int vlRemoveCallback(VLServer vlServer, VLPath * path,
           VLEventMask events, VLCallbackProc callback, void
           *clientData)
    int vlRemoveAllCallbacks(VLServer vlServer, VLPath * path, VLEventMask events)

The functions vlAddHandler() and vlRemoveHandler() are analogous to vlAddCallback() and vlRemoveCallback(), respectively. Use them for non-VL events.

In `/usr/people/4Dgifts/examples/dmedia/video/vl`, the example program `eventex.c` illustrates how to create a main loop and event loops.

| Caution: To simplify the code, this example does not check returns. You should, however, always check returns.

Prev | Table of Contents | Next
Chapter 3. Using VL Controls |  | Chapter 5. Managing Connections