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:
But often times this happens:
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!
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)
}
I think your problem was actually that the CGImage
s 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 CGImage
s would fix the problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With