ffmpeg学习十:将pcm格式的音频编码为aac格式

上一节,我们使用alsa库编写了音频的采集和播放的程序。这一节,我们将在采集到的pcm格式的音频数据的基础上,进一步将其编码为aac格式。

音频编码概述

pcm是最原始的音频编码格式,这种编码是无损的。同时意味着存储这种数据的文件将会很庞大,因此必须进行压缩。pcm是音频的编码格式,它不是文件的封装格式,上一节我们录制的声音存储在一个.pcm为后缀的文件中,这只是我们愿意这么做而已,你完全可以不这么做,这没有关系。
aac既是一种文件的封装格式,又是音频的编码格式。一aac为封装格式的文件,以.aac为后缀。aac封装格式一般内部的音频数据编码格式也为aac。
音频编码和视频编码的流程基本一致,而视频编码我们在前面已经做过了。因此,关于程序的流程就没有太多需要废话的了。下面介绍几个音频相关的参数,这几个参数是编码器进行编码所必需的。
我们总共需要设置四个参数即可:
1.sample_rate
codecContext->sample_rate = frame->sample_rate;
sample_rate指的是采样率。也就是我们一秒钟采集多少次声音样本。
2.frame->channels
codecContext->channels = frame->channels;
frame->channels之的是通道的数目。音频一般有双通道或者单通道之分,一般都是双通道吧,我们的程序里面也是设置为双通道的。也就是frame->channels=2.
3.frame->format
codecContext->sample_fmt = frame->format;
frame->format指的是样本的格式。一个音频的样本一般用两个字节来描述,分为大小端。我们的程序中使用的是16bit的小端格式。
4.channel_layout
codecContext->channel_layout = AV_CH_LAYOUT_STEREO;
channel_layout 用来设置输出通道布局。这个参数不太理解!!!

程序概述

程序流程图如下:
这里写图片描述

程序

下面结合程序流程图和程序中的注释,来看程序:

//created by Jinwei Liu
#include <math.h>
#include <stdlib.h>
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
#include <libavutil/frame.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>


int main(int argc, char **argv){
    AVFrame *frame;
    AVCodec *codec = NULL;
    AVPacket packet;
    AVCodecContext *codecContext;
    int readSize=0;
    int ret=0,getPacket;
    FILE * fileIn,*fileOut;
    int frameCount=0;
    /* register all the codecs */
    av_register_all();

    if(argc!=3){
        fprintf(stdout,"usage:./a.out xxx.pcm xxx.aac\n");
        return -1;
    }

    //1.我们需要读一帧一帧的数据,所以需要AVFrame结构
    //读出的一帧数据保存在AVFrame中。
    frame  = av_frame_alloc();
    frame->format = AV_SAMPLE_FMT_S16;
    frame->nb_samples = 1024;
    frame->sample_rate = 11025;
    frame->channels = 2;
    fileIn =fopen(argv[1],"r+");
    frame->data[0] = av_malloc(1024*4);


    //2.读出来的数据保存在AVPacket中,因此,我们还需要AVPacket结构体
    //初始化packet
    memset(&packet, 0, sizeof(AVPacket));  
    av_init_packet(&packet);


    //3.读出来的数据,我们需要编码,因此需要编码器
    //下面的函数找到h.264类型的编码器
    /* find the mpeg1 video encoder */
    codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
    if (!codec){
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }

    //有了编码器,我们还需要编码器的上下文环境,用来控制编码的过程
    codecContext = avcodec_alloc_context3(codec);//分配AVCodecContext实例
    if (!codecContext){
        fprintf(stderr, "Could not allocate video codec context\n");
        return -1;
    }
    /* put sample parameters */  
    codecContext->sample_rate = frame->sample_rate;  
    codecContext->channels = frame->channels;  
    codecContext->sample_fmt = frame->format;  
    /* select other audio parameters supported by the encoder */  
    codecContext->channel_layout = AV_CH_LAYOUT_STEREO;
    //准备好了编码器和编码器上下文环境,现在可以打开编码器了
    //根据编码器上下文打开编码器
    if (avcodec_open2(codecContext, codec, NULL) < 0){
        fprintf(stderr, "Could not open codec\n");
        return -1;
    }
    //4.准备输出文件
    fileOut= fopen(argv[2],"w+");
    //下面开始编码
    while(1){
        //读一帧数据出来
        readSize = fread(frame->data[0],1,1024*4,fileIn);
        if(readSize == 0){
            fprintf(stdout,"end of file\n");
            frameCount++;
            break;
        }
        //初始化packet
        av_init_packet(&packet);
        /* encode the image */
        frame->pts = frameCount;
        ret = avcodec_encode_audio2(codecContext, &packet, frame, &getPacket); //将AVFrame中的像素信息编码为AVPacket中的码流
        if (ret < 0) {
            fprintf(stderr, "Error encoding frame\n");
            goto out;
        }

        if (getPacket) {
            frameCount++;
            //获得一个完整的编码帧
            printf("Write frame %3d (size=%5d)\n", frameCount, packet.size);
            fwrite(packet.data, 1,packet.size, fileOut);
            av_packet_unref(&packet);
        }

    }

    /* flush buffer */
    for ( getPacket= 1; getPacket; frameCount++){
        frame->pts = frameCount;
        ret = avcodec_encode_audio2(codecContext, &packet, NULL, &getPacket);       //输出编码器中剩余的码流
        if (ret < 0){
            fprintf(stderr, "Error encoding frame\n");
            goto out;
        }
        if (getPacket){
            printf("flush buffer Write frame %3d (size=%5d)\n", frameCount, packet.size);
            fwrite(packet.data, 1, packet.size, fileOut);
            av_packet_unref(&packet);
        }
    } //for (got_output = 1; got_output; frameIdx++) 

out:
    fclose(fileIn);
    fclose(fileOut);
    av_frame_free(&frame);
    avcodec_close(codecContext);
    av_free(codecContext);
    return 0;
}

