cleanup Agent recording handling, make loadRecording async

This commit is contained in:
Stephen Birarda 2017-04-04 10:56:25 -07:00
parent 6ed4295b8b
commit c7a28a527a
10 changed files with 107 additions and 37 deletions

View file

@ -33,6 +33,7 @@
#include <ScriptEngines.h>
#include <UUID.h>
#include <recording/ClipCache.h>
#include <recording/Deck.h>
#include <recording/Recorder.h>
#include <recording/Frame.h>
@ -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<EntityScriptingInterface>()->setPacketSender(&_entityEditSender);
ResourceManager::init();
@ -64,12 +66,16 @@ Agent::Agent(ReceivedMessage& message) :
DependencyManager::set<SoundCache>();
DependencyManager::set<AudioScriptingInterface>();
DependencyManager::set<AudioInjectorManager>();
DependencyManager::set<recording::Deck>();
DependencyManager::set<recording::Recorder>();
DependencyManager::set<RecordingScriptingInterface>();
DependencyManager::set<recording::ClipCache>();
DependencyManager::set<ScriptCache>();
DependencyManager::set<ScriptEngines>(ScriptEngine::AGENT_SCRIPT);
DependencyManager::set<RecordingScriptingInterface>();
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListenerForTypes(
@ -327,6 +333,8 @@ void Agent::executeScript() {
_scriptEngine = std::unique_ptr<ScriptEngine>(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<RecordingScriptingInterface>()->setScriptEngine(_scriptEngine.get());
// setup an Avatar for the script to use
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
@ -470,6 +478,8 @@ void Agent::executeScript() {
Frame::clearFrameHandler(AUDIO_FRAME_TYPE);
Frame::clearFrameHandler(AVATAR_FRAME_TYPE);
DependencyManager::destroy<RecordingScriptingInterface>();
setFinished(true);
}
@ -753,8 +763,19 @@ void Agent::aboutToFinish() {
// cleanup the AudioInjectorManager (and any still running injectors)
DependencyManager::destroy<AudioInjectorManager>();
// destroy all other created dependencies
DependencyManager::destroy<ScriptCache>();
DependencyManager::destroy<ScriptEngines>();
DependencyManager::destroy<ResourceCacheSharedItems>();
DependencyManager::destroy<SoundCache>();
DependencyManager::destroy<AudioScriptingInterface>();
DependencyManager::destroy<recording::Deck>();
DependencyManager::destroy<recording::Recorder>();
DependencyManager::destroy<recording::ClipCache>();
emit stopAvatarAudioTimer();
_avatarAudioTimerThread.quit();

View file

@ -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();

View file

@ -118,6 +118,7 @@
#include <udt/PacketHeaders.h>
#include <UserActivityLogger.h>
#include <UsersScriptingInterface.h>
#include <recording/ClipCache.h>
#include <recording/Deck.h>
#include <recording/Recorder.h>
#include <shared/StringHelpers.h>
@ -464,6 +465,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<StatTracker>();
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT);
DependencyManager::set<Preferences>();
DependencyManager::set<recording::ClipCache>();
DependencyManager::set<recording::Deck>();
DependencyManager::set<recording::Recorder>();
DependencyManager::set<AddressManager>();
@ -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<RecordingScriptingInterface>()->setScriptEngine(scriptEngine);
if (property(hifi::properties::TEST).isValid()) {
scriptEngine->registerGlobalObject("Test", TestScriptingInterface::getInstance());
}

View file

@ -19,7 +19,6 @@ QThreadStorage<QNetworkAccessManager*> networkAccessManagers;
QNetworkAccessManager& NetworkAccessManager::getInstance() {
if (!networkAccessManagers.hasLocalData()) {
networkAccessManagers.setLocalData(new QNetworkAccessManager());
}
return *networkAccessManagers.localData();

View file

@ -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 <QThread>
#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<NetworkClipLoader>();
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<NetworkClipLoader>();
}
QSharedPointer<Resource> ClipCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, const void* extra) {
qDebug() << "Loading recording at" << url;
return QSharedPointer<Resource>(new NetworkClipLoader(url), &Resource::deleter);
}

View file

@ -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<NetworkClipLoader>;
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<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, const void* extra) override;
private:
ClipCache(QObject* parent = nullptr);
};
}

View file

@ -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<recording::ClipCache>()->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);

View file

@ -15,9 +15,11 @@
#include <QtCore/QObject>
#include <DependencyManager.h>
#include <recording/ClipCache.h>
#include <recording/Forward.h>
#include <recording/Frame.h>
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<recording::NetworkClipLoaderPointer> _clipLoaders;
};
#endif // hifi_RecordingScriptingInterface_h

View file

@ -21,7 +21,7 @@
#include <typeinfo>
#define SINGLETON_DEPENDENCY \
friend class DependencyManager;
friend class ::DependencyManager;
class Dependency {
public:

View file

@ -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);
}