Not able to Extract each and every frame from Video - objective-c

I have a 7 second MOV video at 30fps, but when I try to loop the code for multiple values it doesn't work. It always give me 7-8 unique frames even if I am able to write all frames.
Requested times are always different but I always get actual time frames.
MPMoviePlayerController *moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"video" ofType:#"MOV"]]];
moviePlayer.shouldAutoplay = NO;
NSMutableArray *timesm=[[NSMutableArray alloc]init];
for (int i=0; i<frames; i++) {
CMTime firstThird = CMTimeMakeWithSeconds(videoDurationSeconds *i/(float)frames, 600);
[timesm addObject:[NSValue valueWithCMTime:firstThird]];
double abc=firstThird.value/600;
UIImage *thumbnail = [moviePlayer thumbnailImageAtTime:abc timeOption:MPMovieTimeOptionExact];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *savedImagePath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"zshipra %i.jpg",i]];
NSData *imageData = UIImageJPEGRepresentation(thumbnail,0.7);
[imageData writeToFile:savedImagePath atomically:NO];
}
I also tried another way from the Apple developer site, but both are giving 7 frames from 7 seconds video.
AVAssetImageGenerator *generate1 = [[AVAssetImageGenerator alloc] initWithAsset:asset1];
generate1.appliesPreferredTrackTransform = YES;
NSError *err = NULL;
CMTime videoDuration = asset1.duration;
float videoDurationSeconds = CMTimeGetSeconds(videoDuration);
int frames=(int)videoDurationSeconds*30;
NSMutableArray *timesm=[[NSMutableArray alloc]init];
for (int i=0; i<frames; i++) {
CMTime firstThird = CMTimeMakeWithSeconds(videoDurationSeconds *i/(float)frames, 600);
[timesm addObject:[NSValue valueWithCMTime:firstThird]];
}
[generate1 generateCGImagesAsynchronouslyForTimes:timesm
completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error) {
UIImage* myImage = [[UIImage alloc] initWithCGImage:image];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *savedImagePath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"ankur-%# %i.jpg",image,(int)[[NSDate date] timeIntervalSince1970]]];
NSData *imageData = UIImageJPEGRepresentation(myImage,0.7);
[imageData writeToFile:savedImagePath atomically:NO];
NSString *requestedTimeString = (NSString *)
CFBridgingRelease(CMTimeCopyDescription(NULL, requestedTime));
NSString *actualTimeString = (NSString *)
CFBridgingRelease(CMTimeCopyDescription(NULL, actualTime));
NSLog(#"Requested: %#; actual %#", requestedTimeString, actualTimeString);
if (result == AVAssetImageGeneratorSucceeded) {
// Do something interesting with the image.
}
if (result == AVAssetImageGeneratorFailed) {
NSLog(#"Failed with error: %#", [error localizedDescription]);
}
if (result == AVAssetImageGeneratorCancelled) {
NSLog(#"Canceled");
}
}];

I've tested with your code. And with two changes, it seems all the frame images are successfully generated.
1) Specify the tolerance options
generate1.requestedTimeToleranceAfter = CMTimeMakeWithSeconds(1/30.0, videoDuration.timescale);
generate1.requestedTimeToleranceBefore = CMTimeMakeWithSeconds(1/30.0, videoDuration.timescale);
2) Use native CMTime unit, not second unit
NSMutableArray *timesm=[[NSMutableArray alloc]init];
for (int i=0; i<frames; i++) {
CMTime firstThird = CMTimeMake( i * (videoDuration.timescale / 30.0f), videoDuration.timescale);
[timesm addObject:[NSValue valueWithCMTime:firstThird]];
}

Related

How to save and load multiple photos into my local app in Objective C?

