<?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: Danila Fominykh</title>
    <description>The latest articles on DEV Community by Danila Fominykh (@danila_fominykh_e69ed8592).</description>
    <link>https://dev.to/danila_fominykh_e69ed8592</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%2F2611430%2F3d1558ab-a043-4678-ae18-f8c7e3feb94d.png</url>
      <title>DEV Community: Danila Fominykh</title>
      <link>https://dev.to/danila_fominykh_e69ed8592</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/danila_fominykh_e69ed8592"/>
    <language>en</language>
    <item>
      <title>The Missing Piece in Go Media Processing</title>
      <dc:creator>Danila Fominykh</dc:creator>
      <pubDate>Wed, 03 Jun 2026 18:01:55 +0000</pubDate>
      <link>https://dev.to/danila_fominykh_e69ed8592/the-missing-piece-in-go-media-processing-58mc</link>
      <guid>https://dev.to/danila_fominykh_e69ed8592/the-missing-piece-in-go-media-processing-58mc</guid>
      <description>&lt;p&gt;Most Go media libraries eventually hit the same wall.&lt;/p&gt;

&lt;p&gt;They don't have their own decoder.&lt;/p&gt;

&lt;p&gt;Instead, they rely on a system-installed FFmpeg package, shared libraries, platform-specific DLLs, or external binaries that must already exist on the target machine.&lt;/p&gt;

&lt;p&gt;Everything works fine during development. Then deployment starts.&lt;/p&gt;

&lt;p&gt;And suddenly you're debugging missing DLLs on Windows, incompatible FFmpeg versions on Linux, Homebrew version mismatches on macOS, container images missing required libs, or a routine package update silently breaking media processing in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Goal
&lt;/h2&gt;

&lt;p&gt;I needed a decoder that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Has pure Go API&lt;/li&gt;
&lt;li&gt;Compiles into a single binary&lt;/li&gt;
&lt;li&gt;Requires no FFmpeg or other runtime dependencies&lt;/li&gt;
&lt;li&gt;Supports modern codecs — AV1, H.264, H.265, VP8, VP9, Opus, AAC, MP3, and more&lt;/li&gt;
&lt;li&gt;Can read from files, memory buffers, HTTP streams, pipes, SSH streams, and any &lt;code&gt;io.Reader&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is &lt;a href="https://github.com/d1nch8g/gopeg" rel="noopener noreferrer"&gt;gopeg&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Instead of depending on whatever media stack happens to be installed on the machine, the required FFmpeg components and the dav1d decoder are statically linked into the application at build time via cgo.&lt;/p&gt;

&lt;p&gt;By the time the binary is ready, everything needed to decode video is already inside it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;An enormous amount of time gets spent on deployment problems rather than on actual application logic. A typical media processing pipeline often looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install FFmpeg&lt;/li&gt;
&lt;li&gt;Install compatible shared libraries&lt;/li&gt;
&lt;li&gt;Package them correctly&lt;/li&gt;
&lt;li&gt;Make sure production machines have compatible versions&lt;/li&gt;
&lt;li&gt;Hope that future OS updates don't break anything&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The application becomes dependent on external state. With static linking, the deployment story collapses to a single step: &lt;code&gt;go build&lt;/code&gt;, and you're done.&lt;/p&gt;

&lt;p&gt;No external codec packages. No DLLs. No runtime library search paths or hacky &lt;code&gt;cmd.Exec&lt;/code&gt; wrappers. No "works on my machine."&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;io.Reader&lt;/code&gt; Support
&lt;/h2&gt;

&lt;p&gt;Flexibility was another core design goal. The decoder doesn't need direct file access — it should work with arbitrary streams:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gopeg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;dec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gopeg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or any data arriving over an SSH connection or a custom transport. When the source implements &lt;code&gt;Seek()&lt;/code&gt;, the library can extract metadata as well — though note that for true streams, accurate duration cannot be determined.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simplicity Over Abstraction
&lt;/h2&gt;

&lt;p&gt;One of my main goals was to avoid building a heavyweight framework. The API is intentionally as minimal as possible.&lt;/p&gt;

&lt;p&gt;Open a source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;dec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gopeg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read metadata:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;dec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Decode frames:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;dec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DecodeFrame&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// process frame&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No background workers. No hidden processes. No external dependencies. Just a clean Go interface with idiomatic types.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;FFmpeg is an incredible piece of software, but depending on a system-installed FFmpeg introduces operational complexity that many applications simply don't need. For my use cases, a statically linked approach turned out to be the more sensible choice — and as a bonus, the final binary size came out smaller than expected.&lt;/p&gt;

&lt;p&gt;The result is a media decoder that's easy to embed, easy to deploy, and behaves predictably in any environment.&lt;/p&gt;

&lt;p&gt;One self-contained binary. Everything needed to decode video, already inside.&lt;/p&gt;

&lt;p&gt;That's why gopeg is the first complete Go solution that gives access to all modern decoders without requiring platform DLLs or external packages — making deployment dramatically simpler.&lt;/p&gt;

&lt;p&gt;Link: &lt;a href="https://github.com/d1nch8g/gopeg" rel="noopener noreferrer"&gt;gopeg repository on github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>go</category>
      <category>opensource</category>
      <category>ffmpeg</category>
      <category>decoder</category>
    </item>
  </channel>
</rss>
