音视频播放专栏

系列文章目录

第一章 使用SDL2库播放pcm数据音频



前言

主要阐述如何使用SDL2库播放pcm原始数据音频,且如何实现多个音频混合播放。


一、实现播放的流程

1.初始化音频子系统及退出接口

//初始化音频子系统
SDL_Init(SDL_INIT_AUDIO)) 
//退出
SDL_Quit()

2.音频设备打开及关闭

SDL_AudioSpec spec, spec_out;
SDL_AudioDeviceID m_dAudioDev;

spec.freq = m_nOutSampleRate;
spec.format = m_nOutSampleFmt;
spec.channels = m_nOutChannels;
spec.silence = 0;											//0代表最大声音
spec.samples = 256;
spec.callback = &StaticReadAudioData;	//音频回调函数,由于本实例使用c++实现故需传入类实例参数然后再去调用类函数实现
spec.userdata = this;
//打开音频设备
//第一个参数表示默认音频解码设备
//第二个参数,如果为 1,则表示打开音频设备用于录音;如果为 0,则表示打开音频设备用于播放
//第三个参数表示音频输入参数,第四个表示音频输出参数
//第五个参数用于指定允许哪些音频特性发生变化,比如SDL_AUDIO_ALLOW_FREQUENCY_CHANGE允许频率变化,SDL_AUDIO_ALLOW_SAMPLES_CHANGE允许采样样本数变化
m_dAudioDev = SDL_OpenAudioDevice(NULL, 0, &spec, &spec_out, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_SAMPLES_CHANGE)
/关闭音频设备
SDL_CloseAudioDevice(m_dAudioDev);
//音频回调的执行函数
void AudioPcm::ReadAudioData(unsigned char *stream, int len) 
{
	SDL_memset(stream, 0, len);//必须执行,否则回调时会一直发声
	SDL_MixAudioFormat(stream, m_aAudioPlaying[i].pPlayPos, m_nOutSampleFmt, len, m_aAudioPlaying[i].nPlayVolume);//音频数据播放
	m_aAudioPlaying[i].pPlayPos += len;
	m_aAudioPlaying[i].nPlayLen -= len;
}
			
static void StaticReadAudioData(void *udata, unsigned char *stream, int len)
{
	AudioPcm *ap = static_cast<AudioPcm *>(udata);
	ap->ReadAudioData(stream, len);
}

3.音频数据流创建与推送

SDL_AudioStream *audioStream;
//新建音频流,实际音频参数可以重采样成目标音频参数,也即打开音频设备时配置的参数
audioStream = SDL_NewAudioStream(m_nSampleFmt, m_nChannels, m_nSampleRate, m_nOutSampleFmt, m_nOutChannels, m_nOutSampleRate);
//对应释放接口
SDL_FreeAudioStream(audioStream);
//判断音频流缓冲中是否有数据,返回值为数据字节数
SDL_AudioStreamAvailable(audioStream);
//将音频流缓冲中数据拷贝到read_data缓冲中,read_data指针用于回调函数中使用
SDL_AudioStreamGet(audioStream, read_data, max_play_data_len);
	
