C++使用fdk-aac實(shí)現(xiàn)將音頻PCM編碼成aac
前言
mp4的音頻流通常是aac編碼,我們做音視頻采集的時(shí)候就需要將,采集的音頻PCM編碼成aac,然后再打包進(jìn)mp4,而aac編解碼庫中fdk-aac是性能較好的,使用方式也比較簡單。在C++項(xiàng)目中使用,通常再做一層封裝,提高模塊的復(fù)用性和替換性。本文將展示C++將fdk-aac封裝成一個(gè)編碼對象,以及使用示例。
一、接口設(shè)計(jì)
由于我前面的文章已經(jīng)給出了fdk-aac的使用示例:《Windows上使用vs編譯fdk-aac》,所以在這里直接設(shè)計(jì)對象的接口。
/// <summary> /// aac編碼對象 /// 所有方法采用異常處理進(jìn)行錯(cuò)誤提示,使用時(shí)注意使用try-catch,拋出對象為std::exception。 /// 構(gòu)造方法參數(shù)的聲道數(shù)是最大緩存數(shù)量,并非實(shí)際聲道數(shù),實(shí)際聲道數(shù)由SetChannelMode方法確定,本版本需要確保SetChannelMode的聲道數(shù)與構(gòu)造方法參數(shù)的聲道數(shù)一致。 /// </summary> class FdkaacEncoder { public: /// <summary> /// 編碼數(shù)據(jù)到達(dá)事件參數(shù) /// </summary> class EncodedDataArrivedEventArgs { public: /// <summary> /// 音頻幀 /// </summary> AudioCodingFrame Frame; }; /// <summary> /// 編碼數(shù)據(jù)到達(dá)事件 /// </summary> std::function<void(void*,EncodedDataArrivedEventArgs*)> EncodedDataArrived; /// <summary> /// 構(gòu)造方法 /// </summary> /// <param name="">編碼參數(shù)</param> FdkaacEncoder(SoundFormat format); /// <summary> /// 析構(gòu)方法 /// </summary> ~FdkaacEncoder(); /// <summary> /// 獲取聲音格式 /// </summary> /// <returns>聲音格式</returns> SoundFormat GetSoundFormat(); /// <summary> /// 設(shè)置比特率 /// </summary> /// <param name="value">比特率,單位byte</param> void SetBitrate(int value); /// <summary> /// 獲取比特率 /// </summary> /// <returns>比特率,單位byte</returns> int GetBitrate(); /// <summary> /// 設(shè)置封裝類型 /// </summary> /// <param name="value">封裝類型</param> void SetTransportType(TransportType value); /// <summary> /// 獲取封裝類型 /// </summary> /// <returns>封裝類型</returns> TransportType GetTransportType(); /// <summary> /// 設(shè)置比特率模式(CBR、VBR) /// </summary> /// <param name="value">比特率模式</param> void SetBitrateMode(BitrateModde value); /// <summary> /// 獲取比特率模式(CBR、VBR) /// </summary> /// <returns>比特率模式</returns> BitrateModde GetBitrateMode(); /// <summary> /// 設(shè)置聲道順序 /// </summary> /// <param name="value">聲道順序</param> void SetChannelOrder(ChannelOrder value); /// <summary> /// 獲取聲道順序 /// </summary> /// <returns>聲道順序</returns> ChannelOrder GetChannelOrder(); /// <summary> /// 設(shè)置聲道模式,聲道數(shù)需要小于等于構(gòu)造方法設(shè)置的聲道數(shù)。 /// </summary> /// <param name="value">聲道模式</param> void SetChannelMode(ChannelMode value); /// <summary> /// 獲取聲道模式 /// </summary> /// <returns></returns> ChannelMode GetChannelMode(); /// <summary> /// 設(shè)置頻段復(fù)制模式 /// </summary> /// <param name="value">頻段復(fù)制模式</param> void SetSbrMode(SbrMode value); /// <summary> /// 獲取頻段復(fù)制模式 /// </summary> /// <returns>頻段復(fù)制模式</returns> SbrMode GetSbrMode(); /// <summary> /// 設(shè)置AOT /// </summary> /// <param name="">AOT</param> void SetAudioObjectType(AudioObjectType); /// <summary> /// 獲取AOT /// </summary> /// <returns>AOT</returns> AudioObjectType GetAudioObjectType(); /// <summary> /// 寫入音頻數(shù)據(jù) /// 注意數(shù)據(jù)長度不能過大,否則導(dǎo)致音頻不正常,建議2048。 /// </summary> /// <param name="data">音頻數(shù)據(jù)</param> /// <param name="dataLength">數(shù)據(jù)長度</param> void Write(unsigned char* data, int dataLength); }
二、使用示例
將wav文件編碼成acc文件。其中WavFileReader對象參考:《C++ 讀取wav文件中的PCM數(shù)據(jù)》
1.雙聲道
#include<stdio.h> #include<exception> #include"WavFileReader.h" #include"FdkaacEncoder.h" void main(int argc, char* argv[]) { FILE* f=nullptr; try { //wav文件讀取對象 AC::WavFileReader wr; //aac編碼對象 //其中參數(shù)的聲道數(shù)是最大緩存數(shù)量,并非實(shí)際聲道數(shù),實(shí)際聲道數(shù)由SetChannelMode方法確定,本版本需要確保SetChannelMode的聲道數(shù)與構(gòu)造方法參數(shù)的聲道數(shù)一致。 AC::FdkaacEncoder fe({ 2 ,44100 ,16 }); unsigned char buf[2048]; //設(shè)置參數(shù) fe.SetSbrMode(AC::SbrMode_Disable); fe.SetAudioObjectType(AC::AudioObjectType::AUDIOOBJECTTYPE_AAC_LC); fe.SetBitrate(128 * 1024); fe.SetChannelMode(AC::ChannelMode::CHANNELMODE_2); fe.SetChannelOrder(AC::CHANNELORDER_WAV); fe.SetBitrateMode(AC::BitrateModde::BITRATEMODDE_CBR); fe.SetTransportType(AC::TRANSPORTTYPE_MP4_ADTS); //注冊編碼數(shù)據(jù)到達(dá)事件 fe.EncodedDataArrived = [&](auto s, auto e) { //將得到的編碼數(shù)據(jù)寫入文件 fwrite(e->Frame.Data, 1, e->Frame.DataLength, f); }; //創(chuàng)建aac文件 f = fopen("test_music.aac", "wb+"); if (!f) { throw std::exception("create aac file fail,create name:test_music.aac"); } //打開wav文件 if (!wr.OpenWavFile("test_music.wav")) { throw std::exception("open wav file fail,filename:test_music.wav"); } while (1) { //讀取wav中的pcm數(shù)據(jù) int size = wr.ReadData(buf, 2048); if (size < 1) break; //將pcm數(shù)據(jù)寫入編碼器 fe.Write(buf, size); } printf("encode completed\n"); } catch(std::exception e){ printf("%s\n",e.what()); } catch (...) { printf("unknown errror\n"); } if(f) fclose(f); }
2.單聲道
#include<stdio.h> #include<exception> #include"WavFileReader.h" #include"FdkaacEncoder.h" void main(int argc, char* argv[]) { FILE* f = nullptr; try { //wav文件讀取對象 AC::WavFileReader wr; //aac編碼對象 //其中參數(shù)的聲道數(shù)是最大緩存數(shù)量,并非實(shí)際聲道數(shù),實(shí)際聲道數(shù)由SetChannelMode方法確定,本版本需要確保SetChannelMode的聲道數(shù)與構(gòu)造方法參數(shù)的聲道數(shù)一致。 AC::FdkaacEncoder fe({ 1 ,44100 ,16 }); unsigned char buf[2048]; //設(shè)置參數(shù) fe.SetSbrMode(AC::SbrMode_Disable); fe.SetAudioObjectType(AC::AudioObjectType::AUDIOOBJECTTYPE_AAC_LC); fe.SetBitrate(128 * 1024); fe.SetChannelMode(AC::ChannelMode::CHANNELMODE_1); fe.SetChannelOrder(AC::CHANNELORDER_WAV); fe.SetBitrateMode(AC::BitrateModde::BITRATEMODDE_CBR); fe.SetTransportType(AC::TRANSPORTTYPE_MP4_ADTS); //注冊編碼數(shù)據(jù)到達(dá)事件 fe.EncodedDataArrived = [&](auto s, auto e) { //將得到的編碼數(shù)據(jù)寫入文件 fwrite(e->Frame.Data, 1, e->Frame.DataLength, f); }; //創(chuàng)建aac文件 f = fopen("test_music_1ch441s16b.aac", "wb+"); if (!f) { throw std::exception("create aac file fail,create name:test_music_1ch441s16b.aac"); } //打開wav文件 if (!wr.OpenWavFile("test_music_1ch441s16b.wav")) { throw std::exception("open wav file fail,filename:test_music_1ch441s16b.wav"); } while (1) { //讀取wav中的pcm數(shù)據(jù) int size = wr.ReadData(buf, 2048); if (size < 1) break; //將pcm數(shù)據(jù)寫入編碼器 fe.Write(buf, size); } printf("encode completed\n"); } catch (std::exception e) { printf("%s\n", e.what()); } catch (...) { printf("unknown errror\n"); } if (f) fclose(f); }
總結(jié)
以上就是今天要講的內(nèi)容,將編碼器設(shè)計(jì)成對象可以靈活的使用在項(xiàng)目中,尤其需要使用設(shè)計(jì)模式,比如原型模式:一個(gè)aac編碼器接口可以有不同的實(shí)現(xiàn)fdk-aac只是其中一種實(shí)現(xiàn),寫好的代碼可以隨意切換編碼器對象。或者策略模式,設(shè)計(jì)一個(gè)編碼器接口,調(diào)用時(shí)根據(jù)編碼格式選擇合適的編碼器。總的來說,這個(gè)對象的設(shè)計(jì)和實(shí)現(xiàn)不算特別難,就是在細(xì)節(jié)的地方需要注意,比如錯(cuò)誤提示,邊界值處理等。
以上就是C++使用fdk-aac實(shí)現(xiàn)將音頻PCM編碼成aac的詳細(xì)內(nèi)容,更多關(guān)于C++音頻編碼的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語言中用于產(chǎn)生隨機(jī)數(shù)的函數(shù)使用方法總結(jié)
這篇文章主要介紹了C語言中用于產(chǎn)生隨機(jī)數(shù)的函數(shù)使用方法總結(jié),分別介紹了rand()函數(shù)和srand()函數(shù)以及封裝出的arc4random()函數(shù),需要的朋友可以參考下2016-05-05C++ 標(biāo)準(zhǔn)庫中的 <algorithm> 頭文件算法操作總結(jié)
C++ 標(biāo)準(zhǔn)庫中的 <algorithm> 頭文件提供了大量有用的算法,主要用于操作容器(如 vector, list, array 等),這些算法通常通過迭代器來操作容器元素,本文給大家介紹C++ 標(biāo)準(zhǔn)庫中的 <algorithm> 頭文件算法總結(jié),感興趣的朋友一起看看吧2025-04-04C++實(shí)現(xiàn)圖的遍歷算法(DFS,BFS)的示例代碼
本文給大家?guī)淼氖菆D遍歷的算法,DFS(深度優(yōu)先遍歷),BFS(廣度優(yōu)先遍歷)。這兩個(gè)算法是比較重要和常用的算法,但是在圖中的實(shí)現(xiàn)只是最基本的操作,快跟隨小編一起學(xué)習(xí)一下吧2022-07-07詳解C語言中結(jié)構(gòu)體(struct)的用法
這篇文章主要為大家詳細(xì)介紹了C語言中結(jié)構(gòu)體(struct)的用法,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)C語言有一定幫助,需要的可以參考一下2022-08-08C/C++?QT實(shí)現(xiàn)解析JSON文件的示例代碼
JSON是一種輕量級(jí)的數(shù)據(jù)交換格式,它是基于ECMAScript的一個(gè)子集,使用完全獨(dú)立于編程語言的文本格式來存儲(chǔ)和表示數(shù)據(jù)。這篇文章主要介紹了QT實(shí)現(xiàn)解析JSON文件的示例代碼,需要的可以參考一下2022-01-01