I'm making an app that downloads movies from the server and stores them localy in the NSDocumentDirectory.
This works fine.
I want to add a thumbnail generated from each movie in front of the name in each cell.
My problem:
How can I generate a thumbnail from a movie after it is downloaded (so instantly, without having to play the movie first)? I want to store the thumbnails with the same name of the movie as a jpg in the NSDocumentDirectory.
My guess
-download movie and store it in NSDocumentDirectory (works)
-somehow load the movie in the MPMoviePlayerController's memory (don't know how)
-when loaded in memory, generate thumbnail with thumbnailImageAtTime (MPMovieTimeOptionNearestKeyFrame) (should work)
-store it (should work)
If anyone could help me...
Thanks
#import <MediaPlayer/MediaPlayer.h>
-(UIImage*)getFirstFrameFromVideoFile:(NSString*)sourceFilePath {
NSURL *videoURL = [NSURL fileURLWithPath:sourceFilePath];
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL:videoURL];
UIImage *thumbnail = [player thumbnailImageAtTime:1.0 timeOption:MPMovieTimeOptionNearestKeyFrame];
//Player autoplays audio on init
[player stop];
[player release];
return thumbnail;
}
Other tasks you know already.
Yes, using MPMoviePlayer works... but you must be sure that you do not have another movie player playing elsewhere in your app (even the UIWebView plug-in...) or you will get in trouble.
I do this way:
UIImage *thumbnail = nil;
NSURL *url = [NSURL fileURLWithPath:pathname];
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:url options:nil];
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
generator.appliesPreferredTrackTransform = YES;
NSError *error = nil;
CMTime time = CMTimeMake(3, 1); // 3/1 = 3 second(s)
CGImageRef imgRef = [generator copyCGImageAtTime:time actualTime:nil error:&error];
if (error != nil)
NSLog(#"%#: %#", self, error);
thumbnail = [[UIImage alloc] initWithCGImage:imgRef];
CGImageRelease(imgRef);
Hope this might help
This is the code I use which should generate the thumbnail
(added a big uiimageview for testing, works when I load a local image in it)
NSString *path;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
path = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"snijtechniekendir/videos"];
path = [path stringByAppendingPathComponent:[videos objectAtIndex:indexPath.row]];
NSURL *videoURL = [NSURL fileURLWithPath:path];
NSLog(#"video url: %#", videoURL);
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL:videoURL];
UIImage *thumbnail = [player thumbnailImageAtTime:1 timeOption:MPMovieTimeOptionNearestKeyFrame];
UIImageView *cellimage = [[UIImageView alloc] initWithFrame:CGRectMake(2, 2 , 400, 400)];
[cell.contentView addSubview:cellimage];
NSData *imgData = UIImagePNGRepresentation(thumbnail);
NSLog(#"lenght of video thumb: %#", [imgData length]);
[self.view addSubview:cellimage];
[cellimage setImage:thumbnail];
This is the log for one video file
video url: file://localhost/Users/Home/Library/Application%20Support/iPhone%20Simulator/5.1/Applications/78C165BB-75A9-46A2-A257-469F8652A665/Documents/snijtechniekendir/videos/snijtechniek%2520brunoise.mp4
lenght of video thumb: (null)
Related
I have an app that lets the user record a short video and generate a gif image. Now I want to give the user the possibility to share that gif image using UIActivityViewController, I've done this:
UIImage *img = ...
NSArray *objectsToShare = #[img];
UIActivityViewController *avc = [[UIActivityViewController alloc] initWithActivityItems:objectsToShare applicationActivities:nil];
NSArray *excludeActivities = #[UIActivityTypePrint];
avc.excludedActivityTypes = excludeActivities;
[self presentViewController:avc animated:YES completion:nil];
The problem is that this only shares an static .jpg image (like the first frame of the original .gif image).
On the other hand, the user can save the gif image to the camera roll, and from there it is possible to share it as a .gif. So how can I do the same thing but from my own app?
Thanks in advance.
Ok, I managed to do this, and now I can share gif images by email, but I thing there is something left to do with Twitter.
UIImage *img = ...;
NSError *err;
NSData *imgData = [imageSerialization animatedGIFDataWithImage:img error:&err];
NSLog(#"%#", err);
NSArray *objectsToShare = #[imgData];
UIActivityViewController *avc = [[UIActivityViewController alloc] initWithActivityItems:objectsToShare applicationActivities:nil];
NSArray *excludeActivities = #[UIActivityTypePrint, UIActivityTypeAssignToContact];
avc.excludedActivityTypes = excludeActivities;
[self presentViewController:avc animated:YES completion:nil];
Share GIF File For WhatsApp:
NSURL *imageUrl =[self.ImageArray objectAtIndex:currentPhotoIndex];
NSString *path=imageUrl.absoluteString;
NSArray *strings = [path componentsSeparatedByString:#"/"];
NSString *mygif=[strings objectAtIndex:strings.count-1];
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *dataPath = [documentsPath stringByAppendingPathComponent:#"/MrHRamani"];
NSString *filePath = [dataPath stringByAppendingPathComponent:mygif];
NSURL *urll=[NSURL fileURLWithPath:filePath];
NSLog(#"imag %#",imageUrl);
self.documentationInteractionController.delegate = self;
self.documentationInteractionController.UTI = #"net.whatsapp.image";
self.documentationInteractionController = [self setupControllerWithURL:urll usingDelegate:self];
[self.documentationInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.view animated:YES];
I am using this for reference: Getting thumbnail from a video url or data in IPhone SDK
The method is using the MPMoviePlayerController class instead of the AVFoundation, and I think I want to use that as well because the people said that MPMoviePlayer way is faster than the AVFoundation way.
The problem is, the method used to create the thumbnails, [player thumbnailImageAtTime:1.0 timeOption:MPMovieTimeOptionNearestKeyFrame] is deprecated in iOS 7.0.
By looking at the apple docs, the remaining supported ways to create thumbnails are by the methods (void)requestThumbnailImagesAtTimes:(NSArray *)playbackTimes timeOption:(MPMovieTimeOption)option and (void)cancelAllThumbnailImageRequests. But, as the method signatures dictate, these methods return nothing. So how do I access the UIImage thumbnail created by these methods?
If it helps, this is what I have so far in terms of code:
self.videoURL = info[UIImagePickerControllerMediaURL];
NSData *videoData = [NSData dataWithContentsOfURL:self.videoURL];
//Create thumbnail image
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL:self.videoURL];
[player requestThumbnailImagesAtTimes:#[#1] timeOption:MPMovieTimeOptionNearestKeyFrame];
//UIImage *thumbnail = ???
How do I get a UIImage reference to the thumbnail?
EDIT
I figured out how to create a notification for the thumbnail image request (using this question as reference). However, I realise that this method works asynchronously from the main thread, and so my notification handler method doesn't seem to ever be called.
This is what I have now.
self.videoURL = info[UIImagePickerControllerMediaURL];
NSData *videoData = [NSData dataWithContentsOfURL:self.videoURL];
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL:self.videoURL];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleThumbnailImageRequestFinishNotification:) name:MPMoviePlayerThumbnailImageRequestDidFinishNotification object:player];
[player requestThumbnailImagesAtTimes:#[#1] timeOption:MPMovieTimeOptionNearestKeyFrame];
And then my handler method:
-(void)handleThumbnailImageRequestFinishNotification:(NSNotification*)notification
{
NSDictionary *userinfo = [notification userInfo];
NSError* value = [userinfo objectForKey:MPMoviePlayerThumbnailErrorKey];
if (value != nil)
{
NSLog(#"Error creating video thumbnail image. Details: %#", [value debugDescription]);
}
else
{
UIImage *thumbnail = [userinfo valueForKey:MPMoviePlayerThumbnailImageKey];
}
But the handler never gets called (or so it appears).
Try this way.
import AVFoundation framework
in *.h
#import <AVFoundation/AVFoundation.h>
in *.m
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:self.urlForConevW options:nil];
AVAssetImageGenerator *generateImg = [[AVAssetImageGenerator alloc] initWithAsset:asset];
NSError *error = NULL;
CMTime time = CMTimeMake(1, 65);
CGImageRef refImg = [generateImg copyCGImageAtTime:time actualTime:NULL error:&error];
NSLog(#"error==%#, Refimage==%#", error, refImg);
UIImage *FrameImage= [[UIImage alloc] initWithCGImage:refImg];
Here is a code to make a thumbnail of the video and save the images to the DocumentDirectory..
//pass the video_path to NSURL
NSURL *videoURL = [NSURL fileURLWithPath:strVideoPath];
AVURLAsset *asset1 = [[AVURLAsset alloc] initWithURL:videoURL options:nil];
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset1];
generator.appliesPreferredTrackTransform = YES;
//Set the time and size of thumbnail for image
NSError *err = NULL;
CMTime thumbTime = CMTimeMakeWithSeconds(0,30);
CGSize maxSize = CGSizeMake(425,355);
generator.maximumSize = maxSize;
CGImageRef imgRef = [generator copyCGImageAtTime:thumbTime actualTime:NULL error:&err];
UIImage *thumbnail = [[UIImage alloc] initWithCGImage:imgRef];
//And you can save the image to the DocumentDirectory
NSData *data = UIImagePNGRepresentation(thumbnail);
//Path for the documentDirectory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
[data writeToFile:[documentsDirectory stringByAppendingPathComponent:currentFileName] atomically:YES];
If your URL is to an HTTP live stream, then it won't return anything, per the docs. For a file URL, I found that I had to start the request after playing the movie, or it would never get called.
I´m designing a new app for an iPad. This app will work with a barcode scanner, and when it scans a code, it will show an image asociated.
I was thinking to build that asociating de barcode to an image name for example:
- Barcode 09090909 will show 09090909.png picture
- Barcode 19191919 will show 19191919.png picture
....
I think that there is no problem with that, but the problem comes when I need to add new barcode/pictures to the app. How can I send a new picture to my App? I see that when you develop on XCode and build you App, all the data goes into de App.
Any help or clue? thanks in advance
You can simple load them on the fly from your server, but then a network connection is required. To load them async from an server you can use this code:
dispatch_queue_t image_queue = dispatch_queue_create("com.company.app.imageQueue", NULL);
dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_async(image_queue, ^{
NSString *URLString = #"http://example.com/images/19191919.png";
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:URLString];
UIImage *image = [UIImage imageWithData:imageData];
dispatch_async(main_queue, ^{
[imageView setImage:image];
});
});
If you want you can also save the image to the cache dir (using the document dir is not recommended because it will collide with Apples iOS Data Storage Guidelines) and check before downloading an image twice, then the code will look like this:
NSString *fileName = #"19191919.png";
NSString *URLBaseString = #"http://example.com/images/";
NSString *URLString = [URLBaseString stringByAppendingString:fileName];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachePath = [paths objectAtIndex:0];
NSString *dataPath = [cachePath stringByAppendingPathComponent:fileName];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ( [fileManager fileExistsAtPath:dataPath] )
{
UIImage *image = [UIImage imageWithContentsOfFile:dataPath];
[imageView setImage:image];
}
else
{
dispatch_async(image_queue, ^{
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:URLString];
UIImage *image = [UIImage imageWithData:imageData];
[UIImagePNGRepresentation(image) writeToFile:dataPath atomically:YES];
dispatch_async(main_queue, ^{
[imageView setImage:image];
});
});
}
This code has not been tested, but should work. Note that I'm relying on ARC to manage memory.
I'm very new to Objective-C, and am having some beginner issues. I have an application that has an area that is supposed to behave somewhat like a photo gallery. The user chooses a picture from their camera roll, and the photos get displayed in UIImageViews. I'm trying to save the image that they select. I have 9 UIImageView's, and the issue is that when I select a different photo for each UIImageView, close and relaunch the app, the other 8 UIImageViews display the photo that is stored in the first image view. Here is the code that I'm working with:
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilename9];
}
- (void)applicationDidEnterBackground:(UIApplication*)application {
NSLog(#"Image on didenterbackground: %#", imageView);
self.imageData = [NSData dataWithData:UIImagePNGRepresentation(imageView.image)];
self.imageData = [NSData dataWithData:UIImagePNGRepresentation(imageView2.image)];
self.imageData = [NSData dataWithData:UIImagePNGRepresentation(imageView3.image)];
self.imageData = [NSData dataWithData:UIImagePNGRepresentation(imageView4.image)];
self.imageData = [NSData dataWithData:UIImagePNGRepresentation(imageView5.image)];
self.imageData = [NSData dataWithData:UIImagePNGRepresentation(imageView6.image)];
self.imageData = [NSData dataWithData:UIImagePNGRepresentation(imageView7.image)];
self.imageData = [NSData dataWithData:UIImagePNGRepresentation(imageView8.image)];
self.imageData = [NSData dataWithData:UIImagePNGRepresentation(imageView9.image)];
[self.imageData writeToFile:[self dataFilePath] atomically:YES];
NSLog(#"The image is: %#", [[imageView image] description]);
NSLog(#"dataFilePath is: %#", [self dataFilePath]);
}
- (void)viewDidLoad
{
NSString *filePath = [self dataFilePath];
NSLog(#"FilePath: %#", filePath);
NSLog(#"Image: %#", imageView);
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSData *vdlData = [[NSData alloc] initWithContentsOfFile:filePath];
imageView.image = [[UIImage alloc] initWithData:vdlData];
imageView2.image = [[UIImage alloc] initWithData:vdlData];
imageView3.image = [[UIImage alloc] initWithData:vdlData];
imageView4.image = [[UIImage alloc] initWithData:vdlData];
imageView5.image = [[UIImage alloc] initWithData:vdlData];
imageView6.image = [[UIImage alloc] initWithData:vdlData];
imageView7.image = [[UIImage alloc] initWithData:vdlData];
imageView8.image = [[UIImage alloc] initWithData:vdlData];
imageView9.image = [[UIImage alloc] initWithData:vdlData];
}
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:app];
[super viewDidLoad];
}
I'm trying to figure out what I need to change to get the UIImageViews to display the correct pictures, rather than them all displaying the same picture. This is probably a simple fix, but any help would be greatly appreciated, thanks!
Okay, here's how I would do it:
Use NSUserDefaults to save your images as a mutable array:
ViewController.h
#property(retain) NSUserDefaults *user;
ViewController.m
#synthesize user;
- (void)viewWillAppear:(BOOL)animated
{
self.user = [NSUserDefaults standardUserDefaults];
Edit
NSMutableArray* array = [[self.user objectForKey:#"images"]mutableCopy];
while(array == nil)
{
[self.user setObject:[NSMutableArray arrayWithObject:#""] forKey:#"images"]
array = [[self.user objectForKey:#"images"]mutableCopy];
NSLog(#"%#",#"attempting to create an array to store the images in");
}
End Edit
}
- (void)applicationDidEnterBackground:(UIApplication*)application {
NSLog(#"Image on didenterbackground: %#", imageView);
NSMutableArray* array = [NSMutableArray arrayWithObject:[NSData dataWithData:UIImagePNGRepresentation(imageView.image)]];
[array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView2.image)];
[array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView3.image)];
[array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView4.image)];
[array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView5.image)];
[array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView6.image)];
[array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView7.image)];
[array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView8.image)];
[array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView9.image)];
[self.user setObject:array forKey:#"images"];
}
- (void)viewDidLoad
{
NSMutableArray* array = [[self.user objectForKey:#"images"]mutableCopy];
EDIT
if(array.count == 9)
{
imageView.image = [[UIImage alloc] initWithData:[array objectAtIndex:0]];
imageView2.image = [[UIImage alloc] initWithData:[array objectAtIndex:1]];
imageView3.image = [[UIImage alloc] initWithData:[array objectAtIndex:2]];
imageView4.image = [[UIImage alloc] initWithData:[array objectAtIndex:3]];
imageView5.image = [[UIImage alloc] initWithData:[array objectAtIndex:4]];
imageView6.image = [[UIImage alloc] initWithData:[array objectAtIndex:5]];
imageView7.image = [[UIImage alloc] initWithData:[array objectAtIndex:6]];
imageView8.image = [[UIImage alloc] initWithData:[array objectAtIndex:7]];
imageView9.image = [[UIImage alloc] initWithData:[array objectAtIndex:8]];
}
END EDIT
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:app];
[super viewDidLoad];
}
- (void)viewDidUnload
{
self.user = nil;
}
This way, you will not lose the images or data, they will be stored and easily accessed, and they will not disappear even if you update your app.
Cheers!
Before I start with the solution, I have to warn you that the way you're doing this isn't the right one. I suggest that you start learning iOS development from the ground up. Apple's own documentation is a pretty good start. http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/Introduction/Introduction.html
Now, back to your question. What you do here is save only one image, not all 9 of them. You set self.imageData always with each image you process and you overwrite its previous value, making only the last image to be saved to file.
So, in order to make your code working, you would have to use an imageData object for each image view, then write that data object to file.
In your case, it's probably best to optimize the code by using loops, instead using multiple objects (like imageView, imageView2, ...).
Also, make sure that you take care of your memory. e.g. imageView.image is allocated but not released.
Well, I see two issues. First and foremost, in viewDidLoad, all your images are getting initWithData:vdlData... so they're all getting the same data. That's why they're all the same.
Also, when you're trying to save them, in ...didEnterBackground, you are overwriting the value of imageData over and over again... when you save it, it's just the last one you've assigned to imageData. You probably want to create an NSArray, and store them in there, pulling them out of the array in viewDidLoad.
I am developing an iPhone app for my University radio station, and I would like to insert ads (that look and feel just like iAds) but with my own custom designs/content/links so I can sell this adspace to possible sponsors.
Would anyone know how I can do this or point me in the right direction? I am building a "utility" style app.
I have a JSON file on my server with certain data about the ad (mine happens to be set up for one, but you could accomodate multiple the same way).
{"promo":"yes","imageURL":"http://somedomain/testAd.png","image2xURL":"http://somedomain/testAd#2x.png","link":"http://www.whereTheAdShouldDirect.com"}
Then, in the app, I have this amongst the rest of viewWillAppear:
NSURL *url = [NSURL URLWithString:#"http://www.mydomain/promo.php"];
NSString *response = [[NSString alloc] initWithContentsOfURL:url];
const char *convert = [response UTF8String];
NSString *responseString = [NSString stringWithUTF8String:convert];
NSDictionary *promo = [responseString JSONValue];
[response release];
if([[promo objectForKey:#"promo"] isEqualToString:#"yes"]){
self.linkURL = [NSURL URLWithString:[promo objectForKey:#"link"]];
NSURL *picURL = [NSURL URLWithString:[promo objectForKey:#"imageURL"]];
if([[[UIDevice currentDevice] systemVersion]intValue]>=4){
if([[UIScreen mainScreen] scale]==2.0){
picURL = [NSURL URLWithString:[promo objectForKey:#"image2xURL"]];
}
}
CGRect imgFrame = CGRectMake(0, 0, 320, 50);
UIButton *adImage=[[UIButton alloc] initWithFrame:imgFrame];
NSData * imageData = [NSData dataWithContentsOfURL:picURL];
UIImage * image = [UIImage imageWithData:imageData];
[adImage setBackgroundImage:image forState:UIControlStateNormal];
[adImage addTarget:self action:#selector(ad) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:adImage];
[adImage release];
}
and this method as well:
-(void)ad{
[[UIApplication sharedApplication] openURL:self.linkURL];
}
You may want to change that last method depending on how you want the ad to react (load a webview right in the app?)