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) | 结果 |
30s | 194 | 正常播放 |
59 | 64 | 正常播放 |
8s | 128 | 无法播放 |
5s | 128 | 无法播放 |
1s | 320 | 无法播放 |
<1s | 192 | 无法播放 |
调用的地方
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 框架中的一个函数,用于设置外部音频文件的属性。
最终定位原因
alBufferDataStatic是OpenAL的一个扩展,相对于alBufferData来说的。功能是加载音频数据到内存并关联到bufferId。只不过,alBufferData会拷贝音频数据所以调用后,我们可以free掉音频数据。而alBufferDataStatic并不会拷贝,所以音频数据data我们要一直保留并自己管理。
openal的标准里面压根就没有alBufferDataStatic这个函数!