iOS OpenAL处理直播音频流数据

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;
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值