m_aAudioPlaying[m_nCurAudioIndex].pPlayPos = (uint8_t *)read_data;
m_aAudioPlaying[m_nCurAudioIndex].nPlayLen = ret; //长度为读出数据长度,在read_audio_data中做减法
//等待回调函数中数据播放完毕
while (1) //判断是否播放完毕
{
	if (m_aAudioPlaying[m_nCurAudioIndex].nPlayLen == 0)
	{
		timeout_count = 0;
		SDL_AudioStreamFlush(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
		break;
	}
	if (timeout_count >= 2000)
	{
		timeout_count = 0;
		break;
	}
	timeout_count++;
	SDL_Delay(1);
}
//音频数据推送接口
SDL_AudioStreamPut(audioStream, data, len)

二、如何实现多路音频播放

1.创建多个音频流并推送

使用多线程结合上述“音频数据流创建与推送”章节实现多路音频流创建和推送

2.音频设备打开的回调函数中多次播放音频流

实现多路音频播放的关键点就在于在回调函数中多次调用SDL_MixAudioFormat接口播放各路音频数据

三、具体代码实例

为方便理解SDL2库相关接口,为避免重复制造轮子,现将代码粘贴共享如下。

1.头文件代码

#pragma once

#include <stdio.h>
#include <stdint.h>
#include <thread>
#include <mutex>
#include <memory>

#ifdef __linux
#include <SDL2/SDL_types.h>
#include <SDL2/SDL.h>

using namespace std;

#define AUDIO_PLAYING_MAX			4

typedef struct AudioPlaying {
	uint8_t *pPlayPos;
	uint32_t nPlayLen;
	int nPlayVolume;		// 0<= nPlayVolume <= SDL_MIX_MAXVOLUME
	SDL_AudioStream *audioStream;
	bool bUsed;
	std::mutex mutexAudioPlay;
}AudioPlaying_t;

class AudioPcm {
public:
    AudioPcm();
    ~AudioPcm();
    AudioPcm(int sampleRate, SDL_AudioFormat sampleFmt, int channels);
    AudioPcm(int sampleRate, SDL_AudioFormat sampleFmt, int channels, int resampleRate);
	AudioPcm(int sampleRate, SDL_AudioFormat sampleFmt, int channels, int resampleRate, SDL_AudioFormat outSampleFmt);
public:
	// 0<=volume<=SDL_MIX_MAXVOLUME=128
	void SetVolume(int audioIndex, int volume);
	void SetSampleRate(int sampleRate);
	void SetSampleRateAndStart(int sampleRate);
    void SetSampleRateAndStart(int iSrcSampleRate, int iOutSampleRate);
	void SetSampleFmt(SDL_AudioFormat sampleFmt);
	void SetChannels(int channels);
	
	int GetSampleRate(void);
	SDL_AudioFormat GetSampleFmt(void);
	int GetChannels(void);
	int GetOutSampleRate(void);
	SDL_AudioFormat GetOutSampleFmt(void);
	int GetOutChannels(void);
	int GetVolume(int audioIndex);
	int RequestAudioPlay();
	void SetAudioPlayData(int index, uint8_t *audioPlayPos, uint32_t audioPlayLen);
	uint32_t GetAudioPlayLen(int index);
	void SetAudioPlayUsed(int index, bool used);
	bool GetAudioPlayUsed(int index);
	void SetAudioPlayStream(int index, SDL_AudioStream *audioStream);
	SDL_AudioStream *GetAudioPlayStream(int index);
	
	int AudioClearData();
	int AudioPushData(char *data, int len);
	int AudioOpenDevice(int index);
	void AudioCloseDevice();
	void AudioThreadStart();
	void AudioThreadStop();
	void AudioThreadFunc();
	bool AudioThreadStatus();
    static int AudioPlayByFile(const char *filename, int sampleRate, SDL_AudioFormat sampleFmt = AUDIO_S16SYS, int channels = 1);
	static int AudioPlayData(char *data, int len, int sampleRate, SDL_AudioFormat sampleFmt = AUDIO_S16SYS, int channels = 1);
	static int AudioPlayStop(int audioIndex);
	
	static std::shared_ptr<AudioPcm> CreateInstanse(int sampleRate, SDL_AudioFormat sampleFmt, int channels);
	static std::shared_ptr<AudioPcm> GetInstanse();
	static void DeleteInstanse();
	void ReadAudioData(unsigned char *stream, int len);
	
private:
	int AudioDataFinished();
	
private:
	//最多同时播放4个
	AudioPlaying_t m_aAudioPlaying[AUDIO_PLAYING_MAX];
	int m_nSampleRate;
	int m_nOutSampleRate;
	SDL_AudioFormat m_nSampleFmt;
	SDL_AudioFormat m_nOutSampleFmt;
	int m_nChannels;
	int m_nOutChannels;
	int m_nNbSamples;
	bool m_bAudioThreadStart;
    SDL_Thread * m_threadAudio;
	pthread_attr_t m_attrAudio;
	int m_nCurAudioIndex;
	std::mutex m_mutexAudioStream;
	SDL_AudioDeviceID m_dAudioDev;
	std::mutex m_mutexAudioDev;
};

#endif

2.源文件代码

#ifdef __linux
#include <unistd.h>
#include <iostream>
#include <signal.h>

#include "audio_pcm.h"

std::shared_ptr<AudioPcm> s_pAudioPlay = nullptr;

std::shared_ptr<AudioPcm> AudioPcm::CreateInstanse(int sampleRate, SDL_AudioFormat sampleFmt, int channels)
{
	if (s_pAudioPlay == nullptr)
	{
		s_pAudioPlay = std::make_shared<AudioPcm>(sampleRate, sampleFmt, channels);
		s_pAudioPlay->SetSampleRate(sampleRate);
	}
	else
	{
		if (sampleRate != s_pAudioPlay->GetSampleRate() || sampleFmt != s_pAudioPlay->GetSampleFmt() || channels != s_pAudioPlay->GetChannels())
		{
			s_pAudioPlay->SetSampleFmt(sampleFmt);
			s_pAudioPlay->SetChannels(channels);
			s_pAudioPlay->SetSampleRate(sampleRate);
		}
	}
	
    return s_pAudioPlay;
}

std::shared_ptr<AudioPcm> AudioPcm::GetInstanse()
{
	return s_pAudioPlay;
}

void AudioPcm::DeleteInstanse()
{
	if (s_pAudioPlay != nullptr)
	{
		s_pAudioPlay = nullptr;
	}
}

AudioPcm::AudioPcm(int sampleRate, SDL_AudioFormat sampleFmt, int channels)
    : m_nSampleRate(sampleRate)
    , m_nSampleFmt(sampleFmt)
    , m_nChannels(channels)
{
	m_nOutSampleFmt = AUDIO_F32SYS;
	m_nNbSamples = 1;
	m_dAudioDev = 0;
	
	if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
		printf("Could not initialize SDL - %s\n", SDL_GetError());
	}

	m_bAudioThreadStart = false;
	for (int i=0; i<AUDIO_PLAYING_MAX; i++)
	{
		m_aAudioPlaying[i].nPlayVolume = 50;
		m_aAudioPlaying[i].nPlayLen = 0;
		m_aAudioPlaying[i].pPlayPos = NULL;
		m_aAudioPlaying[i].audioStream = NULL;
		m_aAudioPlaying[i].bUsed = false;
	}
	//SetSampleRateAndStart(sampleRate);
}

