When you run the following project, edited video will be generated on the device. I tried the following implementation with audio file and succeeded. However, if you run it as a movie, no error will be issued but a movie will be generated and will not go into the directory
https://github.com/Ryosuke-Hujisawa/My_AVAssetExportSession_AVMutableComposition-2
There is a model in my project. The model is below
https://github.com/justinlevi/AVAssetExportSession_AVMutableComposition
I succeeded in audio file. Audio was in the directory in trimmed and edited state. I want to edit the video. Although I could edit the video, it is edited and no error occurs, but the result does not exist in the directory. Or it exists in the directory in the state of the audio file and it is not generated as animation. Please help me.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var asset: AVAsset?
#IBAction func exportBtnDidTap(_ sender: AnyObject) {
guard let asset = asset else {
return
}
createAudioFileFromAsset(asset)
}
override func viewDidLoad() {
super.viewDidLoad()
let videoAsset = AVURLAsset(url: Bundle.main.url(forResource: "sample", withExtension: "m4v")!)
let comp = AVMutableComposition()
let videoAssetSourceTrack = videoAsset.tracks(withMediaType: AVMediaTypeVideo).first! as AVAssetTrack
let videoCompositionTrack = comp.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)
do {
try videoCompositionTrack.insertTimeRange(
CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(10, 600)),
of: videoAssetSourceTrack,
at: kCMTimeZero)
}catch { print(error) }
asset = comp
}
func deleteFile(_ filePath:URL) {
guard FileManager.default.fileExists(atPath: filePath.path) else {
return
}
do {
try FileManager.default.removeItem(atPath: filePath.path)
}catch{
fatalError("Unable to delete file: \(error) : \(#function).")
}
}
func createAudioFileFromAsset(_ asset: AVAsset){
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL
let filePath = documentsDirectory.appendingPathComponent("rendered-audio.m4v")
deleteFile(filePath)
if let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A){
exportSession.canPerformMultiplePassesOverSourceMediaData = true
exportSession.outputURL = filePath
exportSession.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration)
exportSession.outputFileType = AVFileTypeAppleM4A
exportSession.exportAsynchronously {
_ in
print("finished: \(filePath) : \(exportSession.status.rawValue) ")
}
}
}
}
I could crop the video with the implementation below. Also updated github
import UIKit
import AVFoundation
class ViewController: UIViewController {
var asset: AVAsset?
#IBAction func exportBtnDidTap(_ sender: AnyObject) {
guard let asset = asset else {
return
}
createAudioFileFromAsset(asset)
}
override func viewDidLoad() {
super.viewDidLoad()
let videoAsset = AVURLAsset(url: Bundle.main.url(forResource: "sample", withExtension: "m4v")!)
let comp = AVMutableComposition()
let videoAssetSourceTrack = videoAsset.tracks(withMediaType: AVMediaTypeVideo).first! as AVAssetTrack
let videoCompositionTrack = comp.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)
do {
try videoCompositionTrack.insertTimeRange(
CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(1, 600)),
of: videoAssetSourceTrack,
at: kCMTimeZero)
}catch { print(error) }
asset = comp
}
func deleteFile(_ filePath:URL) {
guard FileManager.default.fileExists(atPath: filePath.path) else {
return
}
do {
try FileManager.default.removeItem(atPath: filePath.path)
}catch{
fatalError("Unable to delete file: \(error) : \(#function).")
}
}
func createAudioFileFromAsset(_ asset: AVAsset){
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL
let filePath = documentsDirectory.appendingPathComponent("rendered-audio.m4v")
deleteFile(filePath)
if let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset640x480){
exportSession.canPerformMultiplePassesOverSourceMediaData = true
exportSession.outputURL = filePath
exportSession.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration)
exportSession.outputFileType = AVFileTypeQuickTimeMovie
exportSession.exportAsynchronously {
_ in
print("finished: \(filePath) : \(exportSession.status.rawValue) ")
}
}
}
}
Related
I wrote a program in Swift.I want to merge a video with an audio file, but got this error.
"failed Error Domain=AVFoundationErrorDomain Code=-11838 "Operation Stopped" UserInfo=0x17da4230 {NSLocalizedDescription=Operation Stopped, NSLocalizedFailureReason=The operation is not supported for this media.}"
Code
func mergeVideoWithAudio(videoUrl: URL, audioUrl: URL, success: #escaping ((URL) -> Void), failure: #escaping ((Error?) -> Void)) {
let mixComposition: AVMutableComposition = AVMutableComposition()
var mutableCompositionVideoTrack: [AVMutableCompositionTrack] = []
var mutableCompositionAudioTrack: [AVMutableCompositionTrack] = []
let totalVideoCompositionInstruction : AVMutableVideoCompositionInstruction = AVMutableVideoCompositionInstruction()
let aVideoAsset: AVAsset = AVAsset(url: videoUrl)
let aAudioAsset: AVAsset = AVAsset(url: audioUrl)
if let videoTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid), let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) {
mutableCompositionVideoTrack.append(videoTrack)
mutableCompositionAudioTrack.append(audioTrack)
if let aVideoAssetTrack: AVAssetTrack = aVideoAsset.tracks(withMediaType: .video).first, let aAudioAssetTrack: AVAssetTrack = aAudioAsset.tracks(withMediaType: .audio).first {
do {
try mutableCompositionVideoTrack.first?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: aVideoAssetTrack.timeRange.duration), of: aVideoAssetTrack, at: CMTime.zero)
try mutableCompositionAudioTrack.first?.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: aVideoAssetTrack.timeRange.duration), of: aAudioAssetTrack, at: CMTime.zero)
videoTrack.preferredTransform = aVideoAssetTrack.preferredTransform
} catch{
print(error)
}
totalVideoCompositionInstruction.timeRange = CMTimeRangeMake(start: CMTime.zero,duration: videoTrack.timeRange.duration)
}
}
let mutableVideoComposition: AVMutableVideoComposition = AVMutableVideoComposition()
mutableVideoComposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
mutableVideoComposition.renderSize = CGSize(width: 480, height: 640)
if let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
let outputURL = URL(fileURLWithPath: documentsPath).appendingPathComponent("\("fileName").mp4")
do {
if FileManager.default.fileExists(atPath: outputURL.path) {
try FileManager.default.removeItem(at: outputURL)
}
} catch { }
// var outputURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("temp_video_data.mp4", isDirectory: false)
// outputURL = URL(fileURLWithPath: outputURL.path)
// let mediaItems = MPMediaQuery.songs().items
// let mediaCollection = MPMediaItemCollection(items: mediaItems ?? [])
// let player1 = MPMusicPlayerController.systemMusicPlayer
// player1.setQueue(with: mediaCollection)
// player1.play()
// guard let url = URL(string: url )else {return}
// let player = AVPlayer(url: url)
// let playerController = AVPlayerViewController()
// playerController.player = player
// playerController.allowsPictureInPicturePlayback = true
// playerController.delegate = self
// playerController.player?.play()
// present(playerController, animated: true, completion: nil)
// let player3 = AVPlayer(url: vidUrl!)
// let playerLayer3 = AVPlayerLayer(player: player3)
// playerLayer3.videoGravity = .resizeAspect
// playerLayer3.needsDisplayOnBoundsChange = true //
// playerLayer3.frame = self.videoImg.bounds // 1
//
// self.videoImg.layer.masksToBounds = true // 2
// self.videoImg.layer.addSublayer(playerLayer3)
// player3.play()
// let assetsLib = PHPhotoLibrary()
// assetsLib.
// assetsLib.writeVideoAtPath(toSavedPhotosAlbum: outputURL, completionBlock: nil)
print(outputURL, "outputURL.....")
if let exportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) {
exportSession.outputURL = NSURL.fileURL(withPath: outputURL.path)
exportSession.outputFileType = AVFileType.mov
exportSession.shouldOptimizeForNetworkUse = true
// try to export the file and handle the status cases
exportSession.exportAsynchronously(completionHandler: {
switch exportSession.status {
case .failed:
if let _error = exportSession.error {
print("failedddd",_error)
failure(_error)
}
case .cancelled:
if let _error = exportSession.error {
print("cancellled")
failure(_error)
}
default:
print("finished")
success(outputURL)
}
})
} else {
print("failurrrrrrrr")
failure(nil)
}
}
}
Here the calling of Function
let vidUrl! = file:///private/var/mobile/Containers/Data/PluginKitPlugin/C6BD4529-DF11-4A67-B56E-C3A841D86D75/tmp/trim.CC7EABA8-08EC-4C33-8E6A-E85A5EBCFB64.MOV
let outputURL! = file:///var/mobile/Containers/Data/Application/9115DCFE-4134-48AD-84BF-452AC0D33278/Documents/y2matecom-ChallaOfficialFullVideoKhanSaabAYMediaRecordsLatestPunjabiSongs2016.mp3
mergeVideoWithAudio(videoUrl: vidUrl!, audioUrl: outputURL!) { resultUrl in
print("sucessssssssss",resultUrl)
} failure: { error in
print(error?.localizedDescription,"hgjjhfghdg")
}
Note:Every time the Failure Block calls and cause operation stopped
In my idea media type like mpeg4 is wrong. Where is the problem? What am i missing?
I am facing the same issues in ios 13.3 in real device it is working in ios 13.2 simulator but gives below error.
Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could
not be completed" UserInfo={NSLocalizedFailureReason=An unknown error
occurred (-17508), NSLocalizedDescription=The operation could not be
completed, NSUnderlyingError=0x2816d11d0 {Error
Domain=NSOSStatusErrorDomain Code=-17508 "(null)"}}
Here is my code I want to convert .mov file to mp4.
class func encodeVideo(at videoURL: String, completionHandler: ((URL?, Error?) -> Void)?) {
let avAsset = AVURLAsset(url: URL.init(fileURLWithPath: videoURL), options: nil)
let startDate = Date()
//Create Export session
guard let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetPassthrough) else {
completionHandler?(nil, nil)
return
}
//Creating temp path to save the converted video
let filename = "Video_\(Date().timeIntervalSince1970).mp4"
// Below Folder Path used tor getting directory path
let strfilePath = (FolderPath.temporaryDirectory.getDirectoryPath as NSString).appendingPathComponent(filename)
let filePath = URL.init(fileURLWithPath: strfilePath)
//Check if the file already exists then remove the previous file
if FileManager.default.fileExists(atPath: filePath.path) {
do {
try FileManager.default.removeItem(at: filePath)
} catch {
completionHandler?(nil, error)
}
}
exportSession.outputURL = filePath
exportSession.outputFileType = AVFileType.mp4
exportSession.shouldOptimizeForNetworkUse = true
let start = CMTimeMakeWithSeconds(0.0, preferredTimescale: 0)
let range = CMTimeRangeMake(start: start, duration: avAsset.duration)
exportSession.timeRange = range
exportSession.exportAsynchronously(completionHandler: {() -> Void in
switch exportSession.status {
case .failed:
print(exportSession.error ?? "NO ERROR")
completionHandler?(nil, exportSession.error)
case .cancelled:
print("Export canceled")
completionHandler?(nil, nil)
case .completed:
//Video conversion finished
let endDate = Date()
let time = endDate.timeIntervalSince(startDate)
print(time)
print("Successful!")
print(exportSession.outputURL ?? "NO OUTPUT URL")
completionHandler?(exportSession.outputURL, nil)
default: break
}
})
}
let strfilePath = (FolderPath.temporaryDirectory.getDirectoryPath as NSString).appendingPathComponent(filename)
you cannot store in that folder directly, but you need to store your file in a subfolder, e.g. like this:
let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! as NSURL
let strfilePath = documentDirectoryURL.appendingPathComponent("Subfolder/filename.mp4") as URL
Further you can read this article
Finally, I solve my issues by using AVMutableComposition not directly using AVURL asset. I adding audio and video track in AVMutableComposition.
This is the code I use to convert .mov to .mp4
var outputURL: URL!
func exportVideo(key:String, inputurl: URL, presetName: String, outputFileType: AVFileType = .mp4, fileExtension: String = "mp4", then completion: #escaping (URL?) -> Void) {
let asset = AVAsset(url: inputurl)
outputURL = FileManager.default.temporaryDirectory.appendingPathComponent(key)
if let session = AVAssetExportSession(asset: asset, presetName: presetName) {
session.outputURL = outputURL
session.outputFileType = outputFileType
session.shouldOptimizeForNetworkUse = true
session.exportAsynchronously {
switch session.status {
case .completed:
completion(self.outputURL)
case .cancelled:
debugPrint("Video export cancelled.")
completion(nil)
case .failed:
let errorMessage = session.error?.localizedDescription ?? "n/a"
debugPrint("Video export failed with error: \(errorMessage)")
completion(nil)
default:
break
}
}
} else {
completion(nil)
}
}
And then I call this function to get the output URL of the converted file and use it
exportVideo(key: key, inputurl: path, presetName: AVAssetExportPresetHighestQuality, outputFileType: .mp4, fileExtension: "mp4") { (outputURL) in
// do whatever with the file here
}
For anyone stuck in this frustrating error. Use a AVmutablecomposition as export asset.
Sample code for trimming a video:
let manager = FileManager.default
guard let documentDirectory = try? manager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) else { print("TRIM Failed to access directory")
return}
let mediaType = "mp4"
if mediaType == kUTTypeMovie as String || mediaType == "mp4" as String {
let asset = AVAsset(url:videoURL!)
let length = Float(asset.duration.value) / Float(asset.duration.timescale)
print("TRIM video length: \(length) seconds")
let composition = AVMutableComposition()
let audioTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)!
let videoTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)!
var outURL_speed=documentDirectory.appendingPathComponent("output")
var outputURL = documentDirectory.appendingPathComponent("output")
do {
try audioTrack.insertTimeRange(CMTimeRangeFromTimeToTime(start: self.startTime, end: self.endTime), of: asset.tracks(withMediaType: AVMediaType.audio)[0], at: CMTime.zero)
try videoTrack.insertTimeRange(CMTimeRangeFromTimeToTime(start: self.startTime, end: self.endTime), of: asset.tracks(withMediaType: AVMediaType.video)[0], at: CMTime.zero)
try manager.createDirectory(at: outputURL, withIntermediateDirectories: true, attributes: nil)
outputURL = outputURL.appendingPathComponent("preVideo-\(self.postID).\(mediaType)")
outURL_speed = outURL_speed.appendingPathComponent("Video-\(self.postID).\(mediaType)")
print("TRIM output dir: \(outputURL)")
}catch let error {
print(error)
}
_ = try? manager.removeItem(at: outputURL)
guard let exportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality) else {return}
exportSession.outputURL = outputURL
exportSession.shouldOptimizeForNetworkUse = true
exportSession.outputFileType = .mp4
let timeRange = CMTimeRange(start: self.startTime, end: self.endTime)
exportSession.timeRange = timeRange
exportSession.exportAsynchronously{
switch exportSession.status {
case .completed:
print("TRIM exported at \(outputURL)")
self.changeSpeed(url: outputURL,outUrl:outURL_speed)
case .failed:
print("TRIM failed \(exportSession.error)")
case .cancelled:
print("TRIM cancelled \(exportSession.error)")
default: break
}
}
I recently have struggling to get AVMutableComposition working in my synthesizer app. I implemented a function like the one below without Audiokit and it worked perfectly fine. However, when I run the function below I only can here the first file in the files arrray in the outputed AKAudioFile. Is this a problem with how AKAudioFiles are copied or how AVMutableComposition works?
private func createCombinedRecordingFrom(_ files: [AKAudioFile], completion: #escaping (AKAudioFile?) -> ()) {
let composition = AVMutableComposition()
for file in files {
let asset = file.avAsset
let timeRange = CMTimeRange(start: kCMTimeZero, duration: asset.duration)
let track = composition.addMutableTrack(withMediaType: .audio,
preferredTrackID: kCMPersistentTrackID_Invalid)
do {
try track?.insertTimeRange(timeRange, of: asset.tracks[0], at: kCMTimeZero)
} catch {
print(error.localizedDescription)
completion(nil)
}
}
let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A)!
assetExport.audioTimePitchAlgorithm = .spectral
assetExport.outputFileType = .m4a
let temporaryURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString).appendingPathExtension("m4a")
assetExport.outputURL = temporaryURL
assetExport.exportAsynchronously() {
do {
let audioFile = try AKAudioFile(forReading: temporaryURL)
completion(audioFile)
} catch {
print(error.localizedDescription)
completion(nil)
}
}
}
I start saying that I spent a lot of time searching through documentation, posts here and somewhere else, but I can't figure out the solution for this problem.
I'm using AVAssetExportSession for exporting an .mp4 file stored in a AVAsset instance.
What I do is:
I check the isExportable property of AVAsset
I then get an array of exportPresets compatible with the AVAsset instance
I take the AVAssetExportPreset1920x1080, or, if not existing I try to export the media with AVAssetExportPresetPassthrough (FYI, 100% of times, the preset I need is always contained in the list, but I tried also the passthrough option and it doesn't work anyway)
The outputFileType is AVFileTypeMPEG4 and I tried also by assigning the .mp4 extension to the file, but nothing makes it work.
I always receive this error
Error Domain=AVFoundationErrorDomain Code=-11838 "Operation Stopped"
UserInfo={NSUnderlyingError=0x600000658c30 {Error
Domain=NSOSStatusErrorDomain Code=-12109 "(null)"},
NSLocalizedFailureReason=The operation is not supported for this
media., NSLocalizedDescription=Operation Stopped}
Below is the code I'm using
func _getDataFor(_ item: AVPlayerItem, completion: #escaping (Data?) -> ()) {
guard item.asset.isExportable else {
completion(nil)
return
}
let compatiblePresets = AVAssetExportSession.exportPresets(compatibleWith: item.asset)
var preset: String = AVAssetExportPresetPassthrough
if compatiblePresets.contains(AVAssetExportPreset1920x1080) { preset = AVAssetExportPreset1920x1080 }
guard
let exportSession = AVAssetExportSession(asset: item.asset, 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("\(exportSession.error)")
let data = try? Data(contentsOf: tempFileUrl)
_ = try? FileManager.default.removeItem(at: tempFileUrl)
completion(data)
}
}
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)
}
}
Swift 5:
import Foundation
import AVKit
func getDataFor(_ asset: AVAsset, completion: #escaping (Data?) -> ()) {
guard asset.isExportable,
let sourceVideoTrack = asset.tracks(withMediaType: .video).first,
let sourceAudioTrack = asset.tracks(withMediaType: .audio).first else {
completion(nil)
return
}
let composition = AVMutableComposition()
let compositionVideoTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))
let compositionAudioTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))
do {
try compositionVideoTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: asset.duration), of: sourceVideoTrack, at: .zero)
try compositionAudioTrack?.insertTimeRange(CMTimeRangeMake(start: .zero, duration: asset.duration), of: sourceAudioTrack, at: .zero)
} catch {
completion(nil)
return
}
let compatiblePresets = AVAssetExportSession.exportPresets(compatibleWith: composition)
var preset = AVAssetExportPresetPassthrough
let preferredPreset = AVAssetExportPreset1920x1080
if compatiblePresets.contains(preferredPreset) {
preset = preferredPreset
}
let fileType: AVFileType = .mp4
guard let exportSession = AVAssetExportSession(asset: composition, presetName: preset),
exportSession.supportedFileTypes.contains(fileType) else {
completion(nil)
return
}
let tempFileUrl = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("temp_video_data")
exportSession.outputURL = tempFileUrl
exportSession.outputFileType = fileType
let startTime = CMTimeMake(value: 0, timescale: 1)
let timeRange = CMTimeRangeMake(start: startTime, duration: asset.duration)
exportSession.timeRange = timeRange
exportSession.exportAsynchronously {
print(tempFileUrl)
print(String(describing: exportSession.error))
let data = try? Data(contentsOf: tempFileUrl)
try? FileManager.default.removeItem(at: tempFileUrl)
completion(data)
}
}
Check if you set delegate property for AVURLAsset correctly.
[self.playerAsset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
And conform to AVAssetResourceLoaderDelegate protocol.
That is all you need to do.
I had this same issue because I was adding an audio track to a video without audio. Removing the audio track fixed it.
I ran into this problem because the Microphone permission was off/denied. Once I set turned it on this error went away.
I solved this problem by removing the CompositionTrack with media type .audio and empty segments from the AVMutableComposition
if let audioTrack = exportComposition.tracks(withMediaType: .audio).first,
audioTrack.segments.isEmpty {
exportComposition.removeTrack(audioTrack)
}
I had the same error, when I try to ExportSession with AVAssetExportPresetPassthrough always fail and in my case I can't use another preset because I must have the same resolution like at origin video
I fixed
let asset = AVAsset(url: originUrl)
let videoTrack = asset.tracks( withMediaType: .video ).first! as AVAssetTrack
let videoComposition = AVMutableVideoComposition()
videoComposition.renderSize = CGSize(
width: videoTrack.naturalSize.width,
height: videoTrack.naturalSize.height
)
videoComposition.frameDuration = CMTime(
value: 1,
timescale: videoTrack.naturalTimeScale
)
let transformer = AVMutableVideoCompositionLayerInstruction(
assetTrack: videoTrack
)
transformer.setOpacity(1.0, at: CMTime.zero)
let instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = timeRange
instruction.layerInstructions = [transformer]
videoComposition.instructions = [instruction]
guard let exportSession = AVAssetExportSession(
asset: asset,
presetName: AVAssetExportPresetMediumQuality
) else {
return handleFailure(error: .mediaSavingError, completion: completion)
}
exportSession.videoComposition = videoComposition
exportSession.outputURL = outputUrl
exportSession.outputFileType = .mp4
exportSession.timeRange = timeRange
exportSession.exportAsynchronously { [weak self] in
"your code"
}
Forks great for me, and it's saved the same resolution as video before
How I can edit or trim video start and point particular parts of video?
Also I want to use slider to point out trimming start and end points.
func trimVideo(sourceURL: NSURL, destinationURL: NSURL, trimPoints: TrimPoints, completion: TrimCompletion?) {
assert(sourceURL.fileURL)
assert(destinationURL.fileURL)
let options = [ AVURLAssetPreferPreciseDurationAndTimingKey: true ]
let asset = AVURLAsset(URL: sourceURL, options: options)
let preferredPreset = AVAssetExportPresetPassthrough
if verifyPresetForAsset(preferredPreset, asset: asset) {
let composition = AVMutableComposition()
let videoCompTrack = composition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())
let audioCompTrack = composition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
let assetVideoTrack: AVAssetTrack = asset.tracksWithMediaType(AVMediaTypeVideo).first as! AVAssetTrack
let assetAudioTrack: AVAssetTrack = asset.tracksWithMediaType(AVMediaTypeAudio).first as! AVAssetTrack
var compError: NSError?
var accumulatedTime = kCMTimeZero
for (startTimeForCurrentSlice, endTimeForCurrentSlice) in trimPoints {
let durationOfCurrentSlice = CMTimeSubtract(endTimeForCurrentSlice, startTimeForCurrentSlice)
let timeRangeForCurrentSlice = CMTimeRangeMake(startTimeForCurrentSlice, durationOfCurrentSlice)
videoCompTrack.insertTimeRange(timeRangeForCurrentSlice, ofTrack: assetVideoTrack, atTime: accumulatedTime, error: &compError)
audioCompTrack.insertTimeRange(timeRangeForCurrentSlice, ofTrack: assetAudioTrack, atTime: accumulatedTime, error: &compError)
if compError != nil {
NSLog("error during composition: \(compError)")
if let completion = completion {
completion(compError)
}
}
accumulatedTime = CMTimeAdd(accumulatedTime, durationOfCurrentSlice)
}
let exportSession = AVAssetExportSession(asset: composition, presetName: preferredPreset)
exportSession.outputURL = destinationURL
exportSession.outputFileType = AVFileTypeAppleM4V
exportSession.shouldOptimizeForNetworkUse = true
removeFileAtURLIfExists(destinationURL)
exportSession.exportAsynchronouslyWithCompletionHandler({ () -> Void in
if let completion = completion {
completion(exportSession.error)
}
})
} else {
NSLog("Could not find a suitable export preset for the input video")
let error = NSError(domain: "org.linuxguy.VideoLab", code: -1, userInfo: nil)
if let completion = completion {
completion(error)
}
}
}