Bootstrap

创建AAC编码器

 

创建编码器的流程图

如何创建并打开编码器

  • 创建编码器 avcodec_find_encoder(<#enum AVCodecID id#>)
  • 创建上下文 avcodec_alloc_context3(<#const AVCodec *codec#>)
  • 打开编码器 avcodec_open2(<#AVCodecContext *avctx#>, <#const AVCodec *codec#>, <#AVDictionary **options#>)
  • 将数据送给编码器 avcodec_send_frame(<#AVCodecContext *avctx#>, <#const AVFrame *frame#>)
  • 获取编码后的数据 avcodec_receive_packet(<#AVCodecContext *avctx#>, <#AVPacket *avpkt#>), <#AVFrame *frame#>)
  • AVFrame 没有编码的数据
  • AVPacket 编码后的数据。

 

AVCodecContext* open_coder(){
    //查找编码器
    int ret;
    char error[1024] = {0, };
    AVCodec *codec = avcodec_find_encoder_by_name("libfdk_aac");  
    //    AVCodec *codec =  avcodec_find_encoder(AV_CODEC_ID_AAC);
    //
    //    //创建上下文
    AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
    codec_ctx->sample_fmt = AV_SAMPLE_FMT_S16; //输入音频的采样率
    codec_ctx->channel_layout = AV_CH_LAYOUT_STEREO; //输入音频的通道布局
    codec_ctx->channels = 2; //输入音频的通道个数
    codec_ctx->sample_rate = 44100; //输入音频的采样率
    //bitrate 和profile互斥,bit_rate设置为0后,才会查找profile的值
    codec_ctx->bit_rate = 0; //AAC 128K AAC HE 64K AAC HE V2 32K
    codec_ctx->profile = FF_PROFILE_AAC_HE_V2;
    
    //打开编码器
    if((ret = avcodec_open2(codec_ctx, codec, NULL)) < 0){
        av_strerror(ret, error, 1024);
        printf(stderr, "Failed to open audio devices, [%d] %s\n", ret, error);
        return  NULL;
    }
    
    return codec_ctx;
    
}

在打开编码器的工程中发现以下两个问题

1、通过avcodec_find_encoder_by_name查找的aac编码器,查看编码的位深是

那么上下文的位深必须是16位 AV_SAMPLE_FMT_S16

通道数必须是2

通道布局必须是AV_CH_LAYOUT_STEREO

我自己设置为通道数是1 通道布局是AV_CH_LAYOUT_MONO。打开编码器的时候提示SBR初始化错误。具体原因还需查找。

2、通过avcodec_find_encoder查找的aac编码器,

位深是AV_SAMPLE_FMT_FLTP,否则打开编码的过程中发现参数错误。这个我是通过查找的编码器后在

那么上下文的位深必须是float类型  AV_SAMPLE_FMT_FLTP

 

 

查找编码器,分配上下文,设置上下文参数,打开编码器后。接下来就应该将数据输入编码器。

如何将数据输入编码器呢?

这里讲两个概念AVFrame,AVPacket.

AVFrame是没有编码的数据,AVPacket是编码后的数据。

所以就是将AVFrame输入编码器进行编码,然后得到AVPacket数据,然后将AVPacket的保存进文件就是aac数据了。

由于ffmpeg的avformat_open_input是将多媒体文件打开。avformat_open_input既可以打开文件,也可以打开设备。avformat_open_input就是将所有的东西当做多媒体文件打开,所以通过av_read_frame读出AVPacket数据,按道理说AVPacket存的是编码后的数据,可是这里存储的是PCM原始数据。

编码器需要的是AVFrame原始数据,所以我们这里把AVPacket数据存进AVFrame当中。

如何创建AVFrame

有两个函数

av_frame_alloc 

av_frame_get_buffer  实际上数据是存在AVFrame中buffer当中,会根据设置的frame的采样大小(位深),通道个数,采样个数进行分配buffer。

AVFrame* create_frame(){
    AVFrame *frame = av_frame_alloc(); //其中的buffer才是存着主要的数据
    if(!frame){
        return NULL;
    }
    frame->nb_samples = 512; //因为我的电脑的每个包的大小是2048/位深32位(4个字节)/1个通道 所以就是2048/4/1=512
    frame->format = AV_SAMPLE_FMT_S16;//AAC需要的位深
    frame->channel_layout = AV_CH_LAYOUT_STEREO; //AAC需要的通道
    av_frame_get_buffer(frame, 0);
    if(!frame->data[0]){
        printf("Error, Failed to alloc buf in frame!\n");
        //内存泄漏
        goto __ERROR;
    }
    return frame;
__ERROR:
    if(frame){
        av_frame_free(&frame);
    }
    return NULL;
    
}

 

如何存进去呢?

1、创建缓存区

 av_samples_alloc_array_and_samples(&dst_data,
                                       &dst_linesize,
                                       2,
                                       512,
                                       AV_SAMPLE_FMT_S16,
                                       0);

2、将AVPacket中的数据放进缓存区

        memcpy((void *)dst_data[0], (void *)pkt.data, pkt.size);

3、将缓存区的数据放进AVFrame

        memcpy((void *)frame->data[0], (void*)dst_data[0], dst_linesize);

4、将frame数据送给编码器

    ret = avcodec_send_frame(codec_ctx, frame);

5、接受编码后的数据AVPacket

        ret = avcodec_receive_packet(codec_ctx, pkt);

avcodec_send_frame传入要编码的数据不一定传入就会马上返回编码后的数据,而是编码好多数据后多次返回。

所以获取编码后的数据要下面这样写

 while(ret > 0){
        ret = avcodec_receive_packet(codec_ctx, pkt);
        if(ret < 0 ){
            if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
                break;
            }else{
                printf("error encodeing avframe");
                exit(-1);
            }
        }
        
        
        fwrite(pkt->data,pkt->size, 1, outFile);
        fflush(outFile);
        av_packet_unref(&pkt);
    }

直到avcodec_receive_packet返回<= 0,说明当前没有要处理的数据了,可以接受下一个AVFrame数据进行处理了。

这个<=0 又有下面的情况

返回值是AVERROR(EAGAIN) 当前数据还不能处理

返回值是AVERROR_EOF  当前数据读完了 

其他情况 发生了编码异常,直接终止程序

6、读取到编码后的数据后写入文件

 fwrite(pkt->data,pkt->size, 1, outFile);
 fflush(outFile);

 

在实践过程中 总是发现解码返回-35.那么对于这样就应该休眠一会再继续。

源代码地址

https://gitee.com/creat151/ffmpeg.git

 

 

 

 

;