实验

使能aac编码器

现在,我们还不能运行这个程序,因为我们在便宜ffmpeg工程的时候没有使能aac编码器,所以我们需要重新编译ffmpeg,是的aac编码器可用。
libfdk_aac:
官网关于使能aac的介绍
-enable-nonfree –enable-libfdk-aac
同时我们需要安装libfdk-aac库:
sudo apt-get install libfdk-aac-dev
然后我们需要重新配置和便宜ffmpeg工程:
1.执行

./configure --enable-libx264 --enable-gpl --enable-decoder=h264 --enable-encoder=libx264 --enable-shared --enable-static --disable-yasm --enable-nonfree  --enable-libfdk-aac --enable-shared --prefix=tmp 

2.make
3.make install

运行程序

有了aac编码器以后,再次运行这个程序,就可以将pcm格式的音频数据编码为aac格式了,例如,执行如下命令:
./out.bin recorder.pcm recorder.aac
使用ffplay recorder.aac即可听到声音。

附注

Makefile

VAR_INCLUDE := -I /home/mlinux/work/ffmpeg-2.7/ffmpeg-2.7.6/tmp/include
VAR_SHARED_LIB_DIR = -L /home/mlinux/work/ffmpeg-2.7/ffmpeg-2.7.6/tmp/lib
VAR_SHARED_LIBS = -l avcodec -l avformat  -l avdevice -l avutil -l swresample -l avfilter -l swscale

out.bin:pcm_2_aac.o
    gcc  -o $@ $^  $(VAR_INCLUDE)  $(VAR_SHARED_LIB_DIR) $(VAR_SHARED_LIBS) 

%.o:%.c
    gcc   -c $<  $(VAR_INCLUDE)  $(VAR_SHARED_LIB_DIR) $(VAR_SHARED_LIBS)
clean:
    rm -rf *.o *.bin

init.sh

export LD_LIBRARY_PATH=/home/mlinux/work/ffmpeg-2.7/ffmpeg-2.7.6/tmp/lib

看过前面文章的reader就应该知道,init.sh就是到处共享库的位置,让我们的可执行程序知道动态链接库的位置。

  • 0
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值