OpenAL处理直播音频流数据
音频PCM数据处理,解码部分这里不包括
openAL介绍
对于音频处理,openAL有上下文context
,设备device
,声源source
和声音数据buffer
四个东西
我用我自己理解的一种方式阐述:
想像下,在一个空间(即
Context
)内,例如一个正方形的房间
有很多喇叭,这些喇叭播放不同的声音,同时,这些喇叭可以进行操作,不断移动或固定在某一位置
openAL就想当与模拟了这样一个场景
buffer
是用于存储数据的,它用来放在声源source
中source
声源可以理解就是一个喇叭,声音通过它来播放,它也可以通过代码移动来模拟各种音效context
上下文就相当于我们比如的一个正方形房间device
设备在上方比喻中没有出现,它其实就是表示负责处理openAL这些资源的设备
openAL使用
openAL的操作不需要在主线程,可以自开一个串行队列来处理
初始化方法
pthread_mutex_lock(&_lock);
if (self.isInit) {
GSLog(@"must clean before init");
pthread_mutex_unlock(&_lock);
return NO;
}
pthread_mutex_unlock(&_lock);
//设置定时器
self.timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(cleanBuffers)
userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
NSLog(@"[OpenAL] initOpenAL start");
if (!self.mDevice) {
pthread_mutex_lock(&_lock);
self.mDevice = alcOpenDevice(NULL);
self.isInit = YES;
pthread_mutex_unlock(&_lock);
// 得到设备说明.
printf("[OpenAL] Using device '%s' \n", alcGetString(self.mDevice, ALC_DEVICE_SPECIFIER));
}
if (!self.mDevice) {
NSLog(@"[OpenAL] initOpenAL failed: device is NULL");
return NO;
}
if (!self.mContext) {
self.mContext = alcCreateContext(self.mDevice, NULL);
alcMakeContextCurrent(self.mContext);
}
//创建音源
alGenSources(1, &outSourceID);
//设置播放速度, (无效, 不知道为何)
alSpeedOfSound(1.0f);
//设为不循环
alSourcei(outSourceID, AL_LOOPING, AL_FALSE);
//播放模式设为流式播放
alSourcef(outSourceID, AL_SOURCE_TYPE, AL_STREAMING);
//设置播放音量
alSourcef(outSourceID, AL_GAIN, 1);
//清除错误
alGetError();
if (!self.mContext) {
NSLog(@"[OpenAL] initOpenAL failed: context is NULL");
return NO;
}
NSLog(@"[OpenAL] initOpenAL end");
这里值得注意的是alcOpenDevice方法在调用成功一次后,后续的所有调用都会失败,所以如果同时获取两次alcOpenDevice的话,会导致device获取失败!
数据绑定buffer,buffer放入source
- (void)insertPCMDataToQueue:(const unsigned char *)data size:(UInt32)size
{
if (!self.isRunning) {
return;
}
dispatch_sync(self.taskQueue, ^{
if (!self.mDevice) {
NSLog(@"[OpenAL] mDevice is nil");
}
if (!self.mContext) {
self.mContext = alcCreateContext(self.mDevice, NULL);
alcMakeContextCurrent(self.mContext);
NSLog(@"[OpenAL] mcontext is nil");
}
ALenum error;
//读取错误信息
error = alGetError();
if (error != AL_NO_ERROR) {
NSLog(@"[OpenAL] alGetError error :%@",openAlErrorToString(error));
return;
}
//常规安全性判断
if (data == NULL) {
NSLog(@"[OpenAL] data is NULL");
return;
}
//建立缓存区
ALuint bufferID = 0;
alGenBuffers(1, &bufferID);
error = alGetError();
if (error != AL_NO_ERROR) {
NSLog(@"[OpenAL] alGenBuffers error :%@",openAlErrorToString(error));
return ;
}
// //删除已经处理的音频数据
ALint processed;
alGetSourcei(self.outSourceID, AL_BUFFERS_PROCESSED, &processed);
while (processed--) {
ALuint mbufferID;
alSourceUnqueueBuffers(self.outSourceID, 1, &mbufferID);
alDeleteBuffers(1, &mbufferID);
}
//将数据存入缓存区
alBufferData(bufferID, AL_FORMAT_MONO16, (char *)data, (ALsizei)size, 16000);
error = alGetError();
if (error != AL_NO_ERROR) {
NSLog(@"[OpenAL] alBufferData error :%@",openAlErrorToString(error));
return;
}
//添加到队列
alSourceQueueBuffers(self.outSourceID, 1, &bufferID);
error = alGetError();
if (error != AL_NO_ERROR) {
NSLog(@"[OpenAL] alSourceQueueBuffers error :%@",openAlErrorToString(error));
return;
}
//开始播放
[self play];
});
}
暂停播放,释放
- (void)play
{
ALint state;