Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get UIImage from the CALayer attached to AVPlayer (extract frame from video playing)

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

like image 907
Twil Avatar asked Nov 28 '10 02:11

Twil


2 Answers

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.

like image 181
Tim Arnold Avatar answered Sep 22 '22 02:09

Tim Arnold


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.

like image 24
nova Avatar answered Sep 20 '22 02:09

nova