Сторнирование аудио файла
Я использую AVAssetReader и AVAssetWriter для реверса аудио файла. Тем не менее, результирующий реверсированный звук очень резкий.
Как лучше всего поменять аудиофайл?
Буду признателен за любую оказанную помощь.
-(void)reverseAudio:(NSURL *)videoURL andVideoAsset:(AVURLAsset *)videoAsset{
AVAssetReader *video2AssetReader = [[AVAssetReader alloc] initWithAsset:videoAsset error:nil];
video2AssetReader.timeRange = CMTimeRangeFromTimeToTime(kCMTimeZero, [videoAsset duration]);
NSArray *audioTracks = [videoAsset tracksWithMediaType:AVMediaTypeAudio];
AVAssetTrack* audioTrack = [audioTracks objectAtIndex:0];
NSDictionary *outputSettingsDict = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:kAudioFormatLinearPCM],AVFormatIDKey,
[NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,
[NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey,
[NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,
[NSNumber numberWithBool:NO],AVLinearPCMIsNonInterleaved,
nil];
AVAssetReaderTrackOutput *readerAudioTrackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:audioTrack outputSettings:outputSettingsDict];
[video2AssetReader addOutput:readerAudioTrackOutput];
[video2AssetReader startReading];
// read in the samples
NSMutableArray *audioSamples = [[NSMutableArray alloc] init];
CMSampleBufferRef audioSample;
while((audioSample = [readerAudioTrackOutput copyNextSampleBuffer])){
[audioSamples addObject:(__bridge id)audioSample];
CFRelease(audioSample);
}
videoReverseProcess3TotalFrames = audioSamples.count;
NSLog(@"AUDIO SAMPLES COUNT = %f", videoReverseProcess3TotalFrames);
[video2AssetReader cancelReading];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *videoPath = [documentsDirectory stringByAppendingPathComponent:@"videoReverseAudioFile.m4a"];
NSError *error = nil;
if([[NSFileManager defaultManager] fileExistsAtPath:videoPath]){
[[NSFileManager defaultManager] removeItemAtPath:videoPath error:&error];
if(error){
NSLog(@"VIDEO DELETE FAILED");
}
else{
NSLog(@"VIDEO DELETED");
}
}
NSURL *audioExportURL = [[NSURL alloc] initFileURLWithPath:videoPath];
AVAssetWriter *writer = [[AVAssetWriter alloc] initWithURL:audioExportURL fileType:AVFileTypeAppleM4A error:&error];
AudioChannelLayout channelLayout;
memset(&channelLayout, 0, sizeof(AudioChannelLayout));
channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
NSDictionary *audioCompressionSettings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt: kAudioFormatMPEG4AAC], AVFormatIDKey,
[NSNumber numberWithFloat:44100.0], AVSampleRateKey,
[NSNumber numberWithInt:2], AVNumberOfChannelsKey,
[NSNumber numberWithInt:128000], AVEncoderBitRateKey,
[NSData dataWithBytes:&channelLayout length:sizeof(AudioChannelLayout)], AVChannelLayoutKey, nil];
AVAssetWriterInput *writerAudioInput;
writerAudioInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio outputSettings:audioCompressionSettings];
writerAudioInput.expectsMediaDataInRealTime = NO;
if([writer canAddInput:writerAudioInput]){
[writer addInput:writerAudioInput];
}
else{
NSLog(@"ERROR ADDING AUDIO");
}
[writer startWriting];
CMTime timeStamp = CMSampleBufferGetPresentationTimeStamp((__bridge CMSampleBufferRef)audioSamples[0]);
[writer startSessionAtSourceTime:timeStamp];
while(audioSamples.count > 0){
if(writer && writerAudioInput.readyForMoreMediaData){
CMSampleBufferRef audioBufferRef = (__bridge CMSampleBufferRef)audioSamples[audioSamples.count - 1];
[writerAudioInput appendSampleBuffer:audioBufferRef];
[audioSamples removeObjectAtIndex:audioSamples.count - 1];
}
else{
[NSThread sleepForTimeInterval:0.2];
}
}
if(writer.status != AVAssetWriterStatusCancelled){
[writerAudioInput markAsFinished];
[writer finishWritingWithCompletionHandler:^{
}];
}
}
1 ответ
Вы не меняете звук, вы просто меняете порядок аудио фрагментов (буферов).
Итак, у вас есть этот вход: S1, S2, S3, S4, и вы производите следующий вывод: S4, S3, S2, S1. Но внутри этих фрагментов у вас все еще есть оригинальный порядок кадров.
Вы должны также обратить данные буфера.
Обновление № 1
Вот пример, как вы можете сделать это.
- (void)reverseAudio:(AVURLAsset *)videoAsset {
AVAssetReader *video2AssetReader = [[AVAssetReader alloc] initWithAsset:videoAsset error:nil];
video2AssetReader.timeRange = CMTimeRangeFromTimeToTime(kCMTimeZero, [videoAsset duration]);
NSArray *audioTracks = [videoAsset tracksWithMediaType:AVMediaTypeAudio];
AVAssetTrack* audioTrack = [audioTracks objectAtIndex:0];
NSDictionary *outputSettingsDict = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:kAudioFormatLinearPCM],AVFormatIDKey,
[NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,
[NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey,
[NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,
[NSNumber numberWithBool:NO],AVLinearPCMIsNonInterleaved,
nil];
AVAssetReaderTrackOutput *readerAudioTrackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:audioTrack outputSettings:outputSettingsDict];
[video2AssetReader addOutput:readerAudioTrackOutput];
[video2AssetReader startReading];
// read in the samples
CMTime timeStamp = kCMTimeInvalid;
NSMutableArray *audioSamples = [[NSMutableArray alloc] init];
CMSampleBufferRef audioSample;
while ((audioSample = [readerAudioTrackOutput copyNextSampleBuffer])) {
[audioSamples addObject:(__bridge id)[self reverseSampleBuffer:audioSample]];
if (CMTIME_IS_INVALID(timeStamp)) {
timeStamp = CMSampleBufferGetPresentationTimeStamp(audioSample);
}
CFRelease(audioSample);
}
NSLog(@"AUDIO SAMPLES COUNT = %d", (int)audioSamples.count);
[video2AssetReader cancelReading];
// rest of the code
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *videoPath = [documentsDirectory stringByAppendingPathComponent:@"videoReverseAudioFile.m4a"];
NSError *error = nil;
if ([[NSFileManager defaultManager] fileExistsAtPath:videoPath]) {
[[NSFileManager defaultManager] removeItemAtPath:videoPath error:&error];
if (error) {
NSLog(@"VIDEO DELETE FAILED");
} else {
NSLog(@"VIDEO DELETED");
}
}
NSURL *audioExportURL = [[NSURL alloc] initFileURLWithPath:videoPath];
AVAssetWriter *writer = [[AVAssetWriter alloc] initWithURL:audioExportURL fileType:AVFileTypeAppleM4A error:&error];
AudioChannelLayout channelLayout;
memset(&channelLayout, 0, sizeof(AudioChannelLayout));
channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
NSDictionary *audioCompressionSettings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:kAudioFormatMPEG4AAC], AVFormatIDKey,
[NSNumber numberWithFloat:44100.0], AVSampleRateKey,
[NSNumber numberWithInt:2], AVNumberOfChannelsKey,
[NSNumber numberWithInt:128000], AVEncoderBitRateKey,
[NSData dataWithBytes:&channelLayout length:sizeof(AudioChannelLayout)], AVChannelLayoutKey, nil];
AVAssetWriterInput *writerAudioInput;
writerAudioInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio outputSettings:audioCompressionSettings];
writerAudioInput.expectsMediaDataInRealTime = NO;
if ([writer canAddInput:writerAudioInput]) {
[writer addInput:writerAudioInput];
} else {
NSLog(@"ERROR ADDING AUDIO");
}
[writer startWriting];
[writer startSessionAtSourceTime:timeStamp];
while (audioSamples.count > 0) {
if(writer && writerAudioInput.readyForMoreMediaData) {
CMSampleBufferRef audioBufferRef = (__bridge CMSampleBufferRef)audioSamples[audioSamples.count - 1];
[writerAudioInput appendSampleBuffer:audioBufferRef];
[audioSamples removeObjectAtIndex:audioSamples.count - 1];
} else {
[NSThread sleepForTimeInterval:0.2];
}
}
if (writer.status != AVAssetWriterStatusCancelled) {
[writerAudioInput markAsFinished];
[writer finishWritingWithCompletionHandler:^{
}];
}
}
- (CMSampleBufferRef)reverseSampleBuffer:(CMSampleBufferRef)buffer {
AudioBufferList list;
CMBlockBufferRef dataBuffer = NULL;
// TODO check result code
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(buffer,
NULL,
&list,
sizeof(list),
NULL,
NULL,
kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
&dataBuffer);
CMItemCount numberOfSamples = CMSampleBufferGetNumSamples(buffer);
for (int i = 0; i < list.mNumberBuffers; i++) {
SInt16 *samples = (SInt16 *)list.mBuffers[i].mData;
for (int j = 0; j < numberOfSamples / 2; j++) {
SInt16 t = samples[j];
samples[j] = samples[numberOfSamples - 1 - j];
samples[numberOfSamples - 1 - j] = t;
}
}
CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(buffer);
CMSampleBufferRef result = NULL;
// TODO check result code
CMSampleBufferCreate(kCFAllocatorDefault, dataBuffer, true, NULL, NULL, format, 0, 0, NULL, 0, NULL, &result);
return result;
}