AudioToolBox 解碼AAC

上一篇文章中,我們針對(duì)PCM 數(shù)據(jù),通過AudioToolBoxPCM 數(shù)據(jù)編碼成AAC 數(shù)據(jù),并把AAC 數(shù)據(jù)添加ADTS Header,并把AAC格式的音頻數(shù)據(jù)寫入文件;

這一章呢,我們主要是用AudioToolBoxAAC數(shù)據(jù) 解碼成PCM格式,并利用AVFoundation框架把PCM數(shù)據(jù) 從揚(yáng)聲器播放處理;

1. 音頻采集

關(guān)于音頻采集部分,上篇文章已經(jīng)介紹過了,是采用 AVFoundation 框架 對(duì)AVCaptureSessionSession 進(jìn)行封裝,添加音頻輸入源,然后添加 output輸出,通過采集音頻設(shè)備,最后通過代理方法拿到音頻流PCM 數(shù)據(jù);這里不做過多贅述 ,可以參考上篇文章AudioToolBox 編碼AAC 或者直接看源碼:https://github.com/hunter858/OpenGL_Study

2. AAC數(shù)據(jù)獲取

AAC的原始數(shù)據(jù),也是上篇文章介紹過的,通過 AudioEncoder 拿到的編碼后的AAC數(shù)據(jù)部分,不包含ADTS header部分;因?yàn)?code>ADTS Header主要是寫入文件需要的;
通過AudioEncoderaudioEncodeCallback 代理方法拿到編碼后的AAC 數(shù)據(jù);

- (void)audioEncodeCallback:(NSData *)aacData;

3. AudioToolBox 創(chuàng)建

關(guān)于AudioToolBox的創(chuàng)建 和編碼部分一致,只是解碼部分,把inputoutput的配置 做了一個(gè)調(diào)換(這么理解);
這里我們創(chuàng)建一個(gè)AudioDecoder類封裝AudioToolBox對(duì)象,通過AudioConfig音頻配置來初始化 硬件編碼器所需要的一些參數(shù) ;

源碼如下:

- (void)setupEncoder {
   
   //輸出參數(shù)pcm
   AudioStreamBasicDescription outputAudioDes = {0};
   outputAudioDes.mSampleRate = (Float64)_config.sampleRate;       //采樣率
   outputAudioDes.mChannelsPerFrame = (UInt32)_config.channelCount; //輸出聲道數(shù)
   outputAudioDes.mFormatID = kAudioFormatLinearPCM;                //輸出格式
   outputAudioDes.mFormatFlags = (kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked); //編碼 12
   outputAudioDes.mFramesPerPacket = 1;                            //每一個(gè)packet幀數(shù) ;
   outputAudioDes.mBitsPerChannel = 16;                             //數(shù)據(jù)幀中每個(gè)通道的采樣位數(shù)。
   outputAudioDes.mBytesPerFrame = outputAudioDes.mBitsPerChannel / 8 *outputAudioDes.mChannelsPerFrame;                              //每一幀大小(采樣位數(shù) / 8 *聲道數(shù))
   outputAudioDes.mBytesPerPacket = outputAudioDes.mBytesPerFrame * outputAudioDes.mFramesPerPacket;                             //每個(gè)packet大?。◣笮?* 幀數(shù))
   outputAudioDes.mReserved =  0;                                  //對(duì)其方式 0(8字節(jié)對(duì)齊)
   
   //輸入?yún)?shù)aac
   AudioStreamBasicDescription inputAduioDes = {0};
   inputAduioDes.mSampleRate = (Float64)_config.sampleRate;
   inputAduioDes.mFormatID = kAudioFormatMPEG4AAC;
   inputAduioDes.mFormatFlags = kMPEG4Object_AAC_LC;
   inputAduioDes.mFramesPerPacket = 1024;
   inputAduioDes.mChannelsPerFrame = (UInt32)_config.channelCount;
   
   //填充輸出相關(guān)信息
   UInt32 inDesSize = sizeof(inputAduioDes);
   AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &inDesSize, &inputAduioDes);
   
   //獲取解碼器的描述信息(只能傳入software)
   AudioClassDescription *audioClassDesc = [self getAudioCalssDescriptionWithType:outputAudioDes.mFormatID fromManufacture:kAppleSoftwareAudioCodecManufacturer];
   /** 創(chuàng)建converter
    參數(shù)1:輸入音頻格式描述
    參數(shù)2:輸出音頻格式描述
    參數(shù)3:class desc的數(shù)量
    參數(shù)4:class desc
    參數(shù)5:創(chuàng)建的解碼器
    */
   OSStatus status = AudioConverterNewSpecific(&inputAduioDes, &outputAudioDes, 1, audioClassDesc, &_audioConverter);
   if (status != noErr) {
       NSLog(@"Error!:硬解碼AAC創(chuàng)建失敗, status= %d", (int)status);
       return;
   }
}

