Exporting mp4 through AVAssetExportSession fails
Seems like converting the AVAsset
instance in a AVMutableComposition
did the trick. If, please, anyone knows the reason why this works let me know.
This is the new _getDataFor(_:completion:)
method implementation
func _getDataFor(_ item: AVPlayerItem, completion: @escaping (Data?) -> ()) {
guard item.asset.isExportable else {
completion(nil)
return
}
let composition = AVMutableComposition()
let compositionVideoTrack = composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))
let compositionAudioTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))
let sourceVideoTrack = item.asset.tracks(withMediaType: AVMediaTypeVideo).first!
let sourceAudioTrack = item.asset.tracks(withMediaType: AVMediaTypeAudio).first!
do {
try compositionVideoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, item.duration), of: sourceVideoTrack, at: kCMTimeZero)
try compositionAudioTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, item.duration), of: sourceAudioTrack, at: kCMTimeZero)
} catch(_) {
completion(nil)
return
}
let compatiblePresets = AVAssetExportSession.exportPresets(compatibleWith: composition)
var preset: String = AVAssetExportPresetPassthrough
if compatiblePresets.contains(AVAssetExportPreset1920x1080) { preset = AVAssetExportPreset1920x1080 }
guard
let exportSession = AVAssetExportSession(asset: composition, presetName: preset),
exportSession.supportedFileTypes.contains(AVFileTypeMPEG4) else {
completion(nil)
return
}
var tempFileUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("temp_video_data.mp4", isDirectory: false)
tempFileUrl = URL(fileURLWithPath: tempFileUrl.path)
exportSession.outputURL = tempFileUrl
exportSession.outputFileType = AVFileTypeMPEG4
let startTime = CMTimeMake(0, 1)
let timeRange = CMTimeRangeMake(startTime, item.duration)
exportSession.timeRange = timeRange
exportSession.exportAsynchronously {
print("\(tempFileUrl)")
print("\(exportSession.error)")
let data = try? Data(contentsOf: tempFileUrl)
_ = try? FileManager.default.removeItem(at: tempFileUrl)
completion(data)
}
}
AVAssetExportSession is not exporting M4V files
AVAssetExportPresetHighestQuality
only supports .mov files. You have to use AVAssetExportPresetPassthrough
instead.
let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetPassthrough)
According to the Documentation, AVAssetExportPresetLowQuality
, AVAssetExportPresetMediumQuality
and AVAssetExportPresetHighQuality
are for QuickTime .mov files only.
You can find out more by trying to print the contents of the variable supportedFileTypes
of the AVAssetExportSession
.
AVAssetExportSession fails on IOS 13, muxing together audio and video
Issue seems to be related to AVAssetExportPresetPassthrough (and a combination of dealing with an AAC, maybe)
Changing to AVAssetExportPresetLowQuality or AVAssetExportPresetHighestQuality and the video/audio are properly muxed into one. Again, this is just an IOS 13 issue, and likely a bug.
Why don't I get video when exporting a movie using AVAssetExportSession?
Thanks ChrisH, you were right! The Export was taking place on another thread so in the handler I need to get the main queue...
I needed to get the main thread after
case AVAssetExportSessionStatusCompleted:{
dispatch_async(dispatch_get_main_queue(), ^{
//post the notification!
});
break;
}
AVAssetExportSession throws error when exporting AVAsset to temporary iOS path
For anyone with the same problem: I could solve it using an AVMutableComposition()
:
// Access url
guard songUrl.startAccessingSecurityScopedResource() else {
print("failed to access path")
return
}
// Make sure you release the security-scoped resource when you are done.
defer { songUrl.stopAccessingSecurityScopedResource() }
// Use file coordination for reading and writing any of the URL’s content.
var error: NSError? = nil
NSFileCoordinator().coordinate(readingItemAt: songUrl, error: &error) { (url) in
// Set temporary file's path
let temporaryDirectoryUrl: URL = FileManager.default.temporaryDirectory
let temporaryFilename = ProcessInfo().globallyUniqueString
let temporaryFilepath = temporaryDirectoryUrl.appendingPathComponent("\(temporaryFilename).m4a")
// Prework
let originalAsset = AVURLAsset(url: url)
print(originalAsset)
let composition = AVMutableComposition()
let audioTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)!
let originalDuration = Int64(CMTimeGetSeconds(originalAsset.duration))
let startTime, stopTime: CMTime
// Shorten audio file if longer than 20 seconds
if originalDuration < 20 {
startTime = CMTimeMake(value: 0, timescale: 1)
stopTime = CMTimeMake(value: originalDuration, timescale: 1)
}
else {
let halftime: Int64 = (originalDuration/2)
startTime = CMTimeMake(value: (halftime-10), timescale: 1)
stopTime = CMTimeMake(value: (halftime+10), timescale: 1)
}
// Export shortened file
do {
try audioTrack.insertTimeRange(CMTimeRangeFromTimeToTime(start: startTime, end: stopTime), of: originalAsset.tracks(withMediaType: AVMediaType.audio)[0], at: CMTime.zero)
let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A)!
if FileManager.default.fileExists(atPath: temporaryFilepath.absoluteString) {
try? FileManager.default.removeItem(atPath: temporaryFilepath.absoluteString)
print("removed existing file")
}
assetExport.outputFileType = AVFileType.m4a
assetExport.outputURL = temporaryFilepath
assetExport.shouldOptimizeForNetworkUse = true
assetExport.exportAsynchronously(completionHandler: {
switch assetExport.status {
case AVAssetExportSessionStatus.failed:
if let e = assetExport.error {
print("export failed \(e)")
}
case AVAssetExportSessionStatus.cancelled:
print("export cancelled \(String(describing: assetExport.error))")
default:
print("export completed")
}
})
}
catch {
print("error trying to shorten audio file")
}
Related Topics
Listing All Files in a Folder Recursively with Swift
Value of Optional Type Cgfloat Not Unwrapped Error in Swift
Creating a Countableclosedrange<Character>
Non-Strong References Not Working in Playground
Why Does Swift Playground Shows Wrong Number of Executions
Swiftui - Wait Until Firestore Getdocuments() Is Finished Before Moving On
Self' Captured by a Closure Before All Members Were Initialized
Swift 4.2 Setter Getter, All Paths Through This Function Will Call Itself
Swift String Interpolation Displaying Optional
Adding Local Dependencies in Xcode11 Using Spm
Parsing a Iso8601 String to Date in Swift
Default Argument Not Permitted in a Tuple Type When Defining Function Type
Nsurl Found Nil While Unwraping an Optional Value
Why Can't You Assign an Optional to a Variable of Type 'Any' Without a Warning
Get an Array of Dates of the Current Week Starting on Monday