Created
January 8, 2018 15:14
-
-
Save alejandroivan/5c995d2883f58ebc76d24792dd99c837 to your computer and use it in GitHub Desktop.
[Swift4-iOS] Convertir video a MP4 y enviarlo servidor web mediante multipart/form-data (Alamofire)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import AVFoundation | |
import Photos | |
import Alamofire | |
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { | |
let imagePicker = UIImagePickerController() | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
// Do any additional setup after loading the view, typically from a nib. | |
imagePicker.sourceType = .camera | |
imagePicker.delegate = self | |
imagePicker.showsCameraControls = true | |
imagePicker.allowsEditing = true | |
imagePicker.mediaTypes = ["public.movie"] | |
DispatchQueue.main.async { | |
self.present(self.imagePicker, animated: true, completion: nil) | |
} | |
} | |
// MARK: UIImagePickerControllerDelegate | |
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { | |
print("Cancelled video operation.") | |
dismiss(animated: true, completion: nil) | |
} | |
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { | |
guard let mediaType = info[UIImagePickerControllerMediaType] as? String else { | |
print("Error with media type. Cancelling.") | |
dismiss(animated: true, completion: nil) | |
return; | |
} | |
print("Media type: \(mediaType)") | |
if ( mediaType == "public.movie" ) { // Video selected | |
let videoURL: URL | |
if info[UIImagePickerControllerMediaURL] != nil { | |
videoURL = info[UIImagePickerControllerMediaURL] as! URL | |
} | |
else { | |
videoURL = info[UIImagePickerControllerReferenceURL] as! URL | |
} | |
if ( picker.sourceType == .camera ) { | |
// The original video came from the camera, so it's considered new | |
// Save it to the photo library | |
saveVideo(url: videoURL, albumName: "MyApp") | |
} | |
// Dismiss the media picker and then re-encode the video | |
dismiss(animated: true) { | |
self.exportVideoToMP4(url: videoURL) { (exportedVideoURL) in | |
guard let tempURL = exportedVideoURL else { | |
print("ERROR: Unknown error. The exported video URL is nil.") | |
return | |
} | |
print("Temporary video successfully exported to: \(tempURL.absoluteString)") | |
// TODO: Add your own POST parameters | |
let uuid = UUID().uuidString | |
let params = [ | |
"uuid" : uuid, | |
"user" : "myUserNameOrWhatever" | |
] | |
// TODO: Change the parameters for uploading | |
self.upload( to: "http://yourweb.com/uploadVideo.php", // The URL to send the upload to | |
videoURL: tempURL, // The file URL of the temporary video file | |
parameters: params, // The POST parameters you want to send along with the upload | |
fileName: "vid-\(uuid).mp4", // The filename you want the server to receive. | |
fieldName: "video_file" // This is "name" from <input type="file" name="video_file" ... /> in HTML | |
) { (response) in | |
guard let resp = response else { | |
print("ERROR: Empty or unrecognizable response from server.") | |
return | |
} | |
print("Video uploaded. RESPONSE: \(resp)") | |
// TODO: Parse the server response after uploading | |
} | |
} | |
} | |
} | |
} | |
// MARK: Photo Library | |
func saveVideo(url: URL, albumName: String) { | |
// Check authorization status before trying to save the video | |
switch PHPhotoLibrary.authorizationStatus() { | |
case .notDetermined: | |
PHPhotoLibrary.requestAuthorization() { (status) in | |
switch status { | |
case .authorized: | |
self.saveVideo(url: url, albumName: albumName) // Re-try to save the video after authorization | |
return | |
default: | |
return | |
} | |
} | |
case .authorized: | |
// Save the video to the Photo Library here | |
if let assetCollection = assetCollection(albumName: albumName) { | |
// Asset collection exists, insert directly | |
insertVideo(url: url, assetCollection: assetCollection) | |
} | |
else { | |
// Asset collection doesn't exist, create it and then insert | |
PHPhotoLibrary.shared().performChanges({ | |
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: albumName) | |
}, completionHandler: { (success, error) in | |
guard success else { | |
print("ERROR: \(error!.localizedDescription)") | |
return | |
} | |
let createdAssetCollection = self.assetCollection(albumName: albumName)! | |
self.insertVideo(url: url, assetCollection: createdAssetCollection) | |
}) | |
} | |
return | |
default: | |
// Not authorized | |
print("Not authorized to save a video to the Photo Library.") | |
return | |
} | |
} | |
func assetCollection(albumName: String) -> PHAssetCollection? { | |
let fetchOptions = PHFetchOptions() | |
fetchOptions.predicate = NSPredicate(format:"title == '\(albumName)'") | |
let fetchResult = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: fetchOptions) | |
return fetchResult.firstObject | |
} | |
func insertVideo(url: URL?, assetCollection: PHAssetCollection) { | |
guard let videoURL = url else { | |
print("ERROR: The URL to insert into the Photo Library is empty.") | |
return | |
} | |
PHPhotoLibrary.shared().performChanges({ | |
let createAssetRequest = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL) | |
let assetPlaceholder = createAssetRequest?.placeholderForCreatedAsset | |
let changeRequest = PHAssetCollectionChangeRequest(for: assetCollection) | |
let enumeration: NSArray = [assetPlaceholder!] | |
changeRequest?.addAssets(enumeration) | |
}, completionHandler: { (success, error) in | |
guard success else { | |
print("ERROR: \(error!.localizedDescription)") | |
return | |
} | |
print("Video saved successfully to the Photo Library album.") | |
}) | |
} | |
// MARK: Video upload | |
func upload(to uploadAddress: String, videoURL: URL, parameters: [String:Any]?, fileName: String, fieldName: String, _ completion: ((String?) -> Void)?) { | |
Alamofire.upload(multipartFormData: { (multipartFormData) in | |
// Add the video file (if data is correct) | |
if let videoData = FileManager.default.contents(atPath: videoURL.path) { | |
multipartFormData.append(videoData, withName: fileName) | |
} | |
// Add the post params (if available) | |
if let params = parameters { | |
for (key, value) in params { | |
multipartFormData.append( (value as! String).data(using: .utf8)! , withName: key) | |
} | |
} | |
}, to: uploadAddress) | |
{ (result) in | |
switch result { | |
case .success(let upload, _, _): | |
upload.responseString { (response) in | |
if let completionHandler = completion { | |
completionHandler(response.result.value) | |
} | |
} | |
case .failure(let encodingError): | |
print("ERROR: \(encodingError.localizedDescription)") | |
if let completionHandler = completion { | |
completionHandler(nil) | |
} | |
} | |
} | |
} | |
// MARK: AVFoundation | |
func exportVideoToMP4(url: URL, _ completion: @escaping ((URL?) -> Void)) { | |
// Show some sort of indicator here, as this could take a while | |
// Generate a temporary URL path to export the video | |
let relativePath = "myAppTempVideoExport.mp4"; | |
let outputFilePath = NSTemporaryDirectory() + relativePath; | |
print("Temp file path: \(outputFilePath)") | |
// If there's any temp file from before at that path, delete it | |
if FileManager.default.fileExists(atPath: outputFilePath) { | |
do { | |
try FileManager.default.removeItem(atPath: outputFilePath) | |
} | |
catch { | |
print("ERROR: Can't remove temporary file from before. Cancelling export.") | |
completion(nil) | |
return | |
} | |
} | |
// Export session setup | |
let outputFileURL = URL(fileURLWithPath: outputFilePath) | |
let asset = AVAsset(url: url) // Original (source) video | |
// The original video codec is probably HEVC, so we'll force the system to re-encode it at the highest quality in MP4 | |
// You probably want to use medium quality if this video is intended to be uploaded (as this example is doing) | |
if let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) { | |
exportSession.outputURL = outputFileURL | |
exportSession.outputFileType = .mp4 | |
exportSession.exportAsynchronously { | |
// Hide the indicator for the export session | |
switch exportSession.status { | |
case .completed: | |
print("Video export completed.") | |
completion(outputFileURL) | |
return | |
case .failed: | |
print("ERROR: Video export failed. \(exportSession.error!.localizedDescription)") | |
completion(nil) | |
return | |
case .cancelled: | |
print("Video export cancelled.") | |
completion(nil) | |
return | |
default: | |
break | |
} | |
} | |
} | |
else { | |
print("ERROR: Cannot create an AVAssetExportSession.") | |
return | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uses DebugLog-Swift4