作为播放器,只会播放视频还是不行的,因此需要研究一下SDL如何播放音频。
SDL播放音频的过程相对于播放视频更为简单,主要流程如下:
初始化工作:
1.初始化SDL
2.根据设定参数打开音频设备
循环播放:
1.播放音频数据
2.延时等待播放完成
#include <stdio.h>
#include <tchar.h>
#include <iostream>
extern "C"
{
#include "SDL.h"
};
using namespace std;
static Uint8 *audio_chunk; //音频块
static Uint32 audio_len; //音频剩下的长度
static Uint8 *audio_pos; //音频当前的位置
//回调函数的作用是填充音频缓冲区,当音频设备需要更多数据的时候会调用该回调函数
//userdata一般不使用,stream是音频缓冲区,len是音频缓冲区大小
void fill_audio(void *udata, Uint8 *stream, int len) {
//将stream置0,SDL2必须有的操作
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[])
{
//初始化SDL
if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
cout << "can not initialize the SDL" << endl;
return -1;
}
//设置音频相关参数
SDL_AudioSpec wanted_spec;
wanted_spec.freq = 44100; //PCM采样率,通常为44100
wanted_spec.format = AUDIO_S16SYS; //音频格式,16bit
wanted_spec.channels = 2; //声道数量,单声道1,立体声2
wanted_spec.silence = 0; //设置静音的值
wanted_spec.samples = 1024; //音频缓冲区的采样个数,为2的n次方
wanted_spec.callback = fill_audio; //回调函数
//打开SDL Audio组件
if (SDL_OpenAudio(&wanted_spec, NULL) < 0) {
cout << "can not open the audio" << endl;
return -1;
}
//打开视频文件
const char *audio_path = "audio.pcm";
FILE *fp = nullptr;
errno_t err;
err = fopen_s(&fp, audio_path, "rb+");
if (fp == NULL) {
cout << "can not open this file" << endl;
return -1;
}
//For YUV420P
int pcm_buffer_size = 4096;
char *pcm_buffer = (char *)malloc(pcm_buffer_size);
int data_count = 0; //记录已经播放的数据数量
while (1) {
//读取数据
//pcm_buffer:从音频文件中读出的数据存放到此地址中
//每次读取一个字节,一共读取pcm_buffer_size个
if (fread(pcm_buffer, 1, pcm_buffer_size, fp) != pcm_buffer_size) {
// 如果剩下的数据不满足缓冲块的大小
fseek(fp, 0, SEEK_SET);//重定位fp到开头
fread(pcm_buffer, 1, pcm_buffer_size, fp);
data_count = 0;
}
cout << "now playing " << data_count << " bytes data" << endl;
//更新已经播放的数据数量
data_count += pcm_buffer_size;
//设置缓冲区
audio_chunk = (Uint8 *)pcm_buffer;
//音频缓冲区长度
audio_len = pcm_buffer_size;
audio_pos = audio_chunk;
//播放PCM数据
SDL_PauseAudio(0);
while (audio_len > 0)//判断是否读取完毕
SDL_Delay(1);
}
free(pcm_buffer);
SDL_Quit();
return 0;
}
从程序中可以看出,SDL通过注册了一个自定义的回调函数,来保证音频数据能够及时读取到音频播放设备中。
工作的整体思路为:创建一个缓冲区,将音频文件的数据读取到缓冲区中,再通过SDL进行播放。同时需要不断更新关于缓冲区以及音频文件定位的信息。