How to generate thumbnail from Video url? - ios

As mentioned in this answer, I am using below code to generate thumbnail from Video URL:
//MARK: - - Generate Thumbnail
func getThumbnailFrom(path: URL) -> UIImage? {
do {
let asset = AVURLAsset(url: path, options: nil)
let imgGenerator = AVAssetImageGenerator(asset: asset)
imgGenerator.appliesPreferredTrackTransform = true
let cgImage = try imgGenerator.copyCGImage(at: CMTimeMake(0, 1), actualTime: nil)
let uiImage = UIImage.init(cgImage: cgImage)
return uiImage
} catch let error as NSError {
print("Error generating thumbnail: \(error.localizedDescription)")
return nil
}
}
but it is throwing an error:
Error generating thumbnail: The operation could not be completed

Use below code for generating thumb image from video url.
DispatchQueue.global().async {
let asset = AVAsset(url: "**your url of video**")
let assetImgGenerate : AVAssetImageGenerator = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
let time = CMTimeMake(1, 2)
let img = try? assetImgGenerate.copyCGImage(at: time, actualTime: nil)
if img != nil {
let frameImg = UIImage(cgImage: img!)
DispatchQueue.main.async(execute: {
// assign your image to UIImageView
})
}
}

Hey I have also used same logic for thumbnail of video.
do {
let videoUrl = "Url"
let asset = AVURLAsset(url: videoUrl, options: nil)
let imgGenerator = AVAssetImageGenerator(asset: asset)
let cgImage = try imgGenerator.copyCGImage(at: CMTimeMake(0, 1), actualTime: nil)
// Rotate image before proceeding
let uiImage = UIImage(cgImage: cgImage, scale: CGFloat(1.0), orientation: .right)
image = uiImage
} catch let error as NSError {
print("Error occurred: \(error)")
}
It works for me. After getting CGImage image orientation was not proper so made orientation proper. See if it works for you.

Related

Getting black blank image when extracting a thumbnail from a video

I am trying to get a thumbnail image from a video from a URL using AVFoundation but I am getting a black blank image as attached.
The video URL is https://www.riptide.tv/app/assets/2/3/small_3001190658061_2.mp4
Below is my code:
func videoSnapshot(videoURL: String, imgName : String) -> UIImage? {
let asset = AVAsset(url: URL(string: videoURL)!)
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
let time = CMTimeMakeWithSeconds(Float64(1), preferredTimescale: 100)
do {
let img = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
let thumbnail = UIImage(cgImage: img)
downloadedThumbnails.updateValue(thumbnail, forKey: imgName)
return thumbnail
} catch {
return UIImage(named: imgName)
}
}
black img extracted
If you change your code to
func videoSnapshot(videoURL: String, imgName : String) -> UIImage? {
let asset = AVAsset(url: URL(string: videoURL)!)
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
let time = CMTimeMakeWithSeconds(Float64(5), preferredTimescale: 100)
do {
let img = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
let thumbnail = UIImage(cgImage: img)
return thumbnail
} catch {
return UIImage(named: imgName)
}
}
thats 5 seconds in. You can see the thumbnail generated.
This is because AVAssetImageGenerator is pretty flexible in what times it gets its frames
func videoSnapshot(videoURL: String, imgName : String) -> UIImage? {
let asset = AVAsset(url: URL(string: videoURL)!)
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
assetImgGenerate.requestedTimeToleranceAfter = .zero
assetImgGenerate.requestedTimeToleranceBefore = .zero
let time = CMTimeMakeWithSeconds(Float64(1), preferredTimescale: 100)
do {
let img = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
let thumbnail = UIImage(cgImage: img)
return thumbnail
} catch {
return UIImage(named: imgName)
}
}
Setting
assetImgGenerate.requestedTimeToleranceAfter = .zero
assetImgGenerate.requestedTimeToleranceBefore = .zero
ensures you get a frame accurate thumbnail.
For more info see https://developer.apple.com/documentation/avfoundation/avassetimagegenerator/1390571-requestedtimetolerancebefore

Unable to generate video thumbnail from firebase video url in iOS swift?

I am using a below method to generate a video thumbnail from a remote server url. If my url is not a firebase url for eg.
https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4
Then I am able to generate the thumbnail but if my url is firebase url like below
https://firebasestorage.googleapis.com/v0/b/shaberi-a249e.appspot.com/o/message-videos%2F8EDAC3FC-D754-4165-990A-97F6ECE120A6.mp4?alt=media&token=b3271370-a408-467d-abbc-7df2beef45c7
Then video thumbnail is not generated.
Method for getting video thumbnail
func createThumbnailOfVideoFromRemoteUrl(url: String) -> UIImage? {
let asset = AVAsset(url: URL(string: url)!)
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
//Can set this to improve performance if target size is known before hand
//assetImgGenerate.maximumSize = CGSize(width,height)
let time = CMTimeMakeWithSeconds(1.0, 100)
do {
let img = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
let thumbnail = UIImage(cgImage: img)
return thumbnail
} catch {
print(error.localizedDescription)
return nil
}
}
Please let me know what is the issue ?
Please try below code it's working for me.
func createThumbnailOfVideoFromRemoteUrl(url: String) {
if let asset = AVAsset(url: URL(string: url)!) as? AVAsset {
//let durationSeconds = CMTimeGetSeconds(asset.duration)
let generator = AVAssetImageGenerator(asset: asset)
generator.appliesPreferredTrackTransform = true
let time = CMTimeMakeWithSeconds(3.0, preferredTimescale: 600)
//var thumbnailImage: CGImage
generator.generateCGImagesAsynchronously(forTimes: [NSValue(time: time)]) { (time, thumbnail, cmtime, result, error) in
if (thumbnail != nil) {
DispatchQueue.main.async {
self.profileImgView.image = UIImage(cgImage: thumbnail!)
}
}
}
}
}
self.createThumbnailOfVideoFromRemoteUrl(url: "https://firebasestorage.googleapis.com/v0/b/shaberi-a249e.appspot.com/o/message-videos%2F8EDAC3FC-D754-4165-990A-97F6ECE120A6.mp4?alt=media&token=b3271370-a408-467d-abbc-7df2beef45c7")

Get preview image for remote video

