前言
在之前的博客中,已经对于FFmpeg的介绍、编译、拉流、解码等做了详细的介绍。现在紧跟着上一篇博客,在之前的拉流编解码后,使用SDL进行播放。
具体对于SDL的介绍与编译请查看上一篇博客
一、SDLAPI介绍
1.初始化子系统
int SDL_Init(Uint32 flags);
flags值可以是以下几种或者一起:
SDL_INIT_TIMER: 定时器子系统
SDL_INIT_AUDIO: 音频子系统
SDL_INIT_VIDEO:视频子系统;自动初始化事件子系统
SDL_INIT_JOYSTICK:操纵杆子系统;自动初始化事件子系统
SDL_INIT_HAPTIC:触觉(力反馈)子系统
SDL_INIT_GAMECONTROLLER:控制器子系统;自动初始化操纵杆子系统
SDL_INIT_EVENTS: 事件子系统
SDL_INIT_EVERYTHING: 以上所有子系统
SDL_INIT_NOPARACHUTE:兼容性;该标志被忽略
return:成功时返回 0,失败时返回负错误代码;调用SDL_GetError () 获取更多信息。
备注:在子系统使用结束后,需要调用SDL_Quit () 以关闭
2.打开音频设备
int SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained);
参数:
desired:表示所需输出格式的SDL_AudioSpec结构。有关如何准备此结构的详细信息,请参阅SDL_OpenAudioDevice文档。
obtained: 用实际参数填充的SDL_AudioSpec结构,或 NULL。
return:如果成功则返回 0,将实际的硬件参数放入 指向的结构中obtained。
备注:更强大的方法是使用SDL_OpenAudioDevice (),指定使用设备
3.SDL_AudioSpec 结构体
包含音频输出格式的结构。它还包含在音频设备需要更多数据时调用的回调。

其中
- 音频数据格式参数值如下:

2.支持的通道数如下
SDL2.0以后可以支持的值为 1(单声道)、2(立体声)、4(四声道)和 6 (5.1)
3.回调函数模型如下
void SDL_AudioCallback(void* userdata, Uint8* stream, int len)
4.样本中的音频缓冲区大小
由于本例子中播放的是MP3,MP3一帧数据大小为1152采样,所以该值为1152
4.暂停音频设备
void SDL_PauseAudio(int pause_on);
参数:非零暂停,0取消暂停
备注:一般是传入1代表暂停
二、使用实例
1.粗浅设计
播放的基础是建立在正确拉流、并进行编解码的基础之上,所以下面的代码是对之前的例子的修改和补充。关于拉流、解码的API就不进行过多的介绍了。
整体的设计结构如下图:
整体对外接口
bool StartPlay(const char* pAudioFilePath);
bool StopPlay();
bool PausePlay();
bool ResumePlay();
分别对应开始播放、暂停播放、恢复播放、停止播放。
2.头文件
代码如下:
#pragma once
extern "C" {
#include "include/libavformat/avformat.h"
#include "include/libavcodec/avcodec.h"
#include "include/libavutil/avutil.h"
#include "include/libswresample/swresample.h"
}
#include "SDL.h"
#include <iostream>
#include <mutex>
#include <Windows.h>
#include <vector>
namespace AudioReadFrame
{
#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 16bit audio 2 channel
#define MAX_CACHE_BUFFER_SIZE 50*1024*1024 // 5M
//自定义结构体存储信息
struct FileAudioInst
{
long long duration; ///< second
long long curl_time; ///< second
int sample_rate; ///< samples per second
int channels; ///< number of audio channels
FileAudioInst()
{
duration = 0;
curl_time = 0;
sample_rate = 0;
channels = 0;
}
};
//拉流线程状态
enum ThreadState
{
eRun = 1,
eExit,
};
enum PlaySate
{
ePlay = 1,
ePause,
eStop
};
struct CircleBuffer
{
CircleBuffer() : dataBegin(0), dataEnd(0), buffLen_(0) {}
uint32_t buffLen_;
uint32_t dataBegin;
uint32_t dataEnd;
std::vector<uint8_t> decodeBuffer_;
};
class CAudioReadFrame
{
public:
CAudioReadFrame();
~CAudioReadFrame();
public:
bool StartPlay(const char* pAudioFilePath);
bool StopPlay();
bool PausePlay();
bool ResumePlay();
private:
//加载流文件
bool LoadAudioFile(const char* pAudioFilePath);
//开始读流
bool StartReadFile();
//停止读流
bool StopReadFile();
//释放资源
bool FreeResources();
//改变拉流线程的装填
void ChangeThreadState(ThreadState eThreadState);
//拉流线程
void ReadFrameThreadProc();
//utf转GBK
std::string UTF8ToGBK(const std::string& strUTF8);
private: