<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Leo</title>
    <description>The latest articles on DEV Community by Leo (@leopf).</description>
    <link>https://dev.to/leopf</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1893595%2F21b06da0-dc11-4a3a-a880-91859b86bc3b.png</url>
      <title>DEV Community: Leo</title>
      <link>https://dev.to/leopf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/leopf"/>
    <language>en</language>
    <item>
      <title>Connecting streamtasks over the Internet</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Wed, 21 Aug 2024 12:26:46 +0000</pubDate>
      <link>https://dev.to/leopf/connecting-streamtasks-over-the-internet-1mmc</link>
      <guid>https://dev.to/leopf/connecting-streamtasks-over-the-internet-1mmc</guid>
      <description>&lt;p&gt;Streamtasks is build on a robust and flexible networking layer.&lt;/p&gt;

&lt;p&gt;The networking layer, unlike the classical IP network, does not distinguish between communication inside a process, between processes or between different machines. Everything is handled over a uniform network.&lt;/p&gt;

&lt;p&gt;This enables us to connect different computers running instances of streamtasks, to distribute data or workloads. &lt;/p&gt;

&lt;p&gt;One interesting application is the connection of two instances, which can not directly connect to each other over the IP protocol. &lt;/p&gt;

&lt;p&gt;This is when we need an intermediate server running in the cloud.&lt;/p&gt;

&lt;p&gt;One way to solve this problem, is by running a tunneling server like ngrok. We can also solve this problem with streamtasks directly.&lt;/p&gt;

&lt;p&gt;This demo will show, how you can run such an intermediate server and connect your instances over websockets. This will allow you to benefit from the software solutions present for websockets, like standardized encryption and nginx. &lt;/p&gt;

&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;p&gt;To securely send data over an internet connection we need to use encryption.&lt;/p&gt;

&lt;p&gt;We have great standards for this and don't need to reinvent the wheel.&lt;/p&gt;

&lt;p&gt;On the cloud server, you can install streamtasks and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;streamtasks &lt;span class="nt"&gt;--serve&lt;/span&gt; ws://127.0.0.1:9000 &lt;span class="nt"&gt;-C&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will run streamtasks and accept websocket connections received on 127.0.0.1:9000.&lt;/p&gt;

&lt;p&gt;In order to have encryption, we will use nginx as a proxy and use a TLS certificate to secure our connections.&lt;/p&gt;

&lt;p&gt;Our config could look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;yourdomain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# SSL Configuration&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/ssl/yourdomain.com.crt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/ssl/yourdomain.com.key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:9000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;# Proxy headers for WebSocket&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_http_version&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Upgrade&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Connection&lt;/span&gt; &lt;span class="s"&gt;"upgrade"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;# Other proxy headers (optional)&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Redirect HTTP to HTTPS&lt;/span&gt;
&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;yourdomain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;301&lt;/span&gt; &lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="nv"&gt;$host$request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Authentication
&lt;/h2&gt;

&lt;p&gt;Right now there is a rather primitive authentication method integrated into streamtasks. You can specify an authentication token in the URL that, that will be verified by the server. In order for this to work, you must specify this token in the URL for both the client and server.&lt;/p&gt;

&lt;p&gt;On the server side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;streamtasks &lt;span class="nt"&gt;--serve&lt;/span&gt; ws://127.0.0.1:9000?auth&lt;span class="o"&gt;=&lt;/span&gt;1234 &lt;span class="nt"&gt;-C&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will make sure only clients with the correct credentials are allowed to join.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting
&lt;/h2&gt;

&lt;p&gt;In order to connect to an instance, you must specify the connect URL when running the instance.&lt;/p&gt;

