The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

835 lines
26KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. // (This file gets included by juce_win32_NativeCode.cpp, rather than being
  24. // compiled on its own).
  25. #if JUCE_INCLUDED_FILE && JUCE_USE_CAMERA
  26. //==============================================================================
  27. class DShowCameraDeviceInteral : public ChangeBroadcaster
  28. {
  29. public:
  30. DShowCameraDeviceInteral (CameraDevice* const owner_,
  31. const ComSmartPtr <ICaptureGraphBuilder2>& captureGraphBuilder_,
  32. const ComSmartPtr <IBaseFilter>& filter_,
  33. int minWidth, int minHeight,
  34. int maxWidth, int maxHeight)
  35. : owner (owner_),
  36. captureGraphBuilder (captureGraphBuilder_),
  37. filter (filter_),
  38. ok (false),
  39. imageNeedsFlipping (false),
  40. width (0),
  41. height (0),
  42. activeUsers (0),
  43. recordNextFrameTime (false)
  44. {
  45. HRESULT hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph, CLSCTX_INPROC);
  46. if (FAILED (hr))
  47. return;
  48. hr = captureGraphBuilder->SetFiltergraph (graphBuilder);
  49. if (FAILED (hr))
  50. return;
  51. hr = graphBuilder->QueryInterface (IID_IMediaControl, (void**) &mediaControl);
  52. if (FAILED (hr))
  53. return;
  54. {
  55. ComSmartPtr <IAMStreamConfig> streamConfig;
  56. hr = captureGraphBuilder->FindInterface (&PIN_CATEGORY_CAPTURE,
  57. 0,
  58. filter,
  59. IID_IAMStreamConfig,
  60. (void**) &streamConfig);
  61. if (streamConfig != 0)
  62. {
  63. getVideoSizes (streamConfig);
  64. if (! selectVideoSize (streamConfig, minWidth, minHeight, maxWidth, maxHeight))
  65. return;
  66. }
  67. }
  68. hr = graphBuilder->AddFilter (filter, _T("Video Capture"));
  69. if (FAILED (hr))
  70. return;
  71. hr = smartTee.CoCreateInstance (CLSID_SmartTee, CLSCTX_INPROC_SERVER);
  72. if (FAILED (hr))
  73. return;
  74. hr = graphBuilder->AddFilter (smartTee, _T("Smart Tee"));
  75. if (FAILED (hr))
  76. return;
  77. if (! connectFilters (filter, smartTee))
  78. return;
  79. ComSmartPtr <IBaseFilter> sampleGrabberBase;
  80. hr = sampleGrabberBase.CoCreateInstance (CLSID_SampleGrabber, CLSCTX_INPROC_SERVER);
  81. if (FAILED (hr))
  82. return;
  83. hr = sampleGrabberBase->QueryInterface (IID_ISampleGrabber, (void**) &sampleGrabber);
  84. if (FAILED (hr))
  85. return;
  86. AM_MEDIA_TYPE mt;
  87. zerostruct (mt);
  88. mt.majortype = MEDIATYPE_Video;
  89. mt.subtype = MEDIASUBTYPE_RGB24;
  90. mt.formattype = FORMAT_VideoInfo;
  91. sampleGrabber->SetMediaType (&mt);
  92. callback = new GrabberCallback (*this);
  93. sampleGrabber->SetCallback (callback, 1);
  94. hr = graphBuilder->AddFilter (sampleGrabberBase, _T("Sample Grabber"));
  95. if (FAILED (hr))
  96. return;
  97. ComSmartPtr <IPin> grabberInputPin;
  98. if (! (getPin (smartTee, PINDIR_OUTPUT, &smartTeeCaptureOutputPin, "capture")
  99. && getPin (smartTee, PINDIR_OUTPUT, &smartTeePreviewOutputPin, "preview")
  100. && getPin (sampleGrabberBase, PINDIR_INPUT, &grabberInputPin)))
  101. return;
  102. hr = graphBuilder->Connect (smartTeePreviewOutputPin, grabberInputPin);
  103. if (FAILED (hr))
  104. return;
  105. zerostruct (mt);
  106. hr = sampleGrabber->GetConnectedMediaType (&mt);
  107. VIDEOINFOHEADER* pVih = (VIDEOINFOHEADER*) (mt.pbFormat);
  108. width = pVih->bmiHeader.biWidth;
  109. height = pVih->bmiHeader.biHeight;
  110. ComSmartPtr <IBaseFilter> nullFilter;
  111. hr = nullFilter.CoCreateInstance (CLSID_NullRenderer, CLSCTX_INPROC_SERVER);
  112. hr = graphBuilder->AddFilter (nullFilter, _T("Null Renderer"));
  113. if (connectFilters (sampleGrabberBase, nullFilter)
  114. && addGraphToRot())
  115. {
  116. activeImage = new Image (Image::RGB, width, height, true);
  117. loadingImage = new Image (Image::RGB, width, height, true);
  118. ok = true;
  119. }
  120. }
  121. ~DShowCameraDeviceInteral()
  122. {
  123. mediaControl->Stop();
  124. removeGraphFromRot();
  125. for (int i = viewerComps.size(); --i >= 0;)
  126. ((DShowCaptureViewerComp*) viewerComps.getUnchecked(i))->ownerDeleted();
  127. callback = 0;
  128. graphBuilder = 0;
  129. sampleGrabber = 0;
  130. mediaControl = 0;
  131. filter = 0;
  132. captureGraphBuilder = 0;
  133. smartTee = 0;
  134. smartTeePreviewOutputPin = 0;
  135. smartTeeCaptureOutputPin = 0;
  136. mux = 0;
  137. fileWriter = 0;
  138. delete activeImage;
  139. delete loadingImage;
  140. }
  141. void addUser()
  142. {
  143. if (ok && activeUsers++ == 0)
  144. mediaControl->Run();
  145. }
  146. void removeUser()
  147. {
  148. if (ok && --activeUsers == 0)
  149. mediaControl->Stop();
  150. }
  151. void handleFrame (double /*time*/, BYTE* buffer, long /*bufferSize*/)
  152. {
  153. if (recordNextFrameTime)
  154. {
  155. firstRecordedTime = Time::getCurrentTime();
  156. recordNextFrameTime = false;
  157. }
  158. imageSwapLock.enter();
  159. int ls, ps;
  160. const int lineStride = width * 3;
  161. uint8* const dest = loadingImage->lockPixelDataReadWrite (0, 0, width, height, ls, ps);
  162. for (int i = 0; i < height; ++i)
  163. memcpy (dest + ls * ((height - 1) - i),
  164. buffer + lineStride * i,
  165. lineStride);
  166. loadingImage->releasePixelDataReadWrite (dest);
  167. imageNeedsFlipping = true;
  168. imageSwapLock.exit();
  169. callListeners (*loadingImage);
  170. sendChangeMessage (this);
  171. }
  172. void drawCurrentImage (Graphics& g, int x, int y, int w, int h)
  173. {
  174. if (imageNeedsFlipping)
  175. {
  176. imageSwapLock.enter();
  177. swapVariables (loadingImage, activeImage);
  178. imageNeedsFlipping = false;
  179. imageSwapLock.exit();
  180. }
  181. RectanglePlacement rp (RectanglePlacement::centred);
  182. double dx = 0, dy = 0, dw = width, dh = height;
  183. rp.applyTo (dx, dy, dw, dh, x, y, w, h);
  184. const int rx = roundDoubleToInt (dx), ry = roundDoubleToInt (dy);
  185. const int rw = roundDoubleToInt (dw), rh = roundDoubleToInt (dh);
  186. g.saveState();
  187. g.excludeClipRegion (rx, ry, rw, rh);
  188. g.fillAll (Colours::black);
  189. g.restoreState();
  190. g.drawImage (activeImage, rx, ry, rw, rh, 0, 0, width, height);
  191. }
  192. bool createFileCaptureFilter (const File& file)
  193. {
  194. removeFileCaptureFilter();
  195. file.deleteFile();
  196. mediaControl->Stop();
  197. firstRecordedTime = Time();
  198. recordNextFrameTime = true;
  199. HRESULT hr = mux.CoCreateInstance (CLSID_AviDest, CLSCTX_INPROC_SERVER);
  200. if (SUCCEEDED (hr))
  201. {
  202. hr = graphBuilder->AddFilter (mux, _T("AVI Mux"));
  203. if (SUCCEEDED (hr))
  204. {
  205. fileWriter.CoCreateInstance (CLSID_FileWriter, CLSCTX_INPROC_SERVER);
  206. if (SUCCEEDED (hr))
  207. {
  208. ComSmartPtr <IFileSinkFilter> fileSink;
  209. hr = fileWriter->QueryInterface (IID_IFileSinkFilter, (void**) &fileSink);
  210. if (SUCCEEDED (hr))
  211. {
  212. AM_MEDIA_TYPE mt;
  213. zerostruct (mt);
  214. mt.majortype = MEDIATYPE_Stream;
  215. mt.subtype = MEDIASUBTYPE_Avi;
  216. mt.formattype = FORMAT_VideoInfo;
  217. hr = fileSink->SetFileName (file.getFullPathName(), &mt);
  218. if (SUCCEEDED (hr))
  219. {
  220. hr = graphBuilder->AddFilter (fileWriter, _T("File Writer"));
  221. if (SUCCEEDED (hr))
  222. {
  223. ComSmartPtr <IPin> muxInputPin, muxOutputPin, writerInput;
  224. if (getPin (mux, PINDIR_INPUT, &muxInputPin)
  225. && getPin (mux, PINDIR_OUTPUT, &muxOutputPin)
  226. && getPin (fileWriter, PINDIR_INPUT, &writerInput))
  227. {
  228. hr = graphBuilder->Connect (smartTeeCaptureOutputPin, muxInputPin);
  229. if (SUCCEEDED (hr))
  230. {
  231. hr = graphBuilder->Connect (muxOutputPin, writerInput);
  232. if (SUCCEEDED (hr))
  233. {
  234. if (ok && activeUsers > 0)
  235. mediaControl->Run();
  236. return true;
  237. }
  238. }
  239. }
  240. }
  241. }
  242. }
  243. }
  244. }
  245. }
  246. removeFileCaptureFilter();
  247. if (ok && activeUsers > 0)
  248. mediaControl->Run();
  249. return false;
  250. }
  251. void removeFileCaptureFilter()
  252. {
  253. mediaControl->Stop();
  254. if (mux != 0)
  255. {
  256. graphBuilder->RemoveFilter (mux);
  257. mux = 0;
  258. }
  259. if (fileWriter != 0)
  260. {
  261. graphBuilder->RemoveFilter (fileWriter);
  262. fileWriter = 0;
  263. }
  264. if (ok && activeUsers > 0)
  265. mediaControl->Run();
  266. }
  267. //==============================================================================
  268. void addListener (CameraImageListener* listenerToAdd)
  269. {
  270. const ScopedLock sl (listenerLock);
  271. if (listeners.size() == 0)
  272. addUser();
  273. listeners.addIfNotAlreadyThere (listenerToAdd);
  274. }
  275. void removeListener (CameraImageListener* listenerToRemove)
  276. {
  277. const ScopedLock sl (listenerLock);
  278. listeners.removeValue (listenerToRemove);
  279. if (listeners.size() == 0)
  280. removeUser();
  281. }
  282. void callListeners (Image& image)
  283. {
  284. const ScopedLock sl (listenerLock);
  285. for (int i = listeners.size(); --i >= 0;)
  286. {
  287. CameraImageListener* l = (CameraImageListener*) listeners[i];
  288. if (l != 0)
  289. l->imageReceived (image);
  290. }
  291. }
  292. //==============================================================================
  293. class DShowCaptureViewerComp : public Component,
  294. public ChangeListener
  295. {
  296. public:
  297. DShowCaptureViewerComp (DShowCameraDeviceInteral* const owner_)
  298. : owner (owner_)
  299. {
  300. setOpaque (true);
  301. owner->addChangeListener (this);
  302. owner->addUser();
  303. owner->viewerComps.add (this);
  304. setSize (owner_->width, owner_->height);
  305. }
  306. ~DShowCaptureViewerComp()
  307. {
  308. if (owner != 0)
  309. {
  310. owner->viewerComps.removeValue (this);
  311. owner->removeUser();
  312. owner->removeChangeListener (this);
  313. }
  314. }
  315. void ownerDeleted()
  316. {
  317. owner = 0;
  318. }
  319. void paint (Graphics& g)
  320. {
  321. g.setColour (Colours::black);
  322. g.setImageResamplingQuality (Graphics::lowResamplingQuality);
  323. if (owner != 0)
  324. owner->drawCurrentImage (g, 0, 0, getWidth(), getHeight());
  325. else
  326. g.fillAll (Colours::black);
  327. }
  328. void changeListenerCallback (void*)
  329. {
  330. repaint();
  331. }
  332. private:
  333. DShowCameraDeviceInteral* owner;
  334. };
  335. //==============================================================================
  336. bool ok;
  337. int width, height;
  338. Time firstRecordedTime;
  339. VoidArray viewerComps;
  340. private:
  341. CameraDevice* const owner;
  342. ComSmartPtr <ICaptureGraphBuilder2> captureGraphBuilder;
  343. ComSmartPtr <IBaseFilter> filter;
  344. ComSmartPtr <IBaseFilter> smartTee;
  345. ComSmartPtr <IGraphBuilder> graphBuilder;
  346. ComSmartPtr <ISampleGrabber> sampleGrabber;
  347. ComSmartPtr <IMediaControl> mediaControl;
  348. ComSmartPtr <IPin> smartTeePreviewOutputPin;
  349. ComSmartPtr <IPin> smartTeeCaptureOutputPin;
  350. ComSmartPtr <IBaseFilter> mux, fileWriter;
  351. int activeUsers;
  352. Array <int> widths, heights;
  353. DWORD graphRegistrationID;
  354. CriticalSection imageSwapLock;
  355. bool imageNeedsFlipping;
  356. Image* loadingImage;
  357. Image* activeImage;
  358. bool recordNextFrameTime;
  359. void getVideoSizes (IAMStreamConfig* const streamConfig)
  360. {
  361. widths.clear();
  362. heights.clear();
  363. int count = 0, size = 0;
  364. streamConfig->GetNumberOfCapabilities (&count, &size);
  365. if (size == sizeof (VIDEO_STREAM_CONFIG_CAPS))
  366. {
  367. for (int i = 0; i < count; ++i)
  368. {
  369. VIDEO_STREAM_CONFIG_CAPS scc;
  370. AM_MEDIA_TYPE* config;
  371. HRESULT hr = streamConfig->GetStreamCaps (i, &config, (BYTE*) &scc);
  372. if (SUCCEEDED (hr))
  373. {
  374. const int w = scc.InputSize.cx;
  375. const int h = scc.InputSize.cy;
  376. bool duplicate = false;
  377. for (int j = widths.size(); --j >= 0;)
  378. {
  379. if (w == widths.getUnchecked (j) && h == heights.getUnchecked (j))
  380. {
  381. duplicate = true;
  382. break;
  383. }
  384. }
  385. if (! duplicate)
  386. {
  387. DBG ("Camera capture size: " + String (w) + ", " + String (h));
  388. widths.add (w);
  389. heights.add (h);
  390. }
  391. deleteMediaType (config);
  392. }
  393. }
  394. }
  395. }
  396. bool selectVideoSize (IAMStreamConfig* const streamConfig,
  397. const int minWidth, const int minHeight,
  398. const int maxWidth, const int maxHeight)
  399. {
  400. int count = 0, size = 0;
  401. streamConfig->GetNumberOfCapabilities (&count, &size);
  402. if (size == sizeof (VIDEO_STREAM_CONFIG_CAPS))
  403. {
  404. for (int i = 0; i < count; ++i)
  405. {
  406. VIDEO_STREAM_CONFIG_CAPS scc;
  407. AM_MEDIA_TYPE* config;
  408. HRESULT hr = streamConfig->GetStreamCaps (i, &config, (BYTE*) &scc);
  409. if (SUCCEEDED (hr))
  410. {
  411. if (scc.InputSize.cx >= minWidth
  412. && scc.InputSize.cy >= minHeight
  413. && scc.InputSize.cx <= maxWidth
  414. && scc.InputSize.cy <= maxHeight)
  415. {
  416. hr = streamConfig->SetFormat (config);
  417. deleteMediaType (config);
  418. return SUCCEEDED (hr);
  419. }
  420. deleteMediaType (config);
  421. }
  422. }
  423. }
  424. return false;
  425. }
  426. static bool getPin (IBaseFilter* filter, const PIN_DIRECTION wantedDirection, IPin** result, const char* pinName = 0)
  427. {
  428. ComSmartPtr <IEnumPins> enumerator;
  429. ComSmartPtr <IPin> pin;
  430. filter->EnumPins (&enumerator);
  431. while (enumerator->Next (1, &pin, 0) == S_OK)
  432. {
  433. PIN_DIRECTION dir;
  434. pin->QueryDirection (&dir);
  435. if (wantedDirection == dir)
  436. {
  437. PIN_INFO info;
  438. zerostruct (info);
  439. pin->QueryPinInfo (&info);
  440. if (pinName == 0 || String (pinName).equalsIgnoreCase (String (info.achName)))
  441. {
  442. pin.p->AddRef();
  443. *result = pin;
  444. return true;
  445. }
  446. }
  447. }
  448. return false;
  449. }
  450. bool connectFilters (IBaseFilter* const first, IBaseFilter* const second) const
  451. {
  452. ComSmartPtr <IPin> in, out;
  453. return getPin (first, PINDIR_OUTPUT, &out)
  454. && getPin (second, PINDIR_INPUT, &in)
  455. && SUCCEEDED (graphBuilder->Connect (out, in));
  456. }
  457. bool addGraphToRot()
  458. {
  459. ComSmartPtr <IRunningObjectTable> rot;
  460. if (FAILED (GetRunningObjectTable (0, &rot)))
  461. return false;
  462. ComSmartPtr <IMoniker> moniker;
  463. WCHAR buffer[128];
  464. HRESULT hr = CreateItemMoniker (_T("!"), buffer, &moniker);
  465. if (FAILED (hr))
  466. return false;
  467. graphRegistrationID = 0;
  468. return SUCCEEDED (rot->Register (0, graphBuilder, moniker, &graphRegistrationID));
  469. }
  470. void removeGraphFromRot()
  471. {
  472. ComSmartPtr <IRunningObjectTable> rot;
  473. if (SUCCEEDED (GetRunningObjectTable (0, &rot)))
  474. rot->Revoke (graphRegistrationID);
  475. }
  476. static void deleteMediaType (AM_MEDIA_TYPE* const pmt)
  477. {
  478. if (pmt->cbFormat != 0)
  479. CoTaskMemFree ((PVOID) pmt->pbFormat);
  480. if (pmt->pUnk != 0)
  481. pmt->pUnk->Release();
  482. CoTaskMemFree (pmt);
  483. }
  484. //==============================================================================
  485. class GrabberCallback : public ISampleGrabberCB
  486. {
  487. public:
  488. GrabberCallback (DShowCameraDeviceInteral& owner_)
  489. : owner (owner_)
  490. {
  491. }
  492. HRESULT __stdcall QueryInterface (REFIID id, void** result)
  493. {
  494. if (id == IID_IUnknown)
  495. *result = dynamic_cast <IUnknown*> (this);
  496. else if (id == IID_ISampleGrabberCB)
  497. *result = dynamic_cast <ISampleGrabberCB*> (this);
  498. else
  499. {
  500. *result = 0;
  501. return E_NOINTERFACE;
  502. }
  503. AddRef();
  504. return S_OK;
  505. }
  506. ULONG __stdcall AddRef() { return ++refCount; }
  507. ULONG __stdcall Release() { const int r = --refCount; if (r == 0) delete this; return r; }
  508. //==============================================================================
  509. STDMETHODIMP SampleCB (double /*SampleTime*/, IMediaSample* /*pSample*/)
  510. {
  511. return E_FAIL;
  512. }
  513. STDMETHODIMP BufferCB (double time, BYTE* buffer, long bufferSize)
  514. {
  515. owner.handleFrame (time, buffer, bufferSize);
  516. return S_OK;
  517. }
  518. private:
  519. int refCount;
  520. DShowCameraDeviceInteral& owner;
  521. GrabberCallback (const GrabberCallback&);
  522. const GrabberCallback& operator= (const GrabberCallback&);
  523. };
  524. ComSmartPtr <GrabberCallback> callback;
  525. VoidArray listeners;
  526. CriticalSection listenerLock;
  527. //==============================================================================
  528. DShowCameraDeviceInteral (const DShowCameraDeviceInteral&);
  529. const DShowCameraDeviceInteral& operator= (const DShowCameraDeviceInteral&);
  530. };
  531. //==============================================================================
  532. CameraDevice::CameraDevice (const String& name_, int /*index*/)
  533. : name (name_)
  534. {
  535. isRecording = false;
  536. }
  537. CameraDevice::~CameraDevice()
  538. {
  539. stopRecording();
  540. delete (DShowCameraDeviceInteral*) internal;
  541. internal = 0;
  542. }
  543. Component* CameraDevice::createViewerComponent()
  544. {
  545. return new DShowCameraDeviceInteral::DShowCaptureViewerComp ((DShowCameraDeviceInteral*) internal);
  546. }
  547. const String CameraDevice::getFileExtension()
  548. {
  549. return ".avi";
  550. }
  551. void CameraDevice::startRecordingToFile (const File& file)
  552. {
  553. stopRecording();
  554. DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal;
  555. d->addUser();
  556. isRecording = d->createFileCaptureFilter (file);
  557. }
  558. const Time CameraDevice::getTimeOfFirstRecordedFrame() const
  559. {
  560. DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal;
  561. return d->firstRecordedTime;
  562. }
  563. void CameraDevice::stopRecording()
  564. {
  565. if (isRecording)
  566. {
  567. DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal;
  568. d->removeFileCaptureFilter();
  569. d->removeUser();
  570. isRecording = false;
  571. }
  572. }
  573. void CameraDevice::addListener (CameraImageListener* listenerToAdd)
  574. {
  575. DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal;
  576. if (listenerToAdd != 0)
  577. d->addListener (listenerToAdd);
  578. }
  579. void CameraDevice::removeListener (CameraImageListener* listenerToRemove)
  580. {
  581. DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal;
  582. if (listenerToRemove != 0)
  583. d->removeListener (listenerToRemove);
  584. }
  585. //==============================================================================
  586. static ComSmartPtr <IBaseFilter> enumerateCameras (StringArray* const names,
  587. const int deviceIndexToOpen,
  588. String& name)
  589. {
  590. int index = 0;
  591. ComSmartPtr <IBaseFilter> result;
  592. ComSmartPtr <ICreateDevEnum> pDevEnum;
  593. HRESULT hr = pDevEnum.CoCreateInstance (CLSID_SystemDeviceEnum, CLSCTX_INPROC);
  594. if (SUCCEEDED (hr))
  595. {
  596. ComSmartPtr <IEnumMoniker> enumerator;
  597. hr = pDevEnum->CreateClassEnumerator (CLSID_VideoInputDeviceCategory, &enumerator, 0);
  598. if (SUCCEEDED (hr) && enumerator != 0)
  599. {
  600. ComSmartPtr <IBaseFilter> captureFilter;
  601. ComSmartPtr <IMoniker> moniker;
  602. ULONG fetched;
  603. while (enumerator->Next (1, &moniker, &fetched) == S_OK)
  604. {
  605. hr = moniker->BindToObject (0, 0, IID_IBaseFilter, (void**) &captureFilter);
  606. if (SUCCEEDED (hr))
  607. {
  608. ComSmartPtr <IPropertyBag> propertyBag;
  609. hr = moniker->BindToStorage (0, 0, IID_IPropertyBag, (void**) &propertyBag);
  610. if (SUCCEEDED (hr))
  611. {
  612. VARIANT var;
  613. var.vt = VT_BSTR;
  614. hr = propertyBag->Read (_T("FriendlyName"), &var, 0);
  615. propertyBag = 0;
  616. if (SUCCEEDED (hr))
  617. {
  618. if (names != 0)
  619. names->add (var.bstrVal);
  620. if (index == deviceIndexToOpen)
  621. {
  622. name = var.bstrVal;
  623. result = captureFilter;
  624. captureFilter = 0;
  625. break;
  626. }
  627. ++index;
  628. }
  629. moniker = 0;
  630. }
  631. captureFilter = 0;
  632. }
  633. }
  634. }
  635. }
  636. return result;
  637. }
  638. const StringArray CameraDevice::getAvailableDevices()
  639. {
  640. StringArray devs;
  641. String dummy;
  642. enumerateCameras (&devs, -1, dummy);
  643. return devs;
  644. }
  645. CameraDevice* CameraDevice::openDevice (int index,
  646. int minWidth, int minHeight,
  647. int maxWidth, int maxHeight)
  648. {
  649. ComSmartPtr <ICaptureGraphBuilder2> captureGraphBuilder;
  650. HRESULT hr = captureGraphBuilder.CoCreateInstance (CLSID_CaptureGraphBuilder2, CLSCTX_INPROC);
  651. if (SUCCEEDED (hr))
  652. {
  653. String name;
  654. const ComSmartPtr <IBaseFilter> filter (enumerateCameras (0, index, name));
  655. if (filter != 0)
  656. {
  657. CameraDevice* const cam = new CameraDevice (name, index);
  658. DShowCameraDeviceInteral* const intern
  659. = new DShowCameraDeviceInteral (cam, captureGraphBuilder, filter,
  660. minWidth, minHeight, maxWidth, maxHeight);
  661. cam->internal = intern;
  662. if (intern->ok)
  663. return cam;
  664. else
  665. delete cam;
  666. }
  667. }
  668. return 0;
  669. }
  670. #endif