Trouble Sending Parameter to Main Thread From Async Block - iphone

I have the following method which retrieves an ALAsset from the UIImagePicker after it snaps a photo. It then attempts to send this ALAsset to another one of my methods via the main thread:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSURL *imageURL = [[info valueForKey:UIImagePickerControllerReferenceURL] retain];
__block ALAsset *result;
[self.assetsLibrary assetForURL:imageURL resultBlock:^(ALAsset *asset)
{
result = [asset retain];
dispatch_async(dispatch_get_main_queue(), ^
{
[self loadPhotoImageViewWithAsset:result];
[self dismissModalViewControllerAnimated:YES];
[imageURL release];
[result release];
});
}
failureBlock:^(NSError *error)
{
}];
}
When I get into the dispatch_async(dispatch_get_main_queue(), ^ block, result is showing as nil. Anyone know what I'm doing wrong here?

Refer UIImagePickerController to use for a UIImageView for result is showing as nil.
Use like this:
[self.assetsLibrary assetForURL:imageURL resultBlock:^(ALAsset *asset)
{
result = [asset retain];
[self loadPhotoImageViewWithAsset:result];
[self dismissModalViewControllerAnimated:YES];
[imageURL release];
[result release];
}
failureBlock:^(NSError *error)
{
}];
Change your loadPhotoImageViewWithAsset Method
-(void)loadPhotoImageViewWithAsset:(ALAsset *)asset
{
//dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
//dispatch_async(queue, ^{
dispatch_async(dispatch_get_main_queue(), ^
{
//here code for loading image
});
// });
}

Related

Raw image data from camera

I've been searching this forum up and down but I couldn't find what I really need. I want to get raw image data from the camera. Up till now I tried to get the data out of the imageDataSampleBuffer from that method captureStillImageAsynchronouslyFromConnection:completionHandler: and to write it to an NSData object, but that didn't work.
Maybe I'm on the wrong track or maybe I'm just doing it wrong.
What I don't want is for the image to be compressed in any way.
The easy way is to use jpegStillImageNSDataRepresentation: from AVCaptureStillImageOutput, but like I said I don't want it to be compressed.
Thanks!
This is how i do it:
1: I first open the camera using:
- (void)openCamera
{
imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
{
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
imagePicker.mediaTypes = [NSArray arrayWithObjects:
(NSString *) kUTTypeImage,
(NSString *) kUTTypeMovie, nil];
imagePicker.allowsEditing = NO;
[self presentModalViewController:imagePicker animated:YES];
}
else {
lblError.text = NSLocalizedStringFromTable(#"noCameraFound", #"Errors", #"");
}
}
When the picture is taken this method gets called:
-(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
// Save and get the path of the image
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
if([mediaType isEqualToString:(NSString *)kUTTypeImage])
{
// Save the image
image = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
[library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error){
if(!error) {
//Save path and location to database
NSString *pathLocation = [[NSString alloc] initWithFormat:#"%#", assetURL];
} else {
NSLog(#"CameraViewController: Error on saving image : %# {imagePickerController}", error);
}
}];
}
[imagePicker dismissModalViewControllerAnimated:YES];
}
Then with that path i get the picture from the library in FULL resolution (using the "1"):
-(void)preparePicture: (NSString *) filePathPicture{
ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset *myasset)
{
if(myasset != nil){
ALAssetRepresentation *assetRep = [myasset defaultRepresentation];
CGImageRef imageRef = [assetRep fullResolutionImage];
if (imageRef) {
NSData *imageData = UIImageJPEGRepresentation([UIImage imageWithCGImage:imageRef], 1);
}
}else {
//error
}
};
ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError *error)
{
NSString *errorString = [NSString stringWithFormat:#"can't get image, %#",[error localizedDescription]];
NSLog(#"%#", errorString);
};
if(filePathPicture && [filePathPicture length])
{
NSURL *assetUrl = [NSURL URLWithString:filePathPicture];
ALAssetsLibrary *assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:assetUrl
resultBlock:resultBlock
failureBlock:failureBlock];
}
}
Hope this helps you a bit further :-).

Wait for assetForURL blocks to be completed

I would like to wait this code to be executed before to continue but as these blocks are called assynchronously I don't know how to do???
NSURL *asseturl;
NSMutableArray *tmpListAsset = [[NSMutableArray alloc] init];
ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init] autorelease];
NSMutableArray *objectsToRemove = [[NSMutableArray alloc] init];
for (NSDictionary *dico in assetsList) {
asseturl = [NSURL URLWithString:[dico objectForKey:#"assetUrl"]];
NSLog(#"asset url %#", asseturl);
// Try to load asset at mediaURL
[library assetForURL:asseturl resultBlock:^(ALAsset *asset) {
// If asset doesn't exists
if (!asset){
[objectsToRemove addObject:dico];
}else{
[tmpListAsset addObject:[asseturl absoluteString]];
NSLog(#"tmpListAsset : %#", tmpListAsset);
}
} failureBlock:^(NSError *error) {
// Type your code here for failure (when user doesn't allow location in your app)
}];
}
GCD semaphore approach:
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
for (NSURL *url in self.assetUrls) {
dispatch_async(queue, ^{
[library assetForURL:url resultBlock:^(ALAsset *asset) {
[self.assets addObject:asset];
dispatch_semaphore_signal(sema);
} failureBlock:^(NSError *error) {
dispatch_semaphore_signal(sema);
}];
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
dispatch_release(sema);
/* Check out ALAssets */
NSLog(#"%#", self.assets);
The solution is here
http://omegadelta.net/2011/05/10/how-to-wait-for-ios-methods-with-completion-blocks-to-finish/
Note that assetForURL:resultBlock:failureBlock: will stuck if the main thread is waiting without RunLoop running. This is alternative ( cleaner :-) ) solution:
#import <libkern/OSAtomic.h>
...
ALAssetsLibrary *library;
NSMutableArray *assets;
...
__block int32_t counter = 0;
for (NSURL *url in urls) {
OSAtomicIncrement32(&counter);
[library assetForURL:url resultBlock:^(ALAsset *asset) {
if (asset)
[assets addObject:asset];
OSAtomicDecrement32(&counter);
} failureBlock:^(NSError *error) {
OSAtomicDecrement32(&counter);
}];
}
while (counter > 0) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}
The easiest thing to do is to move your code to inside (at the end of) the resultBlock or the failureBlock. That way, your code will run in the correct order, and you will also retain asynchronous behaviour.
This is an easy way to do it. Maybe not as elegant as using GCD but it should get the job done ... This will make your method blocking instead of non-blocking.
__block BOOL isFinished = NO;
NSURL *asseturl;
NSMutableArray *tmpListAsset = [[NSMutableArray alloc] init];
ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init];
NSMutableArray *objectsToRemove = [[NSMutableArray alloc] init];
for (NSDictionary *dico in assetsList) {
asseturl = [NSURL URLWithString:[dico objectForKey:#"assetUrl"]];
NSLog(#"asset url %#", asseturl);
// Try to load asset at mediaURL
[library assetForURL:asseturl resultBlock:^(ALAsset *asset) {
// If asset doesn't exists
if (!asset){
[objectsToRemove addObject:dico];
}else{
[tmpListAsset addObject:[asseturl absoluteString]];
NSLog(#"tmpListAsset : %#", tmpListAsset);
}
if (objectsToRemove.count + tmpListAsset.count == assetsList.count) {
isFinished = YES;
}
} failureBlock:^(NSError *error) {
// Type your code here for failure (when user doesn't allow location in your app)
isFinished = YES;
}];
}
while (!isFinished) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01f]];
}

AVCamDemo: completionHandler code not hit

I am streamlining the AVCamDemo project so that I can experiment with capturing only still images.
Below is the new code for captureStillImage() method:
- (void) captureStillImage
{
AVCaptureConnection *stillImageConnection = [AVCamUtilities connectionWithMediaType:AVMediaTypeVideo fromConnections:[[self stillImageOutput] connections]];
if ([stillImageConnection isVideoOrientationSupported]) {
[stillImageConnection setVideoOrientation:orientation];
}
[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:stillImageConnection
completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
NSLog(#"In the completionHandler block");
if (imageDataSampleBuffer != NULL) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [[UIImage alloc] initWithData:imageData];
[self writeImageFile:image];
[image release];
}
if ([[self delegate] respondsToSelector:#selector(captureManagerStillImageCaptured:)]) {
[[self delegate] captureManagerStillImageCaptured:self];
}
}];
NSLog(#"exiting....");
}
In my testing, I have found that sometimes, the NSLog statement in the block is not executed....and therefore the stillImage file does not get saved.
Am I running into timing issues? If so, how do I address them?
Thanks, and regards.
Sam.

iphone image ALAsset problem

First of all sorry for the crappy formatting. I dont know how to format the code here.I am having a problem with retrieving the image from the photo library. I have pasted my code Please help me. I am having this problem from many days.
here, I am storing the image path in the database using the referenceURL. It is in the
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
NSURL *imageUrl = [info valueForKey:UIImagePickerControllerReferenceURL];
NSURL *imageUrl = [info valueForKey:UIImagePickerControllerReferenceURL];
ALAssetsLibrary *assetLibrary = [[ALAssetsLibrary alloc]init];
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
NSLog(#"asset");
CGImageRef iref = [myasset thumbnail];
if(iref)
{
NSLog(#"ifref");
UIImage *thethumbnail = [UIImage imageWithCGImage:iref];
NSLog(#"the thumbnail %#",thethumbnail);
[[self photo]setImage:thethumbnail];
lpdel.imageurl = UIImageJPEGRepresentation (thethumbnail, 1);
}
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
NSLog(#"failed");
NSLog(#"cant get image -- %#",[myerror localizedDescription]);
};
if (imageUrl) {
NSLog(#"if url");
[assetLibrary assetForURL:imageUrl
resultBlock:resultblock
failureBlock:failureblock];
//lpdel.imageurl =imageUrl;
NSLog(#"the image string %#",lpdel.imageurl);
NSLog(#"lpdel.image%#",lpdel.imageurl);
[picker dismissModalViewControllerAnimated:YES];
[imageUrl release];
}
[picker dismissModalViewControllerAnimated:YES];
[picker release]
}
I am storing the imageURL in the database. Then in another View Controller I am trying to retrive the image using the same url using the below code
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
NSLog(#"asset");
CGImageRef iref = [myasset thumbnail];
if(iref)
{
NSLog(#"ifref");
thethumbnail = [UIImage imageWithCGImage:iref];
NSLog(#"the thumbnail to upload %#",thethumbnail);
[self uploadImage:UIImageJPEGRepresentation (thethumbnail, 1)];
//[[self photo]setImage:thethumbnail];
}
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
NSLog(#"failed");
NSLog(#"cant get image -- %#",[myerror localizedDescription]);
};
if (image)
{
NSLog(#"if url");
ALAssetsLibrary *assetLibrary = [[[ALAssetsLibrary alloc]init]autorelease];
NSLog(#"url");
[assetLibrary assetForURL:image
resultBlock:resultblock
failureBlock:failureblock];
}
NSLog(#"the thumbnail %#",thethumbnail);
The application crashes at this point without any error message. Can anyone please help me on this
I found the solution. It was the problem with the last nslog. But I need to know why. the variable thethumbnail is declared with class scope. Has it got to do anything with blocks

Saving a thumbnail on a background thread

I'm trying to create thumbnails (288x288) of selected photos from iPad photo library. I have an array of ALAsset objects presented in a UITableView and as I select a row, a larger preview (288x288) of that image is displayed. In order to prevent main thread blocking, I'm trying to create the thumbnail on a background thread and also cache a copy of the thumbnail to the file system.
In a view controller when a tableview row is selected, I call loadPreviewImage in background:
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// get the upload object from an array that contains a ALAsset object
upload = [uploads objectAtIndex:[indexPath row]];
[self performSelectorInBackground:#selector(loadPreviewImage:)
withObject:upload];
}
I pass a custom upload object that contains asseturl property:
- (void)loadPreviewImage:(MyUploadClass*)upload
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *preview = [upload previewImage];
[self performSelectorOnMainThread:#selector(setPreviewImage:)
withObject:preview
waitUntilDone:YES];
[pool release];
}
This is called on main thread to display the thumbnail after it's loaded:
- (void)setPreviewImage:(UIImage*)image
{
self.imageViewPreview.image = image;
[self layoutSubviews];
}
This is a method of MyUploadClass:
- (UIImage *)previewImage
{
__block UIImage *previewImage = [[UIImage imageWithContentsOfFile:
[self uploadPreviewFilePath]] retain];
if (previewImage == nil && asseturl)
{
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
[library assetForURL:self.asseturl resultBlock:^(ALAsset *asset)
{
ALAssetRepresentation *rep = [asset defaultRepresentation];
previewImage = [UIImage imageWithCGImage: [rep fullScreenImage]];
previewImage = [[previewImage resizedImageWithContentMode:UIViewContentModeScaleAspectFit
bounds:CGSizeMake(288, 288)
interpolationQuality:kCGInterpolationHigh] retain];
NSData *previewData = UIImageJPEGRepresentation(previewImage, 1.0);
[previewData writeToFile:[self uploadPreviewFilePath] atomically:YES];
}
failureBlock:^(NSError *error){ }];
[library release];
}
return [previewImage autorelease];
}
The problem is that I always get nil previewImage the first time and only after the thumbnail is cached I get an image object. What am I doing wrong? Is there a better approach to this problem?
I didn't clearly understand how the resultBlock of ALAssetsLibrary operates, my mistake was to think that the execution is linear. It turns out that in my case the resultBlock executes on the main thread while the rest of the code in previewImage executes on a background thread. I was getting nil because previewImage returned before resultBlock had a chance to end its execution. I solved the problem by replacing previewImage with the following method:
- (void) loadPreviewImage:(CGSize)size withTarget:(id)target andCallback:(SEL)callback
{
NSString *path = [self uploadPreviewFilePath];
UIImage *previewImage = [UIImage imageWithContentsOfFile:path];
if (previewImage == nil && asseturl)
{
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
[library assetForURL:self.asseturl resultBlock:^(ALAsset *asset)
{
if (asset) {
ALAssetRepresentation *rep = [asset defaultRepresentation];
UIImage *img = [UIImage imageWithCGImage: [rep fullScreenImage]];
img = [img resizedImageWithContentMode:UIViewContentModeScaleAspectFit
bounds:size interpolationQuality:kCGInterpolationHigh];
NSData *previewData = UIImageJPEGRepresentation(img, 1.0);
[previewData writeToFile:path atomically:YES];
[target performSelectorOnMainThread:callback
withObject:img
waitUntilDone:YES];
}
}
failureBlock:^(NSError *error){ }];
[library release];
}
else {
[target performSelectorOnMainThread:callback withObject:img waitUntilDone:YES];
}
}