C++使用fdk-aac實(shí)現(xiàn)將音頻PCM編碼成aac
前言
mp4的音頻流通常是aac編碼,我們做音視頻采集的時(shí)候就需要將,采集的音頻PCM編碼成aac,然后再打包進(jìn)mp4,而aac編解碼庫(kù)中fdk-aac是性能較好的,使用方式也比較簡(jiǎn)單。在C++項(xiàng)目中使用,通常再做一層封裝,提高模塊的復(fù)用性和替換性。本文將展示C++將fdk-aac封裝成一個(gè)編碼對(duì)象,以及使用示例。
一、接口設(shè)計(jì)
由于我前面的文章已經(jīng)給出了fdk-aac的使用示例:《Windows上使用vs編譯fdk-aac》,所以在這里直接設(shè)計(jì)對(duì)象的接口。
/// <summary>
/// aac編碼對(duì)象
/// 所有方法采用異常處理進(jìn)行錯(cuò)誤提示,使用時(shí)注意使用try-catch,拋出對(duì)象為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è)置封裝類(lèi)型
/// </summary>
/// <param name="value">封裝類(lèi)型</param>
void SetTransportType(TransportType value);
/// <summary>
/// 獲取封裝類(lèi)型
/// </summary>
/// <returns>封裝類(lèi)型</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>
/// 寫(xiě)入音頻數(shù)據(jù)
/// 注意數(shù)據(jù)長(zhǎng)度不能過(guò)大,否則導(dǎo)致音頻不正常,建議2048。
/// </summary>
/// <param name="data">音頻數(shù)據(jù)</param>
/// <param name="dataLength">數(shù)據(jù)長(zhǎng)度</param>
void Write(unsigned char* data, int dataLength);
}二、使用示例
將wav文件編碼成acc文件。其中WavFileReader對(duì)象參考:《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文件讀取對(duì)象
AC::WavFileReader wr;
//aac編碼對(duì)象
//其中參數(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);
//注冊(cè)編碼數(shù)據(jù)到達(dá)事件
fe.EncodedDataArrived = [&](auto s, auto e) {
//將得到的編碼數(shù)據(jù)寫(xiě)入文件
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");
}
//打開(kāi)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ù)寫(xiě)入編碼器
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文件讀取對(duì)象
AC::WavFileReader wr;
//aac編碼對(duì)象
//其中參數(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);
//注冊(cè)編碼數(shù)據(jù)到達(dá)事件
fe.EncodedDataArrived = [&](auto s, auto e) {
//將得到的編碼數(shù)據(jù)寫(xiě)入文件
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");
}
//打開(kāi)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ù)寫(xiě)入編碼器
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ì)成對(duì)象可以靈活的使用在項(xiàng)目中,尤其需要使用設(shè)計(jì)模式,比如原型模式:一個(gè)aac編碼器接口可以有不同的實(shí)現(xiàn)fdk-aac只是其中一種實(shí)現(xiàn),寫(xiě)好的代碼可以隨意切換編碼器對(duì)象。或者策略模式,設(shè)計(jì)一個(gè)編碼器接口,調(diào)用時(shí)根據(jù)編碼格式選擇合適的編碼器??偟膩?lái)說(shuō),這個(gè)對(duì)象的設(shè)計(jì)和實(shí)現(xiàn)不算特別難,就是在細(xì)節(jié)的地方需要注意,比如錯(cuò)誤提示,邊界值處理等。
以上就是C++使用fdk-aac實(shí)現(xiàn)將音頻PCM編碼成aac的詳細(xì)內(nèi)容,更多關(guān)于C++音頻編碼的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語(yǔ)言中用于產(chǎn)生隨機(jī)數(shù)的函數(shù)使用方法總結(jié)
這篇文章主要介紹了C語(yǔ)言中用于產(chǎn)生隨機(jī)數(shù)的函數(shù)使用方法總結(jié),分別介紹了rand()函數(shù)和srand()函數(shù)以及封裝出的arc4random()函數(shù),需要的朋友可以參考下2016-05-05
C++ 標(biāo)準(zhǔn)庫(kù)中的 <algorithm> 頭文件算法操作總結(jié)
C++ 標(biāo)準(zhǔn)庫(kù)中的 <algorithm> 頭文件提供了大量有用的算法,主要用于操作容器(如 vector, list, array 等),這些算法通常通過(guò)迭代器來(lái)操作容器元素,本文給大家介紹C++ 標(biāo)準(zhǔn)庫(kù)中的 <algorithm> 頭文件算法總結(jié),感興趣的朋友一起看看吧2025-04-04
C++實(shí)現(xiàn)圖的遍歷算法(DFS,BFS)的示例代碼
本文給大家?guī)?lái)的是圖遍歷的算法,DFS(深度優(yōu)先遍歷),BFS(廣度優(yōu)先遍歷)。這兩個(gè)算法是比較重要和常用的算法,但是在圖中的實(shí)現(xiàn)只是最基本的操作,快跟隨小編一起學(xué)習(xí)一下吧2022-07-07
詳解C語(yǔ)言中結(jié)構(gòu)體(struct)的用法
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言中結(jié)構(gòu)體(struct)的用法,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C語(yǔ)言有一定幫助,需要的可以參考一下2022-08-08
C/C++?QT實(shí)現(xiàn)解析JSON文件的示例代碼
JSON是一種輕量級(jí)的數(shù)據(jù)交換格式,它是基于ECMAScript的一個(gè)子集,使用完全獨(dú)立于編程語(yǔ)言的文本格式來(lái)存儲(chǔ)和表示數(shù)據(jù)。這篇文章主要介紹了QT實(shí)現(xiàn)解析JSON文件的示例代碼,需要的可以參考一下2022-01-01

