Files
2nd/10_Wiki/Topics/Coding/iOS_Audio_Video_AVKit.md
T
2026-05-10 22:08:15 +09:00

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
vibe-coding
language applicable_to
Swift
iOS
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)

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 표준.

🔗 관련 문서