<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/rss.xsl" type="text/xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Тестовий сайт</title>
    <description>Тіпа, блоґ.</description>
    <link>https://test.de.co.ua</link>
    <atom:link href="https://test.de.co.ua/rss.xml" rel="self" type="application/rss+xml" />
    <copyright>© dk487</copyright>
    <lastBuildDate>Wed, 07 Oct 2026 22:09:40 +0300</lastBuildDate>
    <pubDate>Wed, 07 Oct 2026 22:09:40 +0300</pubDate>
    <ttl>21600</ttl>

    <item>
      <title>Щось я не вигрібаю</title>
      <description><![CDATA[

<p>Якийсь сьогодні важкий день.</p>

      ]]></description>
      <link>https://test.de.co.ua/2026/10/07/shchos-ya-ne-vyhribaiu.html</link>
      <guid>https://test.de.co.ua/2026/10/07/shchos-ya-ne-vyhribaiu.html</guid>
      <pubDate>Wed, 07 Oct 2026 22:09:12 +0300</pubDate>
    </item>

    <item>
      <title>Привіт, LaTeX!</title>
      <description><![CDATA[

<p>Ох, куди ж я лізу… Мені забракне мізків, сил та часу. Але все ж.</p>

<div class="language-make highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># make another.pdf
# DOC=another make
</span><span class="nv">DOC</span> <span class="o">?=</span> main

<span class="nl">all</span><span class="o">:</span> <span class="nf">$(DOC).pdf</span>

<span class="nl">.SECONDEXPANSION</span><span class="o">:</span>
<span class="nl">%.pdf</span><span class="o">:</span> <span class="nf">%.tex $$(wildcard %_*.tex) $$(wildcard %.bib)</span>
	latexmk <span class="nt">-xelatex</span> <span class="nt">-interaction</span><span class="o">=</span>nonstopmode <span class="nv">$&lt;</span>

<span class="nl">clean</span><span class="o">:</span> <span class="nf">purge</span>
	<span class="p">-</span><span class="nb">rm</span> <span class="nt">-fv</span> <span class="nt">--</span> <span class="nv">$(DOC)</span>.pdf

<span class="nl">purge</span><span class="o">:</span>
	<span class="p">-</span><span class="nb">rm</span> <span class="nt">-fv</span> <span class="nt">--</span> <span class="nf">$(</span><span class="nb">wildcard</span> <span class="k">*</span>.aux <span class="k">*</span>.bbl <span class="k">*</span>.bcf <span class="k">*</span>.blg <span class="k">*</span>.log <span class="se">\</span>
	<span class="k">*</span>.run.xml <span class="k">*</span>.toc <span class="k">*</span>.fls <span class="k">*</span>.fdb_latexmk <span class="k">*</span>.xdv <span class="k">*</span><span class="nt">-SAVE-ERROR</span><span class="nf">)</span>

<span class="c"># xdg-mime query default application/pdf
# xdg-mime default qpdfview.desktop 'application/pdf'
</span><span class="nl">open</span><span class="o">:</span> <span class="nf">all</span>
	open <span class="nv">$(DOC)</span>.pdf

<span class="nl">.PHONY</span><span class="o">:</span> <span class="nf">all clean purge open</span>
</code></pre></div></div>

      ]]></description>
      <link>https://test.de.co.ua/2026/09/12/hello-latex.html</link>
      <guid>https://test.de.co.ua/2026/09/12/hello-latex.html</guid>
      <pubDate>Sat, 12 Sep 2026 09:33:53 +0300</pubDate>
    </item>

    <item>
      <title>46</title>
      <description><![CDATA[

<p>.</p>

      ]]></description>
      <link>https://test.de.co.ua/2026/09/06/46.html</link>
      <guid>https://test.de.co.ua/2026/09/06/46.html</guid>
      <pubDate>Sun, 06 Sep 2026 10:19:57 +0300</pubDate>
    </item>

    <item>
      <title>Про драконів</title>
      <description><![CDATA[

<p>Дракони <a href="/2025/03/14/pro-demoniv.html">теж</a> існують.</p>


      ]]></description>
      <link>https://test.de.co.ua/2026/08/25/pro-drakoniv.html</link>
      <guid>https://test.de.co.ua/2026/08/25/pro-drakoniv.html</guid>
      <pubDate>Tue, 25 Aug 2026 22:02:19 +0300</pubDate>
    </item>

    <item>
      <title>RTSP та WebRTC</title>
      <description><![CDATA[

<p>У мене вже був пост <a href="/2025/10/20/rtmp.html">про RTMP</a>, тепер буде про RTSP.</p>

<p>Це я відкрив для себе офігезний проект <a href="https://mediamtx.org/">MediaMTX</a>. Там в документації вже є все потрібне. Але про всяк випадок зберу докупи свої знахідки, щоб не забути.</p>

<p>Загальна концепція: є передавач відеопотоку, є отримувач, між ними є медіасервер. Це може бути три різних компа. Може два, якщо медіасервер запустити там же, де й передавач. Для перших експериментів, нехай це буде один localhost.</p>

<p>Моя мета: <em>дивитися через веб-камеру на блимаючий світлодіод</em>, ха-ха.</p>

<h2 id="запуск-сервера">Запуск сервера</h2>

<p>Запускаю <a href="https://mediamtx.org/docs/kickoff/install#docker-image">MediaMTX в Docker</a>, якось так, як описано в документації. (Звісно, замість <code class="language-plaintext highlighter-rouge">192.168.xx.xx</code> вказую свою IP-адресу в локальній мережі). Це достатньо надійний варіант.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run <span class="nt">--rm</span> <span class="nt">-it</span> <span class="se">\</span>
  <span class="nt">-e</span> <span class="nv">MTX_RTSPTRANSPORTS</span><span class="o">=</span>tcp <span class="se">\</span>
  <span class="nt">-e</span> <span class="nv">MTX_WEBRTCADDITIONALHOSTS</span><span class="o">=</span>192.168.xx.xx <span class="se">\</span>
  <span class="nt">-p</span> 8554:8554 <span class="se">\</span>
  <span class="nt">-p</span> 1935:1935 <span class="se">\</span>
  <span class="nt">-p</span> 8888:8888 <span class="se">\</span>
  <span class="nt">-p</span> 8889:8889 <span class="se">\</span>
  <span class="nt">-p</span> 8890:8890/udp <span class="se">\</span>
  <span class="nt">-p</span> 8189:8189/udp <span class="se">\</span>
  bluenviron/mediamtx:1
</code></pre></div></div>

<p>Або, якщо можу собі дозволити, то запускаю ось так:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run <span class="nt">--rm</span> <span class="nt">-it</span> <span class="nt">--network</span><span class="o">=</span>host bluenviron/mediamtx:1
</code></pre></div></div>

<p>Також я запускав <a href="https://github.com/bluenviron/mediamtx/releases">готові бінарники MediaMTX</a> на своїй старенькій Raspberry Pi Model 2. Ок норм.</p>

<h2 id="трансляція-rtsp">Трансляція RTSP</h2>

<p>Штатна <a href="https://mediamtx.org/docs/publish/generic-webcams">документація пропонує</a> запускати <code class="language-plaintext highlighter-rouge">ffmpeg</code> прямо під час запуску MediaMTX. Мені більше до смаку тримати медіасервер постійно запущеним, а трансляції розпочинати окремою командою.</p>

<p>Ось варіант команди, який транслює відео з веб-камери, використовуючи кодек H.264:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ffmpeg <span class="nt">-f</span> v4l2 <span class="nt">-framerate</span> 30 <span class="nt">-video_size</span> 640x480 <span class="nt">-i</span> /dev/video0 <span class="se">\</span>
  <span class="nt">-pix_fmt</span> yuv420p <span class="nt">-c</span>:v libx264 <span class="nt">-preset</span> ultrafast <span class="nt">-b</span>:v 600k <span class="se">\</span>
  <span class="nt">-tune</span> zerolatency <span class="nt">-bf</span> 0 <span class="nt">-g</span> 15 <span class="nt">-keyint_min</span> 30 <span class="se">\</span>
  <span class="nt">-f</span> rtsp rtsp://localhost:8554/mystream
</code></pre></div></div>

<p>Або те саме з апаратним прискоренням через <a href="https://en.wikipedia.org/wiki/Video_Acceleration_API">VAAPI</a>:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ffmpeg <span class="nt">-f</span> v4l2 <span class="nt">-framerate</span> 30 <span class="nt">-video_size</span> 640x480 <span class="nt">-i</span> /dev/video0 <span class="se">\</span>
  <span class="nt">-c</span>:v h264_vaapi <span class="nt">-vaapi_device</span> /dev/dri/renderD128 <span class="se">\</span>
  <span class="nt">-vf</span> <span class="s1">'format=nv12,hwupload'</span> <span class="nt">-qp</span> 24 <span class="nt">-quality</span> 1 <span class="nt">-bf</span> 0 <span class="nt">-g</span> 30 <span class="se">\</span>
  <span class="nt">-f</span> rtsp rtsp://localhost:8554/mystream
</code></pre></div></div>

<p>Тут всюди 640×480, бо звичайні веб-камери майже ніколи не дають 30 кадрів на секунду на більших розмірах картинки. Чесно, мені для моїх задач той HD чи FullHD не дуже потрібний, а от відсутність гальм справді важлива.</p>

<p>Звісно, замість <code class="language-plaintext highlighter-rouge">localhost</code> може бути потрібна IP-адреса. Також шлях <code class="language-plaintext highlighter-rouge">/mystream</code> — це на ваш вибір, щоб якось вказати даний конкретний відеопотік. Через один медіасервер може одночасно йти декілька різних відеопотоків з різними шляхами.</p>

<p>Можна робити трансляцію з VP8, але не сьогодні, бо моя відеокарта не робе апаратне прискорення кодування саме для VP8 (ну або я не знайшов як), а софтварний енкодер жере проц. Після експериментів і роздумів, я вирішив зупинитися на H.264 як на оптимальному варіанті.</p>

<h2 id="перегляд-rtsp">Перегляд RTSP</h2>

<p>Ну, наприклад, можна якось так:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ffplay <span class="se">\</span>
  <span class="nt">-fflags</span> nobuffer <span class="se">\</span>
  <span class="nt">-flags</span> low_delay <span class="se">\</span>
  <span class="nt">-framedrop</span> <span class="se">\</span>
  <span class="nt">-probesize</span> 32 <span class="se">\</span>
  <span class="nt">-analyzeduration</span> 0 <span class="se">\</span>
  <span class="nt">-sync</span> ext <span class="se">\</span>
  <span class="nt">-rtsp_transport</span> udp <span class="se">\</span>
  rtsp://localhost:8554/mystream
</code></pre></div></div>

<p>Або так:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mpv rtsp://localhost:8554/mystream <span class="nt">--profile</span><span class="o">=</span>low-latency <span class="nt">--untimed</span>
</code></pre></div></div>

<h2 id="перегляд-webrtc">Перегляд WebRTC</h2>

<p>Просто відкриваю в браузері <code class="language-plaintext highlighter-rouge">http://localhost:8889/mystream/</code> і бачу там відео. Магія!</p>

<p>Є певний нюанс з кодеками. Chrome/Chromium нормально грає H.264, а Firefox його грати не хоче. Треба полізти в <code class="language-plaintext highlighter-rouge">about:config</code>, знайти там <code class="language-plaintext highlighter-rouge">media.webrtc.hw.h264.enabled</code> і увімкнути його. Саме через це я експериментував з VP8, але вирішив що не на часі.</p>

<h2 id="вбудувати-стрім-у-довільну-веб-сторінку">Вбудувати стрім у довільну веб-сторінку</h2>

<p>Теж все робиться просто і зрозуміло, <a href="https://mediamtx.org/docs/other/embed-streams-in-a-website">як в документації</a>:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;iframe</span> <span class="na">src=</span><span class="s">"http://localhost:8889/mystream"</span> <span class="na">scrolling=</span><span class="s">"no"</span><span class="nt">&gt;&lt;/iframe&gt;</span>
</code></pre></div></div>

<p>Можна додати CSS за смаком:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;iframe</span>
 <span class="na">src=</span><span class="s">"http://localhost:8889/mystream"</span>
 <span class="na">style=</span><span class="s">"border: none;
        aspect-ratio: 4 / 3;
        width: 100%;
        height: 100%;
        max-width: 100vw;
        max-height: 100vh"</span>
 <span class="na">scrolling=</span><span class="s">"no"</span><span class="nt">&gt;&lt;/iframe&gt;</span>
</code></pre></div></div>

<p>Для дашборда Node-RED можна створити UI Template node і прямо туди це все покласти. Власне, саме задля цього я занурився у ці нетрі: дуже захотілося поєднати телеметрію та елементи керування моїх саморобних IoT проектів з живим відео. І це заслуговує окремого поста.</p>

<h2 id="додаткова-краса">Додаткова краса</h2>

<p>Коли трансляція не ведеться, можна <a href="https://mediamtx.org/docs/other/always-available">показувати</a> щось красиве, наприклад телевізійну тест-таблицю <a href="https://en.wikipedia.org/wiki/SMPTE_color_bars">SMPTE</a> чи <a href="https://en.wikipedia.org/wiki/Philips_circle_pattern">Philips</a>. Для цього треба з картинки зробити відео довжиною, гммм, 1 секунду.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget https://upload.wikimedia.org/wikipedia/commons/6/66/SMPTE_Color_Bars.svg
rsvg-convert SMPTE_Color_Bars.svg <span class="nt">-w</span> 640 <span class="nt">-h</span> 480 <span class="o">&gt;</span> smpte.png
ffmpeg <span class="nt">-loop</span> 1 <span class="nt">-framerate</span> 30 <span class="nt">-t</span> 1 <span class="nt">-i</span> smpte.png <span class="nt">-c</span>:v libx264 <span class="nt">-pix_fmt</span> yuv420p smpte.mp4
</code></pre></div></div>

<p>Або так:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget https://upload.wikimedia.org/wikipedia/commons/a/aa/Philips_PM5544.svg
rsvg-convert Philips_PM5544.svg -w 640 -h 480 &gt; pm5544.png
ffmpeg -loop 1 -framerate 30 -t 1 -i pm5544.png -c:v libx264 -pix_fmt yuv420p pm5544.mp4
</code></pre></div></div>

<p>Власне, далі треба буде лізти в конфігураційний файл <code class="language-plaintext highlighter-rouge">mediamtx.yml</code>, і якщо у вас MediaMTX все ще запускається через Docker, то вам доведеться з цим розбиратися окремо.</p>

<p>Просто дописую в кінець таку секцію:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>paths:
  mystream:
    alwaysAvailable: true
    alwaysAvailableFile: "pm5544.mp4"
</code></pre></div></div>

<h2 id="проброс-портів">Проброс портів</h2>

<p>Якщо є бажання поділитися своєю трансляцією в WebRTC з усім світом, то треба буде десь на роутері налаштувати port forwarding:</p>

<ul>
  <li>8889/TCP → 192.168.xx.xx:8889</li>
  <li>8189/UDP → 192.168.xx.xx:8189</li>
</ul>

<p>Далі дивлюся трансляцію так само, тільки в якості хоста вказую реальну IP-адресу.</p>

<h2 id="відкриті-питання">Відкриті питання</h2>

<p>Треба буде розібратися з питаннями безпеки:</p>

<ol>
  <li>Обмежити можливість трансляції, дозволити лише «кому треба»</li>
  <li>Обмежити можливість перегляду (JWT?)</li>
  <li>Убезпечити транспорт WebRTC від MitM (https?)</li>
</ol>

<p>Треба більше RTFM.</p>

<p>Треба всерйоз подумати, як би то його точніше виміряти затримку. Візуально бачу що вона відрізняється для різних конфігурацій, зокрема для різних кодеків і різних транспортів. Можливо, параметри запуску <code class="language-plaintext highlighter-rouge">ffmpeg</code> та <code class="language-plaintext highlighter-rouge">ffplay</code> можна ще вдосконалити.</p>

<h2 id="висновки">Висновки</h2>

<p>Маю офігенний інструмент в своєму арсеналі, от!</p>


      ]]></description>
      <link>https://test.de.co.ua/2026/03/29/mediamtx.html</link>
      <guid>https://test.de.co.ua/2026/03/29/mediamtx.html</guid>
      <pubDate>Sun, 29 Mar 2026 17:47:58 +0300</pubDate>
    </item>

    <item>
      <title>Привіт, світе!</title>
      <description><![CDATA[

<p>Дійшли руки написати перший пост в 2026 році.</p>

<p>Сказати мені нічого. Ну тобто насправді багато чого можу сказати, але 
не хочу. У мене в житті останнім часом багато подій, турбулентність.</p>

<p>Закинув свої хобі-проекти.</p>

<p>Одна з останніх штук, яку я колупав заради розваги та душевного спокою, 
була якась <a href="https://github.com/kastaneda/matchbox">«читалка» тексту</a> на мікроскопічних екранчиках SSD1306, 
типу 128×32 пікселів чи щось подібне; наразі з цієї «читалки» мало що 
є, окрім шрифта. (Насправді це мав бути переважно апаратний проект, але 
якось не склалося). <a href="https://mastodon.social/@dk487/115695108525799226">Є фото</a> читалки у мене в Mastodon. Може колись 
я продовжу цей проект.</p>

<p>Колись один мій друг зробив сам для себе <a href="https://github.com/diggya/bookreader">книжкочиталку на J2ME</a>. 
Він читав усякі книжки на чорно-білих кнопкових телефонах в ті часи, 
коли смартфони ще не винайшли, а КПК були дорогими і незручними. Мій 
проект частково натхненний тим його досвідом.</p>


      ]]></description>
      <link>https://test.de.co.ua/2026/01/18/hello-world.html</link>
      <guid>https://test.de.co.ua/2026/01/18/hello-world.html</guid>
      <pubDate>Sun, 18 Jan 2026 20:48:49 +0200</pubDate>
    </item>

    <item>
      <title>Типу, привіт</title>
      <description><![CDATA[

<p>Давно не писав. Занурився у повсякденні дрібниці.</p>

<p>По приколу пишу ці рядки через вбудовану у GitHub веб-версію VS Code, та що запускається по натисканню на кнопку <kbd>.</kbd> і це по-своєму прикольно.</p>

<p>Хоча і тупо. Я міг би без гальм і без споживання зайвого трафіку написати цей текст у Vim на найменшому зі своїх ноутів, і теж було б непогано.</p>

<div lang="en">
  <blockquote>
    <p><em>Braxton:</em>
Fucking grown man wearing a Timex, dude.
He was fucking pathetic. Look at that.
See that?..
It’s worth more than he made in a year.
What do you think he’d say about this?</p>

    <p><em>Christian:</em>
He would say it’s the same time on his watch.</p>
  </blockquote>
</div>

<p>Гм. Треба буде якось написати пост про економію ресурсів. Щось у цьому є таке, що складно висловити словами. Мені не важко запустити VSCode в браузері і не важко запустити його локально. Зазвичай я пишу текст і код в Geany. Але часто буваю в Vim. Чому так?</p>

<p>Це не тільки питання зручності. Я відчуваю, що Vim мене може врятувати, умовно кажучи, «на пустельному острові». (Або навіть не повноцінний потужний Vim, а його злий брат-близнюк <code class="language-plaintext highlighter-rouge">busybox vi</code>, у якого не все добре з Unicode). О, до речі, про свої експерименти з Debian-Live теж треба написати.</p>

<p>Колись про все це напишу.</p>

<p><em>Оновлення 12:07.</em> Щодо економії: іноді кількість переходить в якість. Можливість досягати мети різними шляхами, в тому числі короткими і мінімалістичними, дає свободу вибору.</p>

      ]]></description>
      <link>https://test.de.co.ua/2025/12/03/hi-there.html</link>
      <guid>https://test.de.co.ua/2025/12/03/hi-there.html</guid>
      <pubDate>Wed, 03 Dec 2025 10:00:00 +0200</pubDate>
    </item>

    <item>
      <title>RTMP</title>
      <description><![CDATA[

<p>Привіт світ. Продовжую вивчати прості і всім відомі штуки. Сьогодні взнав як зробити собі трансляцію по RTMP, і напишу про це (переважно сам для себе) короткий брудний howto.</p>

<p>Одразу уточнюю: 1) не через OBS Studo, 2) не на YouTube.</p>

