Skip to content

Commit 308d6dc

Browse files
committed
Input standardization 2 (#41)
* Upload Input Inspection and Standardization 2 Maintain handle to canonical input asset inside UploadInput If input standardized, standardized input URL is passed to UploadInfo instead of the original input URL used for initializer Note: SDK probably needs to re-export a high quality asset anyway so possibly need a bridging status Add dedicated internal initializer for MuxUpload error with unknown error code Store all upload-related options in UploadInfo Disable input standardization when running tests Add inspection logic Request local and remote assets Status -> TransportStatus Rely on input status in MuxUpload computed property getters Expose progress from internal upload state if available Track transport status inside of UploadInput Transport status start time optional Add TransportStatus docs Inspect and check video track frame rate Remove unused file Migrate tests for MuxUpload input result handler
1 parent 4a3178b commit 308d6dc

File tree

15 files changed

+662
-96
lines changed

15 files changed

+662
-96
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//
2+
// UploadInputFormatInspectionResult.swift
3+
//
4+
5+
import AVFoundation
6+
import Foundation
7+
8+
enum UploadInputFormatInspectionResult {
9+
10+
enum NonstandardInputReason {
11+
case videoCodec
12+
case audioCodec
13+
case videoGOPSize
14+
case videoFrameRate
15+
case videoResolution
16+
case videoBitrate
17+
case pixelAspectRatio
18+
case videoEditList
19+
case audioEditList
20+
case unexpectedMediaFileParameters
21+
case unsupportedPixelFormat
22+
}
23+
24+
case inspectionFailure
25+
case standard
26+
case nonstandard([NonstandardInputReason])
27+
28+
var isStandard: Bool {
29+
if case Self.standard = self {
30+
return true
31+
} else {
32+
return false
33+
}
34+
}
35+
36+
var nonstandardInputReasons: [NonstandardInputReason]? {
37+
if case Self.nonstandard(let nonstandardInputReasons) = self {
38+
return nonstandardInputReasons
39+
} else {
40+
return nil
41+
}
42+
}
43+
44+
}

Sources/MuxUploadSDK/InputInspection/UploadInputInspectionWorker.swift

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 113 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,122 @@
11
//
22
// UploadInputInspector.swift
3-
//
3+
//
44

55
import AVFoundation
6+
import CoreMedia
67
import Foundation
78

8-
class UploadInputInspector {
9-
9+
protocol UploadInputInspector {
10+
func performInspection(
11+
sourceInput: AVAsset,
12+
completionHandler: @escaping (UploadInputFormatInspectionResult) -> ()
13+
)
1014
}
1115

16+
class AVFoundationUploadInputInspector: UploadInputInspector {
17+
18+
static let shared = AVFoundationUploadInputInspector()
19+
20+
func performInspection(
21+
sourceInput: AVAsset,
22+
completionHandler: @escaping (UploadInputFormatInspectionResult) -> ()
23+
) {
24+
// TODO: Eventually load audio tracks too
25+
if #available(iOS 15, *) {
26+
sourceInput.loadTracks(
27+
withMediaType: .video
28+
) { tracks, error in
29+
if error != nil {
30+
completionHandler(.inspectionFailure)
31+
return
32+
}
33+
34+
if let tracks {
35+
self.inspect(
36+
tracks: tracks,
37+
completionHandler: completionHandler
38+
)
39+
}
40+
}
41+
} else {
42+
sourceInput.loadValuesAsynchronously(
43+
forKeys: ["tracks"]
44+
) {
45+
// Non-blocking if "tracks" is already loaded
46+
let tracks = sourceInput.tracks(
47+
withMediaType: .video
48+
)
49+
self.inspect(
50+
tracks: tracks,
51+
completionHandler: completionHandler
52+
)
53+
}
54+
}
55+
56+
}
57+
58+
func inspect(
59+
tracks: [AVAssetTrack],
60+
completionHandler: @escaping (UploadInputFormatInspectionResult) -> ()
61+
) {
62+
switch tracks.count {
63+
case 0:
64+
// Nothing to inspect, therefore nothing to standardize
65+
// declare as already standard
66+
completionHandler(.standard)
67+
case 1:
68+
if let track = tracks.first {
69+
track.loadValuesAsynchronously(
70+
forKeys: [
71+
"formatDescriptions",
72+
"nominalFrameRate"
73+
]
74+
) {
75+
guard let formatDescriptions = track.formatDescriptions as? [CMFormatDescription] else {
76+
completionHandler(.inspectionFailure)
77+
return
78+
}
79+
80+
guard let formatDescription = formatDescriptions.first else {
81+
completionHandler(.inspectionFailure)
82+
return
83+
}
84+
85+
var nonStandardReasons: [UploadInputFormatInspectionResult.NonstandardInputReason] = []
1286

87+
let videoDimensions = CMVideoFormatDescriptionGetDimensions(
88+
formatDescription
89+
)
90+
91+
if max(videoDimensions.width, videoDimensions.height) > 1920 {
92+
nonStandardReasons.append(.videoResolution)
93+
}
94+
95+
let videoCodecType = formatDescription.mediaSubType
96+
97+
let standard = CMFormatDescription.MediaSubType.h264
98+
99+
if videoCodecType != standard {
100+
nonStandardReasons.append(.videoCodec)
101+
}
102+
103+
let frameRate = track.nominalFrameRate
104+
if frameRate > 120.0 {
105+
nonStandardReasons.append(.videoFrameRate)
106+
}
107+
108+
if nonStandardReasons.isEmpty {
109+
completionHandler(.standard)
110+
} else {
111+
completionHandler(.nonstandard(nonStandardReasons))
112+
}
113+
114+
}
115+
}
116+
default:
117+
// Inspection fails for multi-video track inputs
118+
// for the time being
119+
completionHandler(.inspectionFailure)
120+
}
121+
}
122+
}

Sources/MuxUploadSDK/InternalUtilities/UploadInput.swift

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,17 @@ struct UploadInput {
1414
case started(AVAsset, UploadInfo)
1515
case underInspection(AVAsset, UploadInfo)
1616
case standardizing(AVAsset, UploadInfo)
17-
case standardizationSucceeded(AVAsset, UploadInfo)
17+
case standardizationSucceeded(
18+
source: AVAsset,
19+
standardized: AVAsset?,
20+
uploadInfo: UploadInfo
21+
)
1822
case standardizationFailed(AVAsset, UploadInfo)
1923
case awaitingUploadConfirmation(UploadInfo)
20-
case uploadInProgress(UploadInfo)
21-
case uploadPaused(UploadInfo)
22-
case uploadSucceeded(UploadInfo)
23-
case uploadFailed(UploadInfo)
24+
case uploadInProgress(UploadInfo, MuxUpload.TransportStatus)
25+
case uploadPaused(UploadInfo, MuxUpload.TransportStatus)
26+
case uploadSucceeded(UploadInfo, MuxUpload.TransportStatus)
27+
case uploadFailed(UploadInfo, MuxUpload.TransportStatus)
2428
}
2529

2630
var status: Status
@@ -35,19 +39,19 @@ struct UploadInput {
3539
return sourceAsset
3640
case .standardizing(let sourceAsset, _):
3741
return sourceAsset
38-
case .standardizationSucceeded(let sourceAsset, _):
42+
case .standardizationSucceeded(let sourceAsset, _, _):
3943
return sourceAsset
4044
case .standardizationFailed(let sourceAsset, _):
4145
return sourceAsset
4246
case .awaitingUploadConfirmation(let uploadInfo):
4347
return uploadInfo.sourceAsset()
44-
case .uploadInProgress(let uploadInfo):
48+
case .uploadInProgress(let uploadInfo, _):
4549
return uploadInfo.sourceAsset()
46-
case .uploadSucceeded(let uploadInfo):
50+
case .uploadSucceeded(let uploadInfo, _):
4751
return uploadInfo.sourceAsset()
48-
case .uploadFailed(let uploadInfo):
52+
case .uploadFailed(let uploadInfo, _):
4953
return uploadInfo.sourceAsset()
50-
case .uploadPaused(let uploadInfo):
54+
case .uploadPaused(let uploadInfo, _):
5155
return uploadInfo.sourceAsset()
5256
}
5357
}
@@ -62,22 +66,71 @@ struct UploadInput {
6266
return uploadInfo
6367
case .standardizing(_, let uploadInfo):
6468
return uploadInfo
65-
case .standardizationSucceeded(_, let uploadInfo):
69+
case .standardizationSucceeded(_, _, let uploadInfo):
6670
return uploadInfo
6771
case .standardizationFailed(_, let uploadInfo):
6872
return uploadInfo
6973
case .awaitingUploadConfirmation(let uploadInfo):
7074
return uploadInfo
71-
case .uploadInProgress(let uploadInfo):
75+
case .uploadInProgress(let uploadInfo, _):
7276
return uploadInfo
73-
case .uploadPaused(let uploadInfo):
77+
case .uploadPaused(let uploadInfo, _):
7478
return uploadInfo
75-
case .uploadSucceeded(let uploadInfo):
79+
case .uploadSucceeded(let uploadInfo, _):
7680
return uploadInfo
77-
case .uploadFailed(let uploadInfo):
81+
case .uploadFailed(let uploadInfo, _):
7882
return uploadInfo
7983
}
8084
}
85+
86+
var transportStatus: MuxUpload.TransportStatus? {
87+
switch status {
88+
case .ready:
89+
return nil
90+
case .started:
91+
return nil
92+
case .underInspection:
93+
return nil
94+
case .standardizing:
95+
return nil
96+
case .standardizationSucceeded:
97+
return nil
98+
case .standardizationFailed:
99+
return nil
100+
case .awaitingUploadConfirmation:
101+
return nil
102+
case .uploadInProgress(_, let transportStatus):
103+
return transportStatus
104+
case .uploadPaused(_, let transportStatus):
105+
return transportStatus
106+
case .uploadSucceeded(_, let transportStatus):
107+
return transportStatus
108+
case .uploadFailed(_, let transportStatus):
109+
return transportStatus
110+
}
111+
}
112+
}
113+
114+
extension UploadInput {
115+
116+
mutating func processUploadCancellation() {
117+
if case UploadInput.Status.ready = status {
118+
return
119+
}
120+
121+
status = .ready(sourceAsset, uploadInfo)
122+
}
123+
124+
mutating func processStartNetworkTransport(
125+
startingTransportStatus: MuxUpload.TransportStatus
126+
) {
127+
if case UploadInput.Status.underInspection = status {
128+
status = .uploadInProgress(uploadInfo, startingTransportStatus)
129+
} else {
130+
return
131+
}
132+
}
133+
81134
}
82135

83136
extension UploadInput.Status: Equatable {

Sources/MuxUploadSDK/PublicAPI/AVFoundation+MuxUpload/MuxUpload+AVFoundation.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ extension MuxUpload {
3030
options: options
3131
)
3232
),
33-
uploadManager: .shared
33+
uploadManager: .shared,
34+
inputInspector: .shared
3435
)
3536
}
3637

0 commit comments

Comments
 (0)