西安网站建设网站制作,本科毕设做网站多少钱,公司简介300字,怎么建设个人网站系列文章目录
FFmpeg音视频开发知识点#xff08;一#xff09; 文章目录 系列文章目录前言一、AAC音频编码1. ffmpeg编译第三方的libfdk_aac2. S16重采样FLTP 二、AAC音频解码总结 前言
该篇讲解一下#xff0c;音频编解码中的难点#xff0c;以及开发过程中遇到问题一 文章目录 系列文章目录前言一、AAC音频编码1. ffmpeg编译第三方的libfdk_aac2. S16重采样FLTP 二、AAC音频解码总结 前言
该篇讲解一下音频编解码中的难点以及开发过程中遇到问题有不对的地方欢迎大佬指正 一、AAC音频编码
在开发音频编解码AAC我使用QAudioInput进行采样但是采样格式只有S16有符号16位最接近AAC的采样我看了下安卓采样的样本长度也是16PS需要和安卓终端通话于是查找并打开编码器 AVCodec* pCodec avcodec_find_encoder(AV_CODEC_ID_AAC);if (pCodec nullptr){//...省略return;}AVCodecContext* pCodecCtx avcodec_alloc_context3(pCodec);if(pCodecCtx NULL){//...省略return;}pCodecCtx-codec_id pCodec-id;pCodecCtx-codec_type AVMEDIA_TYPE_AUDIO;//...省略int iRet avcodec_open2(pCodecCtx, pCodec, NULL);if (iRet 0){//...省略return;}但是会报错忘了是查找还是打开编码器报错了后面一查ffmpeg本身自带的aac并不支持AV_SAMPLE_FMT_S16的有两种方式可以编码S16音频采样
1. ffmpeg编译第三方的libfdk_aac
编译libfdk_aac可以参考我这篇文章Linux部分开源库编译附上我的ffmepg编译的configure配置命令具体如下
sudo ./configure \
--prefix/home/lzy/Project/new_project/libH323Stack_src_1.2.0/bin4 \
--extra-cflags-I/home/lzy/Project/bin/include -Wall -fPIC \ #第三方库的头文件路径
--extra-ldflags-L/home/lzy/Project/bin/lib -ldl \ #第三方库的所在路径
--disable-static \
--enable-shared \
--disable-debug \
--disable-doc \
--disable-ffplay \
--disable-ffprobe \
--disable-symver \
--enable-small \
--enable-gpl \
--enable-nonfree \
--enable-libfdk-aac \
--enable-libx264 \
--enable-libx265 \
--enable-openssl \
--enable-hardcoded-tables \
--enable-avresample \
--enable-decoderh264 \
--enable-decoderhevc \
--enable-decodermjpeg \
--enable-decoderaac \
--enable-encoderlibx264 \
--enable-encoderlibx265 \
--enable-encoderlibfdk_aac \
--enable-encodermjpeg \
--enable-encoderpcm_s16le \
--enable-decoderpcm_s16le \
--enable-protocolfile \
--enable-protocolrtp \
--enable-protocoltcp \
--enable-protocoludp \
--enable-demuxermp3 \
--enable-demuxerwav \
--enable-demuxermpegts \
--enable-demuxermov \
--enable-demuxerflv \
--enable-bsfh264_mp4toannexb \
--enable-bsfhevc_mp4toannexb \
--enable-bsfaac_adtstoasc编译之后就可以打开AV_SAMPLE_FMT_S16采样格式的编码器了具体如下 AVCodec* pCodec avcodec_find_encoder_by_name(libfdk_aac);if (pCodec nullptr){//...省略return;}//...省略最后附上一个比较关键的部分就是将S16的音频采样数据赋值给AVFrame之前参数不对也折腾了很久 // 创建输入帧AVFrame* pS16AudioFrame av_frame_alloc();if (pS16AudioFrame nullptr){//...省略return;}// frame缓冲区中的样本帧数量由ctx-frame_size决定pS16AudioFrame-nb_samples pCodecCtx-frame_size;// 音频采样格式pS16AudioFrame-format pCodecCtx-sample_fmt;// 声道布局pS16AudioFrame-channel_layout pCodecCtx-channel_layout;pS16AudioFrame-channels pCodecCtx-channels;// 采样率pS16AudioFrame-sample_rate pCodecCtx-sample_rate;// 利用nb_samples、format、channel_layout创建frame的数据缓冲区int iRet av_frame_get_buffer(pS16AudioFrame, 0);if (iRet 0){//...省略return;}//...省略// 将读取到的PCM数据填充到frame去但要注意格式的匹配, 是planar还是packed都要区分清楚iRet av_samples_fill_arrays(pS16AudioFrame-data, pS16AudioFrame-linesize,stFrame.pFrame, pS16AudioFrame-channels,pCodecCtx-frame_size, pCodecCtx-sample_fmt, 0);if (iRet 0){//...省略return;}2. S16重采样FLTP // 创建音频转换上下文SwrContext* pSwrCtx swr_alloc_set_opts(NULL, pCodecCtx-channel_layout, AV_SAMPLE_FMT_FLTP, pCodecCtx-sample_rate,pCodecCtx-channel_layout, AV_SAMPLE_FMT_S16, pCodecCtx-sample_rate, 0, NULL);if (pSwrCtx nullptr){printf(无法分配音频转换上下文\n);return;}// 初始化音频转换上下文if (swr_init(pSwrCtx) 0){printf(音频转换上下文初始化失败\n);return;}// 进行音频转换AVFrame* pFltpAudioFrame av_frame_alloc();if (pCodec nullptr){//...省略return;}pFltpAudioFrame-format pCodecCtx-sample_fmt;pFltpAudioFrame-channel_layout AV_CH_LAYOUT_STEREO;pFltpAudioFrame-sample_rate pCodecCtx-sample_rate;pFltpAudioFrame-nb_samples 1024; //一帧音频一通道的采样数量int iRet av_frame_get_buffer(pFltpAudioFrame, 0); //给pcm分配存储空间if (iRet 0){//...省略return;}//...省略PCM复制给AVFrame// 执行音频转换iRet swr_convert_frame(pSwrCtx, pFltpAudioFrame, pS16AudioFrame);if(iRet 0){//...省略return;}二、AAC音频解码
音频编码完成后发送给安卓端能够正常播放音频现在开始解码安卓发过来的AAC音频原本以为很快就能解决结果发现调用avcodec_receive_frame函数一直返回-11也就是说没有能获取到解码后的完整的一帧数据我打印了一下返回值发现一次都没成功由于我发送S16的编码数据给安卓能够正常播放且安卓采样也是S16但是走的硬编解码让我一度认为安卓发过来的音频编码数据的采样格式是S16直到我一次偶然的尝试将
AVCodec* pCodec avcodec_find_decoder_by_name(libfdk_aac);
// ...省略
AVCodecContext* pCodecCtx avcodec_alloc_context3(pCodec);
// ...省略
pCodecCtx-request_sample_fmt AV_SAMPLE_FMT_S16;改为
AVCodec* pCodec avcodec_find_decoder(AV_CODEC_ID_AAC);
// ...省略
AVCodecContext* pCodecCtx avcodec_alloc_context3(pCodec);
// ...省略
pCodecCtx-request_sample_fmt AV_SAMPLE_FMT_FLTP;结果发现解码成功了…附上FLTP重采样S16代码其实和S16重采样FLTP差不多
// 创建音频转换上下文SwrContext* pSwrCtx swr_alloc_set_opts(NULL, pCodecCtx-channel_layout, AV_SAMPLE_FMT_S16, pCodecCtx-sample_rate,pCodecCtx-channel_layout, AV_SAMPLE_FMT_FLTP, pCodecCtx-sample_rate, 0, NULL);if (pSwrCtx nullptr){printf(无法分配音频转换上下文\n);return;}// 初始化音频转换上下文if (swr_init(pSwrCtx) 0){printf(音频转换上下文初始化失败\n);return;}// 进行音频转换AVFrame* pS16AudioFrame av_frame_alloc();if (NULL pS16AudioFrame ){printf(av_frame_alloc failed!\n);return ;}pS16AudioFrame-format AV_SAMPLE_FMT_S16;pS16AudioFrame-channel_layout AV_CH_LAYOUT_STEREO;pS16AudioFrame-sample_rate pCodecCtx-sample_rate;pS16AudioFrame-nb_samples 1024; //一帧音频一通道的采样数量iRet av_frame_get_buffer(pS16AudioFrame, 0); //给pcm分配存储空间if(iRet 0){//...省略return;}// 分配一帧空间存放解码后的一帧数据AVFrame* pAudioFrame av_frame_alloc();//...省略// 执行音频转换iRet swr_convert_frame(pSwrCtx, pS16AudioFrame, pAudioFrame);//...省略总结
音频编解码相对来说比较简单就AAC稍微复杂一点如果编解码失败大概分两种情况 1编解码上下文参数不对 2传给编解码器的数据不对 另外每个函数的返回值也要判断一下这样出现异常也能迅速定位所在位置