Browse Source

Video Composer

master
marcoschmickler 5 years ago
parent
commit
f29f735754
  1. 4
      kplayer.xcodeproj/project.pbxproj
  2. 17
      kplayer/core/MediaItem.swift
  3. 1
      kplayer/core/NetworkManager.swift
  4. 37
      kplayer/detail/DetailViewController.swift
  5. 49
      kplayer/detail/VideoController.swift
  6. 4
      kplayer/master/MasterViewController.swift
  7. 2
      kplayer/photo/PhotoController.swift
  8. 26
      kplayer/util/VideoHelper.swift
  9. 2
      kplayer/video/BMPlayer.swift
  10. 19
      kplayer/video/BMPlayerCompositionResourceDefinition.swift
  11. 2
      kplayer/video/BMPlayerItem.swift
  12. 4
      kplayer/video/BMPlayerLayerView.swift

4
kplayer.xcodeproj/project.pbxproj

@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */
1C7360C0F2A4F0214FE353BD /* FileHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7367ECBD369A2A0C94C499 /* FileHelper.swift */; };
1C7360F1D2CF83ECDD586B84 /* BMPlayerCompositionResourceDefinition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C73610B997EBA367C806C1B /* BMPlayerCompositionResourceDefinition.swift */; };
1C7361D2B6E0AE689FAAF4F4 /* VideoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736C7FFBDAC665AE04CB65 /* VideoController.swift */; };
1C7362A6FA1C5DA0B0677F1E /* readme.md in Sources */ = {isa = PBXBuildFile; fileRef = 1C736871C9B012CB704AB803 /* readme.md */; };
1C73631EACF56BABD3B2BCFB /* LayoutTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C736BC4450890C45F8FBC63 /* LayoutTools.swift */; };
@ -73,6 +74,7 @@
1C7360A94DBECA685ED8602F /* ImageLoadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageLoadOperation.swift; sourceTree = "<group>"; };
1C7360AE55EB115762C42EB9 /* BMTimeSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BMTimeSlider.swift; sourceTree = "<group>"; };
1C7360D6580FB5D09C2BBCCB /* BMPlayerManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BMPlayerManager.swift; sourceTree = "<group>"; };
1C73610B997EBA367C806C1B /* BMPlayerCompositionResourceDefinition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BMPlayerCompositionResourceDefinition.swift; sourceTree = "<group>"; };
1C73611D226B48C24DB37535 /* MasterViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MasterViewController.swift; sourceTree = "<group>"; };
1C73620D01687FB4F1811C5C /* NetworkHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkHelper.swift; sourceTree = "<group>"; };
1C736253AB7A95EA41B605B7 /* ItemModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemModel.swift; sourceTree = "<group>"; };
@ -213,6 +215,7 @@
1C7366AAB82A46086690E164 /* BMSubtitles.swift */,
1C7360AE55EB115762C42EB9 /* BMTimeSlider.swift */,
1C7360D6580FB5D09C2BBCCB /* BMPlayerManager.swift */,
1C73610B997EBA367C806C1B /* BMPlayerCompositionResourceDefinition.swift */,
);
path = video;
sourceTree = "<group>";
@ -502,6 +505,7 @@
1C7360C0F2A4F0214FE353BD /* FileHelper.swift in Sources */,
1C7366A0CFD2B55BF8C3BAF0 /* NetworkDelegate.swift in Sources */,
1C7368242038C0FF6C9631E7 /* VideoHelper.swift in Sources */,
1C7360F1D2CF83ECDD586B84 /* BMPlayerCompositionResourceDefinition.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

17
kplayer/core/MediaItem.swift

@ -136,6 +136,10 @@ class MediaItem: CustomDebugStringConvertible {
Absolute URL, unter der das Thumbnail des Items abgerufen werden kann.
*/
var thumbUrlAbsolute: String {
if thumbUrl!.starts(with: "file:") {
return thumbUrl!
}
let enc = thumbUrl!.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)!
return NetworkManager.sharedInstance.baseurl + "/service/download" + enc
}
@ -161,9 +165,20 @@ class MediaItem: CustomDebugStringConvertible {
file = file.appendingPathComponent(name)
return file
}
let enc = name.replacingOccurrences(of: " ", with: "%20")
var encoded : String
if encodedDir!.count >= 10 {
let index = encodedDir!.index(encodedDir!.startIndex, offsetBy: 10)
let s = NetworkManager.sharedInstance.vidurl + encodedDir!.substring(from:index)
encoded = encodedDir!.substring(from:index)
}
else {
encoded = encodedDir!
}
let s = NetworkManager.sharedInstance.vidurl + encoded
if s.endsWith("/") {
return URL(string: s + enc)

1
kplayer/core/NetworkManager.swift

@ -58,6 +58,7 @@ class NetworkManager {
let m = MediaItem(name: fileURL.lastPathComponent, path: path, root: "fav", type: ItemType.VIDEO)
m.local = true
m.thumbUrl = fileURL.appendingPathExtension("jpg").absoluteString
res.append(m)

37
kplayer/detail/DetailViewController.swift

@ -9,6 +9,7 @@
import UIKit
import Alamofire
import FileBrowser
import AVFoundation
protocol DetailDelegate {
func loadDetails(selectedItem: MediaItem, completionHandler: @escaping () -> Void)
@ -112,11 +113,16 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
}
@objc func overview() {
let pc = MediaPhotoController()
var i = [MediaItem]()
if let d = detailItem {
if (d.local) {
showComposition(d)
return
}
let pc = MediaPhotoController()
for it in d.children {
if it.children.count > 1 || !showFavoritesOnly {
for c in it.children {
@ -136,6 +142,35 @@ class DetailViewController: UIViewController, UICollectionViewDelegateFlowLayout
}
}
private func showComposition(_ item: MediaItem) {
var assets = [URL]()
for d in item.children {
assets.append(d.playerURL!)
}
let vc = VideoController()
let item = MediaItem(name: item.name, path: item.path, root: item.root, type: ItemType.VIDEO)
vc.setItems(items: [item])
vc.setCurrentItem(item: item)
vc.urls = assets
vc.setCompletionHandler(handler: {
self.dismiss(animated: true, completion: nil);
})
let navController = UINavigationController(rootViewController: (vc as UIViewController))
navController.modalPresentationStyle = .fullScreen
navController.modalPresentationCapturesStatusBarAppearance = true
navController.navigationBar.barTintColor = UIColor.black
(vc as UIViewController).navigationItem.leftItemsSupplementBackButton = true
self.present(navController, animated: false, completion: nil)
}
@objc func refreshItems(_ notification: Notification) {
if notification.object == nil {

49
kplayer/detail/VideoController.swift

@ -49,6 +49,8 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate {
var allowEdit = true
var index = 0
var urls : [URL]?
override func viewDidLoad() {
super.viewDidLoad()
@ -83,7 +85,7 @@ 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
// player.playerLayer!.player!.volume = 0.0
update()
}
@ -179,16 +181,23 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate {
VideoHelper.export(item: player.avPlayer!.currentItem!, clipStart: loopStart, clipDuration: dur, file: file) { url in
self.showAlert(title: c.name, message: "saved")
}
var s = c;
var s : MediaModel;
if (c.type == ItemType.SNAPSHOT && c.parent != nil) {
s = c.parent!
s = c.parent!.toMediaModel()
let local = try Data(contentsOf: URL(string: c.thumbUrlAbsolute)!)
let tfile = FileHelper.getDocumentsDirectory().appendingPathComponent(name).appendingPathComponent(s.name).appendingPathExtension("jpg")
try local.write(to: tfile)
}
else {
s = c.toMediaModel()
}
let jfile = FileHelper.getDocumentsDirectory().appendingPathComponent(name).appendingPathComponent(c.name).appendingPathExtension("json")
let json = s.toJSON()
let jfile = FileHelper.getDocumentsDirectory().appendingPathComponent(name).appendingPathComponent(s.name).appendingPathExtension("json")
let jsonEncoder = JSONEncoder()
let jsonData = try! jsonEncoder.encode(s)
let json = String(data: jsonData, encoding: String.Encoding.utf8)
print(json)
try json.write(to: jfile, atomically: true, encoding: .utf8)
try json!.write(to: jfile, atomically: true, encoding: .utf8)
} catch {
print(error)
}
@ -199,20 +208,14 @@ class VideoController: UIViewController, ItemController, BMPlayerDelegate {
player.pause()
}
else {
if player.isPlayToTheEnd {
player.seek(0, completion: {[weak self] in
self!.player.play()
})
player.isPlayToTheEnd = false
}
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")
}
@ -300,6 +303,13 @@ print("play")
}
for i in allItems {
if let a = urls {
let r = BMPlayerCompositionResourceDefinition(url: i.playerURL!, definition: i.name)
r.assets = a
def.append(r)
}
else {
let r = BMPlayerResourceDefinition(url: i.playerURL!, definition: i.name);
def.append(r)
if (url == i.playerURL) {
@ -307,6 +317,7 @@ print("play")
}
count += 1
}
}
let asset = BMPlayerResource(name: "video", definitions: def)
// let asset = BMPlayerResource(url: url)

4
kplayer/master/MasterViewController.swift

@ -302,6 +302,10 @@ class MasterViewController: UITableViewController, UISearchResultsUpdating, UITa
do {
try FileManager.default.moveItem(at: at, to: to)
try FileManager.default.moveItem(at: at.appendingPathExtension("json"), to: to.appendingPathExtension("json"))
do {
try FileManager.default.moveItem(at: at.appendingPathExtension("jpg"), to: to.appendingPathExtension("jpg"))
} catch {
}
let weiter:Weiter = {
(g) in

2
kplayer/photo/PhotoController.swift

@ -47,9 +47,11 @@ class MediaPhotoController: NIToolbarPhotoViewController, NIPhotoAlbumScrollView
override func setChromeTitle() {
let idx = photoAlbumView.centerPageIndex
if (items.count > idx) {
let selected = items[idx]
title = "(\(idx + 1) / \(photoAlbumView.numberOfPages)) \(selected.path) \(selected.name)"
}
}
override func viewDidLoad() {
super.viewDidLoad()

26
kplayer/util/VideoHelper.swift

@ -19,8 +19,8 @@ class VideoHelper {
let sourceVideoTrack = item.asset.tracks(withMediaType: AVMediaType.video).first!
let sourceAudioTrack = item.asset.tracks(withMediaType: AVMediaType.audio).first!
let start = CMTime(seconds: clipStart, preferredTimescale: 1);
let dur = CMTime(seconds: clipDuration, preferredTimescale: 100);
let start = CMTime(seconds: clipStart, preferredTimescale: 10000);
let dur = CMTime(seconds: clipDuration, preferredTimescale: 10000);
do {
try compositionVideoTrack!.insertTimeRange(CMTimeRangeMake(start: start, duration: dur), of: sourceVideoTrack, at: start)
@ -61,4 +61,26 @@ class VideoHelper {
completion(file)
}
}
static func combine(urls: [URL]) -> AVAsset {
let c = AVMutableComposition()
let tr = c.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
var currentTime = CMTime.zero
do {
for url in urls {
var urlAsset = AVURLAsset(url: url)
let range = CMTimeRangeMake(start: CMTime.zero, duration: urlAsset.duration)
try tr!.insertTimeRange(range, of: urlAsset.tracks(withMediaType: AVMediaType.video)[0], at: currentTime)
currentTime = CMTimeAdd(currentTime, urlAsset.duration)
}
} catch {
print(error)
}
return c
}
}

2
kplayer/video/BMPlayer.swift

@ -157,7 +157,7 @@ open class BMPlayer: UIView {
fileprivate var isMaskShowing = false
fileprivate var isSlowed = false
fileprivate var isMirrored = false
fileprivate var isPlayToTheEnd = false
var isPlayToTheEnd = false
//
fileprivate var aspectRatio: BMPlayerAspectRatio = .default

19
kplayer/video/BMPlayerCompositionResourceDefinition.swift

@ -0,0 +1,19 @@
//
// Created by Marco Schmickler on 05.05.21.
// Copyright (c) 2021 Marco Schmickler. All rights reserved.
//
import Foundation
import AVFoundation
class BMPlayerCompositionResourceDefinition : BMPlayerResourceDefinition {
var assets: [URL]?
override var avURLAsset: AVAsset {
get {
let asset = VideoHelper.combine(urls: assets!)
return asset
}
}
}

2
kplayer/video/BMPlayerItem.swift

@ -59,7 +59,7 @@ open class BMPlayerResourceDefinition {
/// An instance of NSDictionary that contains keys for specifying options for the initialization of the AVURLAsset. See AVURLAssetPreferPreciseDurationAndTimingKey and AVURLAssetReferenceRestrictionsKey above.
public var options: [String : Any]?
open var avURLAsset: AVURLAsset {
open var avURLAsset: AVAsset {
get {
guard !url.isFileURL, url.pathExtension != "m3u8" else {
return AVURLAsset(url: url)

4
kplayer/video/BMPlayerLayerView.swift

@ -96,7 +96,7 @@ open class BMPlayerLayerView: UIView {
///
var timer: Timer?
fileprivate var urlAsset: AVURLAsset?
fileprivate var urlAsset: AVAsset?
fileprivate var lastPlayerItem: AVPlayerItem?
/// playerLayer
@ -137,7 +137,7 @@ open class BMPlayerLayerView: UIView {
playAsset(asset: asset)
}
open func playAsset(asset: AVURLAsset) {
open func playAsset(asset: AVAsset) {
urlAsset = asset
onSetVideoAsset()
play()

Loading…
Cancel
Save