ffmpeg pcm转化为aac

我们先看一张pcm转化为aac的流程图

                                                                                                    

我们根据流程图

第一步获取AVOutputFormat

ff_const59 AVOutputFormat *av_guess_format(const char *short_name,

const char *filename,

const char *mime_type);

函数会根据文件名字,mime类型返回最适合的输出格式

第二步创建我们要创建的aac文件的上下文AVFormatContext *ofmt_ctx

avformat_alloc_output_context2(&ofmt_ctx, oformat, oformat->name, output)

对aac文件的操作要用到上下文,创建者上下文,我们知道要用AVOutputFormat的内容,还有输出文件的地址

第三步 创建编码器

我们要对pcm进行aac编码,要设置编码器的codec_type,位深,声道布局,声道数, 采样率,比特率,profile(具体的aac,aac low, aac he v1, aac he v2)

第四步创建输出流并设置输出流的编码参数

AVStream *out_stream = avformat_new_stream(ofmt_ctx, pCodec);

avcodec_parameters_from_context(out_stream->codecpar, pCodecCtx);

第五步 打开编码器 avcodec_open2(pCodecCtx, pCodec, NULL)

第六步 获取输出aac文件的AVIOContext

avio_open

第七步 写aac文件头

avformat_write_header adif 和 adts头

第八步 创建AVFrame ,设置声道数,单通道采样数,位深

第九步 初始化重采样上下文,设置重采样的输入格式输出格式

第十步 从输入文件读取单通道采样数 * 通道数 * 位深字节数大小的数据

第十一步 对读取的数据进行重采样

第十二步 设置pts,表明当前数据在aac的位置

第十三步 avcodec_send_frame 发送数据到编码器

第十四步 avcodec_receive_packet 获取到编码数据 并通过av_write_frame写入aac文件

第十五步 循环 读取数据并写入文件

第十六步 flush_encoder

第十七步 av_write_trailer

第十八步 释放资源

 

#include <stdio.h>
#include <stdlib.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswresample/swresample.h>

int flush_encoder(AVFormatContext *ofmt_ctx, AVCodecContext * pCodecCtx,  int stream_index)
{
    AVCodecParameters *codecpar  = ofmt_ctx->streams[stream_index]->codecpar;
    int got_fame = 0;
    AVPacket *pkt = av_packet_alloc();
    int ret = 0;
    while (1)
    {      
        av_init_packet(pkt);
        pkt->data = NULL;
        pkt->size = 0;
        if (avcodec_send_frame(pCodecCtx, NULL) < 0)
        {
            printf("fail to send frame\n");
            return -1;
        }

        // 第十四步  avcodec_receive_packet  获取到编码数据  并通过av_write_frame写入aac文件
        while (avcodec_receive_packet(pCodecCtx, pkt) >= 0)
        {
            pkt->stream_index = stream_index;
            av_write_frame(ofmt_ctx, pkt);
            av_packet_unref(pkt);
        }
       
        if (ret < 0)
        {
            break;
        }
    }
    av_packet_free(&pkt);
    return 0;
}

