JoJo的个人博客

记录精彩的程序人生

目录
音视频系统入门5之实战音频AAC编码
/    

音视频系统入门5之实战音频AAC编码

音频重采样

将音频三元组(采样率,采样大小和通道数)的值转成另一组值

例如44.1k/16/2转成48k/16/1

  • 从设备采集的音频数据与编码器要求的数据不一样
  • 扬声器要求的音频数据与要播放的音频数据不一致
  • 更方便运算

如何知道是否需要进行重采样?

  • 要了解音频设备的参数
  • 查看ffmpeg源码

重采样的步骤

  • 创建重采样上下文
  • 设置参数
  • 初始化重采样
  • 进行重采样

几个重要的API

  • swr_alloc_set_opts
  • swr_init
  • swr_convert
  • swr_free

重采样代码

创建重采样上下文,并设置参数

将采样率44100,采样格式AV_SAMPLE_FMT_FLT,采样格式为AV_SAMPLE_FMT_S16的数据转换为采样率44100,采样格式AV_SAMPLE_FMT_S16,采样格式为AV_SAMPLE_FMT_S16的数据

SwrContext *swr_ctx = NULL;
//channel, number/
swr_ctx = swr_alloc_set_opts(NULL,                    //ctx
                            AV_CH_LAYOUT_STEREO,     //输出channel布局
                            AV_SAMPLE_FMT_S16,       //输出的采样格式
                            44100,                   //采样率
                            AV_CH_LAYOUT_STEREO,     //输入channel布局
                            AV_SAMPLE_FMT_FLT,       //输入的采样格式
                            44100,                   //输入的采样率
                            0NULL);

创建输入输出缓冲区

uint8_t **src_data = NULL;
int src_linsize = 0;

uint8_t **dst_data = NULL;
int dst_linesize= 0;

//创建输入缓冲区
av_samples_alloc_array_and_samples(&src_data,            //输出缓冲区地址
                                   &src_linsize,         //缓冲区大小
                                   2,                    //通道个数
                                   512,                  //单通道采样个数   4096/4/2
                                   AV_SAMPLE_FMT_FLT,    //采样格式
                                   0);

//创建输出缓冲区
av_samples_alloc_array_and_samples(&dst_data,            //输出缓冲区地址
                                   &dst_linesize,         //缓冲区大小
                                   2,                    //通道个数
                                   512,                  //单通道采样个数   
                                   AV_SAMPLE_FMT_S16,    //采样格式
                                   0);

拷贝读取数据,进行重采样

//进行内存拷贝,按字节进行拷贝
memcpy((void*)src_data[0], (void*)pkt.data, pkt.size);

//重采样
swr_convert(swr_ctx,                        //重采样的上下文
            dst_data,                       //输出结果缓冲区
            512,                            //每个通道的采样数
            (const uint8_t**) src_data,     //输入缓冲区
            512);                           //输入单个通道的采样数

释放资源

//释放输入输出缓冲区
if (src_data) {
    av_free(&src_data[0]);
}
av_freep(src_data);
if (dst_data) {
    av_freep(&dst_data[0]);
}
av_freep(&dst_data);

//释放重采样的上下文
swr_free(&swr_ctx);

创建AAC编码器

ffmpeg编码过程

  • 创建编码器

  • 创建上下文

  • 打开编码器

  • 送数据给编码器

  • 编码

  • 释放资源

创建并打开编码器

  • 创建编码器 avcodec_find_encoder
  • 创建上下文 avcodec_alloc_context3
  • 打开编码器 avcodec_open2

代码

//创建编码器
//avcodec_find_encoder(AV_CODEC_ID_AAC);
AVCodec *codec = avcodec_find_encoder_by_name("libfdk_aac");

//创建codec上下文
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);

codec_ctx->sample_fmt = AV_SAMPLE_FMT_S16;            //输入音频的采样大小   libfdk_aac要求的采样大小为16位,因此必须设置为16位
codec_ctx->channel_layout = AV_CH_LAYOUT_STEREO;      //输入音频的channel_layout
codec_ctx->channels = 2;                              //输入音频的channel个数
codec_ctx->sample_rate = 44100;                       //输入音频的采样率
codec_ctx->bit_rate = 0;                              //AAC_LC:128k, AAC_HE: 64k, AAC_HE_V2: 32k
codec_ctx->profile = FF_PROFILE_AAC_HE_V2;            //如果设置了profile,bit_rate需要设置为0

//打开编码器
avcodec_open2(codec_ctx, codec, NULL)

编码使用的输入输出数据

重要API

  • avcodec_send_frame 将数据发送给编码器
  • avcodec_receive_packet 编码后的数据

输入输出数据

  • AVFrame
  • AVPacket

代码

//音频输入数据
AVFrame *frame = av_frame_alloc();

frame->nb_samples =     512;                    //单通道一个音频帧的采样数
frame->format =         AV_SAMPLE_FMT_S16;      //每个采样的大小
frame->channel_layout = AV_CH_LAYOUT_STEREO;    //channel layout
av_frame_get_buffer(frame, 0);                  //分配buffer大小: 采样数*采样大小(字节)*通道数  512*2*2=2048

//音频输出数据
AVPacket *newpkt = av_packet_alloc();   //分配编码后的数据空间

//将重采样的数据拷贝到frame中
memcpy((void*)frame->data[0], dst_data[0], dst_linesize);

AAC编码

编码

void encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, FILE *output) {
    
    int ret = 0;  
    ret = avcodec_send_frame(ctx, frame);
           
    //如果ret>=0说明数据设置成功
    while (ret >= 0) {
       //获取编码后的音频数据,如果成功,需要重复获取,知道失败为止
       ret = avcodec_receive_packet(ctx, pkt);
        
       if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
           return;
       } else if(ret < 0){
           printf("Error, encoding audio frame\n");
           exit(-1);
       }
       
       fwrite(pkt->data, 1, pkt->size, output);
       fflush(output);
    }
    return;
}

 //encode
encode(c_ctx, frame, newpkt, outfile);

释放资源

//强制将编码器缓冲区中的音频进行编码输出
encode(c_ctx, NULL, newpkt, outfile);
//释放 AVFrame 和 AVPacket
av_frame_free(&frame);
av_packet_free(&newpkt);

代码仓

FFmpegProject


标题:音视频系统入门5之实战音频AAC编码
作者:SunnySky
地址:https://www.tianyang.pub/articles/2020/11/17/1605610254649.html

评论