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

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

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 框架中的一个函数,用于设置外部音频文件的属性。

最终定位原因

image.png

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

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

目录
相关文章
|
6月前
|
存储 监控 iOS开发
iOS应用崩溃了,如何通过崩溃手机连接电脑查找日志方法
在iOS应用开发过程中,调试日志和奔溃日志是开发者必不可少的工具。当iOS手机崩溃时,我们可以连接电脑并使用Xcode Console等工具来查看日志。然而,这种方式可能不够方便,并且处理奔溃日志也相当繁琐。克魔助手的出现为开发者带来了极大的便利,本文将详细介绍其功能和使用方法。 克魔助手会提供两种日志,一种是实时的,一种的是崩溃的。(由于崩溃日志的环境很麻烦,目前只展示实时日志操作步骤)
|
iOS开发
iOS开发 - 打包静态framework后,引用时必须做的一件事,否则崩溃
iOS开发 - 打包静态framework后,引用时必须做的一件事,否则崩溃
191 0
|
iOS开发
iOS开发 - 关于MJRefresh刷新崩溃的问题
iOS开发 - 关于MJRefresh刷新崩溃的问题
172 0
|
存储 运维 监控
mPass iOS崩溃与Crash⽇志符号化详解
在日常mPaas客户端运维中,经常遇到一些iOS闪退,无法直接从闪退堆栈看到原因。主要是因为iOS客户端上传的崩溃日志里的调用栈信息都是通过内存地址记录的,无法直接看到闪退的调用栈信息。如果需要定位到调用栈,需要使用符号表对闪退日志进行符号化。本文从日志收集、日志符号化原理、符号化工具等方向介绍下iOS下crash日志符号化方案。
1985 1
mPass iOS崩溃与Crash⽇志符号化详解
|
存储 iOS开发
iOS崩溃排查技巧:symbolicatecrash符号化分析问题、导出和隐藏符号
iOS崩溃排查技巧:symbolicatecrash符号化分析问题、导出和隐藏符号
460 0
iOS崩溃排查技巧:symbolicatecrash符号化分析问题、导出和隐藏符号
|
iOS开发
山不过来我过去-iOS 10联网问题分析及解决之道
从已知的用户反馈来看,目前iOS 10所有的版本都被偶现的首次安装App无法联网的问题所困扰。苹果迟迟未修复这个bug。详细的原因和解决办法可以参考:[具透 | 你可能不知道,iOS 10 有一个中国「特供」的联网权限功能](http://sspai.com/35720)。 不少用户因为这个原因给我们一颗星,严重影响我们App的声誉。所以我针对这个问题做一些分析,看看能否找到一些好的解决办法
6343 0
|
iOS开发
gcd崩溃系统堆栈等,iOS性能相关度量技术,很不错,备查。
https://ming1016.github.io/2017/06/20/deeply-ios-performance-optimization/?hmsr=toutiao.
1346 0