How to use Blob URL, MediaSource or other methods to play concatenated Blobs of media fragments? : <p>Am attempting to implement, for lack of a different description, an offline media context.</p>

<p>The concept is to create 1 second <code>Blob</code>s of recorded media, with the ability to </p>

<ol>
<li>Play the 1 second <code>Blobs</code> independently at an <code>HTMLMediaElement</code></li>
<li>Play the full media resource from concatenated <code>Blob</code>s</li>
</ol>

<p>The issue is that once the <code>Blob</code>s are concatenated the media resource does not play at <code>HTMLMedia</code> element using either a <code>Blob URL</code> or <code>MediaSource</code>.</p>

<p>The created <code>Blob URL</code> only plays 1 second of the concatenated <code>Blob</code>'s. <code>MediaSource</code> throws two exceptions</p>

<pre><code>DOMException: Failed to execute 'addSourceBuffer' on 'MediaSource': The MediaSource's readyState is not 'open'
</code></pre>

<p>and</p>

<pre><code>DOMException: Failed to execute 'appendBuffer' on 'SourceBuffer': This SourceBuffer has been removed from the parent media source.
</code></pre>

<p>How to properly encode the concatenated <code>Blob</code>s or otherwise implement a workaround to play the media fragments as a single re-constituted media resource?</p>

<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;

&lt;head&gt;
&lt;/head&gt;

&lt;body&gt;
  &lt;script&gt;
    const src = "https://nickdesaulniers.github.io/netfix/demo/frag_bunny.mp4";
    fetch(src)
      .then(response =&gt; response.blob())
      .then(blob =&gt; {
        const blobURL = URL.createObjectURL(blob);
        const chunks = [];
        const mimeCodec = "vdeo/webm; codecs=opus";
        let duration;
        let media = document.createElement("video");
        media.onloadedmetadata = () =&gt; {
          media.onloadedmetadata = null;
          duration = Math.ceil(media.duration);
          let arr = Array.from({
            length: duration
          }, (_, index) =&gt; index);
          // record each second of media
          arr.reduce((p, index) =&gt;
              p.then(() =&gt;
                new Promise(resolve =&gt; {
                  let recorder;
                  let video = document.createElement("video");
                  video.onpause = e =&gt; {
                    video.onpause = null;
                    console.log(e);
                    recorder.stop();
                  }

                  video.oncanplay = () =&gt; {
                    video.oncanplay = null;
                    video.play();

                    let stream = video.captureStream();

                    recorder = new MediaRecorder(stream);

                    recorder.start();

                    recorder.ondataavailable = e =&gt; {
                      console.log("data event", recorder.state, e.data);
                      chunks.push(e.data);
                    }

                    recorder.onstop = e =&gt; {
                      resolve();
                    }

                  }
                  video.src = `${blobURL}#t=${index},${index+1}`;
                })
              ), Promise.resolve())
            .then(() =&gt; {
              console.log(chunks);
              let video = document.createElement("video");
              video.controls = true;
              document.body.appendChild(video);
              let select = document.createElement("select");
              document.body.appendChild(select);
              let option = new Option("select a segment");
              select.appendChild(option);
              for (let chunk of chunks) {
                let index = chunks.indexOf(chunk);
                let option = new Option(`Play ${index}-${index + 1} seconds of media`, index);
                select.appendChild(option)
              }
              let fullMedia = new Blob(chunks, {
                type: mimeCodec
              });

              let opt = new Option("Play full media", "Play full media");
              select.appendChild(opt);
              select.onchange = () =&gt; {
                if (select.value !== "Play full media") {
                  video.src = URL.createObjectURL(chunks[select.value])
                } else {

                  const mediaSource = new MediaSource();
                  video.src = URL.createObjectURL(mediaSource);
                  mediaSource.addEventListener("sourceopen", sourceOpen);

                  function sourceOpen(event) {
                    // if the media type is supported by `mediaSource`
                    // fetch resource, begin stream read, 
                    // append stream to `sourceBuffer`
                    if (MediaSource.isTypeSupported(mimeCodec)) {
                      var sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
                      // set `sourceBuffer` `.mode` to `"sequence"`
                      sourceBuffer.mode = "segments";

                      fetch(URL.createObjectURL(fullMedia))
                        // return `ReadableStream` of `response`
                        .then(response =&gt; response.body.getReader())
                        .then(reader =&gt; {

                          const processStream = (data) =&gt; {
                              if (data.done) {
                                return;
                              }
                              // append chunk of stream to `sourceBuffer`
                              sourceBuffer.appendBuffer(data.value);
                            }
                            // at `sourceBuffer` `updateend` call `reader.read()`,
                            // to read next chunk of stream, append chunk to 
                            // `sourceBuffer`
                          sourceBuffer.addEventListener("updateend", function() {
                            reader.read().then(processStream);
                          });
                          // start processing stream
                          reader.read().then(processStream);
                          // do stuff `reader` is closed, 
                          // read of stream is complete
                          return reader.closed.then(() =&gt; {
                            // signal end of stream to `mediaSource`
                            mediaSource.endOfStream();
                            return mediaSource.readyState;
                          })
                        })
                        // do stuff when `reader.closed`, `mediaSource` stream ended
                        .then(msg =&gt; console.log(msg))
                        .catch(err =&gt; console.log(err))
                    }
                    // if `mimeCodec` is not supported by `MediaSource`  
                    else {
                      alert(mimeCodec + " not supported");
                    }
                  };

                }

              }

            })
        }
        media.src = blobURL;
      })
  &lt;/script&gt;
&lt;/body&gt;

&lt;/html&gt;
</code></pre>

<p>using <code>Blob URL</code> at <code>else</code> statement at <code>select</code> <code>change</code> event, which only plays first second of media resource</p>

<pre><code>video.src = URL.createObjectURL(fullMedia);
</code></pre>

<p>plnkr <a href="http://plnkr.co/edit/dNznvxe504JX7RWY658T?p=preview" rel="noreferrer">http://plnkr.co/edit/dNznvxe504JX7RWY658T?p=preview</a> version 1 <code>Blob URL</code>, version 2 <code>MediaSource</code></p>
