Reputation: 14317
I am trying to get the first thumbnail from a video.
I tried the following:
ONE
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:_moviePath] options:nil];
AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:asset];
gen.appliesPreferredTrackTransform = YES;
CMTime time = CMTimeMakeWithSeconds(0.0, 600);
NSError *error = nil;
CMTime actualTime;
CGImageRef image = [gen copyCGImageAtTime:time actualTime:&actualTime error:&error];
UIImage *thumb = [[UIImage alloc] initWithCGImage:image];
CGImageRelease(image);
NSLog(@"the error is %@: image is %@: url is %@: ",error,_mainThumbnail,[NSURL fileURLWithPath:_moviePath] );
However, my log is:
the error is (null): image is (null):
url is file:///private/var/mobile/Applications/D1293BDC-EA7E-4AC7-AD3C-1BA3548F37D6/tmp/trim.6F0C4631-E5E8-43CD-BF32-9B8F09D4ACF1.MOV:
TWO
AVURLAsset *asset=[[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:_moviePath] options:nil];
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
generator.appliesPreferredTrackTransform=TRUE;
CMTime thumbTime = CMTimeMakeWithSeconds(0,30);
AVAssetImageGeneratorCompletionHandler handler = ^(CMTime requestedTime, CGImageRef im, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error){
if (result != AVAssetImageGeneratorSucceeded) {
NSLog(@"couldn't generate thumbnail, error:%@", error);
}
_mainThumbnail.image = [UIImage imageWithCGImage:im];
NSLog(@"thumbnails %@ ",_mainThumbnail.image);
};
CGSize maxSize = CGSizeMake(320, 180);
generator.maximumSize = maxSize;
[generator generateCGImagesAsynchronouslyForTimes:[NSArray arrayWithObject:[NSValue valueWithCMTime:thumbTime]] completionHandler:handler];
The log thumnails null.
Why is the image null? I looked in other forums, and they suggested to use fileURLWithPath -which I am already using.
I am using ios7
Upvotes: 5
Views: 5948
Reputation: 168
SWIFT 5.2 VERSION:
You can find my original post here, where I took inspiration from rozochkin's answer.
func getCurrentFrame() -> UIImage? {
guard let player = self.player, let avPlayerAsset = player.currentItem?.asset else {return nil}
let assetImageGenerator = AVAssetImageGenerator(asset: avPlayerAsset)
assetImageGenerator.requestedTimeToleranceAfter = .zero
assetImageGenerator.requestedTimeToleranceBefore = .zero
assetImageGenerator.appliesPreferredTrackTransform = true
let imageRef = try! assetImageGenerator.copyCGImage(at: player.currentTime(), actualTime: nil)
let image = UIImage(cgImage: imageRef)
return image
}
IMPORTANT NOTES:
requestedTimeToleranceAfter and requestedTimeToleranceBefore should be set to .zero, because, according to source code, "The actual time of the generated images [...] may differ from the requested time for efficiency".
appliesPreferredTrackTransform must be set to TRUE (default is FALSE), otherwise you get a bad-rotated frame. With this property set to TRUE you get what you really see in the player.
Upvotes: 1
Reputation: 19572
Swift version of the accepted answer @Lukas Kukacka answer:
func thumbnailFromVideoAtURL(url: URL) -> UIImage?{
let asset = AVAsset(url: url)
// Get thumbnail at the very start of the video
var thumbnailTime: CMTime = asset.duration
thumbnailTime.value = 0
// Get image from the video at the given time
let imageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.appliesPreferredTrackTransform = true // this rotates the image to the correct position the camera took it in
do{
let imageRef = try imageGenerator.copyCGImage(at: thumbnailTime, actualTime: nil)
return UIImage(cgImage: imageRef)
}catch let err as NSError{
print(err.localizedDescription)
}
return nil
}
Bonus
Under let imageGenerator = AVAssetImageGenerator(asset: asset
) I added:
imageGenerator.appliesPreferredTrackTransform = true
I had a problem where I would record in portrait, get the thumbnail image and it would get returned in landscape. This answer solved that problem.
Upvotes: 0
Reputation: 7704
You can try this method. It takes the first frame of the video at the given URL and returns it as UIImage
.
- (UIImage *)thumbnailFromVideoAtURL:(NSURL *)url
{
AVAsset *asset = [AVAsset assetWithURL:url];
// Get thumbnail at the very start of the video
CMTime thumbnailTime = [asset duration];
thumbnailTime.value = 0;
// Get image from the video at the given time
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
CGImageRef imageRef = [imageGenerator copyCGImageAtTime:thumbnailTime actualTime:NULL error:NULL];
UIImage *thumbnail = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
return thumbnail;
}
The method does not check for errors as my code using it is OK with just nil
returned if anything fails.
Edit: Clarification of CMTime
values
CMTime
has
timescale
- think about this as a FPS (frames per second) of the video (25 FPS -> 1s of the video has 25 video frames)value
- identification of concrete frame. In the code above, this says which frame you want to take thumbnail of.For calculation of frame at exact time, you have to make this calculation:
value = timescale * seconds_to_skip
which is the equivalent to
"Frame of the video to take" = "FPS" * "Number of seconds to skip"
If you want to take for example 1st frame of 2nd second of the video, you want in fact to skip 1st second of the video. So you would use:
CMTime thumbnailTime = [asset duration];
thumbnailTime.value = thumbnailTime.timescale * 1;
Another look: you want to skip first 1s of the video and than take a thumbnail. I.e. on 25 FPS, you want to skip 25 frames. So CMTime.value = 25
(skip 25 frames).
Upvotes: 18
Reputation: 2281
Just in case someone made the same mistake as I did:
Check out if the URL
is generated using appropriate method. You shouldn't get counfused for local and web URL
s of the file.
In case of local video file, use below code:
NSURL *movieURL = [NSURL fileURLWithPath:_moviePath];
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:movieURL options:nil];
In case of video file from internet, use below code:
NSURL *movieURL = [NSURL URLWithString:_moviePath];
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:movieURL options:nil];
Upvotes: 2
Reputation: 4762
I successfully use this snippet, to get a screenshot from video:
- (UIImage *)currentItemScreenShot
{
AVURLAsset *asset = (AVURLAsset *)playerItem_.asset;
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
imageGenerator.appliesPreferredTrackTransform = YES;
CGImageRef thumb = [imageGenerator
copyCGImageAtTime:CMTimeMakeWithSeconds(0, 1.0)
actualTime:NULL
error:NULL];
UIImage *image = [UIImage imageWithCGImage:thumb];
CGImageRelease(thumb);
[imageGenerator release];
return image;
}
But in my case - I am doing it two seconds after video was successfully imported in application. ( It had problems with picture orientation, sometimes returned no image or half image if I did it once video was imported).
Upvotes: 0