3.8.3
// rotateBufferThread is used to rotate alBufferData for _alSource when playing big audio file
void AudioPlayer::rotateBufferThread(int offsetFrame) {
char *tmpBuffer = nullptr;
AudioDecoder decoder;
long long rotateSleepTime = static_cast<long long>(QUEUEBUFFER_TIME_STEP * 1000) / 2;
do {
BREAK_IF(!decoder.open(_audioCache->_fileFullPath.c_str()));
uint32_t framesRead = 0;
const uint32_t framesToRead = _audioCache->_queBufferFrames;
const uint32_t bufferSize = framesToRead * decoder.getBytesPerFrame();
tmpBuffer = (char *)malloc(bufferSize);
memset(tmpBuffer, 0, bufferSize);
if (offsetFrame != 0) {
decoder.seek(offsetFrame);
}
ALint sourceState;
ALint bufferProcessed = 0;
bool needToExitThread = false;
while (!_isDestroyed) {
alGetSourcei(_alSource, AL_SOURCE_STATE, &sourceState);
/* On IOS, audio state will lie, when the system is not fully foreground,
* openAl will process the buffer in queue, but our condition cannot make sure that the audio
* is playing as it's too short. Interesting IOS system.
* Solution is to load buffer even if it's paused, just make sure that there's no bufferProcessed in
*/
if (sourceState == AL_PLAYING || sourceState == AL_PAUSED) {
alGetSourcei(_alSource, AL_BUFFERS_PROCESSED, &bufferProcessed);
while (bufferProcessed > 0) {
bufferProcessed--;
if (_timeDirty) {
_timeDirty = false;
offsetFrame = _currTime * decoder.getSampleRate();
decoder.seek(offsetFrame);
} else {
_currTime += QUEUEBUFFER_TIME_STEP;
if (_currTime > _audioCache->_duration) {
if (_loop) {
_currTime = 0.0f;
} else {
_currTime = _audioCache->_duration;
}
}
}
framesRead = decoder.readFixedFrames(framesToRead, tmpBuffer);
if (framesRead == 0) {
if (_loop) {
decoder.seek(0);
framesRead = decoder.readFixedFrames(framesToRead, tmpBuffer);
} else {
needToExitThread = true;
break;
}
}
/*
While the source is playing, alSourceUnqueueBuffers can be called to remove buffers which have
already played. Those buffers can then be filled with new data or discarded. New or refilled
buffers can then be attached to the playing source using alSourceQueueBuffers. As long as there is
always a new buffer to play in the queue, the source will continue to play.
*/
ALuint bid;
alSourceUnqueueBuffers(_alSource, 1, &bid);
alBufferData(bid, _audioCache->_format, tmpBuffer, framesRead * decoder.getBytesPerFrame(), decoder.getSampleRate());
alSourceQueueBuffers(_alSource, 1, &bid);
}
}
std::unique_lock<std::mutex> lk(_sleepMutex);
if (_isDestroyed || needToExitThread) {
break;
}
if (!_needWakeupRotateThread) {
_sleepCondition.wait_for(lk, std::chrono::milliseconds(rotateSleepTime));
}
_needWakeupRotateThread = false;
}
} while (false);
ALOGVV("Exit rotate buffer thread ...");
decoder.close();
free(tmpBuffer);
_isRotateThreadExited = true;
}
AudioPlayer中的rotateBufferThread线程函数崩溃,具体位置是在alBufferData函数的内部的CleanUpDeadBufferList()函数。
复现方法:准备一个10秒的音效。 快速点击按钮(尽可能快),每点击一次就播放这个10秒的音效(也可以点击一次播放2次这个音效),持续一会必崩溃。
当我在alSourceUnqueueBuffers(_alSource, 1, &bid);后 加入代码
if(!alIsBuffer(bid)){
needToExitThread = true;
break;
}
就不会崩溃。
@dumganhar @wangzhe @song2008_2001 请大佬帮我解释下