[iOS - swift] 3. AVFoundation 개념 - AVPlayer 오디오 재생, 일시정지, 재생구간 이동 구현 (play, pause, seek)
1. AVFoundation 개념 - 구조, AVAsset, AVKit
2. AVFoundation 개념 - AVPlayer, AVPlayerItem
3. AVFoundation 개념 - AVPlayer 오디오 재생, 일시정지, 재생구간 이동 구현 (play, pause, seek)
4. AVFoundation 개념 - AVPlayer, AVPlayerLayer로 동영상 재생 방법
5. AVFoundation 개념 - AVAudioSession 개념 (오디오 활성화, 입출력 변경, 잠금화면 재생, 재생 중 전화가 온 경우 처리)
* 오디오 재생 관련 방법은 AVPlayer 방법과 AVAudioPlayer 사용이 존재
- 단순히 오디오를 재생시키는 경우 AVPlayer를 사용 (play, pause, seek)
- 조금 더 기능많은 AVAudioPlayer는 오디오를 stop, setVolume, 반복 횟수 지정 가능, prepareToPlay(사운드 재생 전에 미리 로드해주고 로딩속도 향상)
AVPlayer로 오디오 재생, 일시정지 방법
- AVPlayerItem(url:) 인스턴스를 생성하여 AVPlayer 인스턴스 생성
- replaceCurrentItem(with:)을 통해 AVPlayer 인스턴스에 AVPlayerItem을 주입하는데, AVPlayer는 오직 한개씩만 item을 다룰 수 있기 때문에 사용
private var player: AVPlayer = {
guard let url = URL(string: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3") else { fatalError() }
let player = AVPlayer()
let playerItem = AVPlayerItem(url: url)
player.replaceCurrentItem(with: playerItem) // AVPlayer는 한번에 하나씩만 다룰 수 있음
return player
}()
- addPeriodicTimeObserver(forInterval:queue:using:)을 통해, 현재까지 재생된 시간 정보를 획득 (옵저빙)
- CMTimeMakeWithSeconds(1, peferredTimescale: Int32(NSEC_PER_SEC)라는 인스턴스는 1초마다 데이터를 받는다는 의미
- CMTime인스턴스를 CMTimeGetSeconds()안에 넣어서 Float64 타입으로 획득
- Float64 타입 값을 String으로 변경해서 UILabel의 text에 입력 (elpasedTimeSecondsFloat, totalTimeSecondsFloat 변수의 didSet에서 수행)
- AVPlayer의 item이 바뀌어도 periodicTimeObserver는 최초 한번만 등록하면 계속 유지되므로 여러번 호출할 필요 x
// call in viewDidLoad
private func addPeriodicTimeObserver() {
let interval = CMTimeMakeWithSeconds(1, preferredTimescale: Int32(NSEC_PER_SEC))
self.player.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] elapsedTime in
let elapsedTimeSecondsFloat = CMTimeGetSeconds(elapsedTime)
let totalTimeSecondsFloat = CMTimeGetSeconds(self?.player.currentItem?.duration ?? CMTimeMake(value: 1, timescale: 1))
guard
!elapsedTimeSecondsFloat.isNaN,
!elapsedTimeSecondsFloat.isInfinite,
!totalTimeSecondsFloat.isNaN,
!totalTimeSecondsFloat.isInfinite
else { return }
self?.elapsedTimeSecondsFloat = elapsedTimeSecondsFloat
self?.totalTimeSecondsFloat = totalTimeSecondsFloat
}
}
- 재생(play)와 일시정지(pause)
- 단순히 AVPlayer 인스턴스의 play()와 pause() 메소드를 사용
@objc private func didTapButton() {
switch self.player.timeControlStatus {
case .paused:
self.player.play()
self.buttonTitle = "일시정지"
case .playing:
self.player.pause()
self.buttonTitle = "재생"
default:
break
}
}
- 재생구간 이동 (seek)
- UISlider의 valueChanged 이벤트가 일어날 때 처리
- AVPlayer 인스턴스의 seek(to:) 메소드 사용
private lazy var playSlider: UISlider = {
let slider = UISlider()
slider.addTarget(self, action: #selector(didChangeSlide), for: .valueChanged)
slider.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(slider)
return slider
}()
@objc private func didChangeSlide() {
self.elapsedTimeSecondsFloat = Float64(self.playSlider.value) * self.totalTimeSecondsFloat
self.player.seek(to: CMTimeMakeWithSeconds(self.elapsedTimeSecondsFloat, preferredTimescale: Int32(NSEC_PER_SEC)))
}
* 전체 소스 코드: https://github.com/JK0369/ExAVPlayerAudio
(레이아웃 구성에 편의를 위해 snapKit 프레임워크 사용)
* 참고
https://stackoverflow.com/questions/3282866/avplayer-vs-avaudioplayer
https://medium.com/@yusasarisoy/manage-an-audio-file-using-avplayer-36d359154861