Clean update bug 8027

This commit is contained in:
luiscuenca 2017-10-27 11:03:02 -07:00
parent f21a284324
commit f32282afdd
8 changed files with 153 additions and 6 deletions

View file

@ -33,6 +33,54 @@ var EventBridge;
// replace the TempEventBridge with the real one.
var tempEventBridge = EventBridge;
EventBridge = channel.objects.eventBridge;
EventBridge.audioOutputDeviceChanged.connect(function(deviceName) {
navigator.mediaDevices.getUserMedia({ audio: true, video: true }).then(function(mediaStream) {
navigator.mediaDevices.enumerateDevices().then(function(devices) {
devices.forEach(function(device) {
if (device.kind == "audiooutput") {
if (device.label == deviceName){
console.log("Changing HTML audio output to device " + device.label);
var deviceId = device.deviceId;
var videos = document.getElementsByTagName("video");
for (var i = 0; i < videos.length; i++){
videos[i].setSinkId(deviceId);
}
var audios = document.getElementsByTagName("audio");
for (var i = 0; i < audios.length; i++){
audios[i].setSinkId(deviceId);
}
}
}
});
}).catch(function(err) {
console.log("Error getting media devices"+ err.name + ": " + err.message);
});
}).catch(function(err) {
console.log("Error getting user media"+ err.name + ": " + err.message);
});
});
// To be able to update the state of the output device selection for every element added to the DOM
// we need to listen to events that might precede the addition of this elements.
// A more robust hack will be to add a setInterval that look for DOM changes every 100-300 ms (low performance?)
window.onload = function(){
setTimeout(function() {
EventBridge.forceHtmlAudioOutputDeviceUpdate();
}, 1200);
};
document.onclick = function(){
setTimeout(function() {
EventBridge.forceHtmlAudioOutputDeviceUpdate();
}, 1200);
};
document.onchange = function(){
setTimeout(function() {
EventBridge.forceHtmlAudioOutputDeviceUpdate();
}, 1200);
};
tempEventBridge._callbacks.forEach(function (callback) {
EventBridge.scriptEventReceived.connect(callback);
});

View file

