Panda3D
webcamVideoDS.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file webcamVideoDS.cxx
10  * @author jyelon
11  * @date 2007-11-01
12  *
13  * It goes against Panda3D coding style conventions to hide an
14  * entire class in a C++ file and not expose it through header
15  * files at all. However, in this case, these classes are so full
16  * of OS-specific junk that I feel it is better to hide them
17  * entirely. - Josh
18  *
19  * This code was created by studying and adapting the VDOGRAB
20  * library by Shu-Kai Yang and the videoInput library by Theodore
21  * Watson. We owe both of them a great deal of thanks for
22  * figuring all this out. Both of their libraries have
23  * informal licenses (the "do whatever you want and don't blame
24  * me" sort), so I think there's not a problem using their code.
25  */
26 
27 #if defined(HAVE_DIRECTCAM) && !defined(CPPPARSER)
28 
29 #ifndef WIN32_LEAN_AND_MEAN
30 #define WIN32_LEAN_AND_MEAN 1
31 #endif
32 
33 #undef Configure
34 
35 #pragma warning(disable:4100) // warning C4100: unreferenced formal parameter
36 #pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union
37 #pragma warning(disable:4511) // warning C4511: copy constructor could not be generated
38 #pragma warning(disable:4512) // warning C4512: assignment operator could not be generated
39 #pragma warning(disable:4514) // warning C4514: "unreferenced inline function has been removed"
40 
41 #include <windows.h>
42 #include <windowsx.h>
43 #include <olectl.h>
44 #include <mmsystem.h>
45 #include <strmif.h> // Generated IDL header file for streams interfaces
46 #include <amvideo.h> // ActiveMovie video interfaces and definitions
47 #include <amaudio.h> // ActiveMovie audio interfaces and definitions
48 #include <control.h> // generated from control.odl
49 #include <evcode.h> // event code definitions
50 #include <uuids.h> // declaration of type GUIDs and well-known clsids
51 #include <errors.h> // HRESULT status and error definitions
52 #include <edevdefs.h> // External device control interface defines
53 #include <audevcod.h> // audio filter device error event codes
54 #include <dvdevcod.h> // DVD error event codes
55 #include <comutil.h>
56 
57 #include <wchar.h>
58 #include <string.h>
59 #include <windows.h>
60 
61 using std::cerr;
62 using std::string;
63 
64 /*
65  * This used to work back when qedit.h still existed. The hacks served to
66  * prevent it from including the defunct dxtrans.h. #pragma include_alias(
67  * "dxtrans.h", "qedit.h" ) #define __IDxtCompositor_INTERFACE_DEFINED__
68  * #define __IDxtAlphaSetter_INTERFACE_DEFINED__ #define
69  * __IDxtJpeg_INTERFACE_DEFINED_ #define __IDxtKey_INTERFACE_DEFINED__ #define
70  * IDXEffect IUnknown #include <qedit.h>
71  */
72 
73 // We can use this fugly hack to still access the qedit.h interfaces. When
74 // this stops working, we'll have to just copy the relevant definitions to
75 // this file.
76 #import "libid:78530B68-61F9-11D2-8CAD-00A024580902" \
77  no_namespace named_guids raw_interfaces_only no_implementation \
78  exclude("_AMMediaType", "_FilterState", "IReferenceClock", "IMediaFilter", \
79  "_PinDirection", "IEnumMediaTypes", "IFilterGraph", "_FilterInfo", \
80  "IGraphBuilder", "IBaseFilter", "_PinInfo", "IPin", "IEnumPins", \
81  "IEnumFilters", "IEnumMediaTypes", "IAMSetErrorLog","IAMTimelineObj", \
82  "IMediaDet", "IMediaSample", "IPersistStream", "IPersist", "IStream", \
83  "ISequentialStream", "_LARGE_INTEGER", "_ULARGE_INTEGER", \
84  "tagSTATSTG", "_FILETIME", "IPropertyBag", "IErrorLog")
85 
86 /**
87  * The directshow implementation of webcams.
88  */
89 
90 class WebcamVideoDS : public WebcamVideo
91 {
92 public:
93  static void find_all_webcams_ds();
94  friend void find_all_webcams_ds();
95 
96 private:
97  typedef pvector<PT(WebcamVideoDS)> WebcamVideoList;
98 
99  static int media_score(AM_MEDIA_TYPE *media);
100  static int media_x(AM_MEDIA_TYPE *media);
101  static int media_y(AM_MEDIA_TYPE *media);
102  static int media_fps(AM_MEDIA_TYPE *media);
103  static void delete_media_type(AM_MEDIA_TYPE *media);
104  static string bstr_to_string(const BSTR &source);
105  static string get_moniker_name(IMoniker *pMoniker);
106  static void add_device(WebcamVideoList &list, IMoniker *pMoniker, AM_MEDIA_TYPE *media);
107 
108  virtual PT(MovieVideoCursor) open();
109 
110  IMoniker *_moniker;
111  AM_MEDIA_TYPE *_media;
112 
113  friend class WebcamVideoCursorDS;
114 
115 public:
116  static TypeHandle get_class_type() {
117  return _type_handle;
118  }
119  static void init_type() {
120  WebcamVideo::init_type();
121  register_type(_type_handle, "WebcamVideoDS",
122  WebcamVideo::get_class_type());
123  }
124  virtual TypeHandle get_type() const {
125  return get_class_type();
126  }
127  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
128 
129 private:
130  static TypeHandle _type_handle;
131 };
132 
133 TypeHandle WebcamVideoDS::_type_handle;
134 
135 /**
136  * The directshow implementation of webcams.
137  */
138 
139 
140 class WebcamVideoCursorDS : public MovieVideoCursor
141 {
142 public:
143  WebcamVideoCursorDS(WebcamVideoDS *src);
144  virtual ~WebcamVideoCursorDS();
145  virtual PT(Buffer) fetch_buffer();
146 
147 public:
148  void cleanup();
149 
150  class CSampleGrabberCB : public ISampleGrabberCB
151  {
152  public:
153  WebcamVideoCursorDS *_host;
154 
155  ULONG __stdcall AddRef() { return 2; }
156  ULONG __stdcall Release() { return 1; }
157 
158  HRESULT __stdcall QueryInterface(REFIID riid, void ** ppv);
159  HRESULT __stdcall SampleCB(double SampleTime, IMediaSample *pSample);
160  HRESULT __stdcall BufferCB(double dblSampleTime, BYTE *pBuffer, long lBufferSize);
161  };
162 
163  unsigned char *_buffer;
164  IMediaSample *_saved;
165 
166  IGraphBuilder *_pGraphBuilder;
167  ICaptureGraphBuilder2 *_pCaptureBuilder;
168  IBaseFilter *_pSrcFilter;
169  IAMStreamConfig *_pStreamConfig;
170  ISampleGrabber *_pSampleGrabber;
171  IBaseFilter *_pStreamRenderer;
172  IMediaControl *_pMediaCtrl;
173  // IMemAllocator *_pAllocator;
174  CSampleGrabberCB _sample_cb;
175 
176 public:
177  static TypeHandle get_class_type() {
178  return _type_handle;
179  }
180  static void init_type() {
181  MovieVideoCursor::init_type();
182  register_type(_type_handle, "WebcamVideoCursorDS",
183  MovieVideoCursor::get_class_type());
184  }
185  virtual TypeHandle get_type() const {
186  return get_class_type();
187  }
188  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
189 
190 private:
191  static TypeHandle _type_handle;
192 };
193 
194 TypeHandle WebcamVideoCursorDS::_type_handle;
195 
196 /**
197  * Evaluate an AM_MEDIA_TYPE to determine how desirable it is for our
198  * purposes. Lower is better.
199  */
200 int WebcamVideoDS::
201 media_score(AM_MEDIA_TYPE *media) {
202  const GUID &subtype = media->subtype;
203  if (subtype == MEDIASUBTYPE_RGB24) return 1;
204  if (subtype == MEDIASUBTYPE_RGB32) return 2;
205  if (subtype == MEDIASUBTYPE_RGB555) return 3;
206  if (subtype == MEDIASUBTYPE_RGB565) return 3;
207  return 4;
208 }
209 
210 /**
211  * Returns the x-resolution of the AM_MEDIA_TYPE
212  */
213 int WebcamVideoDS::
214 media_x(AM_MEDIA_TYPE *media) {
215  VIDEOINFOHEADER *header = (VIDEOINFOHEADER*)(media->pbFormat);
216  return (header->bmiHeader.biWidth);
217 }
218 
219 /**
220  * Returns the y-resolution of the AM_MEDIA_TYPE
221  */
222 int WebcamVideoDS::
223 media_y(AM_MEDIA_TYPE *media) {
224  VIDEOINFOHEADER *header = (VIDEOINFOHEADER*)(media->pbFormat);
225  return (header->bmiHeader.biHeight);
226 }
227 
228 /**
229  * Returns the frame-rate of the AM_MEDIA_TYPE
230  */
231 int WebcamVideoDS::
232 media_fps(AM_MEDIA_TYPE *media) {
233  VIDEOINFOHEADER *header = (VIDEOINFOHEADER*)(media->pbFormat);
234  return int(10000000.0 / (header->AvgTimePerFrame));
235 }
236 
237 /**
238  * Free all memory of the AM_MEDIA_TYPE
239  */
240 void WebcamVideoDS::
241 delete_media_type(AM_MEDIA_TYPE *pmt) {
242  if (pmt == nullptr) {
243  return;
244  }
245  if (pmt->cbFormat != 0) {
246  CoTaskMemFree((PVOID)pmt->pbFormat);
247  pmt->cbFormat = 0;
248  pmt->pbFormat = nullptr;
249  }
250  if (pmt->pUnk != nullptr) {
251  // Unecessary because pUnk should not be used, but safest.
252  pmt->pUnk->Release();
253  pmt->pUnk = nullptr;
254  }
255  CoTaskMemFree(pmt);
256 }
257 
258 /**
259  * Converts a visual basic BSTR to a C++ string.
260  */
261 string WebcamVideoDS::
262 bstr_to_string(const BSTR &source) {
263  string res = "";
264  int count = 0;
265  while( source[count] != 0x00 ) {
266  res.push_back(source[count]);
267  count++;
268  }
269  return res;
270 }
271 
272 /**
273  * Obtains the text name associated with an IMoniker
274  */
275 string WebcamVideoDS::
276 get_moniker_name(IMoniker *pMoniker) {
277  string res = "Unknown Device";
278  IPropertyBag *propBag=nullptr;
279  VARIANT name; HRESULT hResult;
280  pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&propBag);
281  VariantInit(&name);
282  hResult = propBag->Read(L"FriendlyName", &name, 0);
283  if (!hResult != S_OK) {
284  res = bstr_to_string(name.bstrVal);
285  goto done;
286  }
287  hResult = propBag->Read(L"Description", &name, 0);
288  if (!hResult != S_OK) {
289  res = bstr_to_string(name.bstrVal);
290  goto done;
291  }
292  done:
293  VariantClear(&name);
294  propBag->Release();
295  return res;
296 }
297 
298 /**
299  * Creates a new WebcamVideoDS and adds it to the list, unless there is
300  * already a very similar configuration in the list. If there is already a
301  * very similar configuration, this routine will leave one or the other on the
302  * list based on a scoring system.
303  */
304 void WebcamVideoDS::
305 add_device(WebcamVideoList &list, IMoniker *pMoniker, AM_MEDIA_TYPE *media) {
306  for (int i=0; i<(int)list.size(); i++) {
307  if ((list[i]->_moniker == pMoniker)&&
308  (media_x(list[i]->_media) == media_x(media))&&
309  (media_y(list[i]->_media) == media_y(media))) {
310  int oldscore = media_score(list[i]->_media);
311  if (media_score(media) < oldscore) {
312  delete_media_type(list[i]->_media);
313  list[i]->_media = media;
314  } else {
315  delete_media_type(media);
316  }
317  return;
318  }
319  }
320  PT(WebcamVideoDS) wc = new WebcamVideoDS;
321  wc->set_name(get_moniker_name(pMoniker));
322  wc->_size_x = media_x(media);
323  wc->_size_y = media_y(media);
324  wc->_fps = media_fps(media);
325  wc->_moniker = pMoniker;
326  wc->_media = media;
327  list.push_back(wc);
328  cerr << "Added device: DirectShow: " << wc << "\n";
329 }
330 
331 
332 /**
333  * Finds all DirectShow webcams and adds them to the global list _all_webcams.
334  */
335 void WebcamVideoDS::
336 find_all_webcams_ds() {
337 
338  pvector <PT(WebcamVideoDS)> list;
339 
340  ICreateDevEnum *pCreateDevEnum=nullptr;
341  IEnumMoniker *pEnumMoniker=nullptr;
342  IGraphBuilder *pGraphBuilder=nullptr;
343  ICaptureGraphBuilder2 *pCaptureGraphBuilder2=nullptr;
344  IMoniker *pMoniker=nullptr;
345  IBaseFilter *pBaseFilter=nullptr;
346  IAMStreamConfig *pStreamConfig=nullptr;
347  HRESULT hResult;
348  ULONG cFetched;
349 
350  hResult=CoCreateInstance(CLSID_FilterGraph, nullptr, CLSCTX_INPROC,
351  IID_IGraphBuilder,(void**)&pGraphBuilder);
352  if (hResult != S_OK) goto cleanup;
353  hResult=CoCreateInstance(CLSID_CaptureGraphBuilder2, nullptr, CLSCTX_INPROC,
354  IID_ICaptureGraphBuilder2, (void**)&pCaptureGraphBuilder2);
355  if (hResult != S_OK) goto cleanup;
356  hResult = pCaptureGraphBuilder2->SetFiltergraph(pGraphBuilder);
357  if (hResult != S_OK) goto cleanup;
358  hResult=CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER,
359  IID_ICreateDevEnum, (void**)&pCreateDevEnum);
360  if (hResult != S_OK) goto cleanup;
361  hResult=pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
362  &pEnumMoniker, 0);
363  if(hResult != S_OK) goto cleanup;
364 
365  while(1) {
366  if (pMoniker) { pMoniker->Release(); pMoniker=0; }
367  if (pBaseFilter) { pBaseFilter->Release(); pBaseFilter=0; }
368  if (pStreamConfig) { pStreamConfig->Release(); pStreamConfig=0; }
369 
370  hResult = pEnumMoniker->Next(1, &pMoniker, &cFetched);
371  if (hResult != S_OK) break;
372 
373  hResult = pMoniker->BindToObject(nullptr,nullptr,IID_IBaseFilter, (void**)&pBaseFilter);
374  if (hResult != S_OK) continue;
375  hResult = pCaptureGraphBuilder2->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pBaseFilter,
376  IID_IAMStreamConfig, (void **)&pStreamConfig);
377  if (hResult != S_OK) continue;
378  int iCount, iSize;
379  hResult = pStreamConfig->GetNumberOfCapabilities(&iCount, &iSize);
380  if (hResult != S_OK || (iSize != sizeof(VIDEO_STREAM_CONFIG_CAPS))) continue;
381  for (int iFormat=0; iFormat<iCount; iFormat++) {
382  AM_MEDIA_TYPE *mtype;
383  VIDEO_STREAM_CONFIG_CAPS caps;
384  hResult = pStreamConfig->GetStreamCaps(iFormat, &mtype, (BYTE*)&caps);
385  if (mtype->majortype == MEDIATYPE_Video) {
386  VIDEOINFOHEADER *header = (VIDEOINFOHEADER*)(mtype->pbFormat);
387  header->bmiHeader.biWidth = caps.MaxOutputSize.cx;
388  header->bmiHeader.biHeight = caps.MaxOutputSize.cy;
389  add_device(list, pMoniker, mtype);
390  }
391  }
392 
393  pMoniker = 0;
394  }
395 
396  cleanup:
397  if (pCreateDevEnum) { pCreateDevEnum->Release(); pCreateDevEnum=0; }
398  if (pEnumMoniker) { pEnumMoniker->Release(); pEnumMoniker=0; }
399  if (pGraphBuilder) { pGraphBuilder->Release(); pGraphBuilder=0; }
400  if (pCaptureGraphBuilder2) { pCaptureGraphBuilder2->Release(); pCaptureGraphBuilder2=0; }
401  if (pMoniker) { pMoniker->Release(); pMoniker=0; }
402  if (pBaseFilter) { pBaseFilter->Release(); pBaseFilter=0; }
403  if (pStreamConfig) { pStreamConfig->Release(); pStreamConfig=0; }
404 
405  for (int i=0; i<(int)list.size(); i++) {
406  WebcamVideoDS *obj = list[i];
407  _all_webcams.push_back(obj);
408  }
409 }
410 
411 void find_all_webcams_ds() {
412  WebcamVideoDS::init_type();
413  WebcamVideoCursorDS::init_type();
414  WebcamVideoDS::find_all_webcams_ds();
415 }
416 
417 /**
418  * Open this video, returning a MovieVideoCursor.
419  */
420 PT(MovieVideoCursor) WebcamVideoDS::
421 open() {
422  return new WebcamVideoCursorDS(this);
423 }
424 
425 /**
426  *
427  */
428 WebcamVideoCursorDS::
429 WebcamVideoCursorDS(WebcamVideoDS *src) :
430  MovieVideoCursor(src),
431  _pGraphBuilder(nullptr),
432  _pCaptureBuilder(nullptr),
433  _pSrcFilter(nullptr),
434  _pStreamConfig(nullptr),
435  _pStreamRenderer(nullptr),
436  _pMediaCtrl(nullptr)
437 {
438  AM_MEDIA_TYPE mediaType;
439  VIDEOINFOHEADER *pVideoInfo;
440 
441  HRESULT hResult;
442 
443  hResult=CoCreateInstance(CLSID_FilterGraph, nullptr, CLSCTX_INPROC,
444  IID_IGraphBuilder,(void**)&_pGraphBuilder);
445  if(hResult != S_OK) { cleanup(); return; }
446 
447  hResult=CoCreateInstance(CLSID_CaptureGraphBuilder2, nullptr, CLSCTX_INPROC,
448  IID_ICaptureGraphBuilder2, (void**)&_pCaptureBuilder);
449  if(hResult != S_OK) { cleanup(); return; }
450 
451  _pCaptureBuilder->SetFiltergraph(_pGraphBuilder);
452  cerr << " IID_IGraphBuilder & IID_ICaptureGraphBuilder2 are established.\n";
453 
454  hResult=_pGraphBuilder->QueryInterface(IID_IMediaControl, (void **)&_pMediaCtrl);
455  if(hResult != S_OK)
456  { cerr << " Can not get the IID_IMediaControl interface!";
457  cleanup(); return; }
458  cerr << " IID_IMediaControl interface is acquired.\n";
459 
460  src->_moniker->BindToObject(nullptr,nullptr,IID_IBaseFilter, (void**)&_pSrcFilter);
461  if(_pSrcFilter == nullptr)
462  { cerr << " Such capture device is not found.\n";
463  cleanup(); return; }
464  cerr << " The capture filter is acquired.\n";
465 
466  hResult=_pGraphBuilder->AddFilter(_pSrcFilter, L"Capture Filter");
467  if(hResult != DD_OK)
468  { cerr << " The capture filter can not be added to the graph.\n";
469  cleanup(); return; }
470  cerr << " The capture filter has been added to the graph.\n";
471 
472 
473  hResult = _pCaptureBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, _pSrcFilter,
474  IID_IAMStreamConfig, (void **)&_pStreamConfig);
475  if (hResult != S_OK) {
476  cerr << "Could not get stream config interface.\n";
477  cleanup(); return;
478  }
479  hResult = _pStreamConfig->SetFormat(src->_media);
480  if (hResult != S_OK) {
481  cerr << "Could not select desired video resolution\n";
482  cleanup(); return;
483  }
484 
485  hResult = CoCreateInstance(CLSID_SampleGrabber, nullptr, CLSCTX_INPROC, IID_PPV_ARGS(&_pSampleGrabber));
486  if(hResult != S_OK)
487  { cerr << " Can not create the sample grabber, maybe qedit.dll is not registered?";
488  cleanup(); return; }
489 
490 
491  // hResult = CoCreateInstance(CLSID_SampleGrabber,) CComQIPtr< IBaseFilter,
492  // &IID_IBaseFilter > pGrabberFilter(_pSampleGrabber);
493  IBaseFilter *pGrabberFilter = nullptr;
494  hResult = _pSampleGrabber->QueryInterface(IID_PPV_ARGS(&pGrabberFilter));
495  cerr << " IID_IBaseFilter of CLSID_SampleGrabber is acquired.\n";
496 
497  ZeroMemory(&mediaType, sizeof(AM_MEDIA_TYPE));
498  mediaType.majortype=MEDIATYPE_Video;
499  mediaType.subtype=MEDIASUBTYPE_RGB24;
500  hResult=_pSampleGrabber->SetMediaType(&mediaType);
501  if(hResult != S_OK)
502  { cerr << " Fail to set the media type!";
503  cleanup(); return; }
504  cerr << " The media type of the sample grabber is set 24-bit RGB.\n";
505 
506  hResult=_pGraphBuilder->AddFilter(pGrabberFilter, L"Sample Grabber");
507  if(hResult != S_OK)
508  { cerr << " Fail to add the sample grabber to the graph.";
509  cleanup(); return; }
510  cerr << " The sample grabber has been added to the graph.\n";
511 
512  // used to give the video stream somewhere to go to.
513  hResult = CoCreateInstance(CLSID_NullRenderer, nullptr, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&_pStreamRenderer);
514  if(hResult != S_OK)
515  { cerr << " Can not create the null renderer.";
516  cleanup(); return; }
517  cerr << " IID_IBaseFilter of CLSID_NullRenderer is acquired.\n";
518 
519  hResult=_pGraphBuilder->AddFilter(_pStreamRenderer, L"Stream Renderer");
520  if(hResult != S_OK)
521  { cerr << " Fail to add the Null Renderer to the graph.";
522  cleanup(); return; }
523  cerr << " The Null Renderer has been added to the graph.\n";
524 
525  hResult=_pCaptureBuilder->RenderStream(&PIN_CATEGORY_CAPTURE,
526  &MEDIATYPE_Video, _pSrcFilter, pGrabberFilter, _pStreamRenderer);
527  if(hResult != S_OK) {
528  cerr << " ICaptureGraphBuilder2::RenderStream() can not connect the pins\n";
529  cleanup(); return;
530  }
531 
532  hResult=_pSampleGrabber->GetConnectedMediaType(&mediaType);
533  if(hResult != S_OK) {
534  cerr << " Failed to read the connected media type.";
535  cleanup(); return;
536  }
537 
538 /*
539  * IPin *iPin; hResult = FindInputPin(pGrabberFilter, &iPin); if ((iPin ==
540  * 0)||(hResult != S_OK)) { cerr << "Could not get sampler input pin.\n";
541  * cleanup(); return; } CComQIPtr< IMemInputPin, &IID_IMemInputPin >
542  * pMemInputPin(iPin); if (pMemInputPin == 0) { cerr << "Could not get sampler
543  * meminput pin.\n"; cleanup(); return; } hResult =
544  * pMemInputPin->GetAllocator(&_pAllocator); if (hResult != S_OK) { cerr <<
545  * "Could not get sample grabber allocator handle.\n"; } ALLOCATOR_PROPERTIES
546  * props, aprops; hResult = _pAllocator->GetProperties(&props); if (hResult !=
547  * S_OK) { cerr << "Could not get allocator properties.\n"; } cerr <<
548  * "Allocator properties: cBuffers=" << props.cBuffers << "\n"; props.cBuffers
549  * += 10; hResult = _pAllocator->SetProperties(&props, &aprops); if (hResult
550  * != S_OK) { cerr << "Could not set allocator properties.\n"; } cerr <<
551  * "Allocator properties (adjusted): cBuffers=" << aprops.cBuffers << "\n";
552  */
553 
554  pVideoInfo=(VIDEOINFOHEADER*)mediaType.pbFormat;
555  _size_x = pVideoInfo->bmiHeader.biWidth;
556  _size_y = pVideoInfo->bmiHeader.biHeight;
557  cerr << "Connected media type " << _size_x << " x " << _size_y << "\n";
558 
559  _sample_cb._host = this;
560  _num_components = 3;
561  _length = 1.0E10;
562  _can_seek = false;
563  _can_seek_fast = false;
564  _aborted = false;
565  _streaming = true;
566  _buffer = new unsigned char[_size_x * _size_y * 3];
567  _ready = false;
568 
569  if(mediaType.cbFormat != 0) {
570  CoTaskMemFree((PVOID)mediaType.pbFormat);
571  mediaType.cbFormat=0;
572  mediaType.pbFormat=nullptr;
573  }
574 
575  if(mediaType.pUnk != nullptr) {
576  mediaType.pUnk->Release();
577  mediaType.pUnk=nullptr;
578  }
579 
580  if(pGrabberFilter != nullptr) {
581  pGrabberFilter->Release();
582  pGrabberFilter=nullptr;
583  }
584 
585  _pSampleGrabber->SetBufferSamples(FALSE);
586  _pSampleGrabber->SetOneShot(FALSE);
587 
588  hResult=_pSampleGrabber->SetCallback(&_sample_cb, 0);
589  if(hResult != S_OK) {
590  cerr << " Can not set the callback interface!";
591  cleanup(); return;
592  }
593 
594  _pMediaCtrl->Run();
595 }
596 
597 /**
598  *
599  */
600 void WebcamVideoCursorDS::
601 cleanup() {
602  if (_buffer) {
603  delete[] _buffer;
604  _buffer = 0;
605  }
606 
607  if (_pMediaCtrl) {
608  _pMediaCtrl->Stop();
609  }
610 
611  if(_pMediaCtrl) { _pMediaCtrl->Release(); _pMediaCtrl=nullptr; }
612  if(_pCaptureBuilder) { _pCaptureBuilder->Release(); _pCaptureBuilder=nullptr; }
613  if(_pGraphBuilder) { _pGraphBuilder->Release(); _pGraphBuilder=nullptr; }
614  if(_pSampleGrabber) { _pSampleGrabber->Release(); _pSampleGrabber=nullptr; }
615  if(_pStreamRenderer) { _pStreamRenderer->Release(); _pStreamRenderer=nullptr; }
616  if(_pSrcFilter) { _pSrcFilter->Release(); _pSrcFilter=nullptr; }
617  if(_pStreamConfig) { _pStreamConfig->Release(); _pStreamConfig=nullptr; }
618 }
619 
620 /**
621  *
622  */
623 WebcamVideoCursorDS::
624 ~WebcamVideoCursorDS() {
625  cleanup();
626 }
627 
628 /**
629  *
630  */
631 PT(MovieVideoCursor::Buffer) WebcamVideoCursorDS::
632 fetch_buffer() {
633  if (!_ready) {
634  return nullptr;
635  }
636 
637  Buffer *buffer = get_standard_buffer();
638  unsigned char *block = buffer->_block;
639 #ifdef LOCKING_MODE
640  unsigned char *ptr;
641  int pixels = _size_x * _size_y;
642  HRESULT res = _saved->GetPointer(&ptr);
643  if (res == S_OK) {
644  int size = _saved->GetActualDataLength();
645  if (size == pixels * 3) {
646  memcpy(block, ptr, pixels * 3);
647  }
648  }
649  _saved->Release();
650 #else
651  int pixels = _size_x * _size_y;
652  memcpy(block, _buffer, pixels * 3);
653 #endif
654 
655  _ready = false;
656  return buffer;
657 }
658 
659 
660 /**
661  *
662  */
663 HRESULT __stdcall WebcamVideoCursorDS::CSampleGrabberCB::QueryInterface(REFIID riid, void **ppv)
664 {
665  if((riid == IID_ISampleGrabberCB) || (riid == IID_IUnknown)) {
666  *ppv=(void *)static_cast<ISampleGrabberCB *> (this);
667  return NOERROR;
668  }
669  return E_NOINTERFACE;
670 }
671 
672 
673 /**
674  *
675  */
676 HRESULT __stdcall WebcamVideoCursorDS::CSampleGrabberCB::SampleCB(double SampleTime, IMediaSample *pSample)
677 {
678  if (_host->_ready) {
679  return 0;
680  }
681 
682 #ifdef LOCKING_MODE
683  pSample->AddRef();
684  _host->_saved = pSample;
685 #else
686  unsigned char *ptr;
687  int pixels = _host->_size_x * _host->_size_y;
688  HRESULT res = pSample->GetPointer(&ptr);
689  if (res == S_OK) {
690  int size = pSample->GetActualDataLength();
691  if (size == pixels * 3) {
692  memcpy(_host->_buffer, ptr, size);
693  }
694  }
695 #endif
696 
697  _host->_ready = true;
698  return 0;
699 }
700 
701 /**
702  *
703  */
704 HRESULT __stdcall WebcamVideoCursorDS::CSampleGrabberCB::BufferCB(double dblSampleTime, BYTE *pBuffer, long lBufferSize)
705 {
706  // Not used.
707  return 0;
708 }
709 
710 /*
711  * HRESULT FindInputPin(IBaseFilter *pFilter, IPin **ppPin) { if (!pFilter ||
712  * ! ppPin) return E_POINTER; *ppPin = 0; HRESULT hr; Find the output pin of
713  * the Source Filter IEnumPins *pPinEnum; hr = pFilter->EnumPins(&pPinEnum);
714  * if (FAILED(hr)) return E_FAIL; IPin *pSearchPin; while (pPinEnum->Next(1,
715  * &pSearchPin, NULL) == S_OK) { PIN_DIRECTION pPinDir; hr =
716  * pSearchPin->QueryDirection(&pPinDir); if (FAILED(hr)) return E_FAIL; if
717  * (pPinDir == PINDIR_INPUT) { Found out pin *ppPin = pSearchPin; break; } }
718  * pPinEnum->Release(); return hr; }
719  */
720 
721 
722 #endif // HAVE_DIRECTSHOW
void register_type(TypeHandle &type_handle, const std::string &name)
This inline function is just a convenient way to call TypeRegistry::register_type(),...
Definition: register_type.I:22
Allows you to open a webcam or other video capture device as a video stream.
Definition: webcamVideo.h:23
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
Definition: buffer.h:24
A MovieVideo is actually any source that provides a sequence of video frames.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81