Get preview image for remote video - ios

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
}

Related

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")

How to generate thumbnail from Video url?

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.

Get thumbail / preview image from a server video URL in Swift 3.0

I want a thumbnail image from a video underlying on the server. The video file is not on local. It's on my server. The video file has extension .m3u8.
You can do it.
First step: You need to import AVFoundation:
import AVFoundation
Then add the code below to your controller:
func getThumbnailImage(forUrl 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
}
Usage:
let imageView = UIImageView()
let url = URL(string: "your_video_url")
if let thumbnailImage = getThumbnailImage(forUrl: url) {
imageView.image = thumbnailImage
}
Change url to your video link.
Step 1
import AVFoundation
Step 2
Add the following function in your ViewController:
func getThumbnailImageFromVideoUrl(url: URL, completion: #escaping ((_ image: UIImage?)->Void)) {
DispatchQueue.global().async { //1
let asset = AVAsset(url: url) //2
let avAssetImageGenerator = AVAssetImageGenerator(asset: asset) //3
avAssetImageGenerator.appliesPreferredTrackTransform = true //4
let thumnailTime = CMTimeMake(value: 2, timescale: 1) //5
do {
let cgThumbImage = try avAssetImageGenerator.copyCGImage(at: thumnailTime, actualTime: nil) //6
let thumbNailImage = UIImage(cgImage: cgThumbImage) //7
DispatchQueue.main.async { //8
completion(thumbNailImage) //9
}
} catch {
print(error.localizedDescription) //10
DispatchQueue.main.async {
completion(nil) //11
}
}
}
}
Step 3
self.getThumbnailImageFromVideoUrl(url: videoUrl) { (thumbNailImage) in
self.yourImageView.image = thumbNailImage
}
If you need a complete explanation, refer to Get a thumbnail from a video URL in the background in Swift.
In Swift 5.1 (older versions too), you can do it like this:
private func createVideoThumbnail(from url: URL) -> UIImage? {
let asset = AVAsset(url: url)
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
assetImgGenerate.maximumSize = CGSize(width: frame.width, height: frame.height)
let time = CMTimeMakeWithSeconds(0.0, preferredTimescale: 600)
do {
let img = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
let thumbnail = UIImage(cgImage: img)
return thumbnail
}
catch {
print(error.localizedDescription)
return nil
}
}
Note that AVKit needs to be imported.
In this solution, I have created a cache as well for the image so that we don't need to fetch the same images again from the internet.
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
}
}
You can use Kingfisher to generate the thumbnail.
Note:- It will handle all the cache work for you.
Usage:-
Step 1
import Kingfisher
Step 2
guard let url = URL(string: videoUrlString) else { return }
self.imageView.kf.setImage(with: AVAssetImageDataProvider(assetURL: url, seconds: 1))
Here is the link of Kingfisher SDK
https://github.com/onevcat/Kingfisher
Note:- It will work fine with latest version of kingfisher/ higher or equals to version 7.2.3

Generation of thumbnail fails with error AVFoundationErrorDomain Code=-11800

I am using following code to generate a URL of remote video using AVAsset class
func generateThumnail(url :URL, fromTime:Float64) -> UIImage? {
let asset :AVAsset = AVAsset(url:url)
let assetImgGenerate : AVAssetImageGenerator = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
assetImgGenerate.requestedTimeToleranceAfter = kCMTimeZero;
assetImgGenerate.requestedTimeToleranceBefore = kCMTimeZero;
let time : CMTime = CMTimeMakeWithSeconds(1,30)
do {
let img = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
return UIImage(cgImage: img)
} catch let error as NSError {
print("Image generation failed with error \(error)")
return nil
}
return nil
}
If fails sometimes ore more often with following error for same video URL
AVFoundationErrorDomain Code=-11800
Not sure whats wrong with the above code I am new to AVAsset and related framework
any help is appreciated
func getVideoThumbNail(_ videoName: String) -> UIImage {
do {
let url = URL(fileURLWithPath: getDocumentsDirectory().appendingPathComponent(videoName))
let asset = AVURLAsset(url: url, options: nil)
let imgGenerator = AVAssetImageGenerator(asset: asset)
imgGenerator.appliesPreferredTrackTransform = true
let cgImage = try imgGenerator.copyCGImage(at: CMTimeMake(2, 3), actualTime: nil)
return UIImage(cgImage: cgImage)
} catch let error as NSError {
print("\(ERROR_GENERATING_THUMNAIL): \(error)")
}
return ERROR_GENERATING_THUMNAIL
}
func getDocumentsDirectory() -> NSString {
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
return paths[0] as NSString
}

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