AudioPcm::AudioPcm(int sampleRate, SDL_AudioFormat sampleFmt, int channels, int resampleRate, SDL_AudioFormat outSampleFmt)
    : m_nSampleRate(sampleRate)
    , m_nSampleFmt(sampleFmt)
    , m_nChannels(channels)
{
	m_nOutSampleFmt = outSampleFmt;
	m_nNbSamples = 1;
	m_dAudioDev = 0;
	
	if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
		printf("Could not initialize SDL - %s\n", SDL_GetError());
	}

	m_bAudioThreadStart = false;
	for (int i=0; i<AUDIO_PLAYING_MAX; i++)
	{
		m_aAudioPlaying[i].nPlayVolume = 50;
		m_aAudioPlaying[i].nPlayLen = 0;
		m_aAudioPlaying[i].pPlayPos = NULL;
		m_aAudioPlaying[i].audioStream = NULL;
		m_aAudioPlaying[i].bUsed = false;
	}
	//SetSampleRateAndStart(sampleRate, resampleRate);
}
	
AudioPcm::AudioPcm(int sampleRate, SDL_AudioFormat sampleFmt, int channels, int resampleRate)
    : m_nSampleRate(sampleRate)
    , m_nSampleFmt(sampleFmt)
    , m_nChannels(channels)
{
	m_nOutSampleFmt = AUDIO_F32SYS;
	m_nNbSamples = 1;
	m_dAudioDev = 0;
	
	if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
		printf("Could not initialize SDL - %s\n", SDL_GetError());
	}
	cout << "=====" << __func__ << "(), line:" << __LINE__ << "\n";
	
	m_bAudioThreadStart = false;
	for (int i=0; i<AUDIO_PLAYING_MAX; i++)
	{
		m_aAudioPlaying[i].nPlayVolume = 50;
		m_aAudioPlaying[i].nPlayLen = 0;
		m_aAudioPlaying[i].pPlayPos = NULL;
		m_aAudioPlaying[i].audioStream = NULL;
		m_aAudioPlaying[i].bUsed = false;
	}
	//SetSampleRateAndStart(sampleRate, resampleRate);
}

AudioPcm::~AudioPcm()
{
	cout << "=====" << __func__ << "(), line:" << __LINE__ << "\n";
	AudioThreadStop();

	SDL_Quit();
}

void AudioPcm::SetVolume(int audioIndex, int volume)
{
	if (audioIndex >= AUDIO_PLAYING_MAX)
	{
		return;
	}
	std::unique_lock<std::mutex> locker(m_aAudioPlaying[audioIndex].mutexAudioPlay);
	m_aAudioPlaying[audioIndex].nPlayVolume = volume;
}

void AudioPcm::SetSampleRate(int sampleRate)
{
	cout << "=====" << __func__ << "(), line:" << __LINE__ << ", sampleRate:" << sampleRate << "\n";
	
	m_nSampleRate = sampleRate;
	m_nOutSampleRate = sampleRate;
	m_nOutChannels = m_nChannels;
}

void AudioPcm::SetSampleRateAndStart(int sampleRate)
{
	m_nSampleRate = sampleRate;
	m_nOutSampleRate = sampleRate;
	m_nOutChannels = m_nChannels;
	
	cout << "=====" << __func__ << "(), line:" << __LINE__ << ", sampleRate:" << sampleRate << "\n";
	AudioThreadStop();
	AudioThreadStart();
}

void AudioPcm::SetSampleRateAndStart(int iSrcSampleRate, int iOutSampleRate)
{
    m_nSampleRate = iSrcSampleRate;
	m_nOutSampleRate = iOutSampleRate;
	m_nOutChannels = m_nChannels;
	
	cout << "=====" << __func__ << "(), line:" << __LINE__ << ", iSrcSampleRate:" << iSrcSampleRate << ", iOutSampleRate:" << iOutSampleRate << "\n";
	AudioThreadStop();
	AudioThreadStart();
}

void AudioPcm::SetSampleFmt(SDL_AudioFormat sampleFmt)
{
	m_nSampleFmt = sampleFmt;
}

void AudioPcm::SetChannels(int channels)
{
	m_nChannels = channels;
}

int AudioPcm::GetSampleRate(void)
{
	return m_nSampleRate;
}

SDL_AudioFormat AudioPcm::GetSampleFmt(void)
{
	return m_nSampleFmt;
}

int AudioPcm::GetChannels(void)
{
	return m_nChannels;
}

int AudioPcm::GetOutSampleRate(void)
{
	return m_nOutSampleRate;
}

SDL_AudioFormat AudioPcm::GetOutSampleFmt(void)
{
	return m_nOutSampleFmt;
}

int AudioPcm::GetOutChannels(void)
{
	return m_nOutChannels;
}

int AudioPcm::GetVolume(int audioIndex)
{
	if (audioIndex >= AUDIO_PLAYING_MAX)
	{
		return -1;
	}
	std::unique_lock<std::mutex> locker(m_aAudioPlaying[audioIndex].mutexAudioPlay);
	return m_aAudioPlaying[audioIndex].nPlayVolume;
}

int AudioPcm::RequestAudioPlay()
{
	int index = -1;

	//0固定被占用,所以从1开始
	for (int i=1; i<AUDIO_PLAYING_MAX; i++)
	{
		m_aAudioPlaying[i].mutexAudioPlay.lock();
		if (!m_aAudioPlaying[i].bUsed)
		{
			m_aAudioPlaying[i].nPlayLen = 0;
			m_aAudioPlaying[i].bUsed = true;
			index = i;
			m_aAudioPlaying[i].mutexAudioPlay.unlock();
			break;
		}
		m_aAudioPlaying[i].mutexAudioPlay.unlock();
	}
	
	return index;
}

void AudioPcm::SetAudioPlayData(int index, uint8_t *audioPlayPos, uint32_t audioPlayLen)
{
	if (index >= AUDIO_PLAYING_MAX)
	{
		return;
	}
	std::unique_lock<std::mutex> locker(m_aAudioPlaying[index].mutexAudioPlay);
	m_aAudioPlaying[index].pPlayPos = audioPlayPos;
	m_aAudioPlaying[index].nPlayLen = audioPlayLen;
}

