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.

829 lines
26KB

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