Skip to content

Commit

Permalink
handle conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
Eliza Sapir committed Jun 28, 2017
2 parents e15aad5 + 1a6a4a3 commit 8003d2a
Show file tree
Hide file tree
Showing 19 changed files with 350 additions and 277 deletions.
14 changes: 5 additions & 9 deletions Classes/Events/PlayerEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ import AVFoundation
/// Sent when source was selected.
@objc public static let sourceSelected: PlayerEvent.Type = SourceSelected.self

/// Sent when an error occurs.
/// Sent when an error occurs in the player that the playback can recover from.
@objc public static let error: PlayerEvent.Type = Error.self
/// Sent when an plugin error occurs.
/// Sent when a plugin error occurs.
@objc public static let pluginError: PlayerEvent.Type = PluginError.self
/// Sent when an error log event received from player.
/// Sent when an error log event received from player (non fatal errors).
@objc public static let errorLog: PlayerEvent.Type = ErrorLog.self

// MARK: - Player Basic Events
Expand All @@ -77,12 +77,8 @@ import AVFoundation
class Seeked: PlayerEvent {}

class SourceSelected: PlayerEvent {
convenience init(contentURL: URL?) {
guard let url = contentURL else {
self.init()
return
}
self.init([EventDataKeys.contentURL: url])
convenience init(mediaSource: MediaSource) {
self.init([EventDataKeys.mediaSource: mediaSource])
}
}

Expand Down
22 changes: 6 additions & 16 deletions Classes/PKError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ enum PlayerError: PKError {
case failedToLoadAssetFromKeys(rootError: NSError?)
case assetNotPlayable
case playerItemFailed(rootError: NSError)
case failed(rootError: NSError)

static let domain = "com.kaltura.playkit.error.player"

Expand All @@ -27,6 +28,7 @@ enum PlayerError: PKError {
case .failedToLoadAssetFromKeys: return PKErrorCode.failedToLoadAssetFromKeys
case .assetNotPlayable: return PKErrorCode.assetNotPlayable
case .playerItemFailed: return PKErrorCode.playerItemFailed
case .failed: return PKErrorCode.playerFailed
}
}

Expand All @@ -35,6 +37,7 @@ enum PlayerError: PKError {
case .failedToLoadAssetFromKeys: return "Can't use this AVAsset because one of it's keys failed to load"
case .assetNotPlayable: return "Can't use this AVAsset because it isn't playable"
case .playerItemFailed: return "Player item failed to play"
case .failed: return "Player failed, you can no longer use the player for playback and need to recreate it"
}
}

Expand All @@ -47,6 +50,7 @@ enum PlayerError: PKError {
return [:]
case .assetNotPlayable: return [:]
case .playerItemFailed(let rootError): return [PKErrorKeys.RootErrorKey: rootError]
case .failed(let rootError): return [PKErrorKeys.RootErrorKey: rootError]
}
}
}
Expand Down Expand Up @@ -96,7 +100,7 @@ public enum PKPluginError: PKError {
public var errorDescription: String {
switch self {
case .failedToCreatePlugin(let pluginName): return "failed to create plugin (\(pluginName)), doesn't exist in registry"
case .missingPluginConfig(let pluginName): return "Missing plugin config for plugin: \(pluginName)"
case .missingPluginConfig(let pluginName): return "Missing plugin config for plugin: \(pluginName) (wrong type or doesn't exist)"
}
}

Expand Down Expand Up @@ -193,21 +197,6 @@ public extension PKError where Self: RawRepresentable, Self.RawValue == String {
}
}

/************************************************************/
// MARK: - Error
/************************************************************/
// extension for easier access to domain and code properties.
extension Error {

public var domain: String {
return self._domain
}

public var code: Int {
return self._code
}
}

