Chapter 5. Transferring Video Data and Ending Data Transfer

This chapter explains how to use buffers for data transfer, set execution times, and end data transfer, in these sections:

Transferring Video Data to and From Devices

This section explains

Using Buffers

The VL supports two buffering mechanisms for capturing or playing back video:

  • VL buffers: the original buffering mechanism supported by the VL and specific to it

  • Digital Media Buffers (DMbuffers): a buffering mechanism allowing video data to be exchanged among video, compression, and graphics devices

    For OCTANE, this buffering mechanism is supported by the Video, Image Converter (dmIC), and Movie Libraries. It is available with IRIX 6.4 and subsequent releases.


Note: For complete information on DMbuffers and digital media image converters, see the Digital Media Programming Guide.

In general, VL buffers and DMbuffers differ in the following ways:

  • buffer structure

    VL buffers are modeled after a ring buffer. The order of segments (buffers) in the ring is inflexible, and care must be taken to ensure that items are obtained and returned in the same order. For example, buffers obtained with vlGetNextValid() must be returned using vlPutFree() in the same order. Order and allocation of ring segments are intricately related.

    All operations on a VL buffer operate in FIFO order. That is, the first element retrieved by vlGetNextValid() is the first returned by vlPutFree(). This function does not take an element as a parameter and always puts back the oldest outstanding element.

    DMbuffers, in contrast, are contained in a DMbufferpool. The pool itself is unordered; buffers can be obtained from and returned to the pool in any order. Ordering is achieved by a first-in-first-out queue, and is maintained only while the buffers are in the queue. The application or library is free to impose any processing order on buffers, once they have been dequeued.

  • buffer size and alignment

    The Video Library is responsible for ensuring that VL buffers are of the appropriate size and alignment for the video device, and for allocating the buffers in the vlCreateBuffer() call. Except in rare cases, applications cannot modify these attributes to suit the needs of another library or device.

    Because DMbuffers can be used with libraries and devices besides video, the application queries each library for its buffering requirements. The exact DMbufferpool requirements are the union of all requested constraints and are enforced when the pool is created. For example, if one library requests alignment on 4K boundaries and another requests alignment on 16K boundaries, the 16K alignment is used. By specifying its own pool requirements list, the application can set minimum buffer sizes (such as for in-place processing of video) or cache policies.

  • buffers and memory nodes

    With VL buffers, a particular ring buffer is strictly tied to a particular memory node; a DMbufferpool is not necessarily tied to a memory node. A memory source node can receive DMbuffers allocated from any DMbufferpool that meets the memory node's pool requirements. Memory drain nodes obtain DMbuffers from a DMbufferpool specified by the application; this pool is fixed for the duration of a transfer.

Each buffering mechanism has a set of API functions for creating, registering, and manipulating buffers. A mismatch between a buffer mechanism and an API call, for example, applying a VL buffer call to a DMbuffer, results in a VLAPIConflict error return.

Applications can use either VL buffers or DMbuffers, as long as a memory node is used with only one buffering mechanism at a time. If an application uses multiple memory paths, each path can use a different buffering mechanism. To switch buffering mechanisms, the VL path should be torn down and reconstructed.

Table 5-1 shows correspondences between VL buffer and DMbuffer API functions.

Table 5-1. VL Buffer and DMBuffer API Functions

VL Buffer API

dmBuffer API

vlCreateBuffer()

dmBufferCreatePool()

vlPutValid()

vlDMBufferPutValid()

vlRegisterBuffer()

vlDMBufferPoolRegister()

vlDeregisterBuffer()

No equivalent

vlPutFree()

dmBufferFree()

vlGetNextValid()

vlDMBufferGetValid()

vlGetLatestValid()

No equivalent

vlGetFilled()

vlGetFilledByNode()

vlDestroyBuffer()

dmBufferDestroyPool()

vlBufferGetFd()

dmBufferGetPoolFD()

dmBufferSetPoolSelectSize()

vlNodeGetFd()

vlBufferAdvise()

dmSetPoolDefaults()

vlBufferReset()

vlDMBufferNodeReset()

vlBufferDone()

Not applicable


Transferring Video Data Using DMbuffers

The DMbuffer is created through the dmBufferCreatePool() routine and is associated with a memory node by the dmPoolRegister() routine.

