Skip to content

Commit 079e8db

Browse files
committed
Pod 1.0.5
1 parent d3e0c24 commit 079e8db

14 files changed

+322
-123
lines changed

Kakapos.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
Pod::Spec.new do |s|
1010
s.name = 'Kakapos'
11-
s.version = '1.0.4'
11+
s.version = '1.0.5'
1212
s.summary = 'It is a filter infused video tool that supports network and local urls, as well as album videos.'
1313

1414
# This description is used to generate tags and improve search results.

Kakapos.xcodeproj/project.pbxproj

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
87A71DB82A8B1F4E007AE2FD /* Options.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87A71DB72A8B1F4E007AE2FD /* Options.swift */; };
2323
87A71DBA2A8B2921007AE2FD /* condy_exporter_video.mov in Resources */ = {isa = PBXBuildFile; fileRef = 87A71DB92A8B2921007AE2FD /* condy_exporter_video.mov */; };
2424
87A71DC02A8B4DE2007AE2FD /* Skateboarding.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 87A71DBF2A8B4DE2007AE2FD /* Skateboarding.mp4 */; };
25+
87AAD95E2B9EAC7B00606353 /* AVAssetSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87AAD95D2B9EAC7B00606353 /* AVAssetSource.swift */; };
26+
87AAD9602B9EAEFF00606353 /* AVSourciable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87AAD95F2B9EAEFF00606353 /* AVSourciable.swift */; };
27+
87C046AF2B9FE150009AD503 /* VideoOrientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87C046AE2B9FE150009AD503 /* VideoOrientation.swift */; };
28+
87C046B12B9FE6AC009AD503 /* Kakapos.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87C046B02B9FE6AC009AD503 /* Kakapos.swift */; };
2529
87C89D5E2B90211900F5EB48 /* MovieMerger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87C89D5D2B90211900F5EB48 /* MovieMerger.swift */; };
2630
87C89D602B9030FE00F5EB48 /* AVAssetTrack+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87C89D5F2B9030FE00F5EB48 /* AVAssetTrack+Ext.swift */; };
2731
87DA16272B8DB966000EA832 /* TimeRangeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87DA16262B8DB966000EA832 /* TimeRangeType.swift */; };
@@ -46,6 +50,10 @@
4650
87A71DB72A8B1F4E007AE2FD /* Options.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Options.swift; sourceTree = "<group>"; };
4751
87A71DB92A8B2921007AE2FD /* condy_exporter_video.mov */ = {isa = PBXFileReference; lastKnownFileType = video.quicktime; path = condy_exporter_video.mov; sourceTree = "<group>"; };
4852
87A71DBF2A8B4DE2007AE2FD /* Skateboarding.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = Skateboarding.mp4; sourceTree = "<group>"; };
53+
87AAD95D2B9EAC7B00606353 /* AVAssetSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVAssetSource.swift; sourceTree = "<group>"; };
54+
87AAD95F2B9EAEFF00606353 /* AVSourciable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVSourciable.swift; sourceTree = "<group>"; };
55+
87C046AE2B9FE150009AD503 /* VideoOrientation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoOrientation.swift; sourceTree = "<group>"; };
56+
87C046B02B9FE6AC009AD503 /* Kakapos.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Kakapos.swift; sourceTree = "<group>"; };
4957
87C89D5D2B90211900F5EB48 /* MovieMerger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieMerger.swift; sourceTree = "<group>"; };
5058
87C89D5F2B9030FE00F5EB48 /* AVAssetTrack+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVAssetTrack+Ext.swift"; sourceTree = "<group>"; };
5159
87DA16262B8DB966000EA832 /* TimeRangeType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeRangeType.swift; sourceTree = "<group>"; };
@@ -90,16 +98,20 @@
9098
87244EF22A77841700875A27 /* Sources */ = {
9199
isa = PBXGroup;
92100
children = (
101+
87AAD95D2B9EAC7B00606353 /* AVAssetSource.swift */,
93102
87C89D5F2B9030FE00F5EB48 /* AVAssetTrack+Ext.swift */,
103+
87AAD95F2B9EAEFF00606353 /* AVSourciable.swift */,
94104
87244EF52A77841700875A27 /* CompositionInstruction.swift */,
95105
87244EF42A77841700875A27 /* Compositor.swift */,
96106
87244EF62A77841700875A27 /* Error.swift */,
97107
87244EF72A77841700875A27 /* Exporter.swift */,
108+
87C046B02B9FE6AC009AD503 /* Kakapos.swift */,
98109
87244F002A778C1C00875A27 /* MovieFileType.swift */,
99110
87C89D5D2B90211900F5EB48 /* MovieMerger.swift */,
100111
87A71DB72A8B1F4E007AE2FD /* Options.swift */,
101112
87244EFE2A778A0100875A27 /* Provider.swift */,
102113
87DA16262B8DB966000EA832 /* TimeRangeType.swift */,
114+
87C046AE2B9FE150009AD503 /* VideoOrientation.swift */,
103115
);
104116
path = Sources;
105117
sourceTree = "<group>";
@@ -204,14 +216,18 @@
204216
buildActionMask = 2147483647;
205217
files = (
206218
87244F012A778C1C00875A27 /* MovieFileType.swift in Sources */,
219+
87C046AF2B9FE150009AD503 /* VideoOrientation.swift in Sources */,
220+
87C046B12B9FE6AC009AD503 /* Kakapos.swift in Sources */,
207221
87C89D602B9030FE00F5EB48 /* AVAssetTrack+Ext.swift in Sources */,
208222
87244EE52A77802000875A27 /* ContentView.swift in Sources */,
223+
87AAD95E2B9EAC7B00606353 /* AVAssetSource.swift in Sources */,
209224
87244EFA2A77841700875A27 /* CompositionInstruction.swift in Sources */,
210225
87244EF92A77841700875A27 /* Compositor.swift in Sources */,
211226
87A71DB82A8B1F4E007AE2FD /* Options.swift in Sources */,
212227
87C89D5E2B90211900F5EB48 /* MovieMerger.swift in Sources */,
213228
87244EFB2A77841700875A27 /* Error.swift in Sources */,
214229
87244EE32A77802000875A27 /* KakaposExamplesApp.swift in Sources */,
230+
87AAD9602B9EAEFF00606353 /* AVSourciable.swift in Sources */,
215231
87244EFC2A77841700875A27 /* Exporter.swift in Sources */,
216232
87DA16272B8DB966000EA832 /* TimeRangeType.swift in Sources */,
217233
87244EFF2A778A0100875A27 /* Provider.swift in Sources */,

Package.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// swift-tools-version:5.0
1+
// swift-tools-version:5.3
22
//
3-
// Harbeth
3+
// Kakapos
44
//
55
// Copyright (c) 2021 Iker <https://github.com/yangKJ/Kakapos>
66
//
@@ -41,7 +41,6 @@ let package = Package(
4141
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
4242
// Targets can depend on other targets in this package, and on products in packages this package depends on.
4343
.target(name: "Kakapos", path: "Sources"),
44-
.testTarget(name: "KakaposTests", dependencies: ["Kakapos"]),
4544
],
4645
swiftLanguageVersions: [.v5]
4746
)

Sources/AVAssetSource.swift

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//
2+
// AVAssetSource.swift
3+
// KakaposExamples
4+
//
5+
// Created by Condy on 2024/3/10.
6+
//
7+
8+
import Foundation
9+
import AVFoundation
10+
11+
public class AVAssetSource: AVSourciable {
12+
public typealias Element = AVAsset
13+
public let element: AVAsset
14+
public var duration: CMTime = .zero
15+
public var isLoaded: Bool = false
16+
17+
public required init(element: AVAsset) {
18+
self.element = element
19+
}
20+
21+
public convenience init(videoURL: URL) {
22+
let urlAsset = AVURLAsset(url: videoURL, options: [AVURLAssetPreferPreciseDurationAndTimingKey: true])
23+
self.init(element: urlAsset)
24+
}
25+
26+
public func load(completion: @escaping (Result<AVAsset, Exporter.Error>) -> Void) {
27+
self.element.loadValuesAsynchronously(forKeys: ["tracks", "duration"]) { [weak self] in
28+
guard let `self` = self else {
29+
return
30+
}
31+
defer { self.isLoaded = true }
32+
var error: NSError?
33+
let tracksStatus = self.element.statusOfValue(forKey: "tracks", error: &error)
34+
if tracksStatus != .loaded {
35+
completion(.failure(Exporter.Error.toError(error)))
36+
return
37+
}
38+
39+
let durationStatus = self.element.statusOfValue(forKey: "duration", error: &error)
40+
if durationStatus != .loaded {
41+
completion(.failure(Exporter.Error.toError(error)))
42+
return
43+
}
44+
45+
if let videoTrack = self.element.tracks(withMediaType: .video).first {
46+
// Make sure source's duration not beyond video track's duration
47+
self.duration = videoTrack.timeRange.duration
48+
} else {
49+
self.duration = self.element.duration
50+
}
51+
completion(.success(self.element))
52+
}
53+
}
54+
55+
public func tracks(for type: AVMediaType) -> [AVAssetTrack] {
56+
var tracks: [AVAssetTrack] = []
57+
let group = DispatchGroup()
58+
group.enter()
59+
self.element.loadValuesAsynchronously(forKeys: ["tracks"], completionHandler: { [weak self] in
60+
guard let weakSelf = self else {
61+
group.leave()
62+
return
63+
}
64+
var error: NSError? = nil
65+
let status = weakSelf.element.statusOfValue(forKey: "tracks", error: &error)
66+
if status == .loaded {
67+
tracks = weakSelf.element.tracks(withMediaType: type)
68+
}
69+
group.leave()
70+
})
71+
group.wait()
72+
return tracks
73+
}
74+
}

Sources/AVAssetTrack+Ext.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import Foundation
99
import AVFoundation
1010

11-
extension AVAssetTrack {
11+
extension KakaposWrapper where Base: AVAssetTrack {
1212

13+
public var videoOrientation: VideoOrientation {
14+
VideoOrientation.getVideoOrientation(with: base)
15+
}
1316
}

Sources/AVSourciable.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//
2+
// AVSourciable.swift
3+
// KakaposExamples
4+
//
5+
// Created by Condy on 2024/3/10.
6+
//
7+
8+
import Foundation
9+
import AVFoundation
10+
11+
public protocol AVSourciable {
12+
associatedtype Element
13+
14+
var element: Element { get }
15+
16+
init(element: Element)
17+
18+
var duration: CMTime { get set }
19+
20+
var isLoaded: Bool { get set }
21+
22+
func load(completion: @escaping (Result<Element, Exporter.Error>) -> Void)
23+
24+
func tracks(for type: AVMediaType) -> [AVAssetTrack]
25+
}

Sources/CompositionInstruction.swift

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class CompositionInstruction: AVMutableVideoCompositionInstruction {
1212

1313
let trackID: CMPersistentTrackID
1414
let videoTrack: AVCompositionTrack
15-
let bufferCallback: PixelBufferCallback
15+
let pixelBufferCallback: PixelBufferCallback
1616
let options: [Exporter.Option: Any]
1717

1818
override var requiredSourceTrackIDs: [NSValue] {
@@ -23,18 +23,17 @@ class CompositionInstruction: AVMutableVideoCompositionInstruction {
2323

2424
override var containsTweening: Bool {
2525
get {
26-
guard options.keys.contains(where: { $0 == .VideoCompositionInstructionContainsTweening }),
27-
let value = options[.VideoCompositionInstructionContainsTweening] as? Bool else {
26+
guard let value = Exporter.Option.VideoCompositionInstructionContainsTweening.has(with: options) as? Bool else {
2827
return false
2928
}
3029
return value
3130
}
3231
}
3332

34-
init(videoTrack: AVCompositionTrack, bufferCallback: @escaping PixelBufferCallback, options: [Exporter.Option: Any]) {
33+
init(videoTrack: AVCompositionTrack, filtering: @escaping PixelBufferCallback, options: [Exporter.Option: Any]) {
3534
self.trackID = videoTrack.trackID
3635
self.videoTrack = videoTrack
37-
self.bufferCallback = bufferCallback
36+
self.pixelBufferCallback = filtering
3837
self.options = options
3938
super.init()
4039
self.setupOptions(options)
@@ -45,26 +44,23 @@ class CompositionInstruction: AVMutableVideoCompositionInstruction {
4544
}
4645

4746
private func setupOptions(_ options: [Exporter.Option: Any]) {
48-
var enablePostProcessing = true
49-
for (key, value) in options {
50-
switch (key, value) {
51-
case (.VideoCompositionInstructionEnablePostProcessing, let value as Bool):
52-
enablePostProcessing = value
53-
default:
54-
break
55-
}
56-
}
57-
self.enablePostProcessing = enablePostProcessing
47+
self.enablePostProcessing = setupEnablePostProcessing(options: options)
5848
self.layerInstructions = setupLayerInstructions(options: options)
5949
}
6050

51+
private func setupEnablePostProcessing(options: [Exporter.Option: Any]) -> Bool {
52+
if let value = Exporter.Option.VideoCompositionInstructionEnablePostProcessing.has(with: options) as? Bool {
53+
return value
54+
}
55+
return true
56+
}
57+
6158
private func setupLayerInstructions(options: [Exporter.Option: Any]) -> [AVVideoCompositionLayerInstruction] {
62-
guard options.keys.contains(where: { $0 == .VideoCompositionInstructionLayerInstructions }),
63-
let value = options[.VideoCompositionInstructionLayerInstructions] as? [AVVideoCompositionLayerInstruction] else {
64-
let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)
65-
layerInstruction.trackID = videoTrack.trackID
66-
return [layerInstruction]
59+
if let value = Exporter.Option.VideoCompositionInstructionLayerInstructions.has(with: options) as? [AVVideoCompositionLayerInstruction] {
60+
return value
6761
}
68-
return value
62+
let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)
63+
layerInstruction.trackID = videoTrack.trackID
64+
return [layerInstruction]
6965
}
7066
}

Sources/Compositor.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ class Compositor: NSObject, AVVideoCompositing {
2121
]
2222

2323
var sourcePixelBufferAttributes: [String : Any]? = [
24-
kCVPixelBufferPixelFormatTypeKey as String : kCVPixelFormatType_32BGRA,
24+
kCVPixelBufferPixelFormatTypeKey as String : [
25+
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
26+
kCVPixelFormatType_32BGRA,
27+
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
28+
],
2529
]
2630

2731
func startRequest(_ request: AVAsynchronousVideoCompositionRequest) {
@@ -33,7 +37,7 @@ class Compositor: NSObject, AVVideoCompositing {
3337
let callback = { buffer in
3438
request.finish(withComposedVideoFrame: buffer)
3539
}
36-
instruction.bufferCallback(pixels, callback)
40+
instruction.pixelBufferCallback(pixels, callback)
3741
//let buffer = instruction.bufferCallback(pixels) ?? pixels
3842
//request.finish(withComposedVideoFrame: buffer)
3943
}

Sources/Exporter.swift

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,6 @@ extension Exporter {
8282
}
8383

8484
private func setupComposition(options: [Exporter.Option: Any], filtering: @escaping PixelBufferCallback) throws -> (AVComposition, AVVideoComposition) {
85-
var videoFrameDuration = CMTimeMake(value: 1, timescale: 30)
86-
for (key, value) in options {
87-
switch (key, value) {
88-
case (.VideoCompositionFrameDuration, let value as CMTime):
89-
videoFrameDuration = value
90-
default:
91-
break
92-
}
93-
}
94-
9585
let asset = self.provider.asset
9686
let videoTracks = asset.tracks(withMediaType: .video)
9787
guard let track = videoTracks.first else {
@@ -111,15 +101,16 @@ extension Exporter {
111101
try audioCompositionTrack.insertTimeRange(timeRange, of: audio, at: .zero)
112102
}
113103

114-
let instruction = CompositionInstruction(videoTrack: videoTrack, bufferCallback: filtering, options: options)
104+
let instruction = CompositionInstruction(videoTrack: videoTrack, filtering: filtering, options: options)
115105
instruction.timeRange = timeRange
116106

117107
let videoComposition = AVMutableVideoComposition(propertiesOf: asset)
118108
videoComposition.customVideoCompositorClass = Compositor.self
119-
videoComposition.frameDuration = videoFrameDuration
109+
videoComposition.frameDuration = Exporter.Option.setupVideoFrameDuration(options: options)
120110
videoComposition.renderSize = naturalSize
121111
videoComposition.instructions = [instruction]
122-
if #available(macOS 10.14, iOS 10, *) {
112+
videoComposition.animationTool = Exporter.Option.setupAnimationTool(options: options)
113+
if #available(macOS 10.14, iOS 10, tvOS 9.0, *) {
123114
videoComposition.renderScale = Exporter.Option.setupRenderScale(options: options)
124115
}
125116

Sources/Kakapos.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//
2+
// Kakapos.swift
3+
// KakaposExamples
4+
//
5+
// Created by Condy on 2024/3/3.
6+
//
7+
8+
import Foundation
9+
import AVFoundation
10+
11+
/// Add the `kaka` prefix namespace.
12+
public struct KakaposWrapper<Base> {
13+
/// Stores the type or meta-type of any extended type.
14+
public private(set) var base: Base
15+
/// Create an instance from the provided value.
16+
public init(base: Base) {
17+
self.base = base
18+
}
19+
}
20+
21+
public protocol KakaposCompatible { }
22+
23+
extension KakaposCompatible {
24+
25+
public var kaka: KakaposWrapper<Self> {
26+
get { return KakaposWrapper(base: self) }
27+
set { }
28+
}
29+
30+
public static var kaka: KakaposWrapper<Self>.Type {
31+
KakaposWrapper<Self>.self
32+
}
33+
}
34+
35+
extension AVAssetTrack: KakaposCompatible { }

0 commit comments

Comments
 (0)