fix macOS build (following Projucer changes made in Windows, which removed /Applications/JUCE/modules from its headers). move JUCE headers under source control, so that Windows and macOS can both build against same version of JUCE. remove AUv3 target (I think it's an iOS thing, so it will never work with this macOS fluidsynth dylib).
This commit is contained in:
249
modules/juce_video/capture/juce_CameraDevice.cpp
Normal file
249
modules/juce_video/capture/juce_CameraDevice.cpp
Normal file
@ -0,0 +1,249 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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 5 End-User License
|
||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
||||
27th April 2017).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-5-licence
|
||||
Privacy Policy: www.juce.com/juce-5-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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_MAC
|
||||
#include "../native/juce_mac_CameraDevice.h"
|
||||
#elif JUCE_WINDOWS
|
||||
#include "../native/juce_win32_CameraDevice.h"
|
||||
#elif JUCE_IOS
|
||||
#if JUCE_CLANG
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
|
||||
#endif
|
||||
|
||||
#include "../native/juce_ios_CameraDevice.h"
|
||||
|
||||
#if JUCE_CLANG
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
#elif JUCE_ANDROID
|
||||
#include "../native/juce_android_CameraDevice.h"
|
||||
#endif
|
||||
|
||||
#if JUCE_ANDROID || JUCE_IOS
|
||||
//==============================================================================
|
||||
class CameraDevice::CameraFactory
|
||||
{
|
||||
public:
|
||||
static CameraFactory& getInstance()
|
||||
{
|
||||
static CameraFactory factory;
|
||||
return factory;
|
||||
}
|
||||
|
||||
void openCamera (int index, OpenCameraResultCallback resultCallback,
|
||||
int minWidth, int minHeight, int maxWidth, int maxHeight, bool useHighQuality)
|
||||
{
|
||||
auto cameraId = getAvailableDevices()[index];
|
||||
|
||||
if (getCameraIndex (cameraId) != -1)
|
||||
{
|
||||
// You are trying to open the same camera twice.
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<CameraDevice> device (new CameraDevice (cameraId, index,
|
||||
minWidth, minHeight, maxWidth,
|
||||
maxHeight, useHighQuality));
|
||||
|
||||
camerasToOpen.add ({ nextRequestId++,
|
||||
std::unique_ptr<CameraDevice> (device.release()),
|
||||
resultCallback });
|
||||
|
||||
auto& pendingOpen = camerasToOpen.getReference (camerasToOpen.size() - 1);
|
||||
|
||||
pendingOpen.device->pimpl->open ([this](const String& deviceId, const String& error)
|
||||
{
|
||||
int index = getCameraIndex (deviceId);
|
||||
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
auto& pendingOpen = camerasToOpen.getReference (index);
|
||||
|
||||
if (error.isEmpty())
|
||||
pendingOpen.resultCallback (pendingOpen.device.release(), error);
|
||||
else
|
||||
pendingOpen.resultCallback (nullptr, error);
|
||||
|
||||
int id = pendingOpen.requestId;
|
||||
|
||||
MessageManager::callAsync ([this, id]() { removeRequestWithId (id); });
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
int getCameraIndex (const String& cameraId) const
|
||||
{
|
||||
for (int i = 0; i < camerasToOpen.size(); ++i)
|
||||
{
|
||||
auto& pendingOpen = camerasToOpen.getReference (i);
|
||||
|
||||
if (pendingOpen.device->pimpl->getCameraId() == cameraId)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void removeRequestWithId (int id)
|
||||
{
|
||||
for (int i = camerasToOpen.size(); --i >= 0;)
|
||||
{
|
||||
if (camerasToOpen.getReference (i).requestId == id)
|
||||
{
|
||||
camerasToOpen.remove (i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PendingCameraOpen
|
||||
{
|
||||
int requestId;
|
||||
std::unique_ptr<CameraDevice> device;
|
||||
OpenCameraResultCallback resultCallback;
|
||||
};
|
||||
|
||||
Array<PendingCameraOpen> camerasToOpen;
|
||||
static int nextRequestId;
|
||||
};
|
||||
|
||||
int CameraDevice::CameraFactory::nextRequestId = 0;
|
||||
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
CameraDevice::CameraDevice (const String& nm, int index, int minWidth, int minHeight, int maxWidth, int maxHeight, bool useHighQuality)
|
||||
: name (nm), pimpl (new Pimpl (*this, name, index, minWidth, minHeight, maxWidth, maxHeight, useHighQuality))
|
||||
{
|
||||
}
|
||||
|
||||
CameraDevice::~CameraDevice()
|
||||
{
|
||||
jassert (juce::MessageManager::getInstance()->currentThreadHasLockedMessageManager());
|
||||
|
||||
stopRecording();
|
||||
pimpl.reset();
|
||||
}
|
||||
|
||||
Component* CameraDevice::createViewerComponent()
|
||||
{
|
||||
return new ViewerComponent (*this);
|
||||
}
|
||||
|
||||
void CameraDevice::takeStillPicture (std::function<void (const Image&)> pictureTakenCallback)
|
||||
{
|
||||
pimpl->takeStillPicture (pictureTakenCallback);
|
||||
}
|
||||
|
||||
void CameraDevice::startRecordingToFile (const File& file, int quality)
|
||||
{
|
||||
stopRecording();
|
||||
pimpl->startRecordingToFile (file, quality);
|
||||
}
|
||||
|
||||
Time CameraDevice::getTimeOfFirstRecordedFrame() const
|
||||
{
|
||||
return pimpl->getTimeOfFirstRecordedFrame();
|
||||
}
|
||||
|
||||
void CameraDevice::stopRecording()
|
||||
{
|
||||
pimpl->stopRecording();
|
||||
}
|
||||
|
||||
void CameraDevice::addListener (Listener* listenerToAdd)
|
||||
{
|
||||
if (listenerToAdd != nullptr)
|
||||
pimpl->addListener (listenerToAdd);
|
||||
}
|
||||
|
||||
void CameraDevice::removeListener (Listener* listenerToRemove)
|
||||
{
|
||||
if (listenerToRemove != nullptr)
|
||||
pimpl->removeListener (listenerToRemove);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
StringArray CameraDevice::getAvailableDevices()
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
return Pimpl::getAvailableDevices();
|
||||
}
|
||||
}
|
||||
|
||||
CameraDevice* CameraDevice::openDevice (int index,
|
||||
int minWidth, int minHeight,
|
||||
int maxWidth, int maxHeight,
|
||||
bool useHighQuality)
|
||||
{
|
||||
jassert (juce::MessageManager::getInstance()->currentThreadHasLockedMessageManager());
|
||||
|
||||
#if ! JUCE_ANDROID && ! JUCE_IOS
|
||||
std::unique_ptr<CameraDevice> d (new CameraDevice (getAvailableDevices() [index], index,
|
||||
minWidth, minHeight, maxWidth, maxHeight, useHighQuality));
|
||||
if (d != nullptr && d->pimpl->openedOk())
|
||||
return d.release();
|
||||
#else
|
||||
ignoreUnused (index, minWidth, minHeight);
|
||||
ignoreUnused (maxWidth, maxHeight, useHighQuality);
|
||||
|
||||
// Use openDeviceAsync to open a camera device on iOS or Android.
|
||||
jassertfalse;
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CameraDevice::openDeviceAsync (int index, OpenCameraResultCallback resultCallback,
|
||||
int minWidth, int minHeight, int maxWidth, int maxHeight, bool useHighQuality)
|
||||
{
|
||||
jassert (juce::MessageManager::getInstance()->currentThreadHasLockedMessageManager());
|
||||
|
||||
if (resultCallback == nullptr)
|
||||
{
|
||||
// A valid callback must be passed.
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
#if JUCE_ANDROID || JUCE_IOS
|
||||
CameraFactory::getInstance().openCamera (index, static_cast<OpenCameraResultCallback&&> (resultCallback),
|
||||
minWidth, minHeight, maxWidth, maxHeight, useHighQuality);
|
||||
#else
|
||||
auto* device = openDevice (index, minWidth, minHeight, maxWidth, maxHeight, useHighQuality);
|
||||
|
||||
resultCallback (device, device != nullptr ? String() : "Could not open camera device");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace juce
|
264
modules/juce_video/capture/juce_CameraDevice.h
Normal file
264
modules/juce_video/capture/juce_CameraDevice.h
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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 5 End-User License
|
||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
||||
27th April 2017).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-5-licence
|
||||
Privacy Policy: www.juce.com/juce-5-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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_USE_CAMERA || DOXYGEN
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Controls any video capture devices that might be available.
|
||||
|
||||
Use getAvailableDevices() to list the devices that are attached to the
|
||||
system, then call openDevice() or openDeviceAsync() to open one for use.
|
||||
Once you have a CameraDevice object, you can get a viewer component from it,
|
||||
and use its methods to stream to a file or capture still-frames.
|
||||
|
||||
@tags{Video}
|
||||
*/
|
||||
class JUCE_API CameraDevice
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~CameraDevice();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a list of the available cameras on this machine.
|
||||
|
||||
You can open one of these devices by calling openDevice() or openDeviceAsync().
|
||||
*/
|
||||
static StringArray getAvailableDevices();
|
||||
|
||||
/** Synchronously opens a camera device. This function should not be used on iOS or
|
||||
Android, use openDeviceAsync() instead.
|
||||
|
||||
The index parameter indicates which of the items returned by getAvailableDevices()
|
||||
to open.
|
||||
|
||||
The size constraints allow the method to choose between different resolutions if
|
||||
the camera supports this. If the resolution can't be specified (e.g. on the Mac)
|
||||
then these will be ignored.
|
||||
|
||||
On Mac, if highQuality is false, then the camera will be opened in preview mode
|
||||
which will allow the OS to drop frames if the computer cannot keep up in processing
|
||||
the frames.
|
||||
*/
|
||||
static CameraDevice* openDevice (int deviceIndex,
|
||||
int minWidth = 128, int minHeight = 64,
|
||||
int maxWidth = 1024, int maxHeight = 768,
|
||||
bool highQuality = true);
|
||||
|
||||
using OpenCameraResultCallback = std::function<void (CameraDevice*, const String& /*error*/)>;
|
||||
|
||||
/** Asynchronously opens a camera device on iOS (iOS 7+) or Android (API 21+).
|
||||
On other platforms, the function will simply call openDevice(). Upon completion,
|
||||
resultCallback will be invoked with valid CameraDevice* and an empty error
|
||||
String on success, or nullptr CameraDevice and a non-empty error String on failure.
|
||||
|
||||
This is the preferred method of opening a camera device, because it works on all
|
||||
platforms, whereas synchronous openDevice() does not work on iOS & Android.
|
||||
|
||||
The index parameter indicates which of the items returned by getAvailableDevices()
|
||||
to open.
|
||||
|
||||
The size constraints allow the method to choose between different resolutions if
|
||||
the camera supports this. If the resolution can't be specified then these will be
|
||||
ignored.
|
||||
|
||||
On iOS, if you want to switch a device, it is more efficient to open a new device
|
||||
before closing the older one, because this way both devices can share the same
|
||||
underlying camera session. Otherwise, the session needs to be close first, and this
|
||||
is a lengthy process that can take several seconds.
|
||||
|
||||
The Android implementation currently supports a maximum recording resolution of
|
||||
1080p. Choosing a larger size will result in larger pictures taken, but the video
|
||||
will be capped at 1080p.
|
||||
*/
|
||||
static void openDeviceAsync (int deviceIndex,
|
||||
OpenCameraResultCallback resultCallback,
|
||||
int minWidth = 128, int minHeight = 64,
|
||||
int maxWidth = 1024, int maxHeight = 768,
|
||||
bool highQuality = true);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the name of this device */
|
||||
const String& getName() const noexcept { return name; }
|
||||
|
||||
/** Creates a component that can be used to display a preview of the
|
||||
video from this camera.
|
||||
|
||||
Note: while you can change the size of the preview component, the actual
|
||||
preview display may be smaller than the size requested, because the correct
|
||||
aspect ratio is maintained automatically.
|
||||
*/
|
||||
Component* createViewerComponent();
|
||||
|
||||
//==============================================================================
|
||||
/** Triggers a still picture capture. Upon completion, pictureTakenCallback will
|
||||
be invoked on a message thread.
|
||||
|
||||
On Android, before calling takeStillPicture(), you need to create a preview with
|
||||
createViewerComponent() and you need to make it visible on screen.
|
||||
|
||||
Android does not support simultaneous video recording and still picture capture.
|
||||
*/
|
||||
void takeStillPicture (std::function<void (const Image&)> pictureTakenCallback);
|
||||
|
||||
/** Starts recording video to the specified file.
|
||||
|
||||
You should use getFileExtension() to find out the correct extension to
|
||||
use for your filename.
|
||||
|
||||
If the file exists, it will be deleted before the recording starts.
|
||||
|
||||
This method may not start recording instantly, so if you need to know the
|
||||
exact time at which the file begins, you can call getTimeOfFirstRecordedFrame()
|
||||
after the recording has finished.
|
||||
|
||||
The quality parameter can be 0, 1, or 2, to indicate low, medium, or high. It may
|
||||
or may not be used, depending on the driver.
|
||||
|
||||
On Android, before calling startRecordingToFile(), you need to create a preview with
|
||||
createViewerComponent() and you need to make it visible on screen.
|
||||
|
||||
The Android camera also requires exclusive access to the audio device, so make sure
|
||||
you close any open audio devices with AudioDeviceManager::closeAudioDevice() first.
|
||||
|
||||
Android does not support simultaneous video recording and still picture capture.
|
||||
|
||||
@see AudioDeviceManager::closeAudioDevice, AudioDeviceManager::restartLastAudioDevice
|
||||
*/
|
||||
void startRecordingToFile (const File& file, int quality = 2);
|
||||
|
||||
/** Stops recording, after a call to startRecordingToFile(). */
|
||||
void stopRecording();
|
||||
|
||||
/** Returns the file extension that should be used for the files
|
||||
that you pass to startRecordingToFile().
|
||||
|
||||
This may be platform-specific, e.g. ".mov" or ".avi".
|
||||
*/
|
||||
static String getFileExtension();
|
||||
|
||||
/** After calling stopRecording(), this method can be called to return the timestamp
|
||||
of the first frame that was written to the file.
|
||||
*/
|
||||
Time getTimeOfFirstRecordedFrame() const;
|
||||
|
||||
/** Set this callback to be notified whenever an error occurs. You may need to close
|
||||
and reopen the device to be able to use it further. */
|
||||
std::function<void (const String& /*error*/)> onErrorOccurred;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Receives callbacks with individual frames from a CameraDevice. It is mainly
|
||||
useful for processing multiple frames that has to be done as quickly as
|
||||
possible. The callbacks can be called from any thread.
|
||||
|
||||
If you just need to take one picture, you should use takeStillPicture() instead.
|
||||
|
||||
@see CameraDevice::addListener
|
||||
*/
|
||||
class JUCE_API Listener
|
||||
{
|
||||
public:
|
||||
Listener() {}
|
||||
virtual ~Listener() {}
|
||||
|
||||
/** This method is called when a new image arrives.
|
||||
|
||||
This may be called by any thread, so be careful about thread-safety,
|
||||
and make sure that you process the data as quickly as possible to
|
||||
avoid glitching!
|
||||
|
||||
Simply add a listener to be continuously notified about new frames becoming
|
||||
available and remove the listener when you no longer need new frames.
|
||||
|
||||
If you just need to take one picture, use takeStillPicture() instead.
|
||||
|
||||
@see CameraDevice::takeStillPicture
|
||||
*/
|
||||
virtual void imageReceived (const Image& image) = 0;
|
||||
};
|
||||
|
||||
/** Adds a listener to receive images from the camera.
|
||||
|
||||
Be very careful not to delete the listener without first removing it by calling
|
||||
removeListener().
|
||||
*/
|
||||
void addListener (Listener* listenerToAdd);
|
||||
|
||||
/** Removes a listener that was previously added with addListener(). */
|
||||
void removeListener (Listener* listenerToRemove);
|
||||
|
||||
private:
|
||||
String name;
|
||||
|
||||
struct Pimpl;
|
||||
friend struct Pimpl;
|
||||
friend struct ContainerDeletePolicy<Pimpl>;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
struct ViewerComponent;
|
||||
friend struct ViewerComponent;
|
||||
|
||||
CameraDevice (const String& name, int index,
|
||||
int minWidth, int minHeight, int maxWidth, int maxHeight, bool highQuality);
|
||||
|
||||
#if JUCE_ANDROID || JUCE_IOS
|
||||
class CameraFactory;
|
||||
#endif
|
||||
|
||||
#if JUCE_ANDROID
|
||||
friend void juce_cameraDeviceStateClosed (int64);
|
||||
friend void juce_cameraDeviceStateDisconnected (int64);
|
||||
friend void juce_cameraDeviceStateError (int64, int);
|
||||
friend void juce_cameraDeviceStateOpened (int64, void*);
|
||||
|
||||
friend void juce_cameraCaptureSessionActive (int64, void*);
|
||||
friend void juce_cameraCaptureSessionClosed (int64, void*);
|
||||
friend void juce_cameraCaptureSessionConfigureFailed (int64, void*);
|
||||
friend void juce_cameraCaptureSessionConfigured (int64, void*);
|
||||
friend void juce_cameraCaptureSessionReady (int64, void*);
|
||||
|
||||
friend void juce_cameraCaptureSessionCaptureCompleted (int64, bool, void*, void*, void*);
|
||||
friend void juce_cameraCaptureSessionCaptureFailed (int64, bool, void*, void*, void*);
|
||||
friend void juce_cameraCaptureSessionCaptureProgressed (int64, bool, void*, void*, void*);
|
||||
friend void juce_cameraCaptureSessionCaptureSequenceAborted (int64, bool, void*, int);
|
||||
friend void juce_cameraCaptureSessionCaptureSequenceCompleted (int64, bool, void*, int, int64);
|
||||
friend void juce_cameraCaptureSessionCaptureStarted (int64, bool, void*, void*, int64, int64);
|
||||
|
||||
friend void juce_deviceOrientationChanged (int64, int);
|
||||
#endif
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CameraDevice)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
Reference in New Issue
Block a user