Chapter 4. Audio/Visual Paths

In the dmSDK, the logical connections between jacks and memory are called paths. For example, a video output path provides the means to transfer video information from buffers in memory, through a video output jack.

Opening a Logical Path

Before you send messages to a device, you must open a processing path that goes through it. This is done by calling dmOpen(3dm) as follows:

DMstatus dmOpen (DMint64 pathId, DMpv* options,
                     DMopenid* openid);

Think of a path as a logical device - a physical device (for example, a PCI card) may simultaneously support several such paths. A side effect of opening a path is that space is allocated for queues of messages from your application to the device and replies from the device back to your application. All of the messages sent to a queue share a common payload area and are required to observe a strictly ordered relationship. That is, if message A is sent before message B, then the reply to A must arrive before the reply to B.

Constructing a Message

Messages are arrays of parameters, where the last parameter is always DM_END. For example, set the image width to be 720 and the image height to be 480 as follows:

DMpv message[3];
message[0].param = DM_IMAGE_WIDTH_INT32;
message[0].value.int32 = 720;
message[1].param = DM_IMAGE_HEIGHT_INT32;
message[1].value.int32 = 480;
message[2].param = DM_END

Processing Out-of-Band Messages

In some cases, an application wishes to influence a device without first waiting for all previously enqueued messages to be processed. Borrowing a term from UNIX communications, we term these cases out-of-band messages. They are performed with the dmSetControls(3dm) or dmGetControls(3dm) calls.

Here is an example of how you can immediately get the width and height of an image:

DMpv message[3];
message[0].param = DM_IMAGE_WIDTH_INT32;
message[1].param = DM_IMAGE_HEIGHT_INT32;
message[2].param = DM_END
if( dmGetControls( somePath, message))
   handleError();
else
  printf("Image size is %d x %d\n",
          message[0].value.int32,
          message[1].value.int32);

Out-of-band messages work well for simple sets and queries. They are blocking calls. If the call succeeds, the message has been successfully processed.

Sending In-Band Messages

Out-of-band messages are appropriate for simple control changes, but they provide no buffering between your application and the device. For most applications, processing real-time data will require using a queuing communication model. The dmSDK supports this with the dmSendControls(3dm), dmSendBuffers(3dm), and dmReceiveMessage (3dm) calls.

For example, to send a controls message to a device input queue use:

DMstatus dmSendControls( DMopenid openId, DMpv* message);

Devices interpret messages in the order in which they are enqueued. Because of this, the time relationship is explicit between, for example, video buffers and changes in video modes.


Note: The send call does not wait for a device to process the message. Rather, it copies it to the device input queue and then returns.


When your application sends a message, it is copied into the send queue. The message is then split between a small fixed header on the input list, and a larger, variable-sized space in the data area.

Sometimes, there is not enough space in the data area and/or send list for new messages. In that case the return code indicates that the message was not enqueued. As a rule, a full input queue is not a problem -- it simply indicates that the application is generating messages faster than the device can process them.

For some devices, the system may use device-specific knowledge to best manage messaging transactions. For example, when you call sendBuffers the system may copy the message exactly as described above, or it may send part or all of the message directly to the hardware. Regardless of what happens, the system always looks to your application as described here.

Each message you send is guaranteed to result in at least one reply message from the device. This is how you know when your message is interpreted and what is the result.

  • In the case of control parameters you should check the return message to make sure your control executed correctly.

  • In the case of video buffers, you should allocate buffer space in your application and then send an indirect reference to that buffer in a message. Once your application receives a reply message you can be certain the device has completed your request and finished with the memory, so you are free to reuse it.

Some devices can send messages to advise your application of important events (for example some video devices can notify you of every vertical retrace). However, no notification messages will be generated unless and until you explicitly request them.

Processing In-Band Messages

The device processes messages as follows:

Removes the message header from the send queue, processes the message and writes any response into the payload area. It then places a reply header on the receive queue.

In general, your application must allow space in the message for any reply you expect to be returned.


Note: The device performs no memory allocation, but rather uses the memory allocated when the application enqueued the input message. This is important because it guarantees there will never be any need for the device to block because it did not have enough space for the reply.


Processing Exception Events

In some cases an exception event occurs which requires that the device pass a message back to your application. Your application must explicitly ask for such events.

Possible exception events are:

DM_EVENT_DEVICE_ERROR  Device encountered an error and is unable to recover.
DM_EVENT_DEVICE_UNAVAILABLE  The device is not available for use.
DM_EVENT_VIDEO_SEQUENCE_LOST  A video buffer was not available for an I/O transfer.
DM_EVENT_VIDEO_SYNC_LOST  Device lost the output genlock sync signal.
DM_EVENT_VIDEO_SYNC_GAINED  Device detected a valid output genlock.
DM_EVENT_VIDEO_SIGNAL_LOST  Device lost the video input signal.
DM_EVENT_VIDEO_SIGNAL_GAINED Device detected a valid input video signal.
DM_EVENT_VIDEO_VERTICAL_RETRACE  A video vertical retrace occurred.
DM_EVENT_AUDIO_SEQUENCE_LOST  An audio buffer was not available for an I/O transfer.
DM_EVENT_AUDIO_SAMPLE_RATE_CHANGED  The audio input sampling frequency changed.

If you ask for events, your application must read its receive queue frequently enough to prevent the device running out of space for messages which you have asked it to enqueue. If the queue starts to fill up, then the device will enqueue an event message advising that it is stopping notification of exception events.


Note: The device never needs to allocate space in the data area for reply messages. It will automatically stop sending notifications of events if the output list starts to fill up. Space is reserved in the receive queue for a reply to every message your application enqueues; but if there is insufficient space, attempts to send new messages will fail.


Processing In-Band Reply Messages

To receive a reply message from a device use dmReceiveMessage(3dm) as follows:

DMstatus dmReceiveMessage(DMopenid openId,
                          DMint32* messageType,
                          DMpv** reply);

This call returns the earliest unread message sent from the device back to your application. The messageType parameter indicates why this reply was generated. It could come from a call to sendControls, sendBuffers, or it could have been generated spontaneously by the device as the result of an event. The reply pointer is guaranteed to remain valid until you attempt to receive a subsequent message. This allows a small optimization -- you can read the current message "in place" without needing to first copy it off the queue. It is acceptable to overwrite a value in a reply message and then send that as a new message.

Beginning and Ending Transfers

Devices do not begin to process enqueued messages until explicitly instructed to by an application.

This is done with the dmBeginTransfer(3dm) call:

DMstatus dmBeginTransfer( DMopenid openId);

This call frees the device to begin processing enqueued messages. It also commands the device to begin generating exception events. Typically, an application will open a device, enqueue several buffers (priming the input queue) and then call BeginTransfer. In this way, it avoids the underflow which could otherwise occur if the application were swapped out immediately after enqueueing the first buffer to the device.

To stop a transfer, call dmEndTransfer(3dm):

DMstatus dmEndTransfer( dmopenid openId);

This causes the device to do the following:

  • Stop processing messages containing buffers

  • Flush its input queue

  • Stop notification of exception events

Closing a Logical Path

When your application has finished using an open path, it may close it (see dmClose(3dm)):

 DMstatus dmClose( DMopenId openId);

This causes an implicit EndTransfer on any device with an active transfer. It then frees any resources used by the device. If you wish to have pending messages processed prior to closing a device, you must identify a message (perhaps by adding a piece of user data or by remembering its MSC number) and make sure it is the last thing you enqueue. When it appears on the output queue, you will know all messages have been processed. At that point you can close the device.