Skip to content

Commit

Permalink
Merge pull request #10 from apivideo/feature/video_options
Browse files Browse the repository at this point in the history
Feature/video options
  • Loading branch information
ThibaultBee authored Oct 27, 2022
2 parents c9127e3 + b04f0ae commit 6e11d70
Show file tree
Hide file tree
Showing 11 changed files with 198 additions and 61 deletions.
84 changes: 57 additions & 27 deletions Sources/ApiVideoPlayer/ApiVideoPlayerController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,27 @@ public class ApiVideoPlayerController: NSObject {
private let avPlayer = AVPlayer(playerItem: nil)
private let offSubtitleLanguage = SubtitleLanguage(language: "Off", code: nil)
private var analytics: PlayerAnalytics?
private let videoType: VideoType
private let videoId: String
private var playerManifest: PlayerManifest!
private var timeObserver: Any?
private var isFirstPlay = true
private var isSeeking = false
private let taskExecutor: TasksExecutorProtocol.Type
#if !os(macOS)
public convenience init(
videoId: String,
videoType: VideoType,
videoOptions: VideoOptions?,
playerLayer: AVPlayerLayer,
events: PlayerEvents? = nil
) {
self.init(videoId: videoId, videoType: videoType, events: events)
self.init(videoOptions: videoOptions, events: events)
playerLayer.player = self.avPlayer
}
#endif

public init(
videoId: String,
videoType: VideoType,
videoOptions: VideoOptions?,
events: PlayerEvents?,
taskExecutor: TasksExecutorProtocol.Type = TasksExecutor.self
) {
self.videoId = videoId
self.videoType = videoType
self.taskExecutor = taskExecutor
super.init()
self.avPlayer.addObserver(
Expand All @@ -43,34 +37,39 @@ public class ApiVideoPlayerController: NSObject {
options: [NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.old],
context: nil
)
self.avPlayer.addObserver(
self,
forKeyPath: "currentItem.presentationSize",
options: NSKeyValueObservingOptions.new,
context: nil
)
if let events = events {
self.addEvents(events: events)
}

self.getPlayerJSON(videoType: videoType) { error in
if let error = error {
self.notifyError(error: error)
}
defer {
self.videoOptions = videoOptions
}
}

private func getVideoUrl(videoType: VideoType, videoId: String, privateToken: String? = nil) -> String {
private func getVideoUrl(videoOptions: VideoOptions) -> String {
let privateToken: String? = nil
var baseUrl = ""
if videoType == .vod {
if videoOptions.videoType == .vod {
baseUrl = "https://cdn.api.video/vod/"
} else {
baseUrl = "https://live.api.video/"
}
var url: String!

if let privateToken = privateToken {
url = baseUrl + "\(videoId)/token/\(privateToken)/player.json"
} else { url = baseUrl + "\(videoId)/player.json" }
url = baseUrl + "\(videoOptions.videoId)/token/\(privateToken)/player.json"
} else { url = baseUrl + "\(videoOptions.videoId)/player.json" }
return url
}

private func getPlayerJSON(videoType: VideoType, completion: @escaping (Error?) -> Void) {
let url = self.getVideoUrl(videoType: videoType, videoId: self.videoId)
private func getPlayerJSON(videoOptions: VideoOptions, completion: @escaping (Error?) -> Void) {
let url = self.getVideoUrl(videoOptions: videoOptions)
guard let path = URL(string: url) else {
completion(PlayerError.urlError("Couldn't set up url from this videoId"))
return
Expand Down Expand Up @@ -182,8 +181,8 @@ public class ApiVideoPlayerController: NSObject {
} catch { print("error with the url") }
}

public func isPlaying() -> Bool {
return self.avPlayer.isPlaying()
public var isPlaying: Bool {
return self.avPlayer.isPlaying
}

public func play() {
Expand Down Expand Up @@ -241,6 +240,19 @@ public class ApiVideoPlayerController: NSObject {
}
}

public var videoOptions: VideoOptions? {
didSet {
guard let videoOptions = videoOptions else {
return
}
self.getPlayerJSON(videoOptions: videoOptions) { error in
if let error = error {
self.notifyError(error: error)
}
}
}
}

public var isMuted: Bool {
get {
self.avPlayer.isMuted
Expand Down Expand Up @@ -285,11 +297,15 @@ public class ApiVideoPlayerController: NSObject {
self.duration.roundedSeconds == self.currentTime.roundedSeconds
}

var hasSubtitles: Bool {
public var videoSize: CGSize {
self.avPlayer.videoSize
}

public var hasSubtitles: Bool {
self.subtitles.count > 1
}

var subtitles: [SubtitleLanguage] {
public var subtitles: [SubtitleLanguage] {
var subtitles: [SubtitleLanguage] = [offSubtitleLanguage]
if let playerItem = avPlayer.currentItem,
let group = playerItem.asset.mediaSelectionGroup(forMediaCharacteristic: .legible)
Expand All @@ -301,7 +317,7 @@ public class ApiVideoPlayerController: NSObject {
return subtitles
}

var currentSubtitle: SubtitleLanguage {
public var currentSubtitle: SubtitleLanguage {
get {
if let playerItem = avPlayer.currentItem,
let group = playerItem.asset.mediaSelectionGroup(forMediaCharacteristic: .legible),
Expand Down Expand Up @@ -338,7 +354,7 @@ public class ApiVideoPlayerController: NSObject {
}
#endif

func hideSubtitle() {
public func hideSubtitle() {
guard let currentItem = self.avPlayer.currentItem else { return }
if let group = currentItem.asset.mediaSelectionGroup(forMediaCharacteristic: .legible) {
currentItem.select(nil, in: group)
Expand Down Expand Up @@ -462,20 +478,34 @@ public class ApiVideoPlayerController: NSObject {
self.doTimeControlStatus()
}
}
if keyPath == "currentItem.presentationSize" {
guard let change = change else { return }
guard let newSize = change[.newKey] as? CGSize else { return }
for events in self.events {
events.didVideoSizeChanged?(newSize)
}
}
}

deinit {
avPlayer.removeObserver(self, forKeyPath: "currentItem.presentationSize", context: nil)
avPlayer.removeObserver(self, forKeyPath: "timeControlStatus", context: nil)
avPlayer.currentItem?.removeObserver(self, forKeyPath: "status", context: nil)
NotificationCenter.default.removeObserver(self)
}
}

extension AVPlayer {
@available(iOS 10.0, *)
func isPlaying() -> Bool {
@available(iOS 10.0, *) var isPlaying: Bool {
return (rate != 0 && error == nil)
}

var videoSize: CGSize {
guard let size = self.currentItem?.presentationSize else {
return CGSize(width: 0, height: 0)
}
return size
}
}

enum PlayerError: Error {
Expand Down
5 changes: 4 additions & 1 deletion Sources/ApiVideoPlayer/PlayerEvents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class PlayerEvents {
public var didSeek: ((_ from: CMTime, _ to: CMTime) -> Void)?
public var didEnd: (() -> Void)?
public var didError: ((_ error: Error) -> Void)?
public var didVideoSizeChanged: ((_ size: CGSize) -> Void)?

public init(
didPrepare: (() -> Void)? = nil,
Expand All @@ -24,7 +25,8 @@ public class PlayerEvents {
didSetVolume: ((Float) -> Void)? = nil,
didSeek: ((CMTime, CMTime) -> Void)? = nil,
didEnd: (() -> Void)? = nil,
didError: ((Error) -> Void)? = nil
didError: ((Error) -> Void)? = nil,
didVideoSizeChanged: ((CGSize) -> Void)? = nil
) {
self.didPrepare = didPrepare
self.didPause = didPause
Expand All @@ -37,5 +39,6 @@ public class PlayerEvents {
self.didSeek = didSeek
self.didEnd = didEnd
self.didError = didError
self.didVideoSizeChanged = didVideoSizeChanged
}
}
2 changes: 1 addition & 1 deletion Sources/ApiVideoPlayer/SubtitleLanguage.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

struct SubtitleLanguage {
public struct SubtitleLanguage {
public var language: String
public var code: String?

Expand Down
17 changes: 17 additions & 0 deletions Sources/ApiVideoPlayer/VideoOptions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Foundation

public struct VideoOptions {
public var videoId: String
public var videoType: VideoType

/* only .vod is supported */
public init(videoId: String, videoType: VideoType = VideoType.vod) {
self.videoId = videoId
self.videoType = videoType
}
}

public enum VideoType: String {
case vod
case live
}
5 changes: 0 additions & 5 deletions Sources/ApiVideoPlayer/VideoType.swift

This file was deleted.

37 changes: 32 additions & 5 deletions Sources/ApiVideoPlayer/View/ApiVideoPlayerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,36 @@ public class ApiVideoPlayerView: UIView {
/// - videoId: Need videoid to display the video.
/// - videoType: VideoType object to display vod or live controls. Only vod is supported yet.
/// - events: Callback to get all the player events.
public init(
public convenience init(
frame: CGRect,
videoId: String,
videoType: VideoType,
hideControls: Bool = false,
events: PlayerEvents? = nil
) {
self.init(
frame: frame,
videoOptions: VideoOptions(videoId: videoId, videoType: videoType),
hideControls: hideControls,
events: events
)
}

/// Init method for PlayerView.
/// - Parameters:
/// - frame: frame of theplayer view.
/// - videoOption: The video option containing the videoId and the videoType
/// - events: Callback to get all the player events.
public init(
frame: CGRect,
videoOptions: VideoOptions,
hideControls: Bool = false,
events: PlayerEvents? = nil
) {
self.userEvents = events
self.isHidenControls = hideControls
self.playerController = ApiVideoPlayerController(
videoId: videoId,
videoType: videoType,
videoOptions: videoOptions,
playerLayer: self.playerLayer,
events: events
)
Expand Down Expand Up @@ -81,10 +99,19 @@ public class ApiVideoPlayerView: UIView {
self.playerLayer.frame = bounds
}

public var videoOptions: VideoOptions? {
get {
self.playerController.videoOptions
}
set {
self.playerController.videoOptions = newValue
}
}

/// Get information if the video is playing.
/// - Returns: Boolean.
public func isPlaying() -> Bool {
return self.playerController.isPlaying()
public var isPlaying: Bool {
return self.playerController.isPlaying
}

/// Play the video.
Expand Down
4 changes: 2 additions & 2 deletions Sources/ApiVideoPlayer/View/VodControlsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ class VodControlsView: UIView, UIGestureRecognizerDelegate {
@objc
func playPauseAction() {
self.resetTimer()
if !self.playerController.isPlaying() {
if !self.playerController.isPlaying {
// Detects end of playing
if self.playerController.isAtEnd {
self.playerController.replay()
Expand Down Expand Up @@ -385,7 +385,7 @@ class VodControlsView: UIView, UIGestureRecognizerDelegate {
switch touchEvent.phase {
case .began:
// handle drag began
if self.playerController.isPlaying() {
if self.playerController.isPlaying {
// Avoid to trigger callbacks and analytics when the user uses the seek slider
self.playerController.pauseBeforeSeek()
self.sliderDidPauseVideo = true
Expand Down
10 changes: 7 additions & 3 deletions Sources/ApiVideoPlayer/ViewController/ApiVideoPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ public struct ApiVideoPlayer: UIViewControllerRepresentable {
private let playerViewController: SwiftUIPlayerViewController

public init(videoId: String, videoType: VideoType, events: PlayerEvents? = nil) {
self.playerViewController = SwiftUIPlayerViewController(videoId: videoId, videoType: videoType, events: events)
self.init(videoOptions: VideoOptions(videoId: videoId, videoType: videoType), events: events)
}

public init(videoOptions: VideoOptions, events: PlayerEvents? = nil) {
self.playerViewController = SwiftUIPlayerViewController(videoOptions: videoOptions, events: events)
}

public func makeUIViewController(context _: Context) -> SwiftUIPlayerViewController {
Expand All @@ -23,8 +27,8 @@ public struct ApiVideoPlayer: UIViewControllerRepresentable {
self.playerViewController.pause()
}

public func isPlaying() -> Bool {
return self.playerViewController.isPlaying()
public var isPlaying: Bool {
return self.playerViewController.isPlaying
}

public func replay() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ public class SwiftUIPlayerViewController: UIViewController {
fatalError("init(coder:) is not supported")
}

init(videoId: String, videoType: VideoType, events: PlayerEvents? = nil) {
init(videoOptions: VideoOptions, events: PlayerEvents? = nil) {
self.playerView = ApiVideoPlayerView(
frame: .zero,
videoId: videoId,
videoType: videoType /* only .vod is supported */,
videoOptions: videoOptions,
events: events
)
super.init(nibName: nil, bundle: nil)
Expand Down Expand Up @@ -50,8 +49,8 @@ public class SwiftUIPlayerViewController: UIViewController {
self.playerView.pause()
}

public func isPlaying() -> Bool {
return self.playerView.isPlaying()
public var isPlaying: Bool {
return self.playerView.isPlaying
}

public func replay() {
Expand Down
Loading

0 comments on commit 6e11d70

Please sign in to comment.