{"id":1600,"date":"2023-10-31T15:56:57","date_gmt":"2023-10-31T14:56:57","guid":{"rendered":"https:\/\/websites.fraunhofer.de\/video-dev\/?p=1600"},"modified":"2023-11-09T09:10:05","modified_gmt":"2023-11-09T08:10:05","slug":"being-trapped-in-a-gap-with-big-buck-bunny","status":"publish","type":"post","link":"https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/","title":{"rendered":"Being trapped in a gap with Big Buck Bunny"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p>When I thought about how to introduce this blog post, the first thing that popped to my mind was to start with a warning. Something like \u201cthis blog post will be technical and focus on details that are only be relevant for player developers\u201d. While the latter is probably still true, I am not sure anymore if I can call it \u201ctechnical\u201d after watching the <a href=\"https:\/\/2023.demuxed.com\/#schedule\">Demuxed presentation<\/a> by Matt Szatmary (Mux).<\/p>\n\n\n\n<p>In any case, just a few days ago I stumbled upon a <a href=\"https:\/\/github.com\/w3c\/media-source\/issues\/160\">GitHub issue<\/a> that was raised in 2016 by <a href=\"https:\/\/github.com\/davemevans\">David Evans<\/a>, a long-time contributor to the <a href=\"https:\/\/github.com\/Dash-Industry-Forum\/dash.js\">dash.js project.<\/a> The issue deals with a limitation of the <a href=\"https:\/\/w3c.github.io\/media-source\/\">Media Source Extensions (MSE)<\/a> that probably every media player developer has at least heard of: <em>\u201cPlaying through unbuffered ranges and allowing the application to provide a buffered gap tolerance\u201d.<\/em> To the few readers that made it until this part of the blog post and have not heard about this limitation: First, thank you for not closing this tab and second, you are in for a real treat. Let\u2019s look at a concrete example of what we mean by \u201c<em>unbuffered ranges<\/em>\u201d and \u201c<em>gaps<\/em>\u201d.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Gaps in the media buffer<\/h2>\n\n\n\n<p>When using the MSE we need to create <a href=\"https:\/\/w3c.github.io\/media-source\/#sourcebuffer\">SourceBuffers<\/a>&nbsp;to which we append our media segments. The SourceBuffer object supports <a href=\"https:\/\/www.w3.org\/TR\/media-source-2\/#dom-appendmode\">two append modes<\/a> namely \u201c<em>segments<\/em>\u201d and \u201c<em>sequence<\/em>\u201d. Typically, the \u201c<em>segments<\/em>\u201d mode is used, and the media segments are placed in the media buffer according to their presentation timestamps. While this allows us to append the media segments in an arbitrary order, it can also cause non-alignments as illustrated below.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"698\" height=\"170\" src=\"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/gap.drawio-1-698x170.png\" alt=\"\" class=\"wp-image-1606\" srcset=\"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/gap.drawio-1-698x170.png 698w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/gap.drawio-1-400x97.png 400w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/gap.drawio-1-768x187.png 768w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/gap.drawio-1.png 1078w\" sizes=\"auto, (max-width: 698px) 100vw, 698px\" \/><\/figure>\n\n\n\n<p>In this example, segment 1 and segment 2 are perfectly aligned, but there is a gap between segment 2 and segment 3. There are various reasons for these kinds of gaps, such as media segments being shifted out of a DASH period (negative <em>@eptDelta<\/em>), wrong presentation timestamps in the media segments or the total sample duration of a media segment not matching its duration. In &#8220;<em><a href=\"https:\/\/websites.fraunhofer.de\/video-dev\/mse-a-story-of-append-windows-presentation-timestamps-and-video-buffers\/\">MSE: A story of append windows, presentation timestamps and video buffers<\/a><\/em>&#8221; we examined such situations in more detail.<\/p>\n\n\n\n<p>Regardless of the reason for the gap, native MSE implementation in all major browsers (Chrome, Firefox, Edge, Safari) stall at such gaps until they are filled with data. The problem is that in most cases there is no data to fill the gaps as they are usually not created intentionally. Due to that reason, media players such as dash.js and Shaka player implement a logic to detect and handle such gaps to continue the media playback.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Native gap handling of the MSE<\/h2>\n\n\n\n<p>Coming back to our original Github issue, the suggestion that Dave made was to leave the handling of the gaps up to the browser and expose an API to control the behavior. Unfortunately, this is still an issue today and there is no native solution for the problem. <a href=\"https:\/\/github.com\/wolenetz\">Matt Wolenetz<\/a> (previously Google, W3C Invited Expert) pointed out that the issue\u00a0was discussed again at the FOMS 2023. The second part of Matt`s comment is where it gets interesting:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Meanwhile, there is an ugly hack that might help coalesce unintended gaps in Chromium SourceBuffers: the coalescence heuristic currently used there is based on the largest frame duration buffered so far in that particular track in that particular SourceBuffer. Auto-coalescence of gaps can thus be achieved by manipulation of the inputs to this heuristic:<\/p>\n\n\n\n<ol class=\"wp-block-list\" type=\"1\" start=\"1\">\n<li>For each track, first append a simple keyframe with huge duration. Ensure it is buffered. Then remove it from the SourceBuffer (or overlap it in your first appends to that track).<\/li>\n\n\n\n<li>This should trick the heuristic.<\/li>\n<\/ol>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">A native solution for Chrome and other browsers?<\/h2>\n\n\n\n<p>Now we are curious to try this workaround. Not only to verify that it is really working in Chrome, but there is also the slight hope that it might work in other browsers such as Firefox and Safari as well.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">A simple MSE player<\/h3>\n\n\n\n<p>The first thing we need is a simple standalone MSE player. We would rather not implement this \u201chack\u201d in dash.js or Shaka. It simply creates lots of overhead and requires us to change several classes. Our simple MSE player looks like this and supports the basic append and remove operations (Note I am not pasting the whole source code here but just the important parts).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>App.prototype.createMediaSource = function () {\n    var self = this;\n\n    self.mediaSource = new MediaSource();\n    self.video.src = URL.createObjectURL(self.mediaSource);\n    self.mediaSource.addEventListener('sourceopen',\nself.onMediaSourceOpen.bind(self));\n\n};\n\nApp.prototype.onMediaSourceOpen = function () {\n    var self = this;\n\n    self.mediaSource.duration = (NUM_SEGMENTS - 1) * 2;\n    self.sourceBuffer = self.mediaSource.addSourceBuffer('video\/mp4; codecs=\"avc1.4d4028\"');\n    self.sourceBuffer.addEventListener('updateend', function () {\n        self.printBufferRanges();\n        if (ENABLE_GAP_WORKAROUND &amp;&amp; self.currentSegment === 2 &amp;&amp; !self.hasRemoved) {\n            self.remove(0, 2000);\n            self.hasRemoved = true;\n            return;\n        } \n\n     self.append();\n});\n\nApp.prototype.append = function () {\n    var self = this;\n\n    if (self.currentSegment &gt;= content.length - 1) {\n        self.finished = true;\n    } else {\n        self.fetchSegment(content&#91;self.currentSegment], function (arrayBuffer) {\n            try {\n                self.sourceBuffer.appendBuffer(arrayBuffer);\n                self.currentSegment += 1;\n            } catch (e) {\n                console.log(e);\n            }\n        });\n    }\n};\n\nApp.prototype.remove = function (start, end) {\n    var self = this;\n    self.debug('Removing buffer from ' + start + ' to ' + end)\n    self.sourceBuffer.remove(start, end);\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Test Content<\/h3>\n\n\n\n<p>Next, we need to create test content that we can feed into our MSE player. Based on Matt\u2019s comment above, we require two things: First, a media segment with a single IDR frame (IDR-segment) and a \u201chuge duration\u201d. Second, our \u201cdefault\u201d media content with a gap in the media buffer.<\/p>\n\n\n\n<p>For that reason, we create a media segment with a single IDR frame and a duration of four seconds. That will be our \u201csimple keyframe with a huge duration\u201d. And to keep us motivated throughout the entire process we use our favorite bunny (the name is avoided here to not cause any mental issues with my fellow developer colleagues) as an input: There is no better feeling than replacing the bunny with something different.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"698\" height=\"353\" src=\"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/1-698x353.png\" alt=\"\" class=\"wp-image-1612\" srcset=\"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/1-698x353.png 698w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/1-400x202.png 400w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/1-768x388.png 768w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/1-1536x776.png 1536w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/1-2048x1034.png 2048w\" sizes=\"auto, (max-width: 698px) 100vw, 698px\" \/><\/figure>\n\n\n\n<p>The \u201cdefault\u201d content that we use to replace the IDR-segment with is encoded and packaged with<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>2 second segments<\/li>\n\n\n\n<li>25 frames per second<\/li>\n\n\n\n<li>GoP size of 50 frames<\/li>\n<\/ul>\n\n\n\n<p>We only need a few seconds to test if the MSE implementation is jumping natively over the gaps, a total duration of 14 seconds is sufficient. If we append all our segments to the media buffer, it looks like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"698\" height=\"192\" src=\"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/normal-698x192.png\" alt=\"\" class=\"wp-image-1614\" srcset=\"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/normal-698x192.png 698w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/normal-400x110.png 400w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/normal-768x211.png 768w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/normal-1536x422.png 1536w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/normal.png 1974w\" sizes=\"auto, (max-width: 698px) 100vw, 698px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Creating gaps in the media buffer<\/h2>\n\n\n\n<p>Now that we have our media player and our test content ready, we need to think about a way to create gaps in the media buffer. (Un)fortunately that is very straightforward: We simply do not append segment 2 and segment 6. That way we create a gap between playback position 2 \u2013 4 and 10 \u2013 12.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"698\" height=\"198\" src=\"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/gap-content-698x198.png\" alt=\"\" class=\"wp-image-1616\" srcset=\"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/gap-content-698x198.png 698w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/gap-content-400x113.png 400w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/gap-content-768x218.png 768w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/gap-content-1536x435.png 1536w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/gap-content.png 1934w\" sizes=\"auto, (max-width: 698px) 100vw, 698px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Test results<\/h2>\n\n\n\n<p>Now we have everything in place to run some tests.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">No gap workaround<\/h3>\n\n\n\n<p>First let\u2019s check what happens if we append our default content with the two gaps illustrated above to the SourceBuffer. We expect our simple MSE player to stall right at the end of segment number 1:<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-regular\"><table><tbody><tr><td><strong>Browser<\/strong><\/td><td><strong>Version<\/strong><\/td><td><strong>Results<\/strong><\/td><\/tr><tr><td>Chrome<\/td><td>118.0.5993.117<\/td><td>Range 0 starts at 0 to 2<br>Range 1 starts at 4 to 6<br>Waiting for more data at 1.952094<\/td><\/tr><tr><td>Firefox<\/td><td>119.0<\/td><td>Range 0 starts at 0 to 2<br>Range 1 starts at 4 to 6<br>Waiting for more data at 1.979588<\/td><\/tr><tr><td>Safari<\/td><td>&nbsp;16.6 (18615.3.12.11.2)<\/td><td>Range 0 starts at 0 to 2<br>Range 1 starts at 4 to 6<br><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><strong>Waiting for more data at 2.258507333<\/strong><\/mark><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>As expected, Chrome and Firefox stall shortly before reaching the two-second mark. Interestingly, Safari claims to have a buffered range from 0 to 2 but plays until 2.25 seconds. I have no clue where it gets the additional 0.25 seconds from.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Active gap workaround<\/h3>\n\n\n\n<p>Now that we have confirmed that all browsers stall when encountering a gap, we can add our IDR frame workaround to the test:<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-regular\"><table><tbody><tr><td><strong>Browser<\/strong><\/td><td><strong>Version<\/strong><\/td><td><strong>Results<\/strong><\/td><\/tr><tr><td>Chrome<\/td><td>118.0.5993.117<\/td><td>Range 0 starts at 0 to 4<br>Removing buffer from 0 to 2000<br>No valid ranges<br>Range 0 starts at 0 to 2<br><strong>Range 0 starts at 0 to 6<\/strong><\/td><\/tr><tr><td>Firefox<\/td><td>119.0<\/td><td>Range 0 starts at 0 to 4<br>Removing buffer from 0 to 2000<br>No valid ranges<br>Range 0 starts at 0 to 2<br><strong>Range 1 starts at 4 to 6<\/strong><\/td><\/tr><tr><td>Safari<\/td><td>&nbsp;16.6 (18615.3.12.11.2)<\/td><td>Range 0 starts at 0 to 4<br>Removing buffer from 0 to 2000<br>No valid ranges<br>Range 0 starts at 0 to 2<br><strong>Range 1 starts at 4 to 6<\/strong><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Looking at the logs we can see that the media buffer is filled for a range from 0 to 4 by appending the IDR-segment. The media buffer is emptied again by removing all the data (results in &#8220;<em>No valid ranges<\/em>&#8220;). Next we append our default content and can identify a gap between 2 \u2013 4 seconds in Firefox and Safari. The Chrome browser has a single range object with not gap between 2 \u2013 4 seconds. This looks promising! Now let\u2019s check what happens if we start the actual playback:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"698\" height=\"439\" src=\"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/chrome-playback-698x439.png\" alt=\"\" class=\"wp-image-1621\" srcset=\"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/chrome-playback-698x439.png 698w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/chrome-playback-400x252.png 400w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/chrome-playback-768x483.png 768w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/chrome-playback-1536x967.png 1536w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/chrome-playback-2048x1289.png 2048w\" sizes=\"auto, (max-width: 698px) 100vw, 698px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"698\" height=\"254\" src=\"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/ff-safari-playback-698x254.png\" alt=\"\" class=\"wp-image-1622\" srcset=\"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/ff-safari-playback-698x254.png 698w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/ff-safari-playback-400x145.png 400w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/ff-safari-playback-768x279.png 768w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/ff-safari-playback-1536x559.png 1536w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/ff-safari-playback.png 2024w\" sizes=\"auto, (max-width: 698px) 100vw, 698px\" \/><\/figure>\n\n\n\n<p>Ups, this is not what we expected. For some reason, we can still see our least favorite bunny for the first four seconds. How is this possible? We removed it from the buffer and the logs clearly stated the there was no data in the buffer before appending the default content. Moreover, even if we don\u2019t remove the IDR segment before appending our default content it should still be replaced as we are adding data to the same buffer position (0-2 seconds).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">IDR-segment only<\/h3>\n\n\n\n<p>Let\u2019s take a step back. What happens if we only append the IDR segment and remove it from the buffer? There should be no data in the buffer and no playback possible, right?<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-regular\"><table><tbody><tr><td><strong>Browser<\/strong><\/td><td><strong>Version<\/strong><\/td><td><strong>Results<\/strong><\/td><\/tr><tr><td>Chrome<\/td><td>118.0.5993.117<\/td><td>Range 0 starts at 0 to 4<br>Removing buffer from 0 to 2000<br>No valid ranges<br><strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">Current Time:0.255889<br>Current Time:0.519626<\/mark><\/strong><br>\u2026<br><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><strong>Waiting for more data at 3.980187<\/strong><\/mark><\/td><\/tr><tr><td>Firefox<\/td><td>119.0<\/td><td>No valid ranges<br>Range 0 starts at 0 to 4<br>Removing buffer from 0 to 2000<br>No valid ranges<br>Current Time:0<br>Waiting for more data at 0<\/td><\/tr><tr><td>Safari<\/td><td>&nbsp;16.6 (18615.3.12.11.2)<\/td><td>No valid ranges<br>Range 0 starts at 0 to 4<br>Removing buffer from 0 to 2000<br>No valid ranges<br>Waiting for more data at 0<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Well, what can I say? He is still here!<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"698\" height=\"425\" src=\"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/bbb-2-698x425.png\" alt=\"\" class=\"wp-image-1625\" srcset=\"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/bbb-2-698x425.png 698w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/bbb-2-400x244.png 400w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/bbb-2-768x468.png 768w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/bbb-2-1536x935.png 1536w, https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/bbb-2-2048x1247.png 2048w\" sizes=\"auto, (max-width: 698px) 100vw, 698px\" \/><\/figure>\n\n\n\n<p>Although there is no valid range in the Chrome SourceBuffer the playback is still progressing for four seconds (duration of the IDR frame segment). I don\u2019t have a reasonable explanation for this, it is probably a Chrome bug. But what does that mean for our gap workaround? Can we still get this to work?<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Adjusting the gap workaround<\/h3>\n\n\n\n<p>Turns out we can still make the gap workaround work. The trick is to do a seek:<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-regular\"><table><tbody><tr><td><strong>Browser<\/strong><\/td><td><strong>Version<\/strong><\/td><td><strong>Results<\/strong><\/td><\/tr><tr><td>Chrome<\/td><td>118.0.5993.117<\/td><td>Range 0 starts at 0 to 4<br>Removing buffer from 0 to 2000<br>No valid ranges<br>Range 0 starts at 0 to 2<br><strong>Seeking by 0.0001<\/strong><br>Range 0 starts at 0 to 6<br><strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-green-cyan-color\">Waiting for more data at 17.92605<\/mark><\/strong><\/td><\/tr><tr><td>Firefox<\/td><td>119.0<\/td><td>Range 0 starts at 0 to 4<br>Removing buffer from 0 to 2000<br>No valid ranges<br>Range 0 starts at 0 to 2<br>Range 1 starts at 4 to 6<br>Seeking by 0.0001<br><strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">Waiting for more data at 1.967075<\/mark><\/strong><\/td><\/tr><tr><td>Safari<\/td><td>&nbsp;16.6 (18615.3.12.11.2)<\/td><td>Range 0 starts at 0 to 4<br>Removing buffer from 0 to 2000<br>No valid ranges<br>Range 0 starts at 0 to 2<br>Range 1 starts at 4 to 6<br>Seeking by 0.0001<br><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\"><strong>Waiting for more data at 2.260509<\/strong><\/mark><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>After appending our first segment of the default content we do a minimal seek. This seems to force some kind of reset of the rendering pipeline. Now Chrome is really jumping over the gaps natively and showing us the right content:<\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"868\" style=\"aspect-ratio: 1280 \/ 868;\" width=\"1280\" controls src=\"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/final-solution-cut.mp4\"><\/video><\/figure>\n\n\n\n<p>Note that Chrome is playing but not seeking over the gap. This means that we see a still frame for two seconds while the playback still progresses. It would also be possible to simply seek over the gaps in the media buffer. However, this would lead to problems with live content as we would move closer and closer to the live edge with each seek.<\/p>\n\n\n\n<p>Unfortunately, the gap \u201chack does not work for Firefox and Safari, they still stall around two seconds.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Native MSE implementations in all major browsers (Chrome, Firefox, Edge, Safari) stall if there are gaps in the media buffer. For that reason, media players such as dash.js and Shaka player typically implement their own logic to detect and handle gaps and continue the media playback.<\/p>\n\n\n\n<p>Based on a <a href=\"https:\/\/github.com\/w3c\/media-source\/issues\/160#issuecomment-1776215889\">GitHub comment<\/a> by Matt Wolenetz (previously Google, W3C Invited Expert) we examined a solution to enable native MSE gap handling. The idea was to append a media segment with a single IDR frame and a large sample duration to the media buffer before adding the default content. We found that a programmatic seek is required to remove the content of the IDR-segment from the media buffer and make the gap fix work. While this solution works in Chrome, it is not an option for Firefox and Safari as the playback still stalls in these browsers. Consequently, it is not a promising option to add this workaround to real-world players unless the playback is limited to a Chromium-based engine.<\/p>\n\n\n\n<p>If you have any question regarding our DASH activities or dash.js in particular, feel free to check out our&nbsp;<a href=\"https:\/\/www.fokus.fraunhofer.de\/go\/dash\">website<\/a>&nbsp;and contact us.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction When I thought about how to introduce this blog post, the first thing that popped to my mind was to start with a warning. Something like \u201cthis blog post will be technical and focus on details that are only&#8230;<\/p>\n","protected":false},"author":2,"featured_media":1625,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3,4,2],"tags":[],"coauthors":[6],"class_list":["post-1600","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dash-js","category-media-source-extensions","category-mpeg-dash"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Being trapped in a gap with Big Buck Bunny - Video-Dev<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Being trapped in a gap with Big Buck Bunny - Video-Dev\" \/>\n<meta property=\"og:description\" content=\"Introduction When I thought about how to introduce this blog post, the first thing that popped to my mind was to start with a warning. Something like \u201cthis blog post will be technical and focus on details that are only...\" \/>\n<meta property=\"og:url\" content=\"https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/\" \/>\n<meta property=\"og:site_name\" content=\"Video-Dev\" \/>\n<meta property=\"article:published_time\" content=\"2023-10-31T14:56:57+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-11-09T08:10:05+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/bbb-2-698x425.png\" \/>\n\t<meta property=\"og:image:width\" content=\"698\" \/>\n\t<meta property=\"og:image:height\" content=\"425\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Daniel Silhavy\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@dsilhavy\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Daniel Silhavy\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"11 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/being-trapped-in-a-gap-with-big-buck-bunny\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/being-trapped-in-a-gap-with-big-buck-bunny\\\/\"},\"author\":{\"name\":\"Daniel Silhavy\",\"@id\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/#\\\/schema\\\/person\\\/f7e1eee3cb4eae87a59648195014a809\"},\"headline\":\"Being trapped in a gap with Big Buck Bunny\",\"datePublished\":\"2023-10-31T14:56:57+00:00\",\"dateModified\":\"2023-11-09T08:10:05+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/being-trapped-in-a-gap-with-big-buck-bunny\\\/\"},\"wordCount\":1944,\"commentCount\":0,\"image\":{\"@id\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/being-trapped-in-a-gap-with-big-buck-bunny\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/bbb-2.png\",\"articleSection\":[\"dash.js\",\"Media Source Extensions\",\"MPEG-DASH\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/being-trapped-in-a-gap-with-big-buck-bunny\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/being-trapped-in-a-gap-with-big-buck-bunny\\\/\",\"url\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/being-trapped-in-a-gap-with-big-buck-bunny\\\/\",\"name\":\"Being trapped in a gap with Big Buck Bunny - Video-Dev\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/being-trapped-in-a-gap-with-big-buck-bunny\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/being-trapped-in-a-gap-with-big-buck-bunny\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/bbb-2.png\",\"datePublished\":\"2023-10-31T14:56:57+00:00\",\"dateModified\":\"2023-11-09T08:10:05+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/#\\\/schema\\\/person\\\/f7e1eee3cb4eae87a59648195014a809\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/being-trapped-in-a-gap-with-big-buck-bunny\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/being-trapped-in-a-gap-with-big-buck-bunny\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/being-trapped-in-a-gap-with-big-buck-bunny\\\/#primaryimage\",\"url\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/bbb-2.png\",\"contentUrl\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/bbb-2.png\",\"width\":2132,\"height\":1298},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/being-trapped-in-a-gap-with-big-buck-bunny\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Being trapped in a gap with Big Buck Bunny\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/#website\",\"url\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/\",\"name\":\"Video-Dev\",\"description\":\"Future Applications and Media - Video Development Blog\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/#\\\/schema\\\/person\\\/f7e1eee3cb4eae87a59648195014a809\",\"name\":\"Daniel Silhavy\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/wp-content\\\/uploads\\\/2019\\\/11\\\/0-1-150x150.jpegccb13c1b60303228bf3c575f3345fe29\",\"url\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/wp-content\\\/uploads\\\/2019\\\/11\\\/0-1-150x150.jpeg\",\"contentUrl\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/wp-content\\\/uploads\\\/2019\\\/11\\\/0-1-150x150.jpeg\",\"caption\":\"Daniel Silhavy\"},\"description\":\"Daniel Silhavy studied Computer Science at the Technical University of Berlin (TUB). He received his Masters degree with the completion of his thesis \u201cAd Insertion in MPEG-DASH\u201d at Fraunhofer Institute for Open Communication Systems (FOKUS) in 2015. Currently, he is employed as a scientific assistant and project manager at the Business Unit Future Applications and Media (FAME). He specializes in the R&amp;D of topics dealing with IPTV and adaptive media streaming.\",\"sameAs\":[\"https:\\\/\\\/www.fokus.fraunhofer.de\\\/fame\\\/team\\\/silhavy\",\"https:\\\/\\\/www.linkedin.com\\\/in\\\/daniel-silhavy-21650a129\\\/\",\"https:\\\/\\\/x.com\\\/dsilhavy\"],\"url\":\"https:\\\/\\\/websites.fraunhofer.de\\\/video-dev\\\/author\\\/silhavy\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Being trapped in a gap with Big Buck Bunny - Video-Dev","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/","og_locale":"en_US","og_type":"article","og_title":"Being trapped in a gap with Big Buck Bunny - Video-Dev","og_description":"Introduction When I thought about how to introduce this blog post, the first thing that popped to my mind was to start with a warning. Something like \u201cthis blog post will be technical and focus on details that are only...","og_url":"https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/","og_site_name":"Video-Dev","article_published_time":"2023-10-31T14:56:57+00:00","article_modified_time":"2023-11-09T08:10:05+00:00","og_image":[{"width":698,"height":425,"url":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/bbb-2-698x425.png","type":"image\/png"}],"author":"Daniel Silhavy","twitter_card":"summary_large_image","twitter_creator":"@dsilhavy","twitter_misc":{"Written by":"Daniel Silhavy","Est. reading time":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/#article","isPartOf":{"@id":"https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/"},"author":{"name":"Daniel Silhavy","@id":"https:\/\/websites.fraunhofer.de\/video-dev\/#\/schema\/person\/f7e1eee3cb4eae87a59648195014a809"},"headline":"Being trapped in a gap with Big Buck Bunny","datePublished":"2023-10-31T14:56:57+00:00","dateModified":"2023-11-09T08:10:05+00:00","mainEntityOfPage":{"@id":"https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/"},"wordCount":1944,"commentCount":0,"image":{"@id":"https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/#primaryimage"},"thumbnailUrl":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/bbb-2.png","articleSection":["dash.js","Media Source Extensions","MPEG-DASH"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/","url":"https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/","name":"Being trapped in a gap with Big Buck Bunny - Video-Dev","isPartOf":{"@id":"https:\/\/websites.fraunhofer.de\/video-dev\/#website"},"primaryImageOfPage":{"@id":"https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/#primaryimage"},"image":{"@id":"https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/#primaryimage"},"thumbnailUrl":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/bbb-2.png","datePublished":"2023-10-31T14:56:57+00:00","dateModified":"2023-11-09T08:10:05+00:00","author":{"@id":"https:\/\/websites.fraunhofer.de\/video-dev\/#\/schema\/person\/f7e1eee3cb4eae87a59648195014a809"},"breadcrumb":{"@id":"https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/#primaryimage","url":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/bbb-2.png","contentUrl":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2023\/10\/bbb-2.png","width":2132,"height":1298},{"@type":"BreadcrumbList","@id":"https:\/\/websites.fraunhofer.de\/video-dev\/being-trapped-in-a-gap-with-big-buck-bunny\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/websites.fraunhofer.de\/video-dev\/"},{"@type":"ListItem","position":2,"name":"Being trapped in a gap with Big Buck Bunny"}]},{"@type":"WebSite","@id":"https:\/\/websites.fraunhofer.de\/video-dev\/#website","url":"https:\/\/websites.fraunhofer.de\/video-dev\/","name":"Video-Dev","description":"Future Applications and Media - Video Development Blog","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/websites.fraunhofer.de\/video-dev\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/websites.fraunhofer.de\/video-dev\/#\/schema\/person\/f7e1eee3cb4eae87a59648195014a809","name":"Daniel Silhavy","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2019\/11\/0-1-150x150.jpegccb13c1b60303228bf3c575f3345fe29","url":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2019\/11\/0-1-150x150.jpeg","contentUrl":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-content\/uploads\/2019\/11\/0-1-150x150.jpeg","caption":"Daniel Silhavy"},"description":"Daniel Silhavy studied Computer Science at the Technical University of Berlin (TUB). He received his Masters degree with the completion of his thesis \u201cAd Insertion in MPEG-DASH\u201d at Fraunhofer Institute for Open Communication Systems (FOKUS) in 2015. Currently, he is employed as a scientific assistant and project manager at the Business Unit Future Applications and Media (FAME). He specializes in the R&amp;D of topics dealing with IPTV and adaptive media streaming.","sameAs":["https:\/\/www.fokus.fraunhofer.de\/fame\/team\/silhavy","https:\/\/www.linkedin.com\/in\/daniel-silhavy-21650a129\/","https:\/\/x.com\/dsilhavy"],"url":"https:\/\/websites.fraunhofer.de\/video-dev\/author\/silhavy\/"}]}},"_links":{"self":[{"href":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-json\/wp\/v2\/posts\/1600","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-json\/wp\/v2\/comments?post=1600"}],"version-history":[{"count":29,"href":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-json\/wp\/v2\/posts\/1600\/revisions"}],"predecessor-version":[{"id":1642,"href":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-json\/wp\/v2\/posts\/1600\/revisions\/1642"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-json\/wp\/v2\/media\/1625"}],"wp:attachment":[{"href":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-json\/wp\/v2\/media?parent=1600"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-json\/wp\/v2\/categories?post=1600"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-json\/wp\/v2\/tags?post=1600"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/websites.fraunhofer.de\/video-dev\/wp-json\/wp\/v2\/coauthors?post=1600"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}