From b7d6eb23588e90fca7c897c72da90d1ad60b3c13 Mon Sep 17 00:00:00 2001 From: Marco Schmickler Date: Thu, 25 May 2017 19:38:24 +0200 Subject: [PATCH] avplayer --- Podfile | 1 + kplayer.xcodeproj/project.pbxproj | 27 +- kplayer/Base.lproj/Main.storyboard | 65 +-- kplayer/core/NetworkManager.swift | 2 + kplayer/detail/AVPlayerController.swift | 449 +++++++++++++++++++++ kplayer/detail/DetailViewController.swift | 82 ++-- kplayer/detail/VideoPlayerController.swift | 70 +++- kplayer/master/MasterViewController.swift | 50 ++- kplayer/video/KBMPlayer.swift | 59 +++ 9 files changed, 687 insertions(+), 118 deletions(-) create mode 100644 kplayer/detail/AVPlayerController.swift create mode 100644 kplayer/video/KBMPlayer.swift diff --git a/Podfile b/Podfile index 5ee1339..7fa8194 100644 --- a/Podfile +++ b/Podfile @@ -15,6 +15,7 @@ target 'kplayer' do pod 'HanekeSwift', :git => 'https://github.com/jasonnoahchoi/HanekeSwift', :branch => 'swift3' pod 'Nimbus/Photos' pod 'Nimbus/PagingScrollView' + pod 'BMPlayer' #pod 'AFNetworking', '~>2.1' #pod 'Dollar', '6.1.0' #pod 'Cent', '6.0.3' diff --git a/kplayer.xcodeproj/project.pbxproj b/kplayer.xcodeproj/project.pbxproj index 41d5908..5ffa168 100644 --- a/kplayer.xcodeproj/project.pbxproj +++ b/kplayer.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 1C7361D2B6E0AE689FAAF4F4 /* AVPlayerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736C7FFBDAC665AE04CB65 /* AVPlayerController.swift */; }; 1C73631EACF56BABD3B2BCFB /* LayoutTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736BC4450890C45F8FBC63 /* LayoutTools.swift */; }; 1C73635138BBD2BB480A308F /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C736777456388CA571DA17B /* MediaPlayer.framework */; }; 1C73640D928DE56D35175D39 /* UploadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736260E748CF136FF37EA7 /* UploadOperation.swift */; }; @@ -22,6 +23,7 @@ 1C73688D13E5A804880C8768 /* UIImageExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736DCCE3AA9993E15F7652 /* UIImageExtension.swift */; }; 1C73691A9C7174E0C6B57267 /* stringutil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736B794396F2E50387B8F2 /* stringutil.swift */; }; 1C73693A1334A7792856FC58 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73611D226B48C24DB37535 /* MasterViewController.swift */; }; + 1C7369763AB6C73472E11B55 /* KBMPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736D981F8315FFD7D40695 /* KBMPlayer.swift */; }; 1C7369ABC44CFB530EA71FB6 /* HeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */; }; 1C736A5FA5BA53B2597F2ED7 /* Kirschkeks-256x256.png in Resources */ = {isa = PBXBuildFile; fileRef = 1C736059262A57AADE6AB761 /* Kirschkeks-256x256.png */; }; 1C736D16E81BA1FB325200E0 /* HanekeFetchOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7360744ABACC3557D05760 /* HanekeFetchOperation.swift */; }; @@ -67,6 +69,8 @@ 1C7369F53095B7A4D65679C2 /* DetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; }; 1C736B794396F2E50387B8F2 /* stringutil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = stringutil.swift; sourceTree = ""; }; 1C736BC4450890C45F8FBC63 /* LayoutTools.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutTools.swift; sourceTree = ""; }; + 1C736C7FFBDAC665AE04CB65 /* AVPlayerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AVPlayerController.swift; sourceTree = ""; }; + 1C736D981F8315FFD7D40695 /* KBMPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KBMPlayer.swift; sourceTree = ""; }; 1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderCell.swift; sourceTree = ""; }; 1C736DCCE3AA9993E15F7652 /* UIImageExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageExtension.swift; sourceTree = ""; }; 1C736E3BE8EC464D6F5DC8FA /* scratch.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = scratch.txt; sourceTree = ""; }; @@ -113,6 +117,7 @@ 1C736069C214E9522BB1BD97 /* ItemCell.swift */, 1C736D9BB5498E7E8F11C754 /* HeaderCell.swift */, 1C7369F53095B7A4D65679C2 /* DetailViewController.swift */, + 1C736C7FFBDAC665AE04CB65 /* AVPlayerController.swift */, ); path = detail; sourceTree = ""; @@ -160,6 +165,14 @@ path = core; sourceTree = ""; }; + 1C736F3946A38499113D351A /* video */ = { + isa = PBXGroup; + children = ( + 1C736D981F8315FFD7D40695 /* KBMPlayer.swift */, + ); + path = video; + sourceTree = ""; + }; 8052F5B3AAC2535E5C08A529 /* Pods */ = { isa = PBXGroup; children = ( @@ -214,6 +227,7 @@ 1C7364808E72BFA7575E75E1 /* master */, 1C7363B608460DED4F522D1C /* photo */, 1C736E3BE8EC464D6F5DC8FA /* scratch.txt */, + 1C736F3946A38499113D351A /* video */, ); path = kplayer; sourceTree = ""; @@ -299,6 +313,7 @@ CreatedOnToolsVersion = 6.3.1; DevelopmentTeam = G9J99WBBN9; LastSwiftMigration = 0820; + ProvisioningStyle = Automatic; }; C98AF5E81B124D6A00D196CC = { CreatedOnToolsVersion = 6.3.1; @@ -376,7 +391,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; 9901417B0A169BA87853DAAE /* [CP] Copy Pods Resources */ = { @@ -422,6 +437,8 @@ 1C73675C34BE0990D44570BE /* ItemModel.swift in Sources */, 1C73691A9C7174E0C6B57267 /* stringutil.swift in Sources */, 1C736821D6DF2237A3EABCC1 /* ViewControllerExtensions.swift in Sources */, + 1C7361D2B6E0AE689FAAF4F4 /* AVPlayerController.swift in Sources */, + 1C7369763AB6C73472E11B55 /* KBMPlayer.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -556,15 +573,17 @@ isa = XCBuildConfiguration; baseConfigurationReference = 6D522F61736592330F481B4F /* Pods-kplayer.debug.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = kplayer/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "schmickler.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 3.0; }; name = Debug; @@ -573,15 +592,17 @@ isa = XCBuildConfiguration; baseConfigurationReference = DF1DF76780D9CDC55209D1CE /* Pods-kplayer.release.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = kplayer/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "schmickler.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 3.0; }; name = Release; diff --git a/kplayer/Base.lproj/Main.storyboard b/kplayer/Base.lproj/Main.storyboard index 76b41e8..1a84ec6 100644 --- a/kplayer/Base.lproj/Main.storyboard +++ b/kplayer/Base.lproj/Main.storyboard @@ -1,11 +1,11 @@ - - + + - + @@ -14,6 +14,7 @@ + @@ -56,69 +57,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -181,6 +125,7 @@ + diff --git a/kplayer/core/NetworkManager.swift b/kplayer/core/NetworkManager.swift index e4ca12f..36dc0b7 100644 --- a/kplayer/core/NetworkManager.swift +++ b/kplayer/core/NetworkManager.swift @@ -12,6 +12,8 @@ class NetworkManager { let baseurl = "http://linkstation.local/tomcat/media" + var authenticated = false + lazy var operationQueue: OperationQueue = { var queue = OperationQueue() queue.name = "Backup queue" diff --git a/kplayer/detail/AVPlayerController.swift b/kplayer/detail/AVPlayerController.swift new file mode 100644 index 0000000..395196b --- /dev/null +++ b/kplayer/detail/AVPlayerController.swift @@ -0,0 +1,449 @@ +// +// Created by Marco Schmickler on 26.05.15. +// Copyright (c) 2015 Marco Schmickler. All rights reserved. +// + +import Foundation +import UIKit +import Haneke +import BMPlayer +import AVFoundation + +protocol ItemController { + func setCurrentItem(item: MediaItem) + func setCompletionHandler(handler: @escaping ((Void) -> Void)) +} + +class AVPlayerController: UIViewController, ItemController { + var player = KBMPlayer() + var currentItem: MediaItem? + + var completionHandler: ((Void) -> Void)? + + var buttons = Dictionary() + + var barbutton: UIBarButtonItem? + var speedButton: UIBarButtonItem? + var zoomButton: UIBarButtonItem? + var aspectButton: UIBarButtonItem? + var playButton: UIBarButtonItem? + var backButton: UIBarButtonItem? + var reviewButton: UIBarButtonItem? + + let speedOptions = [ 0.25, 0.5, 0.75, 1.0, 2.0 ] + var speedOption = 3 + + var aspect = 1 + + let zoomOptions = [ 1, 1.25, 1.5, 2.0 ] + var zoomOption = 1 + + var thumbnailTime: TimeInterval = 0.0 + + var edit = true + var allowEdit = true + var index = 0 + + override func viewDidLoad() { + super.viewDidLoad() + + barbutton = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(AVPlayerController.captureThumbnail)); + navigationItem.rightBarButtonItems = [barbutton!] + + backButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(AVPlayerController.back(_:))) + speedButton = UIBarButtonItem(title:"1.0", style:UIBarButtonItemStyle.plain, target: self, action: #selector(AVPlayerController.speed(_:))) + zoomButton = UIBarButtonItem(title:"1.0", style:UIBarButtonItemStyle.plain, target: self, action: #selector(AVPlayerController.zoom(_:))) + aspectButton = UIBarButtonItem(title:"1", style:UIBarButtonItemStyle.plain, target: self, action: #selector(AVPlayerController.aspect(_:))) + playButton = UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(AVPlayerController.startstop(_:))) + reviewButton = UIBarButtonItem(title:"Edit ", style:UIBarButtonItemStyle.plain, target: self, action: #selector(AVPlayerController.doEdit(_:))) + + navigationItem.leftBarButtonItems = [backButton!, speedButton!, playButton!, zoomButton!, aspectButton!, reviewButton!] + + view.addSubview(player) + player.snp.makeConstraints { (make) in + make.top.equalTo(self.view).offset(100) + make.left.right.equalTo(self.view) + // Note here, the aspect ratio 16:9 priority is lower than 1000 on the line, because the 4S iPhone aspect ratio is not 16:9 + make.height.equalTo(player.snp.width).multipliedBy(9.0/16.0).priority(750) + } +// Back button event + player.backBlock = { (b) in + let _ = self.navigationController?.popViewController(animated: true) + } + + if let c = currentItem, let url = c.playerURL { + print(url) + play(url as URL) + + update() + } + } + + func setCurrentItem(item: MediaItem) { + currentItem = item + } + + func setCompletionHandler(handler: @escaping ((Void) -> Void)) { + completionHandler = handler + } + + func doEdit(_ sender: AnyObject) { + if (!allowEdit) { + return + } + + if (edit) { + edit = false + reviewButton!.tintColor = UIColor.blue + } + else { + edit = true + reviewButton!.tintColor = UIColor.yellow + } + } + + func startstop(_ sender: AnyObject) { + if player.isPlaying { + player.pause() + } + else { + player.play() + } +// if moviePlayer!.playbackState == MPMoviePlaybackState.playing { +// moviePlayer!.pause() +// } +// else { +// moviePlayer!.play() +// +// Timer.scheduledTimer(timeInterval: 0.5, +// target: self, +// selector: #selector(resumePlay), +// userInfo: nil, +// repeats: false) +// } +print("play") + } + + func zoom(_ sender: AnyObject) { + zoomOption += 1 + if zoomOption >= zoomOptions.count { + zoomOption = 0 + } + let zoom = Float(zoomOptions[zoomOption]) + player.playerLayer!.layer.transform = CATransform3DMakeScale(CGFloat(zoom), CGFloat(zoom), 1.0) + + player.zoom = zoom + + zoomButton!.title = "\(zoom)" + print("zoom \(zoom)") + } + + func aspect(_ sender: AnyObject) { + aspect += 1 + if aspect > 3 { + aspect = 1 + } + switch aspect { + case 1: + player.aspectx = 1.0 + player.aspecty = 1.0 + case 2: + player.aspectx = 0.85 + player.aspecty = 1.0 + case 3: + player.aspectx = 1.0 + player.aspecty = 0.85 + default: + print("aspect") + } + player.verticalMoved(0) + + aspectButton!.title = "\(aspect)" + } + + func speed(_ sender: AnyObject) { + speedOption += 1 + if speedOption >= speedOptions.count { + speedOption = 0 + } + let speed = Float(speedOptions[speedOption]) + player.playerLayer!.player!.rate = speed + + speedButton!.title = "\(speed)" + print("speed \(speed)") + } + + @IBAction func back(_ sender: AnyObject) { + player.playerLayer?.pause() + player.playerLayer?.prepareToDeinit() + + completionHandler!() + } + + func play(_ url: URL) { + let asset = BMPlayerResource(url: url) + + player.setVideo(resource: asset) + player.playerLayer!.player!.automaticallyWaitsToMinimizeStalling = false + if let item = player.playerLayer?.playerItem { + item.canUseNetworkResourcesForLiveStreamingWhilePaused = true + item.preferredForwardBufferDuration = 1 + } + + } + + func playerItemDidReachEnd(_ note: Notification) { +print("finish") + // Timer.scheduledTimer(timeInterval: 0.6, target: self, selector: #selector(update), userInfo: nil, repeats: false) + } + + + func addItemButton(_ newItem: MediaItem) { + let frame = CGRect(x: 0, y: 0, width: 66.0, height: 44.0); + + let button = UIButton(frame: frame) + button.showsTouchWhenHighlighted = true + button.addTarget(self, action: #selector(thumbnailClicked(_:)), for: .touchDown) + + if newItem.image != nil { + let icon = newItem.image!.scaleToSize(66.0, height: 44.0) + + button.setBackgroundImage(icon, for: UIControlState()); + } else { + if newItem.thumbUrl != nil { + let URL = Foundation.URL(string: newItem.thumbUrlAbsolute)! + + Shared.imageCache.fetch(URL: URL).onSuccess { + i in + let icon = i.scaleToSize(66.0, height: 44.0) + button.setBackgroundImage(icon, for: UIControlState.normal); + } + } + } + + let barbutton = UIBarButtonItem(customView: button); + + if navigationItem.rightBarButtonItems == nil { + navigationItem.rightBarButtonItems = [] + } + navigationItem.rightBarButtonItems!.append(barbutton) + + buttons[button] = newItem + } + + func thumbnailClicked(_ source: UIButton) { + + player.seek(buttons[source]!.time!) + // moviePlayer!.currentPlaybackRate = Float(speedOptions[speedOption]) + + // print("goto \(buttons[source]!.time!) is \(moviePlayer!.currentPlaybackTime)") + } + + func update() { + + reviewButton!.title = currentItem!.name + + if currentItem!.type == ItemType.SNAPSHOT { + player.seek(currentItem!.time!) + currentItem = currentItem!.parent + } else { + if !currentItem!.children.isEmpty { + player.seek(currentItem!.children[0].time!) + } + else { + let duration = player.playerLayer!.playerItem!.duration.seconds + print(duration) + player.seek(duration / 2.0) + } + } + + navigationItem.rightBarButtonItems = [barbutton!] + + for c in currentItem!.children { + addItemButton(c) + } + + player.play() + + } + + func enteredFullscreen() { +// let mp = UIApplication.shared.keyWindow; +// if let moviePlayerContainer = mp!.recursiveSearchForViewWithName("MPVideoContainerView") { +// if (moviePlayerContainer.gestureRecognizers != nil) { +// return; +// } +// installGestures(moviePlayerContainer) +// } + } + + func exitedFullscreen() { +// moviePlayer!.view.removeFromSuperview(); +// moviePlayer = nil; +// NotificationCenter.default.removeObserver(self); + + } + + func installGestures(_ moviePlayer: UIView) { + let twoFingersTwoTapsGesture = UITapGestureRecognizer(target: self, action: #selector(captureThumbnail)) + twoFingersTwoTapsGesture.numberOfTapsRequired = 2 + twoFingersTwoTapsGesture.numberOfTouchesRequired = 2 + moviePlayer.addGestureRecognizer(twoFingersTwoTapsGesture) + + let sR = UISwipeGestureRecognizer(target: self, action: #selector(swipeRight)) + sR.direction = UISwipeGestureRecognizerDirection.right + sR.numberOfTouchesRequired = 1 + moviePlayer.addGestureRecognizer(sR) + + let sL = UISwipeGestureRecognizer(target: self, action: #selector(swipeLeft)) + sL.direction = UISwipeGestureRecognizerDirection.left + sL.numberOfTouchesRequired = 1 + moviePlayer.addGestureRecognizer(sL) + + let sR2 = UISwipeGestureRecognizer(target: self, action: #selector(swipeDown)) + sR2.direction = UISwipeGestureRecognizerDirection.down + sR2.numberOfTouchesRequired = 1 + moviePlayer.addGestureRecognizer(sR2) + + let sR3 = UISwipeGestureRecognizer(target: self, action: #selector(swipeUp)) + sR3.direction = UISwipeGestureRecognizerDirection.up + sR3.numberOfTouchesRequired = 1 + moviePlayer.addGestureRecognizer(sR3) + + } + + func swipeUp() { + print("u") + print("Type: \(currentItem!.type) Count: \(currentItem!.children.count) Index: \(index) Current: \(currentItem!.index)") + + if !edit && (currentItem!.children.isEmpty || !(index < currentItem!.children.count - 1)) { + print ("switch") + var newIndex = currentItem!.index + 1 + + if currentItem!.parent!.children.count <= newIndex { + newIndex = 0 + } + + currentItem = currentItem!.parent!.children[newIndex] + + print("'Switched Type: \(currentItem!.type) Count: \(currentItem!.children.count) Index: \(index) Current: \(currentItem!.index)") + + index = 0 + // player.playWithURL(currentItem!.playerURL) + Timer.scheduledTimer(timeInterval: 1.2, target: self, selector: #selector(update), userInfo: nil, repeats: false) + + return + } + + if !(currentItem!.children.isEmpty) { + print ("switch internal") + if index < currentItem!.children.count - 1 { + index+=1; + } else { + index = 0; + } + let child = currentItem!.children[index] + // player.currentPlaybackTime = child.time! + // player.currentPlaybackRate = Float(speedOptions[speedOption]) + } + + } + + func swipeRight() { +// print("r") +// player.currentPlaybackRate = Float(0.0) +// player.currentPlaybackTime = player.currentPlaybackTime - 30.0 +// Timer.scheduledTimer(timeInterval: 0.9, +// target: self, +// selector: #selector(resumePlay), +// userInfo: nil, +// repeats: false) + + + } + + func swipeDown() { + print("d") + if !edit { + var newIndex = currentItem!.index - 1 + + if newIndex < 0 { + newIndex = 0 + } + + currentItem = currentItem!.parent!.children[newIndex] + index = 0; + + // player.contentURL = currentItem!.playerURL + player.play() + Timer.scheduledTimer(timeInterval: 1.2, target: self, selector: #selector(update), userInfo: nil, repeats: false) + + return + } + // player.currentPlaybackTime = player.currentPlaybackTime + 10.0 + Timer.scheduledTimer(timeInterval: 0.9, + target: self, + selector: #selector(resumePlay), + userInfo: nil, + repeats: false) + + + } + + func swipeLeft() { + print("l") +// player.currentPlaybackRate = Float(0.0) +// player.currentPlaybackTime = player.currentPlaybackTime + 30.0 + Timer.scheduledTimer(timeInterval: 0.9, + target: self, + selector: #selector(resumePlay), + userInfo: nil, + repeats: false) + + + } + + @objc func resumePlay() { +// player.currentPlaybackRate = Float(speedOptions[speedOption]) + print("resumePlay") + } + + func captureThumbnail() { + if edit { + let asset = player.playerLayer!.playerItem!.asset + + do { + let imgGenerator = AVAssetImageGenerator(asset: asset) + imgGenerator.appliesPreferredTrackTransform = true + let time = player.playerLayer!.playerItem!.currentTime() + let cgImage = try imgGenerator.copyCGImage(at: time, actualTime: nil) + let thumbnail = UIImage(cgImage: cgImage) + + showThumbnail(thumbnail: thumbnail, time: time) + + } catch let error { + print("*** Error generating thumbnail: \(error.localizedDescription)") + } + // thumbnailTime = player.currentPlaybackTime + print("tap \(thumbnailTime)") +// moviePlayer!.requestThumbnailImages(atTimes: [thumbnailTime], +// timeOption: MPMovieTimeOption.exact); + } + else { + NetworkManager.sharedInstance.favItem(currentItem!) + } + } + + func showThumbnail(thumbnail: UIImage, time: CMTime) { + let newItem = MediaItem(name: currentItem!.name, path: currentItem!.path, root: currentItem!.root, type: ItemType.SNAPSHOT) + newItem.image = thumbnail + newItem.time = time.seconds + newItem.parent = currentItem! + currentItem!.children.append(newItem) + + print(newItem.time) + + addItemButton(newItem) + } + +} diff --git a/kplayer/detail/DetailViewController.swift b/kplayer/detail/DetailViewController.swift index d27bdc3..986a836 100644 --- a/kplayer/detail/DetailViewController.swift +++ b/kplayer/detail/DetailViewController.swift @@ -16,6 +16,8 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout var showFavoritesOnly = false var collectionView: UICollectionView! + var videoplayer = false + var currentItem: MediaItem? var defaultItemSize = CGSize(width: (15 * 16) - 6, height: 15 * 9) @@ -72,12 +74,17 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout let overviewButton = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(overview)); let favButton = UIBarButtonItem(barButtonSystemItem: .bookmarks, target: self, action: #selector(favorites)); - navigationItem.rightBarButtonItems = [favButton, overviewButton] + let vidButton = UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(vplayer)); + navigationItem.rightBarButtonItems = [vidButton, favButton, overviewButton] if detailItem != nil { print("Details \(detailItem!.children)") } } + func vplayer() { + videoplayer = !videoplayer + } + func favorites() { showFavoritesOnly = !showFavoritesOnly collectionView.reloadData() @@ -266,7 +273,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout } if items.type == ItemType.VIDEO || items.type == ItemType.SNAPSHOT { - performSegue(withIdentifier: "showVideo", sender: self) + showVideo() } else { let len = items.root.characters.count @@ -313,49 +320,56 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout } } } - - let pc = MediaPhotoController() - - pc.items = im - pc.completionHandler = { - self.dismiss(animated: true, completion: nil); - } - let navController = UINavigationController(rootViewController: pc) // Creating a navigation controller with pc at the root of the navigation stack. - - self.present(navController, animated: false, completion: nil) + self.showPhotos(im) } } } } } + func showPhotos(_ im: [MediaItem]) { + let pc = MediaPhotoController() - // MARK: - Segues + pc.items = im + pc.completionHandler = { + self.dismiss(animated: true, completion: nil); + } + let navController = UINavigationController(rootViewController: pc) // Creating a navigation controller with pc at the root of the navigation stack. - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - if segue.identifier == "showVideo" { - let nc = segue.destination as! UINavigationController - let controller = nc.topViewController as! VideoPlayerController - controller.currentItem = self.currentItem -// controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem() - controller.navigationItem.leftItemsSupplementBackButton = true - nc.navigationBar.barTintColor = UIColor.black + self.present(navController, animated: false, completion: nil) + } - controller.completionHandler = { - () in - self.collectionView.reloadData() - self.collectionView.collectionViewLayout.invalidateLayout() + func showVideo() { +// performSegue(withIdentifier: "showVideo", sender: self) + var pc: ItemController? - if let ci = self.currentItem { - if ci.type == ItemType.SNAPSHOT { - self.currentItem = ci.parent - } - } - if let ci = self.currentItem { - NetworkManager.sharedInstance.saveItem(ci) + if videoplayer { + pc = AVPlayerController() + } + else { + pc = VideoPlayerController() + } + + pc!.setCurrentItem(item: self.currentItem!) + pc!.setCompletionHandler(handler: { + self.collectionView.reloadData() + self.collectionView.collectionViewLayout.invalidateLayout() + + if let ci = self.currentItem { + if ci.type == ItemType.SNAPSHOT { + self.currentItem = ci.parent } - self.dismiss(animated: true, completion: nil); } - } + if let ci = self.currentItem { + NetworkManager.sharedInstance.saveItem(ci) + } + self.dismiss(animated: true, completion: nil); + }) + let navController = UINavigationController(rootViewController: (pc! as! UIViewController)) + navController.navigationBar.barTintColor = UIColor.black + (pc! as! UIViewController).navigationItem.leftItemsSupplementBackButton = true + + self.present(navController, animated: false, completion: nil) + } } diff --git a/kplayer/detail/VideoPlayerController.swift b/kplayer/detail/VideoPlayerController.swift index 2521fe7..cbc3a68 100644 --- a/kplayer/detail/VideoPlayerController.swift +++ b/kplayer/detail/VideoPlayerController.swift @@ -9,7 +9,7 @@ import MediaPlayer import ALMoviePlayerController import Haneke -class VideoPlayerController: UIViewController { +class VideoPlayerController: UIViewController, ItemController { var moviePlayer: ALMoviePlayerController? var currentItem: MediaItem? @@ -23,8 +23,8 @@ class VideoPlayerController: UIViewController { var backButton: UIBarButtonItem? var reviewButton: UIBarButtonItem? - let speedOptions = [ 0.25, 0.5, 1.0, 2.0 ] - var speedOption = 2 + let speedOptions = [ 0.25, 0.5, 0.75, 1.0, 2.0 ] + var speedOption = 3 var thumbnailTime: TimeInterval = 0.0 @@ -32,6 +32,10 @@ class VideoPlayerController: UIViewController { var allowEdit = true var index = 0 + func setCompletionHandler(handler: @escaping ((Void) -> Void)) { + completionHandler = handler + } + override func viewDidLoad() { super.viewDidLoad() @@ -51,6 +55,10 @@ class VideoPlayerController: UIViewController { } } + func setCurrentItem(item: MediaItem) { + currentItem = item + } + func doEdit(_ sender: AnyObject) { if (!allowEdit) { return @@ -72,14 +80,19 @@ class VideoPlayerController: UIViewController { } else { moviePlayer!.play() - moviePlayer!.currentPlaybackRate = Float(speedOptions[speedOption]) + + Timer.scheduledTimer(timeInterval: 0.5, + target: self, + selector: #selector(resumePlay), + userInfo: nil, + repeats: false) } print("play") } func speed(_ sender: AnyObject) { speedOption += 1 - if speedOption > 3 { + if speedOption >= speedOptions.count { speedOption = 0 } @@ -90,9 +103,6 @@ print("play") } @IBAction func back(_ sender: AnyObject) { - if let player = self.moviePlayer { - player.stop() - } completionHandler!() } @@ -108,6 +118,7 @@ print("play") NotificationCenter.default.addObserver(self, selector: #selector(VideoPlayerController.exitedFullscreen), name: NSNotification.Name.MPMoviePlayerDidExitFullscreen, object: nil); NotificationCenter.default.addObserver(self, selector: #selector(VideoPlayerController.enteredFullscreen), name: NSNotification.Name.MPMoviePlayerDidEnterFullscreen, object: nil); NotificationCenter.default.addObserver(self, selector: #selector(VideoPlayerController.showThumbnail(_:)), name: NSNotification.Name.MPMoviePlayerThumbnailImageRequestDidFinish, object: nil); + NotificationCenter.default.addObserver(self, selector: #selector(VideoPlayerController.playerItemDidReachEnd(_:)), name: NSNotification.Name.MPMoviePlayerLoadStateDidChange, object: nil); player.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height) player.view.sizeToFit() @@ -123,6 +134,11 @@ print("play") } } + func playerItemDidReachEnd(_ note: Notification) { +print("finish") + // Timer.scheduledTimer(timeInterval: 0.6, target: self, selector: #selector(update), userInfo: nil, repeats: false) + } + func showThumbnail(_ note: Notification) { let userInfo = note.userInfo! as NSDictionary let thumbnail = userInfo.object(forKey: MPMoviePlayerThumbnailImageKey) as! UIImage @@ -183,11 +199,11 @@ print("play") func update() { if let player = self.moviePlayer { - if !(player.duration > 0.0) { - print("again") - Timer.scheduledTimer(timeInterval: 0.3, target: self, selector: #selector(update), userInfo: nil, repeats: false) - return - } +// if !(player.duration > 0.0) { +// print("again") +// Timer.scheduledTimer(timeInterval: 0.3, target: self, selector: #selector(update), userInfo: nil, repeats: false) +// return +// } reviewButton!.title = currentItem!.name @@ -301,8 +317,14 @@ print("play") func swipeRight() { print("r") if let player = self.moviePlayer { + moviePlayer!.currentPlaybackRate = Float(0.0) player.currentPlaybackTime = player.currentPlaybackTime - 30.0 - moviePlayer!.currentPlaybackRate = Float(speedOptions[speedOption]) + Timer.scheduledTimer(timeInterval: 0.9, + target: self, + selector: #selector(resumePlay), + userInfo: nil, + repeats: false) + } } @@ -327,18 +349,34 @@ print("play") return } player.currentPlaybackTime = player.currentPlaybackTime + 10.0 - moviePlayer!.currentPlaybackRate = Float(speedOptions[speedOption]) + Timer.scheduledTimer(timeInterval: 0.9, + target: self, + selector: #selector(resumePlay), + userInfo: nil, + repeats: false) + } } func swipeLeft() { print("l") if let player = self.moviePlayer { + moviePlayer!.currentPlaybackRate = Float(0.0) player.currentPlaybackTime = player.currentPlaybackTime + 30.0 - moviePlayer!.currentPlaybackRate = Float(speedOptions[speedOption]) + Timer.scheduledTimer(timeInterval: 0.9, + target: self, + selector: #selector(resumePlay), + userInfo: nil, + repeats: false) + } } + @objc func resumePlay() { + moviePlayer!.currentPlaybackRate = Float(speedOptions[speedOption]) + print("resumePlay") + } + func twoFingersTwoTaps() { if edit { thumbnailTime = moviePlayer!.currentPlaybackTime diff --git a/kplayer/master/MasterViewController.swift b/kplayer/master/MasterViewController.swift index 4dd06b8..0c297bd 100644 --- a/kplayer/master/MasterViewController.swift +++ b/kplayer/master/MasterViewController.swift @@ -8,11 +8,14 @@ import UIKit import CoreData +import LocalAuthentication class MasterViewController: UITableViewController, UISearchResultsUpdating { let searchController = UISearchController(searchResultsController: nil) let model = ItemModel() + var authenticated = false + override func awakeFromNib() { super.awakeFromNib() if UIDevice.current.userInterfaceIdiom == .pad { @@ -40,10 +43,12 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating { if str.hasSuffix("*") { let txt = str[str.startIndex ..< str.characters.index(str.endIndex, offsetBy: -1)] - let neu = MediaItem(name: txt, path: model.items[0].parent!.path + "*" + txt, root: model.items[0].parent!.root, type: ItemType.DETAILS) - model.items.append(neu) - neu.parent = model.items[0].parent - self.tableView.reloadData() + if let p = model.items[0].parent { + let neu = MediaItem(name: txt, path: p.path + "*" + txt, root: p.root, type: ItemType.DETAILS) + model.items.append(neu) + neu.parent = p + self.tableView.reloadData() + } } } @@ -63,8 +68,44 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating { } } + func doAuthenticate() { + let authenticationContext = LAContext() + var error : NSError? + + guard authenticationContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else { + self.showAlert(title: "Error", message: "This device does not have a TouchID sensor.") + return; + } + + authenticationContext.evaluatePolicy( + .deviceOwnerAuthenticationWithBiometrics, + localizedReason: "Only awesome people are allowed", + reply: { [unowned self] (success, error) -> Void in + NetworkManager.sharedInstance.authenticated = success + }) + } + + func showAlert(title:String, message:String ) { + + let alertVC = UIAlertController(title: title, message: message, preferredStyle: .alert) + + let okAction = UIAlertAction(title: "Ok", style: .default, handler: nil) + alertVC.addAction(okAction) + + DispatchQueue.main.async() { () -> Void in + self.present(alertVC, animated: true, completion: nil) + } + + } + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { print("\(indexPath.row)") + + if (!NetworkManager.sharedInstance.authenticated) { + doAuthenticate() + return + } + let selectedItem = model.items[indexPath.row] if selectedItem.type == ItemType.DETAILS { @@ -73,7 +114,6 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating { // selectLoadedItem(selectedItem) } - if (selectedItem.isFolder()) { print(selectedItem.path) diff --git a/kplayer/video/KBMPlayer.swift b/kplayer/video/KBMPlayer.swift new file mode 100644 index 0000000..e2744c7 --- /dev/null +++ b/kplayer/video/KBMPlayer.swift @@ -0,0 +1,59 @@ +// +// BMPlayer.swift +// Pods +// +// Created by BrikerMan on 16/4/28. +// +// + +import UIKit +import SnapKit +import MediaPlayer +import BMPlayer + +/** + internal enum to check the pan direction + + - horizontal: horizontal + - vertical: vertical + */ +enum BMPanDirection: Int { + case horizontal = 0 + case vertical = 1 +} + +open class KBMPlayer: BMPlayer { + open var zoom = Float(1.0) + open var aspectx = Float(1.0) + open var aspecty = Float(1.0) + var xpos = 0.0 + var ypos = 0.0 + + open override func horizontalMoved(_ value: CGFloat) { + if zoom == 1.0 { + super.horizontalMoved(value) + xpos = 0.0 + ypos = 0.0 + } + else { + xpos += (Double(value) / 50.0) + } + let t = CATransform3DMakeTranslation(CGFloat(xpos), CGFloat(ypos), 0.0) + playerLayer!.layer.transform = CATransform3DScale(t, CGFloat(zoom * aspectx), CGFloat(zoom * aspecty), 1.0) + } + + + open override func verticalMoved(_ value: CGFloat) { + if zoom == 1.0 { + // super.verticalMoved(value) + xpos = 0.0 + ypos = 0.0 + } + else { + ypos += (Double(value) / 50.0) + } + let t = CATransform3DMakeTranslation(CGFloat(xpos), CGFloat(ypos), 0.0) + playerLayer!.layer.transform = CATransform3DScale(t, CGFloat(zoom * aspectx), CGFloat(zoom * aspecty), 1.0) + } + +}