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
}
Related
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")
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
}
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.
I'm trying to extract frames as UIImages from a video in Swift. I found several Objective C solutions but I'm having trouble finding anything in Swift. Assuming the following is correct can someone either help me to convert the following to Swift or give me their own take on how to do this?
Source:
Grabbing the first frame of a video from UIImagePickerController?
- (UIImage *)imageFromVideo:(NSURL *)videoURL atTime:(NSTimeInterval)time {
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:videoURL options:nil];
NSParameterAssert(asset);
AVAssetImageGenerator *assetIG =
[[AVAssetImageGenerator alloc] initWithAsset:asset];
assetIG.appliesPreferredTrackTransform = YES;
assetIG.apertureMode = AVAssetImageGeneratorApertureModeEncodedPixels;
CGImageRef thumbnailImageRef = NULL;
CFTimeInterval thumbnailImageTime = time;
NSError *igError = nil;
thumbnailImageRef =
[assetIG copyCGImageAtTime:CMTimeMake(thumbnailImageTime, 60)
actualTime:NULL
error:&igError];
if (!thumbnailImageRef)
NSLog(#"thumbnailImageGenerationError %#", igError );
UIImage *image = thumbnailImageRef
? [[UIImage alloc] initWithCGImage:thumbnailImageRef]
: nil;
return image;
}
It actually did work.
func imageFromVideo(url: URL, at time: TimeInterval) -> UIImage? {
let asset = AVURLAsset(url: url)
let assetIG = AVAssetImageGenerator(asset: asset)
assetIG.appliesPreferredTrackTransform = true
assetIG.apertureMode = AVAssetImageGeneratorApertureModeEncodedPixels
let cmTime = CMTime(seconds: time, preferredTimescale: 60)
let thumbnailImageRef: CGImage
do {
thumbnailImageRef = try assetIG.copyCGImage(at: cmTime, actualTime: nil)
} catch let error {
print("Error: \(error)")
return nil
}
return UIImage(cgImage: thumbnailImageRef)
}
But remember that this function is synchronous and it's better not to call it on the main queue.
You can do either this:
DispatchQueue.global(qos: .background).async {
let image = self.imageFromVideo(url: url, at: 0)
DispatchQueue.main.async {
self.imageView.image = image
}
}
Or use generateCGImagesAsynchronously instead of copyCGImage.
Here's a SWIFT 5 alternative to Dmitry's solution, to not have to worry about what queue you're on:
public func imageFromVideo(url: URL, at time: TimeInterval, completion: #escaping (UIImage?) -> Void) {
DispatchQueue.global(qos: .background).async {
let asset = AVURLAsset(url: url)
let assetIG = AVAssetImageGenerator(asset: asset)
assetIG.appliesPreferredTrackTransform = true
assetIG.apertureMode = AVAssetImageGenerator.ApertureMode.encodedPixels
let cmTime = CMTime(seconds: time, preferredTimescale: 60)
let thumbnailImageRef: CGImage
do {
thumbnailImageRef = try assetIG.copyCGImage(at: cmTime, actualTime: nil)
} catch let error {
print("Error: \(error)")
return completion(nil)
}
DispatchQueue.main.async {
completion(UIImage(cgImage: thumbnailImageRef))
}
}
}
Here's now to use it:
imageFromVideo(url: videoUrl, at: 0) { image in
// Do something with the image here
}
You can do this easily on iOS. Below is a code snippet on how to do so with Swift.
let url = Bundle.main.url(forResource: "video_name", withExtension: "mp4")
let videoAsset = AVAsset(url: url!)
let t1 = CMTime(value: 1, timescale: 1)
let t2 = CMTime(value: 4, timescale: 1)
let t3 = CMTime(value: 8, timescale: 1)
let timesArray = [
NSValue(time: t1),
NSValue(time: t2),
NSValue(time: t3)
]
let generator = AVAssetImageGenerator(asset: videoAsset)
generator.requestedTimeToleranceBefore = .zero
generator.requestedTimeToleranceAfter = .zero
generator.generateCGImagesAsynchronously(forTimes: timesArray ) { requestedTime, image, actualTime, result, error in
let img = UIImage(cgImage: image!)
}
You can find the demo code here and the medium article here.
Here's async/await version of #Dmitry 's answer for those who doesn't like completion handlers
func imageFromVideo(url: URL, at time: TimeInterval) async throws -> UIImage {
try await withCheckedThrowingContinuation({ continuation in
DispatchQueue.global(qos: .background).async {
let asset = AVURLAsset(url: url)
let assetIG = AVAssetImageGenerator(asset: asset)
assetIG.appliesPreferredTrackTransform = true
assetIG.apertureMode = AVAssetImageGenerator.ApertureMode.encodedPixels
let cmTime = CMTime(seconds: time, preferredTimescale: 60)
let thumbnailImageRef: CGImage
do {
thumbnailImageRef = try assetIG.copyCGImage(at: cmTime, actualTime: nil)
} catch {
continuation.resume(throwing: error)
return
}
continuation.resume(returning: UIImage(cgImage: thumbnailImageRef))
}
})
}
Usage:
let vidUrl = <#your url#>
do {
let firstFrame = try await imageFromVideo(url: vidUrl, at: 0)
// do something with image
} catch {
// handle error
}
Or like this if you're in throwing function:
func someThrowingFunc() throws {
let vidUrl = <#your url#>
let firstFrame = try await imageFromVideo(url: vidUrl, at: 0)
// do something with image
}
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
}
}