33. 音视频入门
音视频开发的关键在于:音频会话配置正确、播放链路可控、资源释放及时。
一、音频会话
import AVFoundation
func setupAudioSession() throws {
let session = AVAudioSession.sharedInstance()
try session.setCategory(.playback, mode: .default, options: [])
try session.setActive(true)
}
常用分类:
.playback:只播放.playAndRecord:录音 + 播放
二、音频播放(本地)
final class AudioPlayer: NSObject, AVAudioPlayerDelegate {
private var player: AVAudioPlayer?
func play(url: URL) throws {
player = try AVAudioPlayer(contentsOf: url)
player?.delegate = self
player?.prepareToPlay()
player?.play()
}
func pause() { player?.pause() }
func stop() { player?.stop() }
}
三、录音
final class AudioRecorder {
private var recorder: AVAudioRecorder?
func requestAndRecord(to url: URL) throws {
let session = AVAudioSession.sharedInstance()
try session.setCategory(.playAndRecord, mode: .default, options: [.defaultToSpeaker])
try session.setActive(true)
session.requestRecordPermission { granted in
guard granted else { return }
let settings: [String: Any] = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 2,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
do {
self.recorder = try AVAudioRecorder(url: url, settings: settings)
self.recorder?.record()
} catch {
print(error)
}
}
}
func stop() { recorder?.stop() }
}
四、视频播放(AVPlayer)
import AVFoundation
final class VideoView: UIView {
private let player = AVPlayer()
private let playerLayer = AVPlayerLayer()
override init(frame: CGRect) {
super.init(frame: frame)
playerLayer.player = player
playerLayer.videoGravity = .resizeAspect
layer.addSublayer(playerLayer)
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
override func layoutSubviews() {
super.layoutSubviews()
playerLayer.frame = bounds
}
func play(url: URL) {
player.replaceCurrentItem(with: AVPlayerItem(url: url))
player.play()
}
}
五、播放进度监听
final class PlayerObserver {
private var token: Any?
private let player: AVPlayer
init(player: AVPlayer) { self.player = player }
func start() {
token = player.addPeriodicTimeObserver(forInterval: CMTime(seconds: 0.5, preferredTimescale: 600), queue: .main) { time in
let seconds = CMTimeGetSeconds(time)
print("progress: \(seconds)")
}
}
deinit {
if let token { player.removeTimeObserver(token) }
}
}
音视频的稳定性主要靠“会话正确 + 资源释放干净 + 主线程不阻塞”。这三点做到位,体验就稳。