기본 AVPlayerViewController

소개


AVPlayerViewControllerAVKit에 속하는 클래스로 AVPlayer(이는 AVFoundation에 속함)를 래핑하여 훨씬 쉽고 편리하게 구현할 수 있습니다.

이번주에는 현재 작업중인 앱에서 동영상을 재생하는 AVPlayerViewController를 구현했고, 실험으로 PiP(Picture in Picture) 기능을 추가했습니다.

StackOverflow, 기사, 비디오 및 문서에서 정답을 찾은 후 코드는 매우 쉽고 간단했습니다. 처음 상상했던 것보다 훨씬 쉽습니다.

AVPlayer 구현


AVPlayer는 비디오 재생의 핵심입니다. AVPlayer를 생성하는 것은 보기 컨트롤러에 추가하는 것만큼 간단합니다. 여기서 URL는 비디오가 있는 위치입니다.

private lazy var player: AVPlayer = {
    let videoUrl = URL(string: "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")!
    let player = AVPlayer(url: videoUrl)
    return player
}()


플레이어에서 구성할 수 있는 항목이 많이 있지만 이것이 가장 기본적인 구성입니다. 그런 다음 다음을 수행하여 현재 재생 중인 트랙을 교체할 수 있습니다.

player.replaceCurrentItem(with: AVPlayerItem(url: newUrl))

AVPlayer가 실제로 하는 일은 AVPlayerItem 객체입니다. URL로 초기화하는 것은 속기일 뿐입니다.

AVPlayerViewController 구현



뷰 컨트롤러에 AVPlayer가 있으면 AVPlayerViewController를 만들 수 있습니다. 다음 줄을 추가하면 됩니다.

private lazy var playerController: AVPlayerViewController = {
    let playerController = AVPlayerViewController()
    playerController.player = player
    return playerController
}()


내가 말했듯이 AVPlayerViewControllerAVPlayer 인스턴스를 래핑하고 많은 유용한 기능을 무료로 추가합니다.

AVPlayerViewController 제공



이렇게 하면 다른 AVPlayerViewController 하위 클래스와 마찬가지로 UIViewController 모달 방식으로 표시됩니다.

func presentPlayerController() {
    player.play()
    self.present(playerController, animated: true, completion: nil)
}


이 작업을 수행할 시기는 사용자에게 달려 있습니다. 아마도 단추를 클릭하거나 테이블 또는 콜렉션 보기에서 행을 선택한 경우일 것입니다.

편의



이것보다 더 나은 대안이 있을 수 있지만 VideoController 또는 이와 유사한 클래스를 만드는 것이 매우 편리하다는 것을 알았습니다. VideoController 안에 모든 코드를 추가하고 루트UIViewController로 인스턴스화할 수 있습니다.

final class VideoController: NSObject {
    private weak var viewController: UIViewController!

    // MARK: - AV -
    private let player: AVPlayer

    private lazy var playerController: AVPlayerViewController = {
        let playerController = AVPlayerViewController()
        playerController.player = player
        return playerController
    }()

    // MARK: - Init -
    init(viewController: UIViewController, url: URL) {
        self.viewController = viewController
        self.player = AVPlayer(url: url)
        super.init()
    }

    // MARK: - Public -
    func play() {
        player.play()
        viewController.present(playerController, animated: true, completion: nil)
    }
}


사진 속의 사진



이것은 AVPlayerViewController에서 가장 멋진 기능 중 하나입니다. 두 가지 전제 조건이 있습니다.

먼저 대상에 대해 Audio, Airplay, and Picture in Picture 백그라운드 모드를 CapabilitySigning & Capabilities로 구성해야 합니다.



이렇게 하면 앱이 백그라운드에 있을 때 화면 속 화면으로 비디오를 재생할 수 있습니다.

우리가 해야 할 또 다른 일은 didFinishLaunchingWithOptionsAppDelegate 메서드에서 재생 배경 모드를 구성하는 것입니다.

이 정적 메서드를 VideoController에 추가해 보겠습니다.

final class VideoController: NSObject {
    // ...

    static func enableBackgroundMode() {
    let audioSession = AVAudioSession.sharedInstance()
    do {
      try audioSession.setCategory(.playback, mode: .moviePlayback)
    }
    catch {
      print("Setting category to AVAudioSessionCategoryPlayback failed.")
    }
  }
}


그리고 마지막으로 AppDelegate에서 호출합니다.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // ...
    VideoController.enableBackgroundMode()
    // ...
  return true
}


말했듯이 AVPlayerViewController를 사용하면 많은 유용한 기능을 무료로 구현할 수 있으며 PiP는 그 중 하나입니다. 이렇게 하면 속성을 설정하는 것뿐입니다.

private lazy var playerController: AVPlayerViewController = {
  let playerController = AVPlayerViewController()
  playerController.player = player
  playerController.allowsPictureInPicturePlayback = true
  return playerController
}()


의도한 대로 작동합니다.

간단한 단계가 없습니다. PiP 재생 후 비디오가 앱으로 돌아오면 어떻게 됩니까? 지금은 비디오가 닫힙니다. 이를 수정하려면 AVPlayerViewController의 대리자를 설정하고 func playerViewController(_:, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler:)를 구현해야 합니다.

이 방법에서는 다음 두 가지 작업을 수행해야 합니다.
  • (선택 사항) 현재 playerViewController를 발표하고 있는지 확인합니다. 이렇게 하면 몇 가지 이상한 충돌이 해결됩니다.
  • (필수) playerViewController를 제시합니다.

  • 최종 코드




    final class VideoController: NSObject {
        private weak var viewController: UIViewController!
    
        // MARK: - AV -
        private lazy var player: AVPlayer = {
            let videoUrl = URL(string: "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")!
            let player = AVPlayer(url: videoUrl)
            return player
        }()
    
        private lazy var playerController: AVPlayerViewController = {
            let playerController = AVPlayerViewController()
            playerController.delegate = self
            playerController.player = player
            playerController.allowsPictureInPicturePlayback = true
            return playerController
        }()
    
        init(viewController: UIViewController) {
            self.viewController = viewController
            super.init()
        }
    
        func play() {
            player.play()
            viewController.present(playerController, animated: true, completion: nil)
        }
    
        static func enableBackgroundMode() {
            let audioSession = AVAudioSession.sharedInstance()
            do {
                try audioSession.setCategory(.playback, mode: .moviePlayback)
            }
            catch {
                print("Setting category to AVAudioSessionCategoryPlayback failed.")
            }
        }
    }
    
    // MARK: - AVPlayerViewControllerDelegate -
    extension VideoController: AVPlayerViewControllerDelegate {
        func playerViewController(_ playerViewController: AVPlayerViewController,
                                  restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
            if playerViewController === viewController.presentedViewController {
                return
            }
    
            viewController.present(playerViewController, animated: true) {
                completionHandler(false)
            }
        }
    }
    

    좋은 웹페이지 즐겨찾기