[√]cocos2dx openal在ios崩溃的问题分析

254 阅读2分钟

ios上会产生崩溃

alGetSourcei(_alSource, AL_SOURCE_STATE, &state);
assert(state == AL_PLAYING); // 触发断言state=4116

关于state的状态

#define AL_SOURCE_STATE                           0x1010 //4112
#define AL_INITIAL                                0x1011 //4113
#define AL_PLAYING                                0x1012 //4114
#define AL_PAUSED                                 0x1013 //4115
#define AL_STOPPED                                0x1014 //4116
alSourcePlay(_alSource);
alGetSourcei(_alSource, AL_SOURCE_STATE, &state);
  • 在 ios 上 state 是 AL_STOPPED
  • 在win/android上 steate是AL_PLAYING

chatgpt告诉我没有data也是这种表现,这个答案也是正确的!

现象

音频时长(s)采样比特率(kbps)结果
30s194正常播放
5964正常播放
8s128无法播放
5s128无法播放
1s320无法播放
<1s192无法播放

调用的地方

void AudioEngineImpl::_play2d(AudioCache *cache, int audioID)
{
    //Note: It may bn in sub thread or main thread :(
    if (!*cache->_isDestroyed && cache->_state == AudioCache::State::READY)
    {
        _threadMutex.lock();
        auto playerIt = _audioPlayers.find(audioID);
        if (playerIt != _audioPlayers.end() && playerIt->second->play2d()) { // call
            _scheduler->performFunctionInCocosThread([audioID](){

                if (AudioEngine::_audioIDInfoMap.find(audioID) != AudioEngine::_audioIDInfoMap.end()) {
                    AudioEngine::_audioIDInfoMap[audioID].state = AudioEngine::AudioState::PLAYING;
                }
            });
        }
        _threadMutex.unlock();
    }
    else
    {
        ALOGD("AudioEngineImpl::_play2d, cache was destroyed or not ready!");
        auto iter = _audioPlayers.find(audioID);
        if (iter != _audioPlayers.end())
        {
            iter->second->_removeByAudioEngine = true;
        }
    }
}

int AudioEngineImpl::play2d(const std::string &filePath ,bool loop ,float volume){
    // 预加载,没有回调,里面会触发readDataTask,进而触发后续的callback
    auto audioCache = preload(filePath, nullptr);

    // 添加回调的地方
    audioCache->addPlayCallback(std::bind(&AudioEngineImpl::_play2d,this,audioCache,_currentAudioID));

}
void AudioCache::readDataTask(unsigned int selfId){
    AudioDecoder decoder; // ios的decoder是单独实现的,接口一致
    const uint32_t bytesPerFrame = decoder->getBytesPerFrame();
    uint32_t totalFrames = originalTotalFrames;
    uint32_t dataSize = totalFrames * bytesPerFrame;
    if (dataSize <= PCMDATA_CACHEMAXSIZE){ // #define PCMDATA_CACHEMAXSIZE 1048576
         // 发现不能播放的音频走了这个逻辑
    }

}
void AudioCache::addPlayCallback(const std::function<void()>& callback)
{
    // 将回调添加到list里面
    _playCallbacks.push_back(callback);
}
  • 进入dataSize <= PCMDATA_CACHEMAXSIZE的逻辑
const uint32_t bytesPerFrame = decoder->getBytesPerFrame();
uint32_t totalFrames = originalTotalFrames;
uint32_t dataSize = totalFrames * bytesPerFrame;
// _outputFormat的来源
AudioStreamBasicDescription	fileFormat;
ret = ExtAudioFileGetProperty(_extRef, kExtAudioFileProperty_FileDataFormat, &propertySize, &fileFormat);
_outputFormat.mChannelsPerFrame = fileFormat.mChannelsPerFrame;// 每帧的声道数
// getBytesPerFrame的来源
_bytesPerFrame = 2 * _outputFormat.mChannelsPerFrame;


ret = ExtAudioFileSetProperty(_extRef, kExtAudioFileProperty_ClientDataFormat, sizeof(_outputFormat), &_outputFormat);
  • ExtAudioFileGetProperty:Core Audio 框架中的一个函数,用于获取外部音频文件的属性。
  • ExtAudioFileSetProperty: Core Audio 框架中的一个函数,用于设置外部音频文件的属性。

最终定位原因

image.png

alBufferDataStatic是OpenAL的一个扩展,相对于alBufferData来说的。功能是加载音频数据到内存并关联到bufferId。只不过,alBufferData会拷贝音频数据所以调用后,我们可以free掉音频数据。而alBufferDataStatic并不会拷贝,所以音频数据data我们要一直保留并自己管理。

openal的标准里面压根就没有alBufferDataStatic这个函数!