This guide walks through integration with HLS.js to collect video performance metrics with Mux data.
Features
Install mux-embed
Initialize Mux Data
Make your data actionable
Set or update metadata after initialization
Changing the video
Advanced options
Release notes
The following data can be collected by the Mux Data SDK when you use the HLS.js SDK, as described below.
Supported Features:
renditionchange
eventsAverage Bitrate Metrics available in v3.2.0 and newer.
Include the Mux JavaScript SDK on every page of your web app that includes video. You can use the Mux-hosted version of the script or install via npm. mux-embed
follows semantic versioning and the API will not change between major releases.
npm install --save mux-embed
Get your ENV_KEY
from the Mux environments dashboard.
Env Key is different than your API token
ENV_KEY
is a client-side key used for Mux Data monitoring. These are not to be confused with API tokens which are created in the admin settings dashboard and meant to access the Mux API from a trusted server.
import Hls from "hls.js";
import mux from "mux-embed";
const muxPlayerInitTime = mux.utils.now();
const videoEl = document.querySelector('#my-player');
if (Hls.isSupported()) {
let hls = new Hls();
// we're using a Mux HLS URL in this example, but the Mux Data integration
// with HLS.js works with any HLS url
hls.loadSource('https://stream.mux.com/yb2L3z3Z4IKQH02HYkf9xPToVYkOC85WA.m3u8');
hls.attachMedia(videoEl);
mux.monitor(videoEl, {
debug: false,
hlsjs: hls,
Hls: Hls,
data: {
env_key: 'ENV_KEY', // required
// Metadata fields
player_name: 'Main Player', // any arbitrary string you want to use to identify this player
player_init_time: window.muxPlayerInitTime // ex: 1451606400000
// ...
}
});
}
Call mux.monitor
and pass in a valid CSS selector or the video element itself. Followed by the SDK options and metadata. If you use a CSS selector that matches multiple elements, the first matching element in the document will be used.
In the SDK options, be sure to pass in the hlsjs
instance and the Hls
constructor. If the Hls
constructor is available on the global window
object then it can be omitted from the SDK options.
Alternatively, if your player does not immediately have access to the HLS.js player instance, you can start monitoring HLS.js at any time in the future. In order to do this, you can call either of the following:
mux.addHLSJS("#my-player", options)
// or
myVideoEl.mux.addHLSJS(options)
Log in to the Mux dashboard and find the environment that corresponds to your env_key
and look for video views. It takes about a minute or two from tracking a view for it to show up on the Metrics tab.
If you aren't seeing data, check to see if you have an ad blocker, tracking blocker or some kind of network firewall that prevents your player from sending requests to Mux Data servers.
The only required field in the options
that you pass into mux-embed
is env_key
. But without some metadata the metrics in your dashboard will lack the necessary information to take meaningful actions. Metadata allows you to search and filter on important fields in order to diagnose issues and optimize the playback experience for your end users.
Pass in metadata under the data
key when calling mux.monitor
.
mux.monitor('#my-player', {
debug: false,
hlsjs: hls,
Hls,
data: {
env_key: 'ENV_KEY', // required
// Site Metadata
viewer_user_id: '', // ex: '12345'
experiment_name: '', // ex: 'player_test_A'
sub_property_id: '', // ex: 'cus-1'
// Player Metadata
player_name: '', // ex: 'My Main Player'
player_version: '', // ex: '1.0.0'
player_init_time: '', // ex: 1451606400000
// Video Metadata
video_id: '', // ex: 'abcd123'
video_title: '', // ex: 'My Great Video'
video_series: '', // ex: 'Weekly Great Videos'
video_duration: '', // in milliseconds, ex: 120000
video_stream_type: '', // 'live' or 'on-demand'
video_cdn: '' // ex: 'Fastly', 'Akamai'
}
});
For more information, view Make your data actionable.
There are some cases where you may not have the full set of metadata until after the video playback has started. In this case, you should omit the values when you first call monitor
. Then, once you have the metadata, you can update the metadata with the updateData
method.
mux.updateData({ video_title: 'My Updated Great Video' });
There are two cases where the underlying tracking of the video view need to be reset:
Note: You do not need to change the video info when changing to a different source of the same video content (e.g. different resolution or video format).
If your application plays multiple videos back-to-back in the same video player, you need to signal when a new video starts to the Mux SDK. Examples of when this is needed are:
In order to signal the Mux SDK that a new view is starting, you will need to emit a videochange
event, along with metadata about the new video. See metadata in Make your data actionable for the full list of video details you can provide. You can include any metadata when changing the video but you should only need to update the values that start with video_
.
It's best to change the video info immediately after telling the player which new source to play.
mux.emit('#my-player', 'videochange', {
video_id: 'abc345',
video_title: 'My Other Great Video',
video_series: 'Weekly Great Videos',
...
});
In some cases, you may have the program change within a stream, and you may want to track each program as a view on its own. An example of this is a live stream that streams multiple programs back to back, with no interruptions.
In this case, you emit a programchange
event, including the updated metadata for the new program within the continuous stream. This will remove all previous video data and reset all metrics for the video view, creating a new video view. See Metadata for the list of video details you can provide. You can include any metadata when changing the video but you should only need to update the values that start with video
.
Note: The programchange
event is intended to be used only while the player is currently not paused. If you emit this event while the player is paused, the resulting view will not track video startup time correctly, and may also have incorrect watch time. Do not emit this event while the player is paused.
mux.emit('#my-player', 'programchange', {
video_id: 'abc345',
video_title: 'My Other Great Video',
video_series: 'Weekly Great Videos',
// ...
});
By default, Mux plugins for HTML5-based players use a cookie to track playback across subsequent page views in order to understand viewing sessions. This cookie includes information about the tracking of the viewer, such as an anonymized viewer ID that Mux generates for each user. None of this information is personally-identifiable, but you can disable the use of this cookie if desired. For instance, if your site or application is targeted towards children under 13, you should disable the use of cookies. For information about the specific data tracked in the cookie, please refer to: What information is stored in Mux Data HTML cookies.
This is done by setting disableCookies: true
in the options.
mux.monitor('#my-player', {
debug: false,
disableCookies: true,
hlsjs: hls,
Hls,
data: {
env_key: 'ENV_KEY',
// ... rest of metadata
}
}
By default, Mux plugins for HTML5-based players do not respect Do Not Track when set within browsers. This can be enabled in the options passed to Mux, via a setting named respectDoNotTrack
. The default for this is false
. If you would like to change this behavior, pass respectDoNotTrack: true
.
mux.monitor('#my-player', {
debug: false,
hlsjs: hls,
Hls,
respectDoNotTrack: true, // Disable tracking of browsers where Do Not Track is enabled
data: {
env_key: 'ENV_KEY',
// ... rest of metadata
}
}
Errors are fatal
Errors tracked by mux are considered fatal meaning that they are the result of playback failures. If errors are non-fatal they should not be captured.
By default, mux-embed
will track errors emitted from the video element as fatal errors. If a fatal error happens outside of the context of the player, you can emit a custom error to the mux monitor.
mux.emit('#my-player', 'error', {
player_error_code: 100,
player_error_message: 'Description of error',
player_error_context: 'Additional context for the error'
});
When triggering an error event, it is important to provide values for player_error_code
and player_error_message
. The player_error_message
should provide a generalized description of the error as it happened. The player_error_code
must be an integer, and should provide a category of the error. If the errors match up with the HTML Media Element Error, you can use the same codes as the corresponding HTML errors. However, for custom errors, you should choose a number greater than or equal to 100
.
In general you should not send a distinct code for each possible error message, but rather group similar errors under the same code. For instance, if your library has two different conditions for network errors, both should have the same player_error_code
but different messages.
The error message and code are combined together and aggregated with all errors that occur in your environment in order to find the most common errors that occur. To make error aggregation as useful as possible, these values should be general enough to provide useful information but not specific to each individual error (such as stack trace).
You can use player_error_context
to provide instance-specific information derived from the error such as stack trace or segment-ids where an error occurred. This value is not aggregated with other errors and can be used to provide detailed information. Note: Please do not include any personally identifiable information from the viewer in this data.
If your player emits error events that are not fatal to playback or the errors are unclear and/or do not have helpful information in the default error message and codes you might find it helpful to use an error translator or disable automatic error tracking all together.
function errorTranslator (error) {
return {
player_error_code: translateCode(error.player_error_code),
player_error_message: translateMessage(error.player_error_message),
player_error_context: translateContext(error.player_error_context)
};
}
mux.monitor('#my-player', {
debug: false,
errorTranslator,
hlsjs: hls,
Hls,
data: {
env_key: 'ENV_KEY', // required
// ... additional metadata
}
});
If you return false
from your errorTranslator
function then the error will not be tracked. Do this for non-fatal errors that you want to ignore. If your errorTranslator
function itself raises an error, then it will be silenced and the player's original error will be used.
In the case that you want full control over what errors are counted as fatal or not, you may want to consider turning off Mux's automatic error tracking completely. This can be done by passing automaticErrorTracking: false
in the configuration object.
mux.monitor('#my-player', {
debug: false,
automaticErrorTracking: false,
hlsjs: hls,
Hls,
data: {
env_key: 'ENV_KEY', // required
// ... additional metadata
}
mux-embed
now provides TypeScript type definitions with the published package! If you want to opt in, you can check out how here.
If you have integrated a custom domain for Data collection, specify your custom domain by setting beaconCollectionDomain
.
mux.monitor('#my-player', {
debug: false,
beaconCollectionDomain: 'CUSTOM_DOMAIN', // ex: 'foo.bar.com'
hlsjs: hls,
Hls,
data: {
env_key: 'ENV_KEY', // required
// ... additional metadata
}
});
updateData
globally and fix typeserrorTranslator
to work with player_error_severity
and player_error_business_exception
Target ES5 for bundles and validate bundles are ES5
fix an issue where seeking time before first play attempt counted towards video startup time
mux-embed
. The export types updates were required to ensure actual matches between the dist package and corresponding TypeScript types.mux-embed
). TypeScript types should not be applied unless they are explicitly referenced in app (discussed in docs updates).fix an issue causing certain network metrics to not be available for dashjs v4.x
fix an issue where certain IDs used may cause a DOM exception to be raised
beaconDomain
in favor of beaconCollectionDomain
. The beaconDomain
setting will continue to function, but integrations should change to beaconCollectionDomain
instead.Collect Request Id from the response headers, when available, for HLS.js (requestcompleted
and requestfailed
) and Dash.js (requestcompleted
). The following headers are collected: x-request-Id
, cf-ray
(Cloudflare), x-amz-cf-id
(CloudFront), x-akamai-request-id
(Akamai)
Fix an issue where tracking rebuffering can get into an infinite loop
Update Headers type
requestfailed
, and requestcancelled
events on Dash.js. Because of this change, you may see the number of playback failures increase as we now automatically track additional fatal errors.view_has_ad
errorTranslator
to work with player_error_context
renditionchange
fields to Shaka SDKrenditionchange
, error, DRM type, dropped frames, and new custom fieldsrenditionchange
info to Web SDKsupdate mux.utils.now
to use navigationStart
for timing reference
fix issue where views after videochange
might incorrectly accumulate rebuffering duration
Resolved issue sending beacons when view is ended
Record request_url
and request_id
with network events
visibilitychange
eventCollect the x-request-id
header from segment responses to make it easier to correlate client requests to other logs
Upgraded internal webpack version
Flush events on window visibilitychange
event
ImageBeacon
fallback, removing support for IE9view_id
's internallydisablePlayheadRebufferTracking
optiongetStartDate
does not always return a date objectSupport PDT and player_live_edge_program_time for Native Safari
Set a max payload size in mux-embed
Add option disablePlayheadRebufferTracking
to allow players to disable automatic rebuffering metrics.
Players can emit their own rebufferstart
or rebufferend
events and track rebuffering metrics.
Fix an issue with removing player_error_code
and player_error_message
when the error code is 1
.
Also stops emitting MEDIA_ERR_ABORTED
as errors.
Now leaving Player Software Version for HTML5 Video Element unset rather than "No Versions" as it is no longer needed.
io.litix.data.
x-litix-*
envKey
an optional parametermux
object (e.g. mux.events.PLAY
)visibilitychange
listener instead of unload
to handle destructuring the player monitor.video_source_is_live
for HLS.js monitorvideo_source_is_live
is not true
player_program_time
instead of calculating internallyvideo_id
when not specified by the developer.programchange
may not report metrics correctlyel.mux.destroy()
multiple times in a row raised an exceptionplayer_remote_played
wasn't functioning correctlydisableRebufferTracking
that defaults to false
.viewer_connection_type
detection.renditionchange
.mux.monitor
with Hls.js or Dash.js the source hostname was not being properly collected.