@ -212,7 +212,7 @@ ScrollingWindow {
WebEngineScript {
id: createGlobalEventBridge
sourceCode: eventBridgeJavaScriptToInject
injectionPoint: WebEngineScript.DocumentCreation
injectionPoint: WebEngineScript.Deferred
worldId: WebEngineScript.MainWorld
}
@ -233,9 +233,13 @@ ScrollingWindow {
anchors.right: parent.right
onFeaturePermissionRequested: {
permissionsBar.securityOrigin = securityOrigin;
permissionsBar.feature = feature;
root.showPermissionsBar();
if (feature == 2) { // QWebEnginePage::MediaAudioCapture
grantFeaturePermission(securityOrigin, feature, true);
} else {
permissionsBar.securityOrigin = securityOrigin;
permissionsBar.feature = feature;
root.showPermissionsBar();
}
}
onLoadingChanged: {

View file

@ -1,6 +1,6 @@
set(TARGET_NAME ui)
setup_hifi_library(OpenGL Network Qml Quick Script WebChannel WebEngine WebSockets XmlPatterns)
link_hifi_libraries(shared networking gl audio)
setup_hifi_library(OpenGL Multimedia Network Qml Quick Script WebChannel WebEngine WebSockets XmlPatterns)
link_hifi_libraries(shared networking gl audio audio-client plugins)
# Required for some low level GL interaction in the OffscreenQMLSurface
target_opengl()

View file

@ -24,7 +24,11 @@
#include <QtCore/QThread>
#include <QtCore/QMutex>
#include <QtCore/QWaitCondition>
#include <QtMultimedia/qmediaservice.h>
#include <QtMultimedia/qaudiooutputselectorcontrol.h>
#include <QtMultimedia/qmediaplayer.h>
#include <AudioClient.h>
#include <shared/NsightHelpers.h>
#include <shared/GlobalAppProperties.h>
#include <shared/QtHelpers.h>
@ -595,6 +599,14 @@ void OffscreenQmlSurface::create() {
// Find a way to flag older scripts using this mechanism and wanr that this is deprecated
_qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(this, _qmlContext));
_renderControl->initialize(_canvas->getContext());
// Connect with the audio client and listen for audio device changes
auto audioIO = DependencyManager::get<AudioClient>();
connect(audioIO.data(), &AudioClient::deviceChanged, this, [&](QAudio::Mode mode, const QAudioDeviceInfo& device) {
if (mode == QAudio::Mode::AudioOutput) {
QMetaObject::invokeMethod(this, "changeAudioOutputDevice", Qt::DirectConnection, Q_ARG(QString, device.deviceName()));
}
});
// When Quick says there is a need to render, we will not render immediately. Instead,
// a timer with a small interval is used to get better performance.
@ -605,6 +617,68 @@ void OffscreenQmlSurface::create() {
_updateTimer.start();
}
void OffscreenQmlSurface::changeAudioOutputDevice(const QString& deviceName, bool isHtmlUpdate) {
if (_rootItem != nullptr && !isHtmlUpdate) {
QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::DirectConnection);
}
emit audioOutputDeviceChanged(deviceName);
}
void OffscreenQmlSurface::forceHtmlAudioOutputDeviceUpdate() {
auto audioIO = DependencyManager::get<AudioClient>();
QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName();
QMetaObject::invokeMethod(this, "changeAudioOutputDevice", Qt::DirectConnection,
Q_ARG(QString, deviceName), Q_ARG(bool, true));
}
void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() {
if (QThread::currentThread() != qApp->thread()) {
QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection);
}
else {
int waitForAudioQmlMs = 500;
QTimer::singleShot(waitForAudioQmlMs, this, SLOT(updateQmlAudio()));
}
}
void OffscreenQmlSurface::updateQmlAudio() {
auto audioIO = DependencyManager::get<AudioClient>();
QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName();
for (auto player : _rootItem->findChildren<QMediaPlayer*>()) {
auto mediaState = player->state();
QMediaService *svc = player->service();
if (nullptr == svc) {
return;
}
QAudioOutputSelectorControl *out = qobject_cast<QAudioOutputSelectorControl *>
(svc->requestControl(QAudioOutputSelectorControl_iid));
if (nullptr == out) {
return;
}
QString deviceOuput;
auto outputs = out->availableOutputs();
for (int i = 0; i < outputs.size(); i++) {
QString output = outputs[i];
QString description = out->outputDescription(output);
if (description == deviceName) {
deviceOuput = output;
break;
}
}
out->setActiveOutput(deviceOuput);
svc->releaseControl(out);
// if multimedia was paused, it will start playing automatically after changing audio device
// this will reset it back to a paused state
if (mediaState == QMediaPlayer::State::PausedState) {
player->pause();
}
else if (mediaState == QMediaPlayer::State::StoppedState) {
player->stop();
}
}
qDebug() << "QML Audio changed to " << deviceName;
}
static uvec2 clampSize(const uvec2& size, uint32_t maxDimension) {
return glm::clamp(size, glm::uvec2(1), glm::uvec2(maxDimension));
}
@ -798,6 +872,7 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext
if (newItem) {
newItem->setParentItem(_rootItem);
}
QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection);
return;
}
@ -817,6 +892,7 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext
for (const auto& callback : callbacks) {
callback(qmlContext, newObject);
}
QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection);
}
void OffscreenQmlSurface::updateQuick() {

View file

@ -103,6 +103,15 @@ public slots:
void onAboutToQuit();
void focusDestroyed(QObject *obj);
// audio output device
public slots:
void changeAudioOutputDevice(const QString& deviceName, bool isHtmlUpdate = false);
void forceHtmlAudioOutputDeviceUpdate();
void forceQmlAudioOutputDeviceUpdate();
void updateQmlAudio();
signals:
void audioOutputDeviceChanged(const QString& deviceName);
// event bridge
public slots:
void emitScriptEvent(const QVariant& scriptMessage);

View file

@ -15,4 +15,7 @@ if (NOT ANDROID)
setup_hifi_plugin(Script Qml Widgets)
link_hifi_libraries(shared controllers ui plugins ui-plugins input-plugins)
target_sixense()
if (WIN32)
target_link_libraries(${TARGET_NAME} Winmm.lib)
endif()
endif ()

View file

@ -17,5 +17,8 @@ if (WIN32)
target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES})
endif()
if (WIN32)
target_link_libraries(${TARGET_NAME} Winmm.lib)
endif()
package_libraries_for_deployment()

View file

@ -14,6 +14,10 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
# link in the shared libraries
link_hifi_libraries(shared networking model fbx ktx image octree gl gpu gpu-gl render model-networking networking render-utils entities entities-renderer animation audio avatars script-engine physics procedural midi ui)
if (WIN32)
target_link_libraries(${TARGET_NAME} Winmm.lib)
endif()
package_libraries_for_deployment()