Swift4.2 grab screenshot from Video - swift

in Swift 4.2 I am trying to grab screenshot from video
func thumbnailImageFor(fileUrl:URL) -> UIImage? {
let asset = AVAsset(url: fileUrl)
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
let time = CMTimeMakeWithSeconds(1.0, preferredTimescale: 600)
do {
let img = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
let thumbnail = UIImage(cgImage: img)
return thumbnail
} catch {
print(error)
return nil
}
}
But getting error:
Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could
not be completed" UserInfo={NSLocalizedFailureReason=An unknown error
occurred (-12792), NSLocalizedDescription=The operation could not be
completed, NSUnderlyingError=0x600000f46580 {Error
Domain=NSOSStatusErrorDomain Code=-12792 "(null)"}}
video's url:
https://firebasestorage.googleapis.com/v0/b/lailaichatapp.appspot.com/o/message_movies%2F8A61AC4E-4A08-4EC7-BC78-A5D861BE48C5.mov?alt=media&token=8906971d-59d7-4880-988e-135615c10f22
am I missing something?

I think it may be an issue with the filetype of the movie, as this works:
import UIKit
import AVFoundation
func thumbnailImageFor(fileUrl:URL) -> UIImage? {
let video = AVURLAsset(url: fileUrl, options: [:])
let assetImgGenerate = AVAssetImageGenerator(asset: video)
assetImgGenerate.appliesPreferredTrackTransform = true
let videoDuration:CMTime = video.duration
let durationInSeconds:Float64 = CMTimeGetSeconds(videoDuration)
let numerator = Int64(1)
let denominator = videoDuration.timescale
let time = CMTimeMake(value: numerator, timescale: denominator)
do {
let img = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
let thumbnail = UIImage(cgImage: img)
return thumbnail
} catch {
print(error)
return nil
}
}
let url: URL = URL(string: "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4")!
let image: UIImage? = thumbnailImageFor(fileUrl: url)
print("Image: \(image)")

Related

Swift: Getting thumbnail from a video URL often fails with error: "The operation could not be completed" [duplicate]

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.

Can not generate thumbnail from url video in ios 13

I am using the below code to generate a frame from a video URL. This code was working fine for sometime before. right now it's not working. it is throwing an error that it can not decode and the file may be damaged. can someone help with this?
func previewImageFromVideo(url: NSURL) -> UIImage? {
let url = url as URL
let request = URLRequest(url: url)
let cache = URLCache.shared
if
let cachedResponse = cache.cachedResponse(for: request),
let image = UIImage(data: cachedResponse.data)
{
return image
}
let asset = AVAsset(url: url)
let imageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.appliesPreferredTrackTransform = true
imageGenerator.maximumSize = CGSize(width: 250, height: 120)
var time = asset.duration
time.value = min(time.value, 2)
var image: UIImage?
do {
let cgImage = try imageGenerator.copyCGImage(at: time, actualTime: nil)
image = UIImage(cgImage: cgImage)
} catch { }
if
let image = image,
let data = UIImagePNGRepresentation(image),
let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil)
{
let cachedResponse = CachedURLResponse(response: response, data: data)
cache.storeCachedResponse(cachedResponse, for: request)
}
return image
}
This code throws an error while capturing a frame from the URL video. It says the file might be damaged.
I tried your code on Playground, it works perfectly. The file could be damaged as the error message says.
import UIKit
import AVKit
import PlaygroundSupport
let container = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
container.addSubview(imageView)
PlaygroundPage.current.liveView = container
PlaygroundPage.current.needsIndefiniteExecution = true
func previewImageFromVideo(url: NSURL) -> UIImage? {
let url = url as URL
let request = URLRequest(url: url)
let cache = URLCache.shared
if
let cachedResponse = cache.cachedResponse(for: request),
let image = UIImage(data: cachedResponse.data)
{
return image
}
let asset = AVAsset(url: url)
let imageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.appliesPreferredTrackTransform = true
imageGenerator.maximumSize = CGSize(width: 250, height: 120)
var time = asset.duration
time.value = min(time.value, 2)
var image: UIImage?
do {
let cgImage = try imageGenerator.copyCGImage(at: time, actualTime: nil)
image = UIImage(cgImage: cgImage)
} catch { }
if
let image = image,
let data = image.pngData(),
let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil)
{
let cachedResponse = CachedURLResponse(response: response, data: data)
cache.storeCachedResponse(cachedResponse, for: request)
}
return image
}
imageView.image = previewImageFromVideo(url: NSURL(string: "https://www.w3schools.com/html/mov_bbb.mp4")!)
I have made some changes to Suh's answer, and I have used it on the background thread so that while generating the thumbnail our UI won't get blocked.
func createVideoThumbnail( url: String?, completion: #escaping ((_ image: UIImage?)->Void)) {
guard let url = URL(string: url ?? "") else { return }
DispatchQueue.global().async {
let url = url as URL
let request = URLRequest(url: url)
let cache = URLCache.shared
if
let cachedResponse = cache.cachedResponse(for: request),
let image = UIImage(data: cachedResponse.data)
{
DispatchQueue.main.async {
completion(image)
}
}
let asset = AVAsset(url: url)
let imageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.appliesPreferredTrackTransform = true
var time = asset.duration
time.value = min(time.value, 2)
var image: UIImage?
do {
let cgImage = try imageGenerator.copyCGImage(at: time, actualTime: nil)
image = UIImage(cgImage: cgImage)
} catch { DispatchQueue.main.async {
completion(nil)
} }
if
let image = image,
let data = image.pngData(),
let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil)
{
let cachedResponse = CachedURLResponse(response: response, data: data)
cache.storeCachedResponse(cachedResponse, for: request)
}
DispatchQueue.main.async {
completion(image)
}
}
}
Usage:
createVideoThumbnail(url: data.url ?? "") { [weak self] (img) in
guard let strongSelf = self else { return }
if let image = img {
strongSelf.mediaImg.image = image
}
}

