How to detect whether HTML5 video has paused for buffering?

后端 未结 5 1769
长情又很酷
长情又很酷 2021-01-30 06:45

I\'m trying to test whether a video is choppy. I have noticed that the pause event is not triggered when the video pauses for buffering. What is the best way to det

相关标签:
5条回答
  • 2021-01-30 07:21

    As per MDN docs 'waiting' event is -

    Sent when the requested operation (such as playback) is delayed pending the completion of another operation (such as a seek).

    So seek or network request will trigger 'waiting'. Michael in the comments did point out that 'waiting' is reliable as of 2019 so I gave this a try and it worked!

    let slowInternetTimeout = null;
    
    let threshold = 3000; //ms after which user perceives buffering
    
    video.addEventListener('waiting', () => {
        slowInternetTimeout = setTimeout(() => {
            //show buffering
        }, threshold);
    });
    video.addEventListener('playing', () => {
        if(slowInternetTimeout != null){
            clearTimeout(slowInternetTimeout);
            slowInternetTimeout = null;
        }
    });
    
    0 讨论(0)
  • 2021-01-30 07:23

    The event you're looking for is waiting.

    From the spec:

    A waiting DOM event can be fired as a result of an element that is potentially playing stopping playback due to its readyState attribute changing to a value lower than HAVE_FUTURE_DATA.

    The paused state does not change because the video is still "potentially playing" (i.e. "trying" to play). So the waiting event fires. When enough data has been loaded, playing fires.

    You can also check the state at any time by looking at two properties, networkState and readyState

    if (video.networkState === video.NETWORK_LOADING) {
        // The user agent is actively trying to download data.
    }
    
    if (video.readyState < video.HAVE_FUTURE_DATA) {
        // There is not enough data to keep playing from this point
    }
    
    0 讨论(0)
  • 2021-01-30 07:25

    You can just check the buffered video content length and if it is less than the current playing part then just fire the pause event.Using following code you can check the buffered video length.

    $vid = $("#video_id");
    
    $vid.on('progress', function(e) {
    
        percentVidLoaded = null;
        // FF4+, Chrome
        if ($vid[0] && $vid[0].buffered && $vid[0].buffered.length > 0 && $vid[0].buffered.end && $vid[0].duration) {
            percentVidLoaded = $vid[0].buffered.end(0) / $vid[0].duration;
        }
        /* Some browsers (e.g., FF3.6 and Safari 5) cannot calculate target.bufferered.end()
         *  to be anything other than 0. If the byte count is available we use this instead.
         *  Browsers that support the else if do not seem to have the bufferedBytes value and
         *  should skip to there.
         */
        else if ($vid[0] && $vid[0].bytesTotal != undefined && $vid[0].bytesTotal > 0 && $vid[0].bufferedBytes != undefined) {
            percentVidLoaded = $vid[0].bufferedBytes / $vid[0].bytesTotal;
        }
        if (percentVidLoaded !== null) {
            percentVidLoaded = 100 * Math.min(1, Math.max(0, percentVidLoaded));
        }
    });
    
    0 讨论(0)
  • 2021-01-30 07:29

    You need to check if the buffer is less than the current video time. If so, then the video is buffering. However, you should check this with a small tolerance to make sure you detect it before it is acuatally necessary to buffer.

    Example:

    var video = document.getElementById("myVideo");
    var prevBuffer = {
        "buffer": null,
        "time": null
    };
    var isBuffering = function(){
    
        if(video && video.buffered && video.buffered.end && video.buffered.length > 0){
            var buffer = video.buffered.end(0);
            var time   = video.currentTime;
    
            // Check if the video hangs because of issues with e.g. performance
            if(prevBuffer.buffer === buffer && prevBuffer.time === time && !video.paused){
                return true;
            }
            prevBuffer = {
                "buffer": buffer,
                "time": time
            };
            // Check if video buffer is less
            // than current time (tolerance 3 sec)
            if((buffer - 3) < time){
                return true;
            }
        }
        return false;
    
    };
    video.addEventListener("play", function(e){
        // Make sure this handler is only called once
        e.target.removeEventListener(e.type, arguments.callee);
        // Give browsers 3secs time to buffer
        setTimeout(function(){
            // As "progress", "stalled" or "waiting" aren't fired
            // reliable, we need to use an interval
            var interval = setInterval(function(){
                if(isBuffering()){
                    clearInterval(interval);
                    console.log("Buffering");
                }
            }, 500);
        }, 3000);
    });
    
    0 讨论(0)
  • 2021-01-30 07:46

    I did this by inspecting the player progress every x milliseconds, e.g. 50. If the player hasn't advanced as much as it was expected to, then we are buffering. This is quite reliable, since I've found that other events such as waiting or stalled are not fired in all cases of the video buffering.

    Note that the interval must be larger than the expected inter-frame difference, but I'm sure that you won't want to be that precise anyway. An estimation of buffering time within ±300ms would still be fine, given that humans most likely cannot perceive differences in that region.

    It is important to check whether the user hasn't actively paused the playback though.

    var checkInterval  = 50.0 // check every 50 ms (do not use lower values)
    var lastPlayPos    = 0
    var currentPlayPos = 0
    var bufferingDetected = false
    var player = document.getElementById('videoPlayer')
    
    setInterval(checkBuffering, checkInterval)
    function checkBuffering() {
        currentPlayPos = player.currentTime
    
        // checking offset should be at most the check interval
        // but allow for some margin
        var offset = (checkInterval - 20) / 1000
    
        // if no buffering is currently detected,
        // and the position does not seem to increase
        // and the player isn't manually paused...
        if (
                !bufferingDetected 
                && currentPlayPos < (lastPlayPos + offset)
                && !player.paused
            ) {
            console.log("buffering")
            bufferingDetected = true
        }
    
        // if we were buffering but the player has advanced,
        // then there is no buffering
        if (
            bufferingDetected 
            && currentPlayPos > (lastPlayPos + offset)
            && !player.paused
            ) {
            console.log("not buffering anymore")
            bufferingDetected = false
        }
        lastPlayPos = currentPlayPos
    }
    
    0 讨论(0)
提交回复
热议问题