IOS reverse sound through AVAssetWriter

I am trying to cancel sound in iOS using AVAsset and AVAssetWriter. The following code works, but the output file is shorter than the input. For example, the input file has a duration of 1:59, but outputs 1:50 with the same audio content.

- (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]; }); } 

UPDATE:

If I write samples directly in the first while , everything is fine (even when checking writerInput.readyForMoreMediaData ). In this case, the result file has the same duration as the original. But if I write the same samples from the reverse NSArray , the result is shorter.

+5
source share
1 answer

Print the size of each buffer in the number of samples (via "reading" readerOuput while loop) and repeat in the "write" writerInput for-loop. This way you can see all buffer sizes and see if they add up.

For example, you skip or skip the if (writerInput.readyForMoreMediaData) buffer if (writerInput.readyForMoreMediaData) is false, you are "sleeping", but then proceed to the next inverted template in reverseedsamples files (this buffer is effectively removed from writerInput)

UPDATE (based on comments): I found two problems in the code:

  • Incorrect output settings (the input file is mono ( 1 ), but the output parameters are configured as 2. It should be: [NSNumber numberWithInt:1], AVNumberOfChannelsKey . Look at the information on the output and input files:

enter image description hereenter image description here

  1. The second problem is that you are reversing 643 buffers of 8192 audio samples instead of changing the index of each audio sample. To see each buffer, I changed your debugging by looking at the size of each sample to look at the size of the buffer, which is 8192. So, now line 76: size_t sampleSize = CMSampleBufferGetNumSamples(sample);

The result is as follows:

 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 

This shows that you are reordering each buffer of 8192 samples, but in each buffer the sound is still “facing forward”. We can see this in this screenshot, which I took correctly canceled (sample by sample) compared to your buffer spread:

enter image description here

I think that your current circuit can work if you also cancel each sample of each 8192 buffer. I personally would not recommend using NSMray enumerations for signal processing, but it can work if you work at the sample level.

+1
source

Source: https://habr.com/ru/post/1215692/


All Articles