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 个等级, 实在是没必要了.
    
  • 【音频播放】

    原生的音频格式为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);
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,265评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,078评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,852评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,408评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,445评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,772评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,921评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,688评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,130评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,467评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,617评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,276评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,882评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,740评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,967评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,315评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,486评论 2 348

推荐阅读更多精彩内容

  • 前言 说到视频,大家自己脑子里基本都会想起电影、电视剧、在线视频等等,也会想起一些视频格式 AVI、MP4、RMV...
    ForestSen阅读 22,853评论 10 202
  • 前言 本篇开始讲解在Android平台上进行的音频编辑开发,首先需要对音频相关概念有基础的认识。所以本篇要讲解以下...
    Ihesong阅读 7,762评论 2 18
  • 音频技术开发,我们得对声音有所了解,掌握音频的基础知识,这才能更好地去做技术开发。首先介绍音频基础知识,然后介绍音...
    安仔夏天勤奋阅读 7,438评论 3 18
  • 原来视频里说的是真的,“等我有时间我就回家,等我有时间我就给您做顿好吃的,等我看完这段我就和你唠嗑”,每次和父母,...
    懿米柒柒阅读 356评论 0 1
  • 静思/文 从小在记忆中,我们父母辈的婚姻是在吵吵打打闹闹中过的,几乎没有哪家不吵不打不闹的。女人总是吵着,说的硬话...
    言静思阅读 659评论 6 13