THIS IS ELLIE

Adopting Picture in Picture in a Custom Player 본문

개발/iOS

Adopting Picture in Picture in a Custom Player

Ellie Kim 2021. 1. 22. 22:32

Adopting Picture in Picture in a Custom Player 커스텀 플레이어에 PiP 적용하기 

 

개요

커스텀 플레이어 UI에 컨트롤을 추가해 PIP 재생을 호출합니다.

 

살펴보기

AVKit프레임워크의 AVPictureInPictureController클래스를 사용해 커스텀 플레이어에 PIP 재생을 추가하세요. 이 클래스는 AVPlayerViewController에서의 PiP동작과 동일하게 커스텀 플레이어에서 구현할 수 있도록 해줍니다. 

 

커스텀 플레이어 UI 업데이트하기

커스텀 플레이어에 UI를 추가하여 유저가 PiP를 재생할 수 있게 해야 합니다. 이 UI는 AVPlayerViewController에 의해 뜨는 시스템 기본 UI와 일관되어야 합니다. PiP 재생을 컨트롤하기 위한 표준 이미지들에 접근하려면 AVPictureInPictureController의 클래스 프로퍼티인 pictureInPictureButtonStartImage와 pictureInPictureButtonStopImage를 사용하면 됩니다. 이 메서드들은 UI에 나타낼 시스템 기본 이미지를 리턴해줍니다.

@IBOutlet weak var pipButton: UIButton!

override func viewDidLoad() {
    super.viewDidLoad()
    
    let startImage = AVPictureInPictureController.pictureInPictureButtonStartImage
    let stopImage = AVPictureInPictureController.pictureInPictureButtonStopImage
    
    pipButton.setImage(startImage, for: .normal)
    pipButton.setImage(stopImage, for: .selected)
}

 

Picture in Picture Controller 만들기

앱에서 PiP 재생을 컨트롤 하기위해 AVPictureInPictureController의 인스턴스를 생성합니다. 컨트롤러 인스턴스를 만들기 전에 현재 기기가 PiP 재생을 지원하는지 isPictureInPictureSupported() 메서드를 호출해 검증이 필요합니다.  

func setupPictureInPicture() {
    // Ensure PiP is supported by current device.
    if AVPictureInPictureController.isPictureInPictureSupported() {
        // Create a new controller, passing the reference to the AVPlayerLayer.
        pictureInPictureController = AVPictureInPictureController(playerLayer: playerLayer)
        pictureInPictureController.delegate = self
        
        pipPossibleObservation = pictureInPictureController.observe(\AVPictureInPictureController.isPictureInPicturePossible,
                                                                    options: [.initial, .new]) { [weak self] _, change in
            // Update the PiP button's enabled state.
            self?.pictureInPictureButton.isEnabled = change.newValue ?? false
        }
    } else {
        // PiP isn't supported by the current device. Disable the PiP button.
        pictureInPictureButton.isEnabled = false
    }
}

이 예제는 새로운 AVPictureInPictureController 인스턴스를 생성하고, 비디오 콘텐츠를 나타내는 AVPlayerLayer에 레퍼런스를 전달합니다. PiP기능이 작동하려면 컨트롤러 객체에 대해 강력한 참조를 유지해야 합니다.

참고: PiP 디스플레이에서 AVPictureInPictureController로 전달된 AVPlayerLayer가 사용되지 않습니다. 그래서 AVFoundation은 PiP모드가 활성화되어 있는 동안 비디오 프레임 제공을 멈추게 합니다.

PiP 생명주기 이벤트들에 참여하려면, AVPictureInPicutreControllerDelegate프로토콜을 채택하고 스스로를 컨트롤러의 델리게이트로 설정되어야 합니다. 또한 컨트롤러의 isPictureInPicturePossible프로퍼티 KVO를 사용하세요. 이 프로퍼티는 현재 콘텍스트에서 PiP 모드를 사용할 수 있는지 여부를 확인할 수 있습니다. 예를 들어 시스템이 FaceTime을 활성화해 윈도우에 표시한다면 이 프로퍼티를 옵저빙 하여 PiP버튼의 활성화인지 비활성화인지의 상태를 적절한 때에 결정할 수 있습니다.

 

사용자가 시작한 요청 처리

AVPictureInPictureController 설정이 완료되면, @IBAction 메서드를 추가해 PiP 재생을 시작하거나 멈추게 하기 위한 사용자가 시작한 요청을 처리해줍니다.

@IBAction func togglePictureInPictureMode(_ sender: UIButton) {
    if pictureInPictureController.isPictureInPictureActive {
        pictureInPictureController.stopPictureInPicture()
    } else {
        pictureInPictureController.startPictureInPicture()
    }
}

 

중요: PiP를 유저의 인터렉션에 의해서만 시작하게 하세요. 그리고 프로그램적으로 시작하게 하지 마세요. 이 요구사항을 따르지 않은 앱은 앱 스토어 리뷰팀이 리젝 시킬 겁니다.

 

앱에 컨트롤 복구하기 (PiP였다가 다시 앱으로 돌아가는 상황)

사용자가 PiP 윈도우에서 버튼을 탭 하면 앱에 컨트롤 가능함을 리턴합니다. 기본적으로 컨트롤(제어권)이 앱으로 돌아갈 때 재생을 종료합니다. AVKit는 앱이 어떻게 구성되어 있는지 가정할 수 없으며 어떻게 비디오 재생 인터페이스를 복구하는지 올바르게 복구하는지 모릅니다. 대신 그 책임은 개발자에게 위임합니다.

복구 프로세스를 처리하기 위해 pictureInPictureController(_: restoreUserInterfaceForPictureInPictureStopWithCompletionHandler: ) 델리게이트 메서드를 구현하고 필요하면 플레이어 인터페이스를 복구합니다. 복구가 완료되면 true로 완료 핸들러를 호출합니다.

func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController,
                                restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
    // Restore user interface
    completionHandler(true)
}

 

재생 컨트롤 닫기

PiP 모드가 활성화되어 있는 동안, PiP 모드가 활성화 상태임을 나타내기 위해 메인 플레이어의 재생 컨트롤을 해제하고 바운드 안에 아트워크를 표시합니다. 이 기능을 구현하려면 picureInPictureControllerWillStartPictureInPicture(_: )과 pictuerInPictureControllerDidStopPictureInPicutre(_: )델리게이트 메서드를 사용하고 아래 주석(재생 컨트롤 숨기기 등)과 같이 필요한 조치를 취하세요. 

func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
    // hide playback controls
    // show placeholder artwork
}

func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
    // hide placeholder artwork
    // show playback controls
}

 

오역이 있거나 잘못 된 점이 있다면 지적해주세요.

 

resource: developer.apple.com/documentation/avkit/adopting_picture_in_picture_in_a_custom_player

반응형