Site/Pages/Usage/Abr/Abandon Request Rule
---
title: AbandonRequestRule
---
AbandonRequestRule
Description
During the download of a media segment dash.js receives multiple
progress events from the underlying API that isused for downloading the data (e.g.
XMLHttpRequest). These events have a timestamp and contain information about thenumber of bytes that have been received so far. Using this information dash.js decides whether to abort the download of
a segment.
The AbandonRequestRule implements the logic to decide whether the download of a media segment shall be aborted.
Moreover, the rules decides to which quality to switch in such a scenario. Segment downloads are aborted if the current
throughput is not
sufficient to finish the download of the current media segment in a "reasonable" time.
For that reason, the AbandonRequestRule calculates the throughput based on the received samples (progress event)
from the current media segment. If the remaining download time for the current segment is larger than the current
segment duration multiplied
by abandonDurationMultiplier and the number of remaining bytes to download is larger than the number of total bytes
for the new quality the AbandonRequestRule triggers a switch to a lower quality:
if (estimatedTimeOfDownloadInSeconds < request.duration * settings.get().streaming.abr.rules.abandonRequestsRule.parameters.abandonDurationMultiplier || abrController.isPlayingAtLowestQuality(representation)) {
return switchRequest;
}const remainingBytesToDownload = request.bytesTotal - request.bytesLoaded;
const optimalRepresentationForBitrate = abrController.getOptimalRepresentationForBitrate(mediaInfo, throughputInKbit, true);
const totalBytesForOptimalRepresentation = request.bytesTotal * optimalRepresentationForBitrate.bitrateInKbit / currentRequestedRepresentation.bitrateInKbit;
// Switch quality in case there is a Representation that requires less bytes to download
if (remainingBytesToDownload > totalBytesForOptimalRepresentation) {
switchRequest.representation = optimalRepresentationForBitrate;
switchRequest.reason = {
throughputInKbit
}
abandonDict[request.index] = true;
logger.info([AbandonRequestRule]${mediaType} is asking to abandon and switch to quality to ${optimalRepresentationForBitrate.absoluteIndex}. The measured bandwidth was ${throughputInKbit} kbit/s);
}
Configuration Options
| Parameter | Description |
|:--------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------|
| abandonDurationMultiplier | Factor to multiply with the segment duration to compare against the estimated remaining download time of the current segment. See code example above. |
| minSegmentDownloadTimeThresholdInMs | The AbandonRequestRule only kicks if the download time of the current segment exceeds this value. |
| minThroughputSamplesThreshold | Minimum throughput samples (equivalent to number of progress events) required before the AbandonRequestRule kicks in. |
Example
player.updateSettings({
streaming: {
abr: {
rules: {
abandonRequestsRule: {
active: true,
parameters: {
abandonDurationMultiplier: 1.8,
minSegmentDownloadTimeThresholdInMs: 500,
minThroughputSamplesThreshold: 6
}
}
}
}
}
});---
Site/Pages/Usage/Abr/Bola Rule
---
title: BolaRule
---
BolaRule
Description
The BolaRule is a buffer based ABR rule. BOLA uses a bitrate selection function that maps the current buffer level to
the bitrate of the next segment to be downloaded. The illustration below shows an example of a bitrate selection
function for a video that is encoded in three bitrates (1000, 2500 and 5000 kbps) and has a buffer capacity of 18
seconds. The thresholds for switching to a different quality are at 5 and 10 seconds of buffer.
For additional details about the BOLA rule check out the following two paper:
* [From Theory to Practice: Improving Bitrate Adaptation in the DASH Reference Player
* BOLA: Near-optimal bitrate adaptation for online videos
Example
player.updateSettings({
streaming: {
abr: {
rules: {
bolaRule: {
active: true
}
}
}
}
});---
Site/Pages/Usage/Abr/Dropped Frames Rule
---
title: DroppedFramesRule
---
DroppedFramesRule
Description
While the current throughput and the current buffer level might allow playing the video stream on a high
quality Representation the underlying platform might not be able to render the content without dropping frames.
Dropped frames refer to video frames that are not successfully delivered or displayed during playback. This can occur
when the video playback system is unable to keep up with the required frame rate, resulting in skipped frames. Dropped
frames can cause a decrease in video quality and a disruption in smooth playback.
The DroppedFramesRule monitors the ratio of dropped frames and total frames and reduces the video quality if the ratio
exceeds the value defined in droppedFramesPercentageThreshold:
if (totalFrames > settings.get().streaming.abr.rules.droppedFramesRule.parameters.minimumSampleSize
&& droppedFrames / totalFrames > settings.get().streaming.abr.rules.droppedFramesRule.parameters.droppedFramesPercentageThreshold) {
newRepresentation = representations[i - 1];
}
Configuration Options
| Parameter | Description |
|:-----------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------|
| minimumSampleSize | Sum of rendered and dropped frames required for each Representation before the rule kicks in. |
| droppedFramesPercentageThreshold | Minimum percentage of dropped frames compared to total frames to trigger a quality downs-switch. Values are defined in the range of 0 - 1 |
Example
player.updateSettings({
streaming: {
abr: {
rules: {
droppedFramesRule: {
active: true,
parameters: {
minimumSampleSize: 375,
droppedFramesPercentageThreshold: 0.15
}
}
}
}
}
});---
Site/Pages/Usage/Abr/Index
---
title: Adaptive Bitrate Streaming
---
Adaptive Bitrate Streaming
Encoding and packaging the media content with multiple bitrates and resolutions enables adaptive media streaming.
Mediaplayers such as dash.js can dynamically switch between different bitrates and resolutions based on factors such as
the current throughput, the current buffer level and the resolution on the end device.
dash.js has a flexible ABR decision logic in place that can be dynamically adjusted and extended. On the following pages
you can find information about the various settings that dash.js offers and how to plug in your own ABR algorithm:
* ABR Settings - configure the ABR behavior and select the active algorithm
* Manual quality selection - disable ABR and select qualities manually
* Throughput Calculation - how dash.js estimates the available bandwidth
ABR Rules
The ABR decision logic is composed of individual rules. Each rule casts a switch request, the results are aggregated
into the final quality decision. The following pages describe the built-in rules in detail:
* ThroughputRule - selects the quality based on the estimated throughput
* BolaRule - buffer based quality selection using the BOLA algorithm
* InsufficientBufferRule - avoids rebuffering by reacting to critical buffer levels
* AbandonRequestRule - abandons segment downloads that take too long
* DroppedFramesRule - avoids qualities that cause dropped frames
* SwitchHistoryRule - penalizes qualities that were recently abandoned
* L2A Rule - learn2adapt rule for low latency streaming
* LoL+ Rule - low-on-latency rule set for low latency streaming
---
Site/Pages/Usage/Abr/Insufficient Buffer Rule
---
title: InsufficientBufferRule
---
InsufficientBufferRule
Description
The InsufficientBufferRule verifies each ABR choice to make sure the download is unlikely
to cause a rebuffering event. The rule is best explained by looking at a concrete example. Assume the following values:
* currentThroughput = 5Mbit/s
currentSafeThroughput = currentThroughput throughputSafetyFactor = 5Mbit/s * 0.9 = 4.5 Mbit/s
* currentBufferLevel = 10 seconds
* segmentDuration = 4 seconds
To avoid a buffer underrun we need to finish the download of the next segment in 10 seconds. This means we need to
download 4 seconds of content in 10 seconds. This leads us to the following expression that we need to solve:
possibleBitrate <= currentSafeThroughput * currentBufferLevel / segmentDuration
Plugging the examples values from above we get:
possibleBitrate <= 4.5 Mbit/s * 10s / 4s
So in this case we can select a maximum bitrate of 11.25 Mbit/s.
Configuration Options
| Parameter | Description |
|:-------------------------|:--------------------------------------------------------------------------------------------------------------------------|
| throughputSafetyFactor | The safety factor that is applied to the derived throughput, see example in the Description. |
| segmentIgnoreCount | This rule is not taken into account until the first segmentIgnoreCount media segments have been appended to the buffer. |
Example
player.updateSettings({
streaming: {
abr: {
rules: {
insufficientBufferRule: {
active: true,
parameters: {
throughputSafetyFactor: 0.9,
segmentIgnoreCount: 2
}
}
}
}
}
});---
Site/Pages/Usage/Abr/L2a
---
title: L2A Rule
---
L2A
Description
In the context of adaptive streaming, an ABR algorithm aims at seamlessly adjusting (or adapting) the rate of the media
stream, to compensate for changing network conditions. Additionally, a buffer is typically deployed to protect the
client from abrupt changes in the communication channel (throughput, jitter etc.), or temporal misestimations of the ABR
algorithm. Since long buffer queues compound delay of the media rendering process, low-latency streaming requires very
short buffers, that in turn offer less protection against channel state estimation errors. Such errors are propagated to
the ABR decisions, that in turn can have a detrimental effect on streaming experience.
Therefore the goal of Learn2Adapt-LowLatency (L2A-LL), a low-latency ABR, is to strike a favorable balance between
keeping the buffer as short as possible, while provisioning against its complete depletion. This is achieved by
selecting the highest sustainable bitrate for each video fragment, that does not completely consume the buffer budget
available at the time of request. L2A-LL is, in essence, an optimization solution with the objective of minimizing
latency, while at the same time maximizing achievable video bitrate and ensuring uninterrupted and stable streaming.
L2A-LL formulates the ABR optimization problem under an online (machine) learning framework, based on convex
optimization. First, the streaming client is modelled by a learning agent, whose objective is to minimize the average
buffer displacement of a streaming session. Second, certain requirements regarding the decision set (available bitrates)
and constraint functions are fulfilled by a) allowing the learning agent to make decisions on the video bitrate of each
fragment, according to a probability distribution and by b) deriving an appropriate constraint function associated with
the upper bound of the buffer queue, that adheres to time averaging constraints.
Basic dash.js configuration
How to enable L2A:
player.updateSettings({
streaming: {
abr: {
rules: {
l2a: {
active: true
}
}
}
}
});Advanced Tuning parameters
The following changes are for experienced users and need to be made in streaming/rules/abr/L2ARule.js. Please check
the paper for further details.
| Line | Parameter | Description | Conclusion |
|------|-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 306 | horizon=4 | Optimization horizon: This parameter is used to specify the amount of steps required to achieve convergence. In live streaming settings this parameter must be kept low for stable performance. The selected value has been verified experimentally and alteration is not suggested. This parameter is used in lines 307 and 308, at the calculation of the 'vl' and 'alpha' parameters respectively. The calculation of 'vl' and 'alpha' are according to the theoretical performance guarantees as specified in the MMSys Publication [5]. Higher 'vl' values make the algorithm more aggressive in the bitrate selection and 'alpha' is the step size of the gradient descent approach of the learning process. | higher 'horizon' leads to more aggressive bitrate selection (higher 'vl') and less exploration (large 'alpha'). Not advisable for live streaming scenarios with short buffers. |
| 322 | react=2 | Reactiveness to volatility (abrupt throughput drops). This parameter is used to recalibrate the 'l2AParameter.Q'. Higher values make the algorithm more conservative. The chosen value has been experimentally selected and alteration is not suggested. | Higher 'react' results in a more conservative algorithm (higher l2AParameter.Q). Caution: 'react' values higher than the selected (react=2) may make the algorithm select lowest bitrate for extended periods, until recovery of the l2AParameter.Q (updated at every fragment download) |
Reference
* Theo Karagkioules,R. Mekuria,Dirk Griffioen, Arjen Wagenaar - Online learning for low-latency adaptive streaming
---
Site/Pages/Usage/Abr/Lol Plus
---
title: LoL+ Rule
---
LoL+
Description
LoL+ is an algorithm optimized for CMAF low latency. As such it should not be used for "standard" VoD and live content
and only be activated if the CMAG segments contain CMAF chunks. LoL+ is designed as a series of sophisticated yet robust
player improvements for low latency live (LLL) streaming. LoL+ consists of five essential modules:
1. The bitrate selection module implements a learning-based ABR algorithm to choose a suitable bitrate at each
segment download. The ABR algorithm is based on an SOM model that considers multiple QoE metrics as well as bandwidth
variability.
2. The playback speed control module implements a hybrid algorithm that considers both the current latency and
buffer level to control the playback speed.
3. The throughput measurement module accurately calculates the throughout by removing the idle times between the
chunks of a segment through a three-step algorithm.
4. The QoE evaluation module computes the QoE considering five key metrics: selected bitrate, number of bitrate
switches, rebuffering duration, latency and playback speed.
5. Lastly, the weight selection module implements a two-step dynamic weight assignment for the SOM model features.
We also added manual (equal value of 0.4 each) and random (based on Xavier formula) weight assignment for the SOM
model features for comparison.
The modules can be found in the following files:
1. Bitrate selection module (i.e., ABR algorithm):
dash.js/src/streaming/rules/abr/lolp/LoLpRule.js and
LearningAbrController.js
2. Playback speed control module:
dash.js/src/streaming/controllers/CatchupController.js
3. Throughput measurement module:
dash.js/src/streaming/net/FetchLoader.js
4. QoE evaluation module:
dash.js/src/streaming/rules/abr/lolp/LoLpQoeInfo.js and dash.js/src/streaming/rules/abr/lolp/LoLpQoEEvaluator.js
5. Weight selection module:
dash.js/src/streaming/rules/abr/lolp/LoLpWeightSelector.js
Basic dash.js configuration
How to enable each of the LoL+ modules:
player.updateSettings({
streaming: {
abr: {
rules : {
loLPRule: {
active: true
}
},
throughput: {
lowLatencyDownloadTimeCalculationMode: dashjs.Constants.LOW_LATENCY_DOWNLOAD_TIME_CALCULATION_MODE.MOOF_PARSING
}
},
liveCatchup: {
mode: dashjs.Constants.LIVE_CATCHUP_MODE_LOLP
}
}
});Note: The weight selection module is used in the bitrate selection module and does not need to be enabled separately.
Advanced tuning parameters
The parameters below are available but not advised to be changed. For advanced users, please refer to the paper [3] for
further details on these parameters. The following parameters are not exposed in the settings object and have to be
changed in the respective classes.
Module | Parameter(s) | Remarks
--------------------------|-----------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Bitrate selection module | targetLatency = 0 | SOM target latency
Bitrate selection module | targetRebufferLevel = 0 | SOM target rebuffering duration.
Bitrate selection module | targetSwitch = 0 | SOM target number of switches.
Bitrate selection module | throughputDelta = 10000 | SOM variation in throughput.
Weight selection module | DWS_TARGET_LATENCY = 1.5 | Latency constraint used in weight selection module (set in LolpRule.js).
Weight selection module | DWS_BUFFER_MIN = 0.3 | Minimum buffer value constraint used in weight selection module (set in LolpRule.js).
Weight selection module | weightObj.throughput, weightObj.latency, weightObj.buffer, weightObj.switch | The weight (value ranges between 0 and 1) of the current throughput, latency, rebuffering duration, and number of switches features of the SOM model (Bitrate selection module) will be assigned dynamically by this module based on the defined optimization function.
Detailed code description
At each segment download, the bitrate selection module is triggered and works as follows:
##### (1) Obtain input parameters (pseudocode lines 4-10):
* Current player state: throughput, latency, rebuffer duration, bitrate variation (normalized)
* Weight vector from weight selection module: w
##### (2) Update previously selected neuron with current player state values (pseudocode line 11):
// Code snippet from LearningAbrController.js
_updateNeurons(currentNeuron, somElements, [throughputNormalized, latency, rebuffer, bitrateSwitch]);##### (3) Iterate all neurons to find the winner neuron that is closest (i.e., shortest distance) to the target state, while not violating special condition (pseudocode lines 12-27):
// Code snippet from LearningAbrController.js
// special condition downshift immediately
if (somNeuron.bitrate > throughput - throughputDelta || isBufferLow) {
if (somNeuron.bitrate !== minBitrate) {
// encourage to pick smaller bitrates throughputWeight=100
distanceWeights[0] = 100;
}
}// calculate the distance with the target
let distance = _getDistance(somData, [throughputNormalized, targetLatency, targetRebufferLevel, targetSwitch], distanceWeights);
if (minDistance === null || distance < minDistance) {
minDistance = distance;
minIndex = somNeuron.qualityIndex;
winnerNeuron = somNeuron;
winnerWeights = distanceWeights;
}
##### (4) Update the winner neuron with target state values. (pseudocode line 28):
// Code snippet from LearningAbrController.js
_updateNeurons(winnerNeuron, somElements, [throughputNormalized, targetLatency, targetRebufferLevel, bitrateSwitch]);Note: The QoE evaluation module is provided but QoE score is not used as a SOM factor in the current implementation of
the ABR algorithm. Advanced users may consider using it.
Reference
---
Site/Pages/Usage/Abr/Manual Quality Selection
---
title: Manual quality selection
---
Manual quality selection
Next to the adaptive quality selection of dash.js there is also the possibility for applications and users to
manually select a specific quality (Representation). For that reason, dash.js provides two API endpoints, namelysetRepresentationForTypeById() and setRepresentationForTypeByIndex().
Selecting a Representation by ID
The recommended way to select a specific quality is by using the id attribute of the target Representation. A simple
example illustrating this behavior is depicted below:
const availableQualities = player.getRepresentationsByType('video');
const targetRepresentation = availableQualities[0];
player.setRepresentationForTypeById('video', targetRepresentation.id);
In the example above we are querying the available qualities. dash.js will return an array of Representation objects
with a unique id attribute for each of the objects. Finally, we select the first entry in the array and tell dash.js
to switch to this Representation by using the id attribute.
Selecting a Representation by index
Another way to select a specific Representation is by providing its index to dash.js. A simple example illustrating this
behavior is depicted below:
const availableQualities = player.getRepresentationsByType('video');
const targetRepresentationIndex = availableQualities.length - 1;
player.setRepresentationForTypeByIndex('video', targetRepresentationIndex);
In the example above we are querying the available qualities. dash.js will return an array of Representation objects.
Finally, we count the number of entries in the array and select the one with the highest index.
Note: Each Representation object has an attribute absoluteIndex. This attribute is used internally by
dash.js
and might be different from the index of the Representation in the array returned by getRepresentationsByType(). For
that reason do not use the absoluteIndex when selecting a quality with setRepresentationForTypeByIndex().
Instead, use the index of the target Representation in the array returned by getRepresentationsByType().
---
Site/Pages/Usage/Abr/Settings
---
title: ABR Settings
---
ABR Settings
ABR Examples
Multiple samples implementing the functionalities described in this documentation can be found in
the ABR section.
Changing the default ABR algorithm
dash.js ships with multiple ABR rules. Per default, dash.js combines a throughput based ABR rule (throughputRule) with
a buffer based
ABR rule (bolaRule). The abr section in Settings.js allows a reconfiguration of the default ABR algorithms:
player.updateSettings({
streaming: {
abr: {
rules: {
throughputRule: {
active: true
},
bolaRule: {
active: true
}
}
}
}
});| ABR Rule | Description |
|:-----------------|:---------------------------------------|
| throughputRule | ThroughputRule |
| bolaRule | BolaRule |
Important: If both throughputRule and bolaRule are enabled dash.js dynamically switches between those two
rules based on the current buffer level.
An example illustrating how to change the ABR rules is
available here.
Additional ABR rules
Next to the two main ABR rules described above, dash.js defines additional ABR rules that run alongside the main rules
and
can be dynamically enabled and disabled.
player.updateSettings({
abr: {
rules: {
insufficientBufferRule: {
active: false
},
switchHistoryRule: {
active: false
},
droppedFramesRule: {
active: false
},
abandonRequestsRule: {
active: false
},
l2ARule: {
active: false
},
loLPRule: {
active: false
}
}
}
});| ABR Rule | Description |
|:-------------------------|:--------------------------------------------------------|
| insufficientBufferRule | InsufficientBufferRule |
| switchHistoryRule | SwitchHistoryRule |
| droppedFramesRule | DroppedFramesRule |
| abandonRequestsRule | AbandonRequestRule |
| l2ARule | L2ARule |
| loLPRule | LoL+ |
A detailed example is available here.
Adding a custom ABR rule
dash.js allows applications to define their own ABR algorithms. For that reason, disable the default ABR rules and
use player.addABRCustomRule() to add your new rule:
/ don't use dash.js default rules /
player.updateSettings({
abr: {
rules: {
throughputRule: {
active: false
},
bolaRule: {
active: false
},
insufficientBufferRule: {
active: false
},
switchHistoryRule: {
active: false
},
droppedFramesRule: {
active: false
},
abandonRequestsRule: {
active: false
}
}
}
});/ add my custom quality switch rule. Look at LowestBitrateRule.js to know more /
/ about the structure of a custom rule /
player.addABRCustomRule('qualitySwitchRules', 'LowestBitrateRule', LowestBitrateRule);
A detailed example is available here.
Disabling the ABR behavior
dash.js allows applications to disable the adaptive bitrate behavior for the audio and/or video media type. For that
reason, simply disable the autoSwitchBitrate setting for the respective media type:
player.updateSettings({
streaming: {
abr: {
autoSwitchBitrate: { audio: true, video: false },
}
}
});A detailed example is available here.
Selecting the initial bitrate
In some cases the application might want to define the initial bitrate for either the audio track or the video track
prior to
the start of the playback.
For that reason, dash.js exposes the initialBitrate setting. The target value is specified in kbps. In the example
below
the initial bitrate for video is set to 800 kbit/s.
player.updateSettings({
streaming: {
abr: {
initialBitrate: { audio: -1, video: 800 }
}
}
});A detailed example is available here.
Defining a minium/maximum bitrate
It is also possible to define a minimum and/or a maximum bitrate for the ABR algorithms. dash.js will then only adapt
the bitrate
within these thresholds.
In the example below the maximum bitrate for video is set to 5000 kbit/s while the minimum bitrate for video is defined
as 2000 kbit/s.
player.updateSettings({
streaming: {
abr: {
maxBitrate: { audio: -1, video: 5000 },
minBitrate: { audio: -1, video: 2000 },
}
}
});A detailed example is available here.
Fast bitrate switching
When the quality/bitrate for a certain media type is changed dash.js has two options. It can either append the next
fragment at the end of the current buffer or replace existing parts of the buffer with the newly selected quality.
When fastSwitchEnabled is set to true the next fragment is requested and appended close to the current playback
time. Note: When ABR down-switch is detected, dash.js appends the lower quality at the end of the buffer range to
preserve the
higher quality media for as long as possible.
player.updateSettings({
streaming: {
buffer: {
fastSwitchEnabled: true
}
}
});A detailed example is available here.
---
Site/Pages/Usage/Abr/Switch History Rule
---
title: SwitchHistoryRule
---
SwitchHistoryRule
Description
Frequent quality switches result in a negative QoE for the end-user. The main objective of the SwitchHistoryRule is to
to detect and avoid any extreme bitrate oscillations allowed by the ABR algorithms. For that reason,
the SwitchHistoryRule monitors quality down-switches. It derives a ratio of down-switches divided by the number of
times the quality stayed the same or even improved. If this ratio exceeds the switchPercentageThreshold the quality is
reduced.
if (drops + noDrops >= settings.get().streaming.abr.rules.switchHistoryRule.parameters.sampleSize
&& (drops / noDrops > settings.get().streaming.abr.rules.switchHistoryRule.parameters.switchPercentageThreshold)) {
switchRequest.representation = (i > 0 && switchRequests[currentPossibleRepresentation.id].drops > 0) ? representations[i - 1] : currentPossibleRepresentation;
}
Configuration Options
| Parameter | Description |
|:----------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------|
| minimumSampleSize | Sum of ABR quality checks required before the rule kicks in. |
| switchPercentageThreshold | Ratio of quality drops compared to no quality drops must exceed this threshold before a potential quality down-switch by the SwitchHistoryRule is enforced. |
Example
player.updateSettings({
streaming: {
abr: {
rules: {
switchHistoryRule: {
active: true,
parameters: {
minimumSampleSize: 8,
switchPercentageThreshold: 0.075
}
}
}
}
}
});---
Site/Pages/Usage/Abr/Throughput Calculation
---
title: Throughput Calculation
---
Throughput Calculation
Description
dash.js provides multiple options to configure the calculation of the current average throughput. This calculation is an
important input for most of the ABR rules e.g. the ThroughputRule.
Configuration Options
The following options are available:
| Throughput Calculation Mode | Description |
|:-------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| EWMA | Exponential Weighted Moving Average (EWMA) is a calculation mode that assigns exponentially decreasing weights to the historical data points. It gives more importance to recent data while gradually decreasing the influence of older data points. The settings.abr.throughput.ewma object allows the configuration of the EWMA parameters. |
| ZLEMA | Zero-Lag Exponential Moving Average (ZLEMA) is a calculation mode that aims to reduce or eliminate the lag typically associated with traditional exponential moving averages. It achieves this by using a specific formula that adjusts the weights of the data points to minimize lag |
| ARITHMETIC_MEAN | Arithmetic mean, also known as the average, is a statistical measure that calculates the sum of a set of values divided by the number of values in the set. |
| BYTE_SIZE_WEIGHTED_ARITHMETIC_MEAN | Byte size weighted arithmetic mean is a calculation method that assigns weights to different values based on their respective byte sizes. To calculate the byte size weighted arithmetic mean, you multiply each value by its corresponding byte size, then sum up the weighted values, and divide the sum by the total byte size. |
| DATE_WEIGHTED_ARITHMETIC_MEAN | Date-weighted arithmetic mean is a calculation method that assigns weights to different values based on their respective dates. To calculate the date-weighted arithmetic mean, you multiply each value by its corresponding weight based on the date, then sum up the weighted values, and divide the sum by the total weight. |
| HARMONIC_MEAN | To calculate the harmonic mean, you take the total number of values in the set, divide it by the sum of the reciprocals of each value, and then take the reciprocal of that result. As the harmonic mean involves taking the reciprocals of the values, extremely large values have a significant impact on the calculation. |
| BYTE_SIZE_WEIGHTED_HARMONIC_MEAN | Similar to the harmonic mean calculation but assigns weights to the different sample values based on their respective byte sizes. |
| DATE_WEIGHTED_HARMONIC_MEAN | Similar to the harmonic mean calculation but assigns weights to the different sample values based on their respective dates. The most recent sample has a higher weight than the previous samples. |
The default mode is EWMA. Most of the throughput calculation modes work on a fixed number of throughput samples.
The settings.abr.throughput.sampleSettings objects allows the configuration of sample related settings.
Example
In the example below we change the default mode to byte size weighted harmonic mean. In addition, we change the number
of samples to be used to five and disable the automatic adjustment of the sample
size.
A detailed example is
available here.
player.updateSettings({
streaming: {
abr: {
throughput: {
averageCalculationMode: dashjs.Constants.THROUGHPUT_CALCULATION_MODES.BYTE_SIZE_WEIGHTED_HARMONIC_MEAN,
sampleSettings: {
vod: 5,
enableSampleSizeAdjustment: false
}
},
}
}
});---
Site/Pages/Usage/Abr/Throughput Rule
---
title: ThroughputRule
---
ThroughputRule
Description
The ThroughputRule is a very simple ABR rule that uses the average throughput of the previous media segment downloads
to derive the optimal bitrate for the next media segment request.
The essential lines in the implementation are depicted below:
const throughput = throughputController.getSafeAverageThroughput(mediaType);
switchRequest.representation = abrController.getOptimalRepresentationForBitrate(mediaInfo, throughput, true);Configuration Options
There are no values that are specifically targeting the rule. However, there are some throughput related parameters that
implicitly influence this rule as it is using throughputController.getSafeAverageThroughput(). The throughput related
parameters are documented here.
Example
player.updateSettings({
streaming: {
abr: {
rules: {
throughputRule: {
active: true
}
}
}
}
});---
Site/Pages/Usage/Subtitles And Captions/Custom Webvtt Rendering
---
title: Custom WebVTT Rendering
---
Description
Next to the default WebVTT rendering using the native browser APIs, dash.js also provides a way to use the vtt.js
library to render WebVTT subtitles. This allows for more customization and control over the rendering process.
Setup
To enable custom WebVTT rendering we first need to enable the customRenderingEnabled flag in the settings:
player.updateSettings({
streaming: {
text: {
webvtt: {
customRenderingEnabled: true
}
}
}
})Next we add add an HTML <div> element as the target container for rendering the subtitles:
<video preload="auto" muted=""></video>
<div style="position: relative">
<div id="vtt-rendering-div" style="min-width: 600px; min-height: 100px;"></div>
</div>Now, we can attach the <div> element to the dash.js player:
let vttRenderingDiv = document.querySelector("#vtt-rendering-div");
player.attachVttRenderingDiv(vttRenderingDiv)Finally we need to include the vtt.js library in our HTML file. The library is part of the contrib folder of
dash.js:
<script src="../../contrib/videojs-vtt.js/vtt.min.js"></script>Example
A complete working example can be found in
our sample section.
---
Site/Pages/Usage/Subtitles And Captions/Dvb Font Downloading
---
title: DVB Font Downloading
---
DVB Font Downloading
dash.js supports the mechanism described in the DVB DASH profile (ETSI TS 103 285 Section 7.2 Downloadable Fonts) for signalling downloadable fonts using descriptors within an MPD.
This is intended for use with EBU-TT-D (compatible with IMSC1 Text Profile) subtitles.
The key details of the mechanism and how to use it are covered here.
Usage
As a content provider, you may choose to specify fonts within your TTML subtitles that the subtitles should be rendered with.
This could be for accessibility, language, or stylistic reasons.
However this will only work if the specified fonts are available on the device or browser where the dash player is being used, which may not always be under the control of the content provider.
To assist with this, the DVB font download mechanism allows you to signal font resources for download in an MPD, and associate them with specific font family names used within the TTML.
This is achieved by including Supplemental or Essential Property descriptors with the specified scheme in the MPD.
Reference media which signals fonts for download can be found in the dash.js reference player.
This functionality is always available and does not need to be enabled through player settings.
If a downloadable font is correctly signalled, the download will be attempted.
Signalling Downloadable Fonts
Downloadable fonts are signalled in an MPD by using a <SupplementalProperty> descriptor, or a <EssentialProperty> descriptor, within an <AdaptationSet>.
The descriptor used must have a schemeIdUri attribute set to "urn:dvb:dash:fontdownload:2014" and a value attribute set to "1".
DVB Attributes
Additional attributes are required on the descriptors to signal information about the downloadable font.
These attributes are defined in the DVB namespace urn:dvb:dash-extensions:2014-1.
Below gives an example of mapping the namespace to a dvb: prefix in an MPD.
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:dvb="urn:dvb:dash-extensions:2014-1">The table below details the attributes themselves.
| Name | Type | Description |
|:---------------- |:-------|:---------------------------------------------------------------------------------------|
| dvb:url | URI | The URL of the font to download. Can be absolute or can make use of relative BaseURLs. |
| dvb:fontFamily | String | The font family used in EBU-TT-D documents, within the tts:fontFamily style attribute. |
| dvb:mimeType | String | The mimeType of the font available from the URL. |
Property Descriptor Example
<SupplementalProperty
schemeIdUri="urn:dvb:dash:fontdownload:2014"
value="1"
dvb:url="https://example.com/fonts/SubtitleDisplay.woff"
dvb:fontFamily="SubtitleDisplay"
dvb:mimeType="application/font-woff"
/>Mimetype Support
The DVB DASH specification denotes support for two mimeTypes:
* application/font-sfnt - This covers .ttf and .otf fonts.
* application/font-woff - Covering .woff fonts.
No further support for unspecified mimeTypes has been provided.
Supplemental vs Essential Property Descriptors
SupplementalProperty descriptors are used when it is acceptable to show the subtitles in another font if the download fails.
EssentialProperty descriptors are used when the subtitles must not be shown at all if the download fails.
As such the player acts based on what kind of property descriptor was used to describe the downloadable font.
If a <SupplementalProperty> descriptor was used and download fails, then the <AdaptationSet> containing the descriptor continues to be presented as if the <SupplementalProperty> descriptor was not present.
If an <EssentialProperty> descriptor was used and download fails, then the <AdaptationSet> containing the descriptor is not be presented at all.
What this looks like on a client is described in a later section.
Using the Downloaded Fonts
TTML Font Family Attribute
The EBU-TT-D subtitles need to indicate that they want to use the downloaded font.
To do this, they must include the font family name within the comma-separated list of fonts in the tts:fontFamily attribute, and the name must match the value of the dvb:fontFamily attribute in the MPD.
So, for example, if we have this attribute in an MPD,
<SupplementalProperty
dvb:fontFamily="SubtitleDisplay"
/>we would need to ensure this is present in the subtitle TTML documents to put the font to use.
<style
tts:fontFamily="SubtitleDisplay, Arial, default"
/>Note that fallback fonts can still be specified.
In this case we have "Arial" specified as an expected system font, and "default" which is interpreted by IMSC1 as the default monospace serif font provided by the browser/device in use.
Download Process
Download of fonts happens alongside other media, as to not impede the start of playback.
If the download is successful then the font is added to the document interface.
When the player is reset, or a new source is added, any added fonts are then removed from the document.
The download status of a font and the type of property descriptor used to describe the font have an effect on display of the <AdaptationSet> it is related to.
Consider a situation where subtitles are set to display on load, and we have the correct entries in our MPD and TTML documents to use our hypothetical 'SubtitleDisplay' font.
The process followed for the different property descriptors is described below.
For a <SupplementalProperty> descriptor, the displayed subtitles can appear in a 'fall-back' font before 'SubtitleDisplay' has been downloaded, in this case 'Arial'.
This causes, in effect, a flash of unstyled text (FOUT).
The subtitles, handled as a TextTrack can retain a mode property of "showing" throughout the process.
For an <EssentialProperty> descriptor, the subtitles will not appear unless the 'SubtitleDisplay' font downloads. Until this has downloaded, the mode property of the TextTrack handling these subtitles is set to "disabled". This is done to best replicate what is expected by the DVB DASH specification, and also given there is no method to delete a TextTrack from a TextTrackList if the tracks are not linked to DOM elements. Once 'SubtitleDisplay' has downloaded the text track mode can be set to "showing".
---
Site/Pages/Usage/Subtitles And Captions/Index
---
title: Subtitles & Captions
---
Subtitles & Captions
dash.js has support for multiple subtitling and captioning formats including VTT captions, embedded CEA 608/708
captions, and TTML EBU timed text tracks.
Additionally, dash.js has a mechanism for consuming subtitle events so subtitles can be rendered separately, outside of
dash.js.
Multiple samples demonstrating subtitle handling in dash.js can be found in
our sample section. For more
detailed usage instructions check the links below:
---
Site/Pages/Usage/Subtitles And Captions/Subtitle Handling
---
title: Basic Subtitle Handling
---
Basic Subtitle Handling
Many examples can be found in
our samples section.
Enabling / Disabling by default
Subtitles can be enabled and disabled by default by changing the defaultEnabled property:
player.updateSettings({
streaming: {
text: {
defaultEnabled: true
}
}
});
Initial track selection
The initial language or role can be set using the setInitialMediaSettingsFor method. Please refer to
the track selection documentation for details.
A working sample can be
found here.
Track selection at runtime
To select a specific text track during playback use the setTextTrack method. You need to provide a valid index of a
track. To disable the texttrack rendering pass -1 to the setTextTrack method.
``` js
var textTrackList = {};
var streamId = null;
var _onTracksAdded = function (e) {
if (!textTrackList[e.streamId]) {
textTrackList[e.streamId] = [];
}
streamId = e.streamId;
textTrackList[e.streamId] = textTrackList[e.streamId].concat(e.tracks);
};
player.on(dashjs.MediaPlayer.events.TEXT_TRACKS_ADDED, _onTracksAdded, this);
var item = textTrackList[streamId][0];
player.setTextTrack(item.index);
Another way to change the texttrack is to use the setCurrentTrack method. For details refer to
the track selection documentation.---
Site/Pages/Usage/Buffer Management
---
title: Buffer Management
---
Buffer Management
dash.js offers a variety of settings to manage the buffer. The buffer is used to store media segments that have been
downloaded but not yet played. The buffer is managed by the player and can be adjusted to meet specific requirements.
The dash.js settings allow the configuration of the initial, backward and forward buffer.
Configuration Options
The buffer can be configured using the following settings:
| Setting | Description |
|:-----------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| initialBufferLevel | Initial buffer level to be reached before playback is automatically started. Not that this setting only applies at playback start and is not taken into account after a seek or when the buffer ran dry. |
| bufferToKeep | Defines how much backward buffer to keep. The backward buffer is the buffer behind the current play position. |
| bufferTimeDefault | The time that the forward buffer target will be set to when not playing at the top quality. |
| bufferTimeAtTopQuality | The time that the forward buffer target will be set to if playing the top quality. If there are multiple bitrates available, and the media is playing at the highest bitrate, then dash.js tries to build a larger buffer at the top quality to increase stability and to maintain media quality. |
| bufferTimeAtTopQualityLongForm | The time that the forward buffer target will be set to if playing the top quality for long form content. |
| longFormContentDurationThreshold | The threshold which defines if the media is considered long form content. This will directly affect the buffer targets when playing back at the top quality. |
Note: For a full list of all buffer related options including enabling and disabling specific features of the Media
Source Extensions (MSE) buffer
objects please refer to our API documentation.
Examples
In the example below we change the default buffer settings to reduce the forward and backward buffer.
js
player.updateSettings({
streaming: {
buffer: {
bufferTimeAtTopQuality: 20,
bufferTimeAtTopQualityLongForm: 30,
bufferTimeDefault: 10,
longFormContentDurationThreshold: 300,
}
}
});
More detailed examples are
available in the buffer section of the dash.js
sample page.---
Site/Pages/Usage/Clock Sync
---
title: Clock Synchronization
---
Clock synchronization
During playback of dynamic presentations, a wall clock is used as the timing reference for DASH client decisions. This
is a synchronized clock shared by the DASH client and service.
It is critical to synchronize the clocks of the DASH client and service when using a dynamic presentation because the
MPD timeline of a dynamic presentation is mapped to wall clock time and many playback decisions are clock driven and
assume a common understanding of time by the DASH client and service.
Clock synchronization mechanisms are described by UTCTiming elements in the MPD ([DASH] 5.8.4.11). For further
information please check the References [1] and [2].
Clock synchronization in dash.js
dash.js supports multiple schemeIdUri and value combinations for clock synchronization:
* urn:mpeg:dash:utc:http-head:2014
* urn:mpeg:dash:utc:http-xsdate:2014
* urn:mpeg:dash:utc:http-iso:2014
* urn:mpeg:dash:utc:direct:2014
* urn:mpeg:dash:utc:http-head:2012
* urn:mpeg:dash:utc:http-xsdate:2012
* urn:mpeg:dash:utc:http-iso:2012
* urn:mpeg:dash:utc:direct:2012
The default timing source in dash.js uses the following schemeIdUri / value combination and can be configured as
follows:
player.updateSettings({
streaming: {
utcSynchronization: {
defaultTimingSource: {
scheme: 'urn:mpeg:dash:utc:http-xsdate:2014',
value: 'https://time.akamai.com/?iso&ms'
}
}
}
});
UTCTiming elements in the MPD take precedence over the default timing source specified in the settings.Regular synchronization
By default, dash.js performs a clock synchronization at playback start and after each MPD update.
#### Synchronization at startup
At playback start an initial request to the timing server is issued. The offset between the client and the server clock
is calculated as described in the Section Offset calculation.
In addition, dash.js performs a predefined number of background requests to verify the initially calculated offset. The
number of background attempts can be adjusted in the settings:
player.updateSettings({
streaming: {
utcSynchronization: {
backgroundAttempts: 2
}
}
});
#### Synchronization after MPD updatesBy default, dash.js initiates a synchronization request after each MPD update. This behavior is modified by certain
settings parameters. The general workflow is as follows:
An MPD update triggers an event to attempt a clock synchronization. The TimeSyncController handles the event and
checks if a synchronization request is to be made:
function _shouldPerformSynchronization() {
try {
const timeBetweenSyncAttempts = !isNaN(internalTimeBetweenSyncAttempts) ? internalTimeBetweenSyncAttempts : DEFAULT_TIME_BETWEEN_SYNC_ATTEMPTS;
if (!timeOfLastSync || !timeBetweenSyncAttempts || isNaN(timeBetweenSyncAttempts)) {
return true;
}
return ((Date.now() - timeOfLastSync) / 1000) >= timeBetweenSyncAttempts;
} catch (e) {
return true;
}
}
_shouldPerformSynchronization() compares the current wallclock time against the time of the last sync attempt. If the
difference is larger than timeBetweenSyncAttempts a synchronization request is issued. Otherwise, playback continues
without a clock sync.The initial time between the sync attempts can be configured the following way:
player.updateSettings({
streaming: {
utcSynchronization: {
timeBetweenSyncAttempts: 30
}
}
});
#### Post-synchronization parameter adjustmentAfter each regular synchronization attempt, dash.js adjusts its internal internalTimeBetweenSyncAttempts parameter
based on certain criteria:
/ Detailed source-code truncated for AI context efficiency. /
In the first step the player checks if the offset is within certain boundaries:function _isOffsetDriftWithinThreshold(offset) {
try {
if (isNaN(lastOffset)) {
return true;
}
const maxAllowedDrift = settings.get().streaming.utcSynchronization.maximumAllowedDrift && !isNaN(settings.get().streaming.utcSynchronization.maximumAllowedDrift) ? settings.get().streaming.utcSynchronization.maximumAllowedDrift : DEFAULT_MAXIMUM_ALLOWED_DRIFT;
const lowerBound = lastOffset - maxAllowedDrift;
const upperBound = lastOffset + maxAllowedDrift;
return offset >= lowerBound && offset <= upperBound;
} catch (e) {
return true;
}
}
Depending on whether the offset is included in the calculated boundaries, adjustedTimeBetweenSyncAttempts is derived
by either multiplying or dividing the current internalTimeBetweenSyncAttempts
by timeBetweenSyncAttemptsAdjustmentFactor. By assigning specific values to maximumTimeBetweenSyncAttempts
and minimumTimeBetweenSyncAttempts upper and lower bounds for internalTimeBetweenSyncAttempts can be set.The parameters can be adjusted in the settings:
player.updateSettings({
streaming: {
utcSynchronization: {
timeBetweenSyncAttempts: 30,
maximumTimeBetweenSyncAttempts: 600,
minimumTimeBetweenSyncAttempts: 2,
timeBetweenSyncAttemptsAdjustmentFactor: 2,
maximumAllowedDrift: 100,
}
}
})
Synchronization after download errors
In addition to regular synchronization attempts, dash.js triggers a background synchronization in case requests to media
segments result in errors (e.g 404 errors). This is to make sure that the client clock is still synchronized and the
request error is not caused by an erroneous offset.
This feature can be enabled/disabled by adjusting the settings:
player.updateSettings({
streaming: {
utcSynchronization: {
enableBackgroundSyncAfterSegmentDownloadError: true
}
}
})
Offset calculation
The offset between two consecutive synchronization requests is calculated by accounting for the round trip time:
function _calculateOffset(deviceTimeBeforeSync, deviceTimeAfterSync, serverTime) {
const deviceReferenceTime = deviceTimeAfterSync - ((deviceTimeAfterSync - deviceTimeBeforeSync) / 2);
return serverTime - deviceReferenceTime;
}
Configuration example
The available configuration parameters:
| Parameter | Description |
|:------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| enable | Enable/Disable UTC Time synchronization. |
| useManifestDateHeaderTimeSource | Allows you to enable the use of the Date Header, if exposed with CORS, as a timing source for live edge detection. The use of the date header will happen only after the other timing source that take precedence fail or are omitted as described. |
| backgroundAttempts | Number of synchronization attempts to perform in the background after an initial synchronization request has been done. This is used to verify that the derived client-server offset is correct. |
| timeBetweenSyncAttempts | The time in seconds between two consecutive sync attempts. Note: This value is used as an initial starting value. The internal value of the TimeSyncController is adjusted during playback based on the drift between two consecutive synchronization attempts. |
| maximumTimeBetweenSyncAttempts | The maximum time in seconds between two consecutive sync attempts. |
| minimumTimeBetweenSyncAttempts | The minimum time in seconds between two consecutive sync attempts |
| timeBetweenSyncAttemptsAdjustmentFactor | The factor used to multiply or divide the timeBetweenSyncAttempts parameter after a sync. The maximumAllowedDrift defines whether this value is used as a factor or a dividend. |
| maximumAllowedDrift | The maximum allowed drift specified in milliseconds between two consecutive synchronization attempts. |
| enableBackgroundSyncAfterSegmentDownloadError | Enables or disables the background sync after the player ran into a segment download error. |
| defaultTimingSource | The default timing source to be used. The timing sources in the MPD take precedence over this one. |
An example of a full configuration object looks the following:
player.updateSettings({
streaming: {
utcSynchronization: {
enable: true,
useManifestDateHeaderTimeSource: true,
backgroundAttempts: 2,
timeBetweenSyncAttempts: 30,
maximumTimeBetweenSyncAttempts: 600,
minimumTimeBetweenSyncAttempts: 2,
timeBetweenSyncAttemptsAdjustmentFactor: 2,
maximumAllowedDrift: 100,
enableBackgroundSyncAfterSegmentDownloadError: true,
defaultTimingSource: {
scheme: 'urn:mpeg:dash:utc:http-xsdate:2014',
value: 'http://time.akamai.com/?iso&ms'
}
}
}
})
References
* [1] DASH-IF Implementation Guidelines
* [2] DASH-IF IOP Guidelines
---
Site/Pages/Usage/Cmcd
---
title: Common Media Client Data
---
Common Media Client Data
CTA-5004 - Common Media Client Data (
CMCD) defines data
that is collected by the media player and is sent as a custom HTTP header or query parameter alongside each object
request to a CDN. This enables use cases such as log analysis, quality of service monitoring, prioritization of
requests, cross correlation of performance problems with specific devices and platforms and improved edge caching.
CMCD version 1 is fully supported in dash.js. CMCD version 2 fields are gradually being added to dash.js.
Configuration Options
dash.js offers various configuration options related to CMCD. The following settings can be configured:
| Setting | Description |
|------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| applyParametersFromMpd | Enable if dash.js should use the CMCD parameters defined in the MPD |
| enabled | Enable or disable the CMCD reporting. |
| sid | GUID identifying the current playback session.Should be defined in UUID format |
| cid | A unique string to identify the current content. If not specified it will be a hash of the MPD URL. |
| rtp | The requested maximum throughput that the client considers sufficient for delivery of the asset. If not specified this value will be dynamically calculated in the CMCDModel based on the current buffer level. |
| rtpSafetyFactor | This value is used as a factor for the rtp value calculation: rtp = minBandwidth * rtpSafetyFactor. If not specified this value defaults to 5. Note that this value is only used when no static rtp value is defined. |
| mode | The method to use to attach cmcd metrics to the requests. 'query' to use query parameters, 'header' to use http headers.If not specified this value defaults to 'query'. |
| enabledKeys | This value is used to specify the desired CMCD parameters. Parameters not included in this list are not reported. |
| includeInRequests | Specifies which HTTP GET requests shall carry parameters. If not specified this value defaults to ['segment', 'mpd]. |
| version | The version of the CMCD to use. If not specified this value defaults to 1. |
For a full documentation of all CMCD related options please refer to
our API documentation.
Example configuration:
js
player.updateSettings({
streaming: {
cmcd: {
applyParametersFromMpd: true,
enabled: false,
sid: null,
cid: null,
rtp: null,
rtpSafetyFactor: 5,
mode: 'query',
enabledKeys: ['br', 'd', 'ot', 'tb', 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su', 'bs', 'rtp', 'cid', 'pr', 'sf', 'sid', 'st', 'v'],
includeInRequests: ['segment', 'mpd'],
version: 1
}
},
})
CMCD Version 2
CMCD version 2 extends version 1 with additional keys and new reporting modes. dash.js supports CMCD v2 based on
the Common Media Library. To enable it,
set version: 2 in the CMCD settings.
Additional keys
Among others, the following v2 keys are reported by dash.js:
| Key | Description |
|-------|------------------------------------------------------------------------------|
| ltc | Live latency: the delay between the live edge and the current playback position |
| msd | Media start delay: time from the playback request until the first frame is rendered |
| sta | Player state (e.g. playing, paused, seeking) |
| e | Event that triggered an event mode report |
Reporting modes
In addition to the v1 request mode (CMCD data attached to segment and MPD requests as query parameters or HTTP
headers), version 2 introduces dedicated reporting targets. dash.js supports these via the eventTargets setting:
- Response mode: reports are sent to a reporting endpoint after a response was received (rr event).
- Event mode: reports are triggered by player events such as play state changes (ps) or errors, or periodically
via a time interval (t).
- Batching: reports can be collected and sent in batches using the batchSize attribute.
Each target is configured individually:
js
player.updateSettings({
streaming: {
cmcd: {
enabled: true,
version: 2,
eventTargets: [
{
enabled: true,
url: 'https://example.com/cmcd/response-mode',
events: ['rr'],
includeInRequests: ['segment']
},
{
enabled: true,
url: 'https://example.com/cmcd/event-mode',
events: ['ps'],
interval: 10,
enabledKeys: ['e', 'msd', 'sta']
}
]
}
},
})
Example
An example illustrating CMCD reporting can be found in our
dash.js sample section. A dedicated CMCD v2
example including response and event mode reporting is available in
the CMCD v2 sample.
---
Site/Pages/Usage/Cmsd
---
title: Common Media Server Data
---
Common Media Server Data
CTA-5006 - Common Media Server Data (
CMSD) defines a structure for data transmitted in the response to a request from a media player
for an HTTP adaptive streaming media object. The response usually originates at an origin server
and is then propagated through a series of intermediaries to the player.
The purpose of the Common Media Server Data (CMSD) specification is to define a standard
means by which every media server (intermediate and origin) can communicate data with each
media object response and have it received and processed consistently by every intermediary
and player, for the purpose of improving the efficiency and performance of distribution and
ultimately the quality of experience enjoyed by the users.
dash.js currently supports two CMSD keys, namely etp (estimated throughput) and mb (maximum suggested bitrate).
Configuration Options
dash.js offers configuration options related to CMSD. The following settings can be configured:
| Setting | Description |
|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| enabled | Enable or disable the CMSD response headers parsing. |
| abr.applyMb | Set to true if dash.js should apply the maximum suggested bitrate derived from the CMSD mb key in its ABR logic. |
| abr.etpWeightRatio | Sets the weight ratio (between 0 and 1) that shall be applied on the value of the CMSD etp key compared to the measured throughput on client side. For instance, setting this value to 0.5 will result in an equal 50% weight on the throughput value provided by the server via CMSD and 50% weight on the throughput value calculated by dash.js on the client-side. |
For a full documentation of all CMSD related options please refer to
our API documentation.
Example configuration:
js
player.updateSettings({
streaming: {
cmsd: {
enabled: true,
abr: {
applyMb: true,
etpWeightRatio: 0.5
}
},
},
})
---Site/Pages/Usage/Content Steering
---
title: Content Steering
---
Content Steering
Content steering describes a deterministic capability for a content distributor to switch the content source that a
player uses either at start-up
or midstream by means of a remote steering service.
It adds new <ContentSteering> element to the MPD and the <BaseURL> elements will contain a serviceLocation
attribute that can be used as an identifier. In addition, a steering server is required to provide the player with the
required steering information.
dash.js applies content steering if the required information are present in the MPD and the steering server is returning a valid steering manifest
Example
An example of content steering can be found in our sample section.To disable content steering, set the applyContentSteering property to false in the streaming section of the player settings.
js
player.updateSettings({
streaming: {
applyContentSteering: false
}
});
---Site/Pages/Usage/Controlbar
---
title: Controlbar
---
Controlbar
dash.js ships with a self-contained, reusable control bar located in contrib/controlbar. It generates its own DOM
structure, so you only need to provide a wrapper element. The control bar implements the various APIs of the player —
play/pause, seeking, volume, live edge, bitrate and track selection — and is used by the
DASH-IF Reference Player.
Example
An example is available as part of the
sample section.
Prerequisites
- dash.js loaded (the global dashjs object must be available)
- Bootstrap Icons CSS for icon display:
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1/font/bootstrap-icons.min.css" rel="stylesheet">
Usage
Include the control bar CSS:
<link rel="stylesheet" href="path/to/contrib/controlbar/controlbar.css">
Provide a wrapper element in your HTML (must have position: relative):<div id="video-wrapper" style="position: relative;">
<video id="video-element"></video>
</div>
Import and initialize the control bar as an ES module after initializing the player:import { ControlBar } from 'path/to/contrib/controlbar/ControlBar.js';
const player = dashjs.MediaPlayer().create();
const video = document.getElementById('video-element');
player.initialize(video, url, true);
const controlbar = new ControlBar(player, video);
controlbar.init(document.getElementById('video-wrapper'));
controlbar.enable();
API
new ControlBar(player, videoElement)
| Parameter | Type | Description |
|:---------------|:--------------------------|:-----------------------------------------------|
| player | dashjs.MediaPlayerClass | A dash.js MediaPlayer instance |
| videoElement | HTMLVideoElement | The <video> element managed by the player || Method | Description |
|:------------------|:----------------------------------------------------------------------------------------------------------------------------------|
| init(wrapper) | Build the DOM and inject it into the given wrapper element (or CSS selector string). The wrapper should have position: relative. |
| enable() | Enable the control bar (interactive). |
| disable() | Disable the control bar (non-interactive, dimmed). |
| reset() | Reset state (call before loading a new stream). |
| setMuted(muted) | Set the muted visual state (true / false). Does not touch the player — use syncMuteState() for that. |
| syncMuteState() | Re-apply the control bar's current volume/mute state to the player. Call after attaching a new source. |
| destroy() | Remove all event listeners and remove the control bar DOM from the page. |
Typical lifecycle
// Create
const cb = new ControlBar(player, video);
cb.init('#video-wrapper');
cb.disable();
// On stream initialized
cb.enable();
// Before loading a new stream
cb.reset();
cb.disable();
// After loading
cb.syncMuteState();
// On stream initialized again
cb.enable();
// Cleanup
cb.destroy();
Theming
The control bar defines two CSS custom properties with sensible defaults:
| Variable | Default | Description |
|:--------------|:----------|:------------------------------------------------|
| --cb-accent | #5b8def | Accent colour (seekbar played, menu highlights) |
| --cb-danger | #e74c3c | Danger colour (live-edge indicator) |
Override them on the .cb-controlbar selector or any ancestor:
.cb-controlbar {
--cb-accent: #1a73c9;
--cb-danger: #e74c3c;
}
Legacy controlbar
The older Akamai control bar is still available at contrib/akamai/controlbar/ for legacy integrations that use
<script> tags instead of ES modules. It requires you to provide the full control bar DOM yourself and is initialized
via new ControlBar(player) + controlbar.initialize().
---
Site/Pages/Usage/Drm
---
title: Digital Rights Management (DRM)
---
Digital Rights Management (DRM)
dash.js offers support for playback of DRM protected content. In this context, multiple adjustments can be made.
DRM Examples
Multiple samples implementing the functionalities described in this documentation can be found in
the DRM section.
Widevine
Google Widevine is supported on Chromium based browsers (Chrome, Edge, Opera), Firefox, Android and many smart TV
platforms. dash.js registers the key system under the system string com.widevine.alpha
(urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed). Widevine protected DASH content is typically encrypted using the
cenc scheme, cbcs is supported on newer CDM versions.
The initialization data is taken from the cenc:pssh element in the MPD or from the encrypted event thrown by the
EME. If the license server URL is signaled in the MPD (for instance via dashif:Laurl), playback works without any
additional configuration. Otherwise, provide the license server URL via the protection data:
const protData = {
"com.widevine.alpha": {
"serverURL": "https://drm-widevine-licensing.axtest.net/AcquireLicense"
}
};
player.setProtectionData(protData);
A Widevine service certificate can be provided directly or downloaded from a certificate server URL,
see Server certificates. To unlock hardware backed playback (L1) specific robustness levels are
required, see Robustness levels.An example is available in
the Widevine sample.
PlayReady
Microsoft PlayReady is supported on Windows (Edge), Xbox and a large number of smart TVs and set-top boxes. dash.js
registers the key system under the system string com.microsoft.playready
(urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95). Depending on the platform, additional system strings such as
com.microsoft.playready.recommendation and com.microsoft.playready.hardware are available. The recommendation
key system is required for cbcs encrypted content and can be prioritized via the
system string priority. PlayReady protected DASH content is commonly encrypted using
the cenc scheme.
The initialization data is taken from the PlayReady header in the mspr:pro/cenc:pssh elements of the MPD or from
the encrypted event thrown by the EME. If the license server URL is signaled in the MPD (for instance via
dashif:Laurl or the PlayReady header), playback works without any additional configuration. Otherwise, provide the
license server URL via the protection data:
const protData = {
"com.microsoft.playready": {
"serverURL": "https://drm-playready-licensing.axtest.net/AcquireLicense"
}
};
player.setProtectionData(protData);
PlayReady allows passing custom data to the CDM as part of the license acquisition. dash.js supports this via the
cdmData attribute of the protection data, which is wrapped into a PlayReadyCDMData object and handed to the key
session. A server certificate can be provided as well, see Server certificates.An example is available in
the PlayReady sample.
FairPlay
Apple FairPlay Streaming is supported on Apple platforms (Safari on macOS, iOS and iPadOS). dash.js registers the key
system under the system string com.apple.fps (urn:uuid:94ce86fb-07ff-4f43-adb8-93d2fa968ca2). FairPlay protected
DASH content is typically encrypted using the cbcs scheme.
In contrast to Widevine and PlayReady, FairPlay does not use PSSH data from the manifest — the initialization data is
provided by the platform via the encrypted event (sinf init data type). If the license server URL and certificate
are signaled in the MPD (for instance via dashif:laurl), playback works without any additional configuration:
const player = dashjs.MediaPlayer().create();
player.initialize(video, url, true);
Otherwise, provide the license server URL — and, if required by your DRM provider, the FairPlay server certificate —
via the protection data:const protData = {
"com.apple.fps": {
"serverURL": "https://fairplay-license.example.com/license",
"serverCertificate": "<base64 encoded certificate>"
}
};
player.setProtectionData(protData);
In contrast to Widevine and PlayReady, FairPlay usually requires a server certificate before a license request can be
made. If it is not provided via the API, dash.js attempts to download it from the certificate URLs signaled in the MPD,
see Server certificates.An example is available in
the FairPlay sample.
Server certificates
Some DRM systems require or recommend a server certificate before license requests can be made — mandatory for
FairPlay, optional for Widevine (service certificate) and PlayReady. dash.js supports providing the certificate for any
key system in three ways:
1. Directly via the API using the serverCertificate attribute as a Base64 encoded string. The certificate is
applied immediately and no certificate request is performed:
const protData = {
"com.widevine.alpha": {
"serverURL": "https://license.example.com/AcquireLicense",
"serverCertificate": "<base64 encoded certificate>"
}
};
player.setProtectionData(protData);
2. Via certificate server URLs in the API using the certUrls attribute. dash.js downloads the certificate from
the provided URLs:const protData = {
"com.widevine.alpha": {
"serverURL": "https://license.example.com/AcquireLicense",
"certUrls": [
{ "url": "https://certificates.example.com/widevine.der", "certType": "widevine" }
]
}
};
player.setProtectionData(protData);
3. Via certificate server URLs in the MPD signaled in a Certurl element (for instance dashif:Certurl) under
the ContentProtection descriptor:<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed" value="Widevine">
<dashif:Certurl>https://certificates.example.com/widevine.der</dashif:Certurl>
</ContentProtection>
URLs provided via the API take priority over URLs signaled in the MPD. The deduplicated candidates are requested
sequentially, the first successfully downloaded certificate is applied and cached for the key system. The number of
retries per URL can be configured via streaming.retryAttempts. Certificate requests and responses can be modified via
filters, see the
certificate wrapping sample and
the external certificate URL sample.License server settings
In order to specify the license server for a DRM system use the serverURL attribute:
const protData = {
"com.widevine.alpha": {
"serverURL": "https://drm-widevine-licensing.axtest.net/AcquireLicense"
},
"com.microsoft.playready": {
"serverURL": "https://drm-playready-licensing.axtest.net/AcquireLicense"
}
};
player.setProtectionData(protData);
Key system priority
In some cases the underlying platform supports multiple DRM systems, for instance Widevine and Playready. To prioritize
a specific system in the player's selection process use the priority attribute. A lower value means a higher priority.
In the example below, dash.js checks for the support of com.widevine.alpha prior to com.microsoft.playready.
const protData = {
"com.widevine.alpha": {
"serverURL": "someurl",
"priority": 1
},
"com.microsoft.playready": {
"serverURL": "someurl",
"priority": 2
}
}
player.setProtectionData(protData)
Key System String - Priority
In some cases, multiple key system strings map to the same uuid/schemeIdUri of a DRM system. As an example, multiple
platforms support the call to requestMediaKeySystemAccess for the Playready DRM system using the system
strings com.microsoft.playready and com.microsoft.playready.recommendation. A detailed explanation is
given here
and here.
dash.js allows the application to define a system string priority for each key system as part of the protection data:
js
var protData = {
'com.widevine.alpha': {
'serverURL': 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
'systemStringPriority': [
'com.widevine.something',
'com.widevine.alpha'
]
},
'com.microsoft.playready': {
'serverURL': 'https://drm-playready-licensing.axtest.net/AcquireLicense',
'systemStringPriority': [
'com.microsoft.playready.something',
'com.microsoft.playready.recommendation',
'com.microsoft.playready.hardware',
'com.microsoft.playready'
]
}
};
DRM specific headers
License servers might require custom headers in order to provide a valid license. dash.js allows the addition of custom
headers using the httpRequestHeaders attribute:
const protData = {
"com.microsoft.playready": {
"serverURL": "https://drm-playready-licensing.axtest.net/AcquireLicense",
"httpRequestHeaders": {
"custom-header": "data"
}
}
};
player.setProtectionData(protData)
Robustness levels (Hard- & Software DRM)
Some DRM systems like Widevine require specific robustness levels to enable L1-L3 DRM playback. The robustness level can
be set as part of the protection data in the following way:
js
const protData = {
"com.widevine.alpha": {
"serverURL": "https://drm-widevine-licensing.axtest.net/AcquireLicense",
"audioRobustness": "SW_SECURE_CRYPTO",
"videoRobustness": "HW_SECURE_ALL"
}
}
License server url via MPD
DRM systems generally use the concept of license requests as the mechanism for obtaining content keys and associated
usage constraints. For DRM systems that use this concept, one or more dashif:Laurl elements may be present under the
ContentProtection descriptor, with the value of the element being the URL to send license requests to. An example looks
the following:
xml<ContentProtection
schemeIdUri="urn:uuid:d0ee2730-09b5-459f-8452-200e52b37567"
value="FirstDRM 2.0">
<cenc:pssh>
YmFzZTY0IGVuY29kZWQgY29udGVudHMgb2YgkXBzc2iSIGJveCB3aXRoIHRoaXMgU3lzdGVtSUQ=
</cenc:pssh>
<dashif:Authzurl>https://example.com/tenants/5341/authorize</dashif:Authzurl>
<dashif:Laurl>https://example.com/AcquireLicense</dashif:Laurl>
</ContentProtection>
Note: dash.js prioritizes the license server urls in the following order:1. URL provided via the the API
2. URL provided via the MPD
3. URL provided via pssh
Ignoring init data from the PSSH
By default, dash.js listens to needkey and encrypted events thrown by the EME. In case the init data has changed a
new key session is created and a license request is triggered. In order to ignore DRM init data coming from
initialization and media segments the settings object needs to be adjusted:
js
player.updateSettings({
streaming: {
protection: {
ignoreEmeEncryptedEvent: true
}
}
})
Modifying the license payload
dash.js allows the modification of the license request payload and the license response body.
License request modification
In order to modify the license request, filter functions can be added and removed dynamically.
Note: The filter functions are reset when calling player.destroy().
const player = dashjs.MediaPlayer().create();
const callback = (payload) => {
return new Promise((resolve, reject) => {
resolve(payload)
})
}
player.initialize(video, url, false);
player.registerLicenseRequestFilter(callback)
player.unregisterLicenseRequestFilter(callback)
The registered functions are called within the ProtectionController class before the license request is send to the
license serverlet licenseRequest = new LicenseRequest(url, reqMethod, responseType, reqHeaders, withCredentials, messageType, sessionId, reqPayload);
applyFilters(licenseRequestFilters, licenseRequest).then(() => {
doLicenseRequest(licenseRequest, LICENSE_SERVER_REQUEST_RETRIES, timeout, onLoad, onAbort, onError);
});
License response modification
In order to modify the license response, filter functions can be added and removed dynamically:
const player = dashjs.MediaPlayer().create();
const callback = (payload) => {
return new Promise((resolve, reject) => {
resolve(payload)
})
}
player.initialize(video, url, false);
player.registerLicenseResponseFilter(callback)
player.unregisterLicenseResponseFilter(callback)
Keeping the MediaKeySession
The ProtectionController and the created MediaKeys and MediaKeySessions can be preserved during the MediaPlayer
lifetime. As a consequence, only the first playback attempt for a DRM protected stream will result in a license request.
For any subsequent playback attempt of the same content the existing MediaKeySession is reused and no additional license
requests are performed.
To enable MediaKeySession reusage keepProtectionMediaKeys needs to be enabled.
player.updateSettings({
streaming: {
protection: {
keepProtectionMediaKeys: true
}
}
})
Key status changes
After a successful license request or even during playback, the key status of a MediaKeySession can change. With version
5 dash.js handles such key status changes and switches to a different track or a different Representation if required.
If the application needs to know about such key status updates it can register for the KEY_STATUSES_MAP_UPDATED
event. This event is triggered once the internal key status map of dash.js was updated.
js
player.on(dashjs.protectionEvents.KEY_STATUSES_MAP_UPDATED, eventHandler, null);
Different versions of the EME
The EME is the API that enables playback of protected content in the browser.
It provides the necessary function calls to discover and interact with the underlying DRM system. Like any other API,
EME changed over time and the current version is a lot different compared to the one in 2013. While desktop and mobile
browsers are frequently updated, some embedded devices and set-top boxes are still running on outdated or even
customized versions of the EME. For that reason dash.js detects the EME version on the client and triggers the right API
functions [1].
By default, dash.js ships with support for three different versions of EME:
* ProtectionModel_01b.js: initial implementation of the EME, implemented by Google Chrome prior to version 36. This
EME version is not-promised based and uses outdated or prefixed events like “needkey” or “webkitneedkey”.
* ProtectionModel_3Feb2014.js: implementation of EME APIs as of the 3 Feb 2014 state of the specification.
Implemented by Internet Explorer 11 (Windows 8.1).
* ProtectionModel_21Jan2015.js: most recent EME implementation. Latest changes in the EME specification are added to
this model and It supports the promised-based EME function calls.
The detection of the appropriate EME version is done automatically in Protection.js:
if ((!videoElement || videoElement.onencrypted !== undefined) &&
(!videoElement || videoElement.mediaKeys !== undefined)) {
logger.info('EME detected on this user agent! (ProtectionModel_21Jan2015)');
return ProtectionModel_21Jan2015(context).create();
} else if (getAPI(videoElement, APIS_ProtectionModel_3Feb2014)) {
logger.info('EME detected on this user agent! (ProtectionModel_3Feb2014)');
return ProtectionModel_3Feb2014(context).create();
} else if (getAPI(videoElement, APIS_ProtectionModel_01b)) {
logger.info('EME detected on this user agent! (ProtectionModel_01b)');
return ProtectionModel_01b(context).create();
} else {
logger.warn('No supported version of EME detected on this user agent! - Attempts to play encrypted content will fail!');
return null;
}
References
[1] dash.js: License acquisition for multiple EME versions
---
Site/Pages/Usage/Event Handling
---
title: Event handling - MPD and Inband events
---
Events
dash.js supports inline and inband events included in the MPD and the media segments. For more details on events, their
timing and how to use them please checkout ISO/IEC 23009-1 and the DASH-IF IOP Guidelines.
Example
An example is available as part of
the sample section.
Inband Events
Inband events are events that are included in the ISOBMFF segments as an emsg box.
The schemeIdUri and the value of inband events need to be signaled in the MPD using an InbandEventStream element.
An example of an InbandEventStream element and the structure of the emsg box are depicted below:
<InbandEventStream schemeIdUri="urn:scte:scte35:2013:xml" value="999"/>
aligned(8) class DASHEventMessageBox extends FullBox('emsg', version, flags=0){
if (version==0) {
string scheme_id_uri;
string value;
unsigned int(32) timescale;
unsigned int(32) presentation_time_delta;
unsigned int(32) event_duration;
unsigned int(32) id;
}
else if (version==1) {
unsigned int(32) timescale;
unsigned int(64) presentation_time;
unsigned int(32) event_duration;
unsigned int(32) id;
string scheme_id_uri;
string value;
}
unsigned int(8) message_data[];
MPD events
MPD events are signaled directly in the MPD. Events of the same type are summarized in an EventStream element.
An example of an MPD event is depicted below.
<EventStream schemeIdUri="urn:scte:scte35:2013:xml" value="999">
<Event duration="1" presentationTime="10">someMessage</Event>
</EventStream>
dash.js event handling
Application events
dash.js dispatches events that are not directly processed by the player (application events) to the underlying
application. To register for a specific type
of event use the on method of the player object and specify the target schemeIdUri to listen for:
const SCHEMEIDURI = "urn:scte:scte35:2013:xml";
const EVENT_MODE_ON_START = dashjs.MediaPlayer.events.EVENT_MODE_ON_START;
const EVENT_MODE_ON_RECEIVE = dashjs.MediaPlayer.events.EVENT_MODE_ON_RECEIVE;
player.on(SCHEMEIDURI, showStartEvent, null);
player.on(SCHEMEIDURI, showReceiveEvent, null, { mode: EVENT_MODE_ON_RECEIVE });
Two dispatch modes are supported* eventModeOnStart (default): The event is dispatched once its start time is reached.
* eventModeOnReceive: The event is immediately dispatched once it was signaled to the dash.js player.
DASH-specific events
Some events are to be processed by the DASH player directly and are not dispatched to the application:
| schemeIdUri | value | Description |
|:------------------------------------|:------|:-------------------------------------------------------------------|
| urn:mpeg:dash:event:2012 | 1 | Triggers and MPD reload |
| urn:mpeg:dash:event:callback:2015 | 1 | Sends a callback request to the provided URL ignoring the response |
ID3 parsing
dash.js uses the Common Media Library to
support the parsing of ID3 time metadata for inband events. ID3 time metadata is signaled via
the https://aomedia.org/emsg/ID3 schemeIdUri. The parsed message data is dispatched via
the event.parsedMessageData
field. The raw ID3 message data is available via the event.messageData field. As an example:
js
event.messageData = Uint8Array(89)[...]
event.parsedMessageData = [
{
"key": "PRIV",
"info": "com.elementaltechnologies.timestamp.utc",
"data": {}
}
]
---Site/Pages/Usage/Flexible Insertion Url Parameters
---
title: Flexible Insertion of URL Parameters
---
Flexible Insertion of URL Parameters
Annex I of the MPEG-DASH specification defines how to configure URL parameters of media segment URLs in a similar
fashion to the URL template mechanism. This mechanism allows an "inheritance" from MPD URL parameters when the MPD is
delivered over HTTP, i.e. extraction of one or more key-value pairs from the query string of the URL used to fetch MPD.
Example procedure
In an easy example the initial request URL to the MPD looks like this
https://livesim.dashif.org/livesim2/annexI_dashjs=rocks/testpic_6s/Manifest.mpd?dashjs=rocks. The link to the MPD
contains a query parameter with the key dashjs and the corresponding value set to rocks.
The AdaptationSet of type video in the MPD has an additional EssentialProperty:
xml<EssentialProperty schemeIdUri="urn:mpeg:dash:urlparam:2014">
<up:UrlQueryInfo xmlns:up="urn:mpeg:dash:schema:urlparam:2014" queryTemplate="$querypart$"
useMPDUrlQuery="true"></up:UrlQueryInfo>
</EssentialProperty>
This configuration in the MPD tells dash.js to use the query parameters from the MPD URL for the media segment URLs. As
a consequence, the outgoing requests to video segments will look like this:https://livesim.dashif.org/livesim2/annexI_dashjs=rocks/testpic_6s/V300/289578461.m4s?dashjs=rocks. dash.js is
automatically appending the query string dashjs=rocks to the media segment request.
Example implementation
An example of flexible insertion of URL parameters can be found in
the sample section.
---
Site/Pages/Usage/Index
---
title: Usage
---
Usage
dash.js covers a wide set of use cases such as low latency streaming, DRM playback, multi-audio and multi-text playback
and many more.
We suggest to make yourself familiar with the basic concepts of dash.js. This includes understanding how to:
* Add dash.js to your project
* Change the default Settings
Many samples demonstrating the dash.js features can be found in our
sample section.
The pages in this section explain the individual features of dash.js in detail, including code examples and links to
the corresponding samples:
Playback basics
* Settings - configure the player via updateSettings()
* Player Events - subscribe to state changes and metrics
* Logging - configure log levels
* Controlbar - add a UI control bar
* Timing APIs - playback time, seeking and start times
Streaming types
* Live Streaming - live delay, player synchronization, dynamic to static transitions
* Low Latency Streaming - CMAF low latency and catchup mechanisms
* Multiperiod Streams - period transitions and events
* MPD Patching - incremental manifest updates
* Clock Synchronization - client/server clock sync for live playback
* Microsoft Smooth Streaming - MSS playback support
Adaptive streaming and buffering
* Adaptive Bitrate Streaming - ABR rules, settings and custom rules
* Buffer Management - buffer targets and pruning
Tracks and media
* Track Selection - initial and runtime track selection, capability filtering
* Subtitles & Captions - TTML, WebVTT and CEA-608/708 handling
* Thumbnails - seekbar preview thumbnails
* LCEVC - MPEG-5 Part 2 enhancement decoding
Content protection
* Digital Rights Management (DRM) - Widevine, PlayReady, FairPlay and ClearKey playback
Data and reporting
* Common Media Client Data (CMCD) - client-side metrics reporting
* Common Media Server Data (CMSD) - server-side hints
* Event handling - MPD and inband events
Advanced
* Content Steering - CDN switching
* Network Interceptor - request/response interception
* Flexible Insertion of URL Parameters - CMCD-style query injection
* Preloading - buffering before a video element is attached
---
Site/Pages/Usage/Lcevc
---
title: LCEVC
---
Scalable LCEVC in dash.js
Introduction
This approach creates LCEVC representations which are dependent upon native codec representations (by using dependencyId). LCEVC enhancement representations are contained in a 2nd Adaptation Set and linked to the base representations in the 1st Adaptation Set. The outcome is the ability to play adaptive streaming content where one or more higher resolution profiles are generated by applying LCEVC enhancement to existing conventional profiles saving up to 70% bitrate compared to using conventional standalone native profiles.
An external implementation of the MSE classes MediaSource and SourceBuffer is provided to interject the calls from the enhancement representations. The external SourceBuffer implementation triggers events on buffers appended and removed which the external libraries can listen to.
Sample player
An example player is available at https://reference.dashif.org/dash.js/nightly/samples/lcevc/lcevc-dual-track.html
Manifest format
A separate AdaptationSet is added, alongside the backward compatible video AdaptationSet with the base codec Representations, comprising only the LCEVC Representations with dependencyId that corresponds to the associated base representation.
The AdaptationSet of the dependent representations would have the following attributes:
- @contentType = ‘video’
- @mimeType = ‘video/mp4’
- @codecs = ‘lvc1’
- @frameRate = <<frameRate of base representation>>
- @dependencyId = <<representationId of base representation>>
- @width/height = <LCEVC enhanced video width/height>
- @bandwidth = <combined bit rate of the LCEVC + base representation>
- @sar = <sample aspect ratio as signalled in LCEVC Video Usability Information (VUI)>
Example manifest
Sample manifest is available at https://s3.eu-west-1.amazonaws.com/origin-prod-lon-v-nova.com/lcevcDualTrack/1080p30_3Mbps_no_dR/master.mpd
/ Detailed source-code truncated for AI context efficiency. /
Encoding LCEVC-enhanced content with ffmpeg
Instructions on how to use ffmpeg to encode LCEVC are available here https://docs.v-nova.com/v-nova/lcevc/reference-applications/ffmpeg.
Example script to generate scalable LCEVC for MPEG-DASH delivery
LD_LIBRARY_PATH=./ffmpeg ./ffmpeg/ffmpeg.exe -y -i bbb_sunflower_2160p_30fps_normal.mp4 -c:v lcevc_h264 -base_encoder x264 -strict -2 -acodec aac -ar 44100 -ac 2 -ab 64k -b:v 3000000 -g 60 -separate_track 1 -eil_params "rc_pcrf_base_prop=0.65;encoding_debug_residuals=1" -pix_fmt yuv420p -r 30 -s 1920x1080 out1.mp4
Generate .ismv file out of the encodes.
mp4split -o out-sorted.ismv \
out1.mp4
Generate .ism manifest from the ismv file.
mp4split -o out-sorted.ism \
out-sorted.ismv
Generate dash files locally this should generate a manifest.mpd
mp4split \
--store-mpd \
-o manifest.mpd \
out-sorted.ism
Generate Final manifest with addition of dependencyID and supplemental properties
manifest_edit \
-o finalManifest.mpd \
--python_pipeline_config=/etc/manifest-edit/conf/mpd/default.yaml \
out-sorted.mpd
---Site/Pages/Usage/Live Streaming
---
title: Live Streaming
---
Live Streaming
Setting the live delay
In addition to the buffer settings the live delay plays a significant role in live
streaming. The live delay is the time difference between the live edge and the playback position. The live delay can be
set by the application in two ways: by setting the liveDelay or by setting the liveDelayFragmentCount.
The liveDelay is the time in seconds that the player should be behind the live edge. The liveDelayFragmentCount is
the number of fragments that the
player should be behind the live edge. The liveDelay has precedence over the liveDelayFragmentCount.
TheuseSuggestedPresentationDelay is a boolean that indicates whether the player should use the suggested presentation
delay from the MPD if defined. The suggested presentation delay is the time in seconds that the player should be behind
the live
edge. Explicit live delay settings by the appilication using the liveDelay and the liveDelayFragmentCount take
precedence over the useSuggestedPresentationDelay.
Configuration Options
js
player.updateSettings({
streaming: {
delay: {
liveDelayFragmentCount: NaN,
liveDelay: NaN,
useSuggestedPresentationDelay: true
}
},
})
Examples
Multiple examples demonstrating the live delay settings can be found in
the live section of the dash.js sample page.
Synchronizing multiple players
In some scenarios it makes sense to synchronize multiple players. This can be achieved by defining the same live delay
for all instances and enabling catchup mode. In the example configuration below the live delay is set to 10 seconds and
the live catchup mode is enabled. As a result both player instances will play roughly at the same position.
js
player.updateSettings({
streaming: {
delay: {
liveDelay: 10
},
liveCatchup: {
enabled: true
}
},
})
An example illustrating how to synchronize multiple players is available in
the live section
of the dash.js sample page.Dynamic to static transition
A live stream can end and be converted into an on-demand presentation: the MPD changes its type from dynamic to
static. dash.js handles this transition automatically — the duration becomes finite and the presentation behaves
like VoD content. To get notified about the transition, subscribe to the DYNAMIC_TO_STATIC event:
js
player.on(dashjs.MediaPlayer.events.DYNAMIC_TO_STATIC, () => {
console.log('MPD changed from dynamic to static, final duration:', player.duration());
});
player.isDynamic() returns false once the static MPD has been applied.An example is available in
the dynamic to static sample.
---
Site/Pages/Usage/Logging
---
title: Logging
---
Logging
dash.js defines different cumulative log levels to output information in the console during playback. For example, if
you set the log level to dashjs.Debug.LOG_LEVEL_WARNING all warnings, errors and fatals will be logged.
Example
An example is available as part of the sample section.Log level
| Log level | Numeric value | Description |
|:---------------------------------|:--------------|:-------------------------------------------------------------------------------------------|
| dashjs.Debug.LOG_LEVEL_NONE | 0 | No message is written in the browser console |
| dashjs.Debug.LOG_LEVEL_FATAL | 1 | Log fatal errors. An error is considered fatal when it causes playback to fail completely. |
| dashjs.Debug.LOG_LEVEL_ERROR | 2 | Log error messages |
| dashjs.Debug.LOG_LEVEL_WARNING | 3 | Log warning messages |
| dashjs.Debug.LOG_LEVEL_INFO | 4 | Log info messages |
| dashjs.Debug.LOG_LEVEL_DEBUG | 5 | Log all messages |
Setting the log level
To set the target log level simply update the settings with the desired value, for instance
player.updateSettings({
'debug': {
'logLevel': dashjs.Debug.LOG_LEVEL_INFO
}
});
orplayer.updateSettings({
'debug': {
'logLevel': 4
}
});
---Site/Pages/Usage/Low Latency
---
title: Low Latency Streaming
---
Low Latency Streaming
One of the major challenges in OTT streaming is reducing the live streaming latency. This can be crucial for live events
like sport games or for an optimal streamer-user interaction in eSports games.
Use case: On Par with Other Distribution Means
A live event is distributed over DASH as well over regular TV distribution. The event should play-out approximately at
the same time on both devices in order to avoid different perceptions of the same service when received over different
distribution means. The objective should be to get to a range of delay for the DASH based service that is equivalent to
cable and IPTV services [1].
Use case: Sports Bar
Sports bars are commonly in close proximity to each other and may all show the same live sporting event. Some bars may
be using a provider which distributes the content using DVB-T or DVB-S services whilst others may be using DASH ABR.
Viewers in a bar with a high latency will have their viewing spoiled as they will hear cheers for the goal before it
occurs on their local screen.
This creates a commercial incentive for the bar operator to switch to the provider with the lowest latency. The
objective should be to get the latency range to not be perceptibly different to that of a DVB broadcast solution for
those users who have a sufficient quality (high and consistent speed) connection [1].
Use case: Professional streamer with interactive chat
Professional streamers interacting with a live audience on social media, often via a directly coupled chat function in
the viewing app/environment. They can generate direct revenue in several ways including:
* In stream advertising
* Micropayments (for example Twitch “bits”)
A high degree of interactivity between the performer and the audience is required to enable engagement. Lower latencies
increases the engagement and consequently the incentive for the audience members to reward the performer with likes,
shares, subscribes, micropayments, etc.
Typical use cases include gamers, musicians and other performers where in some part the direction of the performance can
be guided by the audience response [1].
Use case: Sports betting
A provider wants to offer a live stream that will be used for wagering within an event. The content must be delivered
with low latency and more importantly within a well-defined sync across endpoints so customers trust the game is fair.
There are in some cases legal considerations, for example the content cannot be shown if it is more than X seconds
behind live.
Visual and aural quality are secondary in priority in these scenarios to sync and latency. The lower the latency the
more opportunities for “in play betting” within the game/event. This in turn increases revenue potential from a
game/event [1].
CMAF low latency streaming
The Common Media Application Format introduces the concept of "chunks". A CMAF chunk has multiple "moof" and "mdat"
boxes, allowing the client to access the media data before the segment is completely finished. The benefits of the
chunked mode become more obvious when looking at a concrete example:
So let’s assume we have 8 second segments and we are currently 3 seconds into segment number four. For classic media
segments, this leaves us with two options:
* Option 1: since segment four is not completed, we start with segment three. That way, we end up 11 seconds behind the
live edge – 8 seconds coming from segment three, and 3 seconds coming from segment four.
* Option 2: we wait for segment four to finish and immediately start downloading and playing it. We end up with 8
seconds of latency and a waiting time of 5 seconds.
With CMAF chunks, on the other hand, we are able to play segment four before it is completely available. In the example
above, we have CMAF chunks with a 1 second duration, which leads to eight chunks per segment. Let’s assume that only the
first chunk contains an IDR frame and therefore we always need to start the playback from the beginning of a segment.
Being three seconds into segment four leaves us with 3 seconds of latency. That’s much better than what we achieved with
classic segments. We can also fast decode the first chunks and play even closer to the live edge [2].
CMAF low latency streaming with dash.js
dash.js supports CMAF low latency streaming since version 2.6.8. For that reason, a dedicated sample page is available:
dash.js configuration
The following Sections below will give a detailed explanation on L2ALL and LoL+. Some parameters are valid for all low
latency algorithms:
| Parameter | Description |
|------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| streaming.delay.liveDelay | Lowering this value will lower latency but may decrease the player's ability to build a stable buffer. |
| streaming.delay.liveDelayFragmentCount | Lowering this value will lower latency but may decrease the player's ability to build a stable buffer. |
| streaming.liveCatchup.maxDrift | Maximum latency deviation allowed before dash.js to do a seeking to live position |
| streaming.liveCatchup.playbackRate | Defines the minimum and maximum catch-up rate, as a percentage relative to the default playback rate of 1. Values must be in the range -0.5 to 1. |
The corresponding API call looks the following:
player.updateSettings({
streaming: {
delay: {
liveDelay: 4
},
liveCatchup: {
maxDrift: 0,
playbackRate: {
max: 1,
min: -0.5
}
}
}
});
Please check the API documentation for additional
information.MPD specific low latency parameters
It is also possible to configure specific low latency settings via MPD. The required information is encapsulated in
a <ServiceDescription> element:
<ServiceDescription id="0">
<Latency max="6000" min="2000" referenceId="0" target="4000"/>
<PlaybackRate max="1.04" min="0.96"/>
</ServiceDescription>
For more details please refer to the DASH-IF IOP guidelines [1].dash.js requirements
In order to use dash.js in low latency mode the following requirements have to be fullfilled:
#### Client requirements
* The Fetch API must be supported on the client browser.
* The server must
support HTTP 1.1 Chunked transfer encoding#### Server and content requirements
The content and the manifest must be conditioned to support CMAF low latency chunks
The manifest must contain two additional attributes
* @availabilityTimeComplete: specifies if all segments of all associated representations are complete at the adjusted
availability start time. If the value is set to false, then it may be inferred by the client that the segment is
available at its announced location prior to completion.
* @availabilityTimeOffset (ATO): provides the time in how much earlier segments are available compared to their computed
availability start time (AST).
The segments must contain multiple CMAF chunks. This will result in multiple "moof" and "mdat" boxes per segment.
Example
[styp] size=8+16
[prft] size=8+24
[moof] size=8+96
[mfhd] size=12+4
sequence number = 827234
[traf] size=8+72
[tfhd] size=12+16, flags=20038
track ID = 1
default sample duration = 1001
default sample size = 15704
default sample flags = 1010000
[tfdt] size=12+8, version=1
base media decode time = 828060233
[trun] size=12+12, flags=5
sample count = 1
data offset = 112
first sample flags = 2000000
[mdat] size=8+15704
[prft] size=8+24
[moof] size=8+92
[mfhd] size=12+4
sequence number = 827235
[traf] size=8+68
[tfhd] size=12+16, flags=20038
track ID = 1
default sample duration = 1001
default sample size = 897
default sample flags = 1010000
[tfdt] size=12+8, version=1
base media decode time = 828061234
[trun] size=12+8, flags=1
sample count = 1
data offset = 108
[mdat] size=8+897
Challenges in low latency streaming
Compared to ABR algorithms for "classic" live streaming an ABR algorithm for low latency streaming has to overcome
additional challenges.
Challenge 1: Throughput estimation
Common throughput based ABR algorithms calculate the available bandwidth on the client side using the download time for
a segment:
Calculated Throughput = Segment@Bitrate * Segment@duration / DownloadTimeExample:
Calculated Throughput = 6Mbit/s * 6s / 3s = 12 Mbit/s
The concept described above is a problem for clients operating in low latency mode. Since segments are transferred via
HTTP 1.1 Chunked transfer encoding the download time of a segment is often times similar to the segment duration. The download
of a segment is started prior to its completion. Therefore, the data is still generated on the server side and arrives
in small chunks at the client side.For instance, the download time for a segment with six second duration will be approximately six seconds. There will be
idle times in which no data is transferred from the server to the client. However, the connection remains open while the
client waits for new data. The total download time includes these idle times. Consequently, the total download time is
not a good indicator for the available bandwidth on the client side.
Low latency throughput estimation in dash.js
dash.js offers two different modes for low latency throughput estimation
#### Default throughput estimation
For every segment that is downloaded the default algorithm saves the timestamp and the length of bytes received
throughout the download process. The data packets do not arrive at moof boundaries. For instance a single "data burst"
might contain multiple moof/mdat pairs. For every data point an entry in the corresponding array is created:
downloadedData.push({
ts: Date.now(), // timestamp when the data arrived
bytes: value.length // length of the data
});
After the download of a segment is completed, the array above is cleared and the throughput is calculated in the
following way:function calculateDownloadedTime(downloadedData, bytesReceived) {
downloadedData = downloadedData.filter(data => data.bytes > ((bytesReceived / 4) / downloadedData.length));
if (downloadedData.length > 1) {
let time = 0;
const avgTimeDistance = (downloadedData[downloadedData.length - 1].ts - downloadedData[0].ts) / downloadedData.length;
downloadedData.forEach((data, index) => {
// To be counted the data has to be over a threshold
const next = downloadedData[index + 1];
if (next) {
const distance = next.ts - data.ts;
time += distance < avgTimeDistance ? distance : 0;
}
});
return time;
}
}
1. In the first step the downloadedData array is filtered and all entries that do not have a certain size are removed.
2. In the next step the average time distance between two consecutive data points is calculated
3. If time distance between two consecutive data points is smaller than the average time distance the time distance is
added to the total download time
4. The total download time is used to calculate the throughput as described before. Using this approach the download
time is no longer equal to the duration of the segment.#### Moof based throughput estimation
In contrast to the default throughput algorithm, the moof based throughput estimation is based on saving the download
time for each CMAF chunk. For that reason, the start and the endtime of each chunk, starting with a moof box and ending
with an mdat box are saved:
// Store the start time of each chunk download
const flag1 = boxParser.parsePayload(['moof'], remaining, offset);
if (flag1.found) {
// Store the beginning time of each chunk download
startTimeData.push({
ts: performance.now(),
bytes: value.length
});
}
const boxesInfo = boxParser.findLastTopIsoBoxCompleted(['moov', 'mdat'], remaining, offset);
if (boxesInfo.found) {
const end = boxesInfo.lastCompletedOffset + boxesInfo.size;
// Store the end time of each chunk download
endTimeData.push({
ts: performance.now(),
bytes: remaining.length
});
}
#### dash.js configurationThe desired download time calculation mode can be selected by changing the respective settings parameter:
| Value | Mode |
|--------------------------------------------------------------|----------------------------------|
| LOW_LATENCY_DOWNLOAD_TIME_CALCULATION_MODE.DOWNLOADED_DATA | Default throughput estimation |
| LOW_LATENCY_DOWNLOAD_TIME_CALCULATION_MODE.MOOF_PARSING | Moof based throughput estimation |
player.updateSettings({
streaming: {
abr: {
throughput: {
lowLatencyDownloadTimeCalculationMode: dashjs.Constants.LOW_LATENCY_DOWNLOAD_TIME_CALCULATION_MODE.MOOF_PARSING
}
}
}
})
Challenge 2: Maintaining a consistent live edge
When playing in low latency mode the client needs to maintain a consistent live edge allowing only small deviations
compared to the target latency.
Maintaining a consistent live edge in dash.js
In order to maintain a consistent live edge dash.js either adjusts the playback rate of the video (catchup mechanism),
or performs a seek back to the live edge. The catchup behavior of dash.js based on the deviation compared to the target
latency is depicted below:
#### Default catchup mechanism
In order to determine whether the catchup mechanism should be enabled the following logic is applied:
function _defaultNeedToCatchUp(currentLiveLatency, liveDelay, liveCatchupLatencyThreshold, minDrift) {
try {
const latencyDrift = Math.abs(_getLatencyDrift());
return latencyDrift > 0;
} catch (e) {
return false;
}
}
The latency drift is compared against the minimum allowed drift minDrift. In addition, the catchup mechanism is only
applied if the current live latency is smaller than the defined threshold in latencyThreshold (see dash.js
configuration above).In case the catchup mechanism is applied the new playback rate is calculated the following way:
function _calculateNewPlaybackRateDefault(liveCatchUpPlaybackRate, currentLiveLatency, liveDelay, bufferLevel, currentPlaybackRate) {
const cpr = liveCatchUpPlaybackRate;
const deltaLatency = currentLiveLatency - liveDelay;
const d = deltaLatency * 5;
// Playback rate must be between (1 - cpr) - (1 + cpr)
// ex: if cpr is 0.5, it can have values between 0.5 - 1.5
const s = (cpr * 2) / (1 + Math.pow(Math.E, -d));
let newRate = (1 - cpr) + s;
// take into account situations in which there are buffer stalls,
// in which increasing playbackRate to reach target latency will
// just cause more and more stall situations
if (playbackStalled) {
// const bufferLevel = getBufferLevel();
if (bufferLevel > liveDelay / 2) {
// playbackStalled = false;
playbackStalled = false;
} else if (deltaLatency > 0) {
newRate = 1.0;
}
}
// don't change playbackrate for small variations (don't overload element with playbackrate changes)
if (Math.abs(currentPlaybackRate - newRate) <= minPlaybackRateChange) {
newRate = null;
}
return {
newRate: newRate
};
}
Note that the new playback rate must differ from the current playback rate by a hardcoded threshold:minPlaybackRateChange = isSafari ? 0.25 : 0.02;
#### LoL+ based catchup mechanismThe LoL+ based catchup mechanism follows the same principles as the default catchup mechanism. In the first step dash.js
checks if the catchup mechanism should be applied:
function _lolpNeedToCatchUpCustom(currentLiveLatency, liveDelay, minDrift, currentBuffer, playbackBufferMin, liveCatchupLatencyThreshold) {
try {
const latencyDrift = Math.abs(_getLatencyDrift());
return latencyDrift > 0 || currentBuffer < playbackBufferMin;
} catch (e) {
return false;
}
}
Compared to the default catchup mechanism, the LoL+ based catchup check uses playbackBufferMin. If either the latency
drift is larger than the minimum allowed drift minDrift or the current buffer length is smaller than the minimum
buffer playbackBufferMin the catchup mode is activated.Note that a change of playback rate can also mean that the playback rate is decreased. This can be useful to avoid
buffer underruns.
The new playback rate is calculated in the following way:
/ Detailed source-code truncated for AI context efficiency. /
``
If the buffer level is smaller than the buffer level defined in
playbackBufferMin the playback rate is decreased. If
the buffer is "safe", the playback rate is adjusted depending on the latency.#### Calculating the new playback rate
Both catchup mechanisms share a common method to determine the calculation of the new playback rate:
javascriptconst s = (cpr * 2) / (1 + Math.pow(Math.E, -d));
newRate = (1 - cpr) + s;
*
cpr is defined as the catchup playbackRate
* d is defined as a multiple of the delta latency or the delta buffer
* Math.E represents the base of natural logarithms, e, approximately 2.718.If the current live latency is greater larger than the target latency
d is positive, otherwise d is negative.
Consequently, if the playback rate needs to be incremented to reach the target latency the
equation Math.pow(Math.E, -d) will result in values smaller than 1. For negative d values, situations in which the
playback rate should be decreased, the equation Math.pow(Math.E, -d) will result in values greater than 1.<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/c/c6/Exp.svg/2880px-Exp.svg.png" height="400" />
As an example consider a situation in which the target latency is set to 2 seconds and the current latency equals 5
seconds:
textcpr = 0.5
delta latency = current latency - target latency = 5 - 2 = 3
d = delta latency 5 = 3 5 = 15
s = (cpr 2) / (1 + Math.pow(Math.E, -d)) = (0.5 2) / (1 + 3.0590232050182605e-7) = 0.999999694097773
new rate = (1 - cpr) + s = (1 - 0.5) + 0.999999694097773 = 1.499999694097773
The new rate will always stay within the target boundaries
1 +/- 0.5#### dash.js configuration
The desired catchup mechanism can be selected by changing the respective settings parameter:
| Value | Mode |
|-----------------------------|------------------------------|
|
LIVE_CATCHUP_MODE_DEFAULT | Default catchup mechanism |
| LIVE_CATCHUP_MODE_LOLP | LoL+ based catchup mechanism |javascriptplayer.updateSettings({
streaming: {
liveCatchup: {
mode: dashjs.Constants.LIVE_CATCHUP_MODE_DEFAULT
}
}
})
#### Seeking to the live edge
In addition, both catchup algorithms share a common logic to seek back to the live edge. If the latency delta exceeds
the threshold defined in
maxDrift the seek is performed:javascript// we reached the maxDrift. Do a seek
const maxDrift = mediaPlayerModel.getCatchupMaxDrift();
if (!isNaN(maxDrift) && maxDrift > 0 &&
deltaLatency > maxDrift) {
logger.info('[CatchupController]: Low Latency catchup mechanism. Latency too high, doing a seek to live point');
isCatchupSeekInProgress = true;
_seekToLive();
}
Low latency ABR algorithms in dash.js
dash.js has two low latency specific algorithms LoL+ and L2A.
Material
Articles
* Daniel Silhavy - dash.js – Low Latency Streaming with CMAF
* Will Law - Using LL-HLS with byte-range addressing to achieve interoperability in low latency streaming
Videos
* Will Law - Chunky Monkey
* Theo Karagkioules,R. Mekuria,Dirk Griffioen, Arjen Wagenaar - Online learning for low-latency adaptive streaming
* May Lim, Mehmet N Akcay, Abdelhak Bentaleb, Ali C. Begen, R. Zimmermann - When they go high, we go low: low-latency live streaming in dash.js with LoL
Bibliography
* [1] DASH-IF - Report on Low-Latency Live Service with DASH
* [2] Daniel Silhavy - dash.js – Low Latency Streaming with CMAF
---
Site/Pages/Usage/Mpd Patching
---
title: MPD Patching
---
MPD Patching
MPF patching is a feature that was introduced in the fifth edition of the MPEG-DASH specification. Updates to the MPD
are provided through MPD patches. MPD patches only contain new information such as signaling an additional media
segment. In general MPD patching allows the addition, removal and update of information in the manifest.
The goal of MPD patching is to provide only mandatory MPD information to the client instead of sending the whole MPD again with each MPD
update. This can significantly reduce the size of the manifest updates and also reduce the parsing time on the client
side.
In the example illustration below the initial request to the MPD contained a reference to segments 1-3. If the client
performs a full MPD update again it will get the missing reference to segment 4 but also the references to segment 1-3
again. This creates overhead in terms of the size of the MPD update and also the parsing time on the client side. An MPD
patch only contains the reference to segment 4 being aware that the client already knows about segment 1-3.
Example
An example of MPD patching can be found in
the sample section.
---
Site/Pages/Usage/Mss
---
title: Microsoft Smooth Streaming
---
Microsoft Smooth Streaming
Description
In adition to playback of MPEG-DASH content, dash.js supports playback of the legacy Smooth Streaming format. For that reason,
a conversion of the Smooth Streaming manifest files and media segments is performed directly in dash.js on the client side.
Example
An example is available as part of
the sample section.dash.js usage
The Smooth Streaming layer in dash.js is implemented as a separate module. To enable Smooth Streaming support in dash.js
import dash.mss.min.js or dash.mss.debug.js right after the main build files:<head>
<script src="../../dist/umd/dash.all.debug.js"></script>
<script class="code" src="../../dist/umd/dash.mss.debug.js"></script>
</head>Initialization of dash.js for Smooth Streaming is similar to initialization for DASH content:
jslet video, player;player = dashjs.MediaPlayer().create();
video = document.querySelector('video');
player.initialize(); / initialize the MediaPlayer instance /
player.attachView(video); / tell the player which videoElement it should use /
player.setProtectionData(protData); / set protection data (sets license server when required) /
player.attachSource(streamUrl); / provide the manifest source /
---
Site/Pages/Usage/Multiperiod
---
title: Multiperiod Streams
---
Multiperiod Streams
A DASH MPD can contain multiple
Period elements. Each period defines its own set of Adaptation Sets and
Representations and can differ from the previous period, for instance in codecs, available tracks or content
protection. Multiperiod streams are commonly used for ad insertion and for concatenating different pieces of content
into a single presentation.How dash.js handles periods
dash.js supports multiperiod streams for both VoD and live content out of the box, no additional configuration is
required:
jsconst player = dashjs.MediaPlayer().create();
player.initialize(videoElement, mpdUrl, true);
Internally, dash.js creates one
Stream object per period. When playback approaches a period boundary, the player
prepares the next period ahead of time and performs a seamless transition. The current track and quality settings are
re-applied after each transition: dash.js tries to continue with the same language, role and quality settings that were
active in the previous period.Period switch events
To get notified about period transitions, subscribe to the corresponding player events:
jsplayer.on(dashjs.MediaPlayer.events.PERIOD_SWITCH_STARTED, (e) => {
console.log('Switching from period', e.fromStreamInfo ? e.fromStreamInfo.index : null,
'to period', e.toStreamInfo.index);
});player.on(dashjs.MediaPlayer.events.PERIOD_SWITCH_COMPLETED, (e) => {
console.log('Now playing period', e.toStreamInfo.index);
});
-
PERIOD_SWITCH_STARTED is triggered when the player starts the transition to a new period. The payload contains
fromStreamInfo (null at playback start) and toStreamInfo.
- PERIOD_SWITCH_COMPLETED is triggered once the new period is active. The payload contains toStreamInfo.The
streamInfo objects expose useful information such as id, index, start and duration of the corresponding
period.Sample
- Multiperiod VoD - VoD stream with two
periods.
- Multiperiod live - Live stream with
multiple periods.
---
Site/Pages/Usage/Network Interceptor
---
title: Network Interceptor
---
Network Interceptor
In some cases it might be necessary to modify the outgoing network requests or the incoming network response data. For
that
reason, dash.js provides a network interceptor API that allows applications to intercept and modify network requests and
responses.
Intercepting network requests
To intercept network requests, the application must register a callback function that will be called before or after the
request is sent. The callback function must return a promise that resolves to the modified request object. The following
example demonstrates how to intercept network requests:
text/ Detailed source-code truncated for AI context efficiency. /
A fully working example can be found in our sample section.
---
Site/Pages/Usage/Player Events
---
title: Player Events
---
Player Events
dash.js dispatches various events during playback to inform the application about changes of the playback state and
metrics being added or updated. A complete list of the available events can be
found here.
Example
An example is available as part of
the sample section.
dash.js usage
To register for a specific event use the on method of the media player instance:
player.on(dashjs.MediaPlayer.events.BUFFER_LOADED, showEvent);
The payload of the event is passed as an object to the callback function
function showEvent(e) {
log("Event received: " + e.type);
}
To remove the listener for an event use the off method:
player.off(dashjs.MediaPlayer.events.BUFFER_LOADED, showEvent);
---
Site/Pages/Usage/Preloading
---
title: Preloading
---
Preloading
dash.js can initialize streaming and start downloading media segments before the player is attached to an HTML5
video element. The downloaded data is kept in a virtual buffer and appended to the newly created Source Buffers once a
video element is attached. This is useful to optimize content insertion — for example pre-buffering the upcoming
content while an advertisement is still playing — especially on platforms that only provide a single decoder.
Usage
Initialize the player without a video element, enable cacheInitSegments and call preload():
const player = dashjs.MediaPlayer().create();// no video element yet: pass null as the view
player.initialize(null, mpdUrl, true);
player.updateSettings({
streaming: {
cacheInitSegments: true
}
});
player.preload();
dash.js now downloads media segments into a virtual buffer. As soon as the application attaches a video element, the
buffered data is transferred and playback can start immediately:
player.attachView(videoElement);Notes
- preload() throws a SOURCE_NOT_ATTACHED_ERROR if it is called before a source was set via initialize() or
attachSource().
- streaming.cacheInitSegments must be enabled so the init segments can be re-appended to the real Source Buffers
after the view is attached.
- Calling preload() has no effect if a video element is already attached or streaming was already initialized.
Sample
- Preload content - preloads into a
virtual buffer, playback starts when "Attach View" is clicked.
---
Site/Pages/Usage/Settings
---
title: Settings
---
Settings
dash.js offers various configuration options that can be set to customize the player behavior. These options can be set
in the player settings object after initializing the player. All settings are maintained in the Settings.js file
and can easily be adjusted using the dash.js API.
An overview of all settings can be found in
our API documentation.
Example
To update a specific setting use the updateSettings method of the player object. A more detailed example can be found
in
our sample section.
The example below shows how to change the log level:
player.updateSettings({
debug: {
logLevel: dashjs.Debug.LOG_LEVEL_WARNING
}
});
---
Site/Pages/Usage/Thumbnails
---
title: Thumbnails
---
Thumbnails
dash.js supports thumbnail tracks as defined by the
DASH-IF Interoperability Guidelines. Thumbnails are provided as an additional
Adaptation Set with @contentType="image", typically containing tiled images (thumbnail sprites). Applications
commonly use thumbnails to show a preview when the user hovers over the seekbar.
Querying thumbnails
Thumbnail tracks are handled automatically when present in the MPD. To retrieve the thumbnail for a specific media
time, use provideThumbnail(). The call is asynchronous, the result is passed to the provided callback:
const player = dashjs.MediaPlayer().create();
player.initialize(videoElement, mpdUrl, true);// time is relative to the value returned by player.duration()
player.provideThumbnail(120, (thumbnail) => {
if (thumbnail === null) {
// no thumbnail track or no thumbnail for this time position
return;
}
console.log(thumbnail.url, thumbnail.x, thumbnail.y, thumbnail.width, thumbnail.height);
});
The thumbnail object describes a single tile within the (potentially tiled) thumbnail image:
| Property | Description |
|:---------|:---------------------------------------------------------|
| url | URL of the image containing the requested thumbnail |
| x, y | Pixel offset of the thumbnail tile inside the image |
| width | Width of the thumbnail tile in pixels |
| height | Height of the thumbnail tile in pixels |
A typical seekbar preview renders the image as a CSS background positioned at -x/-y with the tile's width and
height as the visible area.
Selecting a thumbnail representation
If the MPD contains multiple image representations (for instance different resolutions), they are exposed like any
other media type and can be queried and selected via the track and representation APIs using the media type image,
e.g. player.getRepresentationsByType('image').
Sample
- Thumbnails - stream with tiled
thumbnails, selectable in the control bar's bitrate menu.
---
Site/Pages/Usage/Timing Apis
---
title: Timing APIs
---
Playback Time
dash.js exposes multiple API endpoints in the MediaPlayer class to query the current playback position and information
about the DVR window. Examples for VoD and live playback are illustrated in the sections below.
All methods available via the player instance, as an example:
var video = document.querySelector('video');
var player = dashjs.MediaPlayer().create();
player.initialize(video, url, false);
var time = player.time();
VoD
| API call | Description |
|:-----------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| time() | Returns the current playback time relative to playback start (usually 0). |
| time(periodId) | Returns the current playback time relative to the period specified in periodId. |
| duration() | Returns the total duration of the content. |
| getDvrWindow() | Returns the start, the end and the size of the DVR window. For VoD content all media segments are available. Consequently, the DVR windows spans over the whole duration of the content. |
Live
| API call | Description |
|:--------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------|
| time() | Returns the current playback time relative to playback start (availability start time). |
| time(periodId) | Returns the current playback time relative to the period specified in periodId. |
| timeAsUtc() | Returns the current playback time relative to midnight UTC, Jan 1 1970 |
| timeInDvrWindow() | Returns the current playback time relative to the start of the DVR window. |
| duration() | Returns the size of the DVR window: DVRWindow.end - DVRWindow.start. |
| getDvrWindow() | Returns the start, startAsUtc, the end, endAsUtc and the size of the DVR window. For live content media segments become available over time. |
Seeking
dash.js provides two API endpoints to change the current playback position, namely seek()
and seekToPresentationTime(). Both methods are available via the MediaPlayer class. As an example:
var video = document.querySelector('video');
var player = dashjs.MediaPlayer().create();
player.initialize(video, url, false);
player.seek(10)
While the seek() method expects values relative to DVRWindow.start the seekToPresentationTime() works with
absolute presentation timestamps. Two examples to illustrate this behavior are depicted in the sections below.
VoD
For VoD playback both seek() and seekToPresentationTime() work in the same way and can be used interchangeable. This
is due to the fact the DVR window for VoD spans over the whole duration of the content.
Live
For live playback the seek() method expects values relative to the start of the DVR window.
Internally DVRWindow.start is added to the provided value. The seekToPresentationTime() method uses absolute presentation timestamps.
As an example, the following two code snippets each trigger a seek 20 seconds behind the live edge.
##### seek()var video = document.querySelector('video');
var player = dashjs.MediaPlayer().create();
player.initialize(video, url, false);
var duration = player.duration()
player.seek(duration - 20);
##### seekToPresentationTime()var video = document.querySelector('video');
var player = dashjs.MediaPlayer().create();
player.initialize(video, url, false);
var dvrWindowEnd = player.getDvrWindow().end
player.seekToPresentationTime(dvrWindowEnd - 20);
Setting a start time
By default, playback starts at the beginning of a VoD presentation and at the live edge (minus the live delay) for
live presentations. To start at a different position, pass a start time as the second parameter of attachSource():
player.initialize();
player.attachView(video);
player.attachSource(url, starttime);
The interpretation of starttime depends on the type of content:
- VoD: the start time is relative to the start of the first period, in seconds.
- Live:
- With the posix: prefix the value signifies an absolute time in seconds of Coordinated Universal Time (number
of seconds since 01-01-1970 00:00:00 UTC). Fractions of seconds may be specified down to the millisecond level:
player.attachSource(url, 'posix:1696243200').
- Without the posix: prefix the start time is relative to MPD@availabilityStartTime.
For example, to start playback 60 seconds behind the current wall clock time:
const starttime = new Date().getTime() / 1000 - 60;posix:${starttime}
player.attachSource(url, );
An example is available in
the start time sample.
MPD anchors
Alternatively, the start time can be signaled directly in the MPD URL using an MPD anchor as defined in Annex C.4 of
the DASH specification. dash.js parses the anchor and starts playback at the requested position:
// start playback at second 60
const url = 'https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd#t=60';
player.initialize(video, url, true);
For live streams, #t=posix:... is supported analogously to the attachSource() syntax described above.
An example is available in
the MPD anchor sample.
---
Site/Pages/Usage/Track Selection
---
title: Track Selection
---
Track Selection
Some media streams offer multiple audio or video tracks. In MPEG-DASH this is done by placing the different tracks in
separate Adaptation Sets. dash.js allows the application to define an initial track at startup and switch between tracks
at runtime.
Capability checks
While parsing a manifest, dash.js removes Adaptation Sets / Representations it deems unsupported based on the
@codecs attribute,
EssentialProperty descriptors, required DRM systems and other properties.Handling of @supplementalCodecs
dash.js will evaluate optionally present
@supplementalCodecs and if it is recognized as supported, then its content will be used instead of the value provided with the
@codecs attribute.Basic filtering
The MediaCapabilities API is the default mechanism used by dash.js to determine device capabilities for track selection.
This can be disabled by setting the
streaming.capabilities.useMediaCapabilitiesApi setting to false, which makes dash.js to utilize
isTypeSupported() from MediaSource instead.Advanced filtering using MediaCapabilities
By default, only
@codecs is used to query the media capabilities of the MediaCapabilities API.Audio tracks can also be filtered by matching the AudioChannelConfiguration against the number of
audio channels supported by the device.
This option is disabled by default and can be enabled using the streaming.capabilities.filterAudioChannelConfiguration setting.
Other media capabilities can be matched against EssentialProperty descriptors (see the next section).
Filtering using EssentialProperty descriptors
By default, dash.js will filter-out AdaptationSets / Representations whose
EssentialProperty descriptors are not recognized.
This behavour is controlled by the
streaming.capabilities.filterUnsupportedEssentialProperties setting.If set to false, all EssentialProperty descriptors are ignored and do not influence track selection.
Two mechanisms can be used to configure filtering using EssentialProperty descriptors:
* an allow‑list using a regular-expression syntax; or
* MediaCapabilities API‑based filtering.
Simple filtering can be controlled by defining regular expressions to match values inEssentialProperty descriptors.
The streaming.capabilities.supportedEssentialProperties setting can list the @schemeIdUri of supported descriptors and
regular expressions to match against descriptor @value.
For example, the following configuration would remove all DVB low-latency tracks:
player.updateSettings({
streaming: {
capabilities: {
supportedEssentialProperties: [
{ schemeIdUri: 'urn:dvb:dash:lowlatency:critical:2019', value: 'false' }
]
}
}
});By default, a conservative allow-list is defined that handles EssentialProperty descriptors for
DVB font download, DASH‑IF thumbnails, and an SDR‑only subset of CICP colorimetry
(ColourPrimaries, MatrixCoefficients, TransferCharacteristics).
If your application can rely on the MediaCapabilities API, then dash.js can matchEssentialProperty descriptors against device capabilities.
The following settings can be used to control matching logic forEssentialProperty descriptors:
* filterVideoColorimetryEssentialProperties
* filterHDRMetadataFormatEssentialProperties
These settings are needed to ensure that HDR tracks are correctly matched against device capabilities.
For example:
player.updateSettings({
streaming: {
capabilities: {
useMediaCapabilitiesApi: true,
filterVideoColorimetryEssentialProperties: true,
filterHDRMetadataFormatEssentialProperties: true
}
}
});With these flags, dash.js will query the platform to evaluate colorimetry / HDR EssentialProperty combinations instead
of relying solely on the static allow‑list, reducing the risk of “over‑filtering” valid HDR tracks on capable devices.
Initial track selection
dash.js offers multiple ways to control the initial track selection as described below.
Initial media settings
To select an initial track prior to the start of the playback based on specific media settings use the
setInitialMediaSettingsFor() function. The function takesan object as input allowing you to define initial values such as the target language or accessibility preferences.
For each parameter present in the configuration object, dash.js tries to find matching Adaptation Sets and keeps only
those that match the given setting. If no Adaptation Set is found or the parameter is not present in configuration
object, all Adaptation Sets are kept. This processing iterates sequentially the following parameters in the given order:
1. @id
2. @lang
3. Index (i.e. order of Adaptation Sets in the MPD)
4. Viewpoint
5. Role
6. Accessibility
7. AudioChannelConfiguration
8. @codecs
Notes and Exceptions:
- dash.js does normalize and compare the values provided via the @lang attributes and the lang setting according to
the rules provided with IETF BCP 47 (e.g. spa will get converted to es prior to comparison)
- If accessibility is not provided as parameter, dash.js prioritizes those AdaptationSets where no Accessibility
element is present
For a detailed description of this method checkout
our API documentation.
Example
An example how to set the initial audio track by specifying the target language is shown below:
player.initialize(videoElement, url, true);
player.setInitialMediaSettingsFor('audio', {
lang: 'es',
accessibility: {
schemeIdUri:'urn:mpeg:dash:role:2011',
value:'description'
}
});A working example can be found in
our sample section.
Custom track selection function
You can also define your own custom track selection function. This function will be called by the player to determine
which track to select.
Example
var getTrackWithLowestBitrate = function (trackArr) {
let min = Infinity;
let result = [];
let tmp; trackArr.forEach(function (track) {
tmp = Math.min.apply(Math, track.bitrateList.map(function (obj) {
return obj.bandwidth;
}));
if (tmp < min) {
min = tmp;
result = [track];
}
});
return result;
}
player.setCustomInitialTrackSelectionFunction(getTrackWithLowestBitrate);
A working example can be found in
our sample section
Changing the default track selection logic
When neither initial media setting nor any custom track selection function provided a unique selection, the
selectionPriority attribute from the MPD is used to determine which track to select. This logic can be disabled by adjusting the corresponding settings flag:
player.updateSettings({
streaming: {
ignoreSelectionPriority: true
}
})After this, dash.js tries to find the "main" track based on the Role descriptor.
This logic can be disabled by adjusting the corresponding settings flag:
player.updateSettings({
streaming: {
prioritizeRoleMain: false
}
})To accomplish this and if no Role descriptor with @value="main" is present, dash.js considers the absence of this
descriptor also as "main". This feature can be disabled by adjusting the streaming.assumeDefaultRoleAsMain settings flag.
If still no unique selection could be made, dash.js offers various predefined approaches to select the initial track.
The default track selection mode can be changed using the selectionModeForInitialTrack setting. The following modes
are supported:
| Mode | Description |
|:-------------------------------|:------------------------------------------------------------------------------------------------------------|
| lowestStartupDelay (default) | This mode makes the player select the track that contains partial segments that start with SAP type 0 or 1. |
| highestBitrate | This mode makes the player select the track with a highest bitrate. |
| firstTrack | This mode makes the player select the first track found in the manifest |
| highestEfficiency | This mode makes the player select the track with the lowest bitrate per pixel average. |
| widestRange | This mode makes the player select the track with a widest range of bitrates. |
Example
player.updateSettings({
streaming: {
selectionModeForInitialTrack: 'highestBitrate'
}
})Track selection at runtime
To switch to a different track at runtime use the setCurrentTrack(track) method. You need to provide a valid track as
the input to this function. A list of all available tracks can be obtained by calling getTracksFor().
Example
const targetIndex = 1;
const availableTracks = player.getTracksFor('audio');
const targetTrack = availableTracks[targetIndex];
player.setCurrentTrack(targetTrack);---
Site/Pages/Testing/Functional Test
---
title: Functional Tests
---
Functional Tests
Functional testing in the context of media players refers to the process of testing the functionality and behavior of a
media player. It involves verifying that the media player performs its intended tasks correctly, such as playing various
types of media files (e.g. DASH manifests and segments), managing playback (e.g. play, pause, seek) and
controlling external events (e.g. a user induced switch of the audio or subtitle language). Ideally, the functionality
of a media player can be completely tested in an automated fashion obsoleting the need to perform time-consuming and
resource-consuming manual tests.
Functional Tests in dash.js
A single functional test in dash.js typically consists of multiple steps and test-assertions:
import { const TESTCASE = Constants.TESTCASES.PLAYBACK.PLAY; Utils.getTestvectorsForTestcase(TESTCASE).forEach((item) => { describe( before(() => { after(() => { it( it( it( })import Constants from '../../src/Constants.js';${TESTCASE} - ${item.name} - ${mpd}
import Utils from '../../src/Utils.js';
checkIsPlaying,
checkIsProgressing,
checkNoCriticalErrors,
initializeDashJsAdapter
} from '../common/common.js';
const mpd = item.url;, () => {Checking playing state
let playerAdapter;
playerAdapter = initializeDashJsAdapter(item, mpd);
})
playerAdapter.destroy();
}), async () => {Checking progressing state
await checkIsPlaying(playerAdapter, true);
}), async () => {Expect no critical errors to be thrown
await checkIsProgressing(playerAdapter);
});, () => {
checkNoCriticalErrors(playerAdapter);
})
})
In the example above the player is initialized and the playback is triggered. The test passes if the player successfully
transitions to the playing state and the playback time is progressing. Moreover, no critical errors shall be thrown.
Structure
The functional tests are located in test/functional and are divided into different folders:
- adapter: The tests are implemented in a generic fashion enabling different media players to be
plugged in. The concrete implementation of the player interfaces is realized through adapter classes located in this
folder. As an example, the DashJsAdapter class implements the required functions to control dash.js.
- config: This folder contains the test configuration files. The functional tests are executed through
the Karma Testrunner. The configuration for the Karma Testrunner
is located in karma.functional.conf.cjs. The streams to be tested are located in
dedicated JSON files in the streams subfolder. In addition, example configurations for executing the tests locally
and on Browserstack are provided.
- content: Locally hosted content files that are used by the testcases. As an example, this folder contains manifest
files with missing segments to create a gap in the media buffer.
- results: Contains the results of the testruns in HTML and JUnit format.
- src: Utility functions and classes used in the test implementations.
- test: Implementation of the testcases. The testfiles are divided into different folders depending on the
functionality they are verifying. For instance, tests related to subtitles and captions are located in the text
folder.
- view: Contains the landing page that is launched by the Karma Testrunner for the execution of the tests.
Execution
Via Karma
The execution of the functional testsuite is straight forward. Simply start Karma with the required configuration
options:
karma start test/functional/config/karma.functional.conf.cjs --configfile=local --streamsfile=smoke
* test/functional/config/karma.functional.conf.cjs: The Karma configuration file
* --configfile=local: Path to the test configuration file to define which browsers should be used for test execution
and
how the test reports should be saved. The path is relative to test/functional/config/test-configurations
* --streamsfile=smoke: Path to the streams configuration file defining the streams to be used.
Example Configurations
dash.js ships with predefined configuration files. They are located in test/functional/config/test-configurations.
Supported Testcases
| Testcase | Description |
|:-----------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------|
| advanced/no-reload-after-seek | Build up a backwards buffer and then seek into the backwards buffer range. Expect no redundant segment downloads for segments that are already in the buffer. |
| advanced/seek-in-gaps | Checks playback of MPDs that contain gaps in the timeline or create gaps in the media buffer. The player should recover from such situations. |
| audio/initial-audio | Define an initial audio language and expect the player to choose this language at playback start. |
| audio/switch-audio | Switch the audio language during playback and expect the player to download the right media segments. |
| buffer/buffer-cleanup | Play the stream for some time and expect the buffer level to stay withing the predefined tolerance. |
| buffer/initial-buffer-target | Set an initial buffer target and expect the player to build the buffer before starting playback. |
| feature-support/cmcd | Checks if CMCD parameters are included in outgoing segment requests if enabled. |
| feature-support/emsg-triggered | Checks if EMSG events are correctly parsed and dispatched to the application. |
| feature-support/mpd-patching | Checks if two consecutive manifest updates are of type Patch |
| live/latency-catchup | Expect the player to apply the catchup logic to stick to a certain latency if enabled. |
| live/live-delay | Expect the live delay to correspond to the initial settings. |
| playback/ended | Expect the ended event to be thrown once playback is finished. |
| playback/pause | Expect the player to correctly pause playback. |
| playback/play | Expect the player to correctly trigger playback. |
| playback/seek | Expect the player to correctly seek to a target time. |
| playback-advanced/attach-at-non-zero | Check if the player uses the starttime provided via the attachSource() function |
| playback-advanced/attach-with-posix | Check if the player uses the starttime provided in posix format to attachSource() |
| playback-advanced/cmcd | Enable CMCD reporting and expect the media segment requests to have CMCD parameters. |
| playback-advanced/mpd-anchor | Use MPD anchors to define a starttime. |
| playback-advanced/multiperiod-playback | Verify that the player transitions to a new period when playing a multiperiod MPD. |
| playback-advanced/preload | Preload media segments to a virtual buffer before a video element is attached to the player. |
| text/initial-text | Set an initial language for the selection of a texttrack and expect the player to respect that setting. |
| text/switch-text | Switch the texttrack during playback and expect the player to download the right track. |
| vendor/google-ad-manager-emsg | Check the working integration of the Google Ad Manager |
| video/switch-video | Switch to a different video track e.g. switching between AdaptationSets with different codecs. |
---
Site/Pages/Testing/Index
---
title: Testing
---
Testing
dash.js ships with unit and functional tests that verify various features of the player to make sure that new additions
to the player do not introduce any regression.
---
Site/Pages/Testing/Unit Test
---
title: Unit Tests
---
Unit Tests
dash.js ships with various unit tests for validating the correct behavior of the functions in different classes.
Structure
The unit tests are located in test/unit and divided into different folders:
- config: Contains the configuration files to execute the unit tests. dash.js uses
the Karma Testrunner to execute the unit tests directly in the
browsers.
- data: Contains additional files like manifests, license responses and subtitle files to be used in the unit tests.
- helpers: Contains helper classes that contain common functionality used by the testfiles.
- mocks: Contains mock implementations of specific classes. When instantiating a class for a test, mock dependencies
are injected to trigger a certain behavior.
- results: Contains the final results of the tests in JUnit format.
- test: Contains the concrete implementation of the testcases. The tests are divided into different folders based on
the location of the source file to be tested.
Execution
To execute the unit tests simply run npm run test in the root folder of dash.js. Per default, the tests are then
executed in Chrome and Firefox (running in headless mode). The result for each test is printed in the terminal. The
final result similar to this:
---
Site/Pages/Quickstart/Index
---
title: Quickstart
---
Quickstart
Using dash.js in your application is very straight forward. Just follow the installation
and setup.
For detailed usage instructions refer to the Usage section.
---
Site/Pages/Quickstart/Installation
---
title: Installation / Build
---
Installation & Build
There are multiple ways to obtain the bundled dist files of dash.js for usage in your application.
Which branch should I use?
- Use the master branch if you want the approved and stable public releases without contributing back.
- Use the development branch if you want to improve or extend dash.js — make your changes there and submit a pull
request against it.
Migrating from version 4.x? The migration guide covers all changes to
build files, settings and APIs in version 5.
Bundle formats
Version 4.x and older
In version 4.x and older versions of dash.js there is only one bundle format available. It is a UMD build that does
not contain any polyfills.
Version 5.x and newer
With version 5 of dash.js we introduced three different bundle formats:
* UMD legacy: A UMD build targeting legacy platforms by specifying the babel target ie: '11'. In addition,
core.js polyfills are enabled.
* ESM modern: An ESM build using .browserslistrc as target, with target set to defaults. No core.js
polyfills are enabled.
* UMD modern: A UMD build targeting modern platforms using .browserslistrc as target, with target set to
defaults. No core.js polyfills are enabled.
All the bundled files are located in the dist directory of the repository. The legacy folder inside the dist
folder contains the UMD legacy build. The modern folder inside the dist folder contains both the ESM modern and
the UMD modern build.
General Note
Note that only the master branch of dash.js includes the dist folder. If you are working with the development
branch, you need to build the bundles yourself. For that reason, check
the Building the dist files section below.
CDN hosted files
We provide the latest minified files of all releases on a global CDN. They are free to be used in production
environments. An overview
of the dash.js releases can be found on GitHub.
Version 4.x and older
All releases prior to version 5.0.0 are available under the following urls. Replace vx.x.x with the release version,
for
instance v3.1.0.
- http://cdn.dashjs.org/vx.x.x/dash.all.min.js
- http://cdn.dashjs.org/vx.x.x/dash.all.debug.js
Version 5.x and newer
#### Version builds
With version 5.0.0 we introduced new bundle formats. The URLs for the CDN hosted files for these new bundle formats
are as follows. Replace vx.x.x with the release version, for instance v5.0.0.
- UMD legacy
- Minified
Build: http://cdn.dashjs.org/vx.x.x/legacy/umd/dash.all.min.js
- Debug
Build: http://cdn.dashjs.org/vx.x.x/legacy/umd/dash.all.debug.js
- UMD modern
- Minified
Build: http://cdn.dashjs.org/vx.x.x/modern/umd/dash.all.min.js
- Debug
Build: http://cdn.dashjs.org/vx.x.x/modern/umd/dash.all.debug.js
- ESM modern
- Minified
Build: http://cdn.dashjs.org/vx.x.x/modern/esm/dash.all.min.js
- Debug
Build: http://cdn.dashjs.org/vx.x.x/modern/esm/dash.all.debug.js
Multiple examples how to use dash.js in your Typescript or Webpack based JavaScript project can be found in samples/modules.
#### Latest build
The bundles of the latest release can be found here:
- UMD legacy
- Minified
Build: http://cdn.dashjs.org/latest/legacy/umd/dash.all.min.js
- Debug
Build: http://cdn.dashjs.org/latest/legacy/umd/dash.all.debug.js
- UMD modern
- Minified
Build: http://cdn.dashjs.org/latest/modern/umd/dash.all.min.js
- Debug
Build: http://cdn.dashjs.org/latestmodern/umd/dash.all.debug.js
- ESM modern
- Minified
Build: http://cdn.dashjs.org/latest/modern/esm/dash.all.min.js
- Debug
Build: http://cdn.dashjs.org/latest/modern/esm/dash.all.debug.js
Note: For backwards compatibility we also host the UMD modern bundle under https://cdn.dashjs.org/latest/dash.all.min.js.
NPM package
We publish dash.js to npm. Examples of how to use dash.js in different module
bundlers can be found in
the samples/modules directory of
the dash.js repository.
Release candidates
Release candidates for the upcoming version (x.y.z-rc.N) are published under the next tag:
npm install dashjs@next
The latest tag always points to the latest stable release.
Version 4.x and older
For version 4.x and older, we define the following entry point in the package.json:
{
"main": "dist/dash.all.min.js"
}
Version 5.x and newer
For version 5.x and newer, we define the following entry points in the package.json:
{
"types": "./index.d.ts",
"import": "./dist/modern/esm/dash.all.min.js",
"default": "./dist/modern/esm/dash.all.min.js",
"browser": "./dist/modern/umd/dash.all.min.js",
"script": "./dist/modern/umd/dash.all.min.js",
"require": "./dist/modern/umd/dash.all.min.js"
}
Building the dist files
To build the dist files of the latest stable release yourself run the following steps:
1. Install Core Dependencies --- The standard setup method uses JavaScript to initialize and provide video details to dash.js. Multiple examples showcasing the different ways to initialize the player are available in Create a video element somewhere in your html. For our purposes, make sure the controls attribute is present.
* Install NodeJS
2. Checkout project repository (default branch: development)
* ``git clone https://github.com/Dash-Industry-Forum/dash.js.git3. Change branch to master
*4. Install dependencies
*5. Build the dist files.
*---Site/Pages/Quickstart/Setup
title: Setup
---Setup
Examples
the sample section.UMD
Standard Setup
<video id="videoPlayer" controls></video>
Add dash.all.min.js to the end of the body.<body>
...
<script src="yourPathToDash/dash.all.min.js"></script>
</body>
Now comes the good stuff. We need to create aMediaPlayerand initialize it.
var url = "https://dash.akamaized.net/envivio/EnvivioDash3/manifest.mpd";
var player = dashjs.MediaPlayer().create();
player.initialize(document.querySelector("#videoPlayer"), url, true);
When it is all done, it should look similar to this:<!doctype html>
<html>
<head>
<title>dash.js Rocks</title>
<style>
video {
width: 640px;
height: 360px;
}
</style>
</head>
<body>
<div>
<video id="videoPlayer" controls></video>
</div>
<script src="yourPathToDash/dash.all.min.js"></script>
<script>
(function () {
var url = "https://dash.akamaized.net/envivio/EnvivioDash3/manifest.mpd";
var player = dashjs.MediaPlayer().create();
player.initialize(document.querySelector("#videoPlayer"), url, true);
})();
</script>
</body>
</html>
mpdAlternative Setup
An alternative way to setup the dash.js player on your web page is to use the MediaPlayerFactory. The MediaPlayerFactory
will automatically instantiate and initialize the MediaPlayer module on appropriately tagged video elements.Create a video element somewhere in your html and provide the path to your
file as src. Also ensure that yourdata-dashjs-player
video element has theattribute on it.
<video data-dashjs-player autoplay src="https://dash.akamaized.net/envivio/EnvivioDash3/manifest.mpd" controls>
</video>
Add dash.all.min.js to the end of the body.<body>
...
<script src="yourPathToDash/dash.all.min.js"></script>
</body>
When it is all done, it should look similar to this:<!doctype html>
<html>
<head>
<title>Dash.js Rocks</title>
<style>
video {
width: 640px;
height: 360px;
}
</style>
</head>
<body>
<div>
<video data-dashjs-player autoplay src="https://dash.akamaized.net/envivio/EnvivioDash3/manifest.mpd" controls>
</video>
</div>
<script src="yourPathToDash/dash.all.min.js"></script>
</body>
</html>
ESM
You can also import dash.js as an ES module:
<!doctype html>
<html>
<head>
<title>dash.js Rocks</title>
<style>
video {
width: 640px;
height: 360px;
}
</style>
</head>
<body>
<div>
<video id="videoPlayer" controls></video>
</div>
<script type="module">
import {MediaPlayer} from 'https://cdn.dashjs.org/v5.0.0/modern/esm/dash.all.min.js';
const player = MediaPlayer().create();
player.initialize(document.querySelector('video'), 'https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd', true);
</script>
</body>
</html>
NPM
dash.js is available on npm. Install it as a dependency of your project:
npm install dashjs
In projects that use a module bundler (Webpack, Vite, Rollup, ...) you can then import dash.js directly. The package
resolves to the modern ESM bundle:import { MediaPlayer } from 'dashjs';
const url = 'https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd';
const player = MediaPlayer().create();
player.initialize(document.querySelector('video'), url, true);
To play Microsoft Smooth Streaming content, additionally import the MSS module via thedashjs/msssubpath:
import { MediaPlayer } from 'dashjs';
import 'dashjs/mss';
samples/modulesTypescript and Webpack
You can also use dash.js in your Typescript or Webpack based JavaScript project. Multiple examples can be found in the
directory of the dash.js repository. A simple Typescript example is shown below. It also imports the
Smooth Streaming module that is not exported by default.
import * as dashjs from 'dashjs';
import '../node_modules/dashjs/dist/modern/esm/dash.mss.min.js';
let url = "https://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism/Manifest";
let player = dashjs.MediaPlayer().create();
player.initialize(document.querySelector('#myMainVideoPlayer'), url, true);
``
---