<?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: alfg</title>
    <description>The latest articles on DEV Community by alfg (@alfg).</description>
    <link>https://dev.to/alfg</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%2F243088%2Fe4c568d5-089d-467a-8a47-cb114707f50f.jpg</url>
      <title>DEV Community: alfg</title>
      <link>https://dev.to/alfg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alfg"/>
    <language>en</language>
    <item>
      <title>I built the Postman for Video Engineers</title>
      <dc:creator>alfg</dc:creator>
      <pubDate>Fri, 13 Mar 2026 13:18:07 +0000</pubDate>
      <link>https://dev.to/alfg/i-built-the-postman-for-video-engineers-1hj0</link>
      <guid>https://dev.to/alfg/i-built-the-postman-for-video-engineers-1hj0</guid>
      <description>&lt;p&gt;I've worked in video engineering for the greater part of my career and one thing in common amongst many colleagues are the set of tools we use. &lt;code&gt;ffmpeg&lt;/code&gt;, &lt;code&gt;mp4box&lt;/code&gt;, &lt;code&gt;gpac&lt;/code&gt;, &lt;code&gt;bento4&lt;/code&gt;, &lt;code&gt;ffprobe&lt;/code&gt;, &lt;code&gt;mediainfo&lt;/code&gt; to name a few. Most of them are open source, some are commercial and some are web or desktop based. Some of them are scripts on top of these tools, and sometimes with text files with saved commands. Some of them have a 20+ year GUI.&lt;/p&gt;

&lt;p&gt;This is in addition to our development stack and tooling.&lt;/p&gt;

&lt;p&gt;These tools have no natural home together, so everyone builds their own patchwork. &lt;strong&gt;Why hasn't anyone built this yet?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What if we had a way to consolidate this type of workflow and tooling into a desktop application in the same respect Postman has for Web API developers? Or how JetBrains has for code?&lt;/p&gt;

&lt;p&gt;That's why I made &lt;a href="https://video-commander.com" rel="noopener noreferrer"&gt;Video Commander&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The idea was simple: a familiar UI that wraps the tools we already know, a toolkit for video engineers that you can easily reach for when you need to inspect, encode, analyze, or package any media for testing or development.&lt;/p&gt;

&lt;p&gt;Here's a preview of what it currently looks like:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F179u4nqg2tcjtjfawo13.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F179u4nqg2tcjtjfawo13.png" alt=" " width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're familiar with Postman, or any IDE layout, then you're probably already comfortable with the UI. You have a project sidebar to organize your files, multiple tab instances open, and a few tasks loaded up in the jobs queue.&lt;/p&gt;

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

&lt;p&gt;You can load remote or local media files or streaming manifests.&lt;/p&gt;

&lt;p&gt;Video Commander launches with a focus on inspection and analysis. You can dig into media tracks, metadata, and MP4/ISOBMFF box hierarchies with tree and graph views, the kind of structural detail you'd normally get by piping &lt;code&gt;ffprobe&lt;/code&gt; and &lt;code&gt;mp4box&lt;/code&gt; output into a terminal and reading JSON.&lt;/p&gt;

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

&lt;p&gt;Track and sample analysis gives you timing and layout insight. For encoding and conversion, FFmpeg pipelines run through a jobs queue so you're not blocking your workspace or babysitting a terminal window.&lt;/p&gt;

&lt;p&gt;Everything runs locally as a desktop app, keeping your media files on your machine.&lt;/p&gt;

&lt;p&gt;It's not just about building a set of UI controls for tools, but also the workspace and integration to help empower these tools for productivity. The same reason you use Postman over curl, or VSCode over a text editor.&lt;/p&gt;

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

&lt;p&gt;Video Commander is built on &lt;a href="https://v2.tauri.app/" rel="noopener noreferrer"&gt;Tauri&lt;/a&gt;, using my own crate &lt;a href="https://github.com/video-commander/mp4box" rel="noopener noreferrer"&gt;mp4box&lt;/a&gt; for media inspection. Bring your own FFmpeg or sideload it automatically within the app.&lt;/p&gt;

&lt;p&gt;Today, Video Commander is in preview and available for macOS (Windows coming soon). Try it out and let me know what you think!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://video-commander.com" rel="noopener noreferrer"&gt;https://video-commander.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>ffmpeg</category>
      <category>videoengineering</category>
      <category>videodev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>FFmpeg the Easy Way</title>
      <dc:creator>alfg</dc:creator>
      <pubDate>Tue, 25 May 2021 18:55:00 +0000</pubDate>
      <link>https://dev.to/alfg/ffmpeg-the-easy-way-4a0h</link>
      <guid>https://dev.to/alfg/ffmpeg-the-easy-way-4a0h</guid>
      <description>&lt;p&gt;&lt;a href="https://www.ffmpeg.org/" rel="noopener noreferrer"&gt;FFmpeg&lt;/a&gt; has many &lt;a href="https://ffmpeg.org/ffmpeg.html" rel="noopener noreferrer"&gt;simple&lt;/a&gt; and &lt;a href="https://ffmpeg.org/ffmpeg-filters.html" rel="noopener noreferrer"&gt;complex options&lt;/a&gt;, which can be intimidating at first. So I wanted to create a simple, web-based interface for generating common encoding operations for video and audio, inspired by &lt;a href="https://handbrake.fr/" rel="noopener noreferrer"&gt;HandBrake&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://alfg.github.io/ffmpeg-commander/" rel="noopener noreferrer"&gt;ffmpeg-commander&lt;/a&gt; is a web-based FFmpeg command generator written in JavaScript using the &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue Framework&lt;/a&gt;. It is &lt;a href="https://github.com/alfg/ffmpeg-commander" rel="noopener noreferrer"&gt;open source&lt;/a&gt; and hosted on GitHub Pages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://alfg.github.io/ffmpeg-commander/" rel="noopener noreferrer"&gt;https://alfg.github.io/ffmpeg-commander/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ixx1ap4j9htbejgp10i.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ixx1ap4j9htbejgp10i.PNG" alt="ffmpeg-commander" width="800" height="649"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ffmpeg-commander&lt;/code&gt; supports generating most of the common operations for encoding video, such as the container, codec, video &amp;amp; audio settings, some basic filters and more.&lt;/p&gt;

&lt;p&gt;Once the command is generated, you can easily copy the output to your clipboard or save it to the browser's local storage. A few presets are also available as an example.&lt;/p&gt;

&lt;p&gt;I'm looking to expand on more general options and filters in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;ffmpegd&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/alfg/ffmpegd" rel="noopener noreferrer"&gt;ffmpegd&lt;/a&gt; is an optional companion application that connects &lt;code&gt;ffmpeg-commander&lt;/code&gt; to ffmpeg by providing a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" rel="noopener noreferrer"&gt;websocket&lt;/a&gt; server to send encode tasks and receive realtime progress updates back to the browser. This allows using &lt;code&gt;ffmpeg-commander&lt;/code&gt; as a GUI for ffmpeg.&lt;/p&gt;

