<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>kyte codes</title><link>https://kyteinsky.github.io/</link><description>Recent content on kyte codes</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Tue, 25 Apr 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://kyteinsky.github.io/index.xml" rel="self" type="application/rss+xml"/><item><title>Todo Wallpaper App in Rust</title><link>https://kyteinsky.github.io/p/todo-wallpaper-app-in-rust/</link><pubDate>Tue, 25 Apr 2023 00:00:00 +0000</pubDate><guid>https://kyteinsky.github.io/p/todo-wallpaper-app-in-rust/</guid><description>&lt;img src="https://kyteinsky.github.io/p/todo-wallpaper-app-in-rust/desktop_thumbnail.png" alt="Featured image of post Todo Wallpaper App in Rust" />&lt;p>There is no concept of having desktop widgets/icons in GNOME. The extensions that implement this are for particular use cases like desktop icons for directories and files, showing the clock, etc. and not universal container format that can accomodate different types of widgets.&lt;br>
It is, in theory, possible to create universal containers for all kinds of widgets on the desktop but not now. I am as lazy as always.&lt;/p>
&lt;p>I forked the todo app by Tsoding for my own niche use case, took a page from their book (just kidding).&lt;br>
This app was a TUI that, as the name says, helped store todos. The TUI is fancy and pretty usable but did not present (or pretty print) all the todos on the terminal on startup. Again this is also a very easy task but why see it only upon opening the terminal and not always when you log in and are about to start your day?&lt;br>
The fork writes all your todos and dones on your wallpaper for you to see and get back to whatever you were working with.&lt;/p>
&lt;p>For now, only GNOME &amp;gt;= 40 is supported, more to follow soon.&lt;/p>
&lt;p>Find it here: &lt;a class="link" href="https://github.com/kyteinsky/todo-wall" target="_blank" rel="noopener"
>https://github.com/kyteinsky/todo-wall&lt;/a>&lt;/p>
&lt;p>&lt;figure style="flex-grow: 201; flex-basis: 483px">
&lt;a href="https://kyteinsky.github.io/p/todo-wallpaper-app-in-rust/desktop_thumbnail.png" data-size="1920x954">&lt;img src="https://kyteinsky.github.io/p/todo-wallpaper-app-in-rust/desktop_thumbnail.png"
srcset="https://kyteinsky.github.io/p/todo-wallpaper-app-in-rust/desktop_thumbnail_hu56c7d399285fd2114ee1a6bdcbeb784e_2949243_480x0_resize_box_3.png 480w, https://kyteinsky.github.io/p/todo-wallpaper-app-in-rust/desktop_thumbnail_hu56c7d399285fd2114ee1a6bdcbeb784e_2949243_1024x0_resize_box_3.png 1024w"
width="1920"
height="954"
loading="lazy"
alt="Desktop wallpaper with displayed todos">
&lt;/a>
&lt;figcaption>Desktop wallpaper with displayed todos&lt;/figcaption>
&lt;/figure>&lt;/p>
&lt;p>Disclaimer: This is alpha software made for learning, fun and personal usage. Use it on your own risk.&lt;/p></description></item><item><title>GNOME Asia Feels</title><link>https://kyteinsky.github.io/p/gnome-asia-feels/</link><pubDate>Tue, 13 Dec 2022 00:00:00 +0000</pubDate><guid>https://kyteinsky.github.io/p/gnome-asia-feels/</guid><description>&lt;img src="https://kyteinsky.github.io/p/gnome-asia-feels/2022GNOMEAsia-cover.jpg" alt="Featured image of post GNOME Asia Feels" />&lt;p>First and foremost I want to thank GNOME for sponsoring my visit to Kuala Lumpur, Malaysia.&lt;br>
It was nice meeting the people who have similar interests and also who actually build the GNOME I use and love.&lt;/p>
&lt;p>All of them absolutely love open source and the work they do. And most of all, they were all excited and happy to showcase their art. Those would be one of the most engaging communications I&amp;rsquo;ve had in quite a while.&lt;/p>
&lt;p>There were people from Singapore, Malaysia, India, Denmark, Hong Kong, Albania, Italy, and USA. I attended all that talks I could and apologize for the ones I missed owing to sleeping late in the morning due to my body clock. Malaysian time is 2h30m ahead of Indian time.&lt;br>
Still I enjoyed them a lot and hope that you all enjoyed my presentation as well.&lt;/p>
&lt;blockquote>
&lt;p>Speaking of which, some people asked me &amp;ldquo;what was the presentation actually about?&amp;rdquo; Yeah.&lt;br>
To squash further FAQs and doubts, here is a simple explanation: I made an addition/extension to an already capable and working app. All it does it cast your screen to a bigger screen if it supports Wi-Fi and isn&amp;rsquo;t Apple owned. My addition revolved around Google&amp;rsquo;s Chromecast, while the existing one was Miracast.&lt;br>
Simply put, remove the HDMI cable and just use Wi-Fi in your next presentation.&lt;/p>
&lt;/blockquote>
&lt;p>Here is a pic of me at the conference. Thanks to Jona Azizaj (@jonatoni) for the amazing shot!&lt;br>
&lt;figure style="flex-grow: 133; flex-basis: 320px">
&lt;a href="https://kyteinsky.github.io/p/gnome-asia-feels/pic.jpeg" data-size="2048x1536">&lt;img src="https://kyteinsky.github.io/p/gnome-asia-feels/pic.jpeg"
srcset="https://kyteinsky.github.io/p/gnome-asia-feels/pic_hu798fb2bdcd006031e77732306f994893_291082_480x0_resize_q75_box.jpeg 480w, https://kyteinsky.github.io/p/gnome-asia-feels/pic_hu798fb2bdcd006031e77732306f994893_291082_1024x0_resize_q75_box.jpeg 1024w"
width="2048"
height="1536"
loading="lazy"
alt="GNOME Asia Presentation">
&lt;/a>
&lt;figcaption>GNOME Asia Presentation&lt;/figcaption>
&lt;/figure>&lt;/p>
&lt;p>And not forgetting the main purpose of the whole trip.&lt;/p>
&lt;p>&lt;figure style="flex-grow: 133; flex-basis: 320px">
&lt;a href="https://kyteinsky.github.io/p/gnome-asia-feels/stickers.jpg" data-size="4624x3468">&lt;img src="https://kyteinsky.github.io/p/gnome-asia-feels/stickers.jpg"
srcset="https://kyteinsky.github.io/p/gnome-asia-feels/stickers_hu1d754baf2a7a4a0e8ebb7ac348a5902a_7225119_480x0_resize_q75_box.jpg 480w, https://kyteinsky.github.io/p/gnome-asia-feels/stickers_hu1d754baf2a7a4a0e8ebb7ac348a5902a_7225119_1024x0_resize_q75_box.jpg 1024w"
width="4624"
height="3468"
loading="lazy"
alt="White, Blue and Hearts">
&lt;/a>
&lt;figcaption>White, Blue and Hearts&lt;/figcaption>
&lt;/figure>&lt;/p>
&lt;p>Lastly I would have to loved to see my mentors Benjamin and Claudio at the summit. I&amp;rsquo;ll probably BBB them sometime later :)&lt;/p></description></item><item><title>GSoC Finale</title><link>https://kyteinsky.github.io/p/gsoc-finale/</link><pubDate>Sat, 17 Sep 2022 00:00:00 +0000</pubDate><guid>https://kyteinsky.github.io/p/gsoc-finale/</guid><description>&lt;img src="https://kyteinsky.github.io/p/gsoc-finale/blank.svg" alt="Featured image of post GSoC Finale" />&lt;p>It definitely was a long journey. Although I didn&amp;rsquo;t write enough blogs to outline the exact shaping of it, here&amp;rsquo;s a final report on what has been going on.&lt;/p>
&lt;p>The workings of the protocol and the connection specifics have already been discussed before, so let&amp;rsquo;s keep this short.&lt;/p>
&lt;ul>
&lt;li>Discovery using mDNS&lt;/li>
&lt;li>Connection over TCP&lt;/li>
&lt;li>Messages are structured in JSON and encoded using protobuf&lt;/li>
&lt;li>All the messages have at least a &lt;code>sender id&lt;/code>, &lt;code>destination id&lt;/code>, &lt;code>namespace&lt;/code>, &lt;code>payload type&lt;/code>, and the &lt;code>payload&lt;/code> itself&lt;/li>
&lt;/ul>
&lt;p>Last time we connected to the Chromecast device, we opened a media receiver web app and played a video on it.&lt;br>
Our goal, as it remains, is to cast our desktop/window on the Chromecast display. We can choose from three different streaming protocols to have what we want: DASH, HLS, and Smooth Streaming.&lt;/p>
&lt;p>Implementing one of those was the primary goal until a sparse study of Chromium&amp;rsquo;s code unveiled a namespace called &lt;code>urn:x-cast:com.google.cast.webrtc&lt;/code>.&lt;br>
We also used Google Chrome&amp;rsquo;s (or Chromium&amp;rsquo;s) tab casting feature and recorded the logs. Looking at the logs virtually revealed the entire message exchange and the steps involved, but it was no documentation by any stretch. We had no idea what the possibilities were or the error cases, for that matter.&lt;br>
While trying to figure out the Chromecast protocol, we noticed many logs in the message exchange files that were not in Chrome&amp;rsquo;s logs, even with the highest level of verbosity. &lt;code>DLOG&lt;/code> and &lt;code>VLOG&lt;/code>, for example, did not print out the &lt;code>DLOG&lt;/code>, where I suspect D stands for development and V for verbose.&lt;br>
It would have been easier to see what exactly happens with Chrome&amp;rsquo;s logs. We tried using the dev symbols that come with the Linux distribution, but Arch Linux had some issues at the time.&lt;br>
Nevertheless, I don&amp;rsquo;t suppose that would&amp;rsquo;ve worked for the logs.&lt;/p>
&lt;p>So the only option left was to read the code and figure it out ourselves. And that&amp;rsquo;s what we did since no one was successful with the webrtc stream before on the internet.&lt;br>
We came across &lt;a class="link" href="https://source.chromium.org/chromium/chromium/src/&amp;#43;/main:third_party/openscreen/src/cast/" target="_blank" rel="noopener"
>this directory&lt;/a>. It contained all the knowledge there was. The proto files, the json schemas, the header files with all the constants, all the good stuff we needed. From the names of the files itself we knew how to send the stream to Chromecast, except that we didn&amp;rsquo;t (&lt;em>hint&lt;/em> &lt;em>hint&lt;/em> something to do with encryption).&lt;/p>
&lt;p>Let me loop back to the negotiation part of the WebRTC stream. In normal WebRTC, there is a separate entity that relays messages between the two parties interested. Here that happens to be the sender itself.&lt;br>
We open the app called &lt;code>Chrome Mirroring&lt;/code> (app id &lt;code>0F5096E8&lt;/code>). This is for audio and video streaming. There is also one for only audio streaming. You can find a few others &lt;a class="link" href="https://source.chromium.org/chromium/chromium/src/&amp;#43;/main:third_party/openscreen/src/cast/common/public/cast_streaming_app_ids.h" target="_blank" rel="noopener"
>here&lt;/a>.&lt;br>
Now, when the app is opened, we get a receiver status message, and we start communicating with the app from this point on by addressing the messages to the session id of the app, after connecting to it first.&lt;/p>
&lt;p>The first thing we do is send an offer message with the necessary information for the streams we wish to send and the encryption keys, among other details. Here is an example message:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;sender-990878&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;destination_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;a8a79f97-a63e-4555-baec-53e3f49e6df3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespace&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.webrtc&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_utf8&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;offer&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;castMode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;mirroring&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;receiverGetStatus&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;supportedStreams&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;aesIvMask&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1D20EA1C710E5598ECF80FB26ABC57B0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;aesKey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;BB0CAE24F76EA1CAC9A383CFB1CFD54E&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bitRate&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">102000&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;channels&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;codecName&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;opus&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;index&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;receiverRtcpEventLog&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rtpExtensions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;adaptive_playout_delay&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rtpPayloadType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">127&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rtpProfile&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;cast&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;sampleRate&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">48000&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;ssrc&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">144842&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;targetDelay&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">400&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;timeBase&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1/48000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;audio_source&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;aesIvMask&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1D20EA1C710E5598ECF80FB26ABC57B0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;aesKey&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;BB0CAE24F76EA1CAC9A383CFB1CFD54E&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;codecName&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;vp8&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;index&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;maxBitRate&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">5000000&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;maxFrameRate&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;30000/1000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;receiverRtcpEventLog&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;renderMode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;video&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;resolutions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[{&lt;/span> &lt;span class="nt">&amp;#34;height&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1080&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;width&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1920&lt;/span> &lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rtpExtensions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;adaptive_playout_delay&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rtpPayloadType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">96&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rtpProfile&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;cast&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;ssrc&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">545579&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;targetDelay&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">400&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;timeBase&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1/90000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;video_source&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;seqNum&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">730137397&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;OFFER&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Note: only the &lt;code>payload_utf8&lt;/code> is structured in JSON, other sibling keys are part of protobuf.&lt;/p>
&lt;/blockquote>
&lt;p>You see that? All the details are right there in the offer message.&lt;br>
Other codecs are also supported, such as h264, vp9, hevc, and aac.&lt;br>
Although most of the things should be self-explanatory, focus on the key &lt;code>ssrc&lt;/code> in both the audio and video streams. This is the identifier for the stream.&lt;br>
The AES keys and masks are passed down in the offer message still. The algorithm used however is &lt;a class="link" href="https://source.chromium.org/chromium/chromium/src/&amp;#43;/main:third_party/openscreen/src/cast/streaming/frame_crypto.cc;l=102" target="_blank" rel="noopener"
>AES-CTR-128&lt;/a>.&lt;/p>
&lt;p>We sent an offer. We must receive an answer to it.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;a8a79f97-a63e-4555-baec-53e3f49e6df3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;destination_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;sender-990878&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespace&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.webrtc&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_utf8&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;answer&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;castMode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;mirroring&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;receiverGetStatus&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;receiverRtcpEventLog&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;rtpExtensions&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[[],&lt;/span> &lt;span class="p">[]],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;sendIndexes&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;ssrcs&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">144843&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">545580&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;udpPort&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">51810&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;result&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ok&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;seqNum&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">730137397&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ANSWER&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You&amp;rsquo;d find it weird that the ssrc values have been incremented compared to what we sent. Apart from that, the keys that deserve our attention are &lt;code>updPort&lt;/code>, &lt;code>result&lt;/code>, and &lt;code>type&lt;/code>. If the result is &amp;ldquo;ok&amp;rdquo;, we collect the port number and start streaming RTP packets and exchanging RTCP packets. For streaming RTP packets, we &lt;strong>need&lt;/strong> a controller of the packets, either RTCP or RTSP. Found this out the hard way while messing around with the GStreamer launch command.&lt;/p>
&lt;p>After these findings, we had some time left to reorganise the code and handle the edge cases well.&lt;br>
Daily routine was to read docs, write code, test, check logs, and repeat.&lt;/p>
&lt;p>Now we had what you can call state management (just a variable and if checks) and some nice JSON string generators.&lt;br>
We jumped into media handling right on. Already having a pipeline to process video and audio in GNOME Network Displays made things easy, but tinkering with it was no easy job until &lt;a class="link" href="https://gstreamer.freedesktop.org/documentation/tutorials/basic/index.html?gi-language=c" target="_blank" rel="noopener"
>the saviour&lt;/a> lent a helping hand. The first few tutorials were all that was needed to get to speed.&lt;/p>
&lt;p>The pipeline created for video (only) streaming looked like what follows:&lt;/p>
&lt;p>&lt;figure style="flex-grow: 1584; flex-basis: 3803px">
&lt;a href="https://kyteinsky.github.io/p/gsoc-finale/pipeline.png" data-size="8763x553">&lt;img src="https://kyteinsky.github.io/p/gsoc-finale/pipeline.png"
srcset="https://kyteinsky.github.io/p/gsoc-finale/pipeline_hucb4e2b506f4294e55aca3bcb216d10a7_168220_480x0_resize_box_3.png 480w, https://kyteinsky.github.io/p/gsoc-finale/pipeline_hucb4e2b506f4294e55aca3bcb216d10a7_168220_1024x0_resize_box_3.png 1024w"
width="8763"
height="553"
loading="lazy"
alt="Video Pipeline">
&lt;/a>
&lt;figcaption>Video Pipeline&lt;/figcaption>
&lt;/figure>&lt;/p>
&lt;p>You might want to zoom in a little.&lt;/p>
&lt;p>This was an unencrypted RTP stream with unencrypted RTCP messages. The only thing to add was an encryption layer over the RTP packets. The plugin &lt;code>srtpenc&lt;/code> provided by GStreamer did precisely this, except that it did not do AES-CTR, only AES-ICM and AES-GCM. Going deeper, it used the library &lt;code>libsrtp&lt;/code> by Cisco, which, unfortunately, only has implementations for the same two algorithms.&lt;/p>
&lt;p>There are two ways to go moving forward:&lt;/p>
&lt;ul>
&lt;li>Add necessary headers in &lt;code>srtpenc&lt;/code> and the AES-CTR implementation in &lt;code>libsrtp&lt;/code> upstream&lt;/li>
&lt;li>Implement a GStreamer plugin that does what both &lt;code>srtpenc&lt;/code> and &lt;code>libsrtp&lt;/code> do with some help from &lt;code>openssl&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Despite that, we were enticed to test the unencrypted stream as is. We had the following broadcast message (and a blank Chromecast display to go with it).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;b7319b19-5641-472e-91d8-daeff9746a68&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;destination_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;*&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespace&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.media&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_utf8&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;requestId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;status&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;currentTime&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">0.0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;disableStreamGrouping&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;media&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;contentId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;contentType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;video/webm&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;metadata&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;metadataType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Chrome tab casting&amp;#34;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;streamType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;LIVE&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mediaSessionId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;playbackRate&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">1.0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;playerState&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;PLAYING&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;supportedMediaCommands&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;volume&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;level&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">0.059999998658895493&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;muted&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;MEDIA_STATUS&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>What does all of this mean for the end-user? Do you wait patiently for us to code the plugin?&lt;br>
Of course not.&lt;br>
Remember that Chromecast also provides three streaming options with the media app, specifically the &lt;code>Deafult Media Receiver&lt;/code> app (app id &lt;code>CC1AD845&lt;/code>). We went with HLS.&lt;/p>
&lt;p>&lt;figure style="flex-grow: 180; flex-basis: 433px">
&lt;a href="https://kyteinsky.github.io/p/gsoc-finale/hls_stream_chromecast.jpg" data-size="4382x2426">&lt;img src="https://kyteinsky.github.io/p/gsoc-finale/hls_stream_chromecast.jpg"
srcset="https://kyteinsky.github.io/p/gsoc-finale/hls_stream_chromecast_hu6dbc4d2c9ae95701ac10fab6b21db70f_2274972_480x0_resize_q75_box.jpg 480w, https://kyteinsky.github.io/p/gsoc-finale/hls_stream_chromecast_hu6dbc4d2c9ae95701ac10fab6b21db70f_2274972_1024x0_resize_q75_box.jpg 1024w"
width="4382"
height="2426"
loading="lazy"
alt="Laptop screen mirrored on Chromecast">
&lt;/a>
&lt;figcaption>Laptop screen mirrored on Chromecast&lt;/figcaption>
&lt;/figure>&lt;/p>
&lt;p>There is still some time till this lands on &lt;a class="link" href="https://gitlab.gnome.org/GNOME/gnome-network-displays/-/merge_requests/171" target="_blank" rel="noopener"
>the gitlab&lt;/a>. Some cleanup is left.&lt;br>
And the files (playlist file and the segment files) are served from an nginx server (with CORS header). So that needs to be inside the app as well. Soup is the answer to this one.&lt;/p>
&lt;p>Now you won&amp;rsquo;t be able to competitive game on this or use it as a second display, but this surely can handle presentations well.&lt;/p>
&lt;p>Huge thanks to my mentors Benjamin Berg (@bberg) and Claudio Wunder (@cwunder).&lt;br>
This project was practically impossible to complete without their help.&lt;/p>
&lt;p>PS: In the offer message, we set &lt;code>castMode&lt;/code> to &lt;code>mirroring&lt;/code>. There is a second option called &lt;code>remoting&lt;/code>. From what we gathered, it is a dynamic mode that does not need the codec declarations beforehand and supports RPC messages for some reason. Our best bet is that this has to do with Chrome Remote Desktop and is of no interest to us.&lt;/p>
&lt;hr>
&lt;p>This isn&amp;rsquo;t the finale. This is the beginning.&lt;/p></description></item><item><title>DNS Based Communication</title><link>https://kyteinsky.github.io/p/dns-based-communication/</link><pubDate>Fri, 05 Aug 2022 00:00:00 +0000</pubDate><guid>https://kyteinsky.github.io/p/dns-based-communication/</guid><description>&lt;img src="https://kyteinsky.github.io/p/dns-based-communication/tunnel.jpeg" alt="Featured image of post DNS Based Communication" />&lt;blockquote>
&lt;p>This project is about a DNS server that can handle communications (one way).&lt;/p>
&lt;/blockquote>
&lt;h2 id="brief">Brief&lt;/h2>
&lt;p>So what do I mean when I say it can handle communications? It means that you can send messages to a remote server, which happens to be an authoritative DNS server, in plain text as well as encrypted data.&lt;/p>
&lt;p>The idea mainly stems from a Youtube video (whose link seems to have been lost in the internet haystack).&lt;br>
The person in the video explains its use case when you have a network with HTTP(S) ports blocked, some special service using some custom port, and the default DNS port 53 unblocked (maybe they forgot, maybe they use it for some reason).&lt;br>
This is a particular and unlikely situation, but the problem at hand is rather interesting. We need to make a DNS-based one-way message transfer protocol for fun.&lt;/p>
&lt;h2 id="plan">Plan&lt;/h2>
&lt;p>The exact action plan would be decided upon further inspection of the DNS protocol, its capabilities and limitations. For now, we won&amp;rsquo;t bother ourselves with the fine details.&lt;/p>
&lt;p>Without looking into the DNS specifications, here&amp;rsquo;s a crude plan of how things should go:&lt;/p>
&lt;p>The first step would be to create a DNS server that has access to the DNS query and gets some say in the response (yeah, some libraries can be used for &lt;em>development ease&lt;/em>, and writing a response could be redundant).&lt;br>
Now that reading and writing are possible, it is time to define the protocol structure in Unga Bunga.&lt;/p>
&lt;p>Usually, the query we send in a DNS request is a domain name structured in the form &lt;code>abcd.efgh.com&lt;/code>.&lt;br>
We can use the actual domain name and sub-domain names to act as our payload here (allowed characters are 26 alphabet characters in lowercase, 10 digits, and 1 hyphen)&lt;/p>
&lt;ul>
&lt;li>as plain text&lt;/li>
&lt;li>as encrypted text&lt;/li>
&lt;/ul>
&lt;h4 id="plain-text">Plain text&lt;/h4>
&lt;p>We can use hyphens to replace blank spaces and an encoding method as &lt;code>0-&lt;/code> followed by the word to be capitalized in this manner: &lt;code>hello-0-world&lt;/code>. This would mean &lt;code>hello World&lt;/code>, and as a general rule of thumb, we could just make the first letter capital when such encoding was being used, giving us the sweet output &lt;code>Hello World&lt;/code>.&lt;/p>
&lt;h4 id="encrypted-text">Encrypted text&lt;/h4>
&lt;p>We discuss encoded here too since there is not much difference between them once the plain text has been processed.&lt;br>
We have limitless power here. Just encrypt the text and use a total of 26+10+1 characters to pass it on, or just use the &lt;em>here&lt;/em> less efficient hex characters directly obtained from the encoder or encryptor.&lt;/p>
&lt;hr>
&lt;p>But wait, the length of the domain is limited! How do we send a longer message?&lt;br>
Here comes the &lt;code>com&lt;/code> part of the domain into the picture. It can specify if we have more content waiting to be sent or if we are finished.&lt;br>
Let us, for now, reserve the first character to check if we have more input to read: &lt;code>0&lt;/code> for not finished yet and &lt;code>1&lt;/code> for done.&lt;br>
And the following four characters for the sequence number. This shall start at &lt;code>1&lt;/code> for the first partial message and go on increasing for the following messages. This ensures we assemble the whole message in the correct order in case they are not received in the same order as sent.&lt;/p>
&lt;p>For the response part, we can follow in TCP&amp;rsquo;s footsteps. We respond to all the queries with the sequence numbers of the received messages following &lt;code>base 256&lt;/code> (to say). For sequence number &lt;code>122&lt;/code>, we return IPv4 &lt;code>0.0.0.122&lt;/code>, and for sequence number &lt;code>257&lt;/code>, we return IPv4 &lt;code>0.0.2.0&lt;/code>.&lt;/p>
&lt;p>This should be enough planning for now, I guess.&lt;/p>
&lt;p>I haven&amp;rsquo;t used Go before in any project and want to learn it.&lt;br>
What better opportunity than this right here? Let&amp;rsquo;s Go!&lt;/p></description></item><item><title>Chromecast Protocol</title><link>https://kyteinsky.github.io/p/chromecast-protocol/</link><pubDate>Wed, 03 Aug 2022 00:00:00 +0000</pubDate><guid>https://kyteinsky.github.io/p/chromecast-protocol/</guid><description>&lt;img src="https://kyteinsky.github.io/p/chromecast-protocol/Big-Buck-Bunny.jpg" alt="Featured image of post Chromecast Protocol" />&lt;p> &lt;/p>
&lt;h3 id="after-almost-a-month-of-reconnaissance-through-the-study-of-chromiums-code-vlcs-code-and-other-peoples-attempts-we-finally-have-figured-out-the-chromecast-protocol-and-it-works-flawlessly-and-reliably">After almost a month of reconnaissance through the study of Chromium&amp;rsquo;s code, VLC&amp;rsquo;s code and other people&amp;rsquo;s attempts, we finally have figured out the Chromecast protocol, and it works flawlessly and reliably!&lt;/h3>
&lt;p>&lt;figure style="flex-grow: 89; flex-basis: 215px">
&lt;a href="https://kyteinsky.github.io/p/chromecast-protocol/big-buck-bunny-tv-pc-merged.png" data-size="1920x2142">&lt;img src="https://kyteinsky.github.io/p/chromecast-protocol/big-buck-bunny-tv-pc-merged.png"
srcset="https://kyteinsky.github.io/p/chromecast-protocol/big-buck-bunny-tv-pc-merged_hu8ad568b29edf365d4d5613b7d463e28d_3202865_480x0_resize_box_3.png 480w, https://kyteinsky.github.io/p/chromecast-protocol/big-buck-bunny-tv-pc-merged_hu8ad568b29edf365d4d5613b7d463e28d_3202865_1024x0_resize_box_3.png 1024w"
width="1920"
height="2142"
loading="lazy"
alt="Big Buck Bunny clip playing on my TV (Chromecast built-in) and the GNOME Network Displays app">
&lt;/a>
&lt;figcaption>Big Buck Bunny clip playing on my TV (Chromecast built-in) and the GNOME Network Displays app&lt;/figcaption>
&lt;/figure>&lt;/p>
&lt;h3 id="ignoring-the-previous-blog-posts-heres-a-quick-rundown-of-the-chromecast-protocol">Ignoring the previous blog posts, here&amp;rsquo;s a quick rundown of the Chromecast protocol:&lt;/h3>
&lt;ul>
&lt;li>There are two parties involved: the Sender and the Receiver&lt;/li>
&lt;li>Typical sender apps are the Chrome browser, and the Android and iOS platforms&lt;/li>
&lt;li>Chromecast devices consist of two different receivers (so to say):
&lt;ul>
&lt;li>The in-built receiver app that accepts connection requests and handles all other communications with the Sender&lt;/li>
&lt;li>The Web Receiver app is optionally loaded and opened upon request by the Sender, again through messages handled by the former receiver&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>The first job is to make a Sender app&lt;/li>
&lt;li>Now, we discover the available Chromecast devices in the local network through mDNS with the identifier &lt;code>_googlecast._tcp&lt;/code>&lt;/li>
&lt;li>Next step is to open a TCP and then a TLS connection over that to the Chromecast device on port &lt;code>8009&lt;/code>&lt;/li>
&lt;li>After a successful TLS connection:
&lt;ul>
&lt;li>The first thing we do is check the authenticity of the Chromecast (we fake it in our app)&lt;/li>
&lt;li>Next, a &amp;ldquo;virtual connection&amp;rdquo; is opened to the Chromecast&lt;/li>
&lt;li>At this point, we are eligible to query the status of the Chromecast device (which includes information such as the opened app, volume, and supported namespaces, among other details)&lt;/li>
&lt;li>We can send messages to open an app, Android apps such as the YouTube app or other &amp;ldquo;media player apps&amp;rdquo; either custom made or readily available from Google&lt;/li>
&lt;li>Whenever any app is opened or closed, that is, if the state of the Chromecast changes, a broadcast message is sent out to all the connected senders regarding the present status (you may have already seen it on your Android device when a media controller notification pops up if something is being played on Chromecast)&lt;/li>
&lt;li>The apps accept requests to play, pause, change to the next item in the queue, and other similar commands&lt;/li>
&lt;li>In addition to those, we can send requests to play supported contents on the apps (or custom data for custom web receivers &amp;ndash; the second type of receivers. These should be hosted on HTTPS domains and registered on &lt;a class="link" href="https://cast.google.com/publish" target="_blank" rel="noopener"
>Google Cast SDK Developer Console&lt;/a>)&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Lastly, we close the app, the virtual connection (or as I have recently taken to calling it, VC), and the TCP/TLS connection.&lt;/li>
&lt;li>Important point to note here is that an app can keep running even if the VC and the TCP/TLS connections are closed. We need to be explicit about closing the app with a message.&lt;/li>
&lt;/ul>
&lt;p>&lt;figure style="flex-grow: 133; flex-basis: 320px">
&lt;a href="https://kyteinsky.github.io/p/chromecast-protocol/chromecast-default-media-receiver.jpg" data-size="4000x3000">&lt;img src="https://kyteinsky.github.io/p/chromecast-protocol/chromecast-default-media-receiver.jpg"
srcset="https://kyteinsky.github.io/p/chromecast-protocol/chromecast-default-media-receiver_huda1cfadf36e20cc02f7147c71de4f55d_1550964_480x0_resize_q75_box.jpg 480w, https://kyteinsky.github.io/p/chromecast-protocol/chromecast-default-media-receiver_huda1cfadf36e20cc02f7147c71de4f55d_1550964_1024x0_resize_q75_box.jpg 1024w"
width="4000"
height="3000"
loading="lazy"
alt="Default Media Receiver App (developed and hosted by Google)">
&lt;/a>
&lt;figcaption>Default Media Receiver App (developed and hosted by Google)&lt;/figcaption>
&lt;/figure>&lt;/p>
&lt;p>Haah, that was a lot to take in! Fear not, there&amp;rsquo;s more.&lt;/p>
&lt;hr>
&lt;h3 id="specifics">Specifics&lt;/h3>
&lt;p>This is best explained using logs.&lt;br>
Here are cleaned bits of logs from the GNOME Network Displays app interacting with Chromecast:&lt;br>
(&lt;strong>Only the part inside &amp;ldquo;payload_utf8&amp;rdquo; is structured in JSON, all parameters outside that like &amp;ldquo;source_id&amp;rdquo; are fields in the protobuf schema and only spread out nicely here for presentation purposes &amp;ndash; JSON key-pair highlighting&lt;/strong>)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="err">Sent&lt;/span> &lt;span class="err">message:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;sender-gnd&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;destination_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;receiver-0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespace_&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.tp.deviceauth&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_utf8&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="err">(&lt;/span>&lt;span class="kc">null&lt;/span>&lt;span class="err">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, if you look closely, the first message sent has a sender_id as &lt;code>sender-gnd&lt;/code> and receiver id as &lt;code>receiver-0&lt;/code>. The sender_id can be anything prefixed with &lt;code>sender-&lt;/code> but I doubt it allows more than six characters after the hyphen. It doesn&amp;rsquo;t really matter.&lt;br>
The receiver has a default id &lt;code>receiver-0&lt;/code> as found out through Chromium code, and again doesn&amp;rsquo;t matter since that is all the purpose it serves.&lt;/p>
&lt;p>The first message we ever send is the &amp;ldquo;authentication challenge&amp;rdquo; (look for the further reading links below) that should ideally contain some binary information and &lt;code>has_binary_payload&lt;/code> as 1.&lt;br>
We are not strict with the authenticity of the Chromecast device we are connecting to, so we send an empty request but on the correct namespace. The logs don&amp;rsquo;t print out the &lt;code>payload_binary&lt;/code> field since it is only used once (here) and is empty with length 0 anyways.&lt;/p>
&lt;hr>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="err">Sent&lt;/span> &lt;span class="err">message:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;sender-gnd&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;destination_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;receiver-0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespace_&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.tp.connection&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_utf8&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;CONNECT&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;userAgent&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;GND/0.90.5 (X11; Linux x86_64)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;connType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;origin&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;senderInfo&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;sdkType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;version&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;X11; Linux x86_64&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;browserVersion&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;X11; Linux x86_64&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;platform&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">6&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;connectionType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The second message is the virtual connection message, where we include some random metadata and most importantly the &lt;code>type&lt;/code> and &lt;code>connType&lt;/code> keys. We indicate the connection type as &lt;code>0&lt;/code> or &lt;code>strong&lt;/code>. This comes from Chromium&amp;rsquo;s nomenclature for connection types: strong, weak and invisible, out of which weak is not used (don&amp;rsquo;t ask me why). The connection type must be strong if destination_id &lt;code>receiver-0&lt;/code> is used, as a comment says there. And it seems the default connection type is strong, but we choose not to take any chances.&lt;/p>
&lt;hr>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="err">Sent&lt;/span> &lt;span class="err">message:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;sender-gnd&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;destination_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;receiver-0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespace_&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.receiver&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_utf8&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;GET_STATUS&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;requestId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Onto the next request. We manually request a status report with type &lt;code>GET_STATUS&lt;/code> on namespace &lt;code>urn:x-cast:com.google.cast.receiver&lt;/code>. With sharp eyes and a keen mind, you would have spotted a key called &lt;code>requestId&lt;/code> in the payload. This is a unique identifier for every message we send, excluding those on special namespaces, such as the virtual connection or the ping message.&lt;br>
The easiest way to keep it unique is to initialize it to 1 and increment it for every outgoing message. Guess how Chromium and VLC do this.&lt;br>
The &lt;code>requestId&lt;/code> field is also present in the received messages and is &lt;code>0&lt;/code> for all the broadcast messages.&lt;/p>
&lt;hr>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="err">Received&lt;/span> &lt;span class="err">message:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;receiver-0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;destination_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;sender-gnd&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespace_&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.receiver&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_utf8&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;requestId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;status&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;applications&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;appId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2C6A6E3D&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;appType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ANDROID_TV&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;displayName&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;YouTube&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconUrl&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;isIdleScreen&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;launchedFromCloud&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespaces&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.media&amp;#34;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.system&amp;#34;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.cac&amp;#34;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.youtube.mdx&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;sessionId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0167f70a-6430-45ab-9714-fd2f09d70b2b&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;statusText&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Youtube&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;transportId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0167f70a-6430-45ab-9714-fd2f09d70b2b&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;universalAppId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;233637DE&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;isActiveInput&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;isStandBy&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;userEq&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;volume&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;controlType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;master&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;level&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">0.10000000149011612&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;muted&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;stepInterval&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">0.009999999776482582&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;RECEIVER_STATUS&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Hey, we received a message. It has the type &lt;code>RECEIVER_STATUS&lt;/code> and is not a broadcast one. We specifically requested this of the Chromecast.&lt;/p>
&lt;p>Let&amp;rsquo;s break it down byte by byte.&lt;br>
The usual fields &lt;code>source_id&lt;/code>, &lt;code>destination_id&lt;/code> etc. are nothing special, but the payload sure packs some exciting data. We have received the state of the Chromecast where it reports the volume details, stand-by state, userEq (?), and if the Chromecast is the active input (&lt;a class="link" href="https://developers.google.com/cast/docs/reference/web_sender/chrome.cast.Receiver#isActiveInput" target="_blank" rel="noopener"
>more info&lt;/a>), and the running applications.&lt;br>
Only one app is currently running - YouTube (without YouTube Premium). A bunch of namespaces are supported for this particular app, and we should not dive into that for this post.&lt;/p>
&lt;hr>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="err">Sent&lt;/span> &lt;span class="err">message:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;sender-gnd&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;destination_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;receiver-0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespace_&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.receiver&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_utf8&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;LAUNCH&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;appId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;CC1AD845&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;requestId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Too bad we don&amp;rsquo;t need to watch YouTube videos now. We need to watch our Big Buck Bunny hosted on Google&amp;rsquo;s servers (not from Google&amp;rsquo;s CDNs, but Google&amp;rsquo;s hosted storage, makes sense?).&lt;br>
We launch the Default Media Receiver app (this is the same VLC and &lt;a class="link" href="https://developers.google.com/cast/docs/debugging/cac_tool" target="_blank" rel="noopener"
>the CacTool use&lt;/a>) by sending a &lt;code>LAUNCH&lt;/code> message with its &lt;code>appId&lt;/code>.&lt;/p>
&lt;hr>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="err">Received&lt;/span> &lt;span class="err">message:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;receiver-0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;destination_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;*&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespace_&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.receiver&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_utf8&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;requestId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;status&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;isActiveInput&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;isStandBy&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;userEq&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;volume&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;controlType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;master&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;level&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">0.10000000149011612&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;muted&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;stepInterval&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">0.009999999776482582&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;RECEIVER_STATUS&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Cool, we received a broadcast message that YouTube has been closed.&lt;br>
(&lt;strong>I have configured my Android TV to limit background processes to at most two at a time (makes the system very snappy). This might have caused the YouTube app to close even when it wasn&amp;rsquo;t needed/intended in this case.&lt;/strong>)&lt;/p>
&lt;hr>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="err">Received&lt;/span> &lt;span class="err">message:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;receiver-0&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;destination_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;*&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespace_&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.receiver&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_utf8&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;requestId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;status&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;applications&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;appId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;CC1AD845&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;appType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;WEB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;displayName&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Default Media Receiver&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;iconUrl&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;isIdleScreen&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;launchedFromCloud&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespaces&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.cac&amp;#34;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.debugoverlay&amp;#34;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.broadcast&amp;#34;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.media&amp;#34;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;sessionId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;014f4bf2-2c29-481f-ab50-ef7d6ff210ac&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;statusText&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Default Media Receiver&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;transportId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;014f4bf2-2c29-481f-ab50-ef7d6ff210ac&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;universalAppId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;CC1AD845&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;isActiveInput&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;isStandBy&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;userEq&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;volume&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;controlType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;master&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;level&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">0.10000000149011612&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;muted&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;stepInterval&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">0.009999999776482582&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;RECEIVER_STATUS&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Ah, our media app has taken the stage now. This is a &lt;code>WEB&lt;/code> application and runs on Chromium (maybe bare-bones, but it is Chromium).&lt;br>
It accepts messages with the shown namespaces where you&amp;rsquo;d recognize namespace ending in &lt;code>cac&lt;/code>. The one with &lt;code>debugoverlay&lt;/code> probably relates to &lt;a class="link" href="https://developers.google.com/cast/docs/debugging/cast_debug_logger#debug_overlay" target="_blank" rel="noopener"
>this&lt;/a>.&lt;br>
I suspect the &lt;code>broadcast&lt;/code> one is responsible for passing on the events to be broadcasted to the internal receiver.&lt;br>
Finally, we are here for the &lt;code>media&lt;/code> namespace. We need to send the URL of the media to play on this namespace. But wait, if multiple apps were open, how would the Chromecast or we even figure out for which app a particular message was meant? &lt;code>transportId&lt;/code> serves as the receiver_id for those messages.&lt;/p>
&lt;hr>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="err">Sent&lt;/span> &lt;span class="err">message:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;sender-gnd&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;destination_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;014f4bf2-2c29-481f-ab50-ef7d6ff210ac&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespace_&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.tp.connection&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_utf8&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;CONNECT&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;userAgent&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;GND/0.90.5 (X11; Linux x86_64)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;connType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;origin&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;senderInfo&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;sdkType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;version&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;X11; Linux x86_64&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;browserVersion&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;X11; Linux x86_64&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;platform&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">6&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;connectionType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We need a second connection to the &lt;code>CC1AD845&lt;/code> app to talk to it (it is what it is).&lt;/p>
&lt;hr>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="err">Received&lt;/span> &lt;span class="err">message:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;014f4bf2-2c29-481f-ab50-ef7d6ff210ac&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;destination_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;*&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespace_&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.media&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_utf8&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;MEDIA_STATUS&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;status&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;requestId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now we start receiving broadcast messages from the Default Media Receiver. For now, it&amp;rsquo;s doing nothing. See the dark image above.&lt;br>
Note that the type of message is &lt;code>MEDIA_STATUS&lt;/code> and is on the &lt;code>urn:x-cast:com.google.cast.media&lt;/code> namespace.&lt;/p>
&lt;hr>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="err">Sent&lt;/span> &lt;span class="err">message:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;sender-gnd&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;destination_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;014f4bf2-2c29-481f-ab50-ef7d6ff210ac&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespace_&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.media&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_utf8&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;LOAD&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;media&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;contentId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/mp4/BigBuckBunny.mp4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;streamType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;BUFFERED&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;contentType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;video/mp4&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;requestId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Let&amp;rsquo;s send our Google-hosted media file&amp;rsquo;s URL to the media app. Specifying the mime type of the file and the stream type is mandatory. Another option for the stream type could be &lt;code>LIVE&lt;/code>, where seek functionality can be enabled or disabled as per wish, and there would be a visual clue on the screen to show that (may not be present on custom-made web receivers).&lt;br>
&lt;code>contentId&lt;/code> and &lt;code>contentUrl&lt;/code> have some differences but would not matter in our case, &lt;a class="link" href="https://developers.google.com/cast/docs/reference/web_sender/chrome.cast.media.MediaInfo" target="_blank" rel="noopener"
>Docs for media key&lt;/a>.&lt;/p>
&lt;hr>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="err">Received&lt;/span> &lt;span class="err">message:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;014f4bf2-2c29-481f-ab50-ef7d6ff210ac&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;destination_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;*&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespace_&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.media&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_utf8&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;MEDIA_STATUS&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;status&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mediaSessionId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;playbackRate&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;playerState&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;IDLE&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;currentTime&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;supportedMediaCommands&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">12303&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;volume&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;level&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;muted&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;media&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;contentId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/mp4/BigBuckBunny.mp4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;streamType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;BUFFERED&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;contentType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;video/mp4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mediaCategory&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;VIDEO&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;currentItemId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;extendedStatus&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;playerState&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;LOADING&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;media&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;contentId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/mp4/BigBuckBunny.mp4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;streamType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;BUFFERED&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;contentType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;video/mp4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mediaCategory&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;VIDEO&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mediaSessionId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;repeatMode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;REPEAT_OFF&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;requestId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Yay!! Now would you look at that &amp;ndash; it is loading our Google-hosted media file encoded in mp4.&lt;/p>
&lt;p>Most of the keys should be self-explanatory :)&lt;br>
For &lt;code>supportedMediaCommands&lt;/code>, I just hope that it is not a count.&lt;/p>
&lt;p>If you want, you can put a bunch of videos in a queue and let them play or control them using &lt;code>NEXT&lt;/code> or &lt;code>PREVIOUS&lt;/code> messages or using your hand-held remote or using your IR-equipped Android device or using your Android TV remote (don&amp;rsquo;t have a clue if this works with other Chromecast devices).&lt;br>
Queuing the videos is left as an assignment for the reader and should be trivial to implement.&lt;/p>
&lt;hr>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="err">Received&lt;/span> &lt;span class="err">message:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;014f4bf2-2c29-481f-ab50-ef7d6ff210ac&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;destination_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;*&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespace_&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.media&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_utf8&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;MEDIA_STATUS&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;status&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mediaSessionId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;playbackRate&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;playerState&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;BUFFERING&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;currentTime&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;supportedMediaCommands&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">12303&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;volume&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;level&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;muted&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;currentItemId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;repeatMode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;REPEAT_OFF&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;requestId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>No idea why it would not send the complete status message here but whatever, it is buffering the video.&lt;/p>
&lt;hr>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="err">Received&lt;/span> &lt;span class="err">message:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;source_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;014f4bf2-2c29-481f-ab50-ef7d6ff210ac&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;destination_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;*&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;namespace_&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;urn:x-cast:com.google.cast.media&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;payload_utf8&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;MEDIA_STATUS&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;status&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mediaSessionId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;playbackRate&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;playerState&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;PLAYING&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;currentTime&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;supportedMediaCommands&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">12303&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;volume&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;level&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;muted&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;activeTrackIds&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;media&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;contentId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/mp4/BigBuckBunny.mp4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;streamType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;BUFFERED&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;contentType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;video/mp4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mediaCategory&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;VIDEO&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;duration&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">596.474195&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;tracks&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;breakClips&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;breaks&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;currentItemId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;items&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;itemId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;media&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;contentId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/mp4/BigBuckBunny.mp4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;streamType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;BUFFERED&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;contentType&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;video/mp4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;mediaCategory&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;VIDEO&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;duration&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">596.474195&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;orderId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;repeatMode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;REPEAT_OFF&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;requestId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Spot the difference challenge. Look at the message two messages above, the one that reports that our Google-hosted media file is being loaded, and then this one we got here.&lt;/p>
&lt;p>With sharp eyes and a keen mind, you would have spotted that there are some additional keys here:&lt;/p>
&lt;ul>
&lt;li>&lt;code>activeTrackIds&lt;/code>: No idea what this does. See if you can make sense of &lt;a class="link" href="https://developers.google.com/cast/docs/reference/web_sender/chrome.cast.media.LoadRequest#activeTrackIds" target="_blank" rel="noopener"
>this&lt;/a>.&lt;/li>
&lt;li>&lt;code>duration&lt;/code>: It inferred this itself. We may provide this in the &lt;code>LOAD&lt;/code> request itself though.&lt;/li>
&lt;li>&lt;code>tracks&lt;/code>: List of track objects (track as in an audio track) &amp;ndash; &lt;a class="link" href="https://developers.google.com/cast/docs/reference/web_sender/chrome.cast.media.Track" target="_blank" rel="noopener"
>docs&lt;/a>&lt;/li>
&lt;li>&lt;code>orderId&lt;/code>: This is related to queue, &lt;a class="link" href="https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.QueueItem?hl=en#orderId" target="_blank" rel="noopener"
>see&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>&lt;code>breakClips&lt;/code> and &lt;code>breaks&lt;/code> refer to ad breaks in between the video, not even going to pick that up.&lt;/p>
&lt;p>One category of messages you can&amp;rsquo;t see being sent and received are &lt;code>PING&lt;/code> and &lt;code>PONG&lt;/code>. We send &lt;code>PING&lt;/code> messages every 5 seconds to keep the connection alive. A sub-bullet point in our TODO list is to drop the connection if a &lt;code>PONG&lt;/code> is not received for 6 seconds in response.&lt;/p>
&lt;hr>
&lt;h3 id="guadec-2022">GUADEC 2022&lt;/h3>
&lt;p>I presented my then latest findings and disclosed all of my plans for this month before all of the users and developers of GNOME located all over the world.&lt;br>
Would you believe this? Someone recorded that and called it Intern Lightning Talk?!&lt;br>
Since it is already sitting in the open internet, it wouldn&amp;rsquo;t hurt to show it to my dear readers as well (even if it&amp;rsquo;s not reader&amp;quot;s&amp;quot;).&lt;/p>
&lt;blockquote>
&lt;p>&lt;a class="link" href="https://youtu.be/KK9K2CG8U40?t=18010" target="_blank" rel="noopener"
>Timestamp to the Revelation&lt;/a>&lt;br>
&lt;a class="link" href="https://www.youtube.com/watch?v=KK9K2CG8U40&amp;amp;t=16874s" target="_blank" rel="noopener"
>Timestamp to all the Intern Presentations&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;h3 id="follow-up-on-the-previous-blog-post">Follow up on the previous blog post&lt;/h3>
&lt;ul>
&lt;li>The bug that stood between a successful communication between the Chromecast and us turns out to have nothing to do with Chromecast itself but was a problem with C pointers. I would like to leave it at that, don&amp;rsquo;t wanna look stupid on the internet, right?&lt;/li>
&lt;li>Maybe we don&amp;rsquo;t get to discuss Custom Web Receivers after all. In the &lt;em>very verbose&lt;/em> logs of Chromium recorded for inspection of its communication with the Chromecast, where all we did was connect to Chromecast and cast a tab, we found out that it uses a receiver app named &lt;code>Chrome Mirroring&lt;/code> with id &lt;code>0F5096E8&lt;/code> and namespace &lt;code>urn:x-cast:com.google.cast.webrtc&lt;/code>. This was a rather interesting find. We hopefully wouldn&amp;rsquo;t need to bother making, registering and hosting a Custom Web Receiver app.&lt;/li>
&lt;/ul>
&lt;h3 id="plan-going-forward">Plan going forward&lt;/h3>
&lt;ul>
&lt;li>Clearing out some minor bugs&lt;/li>
&lt;li>Handling of Chromecast states&lt;/li>
&lt;li>Figuring out the WebRTC messages&lt;/li>
&lt;/ul>
&lt;p>Track the project&amp;rsquo;s progress &lt;a class="link" href="https://gitlab.gnome.org/GNOME/gnome-network-displays/-/merge_requests/171" target="_blank" rel="noopener"
>here&lt;/a>&lt;/p>
&lt;h3 id="my-dear-mentors">My dear mentors&lt;/h3>
&lt;p>My mentors, Benjamin Berg and Claudio Wunder were ever present for me, day in and day out.&lt;br>
Stuck somewhere, need advice on some stuff about C, GObject, Chromecast, anything at all, they&amp;rsquo;d just jump in to my rescue.&lt;br>
This project couldn&amp;rsquo;t possibly be done without their consistent support.&lt;/p>
&lt;hr>
&lt;p>* All the images used above were stripped off of all metadata using the &lt;a class="link" href="https://flathub.org/apps/details/fr.romainvigier.MetadataCleaner" target="_blank" rel="noopener"
>Metadata Cleaner app&lt;/a>&lt;br>
* Further reading on authentication of Chromecast devices: &lt;a class="link" href="https://blog.oakbits.com/google-cast-protocol-receiver-authentication.html" target="_blank" rel="noopener"
>Blog post by Romain Picard&lt;/a>&lt;br>
* &lt;a class="link" href="https://openbase.com/js/node-red-contrib-cast/documentation" target="_blank" rel="noopener"
>A Custom Web Receiver in Action&lt;/a>&lt;br>
* &lt;a class="link" href="https://source.chromium.org/chromium/chromium/src/&amp;#43;/main:components/cast_channel/cast_message_util.h;drc=5ef9498cd3f6e965991dacce1333ac6a7050c26a;l=206" target="_blank" rel="noopener"
>Chromium&amp;rsquo;s VirtualConnectionType enum&lt;/a>&lt;br>
* &lt;a class="link" href="https://developers.google.com/cast/docs/reference/web_receiver" target="_blank" rel="noopener"
>Chromecast Web Receiver Docs&lt;/a>&lt;br>
* &lt;a class="link" href="https://developers.google.com/cast/docs/reference/web_sender" target="_blank" rel="noopener"
>Chromecast Web Sender Docs&lt;/a>&lt;/p></description></item><item><title>Chromecast Connect</title><link>https://kyteinsky.github.io/p/chromecast-connect/</link><pubDate>Tue, 05 Jul 2022 00:00:00 +0000</pubDate><guid>https://kyteinsky.github.io/p/chromecast-connect/</guid><description>&lt;img src="https://kyteinsky.github.io/p/chromecast-connect/google-cast-logo.jpg" alt="Featured image of post Chromecast Connect" />&lt;p>It has been a while studying and working with the Chromecast Protocol. This protocol is proprietary, and officially only the Chrome browser, Android, and iOS platforms support casting to a Chromecast-enabled device.&lt;br>
Now, Chromium is open source, and so is Android, but we would not want anything to do with Android for obvious reasons, not to mention this feature may be tucked away in Google Play Services.&lt;br>
So we settle with Chromium which is written in C++. Thankfully almost all of the hard work was done by many other open source projects, notably Node CastV2. Not only this project provides a simple implementation for the Chromecast protocol but also a written explanation of the underlying stuff and how things actually work.&lt;br>
All I need to figure out is how to implement that is C.&lt;/p>
&lt;p>A brief overview of the connection and communication is as follows:&lt;/p>
&lt;ul>
&lt;li>We start by discovering the Chromecast devices using mDNS (Avahi) for devices named &amp;ldquo;googlecast&amp;rdquo;&lt;/li>
&lt;li>Next up we open a socket connection to the selected device on port 8009&lt;/li>
&lt;li>We then negotiate a TLS connection on the same socket connection&lt;/li>
&lt;li>Now comes the part where we open a &amp;ldquo;virtual connection&amp;rdquo; with the device&lt;/li>
&lt;li>With that done, the Chromecast device expects a &amp;ldquo;PING&amp;rdquo; message every 5 seconds to keep the virtual connection alive&lt;/li>
&lt;li>We are in the position to send and receive messages now&lt;/li>
&lt;/ul>
&lt;p>The messages are JSON encoded and serialized in protobuf according to the proto file conveniently found in the Chromium source code. Before sending the binary over, we prefix it with 4 bytes of packet length in Big Endian order. JSON is universal, good and easy to understand mostly because it is text, but what about protobuf? It is binary. Handy-dandy library protobuf-c save the day.&lt;/p>
&lt;p>Here is what the proto file looks like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-proto" data-lang="proto">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Copyright 2013 The Chromium Authors. All rights reserved.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Use of this source code is governed by a BSD-style license that can be
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// found in the LICENSE file.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="n">syntax&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;proto2&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">option&lt;/span> &lt;span class="n">optimize_for&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LITE_RUNTIME&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="kn">package&lt;/span> &lt;span class="nn">castchannel&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="kd">message&lt;/span> &lt;span class="nc">CastMessage&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="kd">enum&lt;/span> &lt;span class="n">ProtocolVersion&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="n">CASTV2_1_0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="k">required&lt;/span> &lt;span class="n">ProtocolVersion&lt;/span> &lt;span class="n">protocol_version&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="k">required&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">source_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="k">required&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">destination_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="k">required&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">namespace&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="kd">enum&lt;/span> &lt;span class="n">PayloadType&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="n">STRING&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="n">BINARY&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="k">required&lt;/span> &lt;span class="n">PayloadType&lt;/span> &lt;span class="n">payload_type&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="k">optional&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">payload_utf8&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">6&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="k">optional&lt;/span> &lt;span class="kt">bytes&lt;/span> &lt;span class="n">payload_binary&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="kd">message&lt;/span> &lt;span class="nc">AuthChallenge&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="kd">message&lt;/span> &lt;span class="nc">AuthResponse&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="k">required&lt;/span> &lt;span class="kt">bytes&lt;/span> &lt;span class="n">signature&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="k">required&lt;/span> &lt;span class="kt">bytes&lt;/span> &lt;span class="n">client_auth_certificate&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="kd">message&lt;/span> &lt;span class="nc">AuthError&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="kd">enum&lt;/span> &lt;span class="n">ErrorType&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="n">INTERNAL_ERROR&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="n">NO_TLS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="k">required&lt;/span> &lt;span class="n">ErrorType&lt;/span> &lt;span class="n">error_type&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="kd">message&lt;/span> &lt;span class="nc">DeviceAuthMessage&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="k">optional&lt;/span> &lt;span class="n">AuthChallenge&lt;/span> &lt;span class="n">challenge&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="k">optional&lt;/span> &lt;span class="n">AuthResponse&lt;/span> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="k">optional&lt;/span> &lt;span class="n">AuthError&lt;/span> &lt;span class="n">error&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We can send many types of messages like &lt;code>CONNECT&lt;/code>, &lt;code>PING&lt;/code>, &lt;code>GET_STATUS&lt;/code>, &lt;code>GET_APP_AVAILIBILITY&lt;/code>, &lt;code>STOP&lt;/code>, &lt;code>CLOSE&lt;/code>, etc.&lt;br>
These messages are sent in their respective namespaces (some of them are: &lt;code>urn:x-cast:com.google.cast.tp.connection&lt;/code>, &lt;code>urn:x-cast:com.google.cast.tp.heartbeat&lt;/code>, &lt;code>urn:x-cast:com.google.cast.receiver&lt;/code>) along with some other values such as &lt;code>sender_id&lt;/code>, &lt;code>destination_id&lt;/code>, &lt;code>payload_type&lt;/code>, &lt;code>payload_utf8&lt;/code>, etc.&lt;br>
Different namespaces don&amp;rsquo;t mean we create new channels for each one. They are just like the other values in the protobuf message.&lt;/p>
&lt;p>We won&amp;rsquo;t bother ourselves to verify the authenticity of the Chromecast device we are talking to. Thus all the Auth- messages shall not be entertained. We are much more interested in the &lt;code>CastMessage&lt;/code>.&lt;br>
Protobuf-c comes with a lot of helper functions, but it offers yet more. We use &lt;code>protoc&lt;/code>, &amp;ldquo;a code generator that converts Protocol Buffer .proto files to C descriptor code&amp;rdquo;.&lt;/p>
&lt;p>Having the TLS connection set up, we serialize a connect message and send it over.&lt;br>
For some odd reason when I sent &lt;code>{ &amp;quot;type&amp;quot;: &amp;quot;CONNECT&amp;quot; }&lt;/code>, it replied with &lt;code>{ &amp;quot;type&amp;quot;: &amp;quot;CLOSE&amp;quot; }&lt;/code>, replied &lt;code>{ &amp;quot;type&amp;quot;: &amp;quot;PING&amp;quot; }&lt;/code> with &lt;code>{ &amp;quot;type&amp;quot;: &amp;quot;PONG&amp;quot; }&lt;/code>, and &lt;code>{ &amp;quot;type&amp;quot;: &amp;quot;GET_STATUS&amp;quot; }&lt;/code> with &lt;code>{ &amp;quot;type&amp;quot;: &amp;quot;CLOSE&amp;quot; }&lt;/code>.&lt;br>
When I insisted on connection multiple times, it got mad and stopped replying to my messages :/&lt;br>
It still played ping pong though (replied to all the ping messages with pong).&lt;/p>
&lt;p>We haven&amp;rsquo;t come to terms since then. Was it because I left it hanging, and I have a feeling it must have waited for my messages all night?&lt;br>
I tried to sever all ties and make them all over again, shut it down and revived it, changed my address (local IP), and changed my name (sender_id), but who knows what is going inside that little mind of it.&lt;/p>
&lt;p>Fear not our next mission involves peering into that mind and getting to the root cause of this confusion (Custom Web Receiver).&lt;/p>
&lt;hr>
&lt;p>We didn&amp;rsquo;t talk about the Custom Web Receiver this time and the protocols that would be implemented. Hang tight because that is what we are going to do in the coming days.&lt;/p>
&lt;p>Many thanks to Benjamin and Claudio! I couldn&amp;rsquo;t have made it without their help and advice.&lt;/p>
&lt;hr>
&lt;p>Relevant Links:&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://gitlab.gnome.org/GNOME/gnome-network-displays/-/merge_requests/171" target="_blank" rel="noopener"
>Merge Request&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://developers.google.com/cast/docs/" target="_blank" rel="noopener"
>Chromecast Docs&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/thibauts/node-castv2" target="_blank" rel="noopener"
>Node CastV2&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://chromium.googlesource.com/chromium/src.git" target="_blank" rel="noopener"
>Chromium Source Code&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/protobuf-c/protobuf-c" target="_blank" rel="noopener"
>Protobuf-c&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages" target="_blank" rel="noopener"
>Cast Framework Messages&lt;/a>&lt;/li>
&lt;/ul>
&lt;p> &lt;/p></description></item><item><title>GNOME Introductory Post</title><link>https://kyteinsky.github.io/p/gnome-introductory-post/</link><pubDate>Sun, 12 Jun 2022 00:00:00 +0000</pubDate><guid>https://kyteinsky.github.io/p/gnome-introductory-post/</guid><description>&lt;img src="https://kyteinsky.github.io/p/gnome-introductory-post/GNOME-logo-500x316.png" alt="Featured image of post GNOME Introductory Post" />&lt;p>I will be working as a Google Summer of Code Intern at GNOME Foundation, and my project will be to add Chromecast support to the already very cool &lt;a class="link" href="https://gitlab.gnome.org/GNOME/gnome-network-displays" target="_blank" rel="noopener"
>GNOME Network Displays app&lt;/a> that has Miracast support as of now. It can be installed through &lt;a class="link" href="https://flathub.org/apps/details/org.gnome.NetworkDisplays" target="_blank" rel="noopener"
>flatpak&lt;/a> as well.&lt;/p>
&lt;h3 id="details">Details&lt;/h3>
&lt;p>Linux desktop users will be able to cast their screens to Miracast and Chromecast devices. For the Android TVs that feature both of them, it would be wise to opt for the Chromecast path when both the devices share the same router (i.e. are on the same network) since Miracast would require setting up a WiFi-Direct connection to function (may be faster with less latency, may not be worth the trouble).&lt;/p>
&lt;p>I believe this will see the light of day with very helpful and experienced mentors, &lt;strong>Claudio Wunder&lt;/strong> and &lt;strong>Benjamin Berg&lt;/strong>.&lt;/p>
&lt;h3 id="progress">Progress&lt;/h3>
&lt;p>We started by looking into the Chromecast documentation (an adventure in itself). So there are two ends to deal with - the sender one and the receiver one.&lt;/p>
&lt;h4 id="the-receiver-end">The receiver end&lt;/h4>
&lt;p>There are two options here: &lt;strong>Styled Media Receiver&lt;/strong> and &lt;strong>Custom Web Receiver&lt;/strong>.&lt;/p>
&lt;p>The &lt;strong>Styled Media Receiver&lt;/strong> does everything for us. It hosts the receiver application (an HTML5 app) and provides all the default styles and functionality. If we want to change the styles and/or the logo, we are free to host them on an HTTPS server, and we&amp;rsquo;ll be done.&lt;/p>
&lt;p>With everything it offers, it has limited still quite adequate media support, including playing video files, audio files, streaming video, and audio, displaying images and opening specific apps on the TV (like YouTube).&lt;/p>
&lt;p>Our main job is to &amp;ldquo;stream&amp;rdquo; our desktop to the Chromecast device. For this purpose, the &lt;strong>Styled Media Receiver&lt;/strong> supports three streaming protocols:&lt;/p>
&lt;ul>
&lt;li>Dynamic Adaptive Streaming over HTTP (DASH)&lt;/li>
&lt;li>Apple&amp;rsquo;s HTTP Live Streaming (HLS)&lt;/li>
&lt;li>Microsoft&amp;rsquo;s Smooth streaming&lt;/li>
&lt;/ul>
&lt;p>We drop considerations for the Smooth Streaming protocol here because of the lack of feature differences compared to the other widely supported protocols, except that HLS and Smooth Streaming support pre-loading by default.&lt;/p>
&lt;p>So we end up here with two choices: DASH and HLS. Now, HLS is widely supported; on the other hand, DASH is codec agnostic for both video and audio. In terms of latency, both are said to have similar latencies that depend on the segment duration we decide upon.&lt;/p>
&lt;p>As per the HLS&amp;rsquo;s RFC (linked below), the &lt;code>EXT-X-TARGETDURATION&lt;/code> tag in the playlist file (or manifest file) accepts a decimal integer value. It dictates how much the maximum duration of each segment can be (rounded off to the nearest integer). It is suspected to be similar to DASH, although its RFC does not clearly mention so. We better find out by actually experimenting and streaming.&lt;/p>
&lt;p>Reducing the segment size too much can cause the bitrate to bump up and the stream quality to degrade thanks to all the added keyframes for each segment, so we need to test what suits this live stream the best.&lt;/p>
&lt;hr>
&lt;p>Next up, we take the discussion to the &lt;strong>Custom Web Receiver&lt;/strong> and the conclusions from some of our tests with the Chromecast streamings using the Command and Control (CaC) Tool, which is a sender app provided by Google for testing and debugging Web Receiver apps.&lt;/p>
&lt;p>We want to test out and know more about other protocols not supported out of the box in Chromecast but can work fine with a Custom Web Receiver: SRT, RTSP and WebRTC.&lt;/p>
&lt;p>Thanks to Benjamin and Claudio for all the help!&lt;/p>
&lt;hr>
&lt;p>Relevant Links:&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://developers.google.com/cast/docs/" target="_blank" rel="noopener"
>Chromecast Docs&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://developers.google.com/cast/docs/styled_receiver" target="_blank" rel="noopener"
>Styled Media Receiver&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://developers.google.com/cast/docs/web_receiver/streaming_protocols" target="_blank" rel="noopener"
>Streaming Protocols Supported by Web Receiver&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.toolbox.com/tech/programming/guest-article/mpeg-dash-vs-hls-which-one-should-you-use/" target="_blank" rel="noopener"
>DASH vs HLS by Toolbox&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.dacast.com/blog/video-streaming-protocol/" target="_blank" rel="noopener"
>Common Video Streaming Protocols&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://bitmovin.com/mpeg-dash-hls-segment-length/" target="_blank" rel="noopener"
>Blog on Segment Length in DASH and HLS&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://streaminglearningcenter.com/learning/choosing-the-optimal-segment-duration.html" target="_blank" rel="noopener"
>Blog on Segment Duration in DASH and HLS&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://datatracker.ietf.org/doc/html/rfc8216#section-4.3.3.1" target="_blank" rel="noopener"
>HLS RFC: EXT-X-TARGETDURATION tag&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://datatracker.ietf.org/doc/html/rfc7933" target="_blank" rel="noopener"
>DASH RFC&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://developers.google.com/cast/docs/debugging/cac_tool" target="_blank" rel="noopener"
>CaC Tool&lt;/a>&lt;/li>
&lt;/ul>
&lt;p> &lt;/p></description></item><item><title>Playing Around with Kubernetes in EC2</title><link>https://kyteinsky.github.io/p/playing-around-with-kubernetes-in-ec2/</link><pubDate>Tue, 06 Jul 2021 00:00:00 +0000</pubDate><guid>https://kyteinsky.github.io/p/playing-around-with-kubernetes-in-ec2/</guid><description>&lt;img src="https://kyteinsky.github.io/p/playing-around-with-kubernetes-in-ec2/kubernetes-logo.png" alt="Featured image of post Playing Around with Kubernetes in EC2" />&lt;p> &lt;/p>
&lt;h3 id="the-minikube-cluster">The Minikube Cluster&lt;/h3>
&lt;p>It is a local playground to learn or test your code as it would run on a full-fledged Kubernetes Cluster. It creates a small cluster consisting of only one node.&lt;br>
The command &lt;code>minikube start&lt;/code> launches the cluster with options for bare-metal, ssh, docker among others. The cluster can be managed with CLI but it also offers a GUI in the web browser, can be launched with &lt;code>minikube dashboard&lt;/code> command.&lt;br>
The task of minikube is over here. Now we head on to using the &lt;code>kubectl&lt;/code> command to manage the cluster.&lt;/p>
&lt;h3 id="manage-the-cluster">Manage the Cluster&lt;/h3>
&lt;p>Information gathering can be done easily using the command &lt;code>kubectl get&lt;/code> followed by &lt;code>pods&lt;/code>, &lt;code>services&lt;/code>, &lt;code>deployments&lt;/code>, &lt;code>replicaset&lt;/code>, etc. A deployment can be easily created (using docker here) by running&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ kubectl create deployment &amp;lt;name&amp;gt; --image&lt;span class="o">=&lt;/span>&amp;lt;docker-image-name&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">deployment.apps/&amp;lt;name&amp;gt; created
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now we can view the pods created using command:&lt;/p>
&lt;pre tabindex="0">&lt;code>$ kubectl get pods
NAME READY STATUS RESTARTS AGE
&amp;lt;name&amp;gt;-&amp;lt;random-string&amp;gt; 1/1 Running 0 5s
&lt;/code>&lt;/pre>&lt;p>There is only one pod and only one replicaset (&lt;code>kubectl get replicasets&lt;/code>), because that is the default value, can be changed with &lt;code>--replicas&lt;/code> flag, just like&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ kubectl create deployment &amp;lt;name&amp;gt; --image&lt;span class="o">=&lt;/span>&amp;lt;docker-image-name&amp;gt; --replicas&lt;span class="o">=&lt;/span>&lt;span class="m">3&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>Doing things this way would be slightly cumbersome when there were many different pods with different configurations, moreover they are hard to track, hence I will be using an easier way to deploy the cluster - using YAML configuration files.&lt;/p>
&lt;p>The following config from &lt;a class="link" href="https://www.youtube.com/watch?v=qmDzcu5uY1I" target="_blank" rel="noopener"
>this YouTube video&lt;/a> creates a &lt;code>nginx&lt;/code> cluster with 3 replicas and exposes the &lt;code>port 8080&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yml" data-lang="yml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">apps/v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Deployment&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">nginx-depl&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">nginx&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">replicas&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">selector&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matchLabels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">nginx&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">template&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">nginx&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">containers&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">nginx&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">nginx:1.16&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">containerPort&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">8080&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And it can be deployed using &lt;code>kubectl apply -f filename.yml&lt;/code>&lt;/p>
&lt;p>Above was an example where three replicas were created with one pod each with one container each, we can also create one pod with two containers which have totally different docker images. This YAML configuration from &lt;a class="link" href="https://collabnix.github.io/kubelabs/pods101/deploy-your-first-nginx-pod.html" target="_blank" rel="noopener"
>this helpful website&lt;/a> below does exactly that.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yml" data-lang="yml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Pod&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">nginx-pod&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">html&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">emptyDir&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>{}&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">containers&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">first&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">nginx&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumeMounts&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">html&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">mountPath&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/usr/share/nginx/html&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">second&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">debian&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumeMounts&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">html&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">mountPath&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/html&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">command&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;/bin/sh&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;-c&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">args&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">while true; do&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="l">date &amp;gt;&amp;gt; /html/index.html;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="l">sleep 1;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="l">done&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It is a pod configuration that attaches a common volume to both the containers named &lt;code>html&lt;/code>, which is an empty directory. Then it defines the configuration for the two containers named &lt;code>first&lt;/code> and &lt;code>second&lt;/code> with docker images &lt;code>nginx&lt;/code> and &lt;code>docker&lt;/code> respectively. In the &lt;code>first&lt;/code> container, the &lt;code>html&lt;/code> volume is mounted at &lt;code>/usr/share/nginx/html&lt;/code> so that nginx would serve content from there, and the &lt;code>second&lt;/code> container is just a linux destribution that mounts the &lt;code>html&lt;/code> volume at location &lt;code>/html&lt;/code> in its file structure and executes the command &lt;code>/bin/sh -c&lt;/code> with these arguments&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="k">while&lt;/span> true&lt;span class="p">;&lt;/span> &lt;span class="k">do&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> date &amp;gt;&amp;gt; /html/index.html&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> sleep 1&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">done&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This piece of code just appends the &lt;code>/html/index.html&lt;/code> file and sets it content to be the present datetime.&lt;br>
Long story short debian writes data to the &lt;code>index.html&lt;/code> file which is inside the &lt;code>html&lt;/code> volume, every second, and nginx reads from the same volume&lt;/p>
&lt;p>One can create the pod using the YAML file with the following command:&lt;/p>
&lt;pre tabindex="0">&lt;code>$ kubectl create -f nginx-pod.yml
pod/nginx-pod created
&lt;/code>&lt;/pre>&lt;p>The pod can be viewed using&lt;/p>
&lt;pre tabindex="0">&lt;code>$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-pod 2/2 Running 0 6s
&lt;/code>&lt;/pre>&lt;p>Now the index.html file can be viewed by running &lt;code>cat&lt;/code> command inside the container&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ kubectl &lt;span class="nb">exec&lt;/span> nginx-pod -c first -- /bin/cat /usr/share/nginx/html/index.html &lt;span class="c1"># for the first container&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:36 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:37 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:38 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:39 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ kubectl &lt;span class="nb">exec&lt;/span> nginx-pod -c second -- /bin/cat /html/index.html &lt;span class="c1"># for the second container&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:36 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:37 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:38 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:39 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:40 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:41 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:42 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:43 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:44 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:45 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:46 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:47 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Thu Jul &lt;span class="m">7&lt;/span> 10:41:48 UTC &lt;span class="m">2021&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>kubectl describe po&lt;/code> command can be used to get detailed information about the pods running in the cluster.&lt;/p>
&lt;p> &lt;/p></description></item><item><title>AWS Console and CLI Exploration</title><link>https://kyteinsky.github.io/p/aws-console-and-cli-exploration/</link><pubDate>Tue, 29 Jun 2021 00:00:00 +0000</pubDate><guid>https://kyteinsky.github.io/p/aws-console-and-cli-exploration/</guid><description>&lt;img src="https://kyteinsky.github.io/p/aws-console-and-cli-exploration/aws-logo-full.png" alt="Featured image of post AWS Console and CLI Exploration" />&lt;p> &lt;/p>
&lt;h2 id="inside-the-aws-console">Inside the AWS Console:&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>Explored compute machine options&lt;/p>
&lt;ul>
&lt;li>EC2 is the easiest of all, provides vanilla virtual machine experience for fully self-managing the infrastructure&lt;/li>
&lt;li>Lambda is for small fuctions or API calls&lt;/li>
&lt;li>App Runner provides a full-managed infrastructure to run our app handling all the scaling, load balancing, installation of software and deployment.&lt;/li>
&lt;li>Elastic Beanstalk is a similar service to App Runner, but there is a slight difference, Elastic Beanstalk provides more control over infrastructure after deployment, whereas App Runner manages it fully even after deployment. &lt;a class="link" href="https://acloudguru.com/blog/engineering/what-you-need-to-know-about-aws-app-runner" target="_blank" rel="noopener"
>(source)&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Explored storage options&lt;/p>
&lt;ul>
&lt;li>S3, Simple Storage Service, used for every purpose like storing files and also hosting static websites&lt;/li>
&lt;li>EFS, Elastic File System, used to store files that can be attached to multiple EC2 instances, are fully-managed and scale automatically&lt;/li>
&lt;li>FSx is used for Windows Servers and playing nice with its ecosystem&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>VPC&lt;/p>
&lt;ul>
&lt;li>Used to create a private space in cloud for related, non-public services to live in&lt;/li>
&lt;li>One or more services can be exposed to the internet and other VPCs or AWS services, while secure services like databases can be secured and only accessed by some hidden service inside the VPC making it more secure&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="creating-two-ec2-instances-inside-a-vpc-and-one-s3-bucket-using-aws-cli-and-then-clearing-off-all-the-resources-vpc-deleted-through-console-because-of-security-group-being-of-type-default-was-not-being-deleted-through-cli-for-some-convoluted-reason">Creating two EC2 instances inside a VPC and one S3 bucket using AWS CLI and then clearing off all the resources (VPC deleted through Console because of security group being of type &amp;lsquo;default&amp;rsquo; was not being deleted through CLI for some convoluted reason&lt;/h2>
&lt;p>
&lt;asciinema-player
src="https://kyteinsky.github.io/casts/aws-basics/aws-basics.cast"
cols="640"
rows="35"
preload="1"
start-at="0"
speed="1"
>&lt;/asciinema-player>
&lt;/p>
&lt;p> &lt;/p></description></item><item><title>Installing Terraform and Creating an EC2 Instance</title><link>https://kyteinsky.github.io/p/installing-terraform-and-creating-an-ec2-instance/</link><pubDate>Sun, 27 Jun 2021 00:00:00 +0000</pubDate><guid>https://kyteinsky.github.io/p/installing-terraform-and-creating-an-ec2-instance/</guid><description>&lt;img src="https://kyteinsky.github.io/p/installing-terraform-and-creating-an-ec2-instance/terraform-logo-extended.jpg" alt="Featured image of post Installing Terraform and Creating an EC2 Instance" />&lt;p> &lt;/p>
&lt;h2 id="ensure-the-prerequisites">Ensure the prerequisites&lt;/h2>
&lt;ul>
&lt;li>An AWS Account&lt;/li>
&lt;li>AWS cli installed, instructions for the same can be found &lt;a class="link" href="https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html" target="_blank" rel="noopener"
>here&lt;/a>.&lt;/li>
&lt;li>Have generated credentials (access key and secret), can be created from &lt;a class="link" href="https://console.aws.amazon.com/iam/home?#/security_credentials" target="_blank" rel="noopener"
>here&lt;/a>.&lt;/li>
&lt;/ul>
&lt;h2 id="setting-up-aws-cli">Setting up AWS CLI&lt;/h2>
&lt;p>Type &lt;code>aws configure&lt;/code> on the terminal and enter the Access Key ID and Secret Access Key, other fields can be blank.&lt;/p>
&lt;h2 id="creating-a-config-file-for-terraform-to-understand">Creating a config file for terraform to understand&lt;/h2>
&lt;ul>
&lt;li>Create a directory and switch to it&lt;/li>
&lt;li>Create a file named main.tf and populate it with the following sample configuration,&lt;br>
where
&lt;ul>
&lt;li>&lt;code>app_server&lt;/code> is the name of the instance&lt;/li>
&lt;li>&lt;code>us-west-2&lt;/code> is the region where the instance will be deployed&lt;/li>
&lt;li>&lt;code>ami-830c94e3&lt;/code> is the unique identifier for the instance&lt;/li>
&lt;li>&lt;code>t2.micro&lt;/code> is the instance type&lt;/li>
&lt;li>&lt;code>ExampleAppServerInstance&lt;/code> is the tag name for grouping of instances&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-tf" data-lang="tf">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">terraform&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">required_providers&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">aws&lt;/span> = &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">source&lt;/span> = &lt;span class="s2">&amp;#34;hashicorp/aws&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">version&lt;/span> = &lt;span class="s2">&amp;#34;~&amp;gt; 3.27&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">required_version&lt;/span> = &lt;span class="s2">&amp;#34;&amp;gt;= 0.14.9&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">provider&lt;/span> &lt;span class="s2">&amp;#34;aws&amp;#34;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">profile&lt;/span> = &lt;span class="s2">&amp;#34;default&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">region&lt;/span> = &lt;span class="s2">&amp;#34;us-west-2&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_instance&amp;#34;&lt;/span> &lt;span class="s2">&amp;#34;app_server&amp;#34;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">ami&lt;/span> = &lt;span class="s2">&amp;#34;ami-830c94e3&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">instance_type&lt;/span> = &lt;span class="s2">&amp;#34;t2.micro&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">tags&lt;/span> = &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">Name&lt;/span> = &lt;span class="s2">&amp;#34;ExampleAppServerInstance&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="installation-of-and-working-with-terraform-cli">Installation of and Working with Terraform CLI:&lt;/h2>
&lt;ul>
&lt;li>Terraform can be installed following &lt;a class="link" href="https://learn.hashicorp.com/tutorials/terraform/install-cli" target="_blank" rel="noopener"
>this guide&lt;/a>&lt;/li>
&lt;li>Configuration of terraform CLI can be done using command &lt;code>terraform init&lt;/code>&lt;/li>
&lt;li>The configuration file can be formatted using &lt;code>terraform fmt&lt;/code> command&lt;/li>
&lt;li>And the file can be validated using &lt;code>terraform validate&lt;/code> command&lt;/li>
&lt;li>Finally all the changes can be viewed using &lt;code>terraform plan&lt;/code> command and perfomed using &lt;code>terraform apply&lt;/code> command&lt;/li>
&lt;/ul>
&lt;h2 id="here-is-a-terminal-cast-performing-the-above-actions">Here is a terminal cast performing the above actions&lt;/h2>
&lt;p>
&lt;asciinema-player
src="https://kyteinsky.github.io/casts/terraform/terraform-1.cast"
cols="640"
rows="35"
preload="1"
start-at="0"
speed="1"
>&lt;/asciinema-player>
&lt;/p></description></item><item><title>Hosting Docker in Cloud (Azure)</title><link>https://kyteinsky.github.io/p/hosting-docker-in-cloud-azure/</link><pubDate>Tue, 15 Jun 2021 00:00:00 +0000</pubDate><guid>https://kyteinsky.github.io/p/hosting-docker-in-cloud-azure/</guid><description>&lt;img src="https://kyteinsky.github.io/p/hosting-docker-in-cloud-azure/rafael-garcin-sqZ4GeyYGx8-unsplash.jpg" alt="Featured image of post Hosting Docker in Cloud (Azure)" />&lt;p> &lt;/p>
&lt;h2 id="azure-part-of-story">Azure part of story&lt;/h2>
&lt;ul>
&lt;li>First create one Azure account, I am using a student account&lt;/li>
&lt;li>Create a basic instance (Ubuntu image works for most cases) with 1 vCPU and 1 GiB RAM, or according to your needs&lt;/li>
&lt;li>Download the private key and SSH into the instance&lt;/li>
&lt;li>Update the packages&lt;/li>
&lt;li>Install docker (following &lt;a class="link" href="https://docs.docker.com/engine/install/ubuntu/" target="_blank" rel="noopener"
>this&lt;/a> guide)&lt;/li>
&lt;/ul>
&lt;p>Let&amp;rsquo;s move on to the local machine setup&lt;/p>
&lt;p> &lt;/p>
&lt;h2 id="local-machine-setup">Local machine setup&lt;/h2>
&lt;h3 id="ssh-keys-setup">SSH Keys Setup&lt;/h3>
&lt;ul>
&lt;li>We need to install docker here as well&lt;/li>
&lt;li>Add ssh key (downloaded from Azure portal) to OpenSSH Auth agent&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">eval&lt;/span> &lt;span class="s1">&amp;#39;ssh-agent&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ssh-add &amp;lt;path-to-pem-file&amp;gt;/&amp;lt;file-name&amp;gt;.pem
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;em>(or)&lt;/em> Add &lt;code>AddKeysToAgent yes&lt;/code> to ~/.ssh/config file to automatically do that for you&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;AddKeysToAgent yes&amp;#34;&lt;/span> &amp;gt;&amp;gt; ~/.ssh/config
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p> &lt;/p>
&lt;h3 id="docker-context-setup">Docker Context Setup&lt;/h3>
&lt;ul>
&lt;li>Create a new docker context to talk to the docker in Azure instance with command&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker context create azure-box --docker &lt;span class="s2">&amp;#34;host=ssh://azureuser@&amp;lt;public-ip-of-instance&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>See all available contexts using &lt;code>docker context ls&lt;/code>&lt;/li>
&lt;li>Switching to Azure docker is done using command &lt;code>docker context use azure-box&lt;/code> and back to the local docker using &lt;code>docker context use default&lt;/code>&lt;/li>
&lt;li>Now when we issue commands to docker as usual &lt;code>docker ps&lt;/code>, it is directly communicating to the docker running in the Azure instance&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>Note: &amp;lsquo;azure-box&amp;rsquo; is just a name, can be replaced with anything of your choice&lt;/p>
&lt;/blockquote>
&lt;h2 id="-now-you-have-docker-running-in-the-cloud-accessible-from-the-terminal">🎉 Now you have docker running in the cloud, accessible from the terminal&lt;/h2>
&lt;h3 id="finally-just-for-testing-we-can-spin-up-a-ubuntu-container-and-bash-into-it">Finally just for testing, we can spin up a Ubuntu container and bash into it:&lt;/h3>
&lt;p>
&lt;asciinema-player
src="https://kyteinsky.github.io/casts/docker-cloud/docker-cloud.cast"
cols="640"
rows="35"
preload="1"
start-at="0"
speed="1"
>&lt;/asciinema-player>
&lt;/p></description></item><item><title>Git Add, Git Commit and Git Push</title><link>https://kyteinsky.github.io/p/git-add-git-commit-and-git-push/</link><pubDate>Fri, 11 Jun 2021 00:00:00 +0000</pubDate><guid>https://kyteinsky.github.io/p/git-add-git-commit-and-git-push/</guid><description>&lt;img src="https://kyteinsky.github.io/p/git-add-git-commit-and-git-push/roman-synkevych-UT8LMo-wlyk-unsplash.jpg" alt="Featured image of post Git Add, Git Commit and Git Push" />&lt;h2 id="how-to-add-and-commit-changes-in-the-and-then-push-the-changes-to-a-remote-repository-here-github">How to add and commit changes in the and then push the changes to a remote repository (here GitHub)&lt;/h2>
&lt;h4 id="outline-of-steps-to-be-performed">Outline of steps to be performed:&lt;/h4>
&lt;ul>
&lt;li>Change some files or create some&lt;/li>
&lt;li>Issue&lt;code>git add &amp;lt;name of files&amp;gt;&lt;/code>command to add files to be committed&lt;/li>
&lt;li>Then commit them using &lt;code>git commit -am &amp;lt;commit message&amp;gt;&lt;/code>, here a is for all files, m is for message, and then you place your message for the changes&lt;/li>
&lt;li>(optional) Finally, push the changes to a remote repository&lt;/li>
&lt;/ul>
&lt;h2 id="lets-see-this-in-action">Let&amp;rsquo;s see this in action&lt;/h2>
&lt;p>
&lt;asciinema-player
src="https://kyteinsky.github.io/casts/git-basics/git-acp.cast"
cols="640"
rows="35"
preload="1"
start-at="0"
speed="1"
>&lt;/asciinema-player>
&lt;/p></description></item><item><title>Embedding Asciinema in Hugo Websites</title><link>https://kyteinsky.github.io/p/embedding-asciinema-in-hugo-websites/</link><pubDate>Thu, 10 Jun 2021 00:00:00 +0000</pubDate><guid>https://kyteinsky.github.io/p/embedding-asciinema-in-hugo-websites/</guid><description>&lt;p> &lt;/p>
&lt;h1 id="following-a-general-approach-for-any-theme-steps-to-be-performed-following-this-articlehttpsjencisogithubioblogembedding-asciinema-cast-in-your-hugo-site">Following a general approach for any theme, steps to be performed (following &lt;a class="link" href="https://jenciso.github.io/blog/embedding-asciinema-cast-in-your-hugo-site/" target="_blank" rel="noopener"
>this article&lt;/a>):&lt;/h1>
&lt;p>Specific for the theme Stack: &lt;a class="link" href="#for-the-stack-theme-the-kind-developer-of-the-theme-zhixuan666httpsgithubcomzhixuan666-mentioned-a-more-efficient-way-to-do-it" >Click Here&lt;/a>&lt;/p>
&lt;ul>
&lt;li>Get code for asciinema player&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">wget https://github.com/asciinema/asciinema-player/releases/download/v2.6.1/asciinema-player.css
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">wget https://github.com/asciinema/asciinema-player/releases/download/v2.6.1/asciinema-player.js
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Place them in static/css and static/js, where static is folder in the root dir of the website&lt;/li>
&lt;li>Copy the &lt;code>layouts/partials/_shared/head.html&lt;/code> or &lt;code>layouts/partials/head/head.html&lt;/code> from &lt;code>themes/&amp;lt;theme-name&amp;gt;&lt;/code> directory to your website&amp;rsquo;s root directory with the same folder structure&lt;/li>
&lt;li>Do the same with &lt;code>layouts/_default/baseof.html&lt;/code>&lt;/li>
&lt;li>Append this code block for css in &lt;code>layouts/partials/{head or _shared}/head.html&lt;/code>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{{ if .Params.asciinema }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">link&lt;/span> &lt;span class="na">rel&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;stylesheet&amp;#34;&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text/css&amp;#34;&lt;/span> &lt;span class="na">href&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Site.BaseURL }}css/asciinema-player.css&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>And this for js in &lt;code>layouts/_default/baseof.html&lt;/code>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{{ if .Params.asciinema }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">defer&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Site.BaseURL }}js/asciinema-player.js&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Now let&amp;rsquo;s make a shortcode for asciinema here: &lt;code>layouts/shortcodes/asciinema.html&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>One can change the default rows and columns attributes inside &lt;code>&amp;lt;asciinema-player&amp;gt;&lt;/code> tag&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">asciinema-player&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;/casts/{{ with .Get &amp;#34;&lt;/span>&lt;span class="na">key&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}{{&lt;/span> &lt;span class="err">.&lt;/span> &lt;span class="err">}}{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">cols&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ if .Get &amp;#34;&lt;/span>&lt;span class="na">cols&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}{{&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Get&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">cols&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}{{&lt;/span> &lt;span class="na">else&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">640&lt;/span>&lt;span class="err">{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">rows&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ if .Get &amp;#34;&lt;/span>&lt;span class="na">rows&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}{{&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Get&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">rows&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}{{&lt;/span> &lt;span class="na">else&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">10&lt;/span>&lt;span class="err">{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">{{&lt;/span> &lt;span class="na">if&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Get&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">autoplay&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">autoplay&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Get &amp;#34;&lt;/span>&lt;span class="na">autoplay&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&amp;#34;{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">{{&lt;/span> &lt;span class="na">if&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Get&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">preload&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">preload&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Get &amp;#34;&lt;/span>&lt;span class="na">preload&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&amp;#34;{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">{{&lt;/span> &lt;span class="na">if&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Get&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">loop&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">loop&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Get &amp;#34;&lt;/span>&lt;span class="na">loop&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&amp;#34;{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">start-at&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ if .Get &amp;#34;&lt;/span>&lt;span class="na">start-at&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}{{&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Get&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">start-at&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}{{&lt;/span> &lt;span class="na">else&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">0&lt;/span>&lt;span class="err">{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">speed&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ if .Get &amp;#34;&lt;/span>&lt;span class="na">speed&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}{{&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Get&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">speed&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}{{&lt;/span> &lt;span class="na">else&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">1&lt;/span>&lt;span class="err">{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">{{&lt;/span> &lt;span class="na">if&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Get&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">idle-time-limit&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">idle-time-limit&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Get &amp;#34;&lt;/span>&lt;span class="na">idle-time-limit&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&amp;#34;{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">{{&lt;/span> &lt;span class="na">if&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Get&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">poster&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">poster&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Get &amp;#34;&lt;/span>&lt;span class="na">poster&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&amp;#34;{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">{{&lt;/span> &lt;span class="na">if&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Get&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">font-size&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">font-size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Get &amp;#34;&lt;/span>&lt;span class="na">font-size&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&amp;#34;{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">{{&lt;/span> &lt;span class="na">if&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Get&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">theme&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">theme&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Get &amp;#34;&lt;/span>&lt;span class="na">theme&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&amp;#34;{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">{{&lt;/span> &lt;span class="na">if&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Get&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">title&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">title&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Get &amp;#34;&lt;/span>&lt;span class="na">title&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&amp;#34;{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">{{&lt;/span> &lt;span class="na">if&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Get&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">author&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">author&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Get &amp;#34;&lt;/span>&lt;span class="na">author&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&amp;#34;{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">{{&lt;/span> &lt;span class="na">if&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Get&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">author-url&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">author-url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Get &amp;#34;&lt;/span>&lt;span class="na">author-url&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&amp;#34;{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">{{&lt;/span> &lt;span class="na">if&lt;/span> &lt;span class="err">.&lt;/span>&lt;span class="na">Get&lt;/span> &lt;span class="err">&amp;#34;&lt;/span>&lt;span class="na">author-img-url&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&lt;/span>&lt;span class="na">author-img-url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;{{ .Get &amp;#34;&lt;/span>&lt;span class="na">author-img-url&lt;/span>&lt;span class="err">&amp;#34;&lt;/span> &lt;span class="err">}}&amp;#34;{{&lt;/span> &lt;span class="na">end&lt;/span> &lt;span class="err">}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">asciinema-player&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">p&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Finally create a new post and set &lt;code>asciinema = true&lt;/code> in the metadata (for toml)&lt;/li>
&lt;li>And embed the asciinema cast as follows in the body part&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{{&lt;span class="p">&amp;lt;&lt;/span> &lt;span class="nt">asciinema&lt;/span> &lt;span class="na">key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;demo-folder/demo-cast.cast&amp;#34;&lt;/span> &lt;span class="na">rows&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;35&amp;#34;&lt;/span> &lt;span class="na">preload&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;1&amp;#34;&lt;/span> &lt;span class="p">&amp;gt;&lt;/span>}}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>And don&amp;rsquo;t forget to put your .cast file in &lt;code>static/casts/demo-folder/democast.cast&lt;/code>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="first-attempt-at-the-task">First attempt at the task&lt;/h2>
&lt;p>
&lt;asciinema-player
src="https://kyteinsky.github.io/casts/embedding-asciinema-casts/embedding-asciinema-casts.cast"
cols="640"
rows="35"
preload="1"
start-at="0"
speed="1"
>&lt;/asciinema-player>
&lt;/p>
&lt;h2 id="correcting-the-mistakes-done-above">Correcting the mistakes done above&lt;/h2>
&lt;p>
&lt;asciinema-player
src="https://kyteinsky.github.io/casts/embedding-asciinema-casts/final.cast"
cols="640"
rows="35"
preload="1"
start-at="0"
speed="1"
>&lt;/asciinema-player>
&lt;/p>
&lt;p> &lt;/p>
&lt;h1 id="for-the-stack-theme-the-kind-developer-of-the-theme-zhixuan666httpsgithubcomzhixuan666-mentioned-a-more-efficient-way-to-do-it">For the Stack theme, the kind developer of the theme &lt;a class="link" href="https://github.com/zhixuan666" target="_blank" rel="noopener"
>@zhixuan666&lt;/a> mentioned a more efficient way to do it.&lt;/h1>
&lt;p>Here is his reply to the GitHub issue: &lt;a class="link" href="https://github.com/CaiJimmy/hugo-theme-stack/issues/221#issuecomment-859537347" target="_blank" rel="noopener"
>link&lt;/a>&lt;/p>
&lt;p> &lt;/p></description></item><item><title>New Hugo Website</title><link>https://kyteinsky.github.io/p/new-hugo-website/</link><pubDate>Tue, 01 Jun 2021 00:00:00 +0000</pubDate><guid>https://kyteinsky.github.io/p/new-hugo-website/</guid><description>&lt;img src="https://kyteinsky.github.io/p/new-hugo-website/hugo-logo-wide.svg" alt="Featured image of post New Hugo Website" />&lt;h3 id="we-create-a-new-hugo-website-in-terminal-and-install-a-custom-theme-named-stack-and-create-a-sample-page">We create a new Hugo website in terminal and install a custom theme named Stack and create a sample page&lt;/h3>
&lt;h4 id="outline-of-steps-to-be-performed">Outline of steps to be performed:&lt;/h4>
&lt;ul>
&lt;li>Create an empty hugo site with boilerplate code&lt;/li>
&lt;li>Initialize the site as a git repository&lt;/li>
&lt;li>Add the theme as a submodule to receive updates automatically, can be just cloned in place or downloaded and extracted at the right place manually&lt;/li>
&lt;li>Copy some example site&amp;rsquo;s posts and config file to the root folder of the website to edit them without disturbing the original theme files (these files override the respective default ones)&lt;/li>
&lt;li>Edit the config.yaml file as per requirements, and delete the config.toml file that was placed by hugo&lt;/li>
&lt;li>Create a new post with sone markdown text&lt;/li>
&lt;li>host the site locally&lt;/li>
&lt;/ul>
&lt;h4 id="this-might-be-the-right-moment-to-commit-the-changes-as-starting-point-of-the-repository">This might be the right moment to commit the changes as starting point of the repository.&lt;/h4>
&lt;h3 id="here-is-the-asciinema-terminal-cast">Here is the asciinema terminal cast:&lt;/h3>
&lt;p>
&lt;asciinema-player
src="https://kyteinsky.github.io/casts/new-hugo-site/hugo-setup.cast"
cols="640"
rows="40"
preload="1"
start-at="0"
speed="1"
>&lt;/asciinema-player>
&lt;/p></description></item></channel></rss>