AVAssetExportSession in share extension - ios

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)
}
}

Related

Convert mov format video file to mp4 video in Swift

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
}
})
}

Is there any way to play the 3gpp video on Iphone?

I'm getting the video from the Twilio server is in 3gpp video format, and when I tried to convert that 3gpp video to mp4 then it is not converting.
For converting the video to mp4 I'm using this code.
func encodeVideo(videoURL: URL){
let avAsset = AVURLAsset(url: videoURL)
let startDate = Date()
let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetPassthrough)
let docDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let myDocPath = NSURL(fileURLWithPath: docDir).appendingPathComponent("temp.mp4")?.absoluteString
let docDir2 = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL
let filePath = docDir2.appendingPathComponent("rendered-Video.mp4")
deleteFile(filePath!)
if FileManager.default.fileExists(atPath: myDocPath!){
do{
try FileManager.default.removeItem(atPath: myDocPath!)
}catch let error{
print(error)
}
}
exportSession?.outputURL = filePath
exportSession?.outputFileType = AVFileType.mp4
exportSession?.shouldOptimizeForNetworkUse = true
let start = CMTimeMakeWithSeconds(0.0, preferredTimescale: 0)
let range = CMTimeRange(start: start, duration: avAsset.duration)
exportSession?.timeRange = range
exportSession!.exportAsynchronously{() -> Void in
switch exportSession!.status{
case .failed:
print("\(exportSession!.error!)")
case .cancelled:
print("Export cancelled")
case .completed:
let endDate = Date()
let time = endDate.timeIntervalSince(startDate)
print(time)
print("Successful")
print(exportSession?.outputURL ?? "")
default:
break
}
}
}
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).")
}
}
}
While using this code I'm getting this error.
AVFoundationErrorDomain Code=-11838 "Operation Stopped" UserInfo={NSLocalizedFailureReason=The operation is not supported for this media., NSLocalizedDescription=Operation Stopped, NSUnderlyingError=0x2834f0240 {Error Domain=NSOSStatusErrorDomain Code=-16976 "(null)"}}
Step for Conversion that I'm following:
First I download the video from the Twilio URL
Save that video in the document directory
and then pass the document directory video URL in the above function.
You need to use AVAssetExportSession to convert videos to .mp4 format, below method convert .3gpp format videos to .mp4.
Check the line exportSession?.outputFileType = .mp4. it specify the output format of the video.
Here inputURL is an url of video which needs to be converted and outputURL will be the final destination of video.
One more thing don't forget to specify .mp4 extension in outputURL video file
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory = paths[0]
let filePath = URL(fileURLWithPath: documentsDirectory).appendingPathComponent("Video.mp4").absoluteString
let outputURL = URL(fileURLWithPath: filePath)
convertVideoToLowQuailty(withInputURL: inputUrl, outputURL: outputURL, handler: { exportSession in
if exportSession?.status == .completed {
// Video conversation completed
}
})
func convertVideoToLowQuailty(withInputURL inputURL: URL?, outputURL: URL?, handler: #escaping (AVAssetExportSession?) -> Void) {
if let anURL = outputURL {
try? FileManager.default.removeItem(at: anURL)
}
var asset: AVURLAsset? = nil
if let anURL = inputURL {
asset = AVURLAsset(url: anURL, options: nil)
}
var exportSession: AVAssetExportSession? = nil
if let anAsset = asset {
exportSession = AVAssetExportSession(asset: anAsset, presetName: AVAssetExportPresetPassthrough)
}
exportSession?.outputURL = outputURL
exportSession?.outputFileType = .mp4
exportSession?.exportAsynchronously(completionHandler: {
handler(exportSession)
})
}

Firebase Storage -File at URL: is not reachable. ensure file url is not a directory, symbolic link, or invalid url

