Workaround crash in OpenAL library.
Fixes #8887. See https://github.com/kcat/openal-soft/issues/486
This commit is contained in:
parent
8afc245422
commit
6ab31219ed
|
@ -34,6 +34,7 @@ namespace {
|
||||||
constexpr auto kSuppressRatioAll = 0.2;
|
constexpr auto kSuppressRatioAll = 0.2;
|
||||||
constexpr auto kSuppressRatioSong = 0.05;
|
constexpr auto kSuppressRatioSong = 0.05;
|
||||||
constexpr auto kWaveformCounterBufferSize = 256 * 1024;
|
constexpr auto kWaveformCounterBufferSize = 256 * 1024;
|
||||||
|
constexpr auto kEffectDestructionDelay = crl::time(1000);
|
||||||
|
|
||||||
QMutex AudioMutex;
|
QMutex AudioMutex;
|
||||||
ALCdevice *AudioDevice = nullptr;
|
ALCdevice *AudioDevice = nullptr;
|
||||||
|
@ -179,7 +180,7 @@ void ClosePlaybackDevice(not_null<Instance*> instance) {
|
||||||
LOG(("Audio Info: Closing audio playback device."));
|
LOG(("Audio Info: Closing audio playback device."));
|
||||||
|
|
||||||
if (Player::mixer()) {
|
if (Player::mixer()) {
|
||||||
Player::mixer()->detachTracks();
|
Player::mixer()->prepareToCloseDevice();
|
||||||
}
|
}
|
||||||
instance->detachTracks();
|
instance->detachTracks();
|
||||||
|
|
||||||
|
@ -383,9 +384,11 @@ void Mixer::Track::resetSpeedEffect() {
|
||||||
if (isStreamCreated()) {
|
if (isStreamCreated()) {
|
||||||
removeSourceSpeedEffect();
|
removeSourceSpeedEffect();
|
||||||
}
|
}
|
||||||
OpenAL::alDeleteEffects(1, &speedEffect->effect);
|
if (Player::mixer()) {
|
||||||
OpenAL::alDeleteAuxiliaryEffectSlots(1, &speedEffect->effectSlot);
|
// Don't destroy effect slot immediately.
|
||||||
OpenAL::alDeleteFilters(1, &speedEffect->filter);
|
// See https://github.com/kcat/openal-soft/issues/486
|
||||||
|
Player::mixer()->scheduleEffectDestruction(*speedEffect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
speedEffect->effect = speedEffect->effectSlot = speedEffect->filter = 0;
|
speedEffect->effect = speedEffect->effectSlot = speedEffect->filter = 0;
|
||||||
}
|
}
|
||||||
|
@ -560,6 +563,7 @@ Mixer::Track::~Track() = default;
|
||||||
|
|
||||||
Mixer::Mixer(not_null<Audio::Instance*> instance)
|
Mixer::Mixer(not_null<Audio::Instance*> instance)
|
||||||
: _instance(instance)
|
: _instance(instance)
|
||||||
|
, _effectsDestructionTimer([=] { destroyStaleEffectsSafe(); })
|
||||||
, _volumeVideo(kVolumeRound)
|
, _volumeVideo(kVolumeRound)
|
||||||
, _volumeSong(kVolumeRound)
|
, _volumeSong(kVolumeRound)
|
||||||
, _fader(new Fader(&_faderThread))
|
, _fader(new Fader(&_faderThread))
|
||||||
|
@ -622,6 +626,60 @@ void Mixer::onUpdated(const AudioMsgId &audio) {
|
||||||
Media::Player::Updated().notify(audio);
|
Media::Player::Updated().notify(audio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Thread: Any. Must be locked: AudioMutex.
|
||||||
|
void Mixer::scheduleEffectDestruction(const SpeedEffect &effect) {
|
||||||
|
_effectsForDestruction.emplace_back(
|
||||||
|
crl::now() + kEffectDestructionDelay,
|
||||||
|
effect);
|
||||||
|
scheduleEffectsDestruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thread: Any. Must be locked: AudioMutex.
|
||||||
|
void Mixer::scheduleEffectsDestruction() {
|
||||||
|
if (_effectsForDestruction.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
InvokeQueued(this, [=] {
|
||||||
|
if (!_effectsDestructionTimer.isActive()) {
|
||||||
|
_effectsDestructionTimer.callOnce(kEffectDestructionDelay + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thread: Main. Locks: AudioMutex.
|
||||||
|
void Mixer::destroyStaleEffectsSafe() {
|
||||||
|
QMutexLocker lock(&AudioMutex);
|
||||||
|
destroyStaleEffects();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thread: Main. Must be locked: AudioMutex.
|
||||||
|
void Mixer::destroyStaleEffects() {
|
||||||
|
const auto now = crl::now();
|
||||||
|
const auto checkAndDestroy = [&](
|
||||||
|
const std::pair<crl::time, SpeedEffect> &pair) {
|
||||||
|
const auto &[when, effect] = pair;
|
||||||
|
if (when && when > now) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
OpenAL::alDeleteEffects(1, &effect.effect);
|
||||||
|
OpenAL::alDeleteAuxiliaryEffectSlots(1, &effect.effectSlot);
|
||||||
|
OpenAL::alDeleteFilters(1, &effect.filter);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
_effectsForDestruction.erase(
|
||||||
|
ranges::remove_if(_effectsForDestruction, checkAndDestroy),
|
||||||
|
end(_effectsForDestruction));
|
||||||
|
scheduleEffectsDestruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thread: Main. Must be locked: AudioMutex.
|
||||||
|
void Mixer::destroyEffectsOnClose() {
|
||||||
|
for (auto &[when, effect] : _effectsForDestruction) {
|
||||||
|
when = 0;
|
||||||
|
}
|
||||||
|
destroyStaleEffects();
|
||||||
|
}
|
||||||
|
|
||||||
void Mixer::onError(const AudioMsgId &audio) {
|
void Mixer::onError(const AudioMsgId &audio) {
|
||||||
emit stoppedOnError(audio);
|
emit stoppedOnError(audio);
|
||||||
|
|
||||||
|
@ -823,6 +881,7 @@ void Mixer::forceToBufferExternal(const AudioMsgId &audioId) {
|
||||||
_loader->forceToBufferExternal(audioId);
|
_loader->forceToBufferExternal(audioId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Thread: Main. Locks: AudioMutex.
|
||||||
void Mixer::setSpeedFromExternal(const AudioMsgId &audioId, float64 speed) {
|
void Mixer::setSpeedFromExternal(const AudioMsgId &audioId, float64 speed) {
|
||||||
QMutexLocker lock(&AudioMutex);
|
QMutexLocker lock(&AudioMutex);
|
||||||
const auto track = trackForType(audioId.type());
|
const auto track = trackForType(audioId.type());
|
||||||
|
@ -1160,12 +1219,14 @@ void Mixer::setStoppedState(Track *current, State state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thread: Main. Must be locked: AudioMutex.
|
// Thread: Main. Must be locked: AudioMutex.
|
||||||
void Mixer::detachTracks() {
|
void Mixer::prepareToCloseDevice() {
|
||||||
for (auto i = 0; i != kTogetherLimit; ++i) {
|
for (auto i = 0; i != kTogetherLimit; ++i) {
|
||||||
trackForType(AudioMsgId::Type::Voice, i)->detach();
|
trackForType(AudioMsgId::Type::Voice, i)->detach();
|
||||||
trackForType(AudioMsgId::Type::Song, i)->detach();
|
trackForType(AudioMsgId::Type::Song, i)->detach();
|
||||||
}
|
}
|
||||||
_videoTrack.detach();
|
_videoTrack.detach();
|
||||||
|
|
||||||
|
destroyEffectsOnClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thread: Main. Must be locked: AudioMutex.
|
// Thread: Main. Must be locked: AudioMutex.
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/chat/attach/attach_prepare.h"
|
#include "ui/chat/attach/attach_prepare.h"
|
||||||
#include "core/file_location.h"
|
#include "core/file_location.h"
|
||||||
#include "base/bytes.h"
|
#include "base/bytes.h"
|
||||||
|
#include "base/timer.h"
|
||||||
|
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
|
|
||||||
|
@ -145,7 +146,10 @@ public:
|
||||||
// External player audio stream interface.
|
// External player audio stream interface.
|
||||||
void feedFromExternal(ExternalSoundPart &&part);
|
void feedFromExternal(ExternalSoundPart &&part);
|
||||||
void forceToBufferExternal(const AudioMsgId &audioId);
|
void forceToBufferExternal(const AudioMsgId &audioId);
|
||||||
|
|
||||||
|
// Thread: Main. Locks: AudioMutex.
|
||||||
void setSpeedFromExternal(const AudioMsgId &audioId, float64 speed);
|
void setSpeedFromExternal(const AudioMsgId &audioId, float64 speed);
|
||||||
|
|
||||||
Streaming::TimePoint getExternalSyncTimePoint(
|
Streaming::TimePoint getExternalSyncTimePoint(
|
||||||
const AudioMsgId &audio) const;
|
const AudioMsgId &audio) const;
|
||||||
crl::time getExternalCorrectedTime(
|
crl::time getExternalCorrectedTime(
|
||||||
|
@ -158,7 +162,7 @@ public:
|
||||||
TrackState currentState(AudioMsgId::Type type);
|
TrackState currentState(AudioMsgId::Type type);
|
||||||
|
|
||||||
// Thread: Main. Must be locked: AudioMutex.
|
// Thread: Main. Must be locked: AudioMutex.
|
||||||
void detachTracks();
|
void prepareToCloseDevice();
|
||||||
|
|
||||||
// Thread: Main. Must be locked: AudioMutex.
|
// Thread: Main. Must be locked: AudioMutex.
|
||||||
void reattachIfNeeded();
|
void reattachIfNeeded();
|
||||||
|
@ -193,11 +197,13 @@ signals:
|
||||||
void suppressAll(qint64 duration);
|
void suppressAll(qint64 duration);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool fadedStop(AudioMsgId::Type type, bool *fadedStart = 0);
|
struct SpeedEffect {
|
||||||
void resetFadeStartPosition(AudioMsgId::Type type, int positionInBuffered = -1);
|
uint32 effect = 0;
|
||||||
bool checkCurrentALError(AudioMsgId::Type type);
|
uint32 effectSlot = 0;
|
||||||
|
uint32 filter = 0;
|
||||||
void externalSoundProgress(const AudioMsgId &audio);
|
int coarseTune = 0;
|
||||||
|
float64 speed = 1.;
|
||||||
|
};
|
||||||
|
|
||||||
class Track {
|
class Track {
|
||||||
public:
|
public:
|
||||||
|
@ -206,8 +212,10 @@ private:
|
||||||
// Thread: Any. Must be locked: AudioMutex.
|
// Thread: Any. Must be locked: AudioMutex.
|
||||||
void reattach(AudioMsgId::Type type);
|
void reattach(AudioMsgId::Type type);
|
||||||
|
|
||||||
|
// Thread: Main. Must be locked: AudioMutex.
|
||||||
void detach();
|
void detach();
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
void started();
|
void started();
|
||||||
|
|
||||||
bool isStreamCreated() const;
|
bool isStreamCreated() const;
|
||||||
|
@ -215,6 +223,7 @@ private:
|
||||||
|
|
||||||
int getNotQueuedBufferIndex();
|
int getNotQueuedBufferIndex();
|
||||||
|
|
||||||
|
// Thread: Main. Must be locked: AudioMutex.
|
||||||
void setExternalData(std::unique_ptr<ExternalSoundData> data);
|
void setExternalData(std::unique_ptr<ExternalSoundData> data);
|
||||||
void changeSpeedEffect(float64 speed);
|
void changeSpeedEffect(float64 speed);
|
||||||
|
|
||||||
|
@ -242,13 +251,6 @@ private:
|
||||||
Stream stream;
|
Stream stream;
|
||||||
std::unique_ptr<ExternalSoundData> externalData;
|
std::unique_ptr<ExternalSoundData> externalData;
|
||||||
|
|
||||||
struct SpeedEffect {
|
|
||||||
uint32 effect = 0;
|
|
||||||
uint32 effectSlot = 0;
|
|
||||||
uint32 filter = 0;
|
|
||||||
int coarseTune = 0;
|
|
||||||
float64 speed = 1.;
|
|
||||||
};
|
|
||||||
std::unique_ptr<SpeedEffect> speedEffect;
|
std::unique_ptr<SpeedEffect> speedEffect;
|
||||||
crl::time lastUpdateWhen = 0;
|
crl::time lastUpdateWhen = 0;
|
||||||
crl::time lastUpdatePosition = 0;
|
crl::time lastUpdatePosition = 0;
|
||||||
|
@ -263,6 +265,12 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool fadedStop(AudioMsgId::Type type, bool *fadedStart = 0);
|
||||||
|
void resetFadeStartPosition(AudioMsgId::Type type, int positionInBuffered = -1);
|
||||||
|
bool checkCurrentALError(AudioMsgId::Type type);
|
||||||
|
|
||||||
|
void externalSoundProgress(const AudioMsgId &audio);
|
||||||
|
|
||||||
// Thread: Any. Must be locked: AudioMutex.
|
// Thread: Any. Must be locked: AudioMutex.
|
||||||
void setStoppedState(Track *current, State state = State::Stopped);
|
void setStoppedState(Track *current, State state = State::Stopped);
|
||||||
|
|
||||||
|
@ -271,7 +279,18 @@ private:
|
||||||
int *currentIndex(AudioMsgId::Type type);
|
int *currentIndex(AudioMsgId::Type type);
|
||||||
const int *currentIndex(AudioMsgId::Type type) const;
|
const int *currentIndex(AudioMsgId::Type type) const;
|
||||||
|
|
||||||
not_null<Audio::Instance*> _instance;
|
// Thread: Any. Must be locked: AudioMutex.
|
||||||
|
void scheduleEffectDestruction(const SpeedEffect &effect);
|
||||||
|
void scheduleEffectsDestruction();
|
||||||
|
|
||||||
|
// Thread: Main. Must be locked: AudioMutex.
|
||||||
|
void destroyStaleEffects();
|
||||||
|
void destroyEffectsOnClose();
|
||||||
|
|
||||||
|
// Thread: Main. Locks: AudioMutex.
|
||||||
|
void destroyStaleEffectsSafe();
|
||||||
|
|
||||||
|
const not_null<Audio::Instance*> _instance;
|
||||||
|
|
||||||
int _audioCurrent = 0;
|
int _audioCurrent = 0;
|
||||||
Track _audioTracks[kTogetherLimit];
|
Track _audioTracks[kTogetherLimit];
|
||||||
|
@ -281,6 +300,9 @@ private:
|
||||||
|
|
||||||
Track _videoTrack;
|
Track _videoTrack;
|
||||||
|
|
||||||
|
std::vector<std::pair<crl::time, SpeedEffect>> _effectsForDestruction;
|
||||||
|
base::Timer _effectsDestructionTimer;
|
||||||
|
|
||||||
QAtomicInt _volumeVideo;
|
QAtomicInt _volumeVideo;
|
||||||
QAtomicInt _volumeSong;
|
QAtomicInt _volumeSong;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue