ffmpeg aac转pcm

我们首先明确一点aac是编码后的数据,pcm是原始数据。所以我们也就是要对aac进行解码操作。

我们先上解码的流程图,然后一步一步进行解析。

                                                                       

第一步创建输入文件AVFormatContext

    fmt_ctx = avformat_alloc_context();
    if (fmt_ctx == NULL)
    {
        ret = -1;
        printf("alloc fail");
        goto __ERROR;
    }
    if (avformat_open_input(&fmt_ctx, in_file, NULL, NULL) != 0)
    {
        ret = -1;
        printf("open fail");
        goto __ERROR;
    }

第二步 查找文件相关流,并初始化AVFormatContext中的流信息

 if (avformat_find_stream_info(fmt_ctx, NULL) < 0)
     {
        ret = -1;
        printf("find stream fail");
        goto __ERROR;
    }

    av_dump_format(fmt_ctx, 0, in_file, 0);

avformat_find_stream_info这一步很重要,不然AVFormatContext中的流信息是空的。

 

第三步查找音频流索引和解码器

    int stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &cod, -1);

这一步我们就可以获得音频流的索引和解码所需要的解码器

 

第四步设置解码器上下文并打开解码器

AVCodecParameters *codecpar = fmt_ctx->streams[stream_index]->codecpar;
    if (!cod)
    {
        ret = -1;
        printf("find codec fail");
        goto __ERROR;
    }
    cod_ctx = avcodec_alloc_context3(cod);
    avcodec_parameters_to_context(cod_ctx, codecpar);
    ret = avcodec_open2(cod_ctx, cod, NULL);
    if (ret < 0)
    {
        printf("can't open codec");
        goto __ERROR;
    }

打开解码器需要解码上下文,解码器上下文的参数我们通过avcodec_parameters_to_context从AVFormatContext对应流的codecpar参数获取

第五步打开输出文件

 FILE *out_fb = NULL;
    out_fb = fopen(out_file, "wb");
    if (!out_fb)
    {
        printf("can't open file");
        goto __ERROR;
    }

 

 

第六步创建Frame,用于存储解码后的数据

//设置转码后输出相关参数
    //采样的布局方式
    uint64_t out_channel_layout = codecpar->channel_layout;
    //采样个数
    int out_nb_samples = codecpar->frame_size;
    //采样格式
    enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
    //采样率
    int out_sample_rate = codecpar->sample_rate;
    //通道数
    int out_channels = codecpar->channels;
    printf("%d\n", out_channels);

    //第六步创建Frame,用于存储解码后的数据
    AVFrame *frame = av_frame_alloc();
    frame->channels = out_channels;
    frame->format = out_sample_fmt;
    frame->nb_samples = out_nb_samples;
    av_frame_get_buffer(frame, 0);

解码后的数据存储于AVFrame->data中,所需要的空间根据通道数,单通道采样数,采样大小(位深)通过av_samples_get_buffer_size获取,通过avcodec_fill_audio_frame关联

第七步重采样初始化与设置参数

struct SwrContext *convert_ctx = swr_alloc();
    convert_ctx = swr_alloc_set_opts(convert_ctx,
                                     out_channel_layout,
                                     out_sample_fmt,
                                     out_sample_rate,
                                     codecpar->channel_layout,
                                     codecpar->format,
                                     codecpar->sample_rate,
                                     0,
                                     NULL);
    swr_init(convert_ctx);
    int buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 0);
    // uint8_t **data = (uint8_t **)av_calloc((size_t)out_channels, sizeof(*data));
    uint8_t **data = (uint8_t **)av_calloc(1, sizeof(*data));
    int alloc_size = av_samples_alloc(data,
                                      NULL,
                                      out_channels,
                                      out_nb_samples,
                                      out_sample_fmt,
                                      0);

 

第八步 读取数据并解码

while (av_read_frame(fmt_ctx, &packet) >= 0)
    {

        if (packet.stream_index != stream_index)
        {
            continue;
        }

        
        ret = avcodec_send_packet(cod_ctx, &packet);
        if (ret < 0)
        {
            ret = -1;
            printf("decode error");
            goto __ERROR;
        }

        while (avcodec_receive_frame(cod_ctx, frame) >= 0)
        {
            swr_convert(convert_ctx, data, alloc_size, (const uint8_t **)frame->data, frame->nb_samples);
            fwrite(data[0], 1, buffer_size, out_fb);
        }

        av_packet_unref(&packet);
    }

音频的数据只存在于frame->data[0]中

下面是完整代码


#include <stdio.h>
#include <stdlib.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswresample/swresample.h>
const char *in_file = "/Users/yuanxuzhen/study/mac/ffmpeg_demo/output/src.aac";
const char *out_file = "/Users/yuanxuzhen/study/mac/ffmpeg_demo/output/dst.pcm";

