9 changed files with 687 additions and 118 deletions
-
1Podfile
-
27kplayer.xcodeproj/project.pbxproj
-
65kplayer/Base.lproj/Main.storyboard
-
2kplayer/core/NetworkManager.swift
-
449kplayer/detail/AVPlayerController.swift
-
54kplayer/detail/DetailViewController.swift
-
70kplayer/detail/VideoPlayerController.swift
-
46kplayer/master/MasterViewController.swift
-
59kplayer/video/KBMPlayer.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<UIButton, MediaItem>() |
||||
|
|
||||
|
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) |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -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) |
||||
|
} |
||||
|
|
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue