Возобновление AVPlayer после телефонного звонка - PullRequest
0 голосов
/ 04 февраля 2020

Похоже, есть много решений для решения этой проблемы, но ни одно из этих решений не помогло мне. В настоящее время я использую Swift 5. У меня есть AVPlayer, воспроизводящий анимацию (которая зацикливается) в моем ViewController. Когда вызов поступает через CallKit, независимо от того, отвечаю я на вызов или отклоняю его, анимация, воспроизводимая AVPlayer, не возобновляется после обработки вызова. Кажется, что обработчик прерываний называется до прерывания, но обычно не вызывается после прерывания.

        override func viewDidLoad() {
            super.viewDidLoad()
            prepareBGVideo()
            ...
            NotificationCenter.default.addObserver(
                self,
                selector: #selector(applicationWillEnterForeground(notification:)),
                name: UIApplication.willEnterForegroundNotification,
                object: nil)
            ...
        }        

       func prepareBGVideo() {
            guard let path = Bundle.main.path(forResource: "animation", ofType:"mp4") else {
                print("video not found")
                return
            }

            let item = AVPlayerItem(url: URL(fileURLWithPath: path))
            avPlayer = AVPlayer(playerItem: item)

            NotificationCenter.default.addObserver(self,
                                                   selector: #selector(loopVideoBG),
                                                   name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
                                                   object: item)
            NotificationCenter.default.addObserver(self, selector: #selector(handleInterruption(notification:)), name: AVAudioSession.interruptionNotification, object: nil)
            avPlayerLayer = AVPlayerLayer(player: avPlayer)
            avPlayerLayer.backgroundColor = UIColor.black.cgColor
            avPlayer.volume = 0
            avPlayer.actionAtItemEnd = .none
            avPlayer.play()

            view.backgroundColor = .clear
            avPlayerLayer.frame = view.layer.bounds
            view.layer.insertSublayer(avPlayerLayer, at: 0)
            avPlayerLayer.videoGravity = isIPAD ? AVLayerVideoGravity.resize : AVLayerVideoGravity.resizeAspectFill // Changed from AVLayerVideoGravity.resizeAspect to AVLayerVideoGravity.resize so that video fits iPad screen

            NotificationCenter.default.addObserver(self,
                                                   selector: #selector(willEnterForeground),
                                                   name: UIApplication.willEnterForegroundNotification,
                                                   object: nil)
        }

        @objc func handleInterruption(notification: Notification) {
            guard let info = notification.userInfo,
                let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt,
                let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
                    return
            }
            if type == .began {
                // Interruption began, take appropriate actions (save state, update user interface)
                self.avPlayer.pause()
            } else if type == .ended {
                guard let optionsValue =
                    info[AVAudioSessionInterruptionOptionKey] as? UInt else {
                        return
                }
                let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
                if options.contains(.shouldResume) {
                    // Interruption Ended - playback should resume
                    self.avPlayer.play()
                }
            }
        }

        /// Resume video while app wake up from background
        @objc func willEnterForeground() {
            avPlayer.seek(to: CMTime.zero)
            JPUtility.shared.performOperation(0.1) {
                self.avPlayer.play()
            }
        }

        @objc func loopVideoBG() {
            avPlayer.seek(to: CMTime.zero)
            avPlayer.play()
        }

Вот все решения, которые у меня есть пробовал:

  1. две секунды ожидания перед вызовом self.avPlayer.play() in if options.contains(.shouldResume){}
  2. Установка AVAudioSession.sharedInstance().setActive в ложь, когда начинается прерывание, а затем установка это не правда, когда прерывание заканчивается . Проблема с этим подходом заключается в том, что блок if interruption == .ended {} не всегда вызывается, поэтому настройка setActive не имела никакого эффекта.
  3. Установка AVAudioSession категории воспроизведения на AVAudioSessionCategoryOptions.MixWithOthers. В моей анимации все равно нет звука.

Я видел упоминания о возобновлении воспроизведения в applicationDidBecomeActive(_:), но некоторые советуют против этого. Будет ли это хорошей практикой?

Есть ли способ обеспечить выполнение блока else if type == .ended {}? Или, возможно, обходной путь, который работает более надежно, чем наблюдение AVAudioSession.interruptionNotification?

1 Ответ

0 голосов
/ 08 февраля 2020

Я решил это, но создал общий класс VideoPlayer, который содержал ссылки на весь экран, на котором были анимации.

import Foundation
import UIKit
import AVKit

    class VideoPlayer: NSObject {

        static var shared: VideoPlayer = VideoPlayer()

        var avPlayer: AVPlayer!
        var avPlayerLayer: AVPlayerLayer!

        weak var vcForConnect:ConnectVC?
        weak var vcForList:ListVC?

        override init() {
            super.init()
            guard let path = Bundle.main.path(forResource: "animation", ofType:"mp4") else {
                print("video not found")
                return
            }
            avPlayer = AVPlayer(url: URL(fileURLWithPath: path))
            avPlayerLayer = AVPlayerLayer(player: avPlayer)
            avPlayerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
            avPlayer.volume = 0
            avPlayer.actionAtItemEnd = .none
            loopVideo(videoPlayer: avPlayer)
            avPlayer.play()
            NotificationCenter.default.addObserver(self, selector: #selector(handleInterruption(notification:)), name: AVAudioSession.interruptionNotification, object: nil)

        }

        deinit {
            avPlayer.pause()
        }

        @objc func handleInterruption(notification: Notification) {
            guard let info = notification.userInfo,
                let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt,
                let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
                    return
            }
            if type == .began {
                // Interruption began, take appropriate actions (save state, update user interface)
                self.avPlayer.pause()
            } else if type == .ended {
                guard let optionsValue =
                    info[AVAudioSessionInterruptionOptionKey] as? UInt else {
                        return
                }
                let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
                if options.contains(.shouldResume) {
                    // Interruption Ended - playback should resume
                    self.avPlayer.play()
                }
            }
        }

        func resumeAllAnimations() {
            self.avPlayer.play()
            if vcForList?.avPlayer != nil {
                vcForList?.avPlayer.play()
            }
            if vcForConnect?.avPlayer != nil {
                vcForConnect?.avPlayer.play()
            }
            if vcForConnect?.avPlayerBG != nil {
                vcForConnect?.avPlayerBG.play()
            }
        }
        ...
    }

Затем я возобновляю анимацию, вызывая resumeAllAnimations() в applicationDidBecomeActive(_:) в AppDelegate.swift примерно так:

func applicationDidBecomeActive(_ application: UIApplication) {
        VideoPlayer.shared.resumeAllAnimations()
        ...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...