Swift get NSData of a video from photos library - swift

Im using UIImagePickerController to select a video from my library. I need to extract the NSData of my video file. Im using the following operation to select a video from my library but my data appears to be nil however my AVPlayer plays the video from the resulting NSURL so I know problem is not with the NSURL. How can I extract the NSData of my selected video file?
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let fileURL = info[UIImagePickerControllerReferenceURL] as! NSURL!
let data = NSData(contentsOfURL: fileURL)
print(data)
player = AVPlayer(URL: fileURL)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = CGRectMake(0, 0, 300, 300)
self.view.layer.addSublayer(playerLayer)
player.play()
self.dismissViewControllerAnimated(true, completion: nil)
}

Try changing UIImagePickerControllerReferenceURL to
UIImagePickerControllerMediaURL
Instead of let data = NSData(contentsOfURL: fileURL)
let video3 = try NSData(contentsOfURL: videoDataURL, options:
.DataReadingMappedIfSafe)

Related

How to get a PHAsset of the video from a video URL?

How do I get a PHAsset of a captured video, if I've got only a video URL? My problem is that I know how to get URL of a video from PHAsset, but don't understand how to get PHAsset from video URL.
This is what I tried to do, but it didn't work. It's not for a video, but for a photo...So Idk how to make it for a video.
let imgData = NSData(contentsOfURL: (NSURL(string: "file:///var/mobile/Media/DCIM/100APPLE/IMG_0043.JPG"))!)
let image = UIImage(data: imgData!)
Here is the answer in code to the question...
PHPhotoLibrary.shared().performChanges({
let assetChangeRequest = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: uurl)
let assetPlaceholder = assetChangeRequest!.placeholderForCreatedAsset
let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection)
}, completionHandler: nil)

How to save video to app's directory and playback in view controller?

I'm trying to save a video in my app directory and then play it back in my view controller. I'm having an issue with saving and making the path. Can anyone help?
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
// Save the video to the app directory
let videoURL = info[UIImagePickerControllerMediaURL] as! NSURL
let videoData = NSData(contentsOf: videoURL as URL)
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let documentsDirectory: AnyObject = paths[0] as AnyObject
let dataPath = documentsDirectory.appendingPathComponent("/vid1.mp4")
videoData?.write(toFile: dataPath, atomically: false)
self.dismiss(animated: true, completion: nil)
}
#IBAction func playVideoAction(_ sender: Any)
{
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let documentsDirectory: AnyObject = paths[0] as AnyObject
let dataPath = documentsDirectory.appendingPathComponent("/vid1.mp4")
let videoAsset = (AVAsset(url: NSURL(fileURLWithPath: dataPath) as URL))
let playerItem = AVPlayerItem(asset: videoAsset)
let player = AVPlayer(playerItem: playerItem)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true)
{
playerViewController.player!.play()
}
}
how to save video file into document directory
I've used code from this link but with the update to xcode 8/swift 3 it is not as helpful. Been stuck on this problem for awhile now.
You want to use PHImageManager.requestExportSession(forVideo:options:). This will prepare the asset asynchronously (including downloading it if needed), and create an AVExportSession you can use to save the file. By specifying you want the original, and to passthrough the content (if possible) you should get the best quality video available.
var dataPath: URL {
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let documentsDirectory = URL(fileURLWithPath: paths[0])
return documentsDirectory.appendingPathComponent("/vid1.mp4")
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let url = info[UIImagePickerControllerReferenceURL] as! URL
let assets = PHAsset.fetchAssets(withALAssetURLs: [url], options: nil)
let asset = assets.object(at: 0)
let options = PHVideoRequestOptions()
options.version = .original
PHImageManager.default().requestExportSession(forVideo: asset, options: options, exportPreset: AVAssetExportPresetPassthrough) { (exportSession, info) in
guard let session = exportSession else { return }
session.outputURL = self.dataPath
session.outputFileType = AVFileTypeMPEG4
session.exportAsynchronously {
DispatchQueue.main.async {
self.dismiss(animated: true, completion: nil)
}
}
}
}
If you didn't want to save to disk but just playback, you can use requestPlayerItem to get an AVPlayerItem you can use in your player:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let url = info[UIImagePickerControllerReferenceURL] as! URL
let assets = PHAsset.fetchAssets(withALAssetURLs: [url], options: nil)
let asset = assets.object(at: 0)
let options = PHVideoRequestOptions()
options.version = .original
PHImageManager.default().requestPlayerItem(forVideo: asset, options: options) { (playerItem, info) in
let player = AVPlayer(playerItem: playerItem)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
DispatchQueue.main.async {
self.dismiss(animated: true, completion: nil)
self.present(playerViewController, animated: true) {
playerViewController.player!.play()
}
}
}
}

Swift / iOS 10 : How to download a VIDEO and store within the app

