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
-
82kplayer/detail/DetailViewController.swift
-
70kplayer/detail/VideoPlayerController.swift
-
50kplayer/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