diff --git a/_sources/build_instructions.md.txt b/_sources/build_instructions.md.txt new file mode 100644 index 0000000000..b160b233b8 --- /dev/null +++ b/_sources/build_instructions.md.txt @@ -0,0 +1,314 @@ +# Build Instructions + +Shaka Packager supports building on Windows, Mac and Linux host systems. + +## Linux build dependencies + +Most development is done on Ubuntu (currently 14.04, Trusty Tahr). The +dependencies mentioned here are only for Ubuntu. There are some instructions +for [other distros below](#notes-for-other-linux-distros). + +```shell +sudo apt-get update +sudo apt-get install build-essential curl git python +``` + +Note that `Git` must be v1.7.5 or above. + +## Mac system requirements + +* [Xcode](https://developer.apple.com/xcode) 7.3+. +* The OS X 10.10 SDK or later. Run + + ```shell + $ ls `xcode-select -p`/Platforms/MacOSX.platform/Developer/SDKs + ``` + + to check whether you have it. + +## Windows system requirements + +* Visual Studio 2015 Update 3, see below (no other version is supported). +* Windows 7 or newer. + +Install Visual Studio 2015 Update 3 or later - Community Edition should work if +its license is appropriate for you. Use the Custom Install option and select: + +- Visual C++, which will select three sub-categories including MFC +- Universal Windows Apps Development Tools > Tools (1.4.1) and Windows 10 SDK + (10.0.14393) + +## Install `depot_tools` + +### Linux and Mac + +Clone the `depot_tools` repository from Chromium: + +```shell +$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git +``` + +Add `depot_tools` to the end of your PATH (you will probably want to put this +in your `~/.bashrc` or `~/.zshrc`). Assuming you cloned `depot_tools` to +`/path/to/depot_tools`: + +```shell +$ export PATH="$PATH:/path/to/depot_tools" +``` + +### Windows + +Download the +[depot_tools bundle](https://storage.googleapis.com/chrome-infra/depot_tools.zip) +and extract it somewhere. + +*** note +**Warning:** **DO NOT** use drag-n-drop or copy-n-paste extract from Explorer, +this will not extract the hidden “.git” folder which is necessary for +depot_tools to autoupdate itself. You can use “Extract all…” from the context +menu though. +*** + +Add depot_tools to the start of your PATH (must be ahead of any installs of +Python). Assuming you unzipped the bundle to C:\src\depot_tools, open: + +Control Panel → System and Security → System → Advanced system settings + +If you have Administrator access, Modify the PATH system variable and +put `C:\src\depot_tools` at the front (or at least in front of any directory +that might already have a copy of Python or Git). + +If you don't have Administrator access, you can add a user-level PATH +environment variable and put `C:\src\depot_tools` at the front, but +if your system PATH has a Python in it, you will be out of luck. + +Also, add a DEPOT_TOOLS_WIN_TOOLCHAIN system variable in the same way, and set +it to 0. This tells depot_tools to use your locally installed version of Visual +Studio (by default, depot_tools will try to use a google-internal version). + +From a cmd.exe shell, run the command gclient (without arguments). On first +run, gclient will install all the Windows-specific bits needed to work with +the code, including msysgit and python. + +* If you run gclient from a non-cmd shell (e.g., cygwin, PowerShell), + it may appear to run properly, but msysgit, python, and other tools + may not get installed correctly. +* If you see strange errors with the file system on the first run of gclient, + you may want to + [disable Windows Indexing](http://tortoisesvn.tigris.org/faq.html#cantmove2). + +## Get the code + +Create a `shaka_packager` directory for the checkout and change to it (you can +call this whatever you like and put it wherever you like, as long as the full +path has no spaces): + +```shell +$ mkdir shaka_packager && cd shaka_packager +``` + +Run the `gclient` tool from `depot_tools` to check out the code and its +dependencies. + +```shell +$ gclient config https://www.github.com/google/shaka-packager.git --name=src --unmanaged +$ gclient sync +``` + +To sync to a particular commit or version, add the '-r \' flag to +`gclient sync`, e.g. + +```shell +$ gclient sync -r 4cb5326355e1559d60b46167740e04624d0d2f51 +``` + +```shell +$ gclient sync -r v1.2.0 +``` + +If you don't want the full repo history, you can save some time by adding the +`--no-history` flag to `gclient sync`. + +When the above commands completes, it will have created a hidden `.gclient` file +and a directory called `src` in the working directory. The remaining +instructions assume you have switched to the `src` directory: + +```shell +$ cd src +``` + +### Build Shaka Packager + +#### Linux and Mac + +Shaka Packager uses [Ninja](https://ninja-build.org) as its main build tool, +which is bundled in depot_tools. + +To build the code, run `ninja` command: + +```shell +$ ninja -C out/Release +``` + +If you want to build debug code, replace `Release` above with `Debug`. + +We also provide a mechanism to change build settings, for example, +you can change build system to `make` by overriding `GYP_GENERATORS`: + +```shell +$ GYP_GENERATORS='make' gclient runhooks +``` + +Another example, you can also disable clang by overriding `GYP_DEFINES`: + +```shell +$ GYP_DEFINES='clang=0' gclient runhooks +``` + +#### Windows + +The instructions are similar, except that Windows allows using either `/` or `\` +as path separator: + +```shell +$ ninja -C out/Release +$ ninja -C out\Release +``` + +Also, unlike Linux / Mac, 32-bit is chosen by default even if the system is +64-bit. 64-bit has to be enabled explicitly and the output directory is +configured to `out/%CONFIGURATION%_x64`, i.e.: + +```shell +$ GYP_DEFINES='target_arch=x64' gclient runhooks +$ ninja -C out/Release_x64 +``` + +### Build artifacts + +After a successful build, you can find build artifacts including the main +`packager` binary in build output directory (`out/Release` or `out/Release_x64` +for release build). + +See [Shaka Packager Documentation](https://google.github.io/shaka-packager/) +on how to use `Shaka Packager`. + +### Update your checkout + +To update an existing checkout, you can run + +```shell +$ git pull origin master --rebase +$ gclient sync +``` + +The first command updates the primary Packager source repository and rebases on +top of tip-of-tree (aka the Git branch `origin/master`). You can also use other +common Git commands to update the repo. + +The second command syncs dependencies to the appropriate versions and re-runs +hooks as needed. + +## Cross compiling for ARM on Ubuntu host + +The install-build-deps script can be used to install all the compiler +and library dependencies directly from Ubuntu: + +```shell +$ ./pacakger/build/install-build-deps.sh +``` + +Install sysroot image and others using `gclient`: + +```shell +$ GYP_CROSSCOMPILE=1 GYP_DEFINES="target_arch=arm" gclient runhooks +``` + +The build command is the same as in Ubuntu: + +```shell +$ ninja -C out/Release +``` + +## Notes for other linux distros + +### Arch Linux + +Instead of running `sudo apt-get install` to install build dependencies, run: + +```shell +$ sudo pacman -S --needed python2 git curl gcc gcc-libs make +$ sudo ln -sf python2 /usr/bin/python +``` + +Clang requires libtinfo.so.5 which is not available by default on Arch Linux. +You can get libtinfo from ncurses5-compat-libs in AUR: + +```shell +$ git clone https://aur.archlinux.org/ncurses5-compat-libs.git +$ cd ncurses5-compat-libs +$ gpg --keyserver pgp.mit.edu --recv-keys F7E48EDB +$ makepkg -si +``` + +### Debian + +Same as Ubuntu. + +### Fedora + +Instead of running `sudo apt-get install` to install build dependencies, run: + +```shell +$ su -c 'yum install -y git python git curl gcc-c++ findutils bzip2 \ + ncurses-compat-libs' +``` + +### OpenSUSE + +Use `zypper` command to install dependencies: + +```shell +sudo zypper in git python python-xml git curl gcc-c++ tar +``` + +## Tips, tricks, and troubleshooting + +### Xcode license agreement + +If you're getting the error + +> Agreeing to the Xcode/iOS license requires admin privileges, please re-run as +> root via sudo. + +the Xcode license hasn't been accepted yet which (contrary to the message) any +user can do by running: + +```shell +$ xcodebuild -license +``` + +Only accepting for all users of the machine requires root: + +```shell +$ sudo xcodebuild -license +``` + +## Contributing + +If you have improvements or fixes, we would love to have your contributions. +See https://github.com/google/shaka-packager/blob/master/CONTRIBUTING.md for +details. + +We have continue integration tests setup on pull requests. You can also verify +locally by running the tests manually. + +If you know which tests are affected by your change, you can limit which tests +are run using the `--gtest_filter` arg, e.g.: + +```shell +$ out/Debug/mp4_unittest --gtest_filter="MP4MediaParserTest.*" +``` + +You can find out more about GoogleTest at its +[GitHub page](https://github.com/google/googletest). diff --git a/_sources/docker_instructions.md.txt b/_sources/docker_instructions.md.txt new file mode 100644 index 0000000000..c1f73b6ced --- /dev/null +++ b/_sources/docker_instructions.md.txt @@ -0,0 +1,43 @@ +# Using Docker + +[Docker](https://www.docker.com/whatisdocker) is a tool that can package an +application and its dependencies in a virtual container to run on different +host operating systems. + +## Get Shaka Packager from Dockerhub + +To pull latest Shaka Packager: + +```shell +$ docker pull google/shaka-packager +``` + +You can pull a specific version, e.g. v1.6.2: + +```shell +$ docker pull google/shaka-packager:release-v1.6.2 +``` + +The full list of tags is available +[here](https://hub.docker.com/r/google/shaka-packager/tags/). + +## Run the container + +Assume you have your media files stored in `host_media_path` in the host +machine. + +This runs the container and maps `host_media_path` to `media` in the container: + +```shell +$ docker run -v /host_media_path/:/media -it --rm google/shaka-packager +``` + +Then in the container, run the packager command, e.g.: + +```shell +$ packager input=/media/example.mp4,stream=audio,output=/media/audio.mp4 \ + input=/media/example.mp4,stream=video,output=/media/video.mp4 \ + --mpd_output /media/example.mpd +``` + +Outputs are available in the host's media folder `host_media_path`. diff --git a/_sources/documentation.rst.txt b/_sources/documentation.rst.txt new file mode 100644 index 0000000000..aa7a087fc9 --- /dev/null +++ b/_sources/documentation.rst.txt @@ -0,0 +1,80 @@ +Packager Documentation +====================== + +Shaka Packager is a tool and a media packaging SDK for DASH and HLS packaging +and encryption. It can transmux input media files from one container to another +container. + +.. note:: + + Shaka Packager does not do transcoding. Content must be pre-encoded before + passing to packager. + +Packager operates in *streams*, described by *stream_descriptor*. The streams +can be read from the same "file" or different "files", which can be regular +files, pipes, udp streams, etc. + +This page is the documentation on using the *packager* tool. If you are +interested in integrating *packager* library into your own tool, please see +:doc:`library`. + +Getting Shaka Packager +---------------------- + +There are several ways you can get Shaka Packager. + +- Using `Docker `_. + Instructions are available at :doc:`docker_instructions`. +- Get prebuilt binaries from + `release `_. +- Built from source, see :doc:`build_instructions` for details. + +Synopsis +-------- + +:: + + $ packager {stream_descriptor} [{stream_descriptor}] ... \ + [--dump_stream_info] \ + [Chunking Options] \ + [MP4 Output Options] \ + [encryption / decryption options] \ + [DASH options] \ + [HLS options] + +.. include:: /options/stream_descriptors.rst + +.. include:: /options/chunking_options.rst + +.. include:: /options/mp4_output_options.rst + +.. include:: /options/dash_options.rst + +.. include:: /options/hls_options.rst + +Encryption / decryption options +------------------------------- + +Shaka Packager supports three different types of key providers: + +- Raw key (fixed key): keys are provided in command line +- Widevine: fetches keys from Widevine key server +- Playready: fetches keys from Playready key server + +Different key providers cannot be specified at the same time. + +:: + + [--enable_widevine_encryption {Widevine Encryption Options}] \ + [--enable_widevine_decryption {Widevine Decryption Options}] \ + [--enable_fixed_key_encryption {Fixed Key Encryption Options}] \ + [--enable_fixed_key_decryption {Fixed Key Decryption Options}] \ + [--enable_playready_encryption {Playready Encryption Options}] + +.. include:: /options/general_encryption_options.rst + +.. include:: /options/raw_key_encryption_options.rst + +.. include:: /options/widevine_encryption_options.rst + +.. include:: /options/playready_encryption_options.rst diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt new file mode 100644 index 0000000000..1dd070ed8f --- /dev/null +++ b/_sources/index.rst.txt @@ -0,0 +1,33 @@ +.. Shaka Packager documentation master file, created by + sphinx-quickstart on Wed Aug 2 12:38:50 2017. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Shaka Packager's documentation! +========================================== + +source code repository: https://github.com/google/shaka-packager + +.. toctree:: + :maxdepth: 2 + + documentation.rst + tutorials/tutorials.rst + library.rst + +.. include a hidden TOC to avoid sphinx complaint. +.. toctree:: + :hidden: + + build_instructions.md + design.rst + docker_instructions.md + library_details.rst + tutorials/encoding.rst + tutorials/ffmpeg_piping.rst + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`search` diff --git a/_sources/library.rst.txt b/_sources/library.rst.txt new file mode 100644 index 0000000000..1cffb4abb2 --- /dev/null +++ b/_sources/library.rst.txt @@ -0,0 +1,37 @@ +Shaka Packager Library +====================== + +Documentation for the top level Shaka packager library. See +`Internal API `_ for +documentation on internal APIs. + +.. doxygenclass:: shaka::Packager + +Sample code: + +.. code-block:: c++ + + shaka::Packager packager; + + // Setup packaging parameters. + shaka::PackagingParams packaging_params; + // Use default parameters here. + + // Setup stream descriptors. + std::vector stream_descriptors; + shaka::StreamDescriptor stream_descriptor; + stream_descriptor.input = "input.mp4"; + stream_descriptor.stream_selector = "video"; + stream_descriptor.output = "output_video.mp4"; + stream_descriptors.push_back(stream_descriptor); + shaka::StreamDescriptor stream_descriptor; + stream_descriptor.input = "input.mp4"; + stream_descriptor.stream_selector = "audio"; + stream_descriptor.output = "output_audio.mp4"; + stream_descriptors.push_back(stream_descriptor); + + shaka::Status status = packager.Initialize(packaging_params, + stream_descriptors); + if (!status.ok()) { ... } + status = packager.Run(); + if (!status.ok()) { ... } diff --git a/_sources/library_details.rst.txt b/_sources/library_details.rst.txt new file mode 100644 index 0000000000..06f3d6024f --- /dev/null +++ b/_sources/library_details.rst.txt @@ -0,0 +1,44 @@ +Shaka Packager Library (Continued) +================================== + +.. doxygenstruct:: shaka::PackagingParams + +.. doxygenstruct:: shaka::StreamDescriptor + +.. doxygenstruct:: shaka::Mp4OutputParams + +.. doxygenstruct:: shaka::ChunkingParams + +.. doxygenstruct:: shaka::MpdParams + +.. doxygenstruct:: shaka::HlsParams + +.. doxygenenum:: shaka::HlsPlaylistType + +.. doxygenenum:: shaka::KeyProvider + +.. doxygenstruct:: shaka::WidevineSigner + +.. doxygenenum:: shaka::WidevineSigner::SigningKeyType + +.. doxygenstruct:: shaka::WidevineEncryptionParams + +.. doxygenstruct:: shaka::PlayreadyEncryptionParams + +.. doxygenstruct:: shaka::RawKeyEncryptionParams + +.. doxygenstruct:: shaka::RawKeyEncryptionParams::KeyPair + +.. doxygenstruct:: shaka::EncryptionParams + +.. doxygenstruct:: shaka::WidevineDecryptionParams + +.. doxygenstruct:: shaka::RawKeyDecryptionParams + +.. doxygenstruct:: shaka::RawKeyDecryptionParams::KeyPair + +.. doxygenstruct:: shaka::DecryptionParams + +.. doxygenclass:: shaka::Status + +.. doxygenenum:: shaka::error::Code diff --git a/_sources/options/chunking_options.rst.txt b/_sources/options/chunking_options.rst.txt new file mode 100644 index 0000000000..560a2a7cc8 --- /dev/null +++ b/_sources/options/chunking_options.rst.txt @@ -0,0 +1,23 @@ +Chunking options +^^^^^^^^^^^^^^^^ + +--segment_duration + + Segment duration in seconds. If single_segment is specified, this parameter + sets the duration of a subsegment; otherwise, this parameter sets the + duration of a segment. Actual segment durations may not be exactly as + requested. + +--fragment_duration + + Fragment duration in seconds. Should not be larger than the segment + duration. Actual fragment durations may not be exactly as requested. + +--segment_sap_aligned + + Force segments to begin with stream access points. Default enabled. + +--fragment_sap_aligned + + Force fragments to begin with stream access points. This flag implies + *segment_sap_aligned*. Default enabled. diff --git a/_sources/options/dash_options.rst.txt b/_sources/options/dash_options.rst.txt new file mode 100644 index 0000000000..749e68def4 --- /dev/null +++ b/_sources/options/dash_options.rst.txt @@ -0,0 +1,45 @@ +DASH options +^^^^^^^^^^^^ + +--generate_static_mpd + + If enabled, generates static mpd. If segment_template is specified in + stream descriptors, shaka-packager generates dynamic mpd by default; if + this flag is enabled, shaka-packager generates static mpd instead. Note + that if segment_template is not specified, shaka-packager always generates + static mpd regardless of the value of this flag. + +--mpd_output + + MPD output file name. + +--base_urls + + Comma separated BaseURLs for the MPD. The values will be added as + element(s) immediately under the element. + +--min_buffer_time + + Specifies, in seconds, a common duration used in the definition of the MPD + Representation data rate. + +--minimum_update_period + + Indicates to the player how often to refresh the media presentation + description in seconds. This value is used for dynamic MPD only. + +--time_shift_buffer_depth + + Guaranteed duration of the time shifting buffer for dynamic media + presentations, in seconds. + +--suggested_presentation_delay + + Specifies a delay, in seconds, to be added to the media presentation time. + This value is used for dynamic MPD only. + +--default_language + + Any tracks tagged with this language will have + in the manifest. This allows the player to choose the correct default + language for the content. diff --git a/_sources/options/general_encryption_options.rst.txt b/_sources/options/general_encryption_options.rst.txt new file mode 100644 index 0000000000..654b2a163c --- /dev/null +++ b/_sources/options/general_encryption_options.rst.txt @@ -0,0 +1,15 @@ +General encryption options +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +--protection_scheme + + Specify a protection scheme, 'cenc' or 'cbc1' or pattern-based protection + schemes 'cens' or 'cbcs'. + +--vp9_subsample_encryption, --novp9_subsample_encryption + + Enable / disable VP9 subsample encryption. Enabled by default. + +--clear_lead + + Clear lead in seconds if encryption is enabled. diff --git a/_sources/options/hls_options.rst.txt b/_sources/options/hls_options.rst.txt new file mode 100644 index 0000000000..d36e28dc85 --- /dev/null +++ b/_sources/options/hls_options.rst.txt @@ -0,0 +1,18 @@ +HLS options +^^^^^^^^^^^ + +--hls_master_playlist_output + + Output path for the master playlist for HLS. This flag must be used to + output HLS. + +--hls_base_url + + The base URL for the Media Playlists and media files listed in the + playlists. This is the prefix for the files. + +--hls_playlist_type + + VOD, EVENT, or LIVE. This defines the EXT-X-PLAYLIST-TYPE in the HLS + specification. For hls_playlist_type of LIVE, EXT-X-PLAYLIST-TYPE tag is + omitted. diff --git a/_sources/options/hls_stream_descriptors.rst.txt b/_sources/options/hls_stream_descriptors.rst.txt new file mode 100644 index 0000000000..384df1d1cb --- /dev/null +++ b/_sources/options/hls_stream_descriptors.rst.txt @@ -0,0 +1,18 @@ +HLS specific stream descriptor fields +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:hls_name: + + Required for audio when outputting HLS. name of the output stream. This is + not (necessarily) the same as output. This is used as the NAME attribute for + EXT-X-MEDIA. + +:hls_group_id: + + Required for audio when outputting HLS. The group ID for the output stream. + This is used as the GROUP-ID attribute for EXT-X-MEDIA. + +:playlist_name: + + Required for HLS output. Name of the playlist for the stream. Usually ends + with '.m3u8'. diff --git a/_sources/options/mp4_output_options.rst.txt b/_sources/options/mp4_output_options.rst.txt new file mode 100644 index 0000000000..7b9ad64715 --- /dev/null +++ b/_sources/options/mp4_output_options.rst.txt @@ -0,0 +1,20 @@ +MP4 output options +^^^^^^^^^^^^^^^^^^ + +--mp4_include_pssh_in_stream + + MP4 only: include pssh in the encrypted stream. Default enabled. + +--mp4_use_decoding_timestamp_in_timeline + + If set, decoding timestamp instead of presentation timestamp will be used + when generating media timeline, e.g. timestamps in sidx and mpd. This is + to workaround a Chromium bug that decoding timestamp is used in buffered + range, https://crbug.com/398130. Default false. + +--num_subsegments_per_sidx + + Set the number of subsegments in each SIDX box. If 0, a single SIDX box is + used per segment; if -1, no SIDX box is used; Otherwise, the muxer packs N + subsegments in the root SIDX of the segment, with + segment_duration/N/fragment_duration fragments per subsegment. diff --git a/_sources/options/playready_encryption_options.rst.txt b/_sources/options/playready_encryption_options.rst.txt new file mode 100644 index 0000000000..78b5740b20 --- /dev/null +++ b/_sources/options/playready_encryption_options.rst.txt @@ -0,0 +1,39 @@ +Playready encryption options +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +--enable_playready_encryption + + Enable encryption with playready key. + +--playready_server_url + + Playready packaging server url. + +--program_identifier + + Program identifier for packaging request. + +--playready_key_id + + Playready key id in hex. + +--playready_key + + Playready key in hex. + +--ca_file + + Absolute path to the certificate authority file for the server cert. + PEM format. + +--client_cert_file + + Absolute path to client certificate file. + +--client_cert_private_key_file + + Absolute path to the private key file. + +--client_cert_private_key_password + + Password to the private key file. diff --git a/_sources/options/raw_key_encryption_options.rst.txt b/_sources/options/raw_key_encryption_options.rst.txt new file mode 100644 index 0000000000..2e34d1d6c8 --- /dev/null +++ b/_sources/options/raw_key_encryption_options.rst.txt @@ -0,0 +1,30 @@ +Raw key encryption options +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +--enable_fixed_key_encryption + + Enable encryption with fixed key. + +--enable_fixed_key_decryption + + Enable decryption with fixed key. + +--key_id <32-digit hex string> + + The key id in hex string format. + HEX. + +--key <32-digit hex string> + + The key in hex string format. + +--iv <16-digit or 32-digit hex string> + + IV in hex string format. If not specified, a random IV will be generated. + This flag should only be used for testing. IV must be either 8 bytes + (16 digits HEX) or 16 bytes (32 digits in HEX). + +--pssh + + One or more concatenated PSSH boxes in hex string format. If not specified, + a `v1 common PSSH box `_ will be generated. diff --git a/_sources/options/segment_template_formatting.rst.txt b/_sources/options/segment_template_formatting.rst.txt new file mode 100644 index 0000000000..0f05c2efb1 --- /dev/null +++ b/_sources/options/segment_template_formatting.rst.txt @@ -0,0 +1,51 @@ +Segment template formatting +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The implementation is based on *Template-based Segment URL construction* +described in ISO/IEC 23009-1:2014. + +.. table:: Supported identifiers + + ============== ============================== ============================== + $$ Substitution parameter Format + ============== ============================== ============================== + $$ is an escape sequence, i.e. Not applicable. + "$$" is replaced with a single + "$". + $Number$ This identifier is substitued The format tag may be present. + with the *number* of the + corresponding Segment. If no format tag is present, a + default format tag with + *width*\=1 shall be used. + $Time$ This identifier is substituted The format tag may be present. + with the value of the + **SegmentTimeline@t** If no format tag is present, a + attribute for the Segment default format tag with + being accessed. Either *width*\=1 shall be used. + $Number$ or $Time$ may be used + but not both at the same time. + ============== ============================== ============================== + +.. note:: + + Identifiers $RepresentationID$ and $Bandwidth$ are not supported in this + version. Please file an `issue + `_ if you want it to be + supported. + +In each URL, the identifiers shall be replaced by the substitution parameter +per the definition in the above table. Identifier matching is case-sensitive. + +Each identifier may be suffixed, within the enclosing '$' characters, with an +additional format tag aligned with the *printf* format tag as defined in IEEE +1003.1-2008 following this prototype:: + + %0[width]d + +The *width* parameter is an unsigned integer that provides the minimum number +of characters to be printed. If the value to be printed is shorter than this +number, the result shall be padded with zeros. The value is not truncated even +if the result is larger. + +Strings outside identifiers shall only contain characters that are permitted +within URLs according to RFC 3986. diff --git a/_sources/options/stream_descriptors.rst.txt b/_sources/options/stream_descriptors.rst.txt new file mode 100644 index 0000000000..ad0fcc6305 --- /dev/null +++ b/_sources/options/stream_descriptors.rst.txt @@ -0,0 +1,71 @@ +Stream descriptors +^^^^^^^^^^^^^^^^^^ + +There can be multiple *stream_descriptor* with input from the same "file" or +multiple different "files". + +Stream is of the form:: + + {field}={value}[,{field}={value}]... + +These are the available fields: + +:input (in): + + input/source media "file" path, which can be regular files, pipes, udp + streams. See :doc:`/options/udp_file_options` on additional options for UDP + files. + +:stream_selector (stream): + + Required field with value 'audio', 'video', 'text' or stream number (zero + based). + +:output (out): + + Required output file path (single file). + +:init_segment: + + initialization segment path (multiple file). + +:segment_template (segment): + + Optional value which specifies the naming pattern for the segment files, + and that the stream should be split into multiple files. Its presence should + be consistent across streams. See + :doc:`/options/segment_template_formatting`. + +:bandwidth (bw): + + Optional value which contains a user-specified content bit rate for the + stream, in bits/sec. If specified, this value is propagated to (HLS) + EXT-X-STREAM-INF:BANDWIDTH or (DASH) Representation@bandwidth and the + $Bandwidth$ template parameter for segment names. If not specified, the + bandwidth value is estimated from content bitrate. Note that it only affects + the generated manifests/playlists; it has no effect on the media content + itself. + +:language (lang): + + Optional value which contains a user-specified language tag. If specified, + this value overrides any language metadata in the input stream. + +:output_format (format): + + Optional value which specifies the format of the output files (MP4 or WebM). + If not specified, it will be derived from the file extension of the output + file. + +:skip_encryption=0|1: + + Optional. Defaults to 0 if not specified. If it is set to 1, no encryption + of the stream will be made. + +:trick_play_factor (tpf): + + Optional value which specifies the trick play, a.k.a. trick mode, stream + sampling rate among key frames. If specified, the output is a trick play + stream. + +.. include:: /options/hls_stream_descriptors.rst diff --git a/_sources/options/udp_file_options.rst.txt b/_sources/options/udp_file_options.rst.txt new file mode 100644 index 0000000000..05de7102dd --- /dev/null +++ b/_sources/options/udp_file_options.rst.txt @@ -0,0 +1,18 @@ +UDP file options +^^^^^^^^^^^^^^^^ + +UDP file is of the form udp://ip:port[?options]. Here is the list of supported +options: + +:reuse=0|1: + + Allow or disallow reusing UDP sockets. + +:interface=, source=: + + Multicast group interface address. Only the packets sent to this address is + received. + +:timeout=: + + UDP timeout in microseconds. diff --git a/_sources/options/widevine_encryption_options.rst.txt b/_sources/options/widevine_encryption_options.rst.txt new file mode 100644 index 0000000000..78b9a3a8dd --- /dev/null +++ b/_sources/options/widevine_encryption_options.rst.txt @@ -0,0 +1,75 @@ +Widevine encryption options +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +--enable_widevine_encryption + + Enable encryption with Widevine key server. User should provide either + AES signing key (--aes_signing_key, --aes_signing_iv) or RSA signing key + (--rsa_signing_key_path). + +--enable_widevine_decryption + + Enable decryption with Widevine key server. User should provide either + AES signing key (--aes_signing_key, --aes_signing_iv) or RSA signing key + (--rsa_signing_key_path). + +--include_common_pssh + + When using Widevine encryption, include an additional v1 PSSH box for the + common system ID that includes the key IDs. See https://goo.gl/s8RIhr. + +--key_server_url + + Key server url. Required for Widevine encryption and decryption. + +--content_id + + Content identifier that uniquely identifies the content. + +--policy + + The name of a stored policy, which specifies DRM content rights. + +--max_sd_pixels + + The video track is considered SD if its max pixels per frame is no higher + than *max_sd_pixels*. Default: 442368 (768 x 576). + +--max_hd_pixels + + The video track is considered HD if its max pixels per frame is higher than + *max_sd_pixels*, but no higher than *max_hd_pixels*. Default: 2073600 + (1920 x 1080). + +--max_uhd1_pixels + + The video track is considered UHD1 if its max pixels per frame is higher + than *max_hd_pixels*, but no higher than *max_uhd1_pixels*. Otherwise it is + UHD2. Default: 8847360 (4096 x 2160). + +--signer + + The name of the signer. + +--aes_signing_key + + AES signing key in hex string. *aes_signing_iv* is required if + *aes_signing_key* is specified. This option is exclusive with + *rsa_signing_key_path*. + +--aes_signing_iv + + AES signing iv in hex string. + +--rsa_signing_key_path + + Path to the file containing PKCS#1 RSA private key for request signing. + This option is exclusive with *aes_signing_key*. + +--crypto_period_duration + + Defines how often key rotates. If it is non-zero, key rotation is enabled. + +--group_id + + Identifier for a group of licenses. diff --git a/_sources/tutorials/basic_usage.md.txt b/_sources/tutorials/basic_usage.md.txt new file mode 100644 index 0000000000..259df894e8 --- /dev/null +++ b/_sources/tutorials/basic_usage.md.txt @@ -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/_sources/tutorials/dash.rst.txt b/_sources/tutorials/dash.rst.txt new file mode 100644 index 0000000000..92f1e86a74 --- /dev/null +++ b/_sources/tutorials/dash.rst.txt @@ -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/_sources/tutorials/drm.rst.txt b/_sources/tutorials/drm.rst.txt new file mode 100644 index 0000000000..c3b2d971b4 --- /dev/null +++ b/_sources/tutorials/drm.rst.txt @@ -0,0 +1,9 @@ +DRM +=== + +.. toctree:: + :maxdepth: 2 + + /tutorials/raw_key.rst + /tutorials/widevine.rst + /tutorials/playready.rst diff --git a/_sources/tutorials/encoding.rst.txt b/_sources/tutorials/encoding.rst.txt new file mode 100644 index 0000000000..c25e5a5fbb --- /dev/null +++ b/_sources/tutorials/encoding.rst.txt @@ -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/_sources/tutorials/ffmpeg_piping.rst.txt b/_sources/tutorials/ffmpeg_piping.rst.txt new file mode 100644 index 0000000000..2625f8d943 --- /dev/null +++ b/_sources/tutorials/ffmpeg_piping.rst.txt @@ -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/_sources/tutorials/hls.rst.txt b/_sources/tutorials/hls.rst.txt new file mode 100644 index 0000000000..4baba4672c --- /dev/null +++ b/_sources/tutorials/hls.rst.txt @@ -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/_sources/tutorials/live.rst.txt b/_sources/tutorials/live.rst.txt new file mode 100644 index 0000000000..d38f013904 --- /dev/null +++ b/_sources/tutorials/live.rst.txt @@ -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/_sources/tutorials/playready.rst.txt b/_sources/tutorials/playready.rst.txt new file mode 100644 index 0000000000..59eaa6dfc9 --- /dev/null +++ b/_sources/tutorials/playready.rst.txt @@ -0,0 +1,4 @@ +Playready +========= + +To be completed. diff --git a/_sources/tutorials/raw_key.rst.txt b/_sources/tutorials/raw_key.rst.txt new file mode 100644 index 0000000000..a477d04d58 --- /dev/null +++ b/_sources/tutorials/raw_key.rst.txt @@ -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/_sources/tutorials/tutorials.rst.txt b/_sources/tutorials/tutorials.rst.txt new file mode 100644 index 0000000000..5ce31569ee --- /dev/null +++ b/_sources/tutorials/tutorials.rst.txt @@ -0,0 +1,11 @@ +Tutorials +==================== + +.. toctree:: + :maxdepth: 2 + + basic_usage.md + dash.rst + hls.md + live.md + drm.rst diff --git a/_sources/tutorials/widevine.rst.txt b/_sources/tutorials/widevine.rst.txt new file mode 100644 index 0000000000..e8f2861be1 --- /dev/null +++ b/_sources/tutorials/widevine.rst.txt @@ -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 diff --git a/_static/ajax-loader.gif b/_static/ajax-loader.gif new file mode 100644 index 0000000000..61faf8cab2 Binary files /dev/null and b/_static/ajax-loader.gif differ diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 0000000000..7ed0e58edb --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,632 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox input[type="text"] { + width: 170px; +} + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px 7px 0 7px; + background-color: #ffe; + width: 40%; + float: right; +} + +p.sidebar-title { + font-weight: bold; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px 7px 0 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +div.admonition dl { + margin-bottom: 0; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + border: 0; + border-collapse: collapse; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +dl { + margin-bottom: 15px; +} + +dd p { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dt:target, .highlighted { + background-color: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; +} + +td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + margin-left: 0.5em; +} + +table.highlighttable td { + padding: 0 0.5em 0 0.5em; +} + +div.code-block-caption { + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +div.code-block-caption + div > div.highlight > pre { + margin-top: 0; +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + padding: 1em 1em 0; +} + +div.literal-block-wrapper div.highlight { + margin: 0; +} + +code.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; +} + +code.descclassname { + background-color: transparent; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: relative; + left: 0px; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/comment-bright.png b/_static/comment-bright.png new file mode 100644 index 0000000000..15e27edb12 Binary files /dev/null and b/_static/comment-bright.png differ diff --git a/_static/comment-close.png b/_static/comment-close.png new file mode 100644 index 0000000000..4d91bcf57d Binary files /dev/null and b/_static/comment-close.png differ diff --git a/_static/comment.png b/_static/comment.png new file mode 100644 index 0000000000..dfbc0cbd51 Binary files /dev/null and b/_static/comment.png differ diff --git a/_static/contents.png b/_static/contents.png new file mode 100644 index 0000000000..6c59aa1f9c Binary files /dev/null and b/_static/contents.png differ diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 0000000000..8163495635 --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,287 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for all documentation. + * + * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + +/** + * make the code below compatible with browsers without + * an installed firebug like debugger +if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", + "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", + "profile", "profileEnd"]; + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +} + */ + +/** + * small helper function to urldecode strings + */ +jQuery.urldecode = function(x) { + return decodeURIComponent(x).replace(/\+/g, ' '); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s == 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node) { + if (node.nodeType == 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) { + var span = document.createElement("span"); + span.className = className; + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this); + }); + } + } + return this.each(function() { + highlight(this); + }); +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initIndexTable(); + + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated == 'undefined') + return string; + return (typeof translated == 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated == 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + if (!body.length) { + body = $('body'); + } + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlighted'); + }); + }, 10); + $('') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) == 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this == '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + }, + + initOnKeyListeners: function() { + $(document).keyup(function(event) { + var activeElementType = document.activeElement.tagName; + // don't navigate when in search box or textarea + if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') { + switch (event.keyCode) { + case 37: // left + var prevHref = $('link[rel="prev"]').prop('href'); + if (prevHref) { + window.location.href = prevHref; + return false; + } + case 39: // right + var nextHref = $('link[rel="next"]').prop('href'); + if (nextHref) { + window.location.href = nextHref; + return false; + } + } + } + }); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); \ No newline at end of file diff --git a/_static/down-pressed.png b/_static/down-pressed.png new file mode 100644 index 0000000000..5756c8cad8 Binary files /dev/null and b/_static/down-pressed.png differ diff --git a/_static/down.png b/_static/down.png new file mode 100644 index 0000000000..1b3bdad2ce Binary files /dev/null and b/_static/down.png differ diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 0000000000..a858a410e4 Binary files /dev/null and b/_static/file.png differ diff --git a/_static/jquery-3.1.0.js b/_static/jquery-3.1.0.js new file mode 100644 index 0000000000..f2fc274787 --- /dev/null +++ b/_static/jquery-3.1.0.js @@ -0,0 +1,10074 @@ +/*eslint-disable no-unused-vars*/ +/*! + * jQuery JavaScript Library v3.1.0 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2016-07-07T21:44Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var document = window.document; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + + + + function DOMEval( code, doc ) { + doc = doc || document; + + var script = doc.createElement( "script" ); + + script.text = code; + doc.head.appendChild( script ).parentNode.removeChild( script ); + } +/* global Symbol */ +// Defining this global in .eslintrc would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.1.0", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android <=4.0 only + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = jQuery.isArray( copy ) ) ) ) { + + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray( src ) ? src : []; + + } else { + clone = src && jQuery.isPlainObject( src ) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isFunction: function( obj ) { + return jQuery.type( obj ) === "function"; + }, + + isArray: Array.isArray, + + isWindow: function( obj ) { + return obj != null && obj === obj.window; + }, + + isNumeric: function( obj ) { + + // As of jQuery 3.0, isNumeric is limited to + // strings and numbers (primitives or objects) + // that can be coerced to finite numbers (gh-2662) + var type = jQuery.type( obj ); + return ( type === "number" || type === "string" ) && + + // parseFloat NaNs numeric-cast false positives ("") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + !isNaN( obj - parseFloat( obj ) ); + }, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + + /* eslint-disable no-unused-vars */ + // See https://github.com/eslint/eslint/issues/6125 + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + DOMEval( code ); + }, + + // Convert dashed to camelCase; used by the css and data modules + // Support: IE <=9 - 11, Edge 12 - 13 + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android <=4.0 only + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: Date.now, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.0 + * https://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2016-01-04 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + disabledAncestor = addCombinator( + function( elem ) { + return elem.disabled === true; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !compilerCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + + if ( nodeType !== 1 ) { + newContext = context; + newSelector = selector; + + // qSA looks outside Element context, which is not what we want + // Thanks to Andrew Dupont for this workaround technique + // Support: IE <=8 + // Exclude object elements + } else if ( context.nodeName.toLowerCase() !== "object" ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[i] = "#" + nid + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement("fieldset"); + + try { + return !!fn( el ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + // Known :disabled false positives: + // IE: *[disabled]:not(button, input, select, textarea, optgroup, option, menuitem, fieldset) + // not IE: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Check form elements and option elements for explicit disabling + return "label" in elem && elem.disabled === disabled || + "form" in elem && elem.disabled === disabled || + + // Check non-disabled form elements for fieldset[disabled] ancestors + "form" in elem && elem.disabled === false && ( + // Support: IE6-11+ + // Ancestry is covered for us + elem.isDisabled === disabled || + + // Otherwise, assume any non-