Skip to content

Commit 432a10b

Browse files
cigumodrelaptop
authored andcommitted
changes AudioCache to use alBufferData instead of alBufferDataStatic
(also makes test 19 faster to trigger openal bugs faster) The original problem: CrashIfClientProvidedBogusAudioBufferList cocos2d#18948 is not happening anymore, but there's still a not very frequent issue that makes OpenAL crash with a call stack like this. AudioCache::readDataTask > alBufferData > CleanUpDeadBufferList It happes more frequently when the device is "cold", which means after half an hour of not using the device (locked). I could not find the actual source code for iOS OpenAL, so I used the macOS versions: https://opensource.apple.com/source/OpenAL/OpenAL-48.7/Source/OpenAL/oalImp.cpp.auto.html They seem to use CAGuard.h to make sure the dead buffer list has no threading issues. I'm worried because the CAGuard code I found has macos and win32 define but no iOS, so I'm not sure. I guess the iOS version is different and has the guard. I could not find a place in the code that's unprotected by the locks except the InitializeBufferMap() which should not be called more than once from cocos, and there's a workaround in AudioEngine-impl for it. I reduced the occurence of the CleanUpDeadBufferList crash by moving the guard in ~AudioCache to cover the alDeleteBuffers call.
1 parent 7faf5cf commit 432a10b

File tree

2 files changed

+37
-47
lines changed

2 files changed

+37
-47
lines changed

cocos/audio/apple/AudioCache.mm

Lines changed: 36 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -150,35 +150,19 @@ static void setTimeout(double seconds, const std::function<void()>& cb)
150150
}
151151
//wait for the 'readDataTask' task to exit
152152
_readDataTaskMutex.lock();
153-
_readDataTaskMutex.unlock();
154153

155-
if (_pcmData)
154+
if (_state == State::READY)
156155
{
157-
if (_state == State::READY)
158-
{
159-
if (_alBufferId != INVALID_AL_BUFFER_ID && alIsBuffer(_alBufferId))
160-
{
161-
ALOGV("~AudioCache(id=%u), delete buffer: %u", _id, _alBufferId);
162-
alDeleteBuffers(1, &_alBufferId);
163-
_alBufferId = INVALID_AL_BUFFER_ID;
164-
}
165-
}
166-
else
156+
if (_alBufferId != INVALID_AL_BUFFER_ID && alIsBuffer(_alBufferId))
167157
{
168-
ALOGW("AudioCache (%p), id=%u, buffer isn't ready, state=%d", this, _id, _state);
158+
ALOGV("~AudioCache(id=%u), delete buffer: %u", _id, _alBufferId);
159+
alDeleteBuffers(1, &_alBufferId);
160+
_alBufferId = INVALID_AL_BUFFER_ID;
169161
}
170-
171-
// fixed #17494: CrashIfClientProvidedBogusAudioBufferList
172-
// We're using 'alBufferDataStaticProc' for speeding up
173-
// the performance of playing audio without preload, but we need to manage the memory by ourself carefully.
174-
// It's probably that '_pcmData' is freed before OpenAL finishes the audio render task,
175-
// then 'CrashIfClientProvidedBogusAudioBufferList' may be triggered.
176-
// 'cpp-tests/NewAudioEngineTest/AudioSwitchStateTest' can reproduce this issue without the following fix.
177-
// The workaround is delaying 200ms to free pcm data.
178-
char* data = _pcmData;
179-
setTimeout(0.2, [data](){
180-
free(data);
181-
});
162+
}
163+
else
164+
{
165+
ALOGW("AudioCache (%p), id=%u, buffer isn't ready, state=%d", this, _id, _state);
182166
}
183167

184168
if (_queBufferFrames > 0)
@@ -189,6 +173,7 @@ static void setTimeout(double seconds, const std::function<void()>& cb)
189173
}
190174
}
191175
ALOGVV("~AudioCache() %p, id=%u, end", this, _id);
176+
_readDataTaskMutex.unlock();
192177
}
193178

194179
void AudioCache::readDataTask(unsigned int selfId)
@@ -200,6 +185,7 @@ static void setTimeout(double seconds, const std::function<void()>& cb)
200185
_state = State::LOADING;
201186

