Modifying a YARP Driver
This page documents the steps taken to make a small change to an existing driver. For background information, or creating a new device, see:
- general approach to writing a device
- how to add a new device to the library
- device invocation examples
This page is just concerned with making a small change to an existing driver.
The change we'd like to make
The opencv_grabber device can read a movie, which is handy for testing programs designed to operate normally on input from a camera. At the time of writing, when the movie finishes black images are generated. It would be nice if instead the device could be asked to loop.
For example, we can currently do:
yarpview /view & yarpdev --device opencv_grabber --movie /scratch/show.avi --framerate 10 --name /grabber --verbose yarp connect /grabber /view
but would like to be able to add a --loop flag.
Without knowing much about YARP, how do we track down where to make this change?
Finding the device documentation
First, lets start with what we do know, which is the name of the device: opencv_grabber. If we do:
we'll see everything yarpdev knows about all devices, including the one we care about:
Here are devices listed for your system: ... Device "opencv_grabber", C++ class OpenCVGrabber, wrapped by "grabber"
So the class associated with the device is called "OpenCVGrabber".
This leads to a description of the class, which mentions its author (Eric Mislevic). We could try to track Eric down, or continue ourselves. Let's continue ourselves.
Finding the source code
Now we need to find the source code for the class. The name of the header file is mentioned in the documentation (OpenCVGrabber.h) so we can just search in our YARP source repository for that. For example, here's what I did on my Linux box:
paulfitz@contact:~/cvs/yarp2$ find src -name OpenCVGrabber.h src/libYARP_dev/src/opencv_grabber/common/yarp/OpenCVGrabber.h
This is indeed the header file corresponding to the documentation we already found.
Now where's the implementation, the cpp file? The directory structure of devices is documented in the Device Driver How-To. It is a little complicated because sometimes there are multiple implementations for different operating systems. Let's now worry about the details here and take a look at the structure of the "opencv_grabber" directory we found:
paulfitz@contact:~/cvs/yarp2$ find src/libYARP_dev/src/opencv_grabber | grep -v CVS src/libYARP_dev/src/opencv_grabber src/libYARP_dev/src/opencv_grabber/common src/libYARP_dev/src/opencv_grabber/common/yarp src/libYARP_dev/src/opencv_grabber/common/yarp/OpenCVGrabber.h src/libYARP_dev/src/opencv_grabber/default src/libYARP_dev/src/opencv_grabber/default/yarp src/libYARP_dev/src/opencv_grabber/default/yarp/OpenCVGrabber.cpp src/libYARP_dev/src/opencv_grabber/default/libraries.txt
So the only relevant C files are:
What should we change?
Now we have to figure out the right change to make. Let's forget about making the device configurable for a moment, and pretend we ALWAYS want it to loop. How can we do this?
Images are read in opencv using cvQueryFrame. we find this in the getImage method. We see that if this call fails, as it presumably does when the movie runs out, then "zeroed" images are returned. So this is the point we need to modify.
The easiest way to kickstart the movie again is just to restart the device by calling open() with a saved copy of the configuration. So that's what I tried first. There are no general rules here, you just have to read the code, figure out what's going on, and make the change you need.
Trying out your change
When you've changed something, you need to recompile:
paulfitz@contact:~/cvs/yarp2$ make [ 45%] Built target YARP_OS [ 70%] Built target harness_os [ 71%] Built target yarp [ 78%] Built target YARP_sig [ 82%] Built target harness_sig Scanning dependencies of target YARP_dev [ 83%] Building CXX object src/libYARP_dev/CMakeFiles/YARP_dev.dir/src/opencv_grabber/default/yarp/OpenCVGrabber.o [ 84%] Building CXX object src/libYARP_dev/CMakeFiles/YARP_dev.dir/src_generated/PopulateDrivers.o Linking CXX static library ../../lib/libYARP_dev.a [ 97%] Built target YARP_dev Linking CXX executable ../../bin/harness_dev [ 98%] Built target harness_dev Linking CXX executable ../../bin/yarpdev [ 99%] Built target yarpdev Linking CXX executable ../../bin/yarphear [100%] Built target yarphear
Good, everything compiled. And upon trying out the motivating example at the start of this page, the video looped as expected.
When you change the behavior of a device, it is good not to surprise anyone already using the device with new behavior. So let's only use looping if it is requested with a --loop option. We do this by adding a boolean variable (called m_loop to fit into the existing programmer's style) and add the following code in the "open" method:
// Should we loop? m_loop = config.check("loop","if present, loop movie");
Then we make our new behavior conditional on this flag.
It is important to check that the flag works -- that without it, behavior is as before, and with it, behavior is as we want. So we recompile and retest.
Also, we should check that the documentation for our new new flag is understandable in yarpdev with the --verbose option:
$ yarpdev --device opencv_grabber --movie /scratch/show.avi --framerate 1 --verbose ... =============================================================== == Options checked by device: == device=opencv_grabber grabber.subdevice=opencv_grabber name (or nested configuration) of device to wrap opencv_grabber.device=opencv_grabber opencv_grabber.movie=/scratch/show.avi if present, read from specified file rather than camera opencv_grabber.loop if present, loop movie ...