9.1 KiB
9.1 KiB
id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
| id | title | category | status | source_trust_level | verification_status | created_at | updated_at | tags | tech_stack | applied_in | aliases | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ios-audio-video-avkit | iOS Audio/Video — AVFoundation / AVKit | Coding | draft | B | conceptual | 2026-05-09 | 2026-05-09 |
|
|
|
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)
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)
import AVKit
let player = AVPlayer(url: url)
let vc = AVPlayerViewController()
vc.player = player
present(vc, animated: true) { player.play() }
Background audio
import AVFoundation
// AppDelegate / SwiftUI App
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print(error)
}
<!-- Info.plist -->
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
→ App backgrounded = audio 계속.
Now Playing Info
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)
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
let pip = AVPictureInPictureController(playerLayer: playerLayer)
pip?.delegate = self
// 자동 또는 manual
pip?.startPictureInPicture()
<!-- Info.plist + Capability: Background Modes - Audio + Picture in Picture -->
AirPlay
let routePicker = AVRoutePickerView()
view.addSubview(routePicker)
→ AirPlay 자동 (Audio session 가 .playback).
HLS streaming
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)
import AVFoundation
let session = AVAssetDownloadURLSession(...)
let task = session.makeAssetDownloadTask(asset: asset, ...)
task.resume()
// Track progress
task.delegate = self
→ HLS 가 offline 가능.
AVAudioEngine (정밀)
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
let recorder = try AVAudioRecorder(url: url, settings: [
AVFormatIDKey: kAudioFormatMPEG4AAC,
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 1,
])
recorder.record()
// Stop
recorder.stop()
// Mic permission
AVAudioApplication.requestRecordPermission { granted in
if granted { ... }
}
<key>NSMicrophoneUsageDescription</key>
<string>Record audio for ...</string>
AudioKit (high-level)
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
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)
session.setPreferredIOBufferDuration(0.005) // 5 ms
session.setPreferredSampleRate(48000)
→ Latency ↓ — battery / CPU ↑.
AirPods 통합
// .playback 가 자동 AirPods.
// Spatial audio (iOS 14+):
session.setCategory(.playback, mode: .spokenAudio, options: [.allowBluetoothA2DP])
Spatial audio (Dolby Atmos)
// AVPlayer 가 자동.
// Track metadata 가 Dolby = automatic spatial.
Video composition (edit)
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)
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)
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
// 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
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)
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 (간단)
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 표준.