OSVR Framework (Internal Development Docs)  0.6-1962-g59773924
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
ImagingComponent.cpp
Go to the documentation of this file.
1 
11 // Copyright 2015 Sensics, Inc.
12 //
13 // Licensed under the Apache License, Version 2.0 (the "License");
14 // you may not use this file except in compliance with the License.
15 // You may obtain a copy of the License at
16 //
17 // http://www.apache.org/licenses/LICENSE-2.0
18 //
19 // Unless required by applicable law or agreed to in writing, software
20 // distributed under the License is distributed on an "AS IS" BASIS,
21 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 // See the License for the specific language governing permissions and
23 // limitations under the License.
24 
25 // Internal Includes
27 #include <osvr/Common/BaseDevice.h>
29 #include <osvr/Common/Buffer.h>
31 #include <osvr/Util/Flag.h>
32 #include <osvr/Util/Verbosity.h>
33 
34 // Library/third-party includes
35 // - none
36 
37 // Standard includes
38 #include <sstream>
39 #include <utility>
40 
41 namespace osvr {
42 namespace common {
43  static inline uint32_t getBufferSize(OSVR_ImagingMetadata const &meta) {
44  return meta.height * meta.width * meta.depth * meta.channels;
45  }
46  namespace messages {
47  namespace {
48  template <typename T>
49  void process(OSVR_ImagingMetadata &meta, T &p) {
50  p(meta.height);
51  p(meta.width);
52  p(meta.channels);
53  p(meta.depth);
54  p(meta.type,
55  serialization::EnumAsIntegerTag<OSVR_ImagingValueType,
56  uint8_t>());
57  }
58  } // namespace
59 
61  public:
63  OSVR_ImageBufferElement *imageData,
64  OSVR_ChannelCount sensor)
65  : m_meta(meta),
66  m_imgBuf(imageData,
68  }), // That's a null-deleter right there for you.
69  m_sensor(sensor) {}
70 
71  MessageSerialization() : m_imgBuf(nullptr) {}
72 
73  template <typename T>
74  void allocateBuffer(T &, size_t bytes, std::true_type const &) {
75  m_imgBuf = util::makeAlignedImageBuffer(bytes);
76  }
77 
78  template <typename T>
79  void allocateBuffer(T &, size_t, std::false_type const &) {
80  // Does nothing if we're serializing.
81  }
82 
83  template <typename T> void processMessage(T &p) {
84  process(m_meta, p);
85  auto bytes = getBufferSize(m_meta);
86 
89  allocateBuffer(p, bytes, p.isDeserialize());
90  p(m_imgBuf.get(),
92  }
93  ImageData getData() const {
94  ImageData ret;
95  ret.sensor = m_sensor;
96  ret.metadata = m_meta;
97  ret.buffer = m_imgBuf;
98  return ret;
99  }
100 
101  private:
102  OSVR_ImagingMetadata m_meta;
103  ImageBufferPtr m_imgBuf;
104  OSVR_ChannelCount m_sensor;
105  };
106  const char *ImageRegion::identifier() {
107  return "com.osvr.imaging.imageregion";
108  }
109 
110 #ifdef OSVR_COMMON_IN_PROCESS_IMAGING
111  namespace {
112  struct InProcessMemoryMessage {
113  OSVR_ImagingMetadata metadata;
114  OSVR_ChannelCount sensor;
115  intptr_t buffer;
116  };
117  template <typename T>
118  void process(InProcessMemoryMessage &ipmmMsg, T &p) {
119  process(ipmmMsg.metadata, p);
120  p(ipmmMsg.sensor);
121  p(ipmmMsg.buffer);
122  }
123  } // namespace
124 
125  const char *ImagePlacedInProcessMemory::identifier() {
126  return "com.osvr.imaging.imageplacedinprocessmemory";
127  }
128 
129  class ImagePlacedInProcessMemory::MessageSerialization {
130  public:
131  MessageSerialization() {}
132  explicit MessageSerialization(InProcessMemoryMessage &&msg)
133  : m_msgData(std::move(msg)) {}
134 
135 #if defined(_MSC_VER) && defined(_PREFAST_)
136  // @todo workaround for apparent bug in VS2013 /analyze
137  explicit MessageSerialization(InProcessMemoryMessage const &msg)
138  : m_msgData(msg) {}
139 #endif
140  template <typename T> void processMessage(T &p) {
141  process(m_msgData, p);
142  }
143 
144  InProcessMemoryMessage const &getMessage() { return m_msgData; }
145 
146  private:
147  InProcessMemoryMessage m_msgData;
148  };
149 #endif
150 
151  namespace {
152  struct SharedMemoryMessage {
153  OSVR_ImagingMetadata metadata;
155  OSVR_ChannelCount sensor;
156  IPCRingBuffer::abi_level_type abiLevel;
157  IPCRingBuffer::BackendType backend;
158  std::string shmName;
159  };
160  template <typename T>
161  void process(SharedMemoryMessage &shmMsg, T &p) {
162  process(shmMsg.metadata, p);
163  p(shmMsg.seqNum);
164  p(shmMsg.sensor);
165  p(shmMsg.abiLevel);
166  p(shmMsg.backend);
167  p(shmMsg.shmName);
168  }
169 
170  } // namespace
172  public:
174  explicit MessageSerialization(SharedMemoryMessage &&msg)
175  : m_msgData(std::move(msg)) {}
176 
177 #if defined(_MSC_VER) && defined(_PREFAST_)
178  explicit MessageSerialization(SharedMemoryMessage const &msg)
180  : m_msgData(msg) {}
181 #endif
182 
183  template <typename T> void processMessage(T &p) {
184  process(m_msgData, p);
185  }
186 
187  SharedMemoryMessage const &getMessage() { return m_msgData; }
188 
189  private:
190  SharedMemoryMessage m_msgData;
191  };
192 
193  const char *ImagePlacedInSharedMemory::identifier() {
194  return "com.osvr.imaging.imageplacedinsharedmemory";
195  }
196  } // namespace messages
197 
198  shared_ptr<ImagingComponent>
200  shared_ptr<ImagingComponent> ret(new ImagingComponent(numChan));
201  return ret;
202  }
203  ImagingComponent::ImagingComponent(OSVR_ChannelCount numChan)
204  : m_numSensor(numChan) {}
205 
206  void ImagingComponent::sendImageData(OSVR_ImagingMetadata metadata,
207  OSVR_ImageBufferElement *imageData,
208  OSVR_ChannelCount sensor,
209  OSVR_TimeValue const &timestamp) {
210 
211  util::Flag dataSent;
212 
213 #ifdef OSVR_COMMON_IN_PROCESS_IMAGING
214  dataSent += m_sendImageDataViaInProcessMemory(metadata, imageData,
215  sensor, timestamp);
216 #else
217  dataSent += m_sendImageDataViaSharedMemory(metadata, imageData, sensor,
218  timestamp);
219 #endif
220  dataSent +=
221  m_sendImageDataOnTheWire(metadata, imageData, sensor, timestamp);
222  if (dataSent) {
223  m_checkFirst(metadata);
224  }
225  }
226 
227 #ifdef OSVR_COMMON_IN_PROCESS_IMAGING
228  bool ImagingComponent::m_sendImageDataViaInProcessMemory(
229  OSVR_ImagingMetadata metadata, OSVR_ImageBufferElement *imageData,
230  OSVR_ChannelCount sensor, OSVR_TimeValue const &timestamp) {
231 
232  auto imageBufferSize = getBufferSize(metadata);
233  auto imageBufferCopy = util::makeAlignedImageBuffer(imageBufferSize);
234  memcpy(imageBufferCopy.get(), imageData, imageBufferSize);
235 
236  Buffer<> buf;
237  messages::ImagePlacedInProcessMemory::MessageSerialization
238  serialization(messages::InProcessMemoryMessage{
239  metadata, sensor,
240  reinterpret_cast<intptr_t>(imageBufferCopy.release())});
241 
242  serialize(buf, serialization);
243  m_getParent().packMessage(
244  buf, imagePlacedInProcessMemory.getMessageType(), timestamp);
245 
246  return true;
247  }
248 #endif
249 
250  bool ImagingComponent::m_sendImageDataViaSharedMemory(
251  OSVR_ImagingMetadata metadata, OSVR_ImageBufferElement *imageData,
252  OSVR_ChannelCount sensor, OSVR_TimeValue const &timestamp) {
253 
254  m_growShmVecIfRequired(sensor);
255  uint32_t imageBufferSize = getBufferSize(metadata);
256  if (!m_shmBuf[sensor] ||
257  m_shmBuf[sensor]->getEntrySize() != imageBufferSize) {
258  // create or replace the shared memory ring buffer.
259  auto makeName = [](OSVR_ChannelCount sensor,
260  std::string const &devName) {
261  std::ostringstream os;
262  os << "com.osvr.imaging/" << devName << "/" << int(sensor);
263  return os.str();
264  };
265  m_shmBuf[sensor] = IPCRingBuffer::create(
266  IPCRingBuffer::Options(
267  makeName(sensor, m_getParent().getDeviceName()))
268  .setEntrySize(imageBufferSize));
269  }
270  if (!m_shmBuf[sensor]) {
271  OSVR_DEV_VERBOSE(
272  "Some issue creating shared memory for imaging, skipping out.");
273  return false;
274  }
275  auto &shm = *(m_shmBuf[sensor]);
276  auto seq = shm.put(imageData, imageBufferSize);
277 
278  Buffer<> buf;
279  messages::ImagePlacedInSharedMemory::MessageSerialization serialization(
280  messages::SharedMemoryMessage{metadata, seq, sensor,
282  shm.getBackend(), shm.getName()});
283  serialize(buf, serialization);
284  m_getParent().packMessage(
285  buf, imagePlacedInSharedMemory.getMessageType(), timestamp);
286 
287  return true;
288  }
289 
290  bool ImagingComponent::m_sendImageDataOnTheWire(
291  OSVR_ImagingMetadata metadata, OSVR_ImageBufferElement *imageData,
292  OSVR_ChannelCount sensor, OSVR_TimeValue const &timestamp) {
294  if (metadata.depth != 1) {
295  return false;
296  }
297  Buffer<> buf;
298  messages::ImageRegion::MessageSerialization msg(metadata, imageData,
299  sensor);
300  serialize(buf, msg);
301  if (buf.size() > vrpn_CONNECTION_TCP_BUFLEN) {
302 #if 0
303  OSVR_DEV_VERBOSE("Skipping imaging message: size is "
304  << buf.size() << " vs the maximum of "
305  << vrpn_CONNECTION_TCP_BUFLEN);
306 #endif
307  return false;
308  }
309  m_getParent().packMessage(buf, imageRegion.getMessageType(), timestamp);
311  return true;
312  }
313 
314  int VRPN_CALLBACK
315  ImagingComponent::m_handleImageRegion(void *userdata, vrpn_HANDLERPARAM p) {
316  auto self = static_cast<ImagingComponent *>(userdata);
317  auto bufReader = readExternalBuffer(p.buffer, p.payload_len);
318 
319  messages::ImageRegion::MessageSerialization msg;
320  deserialize(bufReader, msg);
321  auto data = msg.getData();
322  auto timestamp = util::time::fromStructTimeval(p.msg_time);
323 
324  self->m_checkFirst(data.metadata);
325  for (auto const &cb : self->m_cb) {
326  cb(data, timestamp);
327  }
328  return 0;
329  }
330 
331 #ifdef OSVR_COMMON_IN_PROCESS_IMAGING
332  int VRPN_CALLBACK ImagingComponent::m_handleImagePlacedInProcessMemory(
333  void *userdata, vrpn_HANDLERPARAM p) {
334  auto self = static_cast<ImagingComponent *>(userdata);
335  auto bufReader = readExternalBuffer(p.buffer, p.payload_len);
336 
337  messages::ImagePlacedInProcessMemory::MessageSerialization msgSerialize;
338  deserialize(bufReader, msgSerialize);
339  auto msg = msgSerialize.getMessage();
340  ImageData data;
341  data.sensor = msg.sensor;
342  data.metadata = msg.metadata;
343  data.buffer.reset(
344  reinterpret_cast<OSVR_ImageBufferElement *>(msg.buffer),
346  auto timestamp = util::time::fromStructTimeval(p.msg_time);
347 
348  self->m_checkFirst(msg.metadata);
349  for (auto const &cb : self->m_cb) {
350  cb(data, timestamp);
351  }
352  return 0;
353  }
354 #endif
355 
356  int VRPN_CALLBACK ImagingComponent::m_handleImagePlacedInSharedMemory(
357  void *userdata, vrpn_HANDLERPARAM p) {
358  auto self = static_cast<ImagingComponent *>(userdata);
359  auto bufReader = readExternalBuffer(p.buffer, p.payload_len);
360 
361  messages::ImagePlacedInSharedMemory::MessageSerialization msgSerialize;
362  deserialize(bufReader, msgSerialize);
363  auto &msg = msgSerialize.getMessage();
364  auto timestamp = util::time::fromStructTimeval(p.msg_time);
365 
366  if (IPCRingBuffer::getABILevel() != msg.abiLevel) {
368  OSVR_DEV_VERBOSE("Can't handle SHM ABI level " << msg.abiLevel);
369  return 0;
370  }
371  self->m_growShmVecIfRequired(msg.sensor);
372  auto checkSameRingBuf = [](messages::SharedMemoryMessage const &msg,
373  IPCRingBufferPtr &ringbuf) {
374  return (msg.backend == ringbuf->getBackend()) &&
375  (ringbuf->getEntrySize() == getBufferSize(msg.metadata)) &&
376  (ringbuf->getName() == msg.shmName);
377  };
378  if (!self->m_shmBuf[msg.sensor] ||
379  !checkSameRingBuf(msg, self->m_shmBuf[msg.sensor])) {
380  self->m_shmBuf[msg.sensor] = IPCRingBuffer::find(
381  IPCRingBuffer::Options(msg.shmName, msg.backend));
382  }
383  if (!self->m_shmBuf[msg.sensor]) {
386  OSVR_DEV_VERBOSE("Can't find desired IPC ring buffer "
387  << msg.shmName);
388  return 0;
389  }
390 
391  auto &shm = self->m_shmBuf[msg.sensor];
392  auto getResult = shm->get(msg.seqNum);
393  if (getResult) {
394  auto bufptr = getResult.getBufferSmartPointer();
395  self->m_checkFirst(msg.metadata);
396  auto data = ImageData{msg.sensor, msg.metadata, bufptr};
397 
398  for (auto const &cb : self->m_cb) {
399  cb(data, timestamp);
400  }
401  }
402  return 0;
403  }
404 
405  void ImagingComponent::registerImageHandler(ImageHandler handler) {
406  if (m_cb.empty()) {
407  m_registerHandler(&ImagingComponent::m_handleImageRegion, this,
408  imageRegion.getMessageType());
409 
411  &ImagingComponent::m_handleImagePlacedInSharedMemory, this,
412  imagePlacedInSharedMemory.getMessageType());
413 
414 #ifdef OSVR_COMMON_IN_PROCESS_IMAGING
416  &ImagingComponent::m_handleImagePlacedInProcessMemory, this,
417  imagePlacedInProcessMemory.getMessageType());
418 #endif
419  }
420  m_cb.push_back(handler);
421  }
422  void ImagingComponent::m_parentSet() {
425 #ifdef OSVR_COMMON_IN_PROCESS_IMAGING
426  m_getParent().registerMessageType(imagePlacedInProcessMemory);
427 #endif
428  }
429 
430  void ImagingComponent::m_checkFirst(OSVR_ImagingMetadata const &metadata) {
431  if (m_gotOne) {
432  return;
433  }
434  m_gotOne = true;
435 
436  OSVR_DEV_VERBOSE("Sending/receiving first frame: width="
437  << metadata.width << " height=" << metadata.height);
438  }
439  void ImagingComponent::m_growShmVecIfRequired(OSVR_ChannelCount sensor) {
440  if (m_shmBuf.size() <= sensor) {
441  m_shmBuf.resize(sensor + 1);
442  }
443  }
444 } // namespace common
445 } // namespace osvr
OSVR_ImageDepth depth
the depth (size) in bytes of each channel - valid values are 1, 2, 4, and 8
uint32_t OSVR_ChannelCount
The integer type specifying a number of channels/sensors or a channel/sensor index.
Definition: ChannelCountC.h:51
OSVR_ImagingValueType type
Whether values are unsigned ints, signed ints, or floating point.
A tag for use for raw data (bytestream), optionally aligned.
void deserialize(BufferReaderType &reader, MessageClass &msg)
Deserializes a message from a buffer, using a MessageClass
void serialize(BufferType &buf, MessageClass &msg)
Serializes a message into a buffer, using a MessageClass
STL namespace.
shared_ptr< IPCRingBuffer > IPCRingBufferPtr
Pointer type for holding a shared memory ring buffer.
Definition: IPCRingBuffer.h:49
static IPCRingBufferPtr find(Options const &opts)
Named constructor, for use by client processes: accesses an IPC ring buffer using the options structu...
static shared_ptr< ImagingComponent > create(OSVR_ChannelCount numSensor=0)
Factory method.
void m_registerHandler(vrpn_MESSAGEHANDLER handler, void *userdata, RawMessageType const &msgType)
Registers a handler whose lifetime is tied to the lifetime of the component.
OSVR_ImageDimension width
width in pixels
void fromStructTimeval(TimeValue &dest, struct timeval const &src)
Convert a struct timeval to a TimeValue.
Definition: TimeValue.h:104
static abi_level_type getABILevel()
Gets an integer representing a unique arrangement of the internal shared memory layout, such that if two processes try to communicate with different ABI levels, they will (likely) not succeed and thus should not try.
uint32_t sequence_type
The sequence number is automatically incremented with each "put" into the buffer. Note that...
void sendPending()
Called from a component to send pending messages instead of waiting for next time.
Definition: BaseDevice.cpp:86
Header.
OSVR_ImageChannels channels
number of channels of data for each pixel
Parent & m_getParent()
Gets the parent - only call if m_hasParent() is true.
messages::ImageRegion imageRegion
Message from server to client, containing some image data.
Internal, configured header file for verbosity macros.
Header.
OSVR_ImageDimension height
height in pixels
BaseDevice component.
messages::ImagePlacedInSharedMemory imagePlacedInSharedMemory
Message from server to client, notifying of image data in the shared memory ring buffer.
Standardized, portable parallel to struct timeval for representing both absolute times and time inter...
Definition: TimeValueC.h:81
Header defining buffer types, with optional alignment dependent on Boost version. ...
unsigned char OSVR_ImageBufferElement
Type for raw buffer access to image data.
BufferReader< ExternalBufferReadingWrapper< CharType > > readExternalBuffer(const CharType *buf, size_t len)
Constructs and returns a buffer reader for an externally-allocated buffer: it's on you to supply a va...
Definition: Buffer.h:198
void registerMessageType(MessageRegistration< T > &messageReg)
Call with a MessageRegistration object, and the message type will be registered and stored in the typ...
Definition: BaseDevice.h:72
void alignedFree(void *p)
Aligned deallocation function, uses the pointer to the original memory block to deallocate it...
Definition: AlignedMemory.h:56
A class that lightly wraps a bool, in order to provide easier maintenance of a "dirty" flag...
Definition: Flag.h:43
static IPCRingBufferPtr create(Options const &opts)
Named constructor, for use by server processes: creates a shared memory ring buffer given the options...