From c7a28a527a2f487ad5534189e17690d3231586ba Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 4 Apr 2017 10:56:25 -0700 Subject: [PATCH] cleanup Agent recording handling, make loadRecording async --- assignment-client/src/Agent.cpp | 25 ++++++++- assignment-client/src/Agent.h | 6 +- interface/src/Application.cpp | 5 ++ .../networking/src/NetworkAccessManager.cpp | 1 - .../recording/src/recording/ClipCache.cpp | 21 +++++-- libraries/recording/src/recording/ClipCache.h | 16 ++++-- .../src/RecordingScriptingInterface.cpp | 56 ++++++++++++------- .../src/RecordingScriptingInterface.h | 10 +++- libraries/shared/src/DependencyManager.h | 2 +- libraries/shared/src/SharedUtil.cpp | 2 +- 10 files changed, 107 insertions(+), 37 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index e28c04379f..561d9aa209 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -53,7 +54,8 @@ static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 10; Agent::Agent(ReceivedMessage& message) : ThreadedAssignment(message), - _receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES) { + _receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES) +{ DependencyManager::get()->setPacketSender(&_entityEditSender); ResourceManager::init(); @@ -64,12 +66,16 @@ Agent::Agent(ReceivedMessage& message) : DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(ScriptEngine::AGENT_SCRIPT); + DependencyManager::set(); + auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListenerForTypes( @@ -327,6 +333,8 @@ void Agent::executeScript() { _scriptEngine = std::unique_ptr(new ScriptEngine(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload)); _scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do + DependencyManager::get()->setScriptEngine(_scriptEngine.get()); + // setup an Avatar for the script to use auto scriptedAvatar = DependencyManager::get(); @@ -470,6 +478,8 @@ void Agent::executeScript() { Frame::clearFrameHandler(AUDIO_FRAME_TYPE); Frame::clearFrameHandler(AVATAR_FRAME_TYPE); + DependencyManager::destroy(); + setFinished(true); } @@ -753,8 +763,19 @@ void Agent::aboutToFinish() { // cleanup the AudioInjectorManager (and any still running injectors) DependencyManager::destroy(); + + // destroy all other created dependencies + DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + emit stopAvatarAudioTimer(); _avatarAudioTimerThread.quit(); diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 620ac8e047..2a156aba18 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -46,9 +46,6 @@ class Agent : public ThreadedAssignment { public: Agent(ReceivedMessage& message); - void setIsAvatar(bool isAvatar); - bool isAvatar() const { return _isAvatar; } - bool isPlayingAvatarSound() const { return _avatarSound != NULL; } bool isListeningToAudioStream() const { return _isListeningToAudioStream; } @@ -65,6 +62,9 @@ public: public slots: void run() override; void playAvatarSound(SharedSoundPointer avatarSound); + + void setIsAvatar(bool isAvatar); + bool isAvatar() const { return _isAvatar; } private slots: void requestScript(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 62917b304c..781d866c0f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -118,6 +118,7 @@ #include #include #include +#include #include #include #include @@ -464,6 +465,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(ScriptEngine::CLIENT_SCRIPT); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -5437,6 +5439,9 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri // AvatarManager has some custom types AvatarManager::registerMetaTypes(scriptEngine); + // give the script engine to the RecordingScriptingInterface for its callbacks + DependencyManager::get()->setScriptEngine(scriptEngine); + if (property(hifi::properties::TEST).isValid()) { scriptEngine->registerGlobalObject("Test", TestScriptingInterface::getInstance()); } diff --git a/libraries/networking/src/NetworkAccessManager.cpp b/libraries/networking/src/NetworkAccessManager.cpp index 78a05677fd..73096825e0 100644 --- a/libraries/networking/src/NetworkAccessManager.cpp +++ b/libraries/networking/src/NetworkAccessManager.cpp @@ -19,7 +19,6 @@ QThreadStorage networkAccessManagers; QNetworkAccessManager& NetworkAccessManager::getInstance() { if (!networkAccessManagers.hasLocalData()) { networkAccessManagers.setLocalData(new QNetworkAccessManager()); - } return *networkAccessManagers.localData(); diff --git a/libraries/recording/src/recording/ClipCache.cpp b/libraries/recording/src/recording/ClipCache.cpp index 66d5fb0d88..0665608bad 100644 --- a/libraries/recording/src/recording/ClipCache.cpp +++ b/libraries/recording/src/recording/ClipCache.cpp @@ -5,6 +5,9 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + +#include + #include "ClipCache.h" #include "impl/PointerClip.h" @@ -21,18 +24,28 @@ void NetworkClip::init(const QByteArray& clipData) { void NetworkClipLoader::downloadFinished(const QByteArray& data) { _clip->init(data); finishedLoading(true); + emit clipLoaded(); } -ClipCache& ClipCache::instance() { - static ClipCache _instance; - return _instance; +ClipCache::ClipCache(QObject* parent) : + ResourceCache(parent) +{ + } NetworkClipLoaderPointer ClipCache::getClipLoader(const QUrl& url) { - return ResourceCache::getResource(url, QUrl(), nullptr).staticCast(); + if (QThread::currentThread() != thread()) { + NetworkClipLoaderPointer result; + QMetaObject::invokeMethod(this, "getClipLoader", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(NetworkClipLoaderPointer, result), Q_ARG(const QUrl&, url)); + return result; + } + + return getResource(url).staticCast(); } QSharedPointer ClipCache::createResource(const QUrl& url, const QSharedPointer& fallback, const void* extra) { + qDebug() << "Loading recording at" << url; return QSharedPointer(new NetworkClipLoader(url), &Resource::deleter); } diff --git a/libraries/recording/src/recording/ClipCache.h b/libraries/recording/src/recording/ClipCache.h index 7df83efdbf..2c3465e725 100644 --- a/libraries/recording/src/recording/ClipCache.h +++ b/libraries/recording/src/recording/ClipCache.h @@ -30,26 +30,34 @@ private: }; class NetworkClipLoader : public Resource { + Q_OBJECT public: NetworkClipLoader(const QUrl& url); virtual void downloadFinished(const QByteArray& data) override; ClipPointer getClip() { return _clip; } bool completed() { return _failedToLoad || isLoaded(); } +signals: + void clipLoaded(); + private: const NetworkClip::Pointer _clip; }; using NetworkClipLoaderPointer = QSharedPointer; -class ClipCache : public ResourceCache { -public: - static ClipCache& instance(); - +class ClipCache : public ResourceCache, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + +public slots: NetworkClipLoaderPointer getClipLoader(const QUrl& url); protected: virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, const void* extra) override; + +private: + ClipCache(QObject* parent = nullptr); }; } diff --git a/libraries/script-engine/src/RecordingScriptingInterface.cpp b/libraries/script-engine/src/RecordingScriptingInterface.cpp index 41bb780b47..e76a3859f0 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.cpp +++ b/libraries/script-engine/src/RecordingScriptingInterface.cpp @@ -52,32 +52,48 @@ float RecordingScriptingInterface::playerLength() const { return _player->length(); } -bool RecordingScriptingInterface::loadRecording(const QString& url) { - using namespace recording; +void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue callback) { + auto clipLoader = DependencyManager::get()->getClipLoader(url); - auto loader = ClipCache::instance().getClipLoader(url); - if (!loader) { - qWarning() << "Clip failed to load from " << url; - return false; - } + // hold a strong pointer to the loading clip so that it has a chance to load + _clipLoaders.insert(clipLoader); - if (!loader->isLoaded()) { - QEventLoop loop; - QObject::connect(loader.data(), &Resource::loaded, &loop, &QEventLoop::quit); - QObject::connect(loader.data(), &Resource::failed, &loop, &QEventLoop::quit); - loop.exec(); - } + auto weakClipLoader = clipLoader.toWeakRef(); - if (!loader->isLoaded()) { - qWarning() << "Clip failed to load from " << url; - return false; - } + // when clip loaded, call the callback with the URL and success boolean + connect(clipLoader.data(), &recording::NetworkClipLoader::clipLoaded, this, [this, weakClipLoader, url, callback]() mutable { - _player->queueClip(loader->getClip()); - return true; + if (auto clipLoader = weakClipLoader.toStrongRef()) { + qCDebug(scriptengine) << "Loaded recording from" << url; + + _player->queueClip(clipLoader->getClip()); + + if (callback.isFunction()) { + QScriptValueList args { true, url }; + callback.call(_scriptEngine->globalObject(), args); + } + + // drop our strong pointer to this clip so it is cleaned up + _clipLoaders.remove(clipLoader); + } + }); + + // when clip load fails, call the callback with the URL and failure boolean + connect(clipLoader.data(), &recording::NetworkClipLoader::failed, this, [this, weakClipLoader, url, callback](QNetworkReply::NetworkError error) mutable { + qCDebug(scriptengine) << "Failed to load recording from" << url; + + if (callback.isFunction()) { + QScriptValueList args { false, url }; + callback.call(_scriptEngine->currentContext()->thisObject(), args); + } + + if (auto clipLoader = weakClipLoader.toStrongRef()) { + // drop out strong pointer to this clip so it is cleaned up + _clipLoaders.remove(clipLoader); + } + }); } - void RecordingScriptingInterface::startPlaying() { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "startPlaying", Qt::BlockingQueuedConnection); diff --git a/libraries/script-engine/src/RecordingScriptingInterface.h b/libraries/script-engine/src/RecordingScriptingInterface.h index 9661ee9d80..f6d335b17d 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.h +++ b/libraries/script-engine/src/RecordingScriptingInterface.h @@ -15,9 +15,11 @@ #include #include +#include #include #include +class QScriptEngine; class QScriptValue; class RecordingScriptingInterface : public QObject, public Dependency { @@ -26,8 +28,11 @@ class RecordingScriptingInterface : public QObject, public Dependency { public: RecordingScriptingInterface(); + void setScriptEngine(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; } + public slots: - bool loadRecording(const QString& url); + + void loadRecording(const QString& url, QScriptValue callback = QScriptValue()); void startPlaying(); void pausePlayer(); @@ -79,6 +84,9 @@ protected: Flag _useAttachments { false }; Flag _useSkeletonModel { false }; recording::ClipPointer _lastClip; + + QScriptEngine* _scriptEngine; + QSet _clipLoaders; }; #endif // hifi_RecordingScriptingInterface_h diff --git a/libraries/shared/src/DependencyManager.h b/libraries/shared/src/DependencyManager.h index 719763f706..7a453e63c3 100644 --- a/libraries/shared/src/DependencyManager.h +++ b/libraries/shared/src/DependencyManager.h @@ -21,7 +21,7 @@ #include #define SINGLETON_DEPENDENCY \ - friend class DependencyManager; + friend class ::DependencyManager; class Dependency { public: diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index a7e251f0e0..81a0eed9f3 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -769,7 +769,7 @@ bool similarStrings(const QString& stringA, const QString& stringB) { void disableQtBearerPoll() { // to work around the Qt constant wireless scanning, set the env for polling interval very high - const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit(); + const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT16_MAX).toLocal8Bit(); qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT); }