title: How to send/"stream" video file to HTML5 video tag
body: <p>I've tried to send a video file into HTML5 <code>&lt;video&gt;</code>-tag.<br>
  I've found a snippet which dates back to 2010 if not earlier.<br>
  It's replicated all over the Internet and still circulates it, with some minor differences in code style, names, used Node.js API versions or libraries.<br>
  I have some issues with it.<br>
  This is the snippet:</p>
  
  <pre class="lang-js prettyprint-override"><code>app.get('/video', function(req, res) {
      const path     = 'some_path/video.mp4'
      const stat     = fs.statSync(path)
      const fileSize = stat.size
      const range    = req.headers.range
      if( range ) {
          const parts     = range.replace(/bytes=/, "").split("-")
          const start     = parseInt(parts[0],10);
          const end       = parts[1] ? parseInt(parts[1],10) : fileSize-1
          const chunksize = (end-start)+1
          const file      = fs.createReadStream(path, {start, end})
          const head = {
              'Content-Range' : `bytes ${start}-${end}/${fileSize}`,
              'Accept-Ranges' : 'bytes',
              'Content-Length': chunksize,
              'Content-Type'  : 'video/mp4',
          }
          res.writeHead(206, head)
          file.pipe(res)
      } else {
          const head = {
              'Content-Length': fileSize,
              'Content-Type'  : 'video/mp4',
          }
          res.writeHead(200, head)
          fs.createReadStream(path).pipe(res)
      }
  })
  </code></pre>
  
  <p>Here are just few sources of it:</p>
  
  <ol>
  <li><a href="https://stackoverflow.com/questions/4360060/video-streaming-with-html-5-via-node-js">Video streaming with HTML 5 via node.js</a></li>
  <li><a href="https://stackoverflow.com/questions/59751710/video-streaming-in-nodejs-through-fs-module/59785318">video streaming in nodejs through fs module</a></li>
  <li><a href="https://github.com/davidgatti/How-to-Stream-Movies-using-NodeJS/blob/master/routes/video.js" rel="nofollow noreferrer">https://github.com/davidgatti/How-to-Stream-Movies-using-NodeJS/blob/master/routes/video.js</a></li>
  <li><a href="https://github.com/tsukhu/rwd-spa-alljs-app/blob/master/routes/video_streamer.js" rel="nofollow noreferrer">https://github.com/tsukhu/rwd-spa-alljs-app/blob/master/routes/video_streamer.js</a></li>
  <li><a href="https://tewarid.github.io/2011/04/25/stream-webm-file-to-chrome-using-node.js.html" rel="nofollow noreferrer">https://tewarid.github.io/2011/04/25/stream-webm-file-to-chrome-using-node.js.html</a></li>
  </ol>
  
  <p>It is obvious that this snippet isn't production ready:</p>
  
  <ol>
  <li>it uses synchronous <code>statSync()</code> instead of asynchronous version,</li>
  <li>it does not parse the full grammar of <code>Range</code> header, and has no error handling,</li>
  <li>it is hard coded,</li>
  <li><code>else</code> brunch is possibly redundant,</li>
  <li>etc.</li>
  </ol>
  
  <p>I have no problem with that. It's an "entry level" code. It's OK.<br>
  But the most important thing is that <strong>it does not work as it's intended</strong>... but <strong>still works</strong>.</p>
  
  <p><strong>As far as I know</strong>, browsers send requests for sources of a <code>&lt;video&gt;</code>-tag with <code>Range</code> header in the form of</p>
  
  <pre><code>Range: bytes=12345-
  </code></pre>
  
  <p>and, in case of initial request it will be</p>
  
  <pre><code>Range: bytes=0-
  </code></pre>
  
  <p>so, the line</p>
  
  <pre class="lang-js prettyprint-override"><code>const end = parts[1] ? parseInt(parts[1],10) : fileSize-1
  </code></pre>
  
  <p>is identical to</p>
  
  <pre class="lang-js prettyprint-override"><code>const end = fileSize-1
  </code></pre>
  
  <p>And, on the initial request, the server will send not a small chunk of a video, but the total file. And in case of video rewind - a really big chunk from the requested position until the end.</p>
  
  <p>This definitely won't work as intended if you request a file through Javascript. You will wait for the file to load completely, or you'll need to somehow deal with tracing the request progress, and that will lead to considerably more complex client code.<br>
  But it works like a charm for <code>&lt;video&gt;</code>-tag because browsers deal with this on your behalf.</p>
  
  <p>We can fix this by calculating <code>end</code> this way:</p>
  
  <pre class="lang-js prettyprint-override"><code>const preferred_chunksize = 10000000 // arbitrary selected value
  let end = parts[1] ? parseInt(parts[1],10) : start + preferred_chunksize
  if( end &gt; fileSize-1 ) end = fileSize-1
  </code></pre>
  
  <p>or, considering the form of <code>Range</code> header used for <code>&lt;video&gt;</code>-tag, even this way:</p>
  
  <pre class="lang-js prettyprint-override"><code>const preferred_chunksize = 10000000 // arbitrary selected value
  let end = start + preferred_chunksize
  if( end &gt; fileSize-1 ) end = fileSize-1
  </code></pre>
  
  <p>OK, now it really sends partial responses of expected sizes.
  But</p>
  
  <ol>
  <li>these lines are more complex than
  
  <pre class="lang-js prettyprint-override"><code>const end = fileSize-1
  </code></pre></li>
  <li>we need to choose <code>preferred_chunksize</code> wisely. E.g small chunk sizes like <code>preferred_chunksize=1000</code> would issue a lot of requests and would work noticeably slower.<br>
  While, at least with Chrome and Firefox, the original version of code streams video files quite alright: I see no excessive cache or memory usage, or speed issues. And I have no need to worry about <code>preferred_chunksize</code> value.</li>
  </ol>
  
  <p>So my question is: should I even bother to send chunks of correct sizes (if I just need to send a video into <code>&lt;video&gt;</code>-tag), is there any popular browser/client js library that will fail to play the video file served with the original snippet? Or is there any over hidden issues with this snippet's approach?</p>