int main()
{

    AVFormatContext *fmt_ctx = NULL;
    AVCodecContext *cod_ctx = NULL;
    AVCodec *cod = NULL;
    int ret = 0;
    AVPacket packet;

    //第一步创建输入文件AVFormatContext
    fmt_ctx = avformat_alloc_context();
    if (fmt_ctx == NULL)
    {
        ret = -1;
        printf("alloc fail");
        goto __ERROR;
    }
    if (avformat_open_input(&fmt_ctx, in_file, NULL, NULL) != 0)
    {
        ret = -1;
        printf("open fail");
        goto __ERROR;
    }

    //第二步 查找文件相关流,并初始化AVFormatContext中的流信息
    if (avformat_find_stream_info(fmt_ctx, NULL) < 0)
    {
        ret = -1;
        printf("find stream fail");
        goto __ERROR;
    }

    av_dump_format(fmt_ctx, 0, in_file, 0);

    //第三步查找音频流索引和解码器
    int stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &cod, -1);

    //第四步设置解码器上下文并打开解码器
    AVCodecParameters *codecpar = fmt_ctx->streams[stream_index]->codecpar;
    if (!cod)
    {
        ret = -1;
        printf("find codec fail");
        goto __ERROR;
    }
    cod_ctx = avcodec_alloc_context3(cod);
    avcodec_parameters_to_context(cod_ctx, codecpar);
    ret = avcodec_open2(cod_ctx, cod, NULL);
    if (ret < 0)
    {
        printf("can't open codec");
        goto __ERROR;
    }

    //第五步打开输出文件
    FILE *out_fb = NULL;
    out_fb = fopen(out_file, "wb");
    if (!out_fb)
    {
        printf("can't open file");
        goto __ERROR;
    }

    //创建packet,用于存储解码前的数据
    av_init_packet(&packet);

    //设置转码后输出相关参数
    //采样的布局方式
    uint64_t out_channel_layout = codecpar->channel_layout;
    //采样个数
    int out_nb_samples = codecpar->frame_size;
    //采样格式
    enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
    //采样率
    int out_sample_rate = codecpar->sample_rate;
    //通道数
    int out_channels = codecpar->channels;
    printf("%d\n", out_channels);

    //第六步创建Frame,用于存储解码后的数据
    AVFrame *frame = av_frame_alloc();
    frame->channels = out_channels;
    frame->format = out_sample_fmt;
    frame->nb_samples = out_nb_samples;
    av_frame_get_buffer(frame, 0);



    //第七步重采样初始化与设置参数
    struct SwrContext *convert_ctx = swr_alloc();
    convert_ctx = swr_alloc_set_opts(convert_ctx,
                                     out_channel_layout,
                                     out_sample_fmt,
                                     out_sample_rate,
                                     codecpar->channel_layout,
                                     codecpar->format,
                                     codecpar->sample_rate,
                                     0,
                                     NULL);
    swr_init(convert_ctx);
    int buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 0);
    // uint8_t **data = (uint8_t **)av_calloc((size_t)out_channels, sizeof(*data));
    uint8_t **data = (uint8_t **)av_calloc(1, sizeof(*data));
    int alloc_size = av_samples_alloc(data,
                                      NULL,
                                      out_channels,
                                      out_nb_samples,
                                      out_sample_fmt,
                                      0);
                                      

    //while循环,每次读取一帧,并转码
    //第八步 读取数据并解码,重采样进行保存
    while (av_read_frame(fmt_ctx, &packet) >= 0)
    {

        if (packet.stream_index != stream_index)
        {
            continue;
        }

        
        ret = avcodec_send_packet(cod_ctx, &packet);
        if (ret < 0)
        {
            ret = -1;
            printf("decode error");
            goto __ERROR;
        }

        while (avcodec_receive_frame(cod_ctx, frame) >= 0)
        {
            swr_convert(convert_ctx, data, alloc_size, (const uint8_t **)frame->data, frame->nb_samples);
            fwrite(data[0], 1, buffer_size, out_fb);
        }

        av_packet_unref(&packet);
    }

__ERROR:
    if (fmt_ctx)
    {
        avformat_close_input(&fmt_ctx);
        avformat_free_context(fmt_ctx);
    }

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

    if (out_fb)
    {
        fclose(out_fb);
    }

    if (frame)
    {
        av_frame_free(&frame);
    }

    if (convert_ctx)
    {
        swr_free(&convert_ctx);
    }

    return 0;
}

 

我们三篇文章实现了三个功能

1、从多媒体文件提取aac

2、从pcm编码到aac

3、从aac解码到pcm

 

再一次提醒自己

用慢来对抗快;

用原理来对抗招式;

用体系来对抗碎片。

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

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值