From what i have read apple doesnt expose the api to allow developers to get a thumbnail of a movie using the current sdk.
Can any one share some code as to how they are going about this?
I have heard you can access the camera picker view and find the image view just before the ui image picker is closed. This seems kinda ugly.
Also ive considered using ffmpeg to grab a frame out of the movie but wouldnt hava a clue as to how to compile it as a library for the iphone. Any pointers would be greatly appreciated
Hope my code could help you guys. It's ugly. I think apple should open this kind of APIs.
Of course, all NSLog() should be removed. It's just for demonstration.
alvin
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
// e.g.
NSString *tempFilePath = [(NSURL *)[info valueForKey:UIImagePickerControllerMediaURL] absoluteString];
NSLog(#"didFinishPickingMediaWithInfo: %#",tempFilePath);
// e.g. /private/var/mobile/Applications/D1E784A4-EC1A-402B-81BF-F36D3A08A332/tmp/capture/capturedvideo.MOV
tempFilePath = [[tempFilePath substringFromIndex:16] retain];
NSLog(#"didFinishPickingMediaWithInfo: %#",tempFilePath);
NSLog(#"===Try to save video to camera roll.===");
NSLog(#"UIVideoAtPathIsCompatibleWithSavedPhotosAlbum: %#",UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(tempFilePath)? #"YES":#"NO");
// Check if the video file can be saved to camera roll.
if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(tempFilePath)){
// YES. Copy it to the camera roll.
UISaveVideoAtPathToSavedPhotosAlbum(tempFilePath, self, #selector(video:didFinishSavingWithError:contextInfo:), tempFilePath);
}
[self dismissModalViewControllerAnimated:YES];
}
- (void)video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(NSString *)contextInfo{
NSLog(#"didFinishSavingWithError--videoPath in camera roll:%#",videoPath);
NSLog(#"didFinishSavingWithError--videoPath in temp directory:%#",contextInfo);
// The thumbnail jpg should located in this directory.
NSString *thumbnailDirectory = [[contextInfo stringByDeletingLastPathComponent] stringByDeletingLastPathComponent];
// Debug info. list all files in the directory of the video file.
// e.g. /private/var/mobile/Applications/D1E784A4-EC1A-402B-81BF-F36D3A08A332/tmp/capture
NSLog([contextInfo stringByDeletingLastPathComponent]);
NSLog([[[NSFileManager defaultManager] contentsOfDirectoryAtPath:[contextInfo stringByDeletingLastPathComponent] error:nil] description]);
// Debug info. list all files in the parent directory of the video file, i.e. the "~/tmp" directory.
// e.g. /private/var/mobile/Applications/D1E784A4-EC1A-402B-81BF-F36D3A08A332/tmp
NSLog(thumbnailDirectory);
NSLog([[[NSFileManager defaultManager] contentsOfDirectoryAtPath:thumbnailDirectory error:nil] description]);
///////////////////
// Find the thumbnail for the video just recorded.
NSString *file,*latestFile;
NSDate *latestDate = [NSDate distantPast];
NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:[[contextInfo stringByDeletingLastPathComponent]stringByDeletingLastPathComponent]];
// Enumerate all files in the ~/tmp directory
while (file = [dirEnum nextObject]) {
// Only check files with jpg extension.
if ([[file pathExtension] isEqualToString: #"jpg"]) {
NSLog(#"***latestDate:%#",latestDate);
NSLog(#"***file name:%#",file);
NSLog(#"***NSFileSize:%#", [[dirEnum fileAttributes] valueForKey:#"NSFileSize"]);
NSLog(#"***NSFileModificationDate:%#", [[dirEnum fileAttributes] valueForKey:#"NSFileModificationDate"]);
// Check if current jpg file is the latest one.
if ([(NSDate *)[[dirEnum fileAttributes] valueForKey:#"NSFileModificationDate"] compare:latestDate] == NSOrderedDescending){
latestDate = [[dirEnum fileAttributes] valueForKey:#"NSFileModificationDate"];
latestFile = file;
NSLog(#"***latestFile changed:%#",latestFile);
}
}
}
// The thumbnail path.
latestFile = [NSTemporaryDirectory() stringByAppendingPathComponent:latestFile];
NSLog(#"****** The thumbnail file should be this one:%#",latestFile);
// Your code ...
// Your code ...
// Your code ...
}
There is a jpg in the same folder as the the movie that is the thumbnail. I have only tested it with video recorded from the phone, but it works fine. Its not named the same as the movie, so get the directory path the movie is in and iterate for a .jpg and away you go.
Best method I've found... MPMoviePlayerController thumbnailImageAtTime:timeOption
NSString *videoLink=[info objectForKey:#"UIImagePickerControllerMediaURL"];
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL:(NSURL*)videoLink];
UIImage *imageSel = [player thumbnailImageAtTime:1.0 timeOption:MPMovieTimeOptionNearestKeyFrame];
[player stop];
[player release];
Here's a blog post about extracting frames from movies using ffmpeg:
http://www.codza.com/extracting-frames-from-movies-on-iphone
The corresponding project on github: iFrameExtractor
Just wanted to provide an update on this. Things seem to work in terms of getting the thumbnail when you've set your picker to UIImagePickerControllerSourceTypeCamera. However when you try to pick from the existing library (i.e. UIImagePickerControllerSourceTypePhotoLibrary)
the .jpg for the thumbnail never gets created. I even tried re-saving with UISaveVideoAtPathToSavedPhotosAlbum(... ) and still no dice. It seems like the thumbnail is first created when the video is captured. That is your only opportunity to "grab" it. Afterwards it gets moved into a subdirectory of /var/mobile/Media/DCIM/ . While you can actually list the thumbnail image and see that is there, that file is unreadable and uncopyable as I believe that directory is protected.
If anyone has a workaround for this I would greatly appreciate it as in my case I need to grab the thumbnail of existing video after choosing it from the library.
If your using UIImagePickerController to get the image, then a JPG would not be stored in the tmp directory. I'm not sure of the answer for this, but ffmpeg, more specifically the libraries behind it, may be your option.
Related
I am working on an application that records a "videos", and save it to a folder in the documents directory. I was wondering how I would go about obtaining a thumbnail like in Photo's application. So that when the user clicks on it, I will play a video in full screen.
Here is a screenshot.
Maybe this can help you:
MPMoviePlayerController *movie = [[MPMoviePlayerController alloc] initWithContentURL:fileUrl];
movie.shouldAutoplay = NO;
UIImage *image = [movie thumbnailImageAtTime:0 timeOption:MPMovieTimeOptionNearestKeyFrame];
[movie release];
I am going to ask that one question that perhaps has been already asked a million times.
I am making an app for iPad and want to give users the ability to multi-select images from their photo-library. I already have a working code for user to select one image at a time. (not what I need)
I have already downloaded and looked into ELC image picker sample code but that code is not compatible with iOS 5 or Xcode 4. i.e. it has ARC and compile problems left and right, its using release and dealloc all over the place.
I am just frustrated that apple hasn't already created a built in-api for us developers for this most commonly requested functionality in most of our iPhone/ipad apps. (not one but multi-select pics)
Is there any other sample code available? Trust me, I have been googling for a while.
Ok, I have this figured out. The problem with Assets Library is that it gives you all the GEO data of the image. What that means for your users using your app is that they will get a prompt saying that your app is trying to access their location. Infact all you are trying to do is let them choose multiple images from their photo-album. Most users will be turned off thinking its a piracy issue. The best approach is to use apples api of imagePickerController. I know it lets you choose one pic at a time but if you add the following code, it will let you choose multiple pictures.
The way I am doing is let the users keep selecting pictures they want, keep saving those files in the app documents directory, till they hit the done button. See here my sample code and hopefully it will save you the pain of going through Assets Library
-(IBAction)selectExitingPicture
{
//Specially for fing iPAD
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
imagePicker.mediaTypes = [NSArray arrayWithObject:(NSString *)kUTTypeImage];
popoverController = [[UIPopoverController alloc] initWithContentViewController:imagePicker];
[popoverController presentPopoverFromRect:CGRectMake(0.0, 0.0, 400.0, 300.0)
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
//Done button on top
- (void)navigationController:(UINavigationController *)navigationController
willShowViewController:(UIViewController *)viewController
animated:(BOOL)animated
{
//NSLog(#"Inside navigationController ...");
if (!doneButton)
{
doneButton = [[UIBarButtonItem alloc] initWithTitle:#"Done"
style:UIBarButtonItemStyleDone
target:self action:#selector(saveImagesDone:)];
}
viewController.navigationItem.rightBarButtonItem = doneButton;
}
- (IBAction)saveImagesDone:(id)sender
{
//NSLog(#"saveImagesDone ...");
[popoverController dismissPopoverAnimated:YES];
}
-(void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingImage : (UIImage *)image
editingInfo:(NSDictionary *)editingInfo
{
//DONT DISMISS
//[picker dismissModalViewControllerAnimated:YES];
//[popoverController dismissPopoverAnimated:YES];
IMAGE_COUNTER = IMAGE_COUNTER + 1;
imageView.image = image;
// Get the data for the image
NSData* imageData = UIImageJPEGRepresentation(image, 1.0);
// Give a name to the file
NSString* incrementedImgStr = [NSString stringWithFormat: #"UserCustomPotraitPic%d.jpg", IMAGE_COUNTER];
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
// Now we get the full path to the file
NSString* fullPathToFile2 = [documentsDirectory stringByAppendingPathComponent:incrementedImgStr];
// and then we write it out
[imageData writeToFile:fullPathToFile2 atomically:NO];
}
//Now use this code to get to user selected pictures. Call it from wherever you want in your code
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask ,YES);
NSString* documentsPath = [paths objectAtIndex:0];
NSString* dataFile = [documentsPath stringByAppendingPathComponent:#"UserCustomPotraitPic1.jpg"];
NSData *potraitImgData = [NSData dataWithContentsOfFile:dataFile];
backgroundImagePotrait = [UIImage imageWithData:potraitImgData];
Apple has provided api for this. It is called ALAssetsLibrary.
Using this you can select multiple images/ videos and other operations that you do using photo application on iOS device.
As in documentation Apple says:
Assets Library Framework
Introduced in iOS 4.0, the Assets Library framework
(AssetsLibrary.framework) provides a query-based interface for
retrieving photos and videos from the user’s device. Using this
framework, you can access the same assets that are normally managed by
the Photos application, including items in the user’s saved photos
album and any photos and videos that were imported onto the device.
You can also save new photos and videos back to the user’s saved
photos album.
Here are few links where you can learn more. Now to use it you can search for ALAssetsLibrary.
Assets Library Reference
http://www.fiveminutes.eu/accessing-photo-library-using-assets-library-framework-on-iphone/
Starting with iOS 14, multiple image selection is now supported in the new Photos picker. Here is sample code from Apple: Selecting Photos and Videos in iOS.
I use ALAssetsLibrary and rolled my own UI. The problem with UIImagePickerController is that it says you are supposed to dismiss the view controller in the didFinishPickingMediaWithInfo callback, so hacking multiple selection by not dismissing may run into problems. I know I did when I first tried it. I can't recall exactly what went wrong, but there were cases where UIImagePickerController simply stopped working if I didn't dismiss it like the docs say.
I want to make an app where users can create funny stick figure animations.
It would be cool if it is possible to export them as video. Can I "draw" video frames frame by frame and render them into a H.264 or other video format?
The length will be between 2 seconds and 5 minutes. I heared a while back that there is a framework to edit video but in my case I really need to create a video from scratch. What are my options?
You might need to use a multimedia framework which provides more lower level control, like gstreamer or ffmeg.
Alternately, you can create an MJPEG and figure out a way to transcode it.
Yes, you can examine :
CEMovieMaker
Usage:
UIImage *frameImg = <Some Image>;
NSDictionary *settings = [CEMovieMaker videoSettingsWithCodec:AVVideoCodecTypeH264
withWidth:source.size.width
andHeight:source.size.height
];
///
CEMovieMaker * movieMaker = [[CEMovieMaker alloc] initWithSettings:settings];
/// Complete video
[movieMaker createMovieFromImages:[self.movieImages copy] withCompletion:^(NSURL *fileURL){
//AVPlayerViewController or
MPMoviePlayerViewController *playerController = [[MPMoviePlayerViewController alloc] initWithContentURL:fileURL];
[playerController.view setFrame:self.view.bounds];
[self presentMoviePlayerViewControllerAnimated:playerController];
[playerController.moviePlayer prepareToPlay];
[playerController.moviePlayer play];
[self.view addSubview:playerController.view];
}];
I have an app that is nearly finished but encountered an annoying problem. In the app, I want to play a sound when I tap on some object, then the object disappears. This is the piece of code I used:
In Object.h
#import <UIKit/UIKit.h>
#import <AudioToolbox/AudioServices.h>
#interface Object: UIView {
UIImageView *image;
CGPoint location;
SystemSoundID soundFileID;
}
In Object.m
- (void)initWithSound {
//a bunch of code to define the image and location
NSString *soundPath = [[NSBundle mainBundle] pathForResource:#"Sound" ofType:#"caf"];
CFURLRef soundURL = (CFURLRef)[NSURL fileURLWithPath:soundPath];
AudioServicesCreateSystemSoundID(soundURL, &soundFileID);
return self;
}
Then in my mainViewController, several objects will be created upon user's action and added to the screen at different locations with different images. Upon tapping on the object itself, it will create a sound and disappear.
In Object.m
- (void)tapped {
AudioServicesPlaySystemSound(soundFileID);
image.image = nil;
[self removeFromSuperview];
}
Everything works find in the simulator. But when I tried that in iPhone, the object disappears on tapping as expected but the sound just doesn't play. Tried on a 3G and a 3GS, both don't play the sound. I think this should work. Am I making any mistakes? Thanks for any help.
EDIT: Just think of something that I don't know related or not. I'm also using the microphone to detect sound input from the user by AVAudioRecorder. Not sure if this would affect the audio output.
Finally found where the problem is!!
It turns out that I have to I have to set up an audio session. It's just that! Obviously I need more study on the audio and video things.
Just for someone who may run into the same situation as me, here is the code I added in the viewDidLoad method:
AVAudioSession * audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error: &error];
[audioSession setActive:YES error: &error];
I am writing a simple video uploader application on iPhone 3GS where I first direct the user to photos album, and then select the video to share or upload. I am using the UIImagePickerController in the following way:
videoPickerCtrl = [[UIImagePickerController alloc] init];
videoPickerCtrl.delegate = self;
videoPickerCtrl.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
videoPickerCtrl.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:videoPickerCtrl.sourceType];
videoPickerCtrl.allowsImageEditing = NO;
videoPickerCtrl.mediaTypes = [NSArray arrayWithObject:(NSString *)kUTTypeMovie];
[window addSubview:videoPickerCtrl.view];
But I can see that once the controller is invoked, there is a disturbing video trimming interface that is presented. Once I press "choose", the video is always trimmed no matter whether I touch the trimming controls or not. Is there any way to get around this trimming interface and directly get the path of the video file ?
You should set allowsEditing = NO; instead of allowsImageEditing = NO; (which has been deprecated in 3.1). Then, the trimming interface should not appear unless the selected movie is longer than 10 minutes (from the docs: "Maximum movie duration is 10 minutes. If a user picks a movie that is longer in duration than 10 minutes, they are forced to trim it before saving it.").
Interesting problem. This is just for information should anyone else be looking at this. On iPad OS 3.2 I have found some problems retrieving video, although the picker works and I can select video from albums and not just from the camera roll.
Here's my working code frag
The call
NSArray *mediaTypesAllowed = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
[picker setMediaTypes:mediaTypesAllowed];
picker.delegate = self;
picker.allowsEditing = NO;
picker.wantsFullScreenLayout = YES;
if(!IsEmpty(self.editBackgroundPopover)){
[self.editBackgroundPopover setContentViewController:picker animated:YES];
}
And here is the delegate method
imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
[self.editBackgroundPopover dismissPopoverAnimated:true];
NSString* mediaType = [info objectForKey:UIImagePickerControllerMediaType];
//not production code, do not use hard coded string in real app
if ( [ mediaType isEqualToString:#"public.image" ]) {
NSLog(#"Picked a photo");
}
//not production code, do not use hard coded string in real app
else if ( [ mediaType isEqualToString:#"public.movie" ]){
NSLog(#"Picked a movie at URL %#", [info objectForKey:UIImagePickerControllerMediaURL]);
NSURL *url = [info objectForKey:UIImagePickerControllerMediaURL];
NSLog(#"> %#", [url absoluteString]);
}
[[picker self] dismissModalViewControllerAnimated:YES];
}
However the video URL which I retrieve from the picker has the form
file:/localhost/private/var/mobile/Applications/C6FAC491-D27D-45A6-B805-951727ED2CEC/tmp/-Tmp-/trim.KOzqps.MOV
So it looks to me that the Video might be being processed through the trimming code even if I'm selecting the video as a whole. Note also that the movie, originally of type m4v when I loaded it through iTunes is of type MOV, which is of course unplayable on the device! I did try playing the URL but I received an alert saying "This kind of movie can't be played"
I don't quite understand what Apple is playing at here, the API appears not to really be usable as a way of loading and playing video from the photo library.
Hopefully IOS 4 will be more forthcoming, but for my iPad app, that's still months away.
Ok so I got it working long back after carefully looking at SDK docs. I am able to get videos from Camera Roll directory on my 3GS. But I can not find any way in which UIImagePickerController can choose video from directories other than Camera Roll(for instance, device's Photo Library where the user syncs videos through iTunes). Is there any standard way in SDK to do that ?