Search code examples
javascriptaddeventlistenermedia-source

Why is the Sourceopen Event Listener being executed at the end of the script?


I'm trying to create a Media Player with the use of Media Source Extension API. The Media Player works perfectly but I'm not able to understand a particular event. The sourceopen addEventListner of the MediaSource is declared at line 20. The sourceopen adds the Source Buffer to the MediaSource and then appends to the Source Buffer. At line 21 and 13, I have included console loggings. When the Website is executed, the console outputs the console log of line 13 first. When from my point of view the console log of line 21 should be shown first. I believe that I'm not able to understand how the sourceopen Event Listener works. Can someone Please explain why sourceopen Event Listener is being exceuted after line 13. Thanks

If someone is not able to understand my question, please comment below.

01| <!DOCTYPE html>
02| <html>
03|   <head>
04|     <meta charset="utf-8"/>
05|   </head>
06|   <body>
07|     <video controls></video>
08|     <script>
09|       var video = document.querySelector('video');
10|       var assetURL = 'frag_bunny.mp4';
11|       var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
12|       start();
13|       console.log('2');
14|
15|       function start()
16|       {
17|         var mediaSource = new MediaSource;
18|         video.src = URL.createObjectURL(mediaSource);
19|       
20|         mediaSource.addEventListener('sourceopen', function () {
21|           console.log('1');
22|           var sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
23|           fetchAB(assetURL, function (buf)
24|           {
25|             sourceBuffer.appendBuffer(buf);
26|           });
27|         });
28|       } 
29|
30|       function fetchAB (url, cb)
31|       {
32|         var xhr = new XMLHttpRequest;
33|         xhr.open('get', url);
34|         xhr.responseType = 'arraybuffer';
35|         xhr.onload = function ()
36|         {
37|           cb(xhr.response);
38|         };
39|         xhr.send();
40|       };
41|     </script>
42|   </body>
43| </html>

Solution

  • A correct start() function would be like this:

    function start() {
    
        // create an object, an instance of the MediaSource
        var mediaSource = new MediaSource;
    
        // to this `mediaSource` object add an event listener for the `sourceopen` event
        // and run the code inside the function when `sourceopen` happens
        mediaSource.addEventListener('sourceopen', function () {
            console.log('1');
            var sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
            fetchAB(assetURL, function (buf) {
                sourceBuffer.appendBuffer(buf);
            });
        });
    
        // hey, `video` element, here is the source of the media I'd like you to play
        // it's not a simple url, is something more complex
        // , a `MediaSource` kind of thing
        // and it might take you some time to be ready        
        video.src = URL.createObjectURL(mediaSource);
    
    }
    

    Now, back to the entire code..., if you tell the browser to execute a line like this:

    console.log('2');

    , the browser has no problem to do it immediately, you will see 2 in the console in no time.

    But, this thing:

    video.src = URL.createObjectURL(mediaSource);

    is not that simple for the browser.

    When you ask the browser to execute this, the browser is kind of saying: "Okay, you're asking me to execute, I will start now, and you can continue with the rest of the code, but, this one it's not that easy for me..., I need to start spinning some wheels..., it's going to take me some time..., also, I don't want to go out and fetch the video before I'm ready for it. I will let you know when I'm ready.

    In fact, not directly me, the browser, but the mediaSource object, which is an instance of MediaSource which is one of my (the browser's) APIs, will let you know by raising a sourceopen event.

    So..., when you put this code on the page:

    mediaSource.addEventListener('sourceopen', function () {
      // do things
    });
    

    you're telling the browser what things to do when is ready and sourceopen has fired.

    Let's conclude:

    12| start();

    // start() is called and starts to execute but it has something inside that 
    // will take some time before ready
    // as consequence `console.log('1')` does not happen yet
    

    13| console.log('2');

    // runs imediatelly  
    // you see "2" in the console
    

    ... some time passes, code inside start() is getting things ready


    // the `sourceopen` event fires and a `function ()` 
    // the callback of `mediaSource.addEventListener('sourceopen'` 
    // starts to execute  
    

    21| console.log('1');

    // gets executed
    // you see "1" in the console