관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] 3. AVFoundation 개념 - AVPlayer 오디오 재생, 일시정지, 재생구간 이동 구현 (play, pause, seek) 본문

iOS 기본 (swift)

[iOS - swift] 3. AVFoundation 개념 - AVPlayer 오디오 재생, 일시정지, 재생구간 이동 구현 (play, pause, seek)

jake-kim 2022. 4. 3. 23:17

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로 오디오 재생, 일시정지 방법

play, pause, seek

  • 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

Comments