OSVR Framework (Internal Development Docs)  0.6-1962-g59773924
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
directx_camera_server.cpp
Go to the documentation of this file.
1 
12 // Copyright 2015 Sensics, Inc.
13 //
14 // Licensed under the Apache License, Version 2.0 (the "License");
15 // you may not use this file except in compliance with the License.
16 // You may obtain a copy of the License at
17 //
18 // http://www.apache.org/licenses/LICENSE-2.0
19 //
20 // Unless required by applicable law or agreed to in writing, software
21 // distributed under the License is distributed on an "AS IS" BASIS,
22 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 // See the License for the specific language governing permissions and
24 // limitations under the License.
25 
26 // Internal Includes
27 #include "directx_camera_server.h"
28 #include "ConnectTwoFilters.h"
29 #include "NullRenderFilter.h"
30 #include "PropertyBagHelper.h"
31 #include "dibsize.h"
33 #include <osvr/Util/WideToUTF8.h>
34 
35 // Library/third-party includes
36 // - none
37 
38 // Standard includes
39 #include <chrono>
40 #include <cmath>
41 #include <iostream>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 
46 // Uncomment to get a full device name and path listing in enumeration, instead
47 // of silently enumerating and early-exiting when we find one we like.
48 //#define VERBOSE_ENUM
49 
50 #undef DXCAMSERVER_VERBOSE
51 
52 //#define HACK_TO_REOPEN
53 //#define DEBUG
54 
57 template <typename T>
58 inline void checkForConstructionError(T const &ptr, const char objName[]) {
59  if (!ptr) {
60  fprintf(stderr, "directx_camera_server: Can't create %s\n", objName);
61  throw ConstructionError(objName);
62  }
63 }
64 
71 bool directx_camera_server::read_one_frame(unsigned minX, unsigned maxX,
72  unsigned minY, unsigned maxY,
73  unsigned exposure_millisecs) {
74  HRESULT hr;
75 
76  if (!_status) {
77  return false;
78  };
79 
80 #ifdef HACK_TO_REOPEN
81  open_and_find_parameters();
82  _started_graph = false;
83 #endif
84 
85  // If we have not yet started the media graph running, set up the callback
86  // handler for the sample grabber filter so that we will hear about each
87  // frame
88  // as it comes in. Then set the filter graph running.
89  if (!_started_graph) {
90  // Run the graph and wait until it captures a frame into its buffer
91  switch (_mode) {
92  case 0: // Case 0 = run
93  hr = _pMediaControl->Run();
94  break;
95  case 1: // Case 1 = paused
96  hr = _pMediaControl->Pause();
97  break;
98  default:
99  fprintf(
100  stderr,
101  "directx_camera_server::read_one_frame(): Unknown mode (%d)\n",
102  _mode);
103  _status = false;
104  return false;
105  }
106  if ((hr != S_OK) && (hr != S_FALSE)) {
107  fprintf(stderr, "directx_camera_server::read_one_frame(): Can't "
108  "run filter graph, got %#010x\n",
109  static_cast<uint32_t>(hr));
110  _status = false;
111  return false;
112  }
113 
114  _started_graph = true;
115  }
116 
117  // XXX Should the app be allowed to set the timeout period?
118  const int TIMEOUT_MSECS = 500;
119 
120  // Wait until there is a sample ready in the callback handler. If there is,
121  // copy it into our buffer and then tell it we are done processing the
122  // sample.
123  // If it takes too long, time out.
124  BYTE *imageLocation;
125  auto imageReady = sampleExchange_->waitForSample(
126  std::chrono::milliseconds(TIMEOUT_MSECS));
127 
128  if (!imageReady) {
129 #ifdef DEBUG
130  fprintf(stderr, "directx_camera_server::read_one_frame(): Timeout "
131  "when reading image\n");
132 #endif
133  return false;
134  }
135 
136  // If we are in mode 2, then we pause the graph after we captured one image.
137  if (_mode == 2) {
138  _pMediaControl->Pause();
139  _mode = 1;
140  }
141 
142  auto sampleWrapper = sampleExchange_->get();
143  if (FAILED(sampleWrapper.get().GetPointer(&imageLocation))) {
144  fprintf(stderr,
145  "directx_camera_server::read_one_frame(): Can't get buffer\n");
146  _status = false;
147  return false;
148  }
149 
150  // Store the timestamp
151  ts_ = sampleWrapper.getTimestamp();
152 
153  // Step through each line of the video and copy it into the buffer. We
154  // do one line at a time here because there can be padding at the end of
155  // each line on some video formats.
156  for (DWORD iRow = 0; iRow < _num_rows; iRow++) {
157  memcpy(_buffer.data() + _num_columns * 3 * iRow,
158  imageLocation + _stride * iRow, _num_columns * 3);
159  }
160 
161 #ifdef HACK_TO_REOPEN
162  close_device();
163 #endif
164  return true;
165 }
166 
167 inline std::string getDevicePath(PropertyBagHelper &prop) {
168  return osvr::util::wideToUTF8String(prop.read(L"DevicePath"));
169 }
170 
171 inline std::string getDeviceHumanDesc(PropertyBagHelper &prop) {
172  auto desc = prop.read(L"Description");
173  if (desc.empty()) {
174  desc = prop.read(L"FriendlyName");
175  }
176  return desc.empty() ? std::string() : osvr::util::wideToUTF8String(desc);
177 }
178 
179 bool directx_camera_server::start_com_and_graphbuilder() {
180 //-------------------------------------------------------------------
181 // Create COM and DirectX objects needed to access a video stream.
182 
183 // Initialize COM. This must have a matching uninitialize somewhere before
184 // the object is destroyed.
185 #ifdef DEBUG
186  printf("directx_camera_server::open_and_find_parameters(): Before "
187  "CoInitialize\n");
188 #endif
189 
190  _com = comutils::ComInit::init();
191 // Create the filter graph manager
192 #ifdef DEBUG
193  printf("directx_camera_server::open_and_find_parameters(): Before "
194  "CoCreateInstance FilterGraph\n");
195 #endif
196  CoCreateInstance(CLSID_FilterGraph, nullptr, CLSCTX_INPROC_SERVER,
197  IID_IGraphBuilder, AttachPtr(_pGraph));
198  checkForConstructionError(_pGraph, "graph manager");
199 
200  _pGraph->QueryInterface(IID_IMediaControl, AttachPtr(_pMediaControl));
201 
202 // Create the Capture Graph Builder.
203 #ifdef DEBUG
204  printf("directx_camera_server::open_and_find_parameters(): Before "
205  "CoCreateInstance CaptureGraphBuilder2\n");
206 #endif
207  CoCreateInstance(CLSID_CaptureGraphBuilder2, nullptr, CLSCTX_INPROC,
208  IID_ICaptureGraphBuilder2, AttachPtr(_pBuilder));
209  checkForConstructionError(_pBuilder, "graph builder");
210 
211 // Associate the graph with the builder.
212 #ifdef DEBUG
213  printf("directx_camera_server::open_and_find_parameters(): Before "
214  "SetFilterGraph\n");
215 #endif
216  _pBuilder->SetFiltergraph(_pGraph.get());
217  return true;
218 }
219 
222 template <typename T>
223 inline bool didConstructionFail(T const &ptr, const char objName[]) {
224  if (!ptr) {
225  fprintf(stderr, "directx_camera_server: Can't create %s\n", objName);
226  return true;
227  }
228  return false;
229 }
230 
231 // Enumerates the capture devices available, returning the first one where the
232 // passed functor (taking IMoniker& as a param) returns true.
233 template <typename F>
234 inline comutils::Ptr<IMoniker> find_first_capture_device_where(F &&f) {
235  auto ret = comutils::Ptr<IMoniker>{};
236 // Create the system device enumerator.
237 #ifdef DEBUG
238  printf("find_first_capture_device_where(): Before "
239  "CoCreateInstance SystemDeviceEnum\n");
240 #endif
241  auto pDevEnum = comutils::Ptr<ICreateDevEnum>{};
242  CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC,
243  IID_ICreateDevEnum, AttachPtr(pDevEnum));
244  if (didConstructionFail(pDevEnum, "device enumerator")) {
245  return ret;
246  }
247 
248 // Create an enumerator for video capture devices.
249 // https://msdn.microsoft.com/en-us/library/windows/desktop/dd407292(v=vs.85).aspx
250 #ifdef DEBUG
251  printf("find_first_capture_device_where(): Before "
252  "CreateClassEnumerator\n");
253 #endif
254  auto pClassEnum = comutils::Ptr<IEnumMoniker>{};
255  pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
256  AttachPtr(pClassEnum), 0);
257  if (didConstructionFail(pClassEnum, "video enumerator (no cameras?)")) {
258  return ret;
259  }
260 
261 #ifdef DEBUG
262  printf("find_first_capture_device_where(): Before Loop "
263  "over enumerators\n");
264 #endif
265 // see
266 // https://msdn.microsoft.com/en-us/library/windows/desktop/dd377566(v=vs.85).aspx
267 // for how to choose a camera
268 #ifdef VERBOSE_ENUM
269  printf("\ndirectx_camera_server find_first_capture_device_where(): "
270  "Beginning enumeration of video capture devices.\n\n");
271 #endif
272  auto pMoniker = comutils::Ptr<IMoniker>{};
273  while (pClassEnum->Next(1, AttachPtr(pMoniker), nullptr) == S_OK) {
274 
275 #ifdef VERBOSE_ENUM
276  printf("- '%s' at path:\n '%s'\n\n",
277  getDeviceHumanDesc(*pMoniker).c_str(),
278  getDevicePath(*pMoniker).c_str());
279 #endif // VERBOSE_ENUM
280 
281  if (!ret && f(*pMoniker)) {
282  ret = pMoniker;
283 #ifdef VERBOSE_ENUM
284  printf("^^ Accepted that device! (Would have exited "
285  "enumeration here if VERBOSE_ENUM were not defined)\n\n");
286 #else // !VERBOSE_ENUM
287  return ret; // Early out if we find it and we're not in verbose enum
288  // mode.
289 #endif
290  }
291  }
292 
293 #ifdef VERBOSE_ENUM
294  printf("\ndirectx_camera_server find_first_capture_device_where(): End "
295  "enumeration.\n\n");
296 #endif
297 
298 #ifdef DXCAMSERVER_VERBOSE
299  if (!ret) {
300  fprintf(stderr,
301  "directx_camera_server find_first_capture_device_where(): "
302  "No device satisfied the predicate.\n");
303  }
304 #endif
305  return ret;
306 }
307 
308 //---------------------------------------------------------------------
309 // Open the camera specified and determine its available features and
310 // parameters.
311 
312 bool directx_camera_server::open_and_find_parameters(const int which,
313  unsigned width,
314  unsigned height) {
315  auto graphbuilderRet = start_com_and_graphbuilder();
316  if (!graphbuilderRet) {
317  return false;
318  }
319  std::size_t i = 0;
320 
321  auto pMoniker = find_first_capture_device_where([&i, which](IMoniker &) {
322  if (i == which) {
323  return true;
324  }
325  ++i;
326  return false;
327  });
328 
329  if (!pMoniker) {
330  fprintf(stderr, "directx_camera_server::open_and_find_parameters(): "
331  "Could not get the device requested - not enough "
332  "cameras?\n");
333  return false;
334  }
335 #ifdef DEBUG
336  std::cout << "directx_camera_server::open_and_find_parameters(): Accepted!"
337  << std::endl;
338 #endif
339  return open_moniker_and_finish_setup(pMoniker, FilterOperation{}, width,
340  height);
341 }
342 
343 bool directx_camera_server::open_and_find_parameters(
344  std::string const &pathPrefix, FilterOperation const &sourceConfig,
345  unsigned width, unsigned height) {
346  auto graphbuilderRet = start_com_and_graphbuilder();
347  if (!graphbuilderRet) {
348  return false;
349  }
350 
351  auto pMoniker =
352  find_first_capture_device_where([&pathPrefix](IMoniker &mon) {
353  auto props = PropertyBagHelper{mon};
354  if (!props) {
355  return false;
356  }
357  auto path = getDevicePath(props);
358  if (path.length() < pathPrefix.length()) {
359  return false;
360  }
361  return (path.substr(0, pathPrefix.length()) == pathPrefix);
362  });
363 
364  if (!pMoniker) {
365 #ifdef DXCAMSERVER_VERBOSE
366  fprintf(stderr, "directx_camera_server::open_and_find_parameters(): "
367  "Could not get the device requested - not enough "
368  "cameras?\n");
369 #endif
370  return false;
371  }
372  {
374  auto props = PropertyBagHelper{*pMoniker};
375  if (props) {
376  devicePath_ = getDevicePath(props);
377  }
378  }
379 
380 #ifdef DEBUG
381  std::cout << "directx_camera_server::open_and_find_parameters(): Accepted!"
382  << std::endl;
383 #endif
384  return open_moniker_and_finish_setup(pMoniker, sourceConfig, width, height);
385 }
386 
387 inline bool setBufferLatency(ICaptureGraphBuilder2 &builder,
388  IBaseFilter &filter, std::size_t numBuffers) {
389 
390  auto negotiation =
391  GetVideoCapturePinInterface<IAMBufferNegotiation>(builder, filter);
392 
393  if (!negotiation) {
394  // failure
395  std::cerr << "Could not get IAMBufferNegotiation" << std::endl;
396  return false;
397  }
398 
399  ALLOCATOR_PROPERTIES props;
400  props.cbBuffer = -1;
401  props.cBuffers = numBuffers;
402  props.cbAlign = -1;
403  props.cbPrefix = -1;
404  auto hr = negotiation->SuggestAllocatorProperties(&props);
405  if (FAILED(hr)) {
406  return false;
407  }
408  return true;
409 }
410 
411 bool directx_camera_server::open_moniker_and_finish_setup(
412  comutils::Ptr<IMoniker> pMoniker, FilterOperation const &sourceConfig,
413  unsigned width, unsigned height) {
414 
415  if (!pMoniker) {
416  fprintf(stderr,
417  "directx_camera_server::open_moniker_and_finish_setup(): "
418  "Null device moniker passed: no device found?\n");
419  return false;
420  }
421  auto prop = PropertyBagHelper{*pMoniker};
422  printf("directx_camera_server: Using capture device '%s' at path '%s'\n",
423  getDeviceHumanDesc(prop).c_str(), getDevicePath(prop).c_str());
424 
425  // Bind the chosen moniker to a filter object.
426  auto pSrc = comutils::Ptr<IBaseFilter>{};
427  pMoniker->BindToObject(nullptr, nullptr, IID_IBaseFilter, AttachPtr(pSrc));
428 
429  // Try to set it up with minimal latency by reducing buffers.
430  if (setBufferLatency(*_pBuilder, *pSrc, 1)) {
431  // std::cout << "Call to set buffer latency succeeded..." << std::endl;
432  } else {
433  std::cout << "directx_camera_server: Call to set capture buffer "
434  "latency failed."
435  << std::endl;
436  }
437 
438  //-------------------------------------------------------------------
439  // Construct the sample grabber that will be used to snatch images from
440  // the video stream as they go by. Set its media type and callback.
441 
442  // Create and configure the Sample Grabber.
443  _pSampleGrabberWrapper.reset(new SampleGrabberWrapper);
444 
445  // Get the exchange object for receiving data from the sample grabber.
446  sampleExchange_ = _pSampleGrabberWrapper->getExchange();
447 
448  //-------------------------------------------------------------------
449  // Ask for the video resolution that has been passed in.
450  // This code is based on
451  // intuiting that we need to use the SetFormat call on the IAMStreamConfig
452  // interface; this interface is described in the help pages.
453  // If the width and height are specified as 0, then they are not set
454  // in the header, letting them use whatever is the default.
456  if ((width != 0) && (height != 0)) {
457  auto pStreamConfig = comutils::Ptr<IAMStreamConfig>{};
458  _pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
459  pSrc.get(), IID_IAMStreamConfig,
460  AttachPtr(pStreamConfig));
461  checkForConstructionError(pStreamConfig, "StreamConfig interface");
462 
463  AM_MEDIA_TYPE mt = {0};
464  mt.majortype = MEDIATYPE_Video; // Ask for video media producers
465  mt.subtype = MEDIASUBTYPE_RGB24; // Ask for 8 bit RGB
466  VIDEOINFOHEADER vih = {0};
467  mt.pbFormat = reinterpret_cast<BYTE *>(&vih);
468  auto pVideoHeader = &vih;
469  pVideoHeader->bmiHeader.biBitCount = 24;
470  pVideoHeader->bmiHeader.biWidth = width;
471  pVideoHeader->bmiHeader.biHeight = height;
472  pVideoHeader->bmiHeader.biPlanes = 1;
473  pVideoHeader->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
474  pVideoHeader->bmiHeader.biSizeImage = dibsize(pVideoHeader->bmiHeader);
475 
476  // Set the format type and size.
477  mt.formattype = FORMAT_VideoInfo;
478  mt.cbFormat = sizeof(VIDEOINFOHEADER);
479 
480  // Set the sample size.
481  mt.bFixedSizeSamples = TRUE;
482  mt.lSampleSize = dibsize(pVideoHeader->bmiHeader);
483 
484  // Make the call to actually set the video type to what we want.
485  if (pStreamConfig->SetFormat(&mt) != S_OK) {
486  fprintf(stderr, "directx_camera_server::open_and_find_parameters():"
487  " Can't set resolution to %dx%d using uncompressed "
488  "24-bit video\n",
489  pVideoHeader->bmiHeader.biWidth,
490  pVideoHeader->bmiHeader.biHeight);
491  return false;
492  }
493  }
494 
495 //-------------------------------------------------------------------
496 // Create a null renderer that will be used to discard the video frames
497 // on the output pin of the sample grabber
498 
499 #ifdef DEBUG
500  printf("directx_camera_server::open_and_find_parameters(): Before "
501  "createNullRenderFilter\n");
502 #endif
503  auto pNullRender = createNullRenderFilter();
504  auto sampleGrabberFilter = _pSampleGrabberWrapper->getFilter();
505  //-------------------------------------------------------------------
506  // Build the filter graph. First add the filters and then connect them.
507 
508  // pSrc is the capture filter for the video device we found above.
509  auto hr = _pGraph->AddFilter(pSrc.get(), L"Video Capture");
510  BOOST_ASSERT_MSG(SUCCEEDED(hr), "Adding Video Capture filter to graph");
511 
512  // Add the sample grabber filter
513  hr = _pGraph->AddFilter(sampleGrabberFilter.get(), L"SampleGrabber");
514  BOOST_ASSERT_MSG(SUCCEEDED(hr), "Adding SampleGrabber filter to graph");
515 
516  // Add the null renderer filter
517  hr = _pGraph->AddFilter(pNullRender.get(), L"NullRenderer");
518  BOOST_ASSERT_MSG(SUCCEEDED(hr), "Adding NullRenderer filter to graph");
519 
520  // Connect the output of the video reader to the sample grabber input
521  // ConnectTwoFilters(*_pGraph, *pSrc, *sampleGrabberFilter);
522  auto capturePin = GetVideoCapturePinInterface<IPin>(*_pBuilder, *pSrc);
523  {
524  auto pIn = GetPin(*sampleGrabberFilter, PINDIR_INPUT);
525  _pGraph->Connect(capturePin.get(), pIn.get());
526  }
527 
528  // Connect the output of the sample grabber to the null renderer input
529  ConnectTwoFilters(*_pGraph, *sampleGrabberFilter, *pNullRender);
530 
531  // If we were given a config action for the source, do it now.
532  if (sourceConfig) {
533  sourceConfig(*pSrc);
534  }
535  //-------------------------------------------------------------------
536  // XXX See if this is a video tuner card by querying for that interface.
537  // Set it to read the video channel if it is one.
538  auto pTuner = GetPinInterface<IAMTVTuner>(*_pBuilder, *pSrc);
539  if (pTuner) {
540 #ifdef DEBUG
541  printf("directx_camera_server::open_and_find_parameters(): Found a TV "
542  "Tuner!\n");
543 #endif
544 
545  // XXX Put code here.
546  // Set the first input pin to use the cable as input
547  hr = pTuner->put_InputType(0, TunerInputCable);
548  if (FAILED(hr)) {
549  fprintf(stderr, "directx_camera_server::open_and_find_parameters():"
550  " Can't set input to cable\n");
551  }
552 
553  // Set the channel on the video to be baseband (is this channel zero?)
554  hr = pTuner->put_Channel(0, -1, -1);
555  if (FAILED(hr)) {
556  fprintf(stderr, "directx_camera_server::open_and_find_parameters():"
557  " Can't set channel\n");
558  }
559  }
560 
561  {
562  //-------------------------------------------------------------------
563  // Find _num_rows and _num_columns in the video stream.
564  AM_MEDIA_TYPE mt = {0};
565  _pSampleGrabberWrapper->getConnectedMediaType(mt);
566  VIDEOINFOHEADER *pVih;
567  if (mt.formattype == FORMAT_VideoInfo ||
568  mt.formattype == FORMAT_VideoInfo2) {
569  pVih = reinterpret_cast<VIDEOINFOHEADER *>(mt.pbFormat);
570  } else {
571  fprintf(stderr,
572  "directx_camera_server::open_and_find_parameters(): "
573  "Can't get video header type\n");
574  fprintf(stderr, " (Expected %x or %x, got %x)\n", FORMAT_VideoInfo,
575  FORMAT_VideoInfo2, mt.formattype);
576  fprintf(stderr, " (GetConnectedMediaType is not valid for DirectX "
577  "headers later than version 7)\n");
578  fprintf(stderr, " (We need to re-implement reading video in some "
579  "other interface)\n");
580  return false;
581  }
582 
583  // Number of rows and columns. This is different if we are using a
584  // target
585  // rectangle (rcTarget) than if we are not.
586  if (IsRectEmpty(&pVih->rcTarget)) {
587  _num_columns = pVih->bmiHeader.biWidth;
588  _num_rows = pVih->bmiHeader.biHeight;
589  } else {
590  _num_columns = pVih->rcTarget.right;
591  _num_rows = pVih->bmiHeader.biHeight;
592  printf("XXX directx_camera_server::open_and_find_parameters(): "
593  "Warning: may not work correctly with target rectangle\n");
594  }
595 #ifdef DEBUG
596  printf("Got %dx%d video\n", _num_columns, _num_rows);
597 #endif
598 
599  // Make sure that the image is not compressed and that we have 8 bits
600  // per pixel.
601  if (pVih->bmiHeader.biCompression != BI_RGB) {
602  fprintf(stderr,
603  "directx_camera_server::open_and_find_parameters(): "
604  "Compression not RGB\n");
605  switch (pVih->bmiHeader.biCompression) {
606  case BI_RLE8:
607  fprintf(stderr, " (It is BI_RLE8)\n");
608  break;
609  case BI_RLE4:
610  fprintf(stderr, " (It is BI_RLE4)\n");
611  case BI_BITFIELDS:
612  fprintf(stderr, " (It is BI_BITFIELDS)\n");
613  break;
614  default:
615  fprintf(stderr, " (Unknown compression type)\n");
616  }
617  return false;
618  }
619  int BytesPerPixel = pVih->bmiHeader.biBitCount / 8;
620  if (BytesPerPixel != 3) {
621  fprintf(stderr,
622  "directx_camera_server::open_and_find_parameters(): "
623  "Not 3 bytes per pixel (%d)\n",
624  pVih->bmiHeader.biBitCount);
625  return false;
626  }
627 
628  // A negative height indicates that the images are stored non-inverted
629  // in Y
630  // Not sure what to do with images that have negative height -- need to
631  // read the book some more to find out.
632  if (_num_rows < 0) {
633  fprintf(stderr,
634  "directx_camera_server::open_and_find_parameters(): "
635  "Num Rows is negative (internal error)\n");
636  return false;
637  }
638 
639  // Find the stride to take when moving from one row of video to the
640  // next. This is rounded up to the nearest DWORD.
641  _stride = (_num_columns * BytesPerPixel + 3) & ~3;
642  }
643 
644  return true;
645 }
646 
648 directx_camera_server::directx_camera_server() {
649  // No image in memory yet.
650  _minX = _maxX = _minY = _maxY = 0;
651 }
652 
654 directx_camera_server::directx_camera_server(int which, unsigned width,
655  unsigned height) {
656  //---------------------------------------------------------------------
657  if (!open_and_find_parameters(which, width, height)) {
658 #ifdef DXCAMSERVER_VERBOSE
659  fprintf(stderr, "directx_camera_server::directx_camera_server(): "
660  "Cannot open camera\n");
661 #endif
662  _status = false;
663  return;
664  }
665 
666  allocate_buffer();
667 
668 #ifdef HACK_TO_REOPEN
669  close_device();
670 #endif
671 
672  _status = true;
673 }
674 
675 directx_camera_server::directx_camera_server(std::string const &pathPrefix,
676  unsigned width, unsigned height) {
677  //---------------------------------------------------------------------
678  if (!open_and_find_parameters(pathPrefix, FilterOperation{}, width,
679  height)) {
680 
681 #ifdef DXCAMSERVER_VERBOSE
682  fprintf(stderr, "directx_camera_server::directx_camera_server(): "
683  "Cannot open camera\n");
684 #endif
685  _status = false;
686  return;
687  }
688  allocate_buffer();
689 
690 #ifdef HACK_TO_REOPEN
691  close_device();
692 #endif
693 
694  _status = true;
695 }
696 
697 directx_camera_server::directx_camera_server(
698  std::string const &pathPrefix, FilterOperation const &sourceConfig) {
699  if (!open_and_find_parameters(pathPrefix, sourceConfig)) {
700 
701 #ifdef DXCAMSERVER_VERBOSE
702  fprintf(stderr, "directx_camera_server::directx_camera_server(): "
703  "Cannot open camera\n");
704 #endif
705  _status = false;
706  return;
707  }
708  allocate_buffer();
709 
710 #ifdef HACK_TO_REOPEN
711  close_device();
712 #endif
713 
714  _status = true;
715 }
716 //---------------------------------------------------------------------
717 // Close the camera and the system. Free up memory.
718 
719 void directx_camera_server::close_device() {
720  // Clean up.
721  _pGraph.reset();
722  _pMediaControl.reset();
723  _pBuilder.reset();
724 }
725 
726 directx_camera_server::~directx_camera_server() {
727  // Get the callback device to immediately return all samples
728  // it has queued up, then shut down the filter graph.
729  if (_pSampleGrabberWrapper) {
730  _pSampleGrabberWrapper->shutdown();
731  }
732 
733  close_device();
734 
735  // Delete the callback object, so that it can clean up and
736  // make sure all of its threads exit.
737  _pSampleGrabberWrapper.reset();
738 }
739 
740 bool directx_camera_server::read_image_to_memory(unsigned minX, unsigned maxX,
741  unsigned minY, unsigned maxY,
742  double exposure_millisecs) {
743  if (!_status) {
744  fprintf(stderr,
745  "directx_camera_server::read_image_to_memory(): broken\n");
746  return false;
747  };
748 
749  //---------------------------------------------------------------------
750  // In case we fail, clear these
751  _minX = _minY = _maxX = _maxY = 0;
752 
753  //---------------------------------------------------------------------
754  // If the maxes are greater than the mins, set them to the size of
755  // the image.
756  if (maxX < minX) {
757  minX = 0;
758  maxX = _num_columns - 1;
759  }
760  if (maxY < minY) {
761  minY = 0;
762  maxY = _num_rows - 1;
763  }
764 
765  //---------------------------------------------------------------------
766  // Clip collection range to the size of the sensor on the camera.
767  if (minX < 0) {
768  minX = 0;
769  };
770  if (minY < 0) {
771  minY = 0;
772  };
773  if (maxX >= _num_columns) {
774  maxX = _num_columns - 1;
775  };
776  if (maxY >= _num_rows) {
777  maxY = _num_rows - 1;
778  };
779 
780  //---------------------------------------------------------------------
781  // Store image size for later use
782  _minX = minX;
783  _minY = minY;
784  _maxX = maxX;
785  _maxY = maxY;
786 
787  //---------------------------------------------------------------------
788  // Set up and read one frame, if we can.
789  if (!read_one_frame(_minX, _maxX, _minY, _maxY, (int)exposure_millisecs)) {
790  fprintf(stderr, "directx_camera_server::read_image_to_memory(): "
791  "read_one_frame() failed\n");
792  return false;
793  }
794 
795  return true;
796 }
797 
798 bool directx_camera_server::get_pixel_from_memory(unsigned X, unsigned Y,
799  vrpn_uint8 &val,
800  int RGB) const {
801  if (!_status) {
802  return false;
803  };
804 
805  // Switch red and blue, since they are backwards in the record (blue is
806  // first).
807  RGB = 2 - RGB;
808 
809  // XXX This will depend on what kind of pixels we have!
810  if ((_maxX <= _minX) || (_maxY <= _minY)) {
811  fprintf(stderr, "directx_camera_server::get_pixel_from_memory(): No "
812  "image in memory\n");
813  return false;
814  }
815  if ((X < _minX) || (X > _maxX) || (Y < _minY) || (Y > _maxY)) {
816  return false;
817  }
818  unsigned cols = _num_columns;
819  val = _buffer[(Y * cols + X) * 3 + RGB];
820  return true;
821 }
822 
823 bool directx_camera_server::get_pixel_from_memory(unsigned X, unsigned Y,
824  vrpn_uint16 &val,
825  int RGB) const {
826  if (!_status) {
827  return false;
828  };
829 
830  // Switch red and blue, since they are backwards in the record (blue is
831  // first).
832  RGB = 2 - RGB;
833 
834  // XXX This will depend on what kind of pixels we have!
835  if ((_maxX <= _minX) || (_maxY <= _minY)) {
836  fprintf(stderr, "directx_camera_server::get_pixel_from_memory(): No "
837  "image in memory\n");
838  return false;
839  }
840  if ((X < _minX) || (X > _maxX) || (Y < _minY) || (Y > _maxY)) {
841  return false;
842  }
843  unsigned cols = _num_columns;
844  val = _buffer[(Y * cols + X) * 3 + RGB];
845  return true;
846 }
847 
848 void directx_camera_server::allocate_buffer() {
849  //---------------------------------------------------------------------
850  // Allocate a buffer that is large enough to read the maximum-sized
851  // image with no binning.
853  auto buflen = (_num_rows * _num_columns * 3); // Expect B,G,R; 8-bits each.
854  _buffer.resize(buflen);
855 
856  // No image in memory yet.
857  _minX = _maxX = _minY = _maxY = 0;
858 }
HRESULT ConnectTwoFilters(IGraphBuilder &pGraph, IBaseFilter &pFirst, IBaseFilter &pSecond)
Header.
bool didConstructionFail(T const &ptr, const char objName[])
Checks something to see if it's false-ish, printing a message and throwing an exception if it is...
void checkForConstructionError(T const &ptr, const char objName[])
Checks something to see if it's false-ish, printing a message and throwing an exception if it is...
comutils::Ptr< IPin > GetPin(IBaseFilter &pFilter, PIN_DIRECTION const PinDir)
Helper function to get a pin of a particular direction.
Definition: GetPin.h:38
DWORD dibsize(BITMAPINFOHEADER const &bi)
Computes bytes required for an UNCOMPRESSED RGB DIB.
Definition: dibsize.h:41
Configured header for internal UTF16 or Windows TCHAR to UTF8 conversion. Using this header requires ...
std::wstring read(const wchar_t propName[]) const
Reads a (wide-string) property, returning an empty string if failed.