subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
		
			
				
	
	
		
			392 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			392 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
  ==============================================================================
 | 
						|
 | 
						|
   This file is part of the JUCE examples.
 | 
						|
   Copyright (c) 2020 - Raw Material Software Limited
 | 
						|
 | 
						|
   The code included in this file is provided under the terms of the ISC license
 | 
						|
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
						|
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
						|
   without fee is hereby granted provided that the above copyright notice and
 | 
						|
   this permission notice appear in all copies.
 | 
						|
 | 
						|
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
						|
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
						|
   PURPOSE, ARE DISCLAIMED.
 | 
						|
 | 
						|
  ==============================================================================
 | 
						|
*/
 | 
						|
 | 
						|
/*******************************************************************************
 | 
						|
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
						|
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
						|
 | 
						|
 BEGIN_JUCE_PIP_METADATA
 | 
						|
 | 
						|
 name:             CameraDemo
 | 
						|
 version:          1.0.0
 | 
						|
 vendor:           JUCE
 | 
						|
 website:          http://juce.com
 | 
						|
 description:      Showcases camera features.
 | 
						|
 | 
						|
 dependencies:     juce_audio_basics, juce_audio_devices, juce_core, juce_cryptography,
 | 
						|
                   juce_data_structures, juce_events, juce_graphics, juce_gui_basics,
 | 
						|
                   juce_gui_extra, juce_video
 | 
						|
 exporters:        xcode_mac, vs2019, androidstudio, xcode_iphone
 | 
						|
 | 
						|
 moduleFlags:      JUCE_USE_CAMERA=1, JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
						|
 | 
						|
 type:             Component
 | 
						|
 mainClass:        CameraDemo
 | 
						|
 | 
						|
 useLocalCopy:     1
 | 
						|
 | 
						|
 END_JUCE_PIP_METADATA
 | 
						|
 | 
						|
*******************************************************************************/
 | 
						|
 | 
						|
#pragma once
 | 
						|
 | 
						|
