编码出来的aac播放全是刺耳的噪声

来源:8-8 AAC编码器编码2

慕妹3585395

2024-04-03

问题

重采样结果播放都是很清晰的,但是编码之后就全是噪声了

  1. ffplay -ar 48000 -ac 1 -f f32le audio_raw.pcm             正常播放
  2. ffplay -ar 48000 -ac 1 -f s16le audio_resample.pcm   正常播放
  3. ffplay audio_encode.aac                                              全是刺耳的噪音
  4. 备注:虽然我重采样的时候已经是s16了,播放aac的时候显示的fltp(不知道咋来的)

系统默认参数为:
图片描述

获取的原始PCM为:
图片描述

图片描述

编码后的结果如下:
图片描述

代码如下

打开编码器

static AVCodecContext* open_codec(){
    // 1. 查找编码器
    AVCodec *codec = avcodec_find_encoder_by_name("libfdk_aac"); // 根据名称查找编码器
    if (!codec) {
        fprintf(stderr, "avcodec_find_encoder_by_name error!\n");
        return NULL;
    }
    
    // 2. 创建 codec 上下文,目的是设置codec编码器的参数
    AVCodecContext *codec_context = avcodec_alloc_context3(codec);
    codec_context->sample_fmt = AV_SAMPLE_FMT_S16;          // 输入音频的采样大小
    codec_context->channel_layout = AV_CH_LAYOUT_MONO;      // 输入音频的channel layout
    codec_context->channels = 1;                            // 输入音频 channel 个数
    codec_context->sample_rate = 48000;                     // 输入音频的采样率
    codec_context->bit_rate = 128000;                        // AAC_LC: 128K, AAC HE: 64K, AAC HE V2: 32K
    //codec_context->profile = FF_PROFILE_AAC_HE;           // 若内部指定了profile,就是动态码率,就不要指定码率了
    
    // 3. 打开编码器
    int ret = avcodec_open2(codec_context, codec, NULL);
    if (ret < 0){
        print_error(ret, "avcodec_open2 error");
        return NULL;
    }
    
    return codec_context;
}

读取

    while(rec_status) {
        
        // 4. 从格式中读取packet(假设它是编码后的)
        AVPacket pkt;
        av_init_packet(&pkt);
        ret = av_read_frame(format_context, &pkt); // 这个函数会自动给pkt分配内部数据空间
        if (ret < 0) {
            if (ret == AVERROR(EAGAIN)) {
                usleep(10000);  //如果设备没有准备好,那就等一小会
                continue;
            }
            print_error(ret, "av_read_frame error");
            break;
        }
        printf("av_read_frame success, pkt.data=%p, pkt.size=%d\n", pkt.data, pkt.size);
        
        // 写入本地
        fwrite(pkt.data, 1, pkt.size, out_file_raw);
        fflush(out_file_raw);
        // 5. 如果格式是语音文件的话,这里还要解码,但是这里不用
        // 6. 对av_packet(其实就是av_frame)进行重采样
        memcpy((void*)src_data[0], (void*)pkt.data, pkt.size);  // 将采样前的av_frame拷贝到重采样的输入缓冲中
        ret = swr_convert(swr_context,          // 重采样的上下文
                    dst_data,                   // 输出结果缓冲区
                    512,                       // 输出每个通道的采样数
                    (const uint8_t **)src_data, // 输入缓冲区
                    512);                       // 输入单个通道的采样数
        if (ret < 0) {
            print_error(ret, "swr_convert error");
            break;
        }
        printf("swr_convert success!dst_linesize=%d\n", dst_linesize);
        // 写入本地
        fwrite(dst_data[0], 1, dst_linesize, out_file_resample);
        fflush(out_file_resample);
        memcpy((void *)frame->data[0], dst_data[0], dst_linesize); // 将重采样的输出缓冲(av_frame)拷贝到 frame 中
        
        // 7. 对av_frame编码
        encode(codec_context, frame, newpkt, out_file_encode);
        
        //
        av_packet_unref(&pkt); //release pkt
    }

编码

static void encode(AVCodecContext *codec_context,
            AVFrame *frame,
            AVPacket *newpkt,
            FILE *out_file_encode){
    //将数据送编码器
    int ret = avcodec_send_frame(codec_context, frame);
    if (ret < 0) {
        print_error(ret, "avcodec_send_frame error");
        return;
    }
    printf("avcodec_send_frame success!\n");
    
    // 如果ret>=0说明数据设置成功
    while(ret >= 0) {
        // 获取编码后的音频数据,如果成功,需要重复获取,直到失败为止
        ret = avcodec_receive_packet(codec_context, newpkt);
        if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
            return;
        } else if( ret < 0){
            print_error(ret, "avcodec_receive_packet error");
            exit(-1);
        }
        printf("avcodec_receive_packet success!\n");
        
        // write file
        fwrite(newpkt->data, 1, newpkt->size, out_file_encode);
        fflush(out_file_encode);
    }
    
    return;
}

重采样上下文

static
SwrContext* init_swr(void){
    
    // 创建重采样上下文
    SwrContext *swr_context = swr_alloc_set_opts(NULL,
                                 AV_CH_LAYOUT_MONO,     // 输出channel布局
                                 AV_SAMPLE_FMT_S16,     // 输出的采样格式
                                 48000,                 // 输出的采样率
                                 AV_CH_LAYOUT_MONO,     // 输入channel布局
                                 AV_SAMPLE_FMT_FLT,     // 输入的采样格式
                                 48000,                 // 输入的采样率
                                 0, NULL);
    
    if(!swr_context){
        print_error2("swr_alloc_set_opts error");
        return NULL;
    }
    
    // 初始化重采样上下文
    int ret = swr_init(swr_context);
    if(ret < 0){
        print_error(ret, "swr_init error");
        return NULL;
    }
    
    return swr_context;
}

重采样分配

static
int alloc_data_4_resample(uint8_t ***src_data,
                           int *src_linesize,
                           uint8_t *** dst_data,
                           int *dst_linesize){
    //创建输入缓冲区 2048 / 4 / 1 = 512
    int ret = av_samples_alloc_array_and_samples(src_data,         //输出缓冲区地址
                                       src_linesize,     //缓冲区的大小
                                       1,                 //通道个数
                                       512,               //单通道采样个数
                                       AV_SAMPLE_FMT_FLT, //采样格式
                                       0);
    if (ret < 0) {
        print_error(ret, "av_samples_alloc_array_and_samples error");
        return ret;
    }
    
    //创建输出缓冲区
    ret = av_samples_alloc_array_and_samples(dst_data,      //输出缓冲区地址
                                       dst_linesize,        //缓冲区的大小
                                       1,                   //通道个数
                                       512,                //单通道采样个数
                                       AV_SAMPLE_FMT_S16,   //采样格式
                                       0);
    if (ret < 0) {
        print_error(ret, "av_samples_alloc_array_and_samples error");
        return ret;
    }
    
    return 0;
}

重采样执行

ret = swr_convert(swr_context,          // 重采样的上下文
            dst_data,                   // 输出结果缓冲区
            512,                       // 输出每个通道的采样数
            (const uint8_t **)src_data, // 输入缓冲区
            512);                       // 输入单个通道的采样数
写回答

1回答

李超

2024-04-04

把你重采样后的采样率设置成44100 试试

0
4
李超
回复
慕妹3585395
👍👍👍,期待你的分享
2024-04-04
共4条回复

音视频小白系统入门课 音视频基础+ffmpeg原理

掌握音视频采集、编解码、RTMP传输协议等核心基础

2318 学习 · 813 问题

查看课程