简体   繁体   English

AVAssetWriter如何编写向下采样/压缩的m4a / mp3文件

[英]AVAssetWriter How to write down-sampled/compressed m4a/mp3 files

I'm trying to take a local m4a or mp3 file and compress/down-sample this file (for the purposes of making a smaller file). 我正在尝试获取本地m4a或mp3文件并对该文件进行压缩/降采样(以制作更小的文件)。

Originally, I was using the AVAssetExportSession to export an AVAsset to a temp directory, but I didn't have any control over compression/down-sampling (you can only use presets, which of them, only .wav file formats support quality degradation). 最初,我使用AVAssetExportSession将AVAsset导出到临时目录,但是我对压缩/下采样没有任何控制权(只能使用预设,其中只有.wav文件格式支持质量降级) 。

Then, following several examples here on SO, I tried using AVAssetReader/AVAssetWriter to preform this 'export'. 然后,按照SO上的几个示例,我尝试使用AVAssetReader / AVAssetWriter进行此“导出”。

I create my reader/writer as such: 我这样创建我的读者/作家:

NSString *exportPath = [NSHomeDirectory() stringByAppendingPathComponent:@"out.m4a"];

NSURL *exportURL = [NSURL fileURLWithPath:outPath];

// reader
NSError *readerError = nil;
AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:asset
                                                       error:&readerError];

AVAssetTrack *track = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];    
AVAssetReaderTrackOutput *readerOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:track
                                                                          outputSettings:nil];
[reader addOutput:readerOutput];

// writer
NSError *writerError = nil;
AVAssetWriter *writer = [[AVAssetWriter alloc] initWithURL:exportURL
                                                  fileType:AVFileTypeAppleM4A
                                                     error:&writerError];

AudioChannelLayout channelLayout;
memset(&channelLayout, 0, sizeof(AudioChannelLayout));
channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;

// use different values to affect the downsampling/compression
NSDictionary *outputSettings = [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 *writerInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio
                                                                 outputSettings:outputSettings];
[writerInput setExpectsMediaDataInRealTime:NO];
[writer addInput:writerInput];

And then I start writing... 然后我开始写...

[writer startWriting];
[writer startSessionAtSourceTime:kCMTimeZero];

[reader startReading];
dispatch_queue_t mediaInputQueue = dispatch_queue_create("mediaInputQueue", NULL);
[writerInput requestMediaDataWhenReadyOnQueue:mediaInputQueue usingBlock:^{

    NSLog(@"Asset Writer ready : %d", writerInput.readyForMoreMediaData);
    while (writerInput.readyForMoreMediaData) {
        CMSampleBufferRef nextBuffer;
        if ([reader status] == AVAssetReaderStatusReading && (nextBuffer = [readerOutput copyNextSampleBuffer])) {
            if (nextBuffer) {
                NSLog(@"Adding buffer");
                [writerInput appendSampleBuffer:nextBuffer];
            }
        } else {
            [writerInput markAsFinished];

            switch ([reader status]) {
                case AVAssetReaderStatusReading:
                    break;
                case AVAssetReaderStatusFailed:
                    [writer cancelWriting];
                    break;
                case AVAssetReaderStatusCompleted:
                    NSLog(@"Writer completed");
                    [writer endSessionAtSourceTime:asset.duration];
                    [writer finishWriting];

                    NSData *data = [NSData dataWithContentsOfFile:exportPath];
                    NSLog(@"Data: %@", data);
                    break;
            }
            break;
        }
    }
}];

When I'm done writing, the data i've supposedly written to the exportURL is null, and the writer reports a successful completion. 当我写完后,我应该写到exportURL的数据为空,并且编写器报告成功完成。 Any ideas what might be going wrong? 任何想法可能出什么问题吗?

Update The writer status after calling appendSampleBuffer: is AVAssetWriterStatusFailed, though the readers status is successful, and seems to read through the entire file. 更新调用appendSampleBuffer:之后的appendSampleBuffer:器状态为AVAssetWriterStatusFailed,尽管读取器状态为成功,并且似乎可以读取整个文件。

I came across the solution: 我遇到了解决方案:

Using NSHomeDirectory() in NSString *exportPath = [NSHomeDirectory() stringByAppendingPathComponent:@"out.m4a"] was causing the writer to not be able to create the file. NSString *exportPath = [NSHomeDirectory() stringByAppendingPathComponent:@"out.m4a"]使用NSHomeDirectory() NSString *exportPath = [NSHomeDirectory() stringByAppendingPathComponent:@"out.m4a"]导致NSString *exportPath = [NSHomeDirectory() stringByAppendingPathComponent:@"out.m4a"]无法创建文件。 Not exactly sure why, or what I would need to do to allow this to work, but changing NSHomeDirectiory() to NSTemporaryDirectory() has solved my problems in the meantime. 不完全确定为什么,或者我需要做些什么才能NSTemporaryDirectory()正常工作,但是将NSHomeDirectiory()更改为NSTemporaryDirectory()已经解决了我的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM