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.

863 lines
29KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 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. class DShowCameraDeviceInteral : public ChangeBroadcaster
  19. {
  20. public:
  21. DShowCameraDeviceInteral (CameraDevice* const owner_,
  22. const ComSmartPtr <ICaptureGraphBuilder2>& captureGraphBuilder_,
  23. const ComSmartPtr <IBaseFilter>& filter_,
  24. int minWidth, int minHeight,
  25. int maxWidth, int maxHeight)
  26. : owner (owner_),
  27. captureGraphBuilder (captureGraphBuilder_),
  28. filter (filter_),
  29. ok (false),
  30. imageNeedsFlipping (false),
  31. width (0),
  32. height (0),
  33. activeUsers (0),
  34. recordNextFrameTime (false),
  35. previewMaxFPS (60)
  36. {
  37. HRESULT hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph);
  38. if (FAILED (hr))
  39. return;
  40. hr = captureGraphBuilder->SetFiltergraph (graphBuilder);
  41. if (FAILED (hr))
  42. return;
  43. hr = graphBuilder.QueryInterface (mediaControl);
  44. if (FAILED (hr))
  45. return;
  46. {
  47. ComSmartPtr <IAMStreamConfig> streamConfig;
  48. hr = captureGraphBuilder->FindInterface (&PIN_CATEGORY_CAPTURE, 0, filter,
  49. IID_IAMStreamConfig, (void**) streamConfig.resetAndGetPointerAddress());
  50. if (streamConfig != nullptr)
  51. {
  52. getVideoSizes (streamConfig);
  53. if (! selectVideoSize (streamConfig, minWidth, minHeight, maxWidth, maxHeight))
  54. return;
  55. }
  56. }
  57. hr = graphBuilder->AddFilter (filter, _T("Video Capture"));
  58. if (FAILED (hr))
  59. return;
  60. hr = smartTee.CoCreateInstance (CLSID_SmartTee);
  61. if (FAILED (hr))
  62. return;
  63. hr = graphBuilder->AddFilter (smartTee, _T("Smart Tee"));
  64. if (FAILED (hr))
  65. return;
  66. if (! connectFilters (filter, smartTee))
  67. return;
  68. ComSmartPtr <IBaseFilter> sampleGrabberBase;
  69. hr = sampleGrabberBase.CoCreateInstance (CLSID_SampleGrabber);
  70. if (FAILED (hr))
  71. return;
  72. hr = sampleGrabberBase.QueryInterface (sampleGrabber);
  73. if (FAILED (hr))
  74. return;
  75. {
  76. AM_MEDIA_TYPE mt = { 0 };
  77. mt.majortype = MEDIATYPE_Video;
  78. mt.subtype = MEDIASUBTYPE_RGB24;
  79. mt.formattype = FORMAT_VideoInfo;
  80. sampleGrabber->SetMediaType (&mt);
  81. }
  82. callback = new GrabberCallback (*this);
  83. hr = sampleGrabber->SetCallback (callback, 1);
  84. hr = graphBuilder->AddFilter (sampleGrabberBase, _T("Sample Grabber"));
  85. if (FAILED (hr))
  86. return;
  87. ComSmartPtr <IPin> grabberInputPin;
  88. if (! (getPin (smartTee, PINDIR_OUTPUT, smartTeeCaptureOutputPin, "capture")
  89. && getPin (smartTee, PINDIR_OUTPUT, smartTeePreviewOutputPin, "preview")
  90. && getPin (sampleGrabberBase, PINDIR_INPUT, grabberInputPin)))
  91. return;
  92. hr = graphBuilder->Connect (smartTeePreviewOutputPin, grabberInputPin);
  93. if (FAILED (hr))
  94. return;
  95. AM_MEDIA_TYPE mt = { 0 };
  96. hr = sampleGrabber->GetConnectedMediaType (&mt);
  97. VIDEOINFOHEADER* pVih = (VIDEOINFOHEADER*) (mt.pbFormat);
  98. width = pVih->bmiHeader.biWidth;
  99. height = pVih->bmiHeader.biHeight;
  100. ComSmartPtr <IBaseFilter> nullFilter;
  101. hr = nullFilter.CoCreateInstance (CLSID_NullRenderer);
  102. hr = graphBuilder->AddFilter (nullFilter, _T("Null Renderer"));
  103. if (connectFilters (sampleGrabberBase, nullFilter)
  104. && addGraphToRot())
  105. {
  106. activeImage = Image (Image::RGB, width, height, true);
  107. loadingImage = Image (Image::RGB, width, height, true);
  108. ok = true;
  109. }
  110. }
  111. ~DShowCameraDeviceInteral()
  112. {
  113. if (mediaControl != nullptr)
  114. mediaControl->Stop();
  115. removeGraphFromRot();
  116. for (int i = viewerComps.size(); --i >= 0;)
  117. viewerComps.getUnchecked(i)->ownerDeleted();
  118. callback = nullptr;
  119. graphBuilder = nullptr;
  120. sampleGrabber = nullptr;
  121. mediaControl = nullptr;
  122. filter = nullptr;
  123. captureGraphBuilder = nullptr;
  124. smartTee = nullptr;
  125. smartTeePreviewOutputPin = nullptr;
  126. smartTeeCaptureOutputPin = nullptr;
  127. asfWriter = nullptr;
  128. }
  129. void addUser()
  130. {
  131. if (ok && activeUsers++ == 0)
  132. mediaControl->Run();
  133. }
  134. void removeUser()
  135. {
  136. if (ok && --activeUsers == 0)
  137. mediaControl->Stop();
  138. }
  139. int getPreviewMaxFPS() const
  140. {
  141. return previewMaxFPS;
  142. }
  143. void handleFrame (double /*time*/, BYTE* buffer, long /*bufferSize*/)
  144. {
  145. if (recordNextFrameTime)
  146. {
  147. const double defaultCameraLatency = 0.1;
  148. firstRecordedTime = Time::getCurrentTime() - RelativeTime (defaultCameraLatency);
  149. recordNextFrameTime = false;
  150. ComSmartPtr <IPin> pin;
  151. if (getPin (filter, PINDIR_OUTPUT, pin))
  152. {
  153. ComSmartPtr <IAMPushSource> pushSource;
  154. HRESULT hr = pin.QueryInterface (pushSource);
  155. if (pushSource != nullptr)
  156. {
  157. REFERENCE_TIME latency = 0;
  158. hr = pushSource->GetLatency (&latency);
  159. firstRecordedTime = firstRecordedTime - RelativeTime ((double) latency);
  160. }
  161. }
  162. }
  163. {
  164. const int lineStride = width * 3;
  165. const ScopedLock sl (imageSwapLock);
  166. {
  167. const Image::BitmapData destData (loadingImage, 0, 0, width, height, Image::BitmapData::writeOnly);
  168. for (int i = 0; i < height; ++i)
  169. memcpy (destData.getLinePointer ((height - 1) - i),
  170. buffer + lineStride * i,
  171. lineStride);
  172. }
  173. imageNeedsFlipping = true;
  174. }
  175. if (listeners.size() > 0)
  176. callListeners (loadingImage);
  177. sendChangeMessage();
  178. }
  179. void drawCurrentImage (Graphics& g, int x, int y, int w, int h)
  180. {
  181. if (imageNeedsFlipping)
  182. {
  183. const ScopedLock sl (imageSwapLock);
  184. swapVariables (loadingImage, activeImage);
  185. imageNeedsFlipping = false;
  186. }
  187. RectanglePlacement rp (RectanglePlacement::centred);
  188. double dx = 0, dy = 0, dw = width, dh = height;
  189. rp.applyTo (dx, dy, dw, dh, x, y, w, h);
  190. const int rx = roundToInt (dx), ry = roundToInt (dy);
  191. const int rw = roundToInt (dw), rh = roundToInt (dh);
  192. {
  193. Graphics::ScopedSaveState ss (g);
  194. g.excludeClipRegion (Rectangle<int> (rx, ry, rw, rh));
  195. g.fillAll (Colours::black);
  196. }
  197. g.drawImage (activeImage, rx, ry, rw, rh, 0, 0, width, height);
  198. }
  199. bool createFileCaptureFilter (const File& file, int quality)
  200. {
  201. removeFileCaptureFilter();
  202. file.deleteFile();
  203. mediaControl->Stop();
  204. firstRecordedTime = Time();
  205. recordNextFrameTime = true;
  206. previewMaxFPS = 60;
  207. HRESULT hr = asfWriter.CoCreateInstance (CLSID_WMAsfWriter);
  208. if (SUCCEEDED (hr))
  209. {
  210. ComSmartPtr <IFileSinkFilter> fileSink;
  211. hr = asfWriter.QueryInterface (fileSink);
  212. if (SUCCEEDED (hr))
  213. {
  214. hr = fileSink->SetFileName (file.getFullPathName().toWideCharPointer(), 0);
  215. if (SUCCEEDED (hr))
  216. {
  217. hr = graphBuilder->AddFilter (asfWriter, _T("AsfWriter"));
  218. if (SUCCEEDED (hr))
  219. {
  220. ComSmartPtr <IConfigAsfWriter> asfConfig;
  221. hr = asfWriter.QueryInterface (asfConfig);
  222. asfConfig->SetIndexMode (true);
  223. ComSmartPtr <IWMProfileManager> profileManager;
  224. hr = WMCreateProfileManager (profileManager.resetAndGetPointerAddress());
  225. // This gibberish is the DirectShow profile for a video-only wmv file.
  226. String prof ("<profile version=\"589824\" storageformat=\"1\" name=\"Quality\" description=\"Quality type for output.\">"
  227. "<streamconfig majortype=\"{73646976-0000-0010-8000-00AA00389B71}\" streamnumber=\"1\" "
  228. "streamname=\"Video Stream\" inputname=\"Video409\" bitrate=\"894960\" "
  229. "bufferwindow=\"0\" reliabletransport=\"1\" decodercomplexity=\"AU\" rfc1766langid=\"en-us\">"
  230. "<videomediaprops maxkeyframespacing=\"50000000\" quality=\"90\"/>"
  231. "<wmmediatype subtype=\"{33564D57-0000-0010-8000-00AA00389B71}\" bfixedsizesamples=\"0\" "
  232. "btemporalcompression=\"1\" lsamplesize=\"0\">"
  233. "<videoinfoheader dwbitrate=\"894960\" dwbiterrorrate=\"0\" avgtimeperframe=\"$AVGTIMEPERFRAME\">"
  234. "<rcsource left=\"0\" top=\"0\" right=\"$WIDTH\" bottom=\"$HEIGHT\"/>"
  235. "<rctarget left=\"0\" top=\"0\" right=\"$WIDTH\" bottom=\"$HEIGHT\"/>"
  236. "<bitmapinfoheader biwidth=\"$WIDTH\" biheight=\"$HEIGHT\" biplanes=\"1\" bibitcount=\"24\" "
  237. "bicompression=\"WMV3\" bisizeimage=\"0\" bixpelspermeter=\"0\" biypelspermeter=\"0\" "
  238. "biclrused=\"0\" biclrimportant=\"0\"/>"
  239. "</videoinfoheader>"
  240. "</wmmediatype>"
  241. "</streamconfig>"
  242. "</profile>");
  243. const int fps[] = { 10, 15, 30 };
  244. int maxFramesPerSecond = fps [jlimit (0, numElementsInArray (fps) - 1, quality & 0xff)];
  245. if ((quality & 0xff000000) != 0) // (internal hacky way to pass explicit frame rates for testing)
  246. maxFramesPerSecond = (quality >> 24) & 0xff;
  247. prof = prof.replace ("$WIDTH", String (width))
  248. .replace ("$HEIGHT", String (height))
  249. .replace ("$AVGTIMEPERFRAME", String (10000000 / maxFramesPerSecond));
  250. ComSmartPtr <IWMProfile> currentProfile;
  251. hr = profileManager->LoadProfileByData (prof.toWideCharPointer(), currentProfile.resetAndGetPointerAddress());
  252. hr = asfConfig->ConfigureFilterUsingProfile (currentProfile);
  253. if (SUCCEEDED (hr))
  254. {
  255. ComSmartPtr <IPin> asfWriterInputPin;
  256. if (getPin (asfWriter, PINDIR_INPUT, asfWriterInputPin, "Video Input 01"))
  257. {
  258. hr = graphBuilder->Connect (smartTeeCaptureOutputPin, asfWriterInputPin);
  259. if (SUCCEEDED (hr) && ok && activeUsers > 0
  260. && SUCCEEDED (mediaControl->Run()))
  261. {
  262. previewMaxFPS = (quality < 2) ? 15 : 25; // throttle back the preview comps to try to leave the cpu free for encoding
  263. if ((quality & 0x00ff0000) != 0) // (internal hacky way to pass explicit frame rates for testing)
  264. previewMaxFPS = (quality >> 16) & 0xff;
  265. return true;
  266. }
  267. }
  268. }
  269. }
  270. }
  271. }
  272. }
  273. removeFileCaptureFilter();
  274. if (ok && activeUsers > 0)
  275. mediaControl->Run();
  276. return false;
  277. }
  278. void removeFileCaptureFilter()
  279. {
  280. mediaControl->Stop();
  281. if (asfWriter != nullptr)
  282. {
  283. graphBuilder->RemoveFilter (asfWriter);
  284. asfWriter = nullptr;
  285. }
  286. if (ok && activeUsers > 0)
  287. mediaControl->Run();
  288. previewMaxFPS = 60;
  289. }
  290. //==============================================================================
  291. void addListener (CameraDevice::Listener* listenerToAdd)
  292. {
  293. const ScopedLock sl (listenerLock);
  294. if (listeners.size() == 0)
  295. addUser();
  296. listeners.addIfNotAlreadyThere (listenerToAdd);
  297. }
  298. void removeListener (CameraDevice::Listener* listenerToRemove)
  299. {
  300. const ScopedLock sl (listenerLock);
  301. listeners.removeValue (listenerToRemove);
  302. if (listeners.size() == 0)
  303. removeUser();
  304. }
  305. void callListeners (const Image& image)
  306. {
  307. const ScopedLock sl (listenerLock);
  308. for (int i = listeners.size(); --i >= 0;)
  309. {
  310. CameraDevice::Listener* const l = listeners[i];
  311. if (l != nullptr)
  312. l->imageReceived (image);
  313. }
  314. }
  315. //==============================================================================
  316. class DShowCaptureViewerComp : public Component,
  317. public ChangeListener
  318. {
  319. public:
  320. DShowCaptureViewerComp (DShowCameraDeviceInteral* const owner_)
  321. : owner (owner_), maxFPS (15), lastRepaintTime (0)
  322. {
  323. setOpaque (true);
  324. owner->addChangeListener (this);
  325. owner->addUser();
  326. owner->viewerComps.add (this);
  327. setSize (owner->width, owner->height);
  328. }
  329. ~DShowCaptureViewerComp()
  330. {
  331. if (owner != nullptr)
  332. {
  333. owner->viewerComps.removeValue (this);
  334. owner->removeUser();
  335. owner->removeChangeListener (this);
  336. }
  337. }
  338. void ownerDeleted()
  339. {
  340. owner = nullptr;
  341. }
  342. void paint (Graphics& g)
  343. {
  344. g.setColour (Colours::black);
  345. g.setImageResamplingQuality (Graphics::lowResamplingQuality);
  346. if (owner != nullptr)
  347. owner->drawCurrentImage (g, 0, 0, getWidth(), getHeight());
  348. else
  349. g.fillAll (Colours::black);
  350. }
  351. void changeListenerCallback (ChangeBroadcaster*)
  352. {
  353. const int64 now = Time::currentTimeMillis();
  354. if (now >= lastRepaintTime + (1000 / maxFPS))
  355. {
  356. lastRepaintTime = now;
  357. repaint();
  358. if (owner != nullptr)
  359. maxFPS = owner->getPreviewMaxFPS();
  360. }
  361. }
  362. private:
  363. DShowCameraDeviceInteral* owner;
  364. int maxFPS;
  365. int64 lastRepaintTime;
  366. };
  367. //==============================================================================
  368. bool ok;
  369. int width, height;
  370. Time firstRecordedTime;
  371. Array <DShowCaptureViewerComp*> viewerComps;
  372. private:
  373. CameraDevice* const owner;
  374. ComSmartPtr <ICaptureGraphBuilder2> captureGraphBuilder;
  375. ComSmartPtr <IBaseFilter> filter;
  376. ComSmartPtr <IBaseFilter> smartTee;
  377. ComSmartPtr <IGraphBuilder> graphBuilder;
  378. ComSmartPtr <ISampleGrabber> sampleGrabber;
  379. ComSmartPtr <IMediaControl> mediaControl;
  380. ComSmartPtr <IPin> smartTeePreviewOutputPin;
  381. ComSmartPtr <IPin> smartTeeCaptureOutputPin;
  382. ComSmartPtr <IBaseFilter> asfWriter;
  383. int activeUsers;
  384. Array <int> widths, heights;
  385. DWORD graphRegistrationID;
  386. CriticalSection imageSwapLock;
  387. bool imageNeedsFlipping;
  388. Image loadingImage;
  389. Image activeImage;
  390. bool recordNextFrameTime;
  391. int previewMaxFPS;
  392. void getVideoSizes (IAMStreamConfig* const streamConfig)
  393. {
  394. widths.clear();
  395. heights.clear();
  396. int count = 0, size = 0;
  397. streamConfig->GetNumberOfCapabilities (&count, &size);
  398. if (size == sizeof (VIDEO_STREAM_CONFIG_CAPS))
  399. {
  400. for (int i = 0; i < count; ++i)
  401. {
  402. VIDEO_STREAM_CONFIG_CAPS scc;
  403. AM_MEDIA_TYPE* config;
  404. HRESULT hr = streamConfig->GetStreamCaps (i, &config, (BYTE*) &scc);
  405. if (SUCCEEDED (hr))
  406. {
  407. const int w = scc.InputSize.cx;
  408. const int h = scc.InputSize.cy;
  409. bool duplicate = false;
  410. for (int j = widths.size(); --j >= 0;)
  411. {
  412. if (w == widths.getUnchecked (j) && h == heights.getUnchecked (j))
  413. {
  414. duplicate = true;
  415. break;
  416. }
  417. }
  418. if (! duplicate)
  419. {
  420. DBG ("Camera capture size: " + String (w) + ", " + String (h));
  421. widths.add (w);
  422. heights.add (h);
  423. }
  424. deleteMediaType (config);
  425. }
  426. }
  427. }
  428. }
  429. bool selectVideoSize (IAMStreamConfig* const streamConfig,
  430. const int minWidth, const int minHeight,
  431. const int maxWidth, const int maxHeight)
  432. {
  433. int count = 0, size = 0, bestArea = 0, bestIndex = -1;
  434. streamConfig->GetNumberOfCapabilities (&count, &size);
  435. if (size == sizeof (VIDEO_STREAM_CONFIG_CAPS))
  436. {
  437. AM_MEDIA_TYPE* config;
  438. VIDEO_STREAM_CONFIG_CAPS scc;
  439. for (int i = 0; i < count; ++i)
  440. {
  441. HRESULT hr = streamConfig->GetStreamCaps (i, &config, (BYTE*) &scc);
  442. if (SUCCEEDED (hr))
  443. {
  444. if (scc.InputSize.cx >= minWidth
  445. && scc.InputSize.cy >= minHeight
  446. && scc.InputSize.cx <= maxWidth
  447. && scc.InputSize.cy <= maxHeight)
  448. {
  449. int area = scc.InputSize.cx * scc.InputSize.cy;
  450. if (area > bestArea)
  451. {
  452. bestIndex = i;
  453. bestArea = area;
  454. }
  455. }
  456. deleteMediaType (config);
  457. }
  458. }
  459. if (bestIndex >= 0)
  460. {
  461. HRESULT hr = streamConfig->GetStreamCaps (bestIndex, &config, (BYTE*) &scc);
  462. hr = streamConfig->SetFormat (config);
  463. deleteMediaType (config);
  464. return SUCCEEDED (hr);
  465. }
  466. }
  467. return false;
  468. }
  469. static bool getPin (IBaseFilter* filter, const PIN_DIRECTION wantedDirection,
  470. ComSmartPtr<IPin>& result, const char* pinName = nullptr)
  471. {
  472. ComSmartPtr <IEnumPins> enumerator;
  473. ComSmartPtr <IPin> pin;
  474. filter->EnumPins (enumerator.resetAndGetPointerAddress());
  475. while (enumerator->Next (1, pin.resetAndGetPointerAddress(), 0) == S_OK)
  476. {
  477. PIN_DIRECTION dir;
  478. pin->QueryDirection (&dir);
  479. if (wantedDirection == dir)
  480. {
  481. PIN_INFO info = { 0 };
  482. pin->QueryPinInfo (&info);
  483. if (pinName == nullptr || String (pinName).equalsIgnoreCase (String (info.achName)))
  484. {
  485. result = pin;
  486. return true;
  487. }
  488. }
  489. }
  490. return false;
  491. }
  492. bool connectFilters (IBaseFilter* const first, IBaseFilter* const second) const
  493. {
  494. ComSmartPtr <IPin> in, out;
  495. return getPin (first, PINDIR_OUTPUT, out)
  496. && getPin (second, PINDIR_INPUT, in)
  497. && SUCCEEDED (graphBuilder->Connect (out, in));
  498. }
  499. bool addGraphToRot()
  500. {
  501. ComSmartPtr <IRunningObjectTable> rot;
  502. if (FAILED (GetRunningObjectTable (0, rot.resetAndGetPointerAddress())))
  503. return false;
  504. ComSmartPtr <IMoniker> moniker;
  505. WCHAR buffer[128];
  506. HRESULT hr = CreateItemMoniker (_T("!"), buffer, moniker.resetAndGetPointerAddress());
  507. if (FAILED (hr))
  508. return false;
  509. graphRegistrationID = 0;
  510. return SUCCEEDED (rot->Register (0, graphBuilder, moniker, &graphRegistrationID));
  511. }
  512. void removeGraphFromRot()
  513. {
  514. ComSmartPtr <IRunningObjectTable> rot;
  515. if (SUCCEEDED (GetRunningObjectTable (0, rot.resetAndGetPointerAddress())))
  516. rot->Revoke (graphRegistrationID);
  517. }
  518. static void deleteMediaType (AM_MEDIA_TYPE* const pmt)
  519. {
  520. if (pmt->cbFormat != 0)
  521. CoTaskMemFree ((PVOID) pmt->pbFormat);
  522. if (pmt->pUnk != nullptr)
  523. pmt->pUnk->Release();
  524. CoTaskMemFree (pmt);
  525. }
  526. //==============================================================================
  527. class GrabberCallback : public ComBaseClassHelper <ISampleGrabberCB>
  528. {
  529. public:
  530. GrabberCallback (DShowCameraDeviceInteral& owner_)
  531. : owner (owner_)
  532. {
  533. }
  534. //==============================================================================
  535. STDMETHODIMP SampleCB (double /*SampleTime*/, IMediaSample* /*pSample*/)
  536. {
  537. return E_FAIL;
  538. }
  539. STDMETHODIMP BufferCB (double time, BYTE* buffer, long bufferSize)
  540. {
  541. owner.handleFrame (time, buffer, bufferSize);
  542. return S_OK;
  543. }
  544. private:
  545. DShowCameraDeviceInteral& owner;
  546. GrabberCallback (const GrabberCallback&);
  547. GrabberCallback& operator= (const GrabberCallback&);
  548. };
  549. ComSmartPtr <GrabberCallback> callback;
  550. Array <CameraDevice::Listener*> listeners;
  551. CriticalSection listenerLock;
  552. //==============================================================================
  553. JUCE_DECLARE_NON_COPYABLE (DShowCameraDeviceInteral);
  554. };
  555. //==============================================================================
  556. CameraDevice::CameraDevice (const String& name_, int /*index*/)
  557. : name (name_)
  558. {
  559. isRecording = false;
  560. }
  561. CameraDevice::~CameraDevice()
  562. {
  563. stopRecording();
  564. delete static_cast <DShowCameraDeviceInteral*> (internal);
  565. internal = nullptr;
  566. }
  567. Component* CameraDevice::createViewerComponent()
  568. {
  569. return new DShowCameraDeviceInteral::DShowCaptureViewerComp (static_cast <DShowCameraDeviceInteral*> (internal));
  570. }
  571. String CameraDevice::getFileExtension()
  572. {
  573. return ".wmv";
  574. }
  575. void CameraDevice::startRecordingToFile (const File& file, int quality)
  576. {
  577. stopRecording();
  578. DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal;
  579. d->addUser();
  580. isRecording = d->createFileCaptureFilter (file, quality);
  581. }
  582. Time CameraDevice::getTimeOfFirstRecordedFrame() const
  583. {
  584. DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal;
  585. return d->firstRecordedTime;
  586. }
  587. void CameraDevice::stopRecording()
  588. {
  589. if (isRecording)
  590. {
  591. DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal;
  592. d->removeFileCaptureFilter();
  593. d->removeUser();
  594. isRecording = false;
  595. }
  596. }
  597. void CameraDevice::addListener (Listener* listenerToAdd)
  598. {
  599. DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal;
  600. if (listenerToAdd != nullptr)
  601. d->addListener (listenerToAdd);
  602. }
  603. void CameraDevice::removeListener (Listener* listenerToRemove)
  604. {
  605. DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal;
  606. if (listenerToRemove != nullptr)
  607. d->removeListener (listenerToRemove);
  608. }
  609. //==============================================================================
  610. namespace
  611. {
  612. ComSmartPtr <IBaseFilter> enumerateCameras (StringArray* const names,
  613. const int deviceIndexToOpen,
  614. String& name)
  615. {
  616. int index = 0;
  617. ComSmartPtr <IBaseFilter> result;
  618. ComSmartPtr <ICreateDevEnum> pDevEnum;
  619. HRESULT hr = pDevEnum.CoCreateInstance (CLSID_SystemDeviceEnum);
  620. if (SUCCEEDED (hr))
  621. {
  622. ComSmartPtr <IEnumMoniker> enumerator;
  623. hr = pDevEnum->CreateClassEnumerator (CLSID_VideoInputDeviceCategory, enumerator.resetAndGetPointerAddress(), 0);
  624. if (SUCCEEDED (hr) && enumerator != nullptr)
  625. {
  626. ComSmartPtr <IMoniker> moniker;
  627. ULONG fetched;
  628. while (enumerator->Next (1, moniker.resetAndGetPointerAddress(), &fetched) == S_OK)
  629. {
  630. ComSmartPtr <IBaseFilter> captureFilter;
  631. hr = moniker->BindToObject (0, 0, IID_IBaseFilter, (void**) captureFilter.resetAndGetPointerAddress());
  632. if (SUCCEEDED (hr))
  633. {
  634. ComSmartPtr <IPropertyBag> propertyBag;
  635. hr = moniker->BindToStorage (0, 0, IID_IPropertyBag, (void**) propertyBag.resetAndGetPointerAddress());
  636. if (SUCCEEDED (hr))
  637. {
  638. VARIANT var;
  639. var.vt = VT_BSTR;
  640. hr = propertyBag->Read (_T("FriendlyName"), &var, 0);
  641. propertyBag = nullptr;
  642. if (SUCCEEDED (hr))
  643. {
  644. if (names != nullptr)
  645. names->add (var.bstrVal);
  646. if (index == deviceIndexToOpen)
  647. {
  648. name = var.bstrVal;
  649. result = captureFilter;
  650. break;
  651. }
  652. ++index;
  653. }
  654. }
  655. }
  656. }
  657. }
  658. }
  659. return result;
  660. }
  661. }
  662. StringArray CameraDevice::getAvailableDevices()
  663. {
  664. StringArray devs;
  665. String dummy;
  666. enumerateCameras (&devs, -1, dummy);
  667. return devs;
  668. }
  669. CameraDevice* CameraDevice::openDevice (int index,
  670. int minWidth, int minHeight,
  671. int maxWidth, int maxHeight)
  672. {
  673. ComSmartPtr <ICaptureGraphBuilder2> captureGraphBuilder;
  674. HRESULT hr = captureGraphBuilder.CoCreateInstance (CLSID_CaptureGraphBuilder2);
  675. if (SUCCEEDED (hr))
  676. {
  677. String name;
  678. const ComSmartPtr <IBaseFilter> filter (enumerateCameras (0, index, name));
  679. if (filter != nullptr)
  680. {
  681. ScopedPointer <CameraDevice> cam (new CameraDevice (name, index));
  682. DShowCameraDeviceInteral* const intern
  683. = new DShowCameraDeviceInteral (cam, captureGraphBuilder, filter,
  684. minWidth, minHeight, maxWidth, maxHeight);
  685. cam->internal = intern;
  686. if (intern->ok)
  687. return cam.release();
  688. }
  689. }
  690. return nullptr;
  691. }