<p>Я просто хочу дивитися відео зі своєї веб-камери з мінімальною затримкою. Комп з камерою і комп для перегляду обидва знаходяться в локальній мережі. Ніякого інтерактивного GUIшного софта, тільки ffmpeg та інші scriptable консольні програми.</p>

<p>Раніше я знав лише один супер-простий метод, про який <a href="/2022/08/05/ssh-webcam.html">писав колись</a>:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh <span class="nt">-Y</span> foohost mplayer <span class="nt">-vo</span> x11 tv://
</code></pre></div></div>

<p>(Не робіть так, це страшенні гальма + великий overhead).</p>

<h2 id="як-можна-зробити-краще">Як можна зробити краще</h2>

<p>Погнали.</p>

<p>В принципі у нас може бути три різних хоста: комп з камерою, комп для перегляду і проміжний хост з RTMP-сервером. В моєму випадку RTMP-сервер може бути там же, де знаходиться камера.</p>

<h3 id="сервер">Сервер</h3>

<p>Перше, що нам треба — запустити сервер RTMP. Можна зробити RTMP-сервер через Nginx та <code class="language-plaintext highlighter-rouge">mod_rtmp</code>. А у найпростіших випадках (і щоб погратися) достатньо взяти готовий контейнер:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run <span class="nt">--rm</span> <span class="nt">-p</span> 1935:1935 tiangolo/nginx-rtmp 
</code></pre></div></div>

