From 7290d6dc2ff88a2d2d940c529e6f143f91b03991 Mon Sep 17 00:00:00 2001 From: reuk Date: Mon, 28 Oct 2024 21:26:43 +0000 Subject: [PATCH] CameraDevice: Fix deprecation warnings for iOS 17 --- .../juce_video/native/juce_CameraDevice_ios.h | 166 ++++++++++++++++-- 1 file changed, 148 insertions(+), 18 deletions(-) diff --git a/modules/juce_video/native/juce_CameraDevice_ios.h b/modules/juce_video/native/juce_CameraDevice_ios.h index a6e4e61ecd..54162e9b71 100644 --- a/modules/juce_video/native/juce_CameraDevice_ios.h +++ b/modules/juce_video/native/juce_CameraDevice_ios.h @@ -34,6 +34,55 @@ struct CameraDevice::Pimpl { + static void applyDeviceOrientation (AVCaptureDevice*, AVCaptureVideoPreviewLayer*, AVCaptureConnection* outputConnection) + { + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + const auto statusBarOrientation = [UIApplication sharedApplication].statusBarOrientation; + const auto videoOrientation = statusBarOrientation != UIInterfaceOrientationUnknown + ? (AVCaptureVideoOrientation) statusBarOrientation + : AVCaptureVideoOrientationPortrait; + outputConnection.videoOrientation = videoOrientation; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } + + struct PreviewLayerAngleTrait + { + #if defined (__IPHONE_17_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_17_0 + API_AVAILABLE (ios (17)) + static void newFn (AVCaptureDevice* device, AVCaptureVideoPreviewLayer* previewLayer, AVCaptureConnection* outputConnection) + { + const NSUniquePtr coordinator + { + [[AVCaptureDeviceRotationCoordinator alloc] initWithDevice: device + previewLayer: previewLayer] + }; + + outputConnection.videoRotationAngle = coordinator.get().videoRotationAngleForHorizonLevelPreview; + } + #endif + + static constexpr auto oldFn = applyDeviceOrientation; + }; + + struct CaptureLayerAngleTrait + { + #if defined (__IPHONE_17_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_17_0 + API_AVAILABLE (ios (17)) + static void newFn (AVCaptureDevice* device, AVCaptureVideoPreviewLayer* previewLayer, AVCaptureConnection* outputConnection) + { + const NSUniquePtr coordinator + { + [[AVCaptureDeviceRotationCoordinator alloc] initWithDevice: device + previewLayer: previewLayer] + }; + + outputConnection.videoRotationAngle = coordinator.get().videoRotationAngleForHorizonLevelCapture; + } + #endif + + static constexpr auto oldFn = applyDeviceOrientation; + }; + using InternalOpenCameraResultCallback = std::function; Pimpl (CameraDevice& ownerToUse, const String& cameraIdToUse, int /*index*/, @@ -423,6 +472,9 @@ private: } previewLayer = [AVCaptureVideoPreviewLayer layerWithSession: captureSession.get()]; + + updatePreviewOrientation(); + return previewLayer; } @@ -435,7 +487,7 @@ private: return; } - stillPictureTaker.takePicture (previewLayer.connection.videoOrientation); + stillPictureTaker.takePicture(); } void startRecording (const File& file) @@ -455,7 +507,7 @@ private: return; } - videoRecorder.startRecording (file, previewLayer.connection.videoOrientation); + videoRecorder.startRecording (file); } void stopRecording() @@ -468,6 +520,21 @@ private: return videoRecorder.getTimeOfFirstRecordedFrame(); } + AVCaptureDevice* getDevice() const + { + return cameraDevice; + } + + AVCaptureVideoPreviewLayer* getPreviewLayer() const + { + return previewLayer; + } + + void updatePreviewOrientation() + { + ifelse_17_0 (cameraDevice, previewLayer, previewLayer.connection); + } + JUCE_DECLARE_WEAK_REFERENCEABLE (CaptureSession) private: @@ -554,7 +621,7 @@ private: class StillPictureTaker { public: - StillPictureTaker (CaptureSession& cs) + explicit StillPictureTaker (CaptureSession& cs) : captureSession (cs), captureOutput (createCaptureOutput()), photoOutputDelegate (nullptr) @@ -566,7 +633,7 @@ private: captureSession.addOutputIfPossible (captureOutput); } - void takePicture (AVCaptureVideoOrientation orientationToUse) + void takePicture() { if (takingPicture) { @@ -583,7 +650,8 @@ private: { auto* photoOutput = (AVCapturePhotoOutput*) captureOutput; auto outputConnection = [photoOutput connectionWithMediaType: AVMediaTypeVideo]; - outputConnection.videoOrientation = orientationToUse; + + ifelse_17_0 (captureSession.cameraDevice, captureSession.previewLayer, outputConnection); [photoOutput capturePhotoWithSettings: [AVCapturePhotoSettings photoSettings] delegate: id (photoOutputDelegate.get())]; @@ -844,8 +912,9 @@ private: class VideoRecorder { public: - VideoRecorder (CaptureSession& session) - : movieFileOutput ([AVCaptureMovieFileOutput new]), + explicit VideoRecorder (CaptureSession& session) + : captureSession (session), + movieFileOutput ([AVCaptureMovieFileOutput new]), delegate (nullptr) { static FileOutputRecordingDelegateClass cls; @@ -864,7 +933,7 @@ private: jassert (! recordingInProgress); } - void startRecording (const File& file, AVCaptureVideoOrientation orientationToUse) + void startRecording (const File& file) { printVideoOutputDebugInfo (movieFileOutput); @@ -872,7 +941,7 @@ private: isDirectory: NO]; auto outputConnection = [movieFileOutput connectionWithMediaType: AVMediaTypeVideo]; - outputConnection.videoOrientation = orientationToUse; + ifelse_17_0 (captureSession.cameraDevice, captureSession.previewLayer, outputConnection); [movieFileOutput startRecordingToOutputFileURL: url recordingDelegate: delegate.get()]; } @@ -951,6 +1020,7 @@ private: static void setOwner (id self, VideoRecorder* r) { object_setInstanceVariable (self, "owner", r); } }; + CaptureSession& captureSession; AVCaptureMovieFileOutput* movieFileOutput; std::unique_ptr, NSObjectDeleter> delegate; bool recordingInProgress = false; @@ -1086,7 +1156,7 @@ struct CameraDevice::ViewerComponent : public UIViewComponent //============================================================================== struct JuceCameraDeviceViewerClass : public ObjCClass { - JuceCameraDeviceViewerClass() : ObjCClass ("JuceCameraDeviceViewerClass_") + JuceCameraDeviceViewerClass() : ObjCClass ("JuceCameraDeviceViewerClass_") { addMethod (@selector (layoutSubviews), [] (id self, SEL) @@ -1101,6 +1171,20 @@ struct CameraDevice::ViewerComponent : public UIViewComponent previewLayer.frame = asUIView.bounds; }); + addMethod (@selector (observeValueForKeyPath:ofObject:change:context:), + [] (id self, SEL, NSString* keyPath, id, NSDictionary*, void* context) + { + if ([keyPath isEqualToString: @"videoRotationAngleForHorizonLevelPreview"]) + { + if (auto* previewLayer = getPreviewLayer (self)) + { + auto* viewer = static_cast (context); + auto& session = viewer->cameraDevice.pimpl->captureSession; + session.updatePreviewOrientation(); + } + } + }); + registerClass(); } @@ -1118,6 +1202,9 @@ struct CameraDevice::ViewerComponent : public UIViewComponent static void updateOrientation (id self) { + if (@available (ios 17, *)) + return; + if (auto* previewLayer = getPreviewLayer (self)) { UIDeviceOrientation o = [UIDevice currentDevice].orientation; @@ -1125,13 +1212,52 @@ struct CameraDevice::ViewerComponent : public UIViewComponent if (UIDeviceOrientationIsPortrait (o) || UIDeviceOrientationIsLandscape (o)) { if (previewLayer.connection != nil) + { + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") previewLayer.connection.videoOrientation = (AVCaptureVideoOrientation) o; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } } } } }; - ViewerComponent (CameraDevice& device) + struct AddObserverTrait + { + #if defined (__IPHONE_17_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_17_0 + API_AVAILABLE (ios (17)) + static void newFn (ViewerComponent* self) + { + auto& session = self->cameraDevice.pimpl->captureSession; + self->coordinator.reset ([[AVCaptureDeviceRotationCoordinator alloc] initWithDevice: session.getDevice() + previewLayer: session.getPreviewLayer()]); + [self->coordinator.get() addObserver: static_cast (self->getView()) + forKeyPath: @"videoRotationAngleForHorizonLevelPreview" + options: NSKeyValueObservingOptionNew + context: self]; + } + #endif + + static void oldFn (ViewerComponent*) {} + }; + + struct RemoveObserverTrait + { + API_AVAILABLE (ios (17)) + static void newFn (ViewerComponent* self) + { + if (self->coordinator != nullptr) + { + [self->coordinator.get() removeObserver: static_cast (self->getView()) + forKeyPath: @"videoRotationAngleForHorizonLevelPreview"]; + } + } + + static void oldFn (ViewerComponent*) {} + }; + + explicit ViewerComponent (CameraDevice& device) + : cameraDevice (device) { static JuceCameraDeviceViewerClass cls; @@ -1144,15 +1270,19 @@ struct CameraDevice::ViewerComponent : public UIViewComponent auto* previewLayer = device.pimpl->captureSession.createPreviewLayer(); previewLayer.frame = view.bounds; - UIInterfaceOrientation statusBarOrientation = [UIApplication sharedApplication].statusBarOrientation; - AVCaptureVideoOrientation videoOrientation = statusBarOrientation != UIInterfaceOrientationUnknown - ? (AVCaptureVideoOrientation) statusBarOrientation - : AVCaptureVideoOrientationPortrait; - - previewLayer.connection.videoOrientation = videoOrientation; - [view.layer addSublayer: previewLayer]; + + ifelse_17_0 (this); } + + ~ViewerComponent() override + { + ifelse_17_0 (this); + } + + CameraDevice& cameraDevice; + + NSUniquePtr coordinator; }; //==============================================================================