如何将AVAssetReader和AVAssetWriter同时用于多个音轨(audio和video)?

我知道如何使用AVAssetReader和AVAssetWriter,并成功地使用它们从一部电影中抓取video轨道并将其转码为另一部电影。 不过,我也想用audio来做这个。 在完成初始转码后,是否必须创buildAVAssetExportSession,还是在写作过程中有一些方法可以在音轨之间切换? 我讨厌不得不处理AVAssetExportSession的开销。

我问,因为使用拉风格的方法 – (while([assetWriterInput isReadyForMoreMediaData]){…} – 假设只有一个轨道,它怎么能用于多个轨道,即audio和video轨道?

AVAssetWriter将自动交叉在其相关的AVAssetWriterInput的请求,以便将不同的轨道集成到输出文件中。 只需为每个音轨添加一个AVAssetWriterInput ,然后在每个AVAssetWriterInput上调用requestMediaDataWhenReadyOnQueue:usingBlock: AVAssetWriterInput

这里有一个方法,我调用requestMediaDataWhenReadyOnQueue:usingBlock: 我从一个循环遍历input/input对的数目来调用这个方法。 (对于代码可读性来说,单独的方法是有好处的,并且因为不同于循环,每个调用为该块设置单独的堆栈帧。)

你只需要一个dispatch_queue_t并可以重复使用它的所有轨道。 请注意,您绝对不应该在您的块中调用dispatch_async ,因为requestMediaDataWhenReadyOnQueue:usingBlock:预计该块将被阻塞,直到它填充了与AVAssetWriterInput相同的数据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; } } } ]; } 

您是否尝试过使用两个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}); }]; 

你可以使用调度组

查看MacOSX的AVReaderWriter示例…

我直接从示例RWDocument.m中引用:

 - (BOOL)startReadingAndWritingReturningError:(NSError **)outError { BOOL success = YES; NSError *localError = nil; // Instruct the asset reader and asset writer to get ready to do work success = [assetReader startReading]; if (!success) localError = [assetReader error]; if (success) { success = [assetWriter startWriting]; if (!success) localError = [assetWriter error]; } if (success) { dispatch_group_t dispatchGroup = dispatch_group_create(); // Start a sample-writing session [assetWriter startSessionAtSourceTime:[self timeRange].start]; // Start reading and writing samples if (audioSampleBufferChannel) { // Only set audio delegate for audio-only assets, else let the video channel drive progress id <RWSampleBufferChannelDelegate> delegate = nil; if (!videoSampleBufferChannel) delegate = self; dispatch_group_enter(dispatchGroup); [audioSampleBufferChannel startWithDelegate:delegate completionHandler:^{ dispatch_group_leave(dispatchGroup); }]; } if (videoSampleBufferChannel) { dispatch_group_enter(dispatchGroup); [videoSampleBufferChannel startWithDelegate:self completionHandler:^{ dispatch_group_leave(dispatchGroup); }]; } // Set up a callback for when the sample writing is finished dispatch_group_notify(dispatchGroup, serializationQueue, ^{ BOOL finalSuccess = YES; NSError *finalError = nil; if (cancelled) { [assetReader cancelReading]; [assetWriter cancelWriting]; } else { if ([assetReader status] == AVAssetReaderStatusFailed) { finalSuccess = NO; finalError = [assetReader error]; } if (finalSuccess) { finalSuccess = [assetWriter finishWriting]; if (!finalSuccess) finalError = [assetWriter error]; } } [self readingAndWritingDidFinishSuccessfully:finalSuccess withError:finalError]; }); dispatch_release(dispatchGroup); } if (outError) *outError = localError; return success; } 
Interesting Posts