<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Derek Herman on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Derek Herman</name>
  </author>
  <link href="https://web.dev/authors/derekherman/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/8L1ESx211yWBm8uNAgqvUc2GwNk1/Co3mEQ34Z8eAw6lPGx5o.jpg?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>CTO at XWP</subtitle>
  
  
  <entry>
    <title>Media encryption</title>
    <link href="https://web.dev/media-encryption/"/>
    <updated>2021-07-05T00:00:00Z</updated>
    <id>https://web.dev/media-encryption/</id>
    <content type="html" mode="escaped">&lt;p&gt;In this section we are going to cover two different strategies for encrypting
your media, and some practical examples on how to use them with FFmpeg and
Shaka Packager. The two strategies for encryption we&#39;ll discuss are &lt;a href=&quot;https://w3c.github.io/encrypted-media/index.html#clear-key&quot; rel=&quot;noopener&quot;&gt;Clear Key&lt;/a&gt; and
using a service like &lt;a href=&quot;https://www.widevine.com/&quot; rel=&quot;noopener&quot;&gt;Google Widevine&lt;/a&gt;. Both strategies are a form of digital
rights management (&lt;a href=&quot;https://en.wikipedia.org/wiki/Digital_rights_management&quot; rel=&quot;noopener&quot;&gt;DRM&lt;/a&gt;) to control what users can do with your media. However,
one is inherently less secure than the other due to the way keys are passed for
authentication and is why a DRM service might make more sense.&lt;/p&gt;
&lt;p&gt;The primary DRM services for the web are &lt;a href=&quot;https://www.widevine.com/&quot; rel=&quot;noopener&quot;&gt;Google Widevine&lt;/a&gt;, &lt;a href=&quot;https://www.microsoft.com/playready/&quot; rel=&quot;noopener&quot;&gt;Microsoft PlayReady&lt;/a&gt;
and &lt;a href=&quot;https://developer.apple.com/streaming/fps/&quot; rel=&quot;noopener&quot;&gt;Apple FairPlay&lt;/a&gt;, but we will not be covering all of them in this article.
However, if you are targeting all the modern browsers you are likely going
to be using all three DRM services.&lt;/p&gt;
&lt;p&gt;Conversion and encryption is done with these applications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/google/shaka-packager&quot; rel=&quot;noopener&quot;&gt;Shaka Packager&lt;/a&gt; (&lt;a href=&quot;https://stackoverflow.com/questions/tagged/shaka&quot; rel=&quot;noopener&quot;&gt;#shaka on Stack Overflow&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/download.html&quot; rel=&quot;noopener&quot;&gt;FFmpeg&lt;/a&gt; (&lt;a href=&quot;https://stackoverflow.com/questions/tagged/ffmpeg&quot; rel=&quot;noopener&quot;&gt;#ffmpeg on Stack Overflow&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.openssl.org/&quot; rel=&quot;noopener&quot;&gt;OpenSSL&lt;/a&gt; (&lt;a href=&quot;https://stackoverflow.com/questions/tagged/openssl&quot; rel=&quot;noopener&quot;&gt;#openssl on Stack Overflow&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;clear-key-encryption&quot;&gt;Clear Key encryption &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-encryption/#clear-key-encryption&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First, you should have a good understanding of what Clear Key is and isn&#39;t before
using it. When you do &lt;em&gt;&lt;strong&gt;not&lt;/strong&gt;&lt;/em&gt; want to use an existing DRM service and feel
basic encryption of you media is a viable option, you would use Clear Key. But,
keep in mind that this type of encryption does not provide the same level of
security as using one of the DRM services. This is because the key value pair is
not encrypted under another key, unlike encrypted keys which are generated by a
decryption key that is stored on a licence server. Additionally, Clear Key sends
the key value pair as plain text, so while you are encrypting your media the key to
decrypt it is not a secret.&lt;/p&gt;
&lt;h3 id=&quot;create-a-key&quot;&gt;Create a key &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-encryption/#create-a-key&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can use the same method to create a key for both DASH and HLS. Do this using
&lt;a href=&quot;https://www.openssl.org/&quot; rel=&quot;noopener&quot;&gt;OpenSSL&lt;/a&gt;. The following will create an encryption key made of 16 hex values.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;openssl rand -hex &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; media.key&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; This command creates a file that could contain white space and new line characters, which are not allowed by Shaka Packager. You&#39;ll need to open the key file and manually remove all whitespace including the final carriage return. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;create-an-iv&quot;&gt;Create an IV &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-encryption/#create-an-iv&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Next we can generate an initialization vector (IV).&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;openssl rand -hex &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;br /&gt;6143b5373a51cb46209cfed0d747da66&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Make a note of the output, you&#39;ll need this later. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;encrypt-with-clear-key&quot;&gt;Encrypt with Clear Key &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-encryption/#encrypt-with-clear-key&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The following example uses &lt;a href=&quot;https://google.github.io/shaka-packager/html/tutorials/raw_key.html&quot; rel=&quot;noopener&quot;&gt;Shaka Packager with raw keys&lt;/a&gt;, where &lt;code&gt;keys&lt;/code&gt; and
&lt;code&gt;key_ids&lt;/code&gt; are provided to Shaka Packager directly. Read the documentation for
more examples.&lt;/p&gt;
&lt;p&gt;For the &lt;code&gt;key&lt;/code&gt; flag, use the key created earlier, which is stored in the &lt;code&gt;media.key&lt;/code&gt;
file. However, when entering it on the command line, be sure you&#39;ve removed its
whitespace. For the &lt;code&gt;key_id&lt;/code&gt; flag, repeat the &lt;code&gt;media.id&lt;/code&gt; value or use the IV value
generated above.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;packager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;glocken.mp4,stream&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;audio,output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;glockena.m4a &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;glocken.mp4,stream&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;video,output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;glockenv.mp4 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --enable_fixed_key_encryption &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --keys &lt;span class=&quot;token assign-left variable&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;audio:key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;INSERT_AUDIO_KEY_HERE:key_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;INSERT_AUDIO_KEY_ID_HERE,label&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;video:key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;INSERT_VIDEO_KEY_HERE:key_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;INSERT_VIDEO_KEY_ID_HERE&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Note that we can also encrypt both &lt;code&gt;video&lt;/code&gt; and &lt;code&gt;audio&lt;/code&gt; streams using the same &lt;code&gt;keys&lt;/code&gt; by omitting the label argument instead of defining a different set of &lt;code&gt;keys&lt;/code&gt; per-stream. For example, &lt;code&gt;--keys label=:key=INSERT_KEY_HERE:key_id=INSERT_KEY_ID_HERE&lt;/code&gt;. However, this would decrease the level of security when protecting your streams. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;create-a-key-information-file&quot;&gt;Create a key information file &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-encryption/#create-a-key-information-file&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To encrypt for HLS you need a key information file in addition to a key file. A
key information file is a text file with the format below. It should have the
extension &lt;code&gt;.keyinfo&lt;/code&gt;. For example: &lt;code&gt;encrypt.keyinfo&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;key URI&lt;br /&gt;key &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt; path&lt;br /&gt;private key&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The key URI is where the &lt;code&gt;media.key&lt;/code&gt; (&lt;a href=&quot;https://web.dev/media-encryption/#create-a-key&quot;&gt;created above&lt;/a&gt; will be
located on your server. The key file path is its location relative to the key
information file. Finally, the private key is the contents of the &lt;code&gt;media.key&lt;/code&gt;
file itself, or the IV you created before. For example:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;https://example.com/keys/media.key&lt;br /&gt;/path/to/media.key&lt;br /&gt;6143b5373a51cb46209cfed0d747da66&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;encrypt-for-hls&quot;&gt;Encrypt for HLS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-encryption/#encrypt-for-hls&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;packager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;input=input.mp4,stream=video,segment_template=output$Number$.ts,playlist_name=video_playlist.m3u8&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;input=input.mp4,stream=audio,segment_template=output_audio$Number$.ts,playlist_name=audio_playlist.m3u8,hls_group_id=audio,hls_name=ENGLISH&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --hls_master_playlist_output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;master_playlist.m3u8&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --hls_base_url&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:5000/&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This command will accept a key with either 16 or 32 characters.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i myvideo.mov -c:v libx264 -c:a aac -hls_key_info_file encrypt.keyinfo myvideo.m3u8&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;widevine-encryption&quot;&gt;Widevine encryption &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-encryption/#widevine-encryption&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now you know what Clear Key encryption is and when to use it. But, when should you use a DRM service for additional security? This is where Widevine, or another service,
would be used to securely encrypt and decrypt your media. Widevine supports MPEG-DASH and
HLS and is a DRM from Google. Widevine is used by the Google Chrome and Firefox web
browsers, Android MediaDRM, Android TV, and other consumer electronics devices that use
Encrypted Media Extensions and Media Source Extensions, where Widevine decrypts content.&lt;/p&gt;
&lt;h3 id=&quot;encrypt-with-widevine&quot;&gt;Encrypt with Widevine &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-encryption/#encrypt-with-widevine&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Most of the examples in this article used Clear Key encryption. However, for Widevine you
will want to replace the following options.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;--enable_fixed_key_encryption &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;--enable_fixed_key_decryption &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;--keys &lt;span class=&quot;token assign-left variable&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;:key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;INSERT_KEY_HERE:key_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;INSERT_KEY_ID_HERE&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Everything in the demultiplexer (demux) command except the name of your files and the
&lt;code&gt;--content-id&lt;/code&gt; flag should be copied exactly from the example. The &lt;code&gt;--content-id&lt;/code&gt; is 16
or 32 random hex digits. Use the keys provided here instead of your own. Read the Shaka
Packager documentation on using the &lt;a href=&quot;https://google.github.io/shaka-packager/html/tutorials/widevine.html&quot; rel=&quot;noopener&quot;&gt;Widevine Key Server&lt;/a&gt; for more examples.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Demux (separate) the audio and video, encrypt the new files, and output a
media presentation description (MPD) file.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;packager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;tmp_glocken.mp4,stream&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;video,output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;glocken_video.mp4 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;tmp_glocken.mp4,stream&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;audio,output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;glocken_audio.m4a &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --enable_widevine_encryption &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --key_server_url &lt;span class=&quot;token string&quot;&gt;&quot;https://license.uat.widevine.com/cenc/getcontentkey/widevine_test&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --content_id &lt;span class=&quot;token string&quot;&gt;&quot;fd385d9f9a14bb09&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --signer &lt;span class=&quot;token string&quot;&gt;&quot;widevine_test&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --aes_signing_key &lt;span class=&quot;token string&quot;&gt;&quot;1ae8ccd0e7985cc0b6203a55855a1034afc252980e970ca90e5202689f947ab9&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --aes_signing_iv &lt;span class=&quot;token string&quot;&gt;&quot;d58ce954203b7c9a9a9d467f59839249&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Remux (combine) the audio and video streams. If you&#39;re using a video
framework, you may not need to do this.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i glocken_video.mp4 -i glocken_audio.m4a -c copy glocke.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The default pixel format &lt;code&gt;yuv420p&lt;/code&gt; is used in all the &lt;code&gt;ffmpeg&lt;/code&gt; remux examples in this article because one isn&#39;t supplied in the command line. The &lt;code&gt;ffmpeg&lt;/code&gt; command will give you an error message that it is deprecated. We&#39;ve chosen not to override the default because, though deprecated &lt;code&gt;yuv420p&lt;/code&gt; is still the most widely supported. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;media-conversion-sequence&quot;&gt;Media conversion sequence &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-encryption/#media-conversion-sequence&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This section shows in order commands needed to get from a raw &lt;code&gt;.mov&lt;/code&gt; file to
encrypted assets packaged for DASH or HLS. For the sake of having a goal to
illustrate, we&#39;re converting a source file to a bitrate of 8Mbs at a resolution
of 1080p (1920 x 1080). Adjust these values as your needs dictate.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Not all steps are possible with Shaka Packager alone, so we&#39;ll also use FFmpeg when needed. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;dashwebm&quot;&gt;DASH/WebM &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-encryption/#dashwebm&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Convert the file type and codec.&lt;/p&gt;
&lt;p&gt;For this command you can use either &lt;code&gt;liborbis&lt;/code&gt; or &lt;code&gt;libopus&lt;/code&gt; for the audio codec.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i glocken.mov -c:v libvpx-vp9 -c:a libvorbis -b:v 8M -vf &lt;span class=&quot;token assign-left variable&quot;&gt;setsar&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:1 -f webm tmp_glocken.webm&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a Clear Key encryption key.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;openssl rand -hex &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; media.key&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Demux (separate) the audio and video, encrypt the new files, and output a
media presentation description (MPD) file.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;packager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;tmp_glocken.webm,stream&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;video,output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;glocken_video.webm &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;tmp_glocken.webm,stream&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;audio,output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;glocken_audio.webm &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --enable_fixed_key_encryption &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --enable_fixed_key_decryption &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --keys &lt;span class=&quot;token assign-left variable&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;:key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;INSERT_KEY_HERE:key_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;INSERT_KEY_ID_HERE &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --mpd_output glocken_webm_vod.mpd&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Remux (combine) the audio and video streams. If you&#39;re using a video
framework, you may not need to do this.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i glocken_video.webm -i glocken_audio.webm -c copy glocken.webm&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;dashmp4&quot;&gt;DASH/MP4 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-encryption/#dashmp4&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Convert the file type, video codec and bitrate.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i glocken.mov -c:v libx264 -c:a aac -b:v 8M -strict -2 tmp_glocken.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a Clear Key encryption key.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;openssl rand -hex &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; media.key&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Demux (separate) the audio and video, encrypt the new files, and output a
media presentation description (MPD) file.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;packager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;tmp_glocken.mp4,stream&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;video,output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;glocken_video.mp4 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;tmp_glocken.mp4,stream&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;audio,output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;glocken_audio.m4a &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --enable_fixed_key_encryption &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --enable_fixed_key_decryption &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --keys &lt;span class=&quot;token assign-left variable&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;:key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;INSERT_KEY_HERE:key_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;INSERT_KEY_ID_HERE &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --mpd_output glocken_mp4_vod.mpd&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Remux (combine) the audio and video streams. If you&#39;re using a video
framework, you may not need to do this.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i glocken_video.mp4 -i glocken_audio.m4a -c copy glocken.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;hlsmp4&quot;&gt;HLS/MP4 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-encryption/#hlsmp4&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;HLS only supports MP4, so first you&#39;ll need to convert to the MP4 container and
supported codecs.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Convert the file type, video codec, and bitrate.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i glocken.mov -c:v libx264 -c:a aac -b:v 8M -strict -2 glocken.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a Clear Key encryption key.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;openssl rand -hex &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; media.key&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a key information file&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;packager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;input=glocken.mp4,stream=video,segment_template=output$Number$.ts,playlist_name=video_playlist.m3u8&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;input=glocken.mp4,stream=audio,segment_template=output_audio$Number$.ts,playlist_name=audio_playlist.m3u8,hls_group_id=audio,hls_name=ENGLISH&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --hls_master_playlist_output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;master_playlist.m3u8&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --hls_base_url&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:5000/&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --enable_fixed_key_encryption &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --enable_fixed_key_decryption &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --keys &lt;span class=&quot;token assign-left variable&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;:key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;INSERT_KEY_HERE:key_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;INSERT_KEY_ID_HERE&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That was a lot to digest, but hopefully you are now able to encrypt you media
with confidence. Next we&#39;ll show you how to &lt;a href=&quot;https://web.dev/add-media/&quot;&gt;add media&lt;/a&gt; to
your site.&lt;/p&gt;
</content>
    <author>
      <name>Derek Herman</name>
    </author><author>
      <name>Joe Medley</name>
    </author>
  </entry>
  
  <entry>
    <title>Media frameworks</title>
    <link href="https://web.dev/media-frameworks/"/>
    <updated>2021-07-05T00:00:00Z</updated>
    <id>https://web.dev/media-frameworks/</id>
    <content type="html" mode="escaped">&lt;p&gt;There are several ways to add media to a web page. Previously you learned how to
use the standard &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag. In this article you will learn about a few of the
media frameworks (or libraries) available that you can use to extend or replace
the behavior of the default HTML5 video player.&lt;/p&gt;
&lt;p&gt;Media frameworks come in both the proprietary and open-source variety, and at
their core are a set of APIs that support audio and/or video playback for
various container formats and transmission protocols. A good framework has
a modular architecture and provides clear and detailed documentation. Ideally it
should also be relatively easy to set up and use. You might be asking yourself,
&amp;quot;If the HTML5 video player provides most features already then why would I use a
framework or library?&amp;quot; That&#39;s a great question, let&#39;s dig in.&lt;/p&gt;
&lt;h2 id=&quot;benefits-of-using-a-framework&quot;&gt;Benefits of using a framework &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-frameworks/#benefits-of-using-a-framework&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In most situations that are beyond the needs of a basic web page, you are going
to want to use a media framework to serve your content. Unless you are prepared
to develop and maintain a rich feature set like offline playback, streaming,
analytics, picture-in-picture, preview thumbnails, embedding, and monetization
such as fill rate optimization, Ad scheduling, or header bidding to name a few
then you should probably offload that complexity to an existing solution.&lt;/p&gt;
&lt;p&gt;This is where media frameworks come in. They provide you with a set of features,
and conditions in which you can use those features, and then you have to decide
which framework is right for your project. In the next article we will discuss
how we built a &lt;a href=&quot;https://web.dev/pwa-with-offline-streaming/&quot;&gt;PWA with offline streaming&lt;/a&gt; that implements several complex
features. Spoiler alert, it was a lot of work and is still far from being a
production ready solution. Save yourself the headache and use a framework.&lt;/p&gt;
&lt;p&gt;There are a lot of options out there to choose from, for now this article will
focus on three, which are &lt;a href=&quot;https://github.com/google/shaka-player&quot; rel=&quot;noopener&quot;&gt;Shaka Player&lt;/a&gt;, &lt;a href=&quot;https://developer.jwplayer.com/&quot; rel=&quot;noopener&quot;&gt;JW Player&lt;/a&gt;, and &lt;a href=&quot;http://videojs.com/&quot; rel=&quot;noopener&quot;&gt;Video.js&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;shaka-player&quot;&gt;Shaka Player &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-frameworks/#shaka-player&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;According to the documentation, Google&#39;s &lt;a href=&quot;https://github.com/google/shaka-player&quot; rel=&quot;noopener&quot;&gt;Shaka Player&lt;/a&gt; is an open-source
JavaScript library for adaptive media. It plays adaptive media formats (such as
&lt;a href=&quot;http://dashif.org/&quot; rel=&quot;noopener&quot;&gt;DASH&lt;/a&gt; and &lt;a href=&quot;https://developer.apple.com/streaming/&quot; rel=&quot;noopener&quot;&gt;HLS&lt;/a&gt;) in a browser, without using plugins. Instead, Shaka Player
uses the open web standards &lt;a href=&quot;https://www.w3.org/TR/media-source/&quot; rel=&quot;noopener&quot;&gt;MediaSource Extensions&lt;/a&gt; and
&lt;a href=&quot;https://www.w3.org/TR/encrypted-media/&quot; rel=&quot;noopener&quot;&gt;Encrypted Media Extensions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Shaka Player also supports &lt;a href=&quot;https://shaka-player-demo.appspot.com/docs/api/tutorial-offline.html&quot; rel=&quot;noopener&quot;&gt;offline storage and playback&lt;/a&gt; of media using
&lt;a href=&quot;https://www.w3.org/TR/IndexedDB-2/&quot; rel=&quot;noopener&quot;&gt;IndexedDB&lt;/a&gt;. Content can be stored on any browser. Storage of licenses
depends on browser support.&lt;/p&gt;
&lt;p&gt;There are directions for &lt;a href=&quot;https://shaka-player-demo.appspot.com/docs/api/tutorial-basic-usage.html&quot; rel=&quot;noopener&quot;&gt;basic usage&lt;/a&gt; on the Shaka Player documentation site.
However, in a nutshell to use Shaka Player you first need to create an HTML page
with a video or audio element. Then in your application&#39;s JavaScript you
install the polyfills and check for browser support. Once the browser has
confirmed support for Shaka Player a script will create a Player object to wrap
the media element, listen for errors, then load a manifest file. Shaka Player
will take over from there.&lt;/p&gt;
&lt;p&gt;With Shaka you will need to host and encode your media files yourself.
Creating a media server is not overly complex, however encoding/transcoding media
can be time-consuming and complicated. You will likely want to offload
this part to a service such as &lt;a href=&quot;https://en.wikipedia.org/wiki/Zencoder&quot; rel=&quot;noopener&quot;&gt;Zencoder&lt;/a&gt;, &lt;a href=&quot;https://aws.amazon.com/elastictranscoder&quot; rel=&quot;noopener&quot;&gt;Amazon Elastic Encoder&lt;/a&gt;, or
&lt;a href=&quot;https://cloud.google.com/transcoder/docs&quot; rel=&quot;noopener&quot;&gt;Google Transcoder API&lt;/a&gt; to automate repetitive tasks and speed the process up.&lt;/p&gt;
&lt;p&gt;The great thing about the Shaka Player is there is also a really fantastic tool
and media packaging SDK for DASH and HLS packaging and encryption called Shaka
Packager. It can prepare and package media content for online streaming, which
you learned about earlier in &lt;a href=&quot;https://web.dev/media-conversion/&quot;&gt;Media conversion&lt;/a&gt; and
&lt;a href=&quot;https://web.dev/media-encryption/&quot;&gt;Media encryption&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;jw-player&quot;&gt;JW Player &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-frameworks/#jw-player&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you are looking for an option that provides hosting and encoding/transcoding
services then you might look into &lt;a href=&quot;https://developer.jwplayer.com/&quot; rel=&quot;noopener&quot;&gt;JW Player&lt;/a&gt;, but keep in mind that JW Player is
proprietary software. Meaning, you don&#39;t have much control over the platform or
roadmap. There is a basic free version where videos are displayed with a watermark,
and a commercial version.&lt;/p&gt;
&lt;p&gt;JW Player supports streaming with MPEG-DASH (paid version only), Digital rights
management (DRM) (with Vualto), interactive advertisement, customization of the
interface, and embeds. There is a well documented API and SDK. However, if you
are just looking for a quick and free way to host your media then embedding
YouTube videos are typically going to be your best free option.&lt;/p&gt;
&lt;h2 id=&quot;videojs&quot;&gt;Video.js &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-frameworks/#videojs&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;According to their website, &lt;a href=&quot;http://videojs.com/&quot; rel=&quot;noopener&quot;&gt;Video.js&lt;/a&gt; is built from the
ground up for an HTML5 world. It supports HTML5 video and modern streaming
formats such as DASH and HLS, as well as YouTube, and Vimeo. It supports
video playback on desktop and mobile devices and looks good everywhere with
CSS-based skins.&lt;/p&gt;
&lt;p&gt;There are a few ways to use Video.js, but the easiest is to use the free CDN
version provided by &lt;a href=&quot;https://videojs.com/getting-started/#videojs-cdn&quot; rel=&quot;noopener&quot;&gt;Fastly&lt;/a&gt;. You can learn more on how to get the player set
up on the &lt;a href=&quot;https://videojs.com/getting-started&quot; rel=&quot;noopener&quot;&gt;getting started&lt;/a&gt; page. Video.js is a very powerful video player,
and much like Shaka Player you will also need to host and encode your video.
However, one difference is in the plugin system where you can do things like
play YouTube videos in the Video.js player, which can also be customized.&lt;/p&gt;
&lt;h2 id=&quot;other-frameworks&quot;&gt;Other frameworks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-frameworks/#other-frameworks&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are lots of different frameworks and libraries available, this article
only scratches the surface on what is out there. When choosing a framework you
should consider what features you need for the project and how you plan to host
and encode or transcode your media. Do you need pre-roll Ads or other
monetization strategies? Will your media be available offline? How big or
small is your budget? Or any other number of considerations. Do your research
and ask people in your network for suggestions. There are dozens of other great
options and before you make any choices take some time to pick one that will
be right for your team and avoid creating unnecessary technical debt or
complexity to maintain during the project lifecycle.&lt;/p&gt;
&lt;p&gt;Next, you will learn about the &lt;a href=&quot;https://web.dev/pwa-with-offline-streaming/&quot;&gt;PWA with offline streaming&lt;/a&gt; we built to
demonstrate how to approach and tackle the main challenges that come with
rolling your own solution using just the HTML5 video object without a framework
to handle the heavy lifting.&lt;/p&gt;
</content>
    <author>
      <name>Derek Herman</name>
    </author>
  </entry>
  
  <entry>
    <title>Media streaming basics</title>
    <link href="https://web.dev/media-streaming-basics/"/>
    <updated>2021-07-05T00:00:00Z</updated>
    <id>https://web.dev/media-streaming-basics/</id>
    <content type="html" mode="escaped">&lt;p&gt;In this article, you are going to learn about the more advanced concept of media
streaming and by the end should have a good understanding of the various
streaming use cases, protocols, and extensions. Let&#39;s start with an
explanation of what streaming actually is.&lt;/p&gt;
&lt;p&gt;Media streaming is a way of delivering and playing back media content piece by
piece. Instead of loading a single file, which can be slow if not optimized for
the network, the player reads a manifest file describing how the target media is
split into individual chunks of data. Media chunks are later dynamically stitched
back together at runtime—probably at different &lt;a href=&quot;https://web.dev/bitrate/&quot;&gt;bitrates&lt;/a&gt;, which you&#39;ll learn
about later.&lt;/p&gt;
&lt;p&gt;Keep in mind that to provide streaming on your website the server
must support the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Range&quot; rel=&quot;noopener&quot;&gt;Range&lt;/a&gt; HTTP request header. Learn more about the &lt;code&gt;Accept-Ranges&lt;/code&gt;
header in &lt;a href=&quot;https://web.dev/video-and-source-tags/#specify-start-and-end-times&quot;&gt;The &amp;lt;video&amp;gt; and &amp;lt;source&amp;gt; tags&lt;/a&gt; article.&lt;/p&gt;
&lt;h2 id=&quot;streaming-use-cases&quot;&gt;Streaming use cases &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-streaming-basics/#streaming-use-cases&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Producing media chunks and the necessary manifests describing the stream is not
exactly straightforward, but streaming unlocks some interesting use cases that
are not possible to achieve just by pointing a &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element
to a set of static source files. You&#39;ll learn more about how to
&lt;a href=&quot;https://web.dev/add-media/&quot;&gt;add media to a web page&lt;/a&gt; in a later section. First, you should know about a
few use cases for streaming multimedia if you want to go further than just
loading multiple files into the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Adaptive streaming&lt;/strong&gt; is where media chunks are encoded in several
bitrates, and the highest quality media chunk that &lt;strong&gt;&lt;em&gt;fits&lt;/em&gt;&lt;/strong&gt; the client&#39;s
currently available bandwidth is returned to the media player.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Live broadcast&lt;/strong&gt; is where media chunks are encoded and made available in
real time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Injecting media&lt;/strong&gt; is where other media like advertisements are injected into
a stream without the player having to change the media source.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;streaming-protocols&quot;&gt;Streaming protocols &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-streaming-basics/#streaming-protocols&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The two most commonly used streaming protocols on the web are &lt;strong&gt;Dynamic
Adaptive Streaming over HTTP&lt;/strong&gt; (&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/DASH_Adaptive_Streaming_for_HTML_5_Video&quot; rel=&quot;noopener&quot;&gt;DASH&lt;/a&gt;) and &lt;strong&gt;HTTP Live Streaming&lt;/strong&gt; (&lt;a href=&quot;https://developer.apple.com/documentation/http_live_streaming&quot; rel=&quot;noopener&quot;&gt;HLS&lt;/a&gt;).
Players that support these protocols will fetch the generated manifest file,
figure out which media chunks to request, and then combine them into the final
media experience.&lt;/p&gt;
&lt;h3 id=&quot;using-lessvideogreater-to-play-a-stream&quot;&gt;Using &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; to play a stream &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-streaming-basics/#using-lessvideogreater-to-play-a-stream&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Many browsers are not going to play your stream natively. While there is some
native &lt;a href=&quot;https://caniuse.com/http-live-streaming&quot; rel=&quot;noopener&quot;&gt;support for HLS&lt;/a&gt; playback, browsers generally &lt;a href=&quot;https://caniuse.com/mpeg-dash&quot; rel=&quot;noopener&quot;&gt;don&#39;t support native DASH&lt;/a&gt;
stream playback. This means often it&#39;s not enough to simply point the &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt;
in the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element to a manifest file.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;video&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;controls&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;manifest.mpd&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;application/dash+xml&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; This is valid HTML, but doesn&#39;t actually work. Browsers don&#39;t natively support DASH manifest playback added to the &lt;code&gt;src&lt;/code&gt; property. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;What may seem as a deficit is actually a strength in disguise. Streams are
powerful and applications that consume streams have different needs.&lt;/p&gt;
&lt;p&gt;Manifest files usually describe many variants of single media. Think different
bitrates, several audio tracks, and even the same media encoded in different
formats.&lt;/p&gt;
&lt;p&gt;Some applications may want to keep a larger amount of video in the buffer,
others may want to prefetch the first few seconds of video from an upcoming
episode, and some want to implement their own logic for adaptive streaming.
This is where you would want to have some sort of built-in browser feature
to generate media streams for playback, and it just so happens there is one.&lt;/p&gt;
&lt;h3 id=&quot;media-source-extensions&quot;&gt;Media Source Extensions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-streaming-basics/#media-source-extensions&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Thankfully, the W3C defined something called &lt;a href=&quot;https://w3c.github.io/media-source/&quot; rel=&quot;noopener&quot;&gt;Media Source Extensions (MSE)&lt;/a&gt;
that will let JavaScript generate our media streams. In a nutshell, MSE allows
developers to attach a &lt;code&gt;MediaSource&lt;/code&gt; object to a &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element and have
it play back whatever media data is pumped into the buffers attached to the
&lt;code&gt;MediaSource&lt;/code&gt; instance.&lt;/p&gt;
&lt;h3 id=&quot;basic-example&quot;&gt;Basic example &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-streaming-basics/#basic-example&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; videoEl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;video&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mediaSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MediaSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createObjectURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mediaSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;mediaSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;sourceopen&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mimeString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;video/mp4; codecs=&quot;avc1.42E01E, mp4a.40.2&quot;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; buffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mediaSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addSourceBuffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mimeString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    buffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendBuffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Video data as `ArrayBuffer` object. */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The simplified example above illustrates a few things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;As far as &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; is concerned, it is receiving media data from a URL.&lt;/li&gt;
&lt;li&gt;The generated URL is just a pointer to a &lt;code&gt;MediaSource&lt;/code&gt; instance.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;MediaSource&lt;/code&gt; instance creates one or more &lt;code&gt;SourceBuffer&lt;/code&gt; instances.&lt;/li&gt;
&lt;li&gt;We then just append binary media data into the buffer, e.g. using &lt;code&gt;fetch&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While these basic concepts are simple, and it is certainly possible to write a
DASH and HLS compatible video player from scratch, most people usually pick one
of the mature open source solutions that already exist, such as &lt;a href=&quot;https://github.com/google/shaka-player&quot; rel=&quot;noopener&quot;&gt;Shaka Player&lt;/a&gt;,
&lt;a href=&quot;https://developer.jwplayer.com/&quot; rel=&quot;noopener&quot;&gt;JW Player&lt;/a&gt;, or &lt;a href=&quot;http://videojs.com/&quot; rel=&quot;noopener&quot;&gt;Video.js&lt;/a&gt; to name a few.&lt;/p&gt;
&lt;p&gt;However, we have created a demo Media PWA called Kino that demonstrates how you
would go about developing your own basic streaming media website that provides
offline media playback using just the simple &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element. There are plans
in our roadmap to support frameworks and digital rights management, among other
features. So check back for updates from time to time, or request a feature.
Read more about it in the &lt;a href=&quot;https://web.dev/pwa-with-offline-streaming/&quot;&gt;PWA with offline streaming&lt;/a&gt; article.&lt;/p&gt;
&lt;h2 id=&quot;media-chunks-format&quot;&gt;Media chunks format &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-streaming-basics/#media-chunks-format&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a long time, DASH and HLS required media chunks to be encoded in different
formats. In 2016, however, support for standard &lt;strong&gt;fragmented MP4&lt;/strong&gt; (fMP4) files
was added to HLS, a format that DASH also supports.&lt;/p&gt;
&lt;p&gt;Video chunks using the &lt;code&gt;fMP4&lt;/code&gt; container and the &lt;code&gt;H.264&lt;/code&gt; codec are supported
by both protocols and playable by a vast majority of players. This allows
content producers to encode their videos just once, which in turn &lt;strong&gt;saves time
and disk space&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;To achieve better quality and lower files sizes, you may want to choose to
encode several sets of media chunks using more efficient formats like &lt;code&gt;VP9&lt;/code&gt;,
though before we get to far ahead you will first need to learn how to
&lt;a href=&quot;https://web.dev/prepare-media/&quot;&gt;Prepare media files for the web&lt;/a&gt;, and that&#39;s up next.&lt;/p&gt;
</content>
    <author>
      <name>Derek Herman</name>
    </author><author>
      <name>Jaroslav Polakovič</name>
    </author>
  </entry>
  
  <entry>
    <title>PWA with offline streaming</title>
    <link href="https://web.dev/pwa-with-offline-streaming/"/>
    <updated>2021-07-05T00:00:00Z</updated>
    <id>https://web.dev/pwa-with-offline-streaming/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://web.dev/progressive-web-apps/&quot;&gt;Progressive Web Apps&lt;/a&gt; bring a lot of features previously reserved for native
applications to the web. One of the most prominent features associated with
PWAs is an offline experience.&lt;/p&gt;
&lt;p&gt;Even better would be an offline streaming media experience, which is an
enhancement you could offer to your users in a few different ways. However,
this creates a truly unique problem—media files can be &lt;em&gt;very&lt;/em&gt; large. So
you might be asking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How do I download and store a large video file?&lt;/li&gt;
&lt;li&gt;And how do I serve it to the user?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this article we will discuss answers to these questions, while
referencing the &lt;a href=&quot;https://kinoweb.dev/&quot; rel=&quot;noopener&quot;&gt;Kino&lt;/a&gt; demo PWA we built that provides you with practical
examples of how you can implement an offline streaming media experience without
using any functional or presentational frameworks. The following examples are
mainly for educational purposes, because in most cases you should probably use
one of the existing &lt;a href=&quot;https://web.dev/media-frameworks/&quot;&gt;Media Frameworks&lt;/a&gt; to provide these features.&lt;/p&gt;
&lt;p&gt;Unless you have a good business case for developing your own, building a PWA
with offline streaming has its challenges. In this article you will learn about
the APIs and techniques used to provide users with a high-quality offline media
experience.&lt;/p&gt;
&lt;h2 id=&quot;downloading-and-storing-a-large-media-file&quot;&gt;Downloading and storing a large media file &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-with-offline-streaming/#downloading-and-storing-a-large-media-file&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Progressive Web Apps usually use the convenient &lt;a href=&quot;https://web.dev/cache-api-quick-guide/&quot;&gt;Cache API&lt;/a&gt; to both download
and store the assets required to provide the offline experience: documents,
stylesheets, images, and others.&lt;/p&gt;
&lt;p&gt;Here is a basic example of using the Cache API within a Service Worker:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cacheStorageName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;v1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;install&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;    caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cacheStorageName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token string&quot;&gt;&#39;index.html&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token string&quot;&gt;&#39;style.css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token string&quot;&gt;&#39;scripts.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;        &lt;span class=&quot;token comment&quot;&gt;// Don&#39;t do this.&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;        &lt;span class=&quot;token string&quot;&gt;&#39;very-large-video.mp4&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;While the example above does technically work, using the Cache API has several
limitations that makes its use with large files impractical.&lt;/p&gt;
&lt;p&gt;For example, the Cache API doesn&#39;t:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Allow you to easily pause and resume downloads&lt;/li&gt;
&lt;li&gt;Let you track the progress of downloads&lt;/li&gt;
&lt;li&gt;Offer a way to properly respond to &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Range_requests&quot; rel=&quot;noopener&quot;&gt;HTTP range requests&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these issues are pretty serious limitations for any video application.
Let&#39;s review some other options that might be more appropriate.&lt;/p&gt;
&lt;p&gt;Nowadays, the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Fetch_API&quot; rel=&quot;noopener&quot;&gt;Fetch API&lt;/a&gt; is a cross-browser way to asynchronously access remote
files. In our use case it allows you to access large video files as a stream and
store them incrementally as chunks using an HTTP range request.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Check out the [Background Fetch API] as a candidate for a progressive enhancement of the Fetch API in browsers that [support Background Fetch]. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Now that you can read the chunks of data with the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Fetch_API&quot; rel=&quot;noopener&quot;&gt;Fetch API&lt;/a&gt; you also need to
store them. Chances are there is a bunch of metadata associated with your media
file such as: name, description, runtime length, category, etc.&lt;/p&gt;
&lt;p&gt;You&#39;re not storing just the one media file, you are storing a structured object,
and the media file is just one of its properties.&lt;/p&gt;
&lt;p&gt;In this case the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IndexedDB_API&quot; rel=&quot;noopener&quot;&gt;IndexedDB API&lt;/a&gt; provides an excellent solution to store both the
media data and metadata. It can hold huge amounts of binary data easily, and it
also offers indexes that allow you to perform very fast data lookups.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; There is also a [File System Access API] that you could use in some browsers to store the media files directly on the client device. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;downloading-media-files-using-the-fetch-api&quot;&gt;Downloading media files using the Fetch API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-with-offline-streaming/#downloading-media-files-using-the-fetch-api&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We built a couple of interesting features around the Fetch API in our demo PWA,
which we named &lt;a href=&quot;https://kinoweb.dev/&quot; rel=&quot;noopener&quot;&gt;Kino&lt;/a&gt;—the &lt;a href=&quot;https://github.com/GoogleChrome/kino&quot; rel=&quot;noopener&quot;&gt;source code&lt;/a&gt; is public so feel free to review it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The ability to pause and resume incomplete downloads.&lt;/li&gt;
&lt;li&gt;A custom buffer for storing chunks of data in the database.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before showing how those features are implemented, we&#39;ll first do a
quick recap of how you can use the Fetch API to download files.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br /&gt; * Downloads a single file.&lt;br /&gt; *&lt;br /&gt; * @param {string} url URL of the file to be downloaded.&lt;br /&gt; */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;downloadFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; reader &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; done&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dataChunk &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Store the `dataChunk` to IndexedDB.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;done&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Notice that &lt;code&gt;await reader.read()&lt;/code&gt; is in a loop? That&#39;s how you&#39;ll receive chunks
of data from a readable stream as they arrive from the network. Consider how
useful this is: you can start processing your data even before it all arrives
from the network.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you find the concept of streams confusing, check out [Streams–The definitive guide] before you continue. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;resuming-downloads&quot;&gt;Resuming downloads &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-with-offline-streaming/#resuming-downloads&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When a download is paused or interrupted, the data chunks that have arrived will
be safely stored in an IndexedDB database. You can then display a button to
resume a download in your application. Because the &lt;a href=&quot;https://kinoweb.dev/&quot; rel=&quot;noopener&quot;&gt;Kino&lt;/a&gt; demo PWA server
supports &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Range_requests&quot; rel=&quot;noopener&quot;&gt;HTTP range requests&lt;/a&gt; resuming a download is somewhat straightforward:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;downloadFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// this.currentFileMeta contains data from IndexedDB.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; bytesDownloaded&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; downloadUrl &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentFileMeta&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fetchOpts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// If we already have some data downloaded,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// request everything from that position on.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bytesDownloaded&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    fetchOpts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;bytes=&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;bytesDownloaded&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;downloadUrl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fetchOpts&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; reader &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; dataChunk&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    dataChunk &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;dataChunk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;done&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dataChunk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;dataChunk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;done &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;paused&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;custom-write-buffer-for-indexeddb&quot;&gt;Custom write buffer for IndexedDB &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-with-offline-streaming/#custom-write-buffer-for-indexeddb&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;On paper, the process of writing &lt;code&gt;dataChunk&lt;/code&gt; values into an IndexedDB database
is simple. Those values already are &lt;code&gt;ArrayBuffer&lt;/code&gt; instances, which are storable
in IndexedDB directly, so we can just create an object of an appropriate shape
and store it.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dataItem &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; fileUrl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;rangeStart&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; dataStartByte&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;rangeEnd&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; dataEndByte&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; dataChunk&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Name of the store that will hold your data.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; storeName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;fileChunksStorage&#39;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// `db` is an instance of `IDBDatabase`.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; transaction &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;transaction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;storeName&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;readwrite&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; store &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; transaction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;objectStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;storeName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; putRequest &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; store&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;putRequest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onsuccess&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;While this approach works, you will likely discover that your IndexedDB writes
are significantly slower than your download. This isn&#39;t because IndexedDB writes
are slow, it&#39;s because we are adding a lot of transactional overhead by creating
a new transaction for every data chunk that we receive from a network.&lt;/p&gt;
&lt;p&gt;The downloaded chunks can be rather small and can be emitted by the stream in
rapid succession. You need to limit the rate of IndexedDB writes. In the
&lt;a href=&quot;https://kinoweb.dev/&quot; rel=&quot;noopener&quot;&gt;Kino&lt;/a&gt; demo PWA we do this by implementing an &lt;strong&gt;intermediary write buffer&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;As data chunks arrive from the network, we append them to our buffer first. If
the incoming data doesn&#39;t fit, we flush the full buffer into the database and
clear it before appending the rest of the data. As a result our IndexedDB
writes are less frequent, which leads to significantly improved write
performance.&lt;/p&gt;
&lt;h2 id=&quot;serving-a-media-file-from-offline-storage&quot;&gt;Serving a media file from offline storage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-with-offline-streaming/#serving-a-media-file-from-offline-storage&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you have a media file downloaded, you probably want your service worker to
serve it from IndexedDB instead of fetching the file from the network.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br /&gt; * The main service worker fetch handler.&lt;br /&gt; *&lt;br /&gt; * @param {FetchEvent} event Fetch event.&lt;br /&gt; */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;fetchHandler&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;getResponse&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Omitted Cache API code used to serve static assets.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; videoResponse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getVideoResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;videoResponse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; videoResponse&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Fallback to network.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;respondWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;fetch&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fetchHandler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;So what do you need to do in &lt;code&gt;getVideoResponse()&lt;/code&gt;?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;event.respondWith()&lt;/code&gt; method expects a &lt;code&gt;Response&lt;/code&gt; object as a parameter.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Response/Response&quot; rel=&quot;noopener&quot;&gt;Response() constructor&lt;/a&gt; tells us that there are several types of objects we
could use to instantiate a &lt;code&gt;Response&lt;/code&gt; object: a &lt;code&gt;Blob&lt;/code&gt;, &lt;code&gt;BufferSource&lt;/code&gt;,
&lt;code&gt;ReadableStream&lt;/code&gt;, and more.&lt;/li&gt;
&lt;li&gt;We need an object that doesn&#39;t hold all of its data in memory, so we&#39;ll
probably want to choose the &lt;code&gt;ReadableStream&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, because we&#39;re dealing with large files, and we wanted to allow browsers to
only request the part of the file they currently need, we needed to implement
some basic support for &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Range_requests&quot; rel=&quot;noopener&quot;&gt;HTTP range requests&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br /&gt; * Respond to a request to fetch offline video file and construct a response&lt;br /&gt; * stream.&lt;br /&gt; *&lt;br /&gt; * Includes support for `Range` requests.&lt;br /&gt; *&lt;br /&gt; * @param {Request} request  Request object.&lt;br /&gt; * @param {Object}  fileMeta File meta object.&lt;br /&gt; *&lt;br /&gt; * @returns {Response} Response object.&lt;br /&gt; */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;getVideoResponse&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fileMeta&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rangeRequest &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;range&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; byteRanges &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rangeRequest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;bytes=&lt;span class=&quot;token group punctuation&quot;&gt;(?&amp;lt;&lt;span class=&quot;token group-name variable&quot;&gt;from&lt;/span&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token char-class&quot;&gt;&lt;span class=&quot;token char-class-punctuation punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token range&quot;&gt;0&lt;span class=&quot;token range-punctuation operator&quot;&gt;-&lt;/span&gt;9&lt;/span&gt;&lt;span class=&quot;token char-class-punctuation punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token quantifier number&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token group punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token quantifier number&quot;&gt;?&lt;/span&gt;-&lt;span class=&quot;token group punctuation&quot;&gt;(?&amp;lt;&lt;span class=&quot;token group-name variable&quot;&gt;to&lt;/span&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token char-class&quot;&gt;&lt;span class=&quot;token char-class-punctuation punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token range&quot;&gt;0&lt;span class=&quot;token range-punctuation operator&quot;&gt;-&lt;/span&gt;9&lt;/span&gt;&lt;span class=&quot;token char-class-punctuation punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token quantifier number&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token group punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token quantifier number&quot;&gt;?&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Using the optional chaining here to access properties of&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// possibly nullish objects.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rangeFrom &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;byteRanges&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;groups&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;from &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rangeTo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;byteRanges&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;groups&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;to &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; fileMeta&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bytesTotal &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Omitting implementation for brevity.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; streamSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;     &lt;span class=&quot;token function&quot;&gt;pull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;       &lt;span class=&quot;token comment&quot;&gt;// Read file data here and call `controller.enqueue`&lt;/span&gt;&lt;br /&gt;       &lt;span class=&quot;token comment&quot;&gt;// with every retrieved chunk, then `controller.close`&lt;/span&gt;&lt;br /&gt;       &lt;span class=&quot;token comment&quot;&gt;// once all data is read.&lt;/span&gt;&lt;br /&gt;     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; stream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReadableStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;streamSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Make sure to set proper headers when supporting range requests.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; responseOpts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rangeRequest &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;206&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;statusText&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rangeRequest &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Partial Content&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;OK&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string-property property&quot;&gt;&#39;Accept-Ranges&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;bytes&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string-property property&quot;&gt;&#39;Content-Length&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rangeTo &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; rangeFrom &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rangeRequest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    responseOpts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Content-Range&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;bytes &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;rangeFrom&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;rangeTo&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;fileMeta&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bytesTotal&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stream&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; responseOpts&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Feel free to check out the &lt;a href=&quot;https://kinoweb.dev/&quot; rel=&quot;noopener&quot;&gt;Kino&lt;/a&gt; demo PWA &lt;a href=&quot;https://github.com/GoogleChrome/kino/blob/72055bdc7c8ad3de943a55293b2bf882e467c814/src/js/sw/sw.js#L53-L122&quot; rel=&quot;noopener&quot;&gt;service worker source code&lt;/a&gt; to find
out how we are reading file data from IndexedDB and constructing a stream in
a real application.&lt;/p&gt;
&lt;h2 id=&quot;other-considerations&quot;&gt;Other considerations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-with-offline-streaming/#other-considerations&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With the main obstacles out of your way, you can now start adding some
nice-to-have features to your video application. Here are a few examples of
features you would find in the &lt;a href=&quot;https://kinoweb.dev/&quot; rel=&quot;noopener&quot;&gt;Kino&lt;/a&gt; demo PWA:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/media-session/&quot;&gt;Media Session API&lt;/a&gt; integration that allows your users to control media
playback using dedicated hardware media keys or from media notification
popups.&lt;/li&gt;
&lt;li&gt;Caching of other assets associated with the media files like subtitles, and
poster images using the good old &lt;a href=&quot;https://web.dev/cache-api-quick-guide/&quot;&gt;Cache API&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Support for video streams (DASH, HLS) download within the app. Because stream
manifests generally declare multiple sources of different bitrates, you need to
transform the manifest file and only download one media version before storing
it for offline viewing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Up next, you will learn about &lt;a href=&quot;https://web.dev/fast-playback-with-preload/&quot;&gt;Fast playback with audio and video preload&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Jaroslav Polakovič</name>
    </author><author>
      <name>Derek Herman</name>
    </author>
  </entry>
  
  <entry>
    <title>Media accessibility</title>
    <link href="https://web.dev/media-accessibility/"/>
    <updated>2020-08-20T00:00:00Z</updated>
    <id>https://web.dev/media-accessibility/</id>
    <content type="html" mode="escaped">&lt;p&gt;In this article you will learn about the WebVTT (Web Video Text Tracks) format,
which is used to describe timed text data such as closed captions or subtitles
in order to make videos more accessible to your audience.&lt;/p&gt;
&lt;p&gt;Accessibility isn&#39;t like icing on a cake. It&#39;s never anything you put on a
backlog with the hope of introducing it later. Captions and screen reader
descriptions are the only way many users can experience your videos, and in some
jurisdictions, they&#39;re even required by law or regulation.&lt;/p&gt;
&lt;h2 id=&quot;add-lesstrackgreater-tags&quot;&gt;Add &lt;code&gt;&amp;lt;track&amp;gt;&lt;/code&gt; tags &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-accessibility/#add-lesstrackgreater-tags&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To add captions or screen reader descriptions to a web video, add a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/track&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;track&amp;gt;&lt;/code&gt;&lt;/a&gt;
tag within a &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag. In addition to captions and screen reader
descriptions, &lt;code&gt;&amp;lt;track&amp;gt;&lt;/code&gt; tags may also be used for subtitles and chapter titles.
The &lt;code&gt;&amp;lt;track&amp;gt;&lt;/code&gt; tag can also help search engines understand what&#39;s in a video.
However, those capabilities are outside the scope of this article.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot showing captions displayed using the track element in Chrome on Android&quot; decoding=&quot;async&quot; height=&quot;480&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vbNDp5R05MwQmsxZ0RLI.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Screenshot showing captions displayed using the
track element in Chrome on Android&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;track&amp;gt;&lt;/code&gt; tag is similar to the &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; element in that both have a &lt;code&gt;src&lt;/code&gt;
attribute that points to referenced content. For a &lt;code&gt;&amp;lt;track&amp;gt;&lt;/code&gt; tag, it points to a
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/WebVTT_API&quot; rel=&quot;noopener&quot;&gt;WebVTT file&lt;/a&gt;. The &lt;code&gt;label&lt;/code&gt; attribute specifies how a particular track will be
identified in the interface.&lt;/p&gt;
&lt;p&gt;To provide tracks for multiple languages add a separate &lt;code&gt;&amp;lt;track&amp;gt;&lt;/code&gt; tag for each
WebVTT file you&#39;re providing and indicate the language using the &lt;code&gt;srclang&lt;/code&gt;
attribute.&lt;/p&gt;
&lt;p&gt;An example &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag with two &lt;code&gt;&amp;lt;track&amp;gt;&lt;/code&gt; tags is shown below. There&#39;s also a
sample you can &lt;a href=&quot;https://track-demonstration.glitch.me/&quot; rel=&quot;noopener&quot;&gt;view on Glitch&lt;/a&gt; (&lt;a href=&quot;https://glitch.com/edit/#!/track-demonstration&quot; rel=&quot;noopener&quot;&gt;source&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Add a &lt;code&gt;&amp;lt;track&amp;gt;&lt;/code&gt; element as a child of the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;video&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;controls&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://storage.googleapis.com/webfundamentals-assets/videos/chrome.webm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;video/webm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://storage.googleapis.com/webfundamentals-assets/videos/chrome.mp4&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;video/mp4&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;track&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;chrome-subtitles-en.vtt&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;English captions&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;captions&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srclang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;en&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;track&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;chrome-subtitles-zh.vtt&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;中文字幕&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;captions&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srclang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;zh&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;This browser does not support the video element.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Notice in the example on &lt;strong&gt;line 4&lt;/strong&gt; that the &lt;code&gt;default&lt;/code&gt; attribute indicates which language track is the default. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;webvtt-file-structure&quot;&gt;WebVTT file structure &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-accessibility/#webvtt-file-structure&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Below is a hypothetical WebVTT file for the demo linked to above. The file is a
text file containing a series of &lt;em&gt;cues&lt;/em&gt;. Each cue is a block of text to display
on screen, and the time range during which it will be displayed.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;WEBVTT&lt;br /&gt;&lt;br /&gt;00:00.000 --&gt; 00:04.999&lt;br /&gt;Man sitting on a tree branch, using a laptop.&lt;br /&gt;&lt;br /&gt;00:05.000 --&gt; 00:08.000&lt;br /&gt;The branch breaks, and he starts to fall.&lt;br /&gt;&lt;br /&gt;...&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Each item within the track file is called a &lt;em&gt;cue&lt;/em&gt;. Each cue has a start time and
end time separated by an arrow, with cue text in the line below. Cues can
optionally also have IDs like &lt;code&gt;railroad&lt;/code&gt; and &lt;code&gt;manuscript&lt;/code&gt; in the example below.
Cues are separated by an empty line.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;WEBVTT&lt;br /&gt;&lt;br /&gt;railroad&lt;br /&gt;00:00:10.000 --&gt; 00:00:12.500&lt;br /&gt;Left uninspired by the crust of railroad earth&lt;br /&gt;&lt;br /&gt;manuscript&lt;br /&gt;00:00:13.200 --&gt; 00:00:16.900&lt;br /&gt;that touched the lead to the pages of your manuscript.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Cue times are in &lt;code&gt;hours:minutes:seconds.milliseconds&lt;/code&gt; format. Parsing is strict.
Meaning, numbers must be zero padded if necessary: hours, minutes, and seconds
must have two digits (00 for a zero value) and milliseconds must have three
digits (000 for a zero value). There is an excellent WebVTT validator at
&lt;a href=&quot;https://quuz.org/webvtt/&quot; rel=&quot;noopener&quot;&gt;Live WebVTT Validator&lt;/a&gt;, which checks for errors in time formatting, and
problems such as non-sequential times.&lt;/p&gt;
&lt;p&gt;You can create a VTT file by hand, thought there are &lt;a href=&quot;https://www.google.com/search?q=webvtt+services&quot; rel=&quot;noopener&quot;&gt;many services&lt;/a&gt; that will
create them for you.&lt;/p&gt;
&lt;p&gt;As you can see in our previous examples, the WebVTT format is pretty simple.
Just add your text data along with timings.&lt;/p&gt;
&lt;p&gt;However, what if you want your captions to render in a different position with
left or right alignment? Perhaps to align the captions with the current speaker
position, or to stay out of the way of in-camera text. WebVTT defines settings to do that,
and more, directly inside the
&lt;code&gt;.vtt&lt;/code&gt; file. Take note of how the caption placement is defined by adding
settings after the time interval definitions.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;WEBVTT&lt;br /&gt;&lt;br /&gt;00:00:05.000 --&gt; 00:00:10.000 line:0 position:20% size:60% align:start&lt;br /&gt;The first line of the subtitles.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Another handy feature is the ability to style cues using CSS. Perhaps you want
to use a gray linear gradient as the background, with a foreground color of
&lt;code&gt;papayawhip&lt;/code&gt; for all captions and all bold text colored &lt;code&gt;peachpuff&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;video::cue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to bottom&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dimgray&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lightgray&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; papayawhip&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;video::cue(b)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; peachpuff&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Be careful when coloring text to keep to high-contrast colors so as not to degrade readability. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;If you&#39;re interested in learning more about styling and tagging of individual
cues, the &lt;a href=&quot;https://w3c.github.io/webvtt/&quot; rel=&quot;noopener&quot;&gt;WebVTT specification&lt;/a&gt; is a good source for advanced examples.&lt;/p&gt;
&lt;h2 id=&quot;kinds-of-text-tracks&quot;&gt;Kinds of text tracks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-accessibility/#kinds-of-text-tracks&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Did you notice the &lt;code&gt;kind&lt;/code&gt; attribute of the &lt;code&gt;&amp;lt;track&amp;gt;&lt;/code&gt; element? It&#39;s used to
indicate what relation the particular text track has to the video. The
possible values of the &lt;code&gt;kind&lt;/code&gt; attribute are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;captions&lt;/code&gt;: For closed captions from transcripts and possibly translations
of any audio. Suitable for hearing impaired and in cases when the video is
playing muted.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subtitles&lt;/code&gt;: For subtitles, that is, translations of speech and text in a
language different from the main language of the video.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;descriptions&lt;/code&gt;: For descriptions of visual parts of the video content.
Suitable for visually impaired people.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chapters&lt;/code&gt;: Intended to be displayed when the user is navigating within the
video.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;metadata&lt;/code&gt;: Not visible, and may be used by scripts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that you understand the basics of making a video available and accessible
on your web page, you might wonder about more complex use cases. Next up, you&#39;ll
lean about &lt;a href=&quot;https://web.dev/media-frameworks/&quot;&gt;Media frameworks&lt;/a&gt; and how they can help you add
videos to your web page while providing advanced features.&lt;/p&gt;
</content>
    <author>
      <name>Joe Medley</name>
    </author><author>
      <name>Derek Herman</name>
    </author>
  </entry>
  
  <entry>
    <title>Add media to a web page</title>
    <link href="https://web.dev/add-media/"/>
    <updated>2020-06-19T00:00:00Z</updated>
    <id>https://web.dev/add-media/</id>
    <content type="html" mode="escaped">&lt;p&gt;In this section you&#39;ll learn how to embed a media file in a web page using the
&lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tags, how to add captions to a video for the
hearing-impaired, and some general concepts for using media frameworks.
Additionally, you&#39;ll learn about using preload to speed up playback start, and
we will discuss how we built an updated demo PWA with offline adaptive
streaming capabilities called Kino.&lt;/p&gt;
&lt;p&gt;This section assumes you have a video file that is ready for embedding in a web
page. A &lt;code&gt;.mov&lt;/code&gt; file downloaded from a camera will not work. If that&#39;s all you
have, see &lt;a href=&quot;https://web.dev/prepare-media/&quot;&gt;Prepare media files for the web&lt;/a&gt; then come back.&lt;/p&gt;
&lt;p&gt;This section covers these topics.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In &lt;a href=&quot;https://web.dev/video-and-source-tags/&quot;&gt;The &amp;lt;video&amp;gt; and &amp;lt;source&amp;gt; tags&lt;/a&gt; you&#39;ll
learn specifically how to embed a media file in a web page.&lt;/li&gt;
&lt;li&gt;In &lt;a href=&quot;https://web.dev/media-accessibility/&quot;&gt;Media accessibility&lt;/a&gt; you&#39;ll learn to add captions
to a media file for hearing impaired.&lt;/li&gt;
&lt;li&gt;In &lt;a href=&quot;https://web.dev/media-frameworks/&quot;&gt;Media frameworks&lt;/a&gt; you&#39;ll learn basics about using
media frameworks like Shaka Player, JW Player, and Video.js.&lt;/li&gt;
&lt;li&gt;In &lt;a href=&quot;https://web.dev/fast-playback-with-preload/&quot;&gt;Fast playback with audio and video preload&lt;/a&gt;
you&#39;ll learn to accelerate your media playback by actively preloading
resources.&lt;/li&gt;
&lt;li&gt;In &lt;a href=&quot;https://web.dev/pwa-with-offline-streaming/&quot;&gt;PWA with offline streaming&lt;/a&gt; you&#39;ll learn how
we built an updated demo PWA that is capable of adaptive streaming and offline
playback without using frameworks. Plus you can play with source code.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Be sure not to skip the accessibility section. The technical aspects of supporting accessibility are not that difficult. They are also a regulatory or legal requirement in many places. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;There is a lot of ground to cover in this section, get started by learning
how to use the &lt;a href=&quot;https://web.dev/video-and-source-tags/&quot;&gt;&amp;lt;video&amp;gt; and &amp;lt;source&amp;gt; tags&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Joe Medley</name>
    </author><author>
      <name>Derek Herman</name>
    </author>
  </entry>
  
  <entry>
    <title>Media file basics</title>
    <link href="https://web.dev/media-file-basics/"/>
    <updated>2020-06-19T00:00:00Z</updated>
    <id>https://web.dev/media-file-basics/</id>
    <content type="html" mode="escaped">&lt;p&gt;In this article you will learn about media file basics such as the concepts of a
container, and a few of the many available codec formats that you can use in a
stream. Plus lightly touch on topics such as adaptive streaming, bitrate, and
resolution—but we&#39;ll dive deeper into all these in later sections.&lt;/p&gt;
&lt;h2 id=&quot;serving-video-files&quot;&gt;Serving video files &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-file-basics/#serving-video-files&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You might think that you can take a raw file from a video camera and just upload
it to the web as-is. Indeed, video streaming sites such as &lt;a href=&quot;https://www.youtube.com/&quot; rel=&quot;noopener&quot;&gt;YouTube&lt;/a&gt; or &lt;a href=&quot;https://vimeo.com/&quot; rel=&quot;noopener&quot;&gt;Vimeo&lt;/a&gt;
let you do just that, and even provide live-streaming capabilities—typically by
connecting to your camera&#39;s HDMI port and then processing it through a capture
card. These services greatly simplify video processing and uploading, which
includes generating the many files and manifests needed for adaptive streaming
and various resolutions. Plus the many other complicated and nuanced requirements
that make self-hosting a bit of a chore. Preparing and serving a video from your
own site, and likely a separate media server, is a bit more complicated than just
uploading a raw camera file if you care how your users experience your site.&lt;/p&gt;
&lt;p&gt;Video files come in a variety of formats. The format that comes off your camera is
typically a &lt;code&gt;.mov&lt;/code&gt; file, or an &lt;code&gt;.mp4&lt;/code&gt; if you have a good modern mirrorless camera.
However, while a &lt;code&gt;.mov&lt;/code&gt; is good for recording and for editing and other early
post-production processes, the file size means it&#39;s not good for streaming over
the web. As well, the file size of a raw &lt;code&gt;.mp4&lt;/code&gt; in 4K is going to make playing that
file on mobile very prohibitive. Because browsers support different file formats,
you&#39;ll need to create multiple optimized files and potentially a manifest if you
plan to support adaptive streaming. Before converting files you need to understand
a few basics about them and about their characteristics.&lt;/p&gt;
&lt;h2 id=&quot;containers-and-codecs,-and-streams&quot;&gt;Containers and codecs, and streams? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-file-basics/#containers-and-codecs,-and-streams&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The file that you see in your operating system shell is a &lt;em&gt;container&lt;/em&gt;,
identified by a file extension (&lt;code&gt;.mp4&lt;/code&gt;, &lt;code&gt;.webm&lt;/code&gt;, &lt;code&gt;.ogg&lt;/code&gt; etc.). The container
houses one or more &lt;em&gt;streams&lt;/em&gt;. A media file can have any number of streams, of
many more &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Media/Formats&quot; rel=&quot;noopener&quot;&gt;formats&lt;/a&gt; than we&#39;ll go into here.&lt;/p&gt;
&lt;p&gt;The sample files used later in this section contain at most two streams: an
audio stream and a video stream. Among the other types you might encounter are
captions and data, both of which are beyond the scope of this article. There are
instances where audio and video streams are dealt with separately. Most files
you&#39;ll encounter will only contain a single audio stream and a single video
stream.&lt;/p&gt;
&lt;p&gt;Within the audio and video streams, the actual data is compressed using a codec.
A &lt;em&gt;codec&lt;/em&gt;, or coder/decoder, is a compression format for video or audio data. The
distinction between a container and a codec is important because files with the
same container can have their contents encoded with different codecs.&lt;/p&gt;
&lt;p&gt;The image below illustrates this structure. On the left is the basic container
structure with two streams. On the right are the specifics of that structure for
a single WebM file.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Comparing media file structure with a hypothetical media file.&quot; decoding=&quot;async&quot; height=&quot;250&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 560px) 560px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QwNEBBa8LEMpedJh5imG.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QwNEBBa8LEMpedJh5imG.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QwNEBBa8LEMpedJh5imG.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QwNEBBa8LEMpedJh5imG.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QwNEBBa8LEMpedJh5imG.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QwNEBBa8LEMpedJh5imG.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QwNEBBa8LEMpedJh5imG.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QwNEBBa8LEMpedJh5imG.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QwNEBBa8LEMpedJh5imG.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QwNEBBa8LEMpedJh5imG.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QwNEBBa8LEMpedJh5imG.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QwNEBBa8LEMpedJh5imG.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QwNEBBa8LEMpedJh5imG.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QwNEBBa8LEMpedJh5imG.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QwNEBBa8LEMpedJh5imG.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QwNEBBa8LEMpedJh5imG.png?auto=format&amp;w=1120 1120w&quot; width=&quot;560&quot; /&gt;
  &lt;figcaption&gt;Parts of a media file.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Files in WebM containers can be orders of magnitude smaller than other formats,
making them a good choice for mobile sites to stream. Unfortunately, not all
browsers support up-to-date containers and codecs. For example, WebM was created
specifically for the web as a high-quality and open source option, but its support
is not yet universal. Safari in particular does not, according to &lt;a href=&quot;https://caniuse.com/#feat=webm&quot; rel=&quot;noopener&quot;&gt;Can I use&lt;/a&gt; at
the time of this writing, support WebM for embedded video. However, WebM does have
partial support with the VP8 and VP9 codec used in WebRTC. So your best option is
to provide a fallback video.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; All modern browsers support MP4 files, making them a good general choice for a media container and the best choice as the backup container for WebM. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;codec-formats&quot;&gt;Codec formats &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-file-basics/#codec-formats&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Many file types support multiple codecs within the same container. A complete list
of available &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Media/Formats/Video_codecs&quot; rel=&quot;noopener&quot;&gt;video codecs&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Media/Formats/Audio_codecs&quot; rel=&quot;noopener&quot;&gt;audio codecs&lt;/a&gt; would be a whole website to itself.
The links just provided are for MDN&#39;s practical lists of what&#39;s usable on the web.
Listed below are the currently preferred file types, and the codecs they might use.
Follow the file type links to view the browsers that support them.&lt;/p&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File type&lt;/th&gt;
&lt;th&gt;Video Codec&lt;/th&gt;
&lt;th&gt;Audio Codec&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://caniuse.com/#search=mp4&quot; rel=&quot;noopener&quot;&gt;MP4&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/Media/Formats/Video_codecs#AV1&quot; rel=&quot;noopener&quot;&gt;AV1&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Media/Formats/Video_codecs#AVC_H.264&quot; rel=&quot;noopener&quot;&gt;AVC (H.264)&lt;/a&gt;*, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Media/Formats/Video_codecs#VP9&quot; rel=&quot;noopener&quot;&gt;VP9&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/Media/Formats/Audio_codecs#AAC&quot; rel=&quot;noopener&quot;&gt;AAC&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://caniuse.com/#feat=webm&quot; rel=&quot;noopener&quot;&gt;WebM&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/Media/Formats/Video_codecs#AV1&quot; rel=&quot;noopener&quot;&gt;AV1&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Media/Formats/Video_codecs#VP9&quot; rel=&quot;noopener&quot;&gt;VP9&lt;/a&gt;*&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/Media/Formats/Audio_codecs#Vorbis&quot; rel=&quot;noopener&quot;&gt;Vorbis&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Media/Formats/Audio_codecs#Opus&quot; rel=&quot;noopener&quot;&gt;Opus&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;&lt;p&gt;* Indicates the preferred video codec.&lt;/p&gt;
&lt;h2 id=&quot;bitrate-and-resolution&quot;&gt;Bitrate and resolution &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-file-basics/#bitrate-and-resolution&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Bitrate&lt;/strong&gt; is the maximum number of bits used to encode one second of a stream.
The more bits used to encode a second of stream, the higher the potential detail
and fidelity. We provide more information about this concept in &lt;a href=&quot;https://web.dev/bitrate/&quot;&gt;Bitrate&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Resolution&lt;/strong&gt; is the amount of information in a single frame of video, given as
the number of logical pixels in each dimension. We provide more information about
this concept in &lt;a href=&quot;https://web.dev/resolution/&quot;&gt;Resolution&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Up next, in &lt;a href=&quot;https://web.dev/media-application-basics/&quot;&gt;Media application basics&lt;/a&gt;, we&#39;ll show you
how to examine these characteristics using two command line tools: Shaka Packager
and FFmpeg.&lt;/p&gt;
</content>
    <author>
      <name>Joe Medley</name>
    </author><author>
      <name>Derek Herman</name>
    </author>
  </entry>
  
  <entry>
    <title>Media conversion</title>
    <link href="https://web.dev/media-conversion/"/>
    <updated>2018-09-20T00:00:00Z</updated>
    <id>https://web.dev/media-conversion/</id>
    <content type="html" mode="escaped">&lt;p&gt;In this article we are going to learn some common commands for converting and
manipulating specific characteristics of media files. Although we&#39;ve tried to
show equivalent operations for all procedures, not all operations are possible
in both applications.&lt;/p&gt;
&lt;p&gt;In many cases, the commands we&#39;re showing may be combined in a single command
line operation, and would be when actually used. For example, there&#39;s nothing
preventing you from setting an output file&#39;s bitrate in the same operation as
a file conversion. For this article, we often show these operations as separate
commands for the sake of clarity.&lt;/p&gt;
&lt;p&gt;Conversion is done with these applications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/google/shaka-packager&quot; rel=&quot;noopener&quot;&gt;Shaka Packager&lt;/a&gt; (&lt;a href=&quot;https://stackoverflow.com/questions/tagged/shaka&quot; rel=&quot;noopener&quot;&gt;on Stack Overflow&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/download.html&quot; rel=&quot;noopener&quot;&gt;FFmpeg&lt;/a&gt;, (&lt;a href=&quot;https://stackoverflow.com/questions/tagged/ffmpeg&quot; rel=&quot;noopener&quot;&gt;on Stack Overflow&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;display-characteristics&quot;&gt;Display characteristics &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-conversion/#display-characteristics&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Both Shaka Packager and FFmpeg can be used to inspect the content of a media
file and then display the characteristics of a stream. However, both provide
different output for the same media.&lt;/p&gt;
&lt;h3 id=&quot;characteristics-using-shaka-packager&quot;&gt;Characteristics using Shaka Packager &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-conversion/#characteristics-using-shaka-packager&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;packager &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;glocken.mp4 --dump_stream_info&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The output looks like:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;File &lt;span class=&quot;token string&quot;&gt;&quot;glocken.mp4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt;&lt;br /&gt;Found &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; stream&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;.&lt;br /&gt;Stream &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; type: Video&lt;br /&gt; codec_string: avc1.640028&lt;br /&gt; time_scale: &lt;span class=&quot;token number&quot;&gt;30000&lt;/span&gt;&lt;br /&gt; duration: &lt;span class=&quot;token number&quot;&gt;300300&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10.0&lt;/span&gt; seconds&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt; is_encrypted: &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;br /&gt; codec: H264&lt;br /&gt; width: &lt;span class=&quot;token number&quot;&gt;1920&lt;/span&gt;&lt;br /&gt; height: &lt;span class=&quot;token number&quot;&gt;1080&lt;/span&gt;&lt;br /&gt; pixel_aspect_ratio: &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:1&lt;br /&gt; trick_play_factor: &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;br /&gt; nalu_length_size: &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Stream &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; type: Audio&lt;br /&gt; codec_string: mp4a.40.2&lt;br /&gt; time_scale: &lt;span class=&quot;token number&quot;&gt;48000&lt;/span&gt;&lt;br /&gt; duration: &lt;span class=&quot;token number&quot;&gt;481280&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10.0&lt;/span&gt; seconds&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt; is_encrypted: &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;br /&gt; codec: AAC&lt;br /&gt; sample_bits: &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;br /&gt; num_channels: &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;br /&gt; sampling_frequency: &lt;span class=&quot;token number&quot;&gt;48000&lt;/span&gt;&lt;br /&gt; language: eng&lt;br /&gt; seek_preroll_ns: &lt;span class=&quot;token number&quot;&gt;20833&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;characteristics-using-ffmpeg&quot;&gt;Characteristics using FFmpeg &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-conversion/#characteristics-using-ffmpeg&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i glocken.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The output looks like:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;Input &lt;span class=&quot;token comment&quot;&gt;#0, mov,mp4,m4a,3gp,3g2,mj2, from &#39;glocken.mp4&#39;:&lt;/span&gt;&lt;br /&gt;  Metadata:&lt;br /&gt;    major_brand     &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; isom&lt;br /&gt;    minor_version   &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;512&lt;/span&gt;&lt;br /&gt;    compatible_brands: isomiso2avc1mp41&lt;br /&gt;    encoder         &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; Lavf57.83.100&lt;br /&gt;  Duration: 00:00:10.03, start: &lt;span class=&quot;token number&quot;&gt;0.000000&lt;/span&gt;, bitrate: &lt;span class=&quot;token number&quot;&gt;8063&lt;/span&gt; kb/s&lt;br /&gt;    Stream &lt;span class=&quot;token comment&quot;&gt;#0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuvj420p(pc), 1920x1080, 7939 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 59.94 tbc (default)&lt;/span&gt;&lt;br /&gt;    Metadata:&lt;br /&gt;      handler_name    &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; VideoHandler&lt;br /&gt;    Stream &lt;span class=&quot;token comment&quot;&gt;#0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 127 kb/s (default)&lt;/span&gt;&lt;br /&gt;    Metadata:&lt;br /&gt;      handler_name    &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; SoundHandler&lt;br /&gt;At least one output &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt; must be specified&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Technically, FFmpeg always requires an output file format. Calling FFmpeg this way will give you an error message explaining that; however, it lists information not available using Shaka Packager so feel free to ignore the error. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;demux-separate-the-audio-and-video-streams&quot;&gt;Demux (separate) the audio and video streams &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-conversion/#demux-separate-the-audio-and-video-streams&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Shaka Packager requires demuxing when converting files. This is also required
for using media frameworks.&lt;/p&gt;
&lt;h3 id=&quot;shaka-packager-demuxing&quot;&gt;Shaka Packager demuxing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-conversion/#shaka-packager-demuxing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;MP4&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;packager &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myvideo.mp4,stream&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;video,output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myvideo_video.mp4&lt;br /&gt;packager &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myvideo.mp4,stream&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;audio,output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myvideo_audio.m4a&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Or:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;packager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myvideo.mp4,stream&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;video,output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myvideo_video.mp4 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myvideo.mp4,stream&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;audio,output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myvideo_audio.m4a&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;WebM&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;packager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myvideo.webm,stream&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;video,output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myvideo_video.webm &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myvideo.webm,stream&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;audio,output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myvideo_audio.webm&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;ffmpeg-demuxing&quot;&gt;FFmpeg demuxing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-conversion/#ffmpeg-demuxing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;MP4&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i myvideo.mp4 -vcodec copy -an myvideo_video.mp4&lt;br /&gt;ffmpeg -i myvideo.mp4 -acodec copy -vn myvideo_audio.m4a&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;WebM&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i myvideo.webm -vcodec copy -an myvideo_video.webm&lt;br /&gt;ffmpeg -i myvideo.webm -acodec copy -vn myvideo_audio.webm&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;remux-combine-the-audio-and-video-streams&quot;&gt;Remux (combine) the audio and video streams &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-conversion/#remux-combine-the-audio-and-video-streams&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In some situation you will need to combine the audio and video back into a single
container. Especially when not using a media framework. This is something FFmpeg
can handle quite well and is something Shaka Packager does not currently support.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i myvideo_video.webm -i myvideo_audio.webm -c copy myvideo.webm&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;change-characteristics&quot;&gt;Change characteristics &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-conversion/#change-characteristics&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;bitrate&quot;&gt;Bitrate &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-conversion/#bitrate&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For FFmpeg, we can do this while converting to &lt;code&gt;.mp4&lt;/code&gt; or &lt;code&gt;.webm&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i myvideo.mov -b:v 350K myvideo.mp4&lt;br /&gt;ffmpeg -i myvideo.mov -vf &lt;span class=&quot;token assign-left variable&quot;&gt;setsar&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:1 -b:v 350K myvideo.webm&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;dimensions-resolution&quot;&gt;Dimensions (resolution) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-conversion/#dimensions-resolution&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i myvideo.webm -s 1920x1080 myvideo_1920x1080.webm&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;file-type&quot;&gt;File type &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-conversion/#file-type&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Shaka Packager cannot process &lt;code&gt;.mov&lt;/code&gt; files and hence cannot be used to convert
files from that format.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;.mov&lt;/code&gt; to &lt;code&gt;.mp4&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i myvideo.mov myvideo.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;.mov&lt;/code&gt; to &lt;code&gt;.webm&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i myvideo.mov myvideo.webm&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;synchronize-audio-and-video&quot;&gt;Synchronize audio and video &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-conversion/#synchronize-audio-and-video&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To ensure that audio and video synchronize during playback, insert keyframes.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i myvideo.mp4 -keyint_min &lt;span class=&quot;token number&quot;&gt;150&lt;/span&gt; -g &lt;span class=&quot;token number&quot;&gt;150&lt;/span&gt; -f webm -vf &lt;span class=&quot;token assign-left variable&quot;&gt;setsar&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:1 out.webm&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Visit &lt;em&gt;Containers and codecs&lt;/em&gt; to view the audio and video &lt;a href=&quot;https://web.dev/containers-and-codecs/#codecs&quot;&gt;&lt;strong&gt;codecs&lt;/strong&gt;&lt;/a&gt; and the associated encoding and decoding library the codec uses. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;MP4/H.264&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i myvideo.mp4 -c:v libx264 -c:a copy myvideo.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Audio for an MP4&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i myvideo.mp4 -c:v copy -c:a aac myvideo.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;WebM/VP9&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i myvideo.webm -v:c libvpx-vp9 -v:a copy myvideo.webm&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Audio for a WebM&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i myvideo.webm -v:c copy -v:a libvorbis myvideo.webm&lt;br /&gt;ffmpeg -i myvideo.webm -v:c copy -v:a libopus myvideo.webm&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;video-on-demand-and-live-streaming&quot;&gt;Video-on-demand and live-streaming &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-conversion/#video-on-demand-and-live-streaming&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are two types of streaming protocols we are going to demonstrate in this
article. The first is Dynamic Adaptive Streaming over HTTP (DASH), which is an
adaptive bitrate streaming technique and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/DASH_Adaptive_Streaming_for_HTML_5_Video&quot; rel=&quot;noopener&quot;&gt;web-standards-based&lt;/a&gt; method of
presenting video-on-demand. The second is HTTP Live Streaming (HLS), which is
&lt;a href=&quot;https://developer.apple.com/streaming/&quot; rel=&quot;noopener&quot;&gt;Apple&#39;s standard&lt;/a&gt; for live-streaming and video-on-demand for the web.&lt;/p&gt;
&lt;h3 id=&quot;dashmpd&quot;&gt;DASH/MPD &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-conversion/#dashmpd&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This example generates the Media Presentation Description (MPD) output file
from the audio and video streams.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;packager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myvideo.mp4,stream&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;audio,output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myvideo_audio.mp4 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myvideo.mp4,stream&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;video,output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myvideo_video.mp4 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --mpd_output myvideo_vod.mpd&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;hls&quot;&gt;HLS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-conversion/#hls&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These examples generate an &lt;code&gt;M3U8&lt;/code&gt; output file from the audio and video streams,
which is a UTF-8 encoded multimedia playlist.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i myvideo.mp4 -c:a copy -b:v 8M -c:v copy -f hls &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  -hls_time &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; -hls_list_size &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; myvideo.m3u8&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;OR:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;packager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;input=myvideo.mp4,stream=video,segment_template=output$Number$.ts,playlist_name=video_playlist.m3u8&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;input=myvideo.mp4,stream=audio,segment_template=output_audio$Number$.ts,playlist_name=audio_playlist.m3u8,hls_group_id=audio,hls_name=ENGLISH&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;  --hls_master_playlist_output&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;master_playlist.m3u8&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Now that we hopefully have a good grasp on how to convert files, we can build on
what we&#39;ve learned in this article and go learn about
&lt;a href=&quot;https://web.dev/media-encryption/&quot;&gt;Media encryption&lt;/a&gt; next.&lt;/p&gt;
</content>
    <author>
      <name>Joe Medley</name>
    </author><author>
      <name>Derek Herman</name>
    </author>
  </entry>
  
  <entry>
    <title>Bitrate</title>
    <link href="https://web.dev/bitrate/"/>
    <updated>2017-06-30T00:00:00Z</updated>
    <id>https://web.dev/bitrate/</id>
    <content type="html" mode="escaped">&lt;p&gt;In the previous &lt;a href=&quot;https://web.dev/containers-and-codecs/&quot;&gt;Containers and codecs&lt;/a&gt; article, you
learned how to change the container (extension) and codec of a media file. In
this article, we&#39;ll show you how to change bitrate before explaining
&lt;a href=&quot;https://web.dev/resolution/&quot;&gt;resolution&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Bitrate and resolution correlate to the amount of data in a media file. It
probably goes without saying, but we&#39;re going to say it anyway. You can always
lower bitrate and resolution, but increasing them is a problem. Without special
software and algorithms, quality is going to take a hit.&lt;/p&gt;
&lt;p&gt;So always start your conversion process with the highest quality source file you
can get your hands on. Before doing anything, even before changing the codec or
container, check the file&#39;s
&lt;a href=&quot;https://web.dev/media-conversion/#display-characteristics&quot;&gt;display characteristics&lt;/a&gt; and verify
that your source file has a higher bitrate or resolution than your desired result.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bitrate&lt;/strong&gt; is the maximum number of bits used to encode one second of a media
stream. The more bits used to encode a second of stream, the higher the
fidelity.&lt;/p&gt;
&lt;p&gt;Unsurprisingly, the different bitrates the web can handle are low. The table
below shows you what bitrate you should target for common network conditions. For
the sake of comparison, we&#39;ve thrown in values for Blu-rays and DVDs.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-warn-bg color-state-warn-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block color-state-warn-text&quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Warning sign&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M23 21L12 2 1 21h22zm-12-3v-2h2v2h-2zm0-4h2v-4h-2v4z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; The web numbers are approximations. This chart should &lt;strong&gt;not&lt;/strong&gt; be a substitute for doing your own testing. &lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Delivery method&lt;/th&gt;
&lt;th&gt;Bitrate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Blu-ray&lt;/td&gt;
&lt;td&gt;20Mbs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DVD&lt;/td&gt;
&lt;td&gt;6 Mbs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Desktop web&lt;/td&gt;
&lt;td&gt;2 Mbs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4G mobile&lt;/td&gt;
&lt;td&gt;0.7 Mbs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3G mobile&lt;/td&gt;
&lt;td&gt;0.35 Mbs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2G mobile&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Depends on network type.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;EDGE: 0.4 Mbs&lt;br /&gt;GPRS: 0.04Mbs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;&lt;p&gt;Which value should I use for video on my web pages? The short answer is at
least: desktop, 4G, and 3G. If you&#39;re serving video in one of the markets
referred to as &amp;quot;the next billion users&amp;quot;, say India, for example, you&#39;ll want to
include 2G as well. For demonstration purposes, we&#39;re going to target 3G.&lt;/p&gt;
&lt;p&gt;Using FFmpeg you set the bitrate with the (surprise!) bitrate (&lt;code&gt;-b&lt;/code&gt;) flag.&lt;/p&gt;
&lt;p&gt;If you don&#39;t have FFmpeg installed read
&lt;a href=&quot;https://web.dev/media-application-basics/#installing-applications-with-docker&quot;&gt;Media application basics&lt;/a&gt;
to get it set up with Docker.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;MP4&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/media &lt;span class=&quot;token comment&quot;&gt;# ffmpeg -i glocken.mov -b:v 350k -b:a 64k glocken_3g.mp4&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;WebM&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/media &lt;span class=&quot;token comment&quot;&gt;# ffmpeg -i glocken.mov -b:v 350k -b:a 64k glocken_3g.webm&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Notice that there are two bitrate flags, &lt;code&gt;-b:a&lt;/code&gt; and &lt;code&gt;-b:v&lt;/code&gt;. One is for the audio
stream, and the other is for the video stream.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/media &lt;span class=&quot;token comment&quot;&gt;# ls -l&lt;/span&gt;&lt;br /&gt;-rw-r--r-- &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root root  &lt;span class=&quot;token number&quot;&gt;12080306&lt;/span&gt; Mar  &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;:16 glocken.mov&lt;br /&gt;-rwx------ &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root root    &lt;span class=&quot;token number&quot;&gt;531117&lt;/span&gt; Mar  &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;:42 glocken_3g.mp4&lt;br /&gt;-rwx------ &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root root    &lt;span class=&quot;token number&quot;&gt;706119&lt;/span&gt; Mar  &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;:46 glocken_3g.webm&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Now that your files are prepared, it&#39;s time to &lt;a href=&quot;https://web.dev/resolution&quot;&gt;adjust their resolutions&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Joe Medley</name>
    </author><author>
      <name>Derek Herman</name>
    </author>
  </entry>
  
  <entry>
    <title>Containers and codecs</title>
    <link href="https://web.dev/containers-and-codecs/"/>
    <updated>2017-06-30T00:00:00Z</updated>
    <id>https://web.dev/containers-and-codecs/</id>
    <content type="html" mode="escaped">&lt;p&gt;To support multiple browsers, you&#39;ll need to use FFmpeg to convert your &lt;code&gt;.mov&lt;/code&gt;
file to two different containers: an MP4 container and a WebM container. In
actual practice, you would likely specify a codec at the same time. For now,
we&#39;re letting FFmpeg use its defaults.&lt;/p&gt;
&lt;p&gt;If these concepts are new to you, you should read
&lt;a href=&quot;https://web.dev/media-file-basics/#containers-and-codecs-and-streams&quot;&gt;Media file basics&lt;/a&gt;
before going further. Additionally, if you don&#39;t have FFmpeg installed read
&lt;a href=&quot;https://web.dev/media-application-basics/#installing-applications-with-docker&quot;&gt;Media application basics&lt;/a&gt;
to get it set up with Docker.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;We are using the suggested Docker install and
the &lt;a href=&quot;https://storage.googleapis.com/web-dev-assets/prepare-media/glocken.mov&quot; rel=&quot;noopener&quot;&gt;glocken.mov&lt;/a&gt; file from &lt;a href=&quot;https://web.dev/prepare-media/&quot;&gt;Prepare media files for the web&lt;/a&gt;
added inside the &lt;code&gt;media&lt;/code&gt; directory. We used FFmpeg version 4.3.2 for all the
commands in this section.&lt;/strong&gt;&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; If the commands don&#39;t work for your version of FFmpeg, consult the FFmpeg documentation. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;containers&quot;&gt;Containers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/containers-and-codecs/#containers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First, we need to create our two containers from the &lt;code&gt;.mov&lt;/code&gt; file with the &lt;code&gt;.mp4&lt;/code&gt;
and &lt;code&gt;.webm&lt;/code&gt; file extensions with both an audio and video stream inside the file.
Review &lt;a href=&quot;https://web.dev/media-file-basics/#containers-and-codecs-and-streams&quot;&gt;Media file basics&lt;/a&gt;
for more about containers and streams if you don&#39;t know the different between
them.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;MP4&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/media &lt;span class=&quot;token comment&quot;&gt;# ffmpeg -i glocken.mov glocken.mp4&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;WebM&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/media &lt;span class=&quot;token comment&quot;&gt;# ffmpeg -i glocken.mov glocken.webm&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;WebM takes longer to create than MP4. This isn&#39;t surprising when you look at
the results. While MP4 compresses to about &lt;code&gt;83%&lt;/code&gt; of the original file&#39;s
size, WebM is down to &lt;code&gt;78%&lt;/code&gt; of the original&#39;s size, but can be much smaller.
Your results will vary. It&#39;s important to call out that FFmpeg &lt;code&gt;4.2.2&lt;/code&gt; set the
default video bitrate to &lt;code&gt;200k&lt;/code&gt; and in &lt;code&gt;4.3.2&lt;/code&gt; it does not set a default bitrate.
So the video is no longer  &lt;strong&gt;a mere&lt;code&gt;4%&lt;/code&gt; of the original&lt;/strong&gt;. You can see this for yourself
using the &lt;code&gt;ls -a&lt;/code&gt; &lt;a href=&quot;https://en.wikipedia.org/wiki/Ls&quot; rel=&quot;noopener&quot;&gt;bash command&lt;/a&gt; in the folder where your media files are located.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/media &lt;span class=&quot;token comment&quot;&gt;# ls -l&lt;/span&gt;&lt;br /&gt;-rw-r--r-- &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root  root  &lt;span class=&quot;token number&quot;&gt;12080306&lt;/span&gt; Mar &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;:16 glocken.mov&lt;br /&gt;-rwx------ &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root  root  &lt;span class=&quot;token number&quot;&gt;10106446&lt;/span&gt; Mar &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;:33 glocken.mp4&lt;br /&gt;-rwx------ &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root  root   &lt;span class=&quot;token number&quot;&gt;9503301&lt;/span&gt; Mar &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;18&lt;/span&gt;:30 glocken.webm&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;To get a tiny file, you would do this instead:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/media &lt;span class=&quot;token comment&quot;&gt;# ffmpeg -i glocken.mov -b:v 200k glocken.webm&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;br /&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;fps&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3.6&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;Lsize&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;     483kB &lt;span class=&quot;token assign-left variable&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;00:00:10.01 &lt;span class=&quot;token assign-left variable&quot;&gt;bitrate&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;395&lt;/span&gt;.0kbits/s &lt;span class=&quot;token assign-left variable&quot;&gt;speed&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;.121x&lt;br /&gt;video:359kB audio:117kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: &lt;span class=&quot;token number&quot;&gt;1.356068&lt;/span&gt;%&lt;br /&gt;/media &lt;span class=&quot;token comment&quot;&gt;# ls -l&lt;/span&gt;&lt;br /&gt;-rw-r--r-- &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root  root  &lt;span class=&quot;token number&quot;&gt;12080306&lt;/span&gt; Mar &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;:16 glocken.mov&lt;br /&gt;-rwx------ &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root  root  &lt;span class=&quot;token number&quot;&gt;10106446&lt;/span&gt; Mar &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;:33 glocken.mp4&lt;br /&gt;-rwx------ &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root  root    &lt;span class=&quot;token number&quot;&gt;494497&lt;/span&gt; Mar &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;18&lt;/span&gt;:45 glocken.webm&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;check-your-work&quot;&gt;Check your work &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/containers-and-codecs/#check-your-work&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To verify your results, use FFmpeg and Shaka Packager as already shown in
&lt;a href=&quot;https://web.dev/media-application-basics&quot;&gt;Media Application basics&lt;/a&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/media &lt;span class=&quot;token comment&quot;&gt;# packager input=glocken.mp4 --dump_stream_info&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/media &lt;span class=&quot;token comment&quot;&gt;# ffmpeg -i glocken.mp4&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;codecs&quot;&gt;Codecs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/containers-and-codecs/#codecs&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Next, the codec. As stated in &lt;a href=&quot;https://web.dev/media-file-basics&quot;&gt;Media file basics&lt;/a&gt;, a codec
is &lt;em&gt;not&lt;/em&gt; the same thing as a container (file type). Two files of the same container
type could hold data compressed using different codecs. The WebM format for example
allows audio to be encoded using either &lt;a href=&quot;https://en.wikipedia.org/wiki/Vorbis&quot; rel=&quot;noopener&quot;&gt;Vorbis&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Opus_(audio_format)&quot; rel=&quot;noopener&quot;&gt;Opus&lt;/a&gt;. To change the codec we
use FFmpeg. For example, this command outputs an &lt;code&gt;.mkv&lt;/code&gt; file with a &lt;code&gt;vorbis&lt;/code&gt; audio
codec and an &lt;code&gt;av1&lt;/code&gt; video codec.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/media &lt;span class=&quot;token comment&quot;&gt;# ffmpeg -i glocken.mov -c:a vorbis -c:v av1 glocken.mkv&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In this example, the &lt;code&gt;-c:a&lt;/code&gt; flag and the &lt;code&gt;-c:v&lt;/code&gt; are for specifying the audio and
video codecs respectively.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://web.dev/media-conversion/#change-characteristics&quot;&gt;Media conversion&lt;/a&gt; page lists
commands needed to convert codecs. The tables below summarize the libraries used
in FFmpeg to perform the codec conversions for WebM and MP4 files. These are the
formats recommended for DASH and HLS respectively.&lt;/p&gt;
&lt;h3 id=&quot;video&quot;&gt;Video &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/containers-and-codecs/#video&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Codec&lt;/th&gt;
&lt;th&gt;Extension&lt;/th&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;av1&lt;/td&gt;
&lt;td&gt;WebM, mkv&lt;/td&gt;
&lt;td&gt;libaom-av1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;h264&lt;/td&gt;
&lt;td&gt;MP4&lt;/td&gt;
&lt;td&gt;libx264&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vp9&lt;/td&gt;
&lt;td&gt;WebM&lt;/td&gt;
&lt;td&gt;libvpx-vp9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;&lt;h3 id=&quot;audio&quot;&gt;Audio &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/containers-and-codecs/#audio&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Codec&lt;/th&gt;
&lt;th&gt;Extension&lt;/th&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;aac&lt;/td&gt;
&lt;td&gt;MP4&lt;/td&gt;
&lt;td&gt;aac&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;opus&lt;/td&gt;
&lt;td&gt;WebM&lt;/td&gt;
&lt;td&gt;libopus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vorbis&lt;/td&gt;
&lt;td&gt;WebM&lt;/td&gt;
&lt;td&gt;libvorbis&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;&lt;p&gt;Next, we&#39;ll show you how to change the &lt;a href=&quot;https://web.dev/bitrate/&quot;&gt;bitrate&lt;/a&gt; of your newly created files.&lt;/p&gt;
</content>
    <author>
      <name>Joe Medley</name>
    </author><author>
      <name>Derek Herman</name>
    </author>
  </entry>
  
  <entry>
    <title>What is a media experience?</title>
    <link href="https://web.dev/media-experience/"/>
    <updated>2017-06-30T00:00:00Z</updated>
    <id>https://web.dev/media-experience/</id>
    <content type="html" mode="escaped">&lt;p&gt;Users like media, especially videos; they can be fun and informative. On mobile
devices, videos can be an easier way to consume information than text. For a
good user experience, videos should not need more than the available bandwidth.
Users should be able to use them no matter what device they&#39;re viewing them
with. Users should never need to wait for media to download. Nobody likes it when
they press play and nothing happens.&lt;/p&gt;
&lt;p&gt;You&#39;ve no doubt consumed video on your own device, which means that nothing in
that last paragraph surprises you. Now you need to learn how to put a video or
other media file on your own website. The technical requirements of adding media
should be in service to the user experience.&lt;/p&gt;
&lt;h2 id=&quot;the-technical-requirements&quot;&gt;The technical requirements &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-experience/#the-technical-requirements&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Versions of a media file are in common web-friendly formats containing both audio
and video streams.&lt;/li&gt;
&lt;li&gt;The resolution is appropriate for your users&#39; devices.&lt;/li&gt;
&lt;li&gt;The bitrate doesn&#39;t overload your users&#39; network bandwidth.&lt;/li&gt;
&lt;li&gt;The result is viewable on all major browsers using appropriate technologies.&lt;/li&gt;
&lt;li&gt;The file is encrypted (Optional).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href=&quot;https://web.dev/media/&quot;&gt;media&lt;/a&gt; section of this website will help you achieve these technical
requirements. Don&#39;t worry if these concepts are still a bit abstract. We&#39;ll
explain them progressively throughout all the articles. In the first section you
will learn about the basic concepts of media, then how to add media to the web in
the second section, and in the final section the practical applications, with some
advanced techniques, of using media on the web.&lt;/p&gt;
&lt;h2 id=&quot;displaying-video-on-the-web&quot;&gt;Displaying video on the web &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-experience/#displaying-video-on-the-web&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are four approaches you can take when displaying video on a site.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Upload your videos to a media hosting provider such as &lt;a href=&quot;https://www.youtube.com/&quot; rel=&quot;noopener&quot;&gt;YouTube&lt;/a&gt; or &lt;a href=&quot;https://vimeo.com/&quot; rel=&quot;noopener&quot;&gt;Vimeo&lt;/a&gt;.
These options require you to embed their players within your site.&lt;/li&gt;
&lt;li&gt;Basic self-hosted embedding using the HTML &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; elements.&lt;/li&gt;
&lt;li&gt;More full-featured embedding using a video library such as &lt;a href=&quot;https://github.com/google/shaka-player&quot; rel=&quot;noopener&quot;&gt;Shaka Player&lt;/a&gt;,
&lt;a href=&quot;https://developer.jwplayer.com/&quot; rel=&quot;noopener&quot;&gt;JW Player&lt;/a&gt;, or &lt;a href=&quot;http://videojs.com/&quot; rel=&quot;noopener&quot;&gt;Video.js&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Building your own media server and streaming application.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This site mainly covers the basics of embedding media. However, we do cover
some more advanced topics to get you started on the path towards building your
own media streaming application. The effort to do this is not trivial, so we&#39;ve
built a &lt;a href=&quot;https://github.com/xwp/web-dev-media&quot; rel=&quot;noopener&quot;&gt;Media PWA&lt;/a&gt; with offline support to use as a reference, which
should both show you ways this can be accomplished and just how much
effort it requires. The application is by no means a production ready offering
or competitor to services like YouTube or Vimeo, but it will provide you with a
starting point to learn something challenging and new.&lt;/p&gt;
&lt;p&gt;Frankly, building a competitor to hosted media services would require a team
of expert engineers and thousands of human-hours of work. Unless your goal is
to enter that market as a competitor, you&#39;re better off using one of the other
methods. It&#39;s good to understand the technology and while you may not roll out
your own application or video player, there is utility in exploring and
experimenting on the cutting edge of browser support, or at the very least
using one of the existing video libraries.&lt;/p&gt;
&lt;h2 id=&quot;what-were-going-to-learn&quot;&gt;What we&#39;re going to learn &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-experience/#what-were-going-to-learn&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://web.dev/media/&quot;&gt;media&lt;/a&gt; collection has three parts. In this first section, we&#39;ll
provide concepts and prerequisite information to adding media to your site.
This includes explaining how media files are put together, basics about the
applications you&#39;ll need to prepare your files for the web, and streaming
concepts. The second section explains how to prepare your files and convert
them to various formats and optionally add encryption. In the last section,
we&#39;ll show you how to embed a media file in a web page, discuss autoplay best
practices, using media frameworks, taking videos offline, and making your videos
accessible.&lt;/p&gt;
&lt;p&gt;There&#39;s a lot of ground to cover, so let&#39;s get started with &lt;a href=&quot;https://web.dev/media-file-basics/&quot;&gt;Media file
basics&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Joe Medley</name>
    </author><author>
      <name>Derek Herman</name>
    </author>
  </entry>
  
  <entry>
    <title>Prepare media files for the web</title>
    <link href="https://web.dev/prepare-media/"/>
    <updated>2017-06-30T00:00:00Z</updated>
    <id>https://web.dev/prepare-media/</id>
    <content type="html" mode="escaped">&lt;p&gt;Now that we&#39;ve introduced you to the &lt;a href=&quot;https://web.dev/media-application-basics/&quot;&gt;applications&lt;/a&gt;
we use when manipulating media files, over the next few pages, we&#39;re going to
take a raw video file from a camera and transform it into a resource that you
can embed in a web page. We&#39;re going to show you how to format your
video for mobile web playback, and how to create multiple files to cover a
range of browsers. Specifically, we&#39;ll create a WebM file for use on Chrome and
an MP4 file for use on other browsers.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; The result of this section will be media resources with the following characteristics:  * Versions of a media file in common web-friendly formats containing both audio and video streams. * A &lt;a href=&quot;https://web.dev/resolution/&quot;&gt;&lt;strong&gt;Resolution&lt;/strong&gt;&lt;/a&gt; appropriate for your users&#39; devices. * A &lt;a href=&quot;https://web.dev/bitrate/&quot;&gt;&lt;strong&gt;Bitrate&lt;/strong&gt;&lt;/a&gt; that doesn&#39;t overload your users&#39; network bandwidth. * A result that&#39;s viewable on all major browsers using &lt;strong&gt;appropriate technologies&lt;/strong&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Plus we will dive deeper into common commands used for
&lt;a href=&quot;https://web.dev/media-conversion/&quot;&gt;Media conversion&lt;/a&gt; and &lt;a href=&quot;https://web.dev/media-encryption/&quot;&gt;Media encryption&lt;/a&gt;
that will serve as a good reference for later.&lt;/p&gt;
&lt;p&gt;By &amp;quot;&lt;strong&gt;appropriate technologies&lt;/strong&gt;&amp;quot; we mean Dynamic Adaptive Streaming over HTTP
(&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/DASH_Adaptive_Streaming_for_HTML_5_Video&quot; rel=&quot;noopener&quot;&gt;DASH&lt;/a&gt;) or HTTP Live Streaming (&lt;a href=&quot;https://developer.apple.com/documentation/http_live_streaming&quot; rel=&quot;noopener&quot;&gt;HLS&lt;/a&gt;), which are the two primary means of
providing video in HTML on the major browsers. By the end of this section,
you&#39;ll be able to create media files that are ready for use in DASH and HLS.&lt;/p&gt;
&lt;p&gt;If you want to play along at home, you&#39;ll need a raw video file off a camera,
preferably one that contains both audio and video. If you don&#39;t have one handy,
then here&#39;s ten seconds of an &lt;code&gt;.mov&lt;/code&gt; file named &lt;a href=&quot;https://storage.googleapis.com/web-dev-assets/prepare-media/glocken.mov&quot; rel=&quot;noopener&quot;&gt;glocken.mov&lt;/a&gt; that was taken of
the &lt;a href=&quot;https://en.wikipedia.org/wiki/Rathaus-Glockenspiel&quot; rel=&quot;noopener&quot;&gt;Rathaus-Glockenspiel&lt;/a&gt; in Munich&#39;s MarienPlatz.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The selection of the file formats, bitrate, and resolution are not arbitrary. We&#39;ve selected these values for speedy web playback on mobile devices. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Next, let&#39;s get started with &lt;a href=&quot;https://web.dev/containers-and-codecs/&quot;&gt;Containers and codecs&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Joe Medley</name>
    </author><author>
      <name>Derek Herman</name>
    </author>
  </entry>
  
  <entry>
    <title>Resolution</title>
    <link href="https://web.dev/resolution/"/>
    <updated>2017-06-30T00:00:00Z</updated>
    <id>https://web.dev/resolution/</id>
    <content type="html" mode="escaped">&lt;p&gt;In the previous articles you learned how to change the
&lt;a href=&quot;https://web.dev/containers-and-codecs/&quot;&gt;containers, codecs&lt;/a&gt;, and &lt;a href=&quot;https://web.dev/bitrate/&quot;&gt;bitrate&lt;/a&gt; of the
&lt;a href=&quot;https://storage.googleapis.com/web-dev-assets/prepare-media/glocken.mov&quot; rel=&quot;noopener&quot;&gt;glocken.mov&lt;/a&gt; media file. This article focuses on changing the resolution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Resolution&lt;/strong&gt; is the amount of information in a single frame of video, given as
the number of logical pixels in each dimension. For example, a resolution of
1920 by 1080 works out to 1080 stacked horizontal lines, each of which is one
logical pixel high and 1920 logical pixels wide. This resolution is frequently
abbreviated 1080p because technically the width can vary. The dimensions 1080 by
1920 produce an &lt;a href=&quot;https://en.wikipedia.org/wiki/Aspect_ratio_(image)&quot; rel=&quot;noopener&quot;&gt;aspect ratio&lt;/a&gt; of 16:9, which is the ratio of movie screens and
modern television sets. By the way this is the resolution defined as &lt;a href=&quot;https://en.wikipedia.org/wiki/1080p&quot; rel=&quot;noopener&quot;&gt;full HD&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://support.google.com/youtube/answer/6375112&quot; rel=&quot;noopener&quot;&gt;YouTube recommends&lt;/a&gt; the following resolutions for video uploads, all in the 16:9
aspect ratio. There&#39;s nothing specific to YouTube about this list. It&#39;s just a
list of common 16:9 video resolutions.&lt;/p&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Abbreviation&lt;/th&gt;
&lt;th&gt;Dimensions&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2160p&lt;/td&gt;
&lt;td&gt;3840 x 2160&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1440p&lt;/td&gt;
&lt;td&gt;2560 x 1440&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1080p&lt;/td&gt;
&lt;td&gt;1920 x 1080&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;720p&lt;/td&gt;
&lt;td&gt;1280 x 720&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;480p&lt;/td&gt;
&lt;td&gt;854 x 480&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;360p&lt;/td&gt;
&lt;td&gt;640 x 360&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;240p&lt;/td&gt;
&lt;td&gt;426 x 240&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;&lt;p&gt;Which one should you use? That depends on your application. For simple embedding
you may decide to chose only a single resolution. If you&#39;re preparing files for
DASH or HLS, you may chose one, several, or all. Fortunately, this is one of the
simplest transformations you&#39;ll make with FFmpeg.&lt;/p&gt;
&lt;p&gt;If you don&#39;t have FFmpeg installed read
&lt;a href=&quot;https://web.dev/media-application-basics/#installing-applications-with-docker&quot;&gt;Media application basics&lt;/a&gt;
to get it set up with Docker.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;MP4&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/media &lt;span class=&quot;token comment&quot;&gt;# ffmpeg -i glocken.mov -b:v 350k -b:a 64k -s 1280x720 glocken_3g_720p.mp4&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;WebM&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/media &lt;span class=&quot;token comment&quot;&gt;# ffmpeg -i glocken.mov -b:v 350k -b:a 64k -s 1280x720 glocken_3g_720p.webm&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The following files should now exist:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/media &lt;span class=&quot;token comment&quot;&gt;# ls -l&lt;/span&gt;&lt;br /&gt;-rw-r--r-- &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root root  &lt;span class=&quot;token number&quot;&gt;12080306&lt;/span&gt; Mar  &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;:16 glocken.mov&lt;br /&gt;-rwx------ &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root root    &lt;span class=&quot;token number&quot;&gt;531117&lt;/span&gt; Mar  &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;:42 glocken_3g.mp4&lt;br /&gt;-rwx------ &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root root    &lt;span class=&quot;token number&quot;&gt;706119&lt;/span&gt; Mar  &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;:46 glocken_3g.webm&lt;br /&gt;-rwx------ &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root root    &lt;span class=&quot;token number&quot;&gt;539414&lt;/span&gt; Mar  &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;:15 glocken_3g_720p.mp4&lt;br /&gt;-rwx------ &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root root    &lt;span class=&quot;token number&quot;&gt;735930&lt;/span&gt; Mar  &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;:19 glocken_3g_720p.webm&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;It&#39;s worth reiterating that you should start from the highest resolution and
bitrate file you have available. If you&#39;re upgrading an older site, you&#39;ll want
to find your original camera or other high resolution sources and convert from
that rather than from older web files.&lt;/p&gt;
&lt;p&gt;Now that your files are prepared, you can either &lt;a href=&quot;https://web.dev/add-media/&quot;&gt;add them to a web page&lt;/a&gt;
as they are now or dive a bit deeper and continue to learn more command line options
by reading the &lt;a href=&quot;https://web.dev/media-conversion/&quot;&gt;Media conversion&lt;/a&gt; page, and then close out the
section with &lt;a href=&quot;https://web.dev/media-encryption/&quot;&gt;Media encryption&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Joe Medley</name>
    </author><author>
      <name>Derek Herman</name>
    </author>
  </entry>
  
  <entry>
    <title>Media application basics</title>
    <link href="https://web.dev/media-application-basics/"/>
    <updated>2017-06-09T00:00:00Z</updated>
    <id>https://web.dev/media-application-basics/</id>
    <content type="html" mode="escaped">&lt;p&gt;Working with media often requires changing the characteristics of media files,
such as bitrate or resolution. Finding a straightforward way to get started can
be pretty intimidating. On this page, you will learn about the tools used and how
to install them quickly.&lt;/p&gt;
&lt;p&gt;First, we describe the basic usage for two common command-line media utilities:
&lt;a href=&quot;https://github.com/google/shaka-packager&quot; rel=&quot;noopener&quot;&gt;Shaka Packager&lt;/a&gt; and &lt;a href=&quot;https://ffmpeg.org/download.html&quot; rel=&quot;noopener&quot;&gt;FFmpeg&lt;/a&gt; and then we help you quickly get the tools installed.
Why cover two applications? While both are powerful and useful by themselves,
neither does everything needed to prepare media for the web. We also created the
&lt;a href=&quot;https://web.dev/media-conversion/&quot;&gt;Media conversion&lt;/a&gt; and &lt;a href=&quot;https://web.dev/media-encryption/&quot;&gt;Media encryption&lt;/a&gt;
pages which show many more common operations with these two applications.&lt;/p&gt;
&lt;p&gt;These applications aren&#39;t the only options available for file manipulation tasks,
but they are two of the most common and powerful. Other options include the GUI
applications &lt;a href=&quot;http://www.mirovideoconverter.com/&quot; rel=&quot;noopener&quot;&gt;Miro&lt;/a&gt;, &lt;a href=&quot;https://handbrake.fr/&quot; rel=&quot;noopener&quot;&gt;HandBrake&lt;/a&gt;, and &lt;a href=&quot;https://www.videolan.org/&quot; rel=&quot;noopener&quot;&gt;VLC&lt;/a&gt;. There are also encoding/transcoding
services such as &lt;a href=&quot;https://en.wikipedia.org/wiki/Zencoder&quot; rel=&quot;noopener&quot;&gt;Zencoder&lt;/a&gt;, &lt;a href=&quot;https://aws.amazon.com/elastictranscoder&quot; rel=&quot;noopener&quot;&gt;Amazon Elastic Encoder&lt;/a&gt;, and &lt;a href=&quot;https://cloud.google.com/transcoder/docs&quot; rel=&quot;noopener&quot;&gt;Google Transcoder API&lt;/a&gt;
(currently in beta).&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; You&#39;ll notice in what follows that the word &#39;resolution&#39; doesn&#39;t appear. What the two applications output are just the dimensions; the numbers themselves. That&#39;s because resolution is just an informal shorthand for the dimensions of a video. In every case that follows, We talk about specific numbers. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;shaka-packager&quot;&gt;Shaka Packager &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-application-basics/#shaka-packager&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/google/shaka-packager&quot; rel=&quot;noopener&quot;&gt;Shaka Packager&lt;/a&gt; is a free media packaging SDK. If you were using a media player
on your site, Shaka Packager is what you would use to prepare the files. It
supports conversion for the two most common video streaming protocols: Dynamic
Adaptive Streaming over HTTP (&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/DASH_Adaptive_Streaming_for_HTML_5_Video&quot; rel=&quot;noopener&quot;&gt;DASH&lt;/a&gt;) or HTTP Live Streaming (&lt;a href=&quot;https://developer.apple.com/documentation/http_live_streaming&quot; rel=&quot;noopener&quot;&gt;HLS&lt;/a&gt;). Shaka
Packager supports key security features: common encryption and Widevine digital
rights management (DRM). It can also handle live-streaming, and video-on-demand.&lt;/p&gt;
&lt;p&gt;Despite what it says on the package, this utility is for more than C++
developers. You can use it as both a library for building media software and as
a command-line utility for preparing media files for web playback. It&#39;s the
latter capacity that&#39;s useful for us here. In fact, for web media creators,
Shaka Packager is the only way to do some tasks without spending money on
expensive commercial applications.&lt;/p&gt;
&lt;p&gt;Here&#39;s the basic pattern for a Shaka Packager command:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;packager stream_descriptor &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;stream_descriptor-2 &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;stream_descriptor-n&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;flags&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This isn&#39;t quite what you get if you type &lt;code&gt;packager -help&lt;/code&gt;. This example is
easier to reason about, and this reflects the examples in the
&lt;a href=&quot;https://google.github.io/shaka-packager/html/&quot; rel=&quot;noopener&quot;&gt;Shaka Packager documentation&lt;/a&gt;. Note that there are multiple &lt;code&gt;stream_descriptor&lt;/code&gt;
items in the pattern. Though we don&#39;t show it, you could manipulate the video
and audio streams of a file separately in a single command.&lt;/p&gt;
&lt;p&gt;Compare this basic pattern with a simple use that displays file characteristics.
In the example, We&#39;ve lined up equivalent parts.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;packager stream_descriptor &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;stream_descriptor-n&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;flags&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;packager &lt;span class=&quot;token assign-left variable&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;glocken.mp4                       --dump_stream_info&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The command outputs this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;File &lt;span class=&quot;token string&quot;&gt;&quot;glocken.mp4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt;&lt;br /&gt;Found &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; stream&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;.&lt;br /&gt;Stream &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; type: Video&lt;br /&gt; codec_string: avc1.640028&lt;br /&gt; time_scale: &lt;span class=&quot;token number&quot;&gt;30000&lt;/span&gt;&lt;br /&gt; duration: &lt;span class=&quot;token number&quot;&gt;300300&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10.0&lt;/span&gt; seconds&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt; is_encrypted: &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;br /&gt; codec: H264&lt;br /&gt; width: &lt;span class=&quot;token number&quot;&gt;1920&lt;/span&gt;&lt;br /&gt; height: &lt;span class=&quot;token number&quot;&gt;1080&lt;/span&gt;&lt;br /&gt; pixel_aspect_ratio: &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:1&lt;br /&gt; trick_play_factor: &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;br /&gt; nalu_length_size: &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Stream &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; type: Audio&lt;br /&gt; codec_string: mp4a.40.2&lt;br /&gt; time_scale: &lt;span class=&quot;token number&quot;&gt;48000&lt;/span&gt;&lt;br /&gt; duration: &lt;span class=&quot;token number&quot;&gt;481280&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10.0&lt;/span&gt; seconds&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt; is_encrypted: &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;br /&gt; codec: AAC&lt;br /&gt; sample_bits: &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;br /&gt; num_channels: &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;br /&gt; sampling_frequency: &lt;span class=&quot;token number&quot;&gt;48000&lt;/span&gt;&lt;br /&gt; language: eng&lt;br /&gt; seek_preroll_ns: &lt;span class=&quot;token number&quot;&gt;20833&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Look for the characteristics discussed in &lt;a href=&quot;https://web.dev/media-file-basics/&quot;&gt;Media file basics&lt;/a&gt;
and notice a few things. The height and width are correct for full HD, and the
audio and video codecs are among the preferred codecs for their container types,
AAC for audio and H264 for video. Notice also that streams are identified with
numbers. These are useful for operations that manipulate the audio and video
separately.&lt;/p&gt;
&lt;p&gt;Notice that the output above doesn&#39;t show the bitrate. Despite what&#39;s missing,
this output is easier to read, so we use it whenever we can. When we need
information that Shaka Packager can&#39;t get, such as the bitrate, we use FFmpeg.&lt;/p&gt;
&lt;h2 id=&quot;ffmpeg&quot;&gt;FFmpeg &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-application-basics/#ffmpeg&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://ffmpeg.org/download.html&quot; rel=&quot;noopener&quot;&gt;FFmpeg&lt;/a&gt; is also a free application for recording, converting, and streaming
media files. Its capabilities aren&#39;t better or worse than Shaka Packager&#39;s.
They&#39;re just different.&lt;/p&gt;
&lt;p&gt;The basic pattern for an FFmpeg command looks like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;GeneralOptions&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;InputFileOptions&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; -i input &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;OutputFileOptions&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; output&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Like Shaka Packager this application can handle multiple streams. Some of its
options are used in multiple locations and manipulate the file output differently
depending on where they are in the command. Be aware of this as you
look at &lt;a href=&quot;https://stackoverflow.com/questions/tagged/ffmpeg&quot; rel=&quot;noopener&quot;&gt;FFmpeg questions on Stack Overflow&lt;/a&gt; and similar sites.&lt;/p&gt;
&lt;p&gt;We&#39;ll again compare the basic pattern to the example for displaying file
characteristics.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;    ffmpeg &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;GeneralOptions&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;InputFileOptions&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; -i input        &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;OutputFileOptions&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; output&lt;br /&gt;&lt;br /&gt;    ffmpeg                                     -i glocken.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In addition to the information we requested, this also prints an error message
as shown in the example below. That&#39;s because this is technically an incorrect
usage of FFmpeg. We use it because it displays information we care about.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;Input &lt;span class=&quot;token comment&quot;&gt;#0, mov,mp4,m4a,3gp,3g2,mj2, from &#39;glocken.mp4&#39;:&lt;/span&gt;&lt;br /&gt;  Metadata:&lt;br /&gt;    major_brand     &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; isom&lt;br /&gt;    minor_version   &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;512&lt;/span&gt;&lt;br /&gt;    compatible_brands: isomiso2avc1mp41&lt;br /&gt;    encoder         &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; Lavf58.17.100&lt;br /&gt;  Duration: 00:01:47.53, start: &lt;span class=&quot;token number&quot;&gt;0.000000&lt;/span&gt;, bitrate: &lt;span class=&quot;token number&quot;&gt;10715&lt;/span&gt; kb/s&lt;br /&gt;    Stream &lt;span class=&quot;token comment&quot;&gt;#0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuvj420p(pc), 1920x1080, 10579 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 59.94 tbc (default)&lt;/span&gt;&lt;br /&gt;    Metadata:&lt;br /&gt;      handler_name    &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; VideoHandler&lt;br /&gt;    Stream &lt;span class=&quot;token comment&quot;&gt;#0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s (default)&lt;/span&gt;&lt;br /&gt;    Metadata:&lt;br /&gt;      handler_name    &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; SoundHandler&lt;br /&gt;At least one output &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt; must be specified&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;installing-applications-with-docker&quot;&gt;Installing applications with Docker &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/media-application-basics/#installing-applications-with-docker&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you plan to follow along and try our commands you could either install the
required tools manually, or take the easy path and use &lt;a href=&quot;https://www.docker.com/whatisdocker&quot; rel=&quot;noopener&quot;&gt;Docker&lt;/a&gt;. We suggest using
Docker, because this is going to save you a lot of time. On top of that we&#39;ve
provided the instructions to get you set up quickly.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Start by creating a new directory somewhere on your computer named &lt;code&gt;media-tools&lt;/code&gt;;
you can use whatever name you like, just note that the following instructions
assume you are using &lt;code&gt;media-tools&lt;/code&gt; as the directory name.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;code&gt;docker&lt;/code&gt; and &lt;code&gt;media&lt;/code&gt; directory inside of &lt;code&gt;media-tools&lt;/code&gt;.
This will keep your &lt;code&gt;media&lt;/code&gt; directory out of the build context. This is important
because &lt;code&gt;media&lt;/code&gt; is where files are stored that we plan to do operations on, and
some of them could be quite large. Putting the &lt;code&gt;Dockerfile&lt;/code&gt; directly in
&lt;code&gt;media-tools&lt;/code&gt; would slow down building the image if you ever rebuild it down the
road—perhaps to change the versions installed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create &lt;code&gt;/media-tools/docker/Dockerfile&lt;/code&gt;, and add the following build instructions:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; google/shaka-packager:release-v2.4.3 &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; packager&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; jrottenberg/ffmpeg:4.3.2-alpine38&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; &lt;span class=&quot;token options&quot;&gt;&lt;span class=&quot;token property&quot;&gt;--from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;packager&lt;/span&gt;&lt;/span&gt; /usr/bin /usr/bin&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ENTRYPOINT&lt;/span&gt;  [&lt;span class=&quot;token string&quot;&gt;&quot;sh&quot;&lt;/span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Build the image:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; build -t media-tools ./docker&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the image as an interactive shell. On Linux:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; run -w /media -v &lt;span class=&quot;token variable&quot;&gt;${&lt;span class=&quot;token environment constant&quot;&gt;PWD&lt;/span&gt;}&lt;/span&gt;/media:/media -it --rm media-tools&lt;br /&gt;/media &lt;span class=&quot;token comment&quot;&gt;#&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;On Windows:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; run -w /media -v %cd%/media:/media -it --rm media-tools&lt;br /&gt;/media &lt;span class=&quot;token comment&quot;&gt;#&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Make sure you run the previous &lt;code&gt;docker&lt;/code&gt; commands from inside the &lt;code&gt;media-tools&lt;/code&gt; directory. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;While running the image you can check versions for both FFmpeg and Shaka Packager
to validate everything was successful by running &lt;code&gt;ffmpeg -version&lt;/code&gt; and
&lt;code&gt;packager --version&lt;/code&gt;. The output should look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/media &lt;span class=&quot;token comment&quot;&gt;# ffmpeg -version&lt;/span&gt;&lt;br /&gt;ffmpeg version &lt;span class=&quot;token number&quot;&gt;4.3&lt;/span&gt;.2 Copyright &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;-2021 the FFmpeg developers&lt;br /&gt;built with gcc &lt;span class=&quot;token number&quot;&gt;6.4&lt;/span&gt;.0 &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Alpine &lt;span class=&quot;token number&quot;&gt;6.4&lt;/span&gt;.0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;configuration: --disable-debug --disable-doc --disable-ffplay --enable-shared --enable-avresample --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-gpl --enable-libass --enable-fontconfig --enable-libfreetype --enable-libvidstab --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libxcb --enable-libx265 --enable-libxvid --enable-libx264 --enable-nonfree --enable-openssl --enable-libfdk_aac --enable-postproc --enable-small --enable-version3 --enable-libbluray --enable-libzmq --extra-libs&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;-ldl --prefix&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/opt/ffmpeg --enable-libopenjpeg --enable-libkvazaar --enable-libaom --extra-libs&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;-lpthread --enable-libsrt --enable-libaribb24 --extra-cflags&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;-I/opt/ffmpeg/include --extra-ldflags&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;-L/opt/ffmpeg/lib&lt;br /&gt;libavutil      &lt;span class=&quot;token number&quot;&gt;56&lt;/span&gt;. &lt;span class=&quot;token number&quot;&gt;51.100&lt;/span&gt; / &lt;span class=&quot;token number&quot;&gt;56&lt;/span&gt;. &lt;span class=&quot;token number&quot;&gt;51.100&lt;/span&gt;&lt;br /&gt;libavcodec     &lt;span class=&quot;token number&quot;&gt;58&lt;/span&gt;. &lt;span class=&quot;token number&quot;&gt;91.100&lt;/span&gt; / &lt;span class=&quot;token number&quot;&gt;58&lt;/span&gt;. &lt;span class=&quot;token number&quot;&gt;91.100&lt;/span&gt;&lt;br /&gt;libavformat    &lt;span class=&quot;token number&quot;&gt;58&lt;/span&gt;. &lt;span class=&quot;token number&quot;&gt;45.100&lt;/span&gt; / &lt;span class=&quot;token number&quot;&gt;58&lt;/span&gt;. &lt;span class=&quot;token number&quot;&gt;45.100&lt;/span&gt;&lt;br /&gt;libavdevice    &lt;span class=&quot;token number&quot;&gt;58&lt;/span&gt;. &lt;span class=&quot;token number&quot;&gt;10.100&lt;/span&gt; / &lt;span class=&quot;token number&quot;&gt;58&lt;/span&gt;. &lt;span class=&quot;token number&quot;&gt;10.100&lt;/span&gt;&lt;br /&gt;libavfilter     &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;. &lt;span class=&quot;token number&quot;&gt;85.100&lt;/span&gt; /  &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;. &lt;span class=&quot;token number&quot;&gt;85.100&lt;/span&gt;&lt;br /&gt;libavresample   &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;.  &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;.  &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; /  &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;.  &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;.  &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;br /&gt;libswscale      &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;.  &lt;span class=&quot;token number&quot;&gt;7.100&lt;/span&gt; /  &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;.  &lt;span class=&quot;token number&quot;&gt;7.100&lt;/span&gt;&lt;br /&gt;libswresample   &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;.  &lt;span class=&quot;token number&quot;&gt;7.100&lt;/span&gt; /  &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;.  &lt;span class=&quot;token number&quot;&gt;7.100&lt;/span&gt;&lt;br /&gt;libpostproc    &lt;span class=&quot;token number&quot;&gt;55&lt;/span&gt;.  &lt;span class=&quot;token number&quot;&gt;7.100&lt;/span&gt; / &lt;span class=&quot;token number&quot;&gt;55&lt;/span&gt;.  &lt;span class=&quot;token number&quot;&gt;7.100&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/media &lt;span class=&quot;token comment&quot;&gt;# packager --version&lt;/span&gt;&lt;br /&gt;packager version v2.4.3-dd9870075f-release&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Now that you&#39;ve tried your hand at using Shaka Packager and FFmpeg, you can continue
learning the basic concepts, next up &lt;a href=&quot;https://web.dev/media-streaming-basics/&quot;&gt;Media streaming basics&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Joe Medley</name>
    </author><author>
      <name>Derek Herman</name>
    </author>
  </entry>
  
  <entry>
    <title>The &lt;video&gt; and &lt;source&gt; tags</title>
    <link href="https://web.dev/video-and-source-tags/"/>
    <updated>2014-02-15T00:00:00Z</updated>
    <id>https://web.dev/video-and-source-tags/</id>
    <content type="html" mode="escaped">&lt;p&gt;You&#39;ve properly &lt;a href=&quot;https://web.dev/prepare-media/&quot;&gt;prepared a video file&lt;/a&gt; for the web. You&#39;ve given it correct
dimensions and the correct resolution. You&#39;ve even created separate WebM and
MP4 files for different browsers.&lt;/p&gt;
&lt;p&gt;For anyone to see your video, you still need to add it to a web page. Doing so
properly requires adding two HTML elements: the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/video&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;&lt;/a&gt; element and the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/source&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt;&lt;/a&gt; element. In addition to basics about these tags, this
article explains attributes you should add to those tags to craft a good
user experience.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; You always have the option of uploading your file to [YouTube] or [Vimeo]. In many cases, this is preferable to the procedure described here. Those services handle formatting and filetype conversion for you, as well as provide the means to embed a video in your web page. If you need to manage this yourself, read on. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;specify-a-single-file&quot;&gt;Specify a single file &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#specify-a-single-file&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Although it&#39;s not recommended, you can use the video element by itself. Always
use the &lt;code&gt;type&lt;/code&gt; attribute as shown below. The browser uses this to determine if
it can play the provided video file. If it can&#39;t, the enclosed text displays.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;video&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;chrome.webm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;video/webm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Your browser cannot play the provided video file.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;specify-multiple-file-formats&quot;&gt;Specify multiple file formats &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#specify-multiple-file-formats&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Recall from &lt;a href=&quot;https://web.dev/media-file-basics/&quot;&gt;Media file basics&lt;/a&gt; that not all browsers support the same video
formats. The &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; element lets you specify multiple formats as a fallback
in case the user&#39;s browser doesn&#39;t support one of them.&lt;/p&gt;
&lt;p&gt;The example below produces the embedded video that is used as an example later
in this article.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;video&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;controls&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://storage.googleapis.com/web-dev-assets/video-and-source-tags/chrome.webm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;video/webm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://storage.googleapis.com/web-dev-assets/video-and-source-tags/chrome.mp4&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;video/mp4&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Your browser cannot play the provided video file.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&quot;https://track-demonstration.glitch.me/&quot; rel=&quot;noopener&quot;&gt;Try it on Glitch&lt;/a&gt; (&lt;a href=&quot;https://glitch.com/edit/#!/track-demonstration&quot; rel=&quot;noopener&quot;&gt;source&lt;/a&gt;)&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Notice in the previous example that the &lt;code&gt;controls&lt;/code&gt; attribute was introduced. This instructs browsers to allow the user to control video playback, including volume, seeking, selecting captions, and pause/resume playback among others. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;You should always add a &lt;code&gt;type&lt;/code&gt; attribute to the &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tags event though it
is optional. This ensures that the browser only downloads the file that it is
capable of playing.&lt;/p&gt;
&lt;p&gt;This approach has several advantages over serving different HTML or server-side
scripting, especially on mobile:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can list formats in order of preference.&lt;/li&gt;
&lt;li&gt;Client-side switching reduces latency; only one request is made to
get content.&lt;/li&gt;
&lt;li&gt;Letting the browser choose a format is simpler, quicker, and potentially
more reliable than using a server-side support database with user-agent
detection.&lt;/li&gt;
&lt;li&gt;Specifying each file source&#39;s type improves network performance; the browser
can select a video source without having to download part of the video to
&amp;quot;sniff&amp;quot; the format.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These issues are especially important in mobile contexts, where bandwidth and
latency are at a premium, and the user&#39;s patience is likely limited. Omitting
the &lt;code&gt;type&lt;/code&gt; attribute can affect performance when there are multiple sources
with unsupported types.&lt;/p&gt;
&lt;p&gt;There are a few ways you can dig into the details. Check out
&lt;a href=&quot;https://www.xiph.org/video/vid1.shtml&quot; rel=&quot;noopener&quot;&gt;A Digital Media Primer for Geeks&lt;/a&gt; to find out more about how video and audio
work on the web. You can also use &lt;a href=&quot;https://developer.chrome.com/docs/devtools/remote-debugging/&quot; rel=&quot;noopener&quot;&gt;remote debugging&lt;/a&gt; in DevTools to compare
network activity &lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/media/video-main.html&quot; rel=&quot;noopener&quot;&gt;with type attributes&lt;/a&gt; and &lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/responsive/notype.html&quot; rel=&quot;noopener&quot;&gt;without type attributes&lt;/a&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Be sure to check the response headers in your browser developer tools to [ensure your server reports the right MIME type]; otherwise video source type checks won&#39;t work. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;specify-start-and-end-times&quot;&gt;Specify start and end times &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#specify-start-and-end-times&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Save bandwidth and make your site feel more responsive: use media fragments to
add start and end times to the video element.&lt;/p&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; width=&quot;100%&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/video-and-source-tags/chrome.webm#t=5,10&quot; type=&quot;video/webm&quot; /&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/video-and-source-tags/chrome.mp4#t=5,10&quot; type=&quot;video/mp4&quot; /&gt;
    &lt;p&gt;This browser does not support the video element.&lt;/p&gt;
  &lt;/video&gt;
&lt;/figure&gt;
&lt;p&gt;To use a media fragment, add &lt;code&gt;#t=[start_time][,end_time]&lt;/code&gt; to the media URL. For
example, to play the video from seconds 5 to 10, specify:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;video/chrome.webm#t=5,10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;video/webm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You can also specify the times in &lt;code&gt;&amp;lt;hours&amp;gt;:&amp;lt;minutes&amp;gt;:&amp;lt;seconds&amp;gt;&lt;/code&gt;. For example,
&lt;code&gt;#t=00:01:05&lt;/code&gt; starts the video at one minute, five seconds. To play only the
first minute of video, specify &lt;code&gt;#t=,00:01:00&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can use this feature to deliver multiple views on the same video—like
cue points in a DVD–without having to encode and serve multiple files.&lt;/p&gt;
&lt;p&gt;For this feature to work, your server must support range requests and that
capability must be enabled. Most servers enable range requests by default.
Because some hosting services turn them off, you should confirm that range
requests are available for using fragments on your site.&lt;/p&gt;
&lt;p&gt;Fortunately, you can do this in your browser developer tools. In Chrome, for
instance, it&#39;s in the &lt;a href=&quot;https://developer.chrome.com/docs/devtools/#network&quot; rel=&quot;noopener&quot;&gt;Network panel&lt;/a&gt;. Look for the &lt;code&gt;Accept-Ranges&lt;/code&gt; header and
verify that it says &lt;code&gt;bytes&lt;/code&gt;. In the image, I&#39;ve drawn a red box around this
header. If you do not see &lt;code&gt;bytes&lt;/code&gt; as the value, you&#39;ll need to contact your
hosting provider.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Chrome DevTools screenshot: Accept-Ranges: bytes.&quot; decoding=&quot;async&quot; height=&quot;480&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/20DlLyicG5PAo6TXBKh3.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Chrome DevTools screenshot: Accept-Ranges: bytes.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;include-a-poster-image&quot;&gt;Include a poster image &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#include-a-poster-image&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Add a poster attribute to the &lt;code&gt;video&lt;/code&gt; element so that viewers have an idea of
the content as soon as the element loads, without needing to download the video
or start playback.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;video&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;poster&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;poster.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  …&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;A poster can also be a fallback if the video &lt;code&gt;src&lt;/code&gt; is broken or if none of the
supplied video formats are supported. The only downside to a poster images is an
additional file request, which consumes some bandwidth and requires rendering.
For more information see &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-optimized-images/&quot; rel=&quot;noopener&quot;&gt;Efficiently encode images&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;switcher&quot;&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;worse&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Don&#39;t&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Without a fallback poster, the video just looks broken.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 360px) 360px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/R8VNeplKwajJhOuVkPDT.png?auto=format&amp;w=720 720w&quot; width=&quot;360&quot; /&gt;
&lt;/figure&gt;
&lt;figcaption class=&quot;compare__caption&quot;&gt;
&lt;p&gt;Without a fallback poster, the video just looks broken.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;better&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Do&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A fallback poster makes it seem as if the first frame has been captured.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 360px) 360px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rNhydHVGeL2P0sQ0je5k.png?auto=format&amp;w=720 720w&quot; width=&quot;360&quot; /&gt;
&lt;/figure&gt;
&lt;figcaption class=&quot;compare__caption&quot;&gt;
&lt;p&gt;A fallback poster makes it seem as if the first frame has been captured.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;h3 id=&quot;ensure-videos-dont-overflow-containers&quot;&gt;Ensure videos don&#39;t overflow containers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#ensure-videos-dont-overflow-containers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When video elements are too big for the viewport, they may overflow their
container, making it impossible for the user to see the content or use the
controls.&lt;/p&gt;
&lt;div class=&quot;switcher&quot;&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;Android Chrome screenshot, portrait: unstyled video element overflows viewport.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 338px) 338px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/cDl2OfCE3hQivhaNvMUh.png?auto=format&amp;w=676 676w&quot; width=&quot;338&quot; /&gt;
    &lt;figcaption&gt;Android Chrome screenshot, portrait: unstyled video element overflows
    viewport.&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;Android Chrome screenshot, landscape: unstyled video element overflows viewport.&quot; decoding=&quot;async&quot; height=&quot;450&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bCiZsNkZNsAhWbOBsLCs.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
    &lt;figcaption&gt;Android Chrome screenshot, landscape: unstyled video element overflows
    viewport.&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;You can control video dimensions using CSS. If CSS does not meet all of your
needs, JavaScript libraries and plugins such as &lt;a href=&quot;http://fitvidsjs.com/&quot; rel=&quot;noopener&quot;&gt;FitVids&lt;/a&gt; (outside the scope
of this article) can help, even for videos from YouTube and other sources.
Unfortunately, these resources can increase your &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/total-byte-weight/&quot; rel=&quot;noopener&quot;&gt;network payload sizes&lt;/a&gt; with
negative consequences for your revenues and your users&#39; wallets.&lt;/p&gt;
&lt;p&gt;For simple uses like the ones I&#39;m describing here, use &lt;a href=&quot;https://web.dev/responsive-web-design-basics/#media-queries&quot;&gt;CSS media queries&lt;/a&gt; to
specify the size of elements depending on the viewport dimensions; &lt;code&gt;max-width: 100%&lt;/code&gt; is your friend.&lt;/p&gt;
&lt;p&gt;For media content in iframes (such as YouTube videos), try a responsive approach
(like the one &lt;a href=&quot;http://avexdesigns.com/responsive-youtube-embed/&quot; rel=&quot;noopener&quot;&gt;proposed by John Surdakowski&lt;/a&gt;).&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Don&#39;t force element sizing that results in an [aspect ratio] different from the original video. Squashed or stretched videos looks awful. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;css&quot;&gt;CSS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#css&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.video-container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;padding-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 56.25%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;padding-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.video-container iframe,&lt;br /&gt;.video-container object,&lt;br /&gt;.video-container embed&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h4 id=&quot;html&quot;&gt;HTML &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#html&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;video-container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;iframe&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;//www.youtube.com/embed/l-BA9Ee2XuM&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token attr-name&quot;&gt;frameborder&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;560&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;315&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;iframe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/media/responsive_embed.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Compare the &lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/media/responsive_embed.html&quot; rel=&quot;noopener&quot;&gt;responsive sample&lt;/a&gt; to the &lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/responsive/unyt.html&quot; rel=&quot;noopener&quot;&gt;unresponsive version&lt;/a&gt;. As you can see,
the unresponsive version isn&#39;t a great user experience.&lt;/p&gt;
&lt;h3 id=&quot;device-orientation&quot;&gt;Device orientation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#device-orientation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Device orientation isn&#39;t an issue for desktop monitors or laptops, but it&#39;s
hugely important when considering web page design for mobile devices and
tablets.&lt;/p&gt;
&lt;p&gt;Safari on iPhone does a good job of switching between portrait and landscape
orientation:&lt;/p&gt;
&lt;div class=&quot;switcher&quot;&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of video playing in Safari on iPhone, portrait.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 338px) 338px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/AmHneDShMOioWZwYG2kF.png?auto=format&amp;w=676 676w&quot; width=&quot;338&quot; /&gt;
  &lt;figcaption&gt;Screenshot of video playing in Safari on iPhone, portrait.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of video playing in Safari on iPhone, landscape.&quot; decoding=&quot;async&quot; height=&quot;338&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 600px) 600px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MZwkLJaXVk4g8lruhiKZ.png?auto=format&amp;w=1200 1200w&quot; width=&quot;600&quot; /&gt;
  &lt;figcaption&gt;Screenshot of video playing in Safari on iPhone, landscape.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;Device orientation on an iPad and Chrome on Android can be problematic.
For example, without any customization a video playing on an iPad in landscape
orientation looks like this:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of video playing in Safari on iPad, landscape.&quot; decoding=&quot;async&quot; height=&quot;450&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 600px) 600px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9FsExgY6cJFfMkxOPNkl.png?auto=format&amp;w=1200 1200w&quot; width=&quot;600&quot; /&gt;
  &lt;figcaption&gt;Screenshot of video playing in Safari on iPad, landscape.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Setting the video &lt;code&gt;width: 100%&lt;/code&gt; or &lt;code&gt;max-width: 100%&lt;/code&gt; with CSS can resolve
many device orientation layout problems.&lt;/p&gt;
&lt;h3 id=&quot;autoplay&quot;&gt;Autoplay &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#autoplay&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;autoplay&lt;/code&gt; attribute controls whether the browser downloads and plays a
video immediately. The precise way it works depends on the platform and browser.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Chrome: Depends on multiple factors including but not limited to whether the
viewing is on desktop and whether the mobile user has added your site or app
to their homescreen. For details, see &lt;a href=&quot;https://web.dev/autoplay-best-practices/&quot;&gt;Autoplay best practices&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Firefox: Blocks all video and sound, but gives users the ability to relax these
restrictions for either all sites or particular sites. For details, see
&lt;a href=&quot;https://support.mozilla.org/en-US/kb/block-autoplay&quot; rel=&quot;noopener&quot;&gt;Allow or block media autoplay in Firefox&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Safari: Has historically required a user gesture, but has been relaxing that
requirement in recent versions. For details, see
&lt;a href=&quot;https://webkit.org/blog/6784/new-video-policies-for-ios/&quot; rel=&quot;noopener&quot;&gt;New &amp;lt;video&amp;gt; Policies for iOS&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even on platforms where autoplay is possible, you need to consider whether it&#39;s
a good idea to enable it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Data usage can be expensive.&lt;/li&gt;
&lt;li&gt;Playing media before the user wants it can hog bandwidth and CPU, and thereby
delay page rendering.&lt;/li&gt;
&lt;li&gt;Users may be in a context where playing video or audio is intrusive.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;preload&quot;&gt;Preload &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/video-and-source-tags/#preload&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;preload&lt;/code&gt; attribute provides a hint to the browser as to how much
information or content to preload.&lt;/p&gt;
&lt;table class=&quot;responsive&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Value&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Value&quot;&gt;&lt;code&gt;none&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;The user might chose not to watch the video, so don&#39;t
      preload anything.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Value&quot;&gt;&lt;code&gt;metadata&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;Metadata (duration, dimensions, text tracks) should be
      preloaded, but with minimal video.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Value&quot;&gt;&lt;code&gt;auto&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;Downloading the entire video right away is considered
      desirable. An empty string produces the same result.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;preload&lt;/code&gt; attribute has different effects on different platforms.
For example, Chrome buffers 25 seconds of video on desktop but none on iOS or
Android. This means that on mobile, there may be playback startup delays
that don&#39;t happen on desktop. See &lt;a href=&quot;https://web.dev/fast-playback-with-preload/&quot;&gt;Fast playback with audio and video preload&lt;/a&gt; or
&lt;a href=&quot;https://www.stevesouders.com/blog/2013/04/12/html5-video-preload/&quot; rel=&quot;noopener&quot;&gt;Steve Souders&#39; blog&lt;/a&gt; for more details.&lt;/p&gt;
&lt;p&gt;Now that you know how to add media to your web page it&#39;s time to learn about
&lt;a href=&quot;https://web.dev/media-accessibility/&quot;&gt;Media accessibility&lt;/a&gt; where you will add captions to your video for hearing
impaired, or when playing the audio is not a viable option.&lt;/p&gt;
</content>
    <author>
      <name>Sam Dutton</name>
    </author><author>
      <name>Joe Medley</name>
    </author><author>
      <name>Derek Herman</name>
    </author>
  </entry>
</feed>
