Create a thumbnail from url - ios

I am using the code below to create a video thumbnail from a Url, the code is working perfect but it takes long time and it's jamming the app till it's create the image.
Her is my code:
NSString *one = self.currentList.videoLink;
NSURL * imageURL = [NSURL URLWithString:one];
AVURLAsset *asset1 = [[AVURLAsset alloc] initWithURL:imageURL options:nil];
AVAssetImageGenerator *generate1 = [[AVAssetImageGenerator alloc] initWithAsset:asset1];
generate1.appliesPreferredTrackTransform = YES;
NSError *err = NULL;
CMTime time = CMTimeMake(2,1);
CGImageRef oneRef = [generate1 copyCGImageAtTime:time actualTime:NULL error:&err];
UIImage *oneme = [[UIImage alloc] initWithCGImage:oneRef];
[self.videoImage setImage:oneme];
self.videoImage.contentMode = UIViewContentModeScaleToFill;
As I said, the code work fine. Can any one help me to solve the delay in creating the thumbnail?
Thanks and I hope the question is clear.

If the one URL is some remote URL, you are networking synchronously. That would be a lot of your problem right there. You're blocking the main thread while you network ("jamming the app", as you put it). Network properly, with URLSession or AFNetworking or whatever. That way, you don't block the main thread.
(By the way, blocking the main thread will cause your app to crash if you do it on a device. You might not even get into the app store, if Apple notices you're doing that.)

Related

How to generate thumbnails of images on iCloud Drive?

This appears easy, but the lack of documentation makes this question impossible to guess.
I have pictures and videos on my app's icloud drive and I want to create thumbnails of these assets. I am talking about assets on iCloud Drive, not the iCloud photo stream inside the camera roll. I am talking about the real iCloud Drive folder.
Creating thumbnails from videos are "easy" compared to images. You just need 2 weeks to figure out how it works, having in mind the poor documentation Apple wrote but thumbnails from images seem impossible.
What I have now is an array of NSMetadataItems each one describing one item on the iCloud folder.
These are the methods I have tried so far that don't work:
METHOD 1
[fileURL startAccessingSecurityScopedResource];
NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] init];
__block NSError *error;
[coordinator coordinateReadingItemAtURL:fileURL
options:NSFileCoordinatorReadingImmediatelyAvailableMetadataOnly
error:&error
byAccessor:^(NSURL *newURL) {
NSDictionary *thumb;
BOOL success = [newURL getResourceValue:&thumb forKey:NSURLThumbnailDictionaryKey error:&error];
UIImage *thumbnail = thumb[NSThumbnail1024x1024SizeKey];
}];
[fileURL stopAccessingSecurityScopedResource];
The results of this method are fantastic. Ready for that? Here we go: success = YES, error = nil and thumbnail = nil.
ANOTHER METHOD
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:fileURL
options:nil];
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
imageGenerator.appliesPreferredTrackTransform = YES;
CMTime time = CMTimeMake(0, 60); // time range in which you want
NSValue *timeValue = [NSValue valueWithCMTime:time];
[imageGenerator generateCGImagesAsynchronouslyForTimes:#[timeValue] completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * error) {
thumbnail = [[UIImage alloc] initWithCGImage:image];
}];
error = The requested URL was not found on this server. and thumbnail = nil
This method appears to be just for videos. I was trying this just in case. Any equivalent of this method to images?
PRIMITIVE METHOD
NSData *tempData = [NSData dataWithContentsOfUrl:tempURL];
NOPE - data = nil
METHOD 4
The fourth possible method would be using ALAsset but this was deprecated on iOS 9.
I think that all these methods fail because they just work (bug or not) if the resource is local. Any ideas on how to download the image so I can get the thumbnail?
Any other ideas?
thanks
EDIT: after several tests I see that Method 1 is the only one that seems to be in the right direction. This method works poorly, sometimes grabbing the icon but most part of the time not working.
Another point is this. Whatever people suggests me, they always say about downloading the whole image to get the thumbnail. I don't think this is the way to go. Just see how getting thumbnails of video work. You don't download the whole video to get its thumbnail.
So this
question remains open.
The Photos-Framework or AssetsLibrary will not work here as you would have to import your iCloud Drive Photos first to the PhotoLibrary to use any methods of these two frameworks.
What you should look at is ImageIO:
Get the content of the iCloud Drive Photo as NSData and then proceed like this:
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)(imageData), NULL );
NSDictionary* thumbOpts = [NSDictionary dictionaryWithObjectsAndKeys:
(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform,
(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageAlways,
[NSNumber numberWithInt:160],(id)kCGImageSourceThumbnailMaxPixelSize,
nil];
CGImageRef thumbImageRef = CGImageSourceCreateThumbnailAtIndex(source,0,(__bridge CFDictionaryRef)thumbOpts);
UIImage *thumbnail = [[UIImage alloc] initWithCGImage: thumbImageRef];
After testing several solutions, the one that seems to work better is this one:
[fileURL startAccessingSecurityScopedResource];
NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] init];
__block NSError *error;
[coordinator coordinateReadingItemAtURL:fileURL
options:NSFileCoordinatorReadingImmediatelyAvailableMetadataOnly
error:&error
byAccessor:^(NSURL *newURL) {
NSDictionary *thumb;
BOOL success = [newURL getResourceValue:&thumb forKey:NSURLThumbnailDictionaryKey error:&error];
UIImage *thumbnail = thumb[NSThumbnail1024x1024SizeKey];
}];
[fileURL stopAccessingSecurityScopedResource];
This solution is not perfect. It will fail to bring the thumbnails sometimes but I was not able to find any other solution that works 100%. Others are worst than that.
This works for me.It has a little bit different
func genereatePreviewForOnce(at size: CGSize,completionHandler: #escaping (UIImage?) -> Void) {
_ = fileURL.startAccessingSecurityScopedResource()
let fileCoorinator = NSFileCoordinator.init()
fileCoorinator.coordinate(readingItemAt: fileURL, options: .immediatelyAvailableMetadataOnly, error: nil) { (url) in
if let res = try? url.resourceValues(forKeys: [.thumbnailDictionaryKey]),
let dict = res.thumbnailDictionary {
let image = dict[.NSThumbnail1024x1024SizeKey]
completionHandler(image)
} else {
fileURL.removeCachedResourceValue(forKey: .thumbnailDictionaryKey)
completionHandler(nil)
}
fileURL.stopAccessingSecurityScopedResource()
}
}
It looks like you are generating your thumbnail after the fact. If this is your document and you are using UIDocument, override fileAttributesToWriteToURL:forSaveOperation:error: to insert the thumbnail when the document is saved.

