Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e2e769739 | |||
| f3d48a0a75 | |||
| 7bc6450535 | |||
| a4f697b067 | |||
| dbc6cb250c | |||
| 198197241b | |||
| 98c86c1852 | |||
| e08903c201 | |||
| 89c4f4755d | |||
| 0ca7258e9b | |||
| 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 | |||
| 56946a6509 | |||
| 0b9d4e5096 | |||
| 3a53693935 | |||
| aee3f617d6 | |||
| 41f59b1d45 | |||
| 978efdb667 | |||
| d9f3f12bb8 | |||
| aecc7485a5 | |||
| 09a1d6a051 | |||
| 8280e5e801 | |||
| f2d24ffb2e | |||
| b1b690d6ad | |||
| fa9fa737d5 | |||
| fae36bc646 | |||
| 055c6b727a | |||
| 3e945f1639 | |||
| bc804d729a | |||
| 7222d585e5 | |||
| 365b835447 |
@@ -0,0 +1,50 @@
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
environment:
|
||||
BUNDLE_PATH: vendor/bundle
|
||||
FL_OUTPUT_DIR: output
|
||||
macos:
|
||||
xcode: "10.2.1"
|
||||
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 */,
|
||||
);
|
||||
@@ -210,18 +211,18 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0720;
|
||||
LastUpgradeCheck = 0820;
|
||||
LastUpgradeCheck = 1020;
|
||||
ORGANIZATIONNAME = CocoaPods;
|
||||
TargetAttributes = {
|
||||
607FACCF1AFB9204008FA782 = {
|
||||
CreatedOnToolsVersion = 6.3.1;
|
||||
DevelopmentTeam = 35R365FS4Q;
|
||||
LastSwiftMigration = 0900;
|
||||
LastSwiftMigration = 1020;
|
||||
};
|
||||
607FACE41AFB9204008FA782 = {
|
||||
CreatedOnToolsVersion = 6.3.1;
|
||||
DevelopmentTeam = 35R365FS4Q;
|
||||
LastSwiftMigration = 0900;
|
||||
LastSwiftMigration = 1020;
|
||||
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;
|
||||
@@ -367,18 +382,27 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
@@ -407,7 +431,7 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -415,18 +439,27 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
@@ -447,7 +480,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 +489,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 +496,7 @@
|
||||
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;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -474,7 +505,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 +512,7 @@
|
||||
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;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
@@ -502,8 +531,7 @@
|
||||
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;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -517,8 +545,7 @@
|
||||
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;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0900"
|
||||
LastUpgradeVersion = "1020"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -40,7 +40,6 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
@@ -70,7 +69,6 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
||||
@@ -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 (2.0.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: 1830be30e87ca46708c93e7ef0a6cf6590936b14
|
||||
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>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
source 'https://rubygems.org'
|
||||
gem 'fastlane', '2.42.0'
|
||||
gem 'fastlane', '2.108.0'
|
||||
gem 'cocoapods', '1.5.2'
|
||||
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)
|
||||
|
||||
+63
-55
@@ -1,7 +1,7 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (2.3.6)
|
||||
CFPropertyList (3.0.0)
|
||||
activesupport (4.2.10)
|
||||
i18n (~> 0.7)
|
||||
minitest (~> 5.1)
|
||||
@@ -9,7 +9,7 @@ GEM
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.5.2)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
atomos (0.1.2)
|
||||
atomos (0.1.3)
|
||||
babosa (1.0.2)
|
||||
claide (1.0.2)
|
||||
claide-plugins (0.9.2)
|
||||
@@ -40,12 +40,12 @@ GEM
|
||||
fuzzy_match (~> 2.0.4)
|
||||
nap (~> 1.0)
|
||||
cocoapods-deintegrate (1.0.2)
|
||||
cocoapods-downloader (1.2.0)
|
||||
cocoapods-downloader (1.2.2)
|
||||
cocoapods-plugins (1.0.0)
|
||||
nap
|
||||
cocoapods-search (1.0.0)
|
||||
cocoapods-stats (1.0.0)
|
||||
cocoapods-trunk (1.3.0)
|
||||
cocoapods-trunk (1.3.1)
|
||||
nap (>= 0.8, < 2.0)
|
||||
netrc (~> 0.11)
|
||||
cocoapods-try (1.1.0)
|
||||
@@ -54,7 +54,7 @@ GEM
|
||||
colorize (0.8.1)
|
||||
commander-fastlane (4.4.6)
|
||||
highline (~> 1.7.2)
|
||||
concurrent-ruby (1.0.5)
|
||||
concurrent-ruby (1.1.3)
|
||||
cork (0.3.0)
|
||||
colored2 (~> 3.1)
|
||||
danger (4.0.4)
|
||||
@@ -76,10 +76,11 @@ GEM
|
||||
declarative-option (0.1.0)
|
||||
domain_name (0.5.20180417)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.4.0)
|
||||
dotenv (2.5.0)
|
||||
emoji_regex (0.1.1)
|
||||
escape (0.0.4)
|
||||
excon (0.62.0)
|
||||
faraday (0.15.2)
|
||||
faraday (0.15.3)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-cookie_jar (0.0.6)
|
||||
faraday (>= 0.7.4)
|
||||
@@ -88,22 +89,23 @@ GEM
|
||||
faraday (~> 0.8)
|
||||
faraday_middleware (0.12.2)
|
||||
faraday (>= 0.7.4, < 1.0)
|
||||
fastimage (2.1.3)
|
||||
fastlane (2.42.0)
|
||||
CFPropertyList (>= 2.3, < 3.0.0)
|
||||
fastimage (2.1.4)
|
||||
fastlane (2.108.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.3, < 3.0.0)
|
||||
babosa (>= 1.0.2, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 2.0.0)
|
||||
colored
|
||||
commander-fastlane (>= 4.4.0, < 5.0.0)
|
||||
commander-fastlane (>= 4.4.6, < 5.0.0)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (~> 0.1)
|
||||
excon (>= 0.45.0, < 1.0.0)
|
||||
faraday (~> 0.9)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 0.9)
|
||||
fastimage (>= 1.6)
|
||||
gh_inspector (>= 1.0.1, < 2.0.0)
|
||||
google-api-client (>= 0.12.0, < 0.13.0)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-api-client (>= 0.21.2, < 0.24.0)
|
||||
highline (>= 1.7.2, < 2.0.0)
|
||||
json (< 3.0.0)
|
||||
mini_magick (~> 4.5.1)
|
||||
@@ -111,15 +113,18 @@ GEM
|
||||
multi_xml (~> 0.5)
|
||||
multipart-post (~> 2.0.0)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
rubyzip (>= 1.1.0, < 2.0.0)
|
||||
public_suffix (~> 2.0.0)
|
||||
rubyzip (>= 1.2.2, < 2.0.0)
|
||||
security (= 0.1.3)
|
||||
slack-notifier (>= 1.3, < 2.0.0)
|
||||
simctl (~> 1.6.3)
|
||||
slack-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-notifier (>= 1.6.2, < 2.0.0)
|
||||
terminal-table (>= 1.4.5, < 2.0.0)
|
||||
tty-screen (~> 0.5.0)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.4.4, < 2.0.0)
|
||||
xcpretty (>= 0.2.4, < 1.0.0)
|
||||
xcodeproj (>= 1.6.0, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
fastlane-plugin-pretty_junit (0.1.1)
|
||||
colorize
|
||||
@@ -128,21 +133,21 @@ GEM
|
||||
fourflusher (2.0.1)
|
||||
fuzzy_match (2.0.4)
|
||||
gh_inspector (1.1.3)
|
||||
git (1.4.0)
|
||||
google-api-client (0.12.0)
|
||||
git (1.5.0)
|
||||
google-api-client (0.23.9)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (~> 0.5)
|
||||
googleauth (>= 0.5, < 0.7.0)
|
||||
httpclient (>= 2.8.1, < 3.0)
|
||||
mime-types (~> 3.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
googleauth (0.6.2)
|
||||
signet (~> 0.9)
|
||||
googleauth (0.6.7)
|
||||
faraday (~> 0.12)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
logging (~> 2.0)
|
||||
memoist (~> 0.12)
|
||||
memoist (~> 0.16)
|
||||
multi_json (~> 1.11)
|
||||
os (~> 0.9)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (~> 0.7)
|
||||
highline (1.7.10)
|
||||
http-cookie (1.0.3)
|
||||
@@ -152,74 +157,77 @@ GEM
|
||||
concurrent-ruby (~> 1.0)
|
||||
json (2.1.0)
|
||||
jwt (2.1.0)
|
||||
kramdown (1.16.2)
|
||||
little-plugger (1.1.4)
|
||||
logging (2.2.2)
|
||||
little-plugger (~> 1.1)
|
||||
multi_json (~> 1.10)
|
||||
kramdown (1.17.0)
|
||||
memoist (0.16.0)
|
||||
mime-types (3.1)
|
||||
mime-types (3.2.2)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2016.0521)
|
||||
mime-types-data (3.2018.0812)
|
||||
mini_magick (4.5.1)
|
||||
mini_portile (0.5.3)
|
||||
mini_portile2 (2.3.0)
|
||||
minitest (5.11.3)
|
||||
molinillo (0.6.5)
|
||||
molinillo (0.6.6)
|
||||
multi_json (1.13.1)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
nanaimo (0.2.5)
|
||||
nanaimo (0.2.6)
|
||||
nap (1.1.0)
|
||||
naturally (2.2.0)
|
||||
netrc (0.11.0)
|
||||
nokogiri (1.6.0)
|
||||
mini_portile (~> 0.5.0)
|
||||
octokit (4.9.0)
|
||||
nokogiri (1.8.5)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
octokit (4.13.0)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
open4 (1.3.4)
|
||||
os (0.9.6)
|
||||
os (1.0.0)
|
||||
plist (3.4.0)
|
||||
public_suffix (3.0.2)
|
||||
public_suffix (2.0.5)
|
||||
representable (3.0.4)
|
||||
declarative (< 0.1.0)
|
||||
declarative-option (< 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.1)
|
||||
retriable (3.1.2)
|
||||
rouge (2.0.7)
|
||||
ruby-macho (1.1.0)
|
||||
rubyzip (1.2.1)
|
||||
ruby-macho (1.3.1)
|
||||
rubyzip (1.2.2)
|
||||
sawyer (0.8.1)
|
||||
addressable (>= 2.3.5, < 2.6)
|
||||
faraday (~> 0.8, < 1.0)
|
||||
security (0.1.3)
|
||||
signet (0.8.1)
|
||||
signet (0.11.0)
|
||||
addressable (~> 2.3)
|
||||
faraday (~> 0.9)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
slack-notifier (1.5.1)
|
||||
simctl (1.6.5)
|
||||
CFPropertyList
|
||||
naturally
|
||||
slack-notifier (2.3.2)
|
||||
terminal-notifier (1.8.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
thread_safe (0.3.6)
|
||||
tty-screen (0.5.1)
|
||||
tty-cursor (0.6.0)
|
||||
tty-screen (0.6.5)
|
||||
tty-spinner (0.8.0)
|
||||
tty-cursor (>= 0.5.0)
|
||||
tzinfo (1.2.5)
|
||||
thread_safe (~> 0.1)
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.5)
|
||||
unicode-display_width (1.3.2)
|
||||
unicode-display_width (1.4.0)
|
||||
word_wrap (1.0.0)
|
||||
xcode-install (2.1.0)
|
||||
claide (>= 0.9.1, < 1.1.0)
|
||||
fastlane (>= 2.1.1, < 3.0.0)
|
||||
xcodeproj (1.5.9)
|
||||
xcodeproj (1.7.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.2)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.2.5)
|
||||
xcpretty (0.2.8)
|
||||
nanaimo (~> 0.2.6)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-json-formatter (0.1.0)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
@@ -233,11 +241,11 @@ DEPENDENCIES
|
||||
cocoapods (= 1.5.2)
|
||||
danger (= 4.0.4)
|
||||
danger-xcode_summary (= 0.1.0)
|
||||
fastlane (= 2.42.0)
|
||||
fastlane (= 2.108.0)
|
||||
fastlane-plugin-pretty_junit
|
||||
nokogiri (= 1.6.0)
|
||||
nokogiri (= 1.8.5)
|
||||
xcode-install (= 2.1.0)
|
||||
xcpretty-json-formatter (= 0.1.0)
|
||||
|
||||
BUNDLED WITH
|
||||
1.15.3
|
||||
1.17.1
|
||||
|
||||
+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)
|
||||
}
|
||||
|
||||
@@ -172,23 +145,29 @@ extension AVMediaSelectionOption: TextTrackMetadata
|
||||
}
|
||||
|
||||
// MARK: Setup
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
public var automaticallyWaitsToMinimizeStalling: Bool {
|
||||
get {
|
||||
return self.player.automaticallyWaitsToMinimizeStalling
|
||||
}
|
||||
set {
|
||||
self.player.automaticallyWaitsToMinimizeStalling = newValue
|
||||
}
|
||||
}
|
||||
|
||||
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 +176,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 +213,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 +265,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 +297,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 +318,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 +353,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.2.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
|
||||
|
||||
----
|
||||
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
name: VimeoPlayerKit
|
||||
targets:
|
||||
PlayerKit:
|
||||
type: framework
|
||||
platform: iOS
|
||||
settings:
|
||||
PRODUCT_BUNDLE_IDENTIFIER: com.vimeo.playerkit
|
||||
INFOPLIST_FILE: Example/PlayerKit/Info.plist
|
||||
sources:
|
||||
- path: PlayerKit
|
||||
scheme:
|
||||
testTargets:
|
||||
- PlayerKitTests
|
||||
|
||||
PlayerKitTests:
|
||||
dependencies:
|
||||
- target: PlayerKit
|
||||
type: bundle.unit-test
|
||||
platform: iOS
|
||||
sources:
|
||||
- path: Example/Tests
|
||||
Reference in New Issue
Block a user