<p>Йому треба лише 1935 порт.</p>

<p>Припустимо, що це хост з адресою <code class="language-plaintext highlighter-rouge">192.168.0.99</code>, тоді адреса нашої трансляції буде <code class="language-plaintext highlighter-rouge">rtmp://192.168.0.99/live/123</code> (де <code class="language-plaintext highlighter-rouge">/live/123</code> це довільний обраний мною шлях).</p>

<h3 id="камера">Камера</h3>

<p>Мені потрібна трансляція без звуку. Нехай це камера <code class="language-plaintext highlighter-rouge">/dev/video0</code>. Тоді на компі з камерою запускаємо таку магію, де крім <code class="language-plaintext highlighter-rouge">/dev/video0</code> та адреси трансляції для початку краще нічого не чіпати:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ffmpeg <span class="nt">-f</span> v4l2 <span class="nt">-i</span> /dev/video0 <span class="nt">-c</span>:v libx264 <span class="se">\</span>
  <span class="nt">-pix_fmt</span> yuv420p <span class="nt">-framerate</span> 15 <span class="nt">-g</span> 30 <span class="nt">-b</span>:v 500k <span class="se">\</span>
  <span class="nt">-preset</span> ultrafast <span class="nt">-tune</span> zerolatency <span class="se">\</span>
  <span class="nt">-f</span> flv rtmp://192.168.0.99/live/123