&lt;p&gt;The goal is to provide a simple interface for sending FFmpeg tasks from the browser (and other supported clients in the future) to your local machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/alfg/ffmpegd" rel="noopener noreferrer"&gt;https://github.com/alfg/ffmpegd&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftkykcssrvmcx5tmne5fk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftkykcssrvmcx5tmne5fk.png" alt="screenshot" width="800" height="596"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ffmpegd&lt;/code&gt; is written in &lt;a href="https://golang.org/" rel="noopener noreferrer"&gt;Go&lt;/a&gt; and implements the &lt;a href="https://github.com/gorilla/websocket" rel="noopener noreferrer"&gt;Gorilla Websockets&lt;/a&gt; library.&lt;/p&gt;

&lt;p&gt;Please note, this is still considered experimental and a work-in-progress. Feel free to report any bugs at &lt;a href="https://github.com/alfg/ffmpegd" rel="noopener noreferrer"&gt;https://github.com/alfg/ffmpegd&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Hopefully you find these tools helpful for working with FFmpeg. ☺️&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks for reading!
&lt;/h2&gt;

&lt;p&gt;Find me on GitHub at: &lt;a href="https://github.com/alfg" rel="noopener noreferrer"&gt;https://github.com/alfg&lt;/a&gt; for more video-related projects!&lt;/p&gt;

</description>
      <category>ffmpeg</category>
      <category>javascript</category>
      <category>vue</category>
      <category>webdev</category>
    </item>
    <item>
      <title>FFmpeg for Instagram</title>
      <dc:creator>alfg</dc:creator>
      <pubDate>Thu, 20 May 2021 18:01:51 +0000</pubDate>
      <link>https://dev.to/alfg/ffmpeg-for-instagram-35bi</link>
      <guid>https://dev.to/alfg/ffmpeg-for-instagram-35bi</guid>
      <description>&lt;p&gt;Uploading high quality video to &lt;a href="https://www.instagram.com/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; can result in lower quality video on posts and stories due to aggressive re-encoding to save on bandwidth and storage for users. You will notice this especially if uploading videos from &lt;a href="https://gopro.com" rel="noopener noreferrer"&gt;GoPro&lt;/a&gt; or other devices captured in 4K or 1440p with larger file sizes. Although the video preview on Instagram looks high quality, the resulting output will be degraded drastically.&lt;/p&gt;

&lt;p&gt;This guide describes various &lt;a href="https://ffmpeg.org" rel="noopener noreferrer"&gt;FFmpeg&lt;/a&gt; encoding presets that can be used to help improve your video quality output when uploading to Instagram. These settings can also be applied to other encoders, such as &lt;a href="https://handbrake.fr" rel="noopener noreferrer"&gt;Handbrake&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommended Encoding Settings
&lt;/h2&gt;

&lt;p&gt;Instagram does not provide an official list of encoding requirements. These settings are gathered from various sources and through trial and error. However, Instagram does provide some &lt;a href="https://help.instagram.com/1038071743007909" rel="noopener noreferrer"&gt;encoding requirements for IGTV&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Set your encoder to use these settings: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MP4 Container format&lt;/li&gt;
&lt;li&gt;H.264 Video Codec&lt;/li&gt;
&lt;li&gt;AAC Audio&lt;/li&gt;
&lt;li&gt;3500kbps bitrate&lt;/li&gt;
&lt;li&gt;30 FPS&lt;/li&gt;
&lt;li&gt;60 seconds maximum in length&lt;/li&gt;
&lt;li&gt;1080p 16:9 max (vertical or horizontal)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, if you inspect a story video using a tool like &lt;a href="https://mediaarea.net/en/MediaInfo" rel="noopener noreferrer"&gt;mediainfo&lt;/a&gt;, you'll see something like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;First video stream:
1 367 kb/s 640*1136 (0.563) at 30.000 FPS, AVC (Main@L3)(CABAC 4 Ref Frames)
64.0 kb/s 44.1 kHz, 1 channel, AAC LC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stories without audio, don't have an audio stream within the container at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimize with FFmpeg
&lt;/h2&gt;

&lt;p&gt;We can apply the settings above using &lt;code&gt;FFmpeg&lt;/code&gt;. Here are some encoding profiles to try out.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;720p30 @ CRF 23&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Using CRF only without targetting a bitrate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ffmpeg -i INPUT.MOV -vf scale=-2:720 -c:v libx264 -profile:v main -level:v 3.0 -x264-params scenecut=0:open_gop=0:min-keyint=72:keyint=72 -c:a aac -preset slow -crf 23 -r 30 -sn -f mp4 OUTPUT.mp4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;720p30 @ 3500k video bitrate + 256k audio bitrate&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Variable bitrate with a max of 3500k.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ffmpeg -i INPUT.MOV -vf scale=-2:720 -c:v libx264 -profile:v main -level:v 3.0 -x264-params scenecut=0:open_gop=0:min-keyint=72:keyint=72:ref=4 -c:a aac -crf 23 -maxrate 3500k -bufsize 3500k -r 30 -ar 44100 -b:a 256k -sn -f mp4 OUTPUT.mp4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;720p30 @ 3500k video bitrate + 256k audio bitrate - 2-pass encoding&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Using 2-pass encoding with a targetted bitrate of 3500k. This doesn't necessarily improve quality, but helps target a filesize more accurately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ffmpeg -i INPUT.MOV -vf scale=-2:720 -c:v libx264 -profile:v main -level:v 3.0 -x264-params scenecut=0:open_gop=0:min-keyint=72:keyint=72:ref=4 -c:a aac -b:v 3500k -maxrate 3500k -bufsize 3500k -r 30 -ar 44100 -b:a 256k -pass 1 -sn -f mp4 NUL &amp;amp;&amp;amp; \ 
 ffmpeg -i INPUT.MOV -vf scale=-2:720 -c:v libx264 -profile:v main -level:v 3.0 -x264-params scenecut=0:open_gop=0:min-keyint=72:keyint=72:ref=4 -c:a aac -b:v 3500k -maxrate 3500k -bufsize 3500k -r 30 -ar 44100 -b:a 256k -pass 2 OUTPUT.mp4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try adjusting a few settings to get the optimal quality and filesize before uploading. Maybe try 1080p for IGTV, per their specification.&lt;/p&gt;