uint32_t AudioPcm::GetAudioPlayLen(int index)
{
	if (index >= AUDIO_PLAYING_MAX)
	{
		return 0;
	}
	std::unique_lock<std::mutex> locker(m_aAudioPlaying[index].mutexAudioPlay);
	return m_aAudioPlaying[index].nPlayLen;
}

void AudioPcm::SetAudioPlayUsed(int index, bool used)
{
	if (index >= AUDIO_PLAYING_MAX)
	{
		return;
	}
	std::unique_lock<std::mutex> locker(m_aAudioPlaying[index].mutexAudioPlay);
	m_aAudioPlaying[index].bUsed = used;
}

bool AudioPcm::GetAudioPlayUsed(int index)
{
	if (index >= AUDIO_PLAYING_MAX)
	{
		return true;
	}
	std::unique_lock<std::mutex> locker(m_aAudioPlaying[index].mutexAudioPlay);
	return m_aAudioPlaying[index].bUsed;
}

void AudioPcm::SetAudioPlayStream(int index, SDL_AudioStream *audioStream)
{
	if (index >= AUDIO_PLAYING_MAX)
	{
		return;
	}
	std::unique_lock<std::mutex> locker(m_aAudioPlaying[index].mutexAudioPlay);
	m_aAudioPlaying[index].audioStream = audioStream;
}

SDL_AudioStream *AudioPcm::GetAudioPlayStream(int index)
{
	if (index >= AUDIO_PLAYING_MAX)
	{
		return NULL;
	}
	std::unique_lock<std::mutex> locker(m_aAudioPlaying[index].mutexAudioPlay);
	
	return m_aAudioPlaying[index].audioStream;
}
	
static void StaticReadAudioData(void *udata, unsigned char *stream, int len)
{
	AudioPcm *ap = static_cast<AudioPcm *>(udata);
	ap->ReadAudioData(stream, len);
}

//回调函数,音频设备需要更多数据的时候会调用该回调函数
void AudioPcm::ReadAudioData(unsigned char *stream, int len) 
{
	SDL_memset(stream, 0, len);//必须执行,否则回调时会一直发声
	cout << "==1===ReadAudioData callback" << endl;
	for (int i=0; i<AUDIO_PLAYING_MAX; i++)
	{
		std::unique_lock<std::mutex> locker(m_aAudioPlaying[i].mutexAudioPlay);
		
		if (m_aAudioPlaying[i].bUsed && m_aAudioPlaying[i].nPlayLen > 0)
		{
			len = (len > m_aAudioPlaying[i].nPlayLen ? m_aAudioPlaying[i].nPlayLen : len);
			
			cout << "==2===m_aAudioPlaying[" << i << "].nPlayLen:" << m_aAudioPlaying[i].nPlayLen << ", len:" << len << endl;
			
			SDL_MixAudioFormat(stream, m_aAudioPlaying[i].pPlayPos, m_nOutSampleFmt, len, m_aAudioPlaying[i].nPlayVolume);
			m_aAudioPlaying[i].pPlayPos += len;
			m_aAudioPlaying[i].nPlayLen -= len;
			//if (m_aAudioPlaying[i].nPlayLen == 0)
			//{
				
			//}
		}
	}
	
}

static int StaticAudioThreadFunc(void *arg)
{
	AudioPcm *ap = static_cast<AudioPcm *>(arg);
	ap->AudioThreadFunc();
	
	return 0;
}

