uakihir0 / UIPiPView

This library is a UIView that is capable of Picture-in-Picture (PiP) in iOS.
MIT License
219 stars 32 forks source link

Don't work on iOS16 #11

Closed Reactor13 closed 2 years ago

Reactor13 commented 2 years ago

HI

Unfortunately, the library stopped working on iOS 16. Only a gray screen is displayed. Any ideas how to fix it?

uakihir0 commented 2 years ago

Thanks for the confirmation. We will try to address the correction as soon as possible.

cohen72 commented 2 years ago

@Reactor13 @uakihir0 I was experiencing this issue too, and I had to do some weird magic to get it working ... seems Apple has changed something in their internal implementation of AVPictureInPictureController(contentSource: method. Here's what fixed it for me:

It seems that by initializing the AVPictureInPictureController with an empty contentSource, followed by again setting the contentSource within another DispatchQueue.main.async got it working. Hope this helps somehow!

        if self.pipController == nil {
            // Initializing the pipController with an empty content source allows some
            // unknown internal process to properly setup the `AVPictureInPictureController`
            // before starting the picture-in-picture (`startPictureInPicture`)

            self.pipController = AVPictureInPictureController(
                contentSource: AVPictureInPictureController.ContentSource(
                    sampleBufferDisplayLayer: AVSampleBufferDisplayLayer(),
                    playbackDelegate: self)
            )
            self.pipController?.canStartPictureInPictureAutomaticallyFromInline = false
            self.pipController?.delegate = self
            self.pipController?.requiresLinearPlayback = false
        }

        // Once the `AVPictureInPictureController` has been initialized with an empty `contentSource`
        // the setting of it's actual `contentSource`, together with `startPictureInPicture`
        // is dispatched to the main queue asynchronously in order for it to work properly.
        // Without placing self.pipController?.contentSource = contentSource within the execution block
        // the PiP window shows a gray screen and endless loader.

        DispatchQueue.main.async {
            self.pipController?.contentSource = contentSource
            self.pipController?.startPictureInPicture()
        }
Reactor13 commented 2 years ago

I have fixed this issue by adding pipController.requiresLinearPlayback = true to this part of code: 💁‍♂️

if (pipController.isPictureInPicturePossible) {

            /// Start asynchronously after processing is complete
            /// (will not work if run here synchronously)
            DispatchQueue.main.async { [weak self] in
                pipController.startPictureInPicture()
                pipController.requiresLinearPlayback = true
                if let ti = refreshInterval {
                    self?.setRenderInterval(ti)
                }
            }

        } else {
            /// It will take some time for PiP to become available.
            pipPossibleObservation = pipController.observe(
                \AVPictureInPictureController.isPictureInPicturePossible,
                options: [.initial, .new]) { [weak self] _, change in
                guard let self = self else { return }

                if (change.newValue ?? false) {
                    pipController.startPictureInPicture()
                    pipController.requiresLinearPlayback = true
                    self.pipPossibleObservation = nil
                    if let ti = refreshInterval {
                        self.setRenderInterval(ti)
                    }
                }
            }
        }
uakihir0 commented 2 years ago

@cohen72 @Reactor13

Thank you all so much!

I too have found that I can solve this problem by generating AVPictureInPictureController with lazy as follows. Presumably this problem is an OS bug caused by iOS 16.

I have also confirmed here that requiresLinearPlayback cures the problem. This is fine as a fix, but since PiP using controls is also possible, I'm going to fix it as follows.

    private lazy var pipController: AVPictureInPictureController? = {
        if UIPiPView.isUIPiPViewSupported(), #available(iOS 15.0, *) {
            let controller = AVPictureInPictureController(contentSource: .init(
                sampleBufferDisplayLayer: pipBufferDisplayLayer,
                playbackDelegate: self))
            controller.delegate = self
            return controller
        } else {
            print("[UIPiPView] UIPiPView cannot be used on this device or OS.")
            return nil
        }
    }()
Reactor13 commented 2 years ago

Thank you for this awesome library )