iOS反向音频通过AVAssetWriter

我正在尝试使用AVAsset和AVAssetWriter在iOS中反转音频。 以下代码正常,但输出文件比输入短。 例如,输入文件的持续时间为1:59,但输出1:50且音频内容相同。

- (void)reverse:(AVAsset *)asset { AVAssetReader* reader = [[AVAssetReader alloc] initWithAsset:asset error:nil]; AVAssetTrack* audioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; NSMutableDictionary* audioReadSettings = [NSMutableDictionary dictionary]; [audioReadSettings setValue:[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey]; AVAssetReaderTrackOutput* readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:audioTrack outputSettings:audioReadSettings]; [reader addOutput:readerOutput]; [reader startReading]; NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt: kAudioFormatMPEG4AAC], AVFormatIDKey, [NSNumber numberWithFloat:44100.0], AVSampleRateKey, [NSNumber numberWithInt:2], AVNumberOfChannelsKey, [NSNumber numberWithInt:128000], AVEncoderBitRateKey, [NSData data], AVChannelLayoutKey, nil]; AVAssetWriterInput *writerInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio outputSettings:outputSettings]; NSString *exportPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"out.m4a"]; NSURL *exportURL = [NSURL fileURLWithPath:exportPath]; NSError *writerError = nil; AVAssetWriter *writer = [[AVAssetWriter alloc] initWithURL:exportURL fileType:AVFileTypeAppleM4A error:&writerError]; [writerInput setExpectsMediaDataInRealTime:NO]; [writer addInput:writerInput]; [writer startWriting]; [writer startSessionAtSourceTime:kCMTimeZero]; CMSampleBufferRef sample = [readerOutput copyNextSampleBuffer]; NSMutableArray *samples = [[NSMutableArray alloc] init]; while (sample != NULL) { sample = [readerOutput copyNextSampleBuffer]; if (sample == NULL) continue; [samples addObject:(__bridge id)(sample)]; CFRelease(sample); } NSArray* reversedSamples = [[samples reverseObjectEnumerator] allObjects]; for (id reversedSample in reversedSamples) { if (writerInput.readyForMoreMediaData) { [writerInput appendSampleBuffer:(__bridge CMSampleBufferRef)(reversedSample)]; } else { [NSThread sleepForTimeInterval:0.05]; } } [writerInput markAsFinished]; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); dispatch_async(queue, ^{ [writer finishWriting]; }); } 

更新:

如果我在第一个while循环中直接写样本 – 一切正常(即使使用writerInput.readyForMoreMediaData检查)。 在这种情况下,结果文件与原始文件的持续时间完全相同。 但是如果我从反向NSArray写出相同的样本 – 结果会更短。

以样本数量打印每个缓冲区的大小(通过“读取”readOuput while循环),并在“write”writerInput for-loop中重复。 这样您就可以看到所有缓冲区大小,看看它们是否相加。

例如, if (writerInput.readyForMoreMediaData)为false,则“丢失”或跳过缓冲区,“睡眠”,然后继续执行if (writerInput.readyForMoreMediaData)中的下一个reversedSample(该缓冲区从writerInput中有效地被删除)

更新 (基于评论):我在代码中发现,有两个问题:

  1. 输出设置不正确(输入文件为单声道1声道),但输出设置配置为2声道。应该是: [NSNumber numberWithInt:1], AVNumberOfChannelsKey 。查看输出和输入文件的信息:

在此处输入图像描述在此处输入图像描述

  1. 第二个问题是您正在反转8192个音频样本的643个缓冲区,而不是反转每个音频样本的索引。 为了查看每个缓冲区,我改变了调试,从查看每个样本的大小到查看缓冲区的大小,即size_t sampleSize = CMSampleBufferGetNumSamples(sample);因此,第76行现在是: size_t sampleSize = CMSampleBufferGetNumSamples(sample);

输出如下:

 2015-03-19 22:26:28.171 audioReverse[25012:4901250] Reading [0]: 8192 2015-03-19 22:26:28.172 audioReverse[25012:4901250] Reading [1]: 8192 ... 2015-03-19 22:26:28.651 audioReverse[25012:4901250] Reading [640]: 8192 2015-03-19 22:26:28.651 audioReverse[25012:4901250] Reading [641]: 8192 2015-03-19 22:26:28.651 audioReverse[25012:4901250] Reading [642]: 5056 2015-03-19 22:26:28.651 audioReverse[25012:4901250] Writing [0]: 5056 2015-03-19 22:26:28.652 audioReverse[25012:4901250] Writing [1]: 8192 ... 2015-03-19 22:26:29.134 audioReverse[25012:4901250] Writing [640]: 8192 2015-03-19 22:26:29.135 audioReverse[25012:4901250] Writing [641]: 8192 2015-03-19 22:26:29.135 audioReverse[25012:4901250] Writing [642]: 8192 

这表明您正在颠倒8192个样本的每个缓冲区的顺序,但在每个缓冲区中,音频仍然“朝前”。 我们可以在这个屏幕截图中看到这一点,我采用了正确的反转(逐个样本)与缓冲区逆转:

在此处输入图像描述

我认为如果你还反转每个8192缓冲区的每个样本,你的当前方案也可以工作。 我个人不建议使用NSArray枚举器进行信号处理,但如果你在样本级别操作它可以工作。