Basic Uncompressed Video I/O Code

By Chris Pirazzi.

Discussions about uncompressed video disk I/O are often complicated by the needless complexity and awkwardness of VL.

The videoport API

Since this case study is more about how video fits in with disk I/O than how to program VL, we will provide you with a much simpler "videoport" API, which lets you do the following with uncompressed, VL_PACKING_YVYU_422_8 fields: We implemented the videoport API on top of VL using only the classic buffering API (see What Are the SGI Video-Related Libraries? for info about buffering APIs), so it should run on any VL platform back to IRIX 6.2. If you hack out the UST/MSC part, videoport may even work on IRIX 5.2 VL platforms.

You need the last two features above for asynchronous disk I/O. Implementing them on top of the classic buffering API is quite complicated, as you will see if you look at videoport.c below. If your application runs only on platforms supporting DMbuffers, then we recommend that you re-implement videoport.c using DMbuffers, or just forget videoport.c. The code will likely be one tenth the size and more maintainable, and you will use memory more efficiently.

The last feature above deserves some clarification. The videoport API is not as powerful as the DMbuffer buffering APIs, because videoport is just a wrapper around the VLBuffer ringbuffer of the classic VL API. When doing mem to vid (output), you allocate fields in the order in which you want them to go out the output jack. As you finish filling in the pixels for each field, you commit that field to the videoport API. You may commit any field which you have allocated. You don't have to commit the oldest allocated field as you do in the classic buffering API. But, unlike the DMbuffer-based buffering APIs, the transmission order of a field is decided when you allocate that field, and you cannot send the same field more than once.

The videoport API has 9 calls:

The buffering APIs (classic, O2, cross-platform) are described in What Are the SGI Video-Related Libraries?.

Here's the boilerplate program that uses the videoport API. It does vid to mem if run with an argument of "v", otherwise it does mem to vid.

/*
   nop.c
*/

#include "videoport.h"

int main(int argc, char **argv)
{
  videoport p;
  int direction = (tolower(argv[1][1]) == 'v') ?
    VIDEOPORT_DIRECTION_VM : VIDEOPORT_DIRECTION_MV;
  
  NC(p = videoport_open(direction, 20, 0));
  
  for(;;)
    {
      videofield *f;
      videoport_wait_for_space_or_data(p);
      f = videoport_get_one_field(p);
      assert(f);

      /* do something with data/space! */

      videoport_put_one_field(p, f);
    }
}
NC() is from a set of cheesy error-handling macros that we use in the sample code in this section to increase readability. The macros print an error and exit if their argument does not evaluate to the correct value. See videoport.h for the complete list of these macros. Obviously, you will want to substitute your own handling.

Here is the API and its source code:

Consult these files for details on the implementation of videoport.

You can use unvideo.c to test the maximum disk throughput which a particular method of disk I/O can achieve.

How Much Memory Buffering Do I Need?

The videport_open() call expects a buf_fields argument saying how much memory to allocate for video buffering. You need to choose a value of buf_fields which is large enough so that your video buffer will never overflow (vid to mem) or underflow (mem to vid).

First ask yourself: "any any given time while my app is running, what is the maximum number of fields of I/O that has been issued to my disks but has not yet completed?" Call this number P, for pending. If your app writes one field at a time, P=1. If your app writes N fields at a time (whether via readv()/writev() or asynchronous I/O, both of which are defined in Software Methods for Disk I/O), P=N.

For any chosen value of P, you will probably want to allocate a VL buffer that can hold at least 2*P fields. You would do this on the assumption that your disk system is just fast enough to keep up with video rate, meaning that it takes the disk P items worth of time to transfer P items. Under this assumption, you want the VL buffer to have enough space for another P items to arrive in the VL buffer (video to memory) or drain out of the VL buffer (memory to video) during each disk I/O, hence 2*P.

Practical applications cannot respond instantaneously to the completion of one I/O by issuing the next I/O. They actually have to assume that the disk can transfer data a little bit faster than video rate, and that there exists some upper bound number of fields F (for fudge) which will be sufficient to ride over any inadequacy in the application's reaction time. F is the process scheduling latency described in Seizing Higher Scheduling Priority. In this case, the application allocates a VL buffer that can hold 2*P + F items. It is not hard in practice to come up with a value of F that works enough of the time for your requirements, but it is too bad that we still have to deal unscientifically at this level.