Browse Source

Scrubbing

master
marcoschmickler 5 years ago
parent
commit
944b787ab9
  1. 21
      Podfile
  2. 2
      kplayer.xcodeproj/project.pbxproj
  3. 1
      kplayer/AppDelegate.swift
  4. 4
      kplayer/core/ItemModel.swift
  5. 11
      kplayer/core/NetworkManager.swift
  6. 46
      kplayer/detail/DetailViewController.swift
  7. 100
      kplayer/detail/VideoController.swift
  8. 16
      kplayer/master/MasterViewController.swift
  9. 4
      kplayer/master/NetworkDelegate.swift
  10. 99
      kplayer/video/BMPlayer.swift
  11. 9
      kplayer/video/BMPlayerControlView.swift
  12. 6
      kplayer/video/BMPlayerItem.swift
  13. 5
      kplayer/video/BMPlayerLayerView.swift
  14. 2
      kplayer/video/BMPlayerManager.swift

21
Podfile

@ -4,33 +4,14 @@ source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!
target 'kplayer' do
# pod 'Player'
#pod 'Alamofire', :git => 'https://github.com/Alamofire/Alamofire.git', :branch => 'swift-2.0'
#pod 'Alamofire', '~> 1.2'
pod 'Alamofire' #, '4.7'
#pod 'ALMoviePlayerController', '0.3.0'
#pod 'DZVideoPlayerViewController'
#pod 'SwiftyJSON'
#pod 'HanekeSwift' #, '1.2'
pod 'HanekeSwift', :git => 'https://github.com/Haneke/HanekeSwift.git'
#pod '', :git => 'https://github.com/jasonnoahchoi/HanekeSwift', :branch => 'swift3'
pod 'Nimbus/Photos'
pod 'Nimbus/PagingScrollView'
# pod 'BMPlayer' #, '0.8.6'#
# pod 'BMPlayer/CacheSupport', :git => 'https://github.com/BrikerMan/BMPlayer.git'
pod 'NVActivityIndicatorView'
pod 'SnapKit'
pod 'WebBrowser'
#pod 'AFNetworking', '~>2.1'
#pod 'Dollar', '6.1.0'
#pod 'Cent', '6.0.3'
#, '1.2.0'
# post_install do |installer|
# installer.pods_project.targets.each do |target|
# target.build_configurations.each do |config|
# config.build_settings['SWIFT_VERSION'] = '3.0'
# end
# end
# end
pod 'FileBrowser'
end

2
kplayer.xcodeproj/project.pbxproj

@ -417,6 +417,7 @@
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-kplayer/Pods-kplayer-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework",
"${BUILT_PRODUCTS_DIR}/FileBrowser/FileBrowser.framework",
"${BUILT_PRODUCTS_DIR}/HanekeSwift/Haneke.framework",
"${BUILT_PRODUCTS_DIR}/NVActivityIndicatorView/NVActivityIndicatorView.framework",
"${BUILT_PRODUCTS_DIR}/Nimbus/Nimbus.framework",
@ -426,6 +427,7 @@
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FileBrowser.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Haneke.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/NVActivityIndicatorView.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimbus.framework",

1
kplayer/AppDelegate.swift