&lt;p&gt;In this case we do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;streamtasks &lt;span class="nt"&gt;--connect&lt;/span&gt; wss://yourdomain.com?auth&lt;span class="o"&gt;=&lt;/span&gt;1234
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try streamtasks!&lt;br&gt;
GitHub: &lt;a href="https://github.com/leopf/streamtasks" rel="noopener noreferrer"&gt;https://github.com/leopf/streamtasks&lt;/a&gt;&lt;br&gt;
Documentation/Homepage: &lt;a href="https://streamtasks.3-klicks.de" rel="noopener noreferrer"&gt;https://streamtasks.3-klicks.de&lt;/a&gt;&lt;br&gt;
X: &lt;a href="https://x.com/leopfff" rel="noopener noreferrer"&gt;https://x.com/leopfff&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nginx</category>
      <category>tunnel</category>
      <category>showdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Podcast Production with streamtasks</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Wed, 14 Aug 2024 09:33:11 +0000</pubDate>
      <link>https://dev.to/leopf/podcast-production-with-streamtasks-1h09</link>
      <guid>https://dev.to/leopf/podcast-production-with-streamtasks-1h09</guid>
      <description>&lt;p&gt;Podcasts are easy to produce. Let's make it even easier!&lt;/p&gt;

&lt;p&gt;Usually a podcast has multiple people speaking. Each with their own microphone and their own camera. To compose the podcast, most of the time, it will be cut based on who is speaking.&lt;/p&gt;

&lt;p&gt;In this example we will assume, that this podcast has two participants, a camera and a microphone for each, all plugged into one computer.&lt;/p&gt;

&lt;p&gt;Our goal is to automate the switching between participants then store the video for each participant and the composed video separately.&lt;/p&gt;

&lt;p&gt;We start off with two audio inputs. We mix these inputs using an audio mixer and then encode each audio stream (the stream for P1, P2 and for P1+P2).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq1hd62p8d68tddzn48ks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq1hd62p8d68tddzn48ks.png" alt="two audio inputs mixed and encoded" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next we store our encoded audio streams using an output container (as a MP4).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo54t0ma4b1971oqlh934.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo54t0ma4b1971oqlh934.png" alt="two mixed audio inputs stored as mp4 files" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is the audio side done. Now lets add some video. Like with the audio inputs we create video inputs and encode them as h264 video.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpit4w9a2450d0yckjlm5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpit4w9a2450d0yckjlm5.png" alt="two video inputs with encoders" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To store the video for each participant we add a video track to our output container and connect our encoded video to them. Lets start by just adding video tracks to the individual output files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcq34z9rikqu8uq6bp027.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcq34z9rikqu8uq6bp027.png" alt="storing the video in the individual outputs" width="800" height="597"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now lets do the exciting part. Lets switch between the video feeds and save that to our &lt;code&gt;p12.mp4&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;We start by adding a video track to our composed output container and add a media switch to our deployment. We connect each video feed to one of the inputs on the media switch. We connect the output of the switch to the video track input of our output container.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7fmh0sgyrzwuriivcopj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7fmh0sgyrzwuriivcopj.png" alt="adding a media switch" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right now the switch does nothing. To actually switch between video feeds we will use the volume of our microphones to set the control signal of the media switch. The media switch expects a number input on its control inputs and will switch to the input which has the highest value on its corresponding control input. So it will switch to "input 1" if "control 1" has the highest control value.&lt;/p&gt;

&lt;p&gt;To extract the audio volume from our microphones we use the audio volume meter task. It outputs a number representing the volume of the audio input. The volume output is then connected to the corresponding control inputs. "control 1" is for P1, which means we use the audio volume of the P1 audio data as the control signal for the video of P1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk0g5tvswcmokw9n0vy5x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk0g5tvswcmokw9n0vy5x.png" alt="working switching logic" width="800" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now our automatic switching works, but it is a little slow. Optimally you want to switch just before someone starts speaking. We can't look in the future, but we can look in the past.&lt;/p&gt;

&lt;p&gt;There are multiple ways of doing this. One way is to delay the video feeds by 1 second before we switch between them. Another way is to move our control signals into the future. We would actually just change the timestamp of the control messages, but the synchronizer in the media switch will then synchronize the streams, causing the video streams to be delayed by one second. This approach is better since we don't change the timestamps of the video, which we later use in our output container. If we changed the video time, we would need to change it back before putting it into the output container, otherwise the video will not be synchronized with the audio.&lt;/p&gt;

