• FFMpeg音频混合,背景音(五):将PCM压缩为MP3


    FFMpeg版本:3.3.1

    一、原理

        pcm:样本格式为s16交错存储,左右左右交错存储,采样率为44100,通道数为2,立体声
        mp3:格式为s16P, 也就是平面模式,先存储左声道,再存右声道,双声道,立体声。单帧样本1152个,采样率为44100.

    二、代码

    #include <iostream>
    using namespace std;
    extern "C" {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavutil/avutil.h>
    #include <libavutil/opt.h>
    #include <libswresample/swresample.h>
    
    }
    #pragma comment(lib,"avcodec.lib")
    #pragma comment(lib,"avformat.lib")
    #pragma comment(lib,"avutil.lib")
    #pragma comment(lib,"swresample.lib")
    int main()
    {
        av_register_all();
    
        char inputfile[] = "audio.pcm";
        char outputfile[] = "audio.mp3";
        int ret = 0;
        FILE* finput = NULL;
        FILE* foutput = NULL;
    
        //======================================MP3收尾工作============================================
        //寻找mp3编码器
        AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_MP3);
        //创建并配置mp3编(解)码器上下文
        AVCodecContext* ctx = avcodec_alloc_context3(codec);
        ctx->bit_rate = 64000;
        ctx->channels = 2;
        ctx->channel_layout = AV_CH_LAYOUT_STEREO;
        ctx->sample_rate = 44100;
        ctx->sample_fmt = AV_SAMPLE_FMT_S16P;
        //打开mp3编(解)码器
        ret = avcodec_open2(ctx, codec, 0);
        //打开输出的MP3文件流
        foutput = fopen(outputfile, "wb");
    
        //===========================================PCM重采样为MP3============================================
        //准备一个frame结构体来接受重采样MP3的,每一帧的音频数据,每帧的样本大小为1152。
        //其实就是分配内存缓冲区,放重采样后的数据
        AVFrame* frame = av_frame_alloc();
        frame->nb_samples = 1152;
        frame->channels = 2;
        frame->channel_layout = AV_CH_LAYOUT_STEREO;
        frame->format = AV_SAMPLE_FMT_S16P;
        av_frame_get_buffer(frame, 0);
        //创建音频重采样上下文
        SwrContext* swr = swr_alloc();
        //设置重采样输入参数,通道布局立体声,采样率44100,样本格式不一致,输入S16交错存储,输出S16P平面存储
        av_opt_set_int(swr, "in_channel_layout", AV_CH_LAYOUT_STEREO, 0);
        av_opt_set_int(swr, "in_sample_rate", 44100, 0);
        av_opt_set_sample_fmt(swr, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
        //设置重采样输出参数
        av_opt_set_int(swr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0);
        av_opt_set_int(swr, "out_sample_rate", 44100, 0);
        av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16P, 0);
        swr_init(swr);
    
        //===========================================PCM读入处理工作============================================
        finput = fopen(inputfile, "rb");
        //存储从pcm文件读取过来的数据,进行缓存
        uint8_t** input_data = NULL;
        //缓存重采样之后的数据
        uint8_t** output_data = NULL;
        int input_linesize, output_linesize;
        //给保存pcm文件数据分配空间
        av_samples_alloc_array_and_samples(&input_data, &input_linesize, 2, 1152, AV_SAMPLE_FMT_S16, 0);
        //缓存重采样数据的空间分配
        av_samples_alloc_array_and_samples(&output_data, &output_linesize, 2, 1152, AV_SAMPLE_FMT_S16P, 0);
        //接受编码之后的数据
        AVPacket* pkt = av_packet_alloc();
        while (!feof(finput))
        {
            fread(input_data[0], 1, 1152 * 2 * 2, finput);
            //重采样
            swr_convert(swr, output_data, 1152, (const uint8_t**)input_data, 1152);
            //左声道数据
            frame->data[0] = output_data[0];
            //右声道数据
            frame->data[1] = output_data[1];
            //编码,写入mp3文件,实际上是对frame这个结构体里面的数据进行编码操作。
            ret = avcodec_send_frame(ctx, frame);
            while (ret >= 0) {
                ret = avcodec_receive_packet(ctx, pkt);
                //AVERROR(EEAGAIN) -11  AVERROR_EOF表示已经没有数据了
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                {
                    continue;
                }
                else if (ret < 0) {
                    break;
                }
                fwrite(pkt->data, 1, pkt->size, foutput);
                av_packet_unref(pkt);
            }
        }
        if (input_data)
        {
            av_freep(input_data);
        }
        if (output_data)
        {
            av_freep(output_data);
        }
        fclose(finput);
        fclose(foutput);
        av_frame_free(&frame);
        av_packet_free(&pkt);
    
        swr_free(&swr);
    
        avcodec_free_context(&ctx);
    
        return 0;
    }

  • 相关阅读:
    网站后台编辑器怎样才能兼容IE6、IE8
    map area
    纯CSS圆角
    【转】Linux 查看某一进程的占用CPU的Cacti 脚本
    查看/修改Linux时区和时间,更新系统时间
    Centos下安装X Window+GNOME Desktop+FreeNX
    rhel6 kvm做桥接
    Gentoo网络配置
    常用正则表达式
    VS 设置备忘
  • 原文地址:https://www.cnblogs.com/zhangxianrong/p/13852809.html
Copyright © 2020-2023  润新知