2
  1. I am trying to get an array of images from video. It is working well but I have a doubt that In which thread the completion handler get called.

  2. I called this method(generateCGImagesAsynchronouslyForTimes:) in new operation and I updated the UI in the completion handler. The UI get updated.

  3. But typically the UI updation doesn't happen in secondary thread?. My doubt is the completion handler called in current calling thread or main thread?

My Code is:

__block unsigned int i = 0;
AVAssetImageGeneratorCompletionHandler handler = ^(CMTime requestedTime, CGImageRef im, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error){

    i++;
    if(result == AVAssetImageGeneratorSucceeded){

        //Create a block to save the image in disk
        NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
            NSFileManager *fileMgr = [NSFileManager defaultManager];
            NSString *documentsDirectory = [NSHomeDirectory()
                                            stringByAppendingPathComponent:@"Documents"];
            NSError *error = nil;

            //Create file path for storing the image
            NSString *videoOutputPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"VideoFrames%i.png", i]];

            //Delete if already any image exist 
            if ([fileMgr fileExistsAtPath:videoOutputPath]){
                if ([fileMgr removeItemAtPath:videoOutputPath error:&error] != YES)
                    NSLog(@"Unable to delete file: %@", [error localizedDescription]);
            }

            //Convert the CGImageRef to UIImage
            UIImage *image = [UIImage imageWithCGImage:im];//**This line gives error: EXE_BAD_ACCESS**

            //Save the image
            if(![UIImagePNGRepresentation(image) writeToFile:videoOutputPath options:NSDataWritingFileProtectionNone error:&error])
                NSLog(@"Failed to save image at path %@", videoOutputPath);
        }];

        //Add the operation to the queue
        [self.imageWritingQueue addOperation:operation];
    }
   }
};
jailani
  • 2,260
  • 2
  • 21
  • 45

2 Answers2

4

The documentation states it clearly:

Concurrent Programming with AV Foundation

Callouts from AV Foundation—invocations of blocks, key-value observers, and notification handlers—are not guaranteed to be made on any particular thread or queue. Instead, AV Foundation invokes these handlers on threads or queues on which it performs its internal tasks. You are responsible for testing whether the thread or queue on which a handler is invoked is appropriate for the tasks you want to perform. If it’s not (for example, if you want to update the user interface and the callout is not on the main thread), you must redirect the execution of your tasks to a safe thread or queue that you recognize, or that you create for the purpose.

See also: AV Foundation Programming Guide

Edit:

The problem is, that you don't retain/release the CGImageRef image provided in parameter im since you use it in a NSBlockOperation later. You need to retain it outside the block (from the NSBlockOperation) before you invoke the block, and release it before the block returns (within the block).

Community
  • 1
  • 1
CouchDeveloper
  • 18,174
  • 3
  • 45
  • 67
  • I tried that but when I try to access the imageRef(CGImageRef) returned from AV call out. UIImage *image = [UIImage imageWithCGImage:im]; from separate block operation It gives an error like EXC_BAD_ACCESS. Is any retain cycle needed to hold the image ref but I am using ARC. – jailani Feb 01 '14 at 10:00
  • @jai I've edited my answer as well. You missed to retain/release the `CGImageRef` object. – CouchDeveloper Feb 01 '14 at 10:13
  • Sorry @CouchDeveloper I am using ARC. Creating image out side the block works well...See Wain's command. and thanks for spending your precious time. – jailani Feb 01 '14 at 10:17
  • 1
    @jai A CGImageRef will not be managed by ARC! Creating the UIImage outside the block is an alternative approach. I don't see an advantage, though. – CouchDeveloper Feb 01 '14 at 10:18
  • 1
    @jai CGImageRef isn't an Objective-C object. It's a "Core Foundation object", that is basically a pointer to a C struct ;) Please consult the docs how to retain and release Core Foundation objects (`CGImageRetain()`, `CGImageRelease()`), https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/CGImage/Reference/reference.html – CouchDeveloper Feb 01 '14 at 10:27
  • @CouchDeveloper could you please look at my question: https://stackoverflow.com/questions/44910012/why-avassetimagegenerator-generatecgimagesasynchronouslyfortimes-crashes-the-app My app crashes without any error message while extracting image at 33rd second. I am extracting at 2fps. Not sure what is crashing the app. If it is less than 33 second all works fine. – nr5 Jul 06 '17 at 15:11
2

IIRC, yes. But you can test it, and to be safe you can wrap your code in a block which dispatches it to the main thread.

Generally speaking, the callback has to return to the main thread as the thread it's started from can't be guaranteed to be running a run loop if it isn't the main thread.

Unless you're scheduling the block you're creating in relation to other operations (dependencies) then I'm not sure what advantage the block gives you as the image loading is asynchronous so you can trigger it from the main thread without blocking anything.


From your comment, in that case you should switch your code around. Create the block operation inside the completion block which provides you with the image. Add each block operation to your queue. The block operation just takes the image and saves it to disk.

Wain
  • 118,658
  • 15
  • 128
  • 151
  • Thank you for your response. But I want to store all images in disk so only I decided to create a block. The handler returns 850 images around. So writing images into disk is definitely time consuming process? So I tried to do in separate block. – jailani Feb 01 '14 at 09:15
  • Added another part to my answer. Hopefully my understanding of what you're trying to achieve is better now... – Wain Feb 01 '14 at 09:27
  • I have edited my question this what you are telling? – jailani Feb 01 '14 at 09:42
  • I tried that but when I try to access the imageRef(CGImageRef). UIImage *image = [UIImage imageWithCGImage:im]; from separate block operation It gives an error like EXC_BAD_ACCESS. Is any retain cycle needed to hold the image ref but I am using ARC. – jailani Feb 01 '14 at 09:58
  • Create the UIImage outside the block. – Wain Feb 01 '14 at 10:08