When the OCTANE Personal Video option transfers data from the Video Library to an application, it places data in a buffer element and marks the element as valid. The application can retrieve the element through the vlDMBufferGetValid() routine. When the application is done, it uses the dmBufferFree() routine to alert the video device that the buffer element can be reused. For complete details on using DMbuffers, see Chapter 5 of the Digital Media Programming Guide (007-1799-060 or later).

This section explains

Obtaining DMbufferpool Requirements

Before a DMbufferpool is created, you must obtain the pool requirements of any library that will interact with the pool. Pool requirements are maintained in a DMparams list, created using dmParamsCreate() and initialized by calling dmBufferSetPoolDefaults(). See Chapter 3 in the Digital Media Programming Guide for an overview of DMparams. The function prototype for this call is

DMstatus dmBufferSetPoolDefaults(DMparams *poolParams, int bufferCount, int bufferSize, DMboolean cacheable, DMboolean mapped)

where

poolParams 

specifies the DMparams list to use for gathering pool requirements

bufferCount 

specifies the number of buffers the pool should contain

bufferSize 

specifies the size of each buffer in the pool

cacheable 

specifies whether buffers allocated from the pool can be cached (DM_TRUE) or not (DM_FALSE).

For more information on caching, see “Caching” in Chapter 8.

mapped 

specifies whether the memory allocated for the pool should be mapped as soon as the pool is created (TRUE), or only when dmBufferMapData() is called (FALSE)

If an application requires a pointer to buffer contents, for example, to process or store the contents to disk, then the pool should be created mapped. This option improves the performance of the dmBufferMapData() call.

The Video Library pool requirements are obtained by calling vlDMBufferGetParams() on a memory node:

int vlDMBufferGetParams(VLServer svr, VLPath path, VLNode node, DMparams *params)

where

svr 

names the server to which the path is connected

path 

specifies the data path containing the memory node

node 

specifies the memory node with which the DMbufferpool will be used

params 

specifies the pool requirements list

As with similar calls in other libraries, vlDMBufferGetParams takes as input a DMparams list initialized by dmBufferSetPoolDefaults, and possibly other libraries' pool requirements functions. On output, the Video Library's requirements are merged with the input requirements.

Creating a DMbufferpool

After all libraries that will use the pool have been queried for their requirements, the application can create a DMbufferpool by calling dmBufferCreatePool. Its function prototype is

DMstatus dmBufferCreatePool(const DMparams *poolParams, DMbufferpool *returnPool)

where

poolParams 

specifies the requirements for the pool

returnPool 

points to a location where the DMbufferpool handle will be stored

Registering a DMBufferpool With the Video Library

If the application captures video data, it specifies the DMbufferpool the memory node should use by calling vlDMBufferPoolRegister:

int vlDMBufferPoolRegister(VLServer svr, VLPath path, VLNode node, DMbufferpool pool)

where

svr 

specifies the server that the path is attached to

path 

specifies the path containing the memory node

node 

specifies the memory node

pool 

specifies the pool that the memory node should use

When the video device is ready to capture a new frame or field, it will allocate a DMbuffer from the specified pool, place the field or frame in it, then send the buffer to the application.

Starting Data Transfer

To begin data transfer (for either type of buffer), use vlBeginTransfer(). Its function prototype is

int vlBeginTransfer(VLServer vlSvr, VLPath path, int count,
      VLTransferDescriptor* xferDesc) 

where

vlSvr 

names the server to which the path is connected

path 

specifies the data path

count 

specifies the number of transfer descriptors

xferDesc 

specifies an array of transfer descriptors

Tailor the data transfer by means of transfer descriptors. Multiple transfer descriptors are supplied; they are executed in order. The transfer descriptors are

xferDesc.mode 

Transfer method:

  • VL_TRANSFER_MODE_DISCRETE: a specified number of frames are transferred (burst mode)

  • VL_TRANSFER_MODE_CONTINUOUS (default): frames are transferred continuously, beginning immediately or after a trigger event occurs (such as a frame coincidence pulse), and continues until transfer is terminated with vlEndTransfer()

xferDesc.count 

Number of frames to transfer; if mode is VL_TRANSFER_MODE_CONTINUOUS, this value is ignored.

xferDesc.delay 

Number of frames from the trigger at which data transfer begins.

xferDesc.trigger 

Set of events to trigger on; an event mask. This transfer descriptor is always required. VLTriggerImmediate specifies that transfer begins immediately, with no pause for a trigger event. VLDeviceEvent specifies an external trigger.

If xferDesc is NULL, then VL_TRIGGER_IMMEDIATE and VL_TRANSFER_CONTINUOUS_MODE are assumed and one transfer is performed.

