Android万能音频播放器03--FFmpeg音频数据重采样后生成pcm文件

1、基本概念

采样率:每秒对音频数据采样的个数(44100hz)
采样位数:存储采样数据的位数(16bit 2字节)
输出声道(声道布局):单声道、立体声(左右声道)等

2、重采样

就是把目标音频按照一定的格式重新采样编码成新的音频数据,
方便统一处理,一般的采样标准是:44100HZ、16bit、双声道

3、采样对象:解码出来的音频帧(AVframe)

使用函数:SwrContext

swr_ctx = swr_alloc_set_opts(
    struct SwrContext *s, // 传NULL
    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, // NULL
    void *log_ctx); // NULL

4、

前两篇已经实现了将AVPacket存储到队列中及从队列中取出AVPacket,
在JfAudio中进行重采样

  1. 新建一个用于采样的线程
void JfAudio::play() {
    pthread_create(&playThread,NULL,decodePlay,this);
}

线程回调decodePlay

void *decodePlay(void *data){
    JfAudio *jfAudio = (JfAudio *)(data);
    jfAudio->resampleAudio();
    pthread_exit(&jfAudio->playThread);
}
  1. 音频重采样方法
int JfAudio::resampleAudio() {
    while (playStatus != NULL && !playStatus->exit){
        avPacket = av_packet_alloc();
        if (queue->getAVPacket(avPacket) != 0){
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
            continue;
        }
        ret = avcodec_send_packet(pACodecCtx,avPacket);
        if (ret != NULL){
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
            continue;
        }

        avFrame = av_frame_alloc();
        ret = avcodec_receive_frame(pACodecCtx,avFrame);
        if (ret == 0) {//进行重采样
            if (avFrame->channels > 0 && avFrame->channel_layout == 0){//有声道数没有声道布局,所以要设置声道布局
                avFrame->channel_layout = av_get_default_channel_layout(avFrame->channels);//设置声道布局
            } else if (avFrame->channels == 0 && avFrame->channel_layout > 0){//有声道布局没有声道数,所以要设置声道数
                avFrame->channels = av_get_channel_layout_nb_channels(avFrame->channel_layout);
            }

            SwrContext *swr_ctx = NULL;
            swr_ctx = swr_alloc_set_opts(NULL,
                    AV_CH_LAYOUT_STEREO,// 输出声道布局:立体声
                    AV_SAMPLE_FMT_S16,//输出采样位数格式
                    avFrame->sample_rate,//输出的采样率
                    avFrame->channel_layout,//输入声道布局
                    (AVSampleFormat)(avFrame->format),//输入采样位数格式
                    avFrame->sample_rate,//输入采样率
                    NULL,
                    NULL);

            if (!swr_ctx || swr_init(swr_ctx) < 0){

                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;

                av_frame_free(&avFrame);
                av_free(avFrame);
                avFrame = NULL;

                if (swr_ctx != NULL){
                    swr_free(&swr_ctx);
                    swr_ctx = NULL;
                }
                if (LOG_DEBUG){
                    LOGE("!swr_ctx || swr_init(swr_ctx) < 0");
                }
                continue;
            }

            //返回的是采样的个数
            int nb = swr_convert(swr_ctx,
                                &buffer,//转码后输出的PCM数据大小
                                avFrame->nb_samples,//输出采样个数
                                (const uint8_t **)(avFrame->data),
                                avFrame->nb_samples);

            int out_channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);

            data_size = nb * out_channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);

            fwrite(buffer,1,data_size,outFile);

            LOGD("DATA SIZE == %d",data_size);

            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;

            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;

            swr_free(&swr_ctx);
            swr_ctx = NULL;
        } else {
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;

            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;
            continue;
        }
    }
    fclose(outFile);
    return data_size;
}

源码地址:https://github.com/Xiaoben336/SuperAudioPlayer/tree/resampleToPcm

你可能感兴趣的:(Android万能音频播放器03--FFmpeg音频数据重采样后生成pcm文件)