quanticchaos / OctoPrint-Webcamsb

9 stars 4 forks source link

Sidebar Webcam image is never unloaded even when the tab is not focused. #8

Open JohnEdwa opened 4 years ago

JohnEdwa commented 4 years ago

The default webcam image on the control tab has a script that unloads it and stops the stream when the tab has been on the background for a few seconds, and once the tab is refocused it starts again. You will notice this if you change away from the Octoprint tab for roughly 15 seconds (specifically, it uses the "Stream Timeout" setting from the webcam options) - when you switch back, the default webcam image will display "Webcam stream loading" for a split second. You can also use the element inspector/developer tools (F12) to monitor how the actual <img id="webcam_image"> changes from src="[webcamstreamurl] to src="" style="display:none;" - the src change is the important bit, as that stops the stream from being constantly loaded in the background.

This does not happen with the sidebar webcam and it keeps the stream open and "visible" all the time.

This is the main reason for the lag/desynchronization of the stream and the memory issues. The stream is being sent from the Pi at an exact FPS but the browser will inevitably take just a tiny bit longer to refresh each image, and eventually it results in the stream being multiple seconds behind what is being sent, which then uses a ton of extra memory as it needs to keep all the images between what it's displaying and what is being sent in RAM.

The solution here would be to somehow hook this additional webcam stream to that script, or creating a similar script, so that the sidebar webcam is also unloaded if it's not visible to the user.

quanticchaos commented 4 years ago

This plugin was made to be able to have focus all the time. I have zero lag problems with my setup. If your Pi can't handle the stream, you should use an external IP cam or uninstall the plugin.

JohnEdwa commented 4 years ago

It's not the Pi that cannot handle the stream, but the browser. When the tab is not active and on the foreground, both Firefox and Chrome will throttle the tab to save resources. This causes the stream to easily get out of sync as with any reasonable resolution and framerate it's multiple megabytes per second.

There is no need for the stream to run on the background, as that script and functionality are literally for when the page is not visible at all: i.e it's a tab that is not currently active on the window.

JohnEdwa commented 4 years ago

I whipped this up for myself using the Page Visibility API - it will trigger only when the tab is either minimized to the taskbar, or the tab is changed to another one in the same window, i.e when it cannot be visible to the user. It also won't stop the stream if the tab in the sidebar is collapsed, as it's only looking for the complete document visibility.

It could work as-is with the simple replacement of the delay and stream URL from the Octoprint settings, but a small visual improvement would be adding the additional "Webcam Stream Loading" div to keep the layout size the same for the split second the stream is being loaded.

// Adapted from the Mozilla Page Visibility API example at https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
(function() {
    'use strict';
        var hidden, visibilityChange; // Set the name of the hidden property and the change event for visibility
        if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support
            hidden = "hidden"; visibilityChange = "visibilitychange";
        } else if (typeof document.msHidden !== "undefined") {
            hidden = "msHidden"; visibilityChange = "msvisibilitychange";
        } else if (typeof document.webkitHidden !== "undefined") {
            hidden = "webkitHidden"; visibilityChange = "webkitvisibilitychange";
        }
        document.addEventListener(visibilityChange, handleVisibilityChange, false);

        var sidewebcamElement = document.getElementById("sidewebcam");
        function handleVisibilityChange() {
            if (document[hidden]) {
                var timer1 = setTimeout(function(){
                    if (document[hidden]) { sidewebcamElement.src = (""); }
                    }, 5000); // This should use the Octoprint webcam timeout value
            } else {
                clearTimeout(timer1);
                sidewebcamElement.src = ("/webcam/?action=stream"); // This should use the webcam stream url
            }
        }
})();