iOS 音頻硬編碼AAC

關(guān)于一些參數(shù)的含義和音頻采集參考:
iOS音頻AudioStreamBasicDescription設(shè)置
iOS 音頻AudioComponentDescription類型設(shè)置
iOS 音頻采集

創(chuàng)建一個(gè)編碼器

- (void)createAudioConvert{
    AudioConverterRef m_converter;
    
    //輸入音頻的相關(guān)屬性
    AudioStreamBasicDescription inputFormat = {0};
    inputFormat.mSampleRate = 44100;
    inputFormat.mFormatID = kAudioFormatLinearPCM;
    inputFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
    inputFormat.mChannelsPerFrame = 2;
    inputFormat.mFramesPerPacket = 1;
    inputFormat.mBitsPerChannel = 16;
    inputFormat.mBytesPerFrame = inputFormat.mBitsPerChannel / 8 * inputFormat.mChannelsPerFrame;
    inputFormat.mBytesPerPacket = inputFormat.mBytesPerFrame * inputFormat.mFramesPerPacket;
    
    // 這里開始是輸出音頻格式
    AudioStreamBasicDescription outputFormat = {0};
    outputFormat.mSampleRate = inputFormat.mSampleRate;       // 采樣率保持一致
    outputFormat.mFormatID = kAudioFormatMPEG4AAC;            // AAC編碼 kAudioFormatMPEG4AAC kAudioFormatMPEG4AAC_HE_V2
    outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;// 聲道數(shù)保持一致
    outputFormat.mFramesPerPacket = 1024;                     // AAC一幀是1024個(gè)字節(jié)
    
    //設(shè)置輸出格式,編碼方式
    const OSType subtype = kAudioFormatMPEG4AAC;
    AudioClassDescription requestedCodecs[2] = {
        {
            kAudioEncoderComponentType,
            subtype,
            kAppleSoftwareAudioCodecManufacturer
        },
        {
            kAudioEncoderComponentType,
            subtype,
            kAppleHardwareAudioCodecManufacturer
        }
    };
    
    //初始化m_converter
    OSStatus result = AudioConverterNewSpecific(&inputFormat, &outputFormat, 2, requestedCodecs, &m_converter);
    
    // AAC并不是隨便的碼率都可以支持。比如如果PCM采樣率是44100Hz,那么碼率可以設(shè)置64000bps,如果是16000,可以設(shè)置為32000bps。
    UInt32 outputBitrate = 64000;
    UInt32 propSize = sizeof(outputBitrate);
    if(result == noErr) {
        //設(shè)置碼率
        result = AudioConverterSetProperty(m_converter, kAudioConverterEncodeBitRate, propSize, &outputBitrate);
    }
   
    ///到此,我們?nèi)〉昧司幋a器 m_converter
   
}

開始編碼

接上篇,采集器采集的數(shù)據(jù),在封裝成NSData之后,傳入編碼器進(jìn)行編碼

///開始積累需要編碼的數(shù)據(jù)
- (void)encodeAudioData:(nullable NSData*)audioData timeStamp:(uint64_t)timeStamp{
    ///當(dāng)待編碼數(shù)據(jù)到達(dá)一定量時(shí)候進(jìn)行編碼
    ///[self bufferLength] 為了方便理解這么寫,其實(shí)是一個(gè)常量
    if(leftLength + audioData.length >= [self bufferLength]){
        ///發(fā)送數(shù)據(jù)去編碼
        NSInteger totalSize = leftLength + audioData.length;
        NSInteger encodeCount = totalSize/[self bufferLength];
        char *totalBuf = malloc(totalSize);
        char *p = totalBuf;
        
        memset(totalBuf, (int)totalSize, 0);
        memcpy(totalBuf, leftBuf, leftLength);
        memcpy(totalBuf + leftLength, audioData.bytes, audioData.length);
        
        for(NSInteger index = 0;index < encodeCount;index++){
            [self encodeBuffer:p timeStamp:timeStamp];
            p += [self bufferLength];
        }
        leftLength = totalSize%[self bufferLength];
        memset(leftBuf, 0, [self bufferLength]);
        memcpy(leftBuf, totalBuf + (totalSize -leftLength), leftLength);
        
        free(totalBuf);
    }else{
        ///< 積累數(shù)據(jù)
        memcpy(leftBuf+leftLength, audioData.bytes, audioData.length);
        leftLength = leftLength + audioData.length;
    }
}

開始進(jìn)行編碼