202187
AudioDecoder decoder;
188+
char* l_pcmData = NULL;
203189
do
204190
{
205191
if (!decoder.open(_fileFullPath.c_str()))
@@ -259,37 +245,25 @@ static void setTimeout(double seconds, const std::function<void()>& cb)
259245
// Reset to frame 0
260246
BREAK_IF_ERR_LOG(!decoder.seek(0), "AudioDecoder::seek(0) failed!");
261247

262-
_pcmData = (char*)malloc(dataSize);
263-
memset(_pcmData, 0x00, dataSize);
248+
l_pcmData = (char*)malloc(dataSize);
249+
memset(l_pcmData, 0x00, dataSize);
250+
ALOGV(" id=%u l_pcmData alloc: %p", selfId, l_pcmData);
264251

265252
if (adjustFrames > 0)
266253
{
267-
memcpy(_pcmData + (dataSize - adjustFrameBuf.size()), adjustFrameBuf.data(), adjustFrameBuf.size());
268-
}
269-
270-
alGenBuffers(1, &_alBufferId);
271-
auto alError = alGetError();
272-
if (alError != AL_NO_ERROR) {
273-
ALOGE("%s: attaching audio to buffer fail: %x", __PRETTY_FUNCTION__, alError);
274-
break;
254+
memcpy(l_pcmData + (dataSize - adjustFrameBuf.size()), adjustFrameBuf.data(), adjustFrameBuf.size());
275255
}
276256

277257
if (*_isDestroyed)
278258
break;
279259

280-
alBufferDataStaticProc(_alBufferId, _format, _pcmData, (ALsizei)dataSize, (ALsizei)sampleRate);
281-
282-
framesRead = decoder.readFixedFrames(std::min(framesToReadOnce, remainingFrames), _pcmData + _framesRead * bytesPerFrame);
260+
framesRead = decoder.readFixedFrames(std::min(framesToReadOnce, remainingFrames), l_pcmData + _framesRead * bytesPerFrame);
283261
_framesRead += framesRead;
284262
remainingFrames -= framesRead;
285263

286264
if (*_isDestroyed)
287265
break;
288266

289-
_state = State::READY;
290-
291-
invokingPlayCallbacks();
292-
293267
uint32_t frames = 0;
294268
while (!*_isDestroyed && _framesRead < originalTotalFrames)
295269
{
@@ -298,7 +272,7 @@ static void setTimeout(double seconds, const std::function<void()>& cb)
298272
{
299273
frames = originalTotalFrames - _framesRead;
300274
}
301-
framesRead = decoder.read(frames, _pcmData + _framesRead * bytesPerFrame);
275+
framesRead = decoder.read(frames, l_pcmData + _framesRead * bytesPerFrame);
302276
if (framesRead == 0)
303277
break;
304278
_framesRead += framesRead;
@@ -307,11 +281,24 @@ static void setTimeout(double seconds, const std::function<void()>& cb)
307281

308282
if (_framesRead < originalTotalFrames)
309283
{
310-
memset(_pcmData + _framesRead * bytesPerFrame, 0x00, (totalFrames - _framesRead) * bytesPerFrame);
284+
memset(l_pcmData + _framesRead * bytesPerFrame, 0x00, (totalFrames - _framesRead) * bytesPerFrame);
311285
}
312-
ALOGV("pcm buffer was loaded successfully, total frames: %u, total read frames: %u, adjust frames: %u, remainingFrames: %u", totalFrames, _framesRead, adjustFrames, remainingFrames);
313286

287+
ALOGV("pcm buffer was loaded successfully, total frames: %u, total read frames: %u, adjust frames: %u, remainingFrames: %u", totalFrames, _framesRead, adjustFrames, remainingFrames);
314288
_framesRead += adjustFrames;
289+
290+
alGenBuffers(1, &_alBufferId);
291+
auto alError = alGetError();
292+
if (alError != AL_NO_ERROR) {
293+
ALOGE("%s: attaching audio to buffer fail: %x", __PRETTY_FUNCTION__, alError);
294+
break;
295+
}
296+
ALOGV(" id=%u generated alGenBuffers: %u for l_pcmData: %p", selfId, _alBufferId, l_pcmData);
297+
ALOGV(" id=%u l_pcmData alBufferData: %p", selfId, l_pcmData);
298+
alBufferData(_alBufferId, _format, l_pcmData, (ALsizei)dataSize, (ALsizei)sampleRate);
299+
_state = State::READY;
300+
invokingPlayCallbacks();
301+
315302
}
316303
else
317304
{
@@ -333,6 +320,9 @@ static void setTimeout(double seconds, const std::function<void()>& cb)
333320

334321
} while (false);
335322

323+
if (l_pcmData != NULL)
324+
free(l_pcmData);
325+
336326
decoder.close();
337327

338328
//FIXME: Why to invoke play callback first? Should it be after 'load' callback?
@@ -345,7 +335,7 @@ static void setTimeout(double seconds, const std::function<void()>& cb)
345335
_state = State::FAILED;
346336
if (_alBufferId != INVALID_AL_BUFFER_ID && alIsBuffer(_alBufferId))
347337
{
348-
ALOGV("readDataTask failed, delete buffer: %u", _alBufferId);
338+
ALOGV(" id=%u readDataTask failed, delete buffer: %u", selfId, _alBufferId);
349339
alDeleteBuffers(1, &_alBufferId);
350340
_alBufferId = INVALID_AL_BUFFER_ID;
351341
}

tests/cpp-tests/Classes/NewAudioEngineTest/NewAudioEngineTest.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -852,7 +852,7 @@ bool AudioSwitchStateTest::init()
852852
AudioEngine::play2d("audio/SoundEffectsFX009/FX082.mp3");
853853
AudioEngine::play2d("audio/LuckyDay.mp3");
854854

855-
}, 0.1f, "AudioSwitchStateTest");
855+
}, 0.01f, "AudioSwitchStateTest");
856856

857857
return true;
858858
}

0 commit comments

Comments
 (0)