#include <stdio.h>
#include <SDL.h>
// 每次读取2帧数据, 以1024个采样点一帧 2通道 16bit采样点为例
#define PCM_BUFFER_SIZE (1024*2*2*2)
// 音频PCM数据缓存
static Uint8 *s_audio_buf = NULL;
// 目前读取的位置
static Uint8 *s_audio_pos = NULL;
// 缓存结束位置
static Uint8 *s_audio_end = NULL;
// 音频设备回调函数
void fill_audio_pcm(void *udata, Uint8 *stream, int len)
{
SDL_memset(stream, 0, len);
if(s_audio_pos >= s_audio_end) // 数据读取完毕
{
return;
}
// 数据够了就读预设长度,数据不够就只读部分(不够的时候剩多少就读取多少)
int remain_buffer_len = s_audio_end - s_audio_pos;
len = (len < remain_buffer_len) ? len : remain_buffer_len;
// 拷贝数据到stream并调整音量
SDL_MixAudio(stream, s_audio_pos, len, SDL_MIX_MAXVOLUME/8);
printf("len = %d\n", len);
s_audio_pos += len; // 移动缓存指针
}
// 提取PCM文件
// ffmpeg -i input.mp4 -t 20 -codec:a pcm_s16le -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm
// 测试PCM文件
// ffplay -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm
#undef main
int main(int argc, char* argv[])
{
// 1.初始化SDL
if (SDL_Init(SDL_INIT_AUDIO)) {
fprintf(stderr, "SDL_Init error: %s\n", SDL_GetError());
return -1;
}
// 2.打开PCM文件
FILE *audio_fd = fopen("44100_16bit_2ch.pcm", "rb");
if (audio_fd == NULL) {
printf("fopen pcm file error.\n");
return -1;
}
// 3.打开音频设备
SDL_AudioSpec spec;
spec.freq = 44100; // 采样频率
spec.format = AUDIO_S16SYS; // 采样格式
spec.channels = 2; // 通道
spec.silence = 0; // 静音值
spec.samples = 1024; // 采样个数
spec.callback = fill_audio_pcm;
spec.userdata = NULL;
if (SDL_OpenAudio(&spec, NULL)) {
fprintf(stderr, "SDL_OpenAudio error: %s\n", SDL_GetError());
fclose(audio_fd);
SDL_Quit();
return -1;
}
// 5.播放音频
SDL_PauseAudio(0);
int data_count = 0;
s_audio_buf = (uint8_t *)malloc(PCM_BUFFER_SIZE);
while(1) {
// 读取pcm数据
size_t len = fread(s_audio_buf, 1, PCM_BUFFER_SIZE, audio_fd);
if (len == 0) {
break;
}
data_count += len;
printf("now playing %10d bytes data.\n",data_count);
s_audio_end = s_audio_buf + len;
s_audio_pos = s_audio_buf;
while(s_audio_pos < s_audio_end)
{
SDL_Delay(10); // 等待PCM数据消耗
}
}
// 6.关闭音频设备
printf("play PCM finish\n");
SDL_CloseAudio();
// 7.退出
if (s_audio_buf) {
free(s_audio_buf);
}
if (audio_fd) {
fclose(audio_fd);
}
SDL_Quit();
return 0;
}