ios UICollectionView slow scrolling and memory warning

I am currently working on a project that involves a UICollectionView populated by AVAssets imported from a UIImagepickerController, after 10 or so item are in the collection, Scrolling becomes laggy and slow, and occasionally I receive memory warnings. I believe the problem to be in the thumbnail generation which happens in realtime, here is the code i use:
- (void) setAsset:(AVAsset *)asset
{
_asset = asset;
AVAssetImageGenerator *generate = [[AVAssetImageGenerator alloc] initWithAsset:_asset];
NSError *err = NULL;
CMTime time = CMTimeMake(1, 60);
generate.appliesPreferredTrackTransform = YES;
CGImageRef imgRef = [generate copyCGImageAtTime:time actualTime:NULL error:&err];
self.VideoImageView.image = [UIImage imageWithCGImage:(imgRef)];
}
Is there another less "expensive way" to achieve this without delay?
Any help on the matter would be greatly appreciated.
You can do the real time thumbnail generation on a background thread, and then jump back to the main thread when the operation is done to set the actual thumbnail.
Right now you're doing everything on the main thread, which blocks the UI, and makes the scrolling jerky.
You can set a placeholder image in your cell and generate thumbnail in background queue.
Then set it to your image view.
AsynImageView might be of some use to you.
Open another thread to deal with the image thing. When your image done switch back to main thread to update a cell. You can do this with GCD
Here are the code that asynchronous ganerate thumbnail image for video.
-(void)generateImage
{
NSURL *url = [NSURL fileURLWithPath:_videoPath];
AVURLAsset *asset=[[AVURLAsset alloc] initWithURL:url options:nil];
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
generator.appliesPreferredTrackTransform=TRUE;
CMTime thumbTime = CMTimeMakeWithSeconds(30,30);
AVAssetImageGeneratorCompletionHandler handler = ^(CMTime requestedTime, CGImageRef im, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error){
if (result != AVAssetImageGeneratorSucceeded) {
NSLog(#"couldn't generate thumbnail, error:%#", error);
}
// TODO Do something with the image
};
CGSize maxSize = CGSizeMake(128, 128);
generator.maximumSize = maxSize;
[generator generateCGImagesAsynchronouslyForTimes:[NSArray arrayWithObject:[NSValue valueWithCMTime:thumbTime]] completionHandler:handler];
}
Hope this help you.

How to get just a single image from a video?

I've been going through many codes on it but I've not found a working solution yet. I'm using Xcode 5.1.1 and Iphone Retina 4inch.I want to get a single image from a video by clicking on it. After that, I'll edit the image and apply the effects to entire video. Thank you.
UPDATE:
I've found this code for the same purpose. It's not working on the simulator either. Can somebody tell me what's the problem?
-(UIImage *)generateThumbImage : (NSString *)filepath
{
NSURL *url = [NSURL fileURLWithPath:filepath];
AVAsset *asset = [AVAsset assetWithURL:url];
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc]initWithAsset:asset];
CMTime time = [asset duration];
time.value = 0;
CGImageRef imageRef = [imageGenerator copyCGImageAtTime:time actualTime:NULL error:NULL];
UIImage *thumbnail = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef); // CGImageRef won't be released by ARC
return thumbnail;
}
This code will generating an image from a particular time in the loaded video and convert the image from a CGImage to UIImage.
The code is commented to explain its parts.
- (UIImage*)loadImage {
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:vidURL options:nil];
AVAssetImageGenerator *generate = [[AVAssetImageGenerator alloc] initWithAsset:asset]; // Create object of Video.
NSError *err = NULL;
CMTime time = CMTimeMake(1, 60); // Select time of 1/60ths of a second.
CGImageRef imgRef = [generate copyCGImageAtTime:time actualTime:NULL error:&err]; // Get Image on that time frame.
NSLog(#"err==%#, imageRef==%#", err, imgRef); // if something is not as expected then u can log error.
return [[UIImage alloc] initWithCGImage:imgRef]; // Convert Image from CGImage to UIImage so you can display it easily in a image view.
}
Have you tried AVFoundation framework?
That's what it's for. The documentation is on the Apple site:
AV Foundation framework provides essential services for working with
time-based audiovisual media on iOS and OS X. Through a modern
Objective-C interface, you can easily play, capture, edit, or encode
media formats such as QuickTime movies and MPEG-4 files.

