@@ -91,6 +91,14 @@ public extension Timeline
9191 {
9292 let gapTimeRangeOTIO = try gap. trimmedRangeInParent ( ) ?? gap. rangeInParent ( )
9393 let gapTimeRange = gapTimeRangeOTIO. toCMTimeRange ( )
94+
95+ guard
96+ gapTimeRange. duration != . zero
97+ else
98+ {
99+ continue
100+ }
101+
94102 compositionVideoTrack. insertEmptyTimeRange ( gapTimeRange)
95103 compositionVideoTrack. preferredTransform = . identity
96104
@@ -122,6 +130,13 @@ public extension Timeline
122130 let trackTimeRange = clipTimeMapping. source
123131 let sourceAssetTimeRange = clipTimeMapping. target
124132
133+ guard trackTimeRange. duration != . zero,
134+ sourceAssetTimeRange. duration != . zero
135+ else
136+ {
137+ continue
138+ }
139+
125140 // We attempt to re-use a track per OTIO track, but we may have CMFormatDesc inconsistencies which means insertion will fails
126141 // If so - we make a new one
127142 do
@@ -130,7 +145,7 @@ public extension Timeline
130145 }
131146 catch
132147 {
133- if let compositionVideoTrack = composition. mutableTrack ( compatibleWith : sourceAssetFirstVideoTrack ) ?? composition . addMutableTrack ( withMediaType: . video, preferredTrackID: kCMPersistentTrackID_Invalid)
148+ if let compositionVideoTrack = composition. addMutableTrack ( withMediaType: . video, preferredTrackID: kCMPersistentTrackID_Invalid)
134149 {
135150 try compositionVideoTrack. insertTimeRange ( sourceAssetTimeRange, of: sourceAssetFirstVideoTrack, at: trackTimeRange. start)
136151 }
@@ -172,7 +187,7 @@ public extension Timeline
172187 {
173188 guard
174189 let clip = child as? Clip ,
175- let ( sourceAsset, clipTimeMapping) = try clip. toAVAssetAndMapping ( baseURL: baseURL, useTimecode: useAssetTimecode, rescaleToAsset: rescaleToAsset) ,
190+ let ( sourceAsset, clipTimeMapping) = try clip. toAVAssetAndMapping ( baseURL: baseURL, trackType : . audio , useTimecode: useAssetTimecode, rescaleToAsset: rescaleToAsset) ,
176191 let sourceAssetFirstAudioTrack = try await sourceAsset. loadTracks ( withMediaType: . audio) . first,
177192 let compositionAudioTrack = compositionAudioTrack
178193 else
@@ -183,7 +198,16 @@ public extension Timeline
183198 {
184199 do
185200 {
186- let gapTimeRange = try gap. rangeInParent ( ) . toCMTimeRange ( )
201+ let gapTimeRangeOTIO = try gap. trimmedRangeInParent ( ) ?? gap. rangeInParent ( )
202+ let gapTimeRange = gapTimeRangeOTIO. toCMTimeRange ( )
203+
204+ guard
205+ gapTimeRange. duration != . zero
206+ else
207+ {
208+ continue
209+ }
210+
187211 compositionAudioTrack. insertEmptyTimeRange ( gapTimeRange)
188212
189213 let audioMixParams = AVMutableAudioMixInputParameters ( track: compositionAudioTrack)
@@ -202,6 +226,13 @@ public extension Timeline
202226 let trackTimeRange = clipTimeMapping. source
203227 let sourceAssetTimeRange = clipTimeMapping. target
204228
229+ guard trackTimeRange. duration != . zero,
230+ sourceAssetTimeRange. duration != . zero
231+ else
232+ {
233+ continue
234+ }
235+
205236 // We attempt to re-use a track per OTIO track, but we may have CMFormatDesc inconsistencies which means insertion will fails
206237 // If so - we make a new one
207238 do
@@ -210,28 +241,39 @@ public extension Timeline
210241 }
211242 catch
212243 {
213- if let compositionAudioTrack = composition. mutableTrack ( compatibleWith : sourceAssetFirstAudioTrack ) ?? composition . addMutableTrack ( withMediaType: . audio, preferredTrackID: kCMPersistentTrackID_Invalid)
244+ if let compositionAudioTrack = composition. addMutableTrack ( withMediaType: . audio, preferredTrackID: kCMPersistentTrackID_Invalid)
214245 {
215- try compositionAudioTrack. insertTimeRange ( sourceAssetTimeRange, of: sourceAssetFirstAudioTrack, at: trackTimeRange. start)
246+ do {
247+ try compositionAudioTrack. insertTimeRange ( sourceAssetTimeRange, of: sourceAssetFirstAudioTrack, at: trackTimeRange. start)
248+
249+ }
250+ catch {
251+ print ( " swallowing traack insertion error \( error) " )
252+ }
216253 }
217254 }
218255
219256 // TODO: - FIX Support Time Scaling
220257// let unscaledTrackTime = CMTimeRangeMake(start: trackTimeRange.start, duration: sourceAssetTimeRange.duration)
221258// compositionAudioTrack.scaleTimeRange(unscaledTrackTime, toDuration: trackTimeRange.duration)
222259
223- compositionAudioTrack. isEnabled = try await sourceAssetFirstAudioTrack. load ( . isEnabled)
224-
225260 // TODO: a few milliseconds fade up / fade out to avoid pops
226261 let audioMixParams = AVMutableAudioMixInputParameters ( track: compositionAudioTrack)
227262
228263 compositionAudioMixParams. append ( audioMixParams)
229264 }
230265 }
231266
267+
268+
232269 // Composition Validation
233270 for track in composition. tracks
234271 {
272+ if track. segments. isEmpty
273+ {
274+ composition. removeTrack ( track)
275+ }
276+
235277 do {
236278 try track. validateSegments ( track. segments)
237279 }
@@ -246,7 +288,8 @@ public extension Timeline
246288 // TODO: - Custom Resolution overrides?
247289 videoComposition. renderSize = largestRasterSizeFound //CGSize(width: 1920, height: 1080)
248290 videoComposition. renderScale = 1.0
249-
291+ audioMix. inputParameters = compositionAudioMixParams
292+
250293 // TODO: It seems as though our custom instructions occasionally have a minor time gap
251294 // likely due to numerical conversion precision which throws a validation error
252295 // Im not entirely sure what to do there!
@@ -261,7 +304,6 @@ public extension Timeline
261304 // Video Composition Validation
262305 try await videoComposition. isValid ( for: composition, timeRange: CMTimeRange ( start: . zero, end: composition. duration) , validationDelegate: validator)
263306
264- audioMix. inputParameters = compositionAudioMixParams
265307
266308 return ( composition: composition, videoComposition: videoComposition, audioMix: audioMix)
267309 }
0 commit comments