&lt;p&gt;This is the config of the timestamp updaters, moving the control messages 1000 ms in the future.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xdfk9t6eyibdm3hsi1a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xdfk9t6eyibdm3hsi1a.png" alt="timestamp updater config" width="576" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our final deployment looks like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr7xgnzl0io6r8qqxwfok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr7xgnzl0io6r8qqxwfok.png" alt="back in time switching works" width="800" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just like that! Withing minutes you can automate your podcasting setup.&lt;/p&gt;

&lt;p&gt;Try streamtasks!&lt;br&gt;
GitHub: &lt;a href="https://github.com/leopf/streamtasks" rel="noopener noreferrer"&gt;https://github.com/leopf/streamtasks&lt;/a&gt;&lt;br&gt;
Documentation/Homepage: &lt;a href="https://streamtasks.3-klicks.de" rel="noopener noreferrer"&gt;https://streamtasks.3-klicks.de&lt;/a&gt;&lt;br&gt;
X: &lt;a href="https://x.com/leopfff" rel="noopener noreferrer"&gt;https://x.com/leopfff&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>opensource</category>
      <category>podcast</category>
      <category>productivity</category>
    </item>
    <item>
      <title>AI Integration with streamtasks</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Thu, 08 Aug 2024 05:29:48 +0000</pubDate>
      <link>https://dev.to/leopf/ai-integration-with-streamtasks-4f40</link>
      <guid>https://dev.to/leopf/ai-integration-with-streamtasks-4f40</guid>
      <description>&lt;p&gt;Streamtasks empowers you to configure real-time data pipelines with ease. By leveraging these capabilities, you can build your video production pipelines in software, seamlessly integrating tools like AI that would otherwise be hard to integrate.&lt;/p&gt;

&lt;p&gt;With Streamtasks, the possibilities for incorporating AI into your workflows are vast and varied. I'll showcase some of the ways to harness the power of AI with streamtasks.&lt;/p&gt;

&lt;p&gt;The AI tasks are not integrated into the MSI and Flatpak installers, since they would use up to much space and make the installers explode in size. To use the AI tasks, you must install streamtasks manually with pip, as described in the &lt;a href="https://leopf.github.io/streamtasks/" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;streamtasks[media,inference]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Improving Speech
&lt;/h2&gt;

&lt;p&gt;Two tasks are available to enhance the quality of your audio data: Spectral Mask Enhancement and Waveform Speech Enhancement. Both tasks remove background noise from the audio you supply to them, improving its overall quality.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Spectral Mask Enhancement uses spectral mask enhancement techniques to boost speech clarity. It effectively removes background noise and produces consistent results, but may occasionally introduce additional noise.&lt;/li&gt;
&lt;li&gt;Waveform Speech Enhancement is skilled at eliminating background noise, but sometimes truncates the speech signal.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These tasks only accept mono channel, floating point audio data at 16 kHz.️ The SpeechBrain library is used to make speech enhancement work. If you would like to use different models or explore how it works, I recommend visiting the &lt;a href="https://huggingface.co/speechbrain" rel="noopener noreferrer"&gt;SpeechBrain Huggingface&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the following example we use an audio input, like a microphone, resample its audio data to mono 16 kHz and apply speech enhancement, before we output it using an audio output. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warning: do not do this if there is a feedback loop between your audio output and input (between your speakers and your microphone).&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;In addition we have an audio switch and a radio button UI in front of our audio output, allowing us to switch between the different enhancement methods and test what the different options sound like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0eok6edfpsfi61b1uno.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0eok6edfpsfi61b1uno.png" alt="switching between speech enhancements" width="800" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Live Transcription
&lt;/h2&gt;

&lt;p&gt;In addition to speech enhancement SpeechBrain also provides us with great ways of integrating automatic speech recognition (ASR). &lt;/p&gt;

&lt;p&gt;We can use the &lt;em&gt;Asr Speech Recognition&lt;/em&gt; task to transform our audio data into a text transcription. Like the speech enhancement tasks, it only accepts mono channel floating point audio data at 16 kHz.&lt;/p&gt;

