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)
}
}
}
}
}
Related
So I set up a PHPickerViewController, and it uploads the videos from the camera roll (slowly ugh, but it does it). Sometimes, the video uploads. Other times, I get this error (especially with longer videos), and I'm unsure why:
Error Domain=NSItemProviderErrorDomain Code=-1000 "Cannot load representation of type com.apple.quicktime-movie" UserInfo={NSLocalizedDescription=Cannot load representation of type com.apple.quicktime-movie}
Here is my code:
func uploadVideo(videoURL: URL)
{
let storage = Storage.storage()
let storageRef = storage.reference()
let videoRef = storageRef.child("rPosts/\(uid!)/\(fileID).mov")
let metadata = StorageMetadata()
metadata.contentType = "video/quicktime"
var videoData: Data = Data()
do
{
videoData = try Data(contentsOf: videoURL)
}
catch
{
print(error.localizedDescription)
return
}
videoRef.putData(videoData, metadata: metadata)
{ (metaData, error) in
guard error == nil else
{
self.errorLabel.text = error!.localizedDescription
return
}
print("greenchecktimeebabyyyy AHHHH")
}
}
extension postingRViewController: PHPickerViewControllerDelegate
{
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: nil)
for result in results {
print("made result")
var went = false
result.itemProvider.loadFileRepresentation(forTypeIdentifier: "com.apple.quicktime-movie")
{ videoURL, error in
result.itemProvider.loadFileRepresentation(forTypeIdentifier: "com.apple.quicktime-movie") { videoURL, error in
assert(Thread.isMainThread == false)
let directory = NSTemporaryDirectory()
let fileName = NSUUID().uuidString.appending(".mov")
print("loading")
went = true
if let videoURL = videoURL,
let copiedURLFile = NSURL.fileURL(withPathComponents: [directory, fileName]) {
try! FileManager.default.copyItem(at: videoURL, to: copiedURLFile)
DispatchQueue.main.async {
// the videourl is deleted. Only copiedURLFile exists
// upload_to_firebase(video url)
self.uploadVideo(videoURL: copiedURLFile)
print("uploaded to the cloud")
// after the video is presented or the file uploaded, delete copiedURLFile
}
}
else { print(error!) }
}
}
}
The error is saying that NSItemProvider cannot load your video file. When loading, you're setting the identifier to com.apple.quicktime-movie which refers to .mov files.
With that in mind, perhaps you're trying to load a .mp4. Try to use Apple's UniformTypeIdentifiers framework, so your identifier would be: UTType.movie.identifier.
Note: don't forget to import UniformTypeIdentifiers
I'm trying to get video thumbnails with the following code:
let asset = AVAsset(URL: url)
let imageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.appliesPreferredTrackTransform = true
do {
let cgImage = try imgGenerator.copyCGImageAtTime(CMTimeMake(1, 30), actualTime: nil)
let uiImage = UIImage(CGImage: cgImage)
imageview.image = uiImage
}
catch let error as NSError
{
print("Image generation failed with error \(error)")
}
Sometimes it works and sometime it doesn't showing the following error:
Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could
not be completed" UserInfo={NSLocalizedDescription=The operation could
not be completed, NSUnderlyingError=0x14eab520 {Error
Domain=NSOSStatusErrorDomain Code=-12792 "(null)"},
NSLocalizedFailureReason=An unknown error occurred (-12792)}
I have tried to figure out what is Domain=NSOSStatusErrorDomain Code=-12792 but I don't understand how I can get more details about this error code. How can I convert this error code into a string to get relevant information about what this error means?
I was able to solve this issue by the following approach.
Swift 4.1
func createThumbnailForVideo(atURL videoURL: URL , completion : #escaping (UIImage?)->Void) {
let asset = AVAsset(url: videoURL)
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
let time = CMTimeMakeWithSeconds(1, preferredTimescale: 60)
let times = [NSValue(time: time)]
assetImgGenerate.generateCGImagesAsynchronously(forTimes: times, completionHandler: { _, image, _, _, _ in
if let image = image {
let uiImage = UIImage(cgImage: image)
completion(uiImage)
} else {
completion(nil)
}
})
}
Hope this will help.
I am working on an app where I am getting videos from a firebase link.
To be able to display and play the video I am using this extension:
func generateThumbnailForVideoAtURL(filePathLocal: NSString) -> UIImage? {
let vidURL = NSURL(fileURLWithPath:filePathLocal as String)
let asset = AVURLAsset(url: vidURL as URL)
let generator = AVAssetImageGenerator(asset: asset)
generator.appliesPreferredTrackTransform = true
let timestamp = CMTime(seconds: 1, preferredTimescale: 60)
do {
let imageRef = try generator.copyCGImage(at: timestamp, actualTime: nil)
return UIImage(cgImage: imageRef)
} catch let error as NSError {
print("Image generation failed with error \(error)")
return nil
}
}
And then using it like this in cellAtIndexPath:
if posts[indexPath.row].videoUrl != nil {
cell.videoView.generateThumbnailForVideoAtURL(filePathLocal: self.posts[indexPath.row].videoUrl as! NSString)
print("VIDEO IS: \(posts[indexPath.row].videoUrl)")
}
I am getting this error:
Image generation failed with error Error Domain=NSURLErrorDomain Code=-1102 "You do not have permission to access the requested resource." UserInfo={NSLocalizedDescription=You do not have permission to access the requested resource., NSUnderlyingError=0x1c4046ff0 {Error Domain=NSOSStatusErrorDomain Code=-12660 "(null)"}}
It seems like a UserInfo description, but I can not find anything in my plist to add for permission. It may just sound stupid for somebody else because this is how I feel now as I can't find a way to make it work. Please help!
You should use this
let vidURL = URL(string:filePathLocal as String)
instead of
let vidURL = NSURL(fileURLWithPath:filePathLocal as String)
So I am accessing the user's videos & photos through a custom UICollectionView, my issue is that when I attempt to upload to firebase the video through the mobile phone, i am getting this error:
2017-09-03 13:09:20.884509-0400 Project[5797:2021536] Cannot get file size: Error Domain=NSCocoaErrorDomain Code=257 "The file “IMG_3476.MP4” couldn’t be opened because you don’t have permission to view it." UserInfo={NSURL=file:///var/mobile/Media/DCIM/103APPLE/IMG_3476.MP4, NSFilePath=/var/mobile/Media/DCIM/103APPLE/IMG_3476.MP4, NSUnderlyingError=0x17064f450 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
/var/mobile/Media/DCIM/103APPLE/IMG_3476.MP4
2017-09-03 13:09:21.261767-0400 Project[5797:2021536] Body file is unreachable: /var/mobile/Media/DCIM/103APPLE/IMG_3476.MP4
Error Domain=NSCocoaErrorDomain Code=257 "The file “IMG_3476.MP4” couldn’t be opened because you don’t have permission to view it." UserInfo={NSURL=file:///var/mobile/Media/DCIM/103APPLE/IMG_3476.MP4, NSFilePath=/var/mobile/Media/DCIM/103APPLE/IMG_3476.MP4, NSUnderlyingError=0x170651b20 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
Here is where I am loading the files
struct Media {
var image:UIImage?
var videoURL:NSURL?
}
var mediaArray = [Media]()
func grabPhotos(){
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
requestOptions.deliveryMode = .highQualityFormat
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
if let fetchResult : PHFetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions) {
if fetchResult.count > 0 {
for i in 0..<fetchResult.count{
var mediaItem = Media()
//Used for fetch Image//
imgManager.requestImage(for: fetchResult.object(at: i) as PHAsset , targetSize: CGSize(width: 400, height: 400), contentMode: .aspectFit, options: requestOptions, resultHandler: {
image, error in
let imageOfVideo = image! as UIImage
mediaItem.image = imageOfVideo;
//Used for fetch Video//
imgManager.requestAVAsset(forVideo: fetchResult.object(at: i) as PHAsset, options: PHVideoRequestOptions(), resultHandler: {(avAsset, audioMix, info) -> Void in
if let asset = avAsset as? AVURLAsset {
let videoData = NSURL(string: "\(asset.url)")
let duration : CMTime = asset.duration
let durationInSecond = CMTimeGetSeconds(duration)
print(durationInSecond)
mediaItem.videoURL = videoData!
self.mediaArray.append(mediaItem)
print(self.mediaArray.count)
}
})
})
}
}
else{
//showAllertToImportImage()//A function to show alert
}
}
}
I dont think it has to do with when I am uploading the video because i can record a video an upload it from the phone, just not upload from the phone itself; I am just transferring over the url to the nextVC like so:
let videoVC = PrepareVideoVC()
videoVC.url = outputFileURL as URL!
self.navigationController?.pushViewController(videoVC, animated: false)
I looked into this The file “ ” couldn’t be opened because you don’t have permission to view it but couldnt really figure out how to implement it
I also came across https://stackoverflow.com/a/41517165/7823620 but once again I tried implementing it but couldnt get it to not give me errors
Also I can upload from the simulator but not the mobile phone
It is trying to get a file from this directory:
NSFilePath=/var/mobile/Media/DCIM/
You can try
url.startAccessingSecurityScopedResource()
<...>
url.stopAccessingSecurityScopedResource()
use this options:
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.version = PHVideoRequestOptionsVersionCurrent;
options.deliveryMode = PHVideoRequestOptionsDeliveryModeHighQualityFormat;
options.networkAccessAllowed = true;
if you request the middle-quality video, then u will get an error " Error Domain=NSCocoaErrorDomain Code=257, no permission..."
So if the AppDelegate gets passed in a document on launch via this method:
func application(_: UIApplication, open url: URL, options _: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
I found that I had to do this to FIX it so that FileManager documents loaded successfully using Ivan's solution above but you CAN NOT make that call if say the document is an e-mail attachment.
This fixed it for me and huge thanks again to Ivan Vavilov for his post.
func application(_: UIApplication, open url: URL, options _: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
print("Doc attachment passed")
let docAttachment = url
// files written to 'tmp' are NOT backed up to iCloud or iTunes
let tempDir = getTempDirectory()
// add file name to tmp path
tempFileToLoad = tempDir.appendingPathComponent(docAttachment.lastPathComponent)
let fm = FileManager()
// passed a file from the FileManger app?
let isAttachmentScopedResource = docAttachment.startAccessingSecurityScopedResource()
do {
// put in tmp so we can read/write
try fm.copyItem(at: docAttachment, to: tempFileToLoad!)
if(isAttachmentScopedResource) {
// have MUST release the security access after calling it
docAttachment.stopAccessingSecurityScopedResource()
}
} catch {
print("Error getting file. \(error)")
return false
}
I'm attempting to grab the file path of an image selected through an imagePickerController in order to upload the file to Firebase Storage.
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
mediaUploadView.image = info[UIImagePickerControllerOriginalImage] as? UIImage
let localUrl: NSURL = (info[UIImagePickerControllerReferenceURL] as! NSURL)
print(localUrl)
let localFile: NSURL = localUrl
let mediaRef = storageRef.child("media")
let uploadTask = mediaRef.putFile(localUrl, metadata: nil) { metadata, error in
if (error != nil) {
} else {
let downloadURL = metadata!.downloadURL
}
}
self.dismissViewControllerAnimated(true, completion: nil)
}
The output reads:
assets-library://asset/asset.JPG?id=C224DA06-6B7B-4BE2-8F5E-2EC7BC8B0526&ext=JPG
Body file is unreachable: /asset.JPG
Error Domain=NSCocoaErrorDomain Code=4 "The file doesn’t exist."
It grabs what I believe is the NSURL, but then is unable to find it for upload.
I have attempted to follow along with this similar problem on stack "How do I get Image reference and upload image to Firebase?" but the output then simply returns nil.
Any help would be great, thank you!
I had trouble getting the info[UIImagePickerControllerOriginalImage] to work. I had to do it like this:
let assets = PHAsset.fetchAssetsWithALAssetURLs([localUrl], options: nil)
let asset = assets.firstObject
asset?.requestContentEditingInputWithOptions(nil, completionHandler: { (contentEditingInput, info) in
let imageFile = contentEditingInput?.fullSizeImageURL
// now call putFile with imageFile instead of localURL