/************************************************************/
// MARK: - PKError UserInfo Keys
/************************************************************/
Expand Down Expand Up @@ -237,6 +226,7 @@ public struct PKErrorKeys {
@objc(FailedToLoadAssetFromKeys) public static let failedToLoadAssetFromKeys = 7000
@objc(AssetNotPlayable) public static let assetNotPlayable = 7001
@objc(PlayerItemFailed) public static let playerItemFailed = 7002
@objc(PlayerFailed) public static let playerFailed = 7003
// PlayerErrorLog
@objc(PlayerItemErrorLogEvent) public static let playerItemErrorLogEvent = 7100
// PKPluginError
Expand Down
43 changes: 34 additions & 9 deletions Classes/Player/AVPlayerEngine/AVPlayerEngine+Observation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ extension AVPlayerEngine {
private var observedKeyPaths: [String] {
return [
#keyPath(rate),
#keyPath(status),
#keyPath(currentItem.status),
#keyPath(currentItem),
#keyPath(currentItem.playbackLikelyToKeepUp),
Expand All @@ -34,8 +35,8 @@ extension AVPlayerEngine {
addObserver(self, forKeyPath: keyPath, options: [.new, .initial], context: &observerContext)
}

NotificationCenter.default.addObserver(self, selector: #selector(self.playerFailed(notification:)), name: .AVPlayerItemFailedToPlayToEndTime, object: self.currentItem)
NotificationCenter.default.addObserver(self, selector: #selector(self.playerPlayedToEnd(notification:)), name: .AVPlayerItemDidPlayToEndTime, object: self.currentItem) // TODO: check if fired same as playerItem.status == failed if yes then remove this notificaiton observation.
NotificationCenter.default.addObserver(self, selector: #selector(self.didFailToPlayToEndTime(_:)), name: .AVPlayerItemFailedToPlayToEndTime, object: self.currentItem)
NotificationCenter.default.addObserver(self, selector: #selector(self.didPlayToEndTime(_:)), name: .AVPlayerItemDidPlayToEndTime, object: self.currentItem)
NotificationCenter.default.addObserver(self, selector: #selector(self.onAccessLogEntryNotification), name: .AVPlayerItemNewAccessLogEntry, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.onErrorLogEntryNotification), name: .AVPlayerItemNewErrorLogEntry, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.timebaseChanged), name: Notification.Name(kCMTimebaseNotification_EffectiveRateChanged as String), object: self.currentItem?.timebase)
Expand Down Expand Up @@ -76,7 +77,7 @@ extension AVPlayerEngine {
self.post(event: PlayerEvent.ErrorLog(error: PlayerErrorLog(errorLogEvent: lastEvent)))
}

public func playerFailed(notification: NSNotification) {
func didFailToPlayToEndTime(_ notification: NSNotification) {
let newState = PlayerState.error
self.postStateChange(newState: newState, oldState: self.currentState)
self.currentState = newState
Expand All @@ -88,7 +89,7 @@ extension AVPlayerEngine {
}
}

public func playerPlayedToEnd(notification: NSNotification) {
func didPlayToEndTime(_ notification: NSNotification) {
let newState = PlayerState.idle
self.postStateChange(newState: newState, oldState: self.currentState)
self.currentState = newState
Expand All @@ -100,7 +101,6 @@ extension AVPlayerEngine {
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

PKLog.debug("observeValue:: onEvent/onState")

guard context == &observerContext else {
Expand All @@ -118,7 +118,18 @@ extension AVPlayerEngine {
case #keyPath(currentItem.playbackLikelyToKeepUp): self.handleLikelyToKeepUp()
case #keyPath(currentItem.playbackBufferEmpty): self.handleBufferEmptyChange()
case #keyPath(rate): self.handleRate()
case #keyPath(currentItem.status): self.handleStatusChange()
case #keyPath(status):
guard let statusChange = change?[.newKey] as? NSNumber, let newPlayerStatus = AVPlayerStatus(rawValue: statusChange.intValue) else {
PKLog.error("unknown player status")
return
}
self.handle(status: newPlayerStatus)
case #keyPath(currentItem.status):
guard let statusChange = change?[.newKey] as? NSNumber, let newPlayerItemStatus = AVPlayerItemStatus(rawValue: statusChange.intValue) else {
PKLog.error("unknown player item status")
return
}
self.handle(playerItemStatus: newPlayerItemStatus)
case #keyPath(currentItem): self.handleItemChange()
case #keyPath(currentItem.timedMetadata): self.handleTimedMedia()
default: super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
Expand Down Expand Up @@ -164,8 +175,21 @@ extension AVPlayerEngine {
PKLog.debug("player rate was changed, now: \(self.rate)")
}

private func handleStatusChange() {
if currentItem?.status == .readyToPlay {
private func handle(status: AVPlayerStatus) {
switch status {
case .readyToPlay: PKLog.debug("player is ready to play player items")
case .failed:
PKLog.error("player failed you must recreate the player instance")
if let error = (self.error as NSError?) {
self.post(event: PlayerEvent.Error(error: PlayerError.failed(rootError: error)))
}
case .unknown: break
}
}

private func handle(playerItemStatus status: AVPlayerItemStatus) {
switch status {
case .readyToPlay:
let newState = PlayerState.ready

if self.startPosition > 0 {
Expand All @@ -190,13 +214,14 @@ extension AVPlayerEngine {
self.post(event: PlayerEvent.LoadedMetadata())
self.post(event: PlayerEvent.CanPlay())
}
} else if currentItem?.status == .failed {
case .failed:
let newState = PlayerState.error
self.postStateChange(newState: newState, oldState: self.currentState)
self.currentState = newState
if let error = currentItem?.error as NSError? {
self.post(event: PlayerEvent.Error(error: PlayerError.playerItemFailed(rootError: error)))
}
case .unknown: break
}
}

Expand Down
33 changes: 13 additions & 20 deletions Classes/Player/AVPlayerEngine/AVPlayerEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ class AVPlayerEngine: AVPlayer {

fileprivate var playerLayer: AVPlayerLayer!
private var _view: PlayerView!
private var isDestroyed: Bool = false

/// Keeps reference on the last timebase rate in order to post events accuratly.
var lastTimebaseRate: Float64 = 0
Expand All @@ -39,21 +38,21 @@ class AVPlayerEngine: AVPlayer {
var tracksManager = TracksManager()
var observerContext = 0

public var onEventBlock: ((PKEvent) -> Void)?
var onEventBlock: ((PKEvent) -> Void)?

public var view: PlayerView! {
var view: PlayerView! {
PKLog.trace("get player view: \(_view)")
return _view
}

public var asset: AVURLAsset? {
var asset: AVURLAsset? {
didSet {
guard let newAsset = asset else { return }
self.asynchronouslyLoadURLAsset(newAsset)
}
}

public var currentPosition: Double {
var currentPosition: Double {
get {
PKLog.trace("get currentPosition: \(self.currentTime())")
return CMTimeGetSeconds(self.currentTime() - rangeStart)
Expand All @@ -73,13 +72,13 @@ class AVPlayerEngine: AVPlayer {
}
}

public var startPosition: Double {
var startPosition: Double {
didSet {
PKLog.debug("set startPosition: \(startPosition)")
}
}

public var duration: Double {
var duration: Double {
guard let currentItem = self.currentItem else { return 0.0 }

var result = CMTimeGetSeconds(currentItem.duration)
Expand All @@ -96,7 +95,7 @@ class AVPlayerEngine: AVPlayer {
return result
}

public var isPlaying: Bool {
var isPlaying: Bool {
guard let currentItem = self.currentItem else {
PKLog.error("current item is empty")
return false
Expand All @@ -114,14 +113,14 @@ class AVPlayerEngine: AVPlayer {
return false
}

public var currentAudioTrack: String? {
var currentAudioTrack: String? {
if let currentItem = self.currentItem {
return self.tracksManager.currentAudioTrack(item: currentItem)
}
return nil
}

public var currentTextTrack: String? {
var currentTextTrack: String? {
if let currentItem = self.currentItem {
return self.tracksManager.currentTextTrack(item: currentItem)
}
Expand All @@ -143,7 +142,7 @@ class AVPlayerEngine: AVPlayer {

// MARK: Player Methods

public override init() {
override init() {
PKLog.info("init AVPlayer")

self.startPosition = 0
Expand All @@ -164,10 +163,8 @@ class AVPlayerEngine: AVPlayer {

deinit {
PKLog.debug("\(String(describing: type(of: self))), was deinitialized")
// Avoid dealloc while key value observers were still registered
if (!self.isDestroyed) {
self.removeObservers()
}
// removes the observers only on deinit to prevent chances of being removed twice.
self.removeObservers()
}

func stop() {
Expand Down Expand Up @@ -205,20 +202,16 @@ class AVPlayerEngine: AVPlayer {
// this make sure everything will be cleared without any race conditions
DispatchQueue.main.async {
PKLog.info("destroy player")
self.removeObservers()
self.playerLayer = nil
self._view = nil
self.onEventBlock = nil
// removes app state observer
AppStateSubject.shared.remove(observer: self)
self.replaceCurrentItem(with: nil)
self.isDestroyed = true
}
}



public func selectTrack(trackId: String) {
func selectTrack(trackId: String) {
if trackId.isEmpty == false {
self.tracksManager.selectTrack(item: self.currentItem!, trackId: trackId)
} else {
Expand Down
6 changes: 3 additions & 3 deletions Classes/Player/PKEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public extension PKEvent {
static let newState = "newState"
static let error = "error"
static let metadata = "metadata"
static let contentURL = "contentURL"
static let mediaSource = "mediaSource"
}

// MARK: Player Data Accessors
Expand Down Expand Up @@ -93,7 +93,7 @@ public extension PKEvent {
}

/// Content url, PKEvent Data Accessor
@objc public var contentURL: URL? {
return self.data?[EventDataKeys.contentURL] as? URL
@objc public var mediaSource: MediaSource? {
return self.data?[EventDataKeys.mediaSource] as? MediaSource
}
}
2 changes: 1 addition & 1 deletion Classes/Player/PlayerController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class PlayerController: NSObject, Player, PlayerSettings {

// get the preferred media source and post source selected event
guard let (preferredMediaSource, handlerType) = AssetBuilder.getPreferredMediaSource(from: mediaConfig.mediaEntry) else { return }
self.onEventBlock?(PlayerEvent.SourceSelected(contentURL: preferredMediaSource.playbackUrl))
self.onEventBlock?(PlayerEvent.SourceSelected(mediaSource: preferredMediaSource))
self.preferredMediaSource = preferredMediaSource

// update the media source request adapter with new media uuid if using kaltura request adapter
Expand Down
4 changes: 2 additions & 2 deletions PlayKit.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = 'PlayKit'
s.version = '0.4.0.rc2'
s.version = '0.4.0.rc3'
s.summary = 'PlayKit: Kaltura Mobile Player SDK - iOS'
s.homepage = 'https://github.com/kaltura/playkit-ios'
s.license = { :type => 'AGPLv3', :text => 'AGPLv3' }
Expand All @@ -15,7 +15,7 @@ s.subspec 'Core' do |sp|
sp.source_files = 'Classes/**/*'
sp.dependency 'SwiftyJSON', '3.1.4'
sp.dependency 'Log', '1.0'
sp.dependency 'SwiftyXMLParser', '3.0.0'
sp.dependency 'SwiftyXMLParser', '3.0.3'
sp.dependency 'KalturaNetKit', '~> 0.0'
end

Expand Down
1 change: 0 additions & 1 deletion Plugins/AnalyticsCommon/AnalyticsPluginProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import Foundation

protocol AnalyticsPluginProtocol: PKPlugin {

var config: AnalyticsConfig? { get set }
var isFirstPlay: Bool { get set }
var playerEventsToRegister: [PlayerEvent.Type] { get }

Expand Down
Loading

0 comments on commit 8003d2a

Please sign in to comment.