如何同时将AVAssetReader和AVAssetWriter用于多个轨道(音频和视频)?

aka*_*aru 9 iphone transcoding avfoundation ios avassetwriter

我知道如何使用AVAssetReader和AVAssetWriter,并成功地使用它们从一部电影中获取视频轨道并将其转码为另一部电影.但是,我也想用音频来做这件事.在完成初始转码后,我是否必须创建和AVAssetExportSession,或者在写入会话期间是否有某种方式在轨道之间切换?我不想处理AVAssetExportSession的开销.

我问,因为,使用拉式方法 - (while([assetWriterInput isReadyForMoreMediaData]){...} - 仅假设一个轨道.它如何用于多个轨道,即音频和视频轨道?

Fio*_*ins 8

AVAssetWriter将自动在其关联的AVAssetWriterInputs 上交错请求,以便将不同的轨道集成到输出文件中.只需AVAssetWriterInput为您拥有的每个曲目添加一个,然后调用requestMediaDataWhenReadyOnQueue:usingBlock:AVAssetWriterInput的每个曲目.

这是我调用的方法requestMediaDataWhenReadyOnQueue:usingBlock:.我通过循环调用这个方法来处理我拥有的输出/输入对的数量.(一个单独的方法既适用于代码可读性,也因为与循环不同,每个调用都为该块设置一个单独的堆栈帧.)

您只需要一个dispatch_queue_t并且可以将其重复用于所有轨道.需要注意的是,你绝对应该dispatch_async您块,因为requestMediaDataWhenReadyOnQueue:usingBlock:预计该块,那么,块,直到它填补了尽可能多的数据的AVAssetWriterInput需要.你不想在那之前回来.

- (void)requestMediaDataForTrack:(int)i {
  AVAssetReaderOutput *output = [[_reader outputs] objectAtIndex:i];
  AVAssetWriterInput *input = [[_writer inputs] objectAtIndex:i];

  [input requestMediaDataWhenReadyOnQueue:_processingQueue usingBlock:
    ^{
      [self retain];
      while ([input isReadyForMoreMediaData]) {
        CMSampleBufferRef sampleBuffer;
        if ([_reader status] == AVAssetReaderStatusReading &&
            (sampleBuffer = [output copyNextSampleBuffer])) {

          BOOL result = [input appendSampleBuffer:sampleBuffer];
          CFRelease(sampleBuffer);

          if (!result) {
            [_reader cancelReading];
            break;
          }
        } else {
          [input markAsFinished];

          switch ([_reader status]) {
            case AVAssetReaderStatusReading:
              // the reader has more for other tracks, even if this one is done
              break;

            case AVAssetReaderStatusCompleted:
              // your method for when the conversion is done
              // should call finishWriting on the writer
              [self readingCompleted];
              break;

            case AVAssetReaderStatusCancelled:
              [_writer cancelWriting];
              [_delegate converterDidCancel:self];
              break;

            case AVAssetReaderStatusFailed:
              [_writer cancelWriting];
              break;
          }

          break;
        }
      }
    }
  ];
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*lin 1

您是否尝试过使用两个 AVAssetWriterInputs 并将样本推送到工作队列?这是一个粗略的草图。

processing_queue = dispatch_queue_create("com.mydomain.gcdqueue.mediaprocessor", NULL);

[videoAVAssetWriterInput requestMediaDataWhenReadyOnQueue:myInputSerialQueue usingBlock:^{
    dispatch_asyc(processing_queue, ^{process video});
}];

[audioAVAssetWriterInput requestMediaDataWhenReadyOnQueue:myInputSerialQueue usingBlock:^{
    dispatch_asyc(processing_queue, ^{process audio});
}];
Run Code Online (Sandbox Code Playgroud)