I have a video in one location that I want to copy to another location but I keep getting an error
File at URL:
https://firebasestorage.googleapis.com/v0/b/myapp.appspot.com/o/...mp4?alt=media&token=...
is not reachable. Ensure file URL is not a directory, symbolic link,
or invalid url.
let strFromFirstLocation = "https://firebasestorage.googleapis.com/v0/b/myapp.appspot.com/o/...mp4?alt=media&token=..." // this url is alive
guard let url = URL(string: strFromFirstLocation) else { return }
let secondLocationRef = Storage.storage().reference().child("copy").child(postId)
secondLocationRef.putFile(from: url, metadata: nil, completion: { (metadata, error) in
if let error = error {
print(error.localizedDescription)
return
}
})
I had to do 2 things to fix the issue:
1- I had to convert the first firebase url to AVURLAsset > AVMutableComposition > AVAssetExportSession and I used the exporter.outputURL to save instead.
let strFromFirstLocation = "https://firebasestorage.googleapis.com/v0/b/myapp.appspot.com/o/...mp4?alt=media&token=..."
guard let url = URL(string: strFromFirstLocation) else { return }
let asset = AVURLAsset(url: url)
let mixComposition = AVMutableComposition()
// ...
guard let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
// ...
guard let exporterOutputURL = exporter.outputURL else { return }
2- I had to add .mp4 to the path as in "\(postId).mp4":
let secondLocationRef = Storage.storage().reference().child("copy").child("\(postId).mp4")
secondLocationRef.putFile(from: exporterOutputURL, metadata: nil, completion: { (metadata, error) in
if let error = error {
print(error.localizedDescription)
return
}
})

How to download mp3 file from MPMediaPickerController using swift language?