Issues with read file permission Swift & Firebase - mobile not simulator

I am attempting to upload a video from a custom UICollection that accesses the users videos. When I attempt to upload the URL to firebase, I get the following error:
Body file is unreachable: /var/mobile/Media/DCIM/103APPLE/IMG_3002.MOV
Error Domain=NSCocoaErrorDomain Code=257 "The file “IMG_3002.MOV” couldn’t be opened because you don’t have permission to view it." UserInfo={NSURL=file:///var/mobile/Media/DCIM/103APPLE/IMG_3002.MOV, NSFilePath=/var/mobile/Media/DCIM/103APPLE/IMG_3002.MOV, NSUnderlyingError=0x17024b790 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
I am a little confused as to why this is occuring, b/c when I print out the url of the video I get:
file:///var/mobile/Media/DCIM/103APPLE/IMG_3002.MOV
&& my info.plist looks like this
Here is the upload code:
videosFolder.child("\(nsuid).mov").putFile(self.videoURLToUpload, metadata: nil, completion: { (metadata, error) in
if error != nil {
print("we Had an Error!: \(String(describing: error))")
} else {
print("we uploaded the video correctly!!!")
}
})
&& just in-case here is the where I am fetching the videos
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
}
}
}
This issue is on the mobile not the simulator

Saving Video to Parse & Playback