&lt;p&gt;In this example, we take data from an audio input, resample it, transcribe it, and then display the resulting transcription using a text display.️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsulxjph9mt2yiyo3de0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsulxjph9mt2yiyo3de0.png" alt="asr to text display" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To boost the performance of our speech recognition system, we enhance the quality of speech first. We then employ a switch to toggle through various enhancement methods and determine which one yields the best results.️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwrfsm5002e8mpbpeyr28.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwrfsm5002e8mpbpeyr28.png" alt="speech enhancement deployment" width="800" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I create a dashboard and then arrange the radio buttons and text display in a way that makes it easy to test. In my case, it looks something like this:️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgxalc36m9p7fb9qd19yd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgxalc36m9p7fb9qd19yd.png" alt="speech enhancement dashboard" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Text to Speech
&lt;/h2&gt;

&lt;p&gt;We can also utilize speech brain's models for text-to-speech functionality.&lt;/p&gt;

&lt;p&gt;Let's give it a try!&lt;/p&gt;

&lt;p&gt;In this example, I'm using a text input - a text field with a send button - and connecting it to the &lt;em&gt;Tts Fast Speech 2&lt;/em&gt; task. This task will then generate audio data, which we'll output through an audio output.️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsc6tuw6uy21fb18qnmvd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsc6tuw6uy21fb18qnmvd.png" alt="tts deployment simple" width="800" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  LLaMA.cpp
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/SK1uyyu2noU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;We can utilize LLaMA.cpp (GGUF) models alongside SpeechBrain models. The &lt;em&gt;Llama.cpp Chat&lt;/em&gt; task processes text data, generating text outputs as a chat assistant. User messages serve as input, while the assistant's responses are outputted. To test this functionality, we'll employ the Text Input and Text Display tasks as input and output.️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn643th49k3ko2ide2s8w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn643th49k3ko2ide2s8w.png" alt="llamacpp chat simple" width="800" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After sending the text "Hello!" to our &lt;em&gt;Llama.cpp Chat&lt;/em&gt; task, we get this output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9rn9k9m2pso5qfp1p1ln.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9rn9k9m2pso5qfp1p1ln.png" alt="llamacpp chat simple dashboard" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It works!&lt;/p&gt;

&lt;h2&gt;
  
  
  LLaMA.cpp + ASR + TTS
&lt;/h2&gt;

&lt;p&gt;Lets now try building something more complicated and put it all together to make a speaking and listening LLaMA chat bot.&lt;/p&gt;

&lt;p&gt;In order to create a proper message for our chat bot we have to concatenate the fragments of text the ASR task outputs. We use a &lt;em&gt;String Concatenator&lt;/em&gt; task to do this. It receives our text on "input" and a "control" signal. When the control signal goes from low to high (from something smaller than 0.5 to something greater than 0.5) it will output the concatenated text and clear its buffer. We use a &lt;em&gt;Message Detector&lt;/em&gt; task and a &lt;em&gt;Calculator&lt;/em&gt; task to create this control signal. The &lt;em&gt;Message Detector&lt;/em&gt; is configured to have a timeout of 2 seconds, which means that after not receiving any message for 2 seconds it will output 0 and while receiving text it will output 1. By using the calculator we can invert this signal and produce the desired control signal, which goes from 0 to 1 after not receiving a message for 2 seconds. We then use the output of the &lt;em&gt;String Concatenator&lt;/em&gt; as the input for our LLaMA chat bot. The chat bot output is transformed into speech using the TTS task, which is then played using an audio output.&lt;br&gt;
In addition, we have 3 text displays. One live display which appends all incoming text fragments and displays them, one for the actual chat bot input, and one for the output of our chat bot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8byq9dnolosxuv56hgpl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8byq9dnolosxuv56hgpl.png" alt="llama asr tts deployment" width="800" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We then create a dashboard to arrange our text displays.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0a6xxzd80lgt45h73y2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0a6xxzd80lgt45h73y2.png" alt="llama asr tts dashboard" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We've finally achieved our goal of creating a conversational AI - a chat bot that allows us to engage in discussions with it.️&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/j6mSyNTFCM4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future
&lt;/h2&gt;

