more handling of various AudioInjector states

This commit is contained in:
Stephen Birarda 2015-11-17 14:20:59 -08:00
parent 4fa934dccf
commit 7da6ec46c4
4 changed files with 58 additions and 37 deletions

View file

@ -65,8 +65,8 @@ void AudioInjector::finish() {
} }
void AudioInjector::setupInjection() { void AudioInjector::setupInjection() {
if (!_hasStarted) { if (!_hasSetup) {
_hasStarted = true; _hasSetup = true;
// check if we need to offset the sound by some number of seconds // check if we need to offset the sound by some number of seconds
if (_options.secondOffset > 0.0f) { if (_options.secondOffset > 0.0f) {
@ -79,18 +79,14 @@ void AudioInjector::setupInjection() {
} else { } else {
_currentSendOffset = 0; _currentSendOffset = 0;
} }
if (_options.localOnly) {
injectLocally();
}
} else { } else {
qCDebug(audio) << "AudioInjector::setupInjection called but already started."; qCDebug(audio) << "AudioInjector::setupInjection called but already setup.";
} }
} }
void AudioInjector::restart() { void AudioInjector::restart() {
if (thread() != QThread::currentThread()) { if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "restart"); QMetaObject::invokeMethod(this, "restart");
return; return;
} }
@ -98,23 +94,28 @@ void AudioInjector::restart() {
_currentSendOffset = 0; _currentSendOffset = 0;
// check our state to decide if we need extra handling for the restart request // check our state to decide if we need extra handling for the restart request
if (!isPlaying()) { if (_state == State::Finished) {
// we finished playing, need to reset state so we can get going again // we finished playing, need to reset state so we can get going again
_hasStarted = false; _hasSetup = false;
_shouldStop = false; _shouldStop = false;
_state = State::NotFinished; _state = State::NotFinished;
qDebug() << "Calling inject audio again to restart an injector"; qDebug() << "Emitting restartedWhileFinished to inject audio again to restart an injector";
// call inject audio to start injection over again // call inject audio to start injection over again
setupInjection(); setupInjection();
// emit our restarted signal, this allows the AudioInjectorManager to start considering us again // if we're a local injector call inject locally to start injecting again
if (_options.localOnly) {
injectLocally();
}
// emit our restarted signal, for network injectors this allows the AudioInjectorManager to start considering us again
emit restartedWhileFinished(); emit restartedWhileFinished();
} }
} }
void AudioInjector::injectLocally() { bool AudioInjector::injectLocally() {
bool success = false; bool success = false;
if (_localAudioInterface) { if (_localAudioInterface) {
if (_audioData.size() > 0) { if (_audioData.size() > 0) {
@ -145,7 +146,8 @@ void AudioInjector::injectLocally() {
// we never started so we are finished, call our stop method // we never started so we are finished, call our stop method
stop(); stop();
} }
return success;
} }
const uchar MAX_INJECTOR_VOLUME = 0xFF; const uchar MAX_INJECTOR_VOLUME = 0xFF;
@ -294,18 +296,21 @@ uint64_t AudioInjector::injectNextFrame() {
} }
void AudioInjector::stop() { void AudioInjector::stop() {
_shouldStop = true; // trigger a call on the injector's thread to change state to finished
QMetaObject::invokeMethod(this, "finish");
if (_options.localOnly) {
// we're only a local injector, so we can say we are finished right away too
finish();
}
} }
void AudioInjector::triggerDeleteAfterFinish() { void AudioInjector::triggerDeleteAfterFinish() {
auto expectedState = State::NotFinished; // make sure this fires on the AudioInjector thread
if (!_state.compare_exchange_strong(expectedState, State::NotFinishedWithPendingDelete)) { if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "triggerDeleteAfterFinish", Qt::QueuedConnection);
return;
}
if (_state == State::Finished) {
stopAndDeleteLater(); stopAndDeleteLater();
} else {
_state = State::NotFinishedWithPendingDelete;
} }
} }
@ -357,7 +362,11 @@ AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float vol
AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) { AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) {
AudioInjector* sound = playSound(buffer, options, localInterface); AudioInjector* sound = playSound(buffer, options, localInterface);
sound->_state = AudioInjector::State::NotFinishedWithPendingDelete;
if (sound) {
sound->_state = AudioInjector::State::NotFinishedWithPendingDelete;
}
return sound; return sound;
} }
@ -372,11 +381,21 @@ AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInj
// setup parameters required for injection // setup parameters required for injection
injector->setupInjection(); injector->setupInjection();
// attempt to thread the new injector if (options.localOnly) {
if (injectorManager->threadInjector(injector)) { if (injector->injectLocally()) {
return injector; // local injection succeeded, return the pointer to injector
return injector;
} else {
// unable to inject locally, return a nullptr
return nullptr;
}
} else { } else {
// we failed to thread the new injector (we are at the max number of injector threads) // attempt to thread the new injector
return nullptr; if (injectorManager->threadInjector(injector)) {
return injector;
} else {
// we failed to thread the new injector (we are at the max number of injector threads)
return nullptr;
}
} }
} }

View file

@ -80,14 +80,14 @@ signals:
private: private:
void setupInjection(); void setupInjection();
uint64_t injectNextFrame(); uint64_t injectNextFrame();
void injectLocally(); bool injectLocally();
void finish(); void finish();
QByteArray _audioData; QByteArray _audioData;
AudioInjectorOptions _options; AudioInjectorOptions _options;
std::atomic<State> _state { State::NotFinished }; std::atomic<State> _state { State::NotFinished };
bool _hasStarted = false; bool _hasSetup = false;
bool _shouldStop = false; bool _shouldStop = false;
float _loudness = 0.0f; float _loudness = 0.0f;
int _currentSendOffset = 0; int _currentSendOffset = 0;

View file

@ -116,8 +116,11 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) {
// handle a restart once the injector has finished // handle a restart once the injector has finished
connect(injector, &AudioInjector::restartedWhileFinished, this, &AudioInjectorManager::restartFinishedInjector); connect(injector, &AudioInjector::restartedWhileFinished, this, &AudioInjectorManager::restartFinishedInjector);
// store a QPointer to this injector // add the injector to the queue with a send timestamp of now
addInjectorToQueue(injector); _injectors.emplace(usecTimestampNow(), InjectorQPointer { injector });
// notify our wait condition so we can inject two frames for this injector immediately
_injectorReady.notify_one();
return true; return true;
} else { } else {
@ -130,10 +133,10 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) {
void AudioInjectorManager::restartFinishedInjector() { void AudioInjectorManager::restartFinishedInjector() {
auto injector = qobject_cast<AudioInjector*>(sender()); auto injector = qobject_cast<AudioInjector*>(sender());
addInjectorToQueue(injector);
} // guard the injectors vector with a mutex
std::unique_lock<std::mutex> lock(_injectorsMutex);
void AudioInjectorManager::addInjectorToQueue(AudioInjector* injector) {
// add the injector to the queue with a send timestamp of now // add the injector to the queue with a send timestamp of now
_injectors.emplace(usecTimestampNow(), InjectorQPointer { injector }); _injectors.emplace(usecTimestampNow(), InjectorQPointer { injector });

View file

@ -37,7 +37,6 @@ private slots:
private: private:
bool threadInjector(AudioInjector* injector); bool threadInjector(AudioInjector* injector);
void restartFinishedInjector(); void restartFinishedInjector();
void addInjectorToQueue(AudioInjector* injector);
AudioInjectorManager() {}; AudioInjectorManager() {};
AudioInjectorManager(const AudioInjectorManager&) = delete; AudioInjectorManager(const AudioInjectorManager&) = delete;