part05_ffmpeg实现本地音频转码pcm输出

一、音频基础知识

  • 【通道数】

    即声音的通道的数目。常有单声道和立体声之分,单声道的声音只能使用一个喇叭发声(有的也处理成两个喇叭输出同一个声道的声音),立体声可以使两个喇叭都发声(一般左右声道有分工) ,更能感受到空间效果,当然还有更多的通道数。

  • 【采样频率】

    采样频率即取样频率, 指每秒钟取得声音样本的次数。采样频率越高,声音的质量也就越好,声音的还原也就越真实,但同时它占的资源比较多。由于人耳的分辨率很有限,太高的频率并不能分辨出来。22050 的采样频率是常用的, 44100已是CD音质, 超过48000或96000的采样对人耳已经没有意义。

    根据奈奎斯特采样定理,当采样频率fs.max大于信号中最高频率fmax的2倍时(fs.max>2fmax),采样之后的数字信号完整地保留了原始信号中的信息。我们人耳可以听到的声音频率在20Hz-220KHz之间的声波,所以我们做音频采样时一般采用22000 * 2 + 100 = 44100 Hz(多采100Hz的偏差)

  • 【采样位数】

    采样位数即采样值或取样值(就是将采样样本幅度量化)。它是用来衡量声音波动变化的一个参数,也可以说是声卡的分辨率。它的数值越大,分辨率也就越高,所发出声音的能力越强。

      每个采样数据记录的是振幅, 采样精度取决于采样位数的大小:
      1 字节(也就是8bit) 只能记录 256 个数, 也就是只能将振幅划分成 256 个等级;
      2 字节(也就是16bit) 可以细到 65536 个数, 这已是 CD 标准了;
      4 字节(也就是32bit) 能把振幅细分到 4294967296 个等级, 实在是没必要了.
    
part05_ffmpeg实现本地音频转码pcm输出_第1张图片
  • 【音频播放】

    原生的音频格式为pcm,即我们平时的.MP3等音频封装格式文件最终到喇叭播放时已经将其转码为pcm格式的音频数据,pcm的音频数据体积比压缩格式要大很多倍。

二、本地音频转码成pcm输出到文件

    extern "C" {
    //封装格式
    #include "libavformat/avformat.h"
    //解码
    #include "libavcodec/avcodec.h"
    //缩放
    #include "libswscale/swscale.h"
    //重采样
    #include "libswresample/swresample.h"
    };
    /**
     * 将本地音频文件转码为pcm格式并保存到一个新文件上
     * input: 本地音频文件路径
     * output:转换后的硬盘存储路径
     */
    void musicPlayer(const char *input,const char *output){
        av_register_all();
        AVFormatContext *pFormatCtx = avformat_alloc_context();
        //打开音频文件
        if (avformat_open_input(&pFormatCtx, input, NULL, NULL) != 0) {
            LOGI("%s", "无法打开音频文件");
            return;
        }
        //获取输入文件信息
        if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
            LOGI("%s", "无法获取输入文件信息");
            return;
        }
        //获取音频流索引位置
        int i = 0, audio_stream_idx = -1;
        for (; i < pFormatCtx->nb_streams; i++) {
            if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
                audio_stream_idx = i;
                break;
            }
        }
        //获取解码器
        AVCodecContext *codecCtx = pFormatCtx->streams[audio_stream_idx]->codec;
        AVCodec *codec = avcodec_find_decoder(codecCtx->codec_id);
        //打开解码器
        if (avcodec_open2(codecCtx, codec, NULL) < 0) {
            LOGI("%s", "无法打开解码器");
            return;
        }
        //压缩数据
        AVPacket *packet = (AVPacket *) av_malloc(sizeof(AVPacket));
        //解压缩数据
        AVFrame *frame = av_frame_alloc();
        //frame->16bit 44100 PCM 统一音频采样格式与采样率
        SwrContext *swrContext = swr_alloc();
        //音频格式  重采样设置参数
        AVSampleFormat in_sample = codecCtx->sample_fmt;//原音频的采样位数
        //输出采样格式
        AVSampleFormat out_sample = AV_SAMPLE_FMT_S16;//16位
        int in_sample_rate = codecCtx->sample_rate;// 输入采样率
        int out_sample_rate = 44100;//输出采样
    
        //输入声道布局
        uint64_t in_ch_layout = codecCtx->channel_layout;
        //输出声道布局
        uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;//2通道 立体声
    
        /**
         * struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,
          int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
          int64_t  in_ch_layout, enum AVSampleFormat  in_sample_fmt, int  in_sample_rate,
          int log_offset, void *log_ctx);
         */
        swr_alloc_set_opts(swrContext, out_ch_layout, out_sample, out_sample_rate, in_ch_layout, in_sample,
                           in_sample_rate, 0, NULL);
        swr_init(swrContext);
        int got_frame = 0;
        int ret;
        int out_channerl_nb = av_get_channel_layout_nb_channels(out_ch_layout);
        LOGE("声道数量%d ", out_channerl_nb);
        int count = 0;
        //设置音频缓冲区间 16bit   44100  PCM数据
        uint8_t *out_buffer = (uint8_t *) av_malloc(2 * 44100);
        FILE *fp_pcm = fopen(output, "wb");//输出到文件
        while (av_read_frame(pFormatCtx, packet) >= 0) {
    
            ret = avcodec_decode_audio4(codecCtx, frame, &got_frame, packet);
            LOGE("正在解码%d", count++);
            if (ret < 0) {
                LOGE("解码完成");
            }
            //解码一帧
            if (got_frame > 0) {
                /**
                 * int swr_convert(struct SwrContext *s, uint8_t **out, int out_count,
                                    const uint8_t **in , int in_count);
                 */
                swr_convert(swrContext, &out_buffer, 2 * 44100,
                            (const uint8_t **) frame->data, frame->nb_samples);
                /**
                 * int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples,
                                   enum AVSampleFormat sample_fmt, int align);
                 */
                int out_buffer_size = av_samples_get_buffer_size(NULL, out_channerl_nb, frame->nb_samples,
                                                                 out_sample, 1);
                fwrite(out_buffer, 1, out_buffer_size, fp_pcm);//输出到文件
            }
        }
        fclose(fp_pcm);
        av_frame_free(&frame);
        av_free(out_buffer);
        swr_free(&swrContext);
        avcodec_close(codecCtx);
        avformat_close_input(&pFormatCtx);
    }

你可能感兴趣的:(part05_ffmpeg实现本地音频转码pcm输出)