&lt;h4&gt;
  
  
  What do those options mean?
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-i INPUT.MOV&lt;/code&gt; - Input movie file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-vf scale=-2:720&lt;/code&gt; - Video filter to scale to 720p while keeping aspect ratio.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-c:v libx264&lt;/code&gt; - Use &lt;code&gt;x264&lt;/code&gt; video codec.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-profile:v main -level:v 3.1&lt;/code&gt; - Mostly for H.264 compatibility for older devices. This seems to be the profile Apple devices export to.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-x264-params scenecut=0:open_gop=0:min-keyint=72:keyint=72:ref=4&lt;/code&gt; - Some x264 codec options I use to improve video for streaming.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-c:a aac&lt;/code&gt; - Use AAC audio codec.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-preset slow&lt;/code&gt; - Slower presets will result in better quality. Feel free to also try &lt;code&gt;slower&lt;/code&gt; and &lt;code&gt;veryslow&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-crf 23&lt;/code&gt; - Constant Rate Factor encoding mode that adjusts the file data rate up or down to achieve a selected quality level rather than a specific data rate.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-b:v 3500k&lt;/code&gt; - Targets video bitrate to 3500k.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-b:a 256k&lt;/code&gt; - Targets audio bitrate to 256k.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-ar 44100&lt;/code&gt; - Sets audio sample rate to 44.1 kHz.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-r 30&lt;/code&gt; - Sets the framerate to 30.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-f mp4&lt;/code&gt; - Sets the MP4 container format. This is optional if you have &lt;code&gt;.mp4&lt;/code&gt; in the output filename.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OUTPUT.mp4&lt;/code&gt; - The encoded output movie file.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Here's a before and after test I created using the above requirements. These were downloaded directly from Instagram after posting to compare results.&lt;/p&gt;

&lt;p&gt;The source video file being a 3840x2160 @ 60fps (4K) file recorded from a GoPro Hero 8 at 567MB in size.&lt;/p&gt;

&lt;p&gt;The optimized video encoded at 1280x720 @ 30fps using the 2-pass profile settings at 34.7MB in size.&lt;/p&gt;

&lt;h4&gt;
  
  
  Without optimizing video
&lt;/h4&gt;

&lt;p&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  With optimized video
&lt;/h4&gt;

&lt;p&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Without optimizing as a story
&lt;/h4&gt;

&lt;p&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Optimized video as a story
&lt;/h4&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Notice the unoptimized videos have more jagged edge artifacts around the guard rails and curb, especially for video with a lot of motion.&lt;/p&gt;

&lt;p&gt;The goal is basically to reduce your video to an optimal size and image quality so it doesn't get affected as much by Instagram's aggressive encoding.&lt;/p&gt;

&lt;p&gt;Try adjusting some settings to find the best results for your use case and picture. Results will vary!&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks for reading!
&lt;/h2&gt;

&lt;p&gt;Find me on GitHub at: &lt;a href="https://github.com/alfg" rel="noopener noreferrer"&gt;https://github.com/alfg&lt;/a&gt; for more video-related projects!&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ffmpeg.org/" rel="noopener noreferrer"&gt;https://ffmpeg.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://trac.ffmpeg.org/wiki/Encode/H.264" rel="noopener noreferrer"&gt;https://trac.ffmpeg.org/wiki/Encode/H.264&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://handbrake.fr/" rel="noopener noreferrer"&gt;https://handbrake.fr/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://help.instagram.com/1038071743007909" rel="noopener noreferrer"&gt;Video upload requirements for IGTV &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mediaarea.net/en/MediaInfo" rel="noopener noreferrer"&gt;https://mediaarea.net/en/MediaInfo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ffmpeg</category>
      <category>instagram</category>
      <category>video</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>FFmpeg + WebAssembly</title>
      <dc:creator>alfg</dc:creator>
      <pubDate>Wed, 19 May 2021 18:20:22 +0000</pubDate>
      <link>https://dev.to/alfg/ffmpeg-webassembly-2cbl</link>
      <guid>https://dev.to/alfg/ffmpeg-webassembly-2cbl</guid>
      <description>&lt;p&gt;&lt;a href="https://ffmpeg.org" rel="noopener noreferrer"&gt;FFmpeg&lt;/a&gt; is a powerful command line tool for handling video, audio and other multimedia files and streams. It is any video developer's utility for editing, transcoding, and remuxing virtually any format. It is developed in C and available for most platforms.&lt;/p&gt;

&lt;p&gt;FFmpeg is not just a command line tool, though. It is powered by the FFmpeg libraries known as libav. These libraries enable FFmpeg to read, write and manipulate multimedia files. These libraries provide functionality for remuxing, encoding and decoding, filtering, scaling, colorspace conversion and device interfacing. You can use these libraries directly if you're writing an application in C/C++. There are also libav bindings available for most common languages.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if you could use FFmpeg's libraries in the browser?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;JavaScript in the browser is a different story. It is not designed to run system applications in the browser environment. So how can we run FFmpeg in the browser? WebAssembly!&lt;/p&gt;

&lt;p&gt;WebAssembly (or Wasm) has been gaining popularity recently allowing us to run binary instructions in the browser, along with a compiler toolchain, &lt;a href="https://emscripten.org/" rel="noopener noreferrer"&gt;Emscripten&lt;/a&gt; to help us build and port C/C++ code to Wasm.&lt;/p&gt;

&lt;p&gt;This has already been done before. You can check out &lt;a href="https://github.com/ffmpegwasm/ffmpeg.wasm" rel="noopener noreferrer"&gt;ffmpeg.wasm&lt;/a&gt; for running the FFmpeg CLI in the browser environment.&lt;/p&gt;

&lt;p&gt;However, this guide's focus will step through the process on building FFmpeg's &lt;code&gt;libav&lt;/code&gt; libraries for use in the browser via Web Assembly, rather than the FFmpeg CLI application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why? 🤔
&lt;/h2&gt;

&lt;p&gt;FFmpeg in the browser won't have the greatest performance compared to running it natively on a system that can take advantage of threaded processing and hardware acceleration.&lt;/p&gt;

&lt;p&gt;Normally, you would just build a back-end that wraps FFmpeg or libav and relay to the front-end to provide results.&lt;/p&gt;

&lt;p&gt;However, we can still take advantage of the vast features of FFmpeg's libraries such as parsing format and codec information, decoding frames, applying filters and more. Imagine if we can do this purely in JavaScript, within the browser environment on a static webpage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hello World in &lt;code&gt;libav&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Let's start by writing a simple program in C to print out basic media information using libav. Let's name it &lt;code&gt;mp4info.c&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you are not familiar with &lt;code&gt;libav&lt;/code&gt;, &lt;a href="https://github.com/leandromoreira/ffmpeg-libav-tutorial" rel="noopener noreferrer"&gt;ffmpeg-libav-tutorial&lt;/a&gt; is a great introduction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;libavformat/avformat.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;AVFormatContext&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fmt_ctx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;argv&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argc&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Please specify a media file.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Open the file and read header.&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;avformat_open_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;fmt_ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;av_err2str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Read container data.&lt;/span&gt;
  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"format: %s, duration: %ld us, streams: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;fmt_ctx&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;iformat&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;fmt_ctx&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;fmt_ctx&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nb_streams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&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;p&gt;This is a basic program that takes a media file as an argument and prints out the format name, duration and stream count.&lt;/p&gt;

