|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2020 - Raw Material Software Limited
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- By using JUCE, you agree to the terms of both the JUCE 6 End-User License
- Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
-
- End User License Agreement: www.juce.com/juce-6-licence
- Privacy Policy: www.juce.com/juce-privacy-policy
-
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- interface ISampleGrabberCB : public IUnknown
- {
- JUCE_COMCALL SampleCB (double, IMediaSample*) = 0;
- JUCE_COMCALL BufferCB (double, BYTE*, long) = 0;
- };
-
- interface ISampleGrabber : public IUnknown
- {
- JUCE_COMCALL SetOneShot (BOOL) = 0;
- JUCE_COMCALL SetMediaType (const AM_MEDIA_TYPE*) = 0;
- JUCE_COMCALL GetConnectedMediaType (AM_MEDIA_TYPE*) = 0;
- JUCE_COMCALL SetBufferSamples (BOOL) = 0;
- JUCE_COMCALL GetCurrentBuffer (long*, long*) = 0;
- JUCE_COMCALL GetCurrentSample (IMediaSample**) = 0;
- JUCE_COMCALL SetCallback (ISampleGrabberCB*, long) = 0;
- };
-
- static const IID IID_ISampleGrabberCB = { 0x0579154A, 0x2B53, 0x4994, { 0xB0, 0xD0, 0xE7, 0x73, 0x14, 0x8E, 0xFF, 0x85 } };
- static const IID IID_ISampleGrabber = { 0x6B652FFF, 0x11FE, 0x4fce, { 0x92, 0xAD, 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F } };
- static const CLSID CLSID_SampleGrabber = { 0xC1F400A0, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } };
- static const CLSID CLSID_NullRenderer = { 0xC1F400A4, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } };
-
-
- struct CameraDevice::Pimpl : public ChangeBroadcaster
- {
- Pimpl (CameraDevice& ownerToUse, const String&, int index,
- int minWidth, int minHeight, int maxWidth, int maxHeight,
- bool /*highQuality*/)
- : owner (ownerToUse)
- {
- HRESULT hr = captureGraphBuilder.CoCreateInstance (CLSID_CaptureGraphBuilder2);
- if (FAILED (hr))
- return;
-
- filter = enumerateCameras (nullptr, index);
- if (filter == nullptr)
- return;
-
- hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph);
- if (FAILED (hr))
- return;
-
- hr = captureGraphBuilder->SetFiltergraph (graphBuilder);
- if (FAILED (hr))
- return;
-
- mediaControl = graphBuilder.getInterface<IMediaControl>();
- if (mediaControl == nullptr)
- return;
-
- {
- ComSmartPtr<IAMStreamConfig> streamConfig;
-
- hr = captureGraphBuilder->FindInterface (&PIN_CATEGORY_CAPTURE, nullptr, filter,
- IID_IAMStreamConfig, (void**) streamConfig.resetAndGetPointerAddress());
-
- if (streamConfig != nullptr)
- {
- getVideoSizes (streamConfig);
-
- if (! selectVideoSize (streamConfig, minWidth, minHeight, maxWidth, maxHeight))
- return;
- }
- }
-
- hr = graphBuilder->AddFilter (filter, _T("Video Capture"));
- if (FAILED (hr))
- return;
-
- hr = smartTee.CoCreateInstance (CLSID_SmartTee);
- if (FAILED (hr))
- return;
-
- hr = graphBuilder->AddFilter (smartTee, _T("Smart Tee"));
- if (FAILED (hr))
- return;
-
- if (! connectFilters (filter, smartTee))
- return;
-
- ComSmartPtr<IBaseFilter> sampleGrabberBase;
- hr = sampleGrabberBase.CoCreateInstance (CLSID_SampleGrabber);
- if (FAILED (hr))
- return;
-
- hr = sampleGrabberBase.QueryInterface (IID_ISampleGrabber, sampleGrabber);
- if (FAILED (hr))
- return;
-
- {
- AM_MEDIA_TYPE mt = {};
- mt.majortype = MEDIATYPE_Video;
- mt.subtype = MEDIASUBTYPE_RGB24;
- mt.formattype = FORMAT_VideoInfo;
- sampleGrabber->SetMediaType (&mt);
- }
-
- callback = new GrabberCallback (*this);
- hr = sampleGrabber->SetCallback (callback, 1);
-
- hr = graphBuilder->AddFilter (sampleGrabberBase, _T("Sample Grabber"));
- if (FAILED (hr))
- return;
-
- ComSmartPtr<IPin> grabberInputPin;
- if (! (getPin (smartTee, PINDIR_OUTPUT, smartTeeCaptureOutputPin, "capture")
- && getPin (smartTee, PINDIR_OUTPUT, smartTeePreviewOutputPin, "preview")
- && getPin (sampleGrabberBase, PINDIR_INPUT, grabberInputPin)))
- return;
-
- hr = graphBuilder->Connect (smartTeePreviewOutputPin, grabberInputPin);
- if (FAILED (hr))
- return;
-
- AM_MEDIA_TYPE mt = {};
- hr = sampleGrabber->GetConnectedMediaType (&mt);
-
- if (auto* pVih = unalignedPointerCast<VIDEOINFOHEADER*> (mt.pbFormat))
- {
- width = pVih->bmiHeader.biWidth;
- height = pVih->bmiHeader.biHeight;
- }
-
- ComSmartPtr<IBaseFilter> nullFilter;
- hr = nullFilter.CoCreateInstance (CLSID_NullRenderer);
- hr = graphBuilder->AddFilter (nullFilter, _T("Null Renderer"));
-
- if (connectFilters (sampleGrabberBase, nullFilter)
- && addGraphToRot())
- {
- activeImage = Image (Image::RGB, width, height, true);
- loadingImage = Image (Image::RGB, width, height, true);
-
- openedSuccessfully = true;
- }
- }
-
- ~Pimpl()
- {
- if (mediaControl != nullptr)
- mediaControl->Stop();
-
- removeGraphFromRot();
- disconnectAnyViewers();
-
- if (sampleGrabber != nullptr)
- {
- sampleGrabber->SetCallback (nullptr, 0);
- sampleGrabber = nullptr;
- }
-
- callback = nullptr;
- graphBuilder = nullptr;
- mediaControl = nullptr;
- filter = nullptr;
- captureGraphBuilder = nullptr;
- smartTee = nullptr;
- smartTeePreviewOutputPin = nullptr;
- smartTeeCaptureOutputPin = nullptr;
- asfWriter = nullptr;
- }
-
- bool openedOk() const noexcept { return openedSuccessfully; }
-
- void takeStillPicture (std::function<void (const Image&)> pictureTakenCallbackToUse)
- {
- {
- const ScopedLock sl (pictureTakenCallbackLock);
-
- jassert (pictureTakenCallbackToUse != nullptr);
-
- if (pictureTakenCallbackToUse == nullptr)
- return;
-
- pictureTakenCallback = std::move (pictureTakenCallbackToUse);
- }
-
- addUser();
- }
-
- void startRecordingToFile (const File& file, int quality)
- {
- addUser();
- isRecording = createFileCaptureFilter (file, quality);
- }
-
- void stopRecording()
- {
- if (isRecording)
- {
- removeFileCaptureFilter();
- removeUser();
- isRecording = false;
- }
- }
-
- Time getTimeOfFirstRecordedFrame() const
- {
- return firstRecordedTime;
- }
-
- void addListener (CameraDevice::Listener* listenerToAdd)
- {
- const ScopedLock sl (listenerLock);
-
- if (listeners.size() == 0)
- addUser();
-
- listeners.add (listenerToAdd);
- }
-
- void removeListener (CameraDevice::Listener* listenerToRemove)
- {
- const ScopedLock sl (listenerLock);
- listeners.remove (listenerToRemove);
-
- if (listeners.size() == 0)
- removeUser();
- }
-
- void callListeners (const Image& image)
- {
- const ScopedLock sl (listenerLock);
- listeners.call ([=] (Listener& l) { l.imageReceived (image); });
- }
-
- void notifyPictureTakenIfNeeded (const Image& image)
- {
- {
- const ScopedLock sl (pictureTakenCallbackLock);
-
- if (pictureTakenCallback == nullptr)
- return;
- }
-
- WeakReference<Pimpl> weakRef (this);
- MessageManager::callAsync ([weakRef, image]() mutable
- {
- if (weakRef == nullptr)
- return;
-
- if (weakRef->pictureTakenCallback != nullptr)
- weakRef->pictureTakenCallback (image);
-
- weakRef->pictureTakenCallback = nullptr;
- });
- }
-
- void addUser()
- {
- if (openedSuccessfully && activeUsers++ == 0)
- mediaControl->Run();
- }
-
- void removeUser()
- {
- if (openedSuccessfully && --activeUsers == 0)
- mediaControl->Stop();
- }
-
- void handleFrame (double /*time*/, BYTE* buffer, long /*bufferSize*/)
- {
- if (recordNextFrameTime)
- {
- const double defaultCameraLatency = 0.1;
-
- firstRecordedTime = Time::getCurrentTime() - RelativeTime (defaultCameraLatency);
- recordNextFrameTime = false;
-
- ComSmartPtr<IPin> pin;
- if (getPin (filter, PINDIR_OUTPUT, pin))
- {
- if (auto pushSource = pin.getInterface<IAMPushSource>())
- {
- REFERENCE_TIME latency = 0;
- pushSource->GetLatency (&latency);
-
- firstRecordedTime = firstRecordedTime - RelativeTime ((double) latency);
- }
- }
- }
-
- {
- const int lineStride = width * 3;
- const ScopedLock sl (imageSwapLock);
-
- {
- loadingImage.duplicateIfShared();
- const Image::BitmapData destData (loadingImage, 0, 0, width, height, Image::BitmapData::writeOnly);
-
- for (int i = 0; i < height; ++i)
- memcpy (destData.getLinePointer ((height - 1) - i),
- buffer + lineStride * i,
- (size_t) lineStride);
- }
-
- imageNeedsFlipping = true;
- }
-
- if (listeners.size() > 0)
- callListeners (loadingImage);
-
- notifyPictureTakenIfNeeded (loadingImage);
-
- sendChangeMessage();
- }
-
- void drawCurrentImage (Graphics& g, Rectangle<int> area)
- {
- if (imageNeedsFlipping)
- {
- const ScopedLock sl (imageSwapLock);
- std::swap (loadingImage, activeImage);
- imageNeedsFlipping = false;
- }
-
- Rectangle<int> centred (RectanglePlacement (RectanglePlacement::centred)
- .appliedTo (Rectangle<int> (width, height), area));
-
- RectangleList<int> borders (area);
- borders.subtract (centred);
- g.setColour (Colours::black);
- g.fillRectList (borders);
-
- g.drawImage (activeImage, centred.getX(), centred.getY(),
- centred.getWidth(), centred.getHeight(), 0, 0, width, height);
- }
-
- bool createFileCaptureFilter (const File& file, int quality)
- {
- removeFileCaptureFilter();
- file.deleteFile();
- mediaControl->Stop();
- firstRecordedTime = Time();
- recordNextFrameTime = true;
- previewMaxFPS = 60;
-
- HRESULT hr = asfWriter.CoCreateInstance (CLSID_WMAsfWriter);
-
- if (SUCCEEDED (hr))
- {
- if (auto fileSink = asfWriter.getInterface<IFileSinkFilter>())
- {
- hr = fileSink->SetFileName (file.getFullPathName().toWideCharPointer(), nullptr);
-
- if (SUCCEEDED (hr))
- {
- hr = graphBuilder->AddFilter (asfWriter, _T("AsfWriter"));
-
- if (SUCCEEDED (hr))
- {
- if (auto asfConfig = asfWriter.getInterface<IConfigAsfWriter>())
- {
- asfConfig->SetIndexMode (true);
- ComSmartPtr<IWMProfileManager> profileManager;
- hr = WMCreateProfileManager (profileManager.resetAndGetPointerAddress());
-
- // This gibberish is the DirectShow profile for a video-only wmv file.
- String prof ("<profile version=\"589824\" storageformat=\"1\" name=\"Quality\" description=\"Quality type for output.\">"
- "<streamconfig majortype=\"{73646976-0000-0010-8000-00AA00389B71}\" streamnumber=\"1\" "
- "streamname=\"Video Stream\" inputname=\"Video409\" bitrate=\"894960\" "
- "bufferwindow=\"0\" reliabletransport=\"1\" decodercomplexity=\"AU\" rfc1766langid=\"en-us\">"
- "<videomediaprops maxkeyframespacing=\"50000000\" quality=\"90\"/>"
- "<wmmediatype subtype=\"{33564D57-0000-0010-8000-00AA00389B71}\" bfixedsizesamples=\"0\" "
- "btemporalcompression=\"1\" lsamplesize=\"0\">"
- "<videoinfoheader dwbitrate=\"894960\" dwbiterrorrate=\"0\" avgtimeperframe=\"$AVGTIMEPERFRAME\">"
- "<rcsource left=\"0\" top=\"0\" right=\"$WIDTH\" bottom=\"$HEIGHT\"/>"
- "<rctarget left=\"0\" top=\"0\" right=\"$WIDTH\" bottom=\"$HEIGHT\"/>"
- "<bitmapinfoheader biwidth=\"$WIDTH\" biheight=\"$HEIGHT\" biplanes=\"1\" bibitcount=\"24\" "
- "bicompression=\"WMV3\" bisizeimage=\"0\" bixpelspermeter=\"0\" biypelspermeter=\"0\" "
- "biclrused=\"0\" biclrimportant=\"0\"/>"
- "</videoinfoheader>"
- "</wmmediatype>"
- "</streamconfig>"
- "</profile>");
-
- const int fps[] = { 10, 15, 30 };
- int maxFramesPerSecond = fps[jlimit (0, numElementsInArray (fps) - 1, quality & 0xff)];
-
- if (((uint32_t) quality & 0xff000000) != 0) // (internal hacky way to pass explicit frame rates for testing)
- maxFramesPerSecond = (quality >> 24) & 0xff;
-
- prof = prof.replace ("$WIDTH", String (width))
- .replace ("$HEIGHT", String (height))
- .replace ("$AVGTIMEPERFRAME", String (10000000 / maxFramesPerSecond));
-
- ComSmartPtr<IWMProfile> currentProfile;
- hr = profileManager->LoadProfileByData (prof.toWideCharPointer(), currentProfile.resetAndGetPointerAddress());
- hr = asfConfig->ConfigureFilterUsingProfile (currentProfile);
-
- if (SUCCEEDED (hr))
- {
- ComSmartPtr<IPin> asfWriterInputPin;
-
- if (getPin (asfWriter, PINDIR_INPUT, asfWriterInputPin, "Video Input 01"))
- {
- hr = graphBuilder->Connect (smartTeeCaptureOutputPin, asfWriterInputPin);
-
- if (SUCCEEDED (hr) && openedSuccessfully && activeUsers > 0
- && SUCCEEDED (mediaControl->Run()))
- {
- previewMaxFPS = (quality < 2) ? 15 : 25; // throttle back the preview comps to try to leave the cpu free for encoding
-
- if ((quality & 0x00ff0000) != 0) // (internal hacky way to pass explicit frame rates for testing)
- previewMaxFPS = (quality >> 16) & 0xff;
-
- return true;
- }
- }
- }
- }
- }
- }
- }
- }
-
- removeFileCaptureFilter();
-
- if (openedSuccessfully && activeUsers > 0)
- mediaControl->Run();
-
- return false;
- }
-
- void removeFileCaptureFilter()
- {
- mediaControl->Stop();
-
- if (asfWriter != nullptr)
- {
- graphBuilder->RemoveFilter (asfWriter);
- asfWriter = nullptr;
- }
-
- if (openedSuccessfully && activeUsers > 0)
- mediaControl->Run();
-
- previewMaxFPS = 60;
- }
-
- static ComSmartPtr<IBaseFilter> enumerateCameras (StringArray* names, const int deviceIndexToOpen)
- {
- int index = 0;
- ComSmartPtr<ICreateDevEnum> pDevEnum;
-
- struct Deleter
- {
- void operator() (IUnknown* ptr) const noexcept { ptr->Release(); }
- };
-
- using ContextPtr = std::unique_ptr<IBindCtx, Deleter>;
-
- if (SUCCEEDED (pDevEnum.CoCreateInstance (CLSID_SystemDeviceEnum)))
- {
- ComSmartPtr<IEnumMoniker> enumerator;
- HRESULT hr = pDevEnum->CreateClassEnumerator (CLSID_VideoInputDeviceCategory, enumerator.resetAndGetPointerAddress(), 0);
-
- if (SUCCEEDED (hr) && enumerator != nullptr)
- {
- ComSmartPtr<IMoniker> moniker;
- ULONG fetched;
-
- while (enumerator->Next (1, moniker.resetAndGetPointerAddress(), &fetched) == S_OK)
- {
- auto context = []
- {
- IBindCtx* ptr = nullptr;
- ignoreUnused (CreateBindCtx (0, &ptr));
- return ContextPtr (ptr);
- }();
-
- ComSmartPtr<IBaseFilter> captureFilter;
- hr = moniker->BindToObject (context.get(), nullptr, IID_IBaseFilter, (void**) captureFilter.resetAndGetPointerAddress());
-
- if (SUCCEEDED (hr))
- {
- ComSmartPtr<IPropertyBag> propertyBag;
- hr = moniker->BindToStorage (context.get(), nullptr, IID_IPropertyBag, (void**) propertyBag.resetAndGetPointerAddress());
-
- if (SUCCEEDED (hr))
- {
- VARIANT var;
- var.vt = VT_BSTR;
-
- hr = propertyBag->Read (_T("FriendlyName"), &var, nullptr);
- propertyBag = nullptr;
-
- if (SUCCEEDED (hr))
- {
- if (names != nullptr)
- names->add (var.bstrVal);
-
- if (index == deviceIndexToOpen)
- return captureFilter;
-
- ++index;
- }
- }
- }
- }
- }
- }
-
- return nullptr;
- }
-
- static StringArray getAvailableDevices()
- {
- StringArray devs;
- enumerateCameras (&devs, -1);
- return devs;
- }
-
- struct GrabberCallback : public ComBaseClassHelperBase<ISampleGrabberCB>
- {
- GrabberCallback (Pimpl& p)
- : ComBaseClassHelperBase (0), owner (p) {}
-
- JUCE_COMRESULT QueryInterface (REFIID refId, void** result)
- {
- if (refId == IID_ISampleGrabberCB)
- return castToType<ISampleGrabberCB> (result);
-
- return ComBaseClassHelperBase<ISampleGrabberCB>::QueryInterface (refId, result);
- }
-
- JUCE_COMRESULT SampleCB (double, IMediaSample*) { return E_FAIL; }
-
- JUCE_COMRESULT BufferCB (double time, BYTE* buffer, long bufferSize)
- {
- owner.handleFrame (time, buffer, bufferSize);
- return S_OK;
- }
-
- Pimpl& owner;
-
- JUCE_DECLARE_NON_COPYABLE (GrabberCallback)
- };
-
- CameraDevice& owner;
-
- ComSmartPtr<GrabberCallback> callback;
-
- CriticalSection listenerLock;
- ListenerList<Listener> listeners;
-
- CriticalSection pictureTakenCallbackLock;
- std::function<void (const Image&)> pictureTakenCallback;
-
- bool isRecording = false, openedSuccessfully = false;
- int width = 0, height = 0;
- Time firstRecordedTime;
-
- Array<ViewerComponent*> viewerComps;
-
- ComSmartPtr<ICaptureGraphBuilder2> captureGraphBuilder;
- ComSmartPtr<IBaseFilter> filter, smartTee, asfWriter;
- ComSmartPtr<IGraphBuilder> graphBuilder;
- ComSmartPtr<ISampleGrabber> sampleGrabber;
- ComSmartPtr<IMediaControl> mediaControl;
- ComSmartPtr<IPin> smartTeePreviewOutputPin, smartTeeCaptureOutputPin;
- int activeUsers = 0;
- Array<int> widths, heights;
- DWORD graphRegistrationID;
-
- CriticalSection imageSwapLock;
- bool imageNeedsFlipping = false;
- Image loadingImage, activeImage;
-
- bool recordNextFrameTime = false;
- int previewMaxFPS = 60;
-
- JUCE_DECLARE_WEAK_REFERENCEABLE (Pimpl)
-
- private:
- void getVideoSizes (IAMStreamConfig* const streamConfig)
- {
- widths.clear();
- heights.clear();
-
- int count = 0, size = 0;
- streamConfig->GetNumberOfCapabilities (&count, &size);
-
- if (size == sizeof (VIDEO_STREAM_CONFIG_CAPS))
- {
- for (int i = 0; i < count; ++i)
- {
- VIDEO_STREAM_CONFIG_CAPS scc;
- AM_MEDIA_TYPE* config;
-
- HRESULT hr = streamConfig->GetStreamCaps (i, &config, (BYTE*) &scc);
-
- if (SUCCEEDED (hr))
- {
- const int w = scc.InputSize.cx;
- const int h = scc.InputSize.cy;
-
- bool duplicate = false;
-
- for (int j = widths.size(); --j >= 0;)
- {
- if (w == widths.getUnchecked (j) && h == heights.getUnchecked (j))
- {
- duplicate = true;
- break;
- }
- }
-
- if (! duplicate)
- {
- widths.add (w);
- heights.add (h);
- }
-
- deleteMediaType (config);
- }
- }
- }
- }
-
- bool selectVideoSize (IAMStreamConfig* const streamConfig,
- const int minWidth, const int minHeight,
- const int maxWidth, const int maxHeight)
- {
- int count = 0, size = 0, bestArea = 0, bestIndex = -1;
- streamConfig->GetNumberOfCapabilities (&count, &size);
-
- if (size == sizeof (VIDEO_STREAM_CONFIG_CAPS))
- {
- AM_MEDIA_TYPE* config;
- VIDEO_STREAM_CONFIG_CAPS scc;
-
- for (int i = 0; i < count; ++i)
- {
- HRESULT hr = streamConfig->GetStreamCaps (i, &config, (BYTE*) &scc);
-
- if (SUCCEEDED (hr))
- {
- if (scc.InputSize.cx >= minWidth
- && scc.InputSize.cy >= minHeight
- && scc.InputSize.cx <= maxWidth
- && scc.InputSize.cy <= maxHeight)
- {
- int area = scc.InputSize.cx * scc.InputSize.cy;
- if (area > bestArea)
- {
- bestIndex = i;
- bestArea = area;
- }
- }
-
- deleteMediaType (config);
- }
- }
-
- if (bestIndex >= 0)
- {
- HRESULT hr = streamConfig->GetStreamCaps (bestIndex, &config, (BYTE*) &scc);
-
- hr = streamConfig->SetFormat (config);
- deleteMediaType (config);
- return SUCCEEDED (hr);
- }
- }
-
- return false;
- }
-
- static bool getPin (IBaseFilter* filter, const PIN_DIRECTION wantedDirection,
- ComSmartPtr<IPin>& result, const char* pinName = nullptr)
- {
- ComSmartPtr<IEnumPins> enumerator;
- ComSmartPtr<IPin> pin;
-
- filter->EnumPins (enumerator.resetAndGetPointerAddress());
-
- while (enumerator->Next (1, pin.resetAndGetPointerAddress(), nullptr) == S_OK)
- {
- PIN_DIRECTION dir;
- pin->QueryDirection (&dir);
-
- if (wantedDirection == dir)
- {
- PIN_INFO info = {};
- pin->QueryPinInfo (&info);
-
- if (pinName == nullptr || String (pinName).equalsIgnoreCase (String (info.achName)))
- {
- result = pin;
- return true;
- }
- }
- }
-
- return false;
- }
-
- bool connectFilters (IBaseFilter* const first, IBaseFilter* const second) const
- {
- ComSmartPtr<IPin> in, out;
-
- return getPin (first, PINDIR_OUTPUT, out)
- && getPin (second, PINDIR_INPUT, in)
- && SUCCEEDED (graphBuilder->Connect (out, in));
- }
-
- bool addGraphToRot()
- {
- ComSmartPtr<IRunningObjectTable> rot;
- if (FAILED (GetRunningObjectTable (0, rot.resetAndGetPointerAddress())))
- return false;
-
- ComSmartPtr<IMoniker> moniker;
- WCHAR buffer[128]{};
- HRESULT hr = CreateItemMoniker (_T("!"), buffer, moniker.resetAndGetPointerAddress());
- if (FAILED (hr))
- return false;
-
- graphRegistrationID = 0;
- return SUCCEEDED (rot->Register (0, graphBuilder, moniker, &graphRegistrationID));
- }
-
- void removeGraphFromRot()
- {
- ComSmartPtr<IRunningObjectTable> rot;
-
- if (SUCCEEDED (GetRunningObjectTable (0, rot.resetAndGetPointerAddress())))
- rot->Revoke (graphRegistrationID);
- }
-
- void disconnectAnyViewers();
-
- static void deleteMediaType (AM_MEDIA_TYPE* const pmt)
- {
- if (pmt->cbFormat != 0)
- CoTaskMemFree ((PVOID) pmt->pbFormat);
-
- if (pmt->pUnk != nullptr)
- pmt->pUnk->Release();
-
- CoTaskMemFree (pmt);
- }
-
- JUCE_DECLARE_NON_COPYABLE (Pimpl)
- };
-
- //==============================================================================
- struct CameraDevice::ViewerComponent : public Component,
- public ChangeListener
- {
- ViewerComponent (CameraDevice& d)
- : owner (d.pimpl.get()), maxFPS (15), lastRepaintTime (0)
- {
- setOpaque (true);
- owner->addChangeListener (this);
- owner->addUser();
- owner->viewerComps.add (this);
- setSize (owner->width, owner->height);
- }
-
- ~ViewerComponent() override
- {
- if (owner != nullptr)
- {
- owner->viewerComps.removeFirstMatchingValue (this);
- owner->removeUser();
- owner->removeChangeListener (this);
- }
- }
-
- void ownerDeleted()
- {
- owner = nullptr;
- }
-
- void paint (Graphics& g) override
- {
- g.setColour (Colours::black);
- g.setImageResamplingQuality (Graphics::lowResamplingQuality);
-
- if (owner != nullptr)
- owner->drawCurrentImage (g, getLocalBounds());
- else
- g.fillAll();
- }
-
- void changeListenerCallback (ChangeBroadcaster*) override
- {
- const int64 now = Time::currentTimeMillis();
-
- if (now >= lastRepaintTime + (1000 / maxFPS))
- {
- lastRepaintTime = now;
- repaint();
-
- if (owner != nullptr)
- maxFPS = owner->previewMaxFPS;
- }
- }
-
- private:
- Pimpl* owner;
- int maxFPS;
- int64 lastRepaintTime;
- };
-
- void CameraDevice::Pimpl::disconnectAnyViewers()
- {
- for (int i = viewerComps.size(); --i >= 0;)
- viewerComps.getUnchecked(i)->ownerDeleted();
- }
-
- String CameraDevice::getFileExtension()
- {
- return ".wmv";
- }
|