react-native-video/ios/Video/Features/RCTPlayerObserver.swift
Nick Fujita 8b75438148
VEX-5682: iOS Swift Conversion (#11)
Converts ios implementation from objective-c to swift.
2021-10-27 10:35:07 +09:00

213 lines
9.5 KiB
Swift

import AVFoundation
import AVKit
import Foundation
@objc
protocol RCTPlayerObserverHandlerObjc {
func handleDidFailToFinishPlaying(notification:NSNotification!)
func handlePlaybackStalled(notification:NSNotification!)
func handlePlayerItemDidReachEnd(notification:NSNotification!)
// unused
// func handleAVPlayerAccess(notification:NSNotification!)
}
protocol RCTPlayerObserverHandler: RCTPlayerObserverHandlerObjc {
func handleTimeUpdate(time:CMTime)
func handleReadyForDisplay(changeObject: Any, change:NSKeyValueObservedChange<Bool>)
func handleTimeMetadataChange(playerItem:AVPlayerItem, change:NSKeyValueObservedChange<[AVMetadataItem]?>)
func handlePlayerItemStatusChange(playerItem:AVPlayerItem, change:NSKeyValueObservedChange<AVPlayerItem.Status>)
func handlePlaybackBufferKeyEmpty(playerItem:AVPlayerItem, change:NSKeyValueObservedChange<Bool>)
func handlePlaybackLikelyToKeepUp(playerItem:AVPlayerItem, change:NSKeyValueObservedChange<Bool>)
func handlePlaybackRateChange(player: AVPlayer, change: NSKeyValueObservedChange<Float>)
func handleExternalPlaybackActiveChange(player: AVPlayer, change: NSKeyValueObservedChange<Bool>)
func handleViewControllerOverlayViewFrameChange(overlayView:UIView, change:NSKeyValueObservedChange<CGRect>)
}
class RCTPlayerObserver: NSObject {
var _handlers: RCTPlayerObserverHandler!
var player:AVPlayer? {
didSet {
if player == nil {
removePlayerObservers()
removePlayerTimeObserver()
} else {
addPlayerObservers()
addPlayerTimeObserver()
}
}
}
var playerItem:AVPlayerItem? {
didSet {
if playerItem == nil {
removePlayerItemObservers()
} else {
addPlayerItemObservers()
}
}
}
var playerViewController:AVPlayerViewController? {
didSet {
if playerViewController == nil {
removePlayerViewControllerObservers()
} else {
addPlayerViewControllerObservers()
}
}
}
var playerLayer:AVPlayerLayer? {
didSet {
if playerLayer == nil {
removePlayerLayerObserver()
} else {
addPlayerLayerObserver()
}
}
}
private var _progressUpdateInterval:TimeInterval = 250
private var _timeObserver:Any?
private var _playerRateChangeObserver:NSKeyValueObservation?
private var _playerExpernalPlaybackActiveObserver:NSKeyValueObservation?
private var _playerItemStatusObserver:NSKeyValueObservation?
private var _playerPlaybackBufferEmptyObserver:NSKeyValueObservation?
private var _playerPlaybackLikelyToKeepUpObserver:NSKeyValueObservation?
private var _playerTimedMetadataObserver:NSKeyValueObservation?
private var _playerViewControllerReadyForDisplayObserver:NSKeyValueObservation?
private var _playerLayerReadyForDisplayObserver:NSKeyValueObservation?
private var _playerViewControllerOverlayFrameObserver:NSKeyValueObservation?
deinit {
NotificationCenter.default.removeObserver(_handlers)
}
func addPlayerObservers() {
guard let player = player else {
return
}
_playerRateChangeObserver = player.observe(\.rate, changeHandler: _handlers.handlePlaybackRateChange)
_playerExpernalPlaybackActiveObserver = player.observe(\.isExternalPlaybackActive, changeHandler: _handlers.handleExternalPlaybackActiveChange)
}
func removePlayerObservers() {
_playerRateChangeObserver?.invalidate()
_playerExpernalPlaybackActiveObserver?.invalidate()
}
func addPlayerItemObservers() {
guard let playerItem = playerItem else { return }
_playerItemStatusObserver = playerItem.observe(\.status, options: [.new, .old], changeHandler: _handlers.handlePlayerItemStatusChange)
_playerPlaybackBufferEmptyObserver = playerItem.observe(\.isPlaybackBufferEmpty, options: [.new, .old], changeHandler: _handlers.handlePlaybackBufferKeyEmpty)
_playerPlaybackLikelyToKeepUpObserver = playerItem.observe(\.isPlaybackLikelyToKeepUp, options: [.new, .old], changeHandler: _handlers.handlePlaybackLikelyToKeepUp)
_playerTimedMetadataObserver = playerItem.observe(\.timedMetadata, options: [.new], changeHandler: _handlers.handleTimeMetadataChange)
}
func removePlayerItemObservers() {
_playerItemStatusObserver?.invalidate()
_playerPlaybackBufferEmptyObserver?.invalidate()
_playerPlaybackLikelyToKeepUpObserver?.invalidate()
_playerTimedMetadataObserver?.invalidate()
}
func addPlayerViewControllerObservers() {
guard let playerViewController = playerViewController else { return }
_playerViewControllerReadyForDisplayObserver = playerViewController.observe(\.isReadyForDisplay, options: [.new], changeHandler: _handlers.handleReadyForDisplay)
_playerViewControllerOverlayFrameObserver = playerViewController.contentOverlayView?.observe(\.frame, options: [.new, .old], changeHandler: _handlers.handleViewControllerOverlayViewFrameChange)
}
func removePlayerViewControllerObservers() {
_playerViewControllerReadyForDisplayObserver?.invalidate()
_playerViewControllerOverlayFrameObserver?.invalidate()
}
func addPlayerLayerObserver() {
_playerLayerReadyForDisplayObserver = playerLayer?.observe(\.isReadyForDisplay, options: [.new], changeHandler: _handlers.handleReadyForDisplay)
}
func removePlayerLayerObserver() {
_playerLayerReadyForDisplayObserver?.invalidate()
}
func addPlayerTimeObserver() {
removePlayerTimeObserver()
let progressUpdateIntervalMS:Float64 = _progressUpdateInterval / 1000
// @see endScrubbing in AVPlayerDemoPlaybackViewController.m
// of https://developer.apple.com/library/ios/samplecode/AVPlayerDemo/Introduction/Intro.html
_timeObserver = player?.addPeriodicTimeObserver(
forInterval: CMTimeMakeWithSeconds(progressUpdateIntervalMS, preferredTimescale: Int32(NSEC_PER_SEC)),
queue:nil,
using:_handlers.handleTimeUpdate
)
}
/* Cancels the previously registered time observer. */
func removePlayerTimeObserver() {
if let timeObserver = _timeObserver {
player?.removeTimeObserver(timeObserver)
_timeObserver = nil
}
}
func addTimeObserverIfNotSet() {
if (_timeObserver == nil) {
addPlayerTimeObserver()
}
}
func replaceTimeObserverIfSet(_ newUpdateInterval:Float64? = nil) {
if let newUpdateInterval = newUpdateInterval {
_progressUpdateInterval = newUpdateInterval
}
if (_timeObserver != nil) {
addPlayerTimeObserver()
}
}
func attachPlayerEventListeners() {
NotificationCenter.default.removeObserver(_handlers,
name:NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object:player?.currentItem)
NotificationCenter.default.addObserver(_handlers,
selector:#selector(RCTPlayerObserverHandler.handlePlayerItemDidReachEnd(notification:)),
name:NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object:player?.currentItem)
NotificationCenter.default.removeObserver(_handlers,
name:NSNotification.Name.AVPlayerItemPlaybackStalled,
object:nil)
NotificationCenter.default.addObserver(_handlers,
selector:#selector(RCTPlayerObserverHandler.handlePlaybackStalled(notification:)),
name:NSNotification.Name.AVPlayerItemPlaybackStalled,
object:nil)
NotificationCenter.default.removeObserver(_handlers,
name: NSNotification.Name.AVPlayerItemFailedToPlayToEndTime,
object:nil)
NotificationCenter.default.addObserver(_handlers,
selector:#selector(RCTPlayerObserverHandler.handleDidFailToFinishPlaying(notification:)),
name: NSNotification.Name.AVPlayerItemFailedToPlayToEndTime,
object:nil)
// Unused
// NotificationCenter.default.removeObserver(_handlers,
// name:NSNotification.Name.AVPlayerItemNewAccessLogEntry,
// object:nil)
// NotificationCenter.default.addObserver(_handlers,
// selector: #selector(RCTPlayerObserverHandler.handleAVPlayerAccess(notification:)),
// name:NSNotification.Name.AVPlayerItemNewAccessLogEntry,
// object:nil)
}
func clearPlayer() {
player = nil
playerItem = nil
NotificationCenter.default.removeObserver(_handlers)
}
}