&lt;p&gt;Compile and run the program:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/alfg/ffmpeg-webassembly-example/blob/master/tears-of-steel-10s.mp4?raw=true" rel="noopener noreferrer"&gt;Download Tears of Steel 10s Example&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcc src/mp4info.c -lavformat -lavutil -o bin/mp4info
./bin/mp4info tears-of-steel-10s.mp4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;code&gt;gcc&lt;/code&gt;, &lt;code&gt;ffmpeg&lt;/code&gt; and &lt;code&gt;ffmpeg-dev&lt;/code&gt; are required to build.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You should get the following output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;format: mov,mp4,m4a,3gp,3g2,mj2, duration: 10000000 us, streams: 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Now we can move onto the next step using Emscripten to compile FFmpeg, and build a wrapper for use within a JavaScript environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Emscripten
&lt;/h2&gt;

&lt;p&gt;Emscripten is a compiler toolchain for for WebAssembly. We will use &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; and &lt;a href="https://emscripten.org/" rel="noopener noreferrer"&gt;emscripten&lt;/a&gt; to build FFmpeg's &lt;code&gt;libav&lt;/code&gt; and our custom wrapper code to Wasm. Let's start with FFmpeg first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compiling FFmpeg to Web Assembly
&lt;/h2&gt;

&lt;p&gt;In our Dockerfile, we will use the base emscripten emsdk to build FFmpeg from source, along with the latest stable &lt;code&gt;libx264&lt;/code&gt; version.&lt;/p&gt;

&lt;p&gt;Since we are compiling to WebAssembly, we will make a minimal build by disabling all programs and only enabling the features we are likely to use. This will help keep the binary size small and optimal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;emscripten/emsdk:2.0.16&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; FFMPEG_VERSION=4.3.2&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; X264_VERSION=20170226-2245-stable&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; PREFIX=/opt/ffmpeg&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; MAKEFLAGS="-j4"&lt;/span&gt;

&lt;span class="c"&gt;# Build dependencies.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; autoconf libtool build-essential

&lt;span class="c"&gt;# Download and build x264.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /tmp &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  wget https://download.videolan.org/pub/videolan/x264/snapshots/x264-snapshot-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;X264_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.bz2 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;tar &lt;/span&gt;xvfj x264-snapshot-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;X264_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.bz2

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /tmp/x264-snapshot-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;X264_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  emconfigure ./configure &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PREFIX&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;i686-gnu &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-static&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--disable-cli&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--disable-asm&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--extra-cflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-s USE_PTHREADS=1"&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /tmp/x264-snapshot-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;X264_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  emmake make &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; emmake make &lt;span class="nb"&gt;install&lt;/span&gt; 

&lt;span class="c"&gt;# Download ffmpeg release source.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /tmp/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  wget http://ffmpeg.org/releases/ffmpeg-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FFMPEG_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;tar &lt;/span&gt;zxf ffmpeg-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FFMPEG_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm &lt;/span&gt;ffmpeg-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FFMPEG_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; CFLAGS="-s USE_PTHREADS=1 -O3 -I${PREFIX}/include"&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; LDFLAGS="$CFLAGS -L${PREFIX}/lib -s INITIAL_MEMORY=33554432"&lt;/span&gt;

&lt;span class="c"&gt;# Configure and build FFmpeg with emscripten.&lt;/span&gt;
&lt;span class="c"&gt;# Disable all programs and only enable features we will use.&lt;/span&gt;
&lt;span class="c"&gt;# https://github.com/FFmpeg/FFmpeg/blob/master/configure&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /tmp/ffmpeg-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FFMPEG_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  emconfigure ./configure &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PREFIX&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--target-os&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;none &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--arch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;x86_32 &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-cross-compile&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--disable-debug&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--disable-x86asm&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--disable-inline-asm&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--disable-stripping&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--disable-programs&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--disable-doc&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--disable-all&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-avcodec&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-avformat&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-avfilter&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-avdevice&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-avutil&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-swresample&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-postproc&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-swscale&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-filters&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-protocol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;file &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-decoder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;h264,aac,pcm_s16le &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-demuxer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mov,matroska &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-muxer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mp4 &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-gpl&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--enable-libx264&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--extra-cflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CFLAGS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--extra-cxxflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CFLAGS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--extra-ldflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LDFLAGS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--nm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"llvm-nm -g"&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--ar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;emar &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--as&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;llvm-as &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--ranlib&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;llvm-ranlib &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--cc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;emcc &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--cxx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;em++ &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--objcc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;emcc &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nt"&gt;--dep-cc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;emcc

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /tmp/ffmpeg-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FFMPEG_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  emmake make &lt;span class="nt"&gt;-j4&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  emmake make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build -t mp4info .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will build and install ffmpeg libraries to &lt;code&gt;/opt/ffmpeg&lt;/code&gt;. However, we still need to write our wrapper using the Hello World example we wrote earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the Wrapper
&lt;/h2&gt;

&lt;p&gt;Now that we can build the FFmpeg libraries to Wasm, we need to create a wrapper that integrates the functionality of emscipten so we can import and use in JavaScript within the browser.&lt;/p&gt;

&lt;p&gt;We will write our custom wrapper in C++ to take advantage of &lt;a href="https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html" rel="noopener noreferrer"&gt;Embind&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;From the documentation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Embind is used to bind C++ functions and classes to JavaScript, so that the compiled code can be used in a natural way by “normal” JavaScript.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This will allow us to easily create bindings from C++ constructs. So let's get started!&lt;/p&gt;

&lt;p&gt;Update our Hello World example to C++:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mp4info-wrapper.cpp&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;emscripten.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;emscripten/bind.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;inttypes.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;vector&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="n"&gt;emscripten&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;libavformat/avformat.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;streams&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;AVFormatContext&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fmt_ctx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Open the file and read header.&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;avformat_open_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;fmt_ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_str&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;av_err2str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Read container data.&lt;/span&gt;
  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"format: %s, duration: %lld us, streams: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;fmt_ctx&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;iformat&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;fmt_ctx&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;fmt_ctx&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nb_streams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Initialize response struct with format data.&lt;/span&gt;
  &lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fmt_ctx&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;iformat&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;fmt_ctx&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;streams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;fmt_ctx&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nb_streams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;EMSCRIPTEN_BINDINGS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;structs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;emscripten&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;value_object&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Response"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"streams"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;streams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;run&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;p&gt;Some notable changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;C to C++&lt;/li&gt;
