男人与女人做视频网站,计算机论文,wordpress 永久链接,wordpress 安卓 管理前言 测试环境#xff1a;
ffmpeg的4.3.2自行编译版本windows环境qt5.12 AAC编码是MP3格式的后继产品#xff0c;通常在相同的比特率下可以获得比MP3更高的声音质量#xff0c;是iPhone、iPod、iPad、iTunes的标准音频格式。
AAC相较于MP3的改进包含#xff1a;
更多的采…前言 测试环境
ffmpeg的4.3.2自行编译版本windows环境qt5.12 AAC编码是MP3格式的后继产品通常在相同的比特率下可以获得比MP3更高的声音质量是iPhone、iPod、iPad、iTunes的标准音频格式。
AAC相较于MP3的改进包含
更多的采样率选择8kHz ~ 96kHzMP3为16kHz ~ 48kHz更高的声道数上限48个MP3在MPEG-1模式下为最多双声道MPEG-2模式下5.1声道改进的压缩功能以较小的文件大小提供更高的质量改进的解码效率需要较少的处理能力进行解码… AAC编码为了使用不同场景的需求设计了很多规格
MPEG-2 AAC LC低复杂度规格Low ComplexityMPEG-2 AAC Main主规格MPEG-2 AAC SSR可变采样率规格Scaleable Sample RateMPEG-4 AAC LC低复杂度规格Low Complexity 现在的手机比较常见的MP4文件中的音频部分使用了该规格 MPEG-4 AAC Main主规格MPEG-4 AAC SSR可变采样率规格Scaleable Sample RateMPEG-4 AAC LTP长时期预测规格Long Term PredicitionMPEG-4 AAC LD低延迟规格Low DelayMPEG-4 AAC HE高效率规格High Efficiency
众多规格中只需关注LC和HE pcm与aac的转换需要AAC编解码器如下列举几种常用的AAC编解码器
Nero AAC 支持LC/HE规格目前已经停止开发维护 FFmpeg AAC 支持LC规格FFmpeg官方内置的AAC编解码器在libavcodec库中 编解码器名字叫做aac在开发过程中通过这个名字找到编解码器 FAACFreeware Advanced Audio Coder 支持LC规格可以集成到FFmpeg的libavcodec中 编解码器名字叫做libfaac在开发过程中通过这个名字找到编解码器最后调用FAAC库的功能 从2016年开始FFmpeg已经移除了对FAAC的支持 Fraunhofer FDK AAC 支持LC/HE规格目前质量最高的AAC编解码器可以集成到FFmpeg的libavcodec中 编解码器名字叫做libfdk_aac在开发过程中通过这个名字找到编解码器最后调用FDK AAC库的功能
编码质量排名Fraunhofer FDK AAC FFmpeg AAC FAAC。
由于libfdk_aac最好但是网上下载好的ffmpeg编译好的版本不带libfdk_aac编解码器。所以我们只能自行编译ffmpeg。
如下命令可以查看FFmpeg目前集成的AAC编解码器
ffmpeg -codecs | findstr aac自己手动编译FFmpeg源码将libfdk_aac集成到FFmpeg中这种方式最好但在windows环境下较为麻烦。
因为编译源码需要在类Unix系统上的Linux、Mac等默认无法直接用在Windows上。所以必须先用MSYS2软件在Windows上模拟出Linux环境然后再在其中用MinGW软件对FFmpeg进行编译。
链接windows下msys2编译64位的ffmpeg源码 编译好源码后需要把.pro文件配置成新编译的源码。
fdk-aac对需要编解码的pcm音频有一定的格式要求
采样格式必须为16位整数PCM采样率只支持8000、11025、12000、16000、22050、24000、32000、44100、48000、64000、88200、96000
命令行将pcm和wav文件编码成aac音频
# pcm - aac
ffmpeg -ar 44100 -ac 2 -f s16le -i in.pcm -c:a libfdk_aac out.aac
-ar 44100 -ac 2 -f s16le --PCM输入数据的参数
-c:a 设置音频编码器c表示codec编解码器a表示audio音频。 等价写法 -codec:a或-acodec# wav - aac
ffmpeg -i in.wav -c:a libfdk_aac out.aac 默认生成的aac文件是LC规格的。aac文件比之前的pcm文件小了很多很多。 aac的缩写还可以是m4a和mp4。虽然现在都只认为mp4是视频文件 首先是pcm编码为aac
完整代码
AacEncodeThread.h
#ifndef AACENCODETHREAD_H
#define AACENCODETHREAD_H#include QFile
#include QObject
#include QThreadextern C {
#include libavformat/avformat.h
}typedef struct {const char *filename;int sampleRate;AVSampleFormat sampleFmt;int chLayout;
} AudioEncodeSpec;class AacEncodeThread : public QThread
{Q_OBJECT
public:explicit AacEncodeThread(QObject *parent nullptr);~AacEncodeThread();static int check_sample_fmt(const AVCodec *codec,enum AVSampleFormat sample_fmt);static int encode(AVCodecContext *ctx,AVFrame *frame,AVPacket *pkt,QFile outFile);static void aacEncode(AudioEncodeSpec in,const char *outFilename);signals:// QThread interface
protected:virtual void run() override;
};#endif // AACENCODETHREAD_HAacEncodeThread.cpp
#include aacencodethread.h#include QDebug
#include QFileextern C {
#include libavcodec/avcodec.h
#include libavutil/avutil.h
}#define ERROR_BUF(ret) \char errbuf[1024]; \av_strerror(ret, errbuf, sizeof (errbuf));AacEncodeThread::AacEncodeThread(QObject *parent) : QThread(parent)
{// 当监听到线程结束时finished就调用deleteLater回收内存connect(this, AacEncodeThread::finished,this, AacEncodeThread::deleteLater);
}AacEncodeThread::~AacEncodeThread()
{// 断开所有的连接disconnect();// 内存回收之前正常结束线程requestInterruption();// 安全退出quit();wait();qDebug() this 析构内存被回收;
}// 检查采样格式
int AacEncodeThread::check_sample_fmt(const AVCodec *codec,enum AVSampleFormat sample_fmt) {const enum AVSampleFormat *p codec-sample_fmts;while (*p ! AV_SAMPLE_FMT_NONE) {
// qDebug() av_get_sample_fmt_name(*p);if (*p sample_fmt) return 1;p;}return 0;
}// 音频编码
// 返回负数中途出现了错误
// 返回0编码操作正常完成
int AacEncodeThread::AacEncodeThread::encode(AVCodecContext *ctx,AVFrame *frame,AVPacket *pkt,QFile outFile) {// 发送数据到编码器int ret avcodec_send_frame(ctx, frame);if (ret 0) {ERROR_BUF(ret);qDebug() avcodec_send_frame error errbuf;return ret;}// 不断从编码器中取出编码后的数据// while (ret 0)while (true) {ret avcodec_receive_packet(ctx, pkt);if (ret AVERROR(EAGAIN) || ret AVERROR_EOF) {// 继续读取数据到frame然后送到编码器return 0;} else if (ret 0) { // 其他错误return ret;}// 成功从编码器拿到编码后的数据// 将编码后的数据写入文件outFile.write((char *) pkt-data, pkt-size);// 释放pkt内部的资源av_packet_unref(pkt);}
}void AacEncodeThread::aacEncode(AudioEncodeSpec in, const char *outFilename)
{// 文件QFile inFile(in.filename);QFile outFile(outFilename);// 返回结果int ret 0;// 编码器AVCodec *codec nullptr;// 编码上下文AVCodecContext *ctx nullptr;// 存放编码前的数据pcmAVFrame *frame nullptr;// 存放编码后的数据aacAVPacket *pkt nullptr;// 获取编码器
// codec avcodec_find_encoder(AV_CODEC_ID_AAC);codec avcodec_find_encoder_by_name(libfdk_aac);if (!codec) {qDebug() encoder not found;return;}// libfdk_aac对输入数据的要求采样格式必须是16位整数// 检查输入数据的采样格式if (!check_sample_fmt(codec, in.sampleFmt)) {qDebug() unsupported sample format av_get_sample_fmt_name(in.sampleFmt);return;}// 创建编码上下文ctx avcodec_alloc_context3(codec);if (!ctx) {qDebug() avcodec_alloc_context3 error;return;}// 设置PCM参数ctx-sample_rate in.sampleRate;ctx-sample_fmt in.sampleFmt;ctx-channel_layout in.chLayout;// 比特率ctx-bit_rate 32000;// 规格ctx-profile FF_PROFILE_AAC_HE_V2;// 打开编码器
// AVDictionary *options nullptr;
// av_dict_set(options, vbr, 5, 0);
// ret avcodec_open2(ctx, codec, options);ret avcodec_open2(ctx, codec, nullptr);if (ret 0) {ERROR_BUF(ret);qDebug() avcodec_open2 error errbuf;goto end;}// 创建AVFrameframe av_frame_alloc();if (!frame) {qDebug() av_frame_alloc error;goto end;}// frame缓冲区中的样本帧数量由ctx-frame_size决定frame-nb_samples ctx-frame_size;frame-format ctx-sample_fmt;frame-channel_layout ctx-channel_layout;// 利用nb_samples、format、channel_layout创建缓冲区ret av_frame_get_buffer(frame, 0);if (ret 0) {ERROR_BUF(ret);qDebug() av_frame_get_buffer error errbuf;goto end;}// 创建AVPacketpkt 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中while ((ret inFile.read((char *) frame-data[0],frame-linesize[0])) 0) {// 从文件中读取的数据不足以填满frame缓冲区if (ret frame-linesize[0]) {int bytes av_get_bytes_per_sample((AVSampleFormat) frame-format);int ch av_get_channel_layout_nb_channels(frame-channel_layout);// 设置真正有效的样本帧数量// 防止编码器编码了一些冗余数据frame-nb_samples ret / (bytes * ch);}// 进行编码if (encode(ctx, frame, pkt, outFile) 0) {goto end;}}// 刷新缓冲区encode(ctx, nullptr, pkt, outFile);end:// 关闭文件inFile.close();outFile.close();// 释放资源av_frame_free(frame);av_packet_free(pkt);avcodec_free_context(ctx);qDebug() 线程正常结束;
}void AacEncodeThread::run()
{AudioEncodeSpec in;in.filename E:/media/test.pcm;in.sampleRate 44100;in.sampleFmt AV_SAMPLE_FMT_S16;in.chLayout AV_CH_LAYOUT_STEREO;aacEncode(in, E:/media/test.aac);
}线程调用
void MainWindow::on_pushButton_aac_encode_clicked()
{m_pAacEncodeThreadnew AacEncodeThread(this);m_pAacEncodeThread-start();
}注意.h文件中提前声明了以下全局变量
AacEncodeThread *m_pAacEncodeThreadnullptr;下面是aac解码成pcm
完整代码
AacDecodeThread.h
#ifndef AACDECODETHREAD_H
#define AACDECODETHREAD_H#include QFile
#include QObject
#include QThreadextern C {
#include libavformat/avformat.h
}typedef struct {const char *filename;int sampleRate;AVSampleFormat sampleFmt;int chLayout;
} AudioDecodeSpec;class AacDecodeThread : public QThread
{Q_OBJECT
public:explicit AacDecodeThread(QObject *parent nullptr);~AacDecodeThread();static int decode(AVCodecContext *ctx,AVPacket *pkt,AVFrame *frame,QFile outFile);static void aacDecode(const char *inFilename,AudioDecodeSpec out);signals:// QThread interface
protected:virtual void run() override;
};#endif // AACDECODETHREAD_HAacDecodeThread.cpp
#include aacdecodethread.h#include QDebugextern C {
#include libavcodec/avcodec.h
#include libavutil/avutil.h
}#define ERROR_BUF(ret) \char errbuf[1024]; \av_strerror(ret, errbuf, sizeof (errbuf));// 输入缓冲区的大小
#define IN_DATA_SIZE 20480
// 需要再次读取输入文件数据的阈值
#define REFILL_THRESH 4096AacDecodeThread::AacDecodeThread(QObject *parent) : QThread(parent)
{// 当监听到线程结束时finished就调用deleteLater回收内存connect(this, AacDecodeThread::finished,this, AacDecodeThread::deleteLater);
}AacDecodeThread::~AacDecodeThread()
{// 断开所有的连接disconnect();// 内存回收之前正常结束线程requestInterruption();// 安全退出quit();wait();qDebug() this 析构内存被回收;
}int AacDecodeThread::decode(AVCodecContext *ctx,AVPacket *pkt,AVFrame *frame,QFile outFile) {// 发送压缩数据到解码器int ret avcodec_send_packet(ctx, pkt);if (ret 0) {ERROR_BUF(ret);qDebug() avcodec_send_packet error errbuf;return ret;}while (true) {// 获取解码后的数据ret avcodec_receive_frame(ctx, frame);if (ret AVERROR(EAGAIN) || ret AVERROR_EOF) {return 0;} else if (ret 0) {ERROR_BUF(ret);qDebug() avcodec_receive_frame error errbuf;return ret;}// for (int i 0; i frame-channels; i) {
// frame-data[i];
// }// 将解码后的数据写入文件outFile.write((char *) frame-data[0], frame-linesize[0]);}
}void AacDecodeThread::aacDecode(const char *inFilename, AudioDecodeSpec out)
{// 返回结果int ret 0;// 用来存放读取的输入文件数据aac// 加上AV_INPUT_BUFFER_PADDING_SIZE是为了防止某些优化过的reader一次性读取过多导致越界char inDataArray[IN_DATA_SIZE AV_INPUT_BUFFER_PADDING_SIZE];char *inData inDataArray;// 每次从输入文件中读取的长度aacint inLen;// 是否已经读取到了输入文件的尾部int inEnd 0;// 文件QFile inFile(inFilename);QFile outFile(out.filename);// 解码器AVCodec *codec nullptr;// 上下文AVCodecContext *ctx nullptr;// 解析器上下文AVCodecParserContext *parserCtx nullptr;// 存放解码前的数据(aac)AVPacket *pkt nullptr;// 存放解码后的数据(pcm)AVFrame *frame nullptr;// 获取解码器codec avcodec_find_decoder_by_name(libfdk_aac);if (!codec) {qDebug() decoder not found;return;}// 初始化解析器上下文parserCtx av_parser_init(codec-id);if (!parserCtx) {qDebug() av_parser_init error;return;}// 创建上下文ctx avcodec_alloc_context3(codec);if (!ctx) {qDebug() avcodec_alloc_context3 error;goto end;}// 创建AVPacketpkt av_packet_alloc();if (!pkt) {qDebug() av_packet_alloc error;goto end;}// 创建AVFrameframe av_frame_alloc();if (!frame) {qDebug() av_frame_alloc error;goto end;}// 打开解码器ret avcodec_open2(ctx, codec, nullptr);if (ret 0) {ERROR_BUF(ret);qDebug() avcodec_open2 error errbuf;goto end;}// 打开文件if (!inFile.open(QFile::ReadOnly)) {qDebug() file open error: inFilename;goto end;}if (!outFile.open(QFile::WriteOnly)) {qDebug() file open error: out.filename;goto end;}while ((inLen inFile.read(inDataArray, IN_DATA_SIZE)) 0) {inData inDataArray;while (inLen 0) {// 经过解析器解析// 内部调用的核心逻辑是ff_aac_ac3_parseret av_parser_parse2(parserCtx, ctx,pkt-data, pkt-size,(uint8_t *) inData, inLen,AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);if (ret 0) {ERROR_BUF(ret);qDebug() av_parser_parse2 error errbuf;goto end;}// 跳过已经解析过的数据inData ret;// 减去已经解析过的数据大小inLen - ret;// 解码if (pkt-size 0 decode(ctx, pkt, frame, outFile) 0) {goto end;}}}decode(ctx, nullptr, frame, outFile);// 赋值输出参数out.sampleRate ctx-sample_rate;out.sampleFmt ctx-sample_fmt;out.chLayout ctx-channel_layout;end:inFile.close();outFile.close();av_packet_free(pkt);av_frame_free(frame);av_parser_close(parserCtx);avcodec_free_context(ctx);
}void AacDecodeThread::run()
{AudioDecodeSpec out;out.filename E:/media/test.pcm;aacDecode(E:/media/test.aac, out);qDebug() 采样率 out.sampleRate;qDebug() 采样格式 av_get_sample_fmt_name(out.sampleFmt);qDebug() 声道数 av_get_channel_layout_nb_channels(out.chLayout);
}注意本文为个人记录新手照搬可能会出现各种问题请谨慎使用 码字不易如果这篇博客对你有帮助麻烦点赞收藏非常感谢有不对的地方