When i run my app and click the save button to save the two files mixed together my app crashes saying invalid file output. I dont see why this error shows up because the two files being mixed are mp3 and output file is mp3.
Code:
#IBAction func mixButton (sender:AnyObject!) {
let oldAsset = self.player.currentItem.asset
let type = AVMediaTypeAudio
let audioFile = NSBundle.mainBundle().URLForResource("file1", withExtension: "mp3")
let asset1 = AVURLAsset(URL: audioFile, options: nil)
let arr2 = asset1.tracksWithMediaType(type)
let track2 = arr2.last as AVAssetTrack
let duration : CMTime = track2.timeRange.duration
let comp = AVMutableComposition()
let comptrack = comp.addMutableTrackWithMediaType(type,
preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
comptrack.insertTimeRange(CMTimeRangeMake(CMTimeMakeWithSeconds(0,600), CMTimeMakeWithSeconds(5,600)), ofTrack:track2, atTime:CMTimeMakeWithSeconds(0,600), error:nil)
comptrack.insertTimeRange(CMTimeRangeMake(CMTimeSubtract(duration, CMTimeMakeWithSeconds(5,600)), CMTimeMakeWithSeconds(5,600)), ofTrack:track2, atTime:CMTimeMakeWithSeconds(5,600), error:nil)
let type3 = AVMediaTypeAudio
let s = NSBundle.mainBundle().URLForResource("file2", withExtension:"mp3")
let asset = AVURLAsset(URL:s, options:nil)
let arr3 = asset.tracksWithMediaType(type3)
let track3 = arr3.last as AVAssetTrack
let comptrack3 = comp.addMutableTrackWithMediaType(type3, preferredTrackID:Int32(kCMPersistentTrackID_Invalid))
comptrack3.insertTimeRange(CMTimeRangeMake(CMTimeMakeWithSeconds(0,600), CMTimeMakeWithSeconds(10,600)), ofTrack:track3, atTime:CMTimeMakeWithSeconds(0,600), error:nil)
let params = AVMutableAudioMixInputParameters(track:comptrack3)
params.setVolume(1, atTime:CMTimeMakeWithSeconds(0,600))
params.setVolumeRampFromStartVolume(1, toEndVolume:0, timeRange:CMTimeRangeMake(CMTimeMakeWithSeconds(7,600), CMTimeMakeWithSeconds(3,600)))
let mix = AVMutableAudioMix()
mix.inputParameters = [params]
let item = AVPlayerItem(asset:comp)
item.audioMix = mix
mixedFile = comp //global variable for mixed file
}
}
#IBAction func saveButton(sender: AnyObject) {
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let savedFileTest = documentsPath + "/myfile.mp3"
if (NSFileManager.defaultManager().fileExistsAtPath(savedFileTest)) {
NSFileManager.defaultManager().removeItemAtPath(savedFileTest, error: nil)
}
let url = NSURL.fileURLWithPath(savedFileTest)
let exporter = AVAssetExportSession(asset: mixedFile, presetName: AVAssetExportPresetHighestQuality)
exporter.outputURL = url
exporter.outputFileType = AVFileTypeMPEGLayer3
exporter.exportAsynchronouslyWithCompletionHandler({
switch exporter.status{
case AVAssetExportSessionStatus.Failed:
println("failed \(exporter.error)")
case AVAssetExportSessionStatus.Cancelled:
println("cancelled \(exporter.error)")
default:
println("complete")
}
The output file is not mp3. You can say mp3 but that doesn't make it one. I don't think Apple framework code can save as mp3. It can read it, but due to various licensing issues it can't write it.
Do it as an m4a, like this (I have starred the lines I changed from your original code):
let savedFileTest = documentsPath + "/myfile.m4a" // *
if (NSFileManager.defaultManager().fileExistsAtPath(savedFileTest)) {
NSFileManager.defaultManager().removeItemAtPath(savedFileTest, error: nil)
}
let url = NSURL.fileURLWithPath(savedFileTest)
let exporter = AVAssetExportSession(
asset: mixedFile, presetName: AVAssetExportPresetAppleM4A) // *
exporter.outputURL = url
exporter.outputFileType = AVFileTypeAppleM4A // *
By the way, you're saving the wrong thing (the unmixed comp rather than the mixed item).
You can export it to "AVFileTypeQuickTimeMovie" and rename it to *.mp3.
Init export, here you must set "presentName" argument to "AVAssetExportPresetPassthrough". If not, you will can't export mp3 correct.
AVAssetExportSession *export = [[AVAssetExportSession alloc] initWithAsset:sset presetName:AVAssetExportPresetPassthrough];
Set the ouput type:
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
exportSession.shouldOptimizeForNetworkUse = true;
Export it and set the file name extension to "mp3".
Related
A flutter application that I developed uploads videos to the server, iOS devices upload videos in the mov format, which the Edge browser does not support to play. I attempted to use swift code to convert the mov file to mp4. I'm receiving an error after referring to a code snippet.. By the way, I'm a beginner with Swift
Error : Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Unsupported value for standard codec'
let myArgs = args as? [String: Any]
let movpath = myArgs?["movpath"] as? String
let mp4Path = myArgs?["mp4path"] as? String
let movurl = URL(fileURLWithPath: movpath!)
let mp4url = URL(fileURLWithPath: mp4Path!)
let avAsset = AVURLAsset(url: movurl, options: nil)
//Create Export session
let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetPassthrough)
// exportSession = AVAssetExportSession(asset: composition, presetName: mp4Quality)
exportSession!.outputURL = mp4url
exportSession!.outputFileType = AVFileType.mp4
exportSession!.shouldOptimizeForNetworkUse = true
var start = CMTimeMakeWithSeconds(0.0,preferredTimescale: 0)
var range = CMTimeRangeMake(start:start, duration: avAsset.duration)
exportSession!.timeRange = range
exportSession!.exportAsynchronously {
result(exportSession!.outputURL)
}
You can refer to the code below
func encodeVideo(at videoURL: URL, completionHandler: ((URL?, Error?) -> Void)?) {
let avAsset = AVURLAsset(url: 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 documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL
let filePath = documentsDirectory.appendingPathComponent("rendered-Video.mp4")
//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, 0)
let range = CMTimeRangeMake(start, 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
}
})
}
it crashes when i try to play the merged audio
I am trying to merging two audio file . They merged successfully, but I am seeing with a problem that when i play that audio with AVAudioPlayer it crashes . also there is a problem with formats as the merge audio only store with .m4a format if i save that audio with .wav format the it crashes.
func merge(audio1: NSURL, audio2: NSURL) {
let finalURL = getMergeFileURL()
let preferredTimeScale : Int32 = 100
//This object will be edited to include both audio files
let composition = AVMutableComposition()
let compositionAudioTrack1:AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
//let url1 = audio1
let avAsset1 = AVURLAsset(url: audio1 as URL, options: nil)
let tracks1 = avAsset1.tracks(withMediaType: AVMediaTypeAudio)
let assetTrack1:AVAssetTrack = tracks1[0]
let duration1: CMTime = CMTimeMakeWithSeconds(30.0, preferredTimeScale)
let startCMTime = CMTimeMakeWithSeconds(Double(30.0), preferredTimeScale)
let timeRange1 = CMTimeRangeMake(startCMTime, duration1)
let compositionAudioTrack2:AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
//let url2 = audio2
let avAsset2 = AVURLAsset(url: audio2 as URL, options: nil)
let tracks2 = avAsset2.tracks(withMediaType: AVMediaTypeAudio)
let assetTrack2:AVAssetTrack = tracks2[0]
let duration2: CMTime = CMTimeMakeWithSeconds(30.0, preferredTimeScale)
let startCMTime2 = CMTimeMakeWithSeconds(Double(30.0), preferredTimeScale)
let timeRange2 = CMTimeRangeMake(startCMTime, duration1)
//Insert the tracks into the composition
do {
try compositionAudioTrack1.insertTimeRange(timeRange1, of: assetTrack1, at: kCMTimeZero)
try compositionAudioTrack2.insertTimeRange(timeRange2, of: assetTrack2, at: duration1)
} catch {
print(error)
}
//Perform the merge
let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A)
assetExport!.outputFileType = AVFileTypeAppleM4A
assetExport!.outputURL = finalURL as URL // final url is the url of that merged file
assetExport!.exportAsynchronously(completionHandler: {
switch assetExport!.status{
case AVAssetExportSessionStatus.failed:
print("failed \(assetExport!.error)")
case AVAssetExportSessionStatus.cancelled:
print("cancelled \(assetExport!.error)")
default:
print("complete")
}
})
}
I'm trying to use AVAssetExportSession on video selected in share extension and getting
Error Domain=NSURLErrorDomain Code=-3000 "Cannot create file"
UserInfo={NSLocalizedDescription=Cannot create file,
NSUnderlyingError=0x14811fdb0 {Error Domain=NSOSStatusErrorDomain
Code=-12124 "(null)"}}
But I can create file manually at the same NSURL without an error. Here is a function I'm using
func reencodeVideo() {
let videoAsset = AVURLAsset(URL: video.url)
let videoTrack = videoAsset.tracksWithMediaType(AVMediaTypeVideo)[0] as AVAssetTrack
print(videoTrack.estimatedDataRate)
let exportSession = AVAssetExportSession(asset: videoAsset, presetName: AVAssetExportPreset1920x1080)
guard let outputURL = uploadableFileURL else {
return
}
let fileManager = NSFileManager.defaultManager()
// let created = fileManager.createFileAtPath(outputURL.path!, contents: nil, attributes: nil)
if let path = outputURL.path where fileManager.fileExistsAtPath(path) {
print("file exists")
}
do {
try fileManager.removeItemAtURL(outputURL)
print("deleted")
} catch {
print(error)
}
exportSession?.outputURL = outputURL
exportSession?.outputFileType = AVFileTypeQuickTimeMovie
exportSession?.exportAsynchronouslyWithCompletionHandler{
print(exportSession?.status)
}
}
private var uploadableFileURL: NSURL? {
guard let tempFileName = video.url.lastPathComponent else {
return nil
}
let fileManager = NSFileManager.defaultManager()
guard let containerURL = fileManager.containerURLForSecurityApplicationGroupIdentifier(Constants.appGroupIdentifier) else {
return nil
}
return containerURL.URLByAppendingPathComponent("videoFile.mov")
}
I've successfully created file in the same directory, but AVAssetExportSession returns an error there.
Any ideas what I'm doing wrong?
I've tried using AVAssetReader and AVAssetWriter, and AVAssetWriter returns same error when trying to start. Encode process completes successfully if I'm using Documents directory and fails only when using shared app group container.
You're issue might be related to the use of the document folder and icloud sync.
See https://forums.developer.apple.com/message/77495#77495
If you do something like :
guard let containerURL = fileManager.containerURLForSecurityApplicationGroupIdentifier(Constants.appGroupIdentifier) else {
return nil
}
let libraryURL = containerURL.URLByAppendingPathComponent("Library", isDirectory: true)
let cachesURL = libraryURL.URLByAppendingPathComponent("Caches", isDirectory: true)
return cachesURL.URLByAppendingPathComponent("videoFile.mov")
I can't figure out from your code where your uploadableFileURL comes from, but the following works for me:
if let video = videoAsset {
let fm = NSFileManager.defaultManager().URLsForDirectory(.CachesDirectory, inDomains: .UserDomainMask)
let url = fm[0].URLByAppendingPathComponent("\(NSDate())").URLByAppendingPathExtension("mp4")
let exporter = AVAssetExportSession(asset: video, presetName: AVAssetExportPreset3840x2160)
exporter?.outputURL = url
exporter?.outputFileType = AVFileTypeMPEG4
exporter?.exportAsynchronouslyWithCompletionHandler() { _ in
let data = NSData(contentsOfURL: url)
}
}
I'm trying to play an slow motion video (filmed by the user's iPhone) in an AVPlayer.
I am retrieving the AVAsset with a request on a PHAsset from a picker:
[manager requestAVAssetForVideo:PHAsset
options:videoRequestOptions
resultHandler:^(AVAsset * avasset, AVAudioMix * audioMix, NSDictionary * info) {}];
The problem is once it plays, I get this error:
-[AVComposition URL]: unrecognized selector sent to instance 0x138d17f40
However, if I set this option on the manager request, it will play as normal speed video at 120/240fps and no crashes:
videoRequestOptions.version = PHVideoRequestOptionsVersionOriginal;
Whats going on? The default version property is PHVideoRequestOptionsVersionCurrent which incorporates slow motion, user edits and trims, etc.
I would like to play that video version. Thanks
So it turns out that slow motion videos are passed as AVComposition.
You can export that into a video file / URL, and then handle it like any other video.
Solution here: https://overflow.buffer.com/2016/02/29/slow-motion-video-ios/
//Output URL
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = paths.firstObject;
NSString *myPathDocs = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"mergeSlowMoVideo-%d.mov",arc4random() % 1000]];
NSURL *url = [NSURL fileURLWithPath:myPathDocs];
//Begin slow mo video export
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetHighestQuality];
exporter.outputURL = url;
exporter.outputFileType = AVFileTypeQuickTimeMovie;
exporter.shouldOptimizeForNetworkUse = YES;
[exporter exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (exporter.status == AVAssetExportSessionStatusCompleted) {
NSURL *URL = exporter.outputURL;
NSData *videoData = [NSData dataWithContentsOfURL:URL];
// Upload
[self uploadSelectedVideo:video data:videoData];
}
});
}];
For those coming here looking for a swift answer, this is the swift translation, which I use in my project, where I need the url of the slow-motion Video to play it with the AVPlayerViewController:
else if asset is AVComposition {
//Slow-Motion Assets are passed as AVComposition
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory: NSString? = paths.first as NSString?
if documentsDirectory != nil {
let random = Int(arc4random() % 1000)
let pathToAppend = String(format: "mergeSlowMoVideo-%d.mov", random)
let myPathDocs = documentsDirectory!.strings(byAppendingPaths: [pathToAppend])
let myPath = myPathDocs.first
if myPath != nil {
let url = URL(fileURLWithPath: myPath!)
let exporter = AVAssetExportSession(asset: asset!, presetName: AVAssetExportPresetHighestQuality)
if exporter != nil {
exporter!.outputURL = url
exporter!.outputFileType = AVFileTypeQuickTimeMovie
exporter!.shouldOptimizeForNetworkUse = true
exporter!.exportAsynchronously(completionHandler: {
AsyncUtil.asyncMain {
let url = exporter!.outputURL
if url != nil {
let player = AVPlayer(url: url!)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
playerViewController.modalTransitionStyle = .crossDissolve
view.present(playerViewController, animated: true) {
playerViewController.player!.play()
}
}
}
})
}
}
Playing Slo-mo & Library Video in Swift 4 n above with Custom View
var vcPlayer = AVPlayerViewController()
var player = AVPlayer()
func playallVideo(_ customView: UIView, asset: PHAsset) {
guard asset.mediaType == .video
else {
print("Not a valid video media type")
return
}
let options = PHVideoRequestOptions()
options.isNetworkAccessAllowed = true
PHCachingImageManager().requestPlayerItem(forVideo: asset, options: options) { (playerItem, info) in
DispatchQueue.main.async {
self.player = AVPlayer(playerItem: playerItem)
self.vcPlayer.player = self.player
self.vcPlayer.view.frame = customView.bounds
self.vcPlayer.videoGravity = .resizeAspectFill
self.vcPlayer.showsPlaybackControls = true
//self.vcPlayer.allowsPictureInPicturePlayback = true
self.playerView.addSubview(self.vcPlayer.view)
self.player.play()
}
}
}
/**********Function Call ********/
self.playallVideo(self.playerView/*YourCustomView*/, asset: currentAssetArr[currentIndex]/*Current PHAsset Fetched from Library*/)
:) enjoy
Looking answer in Swift?
here's how I did it
Creating "Slow motion" video in iOS swift is not easy, that I came across many "slow motion" that came to know not working or some of the codes in them are depreciated. And so I finally figured a way to make slow motion in Swift.
This code can be used for 120fps are greater than that too.
Here is the "code snippet I created for achieving slow motion"
Give me a UPVOTE if this code works.
func slowMotion(pathUrl: URL) {
let videoAsset = AVURLAsset.init(url: pathUrl, options: nil)
let currentAsset = AVAsset.init(url: pathUrl)
let vdoTrack = currentAsset.tracks(withMediaType: .video)[0]
let mixComposition = AVMutableComposition()
let compositionVideoTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
let videoInsertError: Error? = nil
var videoInsertResult = false
do {
try compositionVideoTrack?.insertTimeRange(
CMTimeRangeMake(start: .zero, duration: videoAsset.duration),
of: videoAsset.tracks(withMediaType: .video)[0],
at: .zero)
videoInsertResult = true
} catch let videoInsertError {
}
if !videoInsertResult || videoInsertError != nil {
//handle error
return
}
var duration: CMTime = .zero
duration = CMTimeAdd(duration, currentAsset.duration)
//MARK: You see this constant (videoScaleFactor) this helps in achieving the slow motion that you wanted. This increases the time scale of the video that makes slow motion
// just increase the videoScaleFactor value in order to play video in higher frames rates(more slowly)
let videoScaleFactor = 2.0
let videoDuration = videoAsset.duration
compositionVideoTrack?.scaleTimeRange(
CMTimeRangeMake(start: .zero, duration: videoDuration),
toDuration: CMTimeMake(value: videoDuration.value * Int64(videoScaleFactor), timescale: videoDuration.timescale))
compositionVideoTrack?.preferredTransform = vdoTrack.preferredTransform
let dirPaths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).map(\.path)
let docsDir = dirPaths[0]
let outputFilePath = URL(fileURLWithPath: docsDir).appendingPathComponent("slowMotion\(UUID().uuidString).mp4").path
if FileManager.default.fileExists(atPath: outputFilePath) {
do {
try FileManager.default.removeItem(atPath: outputFilePath)
} catch {
}
}
let filePath = URL(fileURLWithPath: outputFilePath)
let assetExport = AVAssetExportSession(
asset: mixComposition,
presetName: AVAssetExportPresetHighestQuality)
assetExport?.outputURL = filePath
assetExport?.outputFileType = .mp4
assetExport?.exportAsynchronously(completionHandler: {
switch assetExport?.status {
case .failed:
print("asset output media url = \(String(describing: assetExport?.outputURL))")
print("Export session faiied with error: \(String(describing: assetExport?.error))")
DispatchQueue.main.async(execute: {
// completion(nil);
})
case .completed:
print("Successful")
let outputURL = assetExport!.outputURL
print("url path = \(String(describing: outputURL))")
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputURL!)
}) { saved, error in
if saved {
print("video successfully saved in photos gallery view video in photos gallery")
}
if (error != nil) {
print("error in saing video \(String(describing: error?.localizedDescription))")
}
}
DispatchQueue.main.async(execute: {
// completion(_filePath);
})
case .none:
break
case .unknown:
break
case .waiting:
break
case .exporting:
break
case .cancelled:
break
case .some(_):
break
}
})
}
How do i go about saving an AVPlayerItem? I looked online and really couldn't find anything. The AVPlayerItem contains two audio files in an asset. How do i save this to the users documents folder? My code is in swift but answers in objective c are welcome also.
Code:
let type = AVMediaTypeAudio
let audioFile = NSBundle.mainBundle().URLForResource("school", withExtension: "mp3")
let asset1 = AVURLAsset(URL: audioFile, options: nil)
let arr2 = asset1.tracksWithMediaType(type)
let track2 = arr2.last as AVAssetTrack
let duration : CMTime = track2.timeRange.duration
let comp = AVMutableComposition()
let comptrack = comp.addMutableTrackWithMediaType(type,
preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
comptrack.insertTimeRange(CMTimeRangeMake(CMTimeMakeWithSeconds(0,600), CMTimeMakeWithSeconds(5,600)), ofTrack:track2, atTime:CMTimeMakeWithSeconds(0,600), error:nil)
comptrack.insertTimeRange(CMTimeRangeMake(CMTimeSubtract(duration, CMTimeMakeWithSeconds(5,600)), CMTimeMakeWithSeconds(5,600)), ofTrack:track2, atTime:CMTimeMakeWithSeconds(5,600), error:nil)
let type3 = AVMediaTypeAudio
let s = NSBundle.mainBundle().URLForResource("file2", withExtension:"m4a")
let asset = AVURLAsset(URL:s, options:nil)
let arr3 = asset.tracksWithMediaType(type3)
let track3 = arr3.last as AVAssetTrack
let comptrack3 = comp.addMutableTrackWithMediaType(type3, preferredTrackID:Int32(kCMPersistentTrackID_Invalid))
comptrack3.insertTimeRange(CMTimeRangeMake(CMTimeMakeWithSeconds(0,600), CMTimeMakeWithSeconds(10,600)), ofTrack:track3, atTime:CMTimeMakeWithSeconds(0,600), error:nil)
let params = AVMutableAudioMixInputParameters(track:comptrack3)
params.setVolume(1, atTime:CMTimeMakeWithSeconds(0,600))
params.setVolumeRampFromStartVolume(1, toEndVolume:0, timeRange:CMTimeRangeMake(CMTimeMakeWithSeconds(7,600), CMTimeMakeWithSeconds(3,600)))
let mix = AVMutableAudioMix()
mix.inputParameters = [params]
let item = AVPlayerItem(asset:comp)
item.audioMix = mix
It's hard to understand from your question what you're trying to do, but I have a vague suspicion that you need to read up on the AVAssetExportSession class.
https://developer.apple.com/library/ios/Documentation/AVFoundation/Reference/AVAssetExportSession_Class/index.html#//apple_ref/occ/cl/AVAssetExportSession