&lt;p&gt;I'm planning to implement more advanced models into streamtasks, including Whisper and Bark, as well as image generating and segmentation models that are currently missing. These new models will expand the possibilities with the system.&lt;/p&gt;

&lt;p&gt;To further integrate AI-generated data with multimedia content, I plan to add support for subtitles to the Output Container task, allowing users to include live transcriptions from ASR tasks in their live streams and video files.&lt;/p&gt;

&lt;p&gt;Portability is a priority, as machine learning frameworks are often too large to be included in prebuilt installers. However, emerging frameworks like &lt;a href="https://github.com/tinygrad/tinygrad" rel="noopener noreferrer"&gt;tinygrad&lt;/a&gt; offer smaller sizes that can be reasonably included, making it easier for less technical users to install. I'm planning to eventually switch all inference tasks to one framework, which will further simplify the installation process.️&lt;/p&gt;

&lt;p&gt;Try streamtasks!&lt;br&gt;
GitHub: &lt;a href="https://github.com/leopf/streamtasks" rel="noopener noreferrer"&gt;https://github.com/leopf/streamtasks&lt;/a&gt;&lt;br&gt;
Documentation: &lt;a href="https://streamtasks.3-klicks.de" rel="noopener noreferrer"&gt;https://streamtasks.3-klicks.de&lt;/a&gt;&lt;br&gt;
X: &lt;a href="https://x.com/leopfff" rel="noopener noreferrer"&gt;https://x.com/leopfff&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Video Production with Streamtasks</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Wed, 07 Aug 2024 01:35:14 +0000</pubDate>
      <link>https://dev.to/leopf/video-production-with-streamtasks-36hl</link>
      <guid>https://dev.to/leopf/video-production-with-streamtasks-36hl</guid>
      <description>&lt;p&gt;Since the inception of video production, we have faced a significant challenge: processing vast amounts of data. Initially, computers were too slow, leading to the emergence of an industry focused on building specialized hardware boxes for various tasks such as encoding, decoding, transmitting, receiving, switching, and displaying video. These boxes became indispensable, making video production an expensive endeavor.&lt;/p&gt;

&lt;p&gt;Today, however, we have powerful general-purpose hardware, such as GPUs and CPUs, capable of handling the video and audio data we need to process. But the industry is still behind on this. Why do video encoder boxes still exist when GPUs can perform these tasks?&lt;/p&gt;

&lt;h2&gt;
  
  
  So what is the problem?
&lt;/h2&gt;

&lt;p&gt;Integration and Software. We have little bits and pieces of software that can do some things, but not others. For simple recording tasks, tools like OBS suffice. For more complex switching, tools like vMix are used. All those tools have one thing in common: They are not as flexible as plugging in some wires wherever you want. You are stuck with what is handed to you. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;We make boxes and wires in software. Instead of buying encoder boxes, you encode your video with little programs and connect these little programs with virtual wires. &lt;strong&gt;This it the idea of streamtasks.&lt;/strong&gt; You can even change what codec you want, what resolution, what pixel formats, whatever this little piece of software („Task“) allows you to change. To get a better understanding of how the software works I recommend watching the overview video. &lt;iframe width="710" height="399" src="https://www.youtube.com/embed/cMmoPQrjQ0M"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F005orgwa2f8pe7tm2e8h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F005orgwa2f8pe7tm2e8h.png" alt="Video input and encoder" width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Streamtasks allows you to connect topics, which are displayed as colored circles, with eachother. During operation, the task will process the input data in realtime. For example, a video encoder will encode the raw video it receives and then output the encoded video on the output topic. &lt;/p&gt;