Getting thumbnail of a video picked from photos library

I know this question has been asked many times on stackoverflow. But my problem is different.
I am iterating on the albumns of photos library to get all videos and their thumbnails.
Now, the problem is, my code is very slow to get the thumbnail of each video. For example, there is 14 videos in my camera roll and the total time taken to generate the thumbnail is around 3-4 seconds. I am using this code.
+(UIImage*)imageFromVideoAtURL:(NSURL*)contentURL {
UIImage* theImage = nil;
AVURLAsset* asset = [[AVURLAsset alloc] initWithURL:contentURL options:nil];
AVAssetImageGenerator* generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
generator.appliesPreferredTrackTransform = YES;
NSError* err = NULL;
CMTime time = CMTimeMake(1, 60);
CGImageRef imgRef = [generator copyCGImageAtTime:time actualTime:NULL error:&err];
theImage = [[[UIImage alloc] initWithCGImage:imgRef] autorelease];
CGImageRelease(imgRef);
[asset release];
[generator release];
return theImage;
}
I am finding a way to get the thumbnail of all videos very fast so that user has not to wait. I have seen apps on Apple store that are doing the same thing in just micro seconds. Please help.
I have also tried this code to generate the thumbnail, it is also very slow.
MPMoviePlayerController *moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:mediaUrl];
moviePlayer.shouldAutoplay = NO;
UIImage *thumbnail = [[moviePlayer thumbnailImageAtTime:0.0 timeOption:MPMovieTimeOptionNearestKeyFrame] retain];
[imageView setImage:thumbnail]; //imageView is a UIImageView
If you are loading videos from image library, it should already have the embedded thumbnail of the video. This can be accessed using thumbnail or aspectRatioThumnail methods of ALAsset class.
So in your case the thumbnails could be loaded like:
ALAssetLibrary* lib = [ALAssetLibrary new];
[lib assetForURL:contentURL resultBlock:^(ALAsset* asset) {
CGImageRef thumb = [asset thumbnail];
dispatch_async(dispatch_get_main_queue(), ^{
//do any UI operation here with thumb
});
}];
Please make sure to make any UIKit call in the main queue as assetForURL:: method may invoke the resultBlock in some background thread.

MPMoviePlayerController: How to turn off sound during requestThumbnailImagesAtTimes:timeOption:

In my app I record a movie, save it to the PhotosAlbum, and request then creation of a thumbnail using the code
self.player = [[MPMoviePlayerController alloc] initWithContentURL:videoURL_];
NSArray *times = #[#(1.1)];
[self.player requestThumbnailImagesAtTimes:times timeOption:MPMovieTimeOptionNearestKeyFrame];
This works fine, but during the thumbnail creation, MPMoviePlayerController plays the sound of the movie, although the movie is not visible, which is annoying.
How can I turn the sound off just of this particular MPMoviePlayerController? The MPMoviePlayerController class has apparently no property to control this.
Just add this line after creating your player:
self.player.shouldAutoplay = NO;
According to the answer in How to mute/unmute audio when playing video using MPMoviePlayerController? it is not possible to turn off the sound of a MPMoviePlayerController.
However I found in Creating Thumbnail for Video in iOS a much easier way to create thumbnails, which does not play sound and is synchronous so that handling of asynchronous callbacks is not required.
For convenience, I copy it here:
Add the AVFoundation framework to you app.
#import <AVFoundation/AVFoundation.h>
Add the following code:
UIImage *theImage = nil;
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:contentURL options:nil];
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
generator.appliesPreferredTrackTransform = YES;
NSError *err = NULL;
CMTime time = CMTimeMake(1, 60);
CGImageRef imgRef = [generator copyCGImageAtTime:time actualTime:NULL error:&err];
theImage = [[UIImage alloc] initWithCGImage:imgRef];
CGImageRelease(imgRef);

Resources