简体   繁体   English

使用AVAssetWriter和AVAssetReader进行音频录制和播放

[英]Using AVAssetWriter and AVAssetReader for Audio Recording and Playback

My app uses AVAssetReader to play songs in the iPOD library. 我的应用程序使用AVAssetReader播放iPOD库中的歌曲。 Now I want to add the audio recording capability. 现在,我想添加音频录制功能。

I recorded audio using AVAssetWriter. 我使用AVAssetWriter录制了音频。 I checked the resulting audio file(MPEG4AAC format) by playing it back successfully using AVAudioPlayer. 我通过使用AVAudioPlayer成功播放了生成的音频文件(MPEG4AAC格式),检查了该文件。 My goal is to play back audio using AVAssetReader. 我的目标是使用AVAssetReader播放音频。 But when I create an AVURLAsset for the file, it has no track and hence AVAssetReader fails (error code: -11828 File Format Not Recognized). 但是,当我为文件创建AVURLAsset时,它没有轨道,因此AVAssetReader失败(错误代码:-11828无法识别文件格式)。

What should I do to make AVAsset recognize the file format? 如何使AVAsset识别文件格式? Is there some special file format required for AVAsset? AVAsset是否需要一些特殊的文件格式?

Here are codes for recording: 以下是记录代码:

void setup_ASBD(void *f, double fs, int sel, int numChannels);
static AVAssetWriter *assetWriter = NULL;
static AVAssetWriterInput *assetWriterInput = NULL;
static CMAudioFormatDescriptionRef formatDesc;
AVAssetWriter *newAssetWriter(NSURL *url) {
    NSError *outError;
    assetWriter = [AVAssetWriter assetWriterWithURL:url fileType:AVFileTypeAppleM4A error:&outError];

    if(assetWriter == nil) {
        NSLog(@"%s: asset=%x, %@\n", __FUNCTION__, (int)assetWriter, outError);
        return assetWriter;
    }

    AudioChannelLayout audioChannelLayout = {
        .mChannelLayoutTag = kAudioChannelLayoutTag_Mono,
        .mChannelBitmap = 0,
        .mNumberChannelDescriptions = 0
    };

    // Convert the channel layout object to an NSData object.
    NSData *channelLayoutAsData = [NSData dataWithBytes:&audioChannelLayout length:offsetof(AudioChannelLayout, mChannelDescriptions)];

    // Get the compression settings for 128 kbps AAC.
    NSDictionary *compressionAudioSettings = @{
                                               AVFormatIDKey         : [NSNumber numberWithUnsignedInt:kAudioFormatMPEG4AAC],
                                               AVEncoderBitRateKey   : [NSNumber numberWithInteger:128000],
                                               AVSampleRateKey       : [NSNumber numberWithInteger:44100],
                                               AVChannelLayoutKey    : channelLayoutAsData,
                                               AVNumberOfChannelsKey : [NSNumber numberWithUnsignedInteger:1]
                                               };

    // Create the asset writer input with the compression settings and specify the media type as audio.
    assetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:compressionAudioSettings];
    assetWriterInput.expectsMediaDataInRealTime = YES;

    // Add the input to the writer if possible.
    if (assetWriterInput != NULL && [assetWriter canAddInput:assetWriterInput]) {
        [assetWriter addInput:assetWriterInput];
    }
    else {
        NSLog(@"%s:assetWriteInput problem: %x\n", __FUNCTION__, (int)assetWriterInput);
        return NULL;
    }

    [assetWriter startWriting];
    // Start a sample-writing session.
    [assetWriter startSessionAtSourceTime:kCMTimeZero];

    if(assetWriter.status != AVAssetWriterStatusWriting) {
        NSLog(@"%s: Bad writer status=%d\n", __FUNCTION__, (int)assetWriter.status);
        return NULL;
    }

    AudioStreamBasicDescription ASBD;
    setup_ASBD(&ASBD, 44100, 2, 1);
    CMAudioFormatDescriptionCreate (NULL, &ASBD, sizeof(audioChannelLayout), &audioChannelLayout, 0, NULL, NULL, &formatDesc);
    //CMAudioFormatDescriptionCreate (NULL, &ASBD, 0, NULL, 0, NULL, NULL, &formatDesc);

    return assetWriter;

}

static int sampleCnt = 0;
void writeNewSamples(void *buffer, int len) {
    if(assetWriterInput == NULL) return;
    if([assetWriterInput isReadyForMoreMediaData]) {
        OSStatus result;
        CMBlockBufferRef blockBuffer = NULL;
        result = CMBlockBufferCreateWithMemoryBlock (NULL, buffer, len, NULL, NULL, 0, len, 0, &blockBuffer);
        if(result == noErr) {
            CMItemCount numSamples = len >> 1;

            const CMSampleTimingInfo sampleTiming = {CMTimeMake(1, 44100), CMTimeMake(sampleCnt, 44100), kCMTimeInvalid};
            CMItemCount numSampleTimingEntries = 1;

            const size_t sampleSize = 2;
            CMItemCount numSampleSizeEntries = 1;

            CMSampleBufferRef sampleBuffer;
            result = CMSampleBufferCreate(NULL, blockBuffer, true, NULL, NULL, formatDesc, numSamples, numSampleTimingEntries, &sampleTiming, numSampleSizeEntries, &sampleSize, &sampleBuffer);

            if(result == noErr) {
                if([assetWriterInput appendSampleBuffer:sampleBuffer] == YES) sampleCnt += numSamples;
                else {
                    NSLog(@"%s: ERROR\n", __FUNCTION__);
                }
                printf("sampleCnt = %d\n", sampleCnt);
                CFRelease(sampleBuffer);

            }
        }
    }
    else {
        NSLog(@"%s: AVAssetWriterInput not taking input data: status=%ld\n", __FUNCTION__, assetWriter.status);
    }
}

void stopAssetWriter(AVAssetWriter *assetWriter) {
    [assetWriterInput markAsFinished];
    [assetWriter finishWritingWithCompletionHandler:^{
        NSLog(@"%s: Done: %ld: %d samples\n", __FUNCTION__, assetWriter.status, sampleCnt);
        sampleCnt = 0;
    }];
    assetWriterInput = NULL;
}

It turns out that AVAsset expects "valid" file extension. 事实证明,AVASset期望“有效”文件扩展名。 So when the file name does not have one of those common extensions such as *.mp3, *.caf, *.m4a, etc, AVAsset refuses to look at the file header to figure out the media format. 因此,当文件名没有诸如* .mp3,*。caf,*。m4a等常用扩展名之一时,AVASset拒绝查看文件头以找出媒体格式。 On the other hand, AVAudioPlay seems completely indifferent to the file name and figures out the media format by itself by looking at the file header. 另一方面,AVAudioPlay似乎对文件名完全无动于衷,并通过查看文件头本身来确定媒体格式。

This difference does not appear anywhere in Apple doc. 这种差异不会出现在Apple文档的任何地方。 and I ended up wasting more than a week on this. 我最终为此浪费了一个多星期。 Sigh... 叹...

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

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