Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support attachVideo() for visionOS. #1395

Merged
merged 1 commit into from
Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Examples/visionOS/ContentView.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import RealityKit
import RealityKitContent
import SwiftUI

struct ContentView: View {
Expand Down
4 changes: 0 additions & 4 deletions Sources/Extension/AVCaptureDevice+Extension.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#if os(iOS) || os(tvOS) || os(macOS)

import AVFoundation
import Foundation

Expand All @@ -21,5 +19,3 @@ extension AVCaptureDevice {
}
}
}

#endif
8 changes: 6 additions & 2 deletions Sources/Extension/AVCaptureDevice.Format+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@ extension AVCaptureDevice.Format {
return true
}
}
#elseif os(visionOS)
extension AVCaptureDevice.Format {
var isMultiCamSupported: Bool {
return false
}
}
#endif

#if os(iOS) || os(tvOS) || os(macOS)
@available(tvOS 17.0, *)
extension AVCaptureDevice.Format {
func isFrameRateSupported(_ frameRate: Float64) -> Bool {
Expand All @@ -42,4 +47,3 @@ extension AVCaptureDevice.Format {
return false
}
}
#endif
4 changes: 2 additions & 2 deletions Sources/Extension/AVCaptureSession+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ extension AVCaptureSession {
}
}
}
#elseif os(iOS) || os(tvOS) || os(macOS)
#endif

@available(tvOS 17.0, *)
extension AVCaptureSession {
@available(iOS, obsoleted: 16.0)
Expand All @@ -35,5 +36,4 @@ extension AVCaptureSession {
}
}
}
#endif
// swiftlint:enable unused_setter_value
2 changes: 0 additions & 2 deletions Sources/Extension/AVFrameRateRange+Extension.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import AVFoundation
import Foundation