This example fragment transfers the entire contents of the buffer immediately.

xferDesc.mode = VL_TRANSFER_MODE_DISCRETE;

xferDesc.count = imageCount;
xferDesc.delay = 0;
xferDesc.trigger = VLTriggerImmediate;

This fragment shows the default descriptor, which is the same as passing in a null for the descriptor pointer. Transfer begins immediately; count is ignored.

xferDesc.mode = VL_TRANSFER_MODE_CONTINUOUS;

xferDesc.count = 0;
xferDesc.delay = 0;
xferDesc.trigger = VLTriggerImmediate;

Receiving Buffers From the Video Library

After the transfer has been started, captured video may be retrieved using vlDMBufferGetValid:

int vlDMBufferGetValid(VLServer svr, VLPath path, VLNode node, DMbuffer* dmbuffer)

where

svr 

specifies the server the path is attached to

path 

specifies the path on which data is received from

node 

specifies the memory drain node data is received from

dmbuffer 

points to a location where a DMbuffer handle is stored

The DMbuffer handle returned by vlDMBufferGetValid is an opaque reference to the captured video. dmBufferMapData can be used to obtain a pointer to the actual image data so that it can be processed or written to disk. dmBufferMapData does not have to be called if the buffer will be directly sent to another device or library.

Sending DMbuffers to the Video Library

Applications can use vlDMBufferPutValid to send buffers to a video device:

int vlDMBufferPutValid(VLServer svr, VLPath path, VLNode node, DMbuffer dmbuffer)

where

svr 

specifies the server to which the path is attached

path 

specifies the path on which video is sent

node 

specifies the memory source node to send the buffer to

dmbuffer 

specifies the buffer to send

The DMbuffer may have been obtained from another library, such as dmIC, or generated by the application itself. See Chapter 5 in the Digital Media Programming Guide for an explanation of how to allocate a DMbuffer from a DMbufferpool.

Freeing a DMbuffer

Once the application is done with a buffer, it should call dmBufferFree to indicate that it no longer intends to use the buffer. After all users of a buffer have called dmBufferFree on it, the buffer is considered free to be reallocated. The Video Library never implicitly releases the application's access to a buffer. Consequently, an application can send the same buffer to a memory node multiple times, or hold a captured image for an indefinite period.

Transferring Video Data Using VL Buffers

The processes for data transfer using VL buffers are as follows:

Each process is explained separately.

Creating a Buffer for Video Data

Once you have specified frame parameters in a transfer involving memory (or have determined to use the defaults), create a VL buffer for the video data. In this case, video data is frames or fields, depending on the capture type:

  • frames if the capture type is VL_CAPTURE_INTERLEAVED

  • fields if the capture type is anything else

VL buffers provide a way to read and write varying sizes of video data. A frame of data consists of the actual frame data and an information structure describing the underlying data, including device-specific information.

When a VL buffer is created, constraints are specified that control the total size of the data segment and the number of frame or field buffers (sectors) to allocate. A head and a tail flag are automatically set in a VL buffer so that the latest frame can be accessed. A sector is locked down if it is not called; that is, it remains locked until it is read. When the VL buffer is written to and all sectors are occupied, data transfer stops. The sector last written to remains locked down until it is released.

All sectors in a VL buffer must be of the same size, which is the value returned by vlGetTransferSize(). Its function prototype is

long vlGetTransferSize(VLServer vlSvr, VLPath path)

For example:

transfersize = vlGetTransferSize(vlSvr, path); 

where transfersize is the size of the data in bytes.

To create a VL buffer for the frame data, use vlCreateBuffer(). Its function prototype is

VLBuffer vlCreateBuffer(VLServer vlSvr, VLPath path, VLNode node, 
     int numFrames)

where

VLBuffer 

is the handle of the buffer to be created

vlSvr 

names the server to which the path is connected

path 

specifies the data path

node 

specifies the memory node containing data to transfer to or from the VL buffer

numFrames 

specifies the number of sectors in the buffer (fields or frames, depending on the capture type)

For example:

buf = vlCreateBuffer(vlSvr, path, src, 1); 

Table 5-2 shows the relationship between capture type and minimum VL buffer size.

Table 5-2. Buffer Size Requirements

Capture Type

Minimum Sectors for Capture

Minimum Sectors for Playback

VL_CAPTURE_NONINTERLEAVED

2

4