4. 解碼AAC

在通過 AudioEncoder 的 代理方法 - (void)audioEncodeCallback:(NSData *)aacData; 拿到編碼后的AAC 數(shù)據(jù)后,直接把 AAC數(shù)據(jù)送入解碼器,還原AAC數(shù)據(jù)至PCM格式;

關(guān)于解碼部分的源碼如下:

- (void)decodeAudioAACData:(NSData *)aacData {
  
   if (!_audioConverter) { return; }
   
   dispatch_async(_decoderQueue, ^{
    
       //記錄aac 作為參數(shù)參入解碼回調(diào)函數(shù)
       CCAudioUserData userData = {0};
       userData.channelCount = (UInt32)_config.channelCount;
       userData.data = (char *)[aacData bytes];
       userData.size = (UInt32)aacData.length;
       userData.packetDesc.mDataByteSize = (UInt32)aacData.length;
       userData.packetDesc.mStartOffset = 0;
       userData.packetDesc.mVariableFramesInPacket = 0;
       
       //輸出大小和packet個(gè)數(shù)
       UInt32 pcmBufferSize = (UInt32)(2048 * _config.channelCount);
       UInt32 pcmDataPacketSize = 1024;
       
       //創(chuàng)建臨時(shí)容器pcm
       uint8_t *pcmBuffer = malloc(pcmBufferSize);
       memset(pcmBuffer, 0, pcmBufferSize);
       
       //輸出buffer
       AudioBufferList outAudioBufferList = {0};
       outAudioBufferList.mNumberBuffers = 1;
       outAudioBufferList.mBuffers[0].mNumberChannels = (uint32_t)_config.channelCount;
       outAudioBufferList.mBuffers[0].mDataByteSize = (UInt32)pcmBufferSize;
       outAudioBufferList.mBuffers[0].mData = pcmBuffer;
       
       //輸出描述
       AudioStreamPacketDescription outputPacketDesc = {0};
       
       //配置填充函數(shù),獲取輸出數(shù)據(jù)
       OSStatus status = AudioConverterFillComplexBuffer(_audioConverter, &AudioDecoderConverterComplexInputDataProc, &userData, &pcmDataPacketSize, &outAudioBufferList, &outputPacketDesc);
       if (status != noErr) {
           NSLog(@"Error: AAC Decoder error, status=%d",(int)status);
           return;
       }
       //如果獲取到數(shù)據(jù)
       if (outAudioBufferList.mBuffers[0].mDataByteSize > 0) {
           NSData *rawData = [NSData dataWithBytes:outAudioBufferList.mBuffers[0].mData length:outAudioBufferList.mBuffers[0].mDataByteSize];
           dispatch_async(_callbackQueue, ^{
               [_delegate audioDecodeCallback:rawData];
           });
       }
       free(pcmBuffer);
   });
   
}


