--- id: ios-audio-video-avkit title: iOS Audio/Video β€” AVFoundation / AVKit category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [ios, audio, video, vibe-coding] tech_stack: { language: "Swift", applicable_to: ["iOS"] } applied_in: [] aliases: [AVFoundation, AVPlayer, AVAudioEngine, AVKit, AirPlay, picture-in-picture] --- # iOS Audio / Video > AVFoundation = audio/video 의 native. **AVPlayer (playback), AVAudioEngine (record/process), AVKit (UI)**. Background audio, AirPlay, PiP. ## πŸ“– 핡심 κ°œλ… - AVPlayer: video / audio μž¬μƒ. - AVAudioEngine: μ •λ°€ audio (record, mix). - AVAudioSession: system κ³Ό ν˜‘μ˜. - HLS / MP4 / DASH 지원. ## πŸ’» μ½”λ“œ νŒ¨ν„΄ ### AVPlayer (간단 video) ```swift import AVKit import SwiftUI struct PlayerView: View { let player = AVPlayer(url: URL(string: "https://...mp4")!) var body: some View { VideoPlayer(player: player) .onAppear { player.play() } .onDisappear { player.pause() } } } ``` ### AVPlayerViewController (UIKit) ```swift import AVKit let player = AVPlayer(url: url) let vc = AVPlayerViewController() vc.player = player present(vc, animated: true) { player.play() } ``` ### Background audio ```swift import AVFoundation // AppDelegate / SwiftUI App do { try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default) try AVAudioSession.sharedInstance().setActive(true) } catch { print(error) } ``` ```xml UIBackgroundModes audio ``` β†’ App backgrounded = audio 계속. ### Now Playing Info ```swift import MediaPlayer MPNowPlayingInfoCenter.default().nowPlayingInfo = [ MPMediaItemPropertyTitle: "Song Title", MPMediaItemPropertyArtist: "Artist", MPMediaItemPropertyArtwork: MPMediaItemArtwork(boundsSize: image.size) { _ in image }, MPMediaItemPropertyPlaybackDuration: duration, MPNowPlayingInfoPropertyElapsedPlaybackTime: 0.0, ] ``` β†’ Lock screen + Control Center + AirPods. ### Remote command (lock screen control) ```swift let center = MPRemoteCommandCenter.shared() center.playCommand.addTarget { _ in player.play(); return .success } center.pauseCommand.addTarget { _ in player.pause(); return .success } center.nextTrackCommand.addTarget { _ in nextTrack(); return .success } ``` ### Picture-in-Picture ```swift let pip = AVPictureInPictureController(playerLayer: playerLayer) pip?.delegate = self // μžλ™ λ˜λŠ” manual pip?.startPictureInPicture() ``` ```xml ``` ### AirPlay ```swift let routePicker = AVRoutePickerView() view.addSubview(routePicker) ``` β†’ AirPlay μžλ™ (Audio session κ°€ .playback). ### HLS streaming ```swift let asset = AVURLAsset(url: URL(string: "https://.../master.m3u8")!) let item = AVPlayerItem(asset: asset) let player = AVPlayer(playerItem: item) ``` β†’ HLS κ°€ native. Adaptive bitrate. ### Download (offline) ```swift import AVFoundation let session = AVAssetDownloadURLSession(...) let task = session.makeAssetDownloadTask(asset: asset, ...) task.resume() // Track progress task.delegate = self ``` β†’ HLS κ°€ offline κ°€λŠ₯. ### AVAudioEngine (μ •λ°€) ```swift let engine = AVAudioEngine() let player = AVAudioPlayerNode() engine.attach(player) engine.connect(player, to: engine.mainMixerNode, format: nil) let file = try AVAudioFile(forReading: url) player.scheduleFile(file, at: nil) try engine.start() player.play() ``` β†’ Effect, mixing, recording. ### Recording ```swift let recorder = try AVAudioRecorder(url: url, settings: [ AVFormatIDKey: kAudioFormatMPEG4AAC, AVSampleRateKey: 44100, AVNumberOfChannelsKey: 1, ]) recorder.record() // Stop recorder.stop() ``` ```swift // Mic permission AVAudioApplication.requestRecordPermission { granted in if granted { ... } } ``` ```xml NSMicrophoneUsageDescription Record audio for ... ``` ### AudioKit (high-level) ```swift import AudioKit let oscillator = Oscillator() let mixer = Mixer(oscillator) let engine = AudioEngine() engine.output = mixer try engine.start() oscillator.start() oscillator.frequency = 440 ``` β†’ AudioKit κ°€ AVAudioEngine 의 wrapper. ### Effect chain ```swift let player = AVAudioPlayerNode() let reverb = AVAudioUnitReverb() reverb.loadFactoryPreset(.cathedral) reverb.wetDryMix = 50 engine.attach(reverb) engine.connect(player, to: reverb, format: nil) engine.connect(reverb, to: engine.mainMixerNode, format: nil) ``` β†’ Multi-effect chain. ### Real-time (low latency) ```swift session.setPreferredIOBufferDuration(0.005) // 5 ms session.setPreferredSampleRate(48000) ``` β†’ Latency ↓ β€” battery / CPU ↑. ### AirPods 톡합 ```swift // .playback κ°€ μžλ™ AirPods. // Spatial audio (iOS 14+): session.setCategory(.playback, mode: .spokenAudio, options: [.allowBluetoothA2DP]) ``` ### Spatial audio (Dolby Atmos) ```swift // AVPlayer κ°€ μžλ™. // Track metadata κ°€ Dolby = automatic spatial. ``` ### Video composition (edit) ```swift let composition = AVMutableComposition() let videoTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) try videoTrack?.insertTimeRange( CMTimeRange(start: .zero, duration: asset.duration), of: asset.tracks(withMediaType: .video).first!, at: .zero ) // Export let exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality) exporter?.outputURL = outputUrl exporter?.outputFileType = .mp4 exporter?.exportAsynchronously { ... } ``` β†’ Trim / merge / overlay. ### Capture (camera + mic) ```swift let session = AVCaptureSession() session.sessionPreset = .high if let device = AVCaptureDevice.default(for: .video), let input = try? AVCaptureDeviceInput(device: device) { session.addInput(input) } let output = AVCaptureMovieFileOutput() session.addOutput(output) session.startRunning() // Record output.startRecording(to: url, recordingDelegate: self) ``` ### CMSampleBuffer (frame-by-frame) ```swift let videoOutput = AVCaptureVideoDataOutput() videoOutput.setSampleBufferDelegate(self, queue: queue) session.addOutput(videoOutput) func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { // λ§€ frame 처리 (ML, filter, ...) } ``` β†’ Vision / Core ML pipeline. ### Now Playing β€” TimeControl ```swift // Periodic time observer let interval = CMTime(seconds: 1, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) player.addPeriodicTimeObserver(forInterval: interval, queue: .main) { time in let currentTime = time.seconds nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentTime MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo } ``` ### Audio session interruption ```swift NotificationCenter.default.addObserver(forName: AVAudioSession.interruptionNotification, object: nil, queue: .main) { note in guard let info = note.userInfo, let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt, let type = AVAudioSession.InterruptionType(rawValue: typeValue) else { return } if type == .began { player.pause() } else if type == .ended { if let opts = info[AVAudioSessionInterruptionOptionKey] as? UInt, AVAudioSession.InterruptionOptions(rawValue: opts).contains(.shouldResume) { player.play() } } } ``` β†’ μ „ν™” / Siri κ°€ interrupt. ### Route change (ν—€λ“œν° unplug) ```swift NotificationCenter.default.addObserver(forName: AVAudioSession.routeChangeNotification, ...) { note in // ν—€λ“œν° unplug = μžλ™ pause (iOS κ°€). } ``` ### Performance ``` - Background queue κ°€ audio processing. - Main thread κ°€ UI 만. - Lazy load (큰 file). - Cache HLS segment. ``` ### SwiftUI VideoPlayer (간단) ```swift import AVKit import SwiftUI VideoPlayer(player: AVPlayer(url: url)) { Text("Custom overlay") } ``` β†’ Built-in (iOS 14+). ### 함정 ``` - AVAudioSession μ•ˆ setCategory: silent mode κ°€ audio mute. - Background mode μ•ˆ enable: app close μ‹œ audio 멈좀. - Now Playing μ•ˆ update: lock screen κ°€ stale. - Memory leak (player not released): file 도 hold. ``` ## πŸ€” μ˜μ‚¬κ²°μ • κΈ°μ€€ | μž‘μ—… | API | |---|---| | λ‹¨μˆœ video | VideoPlayer | | Custom UI | AVPlayer + AVPlayerLayer | | Streaming | HLS + AVPlayer | | Recording | AVAudioRecorder | | Mix / effect | AVAudioEngine | | μ •λ°€ frame | CMSampleBuffer | | Background music | .playback + Background Modes | | AirPlay | .playback (μžλ™) | ## ❌ μ•ˆν‹°νŒ¨ν„΄ - **AudioSession λ¬΄μ‹œ**: silent mode 깨짐. - **Main thread audio process**: lag. - **Background mode μ•ˆ enable**: backgrounded 멈좀. - **Now Playing μ•ˆ update**: lock screen 잘λͺ». - **Interruption handle μ•ˆ**: phone call ν›„ μž¬μƒ X. - **HLS 없이 mp4 streaming**: buffering 자주. - **Memory leak**: file μ•ˆ release. ## πŸ€– LLM ν™œμš© 힌트 - AVPlayer / VideoPlayer κ°€ default. - AVAudioEngine κ°€ μ •λ°€. - Now Playing + Remote Command κ°€ lock screen. - HLS κ°€ streaming ν‘œμ€€. ## πŸ”— κ΄€λ ¨ λ¬Έμ„œ - [[Mobile_Spatial_Audio_Video]] - [[iOS_Background_Tasks]] - [[iOS_Push_Notifications]]