音视频 ffmpeg实现pcm转aac

资源准备

  1. 本地pcm文件 ->采样率44100 双通道 AV_SAMPLE_FMT_S16格式
  2. 压缩后的aac ->采样率44100 双通道 AV_SAMPLE_FMT_FLTP格式

实现思路

  1. 创建输出上下文 avformat_alloc_output_context2();
  2. 查找编码器 avcodec_find_encoder();
  3. 创建编码器上下文 avcodec_alloc_context3();
  4. 设置编码器具体参数
  5. 打开编码器
  6. 创建流
  7. 打开输出文件
  8. 写入头文件
  9. 初始化重采样相关
  10. 初始化AVFrame,AVPacket
  11. 计算每次读取pcm一帧的样本大小以及创建缓存空间
  12. 打开输入文件
  13. 开始循环读取,进行重采样然后写入本地文件
  14. 读取完毕后写入文件索引
  15. 释放资源

代码

AVFormatContext *audioFmtCtx;

AVCodec *audioCodec;

AVCodecContext *audioCtx;

AVStream *audioStream;

SwrContext *swrCtx;

AVFrame *audioFrame;

void PcmToAac::PcmToAacInit() {
    const char *inFile = "/sdcard/aaa/test.pcm"; //pcm  44100 双通道 AV_SAMPLE_FMT_S16
    const char *outFile = "/sdcard/aaa/test.aac"; //aac 44100 双通道 AV_SAMPLE_FMT_FLTP
    int ret = 0;

    //创建输出上下文
    ret = avformat_alloc_output_context2(&audioFmtCtx, NULL, NULL, outFile);
    if (ret < 0) {
        LOGE("创建输出上下文失败");
    }

    //查找编码器
    audioCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
    if (!audioCodec) {
        LOGE("AAC编码器寻找失败");
    }

    //创建编码器上下文
    audioCtx = avcodec_alloc_context3(audioCodec);
    if (!audioCtx) {
        LOGE("AAC编码器上下文创建失败");
    }

    audioCtx->channels = 2; //通道数量
    audioCtx->channel_layout = AV_CH_LAYOUT_STEREO; //通道类型
    audioCtx->sample_rate = 44100; //采样率
    audioCtx->sample_fmt = AV_SAMPLE_FMT_FLTP; //采样数据格式

    audioCtx->bit_rate = 64000; //码率
    audioCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; //公共头部

    //打开编码器
    ret = avcodec_open2(audioCtx, audioCodec, NULL);
    if (ret != 0) {
        LOGE("打开编码器失败");
    }

    //创建流
    audioStream = avformat_new_stream(audioFmtCtx, NULL);
    audioStream->codecpar->codec_tag = 0;

    //拷贝参数到流中
    avcodec_parameters_from_context(audioStream->codecpar, audioCtx);

    //打开输出文件(aac文件)
    ret = avio_open(&audioFmtCtx->pb, outFile, AVIO_FLAG_WRITE);
    if (ret < 0) {
        LOGE("打开输出文件失败");
    }

    //写入头文件(成功以后会创建一个输出的文件)
    avformat_write_header(audioFmtCtx, NULL);

    /**
     * SwrContext参数设置
     * 第一个参数SwrContext
     * 第二个参数输出的通道类型
     * 第三个参数输出的采样格式
     * 第四个参数输出的采样率
     * 第五个参数输入的通道类型
     * 第六个参数输入的采样格式
     * 第七个参数输入的采样率
     * 第八个参数偏移量
     * 第九个参数是否需要日志
     */
    swrCtx = swr_alloc_set_opts(swrCtx, audioCtx->channel_layout,
                                audioCtx->sample_fmt, audioCtx->sample_rate,
                                AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16,
                                44100, 0, 0);
    if (!swrCtx) {
        LOGE("SwrContext参数设置失败");
    }

    //初始化SwrContext
    ret = swr_init(swrCtx);
    if (ret < 0) {
        LOGE("SwrContext初始化失败");
    }

    //初始化audioFrame
    audioFrame = av_frame_alloc();
    audioFrame->format = AV_SAMPLE_FMT_FLTP;
    audioFrame->channels = 2;
    audioFrame->channel_layout = AV_CH_LAYOUT_STEREO;
    audioFrame->nb_samples = 1024; //一帧音频样本大小

    ret = av_frame_get_buffer(audioFrame, 0);
    if (ret < 0) {
        LOGE("audioFrame创建失败");
    }

    //读取pcm数据一帧样本的大小  样本数*通道数*存储格式16位还是其他等等
    int readSize = audioFrame->nb_samples * 2 * 2;

    //创建缓存空间,用来接收每一帧读取到的pcm数据
    char *pcmBuffer = new char[readSize];

    //打开输入文件
    FILE *file = fopen(inFile, "rb");

    //循环读取数据
    for (;;) {
        int len = fread(pcmBuffer, 1, readSize, file);
        if (len <= 0) {
            LOGE("文件读取完毕");
            break;
        }

        //用于把读取到的每一帧pcm数据进行一个强制转换成uint8_t
        const uint8_t *pcmData[1];
        pcmData[0] = (uint8_t *) pcmBuffer;

        //重采样
        swr_convert(swrCtx, audioFrame->data, audioFrame->nb_samples,
                    pcmData, 1024);

        //AVPacket初始化
        AVPacket pkt;
        av_init_packet(&pkt);

        //开始编码
        int ret;

        //开始编码
        ret = avcodec_send_frame(audioCtx, audioFrame);
        if (ret != 0) continue;

        //接收解码数据
        ret = avcodec_receive_packet(audioCtx,&pkt);
        if (ret != 0) continue;

        //对编码后数据进行参数设置
        pkt.stream_index = audioStream->index;
        pkt.pts = 0;
        pkt.dts = 0;

        av_interleaved_write_frame(audioFmtCtx,&pkt);
        LOGE("写入本地文件成功");
    }

    //释放资源
    delete pcmBuffer;
    pcmBuffer = NULL;

    //写入文件索引
    av_write_trailer(audioFmtCtx);

    //关闭打开输出文件的流
    avio_close(audioFmtCtx->pb);

    //关闭编码器
    avcodec_close(audioCtx);

    //清理编码器设置的参数
    avcodec_free_context(&audioCtx);

    //清理输出上下文的参数
    avformat_free_context(audioFmtCtx);
}
 
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值