-(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
image=[info objectForKey:UIImagePickerControllerOriginalImage];
[self.images addObject:image];
[self.maintable reloadData];
[self dismissViewControllerAnimated:YES completion:NULL];
}
-(IBAction)savebutton:(id)sender{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory ,
NSUserDomainMask , YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* path = [documentsDirectory stringByAppendingPathComponent:
[NSString stringWithFormat: #"MyImages"]];
for (int i=0; i<_images.count; i++) {
image=[_images objectAtIndex:i];
NSData* data = UIImagePNGRepresentation(image);
[data writeToFile:path atomically:YES];
NSLog(#"saved");
}
}
- (UIImage*)loadImage
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);`
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* path = [documentsDirectory stringByAppendingPathComponent:
[NSString stringWithFormat: #"MyImage.png"] ];
image = [UIImage imageWithContentsOfFile:path];
if(image != nil){
[self.images addObject:image];
}
return image;
}
You are on correct track,
On save button action, save images document directory's particular path and save image name in one array which you need to save in core data or preference whatever you preferred in your app
like this..
NSString *strImageName = #"image.png"
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *filePath = [documentsPath stringByAppendingPathComponent: strImageName];
// Above you can give your custom path like #"App_Name/imageName" for unique identification
[pngData writeToFile:filePath atomically:YES];
[arrCoreData addObject: strImageName];
Once you have done(saved all your images then save image name in preference)
like this
NSData *data = [NSKeyedArchiver archivedDataWithRootObject: arrCoreData];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"MyImageArray"];
When you want to fetch images and display it again,
You may follow this flow:
NSData *mainCatData = [[NSUserDefaults standardUserDefaults] objectForKey:#"MyImageArray"];
arrCoreData = [NSKeyedUnarchiver unarchiveObjectWithData:mainCatData];
Then using these images name you may fetch images back and display as you want..
To load image from document directory, You may use this function
- (UIImage*)loadImage:(NSString)strImageName{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* path = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithString: #"AppName/%#", strImageName] ];
UIImage* image = [UIImage imageWithContentsOfFile:path];
return image;
}
Hope it will help you :)
I did it and worked on my app:)
- (void)loadImage
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSArray *fileArray = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsDirectory error:nil];
for (int i=0; i<fileArray.count; i++) {
NSString* path = [documentsDirectory stringByAppendingPathComponent:fileArray[i]];
image = [UIImage imageWithContentsOfFile:path];
[self.images addObject:image];
}
}
-(IBAction)savebutton:(id)sender{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask , YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
for (int i=0; i<_images.count; i++) {
NSString* path = [documentsDirectory stringByAppendingPathComponent:
[NSString stringWithFormat: #"MyImages%d", i]];
image=[_images objectAtIndex:i];
NSData* data = UIImagePNGRepresentation(image);
[data writeToFile:path atomically:YES];
NSLog(#"saved");
}
}

UIImage gives nil

I'm quite new to iOS development. My app gets a file over a network, writes it as image.png and later on reads and displays the image. However, the display part is not working as my UIImage object is always set to nil (on the iOS simulator). I've tried implementing other answers from stackoverflow, but no luck.
Here's my code to save the file:
//inside utility class for model
NSFileHandle * handle = nil;
//For first packet of new file request
if(CountFileParts == 1)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:#"image.png"];
NSLog(#"%#",appFile);
handle = [NSFileHandle fileHandleForWritingAtPath:appFile];
if(handle == nil)
{
[[NSFileManager defaultManager] createFileAtPath:appFile contents:nil attributes:nil];
handle = [NSFileHandle fileHandleForWritingAtPath:appFile];
}
}
//For other incoming packets of the same request
else
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:#"image.png"];
//NSLog(#"%#",appFile);
handle = [NSFileHandle fileHandleForUpdatingAtPath:appFile];
[handle seekToEndOfFile];
//NSLog(#"Writing continue in new file");
}
if(handle == nil)
NSLog(#"handle nil");
NSData * data = [str dataUsingEncoding:NSUTF8StringEncoding];
[handle writeData:data];
[handle closeFile];
if(index != -1 && index!= NSNotFound)
{
NSLog(#"Inside Bool");
self.isPlotReady = YES;//kvo in view-controller as shown below
self.isPlotReady = NO;
}
Here's my code to load the image file:
-(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if([keyPath isEqualToString:#"isPlotReady"])
{
self.isReady = [[change objectForKey:NSKeyValueChangeNewKey] boolValue];
[self updateUI];
}
}
-(void) updateUI
{
if(self.isReady)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
[self lsOwnDirectory:documentsDirectory];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:#"image.png"];
//NSLog(#"%#",appFile);
UIImage *img = [UIImage imageWithContentsOfFile:appFile];
if(img == nil)
NSLog(#"Couldn't find image");
else
{
UIImageView *imageView = [[UIImageView alloc] initWithImage:img] ;
[self.view addSubview:imageView];
}
}
}
//Prints Directory contents of input directory
- (void) lsOwnDirectory:(NSString *) currentpath {
NSError * error = [[NSError alloc] init];
NSFileManager *filemgr;
filemgr = [[NSFileManager alloc] init];
//currentpath = [filemgr currentDirectoryPath];
NSLog(#"Current Directory Path : %#",currentpath);
NSArray * files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:currentpath error: &error];
for(NSString * file in files){
NSLog(#"%#", file);
}
}
It alway's prints "Couldn't find image" corresponding to the if statement, but I've seen the file is still there (lsOwnDirectory prints directory contents). Maybe I'm doing something basic wrong here. Thanks in advance.

How to insert page break in creation of PDF File using saved .png files?

I am creating pdf File using the screen shots taking programatically from webView.
But main problem is that below code does not insert page break its only creating whole page.
Please tell me how to insert pageBreak or create multiple page in pdf.
- (void) drawPdf
{
CGSize pageSize = CGSizeMake(612, webViewHeight);
NSString *fileName = #"Demo.pdf";
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pdfFileName = [documentsDirectory stringByAppendingPathComponent:fileName];
UIGraphicsBeginPDFContextToFile(pdfFileName, CGRectZero, nil);
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, pageSize.width, pageSize.height), nil);
// Mark the beginning of a new page.
//UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, pageSize.width, pageSize.height), nil);
double currentHeight = 0.0;
for (int index = 1; index <= imageName ; index++)
{
NSString *pngPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%d.png", index]];
UIImage *pngImage = [UIImage imageWithContentsOfFile:pngPath];
[pngImage drawInRect:CGRectMake(0, currentHeight, pageSize.width, pngImage.size.height)];
currentHeight += pngImage.size.height;
}
UIGraphicsEndPDFContext();
}
- (void) drawPdf
{
CGSize pageSize = CGSizeMake(612, webViewHeight);
NSString *fileName = #"Demo.pdf";
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pdfFileName = [documentsDirectory stringByAppendingPathComponent:fileName];
UIGraphicsBeginPDFContextToFile(pdfFileName, CGRectZero, nil);
// Mark the beginning of a new page.
//UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, pageSize.width, pageSize.height), nil);
double currentHeight = 0.0;
for (int index = 1; index <= imageName ; index++)
{
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, pageSize.width, pageSize.height), nil);
//Above line would work to make page break
NSString *pngPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%d.png", index]];
UIImage *pngImage = [UIImage imageWithContentsOfFile:pngPath];
[pngImage drawInRect:CGRectMake(0, 0, pageSize.width, pngImage.size.height)];
currentHeight += pngImage.size.height;
}
UIGraphicsEndPDFContext();
}

UIImageJPEGRepresentation throws EXC BAD ACCESS

I have function, which received 2 arguments - image and number. Function below throws EXC BAD ACCESS on line "imgData = UIImageJPEGRepresentation(image, 1.0);". I don't know why :-( Any ideas?
- (void)saveImages:(UIImage*)image row:(int)row {
if (image != nil) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* path1 = [documentsDirectory stringByAppendingPathComponent:
[NSString stringWithFormat:#"%i.JPEG", row]];
NSString* path2 = [documentsDirectory stringByAppendingPathComponent:
[NSString stringWithFormat:#"%is.JPEG", row]];
NSData* imgData;
imgData = UIImageJPEGRepresentation(image, 1.0);
[imgData writeToFile:path1 atomically:YES];
[self rotateIcon:image];
image = [self imageWithImage:image];
NSData* smalImgData = UIImageJPEGRepresentation(image, 1.0);
[smalImgData writeToFile:path2 atomically:YES];
}
}

Retrieve all images from NSDocumentDirectory and storing into an array

Currently i'm using these codes to save my images into NSDocumentDirectory. I use this counter as the naming convention for them.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)selectedImage editingInfo:(NSDictionary *)editingInfo
{
[self.popoverController dismissPopoverAnimated:YES];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *savedImagePath = [documentsDir stringByAppendingPathComponent:[NSString stringWithFormat:#"%d.png", counter]];
UIImage *image = imageView.image;
NSData *imageData = UIImagePNGRepresentation(image);
[imageData writeToFile:savedImagePath atomically:NO];
}
I use this method because it's easier for me to retrieve all of them by using a loop. I want to retrieve all the images from the NSDocumentDirectory so that i can display them in another view. The following codes show how i retrieve them.
-(NSMutableArray *)GetImage:(NSMutableArray *)arrayImgNames
{
NSMutableArray *tempArray;
for(int i=0;i<[arrayImgNames count]; i++)
{
NSArray *paths1 = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths1 objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent: [arrayImgNames objectAtIndex:i]];
[tempArray addObject:[[UIImage alloc] initWithContentsOfFile:filePath]];
return tempArray;
}
}
However, i do not wish to use the counter as a naming convention for my images. I want to use proper names for them but if i do so, i will have to change my method of retrieving all the images.
Is there any other way that i can retrieve all images other than this method i mentioned?
You can retrieve files using next approach:
NSURL *url = [[AppDelegate sharedAppDelegate] applicationDocumentsDirectory];
NSError *error = nil;
NSArray *properties = [NSArray arrayWithObjects: NSURLLocalizedNameKey, NSURLLocalizedTypeDescriptionKey, nil];
NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:url
includingPropertiesForKeys:properties options:(NSDirectoryEnumerationSkipsPackageDescendants)
error:&error];
In files paths to all files of documents directory will be stored. Next code will help you to get there names:
NSURL *url = [files objectAtIndex:index];
NSString *localizedName = [url lastPathComponent];