From fd3d19ca03bf4cede504af55fa26be511f3a3c59 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 10 Jul 2016 21:18:24 -0700 Subject: [PATCH 1/6] Fix adding a PC that has already been paired --- static/js/index.js | 84 ++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/static/js/index.js b/static/js/index.js index 4eae8b5..bfefae4 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -106,47 +106,49 @@ function pairTo(host, onSuccess, onFailure) { onFailure(); } - if(!api) { - api = new NvHTTP(host, myUniqueid); - } - - if(api.paired) { - onSuccess(); - } - - var randomNumber = String("0000" + (Math.random()*10000|0)).slice(-4); - var pairingDialog = document.querySelector('#pairingDialog'); - $('#pairingDialogText').html('Please enter the number ' + randomNumber + ' on the GFE dialog on the computer. This dialog will be dismissed once complete'); - pairingDialog.showModal(); - console.log('sending pairing request to ' + host + ' with random number ' + randomNumber); - - api.pair(randomNumber).then(function (paired) { - if (!paired) { - if (api.currentGame != 0) { - $('#pairingDialogText').html('Error: ' + host + ' is in app. Cannot pair until the app is stopped.'); - } else { - $('#pairingDialogText').html('Error: failed to pair with ' + host + '. failure reason unknown.'); - } - onFailure(); + api = new NvHTTP(host, myUniqueid); + api.refreshServerInfo().then(function (ret) { + if(api.paired) { + onSuccess(); } - snackbarLog('Pairing successful'); - pairingDialog.close(); + var randomNumber = String("0000" + (Math.random()*10000|0)).slice(-4); + var pairingDialog = document.querySelector('#pairingDialog'); + $('#pairingDialogText').html('Please enter the number ' + randomNumber + ' on the GFE dialog on the computer. This dialog will be dismissed once complete'); + pairingDialog.showModal(); + console.log('sending pairing request to ' + host + ' with random number ' + randomNumber); - var cell = document.createElement('div'); - cell.className += 'mdl-cell mdl-cell--3-col'; - cell.id = 'hostgrid-' + hosts[i]; - cell.innerHTML = hosts[i]; - $('#host-grid').append(cell); - cell.onclick = hostChosen; + api.pair(randomNumber).then(function (paired) { + if (!paired) { + if (api.currentGame != 0) { + $('#pairingDialogText').html('Error: ' + host + ' is in app. Cannot pair until the app is stopped.'); + } else { + $('#pairingDialogText').html('Error: failed to pair with ' + host + '. failure reason unknown.'); + } + onFailure(); + } - saveHosts(); - onSuccess(); + snackbarLog('Pairing successful'); + pairingDialog.close(); - }, function (failedPairing) { - snackbarLog('Failed pairing to: ' + host); - console.log('pairing failed, and returned ' + failedPairing); - onFailure(); + var cell = document.createElement('div'); + cell.className += 'mdl-cell mdl-cell--3-col'; + cell.id = 'hostgrid-' + host; + cell.innerHTML = host; + $('#host-grid').append(cell); + cell.onclick = hostChosen; + + saveHosts(); + onSuccess(); + + }, function (failedPairing) { + snackbarLog('Failed pairing to: ' + host); + console.log('pairing failed, and returned ' + failedPairing); + onFailure(); + }); + }, function (failedRefreshInfo) { + snackbarLog('Failed to connect to ' + host + '! Are you sure the host is on?'); + console.log('Returned error was: ' + failedRefreshInfo); }); } @@ -157,11 +159,7 @@ function hostChosen(sourceEvent) { host = sourceEvent.srcElement.innerText; } - - if(!api || api.address != host) { - api = new NvHTTP(host, myUniqueid); - } - + api = new NvHTTP(host, myUniqueid); api.refreshServerInfo().then(function (ret) { if(!api.paired) { pairTo(host); @@ -169,8 +167,8 @@ function hostChosen(sourceEvent) { if(hosts.indexOf(host) < 0) { // we don't have this host in our list. add it, and save it. var cell = document.createElement('div'); cell.className += 'mdl-cell mdl-cell--3-col'; - cell.id = 'hostgrid-' + hosts[i]; - cell.innerHTML = hosts[i]; + cell.id = 'hostgrid-' + host; + cell.innerHTML = host; $('#host-grid').append(cell); cell.onclick = hostChosen; } From 3767990bb0214888889c2020ea238751bd89a5c9 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 10 Jul 2016 21:31:35 -0700 Subject: [PATCH 2/6] Fix some app list refreshing bugs --- static/js/index.js | 10 +++------- static/js/messages.js | 2 +- static/js/utils.js | 11 ----------- 3 files changed, 4 insertions(+), 19 deletions(-) diff --git a/static/js/index.js b/static/js/index.js index bfefae4..0f6323a 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -222,14 +222,10 @@ function showApps() { return; } + // if game grid is populated, empty it + $("#game-grid").empty(); + api.getAppList().then(function (appList) { - - // if game grid is populated, empty it - if($("#game-grid").children().length > 0) { - $("#game-grid").empty(); - } - - appList.forEach(function (app) { api.getBoxArt(app.id).then(function (resolvedPromise) { var imageBlob = new Blob([resolvedPromise], {type: "image/png"}); diff --git a/static/js/messages.js b/static/js/messages.js index 4586396..cc223c8 100644 --- a/static/js/messages.js +++ b/static/js/messages.js @@ -23,7 +23,7 @@ function handleMessage(msg) { if(msg.data === 'streamTerminated') { // if it's a recognized event, notify the appropriate function $('#loadingSpinner').css('display', 'none'); // This is a fallback for RTSP handshake failing, which immediately terminates the stream. api.refreshServerInfo().then(function (ret) { - showAppsMode(); + showApps(); chrome.app.window.current().restore(); }); } else if(msg.data === 'Connection Established') { diff --git a/static/js/utils.js b/static/js/utils.js index f480c14..27e430a 100644 --- a/static/js/utils.js +++ b/static/js/utils.js @@ -40,7 +40,6 @@ function NvHTTP(address, clientUid) { this.clientUid = clientUid; this._baseUrlHttps = 'https://' + address + ':47984'; this._baseUrlHttp = 'http://' + address + ':47989'; - this._appListCache = null; this._memCachedBoxArtArray = {}; _self = this; }; @@ -133,13 +132,6 @@ NvHTTP.prototype = { }, getAppList: function () { - if (_self._appListCache) { - console.log('Returning app list from cache'); - return new Promise(function (resolve, reject) { - resolve(_self._appListCache); - }); - } - return sendMessage('openUrl', [_self._baseUrlHttps + '/applist?' + _self._buildUidStr(), false]).then(function (ret) { $xml = _self._parseXML(ret); @@ -155,9 +147,6 @@ NvHTTP.prototype = { }); } - if (appList) - _self._appListCache = appList; - return appList; }); }, From 237fcdeef8092b57c75dceac80810583f24a3282 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 10 Jul 2016 21:47:28 -0700 Subject: [PATCH 3/6] Fix hosts not persisting in the grid and duplicating when returning to the PC screen --- static/js/index.js | 53 +++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/static/js/index.js b/static/js/index.js index 0f6323a..db2e5c4 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -130,15 +130,6 @@ function pairTo(host, onSuccess, onFailure) { snackbarLog('Pairing successful'); pairingDialog.close(); - - var cell = document.createElement('div'); - cell.className += 'mdl-cell mdl-cell--3-col'; - cell.id = 'hostgrid-' + host; - cell.innerHTML = host; - $('#host-grid').append(cell); - cell.onclick = hostChosen; - - saveHosts(); onSuccess(); }, function (failedPairing) { @@ -162,17 +153,10 @@ function hostChosen(sourceEvent) { api = new NvHTTP(host, myUniqueid); api.refreshServerInfo().then(function (ret) { if(!api.paired) { - pairTo(host); + pairTo(host, function(){ showApps(); }, function(){}); + } else { + showApps(); } - if(hosts.indexOf(host) < 0) { // we don't have this host in our list. add it, and save it. - var cell = document.createElement('div'); - cell.className += 'mdl-cell mdl-cell--3-col'; - cell.id = 'hostgrid-' + host; - cell.innerHTML = host; - $('#host-grid').append(cell); - cell.onclick = hostChosen; - } - showApps(); }, function (failedRefreshInfo) { snackbarLog('Failed to connect to ' + host + '! Are you sure the host is on?'); console.log('Returned error was: ' + failedRefreshInfo); @@ -190,14 +174,30 @@ function cancelAddHost() { document.querySelector('#addHostDialog').close(); } +function addHostToGrid(host) { + if(hosts.indexOf(host) < 0) { // we don't have this host in our list. add it, and save it. + var cell = document.createElement('div'); + cell.className += 'mdl-cell mdl-cell--3-col'; + cell.id = 'hostgrid-' + host; + cell.innerHTML = host; + $('#host-grid').append(cell); + cell.onclick = hostChosen; + hosts.push(host); + saveHosts(); + } +} + function continueAddHost() { var inputHost = $('#dialogInputHost').val(); pairTo(inputHost, - function() { document.querySelector('#addHostDialog').close() }, - function() {snackbarLog('pairing to ' + inputHost + ' failed!');} - ); - + function() { + addHostToGrid(inputHost); + document.querySelector('#addHostDialog').close(); + }, + function() { + snackbarLog('pairing to ' + inputHost + ' failed!'); + }); } // locally remove the hostname/ip from the saved `hosts` array. @@ -535,12 +535,7 @@ function onWindowLoad(){ var ips = Object.keys(finder.byService_['_nvstream._tcp']); for (var ip in ips) { if (finder.byService_['_nvstream._tcp'][ip]) { - var cell = document.createElement('div'); - cell.className += 'mdl-cell mdl-cell--3-col'; - cell.id = 'hostgrid-' + ip; - cell.innerHTML = ip; - $('#host-grid').append(cell); - cell.onclick = hostChosen; + addHostToGrid(ip); } } } From c2fde815face6aa64e6ed23e1eab61796e80c6b0 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 10 Jul 2016 21:51:29 -0700 Subject: [PATCH 4/6] Return after invoking a completion handler --- static/js/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/static/js/index.js b/static/js/index.js index db2e5c4..348e728 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -104,12 +104,14 @@ function pairTo(host, onSuccess, onFailure) { snackbarLog('ERROR: cert has not been generated yet. Is NaCl initialized?'); console.log("User wants to pair, and we still have no cert. Problem = very yes."); onFailure(); + return; } api = new NvHTTP(host, myUniqueid); api.refreshServerInfo().then(function (ret) { - if(api.paired) { + if (api.paired) { onSuccess(); + return; } var randomNumber = String("0000" + (Math.random()*10000|0)).slice(-4); @@ -126,12 +128,12 @@ function pairTo(host, onSuccess, onFailure) { $('#pairingDialogText').html('Error: failed to pair with ' + host + '. failure reason unknown.'); } onFailure(); + return; } snackbarLog('Pairing successful'); pairingDialog.close(); onSuccess(); - }, function (failedPairing) { snackbarLog('Failed pairing to: ' + host); console.log('pairing failed, and returned ' + failedPairing); From 12e34a9f4e7b8211dc4e9cabb061c370dd2bb7f4 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 10 Jul 2016 22:23:01 -0700 Subject: [PATCH 5/6] Add the code back to request an IDR frame when switching back to Moonlight --- input.cpp | 5 +++++ moonlight.hpp | 3 +++ viddec.cpp | 14 ++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/input.cpp b/input.cpp index 8d72455..2fc0b91 100644 --- a/input.cpp +++ b/input.cpp @@ -23,6 +23,11 @@ static int ConvertPPButtonToLiButton(PP_InputEvent_MouseButton ppButton) { void MoonlightInstance::DidLockMouse(int32_t result) { m_MouseLocked = (result == PP_OK); + if (m_MouseLocked) { + // Request an IDR frame to dump the frame queue that may have + // built up from the GL pipeline being stalled. + g_Instance->m_RequestIdrFrame = true; + } } void MoonlightInstance::MouseLockLost() { diff --git a/moonlight.hpp b/moonlight.hpp index 1810764..fd55ada 100644 --- a/moonlight.hpp +++ b/moonlight.hpp @@ -53,6 +53,7 @@ class MoonlightInstance : public pp::Instance, public pp::MouseLock { pp::MouseLock(this), m_HasNextPicture(false), m_IsPainting(false), + m_RequestIdrFrame(false), m_OpusDecoder(NULL), m_CallbackFactory(this), m_MouseLocked(false), @@ -122,6 +123,7 @@ class MoonlightInstance : public pp::Instance, public pp::MouseLock { void PictureReady(int32_t result, PP_VideoPicture picture); void PaintPicture(void); void InitializeRenderingSurface(int width, int height); + void DidChangeFocus(bool got_focus); static void VidDecSetup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags); static void VidDecCleanup(void); @@ -159,6 +161,7 @@ class MoonlightInstance : public pp::Instance, public pp::MouseLock { bool m_HasNextPicture; PP_VideoPicture m_CurrentPicture; bool m_IsPainting; + bool m_RequestIdrFrame; OpusMSDecoder* m_OpusDecoder; pp::Audio m_AudioPlayer; diff --git a/viddec.cpp b/viddec.cpp index 033fa4c..adebfb4 100644 --- a/viddec.cpp +++ b/viddec.cpp @@ -62,6 +62,14 @@ static const char k_FragmentShaderExternal[] = " gl_FragColor = texture2D(s_texture, v_texCoord); \n" "}"; +void MoonlightInstance::DidChangeFocus(bool got_focus) { + // Request an IDR frame to dump the frame queue that may have + // built up from the GL pipeline being stalled. + if (got_focus) { + g_Instance->m_RequestIdrFrame = true; + } +} + void MoonlightInstance::InitializeRenderingSurface(int width, int height) { if (!glInitializePPAPI(pp::Module::Get()->get_browser_interface())) { return; @@ -230,6 +238,12 @@ int MoonlightInstance::VidDecSubmitDecodeUnit(PDECODE_UNIT decodeUnit) { bool isSps = false; bool isPps = false; bool isIframe = false; + + // Request an IDR frame if needed + if (g_Instance->m_RequestIdrFrame) { + g_Instance->m_RequestIdrFrame = false; + return DR_NEED_IDR; + } // Look at the NALU type if (decodeUnit->bufferList->length > 5) { From f88f55e7c9c6b3df819ddf5f265dce45d4f7d267 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 10 Jul 2016 22:40:41 -0700 Subject: [PATCH 6/6] Launch the request game after quitting existing app. Fixes #72 --- static/js/index.js | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/static/js/index.js b/static/js/index.js index 348e728..0c9e117 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -3,6 +3,7 @@ var hosts = []; var pairingCert; var myUniqueid; var api; +var relaunchSourceEvent; // Called by the common.js module. function attachListeners() { @@ -306,6 +307,10 @@ function startGame(sourceEvent) { api.refreshServerInfo().then(function (ret) { if(api.currentGame != 0 && api.currentGame != appID) { api.getAppById(api.currentGame).then(function (currentApp) { + // This event gets saved and passed back to this callback + // after the game is quit + relaunchSourceEvent = sourceEvent; + var quitAppDialog = document.querySelector('#quitAppDialog'); document.getElementById('quitAppDialogText').innerHTML = currentApp.title + ' is already running. Would you like to quit ' + @@ -333,7 +338,7 @@ function startGame(sourceEvent) { $('#loadingMessage').text('Starting ' + appName + '...'); playGameMode(); - if(api.currentGame == appID) // if user wants to launch the already-running app, then we resume it. + if(api.currentGame == appID) { // if user wants to launch the already-running app, then we resume it. return api.resumeApp(rikey, rikeyid).then(function (ret) { sendMessage('startRequest', [host, streamWidth, streamHeight, frameRate, bitrate.toString(), api.serverMajorVersion.toString(), rikey, rikeyid.toString()]); @@ -342,6 +347,7 @@ function startGame(sourceEvent) { console.log('Returned error was: ' + failedResumeApp); return; }); + } api.launchApp(appID, streamWidth + "x" + streamHeight + "x" + frameRate, @@ -361,6 +367,7 @@ function startGame(sourceEvent) { } function cancelQuitApp() { + relaunchSourceEvent = null; document.querySelector('#quitAppDialog').close(); console.log('closing app dialog, and returning'); } @@ -368,7 +375,18 @@ function cancelQuitApp() { function continueQuitApp(sourceEvent) { // I want the sourceEvent's sourceEvent console.log('stopping game, and closing app dialog, and returning'); - stopGame(); + stopGame( + function() { + if (relaunchSourceEvent != null) { + // Save and null relaunchSourceEvent just in case startGame() + // wants to set it again. + var event = relaunchSourceEvent; + relaunchSourceEvent = null; + + startGame(event); + } + } + ); document.querySelector('#quitAppDialog').close(); } @@ -404,7 +422,6 @@ function fullscreenNaclModule() { } function stopGame(callbackFunction) { - api.refreshServerInfo().then(function (ret) { api.getAppById(api.currentGame).then(function (runningApp) { if (!runningApp) {