14

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!

Addison
  • 3,791
  • 3
  • 28
  • 48
  • 1
    Out of curiosity how are you creating the `AVAsset`'s? If you are using `NSItemProvider` it comes with completiion handler that give's apple's default thumbnail as an image for that file. – NSGangster Aug 03 '16 at 14:53
  • Oh really? That sounds nice, thanks I'll have to try that. I'm using `AVURLAsset(URL: url)` to create them now. – Addison Aug 03 '16 at 14:55
  • [Link](https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSItemProvider_Class/#//apple_ref/occ/instm/NSItemProvider/loadPreviewImageWithOptions:completionHandler]). Here is a link to that preview image handler. And I just checked `NSItemProvider` does have `initWithURL` if you just wanted to use that. – NSGangster Aug 03 '16 at 15:04

2 Answers2

7

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)
}
Bhautik Ziniya
  • 1,554
  • 12
  • 19
3

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.

Marko Hlebar
  • 1,973
  • 17
  • 18
  • 1
    Thanks so much! Even though I'm done with this project, it's interesting knowing what likely happened. It's frustrating that the Apple docs don't mention things like this... – Addison May 13 '18 at 23:54