Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

generateCGImagesAsynchronouslyForTimes sometimes don't generate entire thumbnails

I'm working on an OS X app that is using AVAssetImageGenerator.generateCGImagesAsynchronouslyForTimes, and it normally works fine. However, once in a while the thumbnail I get back only contains the first few rows of pixels, and the rest is green, sometimes the images will be different shades of green. It's very hard to track down because it doesn't consistently happen, but when it does about half of the thumbnails are affected. This is an image of what I expect to see:

enter image description here

But often times this happens:

enter image description here

Here is the code I'm using to generate the thumbnails:

let assetGenerator = AVAssetImageGenerator(asset: AVURLAsset(URL: url))
assetGenerator.appliesPreferredTrackTransform = true
let time = CMTime(seconds: 0, preferredTimescale: 30)

let handler: AVAssetImageGeneratorCompletionHandler = { _, image, _, res, error in
    defer { dispatch_group_leave(self.waitForThumbnail!) }

    guard let image = image where res == .Succeeded else {
        if let error = error { print(error) }
        return
    }

    let s = CGSize(width: CGImageGetWidth(image), height: CGImageGetHeight(image))
    self.thumbnail = NSImage(CGImage: image, size: s)

}

waitForThumbnail = dispatch_group_create()
dispatch_group_enter(waitForThumbnail!)

assetGenerator.maximumSize = maxThumbnailSize
assetGenerator.generateCGImagesAsynchronouslyForTimes([NSValue(CMTime: time)], completionHandler: handler)

And this is how I'm retrieving the thumbnails:

dispatch_group_wait(file.waitForThumbnail!, DISPATCH_TIME_FOREVER)
dispatch_async(dispatch_get_main_queue()) {
    self.imageView.image = file.thumbnail
}

Any help is much appreciated, thanks!

like image 827
Addison Avatar asked Aug 03 '16 14:08

Addison


2 Answers

Instead of using generateCGImagesAsynchronouslyForTimes Method you can use copyCGImageAtTime Method to get the image from the asset and save that image as you are saving before. Here is the code.

let assetGenerator = AVAssetImageGenerator(asset: AVURLAsset(URL: url))
assetGenerator.appliesPreferredTrackTransform = true
assetGenerator.maximumSize = maxThumbnailSize
let time = CMTime(seconds: 0, preferredTimescale: 30)
do {
    let cgImage = try assetGenerator.copyCGImageAtTime(time, actualTime: nil)
    let s = CGSize(width: CGImageGetWidth(cgImage), height: CGImageGetHeight(cgImage))
    self.thumbnail = NSImage(CGImage: cgImage, size: s)    
} catch let error {
    print(error)
}
like image 173
Bhautik Ziniya Avatar answered Nov 09 '22 10:11

Bhautik Ziniya


I think your problem was actually that the CGImages returned from the AVAssetImageGenerator.generateCGImagesAsynchronouslyForTimes were not retained. Funny enough, the official documentation does not mention this, but reading the header file explicitly says so.

/*! @method generateCGImagesAsynchronouslyForTimes:completionHandler: @abstract Returns a series of CGImageRefs for an asset at or near the specified times. @param requestedTimes An NSArray of NSValues, each containing a CMTime, specifying the asset times at which an image is requested. @param handler A block that will be called when an image request is complete. @discussion Employs an efficient "batch mode" for getting images in time order. The client will receive exactly one handler callback for each requested time in requestedTimes. Changes to generator properties (snap behavior, maximum size, etc...) will not affect outstanding asynchronous image generation requests. The generated image is not retained. Clients should retain the image if they wish it to persist after the completion handler returns. */ - (void)generateCGImagesAsynchronouslyForTimes:(NSArray<NSValue *> *)requestedTimes completionHandler:(AVAssetImageGeneratorCompletionHandler)handler;

I believe that retaining those CGImages would fix the problem.

like image 3
Marko Hlebar Avatar answered Nov 09 '22 08:11

Marko Hlebar