&lt;p&gt;What you can do with streamtasks depends on two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the tasks available&lt;/li&gt;
&lt;li&gt;your imagination&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While I cannot improve your imagination, I can make tasks and simplify the process of creating new ones.&lt;br&gt;
Right now, there are over 50 different tasks integrated into streamtasks. These range from very simple to more complex and powerful tasks, many of which are documented &lt;a href="https://leopf.github.io/streamtasks/tasks/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The general idea is to give the user as much control as possible. If a task can be split into two simpler ones, it is usually a good idea. &lt;/p&gt;

&lt;p&gt;If you can write software, you can write your own tasks. The simplest task can be written in 4 lines of code (even less, if you really try). If you are interested in making your own tasks, I refer you to the documentation: &lt;a href="https://leopf.github.io/streamtasks/custom-task.html" rel="noopener noreferrer"&gt;https://leopf.github.io/streamtasks/custom-task.html&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  AI
&lt;/h2&gt;

&lt;p&gt;One of the greatest advancements of our time is AI, which has not yet been widely integrated into video production.  Streamtasks provides a strong framework for integrating AI and other kinds of software into your production pipelines. You can implement AI into you workflows today and build things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Live Audio Transcription&lt;/li&gt;
&lt;li&gt;Live Face Detection&lt;/li&gt;
&lt;li&gt;Face Tracking&lt;/li&gt;
&lt;li&gt;Text To Speech&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can watch this Demo of a chatbot with TTS to get an Idea of what is possible.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/j6mSyNTFCM4"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Distributing Data and Workloads
&lt;/h2&gt;

&lt;p&gt;While our computers are really fast today, there is a limit to their performance. In order to build larger production pipelines that process more data, you will have to distribute data and workload across multiple machines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is possible with streamtasks today&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can have mutliple instances of streamtasks running on different machines and connect them by either using the connection manager UI, or specifying a connect/serve url on the command line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# the main instance (started with -C) accepting tcp connections on 9002 &lt;/span&gt;
streamtasks &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="nt"&gt;--serve&lt;/span&gt; tcp://0.0.0.0:9002
&lt;span class="c"&gt;# a sub instance connecting to the main system (in this case running on the same machine)&lt;/span&gt;
streamtasks &lt;span class="nt"&gt;--connect&lt;/span&gt; tcp://127.0.0.1:9002
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thats it. Now you can create your pipelines with all tasks available on either machine. The only apparent difference between tasks running different machines is a dotted line connecting their topics.&lt;/p&gt;

&lt;p&gt;Each task available on both machines will now be displayed twice in the task selection. Once for each node.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1huo44zl0wfwhw7l83y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1huo44zl0wfwhw7l83y.png" alt="Task selection" width="297" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a result you can now start tasks on different machines and connect them seemlessly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxlf4fng60yp1vzl4j6xq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxlf4fng60yp1vzl4j6xq.png" alt="Audio mixing" width="800" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example we have two different audio inputs (like microphones) one running on node/machine "demo1" and the other running on "demo2". This audio is mixed and then played on an audio output on "demo1". Be cautious! The audio data from the audio input on demo2 is being sent over TCP. If you send raw video over a TCP connection you will likely not get the result you wish for. 1080p 30fps RGB24 video is 1492Mb/s of data. If you do not have enough bandwidth, it won't work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Distributing Workloads
&lt;/h3&gt;

&lt;p&gt;By having tasks run on different machines, you can distribute heavy workloads, like encoding video. This can be done while still extracting useful information like the amount of activity in a video (how many and how much pixels have changed), before the video is encoded.&lt;/p&gt;

&lt;p&gt;In the following example we have two video inputs (like cameras). One on each node. The video activity  is measured, in order to switch between the video feeds, and the video is encoded on each node. The encoded video data from demo2 is then sent over to demo1, where we switch between the video feeds based on their activity. The most active video is then displayed in a video viewer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fefthzs3k7iqc3r6k8vhk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fefthzs3k7iqc3r6k8vhk.png" alt="Video switching" width="800" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This allows us to use one machine as an encoder for each video feed. When using GPU Hardware encoders, each machine can usually encode much more than just one video feed. As we scale up, we will want to distribute the load across machines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Distributing Data
&lt;/h3&gt;