&lt;li&gt;Included the &lt;code&gt;emscripten&lt;/code&gt; headers&lt;/li&gt;
&lt;li&gt;Created a &lt;code&gt;Response&lt;/code&gt; typedef&lt;/li&gt;
&lt;li&gt;Updated &lt;code&gt;main&lt;/code&gt; to &lt;code&gt;run&lt;/code&gt;, taking a filename argument and returns a &lt;code&gt;Response&lt;/code&gt; struct with format, duration and streams.&lt;/li&gt;
&lt;li&gt;Added &lt;code&gt;EMSCRIPTEN_BINDINGS&lt;/code&gt; to export the &lt;code&gt;run&lt;/code&gt; function with the &lt;code&gt;Response&lt;/code&gt; type bindings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add a &lt;code&gt;Makefile&lt;/code&gt; for &lt;code&gt;emcc&lt;/code&gt; to build the wrapper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;dist/mp4info.wasm.js&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; dist &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    emcc &lt;span class="nt"&gt;--bind&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-O3&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-L&lt;/span&gt;/opt/ffmpeg/lib &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-I&lt;/span&gt;/opt/ffmpeg/include/ &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;EXTRA_EXPORTED_RUNTIME_METHODS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"[FS, cwrap, ccall, getValue, setValue, writeAsciiToMemory]"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;INITIAL_MEMORY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;268435456 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;ASSERTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;STACK_OVERFLOW_CHECK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;PTHREAD_POOL_SIZE_STRICT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;ALLOW_MEMORY_GROWTH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-lavcodec&lt;/span&gt; &lt;span class="nt"&gt;-lavformat&lt;/span&gt; &lt;span class="nt"&gt;-lavfilter&lt;/span&gt; &lt;span class="nt"&gt;-lavdevice&lt;/span&gt; &lt;span class="nt"&gt;-lswresample&lt;/span&gt; &lt;span class="nt"&gt;-lpostproc&lt;/span&gt; &lt;span class="nt"&gt;-lswscale&lt;/span&gt; &lt;span class="nt"&gt;-lavutil&lt;/span&gt; &lt;span class="nt"&gt;-lm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-pthread&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-lworkerfs&lt;/span&gt;.js &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; dist/mp4info.js &lt;span class="se"&gt;\&lt;/span&gt;
    src/mp4info-wrapper.cpp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And add the following lines to our Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./src/mp4info-wrapper.cpp /build/src/mp4info-wrapper.cpp&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./Makefile /build/Makefile&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's build via Docker with a mounted volume. This is so we can easily build the artifact from the container to our host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -v ${pwd}:/build -it mp4info make
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will build the following files into &lt;code&gt;dist/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dist
├───mp4info.js
├───mp4info.wasm
└───mp4info.worker.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your WebAssembly artifacts are now ready for use within JavaScript! 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the JavaScript
&lt;/h2&gt;

&lt;p&gt;In this next step, we'll be creating a static webpage using just HTML and JavaScript. &lt;/p&gt;

&lt;p&gt;Let's create a basic &lt;code&gt;index.html&lt;/code&gt; page with a file input and an empty results &lt;code&gt;div&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;'utf-8'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;'X-UA-Compatible'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'IE=edge'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;FFmpeg WebAssembly Example&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;'viewport'&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;'width=device-width, initial-scale=1'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"results"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since our &lt;code&gt;mp4info&lt;/code&gt; wrapper is compiled from synchronous C++ code, it can block the browser's main thread when executing on larger files. This can lead to the browser's UI locking up and a bad experience for the user.&lt;/p&gt;

&lt;p&gt;However, we can take advantage of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers" rel="noopener noreferrer"&gt;Web Workers&lt;/a&gt; to handle background tasks and simply relay the response to the main thread to prevent any blocking code.&lt;/p&gt;

&lt;p&gt;Let's create a new &lt;code&gt;worker.js&lt;/code&gt; file with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Run this script as a Web Worker so it doesn't block the&lt;/span&gt;
&lt;span class="c1"&gt;// browser's main thread.&lt;/span&gt;
&lt;span class="c1"&gt;// See: index.html.&lt;/span&gt;
&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Create and mount FS work directory.&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;FS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;analyzePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/work&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;FS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/work&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;FS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WORKERFS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/work&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Run the Wasm function we exported.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/work/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Post message back to main thread.&lt;/span&gt;
    &lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Unmount the work directory.&lt;/span&gt;
    &lt;span class="nx"&gt;FS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unmount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/work&lt;/span&gt;&lt;span class="dl"&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;// Import the Wasm loader generated from our Emscripten build.&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;importScripts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mp4info.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This contains a message listener to create a virtual file system using the &lt;code&gt;Emscripten&lt;/code&gt; &lt;a href="https://emscripten.org/docs/api_reference/Filesystem-API.html" rel="noopener noreferrer"&gt;Filesystem API&lt;/a&gt;, runs our Wasm module code, and send the results back using &lt;code&gt;postMessage&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So let's also add the following code to your &lt;code&gt;index.html&lt;/code&gt; right before the &lt;code&gt;&amp;lt;/html&amp;gt;&lt;/code&gt; line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;// Create a worker for running Wasm code without blocking main thread.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onFileChange&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Listen for messages back from worker and render to DOM.&lt;/span&gt;
    &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;results&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ul&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ul&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;li&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;format: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;li2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;li&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;li2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;duration: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;li3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;li&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;li3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;streams: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;streams&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;li2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;li3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Send file to worker.&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onFileChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a listener on the &lt;code&gt;input&lt;/code&gt; form element and sends it to the Web Worker. It also creates a listener for messages relayed back from &lt;code&gt;worker.js&lt;/code&gt; so we can render the results back to the DOM.&lt;/p&gt;

&lt;p&gt;Let's add our &lt;code&gt;index.html&lt;/code&gt;, &lt;code&gt;worker.js&lt;/code&gt; and Wasm related files into a new directory &lt;code&gt;www&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;www
├───index.html
├───worker.js
├───mp4info.js
├───mp4info.wasm
└───mp4info.worker.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we load up &lt;code&gt;index.html&lt;/code&gt; into the browser, our Wasm build uses &lt;a href="https://emscripten.org/docs/porting/pthreads.html" rel="noopener noreferrer"&gt;PTHREADS&lt;/a&gt;, which raises a security issue with some browsers. From the Emscripten documentation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Browsers are currently shipping SharedArrayBuffer gated behind Cross Origin Opener Policy (COOP) and Cross Origin Embedder Policy (COEP) headers. Pthreads code will not work in deployed environment unless these headers are correctly set.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So we need to include the proper response headers when serving the webpage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can do this by running simple &lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;NodeJS&lt;/a&gt; server using &lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;Express&lt;/a&gt;, easily:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// CORS headers.&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Methods&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET,PUT,POST,DELETE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Access-Control-Allow-Headers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Required headers for SharedArrayBuffer.&lt;/span&gt;
    &lt;span class="c1"&gt;// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cross-Origin-Opener-Policy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;same-origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cross-Origin-Embedder-Policy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;require-corp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, keep in mind you will need to add these response headers if deploying to any static webpage provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo Time!
