2. Query the capabilities of the device (Optional).
This step may be optional, provided you know that your device can capture video
to memory. Omitting this step may be suitable for prototype development, but
it is a good idea to check the capabilities of the device if your program may
be running on different machines or with different devices.
Communication to the VFL device is performed via calls through ioctl.
Querying the capabilities of the device tells us such things, for example,
as whether this device is a video capture device and what the maximum
and minimum capture dimensions are. The following code will query the
device for its capabilities:
struct video_capability capability; // this structure is filled out by the ioctl call
if (ioctl (deviceHandle, VIDIOCGCAP, &capability) != -1)
{ // query was successful
}
else
{ // query failed
}
The call to ioctl will return a non-zero value if the function was a
success, and it will return -1 if the function failed.
Now our capability structure is filled out. We want to verify that this
device can capture video to memory; we simply mask the type field in
the capability structure with the VID_TYPE_CAPTURE flag:
if ((capability.type & VID_TYPE_CAPTURE) != 0)
{ // this device can capture video to memory
}
else
{ // this device cannot capture video to memory, exit
}
For a list of the fields of the video_capability structure, and the flags
that define the type of device available, view the VFL API documentation
3. Enumerate the video channels (sources of capture) available on the device (Optional).
This step is optional, if you know the numeric value of the channel you want
to open. Again, this may be suitable for prototype development, but if your
program will be running on different machines or with different capture devices,
the numeric value of the channel you want may not remain constant. It may be
a good idea to enumerate the channels and allow the user to select which channel
to use.
To enumerate the channels available on the capture device, you must first query
the capabilities of the capture device. This is because the video_capability
structure contains the field channels that holds the value of the number
of channels available. Assuming you have performed step 2, code to print out
the name and number of each enumerate channel follows:
struct video_channel queryChannel;
i = 0;
while (i < capability.channels)
{
queryChannel.channel = i;
if (ioctl (deviceHandle, VIDIOCGCHAN, &queryChannel) != -1)
{ // ioctl success, queryChannel contains information about this channel
printf ("%d. %s\n", queryChannel.channel, queryChannel.name);
}
else
{ // ioctl failure
}
++ i;
}
10. Setup Memory Mapped Input/Output (MMIO) interface.
This method uses the Memory Mapped Input/Output (MMIO) interface to map
hardware video buffers into our process memory space. From here we can
obtain pointers directly to the captured buffer to perform reading.
The first step is to obtain information from the VFL device needed for MMIO:
struct video_mbuf memoryBuffer;
if (ioctl (deviceHandle, VIDIOCGMBUF, &memoryBuffer) == -1)
{ // failed to retrieve information about capture memory space
}
This structure contains the size in bytes of the memory mapped area, the
number of frames bufferred by the capture device, and an array of offsets
into the memory mapped area for each of the frames.
The next step is to get a pointer to the memory mapped area:
// obtain memory mapped area
char* memoryMap;
memoryMap = (char*)mmap (0, memoryBuffer.size, PROT_READ | PROT_WRITE, MAP_SHARED, deviceHandle, 0);
if ((int)memoryMap == -1)
{ // failed to retrieve pointer to memory mapped area
}
The pointer memoryMap and the offsets within memoryBuffer.offsets
combine to give us the address of each buffered frame. For example:
Buffered Frame 0 is located at: memoryMap + memoryBuffer.offsets[0]
Buffered Frame 1 is located at: memoryMap + memoryBuffer.offsets[1]
Buffered Frame 2 is located at: memoryMap + memoryBuffer.offsets[2]
etc...
The number of buffered frames is stored in memoryBuffer.frames.
Capturing requests require the use of a video_mmap structure for each
buffer. We will need to allocate these structures and fill out their fields:
// allocate structures
struct video_mmap* mmaps;
mmaps = (struct video_mmap*)(malloc (memoryBuffer.frames * sizeof (struct video_mmap)));
// fill out the fields
int i = 0;
while (i < memoryBuffer.frames)
{
mmaps[i].frame = i;
mmaps[i].width = width;
mmaps[i].height = height;
mmaps[i].format = palette;
++ i;
}
The variables width, height, and palette were all
obtained when setting up the capture device.
11. Capturing using MMIO.
The VFL device only begins capturing to a bufferred frame when we ask it to.
Once the device has captured to a buffer, it will not capture to that buffer
again until we ask it to. The strategy for synchronous capture will be to
tell the device that it can begin capturing to each buffer except for the last
buffer. We will track an index that cycles through the buffers till it reaches
the last buffer and then restarts at the first buffer. This index will
initially refer to the last buffer. Each time we request a new frame, we will
ask the device to begin capturing to the buffer we currently index, then we will
move our index to the next buffer, and finally, we will block for the frame at
that index to complete.
The following code will request capturing to each buffer except the last buffer:
int i = 0;
while (i < (memoryBuffer.frames-1))
{
if (ioctl (deviceHandle, VIDIOCMCAPTURE, &mmaps[i]) == -1)
{ // capture request failed
}
++ i;
}
We also need an index to track which buffer we are sending capture requests to:
int bufferIndex;
bufferIndex = memoryBuffer.frames-1;
Now we can write a simple routine that will control capture requests, and will return
the address of the currently available frame:
char* NextFrame()
{
// send a request to begin capturing to the currently indexed buffer
if (ioctl (deviceHandle, VIDIOCMCAPTURE, &mmaps[bufferIndex]) == -1)
{ // capture request failed
}
// move bufferIndex to the next frame
++ bufferIndex;
if (bufferIndex == memoryBuffer.frames)
{ // bufferIndex is indexing beyond the last buffer
// set it to index the first buffer
bufferIndex = 0;
}
// wait for the currently indexed frame to complete capture
if (ioctl (deviceHandle, VIDIOCSYNC, &mmaps[bufferIndex]) == -1)
{ // sync request failed
}
// return the address of the frame data for the current buffer index
return (memoryMap + memoryBuffer.offsets[bufferIndex]);
}
Successive calls to NextFrame will return pointers to successive frames.