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.

858 lines
28KB

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