static OSStatus AudioDecoderConverterComplexInputDataProc(  AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData,  AudioStreamPacketDescription **outDataPacketDescription,  void *inUserData) {
    
    
    CCAudioUserData *audioDecoder = (CCAudioUserData *)(inUserData);
    if (audioDecoder->size <= 0) {
        ioNumberDataPackets = 0;
        return -1;
    }
   
    //填充數(shù)據(jù)
    *outDataPacketDescription = &audioDecoder->packetDesc;
    (*outDataPacketDescription)[0].mStartOffset = 0;
    (*outDataPacketDescription)[0].mDataByteSize = audioDecoder->size;
    (*outDataPacketDescription)[0].mVariableFramesInPacket = 0;
    
    ioData->mBuffers[0].mData = audioDecoder->data;
    ioData->mBuffers[0].mDataByteSize = audioDecoder->size;
    ioData->mBuffers[0].mNumberChannels = audioDecoder->channelCount;
    
    return noErr;
}

5. PCM 播放

拿到PCM數(shù)據(jù)后,如何驗(yàn)證我們解碼是否成功,我們有2種辦法;

  • 第一種方法是把 PCM保存下來使用 ffmpeg 進(jìn)行播放;
  • 第二種方法是把 PCM 從揚(yáng)聲器播放出來;
5.1 PCM播放(方法一):

從沙盒拿到PCM 文件后,在終端鍵入如下命令 (記得安裝ffmpeg
例子:ffplay -ar 44100 -ac 1 -f s16le -i /Users/pengchao/Desktop/2022_06_25_20:44:34.pcm

image.png
fplay -ar 44100 -ac 1 -f s16le -i ./201904091310_test.pcm

-ar 表示采樣率

-ac 表示音頻通道數(shù)
    單聲道是 1,Android 中為 AudioFormat.CHANNEL_IN_MONO
    雙聲道是 2,Android 中為 AudioFormat.CHANNEL_IN_STEREO

-f 表示 pcm 格式,sample_fmts + le(小端)或者 be(大端)
    sample_fmts可以通過ffplay -sample_fmts來查詢

-i 表示輸入文件,這里就是 pcm 文件
5.2 PCM播放(方法二)

通過 AudioPCMPlayer 類,對(duì)PCM數(shù)據(jù)進(jìn)行播放,這里AudioPCMPlayer 是一個(gè) 基于 AudioQueue的封裝;有興趣的同學(xué)可以去看源碼;

總結(jié)

源碼地址: 源碼地址 源碼地址: https://github.com/hunter858/OpenGL_Study/AVFoundation/AudiotoolBox-decoder

最后編輯于
?著作權(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),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 什么是PCM?什么是AAC?本篇我們主要介紹通過AudioToolBox 將PCM 編碼成AAC 格式,并通過文件...
    pengxiaochao閱讀 1,227評(píng)論 1 10
  • 前言:以下是有關(guān)直播中視頻編解碼、推拉流等流程解析,僅用于個(gè)人記錄和學(xué)習(xí) 一、視頻編碼 1、為什么編碼? 編碼就是...
    wuyukobe閱讀 4,614評(píng)論 0 8
  • 視頻編解碼:VideoToolbox 關(guān)于H264 H.264是目前很流行的編碼層視頻壓縮格式,目前項(xiàng)目中的協(xié)議層...
    半島夏天閱讀 1,518評(píng)論 1 4
  • 一、流程說明 該項(xiàng)目是包括了音頻的錄制、編碼(AAC)、解碼(PCM)全部過程;是使用AVFoundation、A...
    三國(guó)韓信閱讀 1,224評(píng)論 0 1
  • 項(xiàng)目github完整代碼這是項(xiàng)目包含了AVFoundation音視頻數(shù)據(jù)捕獲,AudioToolBox實(shí)現(xiàn)音頻數(shù)據(jù)...
    HelloBinary閱讀 857評(píng)論 0 2

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