【问题标题】:Best way to access all movie frames in iOS在 iOS 中访问所有电影帧的最佳方式
【发布时间】:2014-07-31 18:13:27
【问题描述】:

我正在尝试编辑现有电影并在顶部添加效果,因此我需要能够扫描所有电影帧,将它们作为 UIImage 获取,应用效果,然后更新该帧或将其写入新电影。 我发现人们建议使用 AVAssetImageGenerator。以下是我如何做的最终编辑示例:

-(void)processMovie:(NSString*)moviePath {
    NSURL* url = [NSURL fileURLWithPath:moviePath];
    AVURLAsset *asset=[[AVURLAsset alloc] initWithURL:url options:nil];
    float movieTimeInSeconds = CMTimeGetSeconds([movie duration]);
    AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
    generator.requestedTimeToleranceBefore = generator.requestedTimeToleranceAfter = kCMTimeZero;
    generator.appliesPreferredTrackTransform=TRUE;
    [asset release];

    // building array of time with steps as 1/10th of second
    NSMutableArray* arr = [[[NSMutableArray alloc] init] retain];
    for(int i=0; i<movieTimeInSeconds*10; i++) {
        [arr addObject:[NSValue valueWithCMTime:CMTimeMake(i,10)]];
    }

    AVAssetImageGeneratorCompletionHandler handler = ^(CMTime requestedTime, CGImageRef im, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error){
        if (result == AVAssetImageGeneratorSucceeded) {
            UIImage* img = [UIImage imageWithCGImage:im];
            // use img to apply effect and write it in new movie
            // after last frame do [generator release];
        }
    };

    [generator generateCGImagesAsynchronouslyForTimes:arr completionHandler:handler];
}

这种方法的 2 个问题:

  1. 我需要猜测电影的时间步长,或者在我的示例中假设电影的时间步长为 10FPS。实际时间步长不是均匀分布的,而且有时我们会跳帧。
  2. 扫描帧很慢。如果以高分辨率录制电影,我大约需要 0.5 秒来检索每一帧的 UIImage。

这似乎不自然。问题:有没有更好的方法来扫描电影的所有原始帧?

【问题讨论】:

  • 猜时间步是什么意思?
  • @Daniel 表示电影帧以 20FPS 还是 10FPS 录制。或者有些帧可能不会像 100 毫秒、220 毫秒、300 毫秒那样均匀分布...换句话说,我可以击中同一帧两次(如果 2 个电影帧的间隔比我的时间步长得多)或跳过一些帧(如果我的时间步长比两部电影之间的距离大得多)。
  • 您可以从 AVFoundation API 中找到电影的 FPS

标签: ios avfoundation frames movie


【解决方案1】:

终于找到了我要找的东西。下面是我扫描电影中所有样本的代码。顺便说一句,使用 AVAssetImageGenerator 正在工作,但速度很慢。这种方法很快。

inputUrl = [NSURL fileURLWithPath:filePath];
AVURLAsset* movie = [AVURLAsset URLAssetWithURL:inputUrl options:nil];

NSArray* tracks = [movie tracksWithMediaType:AVMediaTypeVideo];
AVAssetTrack* track = [tracks firstObject];

NSError* error = nil;
AVAssetReader* areader = [[AVAssetReader alloc] initWithAsset:movie error:&error];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                         [NSNumber numberWithInt:kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey,
                         nil];

AVAssetReaderTrackOutput* rout = [[AVAssetReaderTrackOutput alloc] initWithTrack:track outputSettings:options];
[areader addOutput:rout];

[areader startReading];
while ([areader status] == AVAssetReaderStatusReading) {
    CMSampleBufferRef sbuff = [rout copyNextSampleBuffer];
    if (sbuff) {
        dispatch_sync(dispatch_get_main_queue(), ^{
            [self writeFrame:sbuff];
        });
    }
}

【讨论】:

  • 到目前为止,我使用这种方法需要 1200-1300 毫秒,而 generateCGImagesAsynchronouslyForTimes 需要大约 122 毫秒。
【解决方案2】:

您可以获得视频的帧速率。看看这里的代码:Given a URL to a movie, how can retrieve it's info?

您只需要真正获取 AVAssetTrack 及其nominalFrameRate。

【讨论】:

  • 谢谢。它有助于。知道如何跟踪跳过的帧吗?
【解决方案3】:

虽然这可能无法帮助您解决跳帧问题,但您可以在 AVAssetTrack 上进行校准以及nominalFrameRate 以获得所需的结果。

【讨论】:

  • 哦,显然有人已经回答了这个问题。抱歉,刚刚加入 Stack,我的错。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-20
  • 2020-06-11
  • 2021-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多