1

您能否指出我的设计模式指南以使我的风格适应 AVFoundation 的异步方法?

工作一个应用程序,您可以在其中创建图像并将音频放置在其上的热点上。我正在实现导出到电影,该电影是在音频下播放效果(热点发光)的图像。

我可以可靠地创建视频和音频轨道,并且可以正确地将音频输入 AVMutableComposition 并进行播放。问题出在视频上。我已将其范围缩小到我为需要使用 AVFoundation 的异步编写方法的问题编写了同步解决方案。

当前的方法及其失败的地方(每个步骤都是自己的方法):

  1. 创建字典数组。字典中的 2 个对象。一个字典对象是表示关键帧的图像,另一个对象是结束于该关键帧的音频 URL。第一个字典有开始关键帧但没有音频 URL。

  2. 对于数组中的每个字典,将 UIImage 替换为起始图像->动画补间图像->结束状态图像的数组,并具有适当的 FPS 计数和音频持续时间。

  3. 对于数组中的每个字典,将图像数组转换为无声mp4并使用[AVAssetWriter finishWritingWithCompletionHandler]保存,然后将字典中的图像数组替换为mp4的URL。mp4 和音频 URL 的每个字典代表最终电影的一段,其中数组中字典的顺序指示最终电影的插入顺序

-- 以上所有作品,制作和订购正确的东西,视频和音频播放 --

  1. 对于每个带有 mp4 和音频 URL 的字典,加载到 AVAssets 并插入 AVMutableComposition 轨道,一个轨道用于音频,一个轨道用于视频。音频加载和插入工作,播放。但是视频失败并且似乎失败了,因为步骤 4 在步骤 3 的 AVAssetWriter finishWritingWithCompletionHandler 完成所有 MP4 轨道之前开始。

一种方法是通过 while 循环暂停并等待 AVAssetWriter 上的状态说完成。这有点违背框架。在实践中,它也会导致丑陋的,有时看似无限的等待循环结束。

但是简单地将步骤 4 设置为 finishWritingWithCompletionHandler 的完成处理程序并非易事,因为我正在编写多个轨道,但我希望步骤 4 仅在写入最后一个轨道后启动。因为第 3 步基本上是一个 for-each 处理器,所以我认为所有完成处理程序都需要相同。我想我可以使用 bools 或 counters 来改变完成处理程序,但它感觉就像一个 kluge。

如果上述任何一项有任何意义,有人可以给我/指出设计模式的入门知识,以进行这样的异步处理吗?TIA。

4

2 回答 2

3

您可以使用 GCD 调度组来解决这类问题。
从文档:

分组块允许聚合同步。您的应用程序可以提交多个块并跟踪它们何时全部完成,即使它们可能在不同的队列上运行。在完成所有指定任务之前无法取得进展时,此行为会很有帮助。

基本思想是,您为每个异步任务调用dispatch_group_enter 。在任务的完成处理程序中,您调用dispatch_group_leave

调度组的工作类似于计数信号量。启动任务时增加一个计数器(使用 dipsatch_group_wait),并在任务完成时减少一个计数器。 dispatch_group_notify允许您为您的组安装完成处理程序块。当计数器达到 0 时执行该块。

这篇博文提供了很好的概述和完整的代码示例:http ://amro.co/post/48248949039/using-gcd-to-wait-on-many-tasks

于 2013-07-10T11:08:53.193 回答
0

@weichsel 非常感谢。这似乎应该工作。但是,我正在使用 dispatch_group_wait ,它似乎没有等待。自从您第一次回复以来,我一直在反对它几个小时,但现在幸运了。这是我所做的:

  • 添加了一个调度组的属性,称为 videoDispatchGroup,并在进行视频处理的类的 init 中调用 dispatch_group_create

  • 在创建视频轨道的方法中,使用 dispatch_group_async(videoDispatchGroup, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [videoWriter finishWritingWithCompletionHandler:^{

  • 视频轨道写入方法是从将各个步骤链接在一起的方法中调用的。在该方法中,在调用写入曲目之后,我调用 dispatch_group_wait(videoProcessingGroup, DISPATCH_TIME_FOREVER);

  • 在 dealloc 中,调用 dispatch_release(videoDispatchGroup)

这一切都被忽略了,但实际上对 dispatch_group_wait 的调用似乎并没有在等待。我的猜测是它与 dispatch_group_asyn 调用有关,但我不确定到底是什么。

我找到了另一种处理方法,通过finishWritingWithCompletion 处理程序上的异步处理程序使用我自己的int 计数/减量。但我真的很想通过更好地理解 GCD 来提高我的技能。


这是代码—— dispatch_group_wait 似乎永远不会触发,但电影本身是制作的。为简洁起见,省略了一些代码,但没有删除任何没有 GCD 代码的代码。

@implementation MovieMaker 

// This is the dispatch group
@synthesize videoProcessingGroup = _videoProcessingGroup;

-(id)init {
  self = [super init];

  if (self) {
     _videoProcessingGroup = dispatch_group_create();
  }

  return self;
}

-(void)dealloc {
  dispatch_release(self.videoProcessingGroup);
}

-(id)convert:(MTCanvasElementViewController *)sourceObject {
  // code fails in same way with or without this line
  dispatch_group_enter(self.videoProcessingGroup);

  // This method works its way down to writeImageArrayToMovie
  _tracksData = [self collectTracks:sourceObject];

  NSString *fileName = @"";

  // The following seems to never stop waiting, the movies themselves get made though
  // Wait until dispatch group finishes processing temp tracks
  dispatch_group_wait(self.videoProcessingGroup, DISPATCH_TIME_FOREVER);

  // never gets to here
  fileName = [self writeTracksToMovie:_tracksData];

  // Wait until dispatch group finishes processing final track
  dispatch_group_wait(self.videoProcessingGroup, DISPATCH_TIME_FOREVER);

  return fileName;
}


// @param videoFrames should be NSArray of UIImage, all of same size
// @return path to temp file
-(NSString *)writeImageArrayToMovie:(NSArray *)videoFrames usingDispatchGroup:(dispatch_group_t)dispatchGroup {
// elided a bunch of stuff, but it all works

  AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:result]
                                                     fileType:AVFileTypeMPEG4
                                                        error:&error];

//elided stuff

  //Finish the session:
  [writerInput markAsFinished];


  dispatch_group_async(dispatchGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [videoWriter finishWritingWithCompletionHandler:^{
      dispatch_group_leave(dispatchGroup);

// not sure I ever get here? NSLogs don't write out.
      CVPixelBufferPoolRelease(adaptor.pixelBufferPool);
    }];
  });

  return result;
}
于 2013-07-13T16:28:04.017 回答