</code></pre></div></div>

<p>Я нагуглив <a href="https://www.baeldung.com/linux/ffmpeg-webcam-stream-video">перший-ліпший випадковий howto</a> і прибрав згадки про звук. Якщо вам буде треба звук (і якщо у вас ALSA), то йдіть подивіться оригінальний пост.</p>

<h3 id="перегляд">Перегляд</h3>

<p>Ну найпростіше, що можна зробити, це щось типу такого:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mplayer rtmp://192.168.0.99/live/123
</code></pre></div></div>

<p>Воно працює. Але ну його до біса! Затримка більше 10 секунд мені абсолютно не підходить. Ось кращий варіант:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mpv <span class="nt">--no-cache</span> <span class="nt">--untimed</span> <span class="nt">--no-demuxer-thread</span> <span class="se">\</span>
  <span class="nt">--video-sync</span><span class="o">=</span>audio <span class="nt">--vd-lavc-threads</span><span class="o">=</span>1 <span class="se">\</span>
  rtmp://192.168.0.99/live/123
</code></pre></div></div>

<p>або такий:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ffplay <span class="nt">-autoexit</span> <span class="nt">-flags</span> low_delay <span class="nt">-framedrop</span> <span class="se">\</span>
  <span class="nt">-strict</span> experimental <span class="nt">-vf</span> <span class="nv">setpts</span><span class="o">=</span>0 <span class="nt">-tcp_nodelay</span> 1 <span class="se">\</span>
  rtmp://192.168.0.99/live/123
