17

I play a video with AVPlayer. It works ok.

Now I want to get a UIImage from the video playing (when I push a button for the moment).

Attached to my AVPlayer there is a CALayer that is used to display video on my UIView. My idea is to get an UIImage from CALayer during video is playing.

I do this with code from another question:

UIImage from CALayer - iPhone SDK

However my UIImage is empty. The resolution is good but it is but fully white !!!

It seems that video doesn't write the contents of my CALayer.

Someone can help me? Thanks

Community
  • 1
  • 1
Twil
  • 171
  • 1
  • 4

3 Answers3

8

I couldn't get Meet's solution to work for me, but it got me thinking in the right direction.

Below is the code that I ended up using in my project. The method screenshotFromPlayer:maximumSize: accepts an instance of an AVPlayer from which to take a screenshot, and a CGSize that will be the returned image's maximum size.

- (UIImage *)screenshotFromPlayer:(AVPlayer *)player maximumSize:(CGSize)maxSize {

    CMTime actualTime;
    NSError *error;

    AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:player.currentItem.asset];

    // Setting a maximum size is not necessary for this code to 
    // successfully get a screenshot, but it was useful for my project.
    generator.maximumSize = maxSize;

    CGImageRef cgIm = [generator copyCGImageAtTime:player.currentTime 
                                        actualTime:&actualTime 
                                             error:&error];
    UIImage *image = [UIImage imageWithCGImage:cgIm];
    CFRelease(cgIm);

    if (nil != error) {
        NSLog(@"Error making screenshot: %@", [error localizedDescription]);
        NSLog(@"Actual screenshot time: %f Requested screenshot time: %f", CMTimeGetSeconds(actualTime),
          CMTimeGetSeconds(self.recordPlayer.currentTime));
        return nil;
    }

    return image;
}

Note also that one could use the method generateCGImagesAsynchronouslyForTimes:completionHandler: instead of copyCGImageAtTime:actualTime:error: (on the instance of AVAssetImageGenerator) to perform the image generation asynchronously.

This code sample generates a screenshot at the AVPlayer's currentTime, but any time could be used instead.

Tim Arnold
  • 8,359
  • 8
  • 44
  • 67
  • 2
    I know this is super old but for anyone that comes across this now, be sure that you actually have a CGImageRef before releasing. If you don't, which definitely can happen, the app will crash. – Richmond Watkins Nov 20 '16 at 07:31
8

The code to get image from avplayer.

 - (UIImage *)currentItemScreenShot
{
    AVPlayer *abovePlayer = [abovePlayerView player];
    CMTime time = [[abovePlayer currentItem] currentTime];
    AVAsset *asset = [[[abovePlayerView player] currentItem] asset];
    AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
    if ([imageGenerator respondsToSelector:@selector(setRequestedTimeToleranceBefore:)] && [imageGenerator respondsToSelector:@selector(setRequestedTimeToleranceAfter:)]) {
        [imageGenerator setRequestedTimeToleranceBefore:kCMTimeZero];
        [imageGenerator setRequestedTimeToleranceAfter:kCMTimeZero];
    }
    CGImageRef imgRef = [imageGenerator copyCGImageAtTime:time
                                          actualTime:NULL
                                               error:NULL];
    if (imgRef == nil) {
        if ([imageGenerator respondsToSelector:@selector(setRequestedTimeToleranceBefore:)] && [imageGenerator respondsToSelector:@selector(setRequestedTimeToleranceAfter:)]) {
            [imageGenerator setRequestedTimeToleranceBefore:kCMTimePositiveInfinity];
            [imageGenerator setRequestedTimeToleranceAfter:kCMTimePositiveInfinity];
        }
        imgRef = [imageGenerator copyCGImageAtTime:time actualTime:NULL error:NULL];
    }
    UIImage *image = [[UIImage alloc] initWithCGImage:imgRef];
    CGImageRelease(imgRef);
    [imageGenerator release];

    return [image autorelease];
}

set [imageGenerator setRequestedTimeToleranceBefore:kCMTimeZero] and [imageGenerator setRequestedTimeToleranceAfter:kCMTimeZero] if you need precise time.

nova
  • 781
  • 11
  • 17
1

Try to get the image from the video file at specified instance using the AVAssetImageGenerator:

    AVURLAsset *asset=[[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:[info objectForKey:@"UIImagePickerControllerReferenceURL"]] options:nil];
    AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
    generator.appliesPreferredTrackTransform=TRUE;
    [asset release];
    CMTime thumbTime = CMTimeMakeWithSeconds(0,30);
    AVAssetImageGeneratorCompletionHandler handler = ^(CMTime requestedTime, CGImageRef im, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error){
    if (result != AVAssetImageGeneratorSucceeded) {

    }
    UIImage *thumbImg = [[UIImage imageWithCGImage:im] retain];
    [generator release];
Meet
  • 4,904
  • 3
  • 24
  • 39