My application is download song from MPMediaPickerController and save mp3 file in Document directory
File is download successfully in .m4a formate, But i need mp3 file. i'm already change file extension .m4a to .mp3 but this file is not playing.
I have tried a lot of near by examples but didn't succeed to solve this issue.
How can I do it?
func mediaPicker(_ mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {
mediaPicker.dismiss(animated: true) {
let item: MPMediaItem = mediaItemCollection.items[0]
let pathURL: URL? = item.value(forProperty: MPMediaItemPropertyAssetURL) as? URL
if pathURL == nil {
self.alert(message: "Sorry, the returned mediaItemCollection appeart to be empty")
return
}
let song_Title = item.value(forProperty: MPMediaItemPropertyTitle) as! String
let filename_song = song_Title.replacingOccurrences(of: " ", with: "_")
// get file extension andmime type
let str = pathURL!.absoluteString
let str2 = str.replacingOccurrences( of : "ipod-library://item/item", with: "")
let arr = str2.components(separatedBy: "?")
var mimeType = arr[0]
mimeType = mimeType.replacingOccurrences( of : ".", with: "")
// Export the ipod library as .m4a file to local directory for remote upload
let exportSession = AVAssetExportSession(asset: AVAsset(url: pathURL!), presetName: AVAssetExportPresetAppleM4A)
exportSession?.shouldOptimizeForNetworkUse = true
exportSession?.outputFileType = AVFileType.m4a
exportSession?.metadata = AVAsset(url: pathURL!).metadata
let documentURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let outputURL = documentURL.appendingPathComponent("\(filename_song).m4a")
//Delete Existing file
do {
try FileManager.default.removeItem(at: outputURL)
} catch let error as NSError {
print(error.debugDescription)
}
exportSession?.outputURL = outputURL
exportSession?.exportAsynchronously(completionHandler: { () -> Void in
if (exportSession!.status == AVAssetExportSession.Status.completed)
{
print("AV export succeeded.")
self.alert(message: "File download successfully")
do {
let newURL = outputURL.deletingPathExtension().appendingPathExtension("mp3")
let str = try FileManager.default.moveItem(at: outputURL, to: newURL)
print(str)
} catch {
print("The file could not be loaded")
}
}
else if (exportSession!.status == AVAssetExportSession.Status.cancelled)
{
print("AV export cancelled.")
}
else
{
print("AV export failed with error:- ", exportSession!.error!.localizedDescription)
}
})
}
}

AudioFile trimming not working

in my app i am trim the audio file.
but its not working every-time its showing me failed
this is my code
#IBAction func trimAudioPressed(sender: AnyObject) {
isTrim = true
let audioFileInput = filepath
let date = NSDate()
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy_MMM_dd_HH_mm_ss"
let strdate = formatter.stringFromDate(date)
print(NSUserDefaults.standardUserDefaults().valueForKey("fileURL"))
let extensionofFile = filepath.pathExtension!
let strOutputFilePath123 : String = String(filepath.URLByDeletingLastPathComponent!)
strOutputFilePath = "\(strOutputFilePath123)\(strdate).\(extensionofFile)"
let fileManger = NSFileManager.defaultManager()
if fileManger.fileExistsAtPath(strOutputFilePath) {
do {
(try fileManger.removeItemAtPath(strOutputFilePath))
}
catch let error { print(error)
}
}
let audioFileOutput : NSURL = NSURL(string: strOutputFilePath)!
let asset = AVAsset(URL: audioFileInput)
let succcess = self.exportAsset(asset, toFilePath: strOutputFilePath)
print(succcess)
self.dismissView()
}
func exportAsset(avAsset: AVAsset, toFilePath filePath: String) -> Bool {
var tracks = avAsset.tracksWithMediaType(AVMediaTypeAudio)
if tracks.count == 0 {
return false
}
let track = tracks[0]
let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetAppleM4A)
let supportedTypeArray = exportSession!.supportedFileTypes
for str: String in supportedTypeArray {
if nil == exportSession {
return false
}
}
print(leftV)
print(rightV)
let startTime = CMTimeMake(Int64(leftV), 1)
let stopTime = CMTimeMake(Int64(rightV), 1)
let exportTimeRange = CMTimeRangeFromTimeToTime(startTime, stopTime)
let exportAudioMix = AVMutableAudioMix()
let exportAudioMixInputParameters = AVMutableAudioMixInputParameters(track: track)
exportAudioMix.inputParameters = [exportAudioMixInputParameters]
exportSession!.outputURL = NSURL.fileURLWithPath(filePath)
exportSession!.outputFileType = AVFileTypeAppleM4A
exportSession!.timeRange = exportTimeRange
exportSession!.audioMix = exportAudioMix
exportSession!.exportAsynchronouslyWithCompletionHandler({() -> Void in
if .Completed == exportSession!.status {
print("Success")
}
else if .Failed == exportSession!.status {
print("Failed")
print("Export Session Status: \(exportSession!.status)")
}
else {
print("Export Session Status: \(exportSession!.status)")
}
})
return true
}
i dont know where its going wrong
here is my string inputfile path
file:///private/var/mobile/Containers/Data/Application/07166600-AAEA-436F-BE6B-93839C180F19/Documents/Default/recording-2016-08-31-18-28-12.m4a
here is my output file path
file:///private/var/mobile/Containers/Data/Application/07166600-AAEA-436F-BE6B-93839C180F19/Documents/Default/2016_Aug_31_18_38_31.m4a
or even starttime and stop time is also perfect. so how can i solve this. and let me knonw if there is any other approch.
this is the error which i got
export failed Optional(Error Domain=NSURLErrorDomain Code=-3000 "Cannot create file" UserInfo={NSLocalizedDescription=Cannot create file, NSUnderlyingError=0x7fc9415142b0 {Error Domain=NSOSStatusErrorDomain Code=-12115 "(null)"}})
The line
exportSession!.outputURL = NSURL.fileURLWithPath(filePath)
is wrong. filePath is not a path; it is already a file:// URL.
The quick fix is to change the outputURL to this:
exportSession!.outputURL = NSURL(string: filePath)!
There's confusion between file paths and file URLs in this code. e.g. filepath is a file NSURL, but strOutputFilePath123 is a String that contains a file:// scheme URI.
To convert a file NSURL to a String path, you should use url.path.

Resources