Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 710deacf91 | |||
| 87cdb24a24 | |||
| f400ad9883 | |||
| a53096df50 | |||
| 4720991d0f | |||
| ca661f1996 | |||
| 5ba8f4db04 | |||
| 34c5cac889 | |||
| 3729921bed | |||
| ce3a42f41a | |||
| 92e00745d7 | |||
| c7ef4c2ebc | |||
| 75b2a10881 | |||
| e60bd3bd3b | |||
| a95c54eb56 | |||
| af54544cbe | |||
| 54003387d5 | |||
| f441a7a03e | |||
| ad04383a32 | |||
| 46d34ae58f | |||
| 6dce0ee5b4 | |||
| a9a9627ce9 | |||
| 70eb3f5039 | |||
| b3ec050d8f | |||
| bea45df706 | |||
| 03482bccda | |||
| 01f34b57e5 | |||
| a83cf1fb3a | |||
| 1b09629f9d | |||
| 97f17e3362 | |||
| 72aec6efa8 | |||
| 708c945840 | |||
| a60a1da448 | |||
| 90530c4961 | |||
| 7f7f2157fd | |||
| 2a11f34165 | |||
| 0a28c0cc1b | |||
| a31eafca1f | |||
| 6e8446675e | |||
| dd3f9937b2 | |||
| fb7879346a | |||
| 393e659cf0 | |||
| 17c04841dd | |||
| 19328921bb | |||
| 14f1e26dfd | |||
| a031f37000 | |||
| ceeb22419f | |||
| c02d5aba36 | |||
| 9490ecc197 | |||
| d9fd6ec9e0 | |||
| bd27ed90de |
@@ -0,0 +1,50 @@
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
environment:
|
||||
BUNDLE_PATH: vendor/bundle
|
||||
FL_OUTPUT_DIR: output
|
||||
macos:
|
||||
xcode: "10.1.0"
|
||||
working_directory: ~/PlayerKit
|
||||
shell: /bin/bash --login -o pipefail
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- restore_cache:
|
||||
key: v1-gems-{{ checksum "Gemfile.lock" }}
|
||||
|
||||
- run:
|
||||
name: Set Ruby version
|
||||
command: echo "ruby-2.4" > ~/.ruby-version
|
||||
|
||||
- run:
|
||||
name: Install bundler dependencies
|
||||
command: bundle install --path vendor/bundle
|
||||
|
||||
- run:
|
||||
name: Build and run tests
|
||||
command: bundle exec fastlane ios test
|
||||
environment:
|
||||
SCAN_DEVICE: iPhone 8
|
||||
SCAN_SCHEME: PlayerKit-Example
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- vendor/bundle
|
||||
key: v1-gems-{{ checksum "Gemfile.lock" }}
|
||||
|
||||
- store_artifacts:
|
||||
path: output
|
||||
|
||||
- store_test_results:
|
||||
path: output/scan
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build:
|
||||
jobs:
|
||||
- build-and-test
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
4.0
|
||||
@@ -0,0 +1,42 @@
|
||||
# Copyright © 2018 Vimeo. All rights reserved.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
# Run `swiftlint rules` for a list of available rules.
|
||||
|
||||
# Only the rules specified here will be enabled.
|
||||
whitelist_rules:
|
||||
- trailing_newline
|
||||
- trailing_whitespace
|
||||
- opening_brace
|
||||
|
||||
# Paths to include during linting.
|
||||
included:
|
||||
- ../PlayerKit/Classes
|
||||
- ../Example
|
||||
|
||||
# Paths to exclude during linting.
|
||||
excluded:
|
||||
- Pods
|
||||
|
||||
# Configurable rules can be customized from this configuration file,
|
||||
# binary rules can set their severity level.
|
||||
trailing_whitespace:
|
||||
ignores_empty_lines: true
|
||||
|
||||
@@ -172,6 +172,7 @@
|
||||
39DBB93200760D1BB486C4CC /* [CP] Check Pods Manifest.lock */,
|
||||
607FACCC1AFB9204008FA782 /* Sources */,
|
||||
607FACCD1AFB9204008FA782 /* Frameworks */,
|
||||
0C1455B6219DDFB7006442F3 /* ShellScript */,
|
||||
607FACCE1AFB9204008FA782 /* Resources */,
|
||||
AEEB7FDEAB18BB5B2C3B7E51 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
@@ -216,12 +217,12 @@
|
||||
607FACCF1AFB9204008FA782 = {
|
||||
CreatedOnToolsVersion = 6.3.1;
|
||||
DevelopmentTeam = 35R365FS4Q;
|
||||
LastSwiftMigration = 0900;
|
||||
LastSwiftMigration = 1010;
|
||||
};
|
||||
607FACE41AFB9204008FA782 = {
|
||||
CreatedOnToolsVersion = 6.3.1;
|
||||
DevelopmentTeam = 35R365FS4Q;
|
||||
LastSwiftMigration = 0900;
|
||||
LastSwiftMigration = 1010;
|
||||
TestTargetID = 607FACCF1AFB9204008FA782;
|
||||
};
|
||||
};
|
||||
@@ -231,6 +232,7 @@
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
English,
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
@@ -267,6 +269,19 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
0C1455B6219DDFB7006442F3 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "WORKSPACE_ROOT=$( cd \"$(dirname \"${SRCROOT[0]}\")\" ; pwd -P )\n\nif which \"$PODS_ROOT\"/SwiftLint/swiftlint > /dev/null; then\n \"$PODS_ROOT\"/SwiftLint/swiftlint autocorrect --config \"$WORKSPACE_ROOT\"/.swiftlint.yml\nelse\n echo \"Warning: SwiftLint is not installed. Make sure you've run `bundle exec pod install`.\"\nfi\n";
|
||||
};
|
||||
39DBB93200760D1BB486C4CC /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -407,7 +422,7 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -447,7 +462,7 @@
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
@@ -456,7 +471,6 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9D69DB7C6B7F626E6391E3FA /* Pods-PlayerKit_Example.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
DEVELOPMENT_TEAM = 35R365FS4Q;
|
||||
INFOPLIST_FILE = PlayerKit/Info.plist;
|
||||
@@ -464,8 +478,6 @@
|
||||
MODULE_NAME = ExampleApp;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -474,7 +486,6 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = CF08222175BD5B0E142D343A /* Pods-PlayerKit_Example.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
DEVELOPMENT_TEAM = 35R365FS4Q;
|
||||
INFOPLIST_FILE = PlayerKit/Info.plist;
|
||||
@@ -482,8 +493,6 @@
|
||||
MODULE_NAME = ExampleApp;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
@@ -502,8 +511,6 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -517,8 +524,6 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
||||
@@ -13,8 +13,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
|
||||
{
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
self.window = UIWindow(frame: UIScreen.main.bounds)
|
||||
self.window?.rootViewController = PlayerViewController()
|
||||
self.window?.makeKeyAndVisible()
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>2.0.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -10,10 +10,8 @@ import UIKit
|
||||
import PlayerKit
|
||||
import AVFoundation
|
||||
|
||||
class PlayerViewController: UIViewController, PlayerDelegate
|
||||
{
|
||||
private struct Constants
|
||||
{
|
||||
class PlayerViewController: UIViewController, PlayerDelegate {
|
||||
private struct Constants {
|
||||
static let VideoURL = URL(string: "https://github.com/vimeo/PlayerKit/blob/master/Example/PlayerKit/video.mp4?raw=true")!
|
||||
}
|
||||
|
||||
@@ -24,8 +22,7 @@ class PlayerViewController: UIViewController, PlayerDelegate
|
||||
|
||||
private let player = RegularPlayer()
|
||||
|
||||
override func viewDidLoad()
|
||||
{
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
player.delegate = self
|
||||
@@ -37,8 +34,7 @@ class PlayerViewController: UIViewController, PlayerDelegate
|
||||
|
||||
// MARK: Setup
|
||||
|
||||
private func addPlayerToView()
|
||||
{
|
||||
private func addPlayerToView() {
|
||||
player.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
player.view.frame = self.view.bounds
|
||||
self.view.insertSubview(player.view, at: 0)
|
||||
@@ -46,13 +42,11 @@ class PlayerViewController: UIViewController, PlayerDelegate
|
||||
|
||||
// MARK: Actions
|
||||
|
||||
@IBAction func didTapPlayButton()
|
||||
{
|
||||
@IBAction func didTapPlayButton() {
|
||||
self.player.playing ? self.player.pause() : self.player.play()
|
||||
}
|
||||
|
||||
@IBAction func didChangeSliderValue()
|
||||
{
|
||||
@IBAction func didChangeSliderValue() {
|
||||
let value = Double(self.slider.value)
|
||||
|
||||
let time = value * self.player.duration
|
||||
@@ -62,12 +56,10 @@ class PlayerViewController: UIViewController, PlayerDelegate
|
||||
|
||||
// MARK: VideoPlayerDelegate
|
||||
|
||||
func playerDidUpdateState(player: Player, previousState: PlayerState)
|
||||
{
|
||||
func playerDidUpdateState(player: Player, previousState: PlayerState) {
|
||||
self.activityIndicator.isHidden = true
|
||||
|
||||
switch player.state
|
||||
{
|
||||
switch player.state {
|
||||
case .loading:
|
||||
|
||||
self.activityIndicator.isHidden = false
|
||||
@@ -82,30 +74,24 @@ class PlayerViewController: UIViewController, PlayerDelegate
|
||||
}
|
||||
}
|
||||
|
||||
func playerDidUpdatePlaying(player: Player)
|
||||
{
|
||||
func playerDidUpdatePlaying(player: Player) {
|
||||
self.playButton.isSelected = player.playing
|
||||
}
|
||||
|
||||
func playerDidUpdateTime(player: Player)
|
||||
{
|
||||
guard player.duration > 0 else
|
||||
{
|
||||
func playerDidUpdateTime(player: Player) {
|
||||
guard player.duration > 0 else {
|
||||
return
|
||||
}
|
||||
|
||||
let ratio = player.time / player.duration
|
||||
|
||||
if self.slider.isHighlighted == false
|
||||
{
|
||||
if self.slider.isHighlighted == false {
|
||||
self.slider.value = Float(ratio)
|
||||
}
|
||||
}
|
||||
|
||||
func playerDidUpdateBufferedTime(player: Player)
|
||||
{
|
||||
guard player.duration > 0 else
|
||||
{
|
||||
func playerDidUpdateBufferedTime(player: Player) {
|
||||
guard player.duration > 0 else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+8
-6
@@ -1,10 +1,12 @@
|
||||
use_frameworks!
|
||||
|
||||
platform :ios, '8.0'
|
||||
|
||||
target 'PlayerKit_Example' do
|
||||
pod 'PlayerKit', :path => '../'
|
||||
|
||||
target 'PlayerKit_Tests' do
|
||||
inherit! :search_paths
|
||||
|
||||
end
|
||||
pod 'PlayerKit', :path => '../'
|
||||
pod 'SwiftLint', '0.28.0'
|
||||
|
||||
target 'PlayerKit_Tests' do
|
||||
inherit! :search_paths
|
||||
end
|
||||
end
|
||||
|
||||
+10
-3
@@ -1,16 +1,23 @@
|
||||
PODS:
|
||||
- PlayerKit (1.2.0)
|
||||
- PlayerKit (1.3.0)
|
||||
- SwiftLint (0.28.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
- PlayerKit (from `../`)
|
||||
- SwiftLint (= 0.28.0)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
- SwiftLint
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
PlayerKit:
|
||||
:path: "../"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
PlayerKit: ff4e4cd4c216d60ebcfccf3a68772d6299373e5a
|
||||
PlayerKit: 16dbe4196fbd6cbc0e677026d13ffb5fb80276fb
|
||||
SwiftLint: 088cfacb75b45970017e62b7524d506776d60148
|
||||
|
||||
PODFILE CHECKSUM: 071d8819500a822237123321021901352f4d91a4
|
||||
PODFILE CHECKSUM: 90f31c00641fb4d5e9453ba687a3a06a32cca83f
|
||||
|
||||
COCOAPODS: 1.5.2
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>2.0.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -5,6 +5,6 @@ gem 'danger', '4.0.4'
|
||||
gem 'xcode-install', '2.1.0'
|
||||
gem 'xcpretty-json-formatter', '0.1.0'
|
||||
gem 'danger-xcode_summary', '0.1.0'
|
||||
gem 'nokogiri', '1.6.0'
|
||||
gem 'nokogiri', '1.8.5'
|
||||
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
|
||||
eval(File.read(plugins_path), binding) if File.exist?(plugins_path)
|
||||
|
||||
+4
-4
@@ -162,7 +162,7 @@ GEM
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2016.0521)
|
||||
mini_magick (4.5.1)
|
||||
mini_portile (0.5.3)
|
||||
mini_portile2 (2.3.0)
|
||||
minitest (5.11.3)
|
||||
molinillo (0.6.5)
|
||||
multi_json (1.13.1)
|
||||
@@ -171,8 +171,8 @@ GEM
|
||||
nanaimo (0.2.5)
|
||||
nap (1.1.0)
|
||||
netrc (0.11.0)
|
||||
nokogiri (1.6.0)
|
||||
mini_portile (~> 0.5.0)
|
||||
nokogiri (1.8.5)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
octokit (4.9.0)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
open4 (1.3.4)
|
||||
@@ -235,7 +235,7 @@ DEPENDENCIES
|
||||
danger-xcode_summary (= 0.1.0)
|
||||
fastlane (= 2.42.0)
|
||||
fastlane-plugin-pretty_junit
|
||||
nokogiri (= 1.6.0)
|
||||
nokogiri (= 1.8.5)
|
||||
xcode-install (= 2.1.0)
|
||||
xcpretty-json-formatter (= 0.1.0)
|
||||
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'PlayerKit'
|
||||
s.version = '1.3.0'
|
||||
s.version = '2.0.0'
|
||||
s.summary = 'A modular video player system.'
|
||||
|
||||
s.description = <<-DESC
|
||||
@@ -15,6 +15,8 @@ Pod::Spec.new do |s|
|
||||
s.ios.deployment_target = '8.0'
|
||||
s.tvos.deployment_target = '9.0'
|
||||
|
||||
s.swift_version = "4.2"
|
||||
|
||||
s.source_files = 'PlayerKit/Classes/**/*'
|
||||
|
||||
end
|
||||
|
||||
@@ -9,30 +9,24 @@
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
|
||||
extension AVPlayer
|
||||
{
|
||||
var errorForPlayerOrItem: NSError?
|
||||
{
|
||||
extension AVPlayer {
|
||||
var errorForPlayerOrItem: NSError? {
|
||||
// First try to return the current item's error
|
||||
|
||||
if let error = self.currentItem?.error
|
||||
{
|
||||
if let error = self.currentItem?.error {
|
||||
// If current item's error has an underlying error, return that
|
||||
|
||||
if let underlyingError = (error as NSError).userInfo[NSUnderlyingErrorKey] as? NSError
|
||||
{
|
||||
if let underlyingError = (error as NSError).userInfo[NSUnderlyingErrorKey] as? NSError {
|
||||
return underlyingError
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
return error as NSError?
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, try to return the player error
|
||||
|
||||
if let error = self.error
|
||||
{
|
||||
if let error = self.error {
|
||||
return error as NSError?
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,9 @@
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
|
||||
extension CMTime
|
||||
{
|
||||
var timeInterval: TimeInterval?
|
||||
{
|
||||
if CMTIME_IS_INVALID(self) || CMTIME_IS_INDEFINITE(self)
|
||||
{
|
||||
extension CMTime {
|
||||
var timeInterval: TimeInterval? {
|
||||
if CMTIME_IS_INVALID(self) || CMTIME_IS_INDEFINITE(self) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,7 @@ import UIKit
|
||||
import AVKit
|
||||
|
||||
/// A player error
|
||||
public enum PlayerError: Int
|
||||
{
|
||||
public enum PlayerError: Int {
|
||||
case unknown
|
||||
case loading
|
||||
|
||||
@@ -20,10 +19,8 @@ public enum PlayerError: Int
|
||||
/// The associated error
|
||||
///
|
||||
/// - Returns: The error
|
||||
public func error() -> NSError
|
||||
{
|
||||
switch self
|
||||
{
|
||||
public func error() -> NSError {
|
||||
switch self {
|
||||
case .unknown:
|
||||
|
||||
return NSError(domain: type(of: self).Domain, code: self.rawValue, userInfo: [NSLocalizedDescriptionKey: "An unknown error occurred."])
|
||||
@@ -40,16 +37,14 @@ public enum PlayerError: Int
|
||||
/// - loading: The player is loading or buffering
|
||||
/// - ready: The player is ready for playback
|
||||
/// - failed: The player has failed
|
||||
@objc public enum PlayerState: Int
|
||||
{
|
||||
@objc public enum PlayerState: Int {
|
||||
case loading
|
||||
case ready
|
||||
case failed
|
||||
}
|
||||
|
||||
/// An object that adopts the PlayerDelegate protocol can receive updates from the player.
|
||||
@objc public protocol PlayerDelegate: class
|
||||
{
|
||||
@objc public protocol PlayerDelegate: class {
|
||||
func playerDidUpdateState(player: Player, previousState: PlayerState)
|
||||
func playerDidUpdatePlaying(player: Player)
|
||||
func playerDidUpdateTime(player: Player)
|
||||
@@ -57,8 +52,7 @@ public enum PlayerError: Int
|
||||
}
|
||||
|
||||
/// An object that adopts the Player protocol is responsible for implementing the API and calling PlayerDelegate methods where appropriate.
|
||||
@objc public protocol Player: class
|
||||
{
|
||||
@objc public protocol Player: class {
|
||||
weak var delegate: PlayerDelegate? { get set }
|
||||
|
||||
var state: PlayerState { get }
|
||||
@@ -88,40 +82,34 @@ public enum PlayerError: Int
|
||||
// MARK: Identity Protocols
|
||||
|
||||
/// A player that adopts the ProvidesView protocol is capable of providing a view to be added to a view hierarchy.
|
||||
@objc public protocol ProvidesView
|
||||
{
|
||||
@objc public protocol ProvidesView {
|
||||
var view: UIView { get }
|
||||
}
|
||||
|
||||
// MARK: Capability Protocols
|
||||
|
||||
/// A player that adopts the ProvidesView protocol is capable of AirPlay playback.
|
||||
@objc public protocol AirPlayCapable
|
||||
{
|
||||
@objc public protocol AirPlayCapable {
|
||||
var isAirPlayEnabled: Bool { get set }
|
||||
}
|
||||
|
||||
/// A player that adopts the ProvidesView protocol is capable of setting audio volume.
|
||||
@objc public protocol VolumeCapable
|
||||
{
|
||||
@objc public protocol VolumeCapable {
|
||||
var volume: Float { get set }
|
||||
}
|
||||
|
||||
/// A player that adopts the ProvidesView protocol is capable of setting the video fill mode.
|
||||
@objc public protocol FillModeCapable
|
||||
{
|
||||
@objc public protocol FillModeCapable {
|
||||
var fillMode: FillMode { get set }
|
||||
}
|
||||
|
||||
@objc public enum FillMode: Int
|
||||
{
|
||||
@objc public enum FillMode: Int {
|
||||
case fit
|
||||
case fill
|
||||
}
|
||||
|
||||
/// The metadata that should be attached to any type of text track.
|
||||
@objc public protocol TextTrackMetadata
|
||||
{
|
||||
@objc public protocol TextTrackMetadata {
|
||||
var displayName: String { get }
|
||||
var locale: Locale? { get }
|
||||
// Indicates that the text track represents subtitles for the def and hard of hearing (SDH).
|
||||
@@ -130,17 +118,14 @@ public enum PlayerError: Int
|
||||
@objc(displayNameWithLocale:) func displayName(with locale: Locale) -> String
|
||||
}
|
||||
|
||||
extension TextTrackMetadata
|
||||
{
|
||||
public func matches(_ other: TextTrackMetadata) -> Bool
|
||||
{
|
||||
extension TextTrackMetadata {
|
||||
public func matches(_ other: TextTrackMetadata) -> Bool {
|
||||
return (self.locale == other.locale && self.isSDHTrack == other.isSDHTrack)
|
||||
}
|
||||
}
|
||||
|
||||
/// A player that conforms to the TextTrackCapable protocol is capable of advertising and displaying text tracks.
|
||||
@objc public protocol TextTrackCapable
|
||||
{
|
||||
@objc public protocol TextTrackCapable {
|
||||
var selectedTextTrack: TextTrackMetadata? { get }
|
||||
var availableTextTracks: [TextTrackMetadata] { get }
|
||||
|
||||
@@ -150,8 +135,7 @@ extension TextTrackMetadata
|
||||
|
||||
#if os(iOS)
|
||||
/// A player that adopts the ProvidesView protocol is capable of Picture in Picture playback.
|
||||
@objc public protocol PictureInPictureCapable
|
||||
{
|
||||
@objc public protocol PictureInPictureCapable {
|
||||
@available(iOS 9.0, *)
|
||||
var pictureInPictureController: AVPictureInPictureController? { get }
|
||||
}
|
||||
|
||||
@@ -11,19 +11,15 @@ import Foundation
|
||||
import AVFoundation
|
||||
import AVKit
|
||||
|
||||
extension AVMediaSelectionOption: TextTrackMetadata
|
||||
{
|
||||
public var isSDHTrack: Bool
|
||||
{
|
||||
extension AVMediaSelectionOption: TextTrackMetadata {
|
||||
public var isSDHTrack: Bool {
|
||||
return self.hasMediaCharacteristic(.describesMusicAndSoundForAccessibility) && self.hasMediaCharacteristic(.transcribesSpokenDialogForAccessibility)
|
||||
}
|
||||
}
|
||||
|
||||
/// A RegularPlayer is used to play regular videos.
|
||||
@objc open class RegularPlayer: NSObject, Player, ProvidesView
|
||||
{
|
||||
public struct Constants
|
||||
{
|
||||
@objc open class RegularPlayer: NSObject, Player, ProvidesView {
|
||||
public struct Constants {
|
||||
public static let TimeUpdateInterval: TimeInterval = 0.1
|
||||
}
|
||||
|
||||
@@ -36,12 +32,10 @@ extension AVMediaSelectionOption: TextTrackMetadata
|
||||
/// Sets an AVAsset on the player.
|
||||
///
|
||||
/// - Parameter asset: The AVAsset
|
||||
@objc open func set(_ asset: AVAsset)
|
||||
{
|
||||
@objc open func set(_ asset: AVAsset) {
|
||||
// Prepare the old item for removal
|
||||
|
||||
if let currentItem = self.player.currentItem
|
||||
{
|
||||
if let currentItem = self.player.currentItem {
|
||||
self.removePlayerItemObservers(fromPlayerItem: currentItem)
|
||||
}
|
||||
|
||||
@@ -56,33 +50,27 @@ extension AVMediaSelectionOption: TextTrackMetadata
|
||||
|
||||
// MARK: ProvidesView
|
||||
|
||||
private class RegularPlayerView: UIView
|
||||
{
|
||||
var playerLayer: AVPlayerLayer
|
||||
{
|
||||
private class RegularPlayerView: UIView {
|
||||
var playerLayer: AVPlayerLayer {
|
||||
return self.layer as! AVPlayerLayer
|
||||
}
|
||||
|
||||
override class var layerClass: AnyClass
|
||||
{
|
||||
override class var layerClass: AnyClass {
|
||||
return AVPlayerLayer.self
|
||||
}
|
||||
|
||||
func configureForPlayer(player: AVPlayer)
|
||||
{
|
||||
func configureForPlayer(player: AVPlayer) {
|
||||
(self.layer as! AVPlayerLayer).player = player
|
||||
}
|
||||
}
|
||||
|
||||
public let view: UIView = RegularPlayerView(frame: .zero)
|
||||
|
||||
private var regularPlayerView: RegularPlayerView
|
||||
{
|
||||
private var regularPlayerView: RegularPlayerView {
|
||||
return self.view as! RegularPlayerView
|
||||
}
|
||||
|
||||
private var playerLayer: AVPlayerLayer
|
||||
{
|
||||
private var playerLayer: AVPlayerLayer {
|
||||
return self.regularPlayerView.playerLayer
|
||||
}
|
||||
|
||||
@@ -90,68 +78,55 @@ extension AVMediaSelectionOption: TextTrackMetadata
|
||||
|
||||
weak public var delegate: PlayerDelegate?
|
||||
|
||||
public private(set) var state: PlayerState = .ready
|
||||
{
|
||||
didSet
|
||||
{
|
||||
public private(set) var state: PlayerState = .ready {
|
||||
didSet {
|
||||
self.delegate?.playerDidUpdateState(player: self, previousState: oldValue)
|
||||
}
|
||||
}
|
||||
|
||||
public var duration: TimeInterval
|
||||
{
|
||||
public var duration: TimeInterval {
|
||||
return self.player.currentItem?.duration.timeInterval ?? 0
|
||||
}
|
||||
|
||||
public private(set) var time: TimeInterval = 0
|
||||
{
|
||||
didSet
|
||||
{
|
||||
public private(set) var time: TimeInterval = 0 {
|
||||
didSet {
|
||||
self.delegate?.playerDidUpdateTime(player: self)
|
||||
}
|
||||
}
|
||||
|
||||
public private(set) var bufferedTime: TimeInterval = 0
|
||||
{
|
||||
didSet
|
||||
{
|
||||
public private(set) var bufferedTime: TimeInterval = 0 {
|
||||
didSet {
|
||||
self.delegate?.playerDidUpdateBufferedTime(player: self)
|
||||
}
|
||||
}
|
||||
|
||||
public var playing: Bool
|
||||
{
|
||||
public var playing: Bool {
|
||||
return self.player.rate > 0
|
||||
}
|
||||
|
||||
public var error: NSError?
|
||||
{
|
||||
public var error: NSError? {
|
||||
return self.player.errorForPlayerOrItem
|
||||
}
|
||||
|
||||
public func seek(to time: TimeInterval)
|
||||
{
|
||||
let cmTime = CMTimeMakeWithSeconds(time, Int32(NSEC_PER_SEC))
|
||||
public func seek(to time: TimeInterval) {
|
||||
let cmTime = CMTimeMakeWithSeconds(time, preferredTimescale: Int32(NSEC_PER_SEC))
|
||||
|
||||
self.player.seek(to: cmTime)
|
||||
|
||||
self.time = time
|
||||
}
|
||||
|
||||
public func play()
|
||||
{
|
||||
public func play() {
|
||||
self.player.play()
|
||||
}
|
||||
|
||||
public func pause()
|
||||
{
|
||||
public func pause() {
|
||||
self.player.pause()
|
||||
}
|
||||
|
||||
// MARK: Lifecycle
|
||||
|
||||
public override init()
|
||||
{
|
||||
public override init() {
|
||||
super.init()
|
||||
|
||||
self.addPlayerObservers()
|
||||
@@ -161,10 +136,8 @@ extension AVMediaSelectionOption: TextTrackMetadata
|
||||
self.setupAirplay()
|
||||
}
|
||||
|
||||
deinit
|
||||
{
|
||||
if let playerItem = self.player.currentItem
|
||||
{
|
||||
deinit {
|
||||
if let playerItem = self.player.currentItem {
|
||||
self.removePlayerItemObservers(fromPlayerItem: playerItem)
|
||||
}
|
||||
|
||||
@@ -173,22 +146,18 @@ extension AVMediaSelectionOption: TextTrackMetadata
|
||||
|
||||
// MARK: Setup
|
||||
|
||||
private func setupAirplay()
|
||||
{
|
||||
private func setupAirplay() {
|
||||
self.player.usesExternalPlaybackWhileExternalScreenIsActive = true
|
||||
}
|
||||
|
||||
// MARK: Observers
|
||||
|
||||
private struct KeyPath
|
||||
{
|
||||
struct Player
|
||||
{
|
||||
private struct KeyPath {
|
||||
struct Player {
|
||||
static let Rate = "rate"
|
||||
}
|
||||
|
||||
struct PlayerItem
|
||||
{
|
||||
struct PlayerItem {
|
||||
static let Status = "status"
|
||||
static let PlaybackLikelyToKeepUp = "playbackLikelyToKeepUp"
|
||||
static let LoadedTimeRanges = "loadedTimeRanges"
|
||||
@@ -197,41 +166,35 @@ extension AVMediaSelectionOption: TextTrackMetadata
|
||||
|
||||
private var playerTimeObserver: Any?
|
||||
|
||||
private func addPlayerItemObservers(toPlayerItem playerItem: AVPlayerItem)
|
||||
{
|
||||
private func addPlayerItemObservers(toPlayerItem playerItem: AVPlayerItem) {
|
||||
playerItem.addObserver(self, forKeyPath: KeyPath.PlayerItem.Status, options: [.initial, .new], context: nil)
|
||||
playerItem.addObserver(self, forKeyPath: KeyPath.PlayerItem.PlaybackLikelyToKeepUp, options: [.initial, .new], context: nil)
|
||||
playerItem.addObserver(self, forKeyPath: KeyPath.PlayerItem.LoadedTimeRanges, options: [.initial, .new], context: nil)
|
||||
}
|
||||
|
||||
private func removePlayerItemObservers(fromPlayerItem playerItem: AVPlayerItem)
|
||||
{
|
||||
private func removePlayerItemObservers(fromPlayerItem playerItem: AVPlayerItem) {
|
||||
playerItem.removeObserver(self, forKeyPath: KeyPath.PlayerItem.Status, context: nil)
|
||||
playerItem.removeObserver(self, forKeyPath: KeyPath.PlayerItem.PlaybackLikelyToKeepUp, context: nil)
|
||||
playerItem.removeObserver(self, forKeyPath: KeyPath.PlayerItem.LoadedTimeRanges, context: nil)
|
||||
}
|
||||
|
||||
private func addPlayerObservers()
|
||||
{
|
||||
private func addPlayerObservers() {
|
||||
self.player.addObserver(self, forKeyPath: KeyPath.Player.Rate, options: [.initial, .new], context: nil)
|
||||
|
||||
let interval = CMTimeMakeWithSeconds(Constants.TimeUpdateInterval, Int32(NSEC_PER_SEC))
|
||||
let interval = CMTimeMakeWithSeconds(Constants.TimeUpdateInterval, preferredTimescale: Int32(NSEC_PER_SEC))
|
||||
|
||||
self.playerTimeObserver = self.player.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using: { [weak self] (cmTime) in
|
||||
|
||||
if let strongSelf = self, let time = cmTime.timeInterval
|
||||
{
|
||||
if let strongSelf = self, let time = cmTime.timeInterval {
|
||||
strongSelf.time = time
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private func removePlayerObservers()
|
||||
{
|
||||
private func removePlayerObservers() {
|
||||
self.player.removeObserver(self, forKeyPath: KeyPath.Player.Rate, context: nil)
|
||||
|
||||
if let playerTimeObserver = self.playerTimeObserver
|
||||
{
|
||||
if let playerTimeObserver = self.playerTimeObserver {
|
||||
self.player.removeTimeObserver(playerTimeObserver)
|
||||
|
||||
self.playerTimeObserver = nil
|
||||
@@ -240,56 +203,44 @@ extension AVMediaSelectionOption: TextTrackMetadata
|
||||
|
||||
// MARK: Observation
|
||||
|
||||
override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
|
||||
{
|
||||
override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||
// Player Item Observers
|
||||
|
||||
if keyPath == KeyPath.PlayerItem.Status
|
||||
{
|
||||
if let statusInt = change?[.newKey] as? Int, let status = AVPlayerItemStatus(rawValue: statusInt)
|
||||
{
|
||||
if keyPath == KeyPath.PlayerItem.Status {
|
||||
if let statusInt = change?[.newKey] as? Int, let status = AVPlayerItem.Status(rawValue: statusInt) {
|
||||
self.playerItemStatusDidChange(status: status)
|
||||
}
|
||||
}
|
||||
else if keyPath == KeyPath.PlayerItem.PlaybackLikelyToKeepUp
|
||||
{
|
||||
if let playbackLikelyToKeepUp = change?[.newKey] as? Bool
|
||||
{
|
||||
else if keyPath == KeyPath.PlayerItem.PlaybackLikelyToKeepUp {
|
||||
if let playbackLikelyToKeepUp = change?[.newKey] as? Bool {
|
||||
self.playerItemPlaybackLikelyToKeepUpDidChange(playbackLikelyToKeepUp: playbackLikelyToKeepUp)
|
||||
}
|
||||
}
|
||||
else if keyPath == KeyPath.PlayerItem.LoadedTimeRanges
|
||||
{
|
||||
if let loadedTimeRanges = change?[.newKey] as? [NSValue]
|
||||
{
|
||||
else if keyPath == KeyPath.PlayerItem.LoadedTimeRanges {
|
||||
if let loadedTimeRanges = change?[.newKey] as? [NSValue] {
|
||||
self.playerItemLoadedTimeRangesDidChange(loadedTimeRanges: loadedTimeRanges)
|
||||
}
|
||||
}
|
||||
|
||||
// Player Observers
|
||||
|
||||
else if keyPath == KeyPath.Player.Rate
|
||||
{
|
||||
if let rate = change?[.newKey] as? Float
|
||||
{
|
||||
else if keyPath == KeyPath.Player.Rate {
|
||||
if let rate = change?[.newKey] as? Float {
|
||||
self.playerRateDidChange(rate: rate)
|
||||
}
|
||||
}
|
||||
|
||||
// Fall Through Observers
|
||||
|
||||
else
|
||||
{
|
||||
else {
|
||||
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Observation Helpers
|
||||
|
||||
private func playerItemStatusDidChange(status: AVPlayerItemStatus)
|
||||
{
|
||||
switch status
|
||||
{
|
||||
private func playerItemStatusDidChange(status: AVPlayerItem.Status) {
|
||||
switch status {
|
||||
case .unknown:
|
||||
|
||||
self.state = .loading
|
||||
@@ -304,22 +255,18 @@ extension AVMediaSelectionOption: TextTrackMetadata
|
||||
}
|
||||
}
|
||||
|
||||
private func playerRateDidChange(rate: Float)
|
||||
{
|
||||
private func playerRateDidChange(rate: Float) {
|
||||
self.delegate?.playerDidUpdatePlaying(player: self)
|
||||
}
|
||||
|
||||
private func playerItemPlaybackLikelyToKeepUpDidChange(playbackLikelyToKeepUp: Bool)
|
||||
{
|
||||
private func playerItemPlaybackLikelyToKeepUpDidChange(playbackLikelyToKeepUp: Bool) {
|
||||
let state: PlayerState = playbackLikelyToKeepUp ? .ready : .loading
|
||||
|
||||
self.state = state
|
||||
}
|
||||
|
||||
private func playerItemLoadedTimeRangesDidChange(loadedTimeRanges: [NSValue])
|
||||
{
|
||||
guard let bufferedCMTime = loadedTimeRanges.first?.timeRangeValue.end, let bufferedTime = bufferedCMTime.timeInterval else
|
||||
{
|
||||
private func playerItemLoadedTimeRangesDidChange(loadedTimeRanges: [NSValue]) {
|
||||
guard let bufferedCMTime = loadedTimeRanges.first?.timeRangeValue.end, let bufferedTime = bufferedCMTime.timeInterval else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -340,25 +287,20 @@ extension AVMediaSelectionOption: TextTrackMetadata
|
||||
|
||||
extension RegularPlayer: AirPlayCapable
|
||||
{
|
||||
public var isAirPlayEnabled: Bool
|
||||
{
|
||||
get
|
||||
{
|
||||
public var isAirPlayEnabled: Bool {
|
||||
get {
|
||||
return self.player.allowsExternalPlayback
|
||||
}
|
||||
set
|
||||
{
|
||||
set {
|
||||
return self.player.allowsExternalPlayback = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
extension RegularPlayer: PictureInPictureCapable
|
||||
{
|
||||
extension RegularPlayer: PictureInPictureCapable {
|
||||
@available(iOS 9.0, *)
|
||||
public var pictureInPictureController: AVPictureInPictureController?
|
||||
{
|
||||
public var pictureInPictureController: AVPictureInPictureController? {
|
||||
return self._pictureInPictureController
|
||||
}
|
||||
}
|
||||
@@ -366,35 +308,27 @@ extension RegularPlayer: PictureInPictureCapable
|
||||
|
||||
extension RegularPlayer: VolumeCapable
|
||||
{
|
||||
public var volume: Float
|
||||
{
|
||||
get
|
||||
{
|
||||
public var volume: Float {
|
||||
get {
|
||||
return self.player.volume
|
||||
}
|
||||
set
|
||||
{
|
||||
set {
|
||||
self.player.volume = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension RegularPlayer: FillModeCapable
|
||||
{
|
||||
public var fillMode: FillMode
|
||||
{
|
||||
get
|
||||
{
|
||||
extension RegularPlayer: FillModeCapable {
|
||||
public var fillMode: FillMode {
|
||||
get {
|
||||
let gravity = (self.view.layer as! AVPlayerLayer).videoGravity
|
||||
|
||||
return gravity == .resizeAspect ? .fit : .fill
|
||||
}
|
||||
set
|
||||
{
|
||||
set {
|
||||
let gravity: AVLayerVideoGravity
|
||||
|
||||
switch newValue
|
||||
{
|
||||
switch newValue {
|
||||
case .fit:
|
||||
|
||||
gravity = .resizeAspect
|
||||
@@ -409,62 +343,48 @@ extension RegularPlayer: FillModeCapable
|
||||
}
|
||||
}
|
||||
|
||||
extension RegularPlayer: TextTrackCapable
|
||||
{
|
||||
public var selectedTextTrack: TextTrackMetadata?
|
||||
{
|
||||
guard let group = self.player.currentItem?.asset.mediaSelectionGroup(forMediaCharacteristic: .legible) else
|
||||
{
|
||||
extension RegularPlayer: TextTrackCapable {
|
||||
public var selectedTextTrack: TextTrackMetadata? {
|
||||
guard let group = self.player.currentItem?.asset.mediaSelectionGroup(forMediaCharacteristic: .legible) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if #available(iOS 9.0, *)
|
||||
{
|
||||
if #available(iOS 9.0, *) {
|
||||
return self.player.currentItem?.currentMediaSelection.selectedMediaOption(in: group)
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
return self.player.currentItem?.selectedMediaOption(in: group)
|
||||
}
|
||||
}
|
||||
|
||||
public var availableTextTracks: [TextTrackMetadata]
|
||||
{
|
||||
guard let group = self.player.currentItem?.asset.mediaSelectionGroup(forMediaCharacteristic: .legible) else
|
||||
{
|
||||
public var availableTextTracks: [TextTrackMetadata] {
|
||||
guard let group = self.player.currentItem?.asset.mediaSelectionGroup(forMediaCharacteristic: .legible) else {
|
||||
return []
|
||||
}
|
||||
return group.options
|
||||
}
|
||||
|
||||
public func fetchTextTracks(completion: @escaping ([TextTrackMetadata], TextTrackMetadata?) -> Void)
|
||||
{
|
||||
public func fetchTextTracks(completion: @escaping ([TextTrackMetadata], TextTrackMetadata?) -> Void) {
|
||||
self.player.currentItem?.asset.loadValuesAsynchronously(forKeys: [#keyPath(AVAsset.availableMediaCharacteristicsWithMediaSelectionOptions)]) { [weak self] in
|
||||
guard let strongSelf = self, let group = strongSelf.player.currentItem?.asset.mediaSelectionGroup(forMediaCharacteristic: .legible) else
|
||||
{
|
||||
guard let strongSelf = self, let group = strongSelf.player.currentItem?.asset.mediaSelectionGroup(forMediaCharacteristic: .legible) else {
|
||||
completion([], nil)
|
||||
return
|
||||
}
|
||||
if #available(iOS 9.0, *)
|
||||
{
|
||||
if #available(iOS 9.0, *) {
|
||||
completion(group.options, strongSelf.player.currentItem?.currentMediaSelection.selectedMediaOption(in: group))
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
completion(group.options, strongSelf.player.currentItem?.selectedMediaOption(in: group))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func select(_ textTrack: TextTrackMetadata?)
|
||||
{
|
||||
guard let group = self.player.currentItem?.asset.mediaSelectionGroup(forMediaCharacteristic: .legible) else
|
||||
{
|
||||
public func select(_ textTrack: TextTrackMetadata?) {
|
||||
guard let group = self.player.currentItem?.asset.mediaSelectionGroup(forMediaCharacteristic: .legible) else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let track = textTrack else
|
||||
{
|
||||
guard let track = textTrack else {
|
||||
self.player.currentItem?.select(nil, in: group)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ To run the example project, clone the repo, and run `pod install` from the Examp
|
||||
## Minimum Requirements
|
||||
|
||||
- iOS 8.0 / tvOS 9.0
|
||||
- Swift 3.2
|
||||
- Swift 4.2
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -62,9 +62,9 @@ player.delegate = delegate
|
||||
|
||||
You can create your own players by creating objects that conform to the Player protocol and call the delegate methods when appropriate.
|
||||
|
||||
## Author
|
||||
## Questions?
|
||||
|
||||
Gavin King, gavin@vimeo.com
|
||||
Post on [Stackoverflow](http://stackoverflow.com/questions/tagged/vimeo-ios) with the tag `vimeo-ios`. Get in touch [here](https://vimeo.com/help/contact). Interested in working at Vimeo? We're [hiring](https://vimeo.com/jobs)!
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Example/Pods/Pods.xcodeproj
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
general:
|
||||
artifacts:
|
||||
- "build/Logs"
|
||||
- "PlayerKit_Example-PlayerKit-Example.log"
|
||||
machine:
|
||||
xcode:
|
||||
version: 9.0
|
||||
|
||||
dependencies:
|
||||
override:
|
||||
- bundle install --deployment
|
||||
cache_directories:
|
||||
- "~/.cocoapods"
|
||||
- "Example/Pods"
|
||||
- "vendor/bundle"
|
||||
|
||||
test:
|
||||
override:
|
||||
- bundle exec fastlane test
|
||||
+23
-2
@@ -17,10 +17,10 @@ default_platform :ios
|
||||
|
||||
platform :ios do
|
||||
before_all do
|
||||
ensure_xcode_version(version: "9.0.1")
|
||||
ensure_xcode_version(version: "10.1")
|
||||
end
|
||||
|
||||
desc "run tests"
|
||||
desc "Run tests"
|
||||
lane :test do |options|
|
||||
|
||||
cocoapods(
|
||||
@@ -43,6 +43,27 @@ platform :ios do
|
||||
end
|
||||
end
|
||||
|
||||
desc "bumps the project and podspec version"
|
||||
lane :version_bump do |options|
|
||||
|
||||
bump_type = options[:bump_type]
|
||||
version_number = options[:version_number]
|
||||
if bump_type.nil? && version_number.nil?
|
||||
UI.user_error!("version_bump requires you to provide a bump_type [patch|minor|major] or specific version_number. Please try again.")
|
||||
end
|
||||
|
||||
increment_version_number(
|
||||
bump_type: bump_type,
|
||||
version_number: version_number,
|
||||
xcodeproj: "Example/PlayerKit.xcodeproj"
|
||||
)
|
||||
|
||||
version_bump_podspec(
|
||||
bump_type: bump_type,
|
||||
version_number: version_number
|
||||
)
|
||||
end
|
||||
|
||||
after_all do |lane|
|
||||
|
||||
end
|
||||
|
||||
+7
-1
@@ -27,13 +27,19 @@ xcode-select --install
|
||||
<td width="33%"><code>sudo gem install fastlane -NV</code></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
# Available Actions
|
||||
## iOS
|
||||
### ios test
|
||||
```
|
||||
fastlane ios test
|
||||
```
|
||||
run tests
|
||||
Run tests
|
||||
### ios version_bump
|
||||
```
|
||||
fastlane ios version_bump
|
||||
```
|
||||
bumps the project and podspec version
|
||||
|
||||
----
|
||||
|
||||
|
||||
Reference in New Issue
Block a user