Swift - generating image thumbnail from a local video - ios

I'm trying to create a Thumbnail image from a video:
func getImageFromUrl(url:URL) -> UIImage?{
print(url)
let video = AVURLAsset(url: url)
let thumbnailGenerator = AVAssetImageGenerator(asset: video)
do
{
let cgImage = try thumbnailGenerator.copyCGImage(at: CMTimeMake(value: 0, timescale: 1), actualTime: nil)
let UiImage = UIImage(cgImage: cgImage)
return UiImage
}
catch
{ print(error) }
return nil
}
and I'm getting this Error:
Error Domain=AVFoundationErrorDomain Code=-11850 "Operation Stopped" UserInfo={NSLocalizedFailureReason=The server is not correctly configured., NSLocalizedDescription=Operation Stopped, NSUnderlyingError=0x2804c50b0 {Error Domain=NSOSStatusErrorDomain Code=-12939 "(null)"}}
HELP ANYONE ?

That error tells us that this is an HTTP, not local issue. The Apple Developer Documentation says "This error might indicate that the HTTP server doesn’t support byte range requests." and or that "The HTTP server sending the media resource is not configured as expected." Check to make sure the HTTP server is configured properly and allows this type of query.

It looks like a server issue as peer apple documentation
case serverIncorrectlyConfigured = -11850
This error might indicate that the server doesn’t support byte-range requests.
You can try this video URL to check if your code actually works:
https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4
Will you be able to share the video URL to check?

Also, copyCGImage is Deprecated
Try using image(at:) instead.

Related

QLThumbnailGenerator starts failing when called multiple times (on actual device) iOS 13

