【FFmpeg】PCM编码成AAC
使用FFmpeg把PCM裸数据编码成AAC音频流,具体步骤跟YUV编码成H264差不多。
1、命令行
ffmpeg -f s16le -ar 44100 -ac 2 -i bb1.pcm output.aac
-f
PCM数据为s16le
-ar
采样率为44100
-ac
通道数为2
这样就通过命令把PCM数据编码成AAC了。
2、使用API编码
FFmpeg内部AAC格式只支持AV_SAMPLE_FMT_FLTP格式的PCM,由于我们的PCM数据是s16le的,因此我们需要把s16le格式转换成fltp格式再进行编码。我们可以在AVCodec结构体中的sample_fmts字段中判断编码器是否支持你的格式。
初始化输出文件上下文
int avformat_alloc_output_context2(AVFormatContext **ctx, ff_const59 AVOutputFormat *oformat,const char *format_name, const char *filename);
ctx
输出文件的上下文oformat
输出文件的AVOutputFormat,传NULL,FFmpeg会根据filename的格式初始化oformatformat_name
输出文件的格式, 传NULL,FFmpeg会根据filename的格式初始化format_namefilename
输出文件路径初始化编码器上下文
dec = avcodec_find_encoder(ofmt_ctx->oformat->audio_codec); if (!dec) {printf("avcodec_find_encoder fail \n");goto __FAIL; } dec_ctx = avcodec_alloc_context3(dec); dec_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; if (!check_sample_fmt(dec, dec_ctx->sample_fmt)) {fprintf(stderr, "Encoder does not support sample format %s",av_get_sample_fmt_name(dec_ctx->sample_fmt));goto __FAIL; } dec_ctx->channel_layout = select_channel_layout(dec); dec_ctx->channels = av_get_channel_layout_nb_channels(dec_ctx->channel_layout); dec_ctx->sample_rate = select_sample_rate(dec); dec_ctx->bit_rate = 64000; ret = avcodec_open2(dec_ctx, dec, NULL);
FFmpeg内部AAC音频流只支持fltp格式的PCM,使用check_sample_fmt函数可以检测编码器是否支持AV_SAMPLE_FMT_FLTP,通过select_channel_layout函数选择最佳的音频通道布局,通过select_sample_rate函数选择最佳的采样率。
检测是否支持AVSampleFormat
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; }
选择最佳采样率
static int select_sample_rate(const AVCodec *codec) {const int *p;int best_samplerate = 0; if (!codec->supported_samplerates)return 44100; p = codec->supported_samplerates;while (*p) {if (!best_samplerate || abs(44100 - *p) < abs(44100 - best_samplerate))best_samplerate = *p;p++;}return best_samplerate; }
选择最佳通道布局
static int select_channel_layout(const AVCodec *codec) {const uint64_t *p;uint64_t best_ch_layout = 0;int best_nb_channels = 0; if (!codec->channel_layouts)return AV_CH_LAYOUT_STEREO; p = codec->channel_layouts;while (*p) {int nb_channels = av_get_channel_layout_nb_channels(*p); if (nb_channels > best_nb_channels) {best_ch_layout = *p;best_nb_channels = nb_channels;}p++;}return best_ch_layout; }
创建输入文件音频流
AVStream *st = avformat_new_stream(ofmt_ctx, dec); ret = avcodec_parameters_from_context(st->codecpar, dec_ctx); if (ret<0) {printf("avcodec_parameters_from_context fail \n");goto __FAIL; }
把编码器上下文参数拷贝给新建的AVSteam
打开输出文件
avio_open(&ofmt_ctx->pb, aacPath.UTF8String, AVIO_FLAG_WRITE);
写入文件头
avformat_write_header(ofmt_ctx, NULL);
读取PCM数据,放到AVFrame中
初始化AVFrame用来存放PCM数据
AVFrame *s16_frame = av_frame_alloc(); if (!s16_frame) {printf("av_frame_alloc fail \n");goto __FAIL; } s16_frame->nb_samples = dec_ctx->frame_size; s16_frame->format = AV_SAMPLE_FMT_S16; s16_frame->channel_layout = AV_CH_LAYOUT_STEREO; s16_frame->sample_rate = 44100; ret = av_frame_get_buffer(s16_frame, 0);
AVFrame的参数要与你的PCM数据参数一致,我用到的PCM数据是s16le、采样率44100Hz、通道数为2。
从文件中读取PCM数据
size_t size = fread(pcm_buffer, 1, pcm_buffer_size, pcm_f);
存放到AVFrame中去
av_samples_fill_arrays(s16_frame->data, s16_frame->linesize, pcm_buffer, s16_frame->channels, s16_frame->nb_samples, s16_frame->format, 0); int av_samples_fill_arrays(uint8_t **audio_data, int *linesize,const uint8_t *buf,int nb_channels, int nb_samples,enum AVSampleFormat sample_fmt, int align);
audio_data
输出buffer,传frame->data即可linesize
输出buffer的行大小,传frame->linesize即可buf
音频数据nb_channels
音频通道数nb_samples
音频采样数sample_fmt
音频数据格式align
buffer的对齐方式 默认为0,不对齐传1
【学习地址】:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发
【文章福利】:免费领取更多音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击1079654574加群领取哦~
s16le->fltp格式转换
创建SwrContext
struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate,int log_offset, void *log_ctx);
s
传NULL即可,会自动分配空间创建SwrContextin_ch_layout
out_ch_layout
输入、输出的通道布局in_sample_fmt
out_sample_fmt
输入、输出的PCM数据格式in_sample_rate
out_sample_rate
输入、输出的采样率初始化SwrContext
int swr_init(struct SwrContext *s);
格式转换
int swr_convert(struct SwrContext *s, uint8_t **out, int out_count,const uint8_t **in , int in_count);
in
out
输入、输出的bufferin_count
out_count
输入、输出的采样数,需要注意的是,这里传的是一个通道的采样数,而不是多个通道数相加的。
编码
int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame); int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);
写文件尾
int av_write_trailer(AVFormatContext *s);
完整代码如下
/* check that a given sample format is supported by the encoder */
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;
}
/* just pick the highest supported samplerate */
static int select_sample_rate(const AVCodec *codec)
{const int *p;int best_samplerate = 0;
if (!codec->supported_samplerates)return 44100;
p = codec->supported_samplerates;while (*p) {if (!best_samplerate || abs(44100 - *p) < abs(44100 - best_samplerate))best_samplerate = *p;p++;}return best_samplerate;
}
/* select layout with the highest channel count */
static int select_channel_layout(const AVCodec *codec)
{const uint64_t *p;uint64_t best_ch_layout = 0;int best_nb_channels = 0;
if (!codec->channel_layouts)return AV_CH_LAYOUT_STEREO;
p = codec->channel_layouts;while (*p) {int nb_channels = av_get_channel_layout_nb_channels(*p);
if (nb_channels > best_nb_channels) {best_ch_layout = *p;best_nb_channels = nb_channels;}p++;}return best_ch_layout;
}
+ (void)convert
{NSString *pcmPath = [[NSBundle mainBundle] pathForResource:@"bb1_44100_2_s16le.pcm" ofType:nil];NSString *aacPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"bb1.aac"];
NSLog(@"%@", aacPath);int ret;AVFormatContext *ofmt_ctx = NULL;AVCodecContext *dec_ctx = NULL;AVCodec *dec = NULL;AVPacket *pkt = NULL;AVFrame *s16_frame = NULL;AVFrame *fltp_frame = NULL;SwrContext *swr_ctx = NULL;FILE *pcm_f = fopen(pcmPath.UTF8String, "rb+");ret = avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, aacPath.UTF8String);if (ret<0) {printf("avformat_alloc_output_context2 fail \n");goto __FAIL;}dec = avcodec_find_encoder(ofmt_ctx->oformat->audio_codec);if (!dec) {printf("avcodec_find_encoder fail \n");goto __FAIL;}dec_ctx = avcodec_alloc_context3(dec);dec_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP;if (!check_sample_fmt(dec, dec_ctx->sample_fmt)) {fprintf(stderr, "Encoder does not support sample format %s",av_get_sample_fmt_name(dec_ctx->sample_fmt));goto __FAIL;}dec_ctx->channel_layout = select_channel_layout(dec);dec_ctx->channels = av_get_channel_layout_nb_channels(dec_ctx->channel_layout);dec_ctx->sample_rate = select_sample_rate(dec);dec_ctx->bit_rate = 64000;
ret = avio_open(&ofmt_ctx->pb, aacPath.UTF8String, AVIO_FLAG_WRITE);if (ret<0) {printf("avio_open fail \n");goto __FAIL;}ret = avcodec_open2(dec_ctx, dec, NULL);if (ret<0) {printf("avcodec_open2 fail \n");goto __FAIL;}AVStream *st = avformat_new_stream(ofmt_ctx, dec);ret = avcodec_parameters_from_context(st->codecpar, dec_ctx);if (ret<0) {printf("avcodec_parameters_from_context fail \n");goto __FAIL;}ret = avformat_write_header(ofmt_ctx, NULL);if (ret<0) {printf("avformat_write_header fail \n");goto __FAIL;}s16_frame = av_frame_alloc();if (!s16_frame) {printf("av_frame_alloc fail \n");goto __FAIL;}s16_frame->nb_samples = dec_ctx->frame_size;s16_frame->format = AV_SAMPLE_FMT_S16;s16_frame->channel_layout = AV_CH_LAYOUT_STEREO;s16_frame->sample_rate = 44100;
// s16_frame->channels = av_get_channel_layout_nb_channels(s16_frame->channel_layout);ret = av_frame_get_buffer(s16_frame, 0);if (ret<0) {printf("av_frame_get_buffer fail \n");goto __FAIL;}pkt = av_packet_alloc();if (!pkt) {printf("av_packet_alloc fail \n");goto __FAIL;}int pts_i = 0;swr_ctx = swr_alloc_set_opts(NULL, dec_ctx->channel_layout, dec_ctx->sample_fmt, dec_ctx->sample_rate, s16_frame->channel_layout, s16_frame->format, s16_frame->sample_rate, 0, NULL);if (!swr_ctx) {printf("swr_alloc_set_opts fail \n");goto __FAIL;}ret = swr_init(swr_ctx);if (ret<0) {printf("swr_init fail \n");goto __FAIL;}fltp_frame = av_frame_alloc();fltp_frame->nb_samples = dec_ctx->frame_size;fltp_frame->format = dec_ctx->sample_fmt;fltp_frame->channel_layout = dec_ctx->channel_layout;fltp_frame->sample_rate = dec_ctx->sample_rate;
// fltp_frame->channels = av_get_channel_layout_nb_channels(s16_frame->channel_layout);ret = av_frame_get_buffer(fltp_frame, 0);if (ret<0) {printf("av_frame_get_buffer fail \n");goto __FAIL;}uint64_t pcm_buffer_size = s16_frame->nb_samples*av_get_bytes_per_sample(s16_frame->format)*s16_frame->channels;uint8_t *pcm_buffer = av_malloc(pcm_buffer_size);
while (feof(pcm_f)==0) {size_t size = fread(pcm_buffer, 1, pcm_buffer_size, pcm_f);int nb_samples = size/(av_get_bytes_per_sample(s16_frame->format)*s16_frame->channels);s16_frame->nb_samples = nb_samples;fltp_frame->nb_samples = nb_samples;av_samples_fill_arrays(s16_frame->data, s16_frame->linesize, pcm_buffer, s16_frame->channels, s16_frame->nb_samples, s16_frame->format, 0);ret = swr_convert(swr_ctx, fltp_frame->data, fltp_frame->nb_samples, s16_frame->data, s16_frame->nb_samples);if (size==0) {printf("fread fail \n");break;}pts_i+=fltp_frame->nb_samples;fltp_frame->pts = pts_i;ret = avcodec_send_frame(dec_ctx, fltp_frame);if (ret<0) {printf("avcodec_send_frame fail \n");break;}while (1) {ret = avcodec_receive_packet(dec_ctx, pkt);if (ret==AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;} else if (ret<0) {printf("avcodec_receive_packet fail \n");break;}ret = av_interleaved_write_frame(ofmt_ctx, pkt);if (ret<0) {printf("av_interleaved_write_frame fail \n");break;}av_packet_unref(pkt);}}ret = avcodec_send_frame(dec_ctx, NULL);if (ret<0) {printf("avcodec_send_frame fail \n");goto __FAIL;}while (1) {ret = avcodec_receive_packet(dec_ctx, pkt);if (ret==AVERROR(EINVAL) || ret == AVERROR_EOF) {break;} else if (ret<0) {printf("avcodec_receive_packet fail \n");break;}ret = av_interleaved_write_frame(ofmt_ctx, pkt);if (ret<0) {printf("av_interleaved_write_frame fail \n");break;}av_packet_unref(pkt);}ret = av_write_trailer(ofmt_ctx);if (ret<0) {printf("av_write_trailer fail \n");}
__FAIL:if (ofmt_ctx->pb) {avio_close(ofmt_ctx->pb);}if (dec_ctx) {avcodec_close(dec_ctx);}if (pcm_buffer) {av_free(pcm_buffer);}if (ofmt_ctx) {avformat_free_context(ofmt_ctx);}if (s16_frame) {av_frame_free(&s16_frame);}if (fltp_frame) {av_frame_free(&fltp_frame);}if (pkt) {av_packet_free(&pkt);}
}
原文链接:【FFmpeg】PCM编码成AAC - 简书
【FFmpeg】PCM编码成AAC相关推荐
- MediaCodec 完成PCM编码成AAC
安卓MediaCodec支持的音视频格式 视频格式 public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8& ...
- 新版ffmpeg PCM编码到AAC,swr_convert转换采样精度,稍微修改兼容PCM编码为G711A及MP3,记录下。
https://blog.csdn.net/venice0708/article/details/80783870
- 基于FFmpeg-4.0 SDK的PCM编码成AAC
https://blog.csdn.net/fireroll/article/details/83032148
- 基于FFmpeg的音频编码(PCM数据编码成AAC android)
概述 在Android上实现录音,并利用 FFmpeg将PCM数据编码成AAC. 详细 代码下载:http://www.demodashi.com/demo/10512.html 之前做的一个demo ...
- pcm编码为aac/MP3格式ffmpeg(八)
前言 pcm音频是未压缩的数据,占空间,在存储或者传输时一般都会选择先进行有损压缩(比如aac,MP3等等).pcm音频数据在文件存储时一般都按照planner格式存储,例如: 声道1声道2声道3.. ...
- PCM 编码为AAC
PCM 编码为AAC 简介 ffmpeg version 3.4 Copyright (c) 2003-2017 the FFmpeg developersbuilt with Apple LLVM ...
- 最简单的基于FFMPEG的音频编码器(PCM编码为AAC)
本文介绍一个最简单的基于FFMPEG的音频编码器.该编码器实现了PCM音频采样数据编码为AAC的压缩编码数据.编码器代码十分简单,但是每一行代码都很重要.通过看本编码器的源代码,可以了解FFMPEG音 ...
- ffmpeg pcm编码aac
本博文参考自雷神博客 https://blog.csdn.net/leixiaohua1020/article/details/25430449 这是ffmpeg加上libfdk-aac 将pcm转a ...
- FFMPEG实现PCM编码(采用封装格式实现)
技术在于交流.沟通,转载请注明出处并保持作品的完整性. 原文:https://blog.csdn.net/hiwubihe/article/details/81260882 [音频编解码系列文章] 音 ...
最新文章
- java nodelist 快速排序,【Leetcode】Sort List in java,你绝对想不到我是怎么做的^^我写完过了我自己都觉得好jian~...
- 【NLP】文本生成?还不快上知识库
- MVC视图之间调用方法总结
- .NET5.0 单文件发布打包操作深度剖析
- 初始化linux-nginx的安装和使用
- LQR轨迹跟踪算法Python/Matlab算法实现_代码(2)
- oracle 11g 数据库恢复技术 --rman catalog
- 大数据总监python可视化分析30W数据后,找到了抹黑我们的原因
- HTML5实践 -- 使用css装饰你的图片画廊
- Python urllib – Python 3 urllib
- 安装部署shipyard
- 半边数据结构(The_Half-Edge_Data_Structure)
- 武大计算机专业学费多少,2017年武汉大学硕士研究生学费及奖助学金
- 【C++ 程序】 解线性方程组(Cramer法则)
- java网上购物商城文献综述,JSP电子商务网上购物系统的设计(源代码+论文+开题报告+外文翻译+文献综述)...
- 如何运用js制作简单的登录界面(html)
- 07-24 什么是串口
- 魔教传奇—阿里软件的魔方文化
- java实现逻辑推断
- 数值分析·学习 | 拉格朗日插值法matlab实现