//開始編碼
- (void)encodeBuffer:(char*)buf timeStamp:(uint64_t)timeStamp{
    AudioBuffer inBuffer;
    inBuffer.mNumberChannels = 1;
    inBuffer.mData = buf;
    inBuffer.mDataByteSize = (UInt32)[self bufferLength];
    
    AudioBufferList buffers;
    buffers.mNumberBuffers = 1;
    buffers.mBuffers[0] = inBuffer;
    
    // 初始化一個(gè)輸出緩沖列表
    AudioBufferList outBufferList;
    outBufferList.mNumberBuffers = 1;
    outBufferList.mBuffers[0].mNumberChannels = inBuffer.mNumberChannels;
    outBufferList.mBuffers[0].mDataByteSize = inBuffer.mDataByteSize;   // 設(shè)置緩沖區(qū)大小
    outBufferList.mBuffers[0].mData = aacBuf;           // 設(shè)置AAC緩沖區(qū)
    UInt32 outputDataPacketSize = 1;
    if (AudioConverterFillComplexBuffer(m_converter, audioInputDataProc, &buffers, &outputDataPacketSize, &outBufferList, NULL) != noErr) {
        return;
    }
    //編碼后的數(shù)據(jù)
    //outBufferList.mBuffers[0].mData
    //size
    //outBufferList.mBuffers[0].mDataByteSize
    //timeStamp
    //timeStamp
    //轉(zhuǎn)為NSData
    //[NSData dataWithBytes:outBufferList.mBuffers[0].mData length:outBufferList.mBuffers[0].mDataByteSize]
    //ADTS 頭
    //NSData *adts = [self adtsData:2 rawDataLength:outBufferList.mBuffers[0].mDataByteSize];
    


}
OSStatus audioInputDataProc(AudioConverterRef inConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription * *outDataPacketDescription, void *inUserData) {
    //AudioConverterFillComplexBuffer 編碼過程中,會(huì)要求這個(gè)函數(shù)來填充輸入數(shù)據(jù),也就是原始PCM數(shù)據(jù)
    AudioBufferList bufferList = *(AudioBufferList *)inUserData;
    ioData->mBuffers[0].mNumberChannels = 1;
    ioData->mBuffers[0].mData = bufferList.mBuffers[0].mData;
    ioData->mBuffers[0].mDataByteSize = bufferList.mBuffers[0].mDataByteSize;
    return noErr;
}

另外,獲取到的數(shù)據(jù)默認(rèn)是沒有ADTS頭的,如果需要添加,調(diào)用以下代碼

- (NSData *)adtsData:(NSInteger)channel rawDataLength:(NSInteger)rawDataLength {
    int adtsLength = 7;
    char *packet = malloc(sizeof(char) * adtsLength);
    // Variables Recycled by addADTStoPacket
    int profile = 2;  //AAC LC
    //39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
    NSInteger freqIdx = [self sampleRateIndex:self.configuration.audioSampleRate];  //44.1KHz
    int chanCfg = (int)channel;  //MPEG-4 Audio Channel Configuration. 1 Channel front-center
    NSUInteger fullLength = adtsLength + rawDataLength;
    // fill in ADTS data
    packet[0] = (char)0xFF;     // 11111111     = syncword
    packet[1] = (char)0xF9;     // 1111 1 00 1  = syncword MPEG-2 Layer CRC
    packet[2] = (char)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
    packet[3] = (char)(((chanCfg&3)<<6) + (fullLength>>11));
    packet[4] = (char)((fullLength&0x7FF) >> 3);
    packet[5] = (char)(((fullLength&7)<<5) + 0x1F);
    packet[6] = (char)0xFC;
    NSData *data = [NSData dataWithBytesNoCopy:packet length:adtsLength freeWhenDone:YES];
    return data;
}

- (NSInteger)sampleRateIndex:(NSInteger)frequencyInHz {
    NSInteger sampleRateIndex = 0;
    switch (frequencyInHz) {
        case 96000:
            sampleRateIndex = 0;
            break;
        case 88200:
            sampleRateIndex = 1;
            break;
        case 64000:
            sampleRateIndex = 2;
            break;
        case 48000:
            sampleRateIndex = 3;
            break;
        case 44100:
            sampleRateIndex = 4;
            break;
        case 32000:
            sampleRateIndex = 5;
            break;
        case 24000:
            sampleRateIndex = 6;
            break;
        case 22050:
            sampleRateIndex = 7;
            break;
        case 16000:
            sampleRateIndex = 8;
            break;
        case 12000:
            sampleRateIndex = 9;
            break;
        case 11025:
            sampleRateIndex = 10;
            break;
        case 8000:
            sampleRateIndex = 11;
            break;
        case 7350:
            sampleRateIndex = 12;
            break;
        default:
            sampleRateIndex = 15;
    }
    return sampleRateIndex;
}

其他參數(shù):

{
    char *leftBuf;
    char *aacBuf;
    NSInteger leftLength;
    AudioConverterRef m_converter;
}
- (instancetype)init{
    if (self = [super init]) {
        /*leftBuf aacBuf銷毀的時(shí)候需要釋放 free(leftBuf) free(aacBuf)*/
        if (!leftBuf) {
            leftBuf = malloc([self bufferLength]);
        }
        if (!aacBuf) {
            aacBuf = malloc([self bufferLength]);
        }
    
        leftLength = 0;
    }
    return self;
}
- (NSUInteger)bufferLength{
    /* 1024 * 2 * 聲道數(shù) */
    return 1024 * 2 * 2;
}

Demo地址整理后奉上。
有其他不明白的,可以留言,看到就會(huì)回復(fù)。
如果喜歡,請(qǐng)幫忙點(diǎn)贊。支持轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)附原文鏈接。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容