{"id":428,"date":"2024-05-16T18:20:28","date_gmt":"2024-05-16T18:20:28","guid":{"rendered":"https:\/\/blog.mozilla.org\/webrtc\/?p=428"},"modified":"2024-06-26T14:04:35","modified_gmt":"2024-06-26T14:04:35","slug":"cross-browser-support-for-choosing-webrtc-codecs","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/webrtc\/cross-browser-support-for-choosing-webrtc-codecs\/","title":{"rendered":"setCodecPreferences is now in all browsers!"},"content":{"rendered":"<p>WebRTC enables video calls directly in browsers. Effectively managing the codecs that encode and decode media streams is a crucial component of delivering high-quality audio and video. The <a href=\"https:\/\/www.w3.org\/TR\/webrtc\/#dom-rtcrtptransceiver-setcodecpreferences\"><code>setCodecPreferences<\/code><\/a> method allows developers to specify which codecs their applications prefer to receive. <\/p>\n<p>With Firefox Nightly 128, <code>setCodecPreferences<\/code> is <a href=\"https:\/\/groups.google.com\/a\/mozilla.org\/g\/dev-platform\/c\/nhKKb7obWU8\">now available<\/a> in all browsers! Let&#8217;s explore how it functions, its implications for web developers, and its role in harmonizing browser behaviors.<\/p>\n<p>Firefox supporting this WebRTC 1.0 feature is a big deal. It helps webpages work the same across all browsers, eliminating browser-conditional code or having to resort to munging WebRTC&#8217;s signaling messages written in <a href=\"https:\/\/en.wikipedia.org\/wiki\/Session_Description_Protocol\">SDP<\/a>, by hand.<\/p>\n<p>But what made it seem blog-worthy was this question: do people know how it works? Looking at <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/RTCRtpTransceiver\/setCodecPreferences\">MDN<\/a> and <a href=\"https:\/\/github.com\/w3c\/webrtc-pc\/pulls?q=is%3Apr+setCodecPreferences\">recent spec changes<\/a> suggested not. Contrary to popular belief, it doesn&#8217;t control what you send. At least not directly.<br \/>\n<!--more--><\/p>\n<h2>How does setCodecPreferences work?<\/h2>\n<p><code>setCodecPreferences<\/code> is a method on a WebRTC transceiver. It lets webpages sort and filter the decoders to be considered when negotiating with a peer over a connection. The following illustrates a baseline for usage e.g. on a video transceiver:<\/p>\n<pre class=\"javascript\">\r\n<code>transceiver.setCodecPreferences(RTCRtpReceiver.getCapabilities(\"video\").codecs)<\/code>\r\n<\/pre>\n<p>This sets the browser&#8217;s whole array of available decoders unmodified. Perhaps surprisingly, this is <em>not<\/em> a no-op. It restricts the browser to consider all its codecs in this specific order. This technically differs from the default <code>transceiver.setCodecPreferences([])<\/code> which doesn&#8217;t restrict the browser in this way. E.g. a browser may choose to leave out some codecs by default.<\/p>\n<p>It&#8217;s also an imperfect API for other reasons. The <code>codecs<\/code> array contains a mix of real decoders (often repeated with different profile levels) along with special entries for features called RTX, RED and ULPFEC (thanks to <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc8829#name-setcodecpreferences\">JSEP<\/a>).<\/p>\n<p>As such, it&#8217;s not advisable to brute-force your own fixed array e.g. <code>setCodecPreferences([h265, h264])<\/code>, as this would inadvertently disable RTX, RED and ULPFEC. Instead, we&#8217;ll demonstrate best practice: carefully sort the array, using a <code>sortByMimeTypes(codecs, preferredOrder)<\/code> function below.<\/p>\n<h2>setCodecPreferences sets what encodings you prefer to <em>receive<\/em><\/h2>\n<p>The important part to remember is that <a href=\"https:\/\/www.w3.org\/TR\/webrtc\/#dom-rtcrtptransceiver-setcodecpreferences\"><code>setCodecPreferences<\/code><\/a> sets what media encodings you prefer to <em>receive<\/em>, not what you want to send. Take this example:<\/p>\n<p><iframe loading=\"lazy\" src=\"https:\/\/jsfiddle.net\/jib1\/2yf1nov0\/38\/embedded\/js,result,html\/\" allow=\"camera\" width=\"100%\" height=\"640px\" frameborder=\"0\"><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><\/iframe><\/p>\n<p>If you click the &#8220;Result&#8221; tab, you should see more or less the following across browsers (use Firefox 128+):<\/p>\n<pre class=\"javascript\">pc1 prefers video\/H264,video\/VP8,video\/VP9,video\/ulpfec,video\/red,video\/rtx\r\npc2 prefers video\/VP9,video\/VP8,video\/H264,video\/ulpfec,video\/red,video\/rtx\r\nchecking\r\nconnected\r\npc1 is sending video\/VP9\r\npc2 is sending video\/H264<\/pre>\n<p>You&#8217;ll see pc1 sending VP9 because that&#8217;s what pc2 prefers to receive, and pc2 sending H.264 because that&#8217;s what pc1 prefers to receive. In other words, <code>setCodecPreferences<\/code> sets preferences for <em>reception<\/em>.<\/p>\n<h2>Does setCodecPreferences configure a codec for sending?<\/h2>\n<p>It does that too sometimes, sort of, implicitly. To understand why, it helps to appreciate how <code>setCodecPreferences<\/code> is rooted in the offer\/answer exchange, where what encoder a sender uses is the result of SDP negotiation with its peer.<\/p>\n<p>The simplest way to see this is to consider what happens when <em>only<\/em> <code>pc1<\/code> expresses a preference:<\/p>\n<p><iframe loading=\"lazy\" src=\"https:\/\/jsfiddle.net\/jib1\/uv2p395r\/12\/embedded\/js,result,html\/\" allow=\"camera\" width=\"100%\" height=\"330px\" frameborder=\"0\"><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><\/iframe><\/p>\n<p>Now, if you click the &#8220;Result&#8221; tab, you&#8217;ll see <code>pc1<\/code>&#8216;s preference controlling what <em>both<\/em> peers send:<\/p>\n<pre class=\"javascript\">pc1 prefers video\/H264,video\/VP8,video\/VP9,video\/ulpfec,video\/red,video\/rtx\r\npc2 has no preference\r\nchecking\r\nconnected\r\npc1 is sending video\/H264\r\npc2 is sending video\/H264<\/pre>\n<p>Essentially, the JSEP offer\/answer exchange negotiates what to use with the peer. The devil is in the details, but if only the offerer (<code>pc1<\/code>) has a preference then it simplifies the negotiation quite a bit.<\/p>\n<p>Especially when the transceiver&#8217;s direction is <code>\"sendonly\"<\/code>, this makes it appear like <code>pc1<\/code>&#8216;s preference is controlling what it sends, because it is, implicitly (provided the peer has no preference).<\/p>\n<p>Since a transceiver&#8217;s direction can be renegotiated over time, having codec negotiation work (mostly) independently of direction is a nice invariant (even if JSEP sometimes gets in the way, more on that below).<\/p>\n<h2>Looking at setCodecPreferences as a sender-control leads to surprises<\/h2>\n<p>Unfortunately, the <code>pc1<\/code> preference trick above to control both sides doesn&#8217;t work on <code>pc2<\/code>:<\/p>\n<p><iframe loading=\"lazy\" src=\"https:\/\/jsfiddle.net\/jib1\/g1mvx9hs\/8\/embedded\/js,result,html\/\" allow=\"camera\" width=\"100%\" height=\"410px\" frameborder=\"0\"><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><\/iframe><\/p>\n<p>Click the &#8220;Result&#8221; tab, you&#8217;ll see <code>pc2<\/code>&#8216;s preference only controls what it receives:<\/p>\n<pre class=\"javascript\">pc1 has no preference\r\npc2 prefers video\/VP9,video\/VP8,video\/H264,video\/ulpfec,video\/red,video\/rtx\r\nchecking\r\nconnected\r\npc1 is sending video\/VP9\r\npc2 is sending video\/VP8<\/code><\/pre>\n<p>This is because SDP negotiation is inherently asymmetric: <code>pc1<\/code> is the offerer and <code>pc2<\/code> is the answerer. By the time pc2&#8217;s call to <code>setCodecPreferences<\/code> is considered the offer has already happened.<\/p>\n<p>This is a case where reversing roles would yield different negotiation results, so it&#8217;s good to be aware of it. <\/p>\n<p>But this anomaly is a result of our expectation to control sending. If we shift our mental model to <code>setCodecPreferences<\/code> controlling reception, then it should hopefully yield fewer surprises.<\/p>\n<h2>Live updates and the <code>negotiationneeded<\/code> event<\/h2>\n<p><code>setCodecPreferences<\/code> can also be used to make runtime changes mid-stream. Below we see <code>pc1<\/code>&#8216;s preferences controlling both sides again, except now with a drop-down picker letting us make live changes:<\/p>\n<p><iframe loading=\"lazy\" src=\"https:\/\/jsfiddle.net\/jib1\/8mosy476\/15\/embedded\/result,js,html\/\" allow=\"camera\" width=\"100%\" height=\"360px\" frameborder=\"0\"><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><\/iframe><\/p>\n<p>Clicking the &#8220;Start!&#8221; button, you should be able to use the drop-down and switch codecs live. But behind the scenes, renegotiation needs to happen for these changes to take effect. It&#8217;s perhaps surprising that (as of this writing) calling <code>setCodecPreferences()<\/code> does <em><strong>not<\/strong><\/em> trigger the <code>negotiationneeded<\/code> event! I&#8217;ve raised <a href=\"https:\/\/github.com\/w3c\/webrtc-pc\/issues\/2964\">an issue with the spec<\/a> about this. Hopefully this can be fixed, but that&#8217;s going to take some time, so in the meantime the fiddle works around this by negotiating explicitly.<\/p>\n<p>To instead change encoder <em>without<\/em> negotiation (choosing between already negotiated codecs), look for a future blog post on setting <a href=\"https:\/\/w3c.github.io\/webrtc-extensions\/#dom-rtcrtpencodingparameters-codec\"><code>codec<\/code><\/a> with <code>setParameters<\/code>.<\/p>\n<h2>When a browser can decode what it cannot encode<\/h2>\n<p>Some <a href=\"https:\/\/github.com\/aboba\/hevc-webrtc\/issues\/22\">concerns were raised<\/a> on the spec recently about decode-only codecs. For example, Chrome might initially only be able to decode H.265 but not encode it, whereas Safari might both encode and decode it.<\/p>\n<p>It&#8217;s a limitation of JSEP that there is no way to indicate per-codec directions in SDP negotiation (where codecs apply to the whole m-line). As a result, some of the spec language, if taken literally, would have browsers exclude decode-only codecs from their sendonly and sendrecv transceivers.<\/p>\n<p>But current thinking based on what <a href=\"https:\/\/github.com\/aboba\/hevc-webrtc\/issues\/22#issuecomment-1975793872\">Justin Uberti suggested<\/a> is that this is too strict of a read. Instead, the leading idea is Chrome could offer (to receive) H.265 with H.264 as a fallback. This way Chrome can negotiate successfully with Safari to have Safari send it H.265 while it sends H.264 back.<\/p>\n<p>This seems like a good solution that wouldn&#8217;t require any changes, and it aligns well with  an understanding of <code>setCodecPreferences<\/code> as a means to control reception. In short, the offerer includes a fallback codec it can send with. This is another good reason to never truncate the array when using this API. It largely avoids this problem.<\/p>\n<h2>Conclusion<\/h2>\n<p>Hopefully, this post has helped clarify how <code>setCodecPreferences<\/code> works. While the method primarily dictates the codecs your application prefers to receive, it also indirectly influences what is sent during peer-to-peer exchanges. This dual role in the negotiation process is helpful to understand for effective use of the API. The method is one of only two on the <a href=\"https:\/\/w3c.github.io\/webrtc-pc\/#idl-def-rtcrtptransceiver\"><code>RTCRtpTransceiver<\/code><\/a>, an interface designed to simplify complex underpinnings. As you integrate this feature into your projects, you\u2019ll likely appreciate both its capabilities and its challenges. I\u2019d love to hear about your experiences. There\u2019s no comments section here unfortunately, but feel free to reach out on <a href=\"https:\/\/twitter.com\/jibrewery\/status\/1791201770623881400\">X<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"WebRTC enables video calls directly in browsers. Effectively managing the codecs that encode and decode media streams is a crucial component of delivering high-quality audio and video. The setCodecPreferences method allows developers to specify which codecs their applications prefer to receive. With Firefox Nightly 128, setCodecPreferences is now available in all browsers! Let&#8217;s explore how [&hellip;]","protected":false},"author":1399,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"coauthors":[301098],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v22.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>setCodecPreferences is now in all browsers! - Advancing WebRTC<\/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:\/\/blog.mozilla.org\/webrtc\/cross-browser-support-for-choosing-webrtc-codecs\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"setCodecPreferences is now in all browsers! - Advancing WebRTC\" \/>\n<meta property=\"og:description\" content=\"WebRTC enables video calls directly in browsers. Effectively managing the codecs that encode and decode media streams is a crucial component of delivering high-quality audio and video. The setCodecPreferences method allows developers to specify which codecs their applications prefer to receive. With Firefox Nightly 128, setCodecPreferences is now available in all browsers! Let&#8217;s explore how [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blog.mozilla.org\/webrtc\/cross-browser-support-for-choosing-webrtc-codecs\/\" \/>\n<meta property=\"og:site_name\" content=\"Advancing WebRTC\" \/>\n<meta property=\"article:published_time\" content=\"2024-05-16T18:20:28+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-06-26T14:04:35+00:00\" \/>\n<meta name=\"author\" content=\"Jan-Ivar Bruaroey\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jan-Ivar Bruaroey\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/cross-browser-support-for-choosing-webrtc-codecs\/\",\"url\":\"https:\/\/blog.mozilla.org\/webrtc\/cross-browser-support-for-choosing-webrtc-codecs\/\",\"name\":\"setCodecPreferences is now in all browsers! - Advancing WebRTC\",\"isPartOf\":{\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/#website\"},\"datePublished\":\"2024-05-16T18:20:28+00:00\",\"dateModified\":\"2024-06-26T14:04:35+00:00\",\"author\":{\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/#\/schema\/person\/f2eb9712b8d85b70aebe1faf24e731fd\"},\"breadcrumb\":{\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/cross-browser-support-for-choosing-webrtc-codecs\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/blog.mozilla.org\/webrtc\/cross-browser-support-for-choosing-webrtc-codecs\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/cross-browser-support-for-choosing-webrtc-codecs\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/blog.mozilla.org\/webrtc\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"setCodecPreferences is now in all browsers!\"}]},{\"@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\/f2eb9712b8d85b70aebe1faf24e731fd\",\"name\":\"Jan-Ivar Bruaroey\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/#\/schema\/person\/image\/5f3d49a61b032619d0d33c4cc7c7433f\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/16d7e05dc9f8a855a02e0796b00aad3f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/16d7e05dc9f8a855a02e0796b00aad3f?s=96&d=mm&r=g\",\"caption\":\"Jan-Ivar Bruaroey\"},\"url\":\"https:\/\/blog.mozilla.org\/webrtc\/author\/jbruaroeymozilla-com\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"setCodecPreferences is now in all browsers! - Advancing WebRTC","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\/cross-browser-support-for-choosing-webrtc-codecs\/","og_locale":"en_US","og_type":"article","og_title":"setCodecPreferences is now in all browsers! - Advancing WebRTC","og_description":"WebRTC enables video calls directly in browsers. Effectively managing the codecs that encode and decode media streams is a crucial component of delivering high-quality audio and video. The setCodecPreferences method allows developers to specify which codecs their applications prefer to receive. With Firefox Nightly 128, setCodecPreferences is now available in all browsers! Let&#8217;s explore how [&hellip;]","og_url":"https:\/\/blog.mozilla.org\/webrtc\/cross-browser-support-for-choosing-webrtc-codecs\/","og_site_name":"Advancing WebRTC","article_published_time":"2024-05-16T18:20:28+00:00","article_modified_time":"2024-06-26T14:04:35+00:00","author":"Jan-Ivar Bruaroey","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Jan-Ivar Bruaroey","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/blog.mozilla.org\/webrtc\/cross-browser-support-for-choosing-webrtc-codecs\/","url":"https:\/\/blog.mozilla.org\/webrtc\/cross-browser-support-for-choosing-webrtc-codecs\/","name":"setCodecPreferences is now in all browsers! - Advancing WebRTC","isPartOf":{"@id":"https:\/\/blog.mozilla.org\/webrtc\/#website"},"datePublished":"2024-05-16T18:20:28+00:00","dateModified":"2024-06-26T14:04:35+00:00","author":{"@id":"https:\/\/blog.mozilla.org\/webrtc\/#\/schema\/person\/f2eb9712b8d85b70aebe1faf24e731fd"},"breadcrumb":{"@id":"https:\/\/blog.mozilla.org\/webrtc\/cross-browser-support-for-choosing-webrtc-codecs\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.mozilla.org\/webrtc\/cross-browser-support-for-choosing-webrtc-codecs\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/blog.mozilla.org\/webrtc\/cross-browser-support-for-choosing-webrtc-codecs\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blog.mozilla.org\/webrtc\/"},{"@type":"ListItem","position":2,"name":"setCodecPreferences is now in all browsers!"}]},{"@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\/f2eb9712b8d85b70aebe1faf24e731fd","name":"Jan-Ivar Bruaroey","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blog.mozilla.org\/webrtc\/#\/schema\/person\/image\/5f3d49a61b032619d0d33c4cc7c7433f","url":"https:\/\/secure.gravatar.com\/avatar\/16d7e05dc9f8a855a02e0796b00aad3f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/16d7e05dc9f8a855a02e0796b00aad3f?s=96&d=mm&r=g","caption":"Jan-Ivar Bruaroey"},"url":"https:\/\/blog.mozilla.org\/webrtc\/author\/jbruaroeymozilla-com\/"}]}},"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/posts\/428"}],"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\/1399"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/comments?post=428"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/posts\/428\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/media?parent=428"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/categories?post=428"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/tags?post=428"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/coauthors?post=428"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}