int main(int argc, char *argv[])
{
    int ret = 0;
    const char *input;
    const char *output;
    input = argv[1];
    output = argv[2];
    // 第一步获取AVOutputFormat
    AVOutputFormat *oformat = av_guess_format(NULL, output, NULL);
    if (oformat == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "fail to find the output format\n");
        ret = -1;
        goto __ERROR;
    }

    // 第二步创建我们要创建的aac文件的上下文AVFormatContext *ofmt_ctx
    AVFormatContext *ofmt_ctx = avformat_alloc_context();
    if (avformat_alloc_output_context2(&ofmt_ctx, oformat, oformat->name, output) < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "fail to alloc output context\n");
        ret = -1;
        goto __ERROR;
    }

    //第三步 创建编码器
    AVCodec *pCodec = avcodec_find_encoder_by_name("libfdk_aac");
    AVCodecContext *pCodecCtx = avcodec_alloc_context3(pCodec);
    pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
    pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16; //其他会出错
    pCodecCtx->channel_layout = AV_CH_LAYOUT_STEREO;
    pCodecCtx->channels = av_get_channel_layout_nb_channels(pCodecCtx->channel_layout);
    pCodecCtx->sample_rate = 44100;
    pCodecCtx->bit_rate = 0;
    pCodecCtx->profile = FF_PROFILE_AAC_HE_V2;

    //第四步创建输出流并设置输出流的编码参数
    AVStream *out_stream = avformat_new_stream(ofmt_ctx, pCodec);
    avcodec_parameters_from_context(out_stream->codecpar, pCodecCtx);
    if (out_stream == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "fail to create new stream\n");
        ret = -1;
        goto __ERROR;
    }
    if (pCodec == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "fail to find codec\n");
        ret = -1;
        goto __ERROR;
    }

    // 第五步 打开编码器
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "fail to open codec\n");
        ret = -1;
        goto __ERROR;
    }
    av_dump_format(ofmt_ctx, 0, output, 1);

    // 第六步 获取输出aac文件的AVIOContext
    if (avio_open(&ofmt_ctx->pb, output, AVIO_FLAG_WRITE) < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "fail to open output\n");
        ret = -1;
        goto __ERROR;
    }

    // 第七步写aac文件头
    if (avformat_write_header(ofmt_ctx, NULL) < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "fail to write header");
        ret = -1;
        goto __ERROR;
    }

    //第八步创建AVFrame ,设置声道数,位深,单通道采样数,为pFrame中的data分配空间
    AVFrame *pframe = av_frame_alloc();
    pframe->channels = pCodecCtx->channels;
    pframe->format = pCodecCtx->sample_fmt;
    pframe->nb_samples = pCodecCtx->frame_size;
    int size = av_samples_get_buffer_size(NULL, pCodecCtx->channels, pCodecCtx->frame_size, pCodecCtx->sample_fmt, 1);
    uint8_t *out_buffer = (uint8_t *)av_malloc(size);
    avcodec_fill_audio_frame(pframe, pCodecCtx->channels, pCodecCtx->sample_fmt, (const uint8_t *)out_buffer, size, 1);
    
    // 第九步 初始化重采样上下文,设置重采样的输入格式输出格式
    SwrContext *pSwrCtx = swr_alloc();
    swr_alloc_set_opts(pSwrCtx, pCodecCtx->channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate,
                       pCodecCtx->channel_layout, AV_SAMPLE_FMT_S16, 44100, 0, NULL);
    swr_init(pSwrCtx);
    /*重采样后的数据保存的缓冲区*/
    uint8_t **data = (uint8_t **)av_calloc(pCodecCtx->channels, sizeof(*data));
    av_samples_alloc(data, NULL, pCodecCtx->channels, pCodecCtx->frame_size, pCodecCtx->sample_fmt, 1);



    AVPacket *pkt = av_packet_alloc();
    av_new_packet(pkt, size);
    pkt->data = NULL;
    pkt->size = 0;

    FILE *fp = fopen(input, "rb");
    if (fp == NULL)
    {
        printf("fail to open file\n");
        ret = -1;
        goto __ERROR;
    }
    int count = 1;
    while (1)
    {
    // 第十步 从输入文件读取单通道采样数 * 通道数 * 位深字节数大小的数据     
       size = pframe->nb_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * pframe->channels;
        if (fread(out_buffer, 1, size, fp) < 0)
        {
            printf("fail to read raw data\n");
            ret = -1;
            goto __ERROR;
        }
        else if (feof(fp))
        {
            break;
        }

        // 第十一步 对读取的数据进行重采样
        swr_convert(pSwrCtx, data, pCodecCtx->frame_size, (const uint8_t **)pframe->data, pframe->nb_samples);
        size = pCodecCtx->frame_size * av_get_bytes_per_sample(pCodecCtx->sample_fmt);
        memcpy(pframe->data[0], data[0], size);
        memcpy(pframe->data[1], data[1], size);
        
        // 第十二步  设置pts,表明当前数据在aac的位置
        pframe->pts = count * 100;

        // 第十三步 avcodec_send_frame 发送数据到编码器
        if (avcodec_send_frame(pCodecCtx, pframe) < 0)
        {
            printf("fail to send frame\n");
            ret = -1;
            goto __ERROR;
        }
        
        // 第十四步  avcodec_receive_packet  获取到编码数据  并通过av_write_frame写入aac文件
        while (avcodec_receive_packet(pCodecCtx, pkt) >= 0)
        {
            pkt->stream_index = out_stream->index;
            av_log(NULL, AV_LOG_INFO, "write %d frame\n", count);
            av_write_frame(ofmt_ctx, pkt);
            av_packet_unref(pkt);
        }
        count++;
       
    }
    // 第十六步 flush_encoder
    flush_encoder(ofmt_ctx, pCodecCtx, out_stream->index);

    // 第十七步  av_write_trailer
    av_write_trailer(ofmt_ctx);

__ERROR:
    // 第十八步  释放资源 
    if (pkt)
    {
        av_packet_free(&pkt);
    }
    if (pSwrCtx)
    {
        swr_free(&pSwrCtx);
    }
    if (out_buffer)
    {
        av_free(out_buffer);
    }
    if (pframe)
    {
        av_frame_free(&pframe);
    }

    if (ofmt_ctx->pb)
    {
        avio_close(ofmt_ctx->pb);
    }

    if (pCodec)
    {
    }

    if (pCodecCtx)
    {
        avcodec_close(pCodecCtx);
        avcodec_free_context(&pCodecCtx);
    }

    if (ofmt_ctx)
    {
        avformat_free_context(ofmt_ctx);
    }

    return 0;
}

 

对于音频来说最重要的是采样率、采样大小、声道数(通道数)。

用慢来对抗块;

用体系对抗碎片;

用原理对抗招式。

加油!!!!!!!!!!!!!

 

 

 

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值