videojs / video.js

Video.js - open source HTML5 video player
https://videojs.com
Other
38.15k stars 7.46k forks source link

Infinite recursive function call #5281

Open ayamamori opened 6 years ago

ayamamori commented 6 years ago

Description

In the latest master branch, infinite recursive function call happens.

I identified that the commit https://github.com/videojs/video.js/pull/5255/commits/7ae5734e6321a6c532cb5c7f41e13e6c973561c2 (https://github.com/videojs/video.js/pull/5255 ) is the cause of this issue, and reverting the commit will resolve the issue.

Steps to reproduce

  1. Build video.js on the latest master branch

    npm install && npm run build
  2. Save the video.js in some directory and the following html file, open the html file.

<!DOCTYPE html>
<html>
  <head> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <link href="http://vjs.zencdn.net/7.0/video-js.min.css" rel="stylesheet">
    <script src="./video.js"></script>
  </head>
  <body>
    <video id="test-video" class="video-js vjs-default-skin" controls muted>
      <source src="https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8" type='application/vnd.apple.mpegurl' />
      <!-- <source id="test-source" src="https://www.sample-videos.com/video/mp4/240/big_buck_bunny_240p_1mb.mp4" type='video/mp4' /> -->
      <!-- Able to reproduce with either mp4 or m3u8(HLS) -->

    </video>
    <script>
      var main = function(){
        player = videojs("test-video");
        player.play();
      }
      main();
    </script>
  </body>
</html>

Results

Expected

Video (either HLS or mp4) is played properly, with no output on the web console.

Actual

Video was played properly but Maximum call stack size exceeded (Chrome) InternalError: too much recursion (Firefox) errors flowed forever.

2018-06-27 17 29 00

Error output

Copied the first 2 errors.

VIDEOJS: ERROR: RangeError: Maximum call stack size exceeded
    at MasterPlaylistController.data.dispatcher (video.js:1766)
    at MasterPlaylistController.data.dispatcher (video.js:1764)
    at MasterPlaylistController.data.dispatcher (video.js:1764)
    at MasterPlaylistController.data.dispatcher (video.js:1764)
    at MasterPlaylistController.data.dispatcher (video.js:1764)
    at MasterPlaylistController.data.dispatcher (video.js:1764)
    at MasterPlaylistController.data.dispatcher (video.js:1764)
    at MasterPlaylistController.data.dispatcher (video.js:1764)
    at MasterPlaylistController.data.dispatcher (video.js:1764)
    at MasterPlaylistController.data.dispatcher (video.js:1764)
(snip 30+ lines)
video.js:128 VIDEOJS: ERROR: RangeError: Maximum call stack size exceeded
    at new RegExp (<anonymous>)
    at logByType (video.js:89)
    at Function.log.error (video.js:249)
    at HTMLDivElement.data.dispatcher (video.js:1766)
    at trigger (video.js:1896)
    at Player.trigger$$1 [as trigger] (video.js:2748)
    at Player.(anonymous function) [as handleTechProgress_] (file:///Users/***********/video.js:25368:19)
    at Html5.bound (video.js:2117)
    at HTMLVideoElement.bound (video.js:2117)
    at HTMLVideoElement.data.dispatcher (video.js:1764)
logByType @ video.js:128

Additional Information

Please include any additional information necessary here. Including the following:

versions

videojs

The latest master branch (b430461335bbb9a9515749c847e068b4f396e9bd).

browsers

Reproduced on Chrome (67.0.3396.99) and Firefox (60.0.2) on Win10 and Mac OS X Sierra (10.12.6) Not reproduced on Safari (11.1.1) and Edge (38.14393.2068.0)

OSes

As noted above

plugins

are any videojs plugins being used on the page? -> NO

gkatsev commented 6 years ago

Thanks for the issue, I can reproduce locally with an HLS url.

gkatsev commented 6 years ago

We didn't get a chance to fix the root cause, which we believe lies in VHS. We're going to be reverting the commit in #5301 before making a 7.1 release.

gkatsev commented 6 years ago

We'll keep this issue open so that we won't forget to fix the root cause and be able to apply #5255 at a later date.

yaelm-carbyne commented 3 years ago

I still get this error sometimes. Any update how to resolve it?

gkatsev commented 3 years ago

@yaelm-carbyne yes, it's fixed. It's been kept open so that we can re-do the work from #5255 which hasn't happened yet.

yaelm-carbyne commented 3 years ago

Thanks @gkatsev ! I still get this error somtimes. I'm using latest hls.js. Any idea why I'm still getting this error and how can it resolved? Thanks! :)

