ffmpeg音视频编码入门:音频解码(acc/mp3 转 pcm)

1、目标:使用ffmpeg库函数,将音频码流(AAC、mp3等)解码为pcm采样数据


2、音频解码过程

1)根据输入的码流文件,初始化格式上下文(fmt_ctx);
2)根据格式上下文,找到合适的解码器(Codec),同时返回流索引(音频对应streams[0],视频对应stream[1]);
3)根据格式上下文,获取编码时的参数设置(fmt_ctx->streams[index]->codecpar);
4)创建解码器的上下文(cod_ctx),根据编码参数初始化解码器上下文,打开解码器;
5)设置解码输出的相关参数,初始化重采样上下文
6)创建packet存储原来的编码数据,创建frame存储解码后的像素/采样数据
7)循环读取一帧编码数据,并进行转码,转码后的采样数据写入输出文件。
8)关闭文件,释放资源


3、代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"

#define sourcefile "testvideo/output.aac"

int init_fmt_ctx(AVFormatContext **fmt_ctx, const char *file) {
    *fmt_ctx = avformat_alloc_context();
    if (*fmt_ctx == NULL) {
        printf("avformat_alloc failed.\n");
        return -1;
    } 
    if (avformat_open_input(fmt_ctx, file, NULL, NULL) != 0) {
        printf("Couldn't open input stream.\n");
        return -1;
    }

    if (avformat_find_stream_info(*fmt_ctx, NULL) < 0) {
        printf("Couldn't find stream information.\n");
        return -1;
    }
    // 转存有效信息到stderr
    av_dump_format(*fmt_ctx, 0, file, 0);
    return 0;
}

int main(int argc, char *argv[])
{
    AVFormatContext *fmt_ctx = NULL;
    AVCodecContext *cod_ctx = NULL;
    AVCodec *cod = NULL; 
    AVPacket packet;

    // 1. 打开相关流,初始化fmt_ctx
    if (init_fmt_ctx(&fmt_ctx, sourcefile) < 0) 
        goto _Error;

    // 2. 找到合适的编解码器,返回第一帧数据索引
    int stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, 
-1, &cod, -1);
    if (cod == NULL) {
        printf("Couldn't find codec.\n");
        goto _Error;
    }
    // printf("stream_index:%d\n", stream_index);

    // 3. 获取编码时的参数
    AVCodecParameters *param = fmt_ctx->streams[stream_index]->codecpar;
    // 4. 设置解码上下文,并打开解码器
    cod_ctx = avcodec_alloc_context3(cod);
    avcodec_parameters_to_context(cod_ctx, param);
    if (avcodec_open2(cod_ctx, cod, NULL) < 0) {
        printf("Couldn't open codec.\n");
        goto _Error;
    }
    
    // 5. 重采样初始化和前后参数设置
    
    /*********** 设置输出相关参数 ******************/
    int out_channels = param->channels;
    int out_sample_fmt = AV_SAMPLE_FMT_S16;
    int out_sample_rate = param->sample_rate;
    int out_nb_samples = param->frame_size;
    uint64_t out_channel_layout = param->channel_layout;

    // 初始化重采样上下文
    struct SwrContext *convert_ctx = swr_alloc();
    convert_ctx = swr_alloc_set_opts(convert_ctx, \
            out_channel_layout, out_sample_fmt, out_sample_rate, \  
            param->channel_layout, param->format, param->sample_rate,\
            0, NULL);
    swr_init(convert_ctx);


    // 6. 创建packet,存储编码数据
    av_init_packet(&packet);
    // 创建Frame, 存储解码后的采样数据
    AVFrame *frame = av_frame_alloc();
    frame->channels = out_channels;          // 通道数不变
    frame->format = out_sample_fmt;          // 采样位深16bit
    frame->nb_samples = out_nb_samples;      // 单通道采样个数
    av_frame_get_buffer(frame, 0);


    // 7. 每次读取一帧编码数据,然后进行转码
    FILE *fout = fopen("output.pcm", "w+");
    uint8_t **data = (uint8_t **)av_calloc(1, sizeof(*data));
    int data_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;

        if (avcodec_send_packet(cod_ctx, &packet) < 0) {
            printf("Decode error.\n");
            goto _Error;
        }
        while (avcodec_receive_frame(cod_ctx, frame) >= 0) {
            swr_convert(convert_ctx, data, data_size, \
                    (const uint8_t **)frame->data, frame->nb_samples);
            fwrite(data[0], 1, data_size, fout);
        }
        av_packet_unref(&packet);
    }

_Error:
    // 8. 关闭文件,释放资源    
    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 (convert_ctx) {
        swr_free(&convert_ctx);
    }
    if (fout) { 
        fclose(fout);
    }
    return 0;
}

4、编译运行:

image-20210502220202619

#!/bin/sh

# myrun.sh --- ffmpeg相关程序运行脚本
file=$1

gcc $file.c -I include/ -L lib/ -lavdevice -lavformat -lavfilter -lavcodec -lswresample -lswscale -lavutil  # -o $file

相关文章推荐:AAC音频码流解析

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值