I have this theory that AVMutableComposition does not work without a video track. I'm trying to change the speed of an audio track (mp3) using the
func scaleTimeRange(_ timeRange: CMTimeRange, toDuration duration: CMTime)
function. Below is the code I have so far,
static func changeSpeed(audio: AVURLAsset, factor: Float64, outputURL: URL, completionHandler:@escaping ((_ success: Bool) -> Void)) {
print(#function + " started: " + Date().description)
guard factor > 0 else {
DispatchQueue.main.async {
print(#function + ": Speed factor must be a positive value")
completionHandler(false)
}
return
}
guard let assetAudioTrack = audio.tracks(withMediaType: .audio).first else {
DispatchQueue.main.async {
print(#function + ": No asset track found")
completionHandler(false)
}
return
}
let composition = AVMutableComposition()
let audioTrack = composition.addMutableTrack(
withMediaType: AVMediaType.audio,
preferredTrackID: kCMPersistentTrackID_Invalid
)
do {
try audioTrack?.insertTimeRange(
CMTimeRangeMake(start: CMTime.zero, duration: audio.duration),
of: assetAudioTrack,
at: CMTime.zero
)
} catch {
DispatchQueue.main.async {
print(#function + ": Failed to add " + audio.url.absoluteString)
completionHandler(false)
}
return
}
let exporter = AVAssetExportSession(
asset: composition,
presetName: AVAssetExportPresetAppleM4A
)
exporter?.outputURL = outputURL
exporter?.outputFileType = AVFileType.m4a
audioTrack?.scaleTimeRange(CMTimeRange.init(start: CMTime.zero, end: audio.duration), toDuration: CMTimeMultiplyByFloat64(audio.duration, multiplier: 1/factor))
exporter?.exportAsynchronously {
if exporter?.status == .completed {
DispatchQueue.main.async {
completionHandler(true)
print(#function + " ended: " + Date().description)
}
} else {
DispatchQueue.main.async {
completionHandler(false)
print(#function + " ended with error: " + Date().description)
if let errorString = exporter?.error.debugDescription {
print(errorString)
}
}
}
}
}
Above code fails with or without the *scaleTimeRange* function. That is why I'm thinking that AVMutableComposition will work only when there are video tracks.
I did play around a little by changing the preset and output file type. Below are my observations,
preset: AVAssetExportPresetAppleM4A
file type: AVFileType.m4a
Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could
not be completed" UserInfo={NSLocalizedFailureReason=An unknown error
occurred (-12780), NSLocalizedDescription=The operation could not be
completed, NSUnderlyingError=0x281164960 {Error
Domain=NSOSStatusErrorDomain Code=-12780 "(null)"}}
preset: AVAssetExportPresetPassthrough
file type: AVFileType.m4a
Error Domain=AVFoundationErrorDomain Code=-11822 "Cannot Open"
UserInfo={NSLocalizedFailureReason=This media format is not
supported., NSLocalizedDescription=Cannot Open,
NSUnderlyingError=0x28178fc30 {Error Domain=NSOSStatusErrorDomain
Code=-16976 "(null)"}}
I printed the supported file types of AVAssetExportSession when the preset is AVAssetExportPresetPassthrough.
print(#function + ": Supported file types -> " + String.init(format: "%@", exporter?.supportedFileTypes ?? []))
Below is the output of the above. I can clearly see m4a in the list.
"com.apple.quicktime-movie",
"com.apple.m4a-audio",
"public.mpeg-4",
"com.apple.m4v-video",
"public.3gpp",
"org.3gpp.adaptive-multi-rate-audio",
"com.microsoft.waveform-audio",
"public.aiff-audio",
"public.aifc-audio",
"com.apple.coreaudio-format"
I need help to build a function that would change the speed of an audio track for me. The audio track could be an asset in the bundle or picked from the MPMediaPickerController.
Thanks!