camera: macOS port via darwin/ split (no shared-file pragmas)
Reuse the AVFoundation Swift files between iOS and macOS without
sprinkling `#if canImport(UIKit)` through them. The split is:
darwin/Camera/ platform-shared (AVFoundation only)
CameraPlugin channel + instance map
CameraInstance session + outputs + texture
CameraSession AVCaptureSession + runtime-error obs
CaptureDevice front/back discovery
PhotoOutput AVCapturePhotoOutput
PreviewSink CVPixelBuffer → FlutterTexture
VideoRecorder AVAssetWriter
DeviceOrientation wire-string enum
ios/Classes/Camera/ iOS-only impls + extensions
AudioSession AVAudioSession.upgradeForRecording
DeviceOrientationBridge UIDevice.orientation listener
CameraSession+iOS AVCaptureSessionWasInterrupted obs
+ InterruptionReason decode + the
application-audio-session flags
(all iOS-only on AVCaptureSession)
CameraSettings UIApplication.openSettingsURLString
FlutterRegistrar+iOS method-form of textures/messenger
macos/Classes/Camera/ macOS no-op stubs (same surface)
AudioSession no-op (no AVAudioSession on macOS)
DeviceOrientationBridge no-op (desktops don't rotate)
CameraSession+macOS no-op setupPlatform()
CameraSettings NSWorkspace → System Settings'
Privacy_Camera pane
FlutterRegistrar+macOS property-form of textures/messenger
`CameraSession.init` now calls `setupPlatform()` which each platform
provides via an extension — keeps the iOS-only interruption observer
and the `automaticallyConfiguresApplicationAudioSession` /
`usesApplicationAudioSession` flags (both iOS-only on AVCaptureSession)
out of the shared file. Flash-mode in PhotoOutput uses
`if #available(macOS 11/13, *)` rather than `#if`, since those are
plain version gates not platform splits.
The shared files compile into the iOS pod from `ios/Classes/Camera-shared/`
and into the macOS pod from `macos/Classes/Camera-shared/`, each a
mirror populated by a `prepare_command` in the podspec:
rm -rf Classes/Camera-shared && cp -R ../darwin/Camera Classes/Camera-shared
Symlinks and `../` source globs both fail — Pathname.glob bails on
symlinks, and CocoaPods silently drops paths that escape the pod
directory. The mirror destinations are .gitignore'd.
macOS UxPlugin now registers CameraPlugin alongside the others.
This commit is contained in:
@@ -1,18 +1,19 @@
|
||||
import AVFoundation
|
||||
import UIKit
|
||||
|
||||
/// Translates between Flutter's `DeviceOrientation` (4 enum values
|
||||
/// shipped as strings across the channel) and AVFoundation's
|
||||
/// `AVCaptureVideoOrientation`, and bridges physical-device rotation
|
||||
/// notifications to a closure callback.
|
||||
///
|
||||
/// Observed orientation source is `UIDevice.current.orientation` —
|
||||
/// independent of any UI orientation lock, so this fires even while
|
||||
/// the app's window is portrait-locked. `.faceUp` / `.faceDown` are
|
||||
/// ignored (no useful direction).
|
||||
/// Bridges physical-device rotation notifications to a closure callback.
|
||||
/// Observed source is `UIDevice.current.orientation` — independent of
|
||||
/// any UI orientation lock, so this fires even while the app's window
|
||||
/// is portrait-locked. `.faceUp` / `.faceDown` are ignored (no useful
|
||||
/// direction).
|
||||
///
|
||||
/// `UIDevice.beginGeneratingDeviceOrientationNotifications()` must be
|
||||
/// called on main, balanced with `end…()`; this class enforces both.
|
||||
///
|
||||
/// iOS-only. The macOS counterpart in
|
||||
/// `macos/Classes/Camera/DeviceOrientationBridge.swift` is a no-op —
|
||||
/// desktops don't rotate, so `current` stays at the initial
|
||||
/// `portraitUp` and the listener never fires.
|
||||
final class DeviceOrientationBridge {
|
||||
typealias Listener = (DeviceOrientationFlutter) -> Void
|
||||
|
||||
@@ -74,23 +75,7 @@ final class DeviceOrientationBridge {
|
||||
deinit { stop() }
|
||||
}
|
||||
|
||||
/// Mirrors Flutter's `DeviceOrientation` — the four cardinal values
|
||||
/// that travel over the `ux/camera` channel as wire strings. `public`
|
||||
/// so the host app's XCTest target can verify the
|
||||
/// `DeviceOrientationFlutter` / `AVCaptureVideoOrientation` mapping
|
||||
/// without `@testable import`.
|
||||
public enum DeviceOrientationFlutter: String {
|
||||
case portraitUp
|
||||
case landscapeLeft
|
||||
case portraitDown
|
||||
case landscapeRight
|
||||
|
||||
/// Parse a wire string. Returns `.portraitUp` for unknown inputs
|
||||
/// (matches the Dart-side fallback in `MethodChannelUxCameraBackend`).
|
||||
public static func parse(_ raw: String?) -> DeviceOrientationFlutter {
|
||||
return DeviceOrientationFlutter(rawValue: raw ?? "") ?? .portraitUp
|
||||
}
|
||||
|
||||
extension DeviceOrientationFlutter {
|
||||
/// `UIDeviceOrientation` → Flutter convention is a direct 1:1 by
|
||||
/// name. Despite the AV-side flip, `UIDeviceOrientation.landscapeLeft`
|
||||
/// and Flutter's `landscapeLeft` describe the same physical pose
|
||||
@@ -105,16 +90,4 @@ public enum DeviceOrientationFlutter: String {
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// AVFoundation video orientation. Translates Flutter's portrait-
|
||||
/// relative convention to AVFoundation's hardware-relative one.
|
||||
/// Used to drive `AVCaptureConnection.videoOrientation`.
|
||||
public var avVideoOrientation: AVCaptureVideoOrientation {
|
||||
switch self {
|
||||
case .portraitUp: return .portrait
|
||||
case .portraitDown: return .portraitUpsideDown
|
||||
case .landscapeLeft: return .landscapeRight
|
||||
case .landscapeRight: return .landscapeLeft
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user