Jekyll2019-06-14T07:10:12+00:00https://sdumetz.github.io/feed.xmlSebastien Dumetz’s BlogA blog on my various creation experiencesPlugins helpers for Jekyll2017-08-22T00:00:00+00:002017-08-22T00:00:00+00:00https://sdumetz.github.io/2017/08/22/jekyll-plugins-helpers<p>I use <a href="https://jekyllrb.com">Jekyll</a> a lot at work for various static sites. Throughout it, I’ve developped some handy <a href="https://github.com/sdumetz/jekyll-inline-svg">plugins</a>. While Jekyll’s plugins system is easy and powerful to use, the doc lacks some info on helpers functions, right_way™ to parse arguments, etc… Here’s a short list, half for myself to remember, half for others to learn.</p> <h2 id="class-documentation">Class documentation</h2> <p>While <a href="https://jekyllrb.com">jekyll</a> is great, developpers might like <a href="http://www.rubydoc.info/gems/jekyll">rubydoc</a>’s page to get a complete Class reference on Jekyll’s internals. The <a href="http://www.rubydoc.info/gems/jekyll/Jekyll/Utils">Utils</a> class has some useful methods. Similarly, <a href="http://www.rubydoc.info/gems/liquid">Liquid</a> gem’s docs are not advertised in any of the mainstream sites. It reveals priceless when digging into templates internals.</p> <p>The <code class="highlighter-rouge">Liquid</code> class has some useful constants, like <code class="highlighter-rouge">Liquid::QuotedFragment</code> which will match any quoted string</p> <h2 id="helper-functions">Helper functions</h2> <h3 id="lookup_variable">lookup_variable</h3> <p><code class="highlighter-rouge">lookup_variable(name, context)</code> is the only export of <a href="https://github.com/jekyll/jekyll/blob/master/lib/jekyll/liquid_extensions.rb">Jekyll::LiquidExtensions</a>, and is a handy shortcut to interpret liquid variables at render time.</p> <p>Here is the example code for a <code class="highlighter-rouge">Liquid::Tag</code> :</p> <figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">require</span> <span class="s1">'jekyll/liquid_extensions'</span> <span class="k">module</span> <span class="nn">Jekyll</span> <span class="k">module</span> <span class="nn">Tags</span> <span class="k">class</span> <span class="nc">FooTag</span> <span class="o">&lt;</span> <span class="no">Liquid</span><span class="o">::</span><span class="no">Tag</span> <span class="kp">include</span> <span class="no">Jekyll</span><span class="o">::</span><span class="no">LiquidExtensions</span> <span class="no">VARIABLE</span> <span class="o">=</span> <span class="sr">/\{\{\s*([\w]+\.?[\w]*)\s*\}\}/i</span> <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">tag_name</span><span class="p">,</span> <span class="n">markup</span><span class="p">,</span> <span class="n">tokens</span><span class="p">)</span> <span class="k">super</span> <span class="vi">@markup</span> <span class="o">=</span> <span class="n">markup</span> <span class="k">end</span> <span class="k">def</span> <span class="nf">interpret</span><span class="p">(</span><span class="n">markup</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span> <span class="n">markup</span><span class="p">.</span><span class="nf">scan</span> <span class="no">VARIABLE</span> <span class="k">do</span> <span class="o">|</span><span class="n">variable</span><span class="o">|</span> <span class="n">markup</span> <span class="o">=</span> <span class="n">markup</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="no">VARIABLE</span><span class="p">,</span> <span class="n">lookup_variable</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">variable</span><span class="p">.</span><span class="nf">first</span><span class="p">))</span> <span class="k">end</span> <span class="n">markup</span> <span class="k">end</span> <span class="k">def</span> <span class="nf">render</span><span class="p">(</span><span class="n">context</span><span class="p">)</span> <span class="n">parsed_markup</span> <span class="o">=</span> <span class="n">interpret</span><span class="p">(</span><span class="vi">@markup</span><span class="p">,</span><span class="n">context</span><span class="p">)</span> <span class="c1"># actual code here</span> <span class="k">end</span> <span class="k">end</span> <span class="k">end</span> <span class="k">end</span></code></pre></figure> <p>It will replace anything enclosed in “{{ }} “ by it’s variable value within context. If the variable does not exists, it outputs the variable’s name.</p> <h3 id="sanitized_path">sanitized_path</h3> <p>Jekyll require you to “jail” paths to the site’s source directory. <code class="highlighter-rouge">Jekyll.sanitized_path(base, file)</code> will effectively ensure this. Complete behaviour tests are available for review <a href="https://github.com/jekyll/jekyll/blob/73419cb374be1b8f45818a23116cf71db93549ce/test/test_path_sanitization.rb">here</a>. Practical usage inside your plugin will look like this :</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def render(context) file_path = Jekyll.sanitized_path(context.registers[:site].source, interpret(@markup,context)) # actual code here end </code></pre></div></div> <p>Thus, <code class="highlighter-rouge">/foo.txt</code>, <code class="highlighter-rouge">foo.txt</code> will resolve relative to the site’s source dir. Furthermore, <code class="highlighter-rouge">../foo.txt</code> and <code class="highlighter-rouge">/../foo.txt</code> will also work. I don’t see why you’d do that but it’s always good to have some layer of safety.</p> <h2 id="testing">Testing</h2> <p>Greatly inspired by work done by others on <a href="https://github.com/jekyll/jekyll-sitemap">jekyll-sitemap</a>.</p> <h3 id="integration-testing">Integration testing</h3> <p>Always check your code actually does something out there in the wild. Fortunately, it’s easy to do just that. Create a <code class="highlighter-rouge">fixture/</code> directory with the required files :</p> <ul> <li>_layouts/default.html</li> <li>index.html</li> <li>any necessary file for test purposes</li> </ul> <p>Then test (using rspec here. adapt to your framework of choice)</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SOURCE_DIR = File.expand_path("../fixtures", __FILE__) DEST_DIR = File.expand_path("../dest", __FILE__) def source_dir(*files) File.join(SOURCE_DIR, *files) end def dest_dir(*files) File.join(DEST_DIR, *files) end describe "Integration" do before(:context) do config = Jekyll.configuration({ "source" =&gt; source_dir, "destination" =&gt; dest_dir, "url" =&gt; "http://example.org", }) site = Jekyll::Site.new(config) site.process end it "render site" do expect(File.exist?(dest_dir("index.html"))).to be_truthy end end </code></pre></div></div> <p>Then add test cases to verify if output is <em>as expected</em>.</p> <h2 id="hooks">Hooks</h2> <p>Documentation on <a href="http://www.rubydoc.info/gems/jekyll/Jekyll/Hooks">Hooks</a> is sparse and it’s a rather underused feature. In my experience, nearly anything you’d want to do with a hook can be done with a template. However :</p> <p><strong>Hooks can dramatically speed up your rendering</strong></p> <p>They require no context switch, no bouncing from Liquid components : pure ruby logic. I had this snippet to get a page’s language depending on its url :</p> <figure class="highlight"><pre><code class="language-liquid" data-lang="liquid"> <span class="p">{%</span><span class="w"> </span><span class="kr">unless</span><span class="w"> </span><span class="nv">lang</span><span class="w"> </span><span class="p">%}</span> <span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nv">page</span><span class="p">.</span><span class="nv">lang</span><span class="w"> </span><span class="p">%}</span> <span class="p">{%</span><span class="w"> </span><span class="nt">assign</span><span class="w"> </span><span class="nv">lang</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">page</span><span class="p">.</span><span class="nv">lang</span><span class="w"> </span><span class="p">%}</span> <span class="p">{%</span><span class="w"> </span><span class="kr">elsif</span><span class="w"> </span><span class="nv">page</span><span class="p">.</span><span class="nv">url</span><span class="w"> </span><span class="ow">contains</span><span class="w"> </span><span class="s2">"/fr"</span><span class="w"> </span><span class="p">%}</span> <span class="p">{%</span><span class="w"> </span><span class="nt">assign</span><span class="w"> </span><span class="nv">lang</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"fr"</span><span class="w"> </span><span class="p">%}</span> <span class="p">{%</span><span class="w"> </span><span class="kr">elsif</span><span class="w"> </span><span class="nv">page</span><span class="p">.</span><span class="nv">url</span><span class="w"> </span><span class="ow">contains</span><span class="w"> </span><span class="s2">"/en"</span><span class="w"> </span><span class="p">%}</span> <span class="p">{%</span><span class="w"> </span><span class="nt">assign</span><span class="w"> </span><span class="nv">lang</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"en"</span><span class="w"> </span><span class="p">%}</span> <span class="p">{%</span><span class="w"> </span><span class="kr">else</span><span class="w"> </span><span class="p">%}</span> <span class="p">{%</span><span class="w"> </span><span class="nt">assign</span><span class="w"> </span><span class="nv">lang</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"fr"</span><span class="w"> </span><span class="p">%}</span> <span class="p">{%</span><span class="w"> </span><span class="kr">endif</span><span class="w"> </span><span class="p">%}</span> <span class="p">{%</span><span class="w"> </span><span class="kr">endunless</span><span class="w"> </span><span class="p">%}</span></code></pre></figure> <p>This simple snippet was adding 2 seconds of render time on every update, with a site of ~100 pages.</p> <p>The exact same thing in ruby would take a negligible amount of time :</p> <figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">re</span> <span class="o">=</span> <span class="sr">/^\/(?&lt;lang&gt;fr|en)\/(.*)$/</span> <span class="no">Jekyll</span><span class="o">::</span><span class="no">Hooks</span><span class="p">.</span><span class="nf">register</span> <span class="ss">:documents</span><span class="p">,</span> <span class="ss">:pre_render</span><span class="p">,</span> <span class="ss">priority: </span><span class="s2">"high"</span> <span class="k">do</span> <span class="o">|</span><span class="n">content</span><span class="p">,</span> <span class="n">doc</span><span class="o">|</span> <span class="n">url</span> <span class="o">=</span> <span class="n">doc</span><span class="p">.</span><span class="nf">page</span><span class="p">[</span><span class="s2">"url"</span><span class="p">]</span> <span class="n">filepath</span> <span class="o">=</span> <span class="n">doc</span><span class="p">.</span><span class="nf">page</span><span class="p">[</span><span class="s2">"relative_path"</span><span class="p">]</span> <span class="k">if</span> <span class="o">!</span> <span class="n">doc</span><span class="p">.</span><span class="nf">page</span><span class="p">.</span><span class="nf">key?</span> <span class="s2">"lang"</span> <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="nf">match</span><span class="p">(</span><span class="n">url</span><span class="p">)</span> <span class="k">if</span> <span class="n">match</span> <span class="n">doc</span><span class="p">.</span><span class="nf">page</span><span class="p">[</span><span class="s2">"lang"</span><span class="p">]</span> <span class="o">=</span> <span class="n">match</span><span class="p">[</span><span class="s2">"lang"</span><span class="p">]</span> <span class="k">else</span> <span class="n">doc</span><span class="p">.</span><span class="nf">page</span><span class="p">[</span><span class="s2">"lang"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"fr"</span> <span class="k">end</span> <span class="k">end</span> <span class="k">end</span></code></pre></figure> <hr /> <p>Jekyll’s doc already disclose the <a href="https://jekyllrb.com/docs/plugins/#hooks">scopes and events</a> available for use. A few precisions :</p> <p>One can not set pages variables in the <code class="highlighter-rouge">:post_init</code> phase : Those hooks use the <code class="highlighter-rouge">Jekyll:Document</code>, <code class="highlighter-rouge">Jekyll:Site</code>, etc… Objects. Trying to do so will yield an error :</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Error: undefined method `[]=' for #&lt;Jekyll::Document:0x00000002fcedb8&gt; </code></pre></div></div> <p>On the other hand, <code class="highlighter-rouge">:pre_render</code> hooks are using <a href="http://www.rubydoc.info/gems/jekyll/Jekyll/Drops/DocumentDrop">Jekyll::Drops::DocumentDrop</a> and equivalents, which set the <code class="highlighter-rouge">[]=</code> operator.</p> <p><code class="highlighter-rouge">:documents</code> hooks have access to a “content” variable, <code class="highlighter-rouge">|content,doc|</code>. It’s defined as “NO CONTENT” in early hook.</p> <p>The <code class="highlighter-rouge">page</code> variable can be modified in <code class="highlighter-rouge">:documents, :pre_render</code> hooks using thissyntax : <code class="highlighter-rouge">doc.page["foo"]="bar"</code>, and will be available in templates as ``. The <code class="highlighter-rouge">site</code> variable can be modified at any stage using <code class="highlighter-rouge">site.config["foo"] = "bar"</code>. Site is generally still accessible in other hooks as a child of the main object.</p> <figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Jekyll</span><span class="o">::</span><span class="no">Hooks</span><span class="p">.</span><span class="nf">register</span> <span class="ss">:documents</span><span class="p">,</span> <span class="ss">:pre_render</span><span class="p">,</span> <span class="ss">priority: </span><span class="s2">"high"</span> <span class="k">do</span> <span class="o">|</span><span class="n">content</span><span class="p">,</span> <span class="n">doc</span><span class="o">|</span> <span class="n">url</span> <span class="o">=</span> <span class="n">doc</span><span class="p">.</span><span class="nf">page</span><span class="p">[</span><span class="s2">"url"</span><span class="p">]</span> <span class="n">doc</span><span class="p">.</span><span class="nf">page</span><span class="p">[</span><span class="s2">"foo"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"hello world"</span> <span class="c1"># ...</span> <span class="k">end</span></code></pre></figure>I use Jekyll a lot at work for various static sites. Throughout it, I’ve developped some handy plugins. While Jekyll’s plugins system is easy and powerful to use, the doc lacks some info on helpers functions, right_way™ to parse arguments, etc… Here’s a short list, half for myself to remember, half for others to learn.Handle linux signals in Unity3D2017-07-01T00:00:00+00:002017-07-01T00:00:00+00:00https://sdumetz.github.io/2017/07/01/handle-unix-signals-unity<p>Unity3D’s portable runtime based on <a href="http://www.mono-project.com/">Mono</a> has become a great “write once run everywhere” tool for interactive apps or video games. However stable it is, there is always some edge cases to portability. The handling of SIGTERM, SIGINT, etc… linux (well, <a href="https://en.wikipedia.org/wiki/Unix_signal#POSIX_signals">POSIX</a>) signals are one of them.</p> <h2 id="the-bug">The bug</h2> <p>Make a dummy unity3D app. Make it do some work in <code class="highlighter-rouge">OnApplicationQuit()</code> callback. Get it’s PID and terminate it with <code class="highlighter-rouge">kill &lt;pid&gt;</code>. The exit handler never get called.</p> <p>Other app lifecycle events like <code class="highlighter-rouge">GameObject.OnDestroy()</code> obviously won’t get called either because scene cleanup is a consequence of a proper exit sequence.</p> <p>linux(<em>UNIX/POSIX/whatever</em>) exit signals behaviour is well documented. Generally only two of them will ask for specific exit handlers :</p> <table> <tbody> <tr> <td>Common name</td> <td>N°</td> <td>Action</td> </tr> <tr> <td><strong>SIGINT</strong></td> <td>2</td> <td>Interruption</td> </tr> <tr> <td><strong>SIGTERM</strong></td> <td>5</td> <td>Exit request</td> </tr> </tbody> </table> <p>Lots of ways exists to handle signals under Mono. However Unity3D really is a thing of its own. As such, it doesn’t let us catch those little things.</p> <h2 id="the-fix">The fix</h2> <p>As a “temporary” fix, I made a barebones C file to catch the process’s signal :</p> <p><em>sighandler.c</em></p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cp">#include &lt;signal.h&gt; </span> <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">c</span><span class="p">)(</span><span class="kt">int</span><span class="p">);</span> <span class="kt">void</span> <span class="n">sig_callback</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">){</span> <span class="n">c</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> <span class="p">}</span> <span class="kt">void</span> <span class="n">OnTerm</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">handler</span><span class="p">)(</span><span class="kt">int</span><span class="p">)){</span> <span class="n">c</span> <span class="o">=</span> <span class="n">handler</span><span class="p">;</span> <span class="n">signal</span><span class="p">(</span><span class="n">SIGTERM</span><span class="p">,</span> <span class="n">sig_callback</span><span class="p">);</span> <span class="p">}</span></code></pre></figure> <hr /> <p>It compiles easily to a “<strong>.so</strong>” shared library</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> gcc -shared -o libsighandler.so -fPIC sighandler.c </code></pre></div></div> <p>The output file (compiled on debian jessie) can be downloaded <a href="/data/posts/libsighandler.so">here</a>.</p> <p>We need this file in <code class="highlighter-rouge">Assets/Plugins</code> to be automagically exported on build targetting linux platform. Then it needs to be referenced from Unity Scripts. In C# :</p> <figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">using</span> <span class="nn">System.Collections</span><span class="p">;</span> <span class="k">using</span> <span class="nn">UnityEngine</span><span class="p">;</span> <span class="k">public</span> <span class="k">class</span> <span class="nc">HandleTerm</span> <span class="p">:</span> <span class="n">MonoBehaviour</span> <span class="p">{</span> <span class="c1">//This is the important part</span> <span class="k">public</span> <span class="k">delegate</span> <span class="k">void</span> <span class="nf">handlerDelegate</span><span class="p">();</span> <span class="p">[</span><span class="nf">DllImport</span> <span class="p">(</span><span class="s">"sighandler"</span><span class="p">)]</span> <span class="k">private</span> <span class="k">static</span> <span class="k">extern</span> <span class="k">void</span> <span class="nf">OnTerm</span><span class="p">(</span><span class="n">handlerDelegate</span> <span class="n">handler</span><span class="p">);</span> <span class="k">void</span> <span class="nf">ExitHandler</span><span class="p">(){</span> <span class="nf">print</span><span class="p">(</span><span class="s">"onExit"</span><span class="p">);</span> <span class="c1">//We must call Application.Quit() manually :</span> <span class="c1">// The default built-in handler is cancelled.</span> <span class="n">Application</span><span class="p">.</span><span class="nf">Quit</span><span class="p">();</span> <span class="p">}</span> <span class="c1">//Then it's just normal code</span> <span class="c1">// Use this for initialization</span> <span class="k">void</span> <span class="nf">Start</span> <span class="p">()</span> <span class="p">{</span> <span class="c1">//Register handler on initialization</span> <span class="nf">OnTerm</span><span class="p">(</span><span class="k">new</span> <span class="nf">handlerDelegate</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="n">ExitHandler</span><span class="p">));</span> <span class="p">}</span> <span class="c1">// Update is called once per frame</span> <span class="k">void</span> <span class="nf">Update</span> <span class="p">()</span> <span class="p">{</span> <span class="p">}</span> <span class="k">void</span> <span class="nf">OnApplicationQuit</span><span class="p">(){</span> <span class="c1">//This doesn't get called under normal circumstances when app get a SIGTERM.</span> <span class="nf">print</span><span class="p">(</span><span class="s">"Application Quit"</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>Note: <code class="highlighter-rouge">print()</code> statements get logged to <code class="highlighter-rouge">~/.config/unity3d/&lt;company_name&gt;/&lt;app_name&gt;/Player.log</code>.</p> <p>This will override system’s default <strong>SIGTERM</strong> and <strong>SIGINT</strong> handlers and let you act accordingly. Note that it’s bad behaviour to take more than a few seconds to exit. As such, most window managers will <strong>SIGKILL</strong> the process after a few seconds. <strong>SIGKILL</strong> by itself is interruptible so behave!</p>Unity3D’s portable runtime based on Mono has become a great “write once run everywhere” tool for interactive apps or video games. However stable it is, there is always some edge cases to portability. The handling of SIGTERM, SIGINT, etc… linux (well, POSIX) signals are one of them.JS CustomEvent efficiency2017-06-20T00:00:00+00:002017-06-20T00:00:00+00:00https://sdumetz.github.io/2017/06/20/js-customevent-efficiency<p>CustomEvent in browser-JS is a rarely used feature, while it’s ubiquitous in it’s server-side counterpart. It looks like an elegant solution to dispatch state changes, redux-style.</p> <p>Let’s see the good in this undervaluated feature</p> <h2 id="browser-support">Browser support</h2> <p>Good ol’ <a href="https://caniuse.com/#feat=CustomEvent">caniuse</a> is positive :</p> <script async="" src="//cdn.jsdelivr.net/caniuse-embed/1.1.0/caniuse-embed.min.js"></script> <p class="ciu_embed" data-feature="customevent" data-periods="current,past_1,past_2"> <a href="http://caniuse.com/#feat=customevent">Can I Use customevent?</a> Data on support for the customevent feature across the major browsers from caniuse.com. </p> <p>We’ll need a <em>IE 11</em> polyfill, which is fortunately simple enough (from <a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill">mdn</a>):</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span> <span class="k">typeof</span> <span class="nb">window</span><span class="p">.</span><span class="nx">CustomEvent</span> <span class="o">===</span> <span class="s2">"function"</span> <span class="p">)</span> <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> <span class="kd">function</span> <span class="nx">CustomEvent</span> <span class="p">(</span> <span class="nx">event</span><span class="p">,</span> <span class="nx">params</span> <span class="p">)</span> <span class="p">{</span> <span class="nx">params</span> <span class="o">=</span> <span class="nx">params</span> <span class="o">||</span> <span class="p">{</span> <span class="na">bubbles</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="na">cancelable</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="na">detail</span><span class="p">:</span> <span class="kc">undefined</span> <span class="p">};</span> <span class="kd">var</span> <span class="nx">evt</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createEvent</span><span class="p">(</span> <span class="s1">'CustomEvent'</span> <span class="p">);</span> <span class="nx">evt</span><span class="p">.</span><span class="nx">initCustomEvent</span><span class="p">(</span> <span class="nx">event</span><span class="p">,</span> <span class="nx">params</span><span class="p">.</span><span class="nx">bubbles</span><span class="p">,</span> <span class="nx">params</span><span class="p">.</span><span class="nx">cancelable</span><span class="p">,</span> <span class="nx">params</span><span class="p">.</span><span class="nx">detail</span> <span class="p">);</span> <span class="k">return</span> <span class="nx">evt</span><span class="p">;</span> <span class="p">}</span> <span class="nx">CustomEvent</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">Event</span><span class="p">.</span><span class="nx">prototype</span><span class="p">;</span> <span class="nb">window</span><span class="p">.</span><span class="nx">CustomEvent</span> <span class="o">=</span> <span class="nx">CustomEvent</span><span class="p">;</span> <span class="p">})();</span></code></pre></figure> <h2 id="usage">Usage</h2> <p>We can easily make helper functions like this :</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">dispatchFoo</span><span class="p">(</span><span class="nx">detail</span><span class="p">){</span> <span class="nb">document</span><span class="p">.</span><span class="nx">dispatchEvent</span><span class="p">(</span><span class="k">new</span> <span class="nx">CustomEvent</span><span class="p">(</span><span class="s2">"foo"</span><span class="p">,{</span><span class="na">detail</span><span class="p">:</span><span class="nx">detail</span><span class="p">}));</span> <span class="p">}</span> <span class="kd">function</span> <span class="nx">listenFoo</span><span class="p">(</span><span class="nx">fn</span><span class="p">){</span> <span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s2">"foo"</span><span class="p">,</span><span class="nx">fn</span><span class="p">);</span> <span class="k">return</span> <span class="nb">document</span><span class="p">.</span><span class="nx">removeEventListener</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="nb">document</span><span class="p">,</span><span class="s2">"foo"</span><span class="p">,</span><span class="nx">fn</span><span class="p">);</span> <span class="p">}</span></code></pre></figure> <p>The listener can unsubscribe itself as it should. It lacks things like a <code class="highlighter-rouge">listenOnce()</code> shortcut, but that should be straightforward to implement as needed.</p> <h2 id="performance">Performance</h2> <p>All’s well, but how fast is it?</p> <p>The main drawback we read about over the web is the performance penalty of using too much event listeners in your app. However here, we’re not setting loads of handlers all over the place. It’s only using the <code class="highlighter-rouge">document</code> element. That change things…</p> <p>We’ll be testing 3 cases on 10000 iterations each. (complete code <a href="/data/posts/customEvents/index.html">here</a>)</p> <h4 id="case-1">Case 1</h4> <p>All events are registered on <code class="highlighter-rouge">document</code> global element. 10000 listeners are called when an event is dispatched.</p> <h4 id="case-2">Case 2</h4> <p>10000 DOM nodes are created. Each one gets an element attached. We dispatch 10000 events and each listener gets its own.</p> <h3 id="results">Results</h3> <table> <tbody> <tr> <td>type</td> <td>registration</td> <td>dispatch</td> <td>unsubscribe</td> </tr> <tr> <td>global listener</td> <td>135ms</td> <td>17ms</td> <td>28ms</td> </tr> <tr> <td>multi listeners</td> <td>13ms</td> <td>62ms</td> <td>8ms</td> </tr> </tbody> </table> <p>Of course it’s hard to get a good measurement on a real world use case (3-4 listeners) because it’s sub-millisecond…</p> <p>Overall, those results are great. Why so much hate over this function? It comes from a bad practice that frameworks like React and other <strong>Shadow DOM</strong> based projects tries to solve.</p> <h3 id="repaints">Repaints</h3> <p>Each time something in the DOM that would change appearance is modified, the browser performs a <code class="highlighter-rouge">repaint</code> (it’s more of a cascade of operations, but let’s keep it simple).</p> <p>Except… not really.</p> <p>To be efficient, the browser does not repaint until <em>enough</em> time has elapsed OR it’s queried for some DOM property and need to recalculate.</p> <table> <tbody> <tr> <td>Operation</td> <td>time</td> </tr> <tr> <td>update (x1000)</td> <td>4ms</td> </tr> <tr> <td>query+update (x1000)</td> <td>472ms</td> </tr> </tbody> </table> <p><strong>Ouch.</strong></p> <p>What does it means for our little listener? If it does <strong>ANY</strong> DOM query, it’s going to take ~30ms to complete (depending on page’s complexity).</p> <h3 id="conclusion">Conclusion</h3> <p>the <code class="highlighter-rouge">CustomEvent</code> mechanism is a great way to propagate, well, events… through your app. However, as always with vanilla JS, it’s easy to hurt performance greatly with no apparent reason.</p>CustomEvent in browser-JS is a rarely used feature, while it’s ubiquitous in it’s server-side counterpart. It looks like an elegant solution to dispatch state changes, redux-style.Providing codecs informations on HTML5 video2017-05-29T00:00:00+00:002017-05-29T00:00:00+00:00https://sdumetz.github.io/2017/05/29/setting%20html5-codec-infos<p>The HTML5 VideoElement allows for codecs informations in the <code class="highlighter-rouge">type</code> property. For once, even safari seems to happily support it. How is it important? Because browsers will choose <strong>the first input they can play</strong> solely depending on the <code class="highlighter-rouge">type</code> property (or file extension, if absent). Once they make the wrong choice (i.e. A too high level mp4 video for iOS/ a low end android), they’ll just hang there forever.</p> <p>However, the codecs infos are really not so well documented. I made detailed search on the subject and here are all the bytes you could need to describe your h.264 presets.</p> <h2 id="introduction">Introduction</h2> <p>an h.264 video is described as :</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> &lt;source src="/path/to/video.mp4" type='video/mp4 codecs="avc1.4D401F"'&gt; </code></pre></div></div> <p><code class="highlighter-rouge">avc1</code> being h.264’s reference. the 3 hex bytes after the dot are :</p> <ul> <li>profile_idc</li> <li>constraint_set</li> <li>level_idc</li> </ul> <p>hereafter you’ll find a complete description of their meaning.</p> <h2 id="byte-1--profile_idc">Byte 1 : profile_idc</h2> <p>in h.264 syntax, profile is usually given as a string. For example using libav or ffmpeg, we’ll set it with <code class="highlighter-rouge">-profile:v high</code>. libx264’s code has a simple if chain to parse those (in <code class="highlighter-rouge">common/common.c</code>)</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"> <span class="k">static</span> <span class="kt">int</span> <span class="nf">profile_string_to_int</span><span class="p">(</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">str</span> <span class="p">)</span> <span class="p">{</span> <span class="k">if</span><span class="p">(</span> <span class="o">!</span><span class="n">strcasecmp</span><span class="p">(</span> <span class="n">str</span><span class="p">,</span> <span class="s">"baseline"</span> <span class="p">)</span> <span class="p">)</span> <span class="k">return</span> <span class="n">PROFILE_BASELINE</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span> <span class="o">!</span><span class="n">strcasecmp</span><span class="p">(</span> <span class="n">str</span><span class="p">,</span> <span class="s">"main"</span> <span class="p">)</span> <span class="p">)</span> <span class="k">return</span> <span class="n">PROFILE_MAIN</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span> <span class="o">!</span><span class="n">strcasecmp</span><span class="p">(</span> <span class="n">str</span><span class="p">,</span> <span class="s">"high"</span> <span class="p">)</span> <span class="p">)</span> <span class="k">return</span> <span class="n">PROFILE_HIGH</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span> <span class="o">!</span><span class="n">strcasecmp</span><span class="p">(</span> <span class="n">str</span><span class="p">,</span> <span class="s">"high10"</span> <span class="p">)</span> <span class="p">)</span> <span class="k">return</span> <span class="n">PROFILE_HIGH10</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span> <span class="o">!</span><span class="n">strcasecmp</span><span class="p">(</span> <span class="n">str</span><span class="p">,</span> <span class="s">"high422"</span> <span class="p">)</span> <span class="p">)</span> <span class="k">return</span> <span class="n">PROFILE_HIGH422</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span> <span class="o">!</span><span class="n">strcasecmp</span><span class="p">(</span> <span class="n">str</span><span class="p">,</span> <span class="s">"high444"</span> <span class="p">)</span> <span class="p">)</span> <span class="k">return</span> <span class="n">PROFILE_HIGH444_PREDICTIVE</span><span class="p">;</span> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="p">}</span></code></pre></figure> <p>We can get the corresponding <em>integer</em> in <code class="highlighter-rouge">common/set.h</code></p> <figure class="highlight"><pre><code class="language-c" data-lang="c"> <span class="k">enum</span> <span class="n">profile_e</span> <span class="p">{</span> <span class="n">PROFILE_BASELINE</span> <span class="o">=</span> <span class="mi">66</span><span class="p">,</span> <span class="n">PROFILE_MAIN</span> <span class="o">=</span> <span class="mi">77</span><span class="p">,</span> <span class="n">PROFILE_HIGH</span> <span class="o">=</span> <span class="mi">100</span><span class="p">,</span> <span class="n">PROFILE_HIGH10</span> <span class="o">=</span> <span class="mi">110</span><span class="p">,</span> <span class="n">PROFILE_HIGH422</span> <span class="o">=</span> <span class="mi">122</span><span class="p">,</span> <span class="n">PROFILE_HIGH444_PREDICTIVE</span> <span class="o">=</span> <span class="mi">244</span><span class="p">,</span> <span class="p">};</span></code></pre></figure> <p>so here is the complete translation from profile string to <strong>profile_idc</strong></p> <table> <tbody> <tr> <td>profile</td> <td>enum</td> <td>idc</td> </tr> <tr> <td>baseline</td> <td>66</td> <td>42</td> </tr> <tr> <td>main</td> <td>77</td> <td>4D</td> </tr> <tr> <td>high</td> <td>100</td> <td>64</td> </tr> <tr> <td>high10</td> <td>110</td> <td>6E</td> </tr> <tr> <td>high422</td> <td>122</td> <td>7A</td> </tr> <tr> <td>high444</td> <td>244</td> <td>7A</td> </tr> </tbody> </table> <h2 id="byte-2--constraint_set">Byte 2 : constraint_set</h2> <p>Again from the code :</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">sps</span><span class="o">-&gt;</span><span class="n">b_constraint_set0</span> <span class="o">=</span> <span class="n">sps</span><span class="o">-&gt;</span><span class="n">i_profile_idc</span> <span class="o">==</span> <span class="n">PROFILE_BASELINE</span><span class="p">;</span> <span class="cm">/* x264 doesn't support the features that are in Baseline and not in Main, * namely arbitrary_slice_order and slice_groups. */</span> <span class="n">sps</span><span class="o">-&gt;</span><span class="n">b_constraint_set1</span> <span class="o">=</span> <span class="n">sps</span><span class="o">-&gt;</span><span class="n">i_profile_idc</span> <span class="o">&lt;=</span> <span class="n">PROFILE_MAIN</span><span class="p">;</span> <span class="cm">/* Never set constraint_set2, it is not necessary and not used in real world. */</span> <span class="n">sps</span><span class="o">-&gt;</span><span class="n">b_constraint_set2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">sps</span><span class="o">-&gt;</span><span class="n">b_constraint_set3</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">sps</span><span class="o">-&gt;</span><span class="n">i_level_idc</span> <span class="o">=</span> <span class="n">param</span><span class="o">-&gt;</span><span class="n">i_level_idc</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span> <span class="n">param</span><span class="o">-&gt;</span><span class="n">i_level_idc</span> <span class="o">==</span> <span class="mi">9</span> <span class="o">&amp;&amp;</span> <span class="p">(</span> <span class="n">sps</span><span class="o">-&gt;</span><span class="n">i_profile_idc</span> <span class="o">==</span> <span class="n">PROFILE_BASELINE</span> <span class="o">||</span> <span class="n">sps</span><span class="o">-&gt;</span><span class="n">i_profile_idc</span> <span class="o">==</span> <span class="n">PROFILE_MAIN</span> <span class="p">)</span> <span class="p">)</span> <span class="p">{</span> <span class="n">sps</span><span class="o">-&gt;</span><span class="n">b_constraint_set3</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="cm">/* level 1b with Baseline or Main profile is signalled via constraint_set3 */</span> <span class="n">sps</span><span class="o">-&gt;</span><span class="n">i_level_idc</span> <span class="o">=</span> <span class="mi">11</span><span class="p">;</span> <span class="p">}</span> <span class="cm">/* Intra profiles */</span> <span class="k">if</span><span class="p">(</span> <span class="n">param</span><span class="o">-&gt;</span><span class="n">i_keyint_max</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">&amp;&amp;</span> <span class="n">sps</span><span class="o">-&gt;</span><span class="n">i_profile_idc</span> <span class="o">&gt;=</span> <span class="n">PROFILE_HIGH</span> <span class="p">)</span> <span class="n">sps</span><span class="o">-&gt;</span><span class="n">b_constraint_set3</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span></code></pre></figure> <hr /> <p>A bit more difficult to read but in the end pretty straightforward. The tricky thing here is given how these are written in headers, <code class="highlighter-rouge">constraint_set0</code> is the highest order bit.</p> <p>b_constraint_set0 to b_constraint_set5 and 2 reserved bytes. The code tells us constraint_set2 is never used. Also, 4 and 5 are not implemented in libx264. The last 4 bytes are written as :</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bs_write( s, 4, 0 ); /* reserved */ </code></pre></div></div> <p>so we have this mask : <code class="highlighter-rouge">xx0x0000</code></p> <p>0 and 1 are easy to figure out :</p> <p>Having PROFILE_MAIN, we get : <code class="highlighter-rouge">constraint_set1=1</code> any higher profile would yield <code class="highlighter-rouge">constraint_set1=0</code>. ``constraint_set0` is set only when using PROFILE_BASELINE.</p> <p>Then constraint_set3 is set if <em>level_idc is 9 AND profile is BASELINE or MAIN.</em> or : *keyint_max = 1 and profile_idc is HIGH or more**</p> <p>Easy to figure out if you know your encoding options. If not, any tool would give those results.</p> <p>A quick look at <code class="highlighter-rouge">common/set.c</code> tells us that bits 4 and 5 are treated as the other reserved.</p> <table> <tbody> <tr> <td>profile</td> <td>bits</td> <td>byte</td> </tr> <tr> <td>baseline</td> <td>11000000</td> <td>0xC0</td> </tr> <tr> <td>main</td> <td>01000000</td> <td>0x40</td> </tr> <tr> <td>high</td> <td>00000000</td> <td>0x00</td> </tr> <tr> <td>high(intra)</td> <td>00010000</td> <td>0x10</td> </tr> </tbody> </table> <p><strong>Note : ** this changes if your level_idc is **9</strong> and <code class="highlighter-rouge">profile &lt;= MAIN</code> . Check later section and add <code class="highlighter-rouge">0x10</code> if it’s the case.</p> <h3 id="byte-3--level_idc">Byte 3 : level_idc</h3> <p>We have 2 syntaxes for levels. <strong>4.1</strong> or <strong>41</strong> means the same thing. The code speaks for itself :</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="k">if</span><span class="p">(</span> <span class="o">!</span><span class="n">strcmp</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="s">"1b"</span><span class="p">)</span> <span class="p">)</span> <span class="n">p</span><span class="o">-&gt;</span><span class="n">i_level_idc</span> <span class="o">=</span> <span class="mi">9</span><span class="p">;</span> <span class="k">else</span> <span class="k">if</span><span class="p">(</span> <span class="n">atof</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">6</span> <span class="p">)</span> <span class="n">p</span><span class="o">-&gt;</span><span class="n">i_level_idc</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)(</span><span class="mi">10</span><span class="o">*</span><span class="n">atof</span><span class="p">(</span><span class="n">value</span><span class="p">)</span><span class="o">+</span><span class="p">.</span><span class="mi">5</span><span class="p">);</span> <span class="k">else</span> <span class="n">p</span><span class="o">-&gt;</span><span class="n">i_level_idc</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span><span class="n">value</span><span class="p">);</span></code></pre></figure> <p><strong>1b</strong> is a special case that yields a <strong>9</strong>. Other values are converted</p> <p>So we just take our level and convert it to hex.</p> <h2 id="conclusion">Conclusion</h2> <p>For a standard h.264 Main Concept level 3.1 video, your codec code would be : <code class="highlighter-rouge">avc1.4D401F</code>. For any other stream, you can then infer the bytes from your encoding options.</p>The HTML5 VideoElement allows for codecs informations in the type property. For once, even safari seems to happily support it. How is it important? Because browsers will choose the first input they can play solely depending on the type property (or file extension, if absent). Once they make the wrong choice (i.e. A too high level mp4 video for iOS/ a low end android), they’ll just hang there forever.Encoding videos for the web in 20172017-01-12T00:00:00+00:002017-01-12T00:00:00+00:00https://sdumetz.github.io/2017/01/12/encoding-video-for-the-web<p>I’ve fiddled a bit recently with self-made HTML5 video players. While it’s easy to use and now widely compatible, I’ve had some trouble encoding videos. So here is my cheat sheet for mobile-first video distribution in 2017, including an in-depth look into mp4/webm performance.</p> <h2 id="encoding-commands">Encoding commands</h2> <p>I used libav’s <code class="highlighter-rouge">avconv</code> tool on debian Jessie. It uses <a href="https://packages.debian.org/jessie/libav-tools">libav56</a>. older versions might lack the proper options for webm format. In the following examples, I’ll assume usage of a png frames sequence as input. Otherwise, just remove the <code class="highlighter-rouge">-f image2</code> option.</p> <p>First thing first : mp4.</p> <figure class="highlight"><pre><code class="language-shell" data-lang="shell"> avconv <span class="nt">-y</span> <span class="nt">-f</span> image2 <span class="nt">-i</span> path/to/frame-%04d.png <span class="nt">-c</span>:v libx264 <span class="nt">-c</span>:a copy <span class="nt">-pix_fmt</span> yuv420p <span class="nt">-profile</span>:v main <span class="nt">-level</span> 31 output_file.mp4</code></pre></figure> <p>Note : The iphone 3 is requiring an even lower set with <code class="highlighter-rouge">-profile baseline -level 31</code>. According to apple’s <a href="https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html">doc</a> on http streaming, earlier iphones support only <code class="highlighter-rouge">-level 30</code>. Those settings are for iphone 4 and above. Apple doesn’t advertise which devices support h.264 High profile so I don’t know when it’ll be “safe” to upgrade.</p> <p>Should we still provide multiple fallbacks now? It depends. MPEG-4/H.264 is clearly the most widely supported format. However a quick look in <a href="http://caniuse.com/#feat=mpeg4">caniuse.com</a> show that Opera Mini still don’t support MPEG4/H.264. Well, it’s not like it supports any other format though.</p> <p>So with firefox supporting mp4 since v35 (Jan 2015) and Opera since v25 (Oct 2014), my own <a href="http://camendesign.com/code/video_for_everybody/test.html">video for everybody</a> script just got a lot simpler with no <em>flashPlayer</em> or <em>video/ogv</em> fallbacks.</p> <p>What of the <em>video/webm</em>?</p> <ul> <li><strong>Pros</strong> <ul> <li>More compact</li> <li>Easy seek (more on this later)</li> </ul> </li> <li><strong>Cons</strong> <ul> <li>iOS crash if we put it first</li> <li>android seems to ignore it if it’s not first</li> </ul> </li> </ul> <p>However, encoding in vp8 is no longer a great trouble. While most earlier guides would make you install some voodoo to be able to export your webm file, a stock avconv will now do :</p> <figure class="highlight"><pre><code class="language-shell" data-lang="shell"> avconv <span class="nt">-f</span> image2 <span class="nt">-i</span> path/to/frame-%04d.png <span class="nt">-c</span>:v libvpx <span class="nt">-c</span>:a libvorbis <span class="nt">-qmin</span> 20 <span class="nt">-qmax</span> 30 <span class="nt">-threads</span> auto output_file.webm</code></pre></figure> <h2 id="encoding-choice">Encoding choice</h2> <p>Now is it worth it to replace good ol’ h.264 ? Let’s check the rate/distortion gain. Unlike previous empirical results <a href="/2016/10/06/how-to-read-a-video-backward-2.html">from me</a> and <a href="http://www.streamingmedia.com/articles/editorial/featured-articles/first-look-h.264-and-vp8-compared-67266.aspx">others</a>, I’ll try to go <em>scientific</em> here. While we’re at it, I’ll also test vp9 encoding.</p> <p>Since we’re serious about it, let’s compare a few things :</p> <ul> <li>H.264 main profile 31</li> <li>H.264 high profile 41</li> <li>VP8 (1 or 2 pass)</li> <li>VP9 (1 or 2 pass)</li> </ul> <p>I didn’t do 2pass on H.264 as crf (default quality setting) doesn’t support it. To be really complete, I should try with constant bitrate and other quality-control settings.</p> <p>However, here’s the script I made to test all this :</p> <script src="https://gist.github.com/sdumetz/3e9cee1e991b8351abe881de0880d937.js"></script> <noscript> Gist available on <a href="https://gist.github.com/sdumetz/3e9cee1e991b8351abe881de0880d937">Github</a></noscript> <p>Give it a png sequence <em>aaaaand it’s done</em>.</p> <h3 id="results">Results?</h3> <p>As of now I only measure PSNR (Peak Signal/Noise ratio), added throughout every images. Run the tests at home, YMMV as always.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MP4(main) : 1665kB . PSNR : 15902.2121 MP4(high) : 1705kB . PSNR : 15904.4900 VP8 (1 p) : 1595kB . PSNR : 15848.7263 VP8 (2 p) : 1617kB . PSNR : 15859.8030 VP9 (1 p) : 1614kB . PSNR : 15880.7009 VP9 (2 p) : 1471kB . PSNR : 15908.0548 </code></pre></div></div> <p><em>Higher PSNR = lower distortion.</em></p> <h3 id="how-much-should-i-trust-this">How much should I trust this?</h3> <p><strong>Not much</strong>. Signal/Noise ratio is a thing, but it doesn’t make a difference between this noise in the background and movement noise that makes your video look bad. However, it’s still a hint on real encoding quality.</p> <h3 id="conclusion">Conclusion</h3> <p>Providing a bazilion of fallbacks is no longer required if you target mobile devices, which tends to have up to date browsers and can’t be older than 2006 (obviously…), or are not meant to play video anyway.</p> <p>a fallback to vp8/vp9 encoded video could provide some meaningfull bonus, but is by no means a no-brainer. It depends how much your viewers will rely on seek capacities, for example.</p> <p>Those measures were enough to convince me to drop ogv support make webm secondary in my <a href="https://pixel.holusion.com">online video service</a>.</p> <p><strong>To be continued</strong> : How good are VP8/VP9 when the user seeks a specific time?</p>I’ve fiddled a bit recently with self-made HTML5 video players. While it’s easy to use and now widely compatible, I’ve had some trouble encoding videos. So here is my cheat sheet for mobile-first video distribution in 2017, including an in-depth look into mp4/webm performance.Using Webworkers with Webpack2017-01-02T00:00:00+00:002017-01-02T00:00:00+00:00https://sdumetz.github.io/2017/01/02/webworkers-with-webpack<p>I started using web workers for real-worl loads a few weeks ago and like many before me, I found it to be quite easy to use, but a real pain to debug. I’ll gather my thought about them for later use.</p> <p>The work was pretty simple :</p> <ul> <li>some static assets to always cache upfront</li> <li>A few API routes I wanted to cache “if possible”.</li> </ul> <p>However, as often with shiny new stuff, documentation can be hard to find.</p> <h2 id="setup">Setup</h2> <p>webworkers don’t have access to <em>exactly</em> the same runtime as normal scripts. Namely, all <code class="highlighter-rouge">window</code> methods and properties are missing. For this reason, webpack’s normal build entries won’t work as webservices. Fortunately, webpack provides a <strong>webworker</strong> target. One would need 2 config objects to generate basic modules + a web worker :</p> <figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">const</span> <span class="nx">config</span> <span class="o">=</span> <span class="p">{</span> <span class="na">target</span><span class="p">:</span> <span class="s2">"web"</span><span class="p">,</span> <span class="c1">//default</span> <span class="na">entry</span><span class="p">:</span><span class="s2">"main.js"</span> <span class="p">}</span> <span class="kd">const</span> <span class="nx">wsConfig</span> <span class="o">=</span> <span class="p">{</span> <span class="na">target</span><span class="p">:</span><span class="s2">"webworker"</span><span class="p">,</span> <span class="na">entry</span><span class="p">:</span><span class="s2">"sw.js"</span> <span class="p">}</span> <span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">[</span><span class="nx">config</span><span class="p">,</span> <span class="nx">wsConfig</span><span class="p">];</span></code></pre></figure> <p>It took me longer than i’d like to admit to figure this one out, but it’s dead simple and works like a charm. It’s even, documented in the shiny new <a href="https://webpack.js.org/configuration/target/">webpack 2 doc</a>.</p> <p>However, I could not make it work with <strong>hot-reload</strong>. I simply disabled hotReplacementPlugin() from my swConfig. It means my page won’t reload automatically when I change <code class="highlighter-rouge">sw.js</code>.</p> <p>Another option, which I finally came to use, is the <a href="https://www.npmjs.com/package/sw-precache-webpack-plugin">sw-precache-webpack-plugin</a>, which is just a wrapper around <a href="https://www.npmjs.com/package/sw-precache">sw-precache</a>. It takes your static files and generates a service file with them.</p> <p>The service is slightly larger this way, but it save a lot of time and (more importantly), allows readable strategy handles depending on file names.</p> <h2 id="update">Update</h2> <p>One main trouble you have, starting a web worker is man,aging updates : You want cache to be invalidated <strong>EVERY TIME</strong> it should, but your clients should be able work offline for months (okay, hours…) without trouble.</p> <p>The first trick is to enable <code class="highlighter-rouge">clientsClaim:true</code> in sw-precache, or call <code class="highlighter-rouge">self.clients.claim()</code> in your worker if you can. It’s not a one-stop-fit-all feature, so read <a href="https://developer.mozilla.org/en-US/docs/Web/API/Clients/claim">the doc</a>.</p> <p>The other quick-start improvement is to have a good “install” step. Google’s <a href="https://developers.google.com/web/fundamentals/getting-started/primers/service-workers">introduction</a> example doesn’t handle errors and that caused me some trouble :</p> <figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="k">if</span> <span class="p">(</span><span class="s1">'serviceWorker'</span> <span class="k">in</span> <span class="nb">navigator</span><span class="p">)</span> <span class="p">{</span> <span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'load'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nb">navigator</span><span class="p">.</span><span class="nx">serviceWorker</span><span class="p">.</span><span class="nx">register</span><span class="p">(</span><span class="s1">'/sw.js'</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">registration</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// Registration was successful</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'ServiceWorker registration successful with scope: '</span><span class="p">,</span> <span class="nx">registration</span><span class="p">.</span><span class="nx">scope</span><span class="p">);</span> <span class="p">}).</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// registration failed :(</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'ServiceWorker registration failed: '</span><span class="p">,</span> <span class="nx">err</span><span class="p">);</span> <span class="p">});</span> <span class="p">});</span> <span class="p">}</span></code></pre></figure> <p>It’s not able to cleanly handle <code class="highlighter-rouge">update</code> events.</p> <p>I found the registration example from <a href="https://github.com/GoogleChrome/sw-precache/blob/master/demo/app/js/service-worker-registration.js">swPrecache</a> to be quite complete :</p> <figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="k">if</span> <span class="p">(</span><span class="s1">'serviceWorker'</span> <span class="k">in</span> <span class="nb">navigator</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// Your service-worker.js *must* be located at the top-level directory relative to your site.</span> <span class="c1">// It won't be able to control pages unless it's located at the same level or higher than them.</span> <span class="c1">// *Don't* register service worker file in, e.g., a scripts/ sub-directory!</span> <span class="c1">// See https://github.com/slightlyoff/ServiceWorker/issues/468</span> <span class="nb">navigator</span><span class="p">.</span><span class="nx">serviceWorker</span><span class="p">.</span><span class="nx">register</span><span class="p">(</span><span class="s1">'service-worker.js'</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">reg</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// updatefound is fired if service-worker.js changes.</span> <span class="nx">reg</span><span class="p">.</span><span class="nx">onupdatefound</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// The updatefound event implies that reg.installing is set; see</span> <span class="c1">// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event</span> <span class="kd">var</span> <span class="nx">installingWorker</span> <span class="o">=</span> <span class="nx">reg</span><span class="p">.</span><span class="nx">installing</span><span class="p">;</span> <span class="nx">installingWorker</span><span class="p">.</span><span class="nx">onstatechange</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">switch</span> <span class="p">(</span><span class="nx">installingWorker</span><span class="p">.</span><span class="nx">state</span><span class="p">)</span> <span class="p">{</span> <span class="k">case</span> <span class="s1">'installed'</span><span class="p">:</span> <span class="k">if</span> <span class="p">(</span><span class="nb">navigator</span><span class="p">.</span><span class="nx">serviceWorker</span><span class="p">.</span><span class="nx">controller</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// At this point, the old content will have been purged and the fresh content will</span> <span class="c1">// have been added to the cache.</span> <span class="c1">// It's the perfect time to display a "New content is available; please refresh."</span> <span class="c1">// message in the page's interface.</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'New or updated content is available.'</span><span class="p">);</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="c1">// At this point, everything has been precached.</span> <span class="c1">// It's the perfect time to display a "Content is cached for offline use." message.</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'Content is now available offline!'</span><span class="p">);</span> <span class="p">}</span> <span class="k">break</span><span class="p">;</span> <span class="k">case</span> <span class="s1">'redundant'</span><span class="p">:</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">'The installing service worker became redundant.'</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> <span class="p">}</span> <span class="p">};</span> <span class="p">};</span> <span class="p">}).</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">'Error during service worker registration:'</span><span class="p">,</span> <span class="nx">e</span><span class="p">);</span> <span class="p">});</span> <span class="p">}</span></code></pre></figure> <p>Still not perfect, but it at least clearly says when cache have been updated. If it have not, you know where the trouble is.</p> <h2 id="cache-control">cache control</h2> <p>At the end of the day, the only reliable way to achieve good cache control is to uniquely name your scripts so they can not be mistaken.</p> <p>The sweetness here is either nothing have been updated, and you still have your full old app working (cheers!), or the <code class="highlighter-rouge"><span class="nt">&lt;html&gt;</span></code> is new, and it’s script are named differently, and everything is loaded anew. There’s no annoying middle ground.</p> <p>Creating uniquely named scripts with webpack is easy :</p> <figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">filename</span><span class="p">:</span> <span class="s1">'[name].[hash].js'</span></code></pre></figure> <p><em>there is also a <code class="highlighter-rouge">chunkhash</code> variable which is unique to each chunk. hash however is shared</em></p> <p>The easy way to use it is to just output your HTML as static files from webpack on each rebuild.</p> <p>When you can not, because of some dumb dynamic content you decided to add in it (you fool, it’s so 2000’s!), you can make webpack output a <code class="highlighter-rouge">stats.json</code> at build time, and read it from your server.</p> <figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="c1">// In webpack.config.js</span> <span class="kd">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">"fs"</span><span class="p">);</span> <span class="nx">config</span><span class="p">.</span><span class="nx">plugins</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">plugin</span><span class="p">(</span><span class="s2">"emit"</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">stats</span><span class="p">,</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span><span class="p">(</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s2">"static/build"</span><span class="p">,</span> <span class="s2">"stats.json"</span><span class="p">),</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">stats</span><span class="p">.</span><span class="nx">hash</span><span class="p">),</span><span class="nx">callback</span><span class="p">);</span> <span class="p">});</span> <span class="p">});</span></code></pre></figure> <p>Then you require your file as <code class="highlighter-rouge">const hash = "."+require(./static/build/stats.json)</code> or whatever your path is.</p> <p>Now you can output html dynamically, using the build’s script names.</p> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="c">&lt;!-- in handlebars template --&gt;</span> <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"/static/build/bundle.js"</span></code></pre></figure> <p>if you use a different temlplate engine, well… it’s not like it’s difficult to port, is it?</p> <p><em>and how will it work in development?</em></p> <p>For the development part, I modified a bit my webpack config :</p> <figure class="highlight"><pre><code class="language-js" data-lang="js"><span class="c1">//webpack.config.js</span> <span class="kd">const</span> <span class="nx">isProduction</span> <span class="o">=</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">NODE_ENV</span> <span class="o">===</span> <span class="s1">'production'</span><span class="p">;</span> <span class="c1">//Later in file</span> <span class="nl">filename</span><span class="p">:</span> <span class="nx">isProduction</span><span class="p">?</span><span class="s1">'[name].[hash].js'</span><span class="p">:</span><span class="s1">'[name].js'</span><span class="p">,</span></code></pre></figure> <p>Then in my server, I just have to set <code class="highlighter-rouge">hash = "";</code> in development</p>I started using web workers for real-worl loads a few weeks ago and like many before me, I found it to be quite easy to use, but a real pain to debug. I’ll gather my thought about them for later use.A faster Leap Motion experience2016-12-05T00:00:00+00:002016-12-05T00:00:00+00:00https://sdumetz.github.io/2016/12/05/faster-gestures-with-leap-motion<p>Like many, I was really enthousiast when the Leap motion was unveiled some years ago. At last, a company that emphasized on gesture recognition’s main problems : Speed and precision. Like most, I have been disappointed. Still not fast enough, still not precise enough. There is still not much to do in the land of gesture control. Or is there?</p> <h2 id="why-is-leap-motion-so-bad">Why is Leap Motion so bad?</h2> <p>Don’t get me wrong, the leap motion SDK is great and is a big deal in why it got some initial traction. I’m not pretending I would have made a better job or anything. However, let’s face it :</p> <p><strong>differencing a <em>blob</em> from a finger is hard as hell</strong></p> <p>To demonstrate it, let’s imagine this common use case :</p> <p>I have a leap motion demo, running idle on my monitor. Suddenly, I want to show a customer how it works. <code class="highlighter-rouge">"Hey, look at how well it recognises movements"</code> * swipes in front of the box*. Aaaand… Nothing. It takes a few frames for the leap motion to detect what just appeared as a finger. Then a few more frames to get a correct velocity approximation. A swipe of the hand is only half a second long, tops!</p> <p>So I need my hand to stay stable for half a second, be sure the leap motion detect it, don’t move too fast or too far away or withh too strange positions or it will need to re-detect my hand.</p> <p>Unusable.</p> <h2 id="why-do-i-need-fingers">Why do I need fingers?</h2> <p>I just need a gesture! Movement! “User ask me to go right” type of thing. Let’s say I don’t care if it’s a figner, or anything reflecting IR light.</p> <p>If something moves right above my sensor, it’s probably meaningful.</p> <p>It might not be your use case. For what I know, it might not be anyone else’s use case. But it’s mine.</p> <h2 id="extracting-data">Extracting data</h2> <p>The <a href="https://developer.leapmotion.com/">Leap SDK</a> won’t help you here. It knows 2 things :</p> <ul> <li>Raw data</li> <li>Hands (and associated subsystems : fingers, tools, …)</li> </ul> <p>No “Unidentified light blob” here. That’s a shame, because I imagine they have such a map at some point in their internal filtering, and it’s probably way better than mine.</p> <p>So let’s get raw data from the Leap.</p> <figure class="highlight"><pre><code class="language-cpp" data-lang="cpp"><span class="n">controller</span><span class="p">.</span><span class="n">images</span><span class="p">()[</span><span class="mi">0</span><span class="p">];</span></code></pre></figure> <p>Not <code class="highlighter-rouge">frame.images()</code> because as stated in the <a href="https://developer.leapmotion.com/documentation/cpp/api/Leap.Controller.html#cppclass_leap_1_1_controller_1a85588dbb02a8ff793cf2ced9067f8263">doc</a> : * the images obtained with this function can be newer than images obtained from the current frame of tracking data.*</p> <p>Don’t add unnecessary delay.</p> <center> <div class="mdl-card mdl-shadow--2dp image-card"> <div class="mdl-card__title"> <img src="/data/posts/leap_camera_image_raw.png" alt="a leap motion raw image" /> </div> <div class="mdl-card__supporting-text mdl-card--border"> Here we discover that my monitor is quite IR-reflective </div> </div> </center> <p>The first thing we’ll do is rule out all unnecessary data. Computations on pixel maps can be quite heavy and it’s a good idea to reduce the dataset early. I just needed to see movement on the X axis so the first step was to linearize the image.</p> <p>Due to the large viewing angle, the leap lenses make a fisheye effect on images. We first need to rectify them.</p> <p>It’s either possible to create a shader for it or a function to rectify the full image. The later is fully explained in the <a href="https://developer.leapmotion.com/documentation/cpp/api/Leap.Image.html#cppclass_leap_1_1_image_1a4c6fa722eba7018e148b13677c7ce609">doc</a>.</p> <p>Now that my image is rectified, I’ll just linearize it :</p> <figure class="highlight"><pre><code class="language-cpp" data-lang="cpp"><span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span> <span class="n">raw_data</span> <span class="o">=</span> <span class="n">controller</span><span class="p">.</span><span class="n">images</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="n">data</span><span class="p">();</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">linear_data</span><span class="p">[</span><span class="mi">640</span><span class="p">];</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span><span class="n">x</span><span class="o">&lt;</span><span class="mi">640</span><span class="p">;</span><span class="n">x</span><span class="o">++</span><span class="p">){</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">y</span><span class="o">=</span><span class="mi">90</span><span class="p">;</span><span class="n">y</span><span class="o">=</span><span class="mi">150</span><span class="p">;</span><span class="n">y</span><span class="o">++</span><span class="p">){</span> <span class="c1">//prevent overflow by dividing the value by the number of lines. </span> <span class="n">linear_data</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">=</span> <span class="n">raw_data</span><span class="p">[</span><span class="n">IMAGE_WIDTH</span><span class="o">*</span><span class="n">y</span><span class="o">+</span><span class="n">x</span><span class="p">]</span><span class="o">/</span><span class="mi">60</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>That’s a 640 bytes array containing all the info we’ll need.</p> <center> <div class="mdl-card mdl-shadow--2dp image-card"> <div class="mdl-card__title"> <img src="/data/posts/leap_image_linearized.png" alt="image of my hand with linearized data" /> </div> <div class="mdl-card__supporting-text mdl-card--border"> The raw image, and the result of linearisation at the bottom </div> </div> </center> <h2 id="theres-more-">There’s more !</h2> <p>You can visualize this greyscale data, it’s already great and precise. There’s another great thing we can get out of the leap : Stereovision. You heard Stereo was hard? How about stereo vision computing on a greyscale 1d array? Let’s try.</p> <p>Just extract data from <code class="highlighter-rouge">controller.images()[1]</code> the same way we just did.</p> <p>We’ll make a multiplication of our values, offsetted by a <code class="highlighter-rouge">STEREO</code> factor. This will naturally filter out dots that are not on our target height. The higher the STEREO diff, the lower the points.</p> <p>This is <strong>NOT</strong> fool-proof. Imagine you set a diff of 60 and got 2 fingers distant of 60px: It will be well correlated and maybe not at the target height. However like I said, we’re looking for movement, not spacial interpolation precision.</p>Like many, I was really enthousiast when the Leap motion was unveiled some years ago. At last, a company that emphasized on gesture recognition’s main problems : Speed and precision. Like most, I have been disappointed. Still not fast enough, still not precise enough. There is still not much to do in the land of gesture control. Or is there?Read a video Backward (Part 2)2016-10-06T00:00:00+00:002016-10-06T00:00:00+00:00https://sdumetz.github.io/2016/10/06/how-to-read-a-video-backward-2<p>In previous post, I stated how I made a video player capable of reading a file from back to front. However I’m quite a beginner in video encoding and I felt there was improvements to be made.</p> <p>video intra modes are a thing for profesionnals. Most consummer applications are doing well with default mode. Here is what I found.</p> <h2 id="benchmark-method">Benchmark method</h2> <p>I needed a benchmark for my particular use case. Two things should be compared :</p> <ul> <li>Decode speed</li> <li>Video quality</li> </ul> <h3 id="speed">Speed</h3> <p>Decode speed is easy : Just make the decoder loop and log results. I got pretty consistent results out of those simple lines of code (using <a href="https://github.com/Holusion/stingray">stingray’s decoder</a>) :</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>void cycle(decoder::VideoDecoder* decoder,entities::Video* video ){ int i,c; std::chrono::high_resolution_clock::time_point p(std::chrono::high_resolution_clock::now()); for (c=0;c&lt;10;c++){ decoder-&gt;decodeAndWrite(*video-&gt;buffer); for(i=0;i&lt;DECODE_SIZE;i++){ video-&gt;buffer-&gt;read(); } } using dura = std::chrono::duration&lt;double&gt;; auto d = std::chrono::high_resolution_clock::now() - p; std::cout &lt;&lt; "Decode cycle: " &lt;&lt; std::chrono::duration_cast&lt;dura&gt;(d/10).count() &lt;&lt;" s" &lt;&lt; std::endl; } </code></pre></div></div> <p>Running it on a highest quality (<code class="highlighter-rouge">-q 2</code>) MJPEG video I got 0.165 s/chunk. (Chunk size of 20) – which maps to 121 fps. Real world speed will be a bit less as the system will be busy doing other things (reading inputs, displaying frames).</p> <p>Lowering the quality helps a lot, obviously. Encoding my test video with <code class="highlighter-rouge">-q 10</code> makes the bitrate drop from <em>45kbps</em> to <em>22kbps</em>.</p> <p>That’s for the old-but-working <strong>MJPEG</strong> codec. the interesting thing to note here is correlation between bitrate and decode speed.</p> <center> <table> <thead> <tr> <th>-q &nbsp;</th><th>bitrate &nbsp;</th><th>decode time</th> </tr> </thead> <tbody> <tr><td>2</td><td>45596.1 </td><td>0.165s </td></tr> <tr><td>10</td><td>22591.4 </td><td>0.123s </td></tr> </tbody> </table> </center> <h3 id="quality">Quality</h3> <p>Quality is a bit harder. However as avconv quality options <code class="highlighter-rouge">-b</code>, <code class="highlighter-rouge">-crf</code>, <code class="highlighter-rouge">-q</code>, etc. do not behave consistently at all across codecs. For example, <em>h.264</em> produce good quality results with <code class="highlighter-rouge">-crf 20</code>, while <em>vp8</em> is really bad with <code class="highlighter-rouge">-q 10</code>…</p> <p>I needed a distortion measurement algorithm to get solid results for comparisons. However, keep in mind that even absolute rate/distortion grade is not a good <a href="https://www.hindawi.com/journals/tswj/2014/743604/">assessment</a> of your encoding’s quality : It does not capture it as perceived by the human eye.</p> <p>I did not find a good tool to do it: Manual review it is.</p> <h2 id="h264-encoding">h.264 encoding</h2> <p><strong>libx264</strong> has an intra mode. let’s see what is does :</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>avconv -y -f image2 -i render/frame-%04d.png -vcodec libx264 -an -crf 20 -g 1 -profile:v high test_stingray.mp4 </code></pre></div></div> <center> <table> <thead> <tr> <th>-crf &nbsp;</th><th>bitrate &nbsp;</th><th>decode time</th> </tr> </thead> <tbody> <tr><td>10</td><td>62021.3 </td><td>0.10s </td></tr> <tr><td>20</td><td>21206.61 </td><td>0.090s </td></tr> <tr><td>40</td><td>5725.8 </td><td>0.0676786 </td></tr> </tbody> </table> </center> <p>A few thing we can already take for granted :</p> <ul> <li>h.264 decoding is faster than MJPEG for the same bitrate.</li> <li>for lower bitrates (&lt; 30kb/s), h.264 yields better quality.</li> </ul> <p>My quality measurement is empiric. Just run the tests if you want to be sure.</p> <h2 id="jpeg2000">jpeg2000</h2> <p>the problem with jpeg2000 is it’s not really well supported by ffmpeg. While it provides (on linux) a wrapper to encode a video using libopenjpeg, libavcodec doesn’t decode it out of the box. i had to fiddle a bit to find suitable options.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>avconv -y -f image2 -i render/frame-%04d.png -vcodec jpeg2000 -compression_level 50 -an -g 1 -pix_fmt yuv420p j2k_test_stingray.mov </code></pre></div></div> <p>compression_level is an int between 0 (lossless) and ?! – I could not find the max, but anything above 100 is clearly unusable.</p> <p>According to previous research, file size vs quality should be roughly equivalent to h.264 intra. However I could not test it as the decoder refuses to decode it properly (as do most regular video players…).</p> <h2 id="vp8">vp8</h2> <p><a href="https://fr.wikipedia.org/wiki/WebM">Webm</a> is an open video file format sponsored by google. It’s modern and supports mainly <strong>vp8</strong> and <strong>vp9</strong> video codecs. Only vp8 is supported natively by ffmpeg so let’s try it</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>avconv -y -f image2 -i render/frame-%04d.png -codec:v libvpx -qmin 1 -qmax 8 -threads auto -codec:a none webm_stingray.webm </code></pre></div></div> <p>Notice how the <code class="highlighter-rouge">-g 1</code> flag is gone? <a href="https://en.wikipedia.org/wiki/VP8">vp8</a> is natively pure-infra. Fun fact : If you encode the video using <code class="highlighter-rouge">-g 1</code>, it will weight 3 times as much. Go figure…</p> <p>Results :</p> <center> <table> <thead> <tr> <th>-q &nbsp;</th><th>bitrate &nbsp;</th><th>decode time</th> </tr> </thead> <tbody> <tr><td>1-4</td><td>12672.8kbits/s </td><td>0.098 s </td></tr> <tr><td>1-8</td><td>6501.6kbits/s </td><td>0.090 s </td></tr> </tbody> </table> </center> <p>Really good. However there is not correlation between libvpx’s <code class="highlighter-rouge">-qmin</code>, <code class="highlighter-rouge">-qmax</code> and h.264’s <code class="highlighter-rouge">-q</code>.</p> <p>Interestingly, using <strong>vp9</strong> though it reduces file size for another 20%, halves decode speed. Further research shows that encoding consistently uses 4 to 5 times more CPU, and decoding 50% more than <strong>vp8</strong>. Which makes it ineligible for our use case.</p> <h2 id="conclusions">Conclusions</h2> <p>The codec war, while not targetted on our use case, provides us with some interesting tools. Video decode speed and file size can be improved significantly over the venerable MJPEG codec.</p>In previous post, I stated how I made a video player capable of reading a file from back to front. However I’m quite a beginner in video encoding and I felt there was improvements to be made.Writing a native evdev adapter2016-09-24T00:00:00+00:002016-09-24T00:00:00+00:00https://sdumetz.github.io/2016/09/24/write-a-native-evdev-adapter<p>I’ve recently found myself in need of a library to read evdev events. Usually, the X server will retrieve those events, parse them and send them to the concerned X client. In my case though, I needed xpad events from a gamepad. The only existing way to get them through X is the joystick driver, but it has been designed to control X server with a controller instead of a mouse.</p> <p>All I wanted was to have a way to fire an event saying :</p> <p><em>hey, someone pressed button A!</em></p> <p>Quite simple. Except I could not find much literature on how to do it, especially in a way I could implement with nodejs.</p> <p>There are two documented projects that act as evdev event reader helpers : Libevdev and python-evdev. I heard there’s also a ruby one but did not use it.</p> <p>I could have written a libevdev wrapper, wut it would have required a deep understanding in libav’s internals to work with async I/O.</p> <p>I decided to write my own simple basic interface. Here is what I learned in the process.</p> <h2 id="linux-input-architecture">Linux input architecture.</h2> <p>First thing first, let’s have a look at what we’re facing.</p> <p>Linux kernel is managing input devices with drivers like <a href="https://github.com/paroj/xpad">xpad</a>. Those devices generate evdev events that are exposed through character devices (which are fifo buffers) located in /dev/input.</p> <p>Those devices are numbered like : <code class="highlighter-rouge">/dev/input/event0</code>, <code class="highlighter-rouge">/dev/input/event1</code> and so on.</p> <p>They are also available in more human-readable ways in <code class="highlighter-rouge">/dev/input/by-path/</code>. A joystick will be exposed as : <code class="highlighter-rouge">/dev/input/by-path/&lt;usb_bus_id&gt;-event-joystick</code></p> <p>When reading this file, kernel’s <a href="https://github.com/torvalds/linux/blob/master/include/uapi/linux/input.h">input.h</a> tell us we’ll get this data structure:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; }; </code></pre></div></div> <p><em>timeval</em> being a structure of two <em>int64</em> on 64bits systems – for 32bits systems, it would be <em>int32</em> – representing seconds and microseconds.</p> <p>So an event is a 24 or 16 bytes datastructures. Note that Evdev may send multiple events at once.</p> <h2 id="writing-the-adapter">Writing the Adapter</h2> <p><strong>With nodejs Examples</strong></p> <p>Once you understand the event model, this part is in fact really simple. All you need to do is really to open a buffer and read from it.</p> <h3 id="fetching-messages">Fetching messages</h3> <p>That’s the easy part. Reading a buffer and parsing it’s data isn’t what’s going to keep us up all night.</p> <p>The sweet part here is we can do it all in native javascript.</p> <p>First you need to find your device, depending on what you’re looking for.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var device = "/dev/input/by-path/your_device_path"; var options = { flags: 'r', encoding: null, fd: null, autoClose: true }; fd = fs.createReadStream(device,options) .on('data', function(buf){ var i,j, chunk = self.arch === 64 ?24 :16; for (i=0,j=buf.length; i&lt;j; i+=chunk) { //parse the buffer } }) .on("error",function(e){ console.error(e); }); </code></pre></div></div> <p>Now for the parsing, the base principle is to just assign bytes to the structure’s fields.</p> <p>There is a small gotcha with <em>int64</em> parsing in javascript. However it has already been solved by others :</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var ev = { time : {} } if (this.arch === 64){ low = buf.readInt32LE(0); ev.time.tv_sec = buf.readInt32LE(4) * 4294967296.0 + low; if (low &lt; 0) time.tv_sec += 4294967296; low = buf.readInt32LE(8); ev.time.tv_usec = buf.readInt32LE(12) * 4294967296.0 + low; if (low &lt; 0) time.tv_usec += 4294967296; offset = 16; }else{ ev.time.tv_sec = buf.readInt32LE(0); ev.time.tv_usec = buf.readInt32LE(4); offset = 8; } ev.type = buf.readUInt16LE(offset); ev.code = buf.readUInt16LE(offset + 2); ev.value = buf.readUInt32LE(offset + 4); </code></pre></div></div> <p>Next steps depends on your goal, but if you want to parse event to assign readable strings like “BTN_A”, “ABS_X”, etc. or generate events from node’s EventEmitter interface, you can reuse my <a href="https://github.com/sdumetz/node-evdev">node-evdev</a> project.</p> <h3 id="querying-evdev">Querying evdev</h3> <p>Now if we look at the way libevdev is querying informations, here is what the source tell us (in <a href="https://github.com/whot/libevdev/blob/master/libevdev/libevdev.c#L379">libevdev.c</a>) :</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ioctl(fd, EVIOCGID, &amp;dev-&gt;ids); Constant EVIOCGID is defined in input.h as : #define EVIOCGID _IOR(‘E’, 0x02, struct input_id) </code></pre></div></div> <p>If we follow the lead, it takes us to :</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#define _IOR(g,n,t) _IOC(IOC_OUT, (g), (n), sizeof(t)) #define _IOC(inout,group,num,len) \ (inout | ((len &amp; IOCPARM_MASK) &lt;&lt; 16) | ((group) &lt;&lt; 8) | (num)) #define IOC_OUT 0x40000000 /* copy out parameters */ </code></pre></div></div> <p>(cherry-picked from linux kernel’s sys/ioctl.h)</p> <p>Based on this, the only real problem is to find sizeof(t). The structure we used in libevdev is easy to find. However it’s quite complex and would require some time to be ported to javascript with Uint/Int16/longint… adapters. But for now, we only need the input_id structure, which is once again found in the kernel’s input.h :</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>input_id { __u16 bustype; __u16 vendor; __u16 product; __u16 version; }; </code></pre></div></div> <p>4 Uint16. Easy. However there is still one problem to solve : There is no way to make ioctl calls in native js. I could have used an adapter, but it would be overkill for just a single call. Let’s just make a c++ addon.</p> <p>** The following sample uses <a href="https://github.com/nodejs/nan">nan</a> for portability**</p> <p>The implementation mimicks libevdev’s method <code class="highlighter-rouge">evdev_new_from_fd()</code>. It’s convenient because we already got a file-descriptor in javascript : We can just pass it through this function to get the peripheral’s infos.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#include &lt;nan.h&gt; #include &lt;sys/ioctl.h&gt; #include &lt;linux/input.h&gt; #include &lt;fcntl.h&gt; #include &lt;errno.h&gt; #include &lt;stdio.h&gt; #include &lt;string.h&gt; using v8::FunctionTemplate; using v8::Handle; using v8::Object; using v8::String; using Nan::GetFunction; using Nan::New; using Nan::Set; using Nan::To; NAN_METHOD(evdev_new_from_fd) { int rc; struct input_id id; rc = ioctl(To&lt;int&gt;(info[0]).FromJust(), EVIOCGID, &amp;id); if(rc == -1 ){ Nan::ThrowError(strerror(errno)); //Nan::ThrowError(info[0]); } v8::Local&lt;v8::Object&gt; size = Nan::New&lt;v8::Object&gt;(); Nan::Set(size,Nan::New("bustype").ToLocalChecked(), Nan::New&lt;v8::Number&gt;((double)id.bustype)); Nan::Set(size,Nan::New("vendor").ToLocalChecked(), Nan::New&lt;v8::Number&gt;((double)id.vendor)); Nan::Set(size,Nan::New("product").ToLocalChecked(), Nan::New&lt;v8::Number&gt;((double)id.product)); Nan::Set(size,Nan::New("version").ToLocalChecked(), Nan::New&lt;v8::Number&gt;((double)id.version)); info.GetReturnValue().Set(size); } NAN_MODULE_INIT(Init) { Nan::Set(target, Nan::New("evdev_new_from_fd").ToLocalChecked(),GetFunction(Nan::New&lt;FunctionTemplate&gt;(evdev_new_from_fd)).ToLocalChecked()); } NODE_MODULE(ioctls, Init) </code></pre></div></div> <p>I think I will never get over v8’s horrible coding style. apart from it, it’s quite straightforward. We fill in an <code class="highlighter-rouge">input_id</code> with <code class="highlighter-rouge">ioctl()</code> then just convert it to v8 data structures.</p> <h3 id="conclusion">Conclusion</h3> <p>The great thing in having a pure-js event parser is that context-switching in nodejs addons <a href="https://www.akawebdesign.com/2015/11/17/node-js-addons-a-case-study-in-speed/">are slow</a>. It also keep the code simple and tidy.</p> <p>While we need to compile an addon to retrieve the peripheral’s metadata, it’s not always required and still quite simple in the end.</p> <p>I’ve been using this module for one year now with no problem in my <a href="http://electron.atom.io/">electron</a></p>I’ve recently found myself in need of a library to read evdev events. Usually, the X server will retrieve those events, parse them and send them to the concerned X client. In my case though, I needed xpad events from a gamepad. The only existing way to get them through X is the joystick driver, but it has been designed to control X server with a controller instead of a mouse.How to read a video Backward (Part 1)2016-09-21T00:00:00+00:002016-09-21T00:00:00+00:00https://sdumetz.github.io/2016/09/21/how-to-read-a-video-backward<p>I’ve recently faced an unexpected challenge in trying to read (decode &amp; display) a video backward at normal speed. For someone who don’t know shit about video encoding, it looks like it should be as straightforward as calling <code class="highlighter-rouge">video.previousFrame()</code>… except it’s not. Let’s see why and how we worked around this to create a new generation of holographic applications.</p> <p>We made a free software based on this : Check out <a href="https://github.com/Holusion/stingray">Stingray</a> for the code!</p> <h2 id="some-background">Some background.</h2> <h3 id="backward-reading">Backward reading</h3> <p>Files are not made to be read backward. It’s a fact in computer science, it has been since computer were big lamps that read punch cards.</p> <p>As far as my reading got, the best way to go back a few bytes in a file is to close it, reopen it, then offet the reader to our known desired position.</p> <p>Reading frame 9, then 8, then 7… will then require us to open the file at the begining of frame 9, then reopen it on 8, etc…</p> <h3 id="video-encoding">Video encoding</h3> <p>Most of today’s video codecs use a similar process for compression. Only the underlying compression algorithm differ. The process is quite similar to images compression. Additionally, recent codecs will use the fact that videos are composed of many images that will often have at least some common content (think : moving foreground on fixed background). h.264, MPEG4, etc… are all taking advantage of this to implement a concept of keyframes (or <a href="https://en.wikipedia.org/wiki/Video_compression_picture_types">I</a> frame). A keyframe is defined as a complete image stored within a data stream. Every other frames (P frames) only contains incremental changes based on the previous frame.</p> <p>This is the reason you sometimes get a few seconds of messed up background with just moving parts : Your device just skipped a keyframe.</p> <p>For the sake of completeness, some algorithms are using B frames that can rely on forward frames for data reference.</p> <div class="mdl-card mdl-shadow--2dp image-card"> <div class="mdl-card__title"> <img src="https://upload.wikimedia.org/wikipedia/commons/6/64/I_P_and_B_frames.svg" alt="I, P and B frames illustration" /> </div> <div class="mdl-card__supporting-text mdl-card--border"> A sequence of video frames. Source : <a href="https://en.wikipedia.org/wiki/File:I_P_and_B_frames.svg">Wikipedia</a> </div> </div> <p>For this reason, seeking precisely in a video is very slow. Backward seek especially as most frames in a video are effectively P frames.</p> <h3 id="draft-a-solution">Draft a solution</h3> <p>So I can’t just make up a <code class="highlighter-rouge">decode_previous_frame()</code> function to decode frames in the inverse order. As far as I know, there is only one thing to do : Position the read pointer, batch decode some frames and reorder them in a buffer for a reading routine.</p> <p>To go from frame 20 to frame 0 :</p> <ul> <li>Decode frames [10..20], revert the array and push to buffer</li> <li>Decode frames [0..10], same thing</li> </ul> <p>And so on. The first batch can be on display while the second one is decoded. To do so, we will need to find balance for our batch size : larger sizes will please the decoder as it doesn’t have to <code class="highlighter-rouge">seek_back()</code> too much (which we know forces it to reopen the file), but a smaller size will reduce initial delay and memory footprint as we won’t need to keep as much images.</p> <p>How to choose the decode batch size? <strong>As small as possible as long as decode speed is enough</strong>.</p> <p>Let’s speed it up then.</p> <h2 id="speed-up">Speed up</h2> <p>I’m going with <a href="https://libav.org/avconv.html">libav</a> here, some differences can be observed with other decoding softwares, but mainly they all work the same.</p> <p>The video encoding problem at its root is unsolvable: Any frame, to be decoded need any amount of previous frames already decoded, each frame only referencing that it needs the previous one. It means if I want to seek to frame 10 and start decoding, the decoder might need to decode every previous frames up to frame 1. Unacceptable.</p> <h3 id="enters-mjpeg">Enters MJPEG</h3> <p>MJPEG or Motion-JPEG is simply a concatenation of JPEG-Encoded images. It offer the compression ratio of JPEG, around 1:5 which is not bad but clearly less than modern high-compression codecs like MPEG (can go up to 1:100).</p> <p>MJPEG has been quite forgotten in recent years with the diffusion of high performance codecs, due to hardware accelerated decoding and web diffusion, respectively nullifying the need for an easy-to-decode format and requiring high bandwidth savings. however it’s still supported mainly for professionnal applications that require a fast random access.</p> <p>Turns out I didn’t even need to re-code anything to make seek fast on MJPEG videos with libav:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>avformat_seek_file([...]); </code></pre></div></div> <p>is fast enough to be negligible for chunks as small as 10 frames.</p> <p>As for the video size, a HD video of 10 seconds weigh in at 30MB. It’s large but not unmanageable.</p> <p>For reference, here is the CLI options to encode a MJPEG video using <strong>avconv</strong> :</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>avconv -r 25 -y -i "&lt;INPUT_VIDEO&gt;" -g 1 -keyint_min 0 -c:v mjpeg -an -q:v 2 -pix_fmt yuvj420p "&lt;OUTPUT_VIDEO&gt;.mov" </code></pre></div></div> <p>The <code class="highlighter-rouge">-keyint_min</code> and <code class="highlighter-rouge">-g</code> options seems mandatory to prevent avconv from further optimizing the video.</p> <h3 id="reduce-delays">Reduce delays</h3> <p>So we can now decode the video stream backward. However there is still a delay when we want to switch from <code class="highlighter-rouge">forward</code> to <code class="highlighter-rouge">backward</code> mode. That’s easily solved using a double buffer :</p> <ul> <li>Decoded frames are put in a front_buffer, waiting to be played</li> <li>Displayed frames are put in a back_buffer</li> <li>Once the back_buffer overflows, it’s frames are automatically discarded.</li> </ul> <p>When the user hits the <code class="highlighter-rouge">switch_direction</code> button, we just have to switch the buffers from front to back.</p> <p>You can now have high frequency direction switch without any delay or performance penalty.</p> <h2 id="conclusion">Conclusion</h2> <p>We got ourselves a decent interactive video player at no cost. I did not describe the code precisely as :</p> <ol> <li>Most of it is boilerplate (keyboard controls, SDL configuration, etc.)</li> <li>It’s available under <a href="https://www.gnu.org/licenses/licenses.fr.html">GNU-GPL</a> as a free software initiative by <a href="http://holusion.com">holusion</a> names <a href="https://github.com/Holusion/stingray">Stingray</a>.</li> </ol> <p>In next part I will talk about our projects to further improve it using next generation video codecs like MJPEG-2000 and H.264/AVC for <a href="http://iphome.hhi.de/marpe/download/perf_spie03.pdf">better compression</a>.</p>I’ve recently faced an unexpected challenge in trying to read (decode &amp; display) a video backward at normal speed. For someone who don’t know shit about video encoding, it looks like it should be as straightforward as calling video.previousFrame()… except it’s not. Let’s see why and how we worked around this to create a new generation of holographic applications.