准备
1. ffmpeg 4.4
2. sdl2
3.一段原始的音频PCM数据
重采样流程
1.设置输入音频参数和输出音频参数
2.根据设置的参数初始化SwrContent上下文
3.创建一个输入buffer, 根据输入的音频参数(采样率,通道数,样本位深度)申请空间,填入默认数据,用于存储输入音频数据
4.创建一个输出buffer, 根据输出的音频参数(采样率,通道数,样本位深度)申请空间,填入默认数据,用于存储重采样后的数据
5.读取PCM数据,每次读取的大小等于输入buffer的大小
6.进行重采样swr_convert
7.将输出的buffer拷贝到SDL2音频回调缓冲区中播放,或者直接写入文件,使用ffplay进行测试,也可以封装成Frame送到音频编码器中(如aac),进行编码后保存。
关键代码
设置重采样参数并初始化SWr_Content结构
struct SwrContext* swr_ctx;swr_ctx = swr_alloc_set_opts(nullptr,AV_CH_LAYOUT_MONO, //输出通道AV_SAMPLE_FMT_S16, //输出样本格式44100, //输出采样率AV_CH_LAYOUT_STEREO, //输入通道AV_SAMPLE_FMT_FLT, //输入样本格式44100, //输入采样率0, nullptr); swr_init(swr_ctx);
输入/输出buffer 创建
//输入数据bufferuint8_t** pcm_buffer;int src_linesize;int src_nb_channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);int ret = av_samples_alloc_array_and_samples(&pcm_buffer, &src_linesize, src_nb_channels, frame_nb_samples, AV_SAMPLE_FMT_FLT, 0);if (ret < 0) {fprintf(stderr, "Could not allocate source samples\n"); }//输出数据bufferuint8_t** out_buffer;int dst_linesize;int dst_nb_channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_MONO);ret = av_samples_alloc_array_and_samples(&out_buffer, &dst_linesize, dst_nb_channels, frame_nb_samples, AV_SAMPLE_FMT_S16, 0);if (ret < 0) {fprintf(stderr, "Could not allocate source samples\n");}
读文件并进行重采样
readcount = fread((char *)pcm_buffer[0], 1, src_linesize, fp);data_count += readcount;
printf(" Now Playing %10d KBytes data. %d \n", data_count / 1024, readcount);swr_convert(swr_ctx, out_buffer, frame_nb_samples, (const uint8_t**)pcm_buffer, frame_nb_samples);
源码分享
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include "sdl.h"extern "C"
{#include <libavutil/opt.h>#include <libavutil/channel_layout.h>#include <libavutil/samplefmt.h>#include <libswresample/swresample.h>
}static Uint32 audio_len;
static Uint8* audio_pos;
int frame_nb_samples = 1024; //一帧数据样本数
struct SwrContext* swr_ctx;void fill_audio_pcm(void* udata, Uint8* stream, int len)
{SDL_memset(stream, 0, len);if (audio_len == 0)return;len = (len > audio_len ? audio_len : len);SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);audio_pos += len;audio_len -= len;
}int main(int argc, char* argv[])
{if (SDL_Init(SDL_INIT_AUDIO || SDL_INIT_TIMER)){printf("SDL init error\n");return -1;}swr_ctx = swr_alloc_set_opts(nullptr,AV_CH_LAYOUT_MONO, //输出通道AV_SAMPLE_FMT_S16, //输出样本格式44100, //输出采样率AV_CH_LAYOUT_STEREO, //输入通道AV_SAMPLE_FMT_FLT, //输入样本格式44100, //输入采样率0, nullptr); swr_init(swr_ctx);//SDL_AudioSpecSDL_AudioSpec wanted_spec;wanted_spec.freq = 44100;wanted_spec.format = AUDIO_F32; //AUDIO_S16LSB; //AUDIO_F32;wanted_spec.channels = 2;wanted_spec.silence = 0;wanted_spec.samples = frame_nb_samples;wanted_spec.callback = fill_audio_pcm;if (SDL_OpenAudio(&wanted_spec, NULL) < 0) {printf("can't open audio.\n");return -1;}//PlaySDL_PauseAudio(0);//输入数据bufferuint8_t** pcm_buffer;int src_linesize;int src_nb_channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);int ret = av_samples_alloc_array_and_samples(&pcm_buffer, &src_linesize, src_nb_channels, frame_nb_samples, AV_SAMPLE_FMT_FLT, 0);if (ret < 0) {fprintf(stderr, "Could not allocate source samples\n"); }//输出数据bufferuint8_t** out_buffer;int dst_linesize;int dst_nb_channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_MONO);ret = av_samples_alloc_array_and_samples(&out_buffer, &dst_linesize, dst_nb_channels, frame_nb_samples, AV_SAMPLE_FMT_S16, 0);if (ret < 0) {fprintf(stderr, "Could not allocate source samples\n");}FILE* fp = nullptr;fopen_s(&fp, "D:/工程/音视频分析/source/f32le.pcm", "rb+");if (fp == NULL) {printf("cannot open this file\n");return -1;}int readcount = -1;int data_count = 0;while (!feof(fp)) {readcount = fread((char *)pcm_buffer[0], 1, src_linesize, fp);data_count += readcount;printf(" Now Playing %10d KBytes data. %d \n", data_count / 1024, readcount);swr_convert(swr_ctx, out_buffer, frame_nb_samples, (const uint8_t**)pcm_buffer, frame_nb_samples);//Set audio buffer (PCM data)audio_len = dst_linesize; audio_pos = (Uint8*)out_buffer[0]; //(Uint8*)pcm_buffer[0];while (audio_len > 0)SDL_Delay(1);}if (pcm_buffer)av_freep(&pcm_buffer[0]);av_freep(&pcm_buffer);if (out_buffer)av_freep(&out_buffer[0]);av_freep(&out_buffer);swr_free(&swr_ctx);fclose(fp);SDL_Quit();return 0;
}