Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
vpDirectShowGrabberImpl.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2023 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See https://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * DirectShow framegrabber implementation.
33  *
34 *****************************************************************************/
35 
36 #ifndef DOXYGEN_SHOULD_SKIP_THIS
37 
38 #include <visp3/core/vpConfig.h>
39 #if (defined(VISP_HAVE_DIRECTSHOW))
40 
41 #include <visp3/sensor/vpDirectShowGrabberImpl.h>
42 
44 vpDirectShowDevice *vpDirectShowGrabberImpl::deviceList = nullptr;
45 unsigned int vpDirectShowGrabberImpl::nbDevices;
46 
50 void vpDirectShowGrabberImpl::HRtoStr(std::string &str)
51 {
52  TCHAR szErr[MAX_ERROR_TEXT_LEN];
53  DWORD res = AMGetErrorText(hr, szErr, MAX_ERROR_TEXT_LEN);
54 
55  if (res == 0)
56  str = "Unknown Error: 0x%2x";
57 
58  char msg[MAX_ERROR_TEXT_LEN];
59  snprintf(msg, MAX_ERROR_TEXT_LEN, "%s", szErr);
60  str = msg;
61 }
62 
67 vpDirectShowGrabberImpl::vpDirectShowGrabberImpl()
68 {
69  init = false;
70  initCo = false;
71  // COM initialization
72  if (FAILED(hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED))) {
73  std::string err;
74  HRtoStr(err);
75  throw(vpFrameGrabberException(vpFrameGrabberException::initializationError, "Can't initialize COM\n" + err));
76  }
77  initCo = true;
78 
79  // create the device list
80  if (deviceList == nullptr) {
81  CComPtr<IEnumMoniker> pVideoInputEnum = nullptr;
82 
83  if (enumerate(pVideoInputEnum)) {
84  createDeviceList(pVideoInputEnum);
85  }
86  // not used anymore, so we release it
87  pVideoInputEnum.Release();
88  }
89 }
90 
95 void vpDirectShowGrabberImpl::open()
96 {
97  // create the device list
98  if (deviceList == nullptr) {
99  CComPtr<IEnumMoniker> pVideoInputEnum = nullptr;
100 
101  if (enumerate(pVideoInputEnum)) {
102  createDeviceList(pVideoInputEnum);
103  }
104  // not used anymore, so we release it
105  pVideoInputEnum.Release();
106  }
107 
108  init = initDirectShow();
109  if (!init) {
110  std::string err;
111  HRtoStr(err);
113  }
114 }
119 void vpDirectShowGrabberImpl::open(vpImage<unsigned char> &I) { open(); }
120 
125 void vpDirectShowGrabberImpl::open(vpImage<vpRGBa> &I) { open(); }
126 
132 bool vpDirectShowGrabberImpl::initDirectShow()
133 {
134 
135  // get the first working device's filter (unused and getdevice works on it)
136  currentDevice = getFirstUnusedDevice(pCapSource);
137 
138  if (currentDevice == nbDevices)
139  return false;
140 
141  // create the filter graph
142  if (!createGraph())
143  return false;
144 
145  // we add the capture source to the filter graph
146  if (FAILED(hr = pGraph->AddFilter(pCapSource, L"Capture Filter")))
147  return false;
148 
149  // we create a sample grabber
150  if (!createSampleGrabber(pGrabberFilter))
151  return false;
152 
153  // we add the grabber to the filter graph
154  if (FAILED(hr = pGraph->AddFilter(pGrabberFilter, L"SampleGrabber")))
155  return false;
156 
157  // we connect the pins
158  if (!connectSourceToGrabber(pCapSource, pGrabberFilter))
159  return false;
160 
161  // get the current connected media type (needed by the callback)
162  if (FAILED(hr = pGrabberI->GetConnectedMediaType(&(sgCB.connectedMediaType))))
163  return false;
164 
165  // Gets the various graph's interfaces
166  CComPtr<IMediaFilter> pMediaFilter;
167 
168  pGraph->QueryInterface(IID_IMediaFilter, (void **)&pMediaFilter);
169  pGraph->QueryInterface(IID_IMediaControl, reinterpret_cast<void **>(&pControl));
170  pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
171 
172  pMediaFilter->SetSyncSource(nullptr);
173  pMediaFilter.Release();
174 
175  return true;
176 }
177 
181 vpDirectShowGrabberImpl::~vpDirectShowGrabberImpl() { close(); }
182 
188 bool vpDirectShowGrabberImpl::enumerate(CComPtr<IEnumMoniker> &ppVideoInputEnum)
189 {
190  CComPtr<ICreateDevEnum> pDevEnum = nullptr;
191  bool res = false;
192 
193  // Enumerate system devices
194  hr = pDevEnum.CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER);
195 
196  // if it is a success
197  if (SUCCEEDED(hr)) {
198  // Create a video input device enumerator
199  hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &ppVideoInputEnum, 0);
200 
201  if (hr == S_OK)
202  res = true;
203  }
204 
205  pDevEnum.Release();
206  return res;
207 }
208 
214 bool vpDirectShowGrabberImpl::createDeviceList(CComPtr<IEnumMoniker> &ppVideoInputEnum)
215 {
216  CComPtr<IMoniker> pMoniker[10]; // const max devices
217  unsigned long nbMoniker;
218 
219  ppVideoInputEnum->Reset();
220 
221  // Enumerates the different inputs
222  ppVideoInputEnum->Next(10, reinterpret_cast<IMoniker **>(&pMoniker), &nbMoniker);
223 
224  // if no input device
225  if (nbMoniker == 0)
226  return false;
227 
228  deviceList = new vpDirectShowDevice[nbMoniker];
229 
230  nbDevices = (unsigned int)nbMoniker;
231 
232  // we try to get the properties of each moniker, if it fails, we skip to the
233  // next one and decrement the number of valid devices
234  unsigned int i = 0;
235  unsigned int j = 0;
236  while (i < nbDevices) {
237  if (!deviceList[i].init(pMoniker[j])) {
238  // if we can't get the device properties, skip to the next device
239  j++;
240  nbDevices--;
241  }
242  else {
243  i++;
244  j++;
245  }
246  }
247 
248  // if no working input device
249  if (nbDevices == 0)
250  return false;
251 
252  // we release the monikers
253  for (unsigned int i = 0; i < nbMoniker; i++) {
254  pMoniker[i].Release();
255  }
256 
257  return true;
258 }
259 
266 bool vpDirectShowGrabberImpl::getDevice(unsigned int n, CComPtr<IBaseFilter> &ppDevice)
267 {
268  // if n is invalid, quit
269  if (n >= nbDevices)
270  return false;
271 
272  // if the device is already in use, quit
273  if (deviceList[n].getState() == true)
274  return false;
275 
276  // if we can't enumerate the devices, quit
277  CComPtr<IEnumMoniker> pVideoInputEnum = nullptr;
278  if (!enumerate(pVideoInputEnum))
279  return false;
280 
281  CComPtr<IMoniker> pMoniker = nullptr;
282  bool deviceFound = false;
283 
284  // Enumerates the different inputs
285  while (pVideoInputEnum->Next(1, &pMoniker, nullptr) == S_OK && !deviceFound) {
286  // implicit conversion should work ...
287  if (deviceList[n] == vpDirectShowDevice(pMoniker)) {
288  // we get the filter
289  if (SUCCEEDED(pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void **)&ppDevice))) {
290  // now the device is in use
291  deviceList[n].setInUse();
292  deviceFound = true;
293  }
294  else {
295  break;
296  } // we can't get the device's filter, quit
297  }
298  pMoniker.Release();
299  }
300 
301  pVideoInputEnum.Release();
302 
303  return deviceFound;
304 }
305 
312 unsigned int vpDirectShowGrabberImpl::getFirstUnusedDevice(CComPtr<IBaseFilter> &ppDevice)
313 {
314  unsigned int n = 0;
315  bool found = false;
316 
317  for (n = 0; n < nbDevices && !found; n++) {
318  // if the device is not being used
319  if (!deviceList[n].getState()) {
320  if (getDevice(n, ppDevice)) {
321  found = true;
322  deviceList[n].setInUse();
323  return n;
324  }
325  }
326  }
327 
328  return n;
329 }
330 
335 bool vpDirectShowGrabberImpl::createGraph()
336 {
337 
338  // Create the Capture Graph Builder.
339  hr = pBuild.CoCreateInstance(CLSID_CaptureGraphBuilder2, 0, CLSCTX_INPROC_SERVER);
340 
341  if (SUCCEEDED(hr)) {
342  // Create the Filter Graph Manager.
343  hr = pGraph.CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER);
344 
345  if (SUCCEEDED(hr)) {
346  // Initialize the Capture Graph Builder.
347  pBuild->SetFiltergraph(pGraph);
348 
349  return true;
350  }
351  }
352 
353  return false;
354 }
355 
361 bool vpDirectShowGrabberImpl::createSampleGrabber(CComPtr<IBaseFilter> &ppGrabberFilter)
362 {
363  // Creates the sample grabber
364  hr = ppGrabberFilter.CoCreateInstance(CLSID_SampleGrabber, nullptr, CLSCTX_INPROC_SERVER);
365 
366  if (FAILED(hr))
367  return false;
368 
369  // gets the SampleGrabber interface in order to configure it later
370  hr = ppGrabberFilter->QueryInterface(IID_ISampleGrabber, reinterpret_cast<void **>(&pGrabberI));
371 
372  if (FAILED(hr))
373  return false;
374 
375  // configure the grabber
376  AM_MEDIA_TYPE mt;
377  ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
378 
379  mt.majortype = MEDIATYPE_Video;
380 
381  // ask for a connection
382  mt.subtype = MEDIATYPE_nullptr;
383 
384  if (FAILED(hr = pGrabberI->SetMediaType(&mt)))
385  return false;
386 
387  // configure the callback of the grabber
388  pGrabberI->SetCallback(&sgCB, 1);
389 
390  // grab only one frame at a time
391  pGrabberI->SetOneShot(TRUE);
392 
393  // no need to bufferize the sample in the grabber
394  pGrabberI->SetBufferSamples(false);
395 
396  return true;
397 }
398 
408 bool vpDirectShowGrabberImpl::checkSourceType(CComPtr<IPin> &pCapSourcePin)
409 {
410  // retrieves the connected media type
411  AM_MEDIA_TYPE mt;
412  if (FAILED(pCapSourcePin->ConnectionMediaType(&mt)))
413  return false;
414 
415  if (mt.majortype != MEDIATYPE_Video)
416  return false;
417 
418  // Known RGB formats
419  if (mt.subtype == MEDIASUBTYPE_ARGB32 || mt.subtype == MEDIASUBTYPE_RGB32 || mt.subtype == MEDIASUBTYPE_RGB24 ||
420  mt.subtype == MEDIASUBTYPE_RGB555 || mt.subtype == MEDIASUBTYPE_RGB565 || mt.subtype == MEDIASUBTYPE_RGB8 ||
421  mt.subtype == MEDIASUBTYPE_RGB4 || mt.subtype == MEDIASUBTYPE_RGB1) {
422  // image orientation will be handled "automatically"
423  sgCB.specialMediaType = false;
424  }
425  // Known YUV formats
426  else if (mt.subtype == MEDIASUBTYPE_AYUV || mt.subtype == MEDIASUBTYPE_UYVY || mt.subtype == MEDIASUBTYPE_Y411 ||
427  mt.subtype == MEDIASUBTYPE_Y41P || mt.subtype == MEDIASUBTYPE_Y211 || mt.subtype == MEDIASUBTYPE_YUY2 ||
428  mt.subtype == MEDIASUBTYPE_YVYU || mt.subtype == MEDIASUBTYPE_YUYV || mt.subtype == MEDIASUBTYPE_IF09 ||
429  mt.subtype == MEDIASUBTYPE_IYUV || mt.subtype == MEDIASUBTYPE_YV12 || mt.subtype == MEDIASUBTYPE_YVU9) {
430  // image orientation will be handled "automatically"
431  sgCB.specialMediaType = false;
432  }
433  // FOURCC formats
434  else {
435  // invertedSource boolean will decide the bitmap orientation
436  sgCB.specialMediaType = true;
437 
438  DWORD format;
439  VIDEOINFOHEADER *pVih = reinterpret_cast<VIDEOINFOHEADER *>(mt.pbFormat);
440  BITMAPINFOHEADER bmpInfo = pVih->bmiHeader;
441 
442  // get the fourcc code
443  format = ((bmpInfo.biCompression & 0xFF000000) >> 24) | ((bmpInfo.biCompression & 0x00FF0000) >> 8) |
444  ((bmpInfo.biCompression & 0x0000FF00) << 8) | (bmpInfo.biCompression & 0x000000FF) << 24;
445 
446  std::cout << "This format is not one of the standard YUV or RGB format "
447  "supported by DirectShow.\n"
448  << "FourCC : " << (char)(bmpInfo.biCompression & 0x000000FF)
449  << (char)((bmpInfo.biCompression & 0x0000FF00) >> 8) << (char)((bmpInfo.biCompression & 0x00FF0000) >> 16)
450  << (char)((bmpInfo.biCompression & 0xFF000000) >> 24) << std::endl;
451 
452 // Y800 is top-down oriented so the image doesn't have to be flipped
453 // vertically
454  if (format == 'Y800') {
455  sgCB.invertedSource = false;
456  }
457  // cyuv seems to be the only yuv bottom-up oriented format (image has to
458  // be flipped)
459  else if (format == 'cyuv') {
460  sgCB.invertedSource = true;
461  }
462  // insert code for other fourcc formats here
463  // see fourcc.org to know which format is bottom-up oriented and thus
464  // needs invertedSource sets to true
465  else {
466  std::cout << "Unknown FourCC compression type, assuming top-down "
467  "orientation. Image may be inverted."
468  << std::endl;
469  sgCB.invertedSource = false; // consider that the image is topdown oriented by default
470  }
471  }
472 
473  return true;
474 }
475 
482 bool vpDirectShowGrabberImpl::connectSourceToGrabber(CComPtr<IBaseFilter> &_pCapSource,
483  CComPtr<IBaseFilter> &_pGrabberFilter)
484 {
485  /*
486  //get the capture source's output pin
487  CComPtr<IPin> pCapSourcePin;
488  if(FAILED(pBuild->FindPin(_pCapSource, PINDIR_OUTPUT, nullptr, nullptr, false, 0,
489  &pCapSourcePin))) return false;
490 
491  //get the grabber's input pin
492  CComPtr<IPin> pGrabberInputPin;
493  if(FAILED(pBuild->FindPin(_pGrabberFilter, PINDIR_INPUT, nullptr, nullptr, false,
494  0, &pGrabberInputPin))) return false;
495 
496  //connect the two of them
497  if(FAILED(pGraph->Connect(pCapSourcePin, pGrabberInputPin)))
498  return false;
499 
500  //not used anymore, we can release it
501  pGrabberInputPin.Release();
502  */
503  if (FAILED(hr = pBuild->RenderStream(nullptr, nullptr, _pCapSource, nullptr, _pGrabberFilter)))
504  return false;
505 
506  // get the Null renderer
507  CComPtr<IBaseFilter> pNull = nullptr;
508  if (FAILED(pNull.CoCreateInstance(CLSID_NullRenderer, nullptr, CLSCTX_INPROC_SERVER)))
509  return false;
510 
511  if (FAILED(pGraph->AddFilter(pNull, L"NullRenderer")) ||
512  FAILED(pBuild->RenderStream(nullptr, nullptr, _pGrabberFilter, nullptr, pNull)))
513  return false;
514 
515  // get the capture source's output pin
516  CComPtr<IPin> pCapSourcePin;
517  if (FAILED(pBuild->FindPin(_pCapSource, PINDIR_OUTPUT, nullptr, nullptr, false, 0, &pCapSourcePin)))
518  return false;
519  // checks the media type of the capture filter
520  // and if the image needs to be inverted
521  if (!checkSourceType(pCapSourcePin))
522  return false;
523 
524  // release the remaining interfaces
525  pCapSourcePin.Release();
526  pNull.Release();
527 
528  return true;
529 }
530 
536 bool vpDirectShowGrabberImpl::removeAll()
537 {
538  CComPtr<IEnumFilters> pEnum = nullptr;
539  CComPtr<IBaseFilter> pFilter;
540  ULONG cFetched;
541 
542  if (FAILED(hr = pGraph->EnumFilters(&pEnum)))
543  return false;
544 
545  while (pEnum->Next(1, &pFilter, &cFetched) == S_OK) {
546  if (FAILED(hr = pGraph->RemoveFilter(pFilter)))
547  return false;
548  pFilter.Release();
549  pEnum->Reset();
550  }
551 
552  pEnum.Release();
553  return true;
554 }
555 
564 void vpDirectShowGrabberImpl::acquire(vpImage<vpRGBa> &I)
565 {
566  if (init == false) {
567  close();
569  }
570 
571  // set the rgbaIm pointer on I (will be filled on the next framegrabber
572  // callback)
573  sgCB.rgbaIm = &I;
574  // there is an acquire demand (execute copy during callback)
575  sgCB.acqRGBaDemand = true;
576 
577  // Run the graph to grab a frame
578  pControl->Run();
579 
580  // Wait untill it's done
581  long ev;
582  hr = pEvent->WaitForCompletion(MAX_DELAY, &ev);
583 
584  width = I.getWidth();
585  height = I.getHeight();
586 
587  // wait for the end of the next callback (copy)
588  if (WaitForSingleObject(sgCB.copySem, MAX_DELAY) != WAIT_OBJECT_0)
589  throw(vpFrameGrabberException(vpFrameGrabberException::otherError, "Can't grab the frame, callback timeout"));
590 }
591 
600 void vpDirectShowGrabberImpl::acquire(vpImage<unsigned char> &I)
601 {
602  if (init == false) {
603  close();
605  }
606 
607  // set the grayIm pointer on I (will be filled on the next framegrabber
608  // callback)
609  sgCB.grayIm = &I;
610  // there is an acquire demand (execute copy during callback)
611  sgCB.acqGrayDemand = true;
612 
613  // Run the graph to grab a frame
614  pControl->Run();
615 
616  // Wait untill it's done
617  long ev;
618  hr = pEvent->WaitForCompletion(MAX_DELAY, &ev);
619 
620  width = I.getWidth();
621  height = I.getHeight();
622 
623  // wait for the end of the next callback (copy)
624  if (WaitForSingleObject(sgCB.copySem, MAX_DELAY) != WAIT_OBJECT_0)
625  throw(vpFrameGrabberException(vpFrameGrabberException::otherError, "Can't grab the frame, callback timeout"));
626 }
627 
634 bool vpDirectShowGrabberImpl::setDevice(unsigned int id)
635 {
636  if (init == false) {
637  close();
639  }
640 
641  // if n is invalid, or the device is already in use, quit
642  if (id >= nbDevices || deviceList[id].getState() == true)
643  return false;
644 
645  // we stop the graph
646  pControl->Stop();
647 
648  // then we can safely remove all the filters
649  if (!removeAll())
650  return false;
651 
652  // we release the previous source's interface
653  pCapSource.Release();
654 
655  // here reset inUse in the old DSDevice
656  deviceList[currentDevice].resetInUse();
657 
658  // we add the grabber back in the graph
659  pGraph->AddFilter(pGrabberFilter, L"SampleGrabber");
660 
661  // get the n-th device's filter
662  if (!getDevice(id, pCapSource))
663  return false;
664 
665  // we add the capture source to the filter graph
666  if (FAILED(hr = pGraph->AddFilter(pCapSource, L"Capture Filter")))
667  return false;
668 
669  // we connect the pins
670  if (!connectSourceToGrabber(pCapSource, pGrabberFilter))
671  return false;
672 
673  // get the current connected media type (needed by the callback)
674  if (FAILED(hr = pGrabberI->GetConnectedMediaType(&(sgCB.connectedMediaType)))) {
675  return false;
676  }
677 
678  // the device is now in use
679  deviceList[id].setInUse();
680  currentDevice = id;
681 
682  return true;
683 }
684 
688 void vpDirectShowGrabberImpl::displayDevices()
689 {
690  if (deviceList == nullptr) {
692  }
693 
694  for (unsigned int i = 0; i < nbDevices; i++)
695  std::cout << i << " : " << deviceList[i].getName() << std::endl;
696 
697  std::cout << "Current device : " << currentDevice << std::endl << std::endl;
698 }
699 
704 void vpDirectShowGrabberImpl::close()
705 {
706  // the current device isn't being used anymore
707  if (init) {
708  deviceList[currentDevice].resetInUse();
709  init = false;
710  }
711  if (initCo) {
712  // uninstalls COM
713  CoUninitialize();
714  initCo = false;
715  }
716 }
720 bool vpDirectShowGrabberImpl::setImageSize(unsigned int width, unsigned int height)
721 {
722  if (init == false) {
723  close();
725  }
726 
727  return setFormat(width, height, nullptr);
728 }
729 
733 bool vpDirectShowGrabberImpl::setFramerate(double framerate)
734 {
735  if (init == false) {
736  close();
738  }
739 
740  VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER *)sgCB.connectedMediaType.pbFormat;
741  return setFormat(pVih->bmiHeader.biWidth, pVih->bmiHeader.biHeight, framerate);
742 }
746 bool vpDirectShowGrabberImpl::setFormat(unsigned int width, unsigned int height, double framerate)
747 {
748  if (init == false) {
749  close();
751  }
752 
753  bool found = false;
754 
755  // gets the stream config interface
756  IAMStreamConfig *pConfig = nullptr;
757 
758  if (FAILED(hr = pBuild->FindInterface(&LOOK_UPSTREAM_ONLY, // Capture pin. / Preview pin
759  0, // Any media type.
760  pGrabberFilter, // Pointer to the grabber filter.
761  IID_IAMStreamConfig, (void **)&pConfig)))
762  return false;
763 
764  // gets the video control interface
765  IAMVideoControl *pVideoControl = nullptr;
766 
767  if (FAILED(hr = pBuild->FindInterface(&LOOK_UPSTREAM_ONLY, // Capture pin. / Preview pin
768  0, // Any media type.
769  pGrabberFilter, // Pointer to the grabber filter.
770  IID_IAMVideoControl, (void **)&pVideoControl)))
771  return false;
772 
773  // get the grabber's input pin
774  CComPtr<IPin> pCapSourcePin;
775  if (FAILED(pBuild->FindPin(pCapSource, PINDIR_OUTPUT, nullptr, nullptr, false, 0, &pCapSourcePin)))
776  return false;
777 
778  int iCount = 0, iSize = 0;
779  if (FAILED(hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize)))
780  return false;
781 
782  // Check the size to make sure we pass in the correct structure.
783  if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS)) {
784  // Use the video capabilities structure.
785 
786  for (int iFormat = 0; iFormat < iCount; iFormat++) {
787  VIDEO_STREAM_CONFIG_CAPS scc;
788  AM_MEDIA_TYPE *pmtConfig;
789  hr = pConfig->GetStreamCaps(iFormat, &pmtConfig, (BYTE *)&scc);
790 
791  if (SUCCEEDED(hr) && found == false) {
792  /* Examine the format, and possibly use it. */
793  if ((pmtConfig->majortype == sgCB.connectedMediaType.majortype) &&
794  (pmtConfig->subtype == sgCB.connectedMediaType.subtype) &&
795  (pmtConfig->formattype == sgCB.connectedMediaType.formattype) &&
796  (pmtConfig->cbFormat >= sizeof(VIDEOINFOHEADER)) && (pmtConfig->pbFormat != nullptr)) {
797  VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER *)pmtConfig->pbFormat;
798 
799  LONG lWidth = pVih->bmiHeader.biWidth;
800  LONG lHeight = pVih->bmiHeader.biHeight;
801  if (framerate != nullptr) {
802  if ((unsigned int)lWidth == width && (unsigned int)lHeight == height) {
803 
804  pVih->AvgTimePerFrame = (LONGLONG)(10000000 / framerate);
805  // set the capture media type and the grabber media type
806  if (FAILED(hr = pConfig->SetFormat(pmtConfig)) || FAILED(hr = pGrabberI->SetMediaType(pmtConfig)))
807  return false;
808  // Run the graph to grab a frame
809  pControl->Run();
810 
811  // get the current connected media type (needed by the callback)
812  if (FAILED(hr = pGrabberI->GetConnectedMediaType(&(sgCB.connectedMediaType))))
813  return false;
814  pVih = (VIDEOINFOHEADER *)sgCB.connectedMediaType.pbFormat;
815  LONGLONG ActualFrameDuration;
816  if (FAILED(hr = pVideoControl->GetCurrentActualFrameRate(pCapSourcePin, &ActualFrameDuration)))
817  std::cout << "Current format (not sure): " << width << " x " << height << " at "
818  << 10000000 / pVih->AvgTimePerFrame << " fps" << std::endl
819  << std::endl;
820  else {
821  std::cout << "Current format : " << width << " x " << height << " at " << 10000000 / ActualFrameDuration
822  << " fps" << std::endl
823  << std::endl;
824  pVih->AvgTimePerFrame = ActualFrameDuration;
825  }
826  found = true;
827  }
828  }
829  else {
830  if ((unsigned int)lWidth == width && (unsigned int)lHeight == height) {
831  pVih->AvgTimePerFrame = scc.MinFrameInterval;
832  // set the capture media type and the grabber media type
833  if (FAILED(hr = pConfig->SetFormat(pmtConfig)) || FAILED(hr = pGrabberI->SetMediaType(pmtConfig)))
834  return false;
835  // get the current connected media type (needed by the callback)
836  if (FAILED(hr = pGrabberI->GetConnectedMediaType(&(sgCB.connectedMediaType))))
837  return false;
838  pVih = (VIDEOINFOHEADER *)sgCB.connectedMediaType.pbFormat;
839  found = true;
840  std::cout << "Current format : " << width << " x " << height << " at "
841  << (10000000 / pVih->AvgTimePerFrame) << " fps" << std::endl
842  << std::endl;
843  }
844  }
845  }
846  }
847  // Delete the media type when you are done.
848  MyDeleteMediaType(pmtConfig);
849  }
850  }
851  if (!found)
852  if (framerate != nullptr)
853  std::cout << "The " << width << " x " << height << " at " << framerate
854  << " fps source image format is not available. " << std::endl
855  << std::endl;
856  else
857  std::cout << "The " << width << " x " << height << "source image size is not available. " << std::endl
858  << std::endl;
859 
860  return found;
861 }
868 void vpDirectShowGrabberImpl::getFormat(unsigned int &width, unsigned int &height, double &framerate)
869 {
870  if (init == false) {
871  close();
873  }
874  VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER *)sgCB.connectedMediaType.pbFormat;
875  width = (unsigned int)pVih->bmiHeader.biWidth;
876  height = (unsigned int)pVih->bmiHeader.biHeight;
877  framerate = (double)(10000000 / pVih->AvgTimePerFrame);
878 }
882 bool vpDirectShowGrabberImpl::getStreamCapabilities()
883 {
884  if (init == false) {
885  close();
887  }
888 
889  // gets the stream config interface
890  IAMStreamConfig *pConfig = nullptr;
891 
892  if (FAILED(hr = pBuild->FindInterface(&LOOK_UPSTREAM_ONLY, // Capture pin. / Preview pin
893  0, // Any media type.
894  pGrabberFilter, // Pointer to the grabber filter.
895  IID_IAMStreamConfig, (void **)&pConfig)))
896  return false;
897 
898  int iCount = 0, iSize = 0;
899  if (FAILED(hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize)))
900  return false;
901 
902  // Check the size to make sure we pass in the correct structure.
903  if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS)) {
904  std::cout << "Available MediaTypes : " << std::endl << std::endl;
905  // Use the video capabilities structure.
906  for (int iFormat = 0; iFormat < iCount; iFormat++) {
907  VIDEO_STREAM_CONFIG_CAPS scc;
908  AM_MEDIA_TYPE *pmtConfig;
909  hr = pConfig->GetStreamCaps(iFormat, &pmtConfig, (BYTE *)&scc);
910 
911  if (SUCCEEDED(hr)) {
912  /* Examine the format, and possibly use it. */
913  VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER *)pmtConfig->pbFormat;
914 
915  std::cout << "MediaType : " << iFormat << std::endl;
916 
917  if (pmtConfig->subtype == MEDIASUBTYPE_ARGB32)
918  std::cout << "subtype (not supported): MEDIASUBTYPE_ARGB32" << std::endl;
919  else if (pmtConfig->subtype == MEDIASUBTYPE_RGB32)
920  std::cout << "subtype : MEDIASUBTYPE_RGB32" << std::endl;
921  else if (pmtConfig->subtype == MEDIASUBTYPE_RGB24)
922  std::cout << "subtype : MEDIASUBTYPE_RGB24" << std::endl;
923  else if (pmtConfig->subtype == MEDIASUBTYPE_RGB555)
924  std::cout << "subtype (not supported): MEDIASUBTYPE_RGB555" << std::endl;
925  else if (pmtConfig->subtype == MEDIASUBTYPE_RGB565)
926  std::cout << "subtype (not supported): MEDIASUBTYPE_RGB565" << std::endl;
927  else if (pmtConfig->subtype == MEDIASUBTYPE_RGB8)
928  std::cout << "subtype (not supported): MEDIASUBTYPE_RGB8" << std::endl;
929  else if (pmtConfig->subtype == MEDIASUBTYPE_RGB4)
930  std::cout << "subtype (not supported): MEDIASUBTYPE_RGB4" << std::endl;
931  else if (pmtConfig->subtype == MEDIASUBTYPE_RGB1)
932  std::cout << "subtype (not supported): MEDIASUBTYPE_RGB1" << std::endl;
933  else if (pmtConfig->subtype == MEDIASUBTYPE_YV12)
934  std::cout << "subtype : MEDIASUBTYPE_YV12" << std::endl;
935  else if (pmtConfig->subtype == MEDIASUBTYPE_YVU9)
936  std::cout << "subtype : MEDIASUBTYPE_YVU9" << std::endl;
937  else if (pmtConfig->subtype == MEDIASUBTYPE_YUY2)
938  std::cout << "subtype : MEDIASUBTYPE_YUY2" << std::endl;
939  else if (pmtConfig->subtype == MEDIASUBTYPE_YUYV)
940  std::cout << "subtype : MEDIASUBTYPE_YUYV" << std::endl;
941  else if (pmtConfig->subtype == MEDIASUBTYPE_YVYU)
942  std::cout << "subtype : MEDIASUBTYPE_YVYU" << std::endl;
943  else if (pmtConfig->subtype == MEDIASUBTYPE_IYUV)
944  std::cout << "subtype : MEDIASUBTYPE_IYUV" << std::endl;
945  else if (pmtConfig->subtype == MEDIASUBTYPE_UYVY)
946  std::cout << "subtype : MEDIASUBTYPE_UYVY" << std::endl;
947  else if ((((pVih->bmiHeader.biCompression & 0xFF000000) >> 24) |
948  ((pVih->bmiHeader.biCompression & 0x00FF0000) >> 8) |
949  ((pVih->bmiHeader.biCompression & 0x0000FF00) << 8) |
950  ((pVih->bmiHeader.biCompression & 0x000000FF) << 24)) == 'I420')
951  std::cout << "subtype : I420" << std::endl;
952  else
953  std::cout << "subtype (not supported) :" << (char)(pVih->bmiHeader.biCompression & 0x000000FF)
954  << (char)((pVih->bmiHeader.biCompression & 0x0000FF00) >> 8)
955  << (char)((pVih->bmiHeader.biCompression & 0x00FF0000) >> 16)
956  << (char)((pVih->bmiHeader.biCompression & 0xFF000000) >> 24) << std::endl;
957 
958  std::cout << "image size : " << pVih->bmiHeader.biWidth << " x " << pVih->bmiHeader.biHeight << std::endl;
959  std::cout << "framerate range: [" << 10000000 / scc.MaxFrameInterval << "," << 10000000 / scc.MinFrameInterval
960  << "]" << std::endl
961  << std::endl;
962 
963 /*
964  long frameRateNum;
965  LONGLONG *frameRateList;
966  if(FAILED(hr =
967  pVideoControl->GetFrameRateList(pCapSourcePin,iFormat,dimensions,
968  //inputs &frameRateNum, &frameRateList))) //outputs return false;
969  for(int i=0; i<(int)frameRateNum ;
970  i++)
971  {
972  std::cout<<(float)(10000000/frameRateList[i])<<"
973  fps"<<std::endl;
974  }
975  std::cout<<std::endl;
976 */
977  }
978  // Delete the media type when you are done.
979  MyDeleteMediaType(pmtConfig);
980  }
981  }
982  return true;
983 }
987 bool vpDirectShowGrabberImpl::setMediaType(int mediaTypeID)
988 {
989  if (init == false) {
990  close();
992  }
993 
994  // gets the stream config interface
995  IAMStreamConfig *pConfig = nullptr;
996 
997  if (FAILED(hr = pBuild->FindInterface(&LOOK_UPSTREAM_ONLY, // Capture pin. / Preview pin
998  0, // Any media type.
999  pGrabberFilter, // Pointer to the grabber filter.
1000  IID_IAMStreamConfig, (void **)&pConfig)))
1001  return false;
1002 
1003  VIDEO_STREAM_CONFIG_CAPS scc;
1004  AM_MEDIA_TYPE *pmtConfig;
1005  hr = pConfig->GetStreamCaps(mediaTypeID, &pmtConfig, (BYTE *)&scc);
1006 
1007  if (SUCCEEDED(hr)) {
1008  VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER *)pmtConfig->pbFormat;
1009  pVih->AvgTimePerFrame = scc.MinFrameInterval;
1010  // set the capture media type and the grabber media type
1011  if (FAILED(hr = pGrabberI->SetMediaType(pmtConfig)) || FAILED(hr = pConfig->SetFormat(pmtConfig)))
1012  return false;
1013  // get the current connected media type (needed by the callback)
1014  if (FAILED(hr = pGrabberI->GetConnectedMediaType(&(sgCB.connectedMediaType))))
1015  return false;
1016  }
1017  // Delete the media type when you are done.
1018  MyDeleteMediaType(pmtConfig);
1019  return true;
1020 }
1021 
1022 /*
1023  Get current capture MediaType
1024  \return mediaTypeID (-1 if failed)
1025 */
1026 int vpDirectShowGrabberImpl::getMediaType()
1027 {
1028  if (init == false) {
1029  close();
1030  throw(vpFrameGrabberException(vpFrameGrabberException::initializationError, "Initialization not done"));
1031  }
1032 
1033  int mediaTypeID = -1;
1034  VIDEOINFOHEADER *pVihConnected = (VIDEOINFOHEADER *)sgCB.connectedMediaType.pbFormat;
1035 
1036  // gets the stream config interface
1037  IAMStreamConfig *pConfig = nullptr;
1038 
1039  if (FAILED(hr = pBuild->FindInterface(&LOOK_UPSTREAM_ONLY, // Capture pin. / Preview pin
1040  0, // Any media type.
1041  pGrabberFilter, // Pointer to the grabber filter.
1042  IID_IAMStreamConfig, (void **)&pConfig)))
1043  return -1;
1044 
1045  int iCount = 0, iSize = 0;
1046  if (FAILED(hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize)))
1047  return -1;
1048 
1049  // Check the size to make sure we pass in the correct structure.
1050  if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS)) {
1051  // Use the video capabilities structure.
1052  for (int iFormat = 0; iFormat < iCount; iFormat++) {
1053  VIDEO_STREAM_CONFIG_CAPS scc;
1054  AM_MEDIA_TYPE *pmtConfig;
1055  hr = pConfig->GetStreamCaps(iFormat, &pmtConfig, (BYTE *)&scc);
1056 
1057  if (SUCCEEDED(hr)) {
1058  /* Examine the format, and possibly use it. */
1059  if ((pmtConfig->majortype == sgCB.connectedMediaType.majortype) &&
1060  (pmtConfig->subtype == sgCB.connectedMediaType.subtype) &&
1061  (pmtConfig->formattype == sgCB.connectedMediaType.formattype) &&
1062  (pmtConfig->cbFormat >= sizeof(VIDEOINFOHEADER)) && (pmtConfig->pbFormat != nullptr)) {
1063  VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER *)pmtConfig->pbFormat;
1064  if (pVih->bmiHeader.biWidth == pVihConnected->bmiHeader.biWidth &&
1065  pVih->bmiHeader.biHeight == pVihConnected->bmiHeader.biHeight)
1066  mediaTypeID = iFormat;
1067  }
1068  }
1069  // Delete the media type when you are done.
1070  MyDeleteMediaType(pmtConfig);
1071  }
1072  }
1073  return mediaTypeID;
1074 }
1075 
1080 void vpDirectShowGrabberImpl::MyDeleteMediaType(AM_MEDIA_TYPE *pmt)
1081 {
1082  if (pmt != nullptr) {
1083  MyFreeMediaType(*pmt); // See FreeMediaType for the implementation.
1084  CoTaskMemFree(pmt);
1085  }
1086 }
1087 
1091 void vpDirectShowGrabberImpl::MyFreeMediaType(AM_MEDIA_TYPE &mt)
1092 {
1093  if (mt.cbFormat != 0) {
1094  CoTaskMemFree((PVOID)mt.pbFormat);
1095  mt.cbFormat = 0;
1096  mt.pbFormat = nullptr;
1097  }
1098  if (mt.pUnk != nullptr) {
1099  // Unecessary because pUnk should not be used, but safest.
1100  mt.pUnk->Release();
1101  mt.pUnk = nullptr;
1102  }
1103 }
1104 END_VISP_NAMESPACE
1105 #elif !defined(VISP_BUILD_SHARED_LIBS)
1106 // Work around to avoid warning:
1107 // libvisp_sensor.a(vpDirectShowGrabberImpl.cpp.o) has no symbols
1108 void dummy_vpDirectShowGrabberImpl() { };
1109 #endif
1110 #endif
Error that can be emitted by the vpFrameGrabber class and its derivates.
@ initializationError
Grabber initialization error.
@ otherError
Grabber returned an other error.
unsigned int getWidth() const
Definition: vpImage.h:242
unsigned int getHeight() const
Definition: vpImage.h:181
void init(unsigned int h, unsigned int w, Type value)
Definition: vpImage.h:372