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)系作者
  • 序言:七十年代末击狮,一起剝皮案震驚了整個(gè)濱河市益老,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌档冬,老刑警劉巖互婿,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慈参,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡驮配,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猜绣,“玉大人,你說我怎么就攤上這事牺陶±敝” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵狮鸭,是天一觀的道長(zhǎng)多搀。 經(jīng)常有香客問我,道長(zhǎng)廊谓,這世上最難降的妖魔是什么麻削? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任呛哟,我火速辦了婚禮,結(jié)果婚禮上扫责,老公的妹妹穿的比我還像新娘。我一直安慰自己者娱,他們只是感情好苏揣,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布平匈。 她就那樣靜靜地躺著,像睡著了一般增炭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上梅垄,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天输玷,我揣著相機(jī)與錄音,去河邊找鬼炭玫。 笑死貌虾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的衔憨。 我是一名探鬼主播袄膏,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼码党!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起眉厨,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤兽狭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后服球,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颠焦,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了似忧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡淳衙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出饺著,到底是詐尸還是另有隱情箫攀,我是刑警寧澤幼衰,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站梢睛,受9級(jí)特大地震影響识椰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腹鹉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一功咒、第九天 我趴在偏房一處隱蔽的房頂上張望绞蹦。 院中可真熱鬧榜旦,春花似錦、人聲如沸章办。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至辜羊,卻和暖如春八秃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背昔驱。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國打工骤肛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人腋颠。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓淑玫,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親混移。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容