I'm new to SWIFT/Programming &
I couldn't find an answer on my question, that's why I'm gonna give it a try here:
HOW Do I download a video (mp4) from an URL and store it within the app**
HOW Do I display the video then in a container**
I've already found this topic:
Swift - Downloading video with downloadTaskWithURL
But in my case, I wouldn't want the video to be safed in the camera-roll. Just within the app.
Thanks for any kind of help/hint !
You can use URLSession's dataTask or downloadTask to download any file from url(if it's downloadble)
Here's the way to use dataTask for downloading:
let videoUrl = "Some video url"
let docsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let destinationUrl = docsUrl.appendingPathComponent("MyFileSaveName.mp4")
if(FileManager().fileExists(atPath: destinationUrl.path)){
print("\n\nfile already exists\n\n")
}
else{
//DispatchQueue.global(qos: .background).async {
var request = URLRequest(url: URL(string: videoUrl)!)
request.httpMethod = "GET"
_ = session.dataTask(with: request, completionHandler: { (data, response, error) in
if(error != nil){
print("\n\nsome error occured\n\n")
return
}
if let response = response as? HTTPURLResponse{
if response.statusCode == 200{
DispatchQueue.main.async {
if let data = data{
if let _ = try? data.write(to: destinationUrl, options: Data.WritingOptions.atomic){
print("\n\nurl data written\n\n")
}
else{
print("\n\nerror again\n\n")
}
}//end if let data
}//end dispatch main
}//end if let response.status
}
}).resume()
//}//end dispatch global
}//end outer else
Now to play the saved file:
class MyViewController: UIViewController {
override func viewDidLoad() {
let baseUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let assetUrl = baseUrl.appendingPathComponent("MyFileSaveName.mp4")
let url = assetUrl
print(url)
let avAssest = AVAsset(url: url)
let playerItem = AVPlayerItem(asset: avAssest)
let player = AVPlayer(playerItem: playerItem)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true, completion: {
player.play()
})
}
}
However, most sites do not provide a direct dwonloadable link for video. You can get that link by playing the video in a UIWebView and register this following observer to get that link:
NotificationCenter.default.addObserver(self, selector: #selector(videoPlayedInWebView), name: NSNotification.Name(rawValue: "AVPlayerItemBecameCurrentNotification"), object: nil)
#objc func videoPlayedInWebView(aNotification: NSNotification) {
if let playerItem: AVPlayerItem = aNotification.object as? AVPlayerItem{
let asset: AVURLAsset = playerItem.asset as! AVURLAsset
var downloadebaleVideoUrl = asset.url
print(downloadebaleVideoUrl)
}
}
Here "downloadebaleVideoUrl" is the link that will be generated once the video plays in the webview.
If you have any questions, feel free to ask.
Note: This is will work only for sites that have mp4 files. 'HLS' streams won't be downloaded with this method. For that you can refer to this following answer:
https://stackoverflow.com/a/54493233/10758374
Edit: this works only with UIWebView and it won't work with WKWebView.
You need to create a local url, that will be a path in your app's file system and write the video's data into it.
func writeToFile(urlString: String) {
guard let videoUrl = URL(string: urlString) else {
return
}
do {
let videoData = try Data(contentsOf: videoUrl)
let fm = FileManager.default
guard let docUrl = fm.urls(for: .documentDirectory, in: .userDomainMask).first else {
print("Unable to reach the documents folder")
return false
}
let localUrl = docUrl.appendingPathComponent("test.mp4")
try videoData.write(to: localUrl)
} catch {
print("could not save data")
}
}
Keep in mind to always call this function in the background thread.

Document file path unreachable in swift

I'm currently working on a small swift application and I'm storing some video records in the documents folder of the app. I would like to retrieve these on a later moment. I already got an array of file locations like this:
file:///private/var/mobile/Containers/Data/Application/6C462C4E-05E2-436F-B2E6-F6D9AAAC9361/Documents/videorecords/196F9A75-28C4-4B65-A06B-6111AEF85F01.mov
Now I want to use such file location to create a thumbnail with the first frame and connect that to my imageview with the following piece of code:
func createVideoStills() {
for video in directoryContents {
print("\(video)")
do {
let asset = AVURLAsset(URL: NSURL(fileURLWithPath: "\(video)"), options: nil)
let imgGenerator = AVAssetImageGenerator(asset: asset)
imgGenerator.appliesPreferredTrackTransform = true
let cgImage = try imgGenerator.copyCGImageAtTime(CMTimeMake(0, 1), actualTime: nil)
let uiImage = UIImage(CGImage: cgImage)
videoCell.imageView = UIImageView(image: uiImage)
//let imageView = UIImageView(image: uiImage)
} catch let error as NSError {
print("Error generating thumbnail: \(error)")
}
}
}
The first print gives me a path like described above. But the AVURLAsset doesn't like this path because it spits out the following error:
Error generating thumbnail: Error Domain=NSURLErrorDomain Code=-1100
"The requested URL was not found on this server."
UserInfo={NSLocalizedDescription=The requested URL was not found on
this server., NSUnderlyingError=0x14ee29170 {Error
Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
Which is weird cause it is right there. Any solutions on how to fix/solve this?
Kind regards,
Wouter
The output of your print("\(video)") is not a file path but a string representation of file URL. You need to use init(string:) than init(fileURLWithPath:) of NSURL.
See what you get with:
let asset = AVURLAsset(URL: NSURL(string: video), options: nil)
(Unnecessary string interpolation would generate some unexpected result without errors -- like getting "Optional(...)", so you should avoid.)
// if You Are Using PHPickerViewController then do this for fetching the url.
// ------
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: nil)
guard let provider = results.first?.itemProvider else { return }
if provider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) {
provider.loadItem(forTypeIdentifier: UTType.movie.identifier, options: [:]) { [self] (videoURL, error) in
print("resullt:", videoURL, error)
DispatchQueue.main.async {
if let url = videoURL as? URL {
let player = AVPlayer(url: url)
let playerVC = AVPlayerViewController()
playerVC.player = player
present(playerVC, animated: true, completion: nil)
}
}
}
}
}

AVPlayer Not Playing Videos

I'm trying to play video from my parse server I'm getting empty video player And when i print the url i get the correct video url
let thevideo:PFFile! = (self.selectedmsg["file"] as! PFFile)
let url:NSURL = NSURL(string: thevideo.url!)!
let player = AVPlayer(URL: url)
let playercontroller = AVPlayerViewController()
playercontroller.player = player
self.presentViewController(playercontroller, animated: true) {
player.play()
}
Any help? to play videos from my parse.