【问题标题】:is AVAssetImageGenerator completion handler called in main thread?是在主线程中调用 AVAssetImageGenerator 完成处理程序吗?
【发布时间】:2014-02-25 02:10:27
【问题描述】:
  1. 我正在尝试从视频中获取一组图像。它运行良好,但我怀疑完成处理程序在哪个线程中被调用。

  2. 我在新操作中调用了这个方法(generateCGImagesAsynchronouslyForTimes:),并在完成处理程序中更新了 UI。用户界面得到更新。

  3. 但 UI 更新通常不会发生在辅助线程中?我怀疑是在当前调用线程还是主线程中调用了完成处理程序?

我的代码是:

__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];
    }
   }
};

【问题讨论】:

    标签: ios multithreading nsoperation avassetimagegenerator


    【解决方案1】:

    文档清楚地说明了这一点:

    使用 AV Foundation 进行并发编程

    来自 AV Foundation 的调用(调用块、键值观察器和通知处理程序)不能保证在任何特定线程或队列上进行。相反,AV Foundation 在执行其内部任务的线程或队列上调用这些处理程序。您负责测试调用处理程序的线程或队列是否适合您要执行的任务。如果不是(例如,如果您想更新用户界面并且标注不在主线程上),您必须将任务的执行重定向到您识别的安全线程或队列,或者您为目的。

    另请参阅:AV Foundation Programming Guide

    编辑:

    问题是,您没有保留/释放参数 im 中提供的CGImageRef 图像,因为您稍后会在 NSBlockOperation 中使用它。您需要在调用块之前将其保留在块之外(来自 NSBlockOperation),并在块返回之前释放它(在块内)。

    【讨论】:

    • 我试过了,但是当我尝试访问从 AV 调用返回的 imageRef(CGImageRef) 时。 UIImage *image = [UIImage imageWithCGImage:im];从单独的块操作它给出一个错误,如 EXC_BAD_ACCESS。是否需要任何保留周期来保存图像参考,但我正在使用 ARC。
    • @jai 我也编辑了我的答案。您错过了保留/释放 CGImageRef 对象。
    • 对不起@CouchDeveloper 我正在使用 ARC。在块外创建图像效果很好...参见 Wain 的命令。感谢您花费宝贵的时间。
    • @jai CGImageRef 不会由 ARC 管理!在块外创建 UIImage 是另一种方法。不过,我看不出有什么优势。
    • @jai CGImageRef 不是 Objective-C 对象。这是一个“核心基础对象”,基本上是一个指向 C 结构的指针;)请查阅文档如何保留和释放核心基础对象(CGImageRetain()CGImageRelease()),developer.apple.com/library/mac/documentation/graphicsimaging/…
    【解决方案2】:

    IIRC,是的。但是您可以对其进行测试,并且为了安全起见,您可以将代码包装在一个将其分派到主线程的块中。

    一般来说,回调必须返回到主线程,因为如果它启动的线程不是主线程,则不能保证它正在运行运行循环。

    除非您将创建的块与其他操作(依赖项)相关联,否则我不确定该块会给您带来什么优势,因为图像加载是异步的,因此您可以从主线程触发它而无需阻止任何东西。


    根据您的评论,在这种情况下,您应该切换代码。在为您提供图像的完成块内创建块操作。将每个块操作添加到您的队列中。块操作只是获取图像并将其保存到磁盘。

    【讨论】:

    • 感谢您的回复。但我想将所有图像存储在磁盘中,所以只有我决定创建一个块。处理程序返回 850 张图像。那么将图像写入磁盘肯定是一个耗时的过程?所以我尝试在单独的块中做。
    • 在我的答案中添加了另一部分。希望我现在对您想要实现的目标的理解更好......
    • 我已经编辑了我的问题,这就是你所说的?
    • 我试过了,但是当我尝试访问 imageRef(CGImageRef) 时。 UIImage *image = [UIImage imageWithCGImage:im];从单独的块操作它给出一个错误,如 EXC_BAD_ACCESS。是否需要任何保留周期来保存图像参考,但我正在使用 ARC。
    • 在块外创建 UIImage。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-18
    • 1970-01-01
    • 2021-06-04
    • 2014-08-16
    相关资源
    最近更新 更多