I have a link to the video (for example: http://example.com/video.mp4). I need to get the first frame of the video.
I use:
func getPreviewImage(_ url: URL) -> UIImage? {
let asset = AVURLAsset(url: url)
let generator = AVAssetImageGenerator(asset: asset)
generator.appliesPreferredTrackTransform = true
do {
let cgImage = try generator.copyCGImage(at: CMTimeMake(0, 1), actualTime: nil)
return UIImage(cgImage: cgImage)
} catch {
print(error)
}
return nil
}
However, this does not work.
I get the following error in the logs:
Code=-1102 "You do not have permission to access the requested
resource.
Tell me how to get the first frame?
Try this.
func getThumbnailImageFromVideoURL(fromUrl url: URL) -> UIImage? {
let asset: AVAsset = AVAsset(url: url)
let imageGenerator = AVAssetImageGenerator(asset: asset)
do {
let thumbnailImage = try imageGenerator.copyCGImage(at: CMTimeMake(value: 1, timescale: 60) , actualTime: nil)
return UIImage(cgImage: thumbnailImage)
} catch let error {
print(error)
}
return nil
}

Getting same Video Thumbnail Image every time from the Url in Swift iOS

I am developing a video based Application in Swift3. Where I have one video url and a Range Slider according to the video duration and user can select any minimum and maximum value from slider. If suppose user has selected min value 3 Sec and Max Value 7 Sec, So for this duration I need to generate a Video Thumbnail Image. For this I am using AVAssetImageGenerator to generate this, I tried below both code to achieve this :
func createThumbnailOfVideoFromFileURL(_ strVideoURL: URL) -> UIImage?{
let asset = AVAsset(url: strVideoURL)
let assetImgGenerate : AVAssetImageGenerator = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
let time = CMTimeMake(1, 30)
let img = try? assetImgGenerate.copyCGImage(at: time, actualTime: nil)
guard let cgImage = img else { return nil }
let frameImg = UIImage(cgImage: cgImage)
return frameImg
}
func generateThumbnailForUrl(vidUrl:URL) -> UIImage {
let asset = AVURLAsset(url: vidUrl, options: nil)
let imgGenerator = AVAssetImageGenerator(asset: asset)
var thmbnlImg = UIImage()
do{
let cgImage = try imgGenerator.copyCGImage(at: CMTimeMake(0, 1), actualTime: nil)
thmbnlImg = UIImage(cgImage: cgImage)
thmbnlImg = thmbnlImg.imageRotatedByDegrees(degrees: 90.0, flip: false)
}
catch{
print(error)
}
// !! check the error before proceeding
return thmbnlImg
}
But the problem is I am getting same thumbnail image using both above methods, bcos I am not setting duration here in both methods. How can I add minimum and maximum duration to generate different thumbnail image for each different duration. Please help me resolve my problem. Thank you!
Edit: I tried to set duration like :
let time: CMTime = CMTimeMakeWithSeconds(rangeSlider!.lowerValue, 1)
Then I am getting different thumbnail image but for some slider ranges I am getting nil thumbnail image also. Can anyone have some idea how to set preferredTimeScale value in CMTimeMakeWithSeconds ?
Try this code
static func generateThumbnail(videoUrl: String) -> UIImage? {
do {
let url = URL(string: videoUrl)
let asset = AVURLAsset(url: url!)
let imageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.appliesPreferredTrackTransform = true
let cgImage = try imageGenerator.copyCGImage(at: CMTime(seconds: 2.0, preferredTimescale: 60),
actualTime: nil)
return UIImage(cgImage: cgImage)
} catch {
print(error.localizedDescription)
return nil
}
}
Assume, your trimmed video URL is videoURL. After successfully trimming a video, add this code snippet. Actually this code snippet will help you to extract images from trimmed video at each second (meaning, if the duration of your trimmed video is 10 second, this code will extract 10 images at each second and all of these images are saved in an array, named videoFrames). Lastly, you can do whatever you want with these images. You can also add an activity indicator while this process is going on. Hope this helps.
var videoFrames = [UIImage]()
let asset : AVAsset = AVAsset(url: videoURL as URL)
let videoDuration = CMTimeGetSeconds(asset.duration)
let integerValueOFVideoDuration = Int(videoDuration)
//start activity indicator here
for index in 0..<integerValueOFVideoDuration + 1 {
self.generateFrames(url: videoURL, fromTime: Float64(index))
}
func generateFrames(url: NSURL, fromTime: Float64) {
if videoFrames.count == integerValueOFVideoDuration {
//end activity indicator here
return
}
let asset: AVAsset = AVAsset(url: url as URL)
let assetImgGenerate: AVAssetImageGenerator = AVAssetImageGenerator(asset: asset)
assetImgGenerate.maximumSize = CGSize(width: 300, height: 300)
assetImgGenerate.appliesPreferredTrackTransform = true
let time: CMTime = CMTimeMakeWithSeconds(fromTime, 600)
var img: CGImage?
do {
img = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
} catch {
}
if img != nil {
let frameImg: UIImage = UIImage(cgImage: img!)
videoFrames.append(frameImg)
} else {
//return nil
}
}

Display a preview image from a video swift

I have a folder in my documents directory containing images and videos, In my app i have a collection view that displays the images in each cell.
I have accomplished this with a load image function that returns a UIImage
// Get the UI Image from the path passed in
let image = UIImage(contentsOfFile: path)
if image == nil
{
print("No Image at this path");
}
else
{
return image
}
However I can't seem to find something equivalent for videos, Ideally I want it to show as the video player thing like in photo album where you see a still of the video with a little play button on it that when clicked plays the video.
Failing that purely being able to get a still from the video at the specified path I have to be returned as a UIImage I can display would be ok.
I am new to iOS stuff so just wondering if there is some equivalent type like UIImage for videos that means I can just load and display it on the collection view.
You need a snapshot of the video and display it along with a play button. Here is my func in Swift 2 that can get you the video-snapshot for a video file at path filePathLocal:
func videoSnapshot(filePathLocal: String) -> UIImage? {
let vidURL = URL(fileURLWithPath:filePathLocal as String)
let asset = AVURLAsset(url: vidURL)
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
}
}
Swift 3 version
func videoPreviewUiimage(fileName:String) -> UIImage? {
let filePath = NSString(string: "~/").expandingTildeInPath.appending("/Documents/").appending(fileName)
let vidURL = NSURL(fileURLWithPath:filePath)
let asset = AVURLAsset(url: vidURL as URL)
let generator = AVAssetImageGenerator(asset: asset)
generator.appliesPreferredTrackTransform = true
let timestamp = CMTime(seconds: 2, 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
}
}
Based on Nishant answer above.
Using a URL for your path is better than using String:
func videoPreviewImage(url: URL) -> UIImage? {
let asset = AVURLAsset(url: url)
let generator = AVAssetImageGenerator(asset: asset)
generator.appliesPreferredTrackTransform = true
if let cgImage = try? generator.copyCGImage(at: CMTime(seconds: 2, preferredTimescale: 60), actualTime: nil) {
return UIImage(cgImage: cgImage)
}
else {
return nil
}
}
For swift 3.0
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
}
}

Resources