&lt;/h2&gt;

&lt;p&gt;Run the server (requires &lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;NodeJS&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install express
node server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Load up &lt;a href="http://localhost:8080/www" rel="noopener noreferrer"&gt;http://localhost:8080/www&lt;/a&gt; in the browser and select an MP4 file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnx1lp38jqe13cdu7a5k1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnx1lp38jqe13cdu7a5k1.png" alt="Alt Text" width="633" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And that's how you run FFmpeg's &lt;code&gt;libav&lt;/code&gt; in the browser!&lt;/strong&gt; ⚡&lt;/p&gt;

&lt;p&gt;This just scratches the surface into the capabilities of FFmpeg + WebAssembly. You can use &lt;code&gt;libav&lt;/code&gt; to do more than just read format details. Check out more &lt;a href="http://ffmpeg.org/doxygen/4.1/examples.html" rel="noopener noreferrer"&gt;libav examples&lt;/a&gt; and try porting them to WebAssembly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks for reading!
&lt;/h2&gt;

&lt;p&gt;Check out &lt;a href="https://github.com/alfg/ffmpeg-webassembly-example" rel="noopener noreferrer"&gt;https://github.com/alfg/ffmpeg-webassembly-example&lt;/a&gt; for the full demo files in this guide.&lt;/p&gt;

&lt;p&gt;I also have a more advanced example of using FFProbe via Wasm:&lt;br&gt;
&lt;a href="https://github.com/alfg/ffprobe-wasm" rel="noopener noreferrer"&gt;https://github.com/alfg/ffprobe-wasm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Find me on GitHub at: &lt;a href="https://github.com/alfg" rel="noopener noreferrer"&gt;https://github.com/alfg&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References and Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://webassembly.org/" rel="noopener noreferrer"&gt;https://webassembly.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://emscripten.org/" rel="noopener noreferrer"&gt;https://emscripten.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ffmpeg.org/doxygen/4.1/examples.html" rel="noopener noreferrer"&gt;http://ffmpeg.org/doxygen/4.1/examples.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/alfg/libav-examples" rel="noopener noreferrer"&gt;https://github.com/alfg/libav-examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/alfg/ffprobe-wasm" rel="noopener noreferrer"&gt;https://github.com/alfg/ffprobe-wasm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/alfg/ffmpeg-webassembly-example" rel="noopener noreferrer"&gt;https://github.com/alfg/ffmpeg-webassembly-example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy Hacking! 🎥&lt;/p&gt;

</description>
      <category>ffmpeg</category>
      <category>webassembly</category>
      <category>cpp</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>A Quick Dive Into MP4</title>
      <dc:creator>alfg</dc:creator>
      <pubDate>Fri, 30 Apr 2021 17:15:41 +0000</pubDate>
      <link>https://dev.to/alfg/a-quick-dive-into-mp4-57fo</link>
      <guid>https://dev.to/alfg/a-quick-dive-into-mp4-57fo</guid>
      <description>&lt;p&gt;What is an MP4? We all know it as a file format for playing video with sound. It's used for streaming video by Netflix, YouTube, Instagram, and also for capturing video on your iPhone, but how does it work? How is it used? What is the byte structure? What is a container?&lt;/p&gt;

&lt;p&gt;This guide is an introduction and a quick dive into the MP4 file format, also known as the ISO Base Media File Format (&lt;a href="https://en.wikipedia.org/wiki/MPEG-4_Part_14" rel="noopener noreferrer"&gt;ISO-BMFF MPEG-4 Part 14&lt;/a&gt;). Fancy name, I know.&lt;/p&gt;

&lt;p&gt;I won't go into the playback details in this guide, but more of the MP4 byte format commonly known as the MP4 Box Structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The MPEG-4 Part 14 (MP4) is one of the most common &lt;a href="https://en.wikipedia.org/wiki/Comparison_of_video_container_formats" rel="noopener noreferrer"&gt;container formats&lt;/a&gt; for video and has an extension of &lt;code&gt;.mp4&lt;/code&gt;. You may already know of other container formats, such as &lt;code&gt;wav&lt;/code&gt;, &lt;code&gt;mov&lt;/code&gt;, &lt;code&gt;mp3&lt;/code&gt; or more recently &lt;code&gt;webm&lt;/code&gt;. A container just "contains" the video or audio track, or both. It can also support embedded subtitle tracks too.&lt;/p&gt;

&lt;p&gt;MP4 is an extension of the ISO Base Media File Format (&lt;a href="https://en.wikipedia.org/wiki/ISO/IEC_base_media_file_format" rel="noopener noreferrer"&gt;ISOBMFF, MPEG-4 Part 12)&lt;/a&gt;, which is a format designed to contain timed media information.&lt;/p&gt;

&lt;p&gt;The ISO-BMFF format is directly based on &lt;a href="https://en.wikipedia.org/wiki/QuickTime_File_Format" rel="noopener noreferrer"&gt;QuickTime&lt;/a&gt;, therefore the MP4 is essentially identical to the QuickTime file format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     MPEG-4 Part 14 - MP4 File Format
 ┌──────────────────────────────────────┐
 ├────────────────────┐                 │
 │  ISO Base Media    │                 │
 │    File Format     │  MP4 Extension  │
 │  (MPEG-4 Part 12)  │                 │
 └────────────────────┴─────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to fully understand the MP4 structure, you'll need to obtain a copy of the &lt;a href="https://en.wikipedia.org/wiki/International_Organization_for_Standardization" rel="noopener noreferrer"&gt;ISO&lt;/a&gt; documents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;14496-12 – MPEG-4 Part 12&lt;/li&gt;
&lt;li&gt;14496-14 - MPEG-4 Part 14&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Google search should result in a few resources to get a copy of the PDF.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in a container?
&lt;/h2&gt;

&lt;p&gt;Since MP4 is a &lt;a href="https://en.wikipedia.org/wiki/Comparison_of_video_container_formats" rel="noopener noreferrer"&gt;container&lt;/a&gt; format, it doesn't actually handle the decoding of the video and audio streams, it just contains them as tracks along with their metadata.&lt;/p&gt;

&lt;p&gt;A container can store some of the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;General metadata such as file type and compatibility&lt;/li&gt;
&lt;li&gt;Video, audio and subtitle tracks and codec details&lt;/li&gt;
&lt;li&gt;Metadata: duration, timescale, bitrate, width/height, etc&lt;/li&gt;
&lt;li&gt;Progressive and fragmented metadata details&lt;/li&gt;
&lt;li&gt;A series of video frames or audio samples known as "Sample Data"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is all the information a player needs to decode and play the content.&lt;/p&gt;

&lt;p&gt;At a high-level, this is what an MP4 structure typically looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;video.mp4
├───general file metadata
├───movie data
├───tracks
│   ├───video
│   │   ├───video metadata
│   │   └───video sample data
│   └───audio
│       ├───audio metadata
│       └───audio sample data
└───more metadata
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Movie Boxes
&lt;/h2&gt;

&lt;p&gt;The MP4 byte structure is composed of a series of boxes, also known as "atoms", according to the QuickTime specification. Each box describes and contains data to build the MP4 container format.&lt;/p&gt;

&lt;p&gt;Boxes typically have a four letter name, also known as a &lt;a href="https://en.wikipedia.org/wiki/FourCC" rel="noopener noreferrer"&gt;FourCC&lt;/a&gt;. This is the shortened version of the full box name, enough to fit into 4 bytes. This is important for when you are reading and writing boxes into or from the byte format.&lt;/p&gt;

&lt;p&gt;Before we jump into the byte structure details, here is a more technical view of the MP4 box tree, compared to the high-level view above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;video.mp4
├───ftyp -------------------&amp;gt; FileType Box
├───mdat -------------------&amp;gt; Movie Data Box
├───moov -------------------&amp;gt; Movie Boxes
│   ├───trak ---------------&amp;gt; Track Box
│   │   ├─── tkhd ----------&amp;gt; Track Header
│   │   └─── mdia ----------&amp;gt; Media Box
│   │        └─── ...
│   └───trak
│   │   ├─── tkhd ----------&amp;gt; Track Header
│   │   └─── mdia ----------&amp;gt; Media Box
│   │        └─── ...
└───udta -------------------&amp;gt; Userdata Box
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is just a simplified view. However, there are many more boxes defined in the MP4 specification.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the box?
&lt;/h2&gt;

&lt;p&gt;An MP4 "Box" contains just enough information to read and parse the box name, size and data.&lt;/p&gt;

&lt;p&gt;Each of these boxes have a different purpose, containing a bit of information and details on a specific piece of data. Some boxes describe the file type, and others can describe codec detail, picture resolution, frame rate, duration, sample sizes and more. There's also boxes containing the encoded video and audio data too.&lt;/p&gt;

&lt;p&gt;A box typically contains the following base information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Size of the box (in bytes)&lt;/li&gt;
&lt;li&gt;Box Name (FourCC)&lt;/li&gt;
&lt;li&gt;Box Data
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────┐
|      Box Header     |
| Size (4) | Type (4) | Box Header = 8 Bytes
| --------------------|
|     Box Data (N)    | Box Data = N Bytes
└─────────────────────┘
           └─────────── Box Size = 8 + N bytes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is just enough information we need to know how to parse a box, along with the MP4 specification document to understand the box fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parse a Box
&lt;/h2&gt;

&lt;p&gt;So let's parse our first box!&lt;/p&gt;

&lt;p&gt;As mentioned above, the first 8 bytes of each box is known as the "Box Header", where the first 4 bytes are the size of the box, and the next 4 bytes are the box name. These are the two values you need to know to iterate and parse each box, byte by byte.&lt;/p&gt;

&lt;p&gt;Here's a box header struct for example:&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;type&lt;/span&gt; &lt;span class="n"&gt;Box&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Size&lt;/span&gt;    &lt;span class="kt"&gt;int32&lt;/span&gt;
  &lt;span class="n"&gt;Name&lt;/span&gt;    &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reading the box data from each atom requires the box size, name and byte structure of each box you are parsing. You can refer to the MPEG-4 Part 14 specification for the byte structure of each known box, or just refer to some existing MP4 parsing open-source code.&lt;/p&gt;

&lt;p&gt;According to the specification, the &lt;code&gt;ftyp&lt;/code&gt; box has the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;aligned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="n"&gt;FileTypeBox&lt;/span&gt;
  &lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="nf"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="n"&gt;ftyp&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;major_brand&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;minor_version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;compatible_brands&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// to end of the box&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;major_brand&lt;/code&gt; – is a brand identifier&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;minor_version&lt;/code&gt; – is an informative integer for the minor version of the major brand&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;compatible_brands&lt;/code&gt; – is a list, to the end of the box, of brands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, reading the FileTypeBox (&lt;code&gt;ftyp&lt;/code&gt;) would look something like the following (in Golang):&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;type&lt;/span&gt; &lt;span class="n"&gt;FtypBox&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Box&lt;/span&gt;
  &lt;span class="n"&gt;MajorBrand&lt;/span&gt;       &lt;span class="kt"&gt;string&lt;/span&gt;
  &lt;span class="n"&gt;MinorVersion&lt;/span&gt;     &lt;span class="kt"&gt;uint32&lt;/span&gt;
  &lt;span class="n"&gt;CompatibleBrands&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;FtypBox&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadBoxData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Read box header.&lt;/span&gt;
  &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MajorBrand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MinorVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BigEndian&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Uint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompatibleBrands&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompatibleBrands&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;4&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Going over the above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reading the first 4 bytes of the box header as an unsigned 32 bit integer (big endian) gives us the box size: &lt;code&gt;32 bytes&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The next 4 bytes gives us: &lt;code&gt;0x66747970&lt;/code&gt; in hexidecimal, or &lt;code&gt;ftyp&lt;/code&gt; as a string.&lt;/li&gt;
&lt;li&gt;Next 4 bytes gives us the Major Brand: &lt;code&gt;0x69736F6D&lt;/code&gt; in hexidecimal, or &lt;code&gt;isom&lt;/code&gt; as a string.&lt;/li&gt;
&lt;li&gt;Next 4 bytes gives us the Minor Version: &lt;code&gt;512&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The next 16 bytes, read as &lt;code&gt;uint32be&lt;/code&gt; (into a string) at a time gives us an array of compatible brands: &lt;code&gt;isom&lt;/code&gt;, &lt;code&gt;iso2&lt;/code&gt;, &lt;code&gt;avc1&lt;/code&gt;,
and &lt;code&gt;mp41&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We have read a total of &lt;code&gt;32 bytes&lt;/code&gt; as defined in the box header. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See a minimal example:&lt;br&gt;
&lt;a href="https://gist.github.com/alfg/7375aee32fda490de4bf62fbced49d2e#file-mp4_example-go" rel="noopener noreferrer"&gt;https://gist.github.com/alfg/7375aee32fda490de4bf62fbced49d2e#file-mp4_example-go&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ go run mp4.go tears-of-steel.mp4
ftyp.name:  ftyp
ftyp.major_brand:  isom
ftyp.minor_version:  512
ftyp.compatible_brands:  [isom iso2 avc1 mp41]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you were to open the mp4 into a hex editor, it would look something like this for the &lt;code&gt;ftyp&lt;/code&gt; box:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0x00 00 00 00 20 66 74 79 70 | 69 73 6F 6D 00 00 02 00 ... ftypisom....
0x10 69 73 6F 6D 69 73 6F 32 | 61 76 63 31 6D 70 34 31 isomiso2avc1mp41
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Box!
&lt;/h2&gt;

&lt;p&gt;Now that we've read the &lt;code&gt;ftyp&lt;/code&gt; box, we can move on to the next box header, which happens to be the &lt;code&gt;moov&lt;/code&gt; box:&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;type&lt;/span&gt; &lt;span class="n"&gt;MoovBox&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Box&lt;/span&gt;
  &lt;span class="n"&gt;Mvhd&lt;/span&gt;  &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MvhdBox&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MoovBox&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;boxes&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;readBoxes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Reader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;BoxHeaderSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Size&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;BoxHeaderSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;box&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;boxes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"mvhd"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mvhd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;MvhdBox&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mvhd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&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;p&gt;The &lt;code&gt;moov&lt;/code&gt; box contains a nested &lt;code&gt;mvhd&lt;/code&gt; box, so we also need to define &lt;code&gt;mvhd&lt;/code&gt; too:&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;type&lt;/span&gt; &lt;span class="n"&gt;MvhdBox&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Box&lt;/span&gt;
  &lt;span class="n"&gt;Flags&lt;/span&gt;            &lt;span class="kt"&gt;uint32&lt;/span&gt;
  &lt;span class="n"&gt;Version&lt;/span&gt;          &lt;span class="kt"&gt;uint8&lt;/span&gt;
  &lt;span class="n"&gt;CreationTime&lt;/span&gt;     &lt;span class="kt"&gt;uint32&lt;/span&gt;
  &lt;span class="n"&gt;ModificationTime&lt;/span&gt; &lt;span class="kt"&gt;uint32&lt;/span&gt;
  &lt;span class="n"&gt;Timescale&lt;/span&gt;        &lt;span class="kt"&gt;uint32&lt;/span&gt;
  &lt;span class="n"&gt;Duration&lt;/span&gt;         &lt;span class="kt"&gt;uint32&lt;/span&gt;
  &lt;span class="n"&gt;Rate&lt;/span&gt;             &lt;span class="n"&gt;Fixed32&lt;/span&gt;
  &lt;span class="n"&gt;Volume&lt;/span&gt;           &lt;span class="n"&gt;Fixed16&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MvhdBox&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadBoxData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timescale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BigEndian&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Uint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BigEndian&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Uint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fixed32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;24&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Volume&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fixed16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;24&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;26&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;p&gt;See the example with &lt;code&gt;moov&lt;/code&gt; and &lt;code&gt;mvhd&lt;/code&gt; box included:&lt;br&gt;
&lt;a href="https://gist.github.com/alfg/7375aee32fda490de4bf62fbced49d2e#file-mp4_example_2-go" rel="noopener noreferrer"&gt;https://gist.github.com/alfg/7375aee32fda490de4bf62fbced49d2e#file-mp4_example_2-go&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ go run mp4.go tears-of-steel.mp4
ftyp.name:  ftyp
ftyp.major_brand:  isom
ftyp.minor_version:  512
ftyp.compatible_brands:  [isom iso2 avc1 mp41]
moov.name:  moov 3170
moov.mvhd.name:  mvhd
moov.mvhd.version:  0
moov.mvhd.volume:  1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  EOF
&lt;/h2&gt;

&lt;p&gt;Now that we've parsed 3 boxes, hopefully you have an idea on how to implement more. The process is iterative when using a reader:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the box header, containing the box size and name.&lt;/li&gt;
&lt;li&gt;Refer to the specification to read and/or skip fields.&lt;/li&gt;
&lt;li&gt;Skip any remaining bytes left in the box size.&lt;/li&gt;
&lt;li&gt;Read the next box (or skip).&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Some things to keep in mind:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;Some boxes have multiple versions, and therefore can differ in the struct and overall size of the box.&lt;/li&gt;
&lt;li&gt;You can skip properties, but the reader must know how many bytes to skip.&lt;/li&gt;
&lt;li&gt;There are various MP4 specifications beyond &lt;code&gt;MPEG-4 Part 14&lt;/code&gt; as more boxes are being added throughout the years.&lt;/li&gt;
&lt;li&gt;Fragmented MP4 files (fMP4) are segmented as a series of &lt;code&gt;moof&lt;/code&gt; and &lt;code&gt;mdat&lt;/code&gt; boxes. This is more common and optimial for streaming delivery. I'll cover this in a future post.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thanks for reading!
&lt;/h2&gt;

&lt;p&gt;For a more complete example of reading MP4 boxes in Go, check out:&lt;br&gt;
&lt;a href="https://github.com/alfg/mp4" rel="noopener noreferrer"&gt;https://github.com/alfg/mp4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also have a more advanced MP4 reader and writer in Rust:&lt;br&gt;
&lt;a href="https://github.com/alfg/mp4rs" rel="noopener noreferrer"&gt;https://github.com/alfg/mp4rs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I highly suggest some of the following tools for inspecting MP4 files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/gpac/gpac/wiki/MP4Box" rel="noopener noreferrer"&gt;MP4Box&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gpac.github.io/mp4box.js" rel="noopener noreferrer"&gt;mp4box.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MediaArea/MediaInfo" rel="noopener noreferrer"&gt;MediaInfo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ffmpeg.org/ffprobe.html" rel="noopener noreferrer"&gt;FFProbe&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find me on GitHub at: &lt;a href="https://github.com/alfg" rel="noopener noreferrer"&gt;https://github.com/alfg&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy Hacking! 🎥&lt;/p&gt;

&lt;h1&gt;
  
  
  References and Resources
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/library/archive/documentation/QuickTime/QTFF" rel="noopener noreferrer"&gt;https://developer.apple.com/library/archive/documentation/QuickTime/QTFF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/MPEG-4_Part_14" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/MPEG-4_Part_14&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/ISO/IEC_base_media_file_format" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/ISO/IEC_base_media_file_format&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Comparison_of_video_container_formats" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Comparison_of_video_container_formats&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/FourCC" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/FourCC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/alfg/7375aee32fda490de4bf62fbced49d2e" rel="noopener noreferrer"&gt;https://gist.github.com/alfg/7375aee32fda490de4bf62fbced49d2e&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/alfg" rel="noopener noreferrer"&gt;https://github.com/alfg&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>multimedia</category>
      <category>video</category>
      <category>streaming</category>
      <category>mp4</category>
    </item>
  </channel>
</rss>
