Writting a camera device

A camera device is an uobject whose val field is a binary object. The urbi kernel itself doesn’t differenciate between all the possible binary formats and data type, but the API provides image-specfic structures for conveniance. You must be careful about memory managment. The UBinary structure handles its own memory: copies are deep copies, and the destructor frees the associated buffer. The UImage and USound structures do not.

Let’s suppose we have an underlying camera API with the following functions:

Our device code can be written as follows:

//inherit from UObject
class Camera : public urbi::UObject
{
public:
  // the class must have a single constructor taking a string
  Camera(const std::string&);

  // URBI constructor
  int init (int id);

  // our image variable and dimensions
  urbi::UVar val;
  urbi::UVar width;
  urbi::UVar height;

  // called on access
  int getVal (UVar &);

  // called periodically
  virtual int update ();

  //frame counter for caching
  int frame;
  //frame number of last access
  int accessFrame;
  //camera id UBinary bin;
  int id_;
};

The constructor only registers init:

//the constructor registers init only
Camera::Camera (const std::string& s)
  : urbi::UObject (s),
    frame (0)
{
  // register init
  UBindFunction (Camera, init);
}

The init function binds the variable, a function called on access, and sets a timer up on update. It also initialises the UBinary structure.

int
Camera::init (int id)
{
  //urbi constructor
  id_ = id;
  frame = 0;
  accessFrame = 0;

  if (!initialize (id))
    return 0;

  UBindVar (Camera,val);
  UBindVar (Camera,width);
  UBindVar (Camera,height);
  width = getWidth (id);
  height = getHeight (id);

  UNotifyAccess (val, &Camera::getVal);

  bin.type=BINARY_IMAGE;
  bin.image.width = width;
  bin.image.height = height;
  bin.image.imageFormat = IMAGE_RGB;
  bin.image.size = width * height * 3;

  //will call update () periodically
  USetUpdate (1);

  return 0;
}

The update function simply updates the frame counter:

int
Camera::update ()
{
  frame++;
  return 0;
}

The getVal updates the camera value, only if it hasn’t already been called this frame, which provides a simple caching mechanism to avoid performing the potentially lenghty operation of acquiring an image too often.

int
Camera::getVal(urbi::UVar &)
{
  if (frame == accessFrame)
    return 1;

  bin.image.data = getImage (id);
  //assign image to bin
  val = bin;
}

UStart(Camera);

The image data is copied inside the kernel when proceeding this way.

Be careful, suppose that we had created the UBinary structure inside the getVal method, our buffer would have been freed at the end of the function. To avoid this, set it to 0 after assigning the UBinary to the UVar.

Optimization in plugin mode

In plugin mode, it is possible to access the buffer used by the kernel by casting the UVar to a UImage. You can modify the content of the kernel buffer but no other argument.