gkatsev commented 3 years ago

@yaelm-carbyne can you provide more information? What version of Video.js? What browser? Ideally, can you provide a live minimal test case?

yaelm-carbyne commented 3 years ago

Thanks @gkatsev !

This is the code:

function startReviewModeVideo(mediaStreamingURL)
{
    mediaStreamingURL = mediaStreamingURL;
    console.log(`Start video with URL: ${mediaStreamingURL}`);

    // For more options see: https://github.com/sampotts/plyr/#options
    // captions.update is required for captions to work with hls.js
    const defaultOptions = {};

    // For more Hls.js options, see https://github.com/dailymotion/hls.js
    m_hls = new Hls();
    m_hls.loadSource(mediaStreamingURL);

    // From the m3u8 playlist, hls parses the manifest and returns
    // all available video qualities. This is important, in this approach,
    // we will have one source on the Plyr player.
    m_hls.on(Hls.Events.MANIFEST_PARSED, function (event, data)
    {
        console.log(`manifest loaded, found ${data.levels.length} quality level`);

        // Transform available levels into an array of integers (height values).
        const availableQualities = m_hls.levels.map((level) => level.height)

        // Add new qualities to option
        defaultOptions.quality =
        {
            default: availableQualities[0],
            options: availableQualities,
            // this ensures Plyr to use Hls to update quality level
            forced: true,
            onChange: (event) => updateQuality(event),
        }

        defaultOptions.hideControls = false;
        defaultOptions.invertTime = false;
        defaultOptions.toggleInvert = false;

        defaultOptions.controls = [
            // 'play-large',
            // 'restart',
            // 'rewind',
            'play',
            // 'fast-forward',
            'progress',
            'current-time',
            'duration',
            'mute',
            'volume',
            //'captions',
            //'settings',
            //'pip',
            'airplay',
            // 'download',
            //'fullscreen',
          ]

        const player = new Plyr(m_reviewModeVideo, defaultOptions);
        player.on('loadedmetadata', event =>
        {
            console.log("Got loadedmetadata event: " + m_isVideoCanPlay);
            console.log("loadedmetadata player.currentTime: " + player.currentTime);
            if (!m_isVideoCanPlay)
            {
                m_isVideoCanPlay = true;
                // Seeks to 0 seconds
                player.currentTime = 0;
            }
        });
    });

    m_hls.attachMedia(m_reviewModeVideo);

    m_hls.on(Hls.Events.ERROR, function (event, data)
    {
        console.error(`errorType: ${errorType}, errorDetails: ${errorDetails}, errorFatal: ${errorFatal}, data.details: ${data.details}`);

        if (data.fatal)
        {
            switch(data.type)
            {
                case Hls.ErrorTypes.NETWORK_ERROR:
                    // try to recover network error
                    console.log("fatal network error encountered, try to recover");
                    m_hls.startLoad();
                    break;

                case Hls.ErrorTypes.MEDIA_ERROR:
                    console.log("fatal media error encountered, try to recover");
                    m_hls.recoverMediaError();
                    break;

                default:
                    // cannot recover
                    console.log("cannot recover");
                    m_hls.destroy();
                    break;
          }
        }
    });
}

function updateQuality(newQuality)
{
    m_hls.levels.forEach((level, levelIndex) =>
    {
        if (level.height === newQuality)
        {
            console.log("Found quality match with " + newQuality);
            m_hls.currentLevel = levelIndex;
        }
    });
}
gkatsev commented 3 years ago

@yaelm-carbyne looks like you're not using Video.js. We can't help out with Plyr issues.