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这个函数!