@ -80,6 +80,7 @@ google
}
func applicationDidBecomeActive(_ application: UIApplication) {
NetworkManager.sharedInstance.alive()
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

4
kplayer/core/ItemModel.swift

@ -30,6 +30,10 @@ class ItemModel {
item.index = folder!.children.count
folder!.children.append(item)
item.parent = folder
if (item.isVideo()) {
item.parent?.type = ItemType.VIDEO
}
}
func removeAll() {

11
kplayer/core/NetworkManager.swift

@ -220,6 +220,17 @@ class NetworkManager {
}
}
func deleteLocal(selectedItem: MediaItem) {
if (selectedItem.local) {
print(selectedItem.playerURL?.absoluteString)
do {
try FileManager.default.removeItem(at: selectedItem.playerURL!)
} catch {
print(error)
}
}
}
func listDirs(_ root: String, completionHandler: @escaping ([MediaItem], Bool) -> Void) -> Void {
let len = root.count
let url = (root as NSString).replacingOccurrences(of: " ", with: "+")

46
kplayer/detail/DetailViewController.swift

@ -8,10 +8,12 @@
import UIKit
import Alamofire
import FileBrowser
protocol DetailDelegate {
func loadDetails(selectedItem: MediaItem, completionHandler: @escaping () -> Void)
func deleteThumb(selectedItem: MediaItem)
func deleteLocal(selectedItem: MediaItem)
func saveItem(selectedItem: MediaItem)
}
@ -83,12 +85,21 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
let overviewButton = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(overview));
let favButton = UIBarButtonItem(barButtonSystemItem: .bookmarks, target: self, action: #selector(favorites));
let vidButton = UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(vplayer));
navigationItem.rightBarButtonItems = [vidButton, favButton, overviewButton]
let browserButton = UIBarButtonItem(barButtonSystemItem: .organize, target: self, action: #selector(fileBrowser));
navigationItem.rightBarButtonItems = [vidButton, favButton, overviewButton,browserButton]
if detailItem != nil {
print("Details \(detailItem!.children)")
}
}
// https://github.com/marmelroy/FileBrowser
@objc func fileBrowser() {
let d = FileHelper.getDocumentsDirectory()
let fileBrowser = FileBrowser(initialPath: d, allowEditing: true)
present(fileBrowser, animated: true, completion: nil)
}
@objc func vplayer() {
videoplayer = !videoplayer
}
@ -200,6 +211,9 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
}
}
}
else if items.local {
self.delegate!.deleteLocal(selectedItem: items)
}
}))
refreshAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action: UIAlertAction!) in
@ -277,7 +291,7 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let detail: MediaItem = self.detailItem {
print(detail.toJSON())
NetworkManager.sharedInstance.saveFavDir(name: "fav.json", item: detail)
// NetworkManager.sharedInstance.saveFavDir(name: "fav.json", item: detail)
var sectionItem = detail.children[indexPath.section]
currentItem = sectionItem
@ -334,21 +348,43 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
}
func showVideo(selectedItem: MediaItem) {
var se = selectedItem
var children = detailItem!.children
if videoplayer {
if selectedItem.local {
let jfile = selectedItem.playerURL!.appendingPathExtension("json")
do {
let jsonData = try Data(contentsOf: jfile)
let items = try JSONDecoder().decode(MediaModel.self, from: jsonData)
se = MediaItem(model: items)
children = se.children
print (String(data: jsonData, encoding: .utf8))
} catch {
print(error)
}
}
}
var pc: ItemController?
pc = VideoController()
pc!.setCurrentItem(item: selectedItem)
pc!.setItems(items: detailItem!.children)
pc!.setCurrentItem(item: se)
pc!.setItems(items: children)
pc!.setCompletionHandler(handler: {
self.collectionView.reloadData()
self.collectionView.collectionViewLayout.invalidateLayout()
self.delegate!.saveItem(selectedItem: self.currentItem!)
if !se.local {
self.delegate!.saveItem(selectedItem: se)
}
self.dismiss(animated: true, completion: nil);
})
let navController = UINavigationController(rootViewController: (pc! as! UIViewController))
navController.modalPresentationStyle = .fullScreen
navController.modalPresentationCapturesStatusBarAppearance = true
navController.navigationBar.barTintColor = UIColor.black
(pc! as! UIViewController).navigationItem.leftItemsSupplementBackButton = true

100
kplayer/detail/VideoController.swift

@ -67,10 +67,11 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate {
view.addSubview(player)
player.snp.makeConstraints { (make) in
make.top.equalTo(self.view).offset(100)
// make.top.equalTo(self.view).offset(100)
make.left.right.equalTo(self.view)
make.height.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)
// make.height.equalTo(player.snp.width).multipliedBy(9.0/16.0).priority(750)
}
player.delegate = self
@ -82,12 +83,17 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate {
if let c = currentItem, let url = c.playerURL {
print(url)
play(url as URL)
player.playerLayer!.player!.volume = 0.0
update()
}
updateLoop()
}
override var prefersStatusBarHidden: Bool {
return true
}
func setItems(items: [MediaItem]) {
allItems = items
}
@ -115,23 +121,79 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate {
}
}
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)
}
}
@objc func favorite(_ sender: AnyObject) {
print("favorite")
if let c = currentSnapshot {
do {
let file = FileHelper.getDocumentsDirectory().appendingPathComponent(c.name)
VideoHelper.export(item: player.avPlayer!.currentItem!, clipStart: c.time!, clipDuration: c.length!, file: file) { url in
print(url)
if (c.loop) {
let alertController = UIAlertController(title: "Title", message: "Message", preferredStyle: .alert)
let oneAction = UIAlertAction(title: "One", style: .default) { (action) in
self.save(currentSnapshot: c, name: "1")
}
let twoAction = UIAlertAction(title: "Two", style: .default) { (action) in
self.save(currentSnapshot: c, name: "2")
}
let threeAction = UIAlertAction(title: "Three", style: .default) { (action) in
self.save(currentSnapshot: c, name: "3")
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in
}
alertController.addAction(oneAction)
alertController.addAction(twoAction)
alertController.addAction(threeAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true) {
}
} catch {
print(error)
}
// NetworkManager.sharedInstance.currentFav.children.append(currentItem!)
}
}
func save(currentSnapshot c: MediaItem, name: String) {
do {
try FileHelper.createDir(name: name)
let file = FileHelper.getDocumentsDirectory().appendingPathComponent(name).appendingPathComponent(c.name)
var dur = loopEnd - loopStart
if (dur < 0) {
return
}
VideoHelper.export(item: player.avPlayer!.currentItem!, clipStart: loopStart, clipDuration: dur, file: file) { url in
self.showAlert(title: c.name, message: "saved")
}
var s = c;
if (c.type == ItemType.SNAPSHOT && c.parent != nil) {
s = c.parent!
}
let jfile = FileHelper.getDocumentsDirectory().appendingPathComponent(name).appendingPathComponent(c.name).appendingPathExtension("json")
let json = s.toJSON()
print(json)
try json.write(to: jfile, atomically: true, encoding: .utf8)
} catch {
print(error)
}
}
@objc func startstop(_ sender: AnyObject) {
if player.isPlaying {
player.pause()
@ -169,7 +231,15 @@ print("play")
func updateLoop() {
if loopOption == 1 {
loopButton!.title = "cont"
var dur = loopEnd - loopStart
if dur > 0 {
loopButton!.title = "(\(dur))"
}
else {
loopButton!.title = "cont"
}
}
else {
loopButton!.title = "loop"
@ -187,11 +257,11 @@ print("play")
player.aspectx = 1.0
player.aspecty = 1.0
case 2:
player.aspectx = 0.85
player.aspectx = 0.9
player.aspecty = 1.0
case 3:
player.aspectx = 1.0
player.aspecty = 0.85
player.aspecty = 0.9
default:
print("aspect")
}
@ -316,9 +386,11 @@ print("finish")
player.seek(currentItem!.children[0].time!)
}
else {
let duration = player.playerLayer!.playerItem!.duration.seconds
print(duration)
player.seek(duration / 2.0)
let duration = player.playerLayer!.playerItem!.duration
if !duration.isIndefinite {
print(duration)
player.seek(duration.seconds / 2.0)
}
}
}

16
kplayer/master/MasterViewController.swift

@ -62,22 +62,6 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating {
}
}
func selectLoadedItem(_ selectedItem: MediaItem) {
switch selectedItem.type {
case ItemType.DETAILS:
performSegue(withIdentifier: "showDetail", sender: self)
case ItemType.FOLDER:
if selectedItem.children.first?.type == ItemType.VIDEO {
performSegue(withIdentifier: "showDetail", sender: self)
} else {
gotoNextFolder(selectedItem)
}
default:
print(selectedItem.type)
}
}
func doAuthenticate() {
let authenticationContext = LAContext()
var error : NSError?

4
kplayer/master/NetworkDelegate.swift

@ -143,4 +143,8 @@ class NetworkDelegate: MasterDelegate, DetailDelegate {
func saveItem(selectedItem: MediaItem) {
NetworkManager.sharedInstance.saveItem(selectedItem)
}
func deleteLocal(selectedItem: MediaItem) {
NetworkManager.sharedInstance.deleteLocal(selectedItem: selectedItem)
}
}

99
kplayer/video/BMPlayer.swift

@ -164,6 +164,11 @@ open class BMPlayer: UIView {
//Cache is playing result to improve callback performance
fileprivate var isPlayingCache: Bool? = nil
var isScrub = false
var isSeekInProgress = false
var chaseTime = CMTime.zero
var continueTime = 0.0
// MARK: - Public functions
/**
@ -198,6 +203,14 @@ open class BMPlayer: UIView {
}
}
func isFile() -> Bool {
guard resource != nil else { return false }
let asset = resource.definitions[currentDefinition]
return asset.url.isFileURL
}
/**
Play
*/
@ -280,9 +293,42 @@ open class BMPlayer: UIView {
return nil
}
// https://gist.github.com/shaps80/ac16b906938ad256e1f47b52b4809512
private func seekSmoothlyToTime(newChaseTime: CMTime) {
if CMTimeCompare(newChaseTime, chaseTime) != 0 {
chaseTime = newChaseTime
if !isSeekInProgress {
trySeekToChaseTime()
}
}
}
private func trySeekToChaseTime() {
guard playerLayer!.player?.status == .readyToPlay else { return }
actuallySeekToTime()
}
private func actuallySeekToTime() {
isSeekInProgress = true
let seekTimeInProgress = chaseTime
playerLayer!.player?.seek(to: seekTimeInProgress, toleranceBefore: CMTime.zero, toleranceAfter: CMTime.zero) { [weak self] _ in
guard let `self` = self else { return }
if CMTimeCompare(seekTimeInProgress, self.chaseTime) == 0 {
self.isSeekInProgress = false
} else {
self.trySeekToChaseTime()
}
}
}
// MARK: - Action Response
@objc open func panDirection(_ pan: UIPanGestureRecognizer) {
let resolution = 10000.0
if pan.numberOfTouches <= 1 {
// viewPan
let locationPoint = pan.location(in: self)
@ -298,9 +344,21 @@ open class BMPlayer: UIView {
let x = abs(velocityPoint.x)
let y = abs(velocityPoint.y)
if (zoom > 1.0) && (locationPoint.y < self.bounds.size.height * 1 / 8) {
self.isSkip = false
if (locationPoint.y < self.bounds.size.height * 1 / 8) {
self.isSkip = (zoom <= 1.0)
isScrub = false
if (!isPlaying) {
isScrub = true
let currentTime = CMTimeGetSeconds(playerLayer!.player!.currentTime())
continueTime = currentTime
let vel = velocityPoint.x + velocityPoint.y
var time = CMTimeMake(value: Int64((currentTime + (Float64(vel) / resolution))*10000), timescale: 10000)
seekSmoothlyToTime(newChaseTime: time)
}
} else {
isScrub = false
self.isSkip = true
self.skipAmount = 0.0
}
@ -326,11 +384,16 @@ open class BMPlayer: UIView {
}
case UIGestureRecognizer.State.changed:
if !self.isSkip {
if !self.isSkip && isPlaying {
xpos += (Double(velocityPoint.x) * 0.1);
ypos += (Double(velocityPoint.y) * 0.1);
transformLayer()
}
if (isScrub) {
let vel = velocityPoint.x + velocityPoint.y
var time = CMTimeMake(value: Int64((Float64(vel) / resolution)*10000), timescale: 10000)
seekSmoothlyToTime(newChaseTime: CMTimeAdd(chaseTime, time))
}
switch self.panDirection {
case BMPanDirection.horizontal:
self.horizontalMoved(velocityPoint.x)
@ -346,7 +409,7 @@ open class BMPlayer: UIView {
controlView.hideSeekToView()
isSliderSliding = false
if isSkip {
if isSkip && !isScrub {
print(skipAmount)
if (skipAmount > 1500) {
self.sumTime += TimeInterval(30)
@ -357,20 +420,28 @@ open class BMPlayer: UIView {
} else {
self.sumTime -= TimeInterval(30)
}
}
if isPlayToTheEnd {
isPlayToTheEnd = false
seek(self.sumTime, completion: { [weak self] in
self?.play()
})
} else {
seek(self.sumTime, completion: { [weak self] in
self?.autoPlay()
})
controlView.controlViewAnimation(isShow: true)
if isPlayToTheEnd {
isPlayToTheEnd = false
seek(self.sumTime, completion: { [weak self] in
self?.play()
})
} else {
seek(self.sumTime, completion: { [weak self] in
self?.autoPlay()
})
}
}
// sumTime
self.sumTime = 0.0
if (isScrub && !isFile()) {
seek(continueTime)
}
isScrub = false
case BMPanDirection.vertical:
self.isVolume = false
}

9
kplayer/video/BMPlayerControlView.swift

@ -519,7 +519,7 @@ open class BMPlayerControlView: UIView {
mainMaskView.addSubview(bottomMaskView)
mainMaskView.insertSubview(maskImageView, at: 0)
mainMaskView.clipsToBounds = true
mainMaskView.backgroundColor = UIColor(white: 0, alpha: 0.4 )
mainMaskView.backgroundColor = UIColor(white: 0, alpha: 0.0 )
// Top views
topMaskView.addSubview(topWrapperView)
@ -552,15 +552,16 @@ open class BMPlayerControlView: UIView {
playButton.addTarget(self, action: #selector(onButtonPressed(_:)), for: .touchUpInside)
currentTimeLabel.textColor = UIColor.white
currentTimeLabel.font = UIFont.systemFont(ofSize: 12)
currentTimeLabel.font = UIFont.systemFont(ofSize: 14)
currentTimeLabel.text = "00:00"
currentTimeLabel.textAlignment = NSTextAlignment.center
currentTimeLabel.backgroundColor = UIColor.black
totalTimeLabel.textColor = UIColor.white
totalTimeLabel.font = UIFont.systemFont(ofSize: 12)
totalTimeLabel.font = UIFont.systemFont(ofSize: 14)
totalTimeLabel.text = "00:00"
totalTimeLabel.textAlignment = NSTextAlignment.center
totalTimeLabel.backgroundColor = UIColor.black
timeSlider.maximumValue = 1.0
timeSlider.minimumValue = 0.0

6
kplayer/video/BMPlayerItem.swift

@ -61,10 +61,10 @@ open class BMPlayerResourceDefinition {
open var avURLAsset: AVURLAsset {
get {
// guard !url.isFileURL, url.pathExtension != "m3u8" else {
guard !url.isFileURL, url.pathExtension != "m3u8" else {
return AVURLAsset(url: url)
// }
// return BMPlayerManager.asset(for: self)
}
return BMPlayerManager.asset(for: self)
}
}

5
kplayer/video/BMPlayerLayerView.swift

@ -220,7 +220,7 @@ open class BMPlayerLayerView: UIView {
}
setupTimer()
if self.player?.currentItem?.status == AVPlayerItem.Status.readyToPlay {
let draggedTime = CMTime(value: Int64(secounds), timescale: 1)
let draggedTime = CMTime(value: Int64(secounds)*10000, timescale: 10000)
// self.player!.cancelPendingPrerolls()
self.playerItem!.cancelPendingSeeks()
self.player!.seek(to: draggedTime, toleranceBefore: CMTimeMake(value: 10, timescale: 1), toleranceAfter: CMTimeMake(value: 10, timescale: 1), completionHandler: { (finished) in
@ -231,7 +231,6 @@ open class BMPlayerLayerView: UIView {
}
}
// MARK: - URL
fileprivate func onSetVideoAsset() {
repeatToPlay = false
@ -398,7 +397,7 @@ open class BMPlayerLayerView: UIView {
print(item.error)
} else if player?.status == AVPlayer.Status.readyToPlay {
self.state = .buffering
if shouldSeekTo != 0 {
if shouldSeekTo > 0 {
seek(to: shouldSeekTo, completion: { [weak self] in
self?.shouldSeekTo = 0
self?.hasReadyToPlay = true

2
kplayer/video/BMPlayerManager.swift

@ -30,7 +30,7 @@ open class BMPlayerManager {
/// should auto play
open var shouldAutoPlay = true
open var topBarShowInCase = BMPlayerTopBarShowCase.always
open var topBarShowInCase = BMPlayerTopBarShowCase.none
open var animateDelayTimeInterval = TimeInterval(5)

Loading…
Cancel
Save