int AudioPcm::AudioOpenDevice(int index)
{
	SDL_AudioSpec spec, spec_out;
	bool usb_audio_flag = false;
	
	cout << __func__ << "(), line:" << __LINE__ << endl;
	std::unique_lock<std::mutex> locker(m_mutexAudioDev);
	//存在则无需再打开
	if (index != 0 && m_dAudioDev != 0)
	{
		return 0;
	}
	else if (index == 0 && m_dAudioDev > 0)
	{
		for (int i=0; i<AUDIO_PLAYING_MAX; i++)
		{
			std::unique_lock<std::mutex> locker(m_aAudioPlaying[i].mutexAudioPlay);
			
			m_aAudioPlaying[i].nPlayLen = 0;
			m_aAudioPlaying[i].pPlayPos = NULL;
			/*if (m_aAudioPlaying[i].audioStream)
			{
				SDL_FreeAudioStream(m_aAudioPlaying[i].audioStream);
				m_aAudioPlaying[i].audioStream = NULL;
			}*/
		}
		
		SDL_PauseAudioDevice(m_dAudioDev, 1);
		SDL_CloseAudioDevice(m_dAudioDev);
		m_dAudioDev = 0;
	}
	cout << "=====m_nOutSampleRate:" << m_nOutSampleRate << ", m_nSampleRate:" << m_nSampleRate << ", m_nSampleFmt:" << m_nSampleFmt 
			<< ", m_nOutSampleFmt:" << m_nOutSampleFmt << ", m_nChannels:" << m_nChannels << ", m_nOutChannels:" << m_nOutChannels << ", index:" << index << endl;
			
	spec.freq = m_nOutSampleRate;
	spec.format = m_nOutSampleFmt;
	spec.channels = m_nOutChannels;
	spec.silence = 0;
	if (index)
	{
		spec.samples = 256;
	}
	else
	{
		spec.samples = (int)std::ceil((float)m_nSampleRate/1000.0)*16;//防止值过小导致无法播放,过大又导致播放卡顿,频率越大样本需要越大
	}
	spec.callback = &StaticReadAudioData;
	spec.userdata = this;

	int n=SDL_GetNumAudioDevices(0);
	for (int i=0; i<n; i++)
	{
		const char *device_name = SDL_GetAudioDeviceName(i, 0);
		if (device_name == NULL)
		{
			cout << i << ", Error:" << SDL_GetError() << "\n";
		}
		else
		{
			cout << i << ",device_name:" << device_name << "\n";
			if (strcmp("USB audio CODEC, USB Audio", device_name) == 0 || strcmp("PCM2912A Audio Codec Analog Stereo", device_name) == 0)
			{
				if ((m_dAudioDev = SDL_OpenAudioDevice(device_name, 0, &spec, &spec_out, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_SAMPLES_CHANGE))==0) {
					cout << "can't open audio, " << SDL_GetError() << endl;
					return -1;
				}
				usb_audio_flag = true;
				break;
			}
		}
    }
	if (!usb_audio_flag)
	{
		if ((m_dAudioDev = SDL_OpenAudioDevice(NULL, 0, &spec, &spec_out, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_SAMPLES_CHANGE))==0) {
			cout << "can't open audio, " << SDL_GetError() << endl;
			return -1;
		}
	}
	if (spec_out.format != m_nOutSampleFmt)
	{
		cout << "spec_out.format:" << spec_out.format << " error\n";
		SDL_CloseAudioDevice(m_dAudioDev);
		return -2;
	}
	if (spec_out.channels != m_nOutChannels) 
	{
		cout << "channels error\n";
		SDL_CloseAudioDevice(m_dAudioDev);
		return -2;
	}	

	m_nOutSampleRate = spec_out.freq;
	m_nNbSamples = spec.samples;
	
	cout << "m_nSampleRate:" << m_nSampleRate << ", m_nOutSampleRate:" << m_nOutSampleRate << ", m_nNbSamples:" << m_nNbSamples << "\n";
	
	//播放
	SDL_PauseAudioDevice(m_dAudioDev, 0);

	return 0;
}

void AudioPcm::AudioCloseDevice()
{
	cout << __func__ << "(), line:" << __LINE__ << endl;
	for (int i=0; i<AUDIO_PLAYING_MAX; i++)
	{
		m_aAudioPlaying[i].mutexAudioPlay.lock();
		//存在音频正在使用则不关闭设备
		if (m_aAudioPlaying[i].bUsed)
		{
			m_aAudioPlaying[i].mutexAudioPlay.unlock();
			return;
		}
		m_aAudioPlaying[i].mutexAudioPlay.unlock();
	}
	
	std::unique_lock<std::mutex> locker(m_mutexAudioDev);
		
	cout << __func__ << "(), line:" << __LINE__ << endl;
	if (m_dAudioDev > 0)
	{
		SDL_PauseAudioDevice(m_dAudioDev, 1);
		SDL_CloseAudioDevice(m_dAudioDev);
		m_dAudioDev = 0;
	}
}