</code></pre></div></div>

<p>І от тут, нарешті, я починаю бачити в цьому всьому профіт. Я можу дивитися в камеру+екран як в дзеркало і не відчувати затримку.</p>

<p>P.S. Що я не знайшов, так це нормального рішення для перегляду стріму на Android. Запускаю там VLC, воно все працює, але лаг 15 секунд (upd.: <a href="https://code.videolan.org/videolan/vlc/-/issues/15597">вони про це знають і їм похуй</a>). Може хтось порадить рішення краще.</p>

<p>Upd.: ще я нагуглив якийсь стрьомний <a href="https://play.google.com/store/apps/details?id=com.softvelum.sldp_player">Larix Player</a>, це дуже мерзенний freemium з обмеженням у 5 хвилин перегляду відео на безкоштовній версії. З того, що я спробував і що вдалося змусити працювати на burner phone, у цього додатку поки що виявився найменший лаг. Треба ще пошукати.</p>


      ]]></description>
      <link>https://test.de.co.ua/2025/10/20/rtmp.html</link>
      <guid>https://test.de.co.ua/2025/10/20/rtmp.html</guid>
      <pubDate>Mon, 20 Oct 2025 13:17:41 +0300</pubDate>
    </item>

    <item>
      <title>Темна ніч та функція show_source()</title>
      <description><![CDATA[

<p>Швидкий брудний пост. Я під час розробки на PHP іноді користуюсь <a href="https://www.php.net/manual/uk/function.show-source.php"><code class="language-plaintext highlighter-rouge">show_source()</code></a>. Ну, знаєте, буває треба показати код і при цьому <em>хоч якось</em> підсвітити синтаксис, не встановлюючи тонну зовнішніх залежностей.</p>

<p>Так, це анахронізм часів PHP4. І так, за замовчанням кольори там розраховані на світле тло. Якщо у вас сайт з темним фоном, то можна <a href="https://www.php.net/manual/uk/misc.configuration.php#ini.syntax-highlighting">поміняти кольори</a> на щось відповідне, але це все трошки бісить.</p>

<p>А що як у нас сучасна адаптивна кольорова схема?</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"color-scheme"</span> <span class="na">content=</span><span class="s">"light dark"</span><span class="nt">&gt;</span>
</code></pre></div></div>

<p>Тоді у нас взагалі не може бути фіксованих кольорів, бо має одночасно існувати і варіант для темної схеми, і варіант для світлої.</p>

<p>Спробував через <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/var">змінні CSS</a>. Погнали.</p>

<p>Пишемо в <code class="language-plaintext highlighter-rouge">php.ini</code> щось таке:</p>

<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="py">highlight.default</span> <span class="p">=</span> <span class="s">"var(--php-default)"</span>
<span class="py">highlight.html</span> <span class="p">=</span> <span class="s">"var(--php-html)"</span>
<span class="py">highlight.keyword</span> <span class="p">=</span> <span class="s">"var(--php-keyword)"</span>
<span class="py">highlight.string</span> <span class="p">=</span> <span class="s">"var(--php-string)"</span>
<span class="py">highlight.comment</span> <span class="p">=</span> <span class="s">"var(--php-comment)"</span>
</code></pre></div></div>

<p>Або, якщо змінювати <code class="language-plaintext highlighter-rouge">php.ini</code> незручно, робимо це в runtime в PHP:</p>

<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">foreach</span> <span class="p">([</span><span class="s1">'default'</span><span class="p">,</span> <span class="s1">'html'</span><span class="p">,</span> <span class="s1">'keyword'</span><span class="p">,</span> <span class="s1">'string'</span><span class="p">,</span> <span class="s1">'comment'</span><span class="p">]</span> <span class="k">as</span> <span class="nv">$key</span><span class="p">)</span> <span class="p">{</span>
    <span class="nb">ini_set</span><span class="p">(</span><span class="s1">'highlight.'</span> <span class="mf">.</span> <span class="nv">$key</span><span class="p">,</span> <span class="s1">'var(--php-'</span> <span class="mf">.</span> <span class="nv">$key</span> <span class="mf">.</span> <span class="s1">')'</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>І тепер нам треба приблизно отакий CSS:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">:root</span> <span class="p">{</span>
  <span class="py">--php-comment</span><span class="p">:</span> <span class="m">#f80</span><span class="p">;</span>
  <span class="py">--php-default</span><span class="p">:</span> <span class="m">#00b</span><span class="p">;</span>
  <span class="py">--php-html</span><span class="p">:</span> <span class="m">#000</span><span class="p">;</span>
  <span class="py">--php-keyword</span><span class="p">:</span> <span class="m">#070</span><span class="p">;</span>
  <span class="py">--php-string</span><span class="p">:</span> <span class="m">#d00</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">@media</span> <span class="p">(</span><span class="n">prefers-color-scheme</span><span class="p">:</span> <span class="n">dark</span><span class="p">)</span> <span class="p">{</span> 
  <span class="nd">:root</span> <span class="p">{</span>
    <span class="py">--php-comment</span><span class="p">:</span> <span class="m">#860</span><span class="p">;</span>
    <span class="py">--php-default</span><span class="p">:</span> <span class="m">#66f</span><span class="p">;</span>
    <span class="py">--php-html</span><span class="p">:</span> <span class="m">#ccc</span><span class="p">;</span>
    <span class="py">--php-keyword</span><span class="p">:</span> <span class="m">#0f3</span><span class="p">;</span>
    <span class="py">--php-string</span><span class="p">:</span> <span class="m">#fc0</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Всьо, пофарбували, порядок :)</p>


      ]]></description>
      <link>https://test.de.co.ua/2025/10/16/temna-nich-ta-funktsiia-show_source.html</link>
      <guid>https://test.de.co.ua/2025/10/16/temna-nich-ta-funktsiia-show_source.html</guid>
      <pubDate>Thu, 16 Oct 2025 03:48:26 +0300</pubDate>
    </item>

    <item>
      <title>Інвертований світлодіод</title>
      <description><![CDATA[

<p>На платі Raspberry Pi Pico є вбудований світлодіод (GPIO25), і на платах ESP8266 теж є вбудований світлодіод (GPIO2). Але є важлива різниця.</p>

<p>Вбудований світлодіод Raspberry Pi Pico світиться, якщо подати на відповідний GPIO високий рівень сигналу, і не світиться якщо встановити низький рівень сигналу. Поведінка ESP8266 протилежна: високий рівень сигналу там <em>вимикає</em> світлодіод.</p>

<p>Тобто, клас <code class="language-plaintext highlighter-rouge">BlinkingLED</code> з <a href="/2025/10/09/python-blinking-led.html">попередьного посту</a> в режимі «не блимати» має різну поведінку на Raspberry Pi Pico та ESP8266: для RP2 вимикання режиму блимання робить світлодіод неактивним, а для ESP8266 вимикання режиму блимання врубає світлодіод на повну. (В режимі «блимати» вони все ж виглядають однаково). Тож треба з цим щось зробити, чи не так?</p>

<p>Окей, давайте і тут зробимо щось сумнівне…</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">InvertedPinOut</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">pin</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">pin</span> <span class="o">=</span> <span class="n">pin</span>

    <span class="k">def</span> <span class="nf">value</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">new_value</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">pin</span><span class="p">.</span><span class="n">value</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">new_value</span><span class="p">)</span>
</code></pre></div></div>

<p>Об’єкт цього класу можна передавати в <code class="language-plaintext highlighter-rouge">BlinkingLED</code> замість <code class="language-plaintext highlighter-rouge">machine.Pin</code>. Норм чи крінж?</p>

<p>Альтернативно, ми можемо додати в клас <code class="language-plaintext highlighter-rouge">BlinkingLED</code> властивість <code class="language-plaintext highlighter-rouge">is_inverted</code> і додаткову логіку:</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code>     async def main(self):
         while True:
<span class="gd">-            self.pin.value(int(self.blinking))
</span><span class="gi">+            self.pin.value(int(self.blinking ^ self.is_inverted))
</span>             await asyncio.sleep_ms(500)
<span class="gd">-            self.pin.value(0)
</span><span class="gi">+            self.pin.value(int(self.is_inverted)
</span>             await asyncio.sleep_ms(500)
</code></pre></div></div>

<p>Це скоріше крінж.</p>

<p>Або так: зробити в класі властивості <code class="language-plaintext highlighter-rouge">value_on</code> та <code class="language-plaintext highlighter-rouge">value_off</code> (за замовчанням (для звичайних світлодіоів), значення 1 та 0, відповідно). Ще довше, але у певному сенсі наочніше.</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code>     async def main(self):
         while True:
<span class="gd">-            self.pin.value(int(self.blinking))
</span><span class="gi">+            self.pin.value(self.value_on if self.blinking else self.value_off)
</span>             await asyncio.sleep_ms(500)
<span class="gd">-            self.pin.value(0)
</span><span class="gi">+            self.pin.value(self.value_off)
</span>             await asyncio.sleep_ms(500)
</code></pre></div></div>

<p>Зовсім крінж. Як же краще?</p>

<p>Увага, правильна відповідь, щойно випадково знайдена в документації: можна використати вже наявний в стандартній бібліотеці <a href="https://docs.micropython.org/en/latest/library/machine.Signal.html">клас <code class="language-plaintext highlighter-rouge">machine.Signal</code></a>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">led</span> <span class="o">=</span> <span class="n">machine</span><span class="p">.</span><span class="n">Signal</span><span class="p">(</span><span class="n">machine</span><span class="p">.</span><span class="n">Pin</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">machine</span><span class="p">.</span><span class="n">Pin</span><span class="p">.</span><span class="n">OUT</span><span class="p">),</span> <span class="n">invert</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</code></pre></div></div>

<p>Яка проста і прикольна штука!</p>


      ]]></description>
      <link>https://test.de.co.ua/2025/10/11/inverted-pin.html</link>
      <guid>https://test.de.co.ua/2025/10/11/inverted-pin.html</guid>
      <pubDate>Sat, 11 Oct 2025 23:15:28 +0300</pubDate>
    </item>

</channel>
</rss>
