Chapter 7. 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:

This chapter explains

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

OCTANE Personal Video VL Events

This section describes the events that the OCTANE Personal 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/dmedia/vl.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 screen or memory nodes or the framebuffer. A path receives a VLBadPath error from vlSelectEvents() if it attempts to register for events it cannot receive.

Table 7-1 summarizes the VL events for the OCTANE Personal Video device.

Table 7-1. VL Events for the OCTANE Personal Video Device

Event

Structure

Description

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.”

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) 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.

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 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 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 or screen 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 or screen 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 or screen 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.


Querying VL Events

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

Table 7-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 2-1 in Chapter 2 summarizes VL event masks.

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 7-1 illustrates the use of vlAddCallback().

Example 7-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/share/src/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.