Mac OS使用FFmpeg进行音频AAC编码

一.概述

iOS 使用FFmpeg 实现音视频软编码

上一篇文章中写到的AAC音频编码,因缺少真机测试,有挺多问题,编码后的音频全是噪声。这篇文章使用Mac OS环境,将逐一解决这些问题且编码成功。本文使用的是FFmpeg4.2版本。

二.编码器打开失败问题

FFmpeg编码器不支持AV_SAMPLE_FMT_S16格式。此为packet格式,声道的pcm数据全部放在AVFramedata[0]中,交替存储。

FFmpeg编码器支持AV_SAMPLE_FMT_FLTP格式。此为planar格式,声道的pcm数据会单独保存。例如双声道,左右声道pcm数据会分别保存在AVFramedata[0]data[1]中。

    //找到aac编码器
    pCodec =  avcodec_find_encoder(AV_CODEC_ID_AAC);
    if (!pCodec) {
        printf("Codec not found\n");
        return;
    }
    pCodecContext = avcodec_alloc_context3(pCodec);
    pCodecContext->codec_id = AV_CODEC_ID_AAC;
    pCodecContext->codec_type = AVMEDIA_TYPE_AUDIO;
    //ffmpeg目前只支持FLTP/FLT格式,否则编码器无法打开
    pCodecContext->sample_fmt = AV_SAMPLE_FMT_FLTP;
    pCodecContext->sample_rate = 44100;
    pCodecContext->channel_layout = AV_CH_LAYOUT_STEREO;
    pCodecContext->channels = av_get_channel_layout_nb_channels(pCodecContext->channel_layout);
    pCodecContext->bit_rate = 64000;
    pCodecContext->profile = FF_PROFILE_AAC_LOW ;
    pCodecContext->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;

三、AVFrame初始化内存问题

之前使用的初始化内存方法:

    int size = av_samples_get_buffer_size(NULL, _pCodecContext->channels, _pCodecContext->frame_size, _pCodecContext->sample_fmt, 1);
    uint8_t *buffer = av_malloc(size);
    avcodec_fill_audio_frame(_pFrame, _pCodecContext->channels, _pCodecContext->sample_fmt, buffer, size, 1);

实际上可以用一句代码:

av_frame_get_buffer(pFrame, 0);

有个疑惑的地方是,当选择双声道时,初始化后data[0]data[1]正常,对应的linesize[0] = 4096,而linesize[1]=0,但是编码后双声道的声音都正常。

四、重采样问题

demo中使用的是Audio Unit进行采样,配置AudioStreamBasicDescription中的是:

dataFormat.mFormatFlags     = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;

即样本格式为AV_SAMPLE_FMT_S16,而编码器格式是AV_SAMPLE_FMT_FLTP,而且如果采样率,位元深度,声道数不一致,也需要进行重采样:

    //根据asbd参数设置重采样参数
    SwrContext *swr = swr_alloc();
    uint64_t in_channel_layout = av_get_default_channel_layout(audioDescription.mChannelsPerFrame);
    av_opt_set_int(swr, "in_channel_layout",  in_channel_layout, 0);
    av_opt_set_int(swr, "out_channel_layout", pCodecContext->channel_layout,  0);
    av_opt_set_int(swr, "in_channel_count", audioDescription.mChannelsPerFrame,  0);
    av_opt_set_int(swr, "out_channel_count", pCodecContext->channels,  0);
    av_opt_set_int(swr, "in_sample_rate",     audioDescription.mSampleRate,0);
    av_opt_set_int(swr, "out_sample_rate",    pCodecContext->sample_rate,0);
    av_opt_set_sample_fmt(swr, "in_sample_fmt",  AV_SAMPLE_FMT_S16, 0);
    av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_FLTP,  0);
    swr_init(swr);
    
    //根据Audio Unit传递的数据量,计算样本量
    int in_nb_samples = (int)pcm_size/(audioDescription.mChannelsPerFrame * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16));
    
    //初始化输入数据内存
    uint8_t **input = NULL;
    int src_linesize;
    av_samples_alloc_array_and_samples(&input, &src_linesize,audioDescription.mChannelsPerFrame, in_nb_samples,AV_SAMPLE_FMT_S16, 0);
    *input = pcm_data;
    
    //初始化输出数据内存
    uint8_t *output = NULL;
    //计算实际的样本量,因为不同分辨率转换后,会出现样本量不一致情况
    int out_samples = (int)av_rescale_rnd(swr_get_delay(swr, audioDescription.mSampleRate) + in_nb_samples, pCodecContext->sample_rate, audioDescription.mSampleRate, AV_ROUND_UP);
    av_samples_alloc(&output, NULL, pCodecContext->channels, out_samples, pCodecContext->sample_fmt, 0);
    
    //转换音频格式
    int out_nb_samples = swr_convert(swr,&output,out_samples, (const uint8_t **)input, in_nb_samples);
    if (out_nb_samples < 0) {
        fprintf(stderr, "Could not convert input samples (error )\n");
        return;
    }

五、缓冲问题

采集到的pcm数据一次是1024个字节,经过转换后输出的samples是512个。此demo中AAC编码器需凑齐1024个samples才能进行编码,所以需要使用AVAudioFifo音频缓冲队列:

//根据实际情况realloc队列内存
    ret = av_audio_fifo_realloc(audioFifo, av_audio_fifo_size(audioFifo)+ (int)out_nb_samples);
    if(ret <0){
        printf("av_audio_fifo_realloc == %s\n", av_err2str(ret));
        return;
    }
    //写入队列
    ret = av_audio_fifo_write(audioFifo, (void **)&output,out_nb_samples);
    if(ret <0){
        printf("av_audio_fifo_write == %s\n", av_err2str(ret));
        return;
    }
    //判断达到aac的1024个样本量后,再提取出来编码
    while(av_audio_fifo_size(audioFifo) >= pCodecContext->frame_size)
    {
        int frame_size = FFMIN(av_audio_fifo_size(audioFifo), pCodecContext->frame_size);
        //从队列中提取1024个样本量数据,写入avframe中
        av_audio_fifo_read(audioFifo, (void **)pFrame->data, frame_size);

      (省略)
    }

六、AAC的ADTS头问题

demo中使用的是4.2版FFmpeg,编码中不会自动添加ADTS头,需在每一包编码后的数据前手动添加ADTS头:

void addADTSHead(uint8_t *head,int length,int channels){
    length &= 0x1FFF;
    int sample_index = 4;//此处为44100采样率对应的index
    head[0] = (char)0xff;
    head[1] = (char)0xf1;
    head[2] = (char)(0x40 | (sample_index << 2) | (channels >> 2));
    head[3] = (char)((channels & 0x3) << 6 | (length >> 11));
    head[4] = (char)(length >> 3) & 0xff;
    head[5] = (char)(((length & 0x7) << 5) & 0xff) | 0x1f;
    head[6] = (char)0xfc;
}

七、变速,音量问题(未解决)

当仅仅是音频采集和编码,CPU的占用率在20%~30%,生成的aac文件就很正常。


正常的aac文件波形图

当开启RTMP,推流到本地的nginx服务器,此过程CPU的占用率在100%~200%,此时生成的aac文件就会有变速,音量问题。此问题暂未解决,有了解的同学望不吝赐教。

异常的aac文件波形图

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容