93 lines
2.9 KiB
Swift
93 lines
2.9 KiB
Swift
//
|
|
// AudioSessionCoordinator.swift
|
|
// Tusker
|
|
//
|
|
// Created by Shadowfacts on 7/8/24.
|
|
// Copyright © 2024 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import AVFoundation
|
|
|
|
final class AudioSessionCoordinator {
|
|
static let shared = AudioSessionCoordinator()
|
|
|
|
private init() {}
|
|
|
|
private let queue = DispatchQueue(label: "AudioSessionCoordinator", qos: .userInitiated)
|
|
|
|
private var videoCount = 0
|
|
private var gifvCount = 0
|
|
|
|
func beginPlayback(mode: Mode, completionHandler: (() -> Void)? = nil) -> Token {
|
|
let token = Token(mode: mode)
|
|
queue.async {
|
|
switch mode {
|
|
case .video:
|
|
self.videoCount += 1
|
|
case .gifv:
|
|
self.gifvCount += 1
|
|
}
|
|
self.update(completionHandler: completionHandler)
|
|
}
|
|
return token
|
|
}
|
|
|
|
func endPlayback(token: Token, completionHandler: (() -> Void)? = nil) {
|
|
// mark the token as consumed, so when it's deinited we don't try to end again
|
|
token.consumed = true
|
|
// the enqueued block can't retain token, since it may be being dealloc'd right now
|
|
let mode = token.mode
|
|
queue.async {
|
|
switch mode {
|
|
case .video:
|
|
self.videoCount -= 1
|
|
case .gifv:
|
|
self.gifvCount -= 1
|
|
}
|
|
self.update(completionHandler: completionHandler)
|
|
}
|
|
}
|
|
|
|
private func update(completionHandler: (() -> Void)?) {
|
|
let currentCategory = AVAudioSession.sharedInstance().category
|
|
if videoCount > 0 {
|
|
try? AVAudioSession.sharedInstance().setCategory(.playback)
|
|
try? AVAudioSession.sharedInstance().setActive(true)
|
|
} else if gifvCount > 0 {
|
|
// if we're transitioning from video to gifv, fully deactivate first
|
|
// in order to let other (music) apps resume, then activate with the
|
|
// ambient category to "mix" with others
|
|
if currentCategory == .playback {
|
|
try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
|
|
}
|
|
// only gifv modes requested, allow mixing with others
|
|
try? AVAudioSession.sharedInstance().setCategory(.ambient)
|
|
try? AVAudioSession.sharedInstance().setActive(true)
|
|
} else {
|
|
try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
|
|
}
|
|
completionHandler?()
|
|
}
|
|
|
|
final class Token {
|
|
let mode: Mode
|
|
fileprivate var consumed = false
|
|
|
|
init(mode: Mode) {
|
|
self.mode = mode
|
|
}
|
|
|
|
deinit {
|
|
if !consumed {
|
|
AudioSessionCoordinator.shared.endPlayback(token: self)
|
|
}
|
|
}
|
|
}
|
|
|
|
enum Mode {
|
|
case video
|
|
case gifv
|
|
}
|
|
}
|