diff --git a/docs/source/tutorials/basic_usage.md b/docs/source/tutorials/basic_usage.md
new file mode 100644
index 0000000000..259df894e8
--- /dev/null
+++ b/docs/source/tutorials/basic_usage.md
@@ -0,0 +1,61 @@
+# Basic Usage
+
+## Getting help
+
+```shell
+$ packager --help
+```
+
+## Media file analysis
+
+Shaka Packager can be used to inspect the content of a media file and dump basic
+stream information.
+
+```shell
+$ packager input=some_content.mp4 --dump_stream_info
+```
+
+The output looks like:
+
+```shell
+File "some_content.mp4":
+Found 2 stream(s).
+Stream [0] type: Video
+ codec_string: avc1.4d001e
+ time_scale: 24000
+ duration: 3002000 (125.1 seconds)
+ is_encrypted: false
+ codec: H264
+ width: 720
+ height: 360
+ pixel_aspect_ratio: 8:9
+ trick_play_factor: 0
+ nalu_length_size: 4
+
+Stream [1] type: Audio
+ codec_string: mp4a.40.2
+ time_scale: 44100
+ duration: 5517311 (125.1 seconds)
+ is_encrypted: false
+ codec: AAC
+ sample_bits: 16
+ num_channels: 2
+ sampling_frequency: 44100
+ language: eng
+```
+
+## Basic transmuxing
+
+Shaka Packager can be used to extract streams, optionally transmuxes the streams
+from one container format to another container format.
+
+Here is a simple command that extracts video and audio from the input file:
+
+```shell
+$ packager in=some_content.mp4,stream=video,out=video.mp4 \
+ in=some_content.mp4,stream=audio,out=audio.mp4
+```
+
+Shaka Packager is also capable of more complex tasks, such as applying
+encryption, packaging contents to DASH or HLS formats, etc. Refer
+:doc:`tutorials`.
diff --git a/docs/source/tutorials/dash.rst b/docs/source/tutorials/dash.rst
new file mode 100644
index 0000000000..92f1e86a74
--- /dev/null
+++ b/docs/source/tutorials/dash.rst
@@ -0,0 +1,63 @@
+DASH
+====
+
+Dynamic Adaptive Streaming over HTTP (DASH) is an adaptive bitrate streaming
+technique that enables high quality streaming of media content over HTTP.
+
+Shaka Packager supports DASH content packaging. This tutorial covers DASH
+packaging of VOD content without encryption. For live content packaging, see
+:doc:`live`; for content encryption, see :doc:`drm`.
+
+Synopsis
+--------
+
+::
+
+ $ packager {stream_descriptor} [stream_descriptor] ... \
+ --mpd_output {manifest output path} \
+ [Other DASH options] \
+ [Other options, e.g. DRM options, HLS options]
+
+See `DASH options`_ for the available DASH related options.
+
+.. note::
+
+ DASH and HLS options can both be specified to output DASH and HLS manifests
+ at the same time. Note that it works only for MP4 outputs.
+
+Examples
+--------
+
+The examples below uses the H264 streams created in :doc:`encoding`. It can be
+applied to VP9 in the same way.
+
+* on-demand::
+
+ $ packager \
+ in=h264_baseline_360p_600.mp4,stream=audio,output=audio.mp4 \
+ in=h264_baseline_360p_600.mp4,stream=video,output=h264_360p.mp4 \
+ in=h264_main_480p_1000.mp4,stream=video,output=h264_480p.mp4 \
+ in=h264_main_720p_3000.mp4,stream=video,output=h264_720p.mp4 \
+ in=h264_high_1080p_6000.mp4,stream=video,output=h264_1080p.mp4 \
+ --mpd_output h264.mpd
+
+The above packaging command creates five single track fragmented mp4 streams
+(4 video, 1 audio) and a manifest, which describes the streams.
+
+* static-live::
+
+ $ packager \
+ 'in=h264_baseline_360p_600.mp4,stream=audio,init_segment=audio_init.mp4,segment_template=audio_$Number$.m4s' \
+ 'in=h264_baseline_360p_600.mp4,stream=video,init_segment=h264_360p_init.mp4,segment_template=h264_360p_$Number$.m4s' \
+ 'in=h264_main_480p_1000.mp4,stream=video,init_segment=h264_480p_init.mp4,segment_template=h264_480p_$Number$.m4s' \
+ 'in=h264_main_720p_3000.mp4,stream=video,init_segment=h264_720p_init.mp4,segment_template=h264_720p_$Number$.m4s' \
+ 'in=h264_main_1080p_6000.mp4,stream=video,init_segment=h264_1080p_init.mp4,segment_template=h264_1080p_$Number$.m4s' \
+ --generate_static_mpd --mpd_output h264.mpd
+
+The above packaging command creates five groups of segments (each with an init
+segment and a series of media segments) for the five streams and a manifest,
+which describes the streams.
+
+.. include:: /options/dash_options.rst
+
+.. include:: /options/segment_template_formatting.rst
diff --git a/docs/source/tutorials/drm.rst b/docs/source/tutorials/drm.rst
new file mode 100644
index 0000000000..c3b2d971b4
--- /dev/null
+++ b/docs/source/tutorials/drm.rst
@@ -0,0 +1,9 @@
+DRM
+===
+
+.. toctree::
+ :maxdepth: 2
+
+ /tutorials/raw_key.rst
+ /tutorials/widevine.rst
+ /tutorials/playready.rst
diff --git a/docs/source/tutorials/encoding.rst b/docs/source/tutorials/encoding.rst
new file mode 100644
index 0000000000..c25e5a5fbb
--- /dev/null
+++ b/docs/source/tutorials/encoding.rst
@@ -0,0 +1,124 @@
+Media Encoding
+--------------
+
+Shaka Packager does not do transcoding internally. The contents need to be
+pre-encoded before passing to Shaka Packager.
+
+General guidelines of how contents should be encoded
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Encode multiple bitrates or resolutions of the same content. Shaka Packager
+ can then package the content into DASH / HLS formats, allowing different
+ bitrates of the content to be served for different network conditions,
+ achieving adaptive bitrate streaming.
+- Not a must, but the multibirate content is recommended to have aligned GOPs
+ across the different bitrate streams. This makes bitrate switching easier and
+ smoother.
+- We recommend setting GOP size to 5s or less. The streams are usually
+ switchable only at GOP boundaries. A smaller GOP size results in faster
+ switching when network condition changes.
+- In the same stream, the bitrate should be more or less the same in the
+ inter-GOP level.
+
+Sample commands to generate multi-bitrate content
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Let us say we have a 1080p original content `original.mp4` containing an audio
+track in `AAC` and a video track in `H264`. The frame rate is 24. We want to
+encode the contents into four resolutions: 360p, 480p, 720p and 1080p with GOP
+size 72, i.e. 3 seconds.
+
+We use `ffmpeg `_ here, which is a common tool used for
+transcoding.
+
+H264 encoding
+"""""""""""""
+
+* 360p::
+
+ $ ffmpeg -i original.mp4 -c:a copy \
+ -vf "scale=-2:360" \
+ -c:v libx264 -profile:v baseline -level:v 3.0 \
+ -x264opts scenecut=0:open_gop=0:min-keyint=72:keyint=72 \
+ -minrate 600k -maxrate 600k -bufsize 600k -b:v 600k \
+ -y h264_baseline_360p_600.mp4
+
+* 480p::
+
+ $ ffmpeg -i original.mp4 -c:a copy \
+ -vf "scale=-2:480" \
+ -c:v libx264 -profile:v main -level:v 3.1 \
+ -x264opts scenecut=0:open_gop=0:min-keyint=72:keyint=72 \
+ -minrate 1000k -maxrate 1000k -bufsize 1000k -b:v 1000k \
+ -y h264_main_480p_1000.mp4
+
+* 720p::
+
+ $ ffmpeg -i original.mp4 -c:a copy \
+ -vf "scale=-2:720" \
+ -c:v libx264 -profile:v main -level:v 4.0 \
+ -x264opts scenecut=0:open_gop=0:min-keyint=72:keyint=72 \
+ -minrate 3000k -maxrate 3000k -bufsize 3000k -b:v 3000k \
+ -y h264_main_720p_3000.mp4
+
+* 1080p::
+
+ $ ffmpeg -i original.mp4 -c:a copy \
+ -vf "scale=-2:1080" \
+ -c:v libx264 -profile:v main -level:v 4.2 \
+ -x264opts scenecut=0:open_gop=0:min-keyint=72:keyint=72 \
+ -minrate 6000k -maxrate 6000k -bufsize 6000k -b:v 6000k \
+ -y h264_main_1080p_6000.mp4
+
+VP9 encoding
+""""""""""""
+
+The audio is encoded into `opus`.
+
+* 360p::
+
+ $ ffmpeg -i original.mp4 \
+ -strict -2 -c:a opus \
+ -vf "scale=-2:360" \
+ -c:v libvpx-vp9 -profile:v 0 \
+ -keyint_min 72 -g 72 \
+ -tile-columns 4 -frame-parallel 1 -speed 1 \
+ -auto-alt-ref 1 -lag-in-frames 25 \
+ -b:v 300k \
+ -y vp9_360p_300.webm
+
+* 480p::
+
+ $ ffmpeg -i original.mp4 \
+ -strict -2 -c:a opus \
+ -vf "scale=-2:480" \
+ -c:v libvpx-vp9 -profile:v 0 \
+ -keyint_min 72 -g 72 \
+ -tile-columns 4 -frame-parallel 1 -speed 1 \
+ -auto-alt-ref 1 -lag-in-frames 25 \
+ -b:v 500k \
+ -y vp9_480p_500.webm
+
+* 720p::
+
+ $ ffmpeg -i original.mp4 \
+ -strict -2 -c:a opus \
+ -vf "scale=-2:720" \
+ -c:v libvpx-vp9 -profile:v 0 \
+ -keyint_min 72 -g 72 \
+ -tile-columns 4 -frame-parallel 1 -speed 1 \
+ -auto-alt-ref 1 -lag-in-frames 25 \
+ -b:v 1500k \
+ -y vp9_720p_1500.webm
+
+* 1080p::
+
+ $ ffmpeg -i original.mp4 \
+ -strict -2 -c:a opus \
+ -vf "scale=-2:1080" \
+ -c:v libvpx-vp9 -profile:v 0 \
+ -keyint_min 72 -g 72 \
+ -tile-columns 4 -frame-parallel 1 -speed 1 \
+ -auto-alt-ref 1 -lag-in-frames 25 \
+ -b:v 3000k \
+ -y vp9_1080p_3000.webm
diff --git a/docs/source/tutorials/ffmpeg_piping.rst b/docs/source/tutorials/ffmpeg_piping.rst
new file mode 100644
index 0000000000..2625f8d943
--- /dev/null
+++ b/docs/source/tutorials/ffmpeg_piping.rst
@@ -0,0 +1,56 @@
+ffmpeg piping
+=============
+
+We can use *ffmpeg* to redirect / pipe input not supported by *packager*
+to *packager*, for example, input from webcam devices. The example below uses
+webcam input device. The concept depicted here can be applied to
+other *ffmpeg* supported capture device.
+
+ffmpeg camera capture
+---------------------
+
+Refer to `ffmpeg Capture/Webcam `_
+on how to use *ffmpeg* to capture webmcam inputs.
+
+The examples below assumes Mac OS X 10.7 (Lion) or later. It is similar on
+other platforms. Refer to the above link for details.
+
+Create pipe
+-----------
+
+We use pipe to connect *ffmpeg* and *packager*::
+
+ $ mkfifo pipe1
+
+Encoding / capture command
+--------------------------
+
+The below command captures from the default audio / video devices on the
+machine::
+
+ $ ffmpeg -f avfoundation -i "default" -f mpegts pipe: > pipe1
+
+The command starts only after packager starts.
+
+.. note::
+
+ After encoding starts, monitor encoding speed carefully. It should always be
+ 1x and above. If not, adjust the encoding parameters to recude it.
+
+Packaging command (DASH)
+------------------------
+
+::
+
+ $ packager \
+ 'in=pipe1,stream=audio,init_segment=live_cam_audio.mp4,segment_template=live_cam_audio_$Number$.m4s' \
+ 'in=pipe1,stream=video,init_segment=live_cam_video.mp4,segment_template=live_cam_video_$Number$.m4s' \
+ --mpd_output live_cam.mpd \
+ --io_block_size 65536
+
+.. note::
+
+ Option -io_block_size 65536 tells packager to use an io_block_size of 65K
+ for threaded io file. This is necessary as reading from pipe blocks until
+ the specified number of bytes, which is specified in io_block_size for
+ threaded io file, thus the value of io_block_size cannot be too large.
diff --git a/docs/source/tutorials/hls.rst b/docs/source/tutorials/hls.rst
new file mode 100644
index 0000000000..4baba4672c
--- /dev/null
+++ b/docs/source/tutorials/hls.rst
@@ -0,0 +1,87 @@
+HLS
+===
+
+HTTP Live Streaming (also known as HLS) is an HTTP-based media streaming
+communications protocol implemented by Apple Inc. as part of its QuickTime,
+Safari, OS X, and iOS software. It resembles MPEG-DASH in that it works by
+breaking the overall stream into a sequence of small HTTP-based file downloads,
+each download loading one short chunk of an overall potentially unbounded
+transport stream. As the stream is played, the client may select from a number
+of different alternate streams containing the same material encoded at a variety
+of data rates, allowing the streaming session to adapt to the available data
+rate. At the start of the streaming session, HLS downloads an extended M3U
+playlist containing the metadata for the various sub-streams which are
+available.
+
+Shaka Packager supports HLS content packaging. This tutorial covers HLS
+packaging of VOD content without encryption. For live content packaging, see
+:doc:`live`; for content encryption, see :doc:`drm`.
+
+Synopsis
+--------
+
+::
+
+ $ packager {stream_descriptor with HLS specific descriptors} \
+ [stream_descriptor with HLS specific descriptors] ... \
+ --hls_master_playlist_output {master playlist output path} \
+ [Other HLS options] \
+ [Other options, e.g. DRM options, DASH options]
+
+See `HLS specific stream descriptor fields`_ for the available HLS specific
+stream descriptor fields.
+
+See `HLS options`_ for the available HLS related options.
+
+.. note::
+
+ DASH and HLS options can both be specified to output DASH and HLS manifests
+ at the same time. Note that it works only for MP4 outputs.
+
+Examples
+---------
+
+The examples below uses the H264 streams created in :doc:`encoding`.
+
+* TS output::
+
+ $ packager \
+ 'in=h264_baseline_360p_600.mp4,stream=audio,output=audio_$Number$.ts,playlist_name=audio.m3u8,hls_group_id=audio,hls_name=ENGLISH' \
+ 'in=h264_baseline_360p_600.mp4,stream=video,output=h264_360p_$Number$.ts,playlist_name=h264_360p.m3u8' \
+ 'in=h264_main_480p_1000.mp4,stream=video,output=h264_480p_$Number$.ts,playlist_name=h264_480p.m3u8' \
+ 'in=h264_main_720p_3000.mp4,stream=video,output=h264_720p_$Number$.ts,playlist_name=h264_720p.m3u8' \
+ 'in=h264_high_1080p_6000.mp4,stream=video,output=h264_1080p_$Number$.ts,playlist_name=h264_1080p.m3u8' \
+ --hls_master_playlist_output h264_master.m3u8
+
+The above packaging command creates five single track TS streams
+(4 video, 1 audio) and a manifest, which describes the streams.
+
+* MP4 output is also supported::
+
+ $ packager \
+ 'in=h264_baseline_360p_600.mp4,stream=audio,init_segment=audio_init.mp4,segment_template=audio_$Number$.m4s,playlist_name=audio.m3u8,hls_group_id=audio,hls_name=ENGLISH' \
+ 'in=h264_baseline_360p_600.mp4,stream=video,init_segment=h264_360p_init.mp4,segment_template=h264_360p_$Number$.m4s,playlist_name=h264_360p.m3u8' \
+ 'in=h264_main_480p_1000.mp4,stream=video,init_segment=h264_480p_init.mp4,segment_template=h264_480p_$Number$.m4s,playlist_name=h264_480p.m3u8' \
+ 'in=h264_main_720p_3000.mp4,stream=video,init_segment=h264_720p_init.mp4,segment_template=h264_720p_$Number$.m4s,playlist_name=h264_720p.m3u8' \
+ 'in=h264_main_1080p_6000.mp4,stream=video,init_segment=h264_1080p_init.mp4,segment_template=h264_1080p_$Number$.m4s,playlist_name=h264_1080p.m3u8' \
+ --hls_master_playlist_output h264_master.m3u8
+
+* Single file MP4 output is also supported::
+
+ $ packager \
+ in=h264_baseline_360p_600.mp4,stream=audio,output=audio.mp4,playlist_name=audio.m3u8,hls_group_id=audio,hls_name=ENGLISH \
+ in=h264_baseline_360p_600.mp4,stream=video,output=h264_360p.mp4,playlist_name=h264_360p.m3u8 \
+ in=h264_main_480p_1000.mp4,stream=video,output=h264_480p.mp4,playlist_name=h264_480p.m3u8 \
+ in=h264_main_720p_3000.mp4,stream=video,output=h264_720p.mp4,playlist_name=h264_720p.m3u8 \
+ in=h264_high_1080p_6000.mp4,stream=video,output=h264_1080p.mp4,playlist_name=h264_1080p.m3u8 \
+ --hls_master_playlist_output h264_master.m3u8
+
+The above packaging command creates five groups of streams (each with an init
+segment and a series of media segments) and a manifest, which describes the
+streams.
+
+.. include:: /options/hls_stream_descriptors.rst
+
+.. include:: /options/hls_options.rst
+
+.. include:: /options/segment_template_formatting.rst
diff --git a/docs/source/tutorials/live.rst b/docs/source/tutorials/live.rst
new file mode 100644
index 0000000000..d38f013904
--- /dev/null
+++ b/docs/source/tutorials/live.rst
@@ -0,0 +1,46 @@
+Live
+====
+
+A typical live source is UDP multicast, which is the only live protocol
+packager supports directly right now.
+
+.. include:: /options/udp_file_options.rst
+
+Pipe through FFmpeg
+-------------------
+
+For other unsupported protocols, you can use FFmpeg to pipe the input.
+See :doc:`ffmpeg_piping` for details.
+
+Examples
+--------
+
+The command is similar to the on-demand, see :doc:`dash` and :doc:`hls`.
+
+Here are some examples.
+
+* DASH::
+
+ $ packager \
+ 'in=udp://225.1.1.8:8001?interface=172.29.46.122,stream=audio,init_segment=audio_init.mp4,segment_template=audio_$Number$.m4s' \
+ 'in=udp://225.1.1.8:8001?interface=172.29.46.122,stream=video,init_segment=h264_360p_init.mp4,segment_template=h264_360p_$Number$.m4s' \
+ 'in=udp://225.1.1.8:8002?interface=172.29.46.122,stream=video,init_segment=h264_480p_init.mp4,segment_template=h264_480p_$Number$.m4s' \
+ 'in=udp://225.1.1.8:8003?interface=172.29.46.122,stream=video,init_segment=h264_720p_init.mp4,segment_template=h264_720p_$Number$.m4s' \
+ 'in=udp://225.1.1.8:8004?interface=172.29.46.122,stream=video,init_segment=h264_1080p_init.mp4,segment_template=h264_1080p_$Number$.m4s' \
+ --mpd_output h264.mpd
+
+
+* HLS::
+
+ $ packager \
+ 'in=udp://225.1.1.8:8001?interface=172.29.46.122,stream=audio,init_segment=audio_init.mp4,segment_template=audio_$Number$.m4s,playlist_name=audio.m3u8,hls_group_id=audio,hls_name=ENGLISH' \
+ 'in=udp://225.1.1.8:8001?interface=172.29.46.122,stream=video,init_segment=h264_360p_init.mp4,segment_template=h264_360p_$Number$.m4s,playlist_name=h264_360p.m3u8' \
+ 'in=udp://225.1.1.8:8002?interface=172.29.46.122,stream=video,init_segment=h264_480p_init.mp4,segment_template=h264_480p_$Number$.m4s,playlist_name=h264_480p.m3u8' \
+ 'in=udp://225.1.1.8:8003?interface=172.29.46.122,stream=video,init_segment=h264_720p_init.mp4,segment_template=h264_720p_$Number$.m4s,playlist_name=h264_720p.m3u8' \
+ 'in=udp://225.1.1.8:8004?interface=172.29.46.122,stream=video,init_segment=h264_1080p_init.mp4,segment_template=h264_1080p_$Number$.m4s,playlist_name=h264_1080p.m3u8' \
+ --hls_master_playlist_output h264_master.m3u8
+
+.. note::
+
+ Packager does not support removing old segments internally. The user is
+ resposible for setting up a cron job to do so.
diff --git a/docs/source/tutorials/playready.rst b/docs/source/tutorials/playready.rst
new file mode 100644
index 0000000000..59eaa6dfc9
--- /dev/null
+++ b/docs/source/tutorials/playready.rst
@@ -0,0 +1,4 @@
+Playready
+=========
+
+To be completed.
diff --git a/docs/source/tutorials/raw_key.rst b/docs/source/tutorials/raw_key.rst
new file mode 100644
index 0000000000..a477d04d58
--- /dev/null
+++ b/docs/source/tutorials/raw_key.rst
@@ -0,0 +1,86 @@
+Raw key
+=======
+
+*Packager* allows encrypting contents with raw key.
+
+Synopsis
+--------
+
+::
+
+ $ packager {stream_descriptor} [stream_descriptor] ... \
+ --enable_fixed_key_encryption \
+ --key_id --key \
+ [--pssh ] \
+ [Other options, e.g. DASH options, HLS options]
+
+Custom PSSH(s) can be provided in *--pssh*. If absent,
+`v1 common PSSH box `_ is generated.
+
+Examples
+--------
+
+The examples below uses the H264 streams created in :doc:`encoding`. Here are examples with DASH. It can be applied to HLS in a similar way.
+
+Common PSSH::
+
+ $ packager \
+ in=h264_baseline_360p_600.mp4,stream=audio,output=audio.mp4 \
+ in=h264_baseline_360p_600.mp4,stream=video,output=h264_360p.mp4 \
+ in=h264_main_480p_1000.mp4,stream=video,output=h264_480p.mp4 \
+ in=h264_main_720p_3000.mp4,stream=video,output=h264_720p.mp4 \
+ in=h264_high_1080p_6000.mp4,stream=video,output=h264_1080p.mp4 \
+ --enable_fixed_key_encryption \
+ --key_id abba271e8bcf552bbd2e86a434a9a5d9 \
+ --key 69eaa802a6763af979e8d1940fb88392 \
+ --mpd_output h264.mpd
+
+Widevine PSSH::
+
+ $ packager \
+ in=h264_baseline_360p_600.mp4,stream=audio,output=audio.mp4 \
+ in=h264_baseline_360p_600.mp4,stream=video,output=h264_360p.mp4 \
+ in=h264_main_480p_1000.mp4,stream=video,output=h264_480p.mp4 \
+ in=h264_main_720p_3000.mp4,stream=video,output=h264_720p.mp4 \
+ in=h264_high_1080p_6000.mp4,stream=video,output=h264_1080p.mp4 \
+ --enable_fixed_key_encryption \
+ --key_id abba271e8bcf552bbd2e86a434a9a5d9 \
+ --key 69eaa802a6763af979e8d1940fb88392 \
+ --pssh 000000407073736800000000edef8ba979d64acea3c827dcd51d21ed000000201a0d7769646576696e655f74657374220f7465737420636f6e74656e74206964 \
+ --mpd_output h264.mpd
+
+Refer to
+`player setup `_
+on how to config the DRM in Shaka Player.
+
+Test vectors used in this tutorial
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Key ID
+
+ abba271e8bcf552bbd2e86a434a9a5d9
+
+ Key ID must be 16 bytes or 32 digits in HEX.
+
+Key
+
+ 69eaa802a6763af979e8d1940fb88392
+
+ Key must be 16 bytes or 32 digits in HEX.
+
+Widevine PSSH
+
+ 000000407073736800000000edef8ba979d64acea3c827dcd51d21ed000000201a0d7769646576696e655f74657374220f7465737420636f6e74656e74206964
+
+ The PSSH is generated using
+ `pssh-box script `_::
+
+ $ pssh-box.py --widevine-system-id \
+ --content-id 7465737420636f6e74656e74206964 --provider widevine_test
+
+.. include:: /options/raw_key_encryption_options.rst
+
+pssh-box (Utility to generate PSSH boxes)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+https://github.com/google/shaka-packager/tree/master/packager/tools/pssh
diff --git a/docs/source/tutorials/tutorials.rst b/docs/source/tutorials/tutorials.rst
new file mode 100644
index 0000000000..5ce31569ee
--- /dev/null
+++ b/docs/source/tutorials/tutorials.rst
@@ -0,0 +1,11 @@
+Tutorials
+====================
+
+.. toctree::
+ :maxdepth: 2
+
+ basic_usage.md
+ dash.rst
+ hls.md
+ live.md
+ drm.rst
diff --git a/docs/source/tutorials/widevine.rst b/docs/source/tutorials/widevine.rst
new file mode 100644
index 0000000000..e8f2861be1
--- /dev/null
+++ b/docs/source/tutorials/widevine.rst
@@ -0,0 +1,87 @@
+Widevine
+========
+
+There are two options to package a Widevine DRM encrypted content:
+
+1. If you know the encryption keys and have the associated Widevine PSSH at
+ hand, you can provide them in clear text to *packager* directly. Refer to
+ :doc:`/tutorials/raw_key` for details.
+
+2. Provide *key_server_url* and associated credentials to *packager*.
+ *Packager* will fetch encryption keys from Widevine key server.
+
+Synopsis
+--------
+
+AES signing::
+
+ $ packager {stream_descriptor} [stream_descriptor] ... \
+ --enable_widevine_encryption \
+ --key_server_url \
+ --content_id \
+ --signer --aes_signing_key \
+ --aes_signing_iv \
+ [Other options, e.g. DASH options, HLS options]
+
+RSA signing::
+
+ $ packager {stream_descriptor} [stream_descriptor] ... \
+ --enable_widevine_encryption \
+ --key_server_url \
+ --content_id \
+ --signer --rsa_signing_key_path \
+ [Other options, e.g. DASH options, HLS options]
+
+Examples
+--------
+
+The examples below uses the H264 streams created in :doc:`encoding`.
+
+Here is an example with DASH. It can be applied to HLS in a similar way::
+
+ $ packager \
+ in=h264_baseline_360p_600.mp4,stream=audio,output=audio.mp4 \
+ in=h264_baseline_360p_600.mp4,stream=video,output=h264_360p.mp4 \
+ in=h264_main_480p_1000.mp4,stream=video,output=h264_480p.mp4 \
+ in=h264_main_720p_3000.mp4,stream=video,output=h264_720p.mp4 \
+ in=h264_high_1080p_6000.mp4,stream=video,output=h264_1080p.mp4 \
+ --mpd_output h264.mpd \
+ --enable_widevine_encryption \
+ --key_server_url https://license.uat.widevine.com/cenc/getcontentkey/widevine_test \
+ --content_id 7465737420636f6e74656e74206964 \
+ --signer widevine_test \
+ --aes_signing_key 1ae8ccd0e7985cc0b6203a55855a1034afc252980e970ca90e5202689f947ab9 \
+ --aes_signing_iv d58ce954203b7c9a9a9d467f59839249
+
+Refer to
+`player setup `_
+on how to config the DRM in Shaka Player.
+
+Widevine test credential
+------------------------
+
+Here is the test crendential used in this tutorial.
+
+key_server_url
+
+ https://license.uat.widevine.com/cenc/getcontentkey/widevine_test
+
+signer
+
+ widevine_test
+
+aes_signing_key
+
+ 1ae8ccd0e7985cc0b6203a55855a1034afc252980e970ca90e5202689f947ab9
+
+aes_signing_iv
+
+ d58ce954203b7c9a9a9d467f59839249
+
+.. note::
+
+ The test credential is only meant for development. Please reach out to
+ `Widevine `_ if
+ you need something for production use.
+
+.. include:: /options/widevine_encryption_options.rst