So i'm using this custom class to record my video -- https://github.com/piemonte/PBJVision. I am attempting to record video in my iOS app and I can't seem to get the code correct to upload the file to my parse server. A few things:
In the PBJVision class it allows you to use NSURL(fileWithPath:videoPath) to access the asset after the video has been recorded.
To access the Data in the asset and save to Parse, I use the following function:
func vision(vision: PBJVision, capturedVideo videoDict: [NSObject : AnyObject]?, error: NSError?) {
if error != nil {
print("Encountered error with video")
isVideo = false
} else {
let currentVideo = videoDict
let videoPath = currentVideo![PBJVisionVideoPathKey] as! String
print("The video path is: \(videoPath)")
self.player = Player()
self.player.delegate = self
self.player.view.frame = CGRect(x: cameraView.frame.origin.x, y: cameraView.frame.origin.y, width: cameraView.frame.width, height: cameraView.frame.height)
self.player.playbackLoops = true
videoUrl = NSURL(fileURLWithPath: videoPath)
self.player.setUrl(videoUrl)
self.cameraView.addSubview(self.player.view)
self.player.playFromBeginning()
nextButton.hidden = false
isVideo = true
let contents: NSData?
do {
contents = try NSData(contentsOfFile: videoPath, options: NSDataReadingOptions.DataReadingMappedAlways)
} catch _ {
contents = nil
}
print(contents)
let videoObject = PFObject(className: "EventChatroomMessages")
videoObject.setValue(user, forKey: "user")
videoObject.setValue("uG7v2KWBQm", forKey: "eventId")
videoObject.setValue(NSDate(), forKey: "timestamp")
let videoFile: PFFile?
do {
videoFile = try PFFile(name: randomAlphaNumericString(26) + ".mp4", data: contents!, contentType: "video/mp4")
print("VideoFile: \(videoFile)")
} catch _ {
print("error")
}
print(videoFile)
videoObject.setValue(videoFile, forKey: "image")
videoObject.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if success == true {
ProgressHUD.showSuccess("Video Saved.", interaction: false)
dispatch_async(dispatch_get_main_queue()) {
ProgressHUD.dismiss()
}
} else {
ProgressHUD.showError("Error Saving Video.", interaction: false)
dispatch_async(dispatch_get_main_queue()) {
ProgressHUD.dismiss()
}
}
}
}
}
I am then using a UITableView to display my data from Parse. Here is how I retrieve my asset back from Parse and into my AVPlayer():
// Create Player for Reaction
let player = Player()
player.delegate = self
player.view.frame = CGRectMake(0.0, nameLabel.frame.origin.y + nameLabel.frame.size.height + 0.0, self.view.frame.width, 150)
player.view.backgroundColor = UIColor.whiteColor()
let video = message.objectForKey("image") as! PFFile
let urlFromParse = video.url!
print(urlFromParse)
let url = NSURL(fileURLWithPath: video.url!)
print(url)
let playerNew = AVPlayer(URL: url!)
let playerLayer = AVPlayerLayer(player: playerNew)
playerLayer.frame = CGRectMake(0.0, nameLabel.frame.origin.y + nameLabel.frame.size.height + 0.0, self.view.frame.width, 150)
cell.layer.addSublayer(playerLayer)
playerLayer.backgroundColor = UIColor.whiteColor().CGColor
playerNew.play()
I copy the value that is returned from urlFromParse which is (http://parlayapp.herokuapp.com/parse/files/smTrXDGZhlYQGh4BZcVvmZ2rYB9kA5EhPkGbj2R2/58c0648ae4ca9900f2d835feb77f165e_file.mp4) and paste it into my browser and the video plays in browser. Am I correct to assume the file has been saved correctly?
When I go to run my app, the video does not play.Any suggestion on what i'm doing wrong?
I have found that playing video using the pfFile.url does not work. You have to write the NSData from the PFFIle to a local file using the right extension (mov) and then play the video using the local file as the source.

Video Trimming failed with block AVAssetExportSessionStatus.Failed

I have converted this code to Swift language but i am getting this
Error: Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo=0x174278600 {NSUnderlyingError=0x170241d10 "The operation couldn’t be completed. (OSStatus error -12780.)", NSLocalizedFailureReason=An unknown error occurred (-12780), NSLocalizedDescription=The operation could not be completed} in (case AVAssetExportSessionStatus.Failed).
Kindly help me to resolved this
func cropVideo(sourceURL: NSURL)
{
let asset = AVURLAsset(URL: sourceURL, options: nil)
let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality)
var error : NSError?
let file = "Finaloutput.mp4"
/* let paths : AnyObject = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask,true)[0]
let outputURL1 = paths[0] as? String*/
let nsDocumentDirectory = NSSearchPathDirectory.DocumentDirectory
let nsUserDomainMask = NSSearchPathDomainMask.UserDomainMask
let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
let outputURL1 = paths[0] as? String
let filemgr = NSFileManager.defaultManager()
filemgr.createDirectoryAtPath(outputURL1!, withIntermediateDirectories: true, attributes: nil, error: &error)
var outputURL = outputURL1!.stringByAppendingPathComponent(file)
filemgr.removeItemAtPath(outputURL, error: &error)
let FinalUrlTosave = NSURL(string: outputURL)
exportSession.outputURL=FinalUrlTosave
exportSession.shouldOptimizeForNetworkUse = true
// exportSession.outputFileType = AVFileTypeQuickTimeMovie
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
let start:CMTime
let duration:CMTime
start = CMTimeMakeWithSeconds(1.0, 600)
duration = CMTimeMakeWithSeconds(19.0, 600)
// let timeRangeForCurrentSlice = CMTimeRangeMake(start, duration)
let range = CMTimeRangeMake(start, duration);
exportSession.timeRange = range
let destinationURL1 = NSURL(string: outputURL)
exportSession.exportAsynchronouslyWithCompletionHandler({
switch exportSession.status{
case AVAssetExportSessionStatus.Failed:
println("failed \(exportSession.error)")
case AVAssetExportSessionStatus.Cancelled:
println("cancelled \(exportSession.error)")
default:
println("complete....complete")
self.SaveVideoToPhotoLibrary(destinationURL1!)
}
})
}
func SaveVideoToPhotoLibrary(outputFileURL: NSURL)
{
assetsLibrary = ALAssetsLibrary()
let videoURL = outputFileURL as NSURL?
if let library = assetsLibrary{
if let url = videoURL{
library.writeVideoAtPathToSavedPhotosAlbum(url,
completionBlock: {(url: NSURL!, error: NSError!) in
print(url)
if let theError = error{
print("Error happened while saving the video")
print("The error is = \(theError)")
} else {
print("no errors happened")
}
})
} else {
print("Could not find the video in the app bundle")
}
}
}
Found Solution :
I have change this line and it works for me
let FinalUrlTosave = NSURL(fileURLWithPath: outputURL)
instead of
let FinalUrlTosave = NSURL(string: outputURL)
I was not getting exact path.