2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 // (This file gets included by juce_win32_NativeCode.cpp, rather than being
27 // compiled on its own).
28 #if JUCE_INCLUDED_FILE && JUCE_USE_CAMERA
31 //==============================================================================
32 class DShowCameraDeviceInteral
: public ChangeBroadcaster
35 DShowCameraDeviceInteral (CameraDevice
* const owner_
,
36 const ComSmartPtr
<ICaptureGraphBuilder2
>& captureGraphBuilder_
,
37 const ComSmartPtr
<IBaseFilter
>& filter_
,
38 int minWidth
, int minHeight
,
39 int maxWidth
, int maxHeight
)
41 captureGraphBuilder (captureGraphBuilder_
),
44 imageNeedsFlipping (false),
48 recordNextFrameTime (false),
51 HRESULT hr
= graphBuilder
.CoCreateInstance (CLSID_FilterGraph
);
55 hr
= captureGraphBuilder
->SetFiltergraph (graphBuilder
);
59 hr
= graphBuilder
.QueryInterface (mediaControl
);
64 ComSmartPtr
<IAMStreamConfig
> streamConfig
;
66 hr
= captureGraphBuilder
->FindInterface (&PIN_CATEGORY_CAPTURE
, 0, filter
,
67 IID_IAMStreamConfig
, (void**) streamConfig
.resetAndGetPointerAddress());
69 if (streamConfig
!= nullptr)
71 getVideoSizes (streamConfig
);
73 if (! selectVideoSize (streamConfig
, minWidth
, minHeight
, maxWidth
, maxHeight
))
78 hr
= graphBuilder
->AddFilter (filter
, _T("Video Capture"));
82 hr
= smartTee
.CoCreateInstance (CLSID_SmartTee
);
86 hr
= graphBuilder
->AddFilter (smartTee
, _T("Smart Tee"));
90 if (! connectFilters (filter
, smartTee
))
93 ComSmartPtr
<IBaseFilter
> sampleGrabberBase
;
94 hr
= sampleGrabberBase
.CoCreateInstance (CLSID_SampleGrabber
);
98 hr
= sampleGrabberBase
.QueryInterface (sampleGrabber
);
103 AM_MEDIA_TYPE mt
= { 0 };
104 mt
.majortype
= MEDIATYPE_Video
;
105 mt
.subtype
= MEDIASUBTYPE_RGB24
;
106 mt
.formattype
= FORMAT_VideoInfo
;
107 sampleGrabber
->SetMediaType (&mt
);
110 callback
= new GrabberCallback (*this);
111 hr
= sampleGrabber
->SetCallback (callback
, 1);
113 hr
= graphBuilder
->AddFilter (sampleGrabberBase
, _T("Sample Grabber"));
117 ComSmartPtr
<IPin
> grabberInputPin
;
118 if (! (getPin (smartTee
, PINDIR_OUTPUT
, smartTeeCaptureOutputPin
, "capture")
119 && getPin (smartTee
, PINDIR_OUTPUT
, smartTeePreviewOutputPin
, "preview")
120 && getPin (sampleGrabberBase
, PINDIR_INPUT
, grabberInputPin
)))
123 hr
= graphBuilder
->Connect (smartTeePreviewOutputPin
, grabberInputPin
);
127 AM_MEDIA_TYPE mt
= { 0 };
128 hr
= sampleGrabber
->GetConnectedMediaType (&mt
);
129 VIDEOINFOHEADER
* pVih
= (VIDEOINFOHEADER
*) (mt
.pbFormat
);
130 width
= pVih
->bmiHeader
.biWidth
;
131 height
= pVih
->bmiHeader
.biHeight
;
133 ComSmartPtr
<IBaseFilter
> nullFilter
;
134 hr
= nullFilter
.CoCreateInstance (CLSID_NullRenderer
);
135 hr
= graphBuilder
->AddFilter (nullFilter
, _T("Null Renderer"));
137 if (connectFilters (sampleGrabberBase
, nullFilter
)
140 activeImage
= Image (Image::RGB
, width
, height
, true);
141 loadingImage
= Image (Image::RGB
, width
, height
, true);
147 ~DShowCameraDeviceInteral()
149 if (mediaControl
!= nullptr)
150 mediaControl
->Stop();
152 removeGraphFromRot();
154 for (int i
= viewerComps
.size(); --i
>= 0;)
155 viewerComps
.getUnchecked(i
)->ownerDeleted();
158 graphBuilder
= nullptr;
159 sampleGrabber
= nullptr;
160 mediaControl
= nullptr;
162 captureGraphBuilder
= nullptr;
164 smartTeePreviewOutputPin
= nullptr;
165 smartTeeCaptureOutputPin
= nullptr;
171 if (ok
&& activeUsers
++ == 0)
177 if (ok
&& --activeUsers
== 0)
178 mediaControl
->Stop();
181 int getPreviewMaxFPS() const
183 return previewMaxFPS
;
186 void handleFrame (double /*time*/, BYTE
* buffer
, long /*bufferSize*/)
188 if (recordNextFrameTime
)
190 const double defaultCameraLatency
= 0.1;
192 firstRecordedTime
= Time::getCurrentTime() - RelativeTime (defaultCameraLatency
);
193 recordNextFrameTime
= false;
195 ComSmartPtr
<IPin
> pin
;
196 if (getPin (filter
, PINDIR_OUTPUT
, pin
))
198 ComSmartPtr
<IAMPushSource
> pushSource
;
199 HRESULT hr
= pin
.QueryInterface (pushSource
);
201 if (pushSource
!= nullptr)
203 REFERENCE_TIME latency
= 0;
204 hr
= pushSource
->GetLatency (&latency
);
206 firstRecordedTime
= firstRecordedTime
- RelativeTime ((double) latency
);
212 const int lineStride
= width
* 3;
213 const ScopedLock
sl (imageSwapLock
);
216 const Image::BitmapData
destData (loadingImage
, 0, 0, width
, height
, Image::BitmapData::writeOnly
);
218 for (int i
= 0; i
< height
; ++i
)
219 memcpy (destData
.getLinePointer ((height
- 1) - i
),
220 buffer
+ lineStride
* i
,
224 imageNeedsFlipping
= true;
227 if (listeners
.size() > 0)
228 callListeners (loadingImage
);
233 void drawCurrentImage (Graphics
& g
, int x
, int y
, int w
, int h
)
235 if (imageNeedsFlipping
)
237 const ScopedLock
sl (imageSwapLock
);
238 swapVariables (loadingImage
, activeImage
);
239 imageNeedsFlipping
= false;
242 RectanglePlacement
rp (RectanglePlacement::centred
);
243 double dx
= 0, dy
= 0, dw
= width
, dh
= height
;
244 rp
.applyTo (dx
, dy
, dw
, dh
, x
, y
, w
, h
);
245 const int rx
= roundToInt (dx
), ry
= roundToInt (dy
);
246 const int rw
= roundToInt (dw
), rh
= roundToInt (dh
);
249 Graphics::ScopedSaveState
ss (g
);
251 g
.excludeClipRegion (Rectangle
<int> (rx
, ry
, rw
, rh
));
252 g
.fillAll (Colours::black
);
255 g
.drawImage (activeImage
, rx
, ry
, rw
, rh
, 0, 0, width
, height
);
258 bool createFileCaptureFilter (const File
& file
, int quality
)
260 removeFileCaptureFilter();
262 mediaControl
->Stop();
263 firstRecordedTime
= Time();
264 recordNextFrameTime
= true;
267 HRESULT hr
= asfWriter
.CoCreateInstance (CLSID_WMAsfWriter
);
271 ComSmartPtr
<IFileSinkFilter
> fileSink
;
272 hr
= asfWriter
.QueryInterface (fileSink
);
276 hr
= fileSink
->SetFileName (file
.getFullPathName().toWideCharPointer(), 0);
280 hr
= graphBuilder
->AddFilter (asfWriter
, _T("AsfWriter"));
284 ComSmartPtr
<IConfigAsfWriter
> asfConfig
;
285 hr
= asfWriter
.QueryInterface (asfConfig
);
286 asfConfig
->SetIndexMode (true);
287 ComSmartPtr
<IWMProfileManager
> profileManager
;
288 hr
= WMCreateProfileManager (profileManager
.resetAndGetPointerAddress());
290 // This gibberish is the DirectShow profile for a video-only wmv file.
291 String
prof ("<profile version=\"589824\" storageformat=\"1\" name=\"Quality\" description=\"Quality type for output.\">"
292 "<streamconfig majortype=\"{73646976-0000-0010-8000-00AA00389B71}\" streamnumber=\"1\" "
293 "streamname=\"Video Stream\" inputname=\"Video409\" bitrate=\"894960\" "
294 "bufferwindow=\"0\" reliabletransport=\"1\" decodercomplexity=\"AU\" rfc1766langid=\"en-us\">"
295 "<videomediaprops maxkeyframespacing=\"50000000\" quality=\"90\"/>"
296 "<wmmediatype subtype=\"{33564D57-0000-0010-8000-00AA00389B71}\" bfixedsizesamples=\"0\" "
297 "btemporalcompression=\"1\" lsamplesize=\"0\">"
298 "<videoinfoheader dwbitrate=\"894960\" dwbiterrorrate=\"0\" avgtimeperframe=\"$AVGTIMEPERFRAME\">"
299 "<rcsource left=\"0\" top=\"0\" right=\"$WIDTH\" bottom=\"$HEIGHT\"/>"
300 "<rctarget left=\"0\" top=\"0\" right=\"$WIDTH\" bottom=\"$HEIGHT\"/>"
301 "<bitmapinfoheader biwidth=\"$WIDTH\" biheight=\"$HEIGHT\" biplanes=\"1\" bibitcount=\"24\" "
302 "bicompression=\"WMV3\" bisizeimage=\"0\" bixpelspermeter=\"0\" biypelspermeter=\"0\" "
303 "biclrused=\"0\" biclrimportant=\"0\"/>"
309 const int fps
[] = { 10, 15, 30 };
310 int maxFramesPerSecond
= fps
[jlimit (0, numElementsInArray (fps
) - 1, quality
& 0xff)];
312 if ((quality
& 0xff000000) != 0) // (internal hacky way to pass explicit frame rates for testing)
313 maxFramesPerSecond
= (quality
>> 24) & 0xff;
315 prof
= prof
.replace ("$WIDTH", String (width
))
316 .replace ("$HEIGHT", String (height
))
317 .replace ("$AVGTIMEPERFRAME", String (10000000 / maxFramesPerSecond
));
319 ComSmartPtr
<IWMProfile
> currentProfile
;
320 hr
= profileManager
->LoadProfileByData (prof
.toWideCharPointer(), currentProfile
.resetAndGetPointerAddress());
321 hr
= asfConfig
->ConfigureFilterUsingProfile (currentProfile
);
325 ComSmartPtr
<IPin
> asfWriterInputPin
;
327 if (getPin (asfWriter
, PINDIR_INPUT
, asfWriterInputPin
, "Video Input 01"))
329 hr
= graphBuilder
->Connect (smartTeeCaptureOutputPin
, asfWriterInputPin
);
331 if (SUCCEEDED (hr
) && ok
&& activeUsers
> 0
332 && SUCCEEDED (mediaControl
->Run()))
334 previewMaxFPS
= (quality
< 2) ? 15 : 25; // throttle back the preview comps to try to leave the cpu free for encoding
336 if ((quality
& 0x00ff0000) != 0) // (internal hacky way to pass explicit frame rates for testing)
337 previewMaxFPS
= (quality
>> 16) & 0xff;
348 removeFileCaptureFilter();
350 if (ok
&& activeUsers
> 0)
356 void removeFileCaptureFilter()
358 mediaControl
->Stop();
360 if (asfWriter
!= nullptr)
362 graphBuilder
->RemoveFilter (asfWriter
);
366 if (ok
&& activeUsers
> 0)
372 //==============================================================================
373 void addListener (CameraDevice::Listener
* listenerToAdd
)
375 const ScopedLock
sl (listenerLock
);
377 if (listeners
.size() == 0)
380 listeners
.addIfNotAlreadyThere (listenerToAdd
);
383 void removeListener (CameraDevice::Listener
* listenerToRemove
)
385 const ScopedLock
sl (listenerLock
);
386 listeners
.removeValue (listenerToRemove
);
388 if (listeners
.size() == 0)
392 void callListeners (const Image
& image
)
394 const ScopedLock
sl (listenerLock
);
396 for (int i
= listeners
.size(); --i
>= 0;)
398 CameraDevice::Listener
* const l
= listeners
[i
];
401 l
->imageReceived (image
);
406 //==============================================================================
407 class DShowCaptureViewerComp
: public Component
,
408 public ChangeListener
411 DShowCaptureViewerComp (DShowCameraDeviceInteral
* const owner_
)
412 : owner (owner_
), maxFPS (15), lastRepaintTime (0)
415 owner
->addChangeListener (this);
417 owner
->viewerComps
.add (this);
418 setSize (owner
->width
, owner
->height
);
421 ~DShowCaptureViewerComp()
423 if (owner
!= nullptr)
425 owner
->viewerComps
.removeValue (this);
427 owner
->removeChangeListener (this);
436 void paint (Graphics
& g
)
438 g
.setColour (Colours::black
);
439 g
.setImageResamplingQuality (Graphics::lowResamplingQuality
);
441 if (owner
!= nullptr)
442 owner
->drawCurrentImage (g
, 0, 0, getWidth(), getHeight());
444 g
.fillAll (Colours::black
);
447 void changeListenerCallback (ChangeBroadcaster
*)
449 const int64 now
= Time::currentTimeMillis();
451 if (now
>= lastRepaintTime
+ (1000 / maxFPS
))
453 lastRepaintTime
= now
;
456 if (owner
!= nullptr)
457 maxFPS
= owner
->getPreviewMaxFPS();
462 DShowCameraDeviceInteral
* owner
;
464 int64 lastRepaintTime
;
467 //==============================================================================
470 Time firstRecordedTime
;
472 Array
<DShowCaptureViewerComp
*> viewerComps
;
475 CameraDevice
* const owner
;
476 ComSmartPtr
<ICaptureGraphBuilder2
> captureGraphBuilder
;
477 ComSmartPtr
<IBaseFilter
> filter
;
478 ComSmartPtr
<IBaseFilter
> smartTee
;
479 ComSmartPtr
<IGraphBuilder
> graphBuilder
;
480 ComSmartPtr
<ISampleGrabber
> sampleGrabber
;
481 ComSmartPtr
<IMediaControl
> mediaControl
;
482 ComSmartPtr
<IPin
> smartTeePreviewOutputPin
;
483 ComSmartPtr
<IPin
> smartTeeCaptureOutputPin
;
484 ComSmartPtr
<IBaseFilter
> asfWriter
;
486 Array
<int> widths
, heights
;
487 DWORD graphRegistrationID
;
489 CriticalSection imageSwapLock
;
490 bool imageNeedsFlipping
;
494 bool recordNextFrameTime
;
497 void getVideoSizes (IAMStreamConfig
* const streamConfig
)
502 int count
= 0, size
= 0;
503 streamConfig
->GetNumberOfCapabilities (&count
, &size
);
505 if (size
== sizeof (VIDEO_STREAM_CONFIG_CAPS
))
507 for (int i
= 0; i
< count
; ++i
)
509 VIDEO_STREAM_CONFIG_CAPS scc
;
510 AM_MEDIA_TYPE
* config
;
512 HRESULT hr
= streamConfig
->GetStreamCaps (i
, &config
, (BYTE
*) &scc
);
516 const int w
= scc
.InputSize
.cx
;
517 const int h
= scc
.InputSize
.cy
;
519 bool duplicate
= false;
521 for (int j
= widths
.size(); --j
>= 0;)
523 if (w
== widths
.getUnchecked (j
) && h
== heights
.getUnchecked (j
))
532 DBG ("Camera capture size: " + String (w
) + ", " + String (h
));
537 deleteMediaType (config
);
543 bool selectVideoSize (IAMStreamConfig
* const streamConfig
,
544 const int minWidth
, const int minHeight
,
545 const int maxWidth
, const int maxHeight
)
547 int count
= 0, size
= 0, bestArea
= 0, bestIndex
= -1;
548 streamConfig
->GetNumberOfCapabilities (&count
, &size
);
550 if (size
== sizeof (VIDEO_STREAM_CONFIG_CAPS
))
552 AM_MEDIA_TYPE
* config
;
553 VIDEO_STREAM_CONFIG_CAPS scc
;
555 for (int i
= 0; i
< count
; ++i
)
557 HRESULT hr
= streamConfig
->GetStreamCaps (i
, &config
, (BYTE
*) &scc
);
561 if (scc
.InputSize
.cx
>= minWidth
562 && scc
.InputSize
.cy
>= minHeight
563 && scc
.InputSize
.cx
<= maxWidth
564 && scc
.InputSize
.cy
<= maxHeight
)
566 int area
= scc
.InputSize
.cx
* scc
.InputSize
.cy
;
574 deleteMediaType (config
);
580 HRESULT hr
= streamConfig
->GetStreamCaps (bestIndex
, &config
, (BYTE
*) &scc
);
582 hr
= streamConfig
->SetFormat (config
);
583 deleteMediaType (config
);
584 return SUCCEEDED (hr
);
591 static bool getPin (IBaseFilter
* filter
, const PIN_DIRECTION wantedDirection
,
592 ComSmartPtr
<IPin
>& result
, const char* pinName
= nullptr)
594 ComSmartPtr
<IEnumPins
> enumerator
;
595 ComSmartPtr
<IPin
> pin
;
597 filter
->EnumPins (enumerator
.resetAndGetPointerAddress());
599 while (enumerator
->Next (1, pin
.resetAndGetPointerAddress(), 0) == S_OK
)
602 pin
->QueryDirection (&dir
);
604 if (wantedDirection
== dir
)
606 PIN_INFO info
= { 0 };
607 pin
->QueryPinInfo (&info
);
609 if (pinName
== nullptr || String (pinName
).equalsIgnoreCase (String (info
.achName
)))
620 bool connectFilters (IBaseFilter
* const first
, IBaseFilter
* const second
) const
622 ComSmartPtr
<IPin
> in
, out
;
624 return getPin (first
, PINDIR_OUTPUT
, out
)
625 && getPin (second
, PINDIR_INPUT
, in
)
626 && SUCCEEDED (graphBuilder
->Connect (out
, in
));
631 ComSmartPtr
<IRunningObjectTable
> rot
;
632 if (FAILED (GetRunningObjectTable (0, rot
.resetAndGetPointerAddress())))
635 ComSmartPtr
<IMoniker
> moniker
;
637 HRESULT hr
= CreateItemMoniker (_T("!"), buffer
, moniker
.resetAndGetPointerAddress());
641 graphRegistrationID
= 0;
642 return SUCCEEDED (rot
->Register (0, graphBuilder
, moniker
, &graphRegistrationID
));
645 void removeGraphFromRot()
647 ComSmartPtr
<IRunningObjectTable
> rot
;
649 if (SUCCEEDED (GetRunningObjectTable (0, rot
.resetAndGetPointerAddress())))
650 rot
->Revoke (graphRegistrationID
);
653 static void deleteMediaType (AM_MEDIA_TYPE
* const pmt
)
655 if (pmt
->cbFormat
!= 0)
656 CoTaskMemFree ((PVOID
) pmt
->pbFormat
);
658 if (pmt
->pUnk
!= nullptr)
659 pmt
->pUnk
->Release();
664 //==============================================================================
665 class GrabberCallback
: public ComBaseClassHelper
<ISampleGrabberCB
>
668 GrabberCallback (DShowCameraDeviceInteral
& owner_
)
673 //==============================================================================
674 STDMETHODIMP
SampleCB (double /*SampleTime*/, IMediaSample
* /*pSample*/)
679 STDMETHODIMP
BufferCB (double time
, BYTE
* buffer
, long bufferSize
)
681 owner
.handleFrame (time
, buffer
, bufferSize
);
686 DShowCameraDeviceInteral
& owner
;
688 GrabberCallback (const GrabberCallback
&);
689 GrabberCallback
& operator= (const GrabberCallback
&);
692 ComSmartPtr
<GrabberCallback
> callback
;
693 Array
<CameraDevice::Listener
*> listeners
;
694 CriticalSection listenerLock
;
696 //==============================================================================
697 JUCE_DECLARE_NON_COPYABLE (DShowCameraDeviceInteral
);
701 //==============================================================================
702 CameraDevice::CameraDevice (const String
& name_
, int /*index*/)
708 CameraDevice::~CameraDevice()
711 delete static_cast <DShowCameraDeviceInteral
*> (internal
);
715 Component
* CameraDevice::createViewerComponent()
717 return new DShowCameraDeviceInteral::DShowCaptureViewerComp (static_cast <DShowCameraDeviceInteral
*> (internal
));
720 String
CameraDevice::getFileExtension()
725 void CameraDevice::startRecordingToFile (const File
& file
, int quality
)
729 DShowCameraDeviceInteral
* const d
= (DShowCameraDeviceInteral
*) internal
;
731 isRecording
= d
->createFileCaptureFilter (file
, quality
);
734 Time
CameraDevice::getTimeOfFirstRecordedFrame() const
736 DShowCameraDeviceInteral
* const d
= (DShowCameraDeviceInteral
*) internal
;
737 return d
->firstRecordedTime
;
740 void CameraDevice::stopRecording()
744 DShowCameraDeviceInteral
* const d
= (DShowCameraDeviceInteral
*) internal
;
745 d
->removeFileCaptureFilter();
751 void CameraDevice::addListener (Listener
* listenerToAdd
)
753 DShowCameraDeviceInteral
* const d
= (DShowCameraDeviceInteral
*) internal
;
755 if (listenerToAdd
!= nullptr)
756 d
->addListener (listenerToAdd
);
759 void CameraDevice::removeListener (Listener
* listenerToRemove
)
761 DShowCameraDeviceInteral
* const d
= (DShowCameraDeviceInteral
*) internal
;
763 if (listenerToRemove
!= nullptr)
764 d
->removeListener (listenerToRemove
);
768 //==============================================================================
771 ComSmartPtr
<IBaseFilter
> enumerateCameras (StringArray
* const names
,
772 const int deviceIndexToOpen
,
776 ComSmartPtr
<IBaseFilter
> result
;
778 ComSmartPtr
<ICreateDevEnum
> pDevEnum
;
779 HRESULT hr
= pDevEnum
.CoCreateInstance (CLSID_SystemDeviceEnum
);
783 ComSmartPtr
<IEnumMoniker
> enumerator
;
784 hr
= pDevEnum
->CreateClassEnumerator (CLSID_VideoInputDeviceCategory
, enumerator
.resetAndGetPointerAddress(), 0);
786 if (SUCCEEDED (hr
) && enumerator
!= nullptr)
788 ComSmartPtr
<IMoniker
> moniker
;
791 while (enumerator
->Next (1, moniker
.resetAndGetPointerAddress(), &fetched
) == S_OK
)
793 ComSmartPtr
<IBaseFilter
> captureFilter
;
794 hr
= moniker
->BindToObject (0, 0, IID_IBaseFilter
, (void**) captureFilter
.resetAndGetPointerAddress());
798 ComSmartPtr
<IPropertyBag
> propertyBag
;
799 hr
= moniker
->BindToStorage (0, 0, IID_IPropertyBag
, (void**) propertyBag
.resetAndGetPointerAddress());
806 hr
= propertyBag
->Read (_T("FriendlyName"), &var
, 0);
807 propertyBag
= nullptr;
811 if (names
!= nullptr)
812 names
->add (var
.bstrVal
);
814 if (index
== deviceIndexToOpen
)
817 result
= captureFilter
;
833 StringArray
CameraDevice::getAvailableDevices()
837 enumerateCameras (&devs
, -1, dummy
);
841 CameraDevice
* CameraDevice::openDevice (int index
,
842 int minWidth
, int minHeight
,
843 int maxWidth
, int maxHeight
)
845 ComSmartPtr
<ICaptureGraphBuilder2
> captureGraphBuilder
;
846 HRESULT hr
= captureGraphBuilder
.CoCreateInstance (CLSID_CaptureGraphBuilder2
);
851 const ComSmartPtr
<IBaseFilter
> filter (enumerateCameras (0, index
, name
));
853 if (filter
!= nullptr)
855 ScopedPointer
<CameraDevice
> cam (new CameraDevice (name
, index
));
857 DShowCameraDeviceInteral
* const intern
858 = new DShowCameraDeviceInteral (cam
, captureGraphBuilder
, filter
,
859 minWidth
, minHeight
, maxWidth
, maxHeight
);
860 cam
->internal
= intern
;
863 return cam
.release();