Add remaining files
[juce-lv2.git] / juce / source / src / native / windows / juce_win32_CameraDevice.cpp
blobb5fcdab47744cdc28d2276ad83ec5698fc186449
1 /*
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
34 public:
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)
40 : owner (owner_),
41 captureGraphBuilder (captureGraphBuilder_),
42 filter (filter_),
43 ok (false),
44 imageNeedsFlipping (false),
45 width (0),
46 height (0),
47 activeUsers (0),
48 recordNextFrameTime (false),
49 previewMaxFPS (60)
51 HRESULT hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph);
52 if (FAILED (hr))
53 return;
55 hr = captureGraphBuilder->SetFiltergraph (graphBuilder);
56 if (FAILED (hr))
57 return;
59 hr = graphBuilder.QueryInterface (mediaControl);
60 if (FAILED (hr))
61 return;
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))
74 return;
78 hr = graphBuilder->AddFilter (filter, _T("Video Capture"));
79 if (FAILED (hr))
80 return;
82 hr = smartTee.CoCreateInstance (CLSID_SmartTee);
83 if (FAILED (hr))
84 return;
86 hr = graphBuilder->AddFilter (smartTee, _T("Smart Tee"));
87 if (FAILED (hr))
88 return;
90 if (! connectFilters (filter, smartTee))
91 return;
93 ComSmartPtr <IBaseFilter> sampleGrabberBase;
94 hr = sampleGrabberBase.CoCreateInstance (CLSID_SampleGrabber);
95 if (FAILED (hr))
96 return;
98 hr = sampleGrabberBase.QueryInterface (sampleGrabber);
99 if (FAILED (hr))
100 return;
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"));
114 if (FAILED (hr))
115 return;
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)))
121 return;
123 hr = graphBuilder->Connect (smartTeePreviewOutputPin, grabberInputPin);
124 if (FAILED (hr))
125 return;
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)
138 && addGraphToRot())
140 activeImage = Image (Image::RGB, width, height, true);
141 loadingImage = Image (Image::RGB, width, height, true);
143 ok = 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();
157 callback = nullptr;
158 graphBuilder = nullptr;
159 sampleGrabber = nullptr;
160 mediaControl = nullptr;
161 filter = nullptr;
162 captureGraphBuilder = nullptr;
163 smartTee = nullptr;
164 smartTeePreviewOutputPin = nullptr;
165 smartTeeCaptureOutputPin = nullptr;
166 asfWriter = nullptr;
169 void addUser()
171 if (ok && activeUsers++ == 0)
172 mediaControl->Run();
175 void removeUser()
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,
221 lineStride);
224 imageNeedsFlipping = true;
227 if (listeners.size() > 0)
228 callListeners (loadingImage);
230 sendChangeMessage();
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();
261 file.deleteFile();
262 mediaControl->Stop();
263 firstRecordedTime = Time();
264 recordNextFrameTime = true;
265 previewMaxFPS = 60;
267 HRESULT hr = asfWriter.CoCreateInstance (CLSID_WMAsfWriter);
269 if (SUCCEEDED (hr))
271 ComSmartPtr <IFileSinkFilter> fileSink;
272 hr = asfWriter.QueryInterface (fileSink);
274 if (SUCCEEDED (hr))
276 hr = fileSink->SetFileName (file.getFullPathName().toWideCharPointer(), 0);
278 if (SUCCEEDED (hr))
280 hr = graphBuilder->AddFilter (asfWriter, _T("AsfWriter"));
282 if (SUCCEEDED (hr))
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\"/>"
304 "</videoinfoheader>"
305 "</wmmediatype>"
306 "</streamconfig>"
307 "</profile>");
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);
323 if (SUCCEEDED (hr))
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;
339 return true;
348 removeFileCaptureFilter();
350 if (ok && activeUsers > 0)
351 mediaControl->Run();
353 return false;
356 void removeFileCaptureFilter()
358 mediaControl->Stop();
360 if (asfWriter != nullptr)
362 graphBuilder->RemoveFilter (asfWriter);
363 asfWriter = nullptr;
366 if (ok && activeUsers > 0)
367 mediaControl->Run();
369 previewMaxFPS = 60;
372 //==============================================================================
373 void addListener (CameraDevice::Listener* listenerToAdd)
375 const ScopedLock sl (listenerLock);
377 if (listeners.size() == 0)
378 addUser();
380 listeners.addIfNotAlreadyThere (listenerToAdd);
383 void removeListener (CameraDevice::Listener* listenerToRemove)
385 const ScopedLock sl (listenerLock);
386 listeners.removeValue (listenerToRemove);
388 if (listeners.size() == 0)
389 removeUser();
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];
400 if (l != nullptr)
401 l->imageReceived (image);
406 //==============================================================================
407 class DShowCaptureViewerComp : public Component,
408 public ChangeListener
410 public:
411 DShowCaptureViewerComp (DShowCameraDeviceInteral* const owner_)
412 : owner (owner_), maxFPS (15), lastRepaintTime (0)
414 setOpaque (true);
415 owner->addChangeListener (this);
416 owner->addUser();
417 owner->viewerComps.add (this);
418 setSize (owner->width, owner->height);
421 ~DShowCaptureViewerComp()
423 if (owner != nullptr)
425 owner->viewerComps.removeValue (this);
426 owner->removeUser();
427 owner->removeChangeListener (this);
431 void ownerDeleted()
433 owner = nullptr;
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());
443 else
444 g.fillAll (Colours::black);
447 void changeListenerCallback (ChangeBroadcaster*)
449 const int64 now = Time::currentTimeMillis();
451 if (now >= lastRepaintTime + (1000 / maxFPS))
453 lastRepaintTime = now;
454 repaint();
456 if (owner != nullptr)
457 maxFPS = owner->getPreviewMaxFPS();
461 private:
462 DShowCameraDeviceInteral* owner;
463 int maxFPS;
464 int64 lastRepaintTime;
467 //==============================================================================
468 bool ok;
469 int width, height;
470 Time firstRecordedTime;
472 Array <DShowCaptureViewerComp*> viewerComps;
474 private:
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;
485 int activeUsers;
486 Array <int> widths, heights;
487 DWORD graphRegistrationID;
489 CriticalSection imageSwapLock;
490 bool imageNeedsFlipping;
491 Image loadingImage;
492 Image activeImage;
494 bool recordNextFrameTime;
495 int previewMaxFPS;
497 void getVideoSizes (IAMStreamConfig* const streamConfig)
499 widths.clear();
500 heights.clear();
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);
514 if (SUCCEEDED (hr))
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))
525 duplicate = true;
526 break;
530 if (! duplicate)
532 DBG ("Camera capture size: " + String (w) + ", " + String (h));
533 widths.add (w);
534 heights.add (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);
559 if (SUCCEEDED (hr))
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;
567 if (area > bestArea)
569 bestIndex = i;
570 bestArea = area;
574 deleteMediaType (config);
578 if (bestIndex >= 0)
580 HRESULT hr = streamConfig->GetStreamCaps (bestIndex, &config, (BYTE*) &scc);
582 hr = streamConfig->SetFormat (config);
583 deleteMediaType (config);
584 return SUCCEEDED (hr);
588 return false;
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)
601 PIN_DIRECTION dir;
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)))
611 result = pin;
612 return true;
617 return false;
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));
629 bool addGraphToRot()
631 ComSmartPtr <IRunningObjectTable> rot;
632 if (FAILED (GetRunningObjectTable (0, rot.resetAndGetPointerAddress())))
633 return false;
635 ComSmartPtr <IMoniker> moniker;
636 WCHAR buffer[128];
637 HRESULT hr = CreateItemMoniker (_T("!"), buffer, moniker.resetAndGetPointerAddress());
638 if (FAILED (hr))
639 return false;
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();
661 CoTaskMemFree (pmt);
664 //==============================================================================
665 class GrabberCallback : public ComBaseClassHelper <ISampleGrabberCB>
667 public:
668 GrabberCallback (DShowCameraDeviceInteral& owner_)
669 : owner (owner_)
673 //==============================================================================
674 STDMETHODIMP SampleCB (double /*SampleTime*/, IMediaSample* /*pSample*/)
676 return E_FAIL;
679 STDMETHODIMP BufferCB (double time, BYTE* buffer, long bufferSize)
681 owner.handleFrame (time, buffer, bufferSize);
682 return S_OK;
685 private:
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*/)
703 : name (name_)
705 isRecording = false;
708 CameraDevice::~CameraDevice()
710 stopRecording();
711 delete static_cast <DShowCameraDeviceInteral*> (internal);
712 internal = nullptr;
715 Component* CameraDevice::createViewerComponent()
717 return new DShowCameraDeviceInteral::DShowCaptureViewerComp (static_cast <DShowCameraDeviceInteral*> (internal));
720 String CameraDevice::getFileExtension()
722 return ".wmv";
725 void CameraDevice::startRecordingToFile (const File& file, int quality)
727 stopRecording();
729 DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal;
730 d->addUser();
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()
742 if (isRecording)
744 DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal;
745 d->removeFileCaptureFilter();
746 d->removeUser();
747 isRecording = false;
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 //==============================================================================
769 namespace
771 ComSmartPtr <IBaseFilter> enumerateCameras (StringArray* const names,
772 const int deviceIndexToOpen,
773 String& name)
775 int index = 0;
776 ComSmartPtr <IBaseFilter> result;
778 ComSmartPtr <ICreateDevEnum> pDevEnum;
779 HRESULT hr = pDevEnum.CoCreateInstance (CLSID_SystemDeviceEnum);
781 if (SUCCEEDED (hr))
783 ComSmartPtr <IEnumMoniker> enumerator;
784 hr = pDevEnum->CreateClassEnumerator (CLSID_VideoInputDeviceCategory, enumerator.resetAndGetPointerAddress(), 0);
786 if (SUCCEEDED (hr) && enumerator != nullptr)
788 ComSmartPtr <IMoniker> moniker;
789 ULONG fetched;
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());
796 if (SUCCEEDED (hr))
798 ComSmartPtr <IPropertyBag> propertyBag;
799 hr = moniker->BindToStorage (0, 0, IID_IPropertyBag, (void**) propertyBag.resetAndGetPointerAddress());
801 if (SUCCEEDED (hr))
803 VARIANT var;
804 var.vt = VT_BSTR;
806 hr = propertyBag->Read (_T("FriendlyName"), &var, 0);
807 propertyBag = nullptr;
809 if (SUCCEEDED (hr))
811 if (names != nullptr)
812 names->add (var.bstrVal);
814 if (index == deviceIndexToOpen)
816 name = var.bstrVal;
817 result = captureFilter;
818 break;
821 ++index;
829 return result;
833 StringArray CameraDevice::getAvailableDevices()
835 StringArray devs;
836 String dummy;
837 enumerateCameras (&devs, -1, dummy);
838 return devs;
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);
848 if (SUCCEEDED (hr))
850 String name;
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;
862 if (intern->ok)
863 return cam.release();
867 return nullptr;
871 #endif