【FFmpeg】PCM编码成AAC

这篇具有很好参考价值的文章主要介绍了【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的格式初始化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加群领取哦~

ffmpeg pcm转aac,程序员,编程,音视频开发,ffmpeg,音视频,webrtc,pcm,aac

  • 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 - 简书文章来源地址https://www.toymoban.com/news/detail-680482.html

到了这里,关于【FFmpeg】PCM编码成AAC的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【FFmpeg】ffmpeg 命令行参数 ⑦ ( 使用 FFmpeg 提取 PCM 音频数据 | PCM 音频格式 | 提取 PCM 音频格式常用参数 | 查询文档方法 )

    PCM 全称 \\\" Pulse Code Modulation \\\" , 脉冲编码调制 , 该 音频数据 是未经压缩的 采样裸数据 , 只有 知道该数据的 采样率 / 采样位数 / 通道数 才能将该音频数据播放出来 ; PCM 数据是 最原始的音频数据 , 音频内容完全无损 , 但是 PCM 数据体积庞大 , 对 PCM 音频数据压缩 分为 无损压缩

    2024年04月11日
    浏览(41)
  • 音视频 ffmpeg命令提取PCM数据

    提取PCM 推荐一个零声学院项目课,个人觉得老师讲得不错,分享给大家: 零声白金学习卡(含基础架构/高性能存储/golang云原生/音视频/Linux内核) https://xxetb.xet.tech/s/VsFMs

    2024年02月09日
    浏览(46)
  • Qt-FFmpeg开发-音频解码为PCM文件(9)

    目录 音视频/FFmpeg #Qt Qt-FFmpeg开发-使用libavcodec API的音频解码示例(MP3转pcm) 1、概述 2、实现效果 3、主要代码 4、完整源代码 更多精彩内容 👉个人内容分类汇总 👈 👉音视频开发 👈 最近研究了一下FFmpeg开发,功能实在是太强大了,网上ffmpeg3、4的文章还是很多的,但是学

    2023年04月08日
    浏览(30)
  • 音视频编码实战-------pcm+yuv数据转成MP4

    avcodec_find_encoder: 根据编码器ID查找编码器 avcodec_alloc_context3:创建编码器上下文 avcodec_open2:打开编码器 avformat_alloc_output_context2:为输出格式创建复用器上下文 avformat_new_stream:创建音视频流 avcodec_parameters_from_context:将编码器上下文中的参数拷贝到音视频流中的编码器参数中AVCodec

    2024年02月15日
    浏览(47)
  • 【通信原理实验】基于A律13折线的PCM编码与解码(附完整代码)

    一、实验原理 PCM,Pulse Code Modulation(脉冲编码调制),即把从模拟信号 抽样、量化、编码 成为二迚制符号的基本过程, 称为脉冲编码调制。 模拟信号的数字化过程: 1、抽样 – 时间离散 :时间连续的信号 - 时间离散、幅度连续的信号; • 抽样定理(香农采样定律、奈奎

    2024年02月08日
    浏览(39)
  • 【HarmonyOS】实现将pcm音频文件进行编码并写入文件(API6 Java)

     【】 音频编码、管道模式、createEncoder 【写在前面】 在使用API6开发HarmonyOS应用时,如何将pcm源文件进行编码并写入文件,最后生成aac文件,本文直接附上主要代码开发步骤供大家参考。 【主要功能代码】 【说明和注意事项】 1、AAC文件有两种添加头文件方式:ADIF与

    2024年02月11日
    浏览(25)
  • 程序员饭碗不保?首个 AI 程序员 “Devin”:从编码辅助到独立完成项目

    昨天一家名为 CognitionAI 的公司,发布了首个 AI 程序员 “Devin” 🌟 CognitionAI 官网提供了多个 Devin 的实际操作视频实例,主要包括: 通过阅读博客,Devin 可以学习如何使用不熟悉的技术(如在 Modal 上运行 ControlNet,Modal 是一个 serverless 平台)。 让 Devin 创建一个个人网站来模

    2024年03月16日
    浏览(38)
  • 【音视频|PCM】PCM格式详解

    😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭 🤣本文内容🤣:🍭介绍数字音频的PCM格式🍭 😎金句分享😎:🍭子曰:君子不器。 ——《论语·为政篇》。意思是,君子不应像器具那样,只有一种用

    2024年02月08日
    浏览(28)
  • 音频基础--PCM音频

      PCM 全称 Pulse-Code Modulation,就是脉冲调制编码,是用于将波形表示的模拟音频信号转换为数字1和0表示的数字音频信号,而不压缩也不丢失信息的处理技术。 简单来说就是一种用数字表示采样模拟信号的方法 。   如下是使用Audacity音频处理软件截取1~2s的时间段内音频波

    2023年04月09日
    浏览(27)
  • PCM音频混合的方法

    音频混音算法的实现 1、线性叠加后求平均 优点:不会产生溢出,噪音较小; 缺点:衰减过大,影响通话质量; 2、归一化混音(自适应加权混音算法) 思路:使用更多的位数(32 bit)来表示音频数据的一个样本,混完音后在想办法降低其振幅,使其仍旧分布在16 bit所能表示的范

    2024年02月13日
    浏览(22)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包