VL_CAPTURE_INTERLEAVED

1

2

VL_CAPTURE_EVEN_FIELDS

1

2

VL_CAPTURE_ODD_FIELDS

1

2

VL_CAPTURE_FIELDS

1

2



Note: For memory nodes, real-time memory or video transfer can be performed only as long as buffer sectors are available to the OCTANE Personal Video device.


Registering the VL Buffer

Use vlRegisterBuffer() to register the VL buffer with the data path. Its function prototype is

int vlRegisterBuffer(VLServer vlSvr, VLPath path,
     VLNode memnodeid, VLBuffer buffer)

where

vlSvr 

names the server to which the path is connected

path 

specifies the data path

memnodeid 

specifies the memory node ID

buffer 

specifies the VL buffer handle

For example:

vlRegisterBuffer(vlSvr, path, drn, Buffer);

Starting Data Transfer

Start data transfer the same way as for DMbuffers; see “Starting Data Transfer” in “Transferring Video Data Using DMbuffers.”

Reading Data From the VL Buffer

If your application uses a VL buffer, use various VL calls for reading frames, getting pointers to active buffers, freeing buffers, and other operations. Table 5-3 lists the buffer-related calls.

Table 5-3. Buffer-Related Calls

Call

Purpose

vlGetNextValid()

Returns a handle on the next valid frame or field of data

vlGetLatestValid()

Reads only the most current frame or field in the buffer, discarding the rest

vlPutValid()

Puts a frame or field into the valid list (memory to video)

vlPutFree()

Puts a valid frame or field back into the free list (video to memory)

vlGetNextFree()

Gets a free buffer into which to write data (memory to video)

vlBufferDone()

Informs you if the buffer has been vacated

vlBufferReset()

Resets the buffer so that it can be used again

Figure 5-1 illustrates the difference between vlGetNextValid() and vlGetLatestValid().

Figure 5-1. vlGetNextValid() and vlGetLatestValid()

Figure 5-1 vlGetNextValid() and vlGetLatestValid()

Table 5-4 lists the calls that extract information from a buffer.

Table 5-4. Calls for Extracting Data From a Buffer

Call

Purpose

vlGetActiveRegion()

Gets a pointer to the data region of the buffer (video to memory); called after vlGetNextValid() and vlGetLatestValid()

vlGetDMediaInfo()

Gets a pointer to the DMediaInfo structure associated with a frame; this structure contains timestamp and field count information

vlGetImageInfo()

Gets a pointer to the DMImageInfo structure associated with a frame; this structure contains image size information



Caution: None of these calls has count or block arguments; appropriate calls in the application must deal with a NULL return in cases of no data being returned.

In summary, for video-to-memory transfer, use

buffer = vlCreateBuffer(vlSvr, path, memnode1);
vlRegisterBuffer(vlSvr, path, memnode1, buffer); 
vlBeginTransfer(vlSvr, path, 0, NULL); 
info = vlGetNextValid(vlSvr, buffer);
/* OR vlGetLatestValid(vlSvr, buffer); */
dataptr = vlGetActiveRegion(vlSvr, buffer, info); 

/* use data for application */
…
vlPutFree(vlSvr, buffer); 

For memory-to-video transfer, use

buffer = vlCreateBuffer(vlSvr, path, memnode1);
vlRegisterBuffer(vlSvr, path, memnode1, buffer); 
vlBeginTransfer(vlSvr, path, 0, NULL); 
buffer = vlGetNextFree(vlSvr, buffer, bufsize); 
/* fill buffer with data */
…
vlPutValid(vlSvr, buffer); 

To read the frames to memory from the buffer, use vlGetNextValid() to read all the frames in the buffer or get a valid frame of data. Its function prototype is

VLInfoPtr vlGetNextValid(VLServer vlSvr, VLBuffer vlBuffer)

Use vlGetLatestValid() to read only the most current frame in the buffer, discarding the rest. Its function prototype is

VLInfoPtr vlGetLatestValid(VLServer vlSvr, VLBuffer vlBuffer) 

After removing interesting data, return the buffer for use with vlPutFree() (video to memory). Its function prototype is

int vlPutFree(VLServer vlSvr, VLBuffer vlBuffer)

To send frames from memory to video, use vlGetNextFree() to get a free buffer to which to write data. Its function prototype is

VLInfoPtr vlGetNextFree(VLServer vlSvr, VLBuffer vlBuffer,
      int size)