// size需为PCM_BUFF_SIZE整数倍,除非为末尾数据
void AudioPcm::AudioThreadFunc()
{
	char *read_data = NULL;
	int max_play_data_len = 20000;
	int ret;
	int timeout_count = 0;

	m_nCurAudioIndex = 0;
	m_aAudioPlaying[m_nCurAudioIndex].bUsed = true;
	m_aAudioPlaying[m_nCurAudioIndex].nPlayVolume = 50;
	
	ret = AudioOpenDevice(0);
	if (ret < 0)
	{
		m_aAudioPlaying[m_nCurAudioIndex].bUsed = false;
		return;
	}
	
	read_data = (char *)malloc(max_play_data_len);
	if (read_data == NULL)
	{
		cout << "malloc fifo data failed!\n";
		m_aAudioPlaying[m_nCurAudioIndex].bUsed = false;
		AudioCloseDevice();
		return;
	}

	m_mutexAudioStream.lock();
	m_aAudioPlaying[m_nCurAudioIndex].audioStream = SDL_NewAudioStream(m_nSampleFmt, m_nChannels, m_nSampleRate, m_nOutSampleFmt, m_nOutChannels, m_nOutSampleRate);
	if (!m_aAudioPlaying[m_nCurAudioIndex].audioStream)
	{
		cout << "SDL_NewAudioStream error!\n";
	}
	m_mutexAudioStream.unlock();
	
	while (m_bAudioThreadStart) 
	{
		m_mutexAudioStream.lock();
		ret = SDL_AudioStreamAvailable(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
        if (ret <= 0)
    	{
    		//cout << "===========1==============wait data.........." << ret << "\n";
    		//调试发现刷新能减轻播放卡顿
			SDL_AudioStreamFlush(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
			ret = SDL_AudioStreamAvailable(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
    	}
		if (ret <= 0)
	    {
            //cout << "===========2==============wait data.........." << ret << "\n";
			if (timeout_count >= 1000)
			{
				;//	m_bAudioThreadStart = false;
			}
			m_mutexAudioStream.unlock();
            usleep(10000);
			timeout_count++;
            continue;
		}
		else
		{
			timeout_count = 0;
			cout << "=====ret data len.........." << ret << "\n";
			if (ret >= max_play_data_len)
			{
				ret = SDL_AudioStreamGet(m_aAudioPlaying[m_nCurAudioIndex].audioStream, read_data, max_play_data_len);
				SDL_AudioStreamClear(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
			}
			else
			{
				ret = SDL_AudioStreamGet(m_aAudioPlaying[m_nCurAudioIndex].audioStream, read_data, ret);
			}
			//SDL_QueueAudio(audio_dev, read_data, ret);
		}
		
		m_mutexAudioStream.unlock();

        cout << "=====atual play data len.........." << ret << ", m_nSampleRate:" << m_nSampleRate << ", m_nOutSampleRate:" << m_nOutSampleRate << ", m_nNbSamples:" << m_nNbSamples << "\n";

		m_aAudioPlaying[m_nCurAudioIndex].mutexAudioPlay.lock();
		m_aAudioPlaying[m_nCurAudioIndex].pPlayPos = (uint8_t *)read_data;
		m_aAudioPlaying[m_nCurAudioIndex].nPlayLen = ret; //长度为读出数据长度,在read_audio_data中做减法
		m_aAudioPlaying[m_nCurAudioIndex].mutexAudioPlay.unlock();
		
		while (1) //判断是否播放完毕
		{
			m_aAudioPlaying[m_nCurAudioIndex].mutexAudioPlay.lock();
			if (m_aAudioPlaying[m_nCurAudioIndex].nPlayLen == 0)
			{
				m_aAudioPlaying[m_nCurAudioIndex].mutexAudioPlay.unlock();
				timeout_count = 0;
				SDL_AudioStreamFlush(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
				break;
			}
			if (timeout_count >= 2000)
			{
				m_aAudioPlaying[m_nCurAudioIndex].mutexAudioPlay.unlock();
				cout << "=========================wait data============================" << m_aAudioPlaying[m_nCurAudioIndex].nPlayLen << endl;
				timeout_count = 0;
				break;
			}
			m_aAudioPlaying[m_nCurAudioIndex].mutexAudioPlay.unlock();
			timeout_count++;
			SDL_Delay(1);
		}
	}

	m_mutexAudioStream.lock();
	if (m_aAudioPlaying[m_nCurAudioIndex].audioStream)
	{
		SDL_AudioStreamClear(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
		SDL_FreeAudioStream(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
		m_aAudioPlaying[m_nCurAudioIndex].audioStream = NULL;
	}
	SetAudioPlayUsed(m_nCurAudioIndex, false);
	m_mutexAudioStream.unlock();
	
	if (read_data)
	{
		free(read_data);
	}
	AudioCloseDevice();
}

int AudioPcm::AudioClearData()
{
	m_mutexAudioStream.lock();
	if (m_aAudioPlaying[m_nCurAudioIndex].audioStream)
	{
		SDL_AudioStreamClear(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
	}
	m_mutexAudioStream.unlock();

	return 0;
}

int AudioPcm::AudioDataFinished()
{
	int ret = -1;
	
	m_mutexAudioStream.lock();
	if (m_aAudioPlaying[m_nCurAudioIndex].audioStream)
	{
		ret = SDL_AudioStreamAvailable(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
	}
	m_mutexAudioStream.unlock();

	return ret;
}

int AudioPcm::AudioPushData(char *data, int len)
{
	int count = 0;
	int rem = 2;

	m_mutexAudioStream.lock();
	//printf("=====%s(), %d, m_bAudioThreadStart:%d\n", __func__, __LINE__, m_bAudioThreadStart);
	if (!m_bAudioThreadStart)
	{
		cout << "Audio push data, but thread stopped!\n";
		m_mutexAudioStream.unlock();
		return -2;
	}
	m_mutexAudioStream.unlock();
	//printf("=====%s(), %d, data:%p, len:%d\n", __func__, __LINE__, data, len);
	//G_INFO("=====" << __func__ << "(), line:" << __LINE__ << ", len:" << len << "\n");
	switch (m_nSampleFmt)
	{
		case AUDIO_S16LSB:
		case AUDIO_U16LSB:
		case AUDIO_S16MSB:
		case AUDIO_U16MSB:
			rem=2;
			break;
		case AUDIO_U8:
		case AUDIO_S8:
			rem=1;
			break;
		default:
			rem=4;
			break;
	}

	if (len%(rem*m_nChannels) != 0)
	{
		len -= len%(rem*m_nChannels);
		cout << "=====" << __func__ << "(), line:" << __LINE__ << ", len:" << len << "\n";
	}
	while (count < 500)
    {
    	m_mutexAudioStream.lock();
		if (m_aAudioPlaying[m_nCurAudioIndex].audioStream == NULL || SDL_AudioStreamPut(m_aAudioPlaying[m_nCurAudioIndex].audioStream, data, len) != 0)
		{
			m_mutexAudioStream.unlock();
			usleep(2000);
            count++;
			continue;
		}
		//SDL_AudioStreamFlush(m_aAudioPlaying[m_nCurAudioIndex].audioStream);
		m_mutexAudioStream.unlock();
        break;
	}
	
	if (count == 500)
	{
		cout << "Audio push data timeout!\n";
		return -1;
	}

	return 0;
}

void AudioPcm::AudioThreadStart()
{
	//size_t stackSize;
	int policy = SCHED_FIFO;
    sched_param param;
    param.sched_priority = 3;
	
	//printf("=====%s(), %d\n", __func__, __LINE__);
	m_bAudioThreadStart = true;
	
	m_threadAudio = SDL_CreateThread(StaticAudioThreadFunc, NULL, this);
	//pthread_attr_init(&m_attrAudio);
	//pthread_attr_setdetachstate(&m_attrAudio, PTHREAD_CREATE_DETACHED);
	//pthread_attr_getstacksize(&m_attrAudio, &stackSize);
	//pthread_attr_setstacksize(&m_attrAudio, stackSize*100);
	//pthread_create(&m_threadAudio, NULL, &StaticAudioThreadFunc, this);
	//pthread_setschedparam(m_threadAudio, policy, &param);
	//m_threadAudio = std::thread(&AudioPcm::AudioThreadFunc, this);
    //pthread_setschedparam(m_threadAudio.native_handle(), policy, &param);
}

void AudioPcm::AudioThreadStop()
{
	int status = -1;
	
	if (m_bAudioThreadStart)
	{
		m_bAudioThreadStart = false;
		//加快线程退出速度
		for (int i=0; i<AUDIO_PLAYING_MAX; i++)
		{
			std::unique_lock<std::mutex> locker(m_aAudioPlaying[i].mutexAudioPlay);
			
			m_aAudioPlaying[i].nPlayLen = 0;
			m_aAudioPlaying[i].pPlayPos = NULL;
		}
	    //if (m_threadAudio.joinable()) {
	    //	m_threadAudio.join();
	    //}
	    //pthread_join(m_threadAudio, NULL);
		//pthread_attr_destroy(&m_attrAudio);
		SDL_WaitThread(m_threadAudio, &status);
	}
	//printf("=====%s(), %d, m_bAudioThreadStart:%d\n", __func__, __LINE__, m_bAudioThreadStart);
}

bool AudioPcm::AudioThreadStatus()
{
	return m_bAudioThreadStart;
}

int AudioPcm::AudioPlayStop(int audioIndex)
{
	std::shared_ptr<AudioPcm> pAudioPlay = s_pAudioPlay;
	
	if (pAudioPlay == nullptr)
	{
		cout << "No audio playing instanse!\n";
		return -1;
	}

	if (audioIndex <= 0 || audioIndex >= AUDIO_PLAYING_MAX)
	{
		cout << __func__ << "Audio player index error!" << endl;
		return -2;
	}
	
	if (!pAudioPlay->GetAudioPlayUsed(audioIndex))
	{
		cout << "AudioPlayer already stopped!\n";
		return -3;
	}
	else
	{
		pAudioPlay->SetAudioPlayUsed(audioIndex, false);
	}
	
	return 0;
}

static int StaticAudioPlayDataThreadFunc(void *arg)
{
	int audio_index = -1;
	if (arg != NULL)
	{
		audio_index = *((char *)arg);
		free(arg);
	}
		
	std::shared_ptr<AudioPcm> pAudioPlay = s_pAudioPlay;
	SDL_AudioStream *audioStream;
	char *read_data = NULL;
	int ret;
	
	if (audio_index <= 0 || audio_index >= AUDIO_PLAYING_MAX)
	{
		cout << __func__ << "Audio player index error! audio_index:" << audio_index << endl;
		return -1;
	}
	cout << __func__ << "(), line:" << __LINE__ << ", audio_index:" << audio_index << endl;
	audioStream = pAudioPlay->GetAudioPlayStream(audio_index);
	if (audioStream == NULL)
	{
		pAudioPlay->SetAudioPlayUsed(audio_index, false);
		return -2;
	}
	
	if (pAudioPlay == nullptr)
	{
		ret = -3;
		goto __error2;
	}
	
	SDL_AudioStreamFlush(audioStream);
	ret = SDL_AudioStreamAvailable(audioStream);
	if (ret > 0)
	{
		cout << __func__ << "(), line:" << __LINE__ << ", audio_index:" << audio_index << endl;
		read_data = (char *)malloc(ret);
		if (read_data == NULL)
		{
			ret = -4;
			goto __error2;
		}
		ret = SDL_AudioStreamGet(audioStream, read_data, ret);
		pAudioPlay->SetAudioPlayData(audio_index, (uint8_t *)read_data, ret);
		ret = pAudioPlay->AudioOpenDevice(audio_index);
		if (ret != 0)
		{
			goto __error1;
		}
	}
	cout << __func__ << "(), line:" << __LINE__ << ", audio_index:" << audio_index << endl;
	while (pAudioPlay->GetAudioPlayUsed(audio_index) && pAudioPlay->GetAudioPlayLen(audio_index) > 0)
	{
		cout << "-----------------------------------\n";
		SDL_Delay(1);
	}

	ret = 0;

	pAudioPlay->AudioCloseDevice();

__error1:
	if (read_data)
	{
		free(read_data);
	}
__error2:
	if (audioStream != NULL)
	{
		SDL_FreeAudioStream(audioStream);
		audioStream = NULL;
	}
	pAudioPlay->SetAudioPlayUsed(audio_index, false);
	
	return ret;
}

int AudioPcm::AudioPlayData(char *data, int len, int sampleRate, SDL_AudioFormat sampleFmt, int channels)
{
	int ret;
	std::shared_ptr<AudioPcm> pAudioPlay = s_pAudioPlay;
	SDL_AudioStream *audioStream;
	SDL_Thread *threadAudioPlay;
	char *pAudioIndex = NULL;
	int audio_index = -1;
	
	if (data == NULL)
	{
		cout << "Audio player need data!\n";
		return -1;
	}
	
	if (pAudioPlay == nullptr)
	{
		pAudioPlay = AudioPcm::CreateInstanse(sampleRate, sampleFmt, channels);	
	}
	
	audio_index = pAudioPlay->RequestAudioPlay();
	if (audio_index <= 0 || audio_index >= AUDIO_PLAYING_MAX)
	{
		cout << __func__ << ":Audio player request failed!" << endl;
		return -1;
	}
	pAudioPlay->SetVolume(audio_index, SDL_MIX_MAXVOLUME);
	
	audioStream = SDL_NewAudioStream(sampleFmt, channels, sampleRate, pAudioPlay->GetOutSampleFmt(), pAudioPlay->GetOutChannels(), pAudioPlay->GetOutSampleRate());
	if (!audioStream)
	{
		cout << "SDL_NewAudioStream error!\n";
		return -2;
	}
	pAudioPlay->SetAudioPlayStream(audio_index, audioStream);
	if (SDL_AudioStreamPut(audioStream, data, len) != 0)
	{
		cout << "SDL_AudioStreamPut error!\n";
		SDL_FreeAudioStream(audioStream);
		audioStream = NULL;
		return -3;
	}
	pAudioIndex = (char *)malloc(1);
	if (pAudioIndex == NULL)
	{
		G_INFO("malloc error!\n");
		SDL_FreeAudioStream(audioStream);
		audioStream = NULL;
		return -4;
	}
	*pAudioIndex = audio_index;
	G_INFO(__func__ << "(), pAudioIndex:" << *pAudioIndex);
	threadAudioPlay = SDL_CreateThread(StaticAudioPlayDataThreadFunc, "AudioPlay", pAudioIndex);
	if (threadAudioPlay)
	{
		SDL_DetachThread(threadAudioPlay);
	}
	
	cout << "--------------audio_index:" << audio_index << endl;
	
	return audio_index;
}

int AudioPcm::AudioPlayByFile(const char *filename, int sampleRate, SDL_AudioFormat sampleFmt, int channels)
{
	int ret;
	int file_size;
	char *file_buffer;
	int audio_index = -1;
	
	if (filename == NULL)
	{
		cout << "Audio player need filename!\n";
	}
	FILE *fp = fopen(filename, "rb");
	if (fp == NULL) {
		cout << "cannot open this file\n";
		return -1;
	}
	fseek(fp, 0, SEEK_END);
	file_size = ftell(fp);
	
	file_buffer = (char *)malloc(file_size);
	if (file_buffer == NULL)
	{
		cout << "Malloc file size:" << file_size << " failed!\n";
		fclose(fp);
		return -2;
	}
	
	fseek(fp, 0, SEEK_SET);
	ret = fread(file_buffer, 1, file_size, fp);
	cout << "ret:" << ret <<", file_size:" << file_size << ", filename:" << filename << endl;
	if (ret != file_size)
	{
		free(file_buffer);
		fclose(fp);
		return -1;
	}
	fclose(fp);
	
	audio_index = AudioPlayData(file_buffer, file_size, sampleRate, sampleFmt, channels);
	free(file_buffer);
	
	return audio_index;
}

#endif


//测试代码

static void sigterm_handler(int sig)
{
	AudioPcm::DeleteInstanse();
	printf("=====%s(), %d\n", __func__, __LINE__);
	exit(123);
}

void *PushThreadFunc(void *arg)
{
	char *filename = (char *)arg;
	int file_size;
	char *file_buffer;
	int count = 0;
	int step = 1024;//必须8的倍数
	int ret;
	std::shared_ptr<AudioPcm> pSoundSpeaker;

	FILE *fp = fopen(filename, "rb");
	if (fp == NULL) {
		printf("cannot open this file\n");
		return NULL;
	}
	fseek(fp, 0, SEEK_END);
	file_size = ftell(fp);

	file_buffer = (char *)malloc(file_size);
	fseek(fp, 0, SEEK_SET);
	ret = fread(file_buffer, 1, file_size, fp);
	printf("ret:%d, file_size:%d\n", ret, file_size);
	if (ret != file_size)
	{
		free(file_buffer);
		fclose(fp);
		return NULL;
	}
	fclose(fp);
	
	pSoundSpeaker = AudioPcm::GetInstanse();
	if (pSoundSpeaker == nullptr)
	{
		return NULL;
	}

	while (count*step < file_size)
	{
		if ((count+1)*step > file_size) {
			pSoundSpeaker->AudioPushData(file_buffer+count*step, file_size - count*step);
		} else {
			pSoundSpeaker->AudioPushData(file_buffer+count*step, step);
		}
		count++;
	}

	free(file_buffer);
	return 0;
}

int main(int argc, char *argv[])
{
	std::shared_ptr<AudioPcm> pSoundSpeaker;
	pthread_t pushThread;	
	int ret;
	
	signal(SIGINT , sigterm_handler); // Interrupt (ANSI).    
	signal(SIGTERM, sigterm_handler); // Termination (ANSI).  
	
	//pSoundSpeaker = AudioPcm::CreateInstanse(atoi(argv[3]), AUDIO_S16SYS, atoi(argv[4]));
	
	//pthread_create(&pushThread, NULL, &PushThreadFunc, argv[1]);

	//AudioPcm::AudioPlayByFile(argv[1], 48000, AUDIO_S16SYS, 1);
	//AudioPcm::AudioPlayByFile(argv[2], 8000, AUDIO_S16SYS, 1);

	while (1)
	{
		getchar();
		AudioPcm::AudioPlayByFile("media/click.pcm", 48000, AUDIO_S16SYS, 1);
		//sleep(2);
	}
	
	//pthread_join(pushThread, NULL);
	//AudioPcm::DeleteInstanse();
	
	return 0;
}




总结

在SDL2中,共提供了两套用于播放音频数据的API,本次介绍了使用回调函数播放音频数据的方式,且实现了多路音频播放。

  • 13
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值