{"id":29190849,"url":"https://github.com/diogoazevedos/nginx-vod-module","last_synced_at":"2025-07-02T00:12:08.966Z","repository":{"id":285702902,"uuid":"925259609","full_name":"diogoazevedos/nginx-vod-module","owner":"diogoazevedos","description":"NGINX-based MP4 Repackager","archived":false,"fork":false,"pushed_at":"2025-06-29T08:28:34.000Z","size":4802,"stargazers_count":6,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-29T09:31:30.698Z","etag":null,"topics":["dash","drm","fairplay","hls","nginx","playready","stream","streaming","video","vod","widevine"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"kaltura/nginx-vod-module","license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/diogoazevedos.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-01-31T14:39:56.000Z","updated_at":"2025-06-29T08:28:36.000Z","dependencies_parsed_at":"2025-05-24T10:21:50.890Z","dependency_job_id":"7032ddc8-1d4a-41f0-8de7-c3247ace5071","html_url":"https://github.com/diogoazevedos/nginx-vod-module","commit_stats":null,"previous_names":["diogoazevedos/nginx-vod-module"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/diogoazevedos/nginx-vod-module","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diogoazevedos%2Fnginx-vod-module","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diogoazevedos%2Fnginx-vod-module/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diogoazevedos%2Fnginx-vod-module/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diogoazevedos%2Fnginx-vod-module/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/diogoazevedos","download_url":"https://codeload.github.com/diogoazevedos/nginx-vod-module/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diogoazevedos%2Fnginx-vod-module/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263052434,"owners_count":23406106,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dash","drm","fairplay","hls","nginx","playready","stream","streaming","video","vod","widevine"],"created_at":"2025-07-02T00:12:08.230Z","updated_at":"2025-07-02T00:12:08.936Z","avatar_url":"https://github.com/diogoazevedos.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NGINX-based VOD Packager\n## nginx-vod-module [![CI](https://github.com/diogoazevedos/nginx-vod-module/actions/workflows/main.yml/badge.svg)](https://github.com/diogoazevedos/nginx-vod-module/actions/workflows/main.yml)\n\n### Features\n\n- On-the-fly repackaging of MP4 files to DASH or HLS.\n- Working modes:\n\n  - Local - serve locally accessible files (local disk or mounted NFS).\n  - Remote - serve files accessible via HTTP using range requests.\n  - Mapped - serve files according to a specification encoded in JSON format. The JSON can pulled\n    from a remote server, or read from a local file.\n\n- Adaptive bitrate support.\n- Playlist support (playing several different media files one after the other) - `mapped` mode only.\n- Simulated live support (generating a live stream from MP4 files) - `mapped` mode only.\n- Fallback support for file not found in `local` or `mapped` modes (useful in multi-datacenter\n  environments).\n- Video codecs: H264, H265, AV1, VP8 (DASH), and VP9 (DASH).\n- Audio codecs: AAC, MP3 (HLS), AC-3, E-AC-3, VORBIS (DASH), OPUS (DASH), FLAC (HLS), and DTS (HLS).\n- Captions support:\n\n  Input:\n\n  - WebVTT\n  - SRT\n  - DFXP/TTML (requires `libxml2`)\n  - CAP (Cheetah)\n\n  Output:\n\n  - DASH - either sidecar WebVTT or SMPTE-TT segments (configurable).\n  - HLS - segmented WebVTT.\n\n- Audio or video only files.\n- Alternative audio renditions - supporting both:\n\n  - Generation of manifest with different audio renditions, allowing selection on the client side.\n  - Muxing together audio and video streams from separate files / tracks - provides the ability to\n    serve different audio renditions of a single video, without the need for any special support on\n    the client side.\n\n- Track selection for multi audio/video MP4 files.\n- Playback rate change - 0.5x up to 2x (requires `libavcodec` and `libavfilter`).\n- Source file clipping (only from I-Frame to P-frame).\n- Support for variable segment lengths - enabling the player to select the optimal bitrate fast,\n  without the overhead of short segments for the whole duration of the video.\n- Clipping of MP4 files for progressive download playback.\n- Thumbnail capture (requires `libavcodec`) and resize (requires `libswscale`).\n- Volume map (requires `libavcodec`) - returns a CSV containing the volume level in each interval.\n- Decryption of CENC-encrypted MP4 files (it is possible to create such files with MP4Box).\n- DASH: common encryption (CENC) support.\n- HLS: support for AES-128 / SAMPLE-AES encryption.\n- HLS: Generation of I-frames playlist (`#EXT-X-I-FRAMES-ONLY`).\n\n### Limitations\n\n- Track selection and playback rate change are not supported in progressive download.\n- I-frames playlist generation is not supported when encryption is enabled.\n\n### Compilation\n\n#### Dependencies\n\nIn general, if the required dependencies for building NGINX are present, it should be possible to\nbuild `nginx-vod-module`. However, some optional features of the module require additional\ndependencies. These are detected during the `configure` step — if any dependency is missing, the\ncorresponding feature will be disabled.\n\nThe optional features are:\n\n- Thumbnail capture and volume map - depends on [`ffmpeg`](https://ffmpeg.org).\n- Audio filtering (for changing playback rate / gain) - depends on [`ffmpeg`](https://ffmpeg.org)\n  and `libfdk_aac`.\n- Encryption / decryption (DRM / HLS AES) - depends on `openssl`.\n- DFXP captions - depends on `libxml2`.\n- UTF-16 encoded SRT files - depends on `iconv`.\n\n#### Build\n\nTo link statically against NGINX, use:\n\n```sh\n./configure --add-module=/path/to/nginx-vod-module\nmake\nmake install\n ```\n\nTo compile as a dynamic module, use:\n\n```sh\n./configure --add-dynamic-module=/path/to/nginx-vod-module\n```\n\n\u003e [!NOTE]\n\u003e The `load_module` directive should be used in `nginx.conf` to load the module.\n\nOptional recommended settings:\n\n- `--with-file-aio` - enable asynchronous I/O support, highly recommended, relevant only to `local`\n  and `mapped` modes.\n- `--with-threads` - enable asynchronous file open using thread pool (also requires\n  `vod_open_file_thread_pool` in `nginx.conf`), relevant only to `local` and `mapped` modes.\n- `--with-cc-opt='-O3 -mpopcnt'` - enable additional compiler optimizations (about 8% reduction\n  noticed in the MP4 parse time and frame processing time compared to the default `-O`).\n  \u003e The `-mpopcnt` is only available on `x86_64` architectures.\n\nDebug settings:\n\n- `--with-debug` - enable debug messages (also requires passing `debug` in the `error_log` directive\n  in `nginx.conf`).\n- `--with-cc-opt='-O0'` - disable compiler optimizations (for debugging with `gdb`).\n\nC Macro Configurations:\n\n- `--with-cc-opt='-DNGX_VOD_MAX_TRACK_COUNT=256 -mavx2'` - increase the maximum track count\n  (preferably to multiples of `64`). It's recommended to enable vector extensions (`AVX2`) as well.\n\nIf you wish to make use of the following features:\n\n- Thumbnail capture\n- Playback rate change - 0.5x up to 2x\n\nYou will also need to install the [`ffmpeg`](https://ffmpeg.org).\n\n#### Quick start\n\nYou can build and run a Docker image using the provided `Dockerfile`, which includes a basic\nimplementation of `nginx-vod-module` with all required dependencies.\n\n```sh\ndocker image build -t nginx-vod-module .\ndocker container run --rm -it -p 8000:80 nginx-vod-module\n```\n\n\u003e [!TIP]\n\u003e The sample configuration files are available in the [`sample` folder](./sample). You can customize\n\u003e the configurations based on your specific requirements.\n\n```sh\nnpx http-server demo -p 8080 -g --cors\n```\n\nWatch the demo content in your browser by visiting\n[Tears of Steel demo](https://shaka-player-demo.appspot.com/demo/#assetBase64=eyJtYW5pZmVzdFVyaSI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODAwMC9obHMvY2xlYXIvdGVhcnMtb2Ytc3RlZWwvLm0zdTgifQ).\n\n\u003e [!TIP]\n\u003e Download the sample media files\n\u003e [`tears-of-steel.zip`](https://media-sample-storage.s3.eu-central-1.amazonaws.com/tears-of-steel.zip)\n\u003e and extract them at `demo/storage/tears-of-steel`.\n\n### URL structure\n\n#### Basic URL structure\n\nThe basic structure of the URL is: `https://\u003cdomain\u003e/\u003clocation\u003e/\u003cfileuri\u003e/\u003cfilename\u003e`\n\nWhere:\n\n- `domain` - the domain of the `nginx-vod-module` server.\n- `location` - the `location` specified in the `nginx.conf`.\n- `fileuri` - a URI to the MP4 file:\n  - `local` mode - the full file path is determined according to the root / alias `nginx.conf`\n    directives.\n  - `mapped` mode - the full file path is determined according to the JSON received from the\n    upstream / local file.\n  - `remote` mode - the MP4 file is read from upstream in chunks.\n- `filename` - detailed below.\n\n\u003e [!NOTE]\n\u003e In `mapped` and `remote` modes, the URL of the upstream request is\n\u003e `https://\u003cupstream\u003e/\u003clocation\u003e/\u003cfileuri\u003e?\u003cextra_args\u003e` (`extra_args` is determined by the\n\u003e `vod_upstream_extra_args` parameter).\n\n#### Multi URL structure\n\nMulti URLs are used to encode several URLs on a single URL. A multi URL can be used to specify the\nURLs of several different MP4 files that should be included together in a DASH MPD for example.\n\nThe structure of a multi URL is:\n\n`http://\u003cdomain\u003e/\u003clocation\u003e/\u003cprefix\u003e,\u003cmiddle1\u003e,\u003cmiddle2\u003e,\u003cmiddle3\u003e,\u003cpostfix\u003e.urlset/\u003cfilename\u003e`\n\nThe sample URL above represents 3 URLs:\n\n- `http://\u003cdomain\u003e/\u003clocation\u003e/\u003cprefix\u003e\u003cmiddle1\u003e\u003cpostfix\u003e/\u003cfilename\u003e`\n- `http://\u003cdomain\u003e/\u003clocation\u003e/\u003cprefix\u003e\u003cmiddle2\u003e\u003cpostfix\u003e/\u003cfilename\u003e`\n- `http://\u003cdomain\u003e/\u003clocation\u003e/\u003cprefix\u003e\u003cmiddle3\u003e\u003cpostfix\u003e/\u003cfilename\u003e`\n\nThe suffix `.urlset` (can be changed using `vod_multi_uri_suffix`) indicates that the URL should be\ntreated as a multi URL. For example - the URL\n`http://\u003cdomain\u003e/hls/big_buck_bunny_,6,9,15,00k.mp4.urlset/master.m3u8` will return a manifest\ncontaining:\n\n- `http://\u003cdomain\u003e/hls/big_buck_bunny_600k.mp4/index.m3u8`\n- `http://\u003cdomain\u003e/hls/big_buck_bunny_900k.mp4/index.m3u8`\n- `http://\u003cdomain\u003e/hls/big_buck_bunny_1500k.mp4/index.m3u8`\n\n#### URL path parameters\n\nThe following parameters are supported on the URL path:\n\n- `clipFrom` - an offset in milliseconds since the beginning of the video, where the generated\n  stream should start. For example, `.../clipFrom/10000/...` will generate a stream that starts 10\n  seconds into the video.\n- `clipTo` - an offset in milliseconds since the beginning of the video, where the generated stream\n  should end. For example, `.../clipTo/60000/...` will generate a stream truncated to 60 seconds.\n- `tracks` - can be used to select specific audio/video tracks. The structure of the parameter is:\n  `v\u003cid1\u003e-v\u003cid2\u003e-a\u003cid1\u003e-a\u003cid2\u003e...` For example, `.../tracks/v1-a1/...` will select the first video\n  and audio track. The default is to include all tracks.\n- `shift` - can be used to apply a timing shift to one or more streams. The structure of the\n  parameter is: `v\u003cvshift\u003e-a\u003cashift\u003e-s\u003csshift\u003e` For example, `.../shift/v100/...` will apply a\n  forward shift of 100ms to the video timestamps.\n\n#### Filename structure\n\nThe structure of filename is:\n\n`\u003cbasename\u003e[\u003cseqparams\u003e][\u003cfileparams\u003e][\u003ctrackparams\u003e][\u003clangparams\u003e].\u003cextension\u003e`\n\nWhere:\n\n- `basename` + `extension` - the set of options is packager specific (the list below applies to the\n  default settings):\n\n  - `dash` - `manifest.mpd`.\n  - `hls` master playlist - `master.m3u8`.\n  - `hls` media playlist - `index.m3u8`.\n  - `thumb` - `thumb-\u003coffset\u003e[\u003cresizeparams\u003e].jpg` (offset is the thumbnail video offset in\n    milliseconds).\n  - `volume_map` - `volume_map.csv`.\n\n- `seqparams` - can be used to select specific sequences by ID (provided in the JSON mapping), e.g.\n  `master-sseq1.m3u8`.\n- `fileparams` - can be used to select specific sequences by index when using multi URLs. For\n  example, `manifest-f1.mpd` will return a MPD only from the first URL.\n- `trackparams` - can be used to select specific audio/video tracks. For example, `manifest-a1.mpd`\n  will return a MPD containing only the first audio stream of each sequence. The default is to\n  include the first audio and first video tracks of each file. The tracks selected on the file name\n  are AND-ed with the tracks selected with the `/tracks/` path parameter. `v0/a0` select all\n  video/audio tracks respectively. The A/V parameters can be combined with f/s, e.g. `f1-v1-f2-a1` =\n  `video1` of `file1` + `audio1` of `file2`, `f1-f2-v1` = `video1` of `file1` + `video1` of `file2`.\n- `langparams` - can be used to filter audio tracks/subtitles according to their language\n  (ISO 639-3 code). For example, `master-leng.m3u8` will return only english audio tracks.\n- `resizeparams` - can be used to resize the returned thumbnail image. For example,\n  `thumb-1000-w150-h100.jpg` captures a thumbnail *1 second* into the video, and resizes it to\n  `150x100`. If one of the dimensions is omitted, its value is set so that the resulting image will\n  retain the aspect ratio of the video frame.\n\n### Mapping response format\n\nWhen configured to run in mapped mode, `nginx-vod-module` issues an HTTP request to a configured\nupstream server in order to receive the layout of media streams it should generate. The response has\nto be in JSON format.\n\nThis section contains a few simple examples followed by a reference of the supported objects and\nfields. But first, a couple of definitions:\n\n- `Source Clip` - a set of audio and/or video frames (tracks) extracted from a single media file.\n- `Generator` - a component that can generate audio/video frames. Currently, the only supported\n  generator is the silence generator.\n- `Filter` - a manipulation that can be applied on audio/video frames. The following filters are\n  supported:\n\n  - `Rate` - (speed) change - applies to both audio and video.\n  - `Gain` - audio volume change.\n  - `Mix` - used to merge several audio tracks together, or to merge the audio of source A\n    with the video of source B.\n\n- `Clip` - the result of applying zero or more filters on a set of [source clips](#source-clip).\n- `Dynamic Clip` - a clip whose contents is not known in advance, e.g. targeted ad content.\n- `Sequence` - a set of clips that should be played one after the other.\n- `Set` - several sequences that play together as an adaptive set, each sequence must have the same\n  number of clips.\n\n#### Simple mapping\n\nThe JSON below maps the request URI to a single MP4 file:\n\n```json\n{\n  \"sequences\": [\n    {\n      \"clips\": [\n        {\n          \"type\": \"source\",\n          \"path\": \"/path/to/video.mp4\"\n        }\n      ]\n    }\n  ]\n}\n```\n\nWhen using multi URLs, this is the only allowed JSON pattern. In other words, it is not possible to\ncombine more complex JSONs using multi URL.\n\n#### Adaptive set\n\nAs an alternative to using multi URL, an adaptive set can be defined via JSON:\n\n```json\n{\n  \"sequences\": [\n    {\n      \"clips\": [\n        {\n          \"type\": \"source\",\n          \"path\": \"/path/to/bitrate1.mp4\"\n        }\n      ]\n    },\n    {\n      \"clips\": [\n        {\n          \"type\": \"source\",\n          \"path\": \"/path/to/bitrate2.mp4\"\n        }\n      ]\n    }\n  ]\n}\n```\n\n#### Playlist\n\nThe JSON below will play *35 seconds* of `video1` followed by *22 seconds* of `video2`:\n\n```json\n{\n  \"durations\": [35000, 22000],\n  \"sequences\": [\n    {\n      \"clips\": [\n        {\n          \"type\": \"source\",\n          \"path\": \"/path/to/video1.mp4\"\n        },\n        {\n          \"type\": \"source\",\n          \"path\": \"/path/to/video2.mp4\"\n        }\n      ]\n    }\n  ]\n}\n```\n\n#### Filters\n\nThe JSON below takes `video1`, plays it at *x1.5* and *mixes* the audio of the result with the audio\nof `video2`, after reducing it to *50% volume*:\n\n```json\n{\n  \"sequences\": [\n    {\n      \"clips\": [\n        {\n          \"type\": \"mixFilter\",\n          \"sources\": [\n            {\n              \"type\": \"rateFilter\",\n              \"rate\": 1.5,\n              \"source\": {\n                \"type\": \"source\",\n                \"path\": \"/path/to/video1.mp4\"\n              }\n            },\n            {\n              \"type\": \"gainFilter\",\n              \"gain\": 0.5,\n              \"source\": {\n                \"type\": \"source\",\n                \"path\": \"/path/to/video2.mp4\",\n                \"tracks\": \"a1\"\n              }\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}\n```\n\n#### Continuous live\n\nThe JSON below is a sample of a continuous live stream (a live stream in which all videos have\n**exactly** the same encoding parameters).\n\n\u003e [!TIP]\n\u003e In practice, this JSON will have to be generated by some script, since it is time dependent. (see\n\u003e `test/playlist.php` for a sample implementation).\n\n```json\n{\n  \"playlistType\": \"live\",\n  \"discontinuity\": false,\n  \"segmentBaseTime\": 1451904060000,\n  \"firstClipTime\": 1451917506000,\n  \"durations\": [83000, 83000],\n  \"sequences\": [\n    {\n      \"clips\": [\n        {\n          \"type\": \"source\",\n          \"path\": \"/path/to/video1.mp4\"\n        },\n        {\n          \"type\": \"source\",\n          \"path\": \"/path/to/video2.mp4\"\n        }\n      ]\n    }\n  ]\n}\n```\n\n#### Non-continuous live\n\nThe JSON below is a sample of a non-continuous live stream (a live stream in which the videos have\n**different** encoding parameters).\n\n\u003e [!TIP]\n\u003e In practice, this JSON will have to be generated by some script, since it is time dependent (see\n\u003e `test/playlist.php` for a sample implementation).\n\n```json\n{\n  \"playlistType\": \"live\",\n  \"discontinuity\": true,\n  \"initialClipIndex\": 171,\n  \"initialSegmentIndex\": 153,\n  \"firstClipTime\": 1451918170000,\n  \"durations\": [83000, 83000],\n  \"sequences\": [\n    {\n      \"clips\": [\n        {\n          \"type\": \"source\",\n          \"path\": \"/path/to/video1.mp4\"\n        },\n        {\n          \"type\": \"source\",\n          \"path\": \"/path/to/video2.mp4\"\n        }\n      ]\n    }\n  ]\n}\n```\n\n### Mapping reference\n\n#### Set\n\n\u003e Top-level object in the mapping.\n\nMandatory fields:\n\n- `sequences` - array of [sequence](#sequence) objects. The mapping has to contain at least one\n  sequence and up to *32 sequences*.\n\nOptional fields:\n\n- `id` - a string that identifies the set. The ID can be retrieved via `$vod_set_id`.\n- `playlistType` - a string whose value can be `live`, `vod` or `event` (only supported\n  for HLS playlists). Defaults to `vod`.\n- `durations` - an array of integers representing clip durations in milliseconds. This field is\n  **mandatory** if the mapping contains more than a single clip per sequence. If specified, this\n  array must contain at least one element and up to *128 elements*.\n- `discontinuity` - a boolean indicating whether the different clips in each sequence have different\n  media parameters. This field has different manifestations according to the delivery protocol - a\n  value of `true` will generate `#EXT-X-DISCONTINUITY` in HLS, and a multi-period MPD in DASH. The\n  default value is `true`, set to `false` only if the media files were transcoded with exactly the\n  same parameters (in AVC for example, the clips should have exactly the same SPS/PPS).\n- `segmentDuration` - an integer that sets the segment duration in milliseconds. This field, if\n  specified, takes priority over the value set in `vod_segment_duration`.\n- `consistentSequenceMediaInfo` - a boolean which currently affects only DASH. When set to `true`\n  (default) the MPD will report the same media parameters in each period element. Setting to `false`\n  can have severe performance implications for long sequences (`nginx-vod-module` has to read the\n  media info of all clips included in the mapping in order to generate the MPD).\n- `referenceClipIndex` - an integer that sets the (*1-based*) index of the clip that should be used\n  to retrieve the video metadata for manifest requests (such as codec, width, height) If\n  `consistentSequenceMediaInfo` is set to `false`, this parameter has no effect - all clips are\n  parsed. If this parameter is not specified, `nginx-vod-module` uses the last clip by default.\n- `notifications` - an array of [notification](#notification) objects, when a segment is requested,\n  all the notifications that fall between the start/end times of the segment are fired. The\n  notifications must be ordered in an increasing offset order.\n- `clipFrom` - an integer that contains a timestamp indicating where the returned stream should\n  start. Setting this parameter is equivalent to passing `/clipFrom/` in the URL.\n- `clipTo` - an integer that contains a timestamp indicating where the returned stream should end.\n  Setting this parameter is equivalent to passing `/clipTo/` in the URL.\n- `cache` - a boolean indicating whether the mapping response should be cached (`vod_mapping_cache`)\n  or not. Defaults to `true`, the response is cached.\n- `closedCaptions` - array of [closed captions](#closed-captions) objects, containing languages and\n  IDs of any embedded CEA-608 / CEA-708 captions. If an empty array is provided, the module will\n  output `CLOSED-CAPTIONS=NONE` on each `#EXT-X-STREAM-INF` tag. If the list does not appear in the\n  JSON, the module will not output any `CLOSED-CAPTIONS` fields in the playlist.\n\nLive fields:\n\n- `firstClipTime` - an integer, **mandatory** for all live playlists unless `clipTimes` is\n  specified, containing the absolute time of the first clip in the playlist in milliseconds since\n  the *epoch* (`unix_timestamp x 1000`).\n- `clipTimes` - an array of integers that sets the absolute time of all the clips in the playlist in\n  milliseconds since the *epoch* (`unix_timestamp x 1000`). This field can be used only when\n  `discontinuity` is set to `true`. The timestamps may contain gaps, but they are not allowed to\n  overlap (`clipTimes[n + 1] \u003e= clipTimes[n] + durations[n]`).\n- `segmentBaseTime` - an integer, **mandatory** for continuous live streams, containing the absolute\n  time of the first segment of the stream in milliseconds since the *epoch*\n  (`unix_timestamp x 1000`). This value must not change during playback. For discontinuous live\n  streams, this field is optional:\n\n  - if **not** set, sequential segment indexes will be used throughout the playlist. In this case,\n    the upstream server generating the JSON mapping has to maintain state, and update\n    `initialSegmentIndex` every time a clip is removed from the playlist.\n  - if set, the timing gaps between clips must not be lower than `vod_segment_duration`.\n\n- `firstClipStartOffset` - an integer, *optional*, measured in milliseconds, containing the\n  difference between first clip time, and the original start time of the first clip - the time it\n  had when it was initially added (before the live window shifted).\n- `initialClipIndex` - an integer, **mandatory** for non-continuous live streams that mix videos\n  having different encoding parameters (SPS/PPS), containg the index of the first clip in the\n  playlist. Whenever a clip is pushed out of the head of the playlist, this value must be\n  incremented by one.\n- `initialSegmentIndex` - an integer, **mandatory** for live streams that do not set\n  `segmentBaseTime`, containing the index of the first segment in the playlist. Whenever a clip is\n  pushed out of the head of the playlist, this value must be incremented by the number of segments\n  in the clip.\n- `presentationEndTime` - an integer, *optional*, measured in milliseconds since the *epoch*. When\n  supplied, the module will compare the current time to the supplied value, and signal the end of\n  the live presentation if `presentationEndTime` has passed. In HLS, for example, this parameter\n  controls whether an `#EXT-X-ENDLIST` tag should be included in the media playlist. When the\n  parameter is **not** supplied, the module will **not** signal live presentation end.\n- `expirationTime` - an integer, *optional*, measured in milliseconds since the *epoch*. When\n  supplied, the module will compare the current time to the supplied value, and if `expirationTime`\n  has passed, the module will return a 404 error for manifest requests (segment requests will\n  continue to be served). When both `presentationEndTime` and `expirationTime` have passed,\n  `presentationEndTime` takes priority, i.e. manifest requests will be served and signal\n  presentation end.\n- `liveWindowDuration` - an integer, *optional*, providing a way to override\n  `vod_live_window_duration` specified in the configuration. If the value exceeds the absolute value\n  specified in `vod_live_window_duration`, it is ignored.\n- `timeOffset` - and integer that sets an offset that should be applied to the server clock when\n  serving live requests. This parameter can be used to test future/past events.\n\n#### Sequence\n\nMandatory fields:\n\n- `clips` - an array of [clip](#clip-abstract) objects (**mandatory**). The number of elements must\n  match the number the `durations` array specified on the set. If the `durations` array is **not**\n  specified, the clips array must contain a **single** element.\n\nOptional fields:\n\n- `id` - a string that identifies the sequence. The string must **not** contain `-`. The ID can be\n  retrieved by `$vod_sequence_id`.\n- `language` - a 3-letter (ISO-639-2) language code, this field takes priority over any language\n  specified on the media file (`mdhd` MP4 atom).\n- `label` - a friendly string that identifies the sequence. If a language is specified, a default\n  label will be automatically derived by it - e.g. if language is `ita`, by default `italiano`\n  will be used as the label.\n  \u003e For `roles`, `characteristics`, and `forced`, the label must be explicitly specified. The\n  \u003e default label does not consider them.\n\n- `roles` - an array of role schemes as defined in\n  [ISO/IEC 23009-1](https://www.iso.org/standard/83314.html) Section 5.8.5.5.\n- `default` - a boolean that sets the value of the `DEFAULT` attribute of `#EXT-X-MEDIA` tags using\n  this sequence. If not specified, the first `#EXT-X-MEDIA` tag in each group returns `DEFAULT=YES`.\n- `autoselect` - a boolean that sets the value of the `AUTOSELECT` attribute of `#EXT-X-MEDIA` tags\n  using this sequence. If not specified, the `#EXT-X-MEDIA` tags return `AUTOSELECT=YES`.\n- `characteristics` - a string of characteristics as defined in\n  [RFC 8216 Section 4.3.4.1](https://datatracker.ietf.org/doc/html/rfc8216#section-4.3.4.1).\n- `forced` - a boolean that sets the value of the `FORCED` attribute of `#EXT-X-MEDIA` tags using\n  this sequence.\n- `bitrate` - an object that can be used to set the bitrate for the different media types,\n  in bits per second. For example, `{\"v\": 900000, \"a\": 64000}`. If the bitrate is not supplied,\n  `nginx-vod-module` will estimate it based on the last clip in the sequence.\n- `avg_bitrate` - an object that can be used to set the average bitrate for the different media\n  types, in bits per second. See `bitrate` above for a sample object. If specified, the module will\n  use the value to populate the `AVERAGE-BANDWIDTH` attribute of `#EXT-X-STREAM-INF` in HLS.\n\n\u003e [!IMPORTANT]\n\u003e The options `label`, `roles`, `characteristics`, and `forced` are used to group tracks. As HLS\n\u003e does not have the concept of **video** `AdaptationSet`, any use of these options will cause the\n\u003e HLS manifest builder to consider only the **first** video group.\n\n#### Clip (abstract)\n\nMandatory fields:\n\n- `type` - a string that defines the type of the clip. Allowed values are:\n\n  - `source`\n  - `rateFilter`\n  - `mixFilter`\n  - `gainFilter`\n  - `silence`\n  - `concat`\n  - `dynamic`\n\nOptional fields:\n\n- `keyFrameDurations` - an array of integers containing the durations in milliseconds of the video\n  key frames in the clip. This property can only be supplied on the top level clips of each\n  sequence. Supplying this property on nested clips has no effect. Supplying the key frame durations\n  enables the module to both:\n\n  - align the segments to key frames.\n  - report the correct segment durations in the manifest - providing an alternative to setting\n    `vod_manifest_segment_durations_mode` to `accurate`, which is not supported for multi clip media\n    sets (*for performance reasons*).\n\n- `firstKeyFrameOffset` - an integer measured in milliseconds relative to `firstClipTime` that sets\n  the offset of the first video key frame in the clip. Defaults to `0`.\n\n#### Source clip\n\nMandatory fields:\n\n- `type` - a string with the value `source`.\n- `path` - a string containing the path of the MP4 file. The string `\"empty\"` can be used to\n  represent an empty captions file (useful in case only some videos in a playlist have captions).\n\nOptional fields:\n\n- `id` - a string that identifies the source clip.\n- `sourceType` - sets the interface that should be used to read the MP4 file, allowed values are:\n  `file` and `http`. Defaults to `http` when `vod_remote_upstream_location` is set, `file`\n  otherwise.\n- `tracks` - a string that specifies the tracks that should be used. Defaults to `v1-a1`, which\n  means the first video and audio track.\n- `clipFrom` - an integer that specifies an offset in milliseconds from the beginning of the media\n  file, from which to start loading frames.\n- `encryptionKey` - a base64 encoded string containing the key (128/192/256 bit) that should be used\n  to decrypt the file.\n- `encryptionIv` - a base64 encoded string containing the iv (128 bit) that should be used to\n  decrypt the file.\n- `encryptionScheme` - the encryption scheme that was used to encrypt the file. Currently, only two\n  schemes are supported - `cenc` for MP4 files and `aes-cbc` for caption files.\n\n#### Rate filter clip\n\nMandatory fields:\n\n- `type` - a string with the value `rateFilter`.\n- `rate` - a float that specified the acceleration factor, e.g. a value of `2` means double speed.\n  Allowed values are in the range `0.5` to `2` with up to two decimal points.\n- `source` - a [clip](#clip-abstract) object on which to perform the rate filtering.\n\n#### Gain filter clip\n\nMandatory fields:\n\n- `type` - a string with the value `gainFilter`.\n- `gain` - a float that specified the amplification factor, e.g. a value of `2` means twice as loud.\n  The gain must be positive with up to two decimal points.\n- `source` - a [clip](#clip-abstract) object on which to perform the gain filtering.\n\n#### Mix filter clip\n\nMandatory fields:\n\n- `type` - a string with the value `mixFilter`.\n- `sources` - an array of [clip](#clip-abstract) objects to mix. This array must contain at least\n  one clip and up to *32 clips*.\n\n#### Concat clip\n\nMandatory fields:\n\n- `type` - a string with the value `concat`.\n- `durations` - an array of integers representing MP4 durations in milliseconds. This array must\n  match the `paths` array in count and order.\n\n\u003e [!IMPORTANT]\n\u003e Either `paths` or `clipIds` must be specified.\n\nOptional fields:\n\n- `paths` - an array of strings containing the paths of the MP4 files.\n- `clipIds` - an array of strings, containing the IDs of [source clips](#source-clip). The IDs are\n  translated to paths by issuing a request to the URI specified in `vod_source_clip_map_uri`.\n- `tracks` - a string that specifies the tracks that should be used. Defaults to `v1-a1`, which\n  means the first video and audio track.\n- `offset` - an integer in milliseconds that indicates the timestamp offset of the first frame in\n  the concatenated stream relative to the clip start time.\n- `basePath` - a string that should be added as a prefix to all the paths.\n- `notifications` - array of [notification](#notification) objects. When a segment is requested, all\n  the notifications that fall between the start/end times of the segment are fired. The\n  notifications must be ordered in an increasing offset order.\n\n#### Dynamic clip\n\nMandatory fields:\n\n- `type` - a string with the value `dynamic`.\n- `id` - a string that uniquely identifies the dynamic clip. Used for mapping the clip to its\n  content.\n\n#### Notification\n\nMandatory fields:\n\n- `offset` - an integer in milliseconds that indicates the time in which the notification should be\n  fired. When the notification object is contained in the media set, the `offset` is relative to\n  `firstClipTime` (`0` for vod). When the notification object is contained in a\n  [concat clip](#concat-clip), the `offset` is relative to the beginning of that clip.\n- `id` - a string that identifies the notification. This ID can be referenced in\n  `vod_notification_uri` using the variable `$vod_notification_id`.\n\n#### Closed captions\n\nMandatory fields:\n\n- `id` - a string that identifies the embedded captions. This will become the `INSTREAM-ID` field\n  and must have one of the following values: `CC1`, `CC3`, `CC3`, `CC4`, or `SERVICEn`, where `n` is\n  between `1` and `63`.\n- `label` - a friendly string that indicates the language of the closed caption track.\n\nOptional fields:\n\n- `language` - a 3-letter (ISO-639-2) language code that indicates the language of the closed\n  caption track.\n\n\n### Security\n\n#### Authorization\n\n##### CDN-based delivery\n\nMedia packaged by `nginx-vod-module` can be protected using CDN tokens, this works as follows:\n\n- Some application authenticates the user and decides whether the user should be allowed to watch a\n  specific video. If the user is allowed, the application generates a tokenized URL for the manifest\n  of the video.\n- The CDN validates the token, and if found to be valid, forwards the request to `nginx-vod-module`\n  on the origin.\n- The NGINX server builds the manifest response and generates tokens for the segment URLs contained\n  inside it. The [nginx-secure-token-module](https://github.com/kaltura/nginx-secure-token-module)\n  can be used to accomplish this task, it currently supports Akamai and CloudFront tokens.\n- The CDN validates the token on each segment that is requested.\n\nIn this setup it is also highly recommended to block direct access to the origin server by\nauthenticating the CDN requests. Without this protection, a user who somehow gets the address of the\norigin will be able to bypass any CDN token enforcement. When using Akamai, this can be accomplished\nusing [nginx_mod_akamai_g2o](https://github.com/refractalize/nginx_mod_akamai_g2o). For other\nCDNs, it may be possible to configure the CDN to send a secret header to the origin and then simply\nenforce the header using an NGINX `if` statement:\n\n```lua\nif ($http_x_secret_origin_header != 'secret value') {\n  return 403;\n}\n```\n\nIn addition to the above, most CDNs support other access control settings, such as geo-location.\nThese restrictions are completely transparent to the origin and should work well.\n\n##### Direct delivery\n\nDeployments in which the media is pulled directly from `nginx-vod-module` can protect the media\nusing NGINX access control directives, such `allow`, `deny`, or `access_by_lua` (for more complex\nscenarios).\n\nIn addition, it is possible to build a token based solution (as detailed in the previous section)\nwithout a CDN, by having the NGINX server validate the token. The\n[nginx-akamai-token-validate-module](https://github.com/kaltura/nginx-akamai-token-validate-module)\ncan be used to validate Akamai tokens. *Locations* on which the module is enabled will return `403`\nunless the request contains a valid Akamai token.\n\n#### URL encryption\n\nAs an alternative to tokenization, URL encryption can be used to prevent an attacker from being able\nto craft a playable URL. URL encryption can be implemented with\n[nginx-secure-token-module](https://github.com/kaltura/nginx-secure-token-module), and is supported\nfor HLS and DASH (with manifest format set to segmentlist).\n\nIn terms of security, the main advantage of CDN tokens over URL encryption is that CDN tokens\nusually expire, while encrypted URLs do not (someone who obtains a playable URL will be able to use\nit indefinitely).\n\n#### Media encryption\n\nThis module supports AES-128 and SAMPLE-AES HLS encryption schemes. The main difference between\nmedia encryption and [DRM](#drm) is the mechanism used to transfer the encryption key to the client.\nWith media encryption the key is fetched by the client by performing a simple GET request to\n`nginx-vod-module`, while with DRM the key is returned inside a vendor specific license response.\n\nMedia encryption reduces the problem of securing the media to the need to secure the encryption key.\nThe media segment URLs (which compose the vast majority of the traffic) can be completely\nunprotected, and easily cacheable by any proxies between the client and servers (unlike\ntokenization). The encryption key request can then be protected using one of the methods mentioned\nabove (CDN tokens, NGINX access rules).\n\nIn addition, it is possible to configure the `nginx-vod-module` to return the encryption key over\nHTTPS while having the segments delivered over HTTP. The way to configure this is to set\n`vod_segments_base_url` to `http://\u003cdomain\u003e` and set `vod_base_url` to `https://\u003cdomain\u003e`.\n\n#### DRM\n\nThis module has the ability to perform on-the-fly encryption for DASH (CENC) and FairPlay HLS. The\nencryption is performed while serving a video / audio segment to the client. Therefore, when working\nwith DRM it is recommended not to serve the content directly from `nginx-vod-module` to end-users. A\nmore scalable architecture would be to use proxy servers or a CDN in order to cache the encrypted\nsegments.\n\nIn order to perform the encryption, `nginx-vod-module` needs several parameters, including `key` and\n`key_id`, these parameters are fetched from an external server via HTTP GET requests. The\n`vod_drm_upstream_location` parameter specifies an NGINX `location` that is used to access the DRM\nserver, and the request URI is configured using `vod_drm_request_uri` (this parameter can include\n[variables](#nginx-variables)). The response of the DRM server is a JSON with the following format:\n\n```json\n[\n  {\n    \"pssh\": [\n      {\n        \"data\": \"CAESEGMyZjg2MTczN2NjNGYzODIaB2thbHR1cmEiCjBfbmptaWlwbXAqBVNEX0hE\",\n        \"uuid\": \"edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\"\n      }\n    ],\n    \"key\": \"GzoNU9Dfwc//Iq3/zbzMUw==\",\n    \"key_id\": \"YzJmODYxNzM3Y2M0ZjM4Mg==\"\n  }\n]\n```\n\n- `pssh.data` - base64 encoded binary data, the format of this data is DRM vendor specific.\n- `pssh.uuid` - the DRM system UUID, in this case, `edef8ba9-79d6-4ace-a3c8-27dcd51d21ed` stands for\n  Widevine.\n- `key` - base64 encoded encryption key (128 bit).\n- `key_id` - base64 encoded key identifier (128 bit).\n- `iv` - **optional** base64 encoded initialization vector (128 bit). The IV is currently used only\n  in HLS (FairPlay), in the other protocols an IV is automatically generated.\n\n##### Sample configurations\n\nApple FairPlay HLS:\n\n```conf\nlocation ~ ^/hls/cbcs/(?\u003cplayback_token\u003e[^/]+)/ {\n  vod hls;\n\n  vod_hls_encryption_method sample-aes;\n  vod_hls_encryption_key_uri 'skd://$vod_set_id';\n  vod_hls_encryption_key_format 'com.apple.streamingkeydelivery';\n  vod_hls_encryption_key_format_versions '1';\n\n  vod_drm_enabled on;\n  vod_drm_request_uri '/drm/$playback_token';\n\n  vod_last_modified_types *;\n\n  add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';\n  add_header Access-Control-Allow-Headers 'Origin, Range';\n  add_header Access-Control-Allow-Origin '*';\n  add_header Access-Control-Expose-Headers 'Server, Range, Content-Length, Content-Range';\n  add_header Access-Control-Max-Age '86400';\n\n  expires 1y;\n}\n```\n\nCommon Encryption HLS:\n\n```conf\nlocation ~ ^/hls/cenc/(?\u003cplayback_token\u003e[^/]+)/ {\n  vod hls;\n\n  vod_hls_encryption_method sample-aes-cenc;\n  vod_hls_encryption_key_format 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed';\n  vod_hls_encryption_key_format_versions '1';\n\n  vod_drm_enabled on;\n  vod_drm_request_uri '/drm/$playback_token';\n\n  vod_last_modified_types *;\n\n  expires 1y;\n\n  add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';\n  add_header Access-Control-Allow-Headers 'Origin, Range';\n  add_header Access-Control-Allow-Origin '*';\n  add_header Access-Control-Expose-Headers 'Server, Range, Content-Length, Content-Range';\n  add_header Access-Control-Max-Age '86400';\n}\n```\n\n### Performance recommendations\n\n- Do not allow customers to play the content directly from `nginx-vod-module`. As all the different\n  streaming protocols supported are HTTP based, so they can be cached by standard HTTP CDNs (such as\n  Akamai, CloudFront, Fastly).\n- Keep the `nginx-vod-module` as close as possible to where the source MP4 files are stored.\n- Enable `nginx-vod-module` cache mechanisms:\n  - `vod_metadata_cache` - saves the video metadata avoiding the need to re-read them for every\n    segment. This cache should be rather large, in the order of **GBs**.\n  - `vod_response_cache` - saves the responses of manifest requests. This cache may not be required\n    when using a second layer of caching server. No need to allocate a large buffer for this cache,\n    `128m` is probably more than enough for most deployments.\n  - `vod_mapping_cache` - for `mapped` mode only, few MBs is usually enough.\n  - `open_file_cache` - caches open file handles.\n  \u003e The hit/miss ratios of these caches can be tracked by enabling `vod_performance_counters` and\n  \u003e setting up a `vod_status` `location`.\n\n- Enable `aio` for `local` and `mapped` modes - NGINX has to be compiled with `aio` support, and it\n  has to be enabled in `nginx.conf` (`aio on`). You can verify the setup by looking at the\n  performance counters on the status page - `read_file` (`aio off`) vs. `async_read_file`\n  (`aio on`).\n- Enable asynchronous file open for `local` and `mapped` modes - NGINX has to be compiled with\n  **threads** support, and `vod_open_file_thread_pool` has to be specified in `nginx.conf`. You can\n  verify the setup by looking at the performance counters on the status page - `open_file` vs.\n  `async_open_file`.\n  \u003e The `open_file` may be non-zero with `vod_open_file_thread_pool` enabled due to the open file\n  \u003e cache - open requests that are served from cache will be counted as synchronous `open_file`.\n\n- When using DASH with DRM, if the video files have a **single** NALU per frame, set\n  `vod_min_single_nalu_per_frame_segment` to non-zero.\n- The muxing overhead of the streams generated by this module can be reduced by changing the\n  following parameters:\n  - HLS - set `vod_hls_mpegts_align_frames` to `off` and `vod_hls_mpegts_interleave_frames` to `on`.\n- Enable gzip compression on manifest responses -\n  `gzip_types application/vnd.apple.mpegurl application/dash+xml text/xml text/vtt`.\n- Apply common NGINX performance best practices, such as `tcp_nodelay on`, `client_header_timeout`.\n- Tweak common Kernel parameters for high-throughput, such as `net.core.*`, `net.ipv4.*`,\n  `vm.swappiness`, as well as file descriptor limits.\n\n### Configuration directives - Basic\n\n#### vod\n\n- **syntax**: `vod none | dash | hls | thumb | volume_map`\n- **default**: `n/a`\n- **context**: `location`\n\nEnables the `nginx-vod-module` on the enclosing `location`:\n\n- `none` - serves the MP4 files as is / clipped.\n- `dash` - Dynamic Adaptive Streaming over HTTP packager.\n- `hls` - Apple HTTP Live Streaming packager.\n- `thumb` - thumbnail capture.\n- `volume_map` - audio volume map.\n\n#### vod_mode\n\n- **syntax**: `vod_mode local | remote | mapped`\n- **default**: `local`\n- **context**: `http`, `server`, `location`\n\nSets the file access mode - `local`, `remote`, or `mapped` (see the features section above for more\ndetails).\n\n#### vod_status\n\n- **syntax**: `vod_status`\n- **default**: `n/a`\n- **context**: `location`\n\nEnables the `nginx-vod-module` status page on the enclosing `location`. The following query params\nare supported:\n\n- `?reset=1` - resets the performance counters and cache stats.\n- `?format=prom` - returns the output in format compatible with Prometheus (the default format is\n  XML).\n\n### Configuration directives - Segmentation\n\n#### vod_segment_duration\n\n- **syntax**: `vod_segment_duration :duration`\n- **default**: `10s`\n- **context**: `http`, `server`, `location`\n\nSets the segment duration in milliseconds. It is highly recommended to use a segment duration that\nis a multiple of the GOP duration. If the segment duration is not a multiple of GOP duration, and\n`vod_align_segments_to_key_frames` is enabled, there may be significant differences between the\nsegment duration that is reported in the manifest and the actual segment duration. This could also\nlead to the appearance of empty segments within the stream.\n\n#### vod_live_window_duration\n\n- **syntax**: `vod_live_window_duration :duration`\n- **default**: `30000`\n- **context**: `http`, `server`, `location`\n\nSets the total duration in milliseconds of the segments that should be returned in a live manifest.\n\n- If the value is *positive*, this module returns a range of maximum `vod_live_window_duration`\n  milliseconds, ending at the current server time.\n- If the value is *negative*, this module returns a range of maximum `vod_live_window_duration`\n  milliseconds from the end of the JSON mapping.\n- If the value is set to `0`, the live manifest will contain all the segments that are fully\n  contained in the JSON mapping time frame.\n\n#### vod_force_playlist_type_vod\n\n- **syntax**: `vod_force_playlist_type_vod on | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nGenerate a vod stream even when the media set has `playlistType=live`. Enabling this setting has the\nfollowing effects:\n\n- Frame timestamps will be continuous and start from zero.\n- Segment indexes will start from one.\n- In case of HLS, the returned manifest will have both `#EXT-X-PLAYLIST-TYPE:VOD` and\n- `#EXT-X-ENDLIST`.\n\nThis can be useful for clipping vod sections out of a live stream.\n\n#### vod_force_continuous_timestamps\n\n- **syntax**: `vod_force_continuous_timestamps on | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nGenerate continuous timestamps even when the media set has gaps (gaps can created by the use of\n`clipTimes`). If ID3 timestamps are enabled (`vod_hls_mpegts_output_id3_timestamps`), they contain\nthe original timestamps that were set in `clipTimes`.\n\n#### vod_bootstrap_segment_durations\n\n- **syntax**: `vod_bootstrap_segment_durations :duration`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nAdds a bootstrap segment duration in milliseconds. This setting can be used to make the first few\nsegments shorter than the default segment duration, thus making the adaptive bitrate selection\nkick-in earlier without the overhead of short segments throughout the video.\n\n#### vod_align_segments_to_key_frames\n\n- **syntax**: `vod_align_segments_to_key_frames on | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nWhen enabled, the module forces all segments to start with a key frame. Enabling this setting can\nlead to differences between the actual segment durations and the durations reported in the manifest\n(unless `vod_manifest_segment_durations_mode` is set to accurate).\n\n#### vod_segment_count_policy\n\n- **syntax**: `vod_segment_count_policy last_short | last_long | last_rounded`\n- **default**: `last_short`\n- **context**: `http`, `server`, `location`\n\nConfigures the policy for calculating the segment count, for `segment_duration` of *10 seconds*:\n\n- `last_short` - a file of *33 seconds* is partitioned as - `10`, `10`, `10`, `3`.\n- `last_long` - a file of *33 seconds* is partitioned as - `10`, `10`, `13`.\n- `last_rounded` - a file of *33 seconds* is partitioned as - `10`, `10`, `13`, a file of\n  *38 seconds* is partitioned as `10`, `10`, `10`, `8`.\n\n#### vod_manifest_duration_policy\n\n- **syntax**: `vod_manifest_duration_policy min | max`\n- **default**: `max`\n- **context**: `http`, `server`, `location`\n\nConfigures the policy for calculating the duration of a manifest containing multiple streams:\n\n- `max` - uses the maximum stream duration.\n- `min` - uses the minimum non-zero stream duration.\n\n#### vod_manifest_segment_durations_mode\n\n- **syntax**: `vod_manifest_segment_durations_mode estimate | accurate`\n- **default**: `estimate`\n- **context**: `http`, `server`, `location`\n\nConfigures the calculation mode of segment durations within manifest requests:\n\n- `estimate` - reports the duration as configured in `nginx.conf`, e.g. if `vod_segment_duration`\n  is `10000`, an HLS manifest will contain `#EXTINF:10`.\n- `accurate` - reports the exact duration of the segment, taking into account the frame durations,\n  e.g. for a frame rate of `29.97` and *10 seconds* segments it will report the first segment as\n  `10.01`. this mode also takes into account the key frame alignment, in case\n  `vod_align_segments_to_key_frames` is `on`.\n\n#### vod_media_set_override_json\n\n- **syntax**: `vod_media_set_override_json :json`\n- **default**: `{}`\n- **context**: `http`, `server`, `location`\n\nThis parameter provides a way to override portions of the media set JSON (mapped mode only). For\nexample, `vod_media_set_override_json '{\"clipTo\":20000}'` clips the media set to *20 seconds*. The\nparameter value can contain [variables](#nginx-variables).\n\n### Configuration directives - Upstream\n\n#### vod_upstream_location\n\n- **syntax**: `vod_upstream_location :location`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSets an NGINX `location` that is used to read the MP4 file (`remote` mode) or mapping the request\nURI (`mapped` mode).\n\n#### vod_remote_upstream_location\n\n- **syntax**: `vod_remote_upstream_location :location`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSets an NGINX `location` that is used to read the MP4 file on `remote` or `mapped` mode. If this\ndirective is set on `mapped` mode, the module reads the MP4 files over HTTP, treating the paths in\nthe JSON mapping as URIs (default behavior is to read from local files).\n\n#### vod_max_upstream_headers_size\n\n- **syntax**: `vod_max_upstream_headers_size :size`\n- **default**: `4k`\n- **context**: `http`, `server`, `location`\n\nSets the size that is allocated for holding the response headers when issuing upstream requests to\n`vod_xxx_upstream_location`.\n\n#### vod_upstream_extra_args\n\n- **syntax**: `vod_upstream_extra_args :querystring`\n- **default**: `empty`\n- **context**: `http`, `server`, `location`\n\nExtra query string arguments, e.g. `\"arg1=value1\u0026arg2=value2\u0026...\"`, that should be added to the\nupstream request (`remote` and `mapped` modes only). The parameter value can contain\n[variables](#nginx-variables).\n\n#### vod_media_set_map_uri\n\n- **syntax**: `vod_media_set_map_uri :uri`\n- **default**: `$vod_suburi`\n- **context**: `http`, `server`, `location`\n\nSets the URI of media set mapping requests, the parameter value can contain\n[variables](#nginx-variables). In case of multi URI, `$vod_suburi` will be the current sub URI (a\nseparate request is issued per sub URL).\n\n#### vod_path_response_prefix\n\n- **syntax**: `vod_path_response_prefix :prefix`\n- **default**: `{\"sequences\":[{\"clips\":[{\"type\":\"source\",\"path\":\"`\n- **context**: `http`, `server`, `location`\n\nSets the prefix that is expected in URI mapping responses (`mapped` mode only).\n\n#### vod_path_response_postfix\n\n- **syntax**: `vod_path_response_postfix postfix`\n- **default**: `\"}]}]}`\n- **context**: `http`, `server`, `location`\n\nSets the postfix that is expected in URI mapping responses (`mapped` mode only).\n\n#### vod_max_mapping_response_size\n\n- **syntax**: `vod_max_mapping_response_size :length`\n- **default**: `1k`\n- **context**: `http`, `server`, `location`\n\nSets the maximum length of a path returned from upstream (`mapped` mode only).\n\n### Configuration directives - Fallback\n\n#### vod_fallback_upstream_location\n\n- **syntax**: `vod_fallback_upstream_location :location`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSets an NGINX `location` to which the request is forwarded after encountering a file not found error\n(`local` and `mapped` modes only).\n\n#### vod_proxy_header_name\n\n- **syntax**: `vod_proxy_header_name :name`\n- **default**: `X-Kaltura-Proxy`\n- **context**: `http`, `server`, `location`\n\nSets the name of an HTTP header that is used to prevent fallback proxy loops (`local` and `mapped`\nmodes only).\n\n#### vod_proxy_header_value\n\n- **syntax**: `vod_proxy_header_value :name`\n- **default**: `dumpApiRequest`\n- **context**: `http`, `server`, `location`\n\nSets the value of an HTTP header that is used to prevent fallback proxy loops (`local` and `mapped`\nmodes only).\n\n### Configuration directives - Performance\n\n#### vod_metadata_cache\n\n- **syntax**: `vod_metadata_cache :zone_name :zone_size [:expiration] | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nConfigures the size and shared memory object name of the video metadata cache. For MP4 files this\ncache holds the `moov` atom.\n\n#### vod_mapping_cache\n\n- **syntax**: `vod_mapping_cache :zone_name :zone_size [:expiration] | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nConfigures the size and shared memory object name of the mapping cache for vod (`mapped` mode only).\n\n#### vod_live_mapping_cache\n\n- **syntax**: `vod_live_mapping_cache :zone_name :zone_size [:expiration] | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nConfigures the size and shared memory object name of the mapping cache for live (`mapped` mode\nonly).\n\n#### vod_response_cache\n\n- **syntax**: `vod_response_cache :zone_name :zone_size [:expiration] | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nConfigures the size and shared memory object name of the response cache. The response cache holds\nmanifests and other non-video content (like DASH init segment, HLS encryption key). Video segments\nare not cached.\n\n#### vod_live_response_cache\n\n- **syntax**: `vod_live_response_cache :zone_name :zone_size [:expiration] | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nConfigures the size and shared memory object name of the response cache for time changing live\nresponses. This cache holds the following types of responses for live: DASH MPD, HLS M3U8 playlist.\n\n#### vod_initial_read_size\n\n- **syntax**: `vod_initial_read_size :size`\n- **default**: `4k`\n- **context**: `http`, `server`, `location`\n\nSets the size of the initial read operation of the MP4 file.\n\n#### vod_max_metadata_size\n\n- **syntax**: `vod_max_metadata_size :size`\n- **default**: `128m`\n- **context**: `http`, `server`, `location`\n\nSets the maximum supported video metadata size (for MP4 - `moov` atom size).\n\n#### vod_max_frames_size\n\n- **syntax**: `vod_max_frames_size :size`\n- **default**: `16m`\n- **context**: `http`, `server`, `location`\n\nSets the limit on the total size of the frames of a single segment.\n\n#### vod_max_frame_count\n\n- **syntax**: `vod_max_frame_count :count`\n- **default**: `1048576`\n- **context**: `http`, `server`, `location`\n\nSets the limit on the total count of the frames read to serve non segment (e.g. playlist) request.\n\n#### vod_segment_max_frame_count\n\n- **syntax**: `vod_segment_max_frame_count :count`\n- **default**: `65536`\n- **context**: `http`, `server`, `location`\n\nSets the limit on the total count of the frames read to serve segment request.\n\n#### vod_cache_buffer_size\n\n- **syntax**: `vod_cache_buffer_size :size`\n- **default**: `256k`\n- **context**: `http`, `server`, `location`\n\nSets the size of the cache buffers used when reading MP4 frames.\n\n#### vod_open_file_thread_pool\n\n- **syntax**: `vod_open_file_thread_pool :pool_name | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nEnables the use of asynchronous file open via thread pool. The thread pool must be defined with a\n`thread_pool` directive, if no pool name is specified the *default* pool is used. This directive is\nsupported when compiling with `--add-threads`.\n\n\u003e [!NOTE]\n\u003e This directive currently disables the use of `open_file_cache`.\n\n#### vod_output_buffer_pool\n\n- **syntax**: `vod_output_buffer_pool :size :count | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nPre-allocates buffers for generating response data, saving the need allocate/free the buffers on\nevery request.\n\n#### vod_performance_counters\n\n- **syntax**: `vod_performance_counters :zone_name | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nConfigures the shared memory object name of the performance counters.\n\n### Configuration directives - URL structure\n\n#### vod_base_url\n\n- **syntax**: `vod_base_url :url`\n- **default**: `see below`\n- **context**: `http`, `server`, `location`\n\nSets the base URL (scheme + domain) that should be returned in manifest responses. The parameter\nvalue can contain [variables](#nginx-variables). If the parameter evaluates to an empty string\nrelative URLs will be used. If the parameter evaluates to a string ending with `/`, it is assumed to\nbe a full URL - the module only appends the file name to it, instead of a full URI.\n\nWhen not set the base URL is determined as follows:\n\n- If the request did not contain a host header (HTTP/1.0) relative URLs will be returned.\n- Otherwise, the base URL will be `$scheme://$http_host`. This affects only HLS and DASH.\n\n#### vod_segments_base_url\n\n- **syntax**: `vod_segments_base_url :url`\n- **default**: `see below`\n- **context**: `http`, `server`, `location`\n\nSets the base URL (scheme + domain) that should be used for delivering video segments. The parameter\nvalue can contain [variables](#nginx-variables), if the parameter evaluates to an empty string\nrelative URLs will be used. When not set `vod_base_url` will be used. This affects only HLS.\n\n#### vod_multi_uri_suffix\n\n- **syntax**: `vod_multi_uri_suffix :suffix`\n- **default**: `.urlset`\n- **context**: `http`, `server`, `location`\n\nA URL suffix that is used to identify multi URLs. A multi URL is a way to encode several different\nURLs that should be played together as an adaptive streaming set, under a single URL. When the\ndefault suffix is used an HLS set URL may look like:\n`http://\u003cdomain\u003e/hls/common-prefix,bitrate1,bitrate2,common-suffix.urlset/master.m3u8`.\n\n#### vod_clip_to_param_name\n\n- **syntax**: `vod_clip_to_param_name :name`\n- **default**: `clipTo`\n- **context**: `http`, `server`, `location`\n\nThe name of the clip to request parameter.\n\n#### vod_clip_from_param_name\n\n- **syntax**: `vod_clip_from_param_name :name`\n- **default**: `clipFrom`\n- **context**: `http`, `server`, `location`\n\nThe name of the clip from request parameter.\n\n#### vod_tracks_param_name\n\n- **syntax**: `vod_tracks_param_name :name`\n- **default**: `tracks`\n- **context**: `http`, `server`, `location`\n\nThe name of the tracks request parameter.\n\n#### vod_time_shift_param_name\n\n- **syntax**: `vod_time_shift_param_name :name`\n- **default**: `shift`\n- **context**: `http`, `server`, `location`\n\nThe name of the shift request parameter.\n\n#### vod_speed_param_name\n\n- **syntax**: `vod_speed_param_name :name`\n- **default**: `speed`\n- **context**: `http`, `server`, `location`\n\nThe name of the speed request parameter.\n\n#### vod_lang_param_name\n\n- **syntax**: `vod_lang_param_name :name`\n- **default**: `lang`\n- **context**: `http`, `server`, `location`\n\nThe name of the language request parameter.\n\n#### vod_force_sequence_index\n\n- **syntax**: `vod_force_sequence_index on | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nUse sequence index in segment URIs even if there is only one sequence.\n\n### Configuration directives - Response headers\n\n#### vod_expires\n\n- **syntax**: `vod_expires :time`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSets the value of the `Expires` and `Cache-Control` response headers for successful requests.\n\nThis directive is similar to built-in `expires` directive, except that it only supports the\nexpiration interval scenario (`epoch`, `max`, `off`, and day time are not supported).\n\nThe main motivation for using this directive instead of the built-in `expires` is to have different\nexpiration for VOD and dynamic live content. If this directive is not specified, `nginx-vod-module`\nwill not set the `Expires` / `Cache-Control` headers. This setting affects all types of requests in\nVOD playlists and segment requests in live playlists.\n\n#### vod_expires_live\n\n- **syntax**: `vod_expires_live :time`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSame as [`vod_expires`](#vod_expires) for live requests that are not time dependent and not segments\n(e.g. HLS - `master.m3u8`).\n\n#### vod_expires_live_time_dependent\n\n- **syntax**: `vod_expires_live_time_dependent :time`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSame as [`vod_expires`](#vod_expires) for live requests that are time dependent (HLS - `index.m3u8`,\nDASH - `manifest.mpd`).\n\n#### vod_last_modified\n\n- **syntax**: `vod_last_modified :time`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSets the value of the `Last-Modified` header returned on the response, by default the module does\nnot return a `Last-Modified` header.\n\nThe reason for having this parameter here is in order to support `If-Modified-Since` and\n`If-Unmodified-Since`. Since the built-in `ngx_http_not_modified_filter_module` runs before any\nother header filter module, it will not see any headers set by `add_headers` or `more_set_headers`.\nThis makes NGINX always reply as if the content changed (`412` for `If-Unmodified-Since` or `200`\nfor `If-Modified-Since`).\n\nFor live requests that are not segments (e.g. live DASH MPD), the `Last-Modified` is set to the\ncurrent server time.\n\n#### vod_last_modified_types\n\n- **syntax**: `vod_last_modified_types :mime_type...`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSets the MIME types for which the `Last-Modified` header should be set. The special value `*`\nmatches any MIME type.\n\n### Configuration directives - Ad stitching\n\n\u003e [!IMPORTANT]\n\u003e For `mapped` mode only.\n\n#### vod_dynamic_mapping_cache\n\n- **syntax**: `vod_dynamic_mapping_cache :zone_name :zone_size [:expiration] | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nConfigures the size and shared memory object name of the cache that stores the mapping of\n[dynamic clips](#dynamic-clip).\n\n#### vod_dynamic_clip_map_uri\n\n- **syntax**: `vod_dynamic_clip_map_uri :uri`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSets the URI that should be used to map [dynamic clips](#dynamic-clip). The parameter value can\ncontain [variables](#nginx-variables), specifically, `$vod_clip_id` contains the ID of the clip that\nshould be mapped. The expected response from this URI is a JSON containing a\n[concat clip](#concat-clip) object.\n\n#### vod_source_clip_map_uri\n\n- **syntax**: `vod_source_clip_map_uri :uri`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSets the URI that should be used to map [source clips](#source-clip) defined using the `clipIds`\nproperty of concat. The parameter value can contain [variables](#nginx-variables), specifically,\n`$vod_clip_id` contains the ID of the clip that should be mapped. The expected response from this\nURI is a JSON containing a [source clip](#source-clip) object.\n\n#### vod_redirect_segments_url\n\n- **syntax**: `vod_redirect_segments_url :url`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSets a URL to which requests for segments should be redirected. The parameter value can contain\nvariables, specifically, `$vod_dynamic_mapping` contains a serialized representation of the mapping\nof [dynamic clips](#dynamic-clip).\n\n#### vod_apply_dynamic_mapping\n\n- **syntax**: `vod_apply_dynamic_mapping :mapping`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nMaps dynamic clips to concat clips using the given expression, previously generated by\n`$vod_dynamic_mapping`. The parameter value can contain [variables](#nginx-variables).\n\n#### vod_notification_uri\n\n- **syntax**: `vod_notification_uri :uri`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSets the URI that should be used to issue notifications. The parameter value can contain\n[variables](#nginx-variables), specifically, `$vod_notification_id` contains the ID of the\nnotification that is being fired. The response from this URI is ignored.\n\n### Configuration directives - DRM / Encryption\n\n#### vod_secret_key\n\n- **syntax**: `vod_secret_key :secret_key`\n- **default**: `empty`\n- **context**: `http`, `server`, `location`\n\nSets the seed that is used to generate the TS encryption key and DASH encryption IVs. The parameter\nvalue can contain [variables](#nginx-variables), and will usually have the structure\n`secret-$vod_filepath`. See the list of [variables](#nginx-variables) added by this module.\n\n#### vod_encryption_iv_seed\n\n- **syntax**: `vod_encryption_iv_seed :iv_seed`\n- **default**: `empty`\n- **context**: `http`, `server`, `location`\n\nSets the seed that is used to generate the encryption IV, currently applies only to HLS/fMP4 with\nAES-128 encryption. The parameter value can contain [variables](#nginx-variables).\n\n#### vod_drm_enabled\n\n- **syntax**: `vod_drm_enabled on | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nWhen enabled, the module encrypts the media segments according to the response it gets from the DRM\nupstream.\n\n#### vod_drm_single_key\n\n- **syntax**: `vod_drm_single_key on | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nWhen enabled, the module requests the DRM info only for the first sequence and applies it to all\nsequences. When disabled, the DRM info is requested for each sequence separately. For DASH, enabling\nthis setting makes the module place the `ContentProtection` tag under `Representation`, otherwise,\nit is placed under `AdaptationSet`.\n\n#### vod_drm_clear_lead_segment_count\n\n- **syntax**: `vod_drm_clear_lead_segment_count :count`\n- **default**: `1`\n- **context**: `http`, `server`, `location`\n\nSets the number of clear (**unencrypted**) segments in the beginning of the stream. A clear lead\nenables the player to start playing without having to wait for the license response.\n\n#### vod_drm_max_info_length\n\n- **syntax**: `vod_drm_max_info_length :length`\n- **default**: `4k`\n- **context**: `http`, `server`, `location`\n\nSets the maximum length of a DRM info returned from upstream.\n\n#### vod_drm_upstream_location\n\n- **syntax**: `vod_drm_upstream_location :location`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSets the NGINX `location` that should be used for getting the DRM info for the file.\n\n#### vod_drm_info_cache\n\n- **syntax**: `vod_drm_info_cache :zone_name :zone_size [:expiration] | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nConfigures the size and shared memory object name of the DRM info cache.\n\n#### vod_drm_request_uri\n\n- **syntax**: `vod_drm_request_uri :uri`\n- **default**: `$vod_suburi`\n- **context**: `http`, `server`, `location`\n\nSets the URI of DRM info requests, the parameter value can contain [variables](#nginx-variables). In\ncase of multi URL, `$vod_suburi` will be the current sub URI (a separate DRM info request is issued\nper sub URL).\n\n#### vod_min_single_nalu_per_frame_segment\n\n- **syntax**: `vod_min_single_nalu_per_frame_segment :index`\n- **default**: `0`\n- **context**: `http`, `server`, `location`\n\nSets the minimum segment index (*1-based*) that should be assumed to have a single H264 NALU per\nframe. If the value is `0`, no assumption is being made on the number of NAL units per frame. This\nsetting only affects DASH configurations that have DRM enabled.\n\nWhen transcoding videos using `libx264`, by default, all frames have a single NAL unit, except the\nfirst frame that contains an additional NALU with the `libx264` copyright information. Setting this\nparameter to a value greater than `0` can provide a significant performance improvement, since the\nlayout of the segment can be calculated in advance, allowing the module to:\n\n- Output segment buffers as they are generated (it doesn't have to wait for the whole segment to\n  complete).\n- Avoid frame processing for requests that do not need the segment data (e.g. `HEAD`, `Range 0-0`).\n\n### Configuration directives - DASH\n\n#### vod_dash_absolute_manifest_urls\n\n- **syntax**: `vod_dash_absolute_manifest_urls on | off`\n- **default**: `on`\n- **context**: `http`, `server`, `location`\n\nWhen enabled the server returns absolute URLs in MPD requests.\n\n#### vod_dash_manifest_file_name_prefix\n\n- **syntax**: `vod_dash_manifest_file_name_prefix :name`\n- **default**: `manifest`\n- **context**: `http`, `server`, `location`\n\nThe name of the MPD file (an `.mpd` extension is implied).\n\n#### vod_dash_profiles\n\n- **syntax**: `vod_dash_profiles :profiles`\n- **default**: `urn:mpeg:dash:profile:isoff-main:2011`\n- **context**: `http`, `server`, `location`\n\nSets the profiles that are returned in the MPD tag in manifest responses.\n\n#### vod_dash_init_file_name_prefix\n\n- **syntax**: `vod_dash_init_file_name_prefix :name`\n- **default**: `init`\n- **context**: `http`, `server`, `location`\n\nThe name of the MP4 initialization file (an `.mp4` extension is implied).\n\n#### vod_dash_fragment_file_name_prefix\n\n- **syntax**: `vod_dash_fragment_file_name_prefix :name`\n- **default**: `frag`\n- **context**: `http`, `server`, `location`\n\nThe name of the fragment files (an `.m4s` extension is implied).\n\n#### vod_dash_manifest_format\n\n- **syntax**: `vod_dash_manifest_format segmentlist | segmenttemplate | segmenttimeline`\n- **default**: `segmenttimeline`\n- **context**: `http`, `server`, `location`\n\nSets the MPD format:\n\n- `segmentlist` - uses `SegmentList` and `SegmentURL` tags, in this format the URL of each fragment\n  is explicitly set in the MPD.\n- `segmenttemplate` - uses `SegmentTemplate` reporting a single duration for all fragments.\n- `segmenttimeline` - uses `SegmentTemplate` and `SegmentTimeline` to explicitly set the duration of\n  the fragments.\n\n#### vod_dash_subtitle_format\n\n- **syntax**: `vod_dash_subtitle_format webvtt | smpte-tt`\n- **default**: `webvtt`\n- **context**: `http`, `server`, `location`\n\nSets the format of the subtitles returned in the MPD.\n\n#### vod_dash_init_mp4_pssh\n\n- **syntax**: `vod_dash_init_mp4_pssh on | off`\n- **default**: `on`\n- **context**: `http`, `server`, `location`\n\nWhen enabled, the DRM `pssh` boxes are returned in the DASH `init` segment and in the manifest. When\ndisabled, the `pssh` boxes are returned only in the manifest.\n\n#### vod_dash_duplicate_bitrate_threshold\n\n- **syntax**: `vod_dash_duplicate_bitrate_threshold :threshold`\n- **default**: `4096`\n- **context**: `http`, `server`, `location`\n\nThe bitrate threshold for removing **identical** bitrates, streams whose bitrate differences are\nless than this value will be considered identical.\n\n#### vod_dash_use_base_url_tag\n\n- **syntax**: `vod_dash_use_base_url_tag on | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nWhen enabled, a `BaseURL` tag will be used to specify the fragments and init segment base URL.\nOtherwise, the `media` and `initialization` attributes under `SegmentTemplate` will contain absolute\nURLs.\n\n### Configuration directives - HLS\n\n#### vod_hls_encryption_method\n\n- **syntax**: `vod_hls_encryption_method none | aes-128 | sample-aes | sample-aes-cenc`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSets the encryption method of HLS segments.\n\n#### vod_hls_force_unmuxed_segments\n\n- **syntax**: `vod_hls_force_unmuxed_segments on | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nWhen enabled the server returns the audio stream in separate segments than the ones used by the\nvideo stream (using `#EXT-X-MEDIA`).\n\n#### vod_hls_version\n\n- **syntax**: `vod_hls_version :version`\n- **default**: `4`\n- **context**: `http`, `server`, `location`\n\nSets the version of the manifest. See\n[RFC 8216 Section 7](https://datatracker.ietf.org/doc/html/rfc8216#section-7) for protocol version\ncompatibility.\n\n\u003e [!IMPORTANT]\n\u003e The mimimum version required by this module is `3`.\n\n\u003e [!TIP]\n\u003e For maximum compatibility, set the version to the lowest value that allows the required features\n\u003e for playback. While this is not strictly spec-compliant, most clients are lenient and will ignore\n\u003e unknown or unsupported tags. However, advanced features (such as fMP4) may not be recognized or\n\u003e used by clients unless the version is set to the minimum required for those features.\n\n#### vod_hls_container_format\n\n- **syntax**: `vod_hls_container_format mpegts | fmp4 | auto`\n- **default**: `auto`\n- **context**: `http`, `server`, `location`\n\nSets the container format of the HLS segments. The default behavior is to use `fmp4` for HEVC, and\n`mpegts` otherwise.\n\n\u003e [!NOTE]\n\u003e Apple does not support HEVC over MPEG-TS.\n\n#### vod_hls_absolute_master_urls\n\n- **syntax**: `vod_hls_absolute_master_urls on | off`\n- **default**: `on`\n- **context**: `http`, `server`, `location`\n\nWhen enabled the server returns absolute playlist URLs in master playlist requests.\n\n#### vod_hls_absolute_index_urls\n\n- **syntax**: `vod_hls_absolute_index_urls on | off`\n- **default**: `on`\n- **context**: `http`, `server`, `location`\n\nWhen enabled the server returns absolute segment URLs in media playlist requests.\n\n#### vod_hls_absolute_iframe_urls\n\n- **syntax**: `vod_hls_absolute_iframe_urls on | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nWhen enabled the server returns absolute segment URLs in iframe playlist requests.\n\n#### vod_hls_output_iframes_playlist\n\n- **syntax**: `vod_hls_output_iframes_playlist on | off`\n- **default**: `on`\n- **context**: `http`, `server`, `location`\n\nWhen disabled iframe playlists are not returned as part of master playlists.\n\n#### vod_hls_master_file_name_prefix\n\n- **syntax**: `vod_hls_master_file_name_prefix :name`\n- **default**: `master`\n- **context**: `http`, `server`, `location`\n\nThe name of the HLS master playlist file (an `.m3u8` extension is implied).\n\n#### vod_hls_index_file_name_prefix\n\n- **syntax**: `vod_hls_index_file_name_prefix :name`\n- **default**: `index`\n- **context**: `http`, `server`, `location`\n\nThe name of the HLS media playlist file (an `.m3u8` extension is implied).\n\n#### vod_hls_iframes_file_name_prefix\n\n- **syntax**: `vod_hls_iframes_file_name_prefix :name`\n- **default**: `iframes`\n- **context**: `http`, `server`, `location`\n\nThe name of the HLS I-frames playlist file (an `.m3u8` extension is implied).\n\n#### vod_hls_segment_file_name_prefix\n\n- **syntax**: `vod_hls_segment_file_name_prefix :name`\n- **default**: `seg`\n- **context**: `http`, `server`, `location`\n\nThe prefix of segment file names, the actual file name is\n`seg-\u003cindex\u003e-v\u003cvideo-track-index\u003e-a\u003caudio-track-index\u003e.ts`.\n\n#### vod_hls_init_file_name_prefix\n\n- **syntax**: `vod_hls_init_file_name_prefix :name`\n- **default**: `init`\n- **context**: `http`, `server`, `location`\n\nThe name of the init segment file name, only relevant when using fMP4 container.\n\n#### vod_hls_encryption_key_file_name\n\n- **syntax**: `vod_hls_encryption_key_file_name :name`\n- **default**: `encryption.key`\n- **context**: `http`, `server`, `location`\n\nThe name of the encryption key file name, only relevant when encryption method is **not** `none`.\n\n#### vod_hls_encryption_key_uri\n\n- **syntax**: `vod_hls_encryption_key_uri :uri`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSets the value of the URI attribute of `#EXT-X-KEY`, only relevant when encryption method is **not**\n`none`. The parameter value can contain [variables](#nginx-variables).\n\n#### vod_hls_encryption_key_format\n\n- **syntax**: `vod_hls_encryption_key_format :format`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSets the value of the `KEYFORMAT` attribute of `#EXT-X-KEY`, only relevant when encryption method is\n**not** `none`.\n\n#### vod_hls_encryption_key_format_versions\n\n- **syntax**: `vod_hls_encryption_key_format_versions :versions`\n- **default**: `none`\n- **context**: `http`, `server`, `location`\n\nSets the value of the `KEYFORMATVERSIONS` attribute of `#EXT-X-KEY`, only relevant when encryption\nmethod is **not** `none`.\n\n#### vod_hls_mpegts_interleave_frames\n\n- **syntax**: `vod_hls_mpegts_interleave_frames on | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nWhen enabled, the HLS muxer interleaves frames of different streams (audio / video). When disabled,\non every switch between audio / video the muxer flushes the MPEG-TS packet.\n\n#### vod_hls_mpegts_align_frames\n\n- **syntax**: `vod_hls_mpegts_align_frames on | off`\n- **default**: `on`\n- **context**: `http`, `server`, `location`\n\nWhen enabled, every video / audio frame is aligned to MPEG-TS packet boundary. Padding is added as\nneeded.\n\n#### vod_hls_mpegts_output_id3_timestamps\n\n- **syntax**: `vod_hls_mpegts_output_id3_timestamps on | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nWhen enabled, an ID3 TEXT frame is outputted in each TS segment. The content of the ID3 TEXT frame\ncan be set using the directive [`vod_hls_mpegts_id3_data`](#vod_hls_mpegts_id3_data).\n\n#### vod_hls_mpegts_id3_data\n\n- **syntax**: `vod_hls_mpegts_id3_data :data`\n- **default**: `{\"timestamp\":$vod_segment_time,\"sequenceId\":\"$vod_sequence_id\"}`\n- **context**: `http`, `server`, `location`\n\nSets the data of the ID3 TEXT frame written in each TS segment when\n[`vod_hls_mpegts_output_id3_timestamps`](#vod_hls_mpegts_output_id3_timestamps) is `on`. By default\nthe ID3 frames contains a JSON object like `{\"timestamp\":1459779115000,\"sequenceId\":\"1\"}`:\n\n- `timestamp` - an absolute time measured in milliseconds since the `epoch`\n  (`unix_timestamp x 1000`).\n- `sequenceId` - the ID field of the [sequence](#sequence) object, as specified in the JSON mapping.\n  The field is omitted when the sequence ID is empty (not set in the JSON mapping). The parameter\n  value can contain [variables](#nginx-variables).\n\n#### vod_hls_mpegts_align_pts\n\n- **syntax**: `vod_hls_mpegts_align_pts on | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nWhen enabled, the module will shift back the DTS by the PTS delay of the initial frame. This can\nhelp keep the PTS aligned across multiple renditions.\n\n#### vod_hls_encryption_output_iv\n\n- **syntax**: `vod_hls_encryption_output_iv on | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nWhen enabled, the module outputs the `IV` attribute in returned `#EXT-X-KEY` tags.\n\n### Configuration directives - Thumbnail capture\n\n#### vod_thumb_file_name_prefix\n\n- **syntax**: `vod_thumb_file_name_prefix :name`\n- **default**: `thumb`\n- **context**: `http`, `server`, `location`\n\nThe name of the thumbnail file (a `.jpg` extension is implied).\n\n#### vod_thumb_accurate_positioning\n\n- **syntax**: `vod_thumb_accurate_positioning on | off`\n- **default**: `on`\n- **context**: `http`, `server`, `location`\n\nWhen enabled, the module grabs the frame that is closest to the requested offset. When disabled, the\nmodule uses the keyframe that is closest to the requested offset. Setting this parameter to `off`\ncan result in faster thumbnail capture, since the module always decodes a single video frame per\nrequest.\n\n#### vod_gop_look_behind\n\n- **syntax**: `vod_gop_look_behind :millis`\n- **default**: `10000`\n- **context**: `http`, `server`, `location`\n\nSets the interval (in milliseconds) before the thumbnail offset that should be loaded. This setting\nshould be set to the **maximum** GOP size, setting it to a lower value may result in capture\nfailure.\n\n\u003e [!NOTE]\n\u003e The metadata of all frames between `offset - vod_gop_look_behind` and\n\u003e `offset + vod_gop_look_ahead` are loaded, however only the frames of the *minimum* GOP containing\n\u003e `offset` will be read and decoded.\n\n#### vod_gop_look_ahead\n\n- **syntax**: `vod_gop_look_ahead :millis`\n- **default**: `1000`\n- **context**: `http`, `server`, `location`\n\nSets the interval (in milliseconds) after the thumbnail offset that should be loaded.\n\n### Configuration directives - Volume map\n\n#### vod_volume_map_file_name_prefix\n\n- **syntax**: `vod_volume_map_file_name_prefix :name`\n- **default**: `volume_map`\n- **context**: `http`, `server`, `location`\n\nThe name of the volume map file (a `.csv` extension is implied).\n\n#### vod_volume_map_interval\n\n- **syntax**: `vod_volume_map_interval :millis`\n- **default**: `1000`\n- **context**: `http`, `server`, `location`\n\nSets the interval or resolution (in milliseconds) of the volume map.\n\n### Configuration directives - Miscellaneous\n\n#### vod_ignore_edit_list\n\n- **syntax**: `vod_ignore_edit_list on | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nWhen enabled, the module ignores any edit lists (`elst`) in the MP4 file.\n\n#### vod_parse_hdlr_name\n\n- **syntax**: `vod_parse_hdlr_name on | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nWhen enabled, the module parses the `name` field of the `hdlr` MP4 atom and uses it as the stream\nlabel.\n\n#### vod_parse_udta_name\n\n- **syntax**: `vod_parse_udta_name on | off`\n- **default**: `off`\n- **context**: `http`, `server`, `location`\n\nWhen enabled, the module parses the `name` atom child of the `udta` MP4 atom and uses it as the\nstream label.\n\n### NGINX variables\n\nIn addition to the built-in [NGINX variables](https://nginx.org/en/docs/varindex.html), this module\nadds the following ones:\n\n- `$vod_suburi` - the current sub URI. For example, the value of `$vod_suburi` would be\n  `http://\u003cdomain\u003e/\u003clocation\u003e/\u003cprefix\u003e\u003cmiddle1\u003e\u003csuffix\u003e/\u003cfilename\u003e` when processing the first URI of\n  `http://\u003cdomain\u003e/\u003clocation\u003e/\u003cprefix\u003e,\u003cmiddle1\u003e,\u003cmiddle2\u003e,\u003cmiddle3\u003e,\u003csuffix\u003e.urlset/\u003cfilename\u003e`.\n- `$vod_filepath` - the file path of current sub URI in `local` and `mapped` modes. Same as\n  `$vod_suburi` in `remote` mode.\n- `$vod_set_id` - contains the ID of the set.\n- `$vod_sequence_id` - contains the ID of the current sequence, if no ID was specified in the JSON\n  mapping this variable will be the same as `$vod_suburi`.\n- `$vod_clip_id` - the ID of the current clip. The variable is set during the following phases:\n\n  - Mapping of [dynamic clips](#dynamic-clip) to [concat clips](#concat-clip).\n  - Mapping of [source clip](#source-clip) to paths.\n\n- `$vod_notification_id` - the ID of the current notification. This variable is only relevant on\n  `vod_notification_uri`.\n- `$vod_dynamic_mapping` - a serialized representation of the mapping of\n  [dynamic clips](#dynamic-clip) to [concat clips](#concat-clip).\n- `$vod_request_params` - a serialized representation of the request params, e.g. `12-f2-v1-a1`. The\n  variable contains:\n\n  - The segment index (for a segment request).\n  - The sequence index.\n  - A selection of audio and video tracks.\n\n- `$vod_status` - the internal error code of the module, provides a more fine grained classification\n  of errors than HTTP status. The following values are defined:\n\n  - `BAD_REQUEST` - the request is invalid, e.g. `clipFrom` is larger than the video duration.\n  - `NO_STREAMS` - an invalid segment index was requested.\n  - `EMPTY_MAPPING` - the mapping response is empty.\n  - `BAD_MAPPING` - the JSON mapping is invalid, e.g. the `sequences` element is missing.\n  - `BAD_DATA` - the video file is corrupt.\n  - `EXPIRED` - the current server time is larger than `expirationTime`.\n  - `ALLOC_FAILED` - the module failed to allocate memory.\n  - `UNEXPECTED` - a scenario that is not supposed to happen, most likely a bug in the module.\n\n- `$vod_segment_time` - for segment requests, contains the absolute timestamp of the first frame in\n  the segment, measured in milliseconds since the `epoch` (`unix_timestamp x 1000`).\n- `$vod_segment_duration` - for segment requests, contains the duration of the segment in\n  milliseconds.\n- `$vod_frames_bytes_read` - for segment requests, total number of bytes read while processing media\n  frames.\n\n\u003e [!NOTE]\n\u003e Configuration directives that can accept variables are explicitly marked as such.\n\n### Sample configurations\n\n#### Local configuration\n\n```conf\nhttp {\n  upstream fallback {\n    server fallback.example.com:80;\n  }\n\n  server {\n    # vod settings\n    vod_mode local;\n    vod_fallback_upstream_location /fallback;\n    vod_last_modified 'Wed, 09 Apr 2025 14:35:00 GMT';\n    vod_last_modified_types *;\n\n    # vod caches\n    vod_metadata_cache metadata_cache 512m;\n    vod_response_cache response_cache 128m;\n\n    # gzip manifests\n    gzip on;\n    gzip_types application/vnd.apple.mpegurl;\n\n    # file handle caching / aio\n    open_file_cache          max=1000 inactive=5m;\n    open_file_cache_valid    2m;\n    open_file_cache_min_uses 1;\n    open_file_cache_errors   on;\n    aio on;\n\n    location ^~ /fallback/ {\n      internal;\n      proxy_pass http://fallback/;\n      proxy_set_header Host $http_host;\n    }\n\n    location /content/ {\n      root /web/;\n\n      vod hls;\n\n      add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';\n      add_header Access-Control-Allow-Headers 'Origin, Range';\n      add_header Access-Control-Allow-Origin '*';\n      add_header Access-Control-Expose-Headers 'Server, Range, Content-Length, Content-Range';\n      add_header Access-Control-Max-Age '86400';\n\n      expires 1y;\n    }\n  }\n}\n```\n\n#### Mapped configuration\n\n```conf\nhttp {\n  upstream api {\n    server api.example.com:80;\n  }\n\n  upstream fallback {\n    server fallback.example.com:80;\n  }\n\n  server {\n    # vod settings\n    vod_mode mapped;\n    vod_upstream_location /map;\n    vod_upstream_extra_args 'pathOnly=1';\n    vod_fallback_upstream_location /fallback;\n    vod_last_modified 'Wed, 09 Apr 2025 14:35:00 GMT';\n    vod_last_modified_types *;\n\n    # vod caches\n    vod_metadata_cache metadata_cache 512m;\n    vod_response_cache response_cache 128m;\n    vod_mapping_cache mapping_cache 5m;\n\n    # gzip manifests\n    gzip on;\n    gzip_types application/vnd.apple.mpegurl;\n\n    # file handle caching / aio\n    open_file_cache          max=1000 inactive=5m;\n    open_file_cache_valid    2m;\n    open_file_cache_min_uses 1;\n    open_file_cache_errors   on;\n    aio on;\n\n    location ^~ /fallback/ {\n      internal;\n      proxy_pass http://fallback/;\n      proxy_set_header Host $http_host;\n    }\n\n    location ^~ /map/ {\n      internal;\n      proxy_pass http://api/;\n      proxy_set_header Host $http_host;\n    }\n\n    location ~ ^/hls/ {\n      # encrypted hls\n      vod hls;\n\n      vod_secret_key 'secret-$vod_filepath';\n      vod_hls_encryption_method aes-128;\n\n      add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';\n      add_header Access-Control-Allow-Headers 'Origin, Range';\n      add_header Access-Control-Allow-Origin '*';\n      add_header Access-Control-Expose-Headers 'Server, Range, Content-Length, Content-Range';\n      add_header Access-Control-Max-Age '86400';\n\n      expires 1y;\n    }\n  }\n}\n```\n\n#### Mapped + Remote configuration\n\n```conf\nhttp {\n  upstream api {\n    server api.example.com:80;\n  }\n\n  server {\n    # vod settings\n    vod_mode mapped;\n    vod_upstream_location /map;\n    vod_remote_upstream_location /proxy;\n    vod_upstream_extra_args 'pathOnly=1';\n    vod_last_modified 'Wed, 09 Apr 2025 14:35:00 GMT';\n    vod_last_modified_types *;\n\n    # vod caches\n    vod_metadata_cache metadata_cache 512m;\n    vod_response_cache response_cache 128m;\n    vod_mapping_cache mapping_cache 5m;\n\n    # gzip manifests\n    gzip on;\n    gzip_types application/vnd.apple.mpegurl;\n\n    # file handle caching / aio\n    open_file_cache    max=1000 inactive=5m;\n    open_file_cache_valid    2m;\n    open_file_cache_min_uses 1;\n    open_file_cache_errors   on;\n    aio on;\n\n    location ^~ /map/ {\n      internal;\n      proxy_pass http://api/;\n      proxy_set_header Host $http_host;\n    }\n\n    location ~ ^/proxy/(?\u003cprotocol\u003e[^/]+)/(.*) {\n      internal;\n      proxy_pass $protocol://$1;\n      resolver 8.8.8.8;\n    }\n\n    location ~ ^/hls/ {\n      vod hls;\n\n      add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';\n      add_header Access-Control-Allow-Headers 'Origin, Range';\n      add_header Access-Control-Allow-Origin '*';\n      add_header Access-Control-Expose-Headers 'Server, Range, Content-Length, Content-Range';\n      add_header Access-Control-Max-Age '86400';\n\n      expires 1y;\n    }\n  }\n}\n```\n\nSet it up so that `http://api.example.com:80/sample.json` returns the following:\n\n```json\n{\n  \"sequences\": [\n    {\n      \"clips\": [\n        {\n          \"type\": \"source\",\n          \"path\": \"/http/commondatastorage.googleapis.com/gtv-videos-bucket/sample/TearsOfSteel.mp4\"\n        }\n      ]\n    }\n  ]\n}\n```\n\nAnd use this stream URL - `http://\u003cdomain\u003e/hls/sample.json/master.m3u8`.\n\n#### Remote configuration\n\n```conf\nhttp {\n  upstream storage {\n    server storage.example.com:80;\n  }\n\n  server {\n    # vod settings\n    vod_mode remote;\n    vod_upstream_location /remote;\n    vod_last_modified 'Wed, 09 Apr 2025 14:35:00 GMT';\n    vod_last_modified_types *;\n\n    # vod caches\n    vod_metadata_cache metadata_cache 512m;\n    vod_response_cache response_cache 128m;\n\n    # gzip manifests\n    gzip on;\n    gzip_types application/vnd.apple.mpegurl;\n\n    location ^~ /remote/ {\n      internal;\n      proxy_pass http://storage/;\n      proxy_set_header Host $http_host;\n    }\n\n    location ~ ^/hls/ {\n      vod hls;\n\n      add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS';\n      add_header Access-Control-Allow-Headers 'Origin, Range';\n      add_header Access-Control-Allow-Origin '*';\n      add_header Access-Control-Expose-Headers 'Server, Range, Content-Length, Content-Range';\n      add_header Access-Control-Max-Age '86400';\n\n      expires 1y;\n    }\n  }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiogoazevedos%2Fnginx-vod-module","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdiogoazevedos%2Fnginx-vod-module","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiogoazevedos%2Fnginx-vod-module/lists"}