After filling the buffer with the data you want to send to video output, use vlPutValid() to put a frame into the valid list for output to video (memory to video). Its function prototype is

int vlPutValid(VLServer vlSvr, VLBuffer vlBuffer)


Caution: These calls do not have count or block arguments; appropriate calls in the application must deal with a NULL return in cases of no data being returned.

To get DMediaInfo and Image Data from the buffer, use vlGetActiveRegion() to get a pointer to the active buffer. Its function prototype is

void * vlGetActiveRegion(VLServer vlSvr, VLBuffer vlBuffer,
     VLInfoPtr ptr)

Use vlGetDMediaInfo() to get a pointer to the DMediaInfo structure associated with a frame. This structure contains timestamp and field count information. The function prototype for this call is

DMediaInfo * vlGetDMediaInfo(VLServer vlSvr, VLBuffer vlBuffer, 
     VLInfoPtr ptr)

Use vlGetImageInfo() to get a pointer to the DMImageInfo structure associated with a frame. This structure contains image size information. The function prototype for this call is

DMImageInfo * vlGetImageInfo(VLServer vlSvr, VLBuffer vlBuffer, 
     VLInfoPtr ptr)

Ending Data Transfer

To end data transfer for either VL buffers or DMbuffers, use vlEndTransfer(). Its function prototype is

int vlEndTransfer(VLServer vlSvr, VLPath path) 

A discrete transfer is finished when the last frame of the sequence is output. Memory nodes emit black video output after a transfer (discrete or continuous) has been completed.

To accomplish the necessary cleanup to exit gracefully, use the following functions:

  • for transfers involving memory:

    • DMbuffers: vlDMBufferPoolDeregister(), vlDestroyPath(), dmBuffer()

    • VL buffers: vlDeregisterBuffer(), vlDestroyPath(), vlDestroyBuffer()

  • for all transfers: vlCloseVideo()

The function prototype for vlDeregisterBuffer() is

int vlDeregisterBuffer(VLServer vlSvr, VLPath path,
    VLNode memnodeid, VLBuffer ringbufhandle) 

where

vlSvr 

is the server handle

path 

is the path handle

memnodeid 

is the memory node ID

ringbufhandle 

is the VL buffer handle

The function prototypes for vlDestroyPath(), vlDestroyBuffer(), dmBuffer(), and vlCloseVideo() are, respectively

int vlDestroyPath(VLServer vlSvr, VLPath path)
int vlDestroyBuffer(VLServer vlSvr, VLBuffer vlBuffer) 
int vlGetFilledByNode(VLServer vlSvr, VLPath path, VLNode node);
int vlDMBufferNodeReset(VLServer vlSvr, VLPath path, VLNode node);
int vlCloseVideo(VLServer vlSvr)

where vlSvr specifies the server to which the application is attached, and path and node identify the memory node on which information is requested.

This example ends a data transfer that used a buffer:

vlEndTransfer(vlSvr, path);
vlDeregisterBuffer(vlSvr, path, memnodeid, buffer);
vlDestroyPath(vlSvr, path);
vlDestroyBuffer(vlSvr, buffer);
vlCloseVideo(vlSvr);

For DMbuffers, vlDMBufferPoolDeregister disassociates a DMbufferpool from a memory node. It should be called to clean up the memory node or allow a new DMbufferpool to be used after a transfer has been stopped.

Once the application is done with a DMbufferpool, the pool should be destroyed using the dmBufferDestroyPool call.

Example Programs

The directory /usr/share/src/dmedia/video/vl includes a number of example programs. These programs illustrate how to create simple video applications; for example:

  • a simple screen application: simplev2s.c

    This program shows how to send live video to the screen.

  • a video-to-memory frame grab: simplegrab.c

    This program demonstrates video frame grabbing.

  • a memory-to-video frame output simplem2v.c

    This program sends a frame to the video output.

  • a continuous frame capture: simpleccapt.c

    This program demonstrates continuous frame capture.


Note: To simplify the code, these examples do not check returns. However, you should always check returns.

See Chapter 7 for a description of eventex.c.

The directory /usr/share/src/dmedia/video/vl/OpenGL contains three example OpenGL programs:

  • contcapt.c: performs continuous capture using buffering and sproc

  • mtov.c: uses the Silicon Graphics Movie Library to play a movie on the selected video output

  • vidtomem.c: captures an incoming video stream to memory

These programs are the OpenGL equivalents of the programs with the same names in /usr/share/src/dmedia/video/vl.