使用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的格式初始化oformat

    format_name 输出文件的格式, 传NULL,FFmpeg会根据filename的格式初始化format_name

    filename 输出文件路径

  • 初始化编码器上下文

    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即可,会自动分配空间创建SwrContext

      in_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 输入、输出的buffer

      in_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相关推荐

  1. MediaCodec 完成PCM编码成AAC

    安卓MediaCodec支持的音视频格式 视频格式 public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8& ...

  2. 新版ffmpeg PCM编码到AAC,swr_convert转换采样精度,稍微修改兼容PCM编码为G711A及MP3,记录下。

    https://blog.csdn.net/venice0708/article/details/80783870

  3. 基于FFmpeg-4.0 SDK的PCM编码成AAC

    https://blog.csdn.net/fireroll/article/details/83032148

  4. 基于FFmpeg的音频编码(PCM数据编码成AAC android)

    概述 在Android上实现录音,并利用 FFmpeg将PCM数据编码成AAC. 详细 代码下载:http://www.demodashi.com/demo/10512.html 之前做的一个demo ...

  5. pcm编码为aac/MP3格式ffmpeg(八)

    前言 pcm音频是未压缩的数据,占空间,在存储或者传输时一般都会选择先进行有损压缩(比如aac,MP3等等).pcm音频数据在文件存储时一般都按照planner格式存储,例如: 声道1声道2声道3.. ...

  6. PCM 编码为AAC

    PCM 编码为AAC 简介 ffmpeg version 3.4 Copyright (c) 2003-2017 the FFmpeg developersbuilt with Apple LLVM ...

  7. 最简单的基于FFMPEG的音频编码器(PCM编码为AAC)

    本文介绍一个最简单的基于FFMPEG的音频编码器.该编码器实现了PCM音频采样数据编码为AAC的压缩编码数据.编码器代码十分简单,但是每一行代码都很重要.通过看本编码器的源代码,可以了解FFMPEG音 ...

  8. ffmpeg pcm编码aac

    本博文参考自雷神博客 https://blog.csdn.net/leixiaohua1020/article/details/25430449 这是ffmpeg加上libfdk-aac 将pcm转a ...

  9. FFMPEG实现PCM编码(采用封装格式实现)

    技术在于交流.沟通,转载请注明出处并保持作品的完整性. 原文:https://blog.csdn.net/hiwubihe/article/details/81260882 [音频编解码系列文章] 音 ...

最新文章

  1. java nodelist 快速排序,【Leetcode】Sort List in java,你绝对想不到我是怎么做的^^我写完过了我自己都觉得好jian~...
  2. 【NLP】文本生成?还不快上知识库
  3. MVC视图之间调用方法总结
  4. .NET5.0 单文件发布打包操作深度剖析
  5. 初始化linux-nginx的安装和使用
  6. LQR轨迹跟踪算法Python/Matlab算法实现_代码(2)
  7. oracle 11g 数据库恢复技术 --rman catalog
  8. 大数据总监python可视化分析30W数据后,找到了抹黑我们的原因
  9. HTML5实践 -- 使用css装饰你的图片画廊
  10. Python urllib – Python 3 urllib
  11. 安装部署shipyard
  12. 半边数据结构(The_Half-Edge_Data_Structure)
  13. 武大计算机专业学费多少,2017年武汉大学硕士研究生学费及奖助学金
  14. 【C++ 程序】 解线性方程组(Cramer法则)
  15. java网上购物商城文献综述,JSP电子商务网上购物系统的设计(源代码+论文+开题报告+外文翻译+文献综述)...
  16. 如何运用js制作简单的登录界面(html)
  17. 07-24 什么是串口
  18. 魔教传奇—阿里软件的魔方文化
  19. java实现逻辑推断
  20. 数值分析·学习 | 拉格朗日插值法matlab实现

热门文章

  1. java基础学习03
  2. walking与Matlab入门教程-ros2命令
  3. openwrt 之 wifi选择编译
  4. 清理备用内存/关闭备用内存(备用内存占用过高且不自动释放)
  5. vulnhub之VENOM:1
  6. 基于自适应遗传算法的异构多无人机协同任务分配
  7. java大集合,JAVA规范大集合(备忘)
  8. unity 使用BMFont 制作自定义字体
  9. 计算机科学博士上海纽约大学,徐立华 - 上海纽约大学 - 计算机科学
  10. ipex天线和板载天线区别