#if os(iOS) || os(tvOS) || os(macOS)
@available(tvOS 17.0, *)
extension AVFrameRateRange {
func clamp(rate: Float64) -> Float64 {
Expand All @@ -12,4 +11,3 @@ extension AVFrameRateRange {
(minFrameRate...maxFrameRate) ~= frameRate
}
}
#endif
43 changes: 26 additions & 17 deletions Sources/IO/IOCaptureSession.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
#if os(iOS) || os(tvOS) || os(macOS)
import AVFoundation

protocol IOCaptureSessionDelegate: AnyObject {
@available(tvOS 17.0, *)
func captureSession(_ session: IOCaptureSession, sessionRuntimeError session: AVCaptureSession, error: AVError)
#if os(iOS) || os(tvOS)
#if os(iOS) || os(tvOS) || os(visionOS)
@available(tvOS 17.0, *)
func captureSession(_ session: IOCaptureSession, sessionWasInterrupted session: AVCaptureSession, reason: AVCaptureSession.InterruptionReason?)
@available(tvOS 17.0, *)
Expand All @@ -21,8 +20,10 @@ final class IOCaptureSession {
return false
}
}
#else
#elseif os(macOS)
static let isMultiCamSupported = true
#elseif os(visionOS)
static let isMultiCamSupported = false
Copy link
Owner Author

@shogo4405 shogo4405 Mar 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ The 'stat' is the require reason api. Please check privacy manifest files.

#endif

#if os(iOS) || os(tvOS)
Expand All @@ -39,9 +40,12 @@ final class IOCaptureSession {
var isMultitaskingCameraAccessEnabled: Bool {
return session.isMultitaskingCameraAccessEnabled
}
#else
#elseif os(macOS)
let isMultiCamSessionEnabled = true
let isMultitaskingCameraAccessEnabled = true
#elseif os(visionOS)
let isMultiCamSessionEnabled = false
let isMultitaskingCameraAccessEnabled = false
#endif

weak var delegate: (any IOCaptureSessionDelegate)?
Expand Down Expand Up @@ -76,7 +80,10 @@ final class IOCaptureSession {
session.commitConfiguration()
}
}
#elseif os(iOS) || os(macOS)
#elseif os(visionOS)
/// The capture session instance.
private(set) lazy var session = AVCaptureSession()
#else
var sessionPreset: AVCaptureSession.Preset = .default {
didSet {
guard sessionPreset != oldValue, session.canSetSessionPreset(sessionPreset) else {
Expand Down Expand Up @@ -125,6 +132,7 @@ final class IOCaptureSession {

@available(tvOS 17.0, *)
func attachCapture(_ capture: any IOCaptureUnit) {
#if !os(visionOS)
if let connection = capture.connection {
if let input = capture.input, session.canAddInput(input) {
session.addInputWithNoConnections(input)
Expand All @@ -135,23 +143,26 @@ final class IOCaptureSession {
if session.canAddConnection(connection) {
session.addConnection(connection)
}
} else {
if let input = capture.input, session.canAddInput(input) {
session.addInput(input)
}
if let output = capture.output, session.canAddOutput(output) {
session.addOutput(output)
}
return
}
#endif
if let input = capture.input, session.canAddInput(input) {
session.addInput(input)
}
if let output = capture.output, session.canAddOutput(output) {
session.addOutput(output)
}
}

@available(tvOS 17.0, *)
func detachCapture(_ capture: any IOCaptureUnit) {
#if !os(visionOS)
if let connection = capture.connection {
if capture.output?.connections.contains(connection) == true {
session.removeConnection(connection)
}
}
#endif
if let input = capture.input, session.inputs.contains(input) {
session.removeInput(input)
}
Expand Down Expand Up @@ -199,15 +210,15 @@ final class IOCaptureSession {
@available(tvOS 17.0, *)
private func addSessionObservers(_ session: AVCaptureSession) {
NotificationCenter.default.addObserver(self, selector: #selector(sessionRuntimeError(_:)), name: .AVCaptureSessionRuntimeError, object: session)
#if os(iOS) || os(tvOS)
#if os(iOS) || os(tvOS) || os(visionOS)
NotificationCenter.default.addObserver(self, selector: #selector(sessionInterruptionEnded(_:)), name: .AVCaptureSessionInterruptionEnded, object: session)
NotificationCenter.default.addObserver(self, selector: #selector(sessionWasInterrupted(_:)), name: .AVCaptureSessionWasInterrupted, object: session)
#endif
}

@available(tvOS 17.0, *)
private func removeSessionObservers(_ session: AVCaptureSession) {
#if os(iOS) || os(tvOS)
#if os(iOS) || os(tvOS) || os(visionOS)
NotificationCenter.default.removeObserver(self, name: .AVCaptureSessionWasInterrupted, object: session)
NotificationCenter.default.removeObserver(self, name: .AVCaptureSessionInterruptionEnded, object: session)
#endif
Expand All @@ -234,7 +245,7 @@ final class IOCaptureSession {
delegate?.captureSession(self, sessionRuntimeError: session, error: error)
}

#if os(iOS) || os(tvOS)
#if os(iOS) || os(tvOS) || os(visionOS)
@available(tvOS 17.0, *)
@objc
private func sessionWasInterrupted(_ notification: Notification) {
Expand Down Expand Up @@ -286,5 +297,3 @@ extension IOCaptureSession: Running {
}
}
}

#endif
2 changes: 0 additions & 2 deletions Sources/IO/IOCaptureUnit.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#if os(iOS) || os(tvOS) || os(macOS)
import AVFoundation
import Foundation

Expand All @@ -14,4 +13,3 @@ protocol IOCaptureUnit {
var output: Output? { get set }
var connection: AVCaptureConnection? { get set }
}
#endif
12 changes: 5 additions & 7 deletions Sources/IO/IOMixer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ protocol IOMixerDelegate: AnyObject {
func mixer(_ mixer: IOMixer, didOutput video: CMSampleBuffer)
func mixer(_ mixer: IOMixer, videoErrorOccurred error: IOVideoUnitError)
func mixer(_ mixer: IOMixer, audioErrorOccurred error: IOAudioUnitError)
#if os(iOS) || os(tvOS)
#if os(iOS) || os(tvOS) || os(visionOS)
@available(tvOS 17.0, *)
func mixer(_ mixer: IOMixer, sessionWasInterrupted session: AVCaptureSession, reason: AVCaptureSession.InterruptionReason?)
@available(tvOS 17.0, *)
Expand Down Expand Up @@ -41,13 +41,11 @@ final class IOMixer {
return videoIO
}()

#if os(iOS) || os(tvOS) || os(macOS)
private(set) lazy var session = {
var session = IOCaptureSession()
session.delegate = self
return session
}()
#endif

private(set) lazy var audioEngine: AVAudioEngine? = {
return IOStream.audioEngineHolder.retain()
Expand All @@ -57,7 +55,7 @@ final class IOMixer {
IOStream.audioEngineHolder.release(audioEngine)
}

#if os(iOS) || os(tvOS)
#if os(iOS) || os(tvOS) || os(visionOS)
func setBackgroundMode(_ background: Bool) {
guard #available(tvOS 17.0, *) else {
return
Expand Down Expand Up @@ -135,11 +133,11 @@ extension IOMixer: AudioCodecDelegate {
}
}

#if os(iOS) || os(tvOS) || os(macOS)
extension IOMixer: IOCaptureSessionDelegate {
// MARK: IOCaptureSessionDelegate
@available(tvOS 17.0, *)
func captureSession(_ capture: IOCaptureSession, sessionRuntimeError session: AVCaptureSession, error: AVError) {
#if os(iOS) || os(tvOS) || os(macOS)
switch error.code {
case .unsupportedDeviceActiveFormat:
guard let device = error.device, let format = device.videoFormat(
Expand All @@ -165,9 +163,10 @@ extension IOMixer: IOCaptureSessionDelegate {
default:
break
}
#endif
}

#if os(iOS) || os(tvOS)
#if os(iOS) || os(tvOS) || os(visionOS)
@available(tvOS 17.0, *)
func captureSession(_ _: IOCaptureSession, sessionWasInterrupted session: AVCaptureSession, reason: AVCaptureSession.InterruptionReason?) {
delegate?.mixer(self, sessionWasInterrupted: session, reason: reason)
Expand All @@ -179,7 +178,6 @@ extension IOMixer: IOCaptureSessionDelegate {
}
#endif
}
#endif

extension IOMixer: IOAudioUnitDelegate {
// MARK: IOAudioUnitDelegate
Expand Down
15 changes: 5 additions & 10 deletions Sources/IO/IOStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public protocol IOStreamDelegate: AnyObject {
func stream(_ stream: IOStream, didOutput audio: AVAudioBuffer, when: AVAudioTime)
/// Tells the receiver to a video incoming.
func stream(_ stream: IOStream, didOutput video: CMSampleBuffer)
#if os(iOS) || os(tvOS)
#if os(iOS) || os(tvOS) || os(visionOS)
/// Tells the receiver to session was interrupted.
@available(tvOS 17.0, *)
func stream(_ stream: IOStream, sessionWasInterrupted session: AVCaptureSession, reason: AVCaptureSession.InterruptionReason?)
Expand Down Expand Up @@ -290,11 +290,9 @@ open class IOStream: NSObject {
guard #available(tvOS 17.0, *) else {
return
}
#if os(iOS) || os(tvOS) || os(macOS)
if newValue != nil && self.mixer.videoIO.hasDevice {
self.mixer.session.startRunning()
}
#endif
}
}
}
Expand Down Expand Up @@ -332,13 +330,12 @@ open class IOStream: NSObject {
/// Creates a NetStream object.
override public init() {
super.init()
#if os(iOS) || os(tvOS)
#if os(iOS) || os(tvOS) || os(visionOS)
NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
#endif
}

#if os(iOS) || os(macOS) || os(tvOS)
/// Attaches the primary camera object.
/// - Warning: This method can't use appendSampleBuffer at the same time.
@available(tvOS 17.0, *)
Expand Down Expand Up @@ -387,6 +384,7 @@ open class IOStream: NSObject {
}
}

#if os(iOS) || os(macOS) || os(tvOS)
/// Attaches the audio capture object.
/// - Warning: This method can't use appendSampleBuffer at the same time.
@available(tvOS 17.0, *)
Expand Down Expand Up @@ -483,10 +481,7 @@ open class IOStream: NSObject {
mixer.muxer = telly
mixer.startRunning()
case .publish:
#if os(iOS) || os(tvOS) || os(macOS)
// Start capture audio and video data.
mixer.session.startRunning()
#endif
case .publishing(let muxer):
mixer.muxer = muxer
mixer.startRunning()
Expand All @@ -495,7 +490,7 @@ open class IOStream: NSObject {
}
}

#if os(iOS) || os(tvOS)
#if os(iOS) || os(tvOS) || os(visionOS)
@objc
private func didEnterBackground(_ notification: Notification) {
// Require main thread. Otherwise the microphone cannot be used in the background.
Expand Down Expand Up @@ -529,7 +524,7 @@ extension IOStream: IOMixerDelegate {
delegate?.stream(self, videoErrorOccurred: error)
}

#if os(iOS) || os(tvOS)
#if os(iOS) || os(tvOS) || os(visionOS)
@available(tvOS 17.0, *)
func mixer(_ mixer: IOMixer, sessionWasInterrupted session: AVCaptureSession, reason: AVCaptureSession.InterruptionReason?) {
delegate?.stream(self, sessionWasInterrupted: session, reason: reason)
Expand Down
Loading