diff --git a/PlayKitReadme.md b/PlayKitReadme.md
index 6dc4340..a65d87e 100644
--- a/PlayKitReadme.md
+++ b/PlayKitReadme.md
@@ -50,9 +50,10 @@ The following setup code should be carried out in the 'viewDidAppear' function:
Create the player:
``` Swift
-amgPlaykit?.createPlayer(analytics: AMGAnalyticsConfig? = nil)
+amgPlaykit?.createPlayer(analytics: AMGAnalyticsConfig? = nil, enableIMA: Bool = true)
```
+'enableIMA' is an optional parameter, enabled by default, that it loads IMA Plugin
(Optional) Add the partnerID - see 'Manually updating the PartnerID'
### Removing the player
@@ -248,6 +249,11 @@ Set the colour of the VOD scrub bar 'tracked' time to a hex formatted RGB colour
.scrubBarVODColour(colour: String)
```
+Toggle the visibility of the bitrate selector
+``` Swift
+.setBitrateSelector(_ isOn: Bool)
+```
+
## Media overlays
AMG Play Kit supports the overlaying of an 'is live' badge and a logo as overlays to any media playing.
@@ -639,12 +645,15 @@ To instruct PlayKit to use a certain highest bitrate when streaming, you can use
amgPlayKit?.setMaximumBitrate(bitrate: Double)
```
-PlayKit will atttempt to change bitrate to that value (or the closest one BELOW that value) for the rest of the stream. This change may not be immediate.
+PlayKit will atttempt to change bitrate to that value (or the closest one BELOW that value) for the rest of the stream.
# Change Log
All notable changes to this project will be documented in this section.
+### 1.1.0
+- Bitrate selector UI
+
### 1.0.4
- Fixed fullscreen button to standard UI
diff --git a/README.md b/README.md
index 944f96a..ab5230a 100755
--- a/README.md
+++ b/README.md
@@ -102,7 +102,7 @@ Change Log:
All notable changes to this project will be documented in this section.
-### 1.0.4 - PlayKit minor UI fixes
+### 1.1.0 - PlayKit bitrate selector UI
### 1.0.3 - PlayKit fixes
diff --git a/Source/Media/PlayKitMedia.xcassets/bitrate_dark_gray.colorset/Contents.json b/Source/Media/PlayKitMedia.xcassets/bitrate_dark_gray.colorset/Contents.json
new file mode 100644
index 0000000..29cefd2
--- /dev/null
+++ b/Source/Media/PlayKitMedia.xcassets/bitrate_dark_gray.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x21",
+ "green" : "0x13",
+ "red" : "0x0D"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x21",
+ "green" : "0x13",
+ "red" : "0x0D"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Source/Media/PlayKitMedia.xcassets/bitrate_medium_gray.colorset/Contents.json b/Source/Media/PlayKitMedia.xcassets/bitrate_medium_gray.colorset/Contents.json
new file mode 100644
index 0000000..d60c740
--- /dev/null
+++ b/Source/Media/PlayKitMedia.xcassets/bitrate_medium_gray.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x2D",
+ "green" : "0x24",
+ "red" : "0x21"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x2D",
+ "green" : "0x24",
+ "red" : "0x21"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Source/Media/PlayKitMedia.xcassets/checkmark.imageset/Contents.json b/Source/Media/PlayKitMedia.xcassets/checkmark.imageset/Contents.json
new file mode 100644
index 0000000..3a8eda3
--- /dev/null
+++ b/Source/Media/PlayKitMedia.xcassets/checkmark.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "checkmark.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/Source/Media/PlayKitMedia.xcassets/checkmark.imageset/checkmark.svg b/Source/Media/PlayKitMedia.xcassets/checkmark.imageset/checkmark.svg
new file mode 100644
index 0000000..414de8d
--- /dev/null
+++ b/Source/Media/PlayKitMedia.xcassets/checkmark.imageset/checkmark.svg
@@ -0,0 +1,3 @@
+
diff --git a/Source/Media/PlayKitMedia.xcassets/settingsButton.imageset/Contents.json b/Source/Media/PlayKitMedia.xcassets/settingsButton.imageset/Contents.json
new file mode 100644
index 0000000..bd0fa52
--- /dev/null
+++ b/Source/Media/PlayKitMedia.xcassets/settingsButton.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "icons8-settings.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/Source/Media/PlayKitMedia.xcassets/settingsButton.imageset/icons8-settings.svg b/Source/Media/PlayKitMedia.xcassets/settingsButton.imageset/icons8-settings.svg
new file mode 100644
index 0000000..4e7f09c
--- /dev/null
+++ b/Source/Media/PlayKitMedia.xcassets/settingsButton.imageset/icons8-settings.svg
@@ -0,0 +1 @@
+
diff --git a/Source/StreamSDKPlayKit/Player/Analytics/AMGAnalyticsPlugin.swift b/Source/StreamSDKPlayKit/Player/Analytics/AMGAnalyticsPlugin.swift
index 62f54bc..3547ac3 100644
--- a/Source/StreamSDKPlayKit/Player/Analytics/AMGAnalyticsPlugin.swift
+++ b/Source/StreamSDKPlayKit/Player/Analytics/AMGAnalyticsPlugin.swift
@@ -19,8 +19,7 @@ public class AMGAnalyticsPlugin: BasePlugin, AnalyticsPluginProtocol {
private static var partnerID = 0
- //private static var baseURL = "https://nudeiys3md.execute-api.eu-west-1.amazonaws.com/api/submit"
- private static var baseURL = "http://stats.mp.streamamg.com/SessionUpdate?"
+ private static var baseURL = "https://stats.mp.streamamg.com/SessionUpdate?"
// MARK: - Private local variables
diff --git a/Source/StreamSDKPlayKit/Player/Analytics/AMGAnalyticsPluginConfig.swift b/Source/StreamSDKPlayKit/Player/Analytics/AMGAnalyticsPluginConfig.swift
index 167df88..2f44322 100644
--- a/Source/StreamSDKPlayKit/Player/Analytics/AMGAnalyticsPluginConfig.swift
+++ b/Source/StreamSDKPlayKit/Player/Analytics/AMGAnalyticsPluginConfig.swift
@@ -28,7 +28,7 @@ import PlayKit
// MARK: - Properties
/************************************************************/
- private let defaultBaseUrl = "http://stats.mp.streamamg.com/SessionUpdate"
+ private let defaultBaseUrl = "https://stats.mp.streamamg.com/SessionUpdate"
/// application ID.
let applicationId = Bundle.main.bundleIdentifier
diff --git a/Source/StreamSDKPlayKit/Player/Controls/AMGPlayKitStandardControl.swift b/Source/StreamSDKPlayKit/Player/Controls/AMGPlayKitStandardControl.swift
index 9b74ab8..fd6bcd7 100644
--- a/Source/StreamSDKPlayKit/Player/Controls/AMGPlayKitStandardControl.swift
+++ b/Source/StreamSDKPlayKit/Player/Controls/AMGPlayKitStandardControl.swift
@@ -22,13 +22,16 @@ class AMGPlayKitStandardControl: UIView, AMGControlDelegate {
let backwardButton = UIButton(type: UIButton.ButtonType.custom)
let fullscreenButton = UIButton(type: UIButton.ButtonType.custom)
let minimiseButton = UIButton(type: UIButton.ButtonType.custom)
+ let settingsButton = UIButton(type: UIButton.ButtonType.custom)
var playImage: UIImage = UIImage()
var skipForawrdImage: UIImage = UIImage()
var skipBackwardImage: UIImage = UIImage()
var pauseImage: UIImage = UIImage()
var fullScreenImage: UIImage = UIImage()
+ var settingsImage: UIImage = UIImage()
var minimiseImage: UIImage = UIImage()
var thumb: UIImage = UIImage()
+ var checkmark: UIImage = UIImage()
var scrubBar: UISlider = UISlider()
var scrubBarBackground: UIView = UIView()
@@ -38,6 +41,9 @@ class AMGPlayKitStandardControl: UIView, AMGControlDelegate {
var spoilerFreeRightView: UIView = UIView()
+ var bitrateView: UIView? = nil
+
+
var liveButton = UIButton()
var mediaLength: TimeInterval = 0
@@ -75,6 +81,12 @@ class AMGPlayKitStandardControl: UIView, AMGControlDelegate {
private var bottomScrubViewTrack: UIView = UIView(frame: CGRect.zero)
private var bottomTrackEnabled = false
+
+ var bitrates: [Int64] = []
+ var selectedBitrate = 0
+ var bitrateColors: [UIColor] = []
+
+ var bitrateScroll: UIScrollView = UIScrollView(frame: .zero) // UIView = UIView()
init (hostView: UIView, delegate: AMGPlayerDelegate, config: AMGPlayKitStandardControlsConfigurationModel? = nil){
super.init(frame: CGRect(x: 0, y: 0, width: hostView.frame.width, height: hostView.frame.height))
@@ -216,10 +228,26 @@ class AMGPlayKitStandardControl: UIView, AMGControlDelegate {
} else if let myImage = UIImage(named: "minimiseButton", in: bundle, compatibleWith: .none){
minimiseImage = myImage
}
-
+// if let customImage = configModel.minimiseImage, let myImage = UIImage(named: customImage) {
+// minimiseImage = myImage
+// } else
+ if let myImage = UIImage(named: "settingsButton", in: bundle, compatibleWith: .none){
+ settingsImage = myImage
+ }
if let myImage = UIImage(named: "slider_thumb", in: bundle, compatibleWith: .none){
thumb = myImage
}
+ if let customImage = UIImage(named: "checkmark", in: bundle, compatibleWith: .none) {
+ checkmark = customImage
+ }
+
+ if let color = UIColor(named: "bitrate_dark_gray", in: bundle, compatibleWith: .none) {
+ bitrateColors.append(color)
+ }
+ if let color = UIColor(named: "bitrate_medium_gray", in: bundle, compatibleWith: .none) {
+ bitrateColors.append(color)
+ }
+
playPause.frame = CGRect(x: x, y: y, width: playPauseSize, height: playPauseSize)
playPause.tintColor = UIColor.white
playPause.contentMode = .scaleToFill
@@ -242,7 +270,7 @@ class AMGPlayKitStandardControl: UIView, AMGControlDelegate {
mainView.addSubview(forwardButton)
// Add scrub bar
- let scrubBarBackY = h-65
+ let scrubBarBackY = h-70
let scrubBarBackX = CGFloat(20)
let scrubBarBackW = w-40
let scrubBarBackH = CGFloat(60)
@@ -326,21 +354,32 @@ class AMGPlayKitStandardControl: UIView, AMGControlDelegate {
if !hideFullScreenButton {
- fullscreenButton.frame = CGRect(x: w - skipSize - 20, y: 20, width: skipSize, height: skipSize)
- fullscreenButton.tintColor = UIColor.white
- fullscreenButton.contentMode = .scaleToFill
- fullscreenButton.setImage(fullScreenImage, for: .normal)
- fullscreenButton.addTarget(self, action: #selector(fullScreenToggle), for: .touchUpInside)
+ fullscreenButton.frame = CGRect(x: w - skipSize - 20, y: 20, width: skipSize, height: skipSize)
+ fullscreenButton.tintColor = UIColor.white
+ fullscreenButton.contentMode = .scaleToFill
+ fullscreenButton.setImage(fullScreenImage, for: .normal)
+ fullscreenButton.addTarget(self, action: #selector(fullScreenToggle), for: .touchUpInside)
mainView.addSubview(fullscreenButton)
}
-
+ if configModel.bitrateSelector {
+ settingsButton.frame = CGRect(x: w - skipSize - 20, y: h - skipSize - 5, width: skipSize, height: skipSize)
+ settingsButton.tintColor = UIColor.white
+ settingsButton.contentMode = .scaleToFill
+ settingsButton.setImage(settingsImage, for: .normal)
+ settingsButton.addTarget(self, action: #selector(openBitrateView), for: .touchUpInside)
+ settingsButton.layer.cornerRadius = 8
+ mainView.addSubview(settingsButton)
+ }
updateIsLive()
updateSpoilerFree()
showControls(false)
+
+ //createBitrateView()
}
+
func updateIsLive(){
var scrubBarx: CGFloat = 10
@@ -447,10 +486,29 @@ class AMGPlayKitStandardControl: UIView, AMGControlDelegate {
player?.fullScreen()
}
}
+
+
+ @objc func closeBitrateView() {
+ bitrateView?.removeFromSuperview()
+ player?.startControlVisibilityTimer()
+ settingsButton.backgroundColor = .clear
+ }
+
+
+ @objc func openBitrateView() {
+ bitrateView = UIView.init(frame: self.bounds)
+ bitrateView?.backgroundColor = .clear // UIColor.init(red: 1, green: 1, blue: 1, alpha: 0.3)
+ let gesture = UITapGestureRecognizer(target: self, action: #selector(closeBitrateView))
+ bitrateView?.addGestureRecognizer(gesture)
+ createBitrateSelector(withBitrateList: bitrates)
+ addSubview(bitrateView!)
+ player?.cancelTimer()
+ settingsButton.backgroundColor = bitrateColors.count > 0 ? bitrateColors.first : UIColor.black
+ }
func play() {
isPlaying = true
- playPause.setImage(pauseImage, for: .normal)
+ playPause.setImage(pauseImage, for: .normal)
}
func pause() {
@@ -460,29 +518,19 @@ class AMGPlayKitStandardControl: UIView, AMGControlDelegate {
func changePlayHead(position: TimeInterval) {
if !updatePlayHeadManually {
- if position <= 0 {
- scrubBar.value = 0
- } else if position >= mediaLength {
- scrubBar.value = Float(mediaLength)
- } else {
- scrubBar.value = Float(position)
- }
- setTrackSize(position: position)
- if trackTimeShowing {
-// if currentTimeShowing {
-// currentTime.text = timeForDisplay(time: position)
-// startTime.text = "00:00"
-// endTime.text = timeForDisplay(time: mediaLength)
-// } else {
- let timeRemaining = mediaLength - position
+ if position <= 0 {
+ scrubBar.value = 0
+ } else if position >= mediaLength {
+ scrubBar.value = Float(mediaLength)
+ } else {
+ scrubBar.value = Float(position)
+ }
+ setTrackSize(position: position)
+ if trackTimeShowing {
startTime.text = "\(timeForDisplay(time: position)) / \(timeForDisplay(time: mediaLength))"
-
-
-// endTime.text = timeForDisplay(time: timeRemaining)
-// }
- } else if currentTimeShowing {
- currentTime.text = timeForDisplay(time: position)
- }
+ } else if currentTimeShowing {
+ currentTime.text = timeForDisplay(time: position)
+ }
}
if (position < mediaLength - 1) {
liveButton.setTitle("GO LIVE", for: .normal)
@@ -555,29 +603,32 @@ class AMGPlayKitStandardControl: UIView, AMGControlDelegate {
forwardButton.frame = CGRect(x: x + playPauseSize + 20, y: skipY, width: skipSize, height: skipSize)
-// let scrubBarBackY = h-65
-// let scrubBarBackX = CGFloat(20)
-// let scrubBarBackW = w-40
-// let scrubBarBackH = CGFloat(40)
-
- let scrubBarBackY = h-65
+ let scrubBarBackY = h-70
let scrubBarBackX = CGFloat(20)
let scrubBarBackW = w-40
let scrubBarBackH = CGFloat(60)
scrubBarBackground.frame = CGRect(x: scrubBarBackX, y: scrubBarBackY, width: scrubBarBackW, height: scrubBarBackH)
- // scrubBarBackground.backgroundColor = UIColor.red
spoilerFreeBackground.frame = CGRect(x: scrubBarBackX, y: scrubBarBackY, width: scrubBarBackW, height: scrubBarBackH)
fullscreenButton.frame = CGRect(x: w - skipSize - 20, y: 20, width: skipSize, height: skipSize)
+
+ settingsButton.frame = CGRect(x: w - skipSize - 20, y: h - skipSize - 5, width: skipSize, height: skipSize)
+
if (isFullScreen) {
fullscreenButton.setImage(minimiseImage, for: .normal)
} else {
fullscreenButton.setImage(fullScreenImage, for: .normal)
}
+
+ if let bView = bitrateView {
+ bView.frame = self.bounds
+ self.bitrateScroll.heightAnchor.constraint(equalToConstant: min(bView.frame.height - 10, self.bitrateScroll.contentSize.height)).isActive = true
+ }
+
updateIsLive()
updateSpoilerFree()
}
@@ -585,6 +636,65 @@ class AMGPlayKitStandardControl: UIView, AMGControlDelegate {
func showControls(_ shouldShow: Bool) {
mainView.isHidden = !shouldShow
bottomScrubView.isHidden = shouldShow
+ closeBitrateView()
+ }
+
+
+
+ func createBitrateSelector(withBitrateList: [Int64]){
+ bitrates = withBitrateList
+ let maxWidth: CGFloat = 165
+ var count = 1
+ DispatchQueue.main.async {
+ self.bitrateScroll.removeFromSuperview()
+ self.bitrateScroll = UIScrollView(frame: .zero)
+ self.bitrateScroll.alwaysBounceVertical = false;
+ self.bitrateScroll.translatesAutoresizingMaskIntoConstraints = false
+ self.bitrateScroll.contentInsetAdjustmentBehavior = .never
+ self.bitrateScroll.contentSize = CGSize(width: maxWidth, height: CGFloat(self.bitrates.count + 1) * 48)
+ self.bitrateScroll.backgroundColor = .clear
+ self.bitrateScroll.layer.cornerRadius = 8
+
+ self.createBitrateLabel(text: "Auto", width: maxWidth, index: 0)
+ withBitrateList.forEach {bitrate in
+ self.createBitrateLabel(text: "\(bitrate)", width: maxWidth, index: count)
+ count += 1
+ }
+ self.bitrateView?.addSubview(self.bitrateScroll)
+
+ if let bView = self.bitrateView {
+ self.bitrateScroll.trailingAnchor.constraint(equalTo: bView.trailingAnchor, constant: -65).isActive = true
+ self.bitrateScroll.heightAnchor.constraint(equalToConstant: min(bView.frame.height - 10, self.bitrateScroll.contentSize.height)).isActive = true
+ self.bitrateScroll.bottomAnchor.constraint(equalTo: bView.bottomAnchor, constant: -5).isActive = true
+ self.bitrateScroll.widthAnchor.constraint(equalToConstant: maxWidth).isActive = true
+ }
+ }
+ }
+
+ func createBitrateLabel(text: String, width: CGFloat, index: Int) {
+ let tText = UIButton(frame: CGRect(x: 0, y: CGFloat(48 * index), width: width, height: 48))
+ tText.titleLabel?.font = UIFont.systemFont(ofSize: 16)
+ tText.setTitle(text, for: .normal)
+ tText.setTitleColor(.white, for: .normal)
+ tText.contentHorizontalAlignment = .left
+ tText.titleEdgeInsets = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 0)
+
+ if index == selectedBitrate {
+ tText.backgroundColor = bitrateColors.count > 0 ? bitrateColors.first : UIColor.black
+ tText.setImage(checkmark.withRenderingMode(.alwaysOriginal), for: .normal)
+ tText.titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
+ tText.imageEdgeInsets = UIEdgeInsets(top: 0, left: width - 30, bottom: 0, right: 0)
+ } else {
+ tText.backgroundColor = bitrateColors.count > 1 ? bitrateColors[1] : UIColor.darkGray
+ }
+
+ tText.tag = index
+ tText.addTarget(self, action: #selector(swapBitRate(button:)), for: .touchUpInside)
+
+ let divider = UIView(frame: CGRect(x: 0, y: 0, width: width, height: 0.5))
+ divider.backgroundColor = bitrateColors.count > 0 ? bitrateColors.first : UIColor.black
+ tText.addSubview(divider)
+ self.bitrateScroll.addSubview(tText)
}
func removeStandardView(){
@@ -592,4 +702,23 @@ class AMGPlayKitStandardControl: UIView, AMGControlDelegate {
playerView = nil
}
+ @objc func swapBitRate(button: UIButton) {
+ let myTag = button.tag
+ if myTag == selectedBitrate || bitrates.count == 0 || tag > bitrates.count {
+ closeBitrateView()
+ return
+ }
+
+ if myTag == 0 {
+ selectedBitrate = 0
+ player?.setMaximumBitrate(bitrate: bitrates.last!)
+ closeBitrateView()
+ return
+ }
+
+ selectedBitrate = myTag
+ player?.setMaximumBitrate(bitrate: bitrates[myTag - 1])
+ closeBitrateView()
+ return
+ }
}
diff --git a/Source/StreamSDKPlayKit/Player/Controls/AMGPlayKitStandardControlsConfigurationModel.swift b/Source/StreamSDKPlayKit/Player/Controls/AMGPlayKitStandardControlsConfigurationModel.swift
index 333a667..b653ae8 100644
--- a/Source/StreamSDKPlayKit/Player/Controls/AMGPlayKitStandardControlsConfigurationModel.swift
+++ b/Source/StreamSDKPlayKit/Player/Controls/AMGPlayKitStandardControlsConfigurationModel.swift
@@ -34,7 +34,7 @@ public struct AMGPlayKitStandardControlsConfigurationModel: Codable {
var liveTrack = "#D0FF39"
var vodTrack = "#71ABF3"
-
+ var bitrateSelector = false
}
/**
@@ -69,6 +69,7 @@ public class AMGControlBuilder {
var liveTrack = "#D0FF39"
var vodTrack = "#71ABF3"
+ var bitrateSelector = false
/**
Specify the image to use for the play button
@@ -270,12 +271,20 @@ public class AMGControlBuilder {
vodTrack = newColour
return self
}
+
+ /**
+ Toggle the visibility of the bitrate selector
+ */
+ public func setBitrateSelector(_ isOn: Bool) -> AMGControlBuilder {
+ bitrateSelector = isOn
+ return self
+ }
/**
Returns a complete and valid AMGPlayKitStandardControlsConfigurationModel
*/
public func build() -> AMGPlayKitStandardControlsConfigurationModel {
- return AMGPlayKitStandardControlsConfigurationModel(fadeInTogglesPausePlay: fadeInTogglesPausePlay, fadeInTime: fadeInTime, fadeOutTime: fadeOutTime, fadeOutAfter: fadeOutAfter, trackTimeShowing: trackTimeShowing, isLiveImage: isLiveImage, logoImage: logoImage, playImage: playImage, pauseImage: pauseImage, skipForwardImage: skipForwardImage, skipBackwardImage: skipBackwardImage, fullScreenImage: fullScreenImage, hideFullscreen: hideFullscreen, hideMinimise: hideMinimise, liveTrack: liveTrack, vodTrack: vodTrack)
+ return AMGPlayKitStandardControlsConfigurationModel(fadeInTogglesPausePlay: fadeInTogglesPausePlay, fadeInTime: fadeInTime, fadeOutTime: fadeOutTime, fadeOutAfter: fadeOutAfter, trackTimeShowing: trackTimeShowing, isLiveImage: isLiveImage, logoImage: logoImage, playImage: playImage, pauseImage: pauseImage, skipForwardImage: skipForwardImage, skipBackwardImage: skipBackwardImage, fullScreenImage: fullScreenImage, hideFullscreen: hideFullscreen, hideMinimise: hideMinimise, liveTrack: liveTrack, vodTrack: vodTrack, bitrateSelector: bitrateSelector)
diff --git a/Source/StreamSDKPlayKit/Player/Media/MediaContext.swift b/Source/StreamSDKPlayKit/Player/Media/MediaContext.swift
new file mode 100644
index 0000000..f415b1b
--- /dev/null
+++ b/Source/StreamSDKPlayKit/Player/Media/MediaContext.swift
@@ -0,0 +1,24 @@
+//
+// MediaContext.swift
+// StreamAMG
+//
+// Created by Mike Hall on 24/11/2021.
+//
+
+import Foundation
+
+public struct MediaContext: Codable {
+ public let flavorAssets: [FlavorAsset]
+
+ public func fetchBitrates() -> [Int64] {
+ return flavorAssets.compactMap {$0.bitrate}.sorted()
+ }
+}
+
+public struct FlavorAsset: Codable {
+ public let width: Int64?
+ public let height: Int64?
+ public let bitrate: Int64?
+ public let id:String?
+ public let entryId:String?
+}
diff --git a/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+Bitrate.swift b/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+Bitrate.swift
new file mode 100644
index 0000000..a122498
--- /dev/null
+++ b/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+Bitrate.swift
@@ -0,0 +1,61 @@
+//
+// AMGPlayKit+Bitrate.swift
+// StreamAMG
+//
+// Created by Mike Hall on 24/11/2021.
+//
+
+
+import Foundation
+
+extension AMGPlayKit {
+
+ func fetchContextData(completion: @escaping ((MediaContext?) -> Void)) {
+ if let med = currentMedia, let server = med.serverURL{
+ guard let validURL = URL(string: "\(server)/api_v3/?service=baseEntry&action=getContextData&entryId=\(med.entryID)&\(validKS(ks: med.ks, trailing: true))contextDataParams:objectType=KalturaEntryContextDataParams&contextDataParams:flavorTags=all&format=1")
+ else {
+ completion(nil)
+ return
+ }
+ let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
+ var urlRequest = URLRequest(url: validURL)
+ urlRequest.httpMethod = "POST"
+ let request = session.dataTask(with: urlRequest) {data, response, error in
+ do {
+ if let data = data {
+ let responseObject = try JSONDecoder().decode(MediaContext.self, from: data)
+ completion(responseObject)
+ } else {
+ completion(nil)
+ }
+ } catch {
+ completion(nil)
+ }
+ }
+ request.resume()
+ }
+ }
+
+ public func setMaximumBitrate(bitrate: Int64){
+ if let media = currentMedia {
+ loadMedia(media: media, mediaType: currentMediaType, startPosition: Int64(player?.currentTime ?? 0), bitrate: Double(bitrate))
+ }
+ }
+
+ func setMaximumBitrate(bitrate: Double){
+ if let media = currentMedia {
+ loadMedia(media: media, mediaType: currentMediaType, startPosition: Int64(player?.currentTime ?? 0), bitrate: bitrate)
+ }
+ }
+
+ func updateBitrateSelector(){
+ fetchContextData {data in
+ if let data = data {
+ self.controlUI?.createBitrateSelector(withBitrateList: data.fetchBitrates())
+ } else {
+ self.controlUI?.createBitrateSelector(withBitrateList: [])
+ }
+ }
+ }
+
+}
diff --git a/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+Orientation.swift b/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+Orientation.swift
index 3f1097c..8329a73 100644
--- a/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+Orientation.swift
+++ b/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+Orientation.swift
@@ -26,6 +26,11 @@ extension AMGPlayKit {
func resizeScreen(){
playerView?.frame = self.bounds
controlUI?.frame = self.bounds
+ if #available(iOS 13.0, *) {
+ self.controlUI?.setFullScreen(UIDevice.current.orientation.isValidInterfaceOrientation ? UIDevice.current.orientation.isLandscape : (UIApplication.shared.windows.first?.windowScene?.interfaceOrientation.isLandscape ?? false))
+ } else {
+ self.controlUI?.setFullScreen(UIDevice.current.orientation.isValidInterfaceOrientation ? UIDevice.current.orientation.isLandscape : UIApplication.shared.statusBarOrientation.isLandscape)
+ }
controlUI?.resize()
}
@@ -34,7 +39,7 @@ extension AMGPlayKit {
return
}
orientationTime = Date().timeIntervalSince1970
- let value = UIInterfaceOrientation.portrait.rawValue
+ let value = UIInterfaceOrientation.portrait.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
UIViewController.attemptRotationToDeviceOrientation()
controlUI?.setFullScreen(false)
@@ -47,7 +52,7 @@ extension AMGPlayKit {
return
}
orientationTime = Date().timeIntervalSince1970
- var value = UIInterfaceOrientation.landscapeRight.rawValue
+ var value = UIInterfaceOrientation.landscapeRight.rawValue
if UIApplication.shared.statusBarOrientation == .landscapeLeft {
value = UIInterfaceOrientation.landscapeLeft.rawValue
}
diff --git a/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+PiP.swift b/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+PiP.swift
index ec84c49..e368006 100644
--- a/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+PiP.swift
+++ b/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+PiP.swift
@@ -29,7 +29,7 @@ extension AMGPlayKit: AVPictureInPictureControllerDelegate {
}
}
- public func setPictureInPictureDelegate(_ delegate: AMGPictureInPictureDelegate) {
+ public func setPictureInPictureDelegate(_ delegate: AMGPictureInPictureDelegate?) {
pipDelegate = delegate
}
@@ -65,9 +65,9 @@ extension AMGPlayKit: AVPictureInPictureControllerDelegate {
}
@objc internal func enterBackground(){
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
+ DispatchQueue.main.async {
if let pip = self.pictureInPictureController, pip.isPictureInPictureActive {
- self.player?.play()
+ self.player?.play()
}
}
}
diff --git a/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+StandardCasting.swift b/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+StandardCasting.swift
index 20aa822..1180c00 100644
--- a/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+StandardCasting.swift
+++ b/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+StandardCasting.swift
@@ -53,13 +53,6 @@ extension AMGPlayKit: URLSessionTaskDelegate {
}
}
- private func validKS(ks: String?)-> String {
- if let ks = ks {
- return "ks/\(ks)/"
- }
- return ""
- }
-
func sendCastingURL(url: String?) {
guard let url = url, let urlToCast = URL(string: url) else {
return
diff --git a/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+UIControls.swift b/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+UIControls.swift
index d560793..22206ab 100644
--- a/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+UIControls.swift
+++ b/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit+UIControls.swift
@@ -159,6 +159,4 @@ extension AMGPlayKit {
}
-
-
}
diff --git a/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit.swift b/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit.swift
index 4fc41da..18cbbe9 100644
--- a/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit.swift
+++ b/Source/StreamSDKPlayKit/Player/PlayKit/AMGPlayKit.swift
@@ -18,7 +18,7 @@ import AVKit
The SDK, at it's most basic, is a UIView, instantiated either programatically, or via Storyboard, that acts as a single point of reference for all Kaltura PlayKit functionality
*/
@objc public class AMGPlayKit: UIView, AMGPlayerDelegate {
-
+
var playerView: PlayerView? = nil
public var player: Player?
var partnerID: Int = 0
@@ -53,12 +53,13 @@ import AVKit
private var playerState: PlayerState? = nil
- internal var pipDelegate: AMGPictureInPictureDelegate?
+ weak internal var pipDelegate: AMGPictureInPictureDelegate?
internal var pictureInPictureController: AVPictureInPictureController?
internal var pipPossibleObservation: NSKeyValueObservation?
var analyticsConfiguration: AMGAnalyticsConfig = AMGAnalyticsConfig()
+ private var ima: Bool = true
private var currentAdvert = ""
@@ -133,11 +134,12 @@ import AVKit
- Parameter analytics: A valid AMGAnalyticsConfig object or 'nil' if no analytics is to be used
*/
- public func createPlayer(analytics: AMGAnalyticsConfig? = nil){
+ public func createPlayer(analytics: AMGAnalyticsConfig? = nil, enableIMA: Bool = true){
tap = UITapGestureRecognizer(target: self, action: #selector(self.bringControlToForeground(_:)))
if let analyticsConfig = analytics {
analyticsConfiguration = analyticsConfig
}
+ ima = enableIMA
setNeedsLayout()
layoutIfNeeded()
constructPlayKit()
@@ -170,7 +172,9 @@ import AVKit
}
func constructPlayKit() {
- PlayKitManager.shared.registerPlugin(IMAPlugin.self)
+ if ima {
+ PlayKitManager.shared.registerPlugin(IMAPlugin.self)
+ }
switch analyticsConfiguration.analyticsService {
case .AMGANALYTICS:
PlayKitManager.shared.registerPlugin(AMGAnalyticsPlugin.self)
@@ -284,10 +288,7 @@ import AVKit
control?.changePlayHead(position: playHead)
}
}
-
- public func setMaximumBitrate(bitrate: Double){
- player?.settings.network.preferredPeakBitRate = bitrate
- }
+
func playEventOccurred() {
control?.play()
@@ -297,6 +298,16 @@ import AVKit
control?.pause()
}
+ internal func validKS(ks: String?, trailing: Bool = false)-> String {
+ if let ks = ks, !ks.isEmpty {
+ if trailing {
+ return "ks=\(ks)&"
+ }
+ return "ks/\(ks)/"
+ }
+ return ""
+ }
+
/**
Adds a Fairplay license provider, if required
@@ -347,7 +358,11 @@ import AVKit
default:
break
}
- config[IMAPlugin.pluginName] = getIMAPluginConfig()
+
+ if ima {
+ config[IMAPlugin.pluginName] = getIMAPluginConfig()
+ }
+
return PluginConfig(config: config)
}
@@ -361,9 +376,11 @@ import AVKit
break
}
- var imaconfig: [String: Any] = [:]
- imaconfig[IMAPlugin.pluginName] = getIMAPluginConfig()
- player?.updatePluginConfig(pluginName: IMAPlugin.pluginName, config: PluginConfig(config: imaconfig))
+ if ima {
+ var imaconfig: [String: Any] = [:]
+ imaconfig[IMAPlugin.pluginName] = getIMAPluginConfig()
+ player?.updatePluginConfig(pluginName: IMAPlugin.pluginName, config: PluginConfig(config: imaconfig))
+ }
return
}
@@ -382,6 +399,7 @@ import AVKit
if let player = player {
updatePluginConfig()
player.prepare(media.media())
+ updateBitrateSelector()
if mediaType == .Live{
self.currentMediaType = .Live
self.controlUI?.setIsLive()
@@ -404,6 +422,41 @@ import AVKit
}
}
+ internal func loadMedia(media: MediaItem, mediaType: AMGMediaType, startPosition: Int64, bitrate: Double){
+ currentMedia = media
+ currentMediaType = mediaType
+ player?.pause()
+ if partnerID > 0{
+ if let player = player {
+ player.settings.network.preferredPeakBitRate = bitrate * 1024
+ updatePluginConfig()
+ let config = media.media()
+ if startPosition > 0 {
+ config.startTime = TimeInterval(startPosition)
+ }
+ player.prepare(config)
+ updateBitrateSelector()
+ if mediaType == .Live{
+ self.currentMediaType = .Live
+ self.controlUI?.setIsLive()
+ } else {
+ isLive(){response in
+ DispatchQueue.main.async {
+ if response {
+ self.currentMediaType = .Live
+ self.controlUI?.setIsLive()
+ } else {
+ self.currentMediaType = .VOD
+ self.controlUI?.setIsVOD()
+ }
+ }
+ }
+ }
+ player.play()
+ }
+ }
+ }
+
/**
Queues and runs the specified media item if available
diff --git a/Source/StreamSDKPlayKit/Player/PlayKit/PlayKitProtocols.swift b/Source/StreamSDKPlayKit/Player/PlayKit/PlayKitProtocols.swift
index 4034558..73e8683 100644
--- a/Source/StreamSDKPlayKit/Player/PlayKit/PlayKitProtocols.swift
+++ b/Source/StreamSDKPlayKit/Player/PlayKit/PlayKitProtocols.swift
@@ -20,6 +20,7 @@ public protocol AMGPlayerDelegate: AnyObject {
func minimise()
func fullScreen()
+ func setMaximumBitrate(bitrate: Int64)
}
diff --git a/StreamAMGSDK.podspec b/StreamAMGSDK.podspec
index ff4ddcd..e3e0ee9 100644
--- a/StreamAMGSDK.podspec
+++ b/StreamAMGSDK.podspec
@@ -2,7 +2,7 @@
Pod::Spec.new do |spec|
spec.name = "StreamAMGSDK"
- spec.version = "1.0.4"
+ spec.version = "1.1.0"
spec.summary = "Stream AMG SDK"
spec.swift_versions = "5"
@@ -56,17 +56,17 @@ end
spec.subspec 'PlayKit' do |playkit|
playkit.source_files = "Source/StreamSDKPlayKit/**/*.*"
-playkit.dependency 'PlayKit', '3.20.0'
-playkit.dependency 'PlayKit_IMA', '1.10.0'
-playkit.dependency 'PlayKitProviders', '1.11.0'
-playkit.dependency 'PlayKitYoubora', '1.9.0'
+playkit.dependency 'PlayKit', '3.25.0'
+playkit.dependency 'PlayKit_IMA', '1.11.0'
+playkit.dependency 'PlayKitProviders', '1.16.0'
+playkit.dependency 'PlayKitYoubora', '1.12.0'
playkit.resource_bundles = { 'AMGPlayKitBundle' => 'Source/Media/*.*'}
end
spec.subspec 'PlayKit2Go' do |playkit2go|
playkit2go.source_files = "Source/StreamSDKPlayKit2Go/**/*.*"
playkit2go.dependency "StreamAMGSDK/PlayKit"
-playkit2go.dependency 'DownloadToGo', '3.15.0'
+playkit2go.dependency 'DownloadToGo', '3.17.0'
end
end