I am trying to create thumbnail images of multiple Wallet Passes (.pkpass) by running a loop over all (around 200) passes in a specific folder and calling generateBestRepresentation(for:) for each of them.
This is the code:
let passesDirURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("Passes")
let size = CGSize(width: 1600, height: 1600)
let scale = UIScreen.main.scale
if let passURLs = try? FileManager.default.contentsOfDirectory(
at: self.passesDirURL,
includingPropertiesForKeys: nil,
options: .skipsHiddenFiles
),
!passURLs.isEmpty {
for passURL in passURLs {
// Create the thumbnail request.
let request = QLThumbnailGenerator.Request(
fileAt: passURL,
size: size,
scale: scale,
representationTypes: .thumbnail
)
// Retrieve the singleton instance of the thumbnail generator and generate the thumbnails.
let generator = QLThumbnailGenerator.shared
generator.generateBestRepresentation(for: request) { thumbnail, error in
if let error = error as? QLThumbnailError {
print ("Thumbnail generation error: \(error)")
print ("Thumbnail generation localizedDescription: \(error.localizedDescription)")
print ("Thumbnail generation errorUserInfo: \(error.errorUserInfo)")
print ("Thumbnail generation errorCode: \(error.errorCode)")
} else {
print ("Thumbnail generation OK")
//do something with thumbnail here
}
}
}
}
This works fine on the simulator, but on an actual device (iPhone Xs Max) sooner or later I start getting errors and the thumbnail generation fails for a big fraction of the passes. The output looks as follows:
Thumbnail generation error: related decl 'e' for QLThumbnailError(_nsError: Error Domain=QLThumbnailErrorDomain Code=3 "No thumbnail in the cloud for file:///private/var/mobile/Containers/Data/Application/DCF703F7-9A1A-4340-86EB-42579D678EEF/Documents/Passes/pass123.pkpass" UserInfo={NSErrorFailingURLKey=file:///private/var/mobile/Containers/Data/Application/DCF703F7-9A1A-4340-86EB-42579D678EEF/Documents/Passes/pass123.pkpass})
Thumbnail generation localizedDescription: The operation couldn’t be completed. (QLThumbnailErrorDomain error 3.)
Thumbnail generation errorUserInfo: ["NSErrorFailingURLKey": file:///private/var/mobile/Containers/Data/Application/DCF703F7-9A1A-4340-86EB-42579D678EEF/Documents/Passes/pass123.pkpass]
Thumbnail generation errorCode: 3
The error description sounds confusing ("No thumbnail in the cloud for file") as these are not iCloud files.
As the error does not occur when calling the thumbnail generation individually, this seems to be some memory/performance issue. I tried to workaround in many ways, including using a semaphore in the for loop waiting for the completion of one call of generateBestRepresentation to start the next call, which reduced but not eliminated the issue. The only way it worked without error was adding a very long sleep (5 seconds) after the semaphore.wait() statement, but this is no acceptable solution.
Another way I tried was using saveBestRepresentation (as suggested in Apple's documentation), but this did not solve the issue.
Has anyone faced a similar issue and was able to find an acceptable solution?

getting error when i trying to convert AVAssetURL to Data

Error Domain=NSCocoaErrorDomain Code=257 "The file “IMG_9807.MOV” couldn’t be opened because you don’t have permission to view it." UserInfo={NSFilePath=/var/mobile/Media/DCIM/109APPLE/IMG_9807.MOV, NSUnderlyingError=0x1c1e5fe00 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"
i am sending asset URL to other controller and try to convert into data
PHImageManager.default().requestAVAsset(forVideo: self.albumView.phAsset, options: options) { (video, audioMix, info) in
DispatchQueue.main.async {
let urlAsset = video as! AVURLAsset
self.dismiss(animated: false, completion: {
self.delegate?.fusumaVideoCompleted(withFileURL: urlAsset.url)
})
}
}
here below methods for convert AVAssetUrl to data
do {
let data = try Data(contentsOf: product.videoURL, options: .mappedIfSafe)
return .upload(.multipart([MultipartFormData(provider: .data(data), name: "post[video]", fileName: "video.\(pathExtension)", mimeType: "video/\(pathExtension)")]))
} catch {
debugPrint(error)
}
As the error tells you, you cannot access the video file in the user's photo library by way of its URL for purposes of uploading it. You should obtain the video data and upload that. A video is very big, so you should not get the data directly and hold it in memory; instead, export the data to a file in a place that you are allowed to access, such as the Temporary folder.
To do that, you might (for example) use this method:
https://developer.apple.com/documentation/photos/phassetresourcemanager/1616280-writedata
Or this one:
https://developer.apple.com/documentation/photos/phimagemanager/1616981-requestexportsession
If you use the Mail app to email a video from your own device's photo library, you will actually see that happening; there is a pause with a progress bar while the video is exported, and then the email is constructed.

Extracting thumbnail from server video url swift

Want to get thumbnail from video url for that I followed this code which is recommended on most of SO questions:
import AVFoundation
private func thumbnailForVideoAtURL(url: NSURL) -> UIImage? {
let asset = AVAsset(URL: url)
let assetImageGenerator = AVAssetImageGenerator(asset: asset)
var time = asset.duration
time.value = min(time.value, 2)
do {
let imageRef = try assetImageGenerator.copyCGImageAtTime(time, actualTime: nil)
return UIImage(CGImage: imageRef)
} catch let error {
print(error)
return nil
}
}
Then my viewdidload method is like this:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let url = NSURL(string: "https://www.youtube.com/watch?v=HrKwx8EdSAE")
if let thumbnailImage = generateThumnail(url!) {
print("hello");
self.imageView.image = thumbnailImage;
}
self.view.addSubview(imageView);
}
But I am getting this error for all of my video urls:
Error Domain=AVFoundationErrorDomain Code=-11850 "Operation Stopped" UserInfo={NSUnderlyingError=0x7fb4a3c04870 {Error Domain=NSOSStatusErrorDomain Code=-12939 "(null)"}, NSLocalizedFailureReason=The server is not correctly configured., NSLocalizedDescription=Operation Stopped}
Can anyone suggest where I am doing wrong or this code is just for local videos? Also if there any alternatives please suggest.
I had go to a lot of places to have found the solution for this.
Basically you have to first get list of video id's(if you need to) and then use the following to get a page that returns a json with link to the actual thumbnail image.
Here is the api link for thumbnails:
https://api.dailymotion.com/video/x26ezrb?fields=thumbnail_medium_url,thumbnail_small_url,thumbnail_large_url
in the above URL, "x26ezrb" is the video id and the "fields" attributes defines the size for thumbnail image. Use the video id's to get their respective thumbnails.
if you hit this link "https://api.dailymotion.com/video/x26ezrb?fields=thumbnail_large_url" you'll get a json like below:
{"thumbnail_large_url":"http://s1.dmcdn.net/HRnTi/x240-oi8.jpg"}
Now all you have to do is parse this json to get the link and then use that link to get to the thumbnail.
NOTE: you have to convert the thumbnail link from "http" into "https" otherwise it won't return anything.
get the url from JSON, split it into components with separator ":" and then combine "https:" with second part of the split url string.

UIImagePickerController - Save Recorded Video to Camera Roll Error

I'm trying to save a video recorded with camera using the UIImagePickerController. I've tried two different ways, and both are not working at all.
First attempt:
if let pathURL = info["UIImagePickerControllerMediaURL"] as! URL?, mediaType == kUTTypeMovie {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: pathURL)
}, completionHandler: { (isSuccessfull, error) in
if error != nil {
// ERROR
// error.localizedDescription value below
// "The operation couldn’t be completed. (Cocoa error -1.)"
} else if isSuccessfull {
// SUCCESS
}
})
}
On that attempt, I'm always getting the error "The operation couldn’t be completed. (Cocoa error -1.)"
Second attempt:
if let filePath = info["UIImagePickerControllerMediaURL"] as! NSURL,
let stringPath = filePath.path {
UISaveVideoAtPathToSavedPhotosAlbum(stringPath, self, #selector(Controller.videoSaved(videoPath:didFinishSavingWithError:contextInfo:)), nil)
}
This is not working either... The callback, the error is nil, but the video is not saved into the camera roll. Also, when I use the UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(stringPath), this method also returning me false all the time. I'm not able to understand why the video recorded would be incompatible to save.
I really don't know what I'm doing wrong. What am I missing? A configuration in plist?
Here's how look like the value of info["UIImagePickerControllerMediaURL"]:
file:///private/var/mobile/Containers/Data/Application/2B9BE04A-17B3-49CE-B4BA-C45F183E9A64/tmp/837294779101__C4825AFE-8140-420F-ACD0-64623C7A4753.MOV
For pictures taken with camera, PHPhotoLibrary method works very fine... It's only videos that is not able to be saved.
Not sure in 100% but I believe that it wasn't working for me because I was saving the video only after a request to my API was successful. My assumption is that the path URL is temporary and has a very short time of life. When I pass it to another controller, and make API call, it doesn't work. But if I save the video directly in the delegate method of UIImagePickerViewController, then it works fine.

Get last frame from video

I'm trying to get last frame from video. Last frame, not last second (because I have very fast videos, one second can have different scenes).
I've written such code for testing:
private func getLastFrame(from item: AVPlayerItem) -> UIImage? {
let imageGenerator = AVAssetImageGenerator(asset: item.asset)
imageGenerator.requestedTimeToleranceAfter = kCMTimeZero
imageGenerator.requestedTimeToleranceBefore = kCMTimeZero
let composition = AVVideoComposition(propertiesOf: item.asset)
let time = CMTimeMakeWithSeconds(item.asset.duration.seconds, composition.frameDuration.timescale)
do {
let cgImage = try imageGenerator.copyCGImage(at: time, actualTime: nil)
return UIImage(cgImage: cgImage)
} catch {
print("\(error)")
return nil
}
}
But I receive always such error when try to execute it:
Domain=AVFoundationErrorDomain Code=-11832 "Cannot Open"
UserInfo={NSUnderlyingError=0x170240180 {Error
Domain=NSOSStatusErrorDomain Code=-12431 "(null)"},
NSLocalizedFailureReason=This media cannot be used.,
NSLocalizedDescription=Cannot Open}
If I remove requestedTimeTolerance (so it will be on default infinite value) everything is okay, but I always receive brighter imaged than I have in video (maybe it is because not latest frame was captured? Or CGImage → UIImage transform has some troubles?)
Questions:
Why I receive error when zero tolerance is specified? How to get exactly last frame?
Why captured images may be overbrighted that in video? For example if I write such code:
self.videoLayer.removeFromSuperlayer()
self.backgroundImageView.image = getLastFrame(from: playerItem)
I see "brightness jump" (video was darker, image is brighter).
Update 1
I found related issue: AVAssetImageGenerator fails at copying image, but that question is not solved.

Resources