本文將分別通過命令行、編程2種方式進行AAC編碼實戰(zhàn),使用的編碼庫是libfdk_aac。
要求
fdk-aac對輸入的PCM數(shù)據(jù)是有參數(shù)要求的,如果參數(shù)不對,就會出現(xiàn)以下錯誤:
[libfdk_aac @ 0x7fa3db033000] Unable to initialize the encoder: SBR library initialization error
Error initializing output stream 0:0 -- Error while opening encoder for output stream #0:0 - maybe incorrect parameters such as bit_rate, rate, width or height
Conversion failed!
采樣格式
必須是16位整數(shù)PCM。
采樣率
支持的采樣率有(Hz):
- 8000、11025、12000、16000、22050、24000、32000
- 44100、48000、64000、88200、96000
命令行
基本使用
最簡單的用法如下所示:
# pcm -> aac
ffmpeg -ar 44100 -ac 2 -f s16le -i in.pcm -c:a libfdk_aac out.aac
# wav -> aac
# 為了簡化指令,本文后面會盡量使用in.wav取代in.pcm
ffmpeg -i in.wav -c:a libfdk_aac out.aac
-
-ar 44100 -ac 2 -f s16le
- PCM輸入數(shù)據(jù)的參數(shù)
-
-c:a
- 設置音頻編碼器
- c表示codec(編解碼器),a表示audio(音頻)
- 等價寫法
- -codec:a
- -acodec
- 需要注意的是:這個參數(shù)要寫在aac文件那邊,也就是屬于輸出參數(shù)
默認生成的aac文件是LC規(guī)格的。
ffprobe out.aac
# 輸出結果如下所示
Audio: aac (LC), 44100 Hz, stereo, fltp, 120 kb/s
常用參數(shù)
-
-b:a
- 設置輸出比特率
- 比如-b:a 96k
ffmpeg -i in.wav -c:a libfdk_aac -b:a 96k out.aac
-
-profile:a
- 設置輸出規(guī)格
- 取值有:
- aac_low:Low Complexity AAC (LC),默認值
- aac_he:High Efficiency AAC (HE-AAC)
- aac_he_v2:High Efficiency AAC version 2 (HE-AACv2)
- aac_ld:Low Delay AAC (LD)
- aac_eld:Enhanced Low Delay AAC (ELD)
- 一旦設置了輸出規(guī)格,會自動設置一個合適的輸出比特率
- 也可以用過-b:a自行設置輸出比特率
ffmpeg -i in.wav -c:a libfdk_aac -profile:a aac_he_v2 -b:a 32k out.aac
-
-vbr
- 開啟VBR模式(Variable Bit Rate,可變比特率)
- 如果開啟了VBR模式,-b:a選項將會被忽略,但-profile:a選項仍然有效
- 取值范圍是0 ~ 5
- 0:默認值,關閉VBR模式,開啟CBR模式(Constant Bit Rate,固定比特率)
- 1:質(zhì)量最低(但是音質(zhì)仍舊很棒)
- 5:質(zhì)量最高
?關注+私信扣1,免費分享2022最新最全學習提升資料包,資料內(nèi)容包括《Andoird音視頻開發(fā)必備手冊+音視頻最新學習視頻+大廠面試真題+2022最新學習路線圖》(C/C++,Linux,F(xiàn)Fmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)等等?
?
AOT是Audio Object Type的簡稱。
ffmpeg -i in.wav -c:a libfdk_aac -vbr 1 out.aac
文件格式
AAC編碼的文件擴展名主要有3種:aac、m4a、mp4。
# m4a
ffmpeg -i in.wav -c:a libfdk_aac out.m4a
# mp4
ffmpeg -i in.wav -c:a libfdk_aac out.mp4
編程
需要用到2個庫:
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
}
// 錯誤處理
#define ERROR_BUF(ret) \
char errbuf[1024]; \
av_strerror(ret, errbuf, sizeof (errbuf));
函數(shù)聲明
我們最終會將PCM轉(zhuǎn)AAC的操作封裝到一個函數(shù)中。
extern "C" {
#include <libavcodec/avcodec.h>
}
// 參數(shù)
typedef struct {
const char *filename;
int sampleRate;
AVSampleFormat sampleFmt;
int chLayout;
} AudioEncodeSpec;
class FFmpegs {
public:
FFmpegs();
static void aacEncode(AudioEncodeSpec &in,
const char *outFilename);
};
函數(shù)實現(xiàn)
變量定義
// 編碼器
AVCodec *codec = nullptr;
// 上下文
AVCodecContext *ctx = nullptr;
// 用來存放編碼前的數(shù)據(jù)
AVFrame *frame = nullptr;
// 用來存放編碼后的數(shù)據(jù)
AVPacket *pkt = nullptr;
// 返回結果
int ret = 0;
// 輸入文件
QFile inFile(in.filename);
// 輸出文件
QFile outFile(outFilename);
獲取編碼器
下面的代碼可以獲取FFmpeg默認的AAC編碼器(并不是libfdk_aac)。
AVCodec *codec1 = avcodec_find_encoder(AV_CODEC_ID_AAC);
AVCodec *codec2 = avcodec_find_encoder_by_name("aac");
// true
qDebug() << (codec1 == codec2);
// aac
qDebug() << codec1->name;
不過我們最終要獲取的是libfdk_aac。
// 獲取fdk-aac編碼器
codec = avcodec_find_encoder_by_name("libfdk_aac");
if (!codec) {
qDebug() << "encoder libfdk_aac not found";
return;
}
檢查采樣格式
接下來檢查編碼器是否支持當前的采樣格式。
// 檢查采樣格式
if (!check_sample_fmt(codec, in.sampleFmt)) {
qDebug() << "Encoder does not support sample format"
<< av_get_sample_fmt_name(in.sampleFmt);
return;
}
檢查函數(shù)check_sample_fmt的實現(xiàn)如下所示。
// 檢查編碼器codec是否支持采樣格式sample_fmt
static int check_sample_fmt(const AVCodec *codec,
enum AVSampleFormat sample_fmt) {
const enum AVSampleFormat *p = codec->sample_fmts;
while (*p != AV_SAMPLE_FMT_NONE) {
if (*p == sample_fmt) return 1;
p++;
}
return 0;
}
創(chuàng)建上下文
avcodec_alloc_context3后面的3說明這已經(jīng)是第3版API,取代了此前的avcodec_alloc_context和avcodec_alloc_context2。
// 創(chuàng)建上下文
ctx = avcodec_alloc_context3(codec);
if (!ctx) {
qDebug() << "avcodec_alloc_context3 error";
return;
}
// 設置參數(shù)
ctx->sample_fmt = in.sampleFmt;
ctx->sample_rate = in.sampleRate;
ctx->channel_layout = in.chLayout;
// 比特率
ctx->bit_rate = 32000;
// 規(guī)格
ctx->profile = FF_PROFILE_AAC_HE_V2;
打開編碼器
// 打開編碼器
ret = avcodec_open2(ctx, codec, nullptr);
if (ret < 0) {
ERROR_BUF(ret);
qDebug() << "avcodec_open2 error" << errbuf;
goto end;
}
如果是想設置一些libfdk_aac特有的參數(shù)(比如vbr),可以通過options參數(shù)傳遞。
AVDictionary *options = nullptr;
av_dict_set(&options, "vbr", "1", 0);
ret = avcodec_open2(ctx, codec, &options);
創(chuàng)建AVFrame
AVFrame用來存放編碼前的數(shù)據(jù)。
// 創(chuàng)建AVFrame
frame = av_frame_alloc();
if (!frame) {
qDebug() << "av_frame_alloc error";
goto end;
}
// 樣本幀數(shù)量(由frame_size決定)
frame->nb_samples = ctx->frame_size;
// 采樣格式
frame->format = ctx->sample_fmt;
// 聲道布局
frame->channel_layout = ctx->channel_layout;
// 創(chuàng)建AVFrame內(nèi)部的緩沖區(qū)
ret = av_frame_get_buffer(frame, 0);
if (ret < 0) {
ERROR_BUF(ret);
qDebug() << "av_frame_get_buffer error" << errbuf;
goto end;
}
創(chuàng)建AVPacket
// 創(chuàng)建AVPacket
pkt = av_packet_alloc();
if (!pkt) {
qDebug() << "av_packet_alloc error";
goto end;
}
打開文件
// 打開文件
if (!inFile.open(QFile::ReadOnly)) {
qDebug() << "file open error" << in.filename;
goto end;
}
if (!outFile.open(QFile::WriteOnly)) {
qDebug() << "file open error" << outFilename;
goto end;
}
開始編碼
// frame->linesize[0]是緩沖區(qū)的大小
// 讀取文件數(shù)據(jù)
while ((ret = inFile.read((char *) frame->data[0],
frame->linesize[0])) > 0) {
// 最后一次讀取文件數(shù)據(jù)時,有可能并沒有填滿frame的緩沖區(qū)
if (ret < frame->linesize[0]) {
// 聲道數(shù)
int chs = av_get_channel_layout_nb_channels(frame->channel_layout);
// 每個樣本的大小
int bytes = av_get_bytes_per_sample((AVSampleFormat) frame->format);
// 改為真正有效的樣本幀數(shù)量
frame->nb_samples = ret / (chs * bytes);
}
// 編碼
if (encode(ctx, frame, pkt, outFile) < 0) {
goto end;
}
}
// flush編碼器
encode(ctx, nullptr, pkt, outFile);
encode函數(shù)專門用來進行編碼,它的實現(xiàn)如下所示。文章來源:http://www.zghlxwxcb.cn/news/detail-434902.html
// 音頻編碼
// 返回負數(shù):中途出現(xiàn)了錯誤
// 返回0:編碼操作正常完成
static int encode(AVCodecContext *ctx,
AVFrame *frame,
AVPacket *pkt,
QFile &outFile) {
// 發(fā)送數(shù)據(jù)到編碼器
int ret = avcodec_send_frame(ctx, frame);
if (ret < 0) {
ERROR_BUF(ret);
qDebug() << "avcodec_send_frame error" << errbuf;
return ret;
}
while (true) {
// 從編碼器中獲取編碼后的數(shù)據(jù)
ret = avcodec_receive_packet(ctx, pkt);
// packet中已經(jīng)沒有數(shù)據(jù),需要重新發(fā)送數(shù)據(jù)到編碼器(send frame)
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
return 0;
} else if (ret < 0) { // 出現(xiàn)了其他錯誤
ERROR_BUF(ret);
qDebug() << "avcodec_receive_packet error" << errbuf;
return ret;
}
// 將編碼后的數(shù)據(jù)寫入文件
outFile.write((char *) pkt->data, pkt->size);
// 釋放資源
av_packet_unref(pkt);
}
return 0;
}
資源回收文章來源地址http://www.zghlxwxcb.cn/news/detail-434902.html
end:
// 關閉文件
inFile.close();
outFile.close();
// 釋放資源
av_frame_free(&frame);
av_packet_free(&pkt);
avcodec_free_context(&ctx);
函數(shù)調(diào)用
AudioEncodeSpec in;
in.filename = "F:/in.pcm";
in.sampleRate = 44100;
in.sampleFmt = AV_SAMPLE_FMT_S16;
in.chLayout = AV_CH_LAYOUT_STEREO;
FFmpegs::aacEncode(in, "F:/out.aac");
到了這里,關于【秒懂音視頻開發(fā)】15_AAC編碼實戰(zhàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!