#include "../Assets/DemoUtilities.h"
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
class CameraDemo  : public Component
 | 
						|
{
 | 
						|
public:
 | 
						|
    CameraDemo()
 | 
						|
    {
 | 
						|
        setOpaque (true);
 | 
						|
 | 
						|
       #if JUCE_ANDROID
 | 
						|
        // Android requires exclusive access to the audio device when recording videos.
 | 
						|
        audioDeviceManager.closeAudioDevice();
 | 
						|
       #endif
 | 
						|
 | 
						|
        addAndMakeVisible (cameraSelectorComboBox);
 | 
						|
        updateCameraList();
 | 
						|
        cameraSelectorComboBox.setSelectedId (1);
 | 
						|
        cameraSelectorComboBox.onChange = [this] { cameraChanged(); };
 | 
						|
 | 
						|
        addAndMakeVisible (snapshotButton);
 | 
						|
        snapshotButton.onClick = [this] { takeSnapshot(); };
 | 
						|
        snapshotButton.setEnabled (false);
 | 
						|
 | 
						|
        addAndMakeVisible (recordMovieButton);
 | 
						|
        recordMovieButton.onClick = [this] { startRecording(); };
 | 
						|
        recordMovieButton.setEnabled (false);
 | 
						|
 | 
						|
        addAndMakeVisible (lastSnapshot);
 | 
						|
 | 
						|
        cameraSelectorComboBox.setSelectedId (2);
 | 
						|
 | 
						|
        setSize (500, 500);
 | 
						|
 | 
						|
       #if JUCE_IOS || JUCE_ANDROID
 | 
						|
        setPortraitOrientationEnabled (true);
 | 
						|
       #endif
 | 
						|
    }
 | 
						|
 | 
						|
    ~CameraDemo() override
 | 
						|
    {
 | 
						|
       #if JUCE_IOS || JUCE_ANDROID
 | 
						|
        setPortraitOrientationEnabled (false);
 | 
						|
       #endif
 | 
						|
 | 
						|
       #if JUCE_ANDROID
 | 
						|
        audioDeviceManager.restartLastAudioDevice();
 | 
						|
       #endif
 | 
						|
    }
 | 
						|
 | 
						|
    //==============================================================================
 | 
						|
    void paint (Graphics& g) override
 | 
						|
    {
 | 
						|
        g.fillAll (Colours::black);
 | 
						|
    }
 | 
						|
 | 
						|
    void resized() override
 | 
						|
    {
 | 
						|
        auto r = getLocalBounds().reduced (5);
 | 
						|
 | 
						|
        auto top = r.removeFromTop (25);
 | 
						|
        cameraSelectorComboBox.setBounds (top.removeFromLeft (250));
 | 
						|
 | 
						|
        r.removeFromTop (4);
 | 
						|
        top = r.removeFromTop (25);
 | 
						|
 | 
						|
        snapshotButton.changeWidthToFitText (24);
 | 
						|
        snapshotButton.setBounds (top.removeFromLeft (snapshotButton.getWidth()));
 | 
						|
        top.removeFromLeft (4);
 | 
						|
        recordMovieButton.changeWidthToFitText (24);
 | 
						|
        recordMovieButton.setBounds (top.removeFromLeft (recordMovieButton.getWidth()));
 | 
						|
 | 
						|
        r.removeFromTop (4);
 | 
						|
        auto previewArea = shouldUseLandscapeLayout() ? r.removeFromLeft (r.getWidth() / 2)
 | 
						|
                                                      : r.removeFromTop (r.getHeight() / 2);
 | 
						|
 | 
						|
        if (cameraPreviewComp.get() != nullptr)
 | 
						|
            cameraPreviewComp->setBounds (previewArea);
 | 
						|
 | 
						|
        if (shouldUseLandscapeLayout())
 | 
						|
            r.removeFromLeft (4);
 | 
						|
        else
 | 
						|
            r.removeFromTop (4);
 | 
						|
 | 
						|
        lastSnapshot.setBounds (r);
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
private:
 | 
						|
    //==============================================================================
 | 
						|
    // if this PIP is running inside the demo runner, we'll use the shared device manager instead
 | 
						|
   #ifndef JUCE_DEMO_RUNNER
 | 
						|
    AudioDeviceManager audioDeviceManager;
 | 
						|
   #else
 | 
						|
    AudioDeviceManager& audioDeviceManager { getSharedAudioDeviceManager (0, 2) };
 | 
						|
   #endif
 | 
						|
 | 
						|
    std::unique_ptr<CameraDevice> cameraDevice;
 | 
						|
    std::unique_ptr<Component> cameraPreviewComp;
 | 
						|
    ImageComponent lastSnapshot;
 | 
						|
 | 
						|
    ComboBox cameraSelectorComboBox  { "Camera" };
 | 
						|
    TextButton snapshotButton        { "Take a snapshot" };
 | 
						|
   #if ! JUCE_ANDROID && ! JUCE_IOS
 | 
						|
    TextButton recordMovieButton     { "Record a movie (to your desktop)..." };
 | 
						|
   #else
 | 
						|
    TextButton recordMovieButton     { "Record a movie" };
 | 
						|
   #endif
 | 
						|
    bool recordingMovie = false;
 | 
						|
    File recordingFile;
 | 
						|
    bool contentSharingPending = false;
 | 
						|
 | 
						|
    void setPortraitOrientationEnabled (bool shouldBeEnabled)
 | 
						|
    {
 | 
						|
        auto allowedOrientations = Desktop::getInstance().getOrientationsEnabled();
 | 
						|
 | 
						|
        if (shouldBeEnabled)
 | 
						|
            allowedOrientations |= Desktop::upright;
 | 
						|
        else
 | 
						|
            allowedOrientations &= ~Desktop::upright;
 | 
						|
 | 
						|
        Desktop::getInstance().setOrientationsEnabled (allowedOrientations);
 | 
						|
    }
 | 
						|
 | 
						|
    bool shouldUseLandscapeLayout() const noexcept
 | 
						|
    {
 | 
						|
       #if JUCE_ANDROID || JUCE_IOS
 | 
						|
        auto orientation = Desktop::getInstance().getCurrentOrientation();
 | 
						|
        return orientation == Desktop::rotatedClockwise || orientation == Desktop::rotatedAntiClockwise;
 | 
						|
       #else
 | 
						|
        return false;
 | 
						|
       #endif
 | 
						|
    }
 | 
						|
 | 
						|
    void updateCameraList()
 | 
						|
    {
 | 
						|
        cameraSelectorComboBox.clear();
 | 
						|
        cameraSelectorComboBox.addItem ("No camera", 1);
 | 
						|
        cameraSelectorComboBox.addSeparator();
 | 
						|
 | 
						|
        auto cameras = CameraDevice::getAvailableDevices();
 | 
						|
 | 
						|
        for (int i = 0; i < cameras.size(); ++i)
 | 
						|
            cameraSelectorComboBox.addItem (cameras[i], i + 2);
 | 
						|
    }
 | 
						|
 | 
						|
    void cameraChanged()
 | 
						|
    {
 | 
						|
        // This is called when the user chooses a camera from the drop-down list.
 | 
						|
       #if JUCE_IOS
 | 
						|
        // On iOS, when switching camera, open the new camera first, so that it can
 | 
						|
        // share the underlying camera session with the old camera. Otherwise, the
 | 
						|
        // session would have to be closed first, which can take several seconds.
 | 
						|
        if (cameraSelectorComboBox.getSelectedId() == 1)
 | 
						|
            cameraDevice.reset();
 | 
						|
       #else
 | 
						|
        cameraDevice.reset();
 | 
						|
       #endif
 | 
						|
        cameraPreviewComp.reset();
 | 
						|
        recordingMovie = false;
 | 
						|
 | 
						|
        if (cameraSelectorComboBox.getSelectedId() > 1)
 | 
						|
        {
 | 
						|
           #if JUCE_ANDROID || JUCE_IOS
 | 
						|
            openCameraAsync();
 | 
						|
           #else
 | 
						|
            cameraDeviceOpenResult (CameraDevice::openDevice (cameraSelectorComboBox.getSelectedId() - 2), {});
 | 
						|
           #endif
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            snapshotButton   .setEnabled (cameraDevice != nullptr && ! contentSharingPending);
 | 
						|
            recordMovieButton.setEnabled (cameraDevice != nullptr && ! contentSharingPending);
 | 
						|
            resized();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    void openCameraAsync()
 | 
						|
    {
 | 
						|
        SafePointer<CameraDemo> safeThis (this);
 | 
						|
 | 
						|
        CameraDevice::openDeviceAsync (cameraSelectorComboBox.getSelectedId() - 2,
 | 
						|
                                       [safeThis] (CameraDevice* device, const String& error) mutable
 | 
						|
                                       {
 | 
						|
                                           if (safeThis)
 | 
						|
                                               safeThis->cameraDeviceOpenResult (device, error);
 | 
						|
                                       });
 | 
						|
    }
 | 
						|
 | 
						|
    void cameraDeviceOpenResult (CameraDevice* device, const String& error)
 | 
						|
    {
 | 
						|
        // If camera opening worked, create a preview component for it..
 | 
						|
        cameraDevice.reset (device);
 | 
						|
 | 
						|
        if (cameraDevice.get() != nullptr)
 | 
						|
        {
 | 
						|
           #if JUCE_ANDROID
 | 
						|
            SafePointer<CameraDemo> safeThis (this);
 | 
						|
            cameraDevice->onErrorOccurred = [safeThis] (const String& cameraError) mutable { if (safeThis) safeThis->errorOccurred (cameraError); };
 | 
						|
           #endif
 | 
						|
            cameraPreviewComp.reset (cameraDevice->createViewerComponent());
 | 
						|
            addAndMakeVisible (cameraPreviewComp.get());
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon, "Camera open failed",
 | 
						|
                                              "Camera open failed, reason: " + error);
 | 
						|
        }
 | 
						|
 | 
						|
        snapshotButton   .setEnabled (cameraDevice.get() != nullptr && ! contentSharingPending);
 | 
						|
        recordMovieButton.setEnabled (cameraDevice.get() != nullptr && ! contentSharingPending);
 | 
						|
        resized();
 | 
						|
    }
 | 
						|
 | 
						|
    void startRecording()
 | 
						|
    {
 | 
						|
        if (cameraDevice.get() != nullptr)
 | 
						|
        {
 | 
						|
            // The user has clicked the record movie button..
 | 
						|
            if (! recordingMovie)
 | 
						|
            {
 | 
						|
                // Start recording to a file on the user's desktop..
 | 
						|
                recordingMovie = true;
 | 
						|
 | 
						|
               #if JUCE_ANDROID || JUCE_IOS
 | 
						|
                recordingFile = File::getSpecialLocation (File::tempDirectory)
 | 
						|
               #else
 | 
						|
                recordingFile = File::getSpecialLocation (File::userDesktopDirectory)
 | 
						|
               #endif
 | 
						|
                                 .getNonexistentChildFile ("JuceCameraVideoDemo", CameraDevice::getFileExtension());
 | 
						|
 | 
						|
               #if JUCE_ANDROID
 | 
						|
                // Android does not support taking pictures while recording video.
 | 
						|
                snapshotButton.setEnabled (false);
 | 
						|
               #endif
 | 
						|
 | 
						|
                cameraSelectorComboBox.setEnabled (false);
 | 
						|
                cameraDevice->startRecordingToFile (recordingFile);
 | 
						|
                recordMovieButton.setButtonText ("Stop Recording");
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // Already recording, so stop...
 | 
						|
                recordingMovie = false;
 | 
						|
                cameraDevice->stopRecording();
 | 
						|
               #if ! JUCE_ANDROID && ! JUCE_IOS
 | 
						|
                recordMovieButton.setButtonText ("Start recording (to a file on your desktop)");
 | 
						|
               #else
 | 
						|
                recordMovieButton.setButtonText ("Record a movie");
 | 
						|
               #endif
 | 
						|
                cameraSelectorComboBox.setEnabled (true);
 | 
						|
 | 
						|
               #if JUCE_ANDROID
 | 
						|
                snapshotButton.setEnabled (true);
 | 
						|
               #endif
 | 
						|
 | 
						|
               #if JUCE_CONTENT_SHARING
 | 
						|
                URL url (recordingFile);
 | 
						|
 | 
						|
                snapshotButton   .setEnabled (false);
 | 
						|
                recordMovieButton.setEnabled (false);
 | 
						|
                contentSharingPending = true;
 | 
						|
 | 
						|
                SafePointer<CameraDemo> safeThis (this);
 | 
						|
 | 
						|
                juce::ContentSharer::getInstance()->shareFiles ({url},
 | 
						|
                                                                [safeThis] (bool success, const String&) mutable
 | 
						|
                                                                {
 | 
						|
                                                                    if (safeThis)
 | 
						|
                                                                        safeThis->sharingFinished (success, false);
 | 
						|
                                                                });
 | 
						|
               #endif
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    void takeSnapshot()
 | 
						|
    {
 | 
						|
        SafePointer<CameraDemo> safeThis (this);
 | 
						|
        cameraDevice->takeStillPicture ([safeThis] (const Image& image) mutable { safeThis->imageReceived (image); });
 | 
						|
    }
 | 
						|
 | 
						|
    // This is called by the camera device when a new image arrives
 | 
						|
    void imageReceived (const Image& image)
 | 
						|
    {
 | 
						|
        if (! image.isValid())
 | 
						|
            return;
 | 
						|
 | 
						|
        lastSnapshot.setImage (image);
 | 
						|
 | 
						|
       #if JUCE_CONTENT_SHARING
 | 
						|
        auto imageFile = File::getSpecialLocation (File::tempDirectory).getNonexistentChildFile ("JuceCameraPhotoDemo", ".jpg");
 | 
						|
 | 
						|
        FileOutputStream stream (imageFile);
 | 
						|
 | 
						|
        if (stream.openedOk()
 | 
						|
             && JPEGImageFormat().writeImageToStream (image, stream))
 | 
						|
        {
 | 
						|
            URL url (imageFile);
 | 
						|
 | 
						|
            snapshotButton   .setEnabled (false);
 | 
						|
            recordMovieButton.setEnabled (false);
 | 
						|
            contentSharingPending = true;
 | 
						|
 | 
						|
            SafePointer<CameraDemo> safeThis (this);
 | 
						|
 | 
						|
            juce::ContentSharer::getInstance()->shareFiles ({url},
 | 
						|
                                                            [safeThis] (bool success, const String&) mutable
 | 
						|
                                                            {
 | 
						|
                                                                if (safeThis)
 | 
						|
                                                                    safeThis->sharingFinished (success, true);
 | 
						|
                                                            });
 | 
						|
        }
 | 
						|
       #endif
 | 
						|
    }
 | 
						|
 | 
						|
    void errorOccurred (const String& error)
 | 
						|
    {
 | 
						|
        AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon,
 | 
						|
                                          "Camera Device Error",
 | 
						|
                                          "An error has occurred: " + error + " Camera will be closed.");
 | 
						|
 | 
						|
        cameraDevice.reset();
 | 
						|
 | 
						|
        cameraSelectorComboBox.setSelectedId (1);
 | 
						|
        snapshotButton   .setEnabled (false);
 | 
						|
        recordMovieButton.setEnabled (false);
 | 
						|
    }
 | 
						|
 | 
						|
    void sharingFinished (bool success, bool isCapture)
 | 
						|
    {
 | 
						|
        AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon,
 | 
						|
                                          isCapture ? "Image sharing result" : "Video sharing result",
 | 
						|
                                          success ? "Success!" : "Failed!");
 | 
						|
 | 
						|
        contentSharingPending = false;
 | 
						|
        snapshotButton   .setEnabled (true);
 | 
						|
        recordMovieButton.setEnabled (true);
 | 
						|
    }
 | 
						|
 | 
						|
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CameraDemo)
 | 
						|
};
 |