{"id":131,"date":"2016-11-02T02:20:38","date_gmt":"2016-11-02T02:20:38","guid":{"rendered":"https:\/\/testmozilla.wpengine.com\/webrtc\/?p=131"},"modified":"2016-11-16T11:43:57","modified_gmt":"2016-11-16T11:43:57","slug":"accomodate-cloning","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/webrtc\/accomodate-cloning\/","title":{"rendered":"Changing Firefox MediaStreams to accommodate cloning"},"content":{"rendered":"<p>This article is a re-post of one that I wrote on the <a href=\"http:\/\/blog.telenor.io\/webrtc\/2016\/04\/29\/webrtc-cloning-in-firefox.html\">Telenor Blog<\/a> earlier this year. It explains how <a href=\"http:\/\/w3c.github.io\/mediacapture-main\/getusermedia.html#mediastream\">MediaStreams<\/a> work in Firefox and the changes I did to them to accommodate cloning.<\/p>\n<p>First of all. What is a <a href=\"http:\/\/w3c.github.io\/mediacapture-main\/getusermedia.html#mediastreamtrack\">MediaStreamTrack<\/a>, and how can you clone it?<\/p>\n<p>A MediaStreamTrack represents a realtime stream of audio or video data.<\/p>\n<p>It provides a common API to the multiple producers (<a href=\"http:\/\/w3c.github.io\/mediacapture-main\/getusermedia.html#dom-mediadevices-getusermedia\">getUserMedia<\/a>, <a href=\"https:\/\/webaudio.github.io\/web-audio-api\/\">WebAudio<\/a>, <a href=\"https:\/\/html.spec.whatwg.org\/multipage\/scripting.html#the-canvas-element\">Canvas<\/a>, etc.) and consumers (<a href=\"http:\/\/w3c.github.io\/webrtc-pc\/\">WebRTC<\/a>, WebAudio, <a href=\"http:\/\/w3c.github.io\/mediacapture-record\/MediaRecorder.html\">MediaRecorder<\/a>, etc.) of MediaStreamTracks.<\/p>\n<p>A MediaStream is simply put a grouping of MediaStreamTracks. A MediaStream also ensures that all tracks contained in it stay synchronized to each other, for instance when it gets played out in a <a href=\"https:\/\/html.spec.whatwg.org\/multipage\/embedded-content.html#media-elements\">media element<\/a>.<\/p>\n<p>Cloning a track means that you get a new MediaStreamTrack instance representing the same data as the original, but where the identifier is unique (consumers don\u2019t know it\u2019s a clone) and disabling and stopping works independently across the original and all its clones.<\/p>\n<p>Now, how does all this come together in Firefox? <!--more--><\/p>\n<hr \/>\n<h3 id=\"my-background\">My Background<\/h3>\n<p>For the last year and a half, I have been representing Telenor in the <a href=\"https:\/\/blog.mozilla.org\/blog\/2014\/12\/09\/mozilla-and-telenor-announce-webrtc-competency-center-to-advance-webrtc-and-help-standardization\/\">WebRTC Competency Center that Mozilla announced back in 2014<\/a>. The WebRTC Competency Center is a project where we as participants work together with Mozilla on driving the Firefox WebRTC stack and the relevant standards forward.<\/p>\n<p>Most of my work so far in this context has been on features and bug fixes in and around the MediaStream and MediaStreamTrack implementation. For instance I fixed <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1081819\">transmitting WebAudio tracks over RTCPeerConnections<\/a>, <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=992685\">the \u201cresize\u201d event for media elements<\/a>, <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1032848\">capturing a canvas to a MediaStream<\/a>, <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1070216\">MediaStream constructors<\/a>, <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1103188\"><code class=\"highlighter-rouge\">MediaStream.addTrack()<\/code> and <code class=\"highlighter-rouge\">removeTrack()<\/code><\/a> and <a href=\"https:\/\/bugzilla.mozilla.org\/buglist.cgi?emailtype1=substring&amp;emailassigned_to1=1&amp;email1=pehrsons&amp;resolution=FIXED&amp;query_format=advanced&amp;product=Core&amp;list_id=12986111\">many many bug fixes<\/a>.<\/p>\n<p>Recently I landed a huge rewrite &#8211; <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1208371\">105 patches in one go<\/a> &#8211; rerouting most of the main thread communications between streams and their sources and sinks, leading to finally enabling <a href=\"http:\/\/w3c.github.io\/mediacapture-main\/getusermedia.html#widl-MediaStream-clone-MediaStream\"><code class=\"highlighter-rouge\">MediaStream.clone()<\/code><\/a> and <a href=\"http:\/\/w3c.github.io\/mediacapture-main\/getusermedia.html#widl-MediaStreamTrack-clone-MediaStreamTrack\"><code class=\"highlighter-rouge\">MediaStreamTrack.clone()<\/code><\/a> in Firefox 48.<\/p>\n<h3 id=\"outline\">Outline<\/h3>\n<p>Since this article gets rather lengthy, here\u2019s an outline so that you can skip directly to the pieces that you find most interesting.<\/p>\n<ul>\n<li><a href=\"#tldr\">TL;DR What does this mean for me?<\/a><\/li>\n<li><a href=\"#msg-explained\">Explaining the MediaStreamGraph<\/a><\/li>\n<li><a href=\"#stream-centered\">The old stream-centered way<\/a><\/li>\n<li><a href=\"#msg-tracks\">MediaStreamGraph has been taught what tracks are<\/a><\/li>\n<li><a href=\"#track-sources\">We have MediaStreamTrackSources now<\/a><\/li>\n<li><a href=\"#security\">Security needs to be track-centered<\/a><\/li>\n<li><a href=\"#intermittents\">Fixing tricky intermittent bugs &#8211; and some perf issues<\/a><\/li>\n<\/ul>\n<h3><a name=\"tldr\"><\/a>TL;DR What does this mean for me?<\/h3>\n<p>Before you dive in, take a look at the jsfiddle below showing track cloning in action. The feature was just released as Firefox 48 became Developer Edition. Make sure you\u2019re on Developer Edition for the latest features!<\/p>\n<p>Once you\u2019ve gotten your gUM (short for getUserMedia()) camera feed going, you can click the \u201cClone it 100 times!\u201d button and if track cloning is supported by your browser, a second video should appear, playing back the 100th clone of the original VideoStreamTrack. The clone can now be disabled and stopped independently from the original.<\/p>\n<p>Try it yourself!<\/p>\n<iframe src=\"https:\/\/jsfiddle.net\/pehrsons\/tx4dfhcp\/embedded\/result,js,html\/\" width=\"100%\" height=\"300px\" frameborder=\"0\"><\/iframe>\n<h3 id=\"a-namemsg-explaineda-explaining-the-mediastreamgraph\"><a name=\"msg-explained\"><\/a> Explaining the MediaStreamGraph<\/h3>\n<p>Gecko\u2019s (the Firefox browser engine) MediaStream and MediaStreamTrack implementation largely has two parts: a main thread API &#8211; much of which is exposed to javascript, and an internal <a href=\"https:\/\/dxr.mozilla.org\/mozilla-central\/rev\/fc15477ce628599519cb0055f52cc195d640dc94\/dom\/media\/MediaStreamGraph.h#1444\">MediaStreamGraph<\/a> which processes media data on a background thread. The main thread APIs communicate with the MediaStreamGraph through message passing to achieve largely lock free inter-thread communication.<\/p>\n<p>The MediaStreamGraph is the central engine for MediaStreams in Gecko. It is easiest explained as a mapping of how all streams and tracks are connected to each other, and on every iteration (typically ~10ms but it depends on how often the operating system calls its audio callback) it goes through all tracks to ensure they contain data for the current time.<\/p>\n<p>There are three main types of internal streams used by the MediaStreamGraph:<\/p>\n<ul>\n<li><a href=\"https:\/\/dxr.mozilla.org\/mozilla-central\/rev\/55d557f4d73ee58664bdf2fa85aaab555224722e\/dom\/media\/MediaStreamGraph.h#722\">SourceMediaStream<\/a>\n<ul>\n<li>Raw data either gets pushed to the source stream by a producer or pulled in by the MediaStreamGraph.<\/li>\n<\/ul>\n<\/li>\n<li><a href=\"https:\/\/dxr.mozilla.org\/mozilla-central\/rev\/55d557f4d73ee58664bdf2fa85aaab555224722e\/dom\/media\/TrackUnionStream.h#17\">TrackUnionStream<\/a>\n<ul>\n<li>Consists of tracks coming from other SourceMediaStreams or TrackUnionStreams.<\/li>\n<\/ul>\n<\/li>\n<li><a href=\"https:\/\/dxr.mozilla.org\/mozilla-central\/rev\/55d557f4d73ee58664bdf2fa85aaab555224722e\/dom\/media\/webaudio\/AudioNodeStream.h#34\">AudioNodeStream<\/a>\n<ul>\n<li>A stream for a WebAudio node. Most WebAudio nodes are built on top of this stream. This is not used directly by a MediaStream but can be exposed as a TrackUnionStream through a special MediaStreamAudioDestinationNode.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>A regular MediaStream (the JS object) is backed by three internal streams in the MediaStreamGraph:<\/p>\n<ul>\n<li>A SourceMediaStream or a TrackUnionStream as <em>input<\/em> stream<\/li>\n<li>A TrackUnionStream for denoting tracks that are native to the MediaStream in question, the <em>owned<\/em> stream<\/li>\n<li>A TrackUnionStream where only the currently active tracks in the MediaStream are present, the <em>playback<\/em> stream<\/li>\n<\/ul>\n<p>What do I then mean by tracks that are <em>native to the MediaStream in question<\/em>?<\/p>\n<p>Consider a call like <code class=\"highlighter-rouge\">getUserMedia({ audio: true, video: true })<\/code>. It will result in a MediaStream with one AudioStreamTrack and one VideoStreamTrack. Both these tracks are native to this MediaStream. If I later <code class=\"highlighter-rouge\">removeTrack()<\/code> the audio track and <code class=\"highlighter-rouge\">addTrack()<\/code> another, external, track the original two tracks will still be native to the MediaStream, and the added track will not. This means that the original video track and the added track will be available in this MediaStream\u2019s internal playback stream.<\/p>\n<p>For a visual representation of this setup, consider the following ASCII drawings, <a href=\"https:\/\/dxr.mozilla.org\/mozilla-central\/rev\/fc15477ce628599519cb0055f52cc195d640dc94\/dom\/media\/DOMMediaStream.h#138\">straight from the codebase<\/a>. Never mind that they\u2019re called <em>DOMStream<\/em> in the drawings. That\u2019s a legacy naming convention in Gecko. They\u2019re regular MediaStreams.<\/p>\n<p>This is a simple case where we have done <code class=\"highlighter-rouge\">A.addTrack(B.getTracks([0]))<\/code>:<\/p>\n<div class=\"highlighter-rouge\">\n<pre class=\"highlight\"><code> DOMStream A\r\n           Input        Owned          Playback\r\n            t1 ---------&gt; t1 ------------&gt; t1     &lt;- MediaStreamTrack X\r\n                                                     (pointing to t1 in A)\r\n                                 --------&gt; t2     &lt;- MediaStreamTrack Y\r\n                                \/                    (pointing to t1 in B)\r\n DOMStream B                   \/\r\n           Input        Owned \/        Playback\r\n            t1 ---------&gt; t1 ------------&gt; t1     &lt;- MediaStreamTrack Y\r\n                                                     (pointing to t1 in B)\r\n<\/code><\/pre>\n<\/div>\n<p>Here another simple case where we remove the only track in a stream, like <code class=\"highlighter-rouge\">A.removeTrack(A.getTracks(0))<\/code>:<\/p>\n<div class=\"highlighter-rouge\">\n<pre class=\"highlight\"><code> DOMStream A\r\n           Input        Owned          Playback\r\n            t1 ---------&gt; t1                      &lt;- No tracks\r\n<\/code><\/pre>\n<\/div>\n<p>When cloning a track, you\u2019re supposed to get a completely different instance from the original. This instance has a new unique identifier and can be stopped and disabled independently from the original or any other clones of the same track (a clone of a clone is no different than a clone of an original). The way this is set up is by having a link from the original MediaStream\u2019s internal <em>input<\/em> stream, to the internal <em>owned<\/em> stream of the clone, with stopping and track disabling happening on that same link.<\/p>\n<p>Here it is visualized in its simplest form. <code class=\"highlighter-rouge\">B = A.clone()<\/code>:<\/p>\n<div class=\"highlighter-rouge\">\n<pre class=\"highlight\"><code> DOMStream A\r\n           Input        Owned          Playback\r\n            t1 ---------&gt; t1 ------------&gt; t1     &lt;- MediaStreamTrack X\r\n                                                    (pointing to t1 in A)\r\n                -----\r\n DOMStream B         \r\n           Input       Owned          Playback\r\n                       -&gt; t1 ------------&gt; t1     &lt;- MediaStreamTrack Y\r\n                                                     (pointing to t1 in B)\r\n<\/code><\/pre>\n<\/div>\n<p>Ok, all those were quite easy. To finish it off, I have one case where these methods have been combined like so:<\/p>\n<div class=\"highlighter-rouge\">\n<pre class=\"highlight\"><code> var A = someStreamWithTwoTracks;\r\n var B = someStreamWithOneTrack;\r\n var X = A.getTracks()[0];\r\n var Y = A.getTracks()[1];\r\n var Z = B.getTracks()[0];\r\n A.addTrack(Z);\r\n A.removeTrack(X);\r\n B.removeTrack(Z);\r\n var A' = A.clone();\r\n<\/code><\/pre>\n<\/div>\n<p>This results in the following graph:<\/p>\n<div class=\"highlighter-rouge\">\n<pre class=\"highlight\"><code> DOMStream A\r\n           Input        Owned          Playback\r\n            t1 ---------&gt; t1                      &lt;- MediaStreamTrack X (removed)\r\n                                                     (pointing to t1 in A)\r\n            t2 ---------&gt; t2 ------------&gt; t2     &lt;- MediaStreamTrack Y\r\n                                                    (pointing to t2 in A)\r\n                                  ------&gt; t3     &lt;- MediaStreamTrack Z\r\n                                 \/                  (pointing to t1 in B)\r\n DOMStream B                    \/\r\n           Input       Owned   \/      Playback\r\n            t1 ---^-----&gt; t1 ---                  &lt;- MediaStreamTrack Z (removed)\r\n                                                   (pointing to t1 in B)\r\n                   \r\n DOMStream A'       \r\n           Input      Owned          Playback\r\n                      -&gt; t1 ------------&gt; t1     &lt;- MediaStreamTrack Y'\r\n                                                    (pointing to t1 in A')\r\n                    ----&gt; t2 ------------&gt; t2     &lt;- MediaStreamTrack Z'\r\n                                                     (pointing to t2 in A')\r\n<\/code><\/pre>\n<\/div>\n<p>As simple as pie!<\/p>\n<h3 id=\"a-namestream-centereda-the-old-stream-centered-way\"><a name=\"stream-centered\"><\/a> The old stream-centered way<\/h3>\n<p>Until now, everything has been centered around streams, including the assumption that a stream contains at most one video track and one audio track. This works fine for basic getUserMedia streams, but doesn\u2019t cater for complicated cases where you want to combine multiple tracks from different sources, like screen capture, camera capture, WebAudio destination nodes, canvas and media element capturing.<\/p>\n<p>Other assumptions were:<\/p>\n<ul>\n<li>All tracks in a stream come from the same source. Security wise (<a href=\"https:\/\/www.w3.org\/TR\/cors\/\">cross-origin<\/a> access) we only have to care about that source. And it won\u2019t change throughout the lifetime of the stream.<\/li>\n<li>Any consumer of a stream (or track) has to use a <a href=\"https:\/\/dxr.mozilla.org\/mozilla-central\/rev\/fc15477ce628599519cb0055f52cc195d640dc94\/dom\/media\/MediaStreamGraph.h#103\">MediaStreamListener<\/a> (internal class) for data access, inadvertently getting notified about the activity of all tracks in the stream it is attached to. While this made sense for APIs that want all the tracks, it is now much more track centered, see for instance <a href=\"https:\/\/webaudio.github.io\/web-audio-api\/#MediaStreamAudioSourceNode\">MediaStreamAudioSourceNodes<\/a> and <a href=\"http:\/\/w3c.github.io\/webrtc-pc\/#rtcpeerconnection-interface\">RTCPeerConnections<\/a>. An example of an API that accepts streams is MediaRecorder, though its spec doesn\u2019t mention how to treat added and removed tracks much.<\/li>\n<\/ul>\n<h3 id=\"a-namemsg-tracksa-mediastreamgraph-has-been-taught-what-tracks-are\"><a name=\"msg-tracks\"><\/a> MediaStreamGraph has been taught what tracks are<\/h3>\n<p>As mentioned in <a href=\"#msg-explained\">Explaining the MediaStreamGraph<\/a> we have links between internal streams. These used to always forward all tracks that were live in the input stream. The MediaStreamGraph now has the ability to forward single tracks between streams. This basically makes tracks first class citizens in the stream graph.<\/p>\n<p>We also used to have a MediaStreamListener class for listening to changes and new data for a stream. We now have a <a href=\"https:\/\/dxr.mozilla.org\/mozilla-central\/rev\/fc15477ce628599519cb0055f52cc195d640dc94\/dom\/media\/MediaStreamGraph.h#300\">MediaStreamTrackListener<\/a> class that can listen to data represented by a single MediaStreamTrack. This could in essence be achieved before by using a MediaStreamListener on the internal <em>owned<\/em> stream while filtering on a track\u2019s TrackID (identifying a track in an internal stream) but now we can avoid those extra cycles by only raising events for the track in question.<\/p>\n<p>APIs that take tracks instead of streams can listen to only the tracks it needs and don\u2019t have to worry about those tracks being removed from their parent stream, clones, etc.<\/p>\n<h3 id=\"a-nametrack-sourcesa-we-have-mediastreamtracksources-now\"><a name=\"track-sources\"><\/a> We have MediaStreamTrackSources now<\/h3>\n<p>I implemented a general interface of a <a href=\"https:\/\/dxr.mozilla.org\/mozilla-central\/rev\/fc15477ce628599519cb0055f52cc195d640dc94\/dom\/media\/MediaStreamTrack.h#46\">MediaStreamTrackSource<\/a> through which all MediaStreamTrack instances can communicate with their respective sources. A track shares its MediaStreamTrackSource instance with all of its clones.<\/p>\n<p>Previously there was a special MediaStream sub-class for getUsermedia streams that allowed methods like <a href=\"http:\/\/w3c.github.io\/mediacapture-main\/getusermedia.html#widl-MediaStreamTrack-applyConstraints-Promise-void--MediaTrackConstraints-constraints\"><code class=\"highlighter-rouge\">applyConstraints()<\/code><\/a> to be called from a getUserMedia track up to the proper source. This would naturally not work if that track is contained in another MediaStream type (like the generic one, MediaStream). Now with MediaStreamTrackSources we have generic access to the source from all tracks, and they don\u2019t have to go through a MediaStream on the way. All sources simply have to implement applyConstraints in some way. Most sources ignore it since it doesn\u2019t apply, but for getUserMedia sources it gets applied appropriately.<\/p>\n<p>Similarly, we do the same forwarding to the source of calls like <a href=\"http:\/\/w3c.github.io\/mediacapture-main\/getusermedia.html#widl-MediaStreamTrack-stop-void\"><code class=\"highlighter-rouge\">stop()<\/code><\/a> (after all clones have been stopped), and various other internal methods.<\/p>\n<h3 id=\"a-namesecuritya-security-needs-to-be-track-centered\"><a name=\"security\"><\/a> Security needs to be track-centered<\/h3>\n<p>To fully understand this section, we need to understand what principals are.<\/p>\n<p>A principal is a representation of an origin, so we can check if a consumer is allowed access to a particular producer\u2019s data, by checking if the producer\u2019s principal <em>subsumes<\/em> the consumer\u2019s principal. It\u2019s also worth knowing that there are system principals &#8211; elevated principals used by for instance browser chrome code For more info on principals, see the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Mozilla\/Gecko\/Script_security#Security_checks\">script security page on Mozilla\u2019s developer network<\/a>.<\/p>\n<p>Being stream-centered like we used to, a stream would be tied to the <a href=\"https:\/\/tools.ietf.org\/html\/rfc6454#section-3.2\">origin<\/a> within which it was created. If you added a track from another origin to it we\u2019d combine the principal from the added track into the stream\u2019s current principal, upgrading it to the system principal if needed. If the same track was removed again, we wouldn\u2019t touch the principal. Should we have done so, we would probably have leaked real data because the track removal operation happens on main thread, and it would take a bounce of message passing to the MediaStreamGraph (and an iteration) and a bounce back to main thread, to actually get new media data to apply. During these two bounces the current data would be protected by a downgraded principal &#8211; hence we never downgraded it.<\/p>\n<p>What I have implemented now is a system where we send the principal (main thread only) from the track source to the MediaStreamGraph, which then notifies the MediaStreamTrack when a new principal has been applied. In summary this allows us to do the following:<\/p>\n<ul>\n<li>When the source of a MediaStreamTrack changes its principal, for instance a 2d canvas that we drew a cross-origin image to or a media element backed by a <a href=\"http:\/\/w3c.github.io\/media-source\/\">MediaSource<\/a> played a chunk from another origin, we\u2019d first combine the new principal with the old (could be an upgrade or a downgrade), and only when we have confirmation from the MediaStreamGraph that the new principal has been rendered, would we apply the new principal completely.<\/li>\n<li>On adding a track to a stream, we immediately combine the new track\u2019s principal into the stream\u2019s. We also keep around a second principal for the stream, which is solely for its video tracks &#8211; this for APIs that only want to access video content of the stream, for instance when you draw a media element playing a stream onto a canvas.<\/li>\n<li>On removing a track from a stream, we keep returning the old principal (we keep the removed track\u2019s principal in a set of <em>tracks pending removal<\/em>) until the MediaStreamGraph has confirmed that we have now rendered another principal.<\/li>\n<\/ul>\n<h3 id=\"a-nameintermittentsa-fixing-tricky-intermittent-bugs---and-some-perf-issues\"><a name=\"intermittents\"><\/a> Fixing tricky intermittent bugs &#8211; and some perf issues<\/h3>\n<p>All patches that cause major refactoring or changes in timing internally in the process tend to cause intermittent failures in automation.<\/p>\n<p>After all the things above had been implemented we noticed how some automated tests started timing out, especially on platforms that were running on virtual machines. This wouldn\u2019t be reproduced on a similar setup on a local virtual machine either, a characteristic many of these intermittents share. <a href=\"https:\/\/treeherder.mozilla.org\/#\/jobs?repo=try&amp;revision=1678e9b22fa0\">This particular performance issue<\/a> turned out to happen when sending a disabled screensharing-frame over an RTCPeerConnection. The fact that it was disabled was interesting and eventually pointed to two things:<\/p>\n<ul>Ftry<\/p>\n<li>Frames going to be sent over an RTCPeerConnection did image format conversion if needed on the MediaStreamGraph thread (stalling the MSG in the worst case).<\/li>\n<li>Disabled frames had a separate buffer allocated and written to each time they came through &#8211; also on the MediaStreamGraph thread.<\/li>\n<\/ul>\n<p>It was actually the latter case above that caused problems on this virtual machine. The allocation (screensharing frames tend to be high resolution!) took longer than a MediaStreamGraph iteration had budgeted. This lead to a much longer queue of frames to process on the next iteration, and then longer, and then longer, until we ran out of memory.<\/p>\n<p>This was fixed by doing multiple things:<\/p>\n<ul>\n<li><a href=\"https:\/\/hg.mozilla.org\/mozilla-central\/rev\/b06d6ff27862\">Don\u2019t queue up frames on the input side if the MediaStreamGraph cannot keep up.<\/a><\/li>\n<li><a href=\"https:\/\/hg.mozilla.org\/mozilla-central\/rev\/469e29166c55\">Only pass on one black frame after disabling happens, so we don\u2019t do new allocations on each frame.<\/a><\/li>\n<li><a href=\"https:\/\/hg.mozilla.org\/mozilla-central\/rev\/da8d6c4eab61\">Send frames for image conversion onto a queue, processed by a separate thread &#8211; and drop frames if the separate thread is busy (&gt; 1 frame queued up).<\/a><\/li>\n<\/ul>\n<p>With that done, the intermittents were gone. Jolly good!<\/p>\n","protected":false},"excerpt":{"rendered":"This article is a re-post of one that I wrote on the Telenor Blog earlier this year. It explains how MediaStreams work in Firefox and the changes I did to them to accommodate cloning. First of all. What is a MediaStreamTrack, and how can you clone it? A MediaStreamTrack represents a realtime stream of audio [&hellip;]","protected":false},"author":1394,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[295336,297123,297416,297656,69,299276,265],"coauthors":[],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v22.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Changing Firefox MediaStreams to accommodate cloning - Advancing WebRTC<\/title>\n<meta name=\"description\" content=\"How MediaStreams and MediaStreamTracks work in Firefox and the changes done to them to accommodate cloning. Complete with fiddle and ASCII graphs.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blog.mozilla.org\/webrtc\/accomodate-cloning\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Changing Firefox MediaStreams to accommodate cloning - Advancing WebRTC\" \/>\n<meta property=\"og:description\" content=\"How MediaStreams and MediaStreamTracks work in Firefox and the changes done to them to accommodate cloning. Complete with fiddle and ASCII graphs.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blog.mozilla.org\/webrtc\/accomodate-cloning\/\" \/>\n<meta property=\"og:site_name\" content=\"Advancing WebRTC\" \/>\n<meta property=\"article:published_time\" content=\"2016-11-02T02:20:38+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2016-11-16T11:43:57+00:00\" \/>\n<meta name=\"author\" content=\"Andreas Pehrson\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@pehrsons\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andreas Pehrson\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/accomodate-cloning\/\",\"url\":\"https:\/\/blog.mozilla.org\/webrtc\/accomodate-cloning\/\",\"name\":\"Changing Firefox MediaStreams to accommodate cloning - Advancing WebRTC\",\"isPartOf\":{\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/#website\"},\"datePublished\":\"2016-11-02T02:20:38+00:00\",\"dateModified\":\"2016-11-16T11:43:57+00:00\",\"author\":{\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/#\/schema\/person\/f3d2446c047646258e2ab8b981794c8b\"},\"description\":\"How MediaStreams and MediaStreamTracks work in Firefox and the changes done to them to accommodate cloning. Complete with fiddle and ASCII graphs.\",\"breadcrumb\":{\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/accomodate-cloning\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/blog.mozilla.org\/webrtc\/accomodate-cloning\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/accomodate-cloning\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/blog.mozilla.org\/webrtc\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Changing Firefox MediaStreams to accommodate cloning\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/#website\",\"url\":\"https:\/\/blog.mozilla.org\/webrtc\/\",\"name\":\"Advancing WebRTC\",\"description\":\"Committed to moving Firefox and WebRTC forward\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/blog.mozilla.org\/webrtc\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/#\/schema\/person\/f3d2446c047646258e2ab8b981794c8b\",\"name\":\"Andreas Pehrson\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/#\/schema\/person\/image\/24b2dc6c4a53af37b8b692131049c86b\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d2c72377184282f094d989c757154f98?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d2c72377184282f094d989c757154f98?s=96&d=mm&r=g\",\"caption\":\"Andreas Pehrson\"},\"description\":\"Andreas was a software engineer at Telenor Digital 2013-2017.\",\"sameAs\":[\"https:\/\/x.com\/pehrsons\"],\"url\":\"https:\/\/blog.mozilla.org\/webrtc\/author\/pehrsontelenordigital-com\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Changing Firefox MediaStreams to accommodate cloning - Advancing WebRTC","description":"How MediaStreams and MediaStreamTracks work in Firefox and the changes done to them to accommodate cloning. Complete with fiddle and ASCII graphs.","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:\/\/blog.mozilla.org\/webrtc\/accomodate-cloning\/","og_locale":"en_US","og_type":"article","og_title":"Changing Firefox MediaStreams to accommodate cloning - Advancing WebRTC","og_description":"How MediaStreams and MediaStreamTracks work in Firefox and the changes done to them to accommodate cloning. Complete with fiddle and ASCII graphs.","og_url":"https:\/\/blog.mozilla.org\/webrtc\/accomodate-cloning\/","og_site_name":"Advancing WebRTC","article_published_time":"2016-11-02T02:20:38+00:00","article_modified_time":"2016-11-16T11:43:57+00:00","author":"Andreas Pehrson","twitter_card":"summary_large_image","twitter_creator":"@pehrsons","twitter_misc":{"Written by":"Andreas Pehrson","Est. reading time":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/blog.mozilla.org\/webrtc\/accomodate-cloning\/","url":"https:\/\/blog.mozilla.org\/webrtc\/accomodate-cloning\/","name":"Changing Firefox MediaStreams to accommodate cloning - Advancing WebRTC","isPartOf":{"@id":"https:\/\/blog.mozilla.org\/webrtc\/#website"},"datePublished":"2016-11-02T02:20:38+00:00","dateModified":"2016-11-16T11:43:57+00:00","author":{"@id":"https:\/\/blog.mozilla.org\/webrtc\/#\/schema\/person\/f3d2446c047646258e2ab8b981794c8b"},"description":"How MediaStreams and MediaStreamTracks work in Firefox and the changes done to them to accommodate cloning. Complete with fiddle and ASCII graphs.","breadcrumb":{"@id":"https:\/\/blog.mozilla.org\/webrtc\/accomodate-cloning\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.mozilla.org\/webrtc\/accomodate-cloning\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/blog.mozilla.org\/webrtc\/accomodate-cloning\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blog.mozilla.org\/webrtc\/"},{"@type":"ListItem","position":2,"name":"Changing Firefox MediaStreams to accommodate cloning"}]},{"@type":"WebSite","@id":"https:\/\/blog.mozilla.org\/webrtc\/#website","url":"https:\/\/blog.mozilla.org\/webrtc\/","name":"Advancing WebRTC","description":"Committed to moving Firefox and WebRTC forward","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blog.mozilla.org\/webrtc\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/blog.mozilla.org\/webrtc\/#\/schema\/person\/f3d2446c047646258e2ab8b981794c8b","name":"Andreas Pehrson","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blog.mozilla.org\/webrtc\/#\/schema\/person\/image\/24b2dc6c4a53af37b8b692131049c86b","url":"https:\/\/secure.gravatar.com\/avatar\/d2c72377184282f094d989c757154f98?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d2c72377184282f094d989c757154f98?s=96&d=mm&r=g","caption":"Andreas Pehrson"},"description":"Andreas was a software engineer at Telenor Digital 2013-2017.","sameAs":["https:\/\/x.com\/pehrsons"],"url":"https:\/\/blog.mozilla.org\/webrtc\/author\/pehrsontelenordigital-com\/"}]}},"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/posts\/131"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/users\/1394"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/comments?post=131"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/posts\/131\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/media?parent=131"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/categories?post=131"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/tags?post=131"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/coauthors?post=131"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}