&lt;p&gt;Local internet connections are often very limited. Even if you are lucky to get 1Gb/s upload speed, it still does not compare to the kind of speed you can archive from a server running in a datacenter.&lt;/p&gt;

&lt;p&gt;When streaming to multiple streaming services at the same time, you might want to first send your video to a server in a datacenter and from there distribute it to multiple streaming services. &lt;/p&gt;

&lt;p&gt;In the following examples we have a video input running on our computer. The video data is encoded and then sent to 3 different output containers. These output containers will then each publish the video feed to a streaming service as a rtmp stream (see &lt;a href="https://www.youtube.com/watch?v=ZkH5p3I3e1M" rel="noopener noreferrer"&gt;the live streaming demo&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl1jqajq8wgo0w2zttobn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl1jqajq8wgo0w2zttobn.png" alt="Live Streaming to multiple Services" width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The streamtasks network is smart enough to only send the video feed once to our server running in the datacenter, despite there being three tasks receiving the data. As a result we are only using a third of the upload bandwidth on our local machine, while streaming to three different services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Dashboards
&lt;/h2&gt;

&lt;p&gt;The more complex your production pipelines are, the more oversight you need in order to monitor them. Streamtasks allows you to create dashboards that consist of windows, each window is the "display" of a task. This can be the video from a video viewer, the text of a text display, the switch of a switch UI.&lt;/p&gt;

&lt;p&gt;The following example is a kill switch. The timestamp updater moves the time of the video feed 5s into the future. When synchronized with the other video feeds this causes the actual feed to be pushed back 5s in time. That way you can preview the video in the live view and then switch the output to a static image 5s before it is displayed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvi3rqjxj5c6c2g6jhi6y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvi3rqjxj5c6c2g6jhi6y.png" alt="Kill switch deployment" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The video viewers each display a video, which can be arranged in the dashboard. The switch UI, used to turn on/off the video, displays a control element.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qsh5i69s3uys92mt3ss.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4qsh5i69s3uys92mt3ss.png" alt="Kill Switch Dashboard Off" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdk2nkyv469qvl5l8ti9l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdk2nkyv469qvl5l8ti9l.png" alt="Kill Switch Dashboard On" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  More Examples
&lt;/h2&gt;

&lt;p&gt;Since you are free to combine any tasks as you wish, you can built awesome things with it. Here are some examples:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can implement video switching by having multiple video streams and using a switch to switch between them. All you need to actually switch is a way of controlling the switch like radio buttons, a little UI that displays some options and lets you select one. The switch will always switches to the video feed that has the largest number as its control signal.
&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh4shjsnypqibe7beyt4d.png" alt="Video Switching" width="800" height="475"&gt;
&lt;/li&gt;
&lt;li&gt;Reducing the volume of ambient sound (like stadium microphones) when someone is speaking.
&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fof3r8c4edfkb61oxbtrd.png" alt="Audio Mixing" width="800" height="424"&gt;
&lt;/li&gt;
&lt;li&gt;Switching the video of podcasts by always switching to the loudest speaker
&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmcb5jthyins6jwvyzzrj.png" alt="Podcast Switching" width="800" height="380"&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=S6cEn3XuzyM" rel="noopener noreferrer"&gt;Playing sounds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=j6mSyNTFCM4" rel="noopener noreferrer"&gt;Creating chatbots that speak&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are interested in seeing more working examples, you can watch them on &lt;a href="https://www.youtube.com/@streamtasks/videos" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Try streamtasks!&lt;br&gt;
GitHub: &lt;a href="https://github.com/leopf/streamtasks" rel="noopener noreferrer"&gt;https://github.com/leopf/streamtasks&lt;/a&gt;&lt;br&gt;
Documentation: &lt;a href="https://streamtasks.3-klicks.de" rel="noopener noreferrer"&gt;https://streamtasks.3-klicks.de&lt;/a&gt;&lt;br&gt;
X: &lt;a href="https://x.com/leopfff" rel="noopener noreferrer"&gt;https://x.com/leopfff&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>videoproduction</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
