<SYSTEM>This is the developer documentation for Mux Data, not including how to integrate Mux Data with your player. For that, see integrations-data.txt.</SYSTEM>

# Understand metric definitions
Understand the playback metrics Mux uses to measure viewership and quality of experience.
Engagement and Quality of Experience metrics are tracked during each playback attempt and a value for each metric is assigned to the view that is generated. Metrics reports aggregate the values from individual, completed views that match a specified filter to calculate the metric for analysis.

Each report is defined by the metric being analyzed, the time range, and a filter that can focus the report on a specific subset of views. Engagement metrics use the start time of each view for determining if it should be included in a time range. Quality of Experience metrics are aggregated based on the end time of each view. Views are only included in a metric calculation if they have a valid value for the metric.

# Views and Watch Time

Views and Watch Time are key metrics that are often shown along with other engagement or Quality of Experience metrics in your Mux dashboard.

## Definition of a View

In Mux Data, a "View" is an attempt (successful or not) to play a video. A view is created when a viewer clicks play or if playback is started programmatically. If the user taps play, the video starts to load and fails, that counts as a single view. If a user taps play, starts watching the video, pauses, then resumes within 60 minutes, that counts as a single view.

Each view is tracked until playback is explicitly ended or 60 minutes after playback stops. Playback can be explicitly ended by the SDK or if a viewer navigates off the page with the video being played.

A single video can be watched multiple times in a single view by looping or seeking to the start of the video. If you see more views than expected in your dashboard or see duplicate views check on the code that initializes the Mux Data SDK to make sure you are initializing it once per playback attempt.

After a view stops receiving playback events for 60 seconds, it is considered complete and is available in Metrics and exported via streaming exports. Playback events will be added to views if playback resumes within 60 minutes. After 60 minutes of inactivity, the view is finalized and new playback events will create a new view.

## How Watch Time is calculated

Watch Time is not currently an aggregated metric in Mux Data but is used in some of the metrics calculations. The Watch Time for a view is the cumulative amount of time the user spent watching or attempting to watch the video. This metric includes actively playing content, starting up, rebuffering, and seeking. It is similar to Playing Time, which is an aggregated metric available for analysis, but Watch Time also includes the time spent rebuffering.

If user watches for 90 seconds, has 4 seconds of rebuffering, spends 2 seconds seeking by rewinding and then watches 60 more seconds that would total 156 seconds of watch time (90 + 4 + 2 + 60).

If a user watches a 2 minute video at 2x speed, Watch Time will be 1 minute (assuming no buffering, seeking, startup time). This is because Watch Time is measuring how much time has elapsed during playback, not how much video duration was watched.

# Metrics

Mux has engagement metrics to track viewership and five top-level metrics to measure quality of experience. Detailed definitions and formulas can be found on these metric guide pages:

* [Viewer Engagement](/docs/guides/data-engagement-metric)
* [Overall Viewer Experience](/docs/guides/data-overall-viewer-experience-metric)
* [Playback Success](/docs/guides/data-playback-success-metric)
* [Startup Time](/docs/guides/data-startup-time-metric)
* [Smoothness](/docs/guides/data-smoothness-metric)
* [Video Quality](/docs/guides/data-video-quality-metric)

These metrics are available in the Mux Data Dashboard and via the Mux Data API. They can be used by your team to track KPIs and optimize the viewing experience for your end users.

This is what the metrics look like on the Mux Data Dashboard:

<Image alt="Mux top 6 metrics" width={321} height={800} src="/docs/images/top-6-metrics.png" />

In order to get the most value out of the metrics measured by Mux, make sure your data is actionable by providing [valuable metadata for each view](/docs/guides/make-your-data-actionable-with-metadata). Use this in conjunction with filters to segment data metrics.


# Understand Monitoring Metrics and Dimensions
Learn about Mux Data's real-time monitoring metrics and dimensions to measure viewer engagement and streaming performance
Mux Data Monitoring offers near real-time to measure streaming performance and current viewers. Mux Data Monitoring metrics are available to Mux Data customers on a Media plan. Monitoring Metrics are offered at sub 20 second latency and are available for 24 hours.

## Monitoring Metrics

### Current Concurrent Viewers (CCV)

The number of viewers currently watching a video. This includes viewers currently waiting for the video to start playing, experiencing rebuffering, or who just experienced a playback failure. It does not include viewers that are paused or have been rebuffering more than five consecutive minutes.

The Monitoring Dashboard includes CCV by geography and Top Titles by CCV. Additional breakdown dimensions are available via API.

### Video startup failures by startup attempts

The number of viewers who have just experienced a video startup failure (an error that prevents the user from seeing the first frame of video, be it ads or content) as a percent of Start Attempts. Start Attempts is defined as the number of viewers who are in the video loading state or have just experienced a jump from the video loading state to video startup success, video startup failure, or exits before video starts.

### Playback Failures by CCV

The number of viewers who have just experienced a playback failure (a fatal error that prevents future playback) as a percent of Current Concurrent Viewers (CCV). Errors defined as non-fatal are not included in this metric.

The Playback Failures by CCV metric is measured differently from the Playback Failure Percentage in Mux QoE Metrics.

### Exits Before Video Start by Start Attempts

The number of viewers who have just abandoned a video view while waiting at least one second for the video to start playing, as a percent of Start Attempts. Examples where this may happen include closing the app/browser or clicking to a different video before playback begins.

The Exits Before Video Start by Start Attempts metric is measured differently from Exits Before Video Start in the Mux QoE Metrics.

### Current Rebuffering Percentage

Current Rebuffering Percentage measures the amount of time viewers spent in the rebuffering state, from the last time measured to the current time, as a percentage of the total watch time. The total watch time is the amount of time viewers spent watching or attempting to watch video, which includes startup time, rebuffering time, and time actually watching the video (it does not include paused, errored, or exited states).

Current Rebuffering Percentage is measured differently from the Rebuffer Percentage in the Mux QoE Metrics.

### Current Average Bitrate

The average of the video bitrates shown to viewers over the time period. The bitrate for a view is the value indicated in the video manifest for the rendition that is played for the viewer during the time period.

### Video Startup Time

Video Startup Time measures the current median startup time. This could be considered a "typical" startup time across viewers; half experience a faster startup time and half experience a slower startup time.

## Monitoring Dimensions

Current concurrent viewers, current rebuffering percentage, exits before video start, playback failure percentage, current average bitrate, video startup failure percentage can be filtered and broken down by the following dimensions.
| Dimension | Description |
|-----------|-------------|
| ASN | An autonomous system number (ASN) is a number assigned to a local network, registered into the carrier's routing community and placed under the umbrella of an administrative domain called an autonomous system. An ASN is often correlated with an ISP, though a single ISP may operate multiple ASNs. |
| CDN | The Content Delivery Network used to deliver the video. If using an SDK that supports CDN header extraction, this value will be auto-populated. |
| Operating System | Operating System (`iOS`, `Windows`, etc.) |
| Player Name | You can provide a name for the player (e.g. `My Player`) if you want to compare different configurations or types of players around your site or application. This is different from the player software (e.g. `Video.js`), which is tracked automatically by the SDK. |
| Region | A geographical subunit of a country. Examples include region, province, or state. |
| Stream Type | The type of video stream (e.g: `live` or `on-demand`) |
| Sub-property ID | A sub property is an optional way to group data within a property. For example, sub properties may be used by a video platform to group data by its own customers, or a media company might use them to distinguish between its many websites. |
| Video Series | The series of the video (e.g.: `Season 1 or Awesome Show`) |
| Video Title | Title of the video (e.g.: `Awesome Show: Pilot`) |
| View Has Ad | Tracks if an ad is present during a view. |
| Video ID | Your internal ID for the video |
| Mux Asset ID | Mux generated ID for Mux Video Assets |
| Mux Livestream ID | Mux generated ID for Mux Video Livestreams |
| Mux Playback ID | Mux generated Playback ID enabling streaming videos and live streams from Mux. An Asset or Livestream may have more than one Mux Playback Id. |


# Viewer Engagement
Engagement metrics help you track the success of your videos by measuring how many people are watching and for how long.
<Image alt="Viewer Engagement Dashboard" width={2092} height={1686} src="/docs/images/viewer-engagement-dashboard.png" />

```md
## Views
Views counts the total number of views that started during the selected time interval. This is calculated differently from the views in Quality of Experience metrics, which counts the total number of views that ended during the selected time interval.

## Unique Viewers
Unique Viewers counts distinct viewers based on the start time of the associated view, using Viewer ID to determine uniqueness. If a Viewer ID isn't provided, the default Viewer ID generated by the Mux SDK is used. We recommend setting a meaningful, anonymized Viewer ID to get an accurate Unique Viewer count. The Viewer ID should not use any value that contains personally identifiable information (such as email address, username, etc).

## Playing Time
Playing Time is the total time (in hours) that viewers watched playing video content or ads, and excludes rebuffering, seeking, and paused time.

## Content Playing Time

Content Playing Time measures the time that is spent on content playback, excluding ad playback. It specifically measures the time when the player's playhead is progressing.


## Ad Playing Time

Ad Playing Time measures the time that is spent on ad playback. It specifically measures the time when the player's playhead is progressing during ad playback.


## Ad Attempts

The Ad Attempts metric counts the number of times that an ad attempted to start playing. The number of Ad Attempts helps you understand how often you attempted to show ads to viewers.

Ad Attempts occur when each individual ad is attempted during the ad break. If a viewer ends the stream before all ads scheduled for the ad break have been attempted, the ads not yet attempted will not be included as Ad Attempts. Not every Ad Attempt will result in the ad being shown because an error can occur or a viewer could leave before the ad starts playing.


## Ad Impressions

The Ad Impressions metric counts the number of times that an ad successfully started playing. The number of Ad Impressions helps you understand how often you showed ads to viewers.

The ad playing is counted as an impression if it plays any frames, regardless of how long the ad plays.


## Ad Breaks

The Ad Breaks metric counts the number of times that ad breaks occurred. The number of Ad Breaks helps you understand how often you broke away from content in order to show ads to viewers.

Ad breaks can contain multiple ads attempts and impressions. An Ad break is counted if it occurs during playback, even if there were no ads actually shown to the viewer due to no ads being scheduled or ad errors that prevented ads from being displayed.

```


# Overall Viewer Experience
Overall Viewer Experience is a high-level score from 0 to 100 that measures the QoE (Quality of Experience).
<Image alt="Overall Viewer Experience dashboard" width={1204} height={940} src="/docs/images/overall-viewer-experience-dashboard.png" />

```md
## Overall Viewer Experience Score

Overall Viewer Experience Score is a metric that describes the overall Quality of Experience (QoE) of video streaming in a single number. A score of 100 means every viewer had a satisfying experience, and a score of 0 means that every viewer had a frustrating experience.

Overall Viewer Experience Score is based on four other Viewer Experience Scores, which each describe one of the four elements of video streaming performance: Playback Success, Startup Time, Playback Smoothness, and Video Quality.

Viewer Experience Scores are useful as a way to describe streaming performance from the perspective of an end viewer and not just from the perspective of system-level metrics. The Viewer Experience Scores are QoE (Quality of Experience) metrics, which describe the actual end-user experience of watching video. This is in contrast to QoS (Quality of Service) metrics, which describe a specific system’s performance without reference to user experience.

For example, "Downscaling Percentage" is a useful metric to track when it comes to QoS, since a high Downscaling Percentage means a service is delivering more video than necessary to fill the player or display. But users don't see downscaling, so Downscaling Percentage doesn't come into QoE when describing video quality. However Upscaling Percentage is a QoE metric because upscaling creates visual artifacts in the video, affecting the viewer’s experience.

Both QoE and QoS metrics are important for different purposes. QoS metrics are useful when troubleshooting specific system problems, while QoE metrics are useful when evaluating technologies or prioritizing problems to improve.

### Formula
Each individual video view is given an experience score of 0-100, and the Overall Viewer Experience Score is calculated by averaging the experience of all video views. Each view’s experience score is measured by first calculating a score between 0 and 100 for each of the elements of streaming performance (Playback Success, Smoothness, Startup Time, and Quality). Those scores are then averaged using higher weights for the more impactful elements of the viewer experience.

The weights are created by measuring the relative importance of each element of the experience. Mux conducted user surveys and research across millions of video views on the relative tradeoffs between increasing one metric at the expense of another. For example, you can increase Quality at the expense of Startup Time and vice versa. However, doing so would be a bad idea because Startup Time is more valuable than Quality. Generally, we found that Playback Success is the most important, followed by Smoothness then Startup Time, and finally Quality.

We want to make sure the Overall Score captures these complex relationships between metrics since developers may decide to make certain tradeoffs in order to improve their QoE scores. So, instead of just averaging the scores, Mux creates a set of tradeoff scores first and combines that into the overall score. The exception is Playback Success, which is a multiplier applied to the Overall Score.


The Overall Viewer Experience Score is defined as:
$$
Playback\ Success\ Score * \frac{T_{Sm, Q} + T_{Sm, Su} + T_{Su, Q}}{3}
$$
where
$$
T_{Sm, Q} = \text{Tradeoff(Smoothness, Quality)}
$$
$$
T_{Sm, Su} = \text{Tradeoff(Smoothness, Startup)}
$$
$$
T_{Su, Q} = \text{Tradeoff(Startup, Quality)}
$$

This way of combining the metrics is a more accurate representation of the viewer’s quality of experience, and increases the usefulness of Mux Data’s scoring system.

### Use this metric to:
* See trends in your overall viewer experience over time
* Begin prioritizing your efforts in areas of your platform (devices, regions, etc.) that have the lowest score and can use the most improvement

```


# Playback Success
Playback Success for a single view is a score of 0, 50, or 100 that measures if the user was able to successfully begin playback.
<Image alt="Playback Success" width={1189} height={881} src="/docs/images/playback-success-dashboard.png" />

````md
## Playback Success Score

Playback Success Score focuses on whether a video played back successfully.

Successful playback includes two components:

* Did the video play without an error?
* Did the user actually get to playback, or did they exit before playback started?

### Formula
**Playback Success Score** is fairly simple. A failure that ends playback is a `0`, while a video that plays through without failure is `100`. A view that is terminated by the viewer before playback starts (an “Exit Before Video Start,” or EBVS) is given a score of `50`.  EBVS views that occur in less than 1 second are given no score.

```
100: successful playback
50: exit before video start
0: playback failure
N/A: exit before video start <1 second
```

Why are EBVS views given a score of 50? The reason is that while exits can often point to streaming problems (e.g. the video took too long to load), some percentage of exits before video start are normal. A user might click the wrong video or see a link to a different video they want to watch more. If a view is abandoned in less than one second, we assume the video start was unintentional or programmatic and exclude those play attempts from the score.

### Use this metric to:
* Understand how playback failures impact the overall viewer experience
* Compare playback success performance to other areas of viewer experience
* Find areas where playback success can be optimized and improved


## Exits Before Video Start

Viewers will sometimes abandon a video (e.g. close the page/app or click the back button) because it is taking too long to load. The Exits Before Video Start Percentage metric captures how frequently this happens.

For this metric we count the number of video views where the viewer clicked play (or the player began to autoplay) but the video never began to play back (Video Startup Time was never recorded), excluding playback failures.  We then divide that number by the total number of video views.

### Use this metric to:
* Watch how changes in Video Startup Time directly impact viewers abandoning the video
* Compare players and understand if factors other than startup time may be causing viewers to leave, for example visual cues like loading indicators and poster frames.

Note: Viewers may leave for reasons other than long startup times, for example deciding that they clicked the wrong video or clicking on a related video. Before becoming concerned with your platform’s specific percentage you should attempt to improve your Video Startup Time and see how that impacts your Exits Before Video Start.


## Playback Failure Percentage

The Playback Failure Percentage metric gives the percentage of video views that failed to play due to a fatal error. Playback failures can happen at any point during video playback, causing the playback to end prematurely.

An error is considered a Playback Failure if it has a severity of fatal and it is not due to a business rule exception.

### Use this metric to:
* Understand where playback failures are happening most frequently
* Watch for spikes in playback failures due to new errors

Visit the Errors section to see which specific errors are happening the most frequently.


## Video Startup Failure Percentage

The Video Start Failure Percentage metric is the percentage of video views that experienced an error that prevents the user from seeing the first frame of video, which could be either ads or content.

An error is considered a Startup Failure if it occurs before the first frame of video is shown, has a severity of fatal, and the error is not due to a business rule exception.

### Use this metric to:
* Understand where video startup failures are happening most frequently
* Watch for spikes in startup failures due to new errors

Visit the Errors section to see which specific errors are happening the most frequently when start failures occur.

## Business Exception Percentage

  The Business Exception Percentage metric gives the percentage of video views that failed to play due to a fatal business rule exception.   Business Rule Exceptions can happen at any point during video playback, causing the playback to end prematurely.

  An error is considered a Business Rule Exception if it has a severity of fatal and it is specified as due to a business rule exception.


  ### Use this metric to:
  * Understand where playback failures are happening most frequently
  * Watch for spikes in playback failures due to new errors

  Visit the Errors section to see which specific business rule exceptions are happening the most frequently.
  

## Video Startup Business Exception Percentage

  The Video Startup Business Exception Percentage metric is the percentage of video views that experienced a business rule exception that   prevents the user from seeing the first frame of video, which could be either ads or content.

  An error is considered a Startup Business Rule Exception if it occurs before the first frame of video is shown,   has a severity of fatal, and the error is specified as due to a business rule exception.

  ### Use this metric to:
  * Understand where video startup business rule exceptions are happening most frequently
  * Watch for spikes in startup business rule exceptions due to new errors

  Visit the Errors section to see which specific errors are happening the most frequently when startup business rule exceptions occur.
  

## View Dropped Percentage

  Video views sometimes end for unknown reasons. This could be caused by a technical issue such as an   application or player crash, loss of session internet connection or browser behavior that   may restrict analytics. The View Dropped Percentage metric captures how frequently this happens.

  For this metric we count the number of video views where the viewing session ended without a clean   exit. We then divide that number by the total number of video views.

  ### Use this metric to:
  * Understand where dropped views are happening most frequently
  * Watch for spikes in dropped views due to player or application updates
  

## Ad Errors

The Ad Errors metric counts the number of times that ad errors occurred when trying to play. The number of Ad Errors helps you understand how often ads you attempt to run have problems being viewed.

An Ad Error is not necessarily a playback failure; errors often result in the ad playback ending or the ad being skipped before returning to video content playback.


## Ad Error Percentage

The Ad Error Percentage metric gives the percentage of ad attempts that failed during playback due to an ad error. Ad Errors can happen at any point during the ad playback, often causing the ad to end prematurely.

### Use this metric to:
* Understand how often errors are occurring when showing ads
* Watch for spikes in ad errors that can occur due a system issue


## Ad Breaks with Errors

The Ad Breaks with Errors metric counts the number of times that ad errors occurred during an ad break. The number of Ad Breaks with Errors helps you understand how often ad breaks have problems showing ads.

Some ad services will skip all remaining ads in the ad pod if an error happens and this metric can help you understand how often that occurs.


## Ad Breaks with Error Percentage

The Ad Breaks with Errors Percentage metric gives the percentage of ad breaks where an ad error occurred during the ad break.

### Use this metric to:
* Understand how often ad breaks have errors that occur when showing ads
* Understand if ad errors are more concentrated within a smaller number of ad breaks or spread out across ad breaks


## Ad Startup Error Percentage

The Ad Startup Error Percentage metric gives the percentage of time that an ad attempted to play but a failure occurred before the ad started and an ad impression was recorded.

### Use this metric to:
* Understand how often errors prevent ads from playing for viewers


## Ad Exits Before Start Percentage

The Ad Exits Before Start Percentage metric gives the percentage of time that an ad attempted to play but the user stopped or left the stream before the ad started playing.

The Ad Exit Before Start metric is intended to capture views where the user exits explicitly; ad start failures are not considered an exit in this metric because the user is not choosing to leave before the ad is shown when an error occurs.

### Use this metric to:
* Understand how often viewers stop the video stream before an ad started playing


## Ad Playback Failure Percentage

The Ad Playback Failure Percentage metric gives the percentage of video views that failed to play due to a fatal error during an ad break, causing ad playback to end prematurely.

An error is considered a Ad Playback Failure if it occurs during ad playback, has a severity of fatal, and is not due to a business rule exception.

### Use this metric to:
* Understand where ad playback failures are happening most frequently
* Watch for spikes in ad playback failures due to new errors

Visit the Errors section to see which specific errors are happening the most frequently.


## Content Playback Failure Percentage

The Content Playback Failure Percentage metric gives the percentage of video views that failed to play due to a fatal error during content playback. Content playback failures can happen any time during video playback outside of ad breaks, causing content playback to end prematurely.

An error is considered a Content Playback Failure if it occurs during content playback, has a severity of fatal and it is not due to a business rule exception.

### Use this metric to:
* Understand where content playback failures are happening most frequently
* Watch for spikes in content playback failures due to new errors

Visit the Errors section to see which specific errors are happening the most frequently.

````


# Startup Time
Startup Time is the time between when the user attempts to start playback and when they see the first frame of video.
<Image alt="Startup Time Dashboard" width={1204} height={993} src="/docs/images/startup-time-dashboard.png" />

```md
## Startup Time Score

Startup Time Score describes how happy or unhappy viewers are with startup time. Longer startup times mean lower scores, while shorter startup times mean higher scores. Once startup time reaches a certain point (around 8 seconds), we begin to decrease the rate of score decay since additional seconds of startup becomes less impactful for long startup times.

### Formula
$$
\frac{8}{8 + startup\_time\_in\_seconds} * 100
$$

This score decreases at a greater rate after 500ms of starting up.

Note that EBVS views do not receive a Startup Time Score.

Example values:
* 400 ms: 95
* 2 seconds: 80
* 8 seconds: 50
* 20 seconds: 29

### Use this metric to:
* Understand how problems with startup time impact the overall viewer experience
* Compare startup time performance to other areas of viewer experience
* Find areas where startup time can be optimized and improved


## Video Startup Time

Video Startup Time measures the time that the viewer waits for the video to play after the page is loaded and the player is ready. It specifically measures from when the player has been instructed to play the video, either directly by the user or via autoplay, to when the first frame of video is showing and the playhead is progressing. In the case that the player is configured with a pre-roll ad, Video Startup Time is the time until the first frame of the pre-roll ad is displayed.

Mux provides two percentiles of this metric:
* Median (50th Percentile) – Helps understand a typical experience (half are better than this number, half are worse)
* 95th Percentile – Helps understand what a poorer experience is like on your platform, while excluding outliers and happening frequently enough (1 in 20 views) to always be worth your attention.

Our data shows that viewers can be very impatient when waiting for a video to start, leaving in as little as two seconds for certain content types.

Network performance and initial rendition selection have the greatest impact on this number.

Preloading the video data before the viewer clicks play can also have a positive impact on this metric, however this should only be done when the video is the primary piece of content.


## Player Startup Time

Player Startup Time measures the time from when the player is first initialized in the page to when it is ready to receive further instructions.

While Player Startup Time is usually low, it can point to subtle difference in the operations of players. When combined with Page Load Time and Video Startup Time we can see Aggregate Startup Time, and understand the full amount of time a viewer waits on a video watch page.

To get Player Startup Time data, you must pass [`player_init_time`](https://docs.mux.com/guides/data/make-your-data-actionable-with-metadata) in your client integration.


## Page Load Time

Page load time measures the time from the initial user request for a page to the time when the video player is first initialized. Use this metric to understand the impact of new page resources (JavaScript files, CSS, etc.) on the viewer wait time. This can also be used to compare video players, and the size and loading speed of their files impacts the wait time.

Page Load Time is only recorded for the first video view on a page, so you may see a smaller number of total views for this metric.


## Aggregate Startup Time

Aggregate Startup Time combines Page Load Time, Player Startup Time, and Video Startup Time to show the total time a viewer waits for a video to play after requesting to watch the video on the previous screen or page.

On the web we often have web pages that are dedicated to individual videos. These pages are referred to as watch pages (e.g. `mydomain.com/watch?video=1234`). Viewers get to these pages by clicking on search results and lists of video thumbnails. In the case of watch pages we need to not only understand the Video Startup Time, but the full time that the viewer waited from when they clicked/tapped to watch the video.

Use this metric to optimize your watch pages for shorter wait times. For example, waiting to load secondary content (comments, related videos) until the video is playing, or choosing a video player that has the lowest impact on aggregate startup time.

This metric is only recorded for the first video view on a page, so you may see lower total view counts represented in this metric.


## Seek Latency

The Seek Latency metric measures the average amount of time that viewers wait for the video to start playing after seeking to a new time. Seek latency is calculating as the amount of time between the start and end of the seeking event or when the video is ready to resume playback. If a user pauses during the seek event, it will still measure the amount of time it took for the video to be ready to resume playback or the end of the seeked event.

Seeking is any time the player is asked to jump backward or forward to a new time in the video outside of normal playback. Aside from the viewer clicking on the progress bar, seeking can also happen programmatically, for example when jumping ahead to resume where the viewer previously stopped watching.

### Use this metric to:
* Look for cases of extreme seek startup times
* Compare video players and their ability to respond quickly to a viewer’s seek request


## Video Startup Preroll Request Time

The Video Startup Preroll Request Time measures the total amount of Video Startup Time that is spent making ad requests, waiting for the ad responses, and parsing the VAST/VMAP response. Specifically,
this measures the amount of time the viewer is waiting for the video to start in which the player does not yet know which ad to play, if any.

It is important to call out that this time _only_ includes time during video startup (i.e. after the user initiates playback, or auto-play does). Any time spent requesting ads before playback is initiated is not included in this metric.

### Use this metric to:
* Understand the performance of your ad server as it affects video startup time
* Attribute slow startup times to ad network performance, versus ad asset performance


## Video Startup Preroll Load Time

The Video Startup Preroll Load Time measures the total amount of Video Startup Time that is spent loading the first preroll ad asset. Specifically, this measures the amount of time the viewer is waiting for the first preroll ad to start playing after all ad responses have been received and parsed.

It is important to call out that this time _only_ includes time during video startup (i.e. after the user initiates playback, or auto-play does). Any time spent preloading the ad asset before playback is initiated is not included in this metric.

### Use this metric to:
* Understand the performance of your ad asset delivery as it affect video startup time
* Attribute slow startup times to ad asset performance, versus ad network performance


## Requests for First Preroll

This metric measures the number of ad requests that are made up to the point of preroll ad playback beginning. Depending on your ad architecture, it is possible that your player may make sequential ad requests in the case that the previous request returned no playable ad in order to fill all possible impressions. In this case, multiple ad requests can lead to slow startup times, which can potentially be improved by reducing the possible waterfall/fallback calls, ensuring playable ads being returned in the first request, or other means within your ad server.

### Use this metric to:
* Understand performance correlation with the number of ad requests made to retrieve the first playable ad


## Content Startup Time

Content Startup Time measures the time that the viewer waits for the video content (not including ads) to play after the page is loaded and the player is ready. It specifically measures from when the player has been instructed to play the video, either directly by the user or via autoplay, to when the first frame of playback content is showing and the playhead is progressing, which excludes the time spent loading and viewing preroll ads.

Mux provides two percentiles of this metric:
* Median (50th Percentile) – Helps understand a typical experience (half are better than this number, half are worse)
* 95th Percentile – Helps understand what a poorer experience is like on your platform, while excluding outliers and happening frequently enough (1 in 20 views) to always be worth your attention.


## Ad Preroll Startup Time

Ad Preroll Startup Time measures the time that the viewer waits for the preroll ad to load and start playing after the player is ready. It specifically measures from when the player has been instructed to play the preroll ad, either directly by the user or via autoplay, to when the first frame of ad content is showing and the playhead is progressing.

Mux provides two percentiles of this metric:
* Median (50th Percentile) – Helps understand a typical experience (half are better than this number, half are worse)
* 95th Percentile – Helps understand what a poorer experience is like on your platform, while excluding outliers and happening frequently enough (1 in 20 views) to always be worth your attention.

```


# Smoothness
Smoothness is a score based on the amount of rebuffering that happened during a view.
<Image alt="Smoothness Dashboard" width={1197} height={958} src="/docs/images/smoothness-dashboard.png" />

```md
## Smoothness Score

Smoothness Score measures the amount of rebuffering a viewer sees when watching video. A higher Smoothness Score means the viewer experiences less rebuffering, while a lower score means a viewer sees more rebuffering.

### Formula
Average of:
$$
\frac{1}{\sqrt{1 + \Big(\frac{\text{rebuffer\_count}}{2}\Big)^2}}*100
$$
and
$$
e^{-10 * rebuffer\_percentage} * 100
$$

Rebuffering can be measured as a combination of the number of rebuffering events and the rebuffering percentage. We measure rebuffering in both ways in order to account for views where rebuffering events are short but occur often. We consider multiple interruptions to be worse than a single interruption, even if total time spent rebuffering is the same. Averaging both measurements helps account for this, and provides a truer representation of the viewer experience.

Rebuffering time is measured as a percentage because a 5-second rebuffering duration is much more meaningful when viewing a 10-second clip versus a 2-hour movie. Our research shows that watch time rapidly decreases from just a single percentage point of rebuffering. We use an exponential curve to model this rapid initial decrease from rebuffering, which then slows down after a rebuffering percent of ~10%. This is because rebuffering is now high enough that incremental amounts of rebuffering becomes less impactful.

Rebuffering counts are slightly less negative under certain circumstances. For example, a single half-second rebuffering event during a long video might be barely noticeable. However, multiple rebuffering events do become noticeable and will rapidly decrease your score. This curve differs from the percentage curve in that a single rebuffering event is not harshly penalized. The score decreases rapidly in the 2 to 4 range, and then slows down past 5 rebuffering events due to the diminishing impact of additional events.

Both rebuffering percentage and rebuffering counts degrade the user experience, so we average the score. This helps balance for cases when either count or percentage is high while the other metric is low. A single rebuffering event of 1% isn’t great, but it’s better than 10 rebuffering events of 0.1%.

Note that EBVS views do not receive a Smoothness Score.

Examples:
* No rebuffering: 100
* 5 minute video with a single 5s rebuffer: average of 80 and 90 = 85
* 20 minute video with four 15s rebuffers: average of 44 and 60 = 54

### Use this metric to:
* Understand how problems with rebuffering impact the overall viewer experience
* Compare rebuffering performance to other areas of viewer experience
* Find areas where rebuffering can be optimized and improved


## Rebuffer Percentage

Rebuffer Percentage measures the volume of rebuffering that is occurring across the platform. Rebuffer Duration is the sum of the time viewers spend rebuffering on the platform or the given set of video views. Watch Time is the sum of all time viewers spend watching video, inclusive of rebuffering time. The Rebuffer Percentage then measures the rebuffer duration as a percentage of watch time.

Rebuffering occurs when the video stalls while a viewer is attempting to play through content, most often because it is taking more time to download (buffer) the content than it takes to play it. Stalls can also occur when a viewer attempts to seek to different times in the media, which is treated as a separate metric in Mux Data called Seek Latency.

### Use this metric to:
* Understand how much time viewers spend waiting for videos to rebuffer
* Optimize an adaptive algorithm or rebuffering strategy
* Compare players and CDNs


## Rebuffer Frequency

Rebuffer Frequency measures how often rebuffering events happen. It’s important to track this number because it can reveal issues of video stuttering, where the player is being too aggressive when restarting playback and has to frequently stop to rebuffer. This issue can be lost when measuring the rebuffering time, but can be just as frustrating as longer rebuffering events.

Rebuffering occurs when the video stalls while a viewer is attempting to play through content, most often because it is taking more time to download (buffer) the content than it takes to play it. Stalls can also occur when a viewer attempts to seek to different times in the media, which is treated as a separate metric in Mux Data called Seek Latency.

### Use this metric to:
* Understand how frequently viewers are interrupted by rebuffering
* Optimize an adaptive algorithm or rebuffering strategy
* Compare players and CDNs


## Rebuffer Duration

Rebuffer Duration is the amount of time in seconds that viewers wait for rebuffering per video view. Videos with longer durations have more opportunities for rebuffing events to occur and can make comparisons with shorter videos difficult, making Total Rebuffer Percentage the safer metric to optimize with. However Rebuffer Duration can be a useful metric for understanding the true viewer experience because it’s measured in seconds as opposed to a percentage.

Rebuffering occurs when the video stalls while a viewer is attempting to play through content, most often because it is taking more time to download (buffer) the content than it takes to play it. Stalls can also occur when a viewer attempts to seek to different times in the media, which is treated as a separate metric in Mux Data called Seek Latency.

### Use this metric to:
* Understand how long viewers wait for videos to rebuffer per video view
* Optimize an adaptive algorithm or rebuffering strategy


## Rebuffer Count

Rebuffer Count shows the number of rebuffering events that happen during video views. Compared to Total Rebuffer Frequency, Rebuffer Count can help you easily understand how many views are seeing more than zero rebuffering events.

Rebuffering occurs when the video stalls while a viewer is attempting to play through content, most often because it is taking more time to download (buffer) the content than it takes to play it. Stalls can also occur when a viewer attempts to seek to different times in the media, which is treated as a separate metric in Mux Data called Seek Latency.

### Use this metric to:
* Understand how often viewers are interrupted by rebuffering per video view
* Optimize an adaptive algorithm or rebuffering strategy


## Rendition Change Count

Rendition Change Count shows the total number of rendition changes (both upshifts and downshifts in video quality) that occurred during playback per video view. This metric helps you understand how frequently adaptive bitrate streaming is adjusting video quality levels or renditions in response to network conditions.

### Use this metric to:
* Understand how stable video quality is during playback
* Identify views with excessive quality fluctuations
* Evaluate the effectiveness of adaptive bitrate algorithms


## Rendition Upshift Count

Rendition Upshift Count shows the number of times video quality shifted upward to a higher quality rendition during playback. Upshifts indicate that network conditions improved enough to support higher bitrate video, providing viewers with better quality.

### Use this metric to:
* Understand how often viewers experience quality improvements
* Measure the responsiveness of adaptive bitrate algorithms to improved network conditions
* Identify scenarios where upshifts correlate with positive viewer engagement


## Rendition Downshift Count

Rendition Downshift Count shows the number of times video quality shifted downward to a lower quality rendition during playback. Downshifts typically occur when network conditions degrade, and the player adapts to prevent rebuffering by reducing video quality.

### Use this metric to:
* Understand how often viewers experience quality degradation
* Correlate downshifts with rebuffering events to evaluate adaptive bitrate effectiveness
* Identify network or CDN issues causing quality problems

```


# Video Quality
Video Quality compares the resolution of the video stream to the dimensions of the player.
<Image alt="Video Quality Dashboard" width={2394} height={1946} src="/docs/images/video-quality-dashboard.png" />

````md
## Video Quality Score

Video Quality Score measures the visual quality a user sees by comparing the resolution of a video stream to the resolution of the player in which it is played. If a video stream is significantly upscaled, quality generally suffers, and viewers have an unacceptable experience.

Note that video quality is notoriously difficult to quantify, especially in a reference-free way (without comparing a video to a pristine master). Bitrate doesn't work, since the same bitrate may look excellent on one video and terrible on another.

Several factors contribute to actual video quality: bitrate, codec, content type, and the quality of the original source. However, if content is encoded well and at the right bitrates, upscaling correlates reasonably well to video quality. We use a combination of average and max upscaling in order to account for extreme drops in quality, even when it only occurs for brief moments.

### Formula
$$
e^{-0.33 * (0.15 * U_{m} + 0.85 * U_{a})} * 100
$$
where
$$
U_{m} = \text{Max Upscale Percentage}
$$
$$
U_{a} = \text{Average Upscale Percentage}
$$

Video Quality Score is inversely related to the upscaling percentage for each view. 85% of the score is based on average upscaling, and 15% is based on max (peak) upscaling.

Note that EBVS views do not receive a Video Quality Score.

Examples:
* No upscaling: 100
* 50% upscaling throughout: 85
* 200% upscaling for the first 30 seconds, and no upscaling for the next 20 minutes: 92


### Use this metric to:
* Understand how video quality impacts the overall viewer experience
* Compare video quality to other areas of viewer experience
* Find areas where video quality can be optimized and improved


## Upscale Percentage

Upscaling is when the video player has to increase the size of the video to fill the player’s display. For example, if the video source is 320x240 and the player size 640x480, the player will stretch the video to fill the player dimensions. In that process the quality of the video degrades.

Upscale Percentage is measured as the change in one dimension, specifically the dimension that fits the player first when upscaling. In the 320x240 to 640x480 example, the Upscale Percentage would be 100%, calculated as (640-320) / 320.

However while the video plays the upscaling percentage may change if a new video rendition is selected or if the player goes to fullscreen. For this reason in the total Upscale Percentage metric we multiply each upscale percentage by the amount of time the video was upscaled. If the video was upscaled 100% for half of the video, and 0% for half of the video, the Total Upscale Percentage would be 50%.

### Use this metric to:
* Optimize the dimensions of videos to reduce poor quality due to stretching


## Downscale Percentage

Downscaling is the inverse of upscaling, measuring when the video source is too big for the player and has to be reduced in size to fit the display. In the process of shrinking the video pixels are
thrown out and essentially wasted. While this does not mean a reduction in video quality it does mean wasted bandwidth for you and the viewer, and significant occurrences of downscaling should be addressed.

### Use this metric to:
* Optimize the dimensions of videos for bandwidth and cost savings


## Max Upscale Percentage

While Upscale Percentage helps understand the volume of upscaling that’s occurring on your platform, the Max Upscale Percentage can help reveal points of significant upscaling, even if they don’t last the full video. It can also be more clear which video rendition may be the culprit as the percentage will exactly match the difference between a rendition and the player dimensions.

### Use this metric to:
* Optimize the dimensions of videos to reduce poor quality due to stretching


## Max Downscale Percentage

While Downscale Percentage helps understand the volume of downscaling that’s occurring on your platform, the Max Downscale Percentage can help reveal points of significant downscaling, even if they don’t last the full video. It can also be more clear which video rendition may be the culprit as the percentage will exactly match the difference between a rendition and the player dimensions.

### Use this metric to:
* Optimize the dimensions of videos for bandwidth and cost savings


## Weighted Average Bitrate

Weighted Average Bitrate is the time weighted average of the indicated bitrates that a viewer experiences during a video stream. The  weighted average is calculated from the amount of time spent at each bitrate while a video is played. The bitrate value is the indicated bitrate from the video manifest for the rendition that is used for each segment of playback.

For example, if during a view lasting 3 minutes a video plays for 1 minute at 1Mbps and 2 minutes at 2Mpbs, the Weighted Average Bitrate would be: [(1Mbps * 1min) + (2Mbps * 2min)] / 3min = 1.67Mbps

This metric only includes the bitrates (as indicated in the manifest) of video segments that are actually played. It does not include the segments that are downloaded but unplayed due to, for instance, an ABR algorithm that switches to a higher bitrate and discards previously downloaded lower bitrate segments in the cache.

### Use this metric to:
* Measure and optimize the visual quality of the videos that viewers experience


## Live Stream Latency

Live Stream Latency measures the time it takes from when a camera captures an action in real life to when viewers of a live stream see it happen on their screen. This metric allows you to quantify the amount of latency viewers experience and to identify viewers that may be encountering high latency which would impact their viewing experience.

This value is sometimes referred to as the glass-to-glass latency for a live stream but it is usually more accurately called ingest-to-render latency. The clock time for the live stream is determined using the time specified in `EXT-X-PROGRAM-DATE-TIME` tags embedded in the HLS manifest. The time specified in the HLS manifest is compared to the current UTC time, as specified by Mux Data servers.

Standard HLS streams usually have a latency of about 30 seconds, and Low Latency HLS (LL-HLS) streams target 5-10 seconds.

Note that if you are attempting to compare latency across different infrastructures or video platforms it is important to understand when during the video capture, ingest, or encoding the `EXT-X-PROGRAM-DATE-TIME` tags are inserted. For example, Mux Video inserts PDT tags when the video is ingested for streaming. Because of this behavior you should expect the latency measured for Mux Video streams to be around 1 second lower than the actual glass-to-glass latency. Some other platforms behave similarly and would also omit the time before ingest time from latency. For each streaming platform you use, you should assess at what point during the streaming pipeline the PDT tags get inserted so you know what is being measured and can take that into consideration.

We make an effort to only calculate the latency when the player is playing near the live edge segment, as identified by the `HOLD-BACK` and `PART-HOLD-BACK` tags in the playlist, if specified. Viewers playing more than 5 minutes behind the live edge will be excluded from the metric.

This metric will be calculated for any HLS or LL-HLS live stream that contains `EXT-X-PROGRAM-DATE-TIME` tags. For more information about `EXT-X-PROGRAM-DATE-TIME` tags please refer to the [HLS specification](https://datatracker.ietf.org/doc/html/draft-pantos-http-live-streaming#section-4.3.2.6).


## Request Throughput

Request Throughput measures the average throughput, in Mbps, for all media requests that were completed. Throughput is measured as the number of bits received per second from the time a request is initiated until it is completed.

Note that request metrics are only available for certain SDKs and playbacks. See our docs for more information.

### Formula
Each request has a total time loaded and a total bits downloaded. All of the bits loaded are added up and divided by the total request time (the sum of all time spent downloading those bits). If two requests are sent in parallel, they still have independent measurements which are added to the totals respectively.

```
    sum(bits downloaded) / sum(request time)
```

For example, if there were three requests that each downloaded 40 Megabits (5MB) in one 4 second request and two 2 second requests then the request throughput is 15 Mbps, `(40 + 40 + 40) / (4 + 2 + 2)`.

### Use this metric to:
* Compare the performance of multiple CDNs
* Troubleshoot throughput problems by CDN, ASN (ISP), or geography
* Understand the bandwidth of your users across different geographies, devices, etc.


## Request Latency

Request Latency measures the average time to first byte for media requests, that is the time from when the request is initiated to the time when the first byte of data is received from the server.

Note that request metrics are only available for certain SDKs and playbacks. See our docs for more information.

### Formula
```
    sum(time to first byte) / (number of requests)
```

For example, if the time for first byte for five requests was 100ms, 200ms, 100ms, 75ms, and 150ms, the Request Latency would be:
```
    (100 + 200 + 100 + 75 + 150) / 5 = 125
```

### Use this metric to:
* Compare the performance of multiple CDNs
* Troubleshoot latency problems by CDN, ASN (ISP), or geography


## Max Request Latency

Max Request Latency measures the maximum time to first byte for a media request, that is the maximum time an individual request took from the time it was initiated to the time when the first byte of data was received from the server.

Note that request metrics are only available for certain SDKs and playbacks. See our docs for more information.

### Formula
```
    max(time to first byte)
```

For example, if the time for first byte for five requests was 100ms, 200ms, 100ms, 75ms, and 150ms, the Max Request Latency would be 200ms.

### Use this metric to:
* Compare the performance of multiple CDNs
* Troubleshoot latency problems by CDN, ASN (ISP), or geography

````


# Make your dimensions actionable with metadata
Configure metadata with your SDK in order to populate dimensions to search, filter, and segment your video performance metrics.
One of Mux Data's core concepts are dimensions, which are the attributes of a video view that you can use to search, filter, and segment your video performance metrics.

While some of these dimensions are populated automatically, Mux Data allows you to provide details about the video and environment that either can't be detected automatically, can't be accessed if the video fails to load, or should be overridden.

Each dimension corresponds with a metadata key which can be used to set these values. While all metadata details except for `env_key` are optional, some may be necessary to calculate certain metrics and you'll see more helpful results as you include more.

## Dimension Details

#### Level

Each dimension is either considered `basic` or `advanced`. All dimensions are available for the standard retention period of 100 days. Long Term Metrics only support `basic` dimensions. If you are interested in Long Term Metrics, please reach out to [Mux Support](mailto:help@mux.com).

<Callout type="info">
  Long Term Metrics are available on **Mux Data Custom Media** plan. Long Term Metrics are available on Media Plans. Learn more about [Mux Data Plans](https://data.mux.com/pricing) or [contact support](https://mux.com/support).
</Callout>

#### Scoping

Many of Mux Data's dimensions are scoped to specific categories. Based on the category, they may have different behavior in terms of how these details are updated.

* Video details (prepended by `video_`) describe the current video that's playing and are all reset automatically when changing the video. This metadata might come from your internal CMS or video management system.
* Player details (prepended by `player_`) describe the player configuration that's being used and should be set whenever monitoring is started on a new player. They do not reset when the video is changed.
* All other details will persist until explicitly changed.

#### Type

There are three types of dimensions based on their availability.

**Tracking**: Enables tracking of additional metrics but are unavailable as dimensions.

**Limited**: Appear as attributes of a view on the individual view page as well as in the API.

**Full**: Can be used as filters and breakdowns in aggregate reports, in the video view page and in the API.

## High Priority Configurable Metadata

The following dimensions are the most important fields whose metadata keys you should populate in order to get the basic functionality from Mux Data.

<Callout type="info" title="Note about `viewer_user_id`">
  For `viewer_user_id` you should not use any value that is personally identifiable on its own (such as email address, username, etc.). Instead, you should supply an anonymized viewer ID which you have stored within your own system.
</Callout>

| Dimension Name | Key Name | Unit | Type | Level | Description |
|:-|:-|:-|:-:|:-:|:-|
| Environment | `env_key` | Unique ID | Required | N/A | Your env key from the Mux dashboard. This field ensures that your data goes into the correct environment. Note this was previously named `property_key` |
| Video ID | `video_id` | Text | Full | basic | Your internal ID for the video. Defaults to the Mux External ID if enabled for Assets and Livestreams hosted by Mux. |
| Video Title | `video_title` | Text | Full | basic | Title of the video being played (e.g.: `Awesome Show: Pilot`). Defaults to the Mux Video Title if enabled for Assets and Livestreams hosted by Mux. |
| Viewer ID | `viewer_user_id` | Unique ID | Full | adv | An ID representing the viewer who is watching the stream. Use this to look up video views for an individual viewer. If no value is specified, a unique ID will be generated by the SDK. Note: You should not use any value that is personally identifiable on its own (such as email address, username, etc.). Instead, you should supply an anonymized viewer ID which you have stored within your own system. |

## Optional Configurable Metadata

The following dimensions can be set manually using the metadata key name and will be reported by Mux Data. The key name provided below is the snake\_case used by Web SDKs. Keynames for iOS and Android SDK use camelCase and may differ in some cases. Review API documentation for API key names.

| Dimension Name | SDK Key Name | Unit | Type | Level | Description |
|:-|:-|:-|:-:|:-:|:-|
| Audio Codec | `video_audio_codec` | Text | Full | adv | The codec of the audio that played during the view. |
| CDN | `video_cdn` | Text | Full | basic | CDN delivering the video, either detected by Mux (via response `X-CDN` header) or specified in the view as `video_cdn`. Specifying a `video_cdn` value on the view does not override the detected value, if the `X-CDN` value is set on the segment response headers. |
| CDN Edge PoP | `view_cdn_edge_pop` | Text | Full | adv | Region where the CDN edge point of presence server is located or other origin server identification. |
| Content Type | `video_content_type` | Text | Full | basic | The type of content: e.g. `short`, `movie`, `episode`, `clip`, `trailer`, or `event` |
| Client Application Name | `view_client_application_name` | Text | Full | adv | Name of the customer application that the viewer is using to watch the content. e.g 'OurBrand iOS App'|
| Client Application Version | `view_client_application_version` | Text | Full | adv | Version of the customer application that the viewer is using to view the content. |
| DRM Type | `view_drm_type` | Text | Full | adv | The DRM SDK or service that is used for the video playback, such as `widevine` or `playready` |
| DRM Level | `view_drm_level` | Text | Full | adv | Security level of the specific DRM type. Some DRM types do not have levels. |
| Duration | `video_duration` | Milliseconds | Limited | (none) | The length of the video in milliseconds |
| Encoding Variant | `video_encoding_variant` | Text | Full | adv | Allows you to compare different encoders or encoding settings. This could designate the encoder used (e.g. `x264`, `hevc`, or `av1`), the preset used (e.g. `av1-0`, `av1-4`, or `av1-8`), or other properties of the encoding you want to track.  |
| Experiment Name | `experiment_name` | Text | Full | adv | You can use this field to separate views into different experiments, if you would like to filter by this dimension later. |
| Origin | `view_cdn_origin` | Text | Full | adv | Identifying name of the Content Origin or Region where the Origin server is located. |
| Page Type | `page_type` | Text | Full | adv | Provide the context of the page for more specific analysis. Values include `watchpage`, `iframe`, or leave empty. **`watchpage`** — A web page that is dedicated to playing a specific video (for example youtube.com/watch/ID or hulu.com/watch/ID) **iframe** — An iframe specifically used to embed a player on different sites/pages |
| Player Initialization Time | `player_init_time` | Milliseconds since Epoch | Tracking | N/A | If you are explicitly loading your player in page (perhaps as a response to a user interaction), include the timestamp (milliseconds since Jan 1 1970) when you initialize the player (or for HTML5 video, when right before you add the  element to the DOM) in order to accurately track page load time and player startup time. |
| Player Name | `player_name` | Text | Full | basic | You can provide a name for the player (e.g. `My Player`) if you want to compare different configurations or types of players around your site or application. This is different from the player software (e.g. `Video.js`), which is tracked automatically by the SDK. |
| Player Version | `player_version` | Text | Full | adv | As you make changes to your player you can compare how new versions of your player perform (e.g. `1.2.0`). This is not the player software version (e.g. `Video.js 5.0.0`), which is tracked automatically by the SDK. |
| Sub Property ID | `sub_property_id` | Text | Full | basic | A sub property is an optional way to group data within a property. For example, sub properties may be used by a video platform to group data by its own customers, or a media company might use them to distinguish between its many websites. |
| Time Shift Enabled | `view_time_shift_enabled` | Boolean | Full | adv | Boolean indicating if this view had timeshift enabled. |
| Used Captions | `player_captions_enabled` | Boolean | Full | adv | Boolean indicating if the player used captions at any time during the view. |
| Used PiP | `player_pip_enabled` | Boolean | Full | adv | Boolean indicating if the player used Picture in Picture at any time during the view. |
| Video Affiliate | `video_affiliate` | Text | Full | adv | Affiliate station that the viewer is watching or associated with the viewer. |
| Video Brand | `video_brand` | Text | Full | adv | Brand associated with the video or the brand of the streaming platform the viewer is using to watch the video. |
| Video Codec | `video_codec` | Text | Full | adv | The codec of the video that played during the view. |
| Video Dynamic Range Type | `video_dynamic_range_type` | Text | Full | adv | The format or type of dynamic range available on the video during the view. |
| Video Language | `video_language_code` | Text | Full | adv | The audio language of the video, assuming it's unchangeable after playing. |
| Video Producer | `video_producer` | Text | Full | adv | The producer of the video title |
| Video Series | `video_series` | Text | Full | basic | The series of the video (e.g.: `Season 1`) |
| Video Stream Type | `video_stream_type` | Text | Full | basic | The type of video stream (e.g: `live` or `on-demand`) |
| View Session ID | `view_session_id` | Unique ID | Full | adv | An ID that can be used to correlate the view with platform services upstream such as CDN or origin logs. |
| Video Variant Name | `video_variant_name` | Text | Full | adv | Allows you to monitor issues with the files of specific versions of the content, for example different audio translations or versions with hard-coded/burned-in subtitles. |
| Video Variant ID | `video_variant_id` | Text | Full | adv | Your internal ID for a video variant |
| Viewer Plan | `viewer_plan` | Text | Full | adv | Name of the viewer's customer-specific plan, product, or subscription. |
| Viewer Plan Status | `viewer_plan_status` | Text | Full | adv | Status pertaining to that viewer's subscription plan (e.g. subscriber, non-subscriber, SVOD, AVOD, free, standard, premium). |
| Viewer Plan Category | `viewer_plan_category` | Text | Full | adv | Category of the viewer's customer-specific subscription plan (e.g. bundle-type, subscription-campaign-id). |
| Custom Dimensions | `custom_1 - 10` | Text | Full | adv | Customer-defined metadata |

## Overridable Metadata

The following dimensions are populated automatically where the data is supported by the SDK. This data can be overridden by the SDK client implementation using the metadata key name, if needed.

| Dimension Name | Key Name | Unit | Type | Level | Description |
|:-|:-|:-|:-:|:-:|:-|
| Autoplay | `player_autoplay_on` | Boolean | Full | adv | Indicates whether the player was set to autoplay the video or not. This tracks whether the video has `autoplay=true` set; it is not always able to tell if the browser disregarded the setting, otherwise prevented the video from playing, or if the video play was triggered via a script. |
| Browser | `viewer_application_name` | Text | Full | basic | Browser used for the video view (`Safari`, `Chrome`, etc.). On Android and iOS  applications this defaults to the bundle identifier. |
| Browser Version | `viewer_application_version` | Version | Full | adv | Browser version (e.g. `66.0.3359.158`). On Android and iOS applications this defaults to the bundle version. |
| Connection Type | `viewer_connection_type` | Text | Full | adv | Type of network infrastructure available to the player: `wifi`, `cellular`, `wired`, `other`; or `no_connection` if none are available; or `unknown` if there is infrastructure available but its type cannot be determined. |
| Device Brand | `viewer_device_manufacturer` | Text | Full | basic | Device Manufacturer (e.g. `Apple`, `Microsoft`, etc.) |
| Device Category | `viewer_device_category` | Text | Full | basic | The form factor of the device: `camera`, `car browser`, `console`, `desktop`, `feature phone`, `peripheral`, `phone`, `portable media player`, `smart display`, `smart speaker`, `tablet`, `tv`, `wearable` |
| Device Model | `viewer_device_model` | Text | Full | adv | Device Model (e.g. `iPhone11,2`) |
| Device Name | `viewer_device_name` | Text | Full | basic | Device Name (e.g. `iPhone 12`) |
| Error Code | `player_error_code` | Text | Full | adv | Error code encountered by the player during playback. |
| Operating System | `viewer_os_family` | Text | Full | basic | Operating System (`iOS`, `Windows`, etc.) |
| Operating System Version | `viewer_os_version` | Version | Full | adv | Operating System version (e.g. `10.6`) |
| Page URL | `page_url` | URL | Limited | adv | Page URL |
| Player Height | `player_height` | Integer | Limited | adv | Height of the player as displayed, in logical pixels |
| Player Instance ID | `player_instance_id` | Unique ID | Limited | (none) | Identifies the instance of the Player class that is created when a video is initialized |
| Player Language | `player_language_code` | Text | Full | adv | Player's text language |
| Player Poster | `player_poster` | URL | Limited | (none) | The image shown as the pre-visualization before play |
| Player Software | `player_software_name` | Text | Full | basic | Player Software being used to play the Video (e.g. `Video.js`, `JW Player`, etc.). Note this was previously named `player_software` |
| Player Software Version | `player_software_version` | Text | Full | adv | Player Software Version (e.g. `2.45.5`) |
| Player Width | `player_width` | Integer | Limited | (none) | Width of the player as displayed, in logical pixels |
| Preload | `player_preload_on` | Boolean | Full | adv | Specifies if the player was configured to load the video when the page loads. |
| Remote Played | `player_remote_played` | Boolean | Full | adv | If the video is remote played to AirPlay as specified by the SDK. |
| Source Height | `player_source_height` | Integer | Limited | adv | Height of the source video being sent to the player, in pixels |
| Source Width | `player_source_width` | Integer | Limited | (none) | Width of the source video being as seen by the player |
| Source Type | `video_source_mime_type` | Text | Full | basic | Format of the source, as determined by the player. E.g. `application/dash+xml`, `x-application/mpegUrl`, `mp4`, etc. |
| Used Full Screen | `player_is_fullscreen` | Boolean | Limited | adv | Indicates whether the viewer used full screen to watch the video. |
| Video Creator ID | `video_creator_id` | Text | Full | adv | A unique identifier for the creator of the video. Defaults to the Mux Creator ID if enabled for Assets and Livestreams hosted by Mux. |

## Internal Metadata

The following dimensions are populated automatically by the SDK, and cannot be overriden by the SDK client implementation.

| Dimension Name | Unit | Type | Level | Description |
|:-|:-|:-:|:-:|:-|
| ASN | Boolean | Full | adv | The Autonomous System Number (ASN) representing the network provider of the viewer. |
| Audio Codec Initial | Text | Full | adv | Initial codec of the audio that played. Derived from the first value of the audio\_codec dimension. |
| CDN Trace | Sequence | Full | adv | Populates all values of video\_cdn field over the course of a view. |
| Continent Code | Text | Full | basic | The continent from which the video was accessed, represented as a code. |
| Country | Text | Full | adv | The country from which the video was accessed, represented as a country code. |
| Exited Before Video Start | Boolean | Full | basic | Indicates whether the viewer exited before the video started playing. |
| Mux Asset ID | Unique ID | Full | basic | A unique identifier for the video asset being played. |
| Mux Live Stream ID | Unique ID | Full | basic | The unique identifier of the live stream being played. |
| Mux Playback ID | Text | Full | basic | A unique identifier for the video view. |
| Mux Plugin | Text | Full | adv | The name of the Mux plugin used by the video player. |
| Mux Plugin Version | Text | Full | adv | The version of the Mux plugin used by the video player. |
| Playback Business Exception | Boolean | Full | adv | Indicates whether a business rule-related issue caused playback failure. |
| Playback Failure | Boolean | Full | adv | Indicates whether the playback failed for any reason. |
| Region | Text | Full | adv | The specific region or state where the video was accessed. |
| Source Bitrate | Integer | Limited | adv | Bitrate of the source video in bps. |
| Source Bitrate Initial | Integer | Limited | adv | Initial bitrate of the source video in bps. Derived from the first value of the player\_source\_bitrate dimension. |
| Source Framerate | Number | Limited | adv | Framerate of the source video. |
| Source Framerate Initial | Number | Limited | adv | Initial framerate of the source video. Derived from the first value of the player\_source\_fps dimension. |
| Source Height | Integer | Limited | adv | The height (in pixels) of the source currently loaded in the player, regardless of the size of the player. |
| Source Height Initial | Integer | Limited | adv | Initial height (in pixels) of the source currently loaded in the player, regardless of the size of the player. Derived from the first value of the player\_source\_height dimension. |
| Source Hostname | Text | Full | adv | The hostname of the video source, such as the CDN or media server. |
| Source Width | Integer | Limited | adv | The width (in pixels) of the source currently loaded in the player, regardless of the size of the player. |
| Source Width Initial | Integer | Limited | adv | Initial width (in pixels) of the source currently loaded in the player, regardless of the size of the player. Derived from the first value of the player\_source\_width dimension. |
| Video Codec Initial | Text | Full | adv | Initial codec of the video that played. Derived from the first value of the video\_codec dimension. |
| Video Dynamic Range Type Initial | Text | Full | adv | Initial format or type of dynamic range available on the video that played. Derived from the first value of the video\_dynamic\_range\_type dimension. |
| Video Startup Failure | Boolean | Full | basic | Indicates whether the video failed to start due to an error. |
| Video Startup Business Exception | Boolean | Full | adv | Indicates whether a business rule-related issue caused a video startup failure. |
| View Dropped | Boolean | Full | adv | Boolean indicating whether the view was finalized without an explicit viewend event. |
| View Has Ad | Boolean | Full | basic | Indicates whether the video view included an ad. |

## Set Metadata with Session Data

Metadata is normally set using code in the SDK configuration. However, some video metadata can also be set using Session Data key/value pairs in the HLS manifest. This method makes it easier to communicate values to the Mux player SDK without having to side-channel information to the client or change client-side code in order to configure metadata for a view.

Some common use cases where this is helpful are, for example, setting a view session id that comes from a backend system which can be used to associate a playback view with the requests that were made to a CDN or being able to easily capture which experiments a viewer is participating in without having to communicate that to the player.

HLS Session Data, which is represented in an HLS master playlist using the `EXT-X-SESSION-DATA` tag, is a key/value pair that can be read by the player. When the master playlist is loaded into a video player integrated with a Mux Data SDK that supports extracting Session Data, the Session Data keys that use the `io.litix.data` prefix will be included in the Mux Data view as dimension metadata the same as if you had configured the value from the SDK configuration code.

<Callout type="info" title="Note about HLS Session Data for developers using Mux Video:">
  This feature is intended for developers using their own custom video delivery pipeline. HLS Session Data is set by Mux Video when videos are viewed; injecting your own HLS Session Data into Mux Video content is not currently supported.
</Callout>

The Session Data tags are interpreted as follows from the master playlist:

```text
Tag: #EXT-X-SESSION-DATA
Key: DATA-ID="io.litix.data.[dimension_name]"
Value: VALUE="dimension value"
```

The dimension names available to be set from the master playlist:

* `video_*`
* `custom_*`
* `experiment_name`
* `view_session_id`
* `viewer_user_id`

The following is an example of Session Data tags in a master playlist:

```text
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-INDEPENDENT-SEGMENTS

#EXT-X-SESSION-DATA:DATA-ID="io.litix.data.experiment_name",VALUE="abr_test:true"
#EXT-X-SESSION-DATA:DATA-ID="io.litix.data.view_session_id",VALUE="12345ABCD"

#EXT-X-STREAM-INF:BANDWIDTH=2516370,AVERAGE-BANDWIDTH=2516370,CODECS="mp4a.40.2,avc1.640020",RESOLUTION=1280x720
...
```

The Session Data tags contained in a master playlist would result in the `Experiment Name` dimension set to `abr_test:true` and `View Session ID` dimension set to `12345ABCD`.

<Callout type="info">
  We're aware of a crash that may occur in [AVPlayer Data SDK](https://github.com/muxinc/mux-stats-sdk-avplayer) versions 2.12.0 - 3.5.0 when processing HLS Session Data that is prefixed with `io.litix.data`. AVPlayer Data SDK integrations that process HLS Session Data not prefixed with `io.litix.data` are not affected. Custom integrations that use the [Objective-C MuxCore SDK](https://github.com/muxinc/stats-sdk-objc) and do not depend on the AVPlayer Data SDK are not affected.
</Callout>

## iOS/Android Metadata

In iOS and Android SDKs, names are converted to lowerCamelCase `setters` and `getters`. For example, to set the Video Stream Type field in iOS or Android, use `videoStreamType` instead of `video_stream_type`.

In the Objective-C SDKs, options are provided via the `MUXSDKCustomerPlayerData`, `MUXSDKCustomerVideoData`, `MUXSDKCustomerViewData`, and `MUXSDKCustomData` objects. See the header directories in MuxCore.xcframework from the [latest release](https://github.com/muxinc/stats-sdk-objc/releases/latest) for a complete list of names.

In the Java SDK, options are provided via the `CustomerPlayerData`, `CustomerVideoData`, and `CustomData` objects. Use your IDE to inspect these objects' API.


# Extend Data with custom metadata
Configure your SDKs to track and report on custom metadata for views in Mux Data.
<Callout type="info">
  Limits

  The number of custom dimensions you can track depends on your plan. See the [pricing page](https://mux.com/pricing/data) for details.
</Callout>

## 1. What are Custom Dimensions?

There are many metadata dimensions that can be used to track information about video views such as Video Title, Video Series, or Encoding Variant. You can find the whole list on the [guide to making your data actionable](/docs/guides/make-your-data-actionable-with-metadata). Custom Dimensions allow you to define, submit, and report on metadata necessary to support your use case that are not in the pre-defined collection of metadata dimensions in Mux Data. Examples could include metadata such as the device firmware version or a subscription plan type.

Each custom dimension can have a display name and an assigned category. They also have a pre-defined field name, such as `custom_1`, that is used to refer to the dimension in code when submitting a value to track as part of a view. You'll use these field names when sending these values via an SDK integration or accessing a dimension value using the Mux Data API.

## 2. Configuring Custom Dimensions

The Custom Dimensions configuration is available from the Settings page and selecting the "Custom Dimensions" tab. You will see the list of the dimensions that are available for reporting.

<Image sm src="/docs/images/custom-dimensions-tab.png" width={1211} height={767} />

To enable a dimension, click the switch next to the left of the dimension name. To disable a dimension, simply click the switch off. The custom dimension data will continue to be collected from the SDKs but it will not be available to users for reporting in the Mux Dashboard.

<Image sm src="/docs/images/custom-dimensions-enable.png" width={1120} height={453} />

To edit a dimension, click the edit pencil to the right of the row. You can set the display name of the dimension to match your preferred definition and assign the most appropriate category. By default, Custom Dimensions are included in the Custom category but they can added to any existing dimension category.

The name and category of the dimension are used wherever dimensions are displayed, such as the Metrics Breakdown page, the View detail page, the Filter model, or the dimensions list on the Compare page.

<Image src="/docs/images/edit-custom-dimension.png" width={545} height={233} alt="Mux Data edit custom dimension" />

## 3. Reporting on Custom Dimensions

Once configured to be visible, Custom Dimensions are available to report on in the same method as pre-defined dimensions. The dimensions are available for filtering, aggregation, and comparison from the Metrics Breakdown screen in the category that was assigned for each visible dimension.

The Custom Dimension values are also available in the export files using the pre-defined field name (i.e. `custom_1`).

<Image sm src="/docs/images/custom-dimensions-breakdown.png" width={839} height={684} />

## 4. Submitting Custom Metadata from Mux Data SDKs

Custom Dimension data is configured in the Mux Data SDKs in a similar method to other view metadata.

Metadata is submitted to the SDKs using the pre-defined field name assigned to the dimension you have configured. For example, if you configured the `custom_1` dimension to have the display name "Secondary User Id," you submit that secondary user id value using the `custom_1` or `CustomData1` metadata field, depending on the platform.

<Callout type="info">
  Make sure you are using an up-to-date version of each Mux Data SDK to enable support for submitting Custom Dimensions.
</Callout>

### HTML5 Video Element and other web SDKs

In web-based SDKs, Custom Dimensions are set in the same `data` object as the other view metadata fields.

```js
mux.monitor('#test_video', {
  data: {
    // Set other view data
    video_title: 'Big Buck Bunny',
    player_init_time: playerInitTime,
    env_key: 'YOUR_ENVIRONMENT_KEY_HERE',

    // Set custom dimension data
    custom_1: 'My Custom Dimension Value'    // Set the custom value here
  }
});
```

For more guidance on using and configuring web-based SDKs, please refer to the guide on [monitoring the HTML5 video element](/docs/guides/monitor-html5-video-element).

Version 4.1.0 or later of the HTML5 Video Element monitor is necessary to support Custom Dimensions.

### ExoPlayer

In Android-based SDKs, Custom Dimensions are set in the `CustomData` object and attached to the `CustomerData` object that is used to initialize the Mux Data SDK.

```java
// Set other view data
CustomerPlayerData customerPlayerData = new CustomerPlayerData();
customerPlayerData.setEnvironmentKey("YOUR_ENVIRONMENT_KEY_HERE");
CustomerVideoData customerVideoData = new CustomerVideoData();
customerVideoData.setVideoTitle("Big Buck Bunny");

// Set custom dimension data
CustomData customData = new CustomData();
customData.setCustomData1("MY_CUSTOM_DIMENSION_VALUE");  // Set the custom value here
CustomerData customerData = new CustomerData(customerPlayerData, customerVideoData, null);
customerData.setCustomData(customData);

muxStats = new MuxStatsExoPlayer(this, player, "demo-player", customerData);
```

An example integration that includes Custom Dimensions can be found in the demo application for [muxinc/mux-stats-sdk-exoplayer](https://github.com/muxinc/mux-stats-sdk-exoplayer) which integrates Mux into an ExoPlayer demo application.

For more guidance on using and configuring Android SDKs, please refer to the guide on [monitoring ExoPlayer](/docs/guides/monitor-exoplayer).

Version 2.5.0 or later of the ExoPlayer monitor is necessary to support Custom Dimensions.

### AVPlayer

In Apple-based SDKs, Custom Dimensions are set in the `MUXSDKCustomData` object and attached to the `MUXSDKCustomerData` object that is used to initialize the Mux Data SDK.

```swift
// Set custom dimension data
let customData = MUXSDKCustomData()
customData.customData1 = "my-custom-dimension-value"

// Set other view data
guard let playerData = MUXSDKCustomerPlayerData(environmentKey: "YOUR_ENVIRONMENT_KEY_HERE") else {
    return
}

let videoData = MUXSDKCustomerVideoData()
videoData.videoTitle = "Big Buck Bunny"

let viewData = MUXSDKCustomerViewData()
viewData.viewClientApplicationName = "my-app-name"

guard let customerData = MUXSDKCustomerData(
    customerPlayerData: playerData,
    videoData: videoData,
    viewData: viewData,
    customData: customData
) else {
    return
}

let playerBinding = MUXSDKStats.monitorAVPlayerViewController(
    playerViewController,
    withPlayerName: "demo-player",
    customerData: customerData
)
```

An example integration that includes Custom Dimensions can be found in the demo application for [muxinc/mux-stats-sdk-avplayer](https://github.com/muxinc/mux-stats-sdk-avplayer/tree/master/Examples/MUXSDKStatsExampleSPM) which integrates Mux into an AVPlayer demo application.

For more guidance on using and configuring iOS SDKs, please refer to the guide on [monitoring AVPlayer](/docs/guides/monitor-avplayer).

Version 2.5.0 or later of the AVPlayer monitor is necessary to support Custom Dimensions.

### Roku

In the Roku SDK, Custom Dimensions are set in the same `muxConfig` object as the other view metadata fields.

```js
m.mux = m.top.CreateNode("mux")
m.mux.setField("video", m.video)

muxConfig = {
  property_key: "YOUR_ENVIRONMENT_KEY_HERE",
  ' Set the custom dimension data
  custom_1: "my-custom-dimension-value"
}

m.mux.setField("config", muxConfig)
m.mux.control = "RUN"

' Load the video into the Video node
```

For more guidance on using and configuring the Roku SDK, please refer to the guide on [monitoring Roku](/docs/guides/monitor-roku).

Version 1.1.0 or later of the Roku monitor is necessary to support Custom Dimensions.


# Build a custom dashboard
Create custom dashboards in Mux Data to visualize and track the metrics that matter most to your video performance. Custom dashboards allow you to combine multiple metrics, apply filters, and organize data in a way that best serves your monitoring and analysis needs.
## What are Custom Dashboards?

Custom dashboards provide a centralized view of your video performance data through configurable components. You can create dashboards with multiple visualization types, apply filters, and customize time periods to focus on specific aspects of your video performance.

### Key features:

* Four component types: Timeseries, Bar charts, Lists, and Metric numbers
* 10 components per Dashboard
* Dashboard and component-level filtering
* Flexible time period selection
* Comparison intervals
* Dashboard sharing and duplication

<Callout type="info">
  Custom Dashboards are available on **Mux Data Media** plans. Learn more about [Mux Data Plans](https://data.mux.com/pricing) or [contact support](https://mux.com/support).
</Callout>

## Creating a Dashboard

To create a new custom dashboard:

1. Navigate to the **Dashboards** section in Mux Data
2. Select **Create Dashboard** from the left menu or main window
3. Enter a descriptive name for your dashboard
4. Select **Create Dashboard**

Your new dashboard will be created and ready for customization with components and filters.

<MultiImage
  images={[
  { src: "/docs/images/build-a-custom-dashboard-A.png", width: 3935, height: 2018, alt: "Dashboard screen in the Mux Video application showing an empty custom dashboards view with a “Create Dashboard” button." },
  { src: "/docs/images/build-a-custom-dashboard-B.png", width: 1464, height: 794, alt: "Dialog titled “Create Dashboard” in Mux, with a text input field filled in as “My first Dashboard” and two buttons at the bottom: “Cancel” and “Create Dashboard.”" },
]}
/>

## Dashboard Configuration

### Time Periods

Configure the time period for your entire dashboard to focus on specific date ranges:

* **Default**: Last 24 hours
* **Relative periods**: Choose from predefined options like last 7 days or last 30 days
* **Specific periods**: Set exact start and end dates for consistent historical analysis

Time period changes apply to all dashboard components. Save your dashboard to preserve time period settings.

<Callout type="info">
  Custom Dashboards are currently only available for the standard 100 days of data. Long-term Metrics are not yet available with Custom Dashboards.
</Callout>

### Dashboard Filters

Dashboard filters apply to all components within the dashboard providing consistent data filtering across visualizations.

#### Dimension Filters

Filter by dimension values such as country, operating system, or player version:

1. Select the **Filter Dimensions** button
2. Search for and select the dimension type
3. Choose specific values to include or exclude
4. Multiple values use OR logic (e.g., selecting iOS and Android shows views from either platform)

<Image alt="New Dashboard creation screen in Mux, showing a dimension filter panel with “Device Model” selected. Viewer Device Model is filtered to “iPhone,” and view counts for different iPhone models are listed." src="/docs/images/build-a-custom-dashboard-C.png" width={2420} height={1164} />

#### Metric Filters

Filter by metric values to focus on specific performance thresholds:

1. Select the **Filter Metrics** button
2. Choose a metric (e.g., rebuffering percentage)
3. Select an operator (≤, ≥, =, etc.)
4. Set the value threshold

<Image alt="Metrics filter interface in Mux dashboard builder, showing a filter applied to only include results where Rebuffer Percentage is greater than 5%." src="/docs/images/build-a-custom-dashboard-D.png" width={1264} height={500} caption="Example: Filter for views with rebuffering percentage ≤ 5% to focus on high-quality playback experiences." />

Filter changes can be previewed without saving. Click **Save** at the bottom of the dashboard to apply filters permanently.

### Component Filters

Components can have their own filters in addition to Dashboard filters. Dashboard filters act as parent filters affecting all components. Component level filters are additive to Dashboard filters but only apply to the component.

<Callout type="info">
  If dashboard and component filters conflict, the component may show no data. Ensure filter combinations are logical and compatible.
</Callout>

## Dashboard Components

Components visualize individual metrics within your dashboard. Each component type serves different analytical purposes and can be customized with specific filters and options.

1. To add a Dashboard component to a new Dashboard, select the Create Component button.
2. To add a Dashboard component to an existing Dashboard, select the Edit Icon next to the date selector.

### Metric Numbers

Display key performance indicators in a prominent metrics bar at the top of your dashboard. Up to 5 Metric numbers can be added per dashboard. Metric numbers (up to 5) collectively count as 1 component.

<Image alt="A dashboard titled “Platform Player Key Metrics” displaying metrics for the last 24 hours, including Views, Unique Viewers, Video Startup Failure Percentage, Playback Failure Percentage, and Rebuffer Percentage." src="/docs/images/build-a-custom-dashboard-E.png" width={1999} height={554} />

#### Configuration:

1. Select **Metric Number** as the component type
2. Choose the metric to display
3. Provide a descriptive name (50 character limit)
4. **Optional**: Add a comparison time period to show rate of change
5. **Optional**: Apply component-specific dimension or metric filters

<Callout type="info">
  Metric number components appear in creation order and cannot be reordered.
</Callout>

### Timeseries

<Image alt="Line graph showing “Video Startup Time” over a 24-hour period in Mux, comparing performance for “Last 24 hours” (orange line) versus “One day ago” (purple dashed line)." src="/docs/images/build-a-custom-dashboard-F.png" width={1134} height={832} />

Track metrics over time to identify trends, patterns, and anomalies in your video performance.

#### Configuration:

1. Select **Timeseries** as the component type
2. Choose the metric to chart over time
3. Set a descriptive component name
4. Select component size (half or full width)
5. **Optional**: Choose either:
   * **Comparison interval**: Compare current period with a previous timeframe
   * **Breakdown values**: Chart multiple values for a single dimension type (e.g., different device types)
6. **Optional**: Apply component-specific filters

<Callout type="info">
  Comparison intervals and breakdown values are mutually exclusive options. Also note that breakdown dimensions will take priority over dashboard and component filters of the same dimension.
</Callout>

### Bars

<Image alt="Bar chart titled “Video Startup Failure Percentage” broken down by browser. Chrome has the highest failure rate, followed by Firefox, Safari, and Edge. A tooltip highlights Firefox with a failure percentage of 1.39%." src="/docs/images/build-a-custom-dashboard-G.png" width={1134} height={834} />

Compare performance across different dimension values using horizontal bars.

#### Configuration:

1. Select **Bars** as the component type
2. Choose the metric to measure in the bars visualization
3. Select component size (half or full width)
4. Choose breakdown dimension type and values that you wish to display
5. **Optional**: Add a comparison interval to compare current period with a previous timeframe
6. **Optional**: Apply component-specific filters

<Callout type="info">
  Breakdown values must come from a single dimension category.
</Callout>

### Lists

Rank and organize data to quickly identify top performers or problem areas.

<Image alt="Table showing Rebuffer Percentage broken down by operating system. Windows has the highest rebuffer rate at 0.70%, followed by iOS and Android, with directional trend indicators in green or red." src="/docs/images/build-a-custom-dashboard-H.png" width={1190} height={770} />

#### Configuration:

1. Select **List** as the component type
2. Choose the metric to measure for each list item
3. Select the dimension to list (e.g., player names, video titles)
4. Set sort order (ascending or descending)
5. Specify the number of items to display in the list component
6. Provide a descriptive component name
7. **Optional**: Add a comparison interval
8. **Optional**: Apply component-specific filters

<Callout type="info">
  Lists are only available in half-width size.
</Callout>

## Dashboard Management

### Sharing Dashboards

When creating a new dashboard, you can choose to share it with everyone in your environment. Public dashboards appear in the Shared folder for all users in your environment. You can change the sharing level at any time from the More Options dropdown.

All users can view public dashboards. To save an editable version of a public dashboard, create a duplicate (see below).

### Sharing via Dashboard Link

Any dashboard can be shared with users who have access to your Mux environment via the dashboard link, even if it's not marked as public. Users who receive a link can:

* View the dashboard
* Favorite it to save it to their personal list
* Create a duplicate to make their own editable copy (see below)

### Editing Dashboard Permissions

Users have the ability to edit dashboards they are the owner of but do not have the ability to edit public dashboards they do not own. Admins have full editing abilities for all dashboards.

Advanced role-based permissions are coming soon.

### Favoriting Dashboards

Favorite personal or shared dashboards to allow quick access to your most frequently used dashboards. You can have up to 20 favorited dashboards across your environments.

Favorite a dashboard by pressing the favoriting star in the dashboard menu. When a dashboard is favorited, the star will be highlighted and the dashboard will be added to the top of the custom dashboard navigation sidebar in the favorites section.

<Image sm alt="Star a custom dashboard by pressing the star icon" src="/docs/images/build-a-custom-dashboard-K.png" width={750} height={224} />

### Saving Dashboard Copies

Save a modified version without affecting the original:

1. Make your desired changes to the dashboard
2. Use the **Save As** option in the save menu
3. Provide a new name for the copy

<Image alt="Bottom section of a Mux dashboard displaying two metric widgets: one for “Exits Before Video Start” (line chart) and another for “Rebuffer Percentage” by Windows. Save, Save As, and Cancel buttons appear below." src="/docs/images/build-a-custom-dashboard-J.png" width={1999} height={436} />

### Exporting Dashboards

Export a dashboard to a PDF to save a snapshot of your dashboard:

1. Select the **More Options** menu (⋯) next to the favorite button
2. Choose **Export PDF**

<Image sm alt="Dropdown menu under the time range selector “Last 24 hours” with options to “Export PDF“, “Duplicate“ or “Delete” the dashboard." src="/docs/images/build-a-custom-dashboard-I.png" width={774} height={364} />

### Duplicating Dashboards

Create an exact copy of an existing dashboard:

1. Select the **More Options** menu (⋯) next to the favorite button
2. Choose **Duplicate**

<Callout type="info">
  Duplication is not available while a dashboard is being edited.
</Callout>

### Deleting Dashboards

Permanently remove dashboards you no longer need:

1. Select the **More Options** menu (⋯) next to the favorite button
2. Choose **Delete**
3. Confirm the deletion

<Callout type="warning">
  Deleting a dashboard removes it for all users. Duplicate dashboards are not affected.
</Callout>

### Dashboard Navigation

#### Exploring Metric Details

Access detailed metric analysis directly from dashboard components:

1. Select the **Go To Metrics** icon on any component
2. The metrics page opens with:
   * Selected filters from your dashboard applied
   * The component's metric pre-selected


# Save and share filter sets
Learn how to create filter sets to improve collaboration across teams, ensure data consistency, and create filtered views of Mux Data.
**What are filter sets?**

Filter sets allow you to save and share commonly used filter combinations into sets to ensure data consistency and streamline operational workflows.

1. To create a filter set, select the filter set button on any dashboard that supports filters.
2. Select ‘create new filter set’ from the menu.

<Image alt="A Mux Data interface showing the filter configuration panel. The user has selected three dimensions: Operating System (iOS), Stream Type (LIVE), and Video ID (456182). A tag labeled “456182” appears under Video ID. The right panel shows filters for iOS, LIVE, and Video ID 456182, with an “Apply” button at the bottom." src="/docs/images/save-and-share-filter-sets-1.png" width={1999} height={800} />

3. Select a name for your filter set.

4. Choose if your filter set is public or private. Private means that only you will see it in the menu under your ‘private’ filter sets. Public means that all users in an environment will be able to see and select the filter set.

5. Select the filters that you wish to add to your filter set. The filter values that were selected on the page will automatically populate in this menu. You can remove or add new dimension and metric filters before saving.

<Image sm alt="A Mux Metrics dashboard with filters applied. The top bar includes options for Filters, Dimensions, and Metrics, with a dropdown showing “Clear all.” A date range selector is set to “Last 24 hours.” The graph area shows zero total video views and no data available. The “Create new filter set” option is visible in the open filter menu." src="/docs/images/save-and-share-filter-sets-2.png" width={454} height={675} />

**Add a new filter value to a filter set**

You can manually create a new filter value if it doesn’t yet exist in Mux Data. This is useful for an upcoming event or new product launch.

1. In the filter menu, select the dimension type on the left.
2. Type the value of the dimension that you wish to add.
3. The value you entered will appear in the results with zero views.
4. Select that value to add it to your filter set.
5. Select apply.
6. Select save.

This value will now be associated with your saved filter set. When selecting this filter set, it will show zero views until there are views that match that criteria.

<Image alt="A Mux interface showing the “Create Filter Set” modal. The form includes a field to name the filter set, a privacy selector (Private selected), and a summary of applied filters: Dimensions (iOS, LIVE) and Metrics (Page Load Time). Buttons at the bottom allow Cancel or Save." src="/docs/images/save-and-share-filter-sets-3.png" width={1962} height={902} />

**Navigating with filter sets**
When a filter set is selected, it will persist when navigating across dashboards in Mux Data. Not all filters are supported across all dashboards.

If you navigate to a dashboard on Mux that doesn’t support a filter in your selected filter set, that filter will be deactivated while on that dashboard. A warning message will appear and the filter set will appear yellow if all values are not applicable to that page. The filter will also appear in a deactivated state in the filter display menu.

Once you navigate to a dashboard where that filter is applicable, it will be reactivated.

<Image alt="Mux Monitoring Overview dashboard in dark mode. A yellow alert in the top right reads “Filters Disabled – There are dimensions or metrics filters that are unavailable for this page.” The dashboard displays six empty charts for metrics such as Video Startup Failure, Playback Failures, Rebuffering Percentage, and Average Bitrate, all showing 0.00%." src="/docs/images/save-and-share-filter-sets-4.png" width={1999} height={1048} />

**Filter sets and Custom Dashboards**

When you build a Custom Dashboard, filters and filter sets are saved as a setting of that dashboard. When you navigate to a Custom Dashboard, all selected filters and filter sets will be reset to the values saved to that dashboard.

Filter sets can be added to custom dashboards. However, filter sets are not available to be applied to custom dashboard components.

**Delete a filter set**

1. Select the edit filter set icon for the filter set you wish to delete.
2. In the filter set menu, select delete filter set icon.

<MultiImage
  images={[
  { sm: true, src: "/docs/images/save-and-share-filter-sets-5.png", width: 1130, height: 924, alt: "A Mux Metrics dashboard with the filter menu open. The filter dropdown lists private saved filter sets titled “IBC Conference” and “Engaged Views,” along with an option to create a new filter set. The dashboard displays graphs for views and overall viewer experience." },
  { sm: true, src: "/docs/images/save-and-share-filter-sets-6.png", width: 822, height: 1130, alt: "A Mux modal titled “Edit Filter Set.” The name field shows “IBC Conference.” Privacy is set to Private. One dimension filter is applied — a long video ID string. The bottom of the modal includes Cancel and Save buttons." },
]}
/>


# Focus your operational response with error categorization
Configure error categorization through the Mux Data Dashboard or your SDKs to track and report on custom error metadata for views in Mux Data.
## 1. What is Error Categorization?

Error Categorization allows you to set custom error metadata to provide more actionable data. By using error categorization, you can distinguish between fatal errors or warnings and classify errors as playback failures or business exceptions. Errors categorized as warnings or as business exceptions are not considered playback failures, meaning these errors are excluded from alerting, giving a more accurate picture of the health of your system with less noise from alerts.

Playback Failure metrics (`Playback Failure Percentage` and `Video Startup Playback Failure Percentage`) only include fatal operational failures, while errors categorized as business exceptions and warnings are excluded. Errors that are categorized as a business exception will be included in the `Playback Business Exception Percentage` and `Video Startup Business Exception Percentage` metrics.

There are two dimensions, `Playback Business Exception` and `Video Startup Business Exception`, that are available as filters. Like the Playback Failure metrics, the `Playback Failure` and `Video Startup Failure` dimensions are not set for business exceptions and warnings.

The category information for errors can be set from the Mux Dashboard or from the individual player SDKs. You only need to set the categorization on an error in one place and information about the categories that are set in the Dashboard overrides the information set in the SDKs.

## 2. Configuring Error Categorization

Categorizing Errors is available from the Settings page and selecting the "Categorize Errors" tab. You must be an admin user to add a new error code categorization.

<Image sm src="/docs/images/categorize-errors-tab.png" width={1254} height={436} />

In the configuration page, you can categorize errors by code. Click the "Add an error code" button. In the dropdown, you will see the error codes your environment has encountered. Select from this dropdown and press "Add" to create a new categorization. By default, errors will have fatal error severity and will be tagged as playback failures.

Type into the filter box to search for specific error codes. If you are configuring an error code not previously seen in this environment, you can press "Enter" to create a new categorization.

<Image sm src="/docs/images/categorize-errors-add.png" width={1254} height={477} />

## 3. Submitting Error metadata from Mux Data SDKs

### Attach severity and type to errors with Mux Data SDKs

Error Categorization can also be configured in the Mux Data SDKs in a similar method to other error metadata. If an error code is already configured in the data dashboard, the settings from the dashboard will take precedence.

### HTML5 Video Element and other web SDKs

In web-based SDKs, Error Categorizations can be set by passing through a function to the player. This function will set the relevant error metadata.

```js
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),
    player_error_severity: translateSeverity(error.player_error_severity),
    player_error_business_exception: translateBusinessException(error.player_error_business_exception)
  };
}

mux.monitor('#my-player', {
  debug: false,
  errorTranslator: errorTranslator,
  data: {
    env_key: 'ENV_KEY', // required
    // ... additional metadata
  }
});
```

For more guidance on using and configuring the error translator in web-based SDKs, please refer to the guide on [monitoring the HTML5 video element](/docs/guides/monitor-html5-video-element#error-translator).

Version 5.2.0 or later of the HTML5 Video Element monitor is necessary to support Error Categorization.

### Android

Error Categorization is supported for custom integrations that use the Core Java-based SDK `v8.0.0` or later.

This is an example of how to categorize an error event to be a warning.

```java
    import com.mux.stats.sdk.core.events.EventBus;
    import com.mux.stats.sdk.core.model.CustomerPlayerData;
    import com.mux.stats.sdk.muxstats.IPlayerListener;
    import com.mux.stats.sdk.events.playback.ErrorEvent;

    public class PlayerListener extends EventBus implements IPlayerListener {
    MuxStats muxStats;

    // Call from the source of warning or player callback meant to trigger warning with parameters appropriate to your integration. Dispatches an error event that Mux will categorize as a warning by default
    public void onPlaybackWarning(String errorCode, String errorMessage, String errorContext) {
        PlayerData playerData = new PlayerData();
        playerData.setErrorCode(errorCode);
        playerData.setErrorMessage(errorMessage);
        
        ErrorEvent errorEvent = new ErrorEvent(playerData, errorContext, ErrorSeverity.ErrorSeverityWarning);

        dispatch(errorEvent);
    }
```

For more guidance and additional examples please refer to the guide on [custom integrations in Java](/docs/guides/data-custom-java-integration).

### Apple (iOS, tvOS, visionOS, macOS Catalyst)

Error Categorization is supported when using the Mux `AVPlayer` integration `v4.0.0` or later and with custom integrations that use the Core Apple SDK `v5.0.0` or later.

#### AVPlayer Integration

This is an example of how to categorize an error event to be a warning.

```swift
func dispatchPlaybackWarning(
    playerName: String,
    playerErrorCode: String,
    playerErrorMessage: String,
    playerErrorContext: String
) {
    MUXSDKStats.dispatchError(
        playerErrorCode,
        withMessage: playerErrorMessage,
        severity: MUXSDKErrorSeverity.warning,
        errorContext: playerErrorContext,
        forPlayer: playerName
    )
}
```

For more guidance and additional examples please refer to the [AVPlayer monitoring guide](/docs/guides/monitor-avplayer).

#### Custom Integrations

This is an example of how to categorize an error event to be a warning.

```swift
// Call this method from the source of the playback warning with parameters
// appropriate to your integration.
func dispatchPlaybackWarning(
    playerName: String,
    playerErrorCode: String,
    playerErrorMessage: String,
    playerErrorContext: String,
    playerPlayheadTime: NSNumber
) {
    // Configure any custom video or view data if necessary.
    let playerData = MUXSDKPlayerData()
    playerData.playerErrorCode = playerErrorCode
    playerData.playerErrorMessage = playerErrorMessage
    playerData.playerPlayheadTime = playerPlayheadTime
    // ... repeat for any other `MUXSDKPlayerData` properties if they've changed

    let errorEvent = MUXSDKErrorEvent(severity: .warning, context: playerErrorContext)
    errorEvent.playerData = playerData

    MUXSDKCore.dispatchEvent(errorEvent, forPlayer: playerName)
}
```

For more guidance and additional examples please refer to the guide on [custom integrations in Swift](/docs/guides/data-custom-swift-integration).

### Roku

Error categorization is supported when using an SDK v2.0.0 or later.

```js
mux.setField("error", {
  player_error_code: errorCode,
  player_error_message: errorMessage,
  player_error_context: errorContext,
  player_error_severity: errorSeverity,
  player_error_business_exception: isBusinessException
})
```

The possible values or `errorSeverity` are `"warning"` or `"fatal"`.

For more guidance on using and configuring the Roku SDK, please refer to the guide on [monitoring Roku](/docs/guides/monitor-roku).


# Export raw video view data
Understand how to export your video views data into your own data warehouse for processing and analysis.
View data can be exported from Mux Data for aggregation and reporting in your data infrastructure. Views are available individually using the <ApiRefLink href="/docs/api-reference/data/video-views/get-video-view">Views API</ApiRefLink> or in bulk with the export methods: daily CSV exports or streaming exports.

## Call the Export API to get daily aggregated data

Full data exports are available via the <ApiRefLink href="/docs/api-reference/data/exports/list-exports-views">Exports API</ApiRefLink>. This API is available for Mux Data customers on Media plans.

Use this API to get a list of CSV files available for download. Files are available to download for seven days after they are generated. Each CSV file is a single day of data and includes every single dimension collected by Mux, for each single video view. The table below details each of these data fields.

The Versions column indicates what fields are included in each version. Newer export versions will include the latest columns available. Some columns may be empty based on the features enabled. From version 2 onward, fields are sorted in alphabetical order. Older versions of the export may have fields in a different order, please refer to the export file for the most accurate ordering. Please contact support to change the export version that is generated.

**We strongly suggest you build the file import to use the field names rather than ordinal order so additional fields can be added to the file without causing an error.**

## Stream views as they complete

<Callout type="info">
  Streaming Exports are available on **Mux Data Media** plans. Learn more about [Mux Data Plans](https://data.mux.com/pricing) or [contact support](https://mux.com/support).
</Callout>

Mux Data supports streaming exports of video views to an Amazon Kinesis Data Stream or Google Cloud Pub/Sub topic in your cloud account. Views are sent to Kinesis or Pub/Sub as they complete and are made available to retrieve from the stream within about one minute after the view ends.

Each message is a single view, with all of the metadata and metrics, and the event timeline for the view. The view data can be stored in your long-term storage for aggregation and reporting.

This method of access is most useful for customers who want to update metrics on a rolling basis throughout the day or are embedding metrics in a user-facing application feature and need faster updates than once per day.

## Setting up a streaming export

Streaming exports can be configured in the **Streaming Exports** settings in your Mux dashboard. See the setup guide for your platform for more information on setting up an export:

* [Amazon Kinesis Data Streams](/docs/guides/export-amazon-kinesis-data-streams)
* [Google Cloud Pub/Sub](/docs/guides/export-google-cloud-pubsub)

## Message format

Messages are in either JSON format or Protobuf (proto2) encoding. You can choose between the two formats when setting up the streaming export in the Mux Dashboard -> Settings -> Streaming Export -> New streaming export page.

For Protobuf encoding, every message uses the `VideoView` message type defined in the export Protobuf spec, which is available in the [mux-protobuf repository](https://github.com/muxinc/mux-protobuf/tree/main/video_view). Use the latest Protobuf spec when creating schemas or generating code.

The fields in the Protobuf definition match those used in the latest version of the Exports API. The available fields are noted in the table below.

## View handling

A view can be updated after it has been exported. This will be expressed with a record of the latest version of the view being emitted to the stream. When processing views, make sure you're able to handle multiple or duplicate records for each view ID (`view_id`). The `view_id` can be used as a unique primary key for each view record.

## Understand the data fields

**Mux API Value**: field name in the CSV file or streaming export

**Unit**: unit of the field, such as text, percentage, or bits per second. Note that all units of type `Time` are represented as timestamps in UTC.

**Type**:

* Dimension: metadata about the view
* Metric: metrics calculated by Mux
* Score: score calculated by Mux

**Versions**: export version in which the fields are included

| Mux API Value | Unit | Type | Definition | Versions |
|---------------|------|------|------------|----------|
|`asn` |Integer |Dim. | Autonomous System Number uniquely identifying each network| v1+ |
|`asset_id` |Text |Dim. | If Mux Video is used, the Asset Id of the video.| v4+ |
|`audio_codec` |Text |Dim. | The codec of the audio that played during the view. | v13+ |
|`browser` |Text |Dim.| Browser used for the video view (`Safari`, `Chrome`, etc.).| v2+ |
|`browser (viewer_application_name)` |Text |Dim.| Deprecated - see `browser`| v1 |
|`browser_version` |Version |Dim. | Browser version (e.g. `66.0.3359.158`).| v2+ |
|`browser_version (viewer_application_version)` |Version |Dim. | Deprecated - see `browser_version (viewer_application_version)`| v1 |
|`cdn` |Text |Dim. | CDN delivering the video view, either determined by response header auto-detection or provided as video\_cdn.| v1+ |
|`city` |Text |Dim. | City of the viewer.| v1+ |
|`client_application_name` |Text |Dim. | Name of the customer application that the viewer is using to watch the content. e.g 'OurBrand iOS App'. | v13+ |
|`client_application_version` |Text |Dim. | Version of the customer application that the viewer is using to view the content. | v13+ |
|`continent_code` |ISO Code |Dim. | 2-letter ISO code identifying the Continent of the viewer (e.g. `NA`, `EU`).| v1+ |
|`country` |ISO Code |Dim. | 2-letter Country Code.| v2+ |
|`country (country_code)` |ISO Code |Dim. | Deprecated - see `country`| v1 |
|`country_name` |Text |Dim. | Country of the viewer.| v1+ |
|`custom_1` |Text |Dim. | Customer-defined metadata.| v2+ |
|`custom_2` |Text |Dim. | Customer-defined metadata.| v2+ |
|`custom_3` |Text |Dim. | Customer-defined metadata.| v2+ |
|`custom_4` |Text |Dim. | Customer-defined metadata.| v2+ |
|`custom_5` |Text |Dim. | Customer-defined metadata.| v2+ |
|`custom_6` |Text |Dim. | Customer-defined metadata.| v5+ |
|`custom_7` |Text |Dim. | Customer-defined metadata.| v5+ |
|`custom_8` |Text |Dim. | Customer-defined metadata.| v5+ |
|`custom_9` |Text |Dim. | Customer-defined metadata.| v5+ |
|`custom_10` |Text |Dim. | Customer-defined metadata.| v5+ |
|`environment_id`|Unique ID |Dim. | Mux Environment ID, linked with a specific environment| v4+ |
|`error_type` |Unique ID |Dim. | Mux-internal ID used to categorize errors.| v2+ |
|`error_type (error_type_id)` |Unique ID |Dim. | Deprecated - see `error_type`| v1 |
|`exit_before_video_start` |Boolean |Metric | Identifies when a viewer abandons the video because it is taking too long to load.| v1+ |
|`experiment_name` |Text |Dim. | A/B Testing:  use this field to separate views into different experiments.| v1+ |
|`isp` |Text |Dim. | Unused| v1+ |
|`latitude` |Degrees |Dim. | Latitude of the viewer, truncated to 1 decimal place.| v1+ |
|`live_stream_id` |Text |Dim. | If Mux Video is used, the Live Stream Id of the video.| v4+ |
|`live_stream_latency` |Integer |Metric | Live Stream Latency measuring the average time from ingest to display for the view.| v4+ |
|`longitude` |Degrees |Dim. | Longitude of the viewer, truncated to one decimal place.| v1+ |
|`max_downscale_percentage` | Percentage | Metric | Maximum Downscale Percentage at any point in time during a video view.| v2+ |
|`max_downscale_percentage (view_max_downscale_percentage)` | Percentage | Metric | Deprecated - see `max_downscale_percentage`| v1 |
|`max_upscale_percentage` | Percentage | Metric |  Maximum Upscale Percentage at any point in time during a video view.| v2+ |
|`max_upscale_percentage (view_max_upscale_percentage)` | Percentage | Metric | Deprecated - see `max_upscale_percentage`| v1 |
|`metro` |Text |Dim. | Unused| v1+ |
|`mux_api_version` | Text|Dim. | Ignore | v1+ |
|`mux_embed_version` |Dim. |Dim. | Internal version of Mux Core SDK. Ignore| v1+ |
|`mux_viewer_id` |Unique ID |Dim. | A Mux Internal ID representing the viewer who is watching the stream.| v1+ |
|`operating_system` |Text |Dim. | Operating System (`iOS`, `Windows`, etc.).| v2+ |
|`operating_system (viewer_os_family)` |Text |Dim. | Deprecated - see `operating_system`| v1 |
|`operating_system_version` |Version |Dim. | Operating System version (e.g. `10.15`).| v2 |
|`operating_system_version (viewer_os_version)` |Version |Dim. | Deprecated - see `operating_system_version`| v1 |
|`page_load_time` |Milliseconds |Metric | Measures the time from the initial user request for a page to the time when the video player is first initialized| v1+ |
|`page_type` |Text |Dim. | Provides the context of the page for more specific analysis. Values include `watchpage` or `iframe`.| v1+ |
|`page_url` |URL |Dim. | Page URL| v1+ |
|`platform_description` |Text |Dim. | Unused| v1+ |
|`playback_id` |Text |Dim. | If Mux Video is used, the Playback Id of the video.| v4+ |
|`playback_business_exception_error_type_id` |Unique ID |Dim. | An ID value that is present when a playback business exception occurs | v9+ |
|`playback_failure_error_type_id` |Unique ID |Dim. | An ID value that is present when a playback failure occurs | v9+ |
|`playback_success_score` |Decimal |Dim. | Playback Success Score| v2+ |
|`player_autoplay` |Boolean |Dim. | Indicates whether the player autoplayed the video or not| v1+ |
|`player_captions_enabled` |Boolean |Dim. | Boolean indicating if the player used captions at any time during the view. | v13+ |
|`player_error_code` |String |Dim. | An error code that represents a fatal error (one resulting in playback failure). Often an integer, but implementation-dependent.| v1+ |
|`player_error_context` |Text |Dim. | Error instance-specific information such as stack trace or segment number.| v5+ |
|`player_error_message` |Text |Dim. | Message sent by the player when an error has been fired up (associated with an error code)| v1+ |
|`player_height` |Integer |Dim. | Height of the player as displayed in page, in pixels| v1+ |
|`player_instance_id` |Unique ID |Dim. | Identifies the instance of the Player class that is created when a video is initialized| v1+ |
|`player_language` |Text |Dim. | Player's text language| v1+ |
|`player_load_time` |Milliseconds |Metric | Deprecated - see `player_startup_time`)| v1+ |
|`player_mux_plugin_name` |Text |Dim. | Mux Integration Plugin name (e.g. `mux-player`)| v1+ |
|`player_mux_plugin_version` |Version |Dim. | Mux Integration Plugin version (e.g. `2.2.0`)| v2+ |
|`player_name` |Text |Dim. | Identifies different configurations or types of players around your site or application (e.g. `My Player`)| v1+ |
|`player_pip_enabled` |Boolean |Dim. | Boolean indicating if the player used Picture in Picture at any time during the view. | v13+ |
|`player_poster`|URL| Dim. | The image shown as the pre-visualization before play | v1+ |
|`player_preload` |Boolean |Dim. | Specifies if the player was configured to load the video when the page loads.| v1+ |
|`player_remote_played` |Boolean |Dim. | Specify from the SDK if the video is remote played to AirPlay or Chromecast.| v2+ |
|`player_software` |Text |Dim. | Player Software being used to play the Video (e.g. `Video.js`, `JW Player`, etc.)| v1+ |
|`player_software_version` |Text |Dim. | Player Software Version (e.g. `2.45.5`)| v1+ |
|`player_source_domain` |Text |Dim. | Video Source Domain (e.g. `myvideostreams.com`)| v1+ |
|`player_source_duration` |Milliseconds |Dim. | Video Source Duration| v1+ |
|`player_source_height` |Integer |Dim. | Height of the source video being sent to the player, in pixels| v1+ |
|`player_source_stream_type` |Text |Dim. | Unused| v1+ |
|`player_source_url` |URL |Dim. | Video Source URL| v1+ |
|`player_source_width` | Integer | Dim. | Width of the source video being as seen by the player | v1+ |
|`player_startup_time` |Milliseconds |Metric | Measures the time from when the player is first initialized in the page to when it is ready to receive further instructions.| v1+ |
|`player_version` |Text |Dim. | As you make changes to your player you can compare how new versions of your player perform. Set in combination with `player_name` (e.g. `1.2.0`) | v1+ |
|`player_view_count` |Integer |Dim. | View Count - equal to 1 in Full Exports (1 line = 1 video view)| v1+ |
|`player_width` |Integer |Dim. | Width of the player as displayed in page, in pixels| v1+ |
|`property_id` |Unique ID |Dim. | Mux Property ID, linked with a specific environment. Deprecated, please use `environment_id`. | v1+ |
|`rebuffer_count` |Integer |Metric | Number of rebuffering events that happen during the video view. | v2+ |
|`rebuffer_count (buffering_count)` |Integer |Metric | Deprecated - see `rebuffer_count` | v1 |
|`rebuffer_duration` |Milliseconds |Metric | Amount of time in milliseconds that viewers wait for rebuffering per video view. | v2+ |
|`rebuffer_duration (buffering_duration)` |Milliseconds |Metric | Deprecated - see `rebuffer_duration` | v1 |
|`rebuffer_frequency` |Events per millisecond |Metric | Measures how often rebuffering events happen. | v2+ |
|`rebuffer_frequency (buffering_rate)` |Events per millisecond |Metric | Deprecated - see `rebuffer_frequency` | v1 |
|`rebuffer_percentage` |Percentage |Metric | Volume of rebuffering that is occurring across the view| v1+ |
|`region` |Text |Dim. | Region of the viewer| v1+ |
|`session_id` |Unique ID |Dim. | Mux Session ID tracking a viewer's session| v1+ |
|`smoothness_score` |Decimal |Score | Smoothness Score| v2+ |
|`source_hostname` |Text |Dim. | Video Hostname (e.g. `media.myvideos.com`).| v2+ |
|`source_hostname (player_source_host_name)` |Text |Dim. | Deprecated - see `source_hostname`| v1 |
|`source_type` |Text |Dim. | Format of the source, as determined by the player. E.g. `application/dash+xml`, `x-application/mpegUrl`, `mp4`, etc.| v2+ |
|`source_type (player_source_type)` |Text |Dim. | Deprecated - see `source_type`| v1 |
|`startup_time_score` |Decimal |Score | Startup Time Score| v2+ |
|`stream_type` |Text |Dim. | Type of stream (e.g. `live` or `on-demand`).| v2+ |
|`stream_type (video_stream_type)` |Text |Dim. | Deprecated - see `stream_type`| v1 |
|`sub_property_id` |Text |Dim. | Sub Property Id| v2+ |
|`time_to_first_frame` |Milliseconds | Metric | Deprecated - see `video_startup_time`| v1 |
|`used_fullscreen` |Boolean |Dim. | Indicates whether the viewer used full screen to watch the video.| v1+ |
|`video_affiliate` |Text |Dim. | Affiliate station that the viewer is watching or associated with the viewer. | v13+ |
|`video_brand` |Text |Dim. | Brand associated with the video or the brand of the streaming platform the viewer is using to watch the video. | v13+ |
|`video_cdn_trace` |Array |Dim. | Sequential values of the video delivery CDN over the course of the view | v14+ |
|`video_codec` |Text |Dim. | The codec of the video that played during the view. | v13+ |
|`video_content_type` |Text |Dim. | Content Type (e.g. `short`, `movie`, `episode`, `clip`, `trailer`, or `event`).| v1+ |
|`video_creator_id` |Text |Dim. | A unique identifier for the creator of the video. Defaults to the Mux Creator ID if enabled for Assets and Livestreams hosted by Mux.| v13+ |
|`video_duration` |Milliseconds |Dim. | The length of the video supplied to Mux via custom metadata| v1+ |
|`video_dynamic_range_type` |Text |Dim. | The format or type of dynamic range available on the video during the view. | v13+ |
|`video_encoding_variant` |Text |Dim. | An optional detail that allows you to compare different encoding settings.| v1+ |
|`video_id` |Unique ID |Dim. | Your internal ID for the video| v1+ |
|`video_language` |Text|Dim. | The audio language of the video, assuming it's unchangeable after playing.| v1+ |
|`video_producer` |Text |Dim. | The producer of the video title| v1+ |
|`video_quality_score` |Decimal |Score | Video Quality Score| v2+ |
|`video_startup_business_exception_error_type_id` |Unique ID |Dim. | An ID value that is present when a video startup business exception occurs | v9+ |
|`video_series` |Text |Dim. | Series name (e.g. `The Girls`)| v1+ |
|`video_startup_time` |Milliseconds | Metric | (Video Startup Time on Mux Dashboards) Measures from when the player has been instructed to play the video, to when the first frame of video (either content or preroll ad) is showing and the playhead is progressing.| v2+ |
|`video_startup_failure` |Boolean | Metric | Identifies when a viewer encounters an error before the first frame of the video begins playback.| v7+ |
|`video_title` |Text |Dim. | Video Title| v1+ |
|`video_variant_id` |Unique ID |Dim. | Your internal ID for a video variant| v1+ |
|`video_variant_name` | Text |Dim. | An optional detail that allows you to monitor issues with the files of specific versions of the content, for example different audio translations or versions with hard-coded/burned-in subtitles.| v1+ |
|`view_cdn_edge_pop` |Text |Dim. | Region where the CDN edge point of presence server is located or other origin server identification. | v13+ |
|`view_cdn_origin` |Text |Dim. | Identifying name of the Content Origin or Region where the Origin server is located. | v13+ |
|`view_content_startup_time` |Milliseconds |Metric | Measures from when the player has been instructed to play the video, to when the first frame of video content is showing and the playhead is progressing.| v10+ |
|`view_content_watch_time` |Milliseconds |Metric | Total Content Watch Time across the view (includes Startup Time, Playing time, potential rebuffering).| v10+ |
|`view_downscaling_percentage` |Percentage |Metric | Downscale Percentage| v2+ |
|`view_drm_level` |Text |Dim. | Security level of the specific DRM type. Some DRM types do not have levels.  v13+ |
|`view_drm_type` |Text |Dim. | The type of DRM used during playback (e.g. `widevine` or `playready`).| v5+ |
|`view_dropped` |Boolean |Dim. | Boolean indicating whether the view was finalized without an explicit viewend event. | v11+ |
|`view_dropped_frame_count` |Integer |Metric | The number of frames that were dropped by the player during playback| v5+ |
|`view_end` |Time |Dim. | Date and Time at which the view ended, in UTC.| v1+ |
|`view_has_ad` |Boolean |Metric | Identifies if an advertisement played or attempted to play during the video view.| v6+ |
|`view_id` |Unique ID |Dim. | Unique View Identifier| v1+ |
|`view_max_playhead_position` |Milliseconds |Dim. | The furthest the video was played, indicated by the maximum time value of the playhead during the view.| v3+ |
|`view_playing_time` |Milliseconds |Metric | The amount of time the video spent playing during the view; this value does not include time spent joining, rebuffering, or seeking.| v3+ |
|`view_seek_count` |Integer |Dim. | The number of times that the viewer attempted to seek to a new location within the view.| v1+ |
|`view_seek_duration` |Milliseconds |Dim. | Total amount of time spent waiting for playback to resume after the viewer seeks to a new location. Seek Latency metric in the Dashboard is this value divided by `view_seek_count`.| v1+ |
|`view_session_id` |Unique ID |Dim. | An id that can be used to correlate the view with platform services upstream such as CDN or origin logs.| v2+ |
|`view_start` |Time |Dim. | Date and Time at which the view started, in UTC.| v1+ |
|`view_time_shift_enabled` |Boolean |Dim. | Boolean indicating if this view had time\_shift enabled. | v13+ |
|`view_total_content_playback_time` |Milliseconds |Dim. | Internal metric used in calculating upscale and downscale percentages.| v1+ |
|`view_total_downscaling` |Milliseconds |Dim. | Internal number used to calculate Downscale Percentage Metric. Downscale Percentage = `view_total_downscaling / view_total_content_playback_time` | v1+ |
|`view_total_upscaling` |Milliseconds |Dim. | Internal number used to calculate Upscale Percentage Metric. Upscale Percentage = `view_total_upscaling / view_total_content_playback_time`| v1+ |
|`view_upscaling_percentage` |Percentage |Metric | Upscale Percentage| v2+ |
|`viewer_application_engine` |Text |Dim. | Web Browser Engine (`Gecko`, `WebKit`, etc.)| v1+ |
|`viewer_connection_type` |Text |Dim. | Type of network infrastructure available to the player: `wifi`, `cellular`, `wired`, `other`; or `no_connection` if none are available; or `unknown` if there is infrastructure available but its type cannot be determined. | v2+ |
|`viewer_device_category` |Text |Dim. | The form factor of the device: `camera`, `car browser`, `console`, `desktop`, `feature phone`, `peripheral`, `phone`, `portable media player`, `smart display`, `smart speaker`, `tablet`, `tv`, `wearable`| v1+ |
|`viewer_device_manufacturer` |Text |Dim. | Device Brand (e.g. `Apple`, `Microsoft`, etc.)| v1+ |
|`viewer_device_model` |Text |Dim. | Device Model (e.g. `iPhone11,2`)| v4+ |
|`viewer_device_name` |Text |Dim. | Device Name (e.g. `iPhone 12`)| v1+ |
|`viewer_experience_score` |Decimal |Score | Overall Viewer Experience Score| v2+ |
|`viewer_os_architecture` |Text |Dim. | No longer used. Ignore.| v1+ |
|`viewer_plan` |Text |Dim. | Name of the viewer's customer-specific plan, product, or subscription. | v13+ |
|`viewer_plan_category` |Text |Dim. | Category of the viewer's customer-specific subscription plan (e.g. bundle-type, subscription-campaign-id). | v13+ |
|`viewer_plan_status` |Text |Dim. | Status pertaining to that viewer's subscription plan (e.g. subscriber, non-subscriber, SVOD, AVOD, free, standard, premium). | v13+ |
|`viewer_user_agent` |Text |Dim. | User Agent (e.g. `Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0)`)| v1+ |
|`viewer_user_id` |Unique ID |Dim. | A Customer-defined ID representing the viewer who is watching the stream. Note: You should not use any value that is personally identifiable such as email address, username, etc. Instead, you should supply an anonymized viewer ID which you have stored within your own system.| v1+ |
|`watch_time` |Milliseconds |Dim. | Total Watch Time across the view (includes Startup Time, Playing time, potential rebuffering).| v1+ |
|`watched` |Boolean |Dim. | Ignore| v1+ |
|`weighted_average_bitrate` |bits/sec |Metric | Weighted Average Bitrate, expressed in bps.| v2+ |

## Ad Metrics and Dimensions

| Mux API Value | Unit | Type | Definition | Versions |
|---------------|------|------|------------|----------|
|`ad_attempt_count` |Integer |Metric | The number of times that the player attempted to play an ad | v8+ |
|`ad_break_count` |Integer |Metric | The number of times that the player entered an ad break| v8+ |
|`ad_break_error_count` |Integer |Metric | The number of times that the viewer encountered an ad error during an ad break| v8+ |
|`ad_break_error_percentage` |Percentage |Metric | Percentage of views that contain ads that encountered an ad break error| v8+ |
|`ad_error_count` |Integer |Metric | The number of times that the player encountered an ad error| v8+ |
|`ad_error_percentage` |Percentage |Metric | Percentage of views that contain ads that encountered an ad error| v8+ |
|`ad_impression_count` |Integer |Metric | The number of times that the player began ad playback| v8+ |
|`ad_startup_error_count` |Integer |Metric | The number of times that the player errored on ad startup| v8+ |
|`ad_startup_error_percentage` |Percentage |Metric | Percentage of views that contain ads that encountered an ad startup error| v8+ |
|`ad_exit_before_start_count` |Integer |Metric | The number of times that the viewer exited before the ad started playback| v8+ |
|`ad_exit_before_start_percentage` |Percentage |Metric | Percentage of views that contain ads that encountered an ad exit before start| v8+ |
|`ad_playback_failure_error_type_id` |Unique ID |Dim. | An ID value that is present when an ad playback failure occurs | v10+ |
|`ad_preroll_startup_time` |Milliseconds |Metric | Measures from when the player has been instructed to play a preroll ad to when the first frame of the ad is showing and the playhead is progressing.| v10+ |
|`ad_watch_time` |Milliseconds |Metric | Total Watch Time for ad playback across the view (includes Ad Preroll Startup Time, ad playing time, potential rebuffering). | v10+ |
|`preroll_ad_asset_hostname` |Hostname |Dim. | Hostname of the Preroll Ad Asset.| v1+ |
|`preroll_ad_tag_hostname` |Hostname |Dim. | Hostname of a Preroll Ad Tag.| v1+ |
|`preroll_played` |Boolean |Dim. | Flag to identify video views for which a Preroll Ad has been successfully played.| v1+ |
|`preroll_requested` |Boolean |Dim. | Flag to identify video views for which a Preroll Ad has been requested.| v1+ |
|`requests_for_first_preroll` |Integer |Metric | Measures the number of ad requests that are made up to the point of preroll ad playback beginning.| v1+ |
|`video_startup_preroll_load_time` |Milliseconds |Metric | Total amount of Video Startup Time that is spent loading the first preroll ad asset.| v1+ |
|`video_startup_preroll_request_time` |Milliseconds |Metric | Total amount of Video Startup Time that is spent making preroll ad requests.| v1+ |

## Request-level Metrics

| Mux API Value | Unit | Type | Definition | Versions |
|---------------|------|------|------------|----------|
|`max_request_latency` |Milliseconds |Metric | Maximum time to first byte for a media request.| v2+ |
|`max_request_latency (view_max_request_latency)` |Milliseconds |Metric | Deprecated - see `max_request_latency`| v1 |
|`request_latency` |Milliseconds |Metric | Measures the average time to first byte for media requests.| v2+ |
|`request_latency (view_average_request_latency)` |Milliseconds |Metric | Deprecated - see `request_latency`| v1 |
|`request_throughput` |bits/sec |Metric | Measures the average throughput, in bits per second, for all media requests that were completed.| v2+ |
|`request_throughput (view_average_request_throughput)` |bits/sec |Metric | Deprecated - see `request_throughput`| v1 |

## CSV file formats

The daily CSV export files are generated based on the specific version that is configured and include the fields specified in the section above.

Sample CSV export files are available to download, for reference:

* [Version 2](/exports/export_v2_sample.csv)
* [Version 3](/exports/export_v3_sample.csv)
* [Version 4](/exports/export_v4_sample.csv)

## Streaming Export message format

The protobuf definition for Streaming Exports of video views is available in the [mux-protobuf repository](https://github.com/muxinc/mux-protobuf/tree/main/video_view). Please subscribe to this repository for updates to the protobuf definition.

The JSON format streaming export contains identical fields as the protobuf-encoded format.

## Streaming Export versioning

## Backward compatibility

The Streaming Export schema provided by Mux Data is backward compatible, meaning that each schema version guarantees that it will still work upon future upgrades. Customers do not need to worry about breaking changes.

## When to upgrade the schema?

When Mux adds new fields into the Streaming Export, we will upgrade the schema version. Without taking any actions you will not be impacted by it: the fields that you used to get will keep working as normal, and the new fields introduced since your last upgrade will not be sent to you either. The benefit of designing it this way is that you will not be getting new fields without knowing.

For customers who want to get the new fields, read below to see the how-tos.

## How to upgrade the schema?

### If integrated with Google Pub/Sub

If your Google Pub/Sub topic is **schematized**, once a schema is associated with a topic, you can no longer change that schema. This means that customers using Google Pub/Sub for Streaming Export must take a couple of steps to move to a new topic that is associated with a new schema.

* Create a new topic in Google Pub/Sub with upgraded schema
* Point the Mux Data Streaming Export to that new topic
* Go to Mux Dashboard → Settings → Streaming Export → Click Upgrade.

If your Google Pub/Sub topic is **schemaless**, which it must be if you want to use JSON, you do not need to create new topics or reconfigure your streaming export, but to get the new fields released from Mux, customer needs to do the 3rd step as mentioned above.

### If integrated with Amazon Kinesis

* If using protobuf message format, make sure you get the latest protobuf definition from Mux public repo. Subscribe to the [mux-protobuf repository](https://github.com/muxinc/mux-protobuf/tree/main/video_view) to receive updates.
* Go to Mux Dashboard → Settings → Streaming Export → Click Upgrade button.


# Stream export data to an Amazon Kinesis data stream
Learn how to send streaming exports data from Mux to Amazon Kinesis Data Streams.
<Callout type="info">
  Streaming Exports are available on **Mux Data Media** plans. Learn more about [Mux Data Plans](https://data.mux.com/pricing) or [contact support](https://mux.com/support).
</Callout>

<Callout type="info">
  For a detailed walkthrough of the Amazon Kinesis Data Streams setup process, see this [blog post](https://www.mux.com/blog/mux-amazon-kinesis-integration).
</Callout>

In order to stream exports from Mux to Amazon Kinesis Data Streams, you’ll need to set up a data stream in your AWS account. This guide covers the high-level steps required for setup.

## 1. Add a new streaming export

To add a new streaming export, go to **Settings > Streaming Exports** in your Mux dashboard. From that tab, click **New streaming export** to open the configuration modal.

Select the type of data you want to export, the environment you want to send data from, the export format, and select **Amazon Kinesis Data Streams** as the service.

## 2. Set up a data stream in Amazon Kinesis

You'll need to complete the following setup in your AWS account before you can create a new streaming export in Mux:

1. Create an Amazon Kinesis data stream.
2. Create an IAM role for Mux’s AWS account. To create the IAM role, you'll need Mux's AWS account ID and an external ID, which are shown in the configuration modal. See [this AWS user guide](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html) for more information about how the external ID is used. When creating the role, choose "AWS account" for the Trusted entity type. Select "Another AWS account" and enter Mux’s AWS account ID. Check "Require external ID" and paste in the "External ID" that Mux provided to you in the configuration modal.
3. Provide the IAM role you created with write access to your data stream. Here’s an example of an IAM policy that grants the necessary permissions (replace the resource with your data stream ARN):

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
          "kinesis:ListShards",
          "kinesis:PutRecord",
          "kinesis:PutRecords"
      ],
      "Resource": [
        "arn:aws:kinesis:{region}:{account-id}:stream/{stream-name}"
      ]
    }
  ]
}
```

## 3. Finish setup in Mux

In the configuration modal, provide the data stream ARN and IAM role ARN. Make sure the values you provide match these formats:

* Data stream ARN\
  `arn:aws:kinesis:{region}:{account-id}:stream/{data-stream-name}`
* IAM role ARN\
  `arn:aws:iam::{account-id}:role/{role-name}`

Click **Enable export**, and your streaming export will be activated immediately. We will start streaming views as soon as they're completed.

## Process messages

With your export set up, you can begin consuming incoming messages. For more information on the message format and processing data, see the main [Export raw Mux data](/docs/guides/export-raw-video-view-data) guide.


# Stream export data to a Google Cloud Pub/Sub topic
Learn how to send streaming exports data from Mux to Google Cloud Pub/Sub.
<Callout type="info">
  Streaming Exports are available on **Mux Data Media** plans. Learn more about [Mux Data Plans](https://data.mux.com/pricing) or [contact support](https://mux.com/support).
</Callout>

In order to stream exports from Mux to a Pub/Sub topic, you'll need to set up a topic in your Google Cloud account. Mux will write data to the topic as it becomes available. This guide covers the high-level steps required for setup.

## 1. Add a new streaming export

To add a new streaming export, go to **Settings > Streaming Exports** in your Mux dashboard. From that tab, click **New streaming export** to open the configuration modal.

Select the type of data you want to export, the environment you want to send data from, the export format, and select **Google Cloud Pub/Sub** as the service.

## 2. Set up a topic in Google Cloud Pub/Sub

You'll need to complete the following setup in your Google Cloud account before you can create a new streaming export in Mux:

1. *(Optional)* If you want to use a schema with your Pub/Sub topic, you can create one using the Protobuf spec for the data you are exporting, which is available in the [mux-protobuf repository](https://github.com/muxinc/mux-protobuf).
2. Create a Pub/Sub topic. If you're creating a topic with a schema, set the message encoding to **Binary**.
3. Add the Mux service account to the topic as a Principal with the **Pub/Sub Publisher** role. The Mux service account is shown in the configuration modal.

## 3. Finish setup in Mux

In the configuration modal, provide the Pub/Sub topic name. This should be the full topic name, including the project ID, and match the format `projects/{project-id}/topics/{topic-id}`.

Click **Enable export**, and your streaming export will be activated immediately. We will start streaming data as soon as it becomes available.

## Process messages

With your export set up, you can begin consuming incoming messages. For more information on the message format and processing data, see the main [Export raw Mux data](/docs/guides/export-raw-video-view-data) guide.


# Set up alerts
Set up alerts so your team can be notified when certain conditions occur on your video platform.
## Define terms

**Rule:** Criteria for when an alert should be triggered based on a metric, threshold, and filter criteria.

**Incident:** A specific instance when the conditions of an alert Rule are met and an alert is triggered.

**Start Time:** The timestamp of when a metric initially crosses over an alert rule threshold.

**End Time:** The timestamp of when a metric crosses out of an alert rule threshold.

**Trigger Interval:** The time period from when a metric initially crosses over an alert rule threshold to when an alert incident notification occurs.

**Resolution Interval:** The time period from when a metric crosses out of an alert rule threshold to when an alert incident notification occurs.

**Incident Duration:** The total length of time spent in an incident, from the start of the open interval to the start of the close interval.

## Alert rules

There are two types of alerts supported by Mux Data: Anomaly and Threshold.

**Anomaly** alerts are configured automatically based on the historical failure rate of views in the organization.

**Threshold** alerts allow you to define specific criteria for viewer experience metrics that will trigger notifications.

## Anomaly Alerts

Anomaly alerts are generated when failures are elevated over a historical level. The historical level is determined by measuring the overall failure rate of videos in each organization over the recent past using a moving window.

There are two levels of playback failures that are used for anomaly detection:

* Organization-wide: The failure rate is calculated using all video views that are tracked within the organization.
* Per Video Title: The failure rates are calculated using the video views of every video title tracked within each environment separately.

To determine whether failure rates are elevated, the anomaly detector groups the on-going views in buckets ordered by time and compares the failure rate of each bucket to the historical organization-wide failure rate. If the failure rate of the bucket of views is determined to be an extreme outlier the failed views will be flagged as anomalous, and an incident will be opened.

The bucket sizes are based on the anomaly alert level:

* Organization-wide: 1000 views
* Per Video Title: 100 views for each video title

The outlier determinations are set dynamically based on your historical values so there is no need for configuring the specific thresholds in the anomaly alerts.

Anomaly alert incidents are automatically closed when the error-rate returns to normal levels. An incident for a video title or organization that hasn't received views in the last 8 days will be marked as expired.

## Threshold Alerts

<Callout type="info">
  Threshold Alerts are available on **Mux Data Media** plans. Learn more about [Mux Data Plans](https://data.mux.com/pricing) or [contact support](https://mux.com/support).
</Callout>

Threshold alerts allow you to define specific criteria for alerts that will trigger incident notifications.

Alert rules can be created for metrics collected in the Monitoring Dashboard:

* Failures
* Rebuffering Percentage
* Video Start Time
* Concurrent Viewers

<Image src="/docs/images/alert-rules-1.png" width={1217} height={643} />

From the menu on the right of list item on the Alert Rules list page, the following actions can be taken:

* Edit
* Duplicate
* Delete

<Image src="/docs/images/alert-rules-dropdown.png" width={1217} height={643} />

### Filters

Filters are applied to the alert definition to track only the specific data you want considered for the alert. Data for alert rules can be included or excluded from the following dimensions:

* ASN
* CDN
* Country
* Mux Asset ID
* Mux Live Stream ID
* Mux Playback ID
* Operating System
* Player Name
* Region / State
* Stream Type
* Sub Property ID
* Video Series
* Video Title

### Value ("trigger if")

The threshold value the metric must cross for the alert condition to be met.

### Above/Below ("rises above/falls below")

For alerts specified with the Concurrent Viewers metric, the criteria can be specified to trigger when the metric is above or below a threshold. Other metrics are specified to trigger when the metric is above the threshold.

### Alert Interval ("for at least X minutes")

The amount of time the threshold criteria needs to be met in order to open or close an alert incident. The interval length can be set between 1 and 60 minutes.

### Minimum Audience ("with a minimum audience of X average concurrent views")

The average number of concurrent viewers must be over the specified value in order to enter or exit an alert incident.

If the number of concurrent viewers falls below the specified minimum audience during an alert incident the incident will continue. The number of concurrent viewers needs to be over the minimum audience in order to for the alert incident to close.

**Note:** If a rule definition is changed, any open incident based on that rule is automatically closed. A new incident will be opened after the alert interval time if the updated rule criteria is met.

## Manage incidents

## Listing Incidents

When Anomaly and Threshold alert incidents are generated, they are listed in the "Incidents" tab.

You can choose the type of alert in the list:

* Threshold
* Anomaly

By default, currently "Open" issues are shown but all historical issues can be viewed by choosing "All".

<Image src="/docs/images/incident-page-1.png" width={1252} height={496} />

## Incidents

When an alert is triggered, the metric performance is captured in an Incident. The Incident page provides a place to see the characteristics of the alert and the metric behavior at start and end of the incident.

<Image src="/docs/images/incident-open-page.png" width={931} height={1153} />

Incidents contain the following information:

**Alert Name:** Name for the alert as defined in the rule definition

**Started:** The timestamp when the metric first crossed over the threshold defined in the alert rule.

**Ended:** The timestamp when the metric first crossed out of the metric threshold defined in the alert rule.

**Duration:** The length of time the alert was firing, the time between the Started and End times.

**\[Metric]** The value of the metric when the alert incident is triggered

**At End:** The value of the metric when the alert incident is resolved

**Peak:** The peak value of the metric while the alert is firing

Some data is only shown once an alert is closed such as the Closed time, Duration, etc.

### Incident Start/Close Charts

Charts and additional details are captured in the Incident page when an alert incident is opened and closed.

**Incident Start:**

**\[Metric]** The value of the metric when the alert incident is triggered

**Concurrent Viewers:** The number of viewers when the incident started.

In order to provide context on your video platform performance as the incident occurs, the chart shows up to 10 minutes before the incident starts and up to 5 minutes after the incident is opened. The lead-up time may be shorter than 10 minutes is the alert rule directly triggers an alert after it is saved.

**Incident Close:**

**\[Metric]** The value of the metric when the alert incident is resolved

**Concurrent Viewers:** The number of viewers when the incident ended.

For post-mortem reviews, the performance of the metric is captured in a chart that shows up to 10 minutes as the incident is resolved and the next 5 minutes after it ends.

Incidents can be queried via the <ApiRefLink href="/docs/api-reference/data/incidents/list-incidents">List Incidents API</ApiRefLink>.

**Note:** If an incident is open when it's rule definition is modified, the incident will be automatically closed. Any configuration data about the incident, such the threshold value or filters applied will reflect the rule configuration as of when the incident is opened.

## Notify your team

Mux Data can send notifications when alert incidents are opened and closed. Notifications are sent to channels that define the method and address of the services where the notifications should be delivered.

Channels are available for:

* Email
* Slack
* [PagerDuty](/docs/guides/pagerduty-alert-notifications)

Notification configuration can be found on the Alerts page in the "Notification Channels" tab. To setup a new channel, click the "Add Channel" button. From there you can choose the notification channel type, enter the destination address (email, Slack channel, or PagerDuty integration key).

You can choose what types of alerts are sent to each channel. Choose "Anomaly" If you only want to receive notification of alerts generated automatically by Mux Data,  "Threshold" will be notified of alerts that are configured in the environment, and "All" will sent notifications for all alerts.


# PagerDuty Alert Notifications
Use Mux Data Alerts with PagerDuty to alert your team about open incidents.
## How the integration works

Alerts can be defined by specified thresholds or based on levels dynamically defined by machine learned anomaly detection.

Video metrics that cause the creation of a new incident in Mux Data will also send a trigger event to PagerDuty which generates a new incident in the configured service or event rule set.

When an alert incident is resolved in Mux Data, a resolve event is sent to PagerDuty and the associated PagerDuty incident will be closed.

## Integration walk-through in PagerDuty

There are two ways to integrate with PagerDuty: via Global Event Routing or on a PagerDuty Service.

If you are adding this integration to an existing PagerDuty service, please skip to the Integrating with a PagerDuty Service section of this guide.

### Integrating with Global Event Routing

Integrating with Global Event Routing enables you to route events to specific services based on the payload of the event from your tool. If you would like to learn more, please visit the PagerDuty article on Global Event Routing.

1. From the Configuration menu, select Event Rules.

2. On the Event Rules screen, click on the arrow next to Incoming Event Source to display the Integration key information. Copy your Integration Key. This is the same integration key you will use for any other tool you want to integrate with using event rules. When you have finished setting up the integration in your tool, you will return to this interface to specify how to route events from your tool to services in PagerDuty.

<Image src="/docs/images/pd-event-routing.png" width={2392} height={1278} />

### Integrating With a PagerDuty Service

1. From the **Configuration** menu, select **Services**.
2. There are two ways to add an integration to a service:
   * **If you are adding your integration to an existing service**: Click the **name** of the service you want to add the integration to. Then, select the **Integrations** tab and click the **New Integration** button.
   * **If you are creating a new service for your integration**: Please read our documentation in section [Configuring Services and Integrations](https://support.pagerduty.com/docs/services-and-integrations#section-configuring-services-and-integrations) and follow the steps outlined in the [Create a New Service](https://support.pagerduty.com/docs/services-and-integrations#section-create-a-new-service) section, selecting ***Mux Data*** as the **Integration Type** in step 4. Continue with the ***In Mux Data*** section (below) once you have finished these steps.
3. Enter an **Integration Name** in the format `monitoring-tool-service-name` (e.g.  ***Mux Data-Production***) and select  ***Mux Data***  from the Integration Type menu.
4. Click the **Add Integration** button to save your new integration. You will be redirected to the Integrations tab for your service.
5. An **Integration Key** will be generated on this screen. Keep this key saved in a safe place, as it will be used when you configure the integration with Mux Data  in the next section.

<Image src="/docs/images/pd-integrations.png" width={2530} height={1054} />

## Integration walk-through in Mux Data

1. From the navigation menu, choose ***Alerts*** and then ***Notifications***.

<Image src="/docs/images/pd-mux-1.png" width={2530} height={1054} />

2. Click on the ***Add Channel*** button to create a new notification channel that sends alerts to PagerDuty.

<Image src="/docs/images/pd-mux-2.png" width={1640} height={498} />

3. In the New Channel dialog, for the ***Service*** choose `PagerDuty`, enter the ***Integration Key*** for the Service or Event Rule from the steps above, and choose which types of Mux Data alerts you would like sent to PagerDuty. `Anomaly` will send all automatically generated Anomaly alerts to the PagerDuty service, `Threshold` will send the notifications generated by the alerts you explicitly configure, and `All` will send all alert notifications generated in Mux Data to PagerDuty.

<Image src="/docs/images/pd-mux-3.png" width={1152} height={866} />

4. Click ***Add Channel*** to create the notification channel for PagerDuty.

## How to Uninstall

To stop notifying PagerDuty of alert incidents, delete the PagerDuty Notification Channel in Mux Data.

1. From the Alerts ***Notification Channels*** tab, scroll to the PagerDuty channel you would like to delete. Click the ***garbage can*** icon to delete the channel.

<Image src="/docs/images/pd-uninstall.png" width={1800} height={152} />

## FAQ

### Can you trigger incidents for more than one PagerDuty service or event rule from Mux Data?

To send alert notifications to multiple services or event rules, you can create more than one PagerDuty Notification Channel in Mux Data. Each PagerDuty Notification Channel in Mux Data can be set with the Integration Key from the desired service or event rule that should be notified when an alert is trigger or resolved.

## Requirements

* Mux Data integrations require access to Anomaly or Threshold Alerts. If you do not have access to this feature, please contact Mux for more information.

## Support

If you need help with this integration or information about Mux, please contact:

* Technical Support: https://www.mux.com/support
* Information: info@mux.com


# Enable automatic CDN detection
See how to configure your CDN so that Mux Data can detect CDN when tracking network requests
Mux has the capability to track each network request made by the player in order to expose network-level metrics such as throughput and latency measurements. In addition, Mux is able to auto-detect the CDN used to serve each manifest, segment, or fragment by inspecting certain response headers.  Enabling CDN auto-detection requires some minor configuration at each of your CDNs.

# Player SDK Integration

Mux currently supports automatic CDN detection for the following player integrations.

## Web

* [HLS.js](/docs/guides/monitor-hls-js)
* [Dash.js](/docs/guides/monitor-hls-js)
* [Video.js](/docs/guides/monitor-video-js)
* [Shaka player](/docs/guides/monitor-shaka-player)

## Android

* [ExoPlayer](/docs/guides/monitor-exoplayer)
* \[AndroidX Media3] (/docs/guides/monitor-androidx-media3)

Simply integrate the player SDK and each network request will be tracked.

For platforms or SDKs that do not support automatic CDN detection using response headers (e.g. iOS, Roku), you can configure your SDK to pass in a CDN value to the corresponding
SDK key if the player is aware of which CDN is delivering content. Learn more in our \[metadata guide]\(/docs//guides/make-your-data-actionable-with-metadata or in the relevant SDK documentation.

# CDN Configuration for automatic CDN detection

In order for Mux to automatically detect which CDN is serving the content to the player, you need to make a few configuration changes to each of your CDNs. These changes are necessary to expose two specific headers.

| Header | Description |
| --- | --- |
| `X-CDN` | This is a custom header that you need to add to *all* responses from each of your CDNs. The value of this should be a name describing that specific CDN; you should lowercase the name and replace spaces with `_`s. For example: `fastly`, `cloudfront`, `level3`, etc. |
| `Access-Control-Expose-Headers` | This should be set on each response, with the value being a comma-separated string of headers to expose to the client. At the least, you should set this to `X-CDN`. It is also suggested that you add other identifying headers that your CDN may use, such as `X-Cache`, `X-Served-By`, `Via`, or similar headers. More information on `Access-Control-Expose-Headers`, see here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers. |

# Mid-stream CDN switching and automatic CDN detection

Mid-stream CDN switching changes which CDN is used for content requests. If Mux is automatically detecting the CDN used for video delivery via network events, all detected CDN
values used to deliver video content will be placed in the `CDN Trace` dimension. The CDN values will be placed in sequential order that they were detected over the course of
view. A `cdn_change` event will also be created when the SDK detects that the detected CDN has been updated in the network event.


# Show how many people are watching your videos
Learn how to get the latest view and unique viewer counts for a video using the Engagement Counts API.
In this guide you will learn how to use the Engagement Counts<BetaTag /> API in order to embed the latest view and unique viewer counts for a particular video ID into your applications.

You will use JSON Web Tokens to authenticate to this API.

## 1. Create a Signing Key

Signing keys can be managed (created, deleted, listed) from the [Signing Keys settings](https://dashboard.mux.com/settings/signing-keys) of the Mux dashboard or via the Mux System API.

<Callout type="warning">
  When making a request to the System API to generate a signing key, the access
  token being used must have the System permission. You can confirm whether your
  access token has this permission by going to Settings > API Access Token. If
  your token doesn't have the System permission listed, you'll need to generate
  another access token with all of the permissions you need, including the
  System permission.
</Callout>

When creating a new signing key, the API will generate a 2048-bit RSA key pair and return the private key and a generated key ID; the public key will be stored at Mux to validate signed tokens. Store the private key in a secure manner.

You probably only need one signing key active at a time and can use the same signing key when requesting counts for multiple videos. However, you can create multiple signing keys to enable key rotation, creating a new key and deleting the old only after any existing signed URLs have expired.

### Example request

```bash
curl -X POST \
-H "Content-Type: application/json" \
-u ${MUX_TOKEN_ID}:${MUX_TOKEN_SECRET} \
'https://api.mux.com/system/v1/signing-keys'
```

### Example response

```json
// POST https://api.mux.com/system/v1/signing-keys
{
  "data": {
    "private_key": "(base64-encoded PEM file with private key)",
    "id": "(unique signing-key identifier)",
    "created_at": "(UNIX Epoch seconds)"
  }
}
```

<Callout type="warning">
  Be sure that the signing key's environment (Staging, Production, etc.) matches
  the environment of the views you would like to count! When creating a signing
  key via API, the environment of the access token used for authentication will
  be used.
</Callout>

This can also be done manually via the UI. If you choose to create and download your signing key as a PEM file from UI, you will need to base64 encode it before using it with (most) libraries.

```bash
❯ cat /path/to/file/my_signing_key.pem | base64
LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktL...
```

## 2. Generate a JSON Web Token

The following JWT claims are required:

| Claim Code | Description                | Value                                                                                                                                                              |
| :--------- | :------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sub`      | Subject of the JWT         | The ID for which counts will be returned                                                                                                                           |
| `aud`      | Audience (identifier type) | `video_id` (Mux Data Video ID) <br /> `asset_id` (Mux Video Asset ID) <br /> `playback_id` (Mux Video Playback ID) <br /> `live_stream_id` (Mux Video Live Stream ID) |
| `exp`      | Expiration time            | UNIX Epoch seconds when the token expires. Use this to ensure any tokens that are distributed become invalid after a period of time.                               |
| `kid`      | Key Identifier             | Key ID returned when signing key was created                                                                                                                       |

<Callout type="warning">
  Each of these ID types (used for the `aud` claim) are distinct and cannot be
  used interchangeably. Video ID is an optional Data dimension provided by the
  customer (you!). For more information on leveraging Video ID, see how to [Make your data actionable](/docs/guides/make-your-data-actionable-with-metadata). Mux Video Asset ID, Playback ID and Live Stream ID are available to Mux
  Video customers only and are generated by Mux. Be sure to double check both
  the query ID type and value!
</Callout>

### Expiration time

Expiration time should be at least the duration of the video or the expected duration of the live stream. When the signed URL expires, you will no longer receive counts from the API.

Your application should consider cases where the user loads a video, leaves your application, then comes back later at some time in the future and tries to play the video again. You will likely want to detect this behavior and make sure you fetch a new signed URL to make sure the counts that are displayed in your application continue to display.

<Callout type="info">
  [See the related video documentation](/docs/guides/secure-video-playback#expiration-time)
</Callout>

## 3. Signing the JWT

The steps can be summarized as:

1. Load the private key used for signing
2. Assemble the claims (`sub`, `aud`, `exp`, `kid` etc) in a map
3. Encode and sign the JWT using the claims map and private key and the RS256 algorithm.

There are dozens of software libraries for creating and reading JWTs. Whether you’re writing in Go, Elixir, Ruby, or a dozen other languages, don’t fret, there’s probably a JWT library that you can rely on. For a list of open source libraries to use, check out [jwt.io](https://jwt.io/libraries).

<Callout type="warning">
  The following examples assume you're working with either a private key
  returned from the API, or copy & pasted from the Dashboard, **not** when
  downloaded as a PEM file. If you've downloaded it as a PEM file, you will need
  to base64 encode the file contents.
</Callout>

```go

package main

import (
    "encoding/base64"
    "fmt"
    "log"
    "time"
    "github.com/golang-jwt/jwt/v4"
)

func main() {

    myId := ""       // Enter the id for which you would like to get counts here
    myIdType := ""   // Enter the type of ID provided in my_id; one of video_id | asset_id | playback_id | live_stream_id
    keyId := ""      // Enter your signing key id here
    key := ""        // Enter your base64 encoded private key here

    decodedKey, err := base64.StdEncoding.DecodeString(key)
    if err != nil {
        log.Fatalf("Could not base64 decode private key: %v", err)
    }

    signKey, err := jwt.ParseRSAPrivateKeyFromPEM(decodedKey)
    if err != nil {
        log.Fatalf("Could not parse RSA private key: %v", err)
    }

    token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
        "sub": myId,
        "aud": myIdType,
        "exp": time.Now().Add(time.Minute * 15).Unix(),
        "kid": keyId,
    })

    tokenString, err := token.SignedString(signKey)
    if err != nil {
        log.Fatalf("Could not generate token: %v", err)
    }

    fmt.Println(tokenString)
}

```

```node

// using @mux/mux-node@8

import Mux from '@mux/mux-node';
const mux = new Mux();
const myId = ''; // Enter the id for which you would like to get counts here
const myIdType = ''; // Enter the type of ID provided in myId; one of video_id | asset_id | playback_id | live_stream_id
const signingKeyId = ''; // Enter your Mux signing key id here
const privateKeyBase64 = ''; // Enter your Mux base64 encoded private key here

const getViewerCountsToken = async () => {
    return await mux.jwt.signViewerCounts(myId, {
        expiration: '1 day',
        type: myIdType,
        keyId: signingKeyId,
        keySecret: privateKeyBase64,
    });
};

const sign = async () => {
    const token = await getViewerCountsToken();
    console.log(token);
};

sign();

```

```php

<?php

  // Using https://github.com/firebase/php-jwt

  use \Firebase\JWT\JWT;

  $myId = "";       // Enter the id for which you would like to get counts here
  $myIdType = "";   // Enter the type of ID provided in my_id; one of video_id | asset_id | playback_id | live_stream_id
  $keyId = "";      // Enter your signing key id here
  $keySecret = "";  // Enter your base64 encoded private key here

  $payload = array(
  "sub" => $myId,
  "aud" => $myIdType,
  "exp" => time() + 600, // Expiry time in epoch - in this case now + 10 mins
  "kid" => $keyId
  );

  $jwt = JWT::encode($payload, base64_decode($keySecret), 'RS256');

  print "$jwt\n";

?>

```

```python

# This example uses pyjwt / cryptography:
# pip install pyjwt
# pip install cryptography

import jwt
import base64
import time

my_id = ''              # Enter the id for which you would like to get counts here
my_id_type = ''         # Enter the type of ID provided in my_id; one of video_id | asset_id | playback_id | live_stream_id
signing_key_id = ''     # Enter your signing key id here
private_key_base64 = '' # Enter your base64 encoded private key here

private_key = base64.b64decode(private_key_base64)

payload = {
    'sub': my_id,
    'aud': my_id_type,
    'exp': int(time.time()) + 3600, # 1 hour
}
headers = {
    'kid': signing_key_id
}

encoded = jwt.encode(payload, private_key, algorithm="RS256", headers=headers)
print(encoded)

```

```ruby

require 'base64'
require 'jwt'

def sign_url(subject, audience, expires, signing_key_id, private_key, params = {})
    rsa_private = OpenSSL::PKey::RSA.new(Base64.decode64(private_key))
    payload = {sub: subject, aud: audience, exp: expires.to_i, kid: signing_key_id}
    payload.merge!(params)
    JWT.encode(payload, rsa_private, 'RS256')
end

my_id = ''                 # Enter the id for which you would like to get counts here
my_id_type = ''            # Enter the type of ID provided in my_id; one of video_id | asset_id | playback_id | live_stream_id
signing_key_id = ''        # Enter your signing key id here
private_key_base64 = ''    # Enter your base64 encoded private key here

token = sign_url(my_id, my_id_type, Time.now + 3600, signing_key_id, private_key_base64)

```



## 4. Making a Request

Supply the JWT in the resource URL using the `token` query parameter. The API will inspect and validate the JWT to make sure the request is allowed.

Example:

```bash
curl 'https://stats.mux.com/counts?token={JWT}'
```

Response:

```json
{
  "data": [{ "views": 95, "viewers": 94, "updated_at": "2021-09-28T18:21:19Z" }]
}
```

* `views` is the total (non-unique) number of views happening
* `viewers` is the total unique number of views happening

Uniqueness is determined by the `viewer_user_id` metadata field. See the [Metadata guide](/docs/guides/make-your-data-actionable-with-metadata) for details on adding metadata fields.


# Build a Custom Integration
If Mux does not have an SDK specific to your player, you may want to build a custom integration.
Mux provides pre-built SDKs and integrations for most major platforms, but there are some platforms for which there is no pre-built integration. In this case, Mux provides core SDKs for multiple languages, including JavaScript, Java, and Swift on Apple platforms. These core libraries encapsulate the majority of the business and metric calculation logic, while exposing a common API for plugging in individual player integrations.

# Integration Overview

Mux Data SDKs operate by tracking the playback events that occur through the idea of a `Player`. To Mux, a `Player` is an object that encapsulates the playback of videos, exposing APIs for playback events and retrieving playback state information.

In most cases, the `Player` is a single object exposed by the player technology. For instance, for our Video.js integration (`videojs-mux`), the `Player` is just the Video.js [Player object](https://docs.videojs.com/player). However, in some scenarios, there may be one or more underlying player instances that are unified through a single composite API/object. In these cases, the `Player` would be that higher-level object.

There are three major steps for building an integration for a `Player`:

1. Initialize a monitor for the `Player` that is being tracked.
2. Provide a set of callbacks for the core SDK to retrieve player/device information
3. Emit events for each of the important playback events.

The core SDKs share the above common architecture, but there are differences driven primarily by each programming language. The individual documentation for each will describe the exact steps for integration:

* [JavaScript - Building a custom Integration](/docs/guides/data-custom-javascript-integration)
* [Java - Building a custom Integration](/docs/guides/data-custom-java-integration)
* [Swift - Building a custom Integration](/docs/guides/data-custom-swift-integration)

Read on for an overview of each of these steps.

# Initialize a Player monitor

Because each core SDK supports the idea of tracking multiple `Player`s (for instance, if more than one video is being played in the same view/web page), each `Player` must be identifiable with a unique ID. This ID is used when initializing the monitor, as well as when emitting events to the core SDK.

The first step that a custom integration must do is initialize a monitor for the `Player`. This is done differently for each core SDK, but the goal is just to allow the core library to prepare the state necessary for tracking a `Player`.

In this step, some information must be provided:

* the `Player` ID
* some integration-specific metadata
* methods to retrieve information from the `Player` (more on this in a later section)

## Integration-level Metadata

When initializing a monitor for a `Player`, metadata about the integration itself should be passed. The possible fields that should be passed are the following (all are strings):

* `player_software_name`: the name of the underlying player software (i.e. 'Video.js')
* `player_software_version`: the version of this player software
* `player_mux_plugin_name`: the name of the plugin
* `player_mux_plugin_version`: the version of the plugin

# Provide Callbacks

To ease the burden of sending a lot of data with each event that is emitted, the Mux core SDKs accept callbacks that allow the core to retrieve information from the player when necessary. The callbacks required differ by core SDK, so read the appropriate section for the core SDK that you are developing with:

* [JavaScript SDK Callbacks](/docs/guides/data-custom-javascript-integration#provide-callbacks)
* [Java SDK Callbacks](/docs/guides/data-custom-java-integration#provide-callbacks)

For Swift on Apple platforms, there are no callback interfaces to implement. Instead, attach the latest player and video state when dispatching events. See the [Swift custom integration guide](/docs/guides/data-custom-swift-integration).

# Emit events

The majority of the work in an integration is creating and emitting the specific playback events as playback occurs. Most players have a concept of `events` such as `play`, `pause`, `error`, and others, but these events are often named differently depending on the player in use. The Mux core SDKs expect events named in a consistent manner, as defined in [Mux Playback Events](/docs/guides/mux-data-playback-events).

Each core SDK has a different mechanism for emitting these events, so read the appropriate section for the core SDK that you are developing with:

* [JavaScript SDK Emit Events](/docs/guides/data-custom-javascript-integration#emit-events)
* [Java SDK Emit Events](/docs/guides/data-custom-java-integration#emit-events)
* [Swift SDK Emit Events](/docs/guides/data-custom-swift-integration#emit-events)


# Custom JavaScript integration
This is a guide for building a custom JavaScript integration with Mux Data. Build a custom integration if Mux does not already have an SDK for your player.
Mux has a pre-built integration with many HTML5-based video players that are available in the market. Check the SDKs in the [Track your video performance](/docs/guides/track-your-video-performance) guide to see if there is not a pre-built integration for your player.

If there is no integration for a given player, you can install the Mux core JavaScript library (`mux-embed`) and build a custom Mux Data integration.

## Important related docs

Before proceeding, read the following overview: [Building a Custom Integration](/docs/guides/build-a-custom-data-integration).

In addition, Mux has made available a [template repository](https://github.com/muxinc/web-player-framework). This repo is intended to provide the basics for creating a working integration, after implementing the necessary callbacks and methods.

## Include the `mux-embed` library

### Install via yarn or npm (preferred)

Mux utilizes NPM to distribute the core Mux library, `mux-embed`. This library includes the internal state machine for tracking playback, as well as helper methods that may be useful while building the integration. Include `mux-embed` via `yarn` or `npm`, whichever you prefer.

```sh
yarn add mux-embed
```

This will add `mux-embed` as a dependency to your package, and will allow you to upgrade it at any time as new versions are released. Mux follows the semver standard, so updates within a major version will not have any breaking changes.

### Load from the CDN (not preferred)

If you do not use a package manager, you can include the source file from https://src.litix.io/core/4/mux.js directly in a vendor folder. The script has been built to support `npm`/`yarn`, but will also work in a standalone environment.

In either case, once the script is included in your library, you can import it as follows:

```js
import mux from 'mux-embed';

// mux.log - logs message
// mux.utils - includes multiple helper methods for massaging data
```

## Initialize the SDK

Loading and importing `mux` will initialize the SDK. However, for each new player that is being tracked, you need to initialize the SDK for that player. This is done by calling

```js
mux.init(playerID, options);
```

The core `mux` library can track multiple players at once, so it is important to pass in a unique player ID for each player that you want to track. This ID is going to be used in all future calls to the `mux` library for each player.

The `init` method also takes an optional `options` JSON object. This JSON object supports the following keys:

| Property | Required | Type | Description |
| --- | --- | --- | --- |
| `debug` | No | boolean | Controls whether debug log statements are logged to the console |
| `getPlayheadTime` | Yes | function | Callback for playhead position (see below) |
| `getStateData` | Yes | function | Callback for player state (see below) |
| `data` | No | object | Data about the viewer, video, and integration |

Within the `data` object, you should pass any information that is listed in [Metadata](/docs/guides/make-your-data-actionable-with-metadata), which is typically about the viewer or the video itself. In addition, the following should be provided:

| Property | Description | Example |
| --- | --- | --- |
| `player_software_name` | The name of the underlying player software | `'Video.js'` |
| `player_software_version` | The version of the underlying player software | `'1.0.1'` |
| `player_mux_plugin_name` | The name of the plugin being built | Some descriptive string |
| `player_mux_plugin_version` | The version of the plugin being built | A version string |

The only required property underneath `data` is the `env_key`, which is your env\_key found for each environment on https://dashboard.mux.com/environments.

For most integrations, there should be some `data` that is passed down to the integration at runtime in the page/application, such as viewer information and video information, and often times the `env_key`. This information should be merged with the above four properties as a whole before being passed to `mux.init`.

See the [JavaScript Integration Framework](https://github.com/muxinc/web-player-framework) for an example of how this is done.

## Provide callbacks

The JavaScript Core SDK expects two callback functions to be passed in the `options` object to `mux.init`: `getPlayheadTime` and `getStateData`. These callbacks make it so that additional data does not need to be provided when emitting most events.

The `getPlayheadTime` callback is a simple function that should return the accurate playhead position, in milliseconds.

The `getStateData` callback is a function that should return the following properties:

```js
options.getStateData = () => {
  return {
    // Required properties - these must be provided every time this is called
    // You _should_ only provide these values if they are defined (i.e. not 'undefined')
    player_is_paused: player.isPaused(), // Return whether the player is paused, stopped, or complete (i.e. in any state that is not actively trying to play back the video)
    player_width: player.getWidth(), // Return the width, in pixels, of the player on screen
    player_height: player.getHeight(), // Return the height, in pixels, of the player on screen
    video_source_height: player.currentSource().height, // Return the height, in pixels, of the current rendition playing in the player
    video_source_width: player.currentSource().width, // Return the height, in pixels, of the current rendition playing in the player

    // Preferred properties - these should be provided in this callback if possible
    // If any are missing, that is okay, but this will be a lack of data for the customer at a later time
    player_is_fullscreen: player.isFullscreen(), // Return true if the player is fullscreen
    player_autoplay_on: player.autoplay(), // Return true if the player is autoplay
    player_preload_on: player.preload(), // Return true if the player is preloading data (metadata, on, auto are all "true")
    video_source_url: player.src().url, // Return the playback URL (i.e. URL to master manifest or MP4 file)
    video_source_mime_type: player.src().mimeType, // Return the mime type (if possible), otherwise the source type (hls, dash, mp4, flv, etc)
    video_source_duration: secondsToMs(player.getDuration()), // Return the duration of the source as reported by the player (could be different than is reported by the customer)

    // Optional properties - if you have them, send them, but if not, no big deal
    video_poster_url: player.poster().url(), // Return the URL of the poster image used
    player_language_code: player.language() // Return the language code (e.g. `en`, `en-us`)
  };
};
```

## Emit events

The [Playback Events](/docs/guides/mux-data-playback-events) should be emitted as the events are defined. For the JavaScript core SDK, all events are emitted via `mux.emit`. This method takes three arguments:

* the player name (the same used in the call to `mux.init`
* the event name (e.g. `play`)
* (optional) additional data to send along with the event.

All playback events should be emitted as defined except for one: `viewinit` does not need to be emitted for custom JavaScript integrations. This is handled directly by the call to `mux.init`, and also within the helper `mux.emit('videochange', data)`.

For the basic [Playback Events](/docs/guides/mux-data-playback-events), no additional metadata is necessary, as it will be pulled via the callbacks defined above. However, for the ad event and network events, there are additional data fields that should be sent, as documented.

Lastly, when changing the video, the new video metadata should be included within the third parameter.

For instance:

```js
// Emit the `play` event
mux.emit('playerId', 'play');

// Emit an ad event, with additional ad metadata
mux.emit('playerId', 'adrequest', {
  ad_tag_url: "https://pubads.g.doubleclick.net/ads/..."
});

// Changing a video
mux.emit('playerId', 'videochange', {
  video_title: 'New Video Title',
    // ... all other metadata about the video
});
```

## Tearing Down

When you are tearing down the player and want to stop monitoring it, make sure to remove any listeners that you have on the player for sending events to `mux`. After this, make sure to call `mux.emit('playerId', 'destroy');` for your player, so that the core library can clean up any monitoring and end the view.


# Custom Swift integration
This is a guide for building a custom integration with Mux Data in Swift.
Mux has a pre-built integration with Apple's `AVPlayer` for iOS, tvOS, visionOS, and Mac Catalyst applications; for these players, see here: [iOS Integration Guide](/docs/guides/monitor-avplayer).

If the player that you use does not expose the `AVPlayer` instance directly, swaps between multiple instances during playback, or uses some other playback mechanism completely, a custom integration may be needed.

## Important Related Docs

Before proceeding, read the following overview: [Building a Custom Integration](/docs/guides/build-a-custom-data-integration).

In addition, the source code for Mux's integration with Apple's AVPlayer is open source and can be found in the [Mux-Stats-AVPlayer GitHub repository](https://github.com/muxinc/mux-stats-sdk-avplayer). This project is a good example of how to use the Apple core library in building a player integration.

## Include the MuxCore library

### Installing in Xcode with Swift Package Manager

1. In your Xcode project click `File` > `Add Package Dependencies...`

2. In the top-right corner of the modal window paste in the SDK repository URL:

   ```text
   https://github.com/muxinc/stats-sdk-objc.git
   ```

3. Choose a Dependency Rule. Since `MuxCore` follows SemVer, we recommend using `Up to Next Major` starting from `5.0.0`. [Here's an overview of the different SPM Dependency Rules and their semantics](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app#Decide-on-package-requirements).

4. Click `Add Package`.

### Installing in `Package.swift`

Open your `Package.swift` file, add the following to `dependencies`:

```swift
    .package(
      url: "https://github.com/muxinc/stats-sdk-objc",
      from: "5.11.0"
    ),
```

Swift Package Manager docs recommend this form, and it resolves the latest compatible `5.x` release of `MuxCore`.

### Installing with CocoaPods

To include the core Apple library via CocoaPods, add the following pod to your `Podfile`:

```ruby
pod "Mux-Stats-Core", "~> 5.0"
```

This will include our current release of the [core Apple library](https://github.com/muxinc/stats-sdk-objc). There will be no breaking updates within major versions of this library, so you can safely run `pod update`.

Since version 3, `Mux-Stats-Core` has been updated for Xcode 12 and [XCFramework bundle type](https://developer.apple.com/videos/play/wwdc2019/416/).

### Including Manually (not preferred)

If you do not use CocoaPods or Swift Package Manager and wish to include the library manually, an `XCFramework` is included in each release on GitHub: [Releases](https://github.com/muxinc/stats-sdk-objc/releases).

## Initialize the SDK

There is no need to initialize a player monitor for each player that is being tracked, as this happens automatically when events are emitted for a specific player. For the Apple core library, the Environment and Viewer-specific data should be emitted to the SDK globally as follows.

```swift
let environmentData = MUXSDKEnvironmentData()
environmentData.muxViewerId = UIDevice.current.identifierForVendor?.uuidString

let viewerData = MUXSDKViewerData()
viewerData.viewerApplicationName = Bundle.main.bundleIdentifier
// Set additional Viewer data as above

let dataEvent = MUXSDKDataEvent()
dataEvent.environmentData = environmentData
dataEvent.viewerData = viewerData

MUXSDKCore.dispatchGlobalDataEvent(dataEvent)
```

The only field that should be modified within `MUXSDKEnvironmentData` is the `muxViewerId`, which should be a device-specific string. This field is used within the Mux Dashboard as the Viewer ID in the case that a user-specific value is not provided in the metadata.

If you are monitoring playback and delivery of Mux Video assets, you may opt-in to Mux Data inferring your environment details from player HTTP traffic. To opt-in, initialize `MUXSDKCustomerPlayerData` with `environmentKey` set to `nil`.

`MUXSDKViewerData` is where you can provide app, device, OS, and connection information such as application name/version, device model/category, OS family/version, and connection type. For the current public field list, inspect the headers in the latest [MuxCore release](https://github.com/muxinc/stats-sdk-objc/releases/latest).

See the [AVPlayer integration](https://github.com/muxinc/mux-stats-sdk-avplayer) for example values used in a production Apple integration.

## Emit events

For the Apple core SDK, there are two types of events that should be emitted: data events and playback events. Data events are events that update metadata about the video or view, whereas playback events are those described here: [Mux Playback Events](/docs/guides/mux-data-playback-events).

All events are emitted to a specific `Player`, so make sure to include the unique player ID with each event emitted.

### Data Events

Data events are emitted via `MUXSDKCore.dispatchEvent(_:forPlayer:)`, and should be emitted when any of the following pieces of metadata change:

`MUXSDKVideoData` fields commonly updated by custom integrations include:

| Property | Description |
| --- | --- |
| `videoSourceWidth` | Width of the video currently being played, in pixels |
| `videoSourceHeight` | Height of the video currently being played, in pixels |
| `videoSourceIsLive` | Whether the video currently being played is live or not |
| `videoSourceDuration` | The duration, in milliseconds, of the video currently being played |
| `videoSourceAdvertisedBitrate` | The bitrate of the current rendition being played, in bits per second |
| `videoSourceFrameDrops` | The total number of dropped video frames for the current View |

Other metadata that should also be sent via data events when it changes:

* `MUXSDKCustomerPlayerData`
* `MUXSDKCustomerVideoData`
* `MUXSDKCustomerViewData`
* `MUXSDKCustomerViewerData`
* `MUXSDKCustomData`

For more details on the customer metadata models, see [Make your data actionable](/docs/guides/make-your-data-actionable-with-metadata#iosandroid-metadata). For the current public field list across the Apple SDK headers, inspect the latest [MuxCore release](https://github.com/muxinc/stats-sdk-objc/releases/latest).

When any of the above fields change, do the following:

* Create one or more instances of `MUXSDKVideoData`, `MUXSDKCustomerPlayerData`, `MUXSDKCustomerVideoData`, and `MUXSDKCustomerViewData` depending on what changed
* Assign all properties with the most recent value via the helper methods to the appropriate instance of data
* Attach these to an instance of `MUXSDKDataEvent`
* Emit this `MUXSDKDataEvent` via `MUXSDKCore.dispatchEvent(_:forPlayer:)`

For example, when the resolution of the video being played back changes (such as in adaptive streaming), the following should be done:

```swift
// Prepare the data update
let videoData = MUXSDKVideoData()
videoData.videoSourceWidth = width as NSNumber
videoData.videoSourceHeight = height as NSNumber

// Put it within a MUXSDKDataEvent
let dataEvent = MUXSDKDataEvent()
dataEvent.videoData = videoData

// Emit the event
MUXSDKCore.dispatchEvent(dataEvent, forPlayer: playerName)
```

### Playback Events

The [Mux Playback Events](/docs/guides/mux-data-playback-events) should be emitted as the events are defined in the referenced document. With regards to naming, the names align with those in the document, with the following changes: `MUXSDK` is appended in front of the name, the name itself is PascalCased, and `Event` is appended at the end. For instance, for `playerready`, the corresponding event is `MUXSDKPlayerReadyEvent`.

With each playback event that is emitted, the following fields within `MUXSDKPlayerData` should be included with the latest values:

| Property | Description |
| --- | --- |
| `playerMuxPluginName` | The name of the integration being built, as a string |
| `playerMuxPluginVersion` | The version of the integration being built, as a string |
| `playerSoftwareName` | The name of the player software (for example `AVPlayer` or your custom player name) |
| `playerLanguageCode` | The language code, such as `en-US`, of the player UI localization |
| `playerWidth` | The width of the player, in logical pixels |
| `playerHeight` | The height of the player, in logical pixels |
| `playerIsFullscreen` | Whether the player is currently displayed in fullscreen |
| `playerIsPaused` | Whether the player is currently paused, meaning not playing or trying to play |
| `playerPlayheadTime` | The current playhead time of the player, in milliseconds |

`MUXSDKPlayerData` also exposes additional fields beyond the required set above. For the current public field list, inspect the headers in the latest [MuxCore release](https://github.com/muxinc/stats-sdk-objc/releases/latest).

For instance, when emitting the `MUXSDKPlayerReadyEvent`, it should look like the following:

```swift
// Get the player data
let playerData = MUXSDKPlayerData()

// Set the player data information
playerData.playerMuxPluginName = "Sample Custom Player"
// ... repeat the above for all values within `MUXSDKPlayerData`

// Emit the event
let event = MUXSDKPlayerReadyEvent()
event.playerData = playerData

MUXSDKCore.dispatchEvent(event, forPlayer: playerName)
```

In addition to the above data fields, for ad and network events there are additional data fields that should be sent. These are documented alongside the events described in [Mux Playback Events](/docs/guides/mux-data-playback-events), and follow similar naming conventions.

In particular:

* Network throughput events should be emitted as `MUXSDKRequestBandwidthEvent`s, with the addition of `MUXSDKBandwidthMetricData` set on the event via `bandwidthMetricData`
* If your player gives you access to your stream's rendition list, you can use the `renditionLists` property of `MUXSDKBandwidthMetricData` to track a stream's renditions with their resolution, framerate, bitrate, and RFC `CODECS` tag ([ref](https://datatracker.ietf.org/doc/html/rfc6381))
* Ad events are emitted via a special method, `dispatchAdEvent`, and details can be seen within [Mux's IMA integration for AVPlayer](https://github.com/muxinc/mux-stats-google-ima)

Lastly, for the `MUXSDKRenditionChangeEvent`, you should make sure to dispatch a `MUXSDKDataEvent` with the latest updated `MUXSDKVideoData` immediately before dispatching the `MUXSDKRenditionChangeEvent`.

## Sample event sequence

There are multiple steps in setting up and tracking a view correctly. A very simple sequence of events to track a basic playback would look like the following steps:

1. Dispatch a global data event with the environment and viewer data
2. Dispatch the `MUXSDKViewInitEvent` with the current state of the player and video
3. Dispatch a `MUXSDKDataEvent` with the updated `MUXSDKCustomerVideoData` and `MUXSDKCustomerPlayerData` for the current video view
4. Dispatch the rest of the [Mux Playback Events](/docs/guides/mux-data-playback-events) (e.g. `MUXSDKPlayerReadyEvent`, `MUXSDKPlayEvent`, `MUXSDKPlayingEvent`, `MUXSDKTimeUpdateEvent`, etc), each time with the updated current state of the player

Note: For each Playback Event and `MUXSDKViewInitEvent` that is dispatched, the current state of the player and video data (`MUXSDKPlayerData` and `MUXSDKVideoData`) should be attached to the event prior to dispatching the event.

```swift
// First, emit the global data event setting up the information about
// the player. This will likely only be called once within your application
// and does not need to be called for each player that is tracked.
let globalDataEvent = MUXSDKDataEvent()
globalDataEvent.environmentData = environmentData
globalDataEvent.viewerData = viewerData
MUXSDKCore.dispatchGlobalDataEvent(globalDataEvent)

// Prepare the view before you emit any other playback events
let viewInitEvent = MUXSDKViewInitEvent()
viewInitEvent.playerData = playerData
MUXSDKCore.dispatchEvent(viewInitEvent, forPlayer: playerName)

// Dispatch data about the view itself.
// Note: customerPlayerData must include your environment key.
let dataEvent = MUXSDKDataEvent()
dataEvent.customerPlayerData = customerPlayerData
dataEvent.customerVideoData = customerVideoData
MUXSDKCore.dispatchEvent(dataEvent, forPlayer: playerName)

// Emit playback events
let playerReadyEvent = MUXSDKPlayerReadyEvent()
playerReadyEvent.playerData = playerData
MUXSDKCore.dispatchEvent(playerReadyEvent, forPlayer: playerName)

// When the player begins to attempt playback
let playEvent = MUXSDKPlayEvent()
playEvent.playerData = playerData
MUXSDKCore.dispatchEvent(playEvent, forPlayer: playerName)

// When the player actually displays first moving frame
let playingEvent = MUXSDKPlayingEvent()
playingEvent.playerData = playerData
MUXSDKCore.dispatchEvent(playingEvent, forPlayer: playerName)

// ... and repeat for all of the playback events
```

## Additional methods

Most of the events are signaled as listed above. However, there are a few cases of events that require additional work.

### Reporting Network Conditions

Versions `5.8.0` and later include `MUXSDKNetworkChangeEvent`. This allows you to report changes in network connection type and optionally if the connection is in low data mode. This event should be posted as soon as network information is available after `viewinit` (`MUXSDKViewInitEvent`) and whenever connection type or low data mode changes.

A complete example implementation is available in the [AVPlayer monitoring SDK's `NetworkMonitor.swift`](https://github.com/muxinc/mux-stats-sdk-avplayer/blob/master/Sources/MUXSDKStatsInternal/NetworkMonitor.swift).

For example:

```swift
let networkChangeEvent = MUXSDKNetworkChangeEvent(
  viewerConnectionType: .wifi,
  viewerConnectionIsLowDataMode: isLowDataMode as NSNumber?
)

MUXSDKCore.dispatchEvent(networkChangeEvent, forPlayer: playerName)
```

### Changing the Video

In order to change the video within a player, there are a few events that need to be fired in sequence. You can see the implementation of this within the [muxinc/mux-stats-sdk-avplayer](https://github.com/muxinc/mux-stats-sdk-avplayer) code. You should do the following:

1. Dispatch a `viewend` event
2. Dispatch a `viewinit` event
3. Dispatch a `MUXSDKDataEvent` with the new video's `MUXSDKCustomerVideoData`, with the `videoChange` property set to `true`

If at various times the same underlying video stream needs to be monitored as effectively separate videos and separate Data views, two additional events, `play` and `playing`, need to be dispatched.

The following are the required steps from start to finish:

1. As before, dispatch a `viewend` event
2. As before, dispatch a `viewinit` event
3. As before, dispatch a `MUXSDKDataEvent` with the new video's `MUXSDKCustomerVideoData`, with the `videoChange` property set to `true`
4. Dispatch a `play` event
5. Dispatch a `playing` event

```swift
// 1. End the current view
let viewEndEvent = MUXSDKViewEndEvent()
viewEndEvent.playerData = playerData
MUXSDKCore.dispatchEvent(viewEndEvent, forPlayer: playerName)

// 2. Start the next view
let viewInitEvent = MUXSDKViewInitEvent()
viewInitEvent.playerData = playerData
MUXSDKCore.dispatchEvent(viewInitEvent, forPlayer: playerName)

// 3. Attach the new video's metadata and mark this as a video change
let dataEvent = MUXSDKDataEvent()
dataEvent.customerVideoData = customerVideoData
dataEvent.videoChange = true
MUXSDKCore.dispatchEvent(dataEvent, forPlayer: playerName)

// 4. If this should be tracked as a fresh view in the same stream,
// dispatch play
let playEvent = MUXSDKPlayEvent()
playEvent.playerData = playerData
MUXSDKCore.dispatchEvent(playEvent, forPlayer: playerName)

// 5. Then dispatch playing once the first moving frame is displayed
let playingEvent = MUXSDKPlayingEvent()
playingEvent.playerData = playerData
MUXSDKCore.dispatchEvent(playingEvent, forPlayer: playerName)
```

### Sending Error events

Your custom integration is able to dispatch error events associated with the current view. These errors can get alerted on and are also visually indicated on the event timeline shown for that view.

When dispatching errors your custom integration can provide additional error metadata with Error Categorization. This section will cover several examples of dispatching errors using the Swift integration. You can find [more general information on Error Categorization here](/docs/guides/error-categorization).

<Callout type="info">
  Any error categories specified by your custom integration can be configured to be overridden based on the player error code. [See the Error Categorization guide for more details](/docs/guides/error-categorization#2-configuring-error-categorization).
</Callout>

This example dispatches an error event that Mux will categorize as a fatal playback error unless a different default for the player error code applies.

```swift
let errorEvent = MUXSDKErrorEvent(context: playerErrorContext)

// Configure any custom video or view data if necessary
let playerData = MUXSDKPlayerData()
playerData.playerErrorCode = playerErrorCode
playerData.playerErrorMessage = playerErrorMessage
playerData.playerPlayheadTime = playerPlayheadTime
errorEvent.playerData = playerData
// ... repeat for any other `MUXSDKPlayerData` properties if they've changed

MUXSDKCore.dispatchEvent(errorEvent, forPlayer: playerName)
```

This example dispatches an error that Mux will categorize as a warning unless a different default for the player error code applies.

```swift
let errorEvent = MUXSDKErrorEvent(severity: .warning, context: playerErrorContext)

// Configure any custom video or view data if necessary
let playerData = MUXSDKPlayerData()
playerData.playerErrorCode = playerErrorCode
playerData.playerErrorMessage = playerErrorMessage
playerData.playerPlayheadTime = playerPlayheadTime
errorEvent.playerData = playerData
// ... repeat for any other `MUXSDKPlayerData` properties if they've changed

MUXSDKCore.dispatchEvent(errorEvent, forPlayer: playerName)
```

This example dispatches an error that Mux will categorize as a business exception unless a different default for the player error code applies.

```swift
let errorEvent = MUXSDKErrorEvent(
  severity: playerErrorSeverity,
  isBusinessException: true,
  context: playerErrorContext
)

// Configure any custom video or view data if necessary
let playerData = MUXSDKPlayerData()
playerData.playerErrorCode = playerErrorCode
playerData.playerErrorMessage = playerErrorMessage
playerData.playerPlayheadTime = playerPlayheadTime
errorEvent.playerData = playerData
// ... repeat for any other `MUXSDKPlayerData` properties if they've changed

MUXSDKCore.dispatchEvent(errorEvent, forPlayer: playerName)
```

### Destroying the Monitor

When you are tearing down the player and want to stop monitoring it, make sure to remove any listeners that you have on the player for sending events to `MUXSDKCore`. After this, make sure to call `MUXSDKCore.destroyPlayer(_:)` for the name of your player, so that the core library can clean up any monitoring and end the view session.

```swift
// Remove any listeners, observers, timers, polling, or other cleanup
// owned by your integration here.

// Finally, tell MuxCore that this player is no longer being monitored.
MUXSDKCore.destroyPlayer(playerName)
```

<LinkedHeader step={steps[4]} />

### Current release

#### v5.11.0

Improvements:

* Track ranges of content played during a view via `video_playback_range` metric

Fixes:

* Resolves an issue where setting `disablePlayheadRebufferTrackingForPlayerID(_:)` could have no effect
* `view_dropped_frame_count` is now reported from both `MUXSDKVideoData.videoSourceFrameDrops` and `MUXSDKViewData.viewDroppedFramesCount`

### Previous releases

#### v5.10.0

Improvements

* Continues tracking cumulative playing time after `MUXSDKSeekedEvent` and `MUXSDKRebufferEndEvent` when their `playerData.playerIsPaused` is true.
* Updates the constant value for `MUXSDKConnectionTypeNoConnection`

#### v5.9.0

Improvements

* Adds `viewerDeviceName` property to `MUXSDKCustomerViewerData`

#### v5.8.1

Fixes

* Resolves a leak (retain cycle) involving an internal class (`MUXSDKCoreView`).

#### v5.8.0

Improvements

* Adds `MUXSDKNetworkChangeEvent` and predefined values for connection type via `MUXSDKConnectionType`

#### v5.7.1

Fixes:

* Ensure `playbackmodechange` events are sent
* Restores tvOS-specific seeking detection behavior, where a best-effort attempt is made to count the preceding pause as part of the seek. This behavior was missing in 5.7.0, potentially causing changes in metrics.

#### v5.7.0

Updates:

* `playbackmodechange` event added for tracking changes to the presentation of a video (ie, fullscreen, pip, etc)
* Added timing metrics for playing time and ad playing time. These metrics track the wall-clock time spent playing (excluding startup time, rebuffering, seeking, etc)

Improvements:

* Added nullability annotations and nil-handling improvements to most public APIs
* Made most properties of `MUXSDKPlayerData`, `MUXSDKVideoData`, `CustomerData`, et al `nonatomic`
* Handle trackable events via typed delegate method
* Enable link-time optimization by thinLTO
* MUXSDKCore methods may now be called from any thread
* Numerous internal improvements

Fixes:

* Fix HTTP retry delay 1000x higher than intentional
* Wait for HTTP connectivity for beacons

#### v5.6.0

Improvements:

* Allow disabling playhead-based rebuffer tracking via `-[MUXSDKCore disablePlayheadRebufferTrackingForPlayerID:]` and manual dispatch of `MUXSDKRebufferStartEvent` and `MUXSDKRebufferEndEvent`

Fixes:

* Prevent overriding `muxEmbed`, `muxEmbedVersion`, and `muxApiVersion` from `MUXSDKEnvironmentData`
* Individual frameworks within the XCFramework are no longer separately code signed. The parent XCFramework is still signed.

#### v5.5.1

Improvements:

* Removes the deprecation on `MUXSDKCustomerVideoData.videoCDN` (added in v5.5.0)

#### v5.5.0

Improvements:

* Adds CDN change tracking, including automatically via `X-CDN` headers
* The MuxCore frameworks are now built as Mergeable Libraries. See this [WWDC session](https://developer.apple.com/videos/play/wwdc2023/10268/) and the [official docs](https://developer.apple.com/documentation/xcode/configuring-your-project-to-use-mergeable-libraries) for more info. This is not enabled for the CocoaPods build.

Fixes:

* Fixes missing videoData in some instances, including after a rendition change
* Corrects a typo in `viewPrerollAdAssetHostname` causing it not to be sent
* Fixes an issue leading to incorrect `viewPlayingTime` and/or `viewMaxPlayheadPosition`
* Resolves a few naming issues that could cause builds to fail on case-sensitive filesystems

#### v5.4.1

Fixes:

* fix rebuffering ending while player is still buffering in some cases

#### v5.4.0

Improvements:

* Add customer viewer data to `MUXSDKDataEvent` and `MUXSDKTrackableEvent`
* Use consistent umbrella header path so `#import <MuxCore/MuxCore.h>` works on all platforms

#### v5.3.1

Improvements:

* Add `MUXSDKCustomerVideoData.videoCreatorId`

#### v5.3.0

Improvements:

* adds additional dimensions

Fixes:

* adds missing macCatalyst platform to package spec

#### v5.2.0

Improvements:

* expose additional custom dimensions

#### v5.1.2

Fixes:

* dispatch queued up events when receiving `adbreakend`, `aderror` events
* patch memory leak when a player monitor is created and destroyed

#### v5.1.1

Fixes:

* Fully resets all player metrics associated with a previous view that had ended due to a time out when receiving a `viewinit` event.

#### v5.1.0

Improvements:

* Include codec and rendition name in `renditionchange` events
* Add safety checks when player identifier is `nil`

#### v5.0.1

Improvements:

* Include privacy manifest file

#### v5.0.0

Improvements:

* Error events can be categorized with warning or fatal severity levels
* Error events can be categorized as business exceptions
* An error translator can be configured to extend or customize the Core SDK error handling logic

Fixes:

* Player error details such as error code, error context, error message, error severity, and whether the error is a business exception are only sent to Mux when an error event is dispatched.
* Player error details (same as listed above) are no longer deduplicated and are explicitly included with each error event sent to Mux.
* The SDK continues to track watch time after an error event is dispatched based on player playhead progression. To explicitly indicate that watch time should no longer be tracked after an error during a playback session please dispatch a `ViewEnd` event.

#### v4.7.1

Improvements:

* Include privacy manifest file

#### v4.7.0

Improvements:

* Add support for monitoring media on `visionOS`. We recommend testing your `visionOS` SDK integration on both the simulator and a physical device prior to deploying to the App Store.

Fixes:

* Compute correct Video Startup Time if `AdPlayingEvent` occurs a significant time after the view has started
* Ensure seeks are excluded from Video Startup Time in all cases

Known Issues:

* Installation using Cocoapods on `visionOS` is not currently supported. Installation on `iOS` and `tvOS` using Cocoapods is not affected.

#### v4.6.0

API Changes:

* Expose player software name and player software version on `MUXSDKPlayerData`

Improvements:

* Bump beacon interval to 10 seconds to match the other Core SDKs

#### v4.5.2

Improvements:

* Backfill header nullability annotations

#### v4.5.1

Fixes:

* Include at playback time in the calculation for total playback time

#### v4.5.0

Updated:

* Add DRM Type to `MUXSDKCustomerViewData` so you can specify it from another source

Deprecated:

* `MUXSDKDispatcher`'s `handleBatch beaconCollectionDomain: osFamily: jsonDict: callback:` has been deprecated in favor of an overload that takes headers for requests to the collection domain
* `MUXSDKNetworkRequestBuilding` `buildRequestFromURL: eventsJsonDict: error:` has been deprecated in favor of an overload that takes headers for requests to the collection domain

Improvements:

* Performance + Reliability improvements during large events

#### v4.4.2

Fixes:

* Fix ad metadata not being reported

#### v4.4.1

Improvements:

* Update beacon interval from 5s to 10s

#### v4.4.0

* Ad per-ad metadata for Ad events
* Fix strange views when a user seeks into an ad break

#### v4.3.0

* Add DRM Type and Error Context metadata fields

#### v4.2.0

* Add more Custom Dimensions

#### v4.1.1

* Fix Rendition::name misnamed

#### v4.1.0

* Add framerate, codec, and name to rendition properties

#### v4.0.0

* Due to Xcode 14, support for iOS and tvOS versions 9 and 10 have been removed. For more information see the last 'Deprecations' block in the release notes. This may result in a warning for client applications with deployment versions below iOS/tvOS 11

#### v3.14.0

* Split Views with >1 hour of inactivity into new views

#### v3.11.0

* Add inferred environment key support for users of Mux Data and Mux Video
* Expose `MUXSDKEndedEvent` in the public headers

#### v3.10.1

* Add weak self/strong self in closure block to avoid any retain cycles

#### v3.10.0

* Capture experiments values from HLS Session Data

#### v3.9.0

* Add Experiment Fields
* Log sent beacons in debug mode
* Set Xcode build setting `APPLICATION_EXTENSION_API_ONLY` = YES

#### v3.8.0

* Add internal device detection properties
* Add project binary specification file for Carthage support

#### v3.7.0

* Use synchronized to make query data objects thread safe.

#### v3.6.0

* Add transmission time and round trip time to beacon requests
* Add `player_live_edge_program_time`
* Add `player_program_time`

#### v3.5.0

* Allow overriding of viewer information (application name)
* Add nullability specifiers
* Custom beacon collection domains

#### v3.4.0

* Adds the `MUXSDKCustomerData` model
* Adds support for setting custom dimensions

#### v3.3.0

* Automatically build statically linked frameworks
* Remove dependency on `UIKit`

#### v3.2.0

* Add Swift PM support

#### v3.1.0

* Submits a new `mux_embed field`
* Fixes bugs with video start-up time for midroll or postroll ads
* Updates ad tracking to be more accurate
* Tracks `view_playing_time`

#### v3.0.3

* No functional changes, just generating a new release on CocoaPods

#### v3.0.2

* Include linker flags that enable the framework to be built without support for modules.
* Move instance variables out of headers

#### v3.0.0

This release moves the build process to use [XCFramework bundle type](https://developer.apple.com/videos/play/wwdc2019/416/). For iOS, there are no changes required to your application code.

If you are using this SDK with TVOS the name of the module has changed (the `Tv` suffix is no longer needed):

TVOS before 3.0:

```objc
@import MuxCoreTv;
```

TVOS after 3.0:

```objc
@import MuxCore;
```

#### v2.4.1

* (bugfix) Works around an issue where a view with no pre-roll ads, but with midroll and/or postroll ads will cause Mux to update the TTFF value erroneously

#### v3.0.0-beta.0

This release moves the build process to use [XCFramework bundle type](https://developer.apple.com/videos/play/wwdc2019/416/). For iOS, there are no changes required to your application code.

If you are using this SDK with TVOS the name of the module has changed (the `Tv` suffix is no longer needed):

TVOS before 3.0:

```objc
@import MuxCoreTv;
```

TVOS after 3.0:

```objc
@import MuxCore;
```

#### v2.4.0

* Adds support for `player_remote_played` and `view_session_id`.
* In addition to existing options that are provided via the `MUXSDKCustomerPlayerData` and `MUXSDKCustomerVideoData` objects, there is now support for `MUXSDKCustomerViewData`. The `view_session_id` may be set on`MUXSDKCustomerViewData`.

#### v2.3.0

* Update build process for Xcode 12 to exclude arm\_64 architectures when building for simulator. Before Xcode 12, xcodebuild never built arm\_64 slices for the simulator. Now, it does (in preparation for Apple silicon). Because arm\_64 slices now get built for the simulator, `lipo` errors out, because it can't have the same architecture for two different platforms (it already has arm\_64 for the device platform). This is a temporary work around until a later major version release which will use the new `XCFramework` hotness
* bump iOS deploy target to '9.0' in `podspec` and project build settings for Xcode 12 compatibility

#### v2.2.0

* bugfix: Removes erroneously committed logs from the compiled frameworks

#### v2.2.1

* bugfix - ignore scaling calculations when player or source width or height dimension is 0

#### v2.2.0

* Add support for `renditionchange` events
* Add support for `orientationchange` events

#### v2.1.3

* bugfix for request metrics calculation. If we don't have `responseStart`, fallback to `requestStart` in order to calculate throughput

#### v2.1.2

* bugfix - Use monotonically increasing time in Objc client library. Avoids a bug if system time changes during a view.

#### v2.1.1

* Expose `videoSourceUrl` on `MUXSDKCustomerVideoData`. This allows a user to set the videoSourceUrl (along with their other VideoData, in which case any videoSourceUrl that is inferred from the player will be ignored.

#### v2.1.0

* Fix build process for Xcode 11
* Make `player_instance_id` a full uuid-style string
* Make sure to always send `player_instance_id`
* Bump Mux API versions for new collectors/processors


# Custom Java integration
This is a guide for building a custom integration with Mux Data in Java. Build a custom integration if Mux does not already have an SDK for your player.
Mux has a pre-built integration with Google's [ExoPlayer v2](/docs/guides/monitor-exoplayer) and [Android Media Player](/docs/guides/monitor-android-media-player) for Android applications.

If the player that you use does not expose the `ExoPlayer` instance directly, swaps between multiple instances during playback, or uses some other playback mechanism completely (for instance, outside of Android), a custom integration may be needed.

## Important Related Docs

Before proceeding, read the following overview: [Building a Custom Integration](/docs/guides/build-a-custom-data-integration).

In addition, the source code for Mux's integration with Google's ExoPlayer is open source and can be found in the [Mux-Stats-SDK-ExoPlayer GitHub repository](https://github.com/muxinc/mux-stats-sdk-exoplayer). This project is a good example of how to use the Java core library in building a player integration.

## Include the Library

The Mux Core Java library is made available as a JAR file which can be installed using the following methods:

## Option 1: Add Gradle dependency (preferred)

Add the Mux Maven repository to your Gradle file:

```text
repositories {
    maven {
        url "https://muxinc.jfrog.io/artifactory/default-maven-release-local"
    }
}
```

Next, add a dependency on Mux Core (current version is 8.8.0):

```
api 'com.mux:stats.muxcore:8.8.0'
```

## Option 2: Add Maven dependency

Add the Mux repository to your Maven pom.xml:

```xml
<repository>
    <id>mux</id>
    <name>Mux Maven Repository</name>
    <url>https://muxinc.jfrog.io/artifactory/default-maven-release-local</url>
    <releases>
        <enabled>true</enabled>
    </releases>
    <snapshots>
        <enabled>false</enabled>
    </snapshots>
</repository>
```

Next, add a dependency on Mux Core (current version is 7.0.11):

```xml
<dependency>
    <groupId>com.mux</groupId>
    <artifactId>stats.muxcore</artifactId>
    <version>8.8.0</version>
</dependency>
```

## Initialize the SDK

The core Java SDK is initialized by implementing certain interfaces and providing these back to the SDK. In general, the structure used within [MuxBaseExoPlayer](https://github.com/muxinc/mux-stats-sdk-exoplayer/blob/14a65c0b365a1245e500543b976b3b9be1101aaa/MuxExoPlayer/src/main/java/com/mux/stats/sdk/muxstats/MuxBaseExoPlayer.java#L89) should be followed, where you create a class that extends `EventBus` and implements `IPlayerListener`, and then follows the following general steps.

```java
import com.mux.stats.sdk.core.events.EventBus;
import com.mux.stats.sdk.core.model.CustomerPlayerData;
import com.mux.stats.sdk.core.model.CustomerVideoData;
import com.mux.stats.sdk.core.model.CustomerViewData;
import com.mux.stats.sdk.muxstats.IPlayerListener;

public class PlayerListener extends EventBus implements IPlayerListener {
      MuxStats muxStats;

    PlayerListener(Context ctx, ExoPlayer player, String playerName, CustomerPlayerData customerPlayerData, CustomerVideoData customerVideoData, CustomerViewData customerViewData) {
        super();
        this.player = new WeakReference<>(player);
        state = PlayerState.INIT;
        MuxStats.setHostDevice(new MuxDevice(ctx));
        MuxStats.setHostNetworkApi(new MuxNetworkRequests());
        muxStats = new MuxStats(this, playerName, customerPlayerData, customerVideoData, customerViewData);
        addListener(muxStats);
    }
}
```

The above does the following:

1. Initializes the `EventBus` superclass
2. Sets the host device to a new instance of a class that implements `IDevice`
3. Sets the host network API to a new instance of a class that implements `INetworkRequest`
4. Instantiates a new instance of `MuxStats`, passing itself (a class that implements `IPlayerListener`) along with metadata
5. Adds muxStats as a listener for `this`'s events (via EventBus)

The `IDevice`, `INetworkRequest`, and `IPlayerListener` interfaces are described in the next section, as they provide the majority of the functionality aside from the actual emitting of events.

## Provide Callbacks

The core Java SDK relies heavily on callbacks, via implemented interfaces. These interfaces provide necessary metadata as well as core functionality that may be different depending on your Java environment.

## `IDevice`

The `IDevice` interface provides device-specific information to the core library, which is used as metadata attached to each view.

```java
package com.mux.stats.sdk.muxstats;

public interface IDevice {
    // Return the hardware name (e.g. Build.HARDWARE)
    String getHardwareArchitecture();
    // Return the OS (e.g. Android)
    String getOSFamily();
    // Return the OS version
    String getOSVersion();
    // Return the device manufacturer (e.g. Build.MANUFACTURER)
    String getManufacturer();
    // Return the model name (e.g. Build.MODEL)
    String getModelName();
    // Return the player version
    String getPlayerVersion();
    // Return a unique identifier for this device
    String getDeviceId();
    // Return the name of the running application
    String getAppName();
    // Return the version of the running application
    String getAppVersion();
    // Return the name of the plugin (e.g. exoplayer-mux)
    String getPluginName();
    // Return the version of the plugin
    String getPluginVersion();
    // Return the player software (e.g. 'ExoPlayer')
    String getPlayerSoftware();
    // Return the network connection type (e.g. 'wifi', 'cellular', 'ethernet')
    String getNetworkConnectionType();
    // Return milliseconds since epoch, ideally from a
    // monotonically increasing clock. For instance, in
    // ExoPlayer and Android, we suggest
    // android.os.SystemClock.elapsedRealtime
    long getElapsedRealtime();
    // Return provide a mechanism to log an output, for instance to logcat
    void outputLog(String tag, String msg);
}
```

There must be an instance of a class that implements the `IDevice` interface, and this should be provided to `MuxStats.setHostDevice` prior to instantiating an instance of `MuxStats`.

You can see the implementation of `IDevice` within Mux's ExoPlayer integration within [MuxBaseExoPlayer.java](https://github.com/muxinc/mux-stats-sdk-exoplayer/blob/14a65c0b365a1245e500543b976b3b9be1101aaa/MuxExoPlayer/src/main/java/com/mux/stats/sdk/muxstats/MuxBaseExoPlayer.java#L605).

## INetworkRequest

The `INetworkRequest` interface defines the methods that the Mux core SDK requires in order to make the necessary network requests.

```java
package com.mux.stats.sdk.muxstats;

/**
 * <b>MuxStats</b> will use this interface implementation to send events and metrics to the backend,
 * overlaying player SDK need to implement this interface and set it to the <b>MuxStats</b> via
 * {@link MuxStats#setHostNetworkApi(INetworkRequest)} method.
 * Always set this interface before instantiating the <b>MuxStats</b> instance.
 */
public interface INetworkRequest {

  /**
   * This interface is used to get from the network implementation that
   * {@link #postWithCompletion(String, String, String, Hashtable, IMuxNetworkRequestsCompletion)}
   * have succeed or not.
   *
   * @deprecated please prefer {@link IMuxNetworkRequestsCompletion2}
   */
  @Deprecated
  interface IMuxNetworkRequestsCompletion {

    /**
     * Called by the implementation object when
     * {@link #postWithCompletion(String, String, String, Hashtable,
     * IMuxNetworkRequestsCompletion)} is called.
     *
     * @param result if post was completed successfully or not.
     */
    void onComplete(boolean result);
  }

  interface IMuxNetworkRequestsCompletion2 {
    void onComplete(boolean result, Map<String, List<String>> headers);
  }

  /**
   * Perform a HTTP GET request.
   *
   * @param url url to send get request to.
   */
  void get(URL url);

  /**
   * Perform HTTP POST request.
   *
   * @param url url to send post request to.
   * @param body post request body.
   * @param headers post request headers.
   */
  void post(URL url, JSONObject body, Hashtable<String, String> headers);

  /**
   * Perform network request with confirmation callback, type of request is left to the
   * implementation.
   *
   * @param domain domain to send beacons to.
   * @param envKey backend key used to authenticate with backend.
   * @param body request body.
   * @param headers request headers.
   * @param callback callback triggered after the request signalling the request status.
   */
  void postWithCompletion(String domain, String envKey, String body,
      Hashtable<String, String> headers, IMuxNetworkRequestsCompletion callback);

  /**
   * Perform a network request with the given completion handler. If implemented, the completion
   * handler will also report the response headers for the call
   *
   * This method has a default implementation, which does not report response headers, and delegates
   * to the other postWithCompletion
   *
   * @param domain domain to send beacons to.
   * @param envKey backend key used to authenticate with backend.
   * @param body request body.
   * @param headers request headers.
   * @param callback callback triggered after the request signalling the request status.
   */
  default void postWithCompletion(String domain, String envKey, String body,
      Hashtable<String, String> headers, IMuxNetworkRequestsCompletion2 callback) {
    postWithCompletion(domain, envKey, body, headers, result -> callback.onComplete(result, null));
  }
}
```

There must be an instance of a class that implements the `INetworkRequest` interface, and this should be provided to `MuxStats.setHostNetworkApi` prior to instantiating an instance of `MuxStats`.

You can see the implementation of `INetworkRequest` within Mux's ExoPlayer integration within [MuxNetworkRequests.java](https://github.com/muxinc/mux-stats-sdk-exoplayer/blob/14a65c0b365a1245e500543b976b3b9be1101aaa/MuxExoPlayer/src/main/java/com/mux/stats/sdk/muxstats/MuxNetworkRequests.java).

## IPlayerListener

The `IPlayerListener` interface defines the callbacks that `MuxStats` will utilize to retrieve player state information.

```java
package com.mux.stats.sdk.muxstats;

public interface IPlayerListener {
    // Return the current playhead position in milliseconds
    // The playhead position must be updated at least every 250 milliseconds,
    // but can be updated more often than this.
    long getCurrentPosition();
    // Return the MIME type of the content being played (e.g. "video/mp4"
    // or "application/x-mpegUrl" etc)
    String getMimeType();
    // Return the width of the source, in pixels
    Integer getSourceWidth();
    // Return the height of the source, in pixels
    Integer getSourceHeight();
    // Return the current advertised bitrate, in bits per second
    Integer getSourceAdvertisedBitrate();
    // Return the current advertised framerate
    Float getSourceAdvertisedFramerate();
    // Return the current codec string
    String getSourceCodec();
    // Return the source duration, in milliseconds
    Long getSourceDuration();
    // Return whether the player is currently paused (i.e. not actively
    // trying to play the content). This should return true if the player
    // is not actively playing, rebuffering, or starting up.
    boolean isPaused();
    // Return whether the player is currently buffering content (e.g. not
    // playing back because the buffer is not full enough).
    boolean isBuffering();
    // Return the width of the player, in logical pixels
    int getPlayerViewWidth();
    // Return the height of the player, in logical pixels
    int getPlayerViewHeight();
    // Return the current playback position as based off of the PDT tags
    Long getPlayerProgramTime();
    // Return the time of the furthest position in the manifest as based
    // off of the PDT tags in the stream
    Long getPlayerManifestNewestTime();
    // Return the configured holdback value for a live stream (ms)
    Long getVideoHoldback();
    // Return the configured holdback value for parts in a low latency live
    // stream (ms)
    Long getVideoPartHoldback();
    // Return the configured target duration for parts in a low latency
    // live stream (ms)
    Long getVideoPartTargetDuration();
    // Return the configured target duration for segments in a live
    // stream (ms)
    Long getVideoTargetDuration();
}
```

The class that implements `IPlayerListener` serves as the interface between `MuxStats` and the actual player API, and is provided when creating an instance of `MuxStats`.

You can see the implementation of `IPlayerListener` within Mux's ExoPlayer integration within [MuxBaseExoPlayer.java](https://github.com/muxinc/mux-stats-sdk-exoplayer/blob/14a65c0b365a1245e500543b976b3b9be1101aaa/MuxExoPlayer/src/main/java/com/mux/stats/sdk/muxstats/MuxBaseExoPlayer.java#L64). This superclass is used to handle the base API interaction, and is subclassed by each individual `MuxStatsExoPlayer.java` to handle the varying APIs that ExoPlayer exposes with each of its minor versions (such as [this one for r2.11.1](https://github.com/muxinc/mux-stats-sdk-exoplayer/blob/14a65c0b365a1245e500543b976b3b9be1101aaa/MuxExoPlayer/src/r2_11_1/java/com/mux/stats/sdk/muxstats/MuxStatsExoPlayer.java)).

## Emit Events

## Playback Events

For the Java core SDK, the [Mux Playback Events](/docs/guides/mux-data-playback-events) are emitted via the `dispatch` method that is inherited from the `EventBus` class. In order to emit a given event, you must first instantiate an instance of the event class that you are trying to emit.

```java
import com.mux.stats.sdk.core.events.EventBus;
import com.mux.stats.sdk.core.model.CustomerPlayerData;
import com.mux.stats.sdk.core.model.CustomerVideoData;
import com.mux.stats.sdk.muxstats.IPlayerListener;
import com.mux.stats.sdk.events.playback.PlayEvent;

public class PlayerListener extends EventBus implements IPlayerListener {
      MuxStats muxStats;

    PlayerListener(Context ctx, ExoPlayer player, String playerName, CustomerPlayerData customerPlayerData, CustomerVideoData customerVideoData) {
        super();
        this.player = new WeakReference<>(player);
        state = PlayerState.INIT;
        MuxStats.setHostDevice(new MuxDevice(ctx));
        MuxStats.setHostNetworkApi(new MuxNetworkRequests());
        muxStats = new MuxStats(this, playerName, customerPlayerData, customerVideoData);
        addListener(muxStats);
    }

    // When the player begins trying to play back the video
    public void onPlay() {
        dispatch(new PlayEvent(null));
    }
}
```

While not necessary, each playback event can take an optional parameter of `PlayerData`, if certain information of the player has changed. This object has the following properties:

| Property | Description |
| --- | --- |
| `playerMuxPluginName` | The name of the integration being built, as a string |
| `playerMuxPluginVersion` | The version of the integration being built, as a string |
| `playerSoftwareName` | The name of the player software (e.g. `Exoplayer`, etc) |
| `playerSoftwareLanguageCode` | The language code (e.g. en-US) of the player UI localization |
| `playerWidth` | The width of the player, in logical pixels |
| `playerHeight` | The height of the player, in logical pixels |
| `playerIsFullscreen` | Boolean of whether the player is currently displayed in full screen or not |
| `playerIsPaused` | Boolean of whether the player is currently paused (i.e. not playing or trying to play) |
| `playerPlayheadTime` | The current playhead time of the player, in milliseconds |

Most of these properties are pulled automatically via the `IPlayerListener` interface, so there is no need to provide these values. You will need to emit all required [Playback Events](/docs/guides/mux-data-playback-events) in order to make a working integration.

<Callout type="info">
  Prior to v5.0.0, the SeekingEvent was not necessary. As of v5.0.0, this is now a required event to be emitted by the player integration.

  Prior to v6.0.0, the RebufferStartEvent and RebufferEndEvent were not necessary. As of v6.0.0 and newer, these events must be emitted by the player integration.
</Callout>

## Data Events

There is an additional type of event that is permissible, the `DataEvent`. This event is emitted the same way (via `EventBus.dispatch`), but should be used when some metadata has changed outside of a playback event. Examples of this are when you may have any of the metadata within `CustomerVideoData`, `CustomerPlayerData`, `EnvironmentData`, `VideoData`, or `ViewerData` changes. This event likely will not be needed, but it is provided in the case that it might be useful. Mux does not use this at all within the [ExoPlayer integration](https://github.com/muxinc/mux-stats-sdk-exoplayer).

### Experiment Values

Values for Experiments can be tracked via the tags of an HLS stream's main playlist. The values in the `SessionTags` will override the values provided via objects like `CustomerPlayerData` or `CustomerVideoData`. When your player has loaded the experiment values (such as through and HLS stream's `X-SESSION-DATA` tags), you may pass them to `MuxStats::setSessionData(List<SessionTag>)`

## Bandwidth Throughput Events

For the bandwidth throughput and latency related events, the structure is slightly different. Rather than having a specific class for each event, there is one high level network event, the `RequestBandwidthEvent`. This event exposes a method, `setBandwidthMetricData(BandwidthMetricData)`, which is used to provide all information about the event. In particular, the `BandwidthMetricData` class exposes a property (via a getter/setter) named `requestEventType`, which is a string that will match the event names as defined in [Playback Events - Bandwidth Throughput Events](/docs/guides/mux-data-playback-events#bandwidth-throughput-events).

The implementation of these events for the Mux ExoPlayer integration can be found [here in this file](https://github.com/muxinc/mux-stats-sdk-exoplayer/blob/14a65c0b365a1245e500543b976b3b9be1101aaa/MuxExoPlayer/src/main/java/com/mux/stats/sdk/muxstats/MuxBaseExoPlayer.java#L751), from the linked line until the end of the file. This can serve as a good example of how to implement these events, though they are not necessary for a functioning integration.

## Ad Events

In the case that your player supports advertising, you should instrument the ad events that are defined in [Mux Playback Events - Ad Events](/docs/guides/mux-data-playback-events#ad-events). Ad events are emitted just as normal events would be, but the ad events should have the ad metadata included via a `ViewData` instance that is attached to each event via `setViewData`. For instance, to emit an `AdPlayEvent`, you should do the following:

```
AdData adData = new AdData();
adData.setAdCreativeId(creativeId);
adData.setAdId(adId);
AdPlayEvent adPlayEvent = new AdPlayEvent(null);
adPlayEvent.setAdData(adData);
dispatch(adPlayEvent);
```

The implementation of ad events within Mux's ExoPlayer integration, on top of Google's IMA SDK, can be found within [AdsImaSDKListener.java](https://github.com/muxinc/mux-stats-sdk-exoplayer/blob/14a65c0b365a1245e500543b976b3b9be1101aaa/MuxExoPlayer/src/main/java/com/mux/stats/sdk/muxstats/AdsImaSDKListener.java), and can serve as a good example.

## Changing the video

Rather than requiring an event to be emitted for changing the video, `MuxStats` exposes two helper methods: `videoChange` and `programChange`. These methods encapsulate the logic necessary to end a view and start a new one, and both take an instance of `CustomerVideoData` containing the metadata about the new video being played.

You should call one of these methods when a new video is being loaded into an already-tracked player.

There is one critical difference between `videoChange` and `programChange` - `programChange` is intended to be used in the case that the underlying video changes *within the same stream*. An example of this would be within live linear playback, where the underlying program changes without the player having to reload a new stream.

In the case that the player is loading a new HLS/Dash/MP4 video, you should use `videoChange`.

```
CustomerVideoData customerVideoData = new CustomerVideoData(null);
customerVideoData.setVideoTitle("New Video Title");
// Add other video metadata here
muxStats.videoChange(customerVideoData);
```

## Reporting Network Changes

If your player is able to detect changes in network connectivity (for instance, switching from cellular to wifi), you can report these changes to Mux by calling the `networkChange` method on your instance of `MuxStats`. This method takes a single parameter, a string representing the new network connection type. Valid values are `"wifi"`, `"cellular"`, `"wired"`, `"other"`, and `"no_connection"`.

There's an overload of `networkChange` that also takes a boolean called `isLowDataMode`, which indicates whether the current network connection is in low data mode. This can be useful for mobile connections where the user has enabled a low data usage setting. This can be null if you don't know whether low data mode is enabled.

```java
// change to wifi
muxStats.networkChange("wifi");
// change to cellular with low data mode enabled
muxStats.networkChange("cellular", true);
```

## Sending Error events

Your custom integration is able to dispatch error events associated with the current view. These errors can get alerted on and are also visually indicated on the event timeline shown for that view.

When dispatching errors your custom integration can provide additional error metadata with Error Categorization. This section will cover several examples of dispatching errors using the Java SDK. You can find [more general information on Error Categorization here](/docs/guides/error-categorization).

This example illustates how to construct and send different categories of error events.

<Callout type="info">
  Any error categories specified by your custom integration can be configured to be overridden based on the player error code. [See the Error Categorization guide for more details](/docs/guides/error-categorization#2-configuring-error-categorization).
</Callout>

```java
import com.mux.stats.sdk.core.events.EventBus;
import com.mux.stats.sdk.core.model.CustomerPlayerData;
import com.mux.stats.sdk.core.model.CustomerVideoData;
import com.mux.stats.sdk.muxstats.IPlayerListener;
import com.mux.stats.sdk.events.playback.PlayEvent;
import com.mux.stats.sdk.events.playback.ErrorEvent;

public class PlayerListener extends EventBus implements IPlayerListener {
    MuxStats muxStats;

    PlayerListener(Context ctx, ExoPlayer player, String playerName, CustomerPlayerData customerPlayerData, CustomerVideoData customerVideoData) {
        super();
        this.player = new WeakReference<>(player);
        state = PlayerState.INIT;
        MuxStats.setHostDevice(new MuxDevice(ctx));
        MuxStats.setHostNetworkApi(new MuxNetworkRequests());
        muxStats = new MuxStats(this, playerName, customerPlayerData, customerVideoData);
        addListener(muxStats);
    }

    // When the player begins trying to play back the video
    public void onPlay() {
        dispatch(new PlayEvent(null));
    }

    // Call from onPlayerError() with parameters appropriate to your integration. Dispatches an error event that Mux will categorize as a fatal playback error by default
    public void onPlaybackError(String errorCode, String errorMessage, String errorContext) {
        PlayerData playerData = new PlayerData();
        playerData.setErrorCode(errorCode);
        playerData.setErrorMessage(errorMessage);

        ErrorEvent errorEvent = new ErrorEvent(playerData, errorContext);

        dispatch(errorEvent);
    }

    // Call from onPlayerError() with parameters appropriate to your integration. Dispatches an error event that Mux will categorize as a warning by default
    public void onPlaybackWarning(String errorCode, String errorMessage, String errorContext) {
        PlayerData playerData = new PlayerData();
        playerData.setErrorCode(errorCode);
        playerData.setErrorMessage(errorMessage);

        ErrorEvent errorEvent = new ErrorEvent(playerData, errorContext, ErrorSeverity.ErrorSeverityWarning);

        dispatch(errorEvent);
    }

    // Call from onPlayerError() with parameters appropriate to your integration. Dispatches an error event that Mux will categorize as a business exception by default
    public void onBusinessException(String errorCode, String errorMessage, String errorContext) {
        PlayerData playerData = new PlayerData();
        playerData.setErrorCode(errorCode);
        playerData.setErrorMessage(errorMessage);

        // This method does not set an explicit error severity, see below for an example method that does.
        ErrorEvent errorEvent = new ErrorEvent(playerData, errorContext);
        errorEvent.setIsBusinessException(true);

        dispatch(errorEvent);
    }

    // Call from onPlayerError() with parameters appropriate to your integration. Dispatches an error event that Mux will categorize as a business exception by default
    public void onBusinessException(String errorCode, String errorMessage, String errorContext, ErrorSeverity severity) {
        PlayerData playerData = new PlayerData();
        playerData.setErrorCode(errorCode);
        playerData.setErrorMessage(errorMessage);

        ErrorEvent errorEvent = new ErrorEvent(playerData, errorContext, severity, true);

        dispatch(errorEvent);
    }
}
```

## Tearing Down

There is no `destroy` event for the core Java SDK. Instead, the `release` method is exposed on `MuxStats` that cleans up all tracking and releases all references held within the core library. This method should be called when you release your player instance, and after calling `release`, the instance of `muxStats` will be unusable.

<LinkedHeader step={steps[4]} />

### Current release

#### v8.9.2

Updates:

* Update json.org to version `20240303`

### Previous releases

#### v8.9.1

Fixes:

* Remove long-unused dependency on commons-math. If you were getting commons-math from us, you'll need to add a dependency for it

#### v8.9.0

New:

* Track ranges of content played during a view via `video_playback_range` metric

Improvements:

* NetworkChangeEvent no longer accepts null values. If the network disconnects, use `no_connection`
* `null` values from `IDevice.getNetworkConnectionType` are now coerced to `"no_connection"`

#### v8.8.2

Fixes:

* count cumulative playing time after `seeked` events

#### v8.8.1

Fixes:

* start cumulative time tracking on rebufferend if player wasn't paused

#### v8.8.0

New:

* Add `MuxStats.networkChange()` API for tracking network connectivity changes during a view

#### v8.7.0

Updates:

* Allow error codes as String values
* Add overloads of `MuxStats.error()` which take code, message, error context and flags directly
* Add `ErrorSeverity.WARNING` and `ErrorSeverity.FATAL`. Deprecate `ErrorSeverity.errorSeverityWarning` and `ErrorSeverity.erorrSeverityFatal`

#### v8.6.0

Updates:

* Add `playbackModeChange` API methods to `MuxStats`. You can specify your own arbitrary playback modes, or use one of the presets in `PlaybackMode`
* Add cumulative ad playing time and total content time metric tracking. The metrics track the "wall-clock" time spent with video playing during a view, and exclude time spent buffering or paused.
* Add `AdData.adType` for indicating whether an ad is a preroll, midroll, or postroll

#### v8.5.3

Fixes:

* do not dedupe error code and message if they were included

#### v8.5.2

Improvements:

* Prevent overriding `mux_embed_version`, `mux_api_version`, and `mux_embed`
* Do not flush beacons for non-fatal `error` events

#### v8.5.1

Fixes:

* Un-deprecate `CustomerVideoData.videoCdn`

#### v8.5.0

New:

* Add `CdnChangeEvent`, which will be sent automatically if using Request Metrics and sending your `x-cdn` header
  Fixes:
* fix: Beacons not sent in v8.4.2 of the SDK

#### v8.4.2

Fixes:

* fix: duplicate events and incorrect metadata when resuming after a long time
* fix: error severity not reported correctly

#### v8.4.1

Fixes:

* fix: Incorrect minified key for ViewerClientApplicationName and ViewerClientApplicationVersion

#### v8.4.0

Updates:

* Add `CustomerVideoData::videoCreatorId`

#### v8.3.0

Updates:

* Add new Standard Dimensions

#### v8.2.0

Updates:

* support 10 more custom dimensions

#### v8.1.4

Fixes:

* fix: Always send metadata on 'renditionchange'
* fix: resolve conflicting UUIDs in rare cases

#### v8.1.3

Fixes:

* fix: flush beacons when ad breaks end

#### v8.1.2

Fixes:

* fix: end rebuffering on seek

#### v8.1.1

Fixes:

* fix: verbose debug logging logging can break beacon dispatch
* fix: seeking should end any active rebuffering

#### v8.1.0

Updates:

* update: expose `enable` and `disable` methods for pausing and resuming data collection

#### v8.0.2

Improvements:

* size metrics are now ignored if values are set to -1

#### v8.0.1

Fixes:

* fix: reported application hang due to event handling

#### v8.0.0

Improvements

* Error events can be categorized with warning or fatal severity levels
* Error events can be categorized as business exceptions
* An error translator can be configured to extend or customize the Core SDK error handling logic

Fixes:

* Player error details such as error code, error context, error message, error severity, and whether the error is a business exception are only sent to Mux when an error event is dispatched.
* Player error details (same as listed above) are no longer deduplicated and are explicitly included with each error event sent to Mux.
* The SDK continues to track watch time after an error event is dispatched based on player playhead progression. To explicitly indicate that watch time should no longer be tracked after an error during a playback session please dispatch a `ViewEnd` event.

#### v7.13.2

Fixes:

* Update json.org to 20231013

#### v7.13.1

Fixes:

* Update json.org to 20230227

#### v7.13.0

Fixes:

* fix issue where seeking time would be included in time-to-first-frame if user seeked before playback started

#### v7.12.0

Updates:

* add `update()` method for `CustomerData`

#### v7.11.0

New:

* Support video source codec in `IPlayerListener`

#### v7.10.0

New:

* Add ability to set lower-priority video data, for auto-detected metadata

#### v7.9.1

Improvements:

* Additional improvements in reliability during large events

#### v7.9.0

Improvements:

* Added `drmType` to `CustomerViewData` so customers can override it
* Added `x-litix-shard-id` header populated with device ID

#### v7.8.0

New:

* Add a field to `CustomOptions` for controlling beacon update interval. Very few cases require `longBeaconDispatch`.

#### v7.7.4

Fixes:

* Fix Beacon interval incorrectly being 10 minutes

#### v7.7.3

Improvements:

* Update beacon interval changed from 5s to 10s

#### v7.7.2

Improvements:

* Fix Ad metadata not being reported properly

#### v7.7.0

New:
Add `AdEvent` with `AdData` to represent data about individual, non-preroll ad events during play


# Mux playback events
This guide is a canonical list of playback events. This is useful if building a custom integration.
## Playback events overview

The main component of a player integration revolves around events. Most players trigger or fire events for the common playback events such as `play`, `pause`, `error`, and others, but these events are typically named differently on different platforms. The primary purpose of each player integration is to translate these events into the events that the core libraries expect.

Each language library has a slightly different naming scheme to the events, but they should be in-line with each other aside from some slight syntax.

Optional events provide additional detail in tracking views, but are not necessarily required for base Quality of Experience tracking within a player.

Each event occurrence contains a common set of time values that are submitted to the server and contained in the view exports:

| Field | Description |
|-------|-------------|
| `viewer_time` | The wall clock time from the device when the event occurred, in milliseconds since unix epoch |
| `playback_time` | The playhead position at the time of the event, in milliseconds |
| `event_time` | The wall clock time on the server when the event is received, in milliseconds since unix epoch (populated in the view exports, not submitted with the event) |

## General playback events

The main playback events that Mux SDKs expect are defined as follows:

### `playerready`

Signals that the player initialization process has completed, and the player is ready for interaction. A video may or may not have been loaded in the player; this event is specific to the player completed any tasks in initial startup of the player.

### `viewinit`

Signals that a new view is beginning and should be recorded. This must be called first before any additional playback events. Note that this should only be emitted for the first view within a player; for a change of videos within the same player, `videochange` should be used.

This event is only required for building integrations using Mux's Apple core SDK. This is handled automatically as a side effect of initialization of the JavaScript and Java Core SDKs.

### `videochange`

Signals that the video being played in the player has changed. This must be called if a new video is loaded within the same player. The event should be fired immediately after the new video has been given to the player.

This event is only available within the JavaScript Core SDK. For Swift on Apple platforms, see the section on changing the video in [Custom Swift integration](/docs/guides/data-custom-swift-integration). For Java, there are helper methods for this exposed within `MuxStats`.

### `play`

Signals that the player is beginning its attempt to play back the video. The video is not yet showing on the screen (or moving forward in the case of a resume). The buffer may be empty or full depending on the pre-loading strategy.

For the HTML5 video element, this correlates to the [play](https://developer.mozilla.org/en-US/docs/Web/Events/play) event on the video element.

For ad playback, once resuming from the ad break, the `play` event should be fired immediately after the `adbreakend` event, assuming the player will continue playing content after the ad break without interaction from the viewer.

### `playing`

Signals that the video is now actually playing. The buffer is full enough that the player has decided it can start showing frames. In other words, this is when the first moving frame is displayed to the end user.

For the HTML5 video element, this correlates to the [playing](https://developer.mozilla.org/en-US/docs/Web/Events/playing) event on the video element.

### `pause`

Signals that playback has been intentionally delayed, either by the viewer or by the player (e.g. starting an ad).

For the HTML5 video element, this correlates to the [pause](https://developer.mozilla.org/en-US/docs/Web/Events/pause) event on the video element.

In the case of playback breaking to play an ad, the `pause` event should be fired just before the `adbreakstart` event is fired.

### `timeupdate`

Signals that the playback has advanced some non-zero amount forward. This event should be emitted at *least* every 250 milliseconds, but can be sent more often than this.

For the HTML5 video element, this correlates to the [`timeupdate`](https://developer.mozilla.org/en-US/docs/Web/Events/timeupdate) event on the video element.

If the `timeupdate` event is not sent, the integration must provide the ability to retrieve the playhead time in the player callback for the SDK. See each language SDK for details on this callback. In all SDKs, emitting the `timeupdate` event is preferred, even if the playhead time callback is provided. In addition, on Java platforms, while emitting `timeupdate` is preferred, you must also provide the callback for `getCurrentPosition` within the `IPlayerListener` interface.

If the `timeupdate` event is sent, you must include the playhead position, in milliseconds, via the following mechanisms:

* JavaScript: provided as `player_playhead_time` key within the data object passed along with `timeupdate` to the call to `emit`.
* Java: provided via `PlayerData.setPlayerPlayheadTime` on the `PlayerData` emitted with the event.
* Objective-C: provided via `[MUXSDKPlayerData setPlayerPlayheadTime: time]` in the `MUXSDKPlayerData` object emitted with the event.

For integrations using the Objective-C Core SDK, this event is required to be sent.

### `seeking`

Signals that the user has attempted to seek forward or backward within the timeline of the video.

For the HTML5 video element, this correlates to the [seeking](https://developer.mozilla.org/en-US/docs/Web/Events/seeking) event on the video element.

### `seeked`

Signals that the player has the video data for the new playback position, and is ready to immediately start playing at this new position.

For the HTML5 video element, this correlates to the [`seeked`](https://developer.mozilla.org/en-US/docs/Web/Events/seeked) event on the video element.

### `rebufferstart`

Signals that the player has stopped playing back content when it is expected that playback should be progressing.

<Callout type="info">
  For JavaScript and Objective-C/Swift integrations, this event is internal to the core library and must not be emitted by the player integration.

  For Java integrations, after v6.0.0 of the core library, this event must be emitted by the player integration.
</Callout>

### `rebufferend`

Signals that the player has resumed playing back content after playback previous stalled while attempting to play back.

<Callout type="info">
  For JavaScript and Objective-C/Swift integrations, this event is internal to the core library and must not be emitted by the player integration.

  For Java integrations, after v6.0.0 of the core library, this event must be emitted by the player integration.
</Callout>

### `error`

Signals an error that will be associated with the view. Error severity can be set to fatal (i.e. not recoverable) or warning. Errors will be assumed to be playback failures within Mux by default but can be categorized as business exceptions either on the client and on the server. [See the Error Categorization guide for more details](/docs/guides/error-categorization).

For the HTML5 video element, this correlates to the [error](https://developer.mozilla.org/en-US/docs/Web/Events/error) event on the video element.

This specific event should be accompanied by the following metadata:

| Field | Description |
|-------|-------------|
| `player_error_code` | An integer that provides a category of the error. 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. |
| `player_error_message` | Details about the error encountered, though should remain relatively generic. It shouldn't include a full stack trace, for instance, as this field is used to group like errors together. |
| `player_error_context` | Used to provide instance-specific details for the error, such as stack trace, segment number, or URL. |

### `ended`

Signals that the current video has played to completion.

For the HTML5 video element, this correlates to the [ended](https://developer.mozilla.org/en-US/docs/Web/Events/ended) event on the video element.

### `renditionchange` (optional)

Signals that the current rendition that is actively being played has changed. Note that this event should be triggered when the playing rendition changes, not necessarily when the player logic has started requesting a different rendition.

This specific event should be accompanied by the following metadata:

| Field | Required | Description |
|-------|----------|-------------|
| `video_source_bitrate` | Required | The current rendition's bitrate (combined video and audio), in bits per second (bps) |
| `video_source_width` | Optional for web/Java, Required for iOS | Optional for web and Java integrations, assuming `video_source_width` is returned by the appropriate callback (e.g. `getStateData` for web) |
| `video_source_height` | Optional for web/Java, Required for iOS | Optional for web and Java integrations, assuming `video_source_width` is returned by the appropriate callback (e.g. `getStateData` for web) |
| `video_source_codec` | Optional for web/Java, Required for iOS | |
| `video_source_fps` | Optional for web/Java, Required for iOS | |
| `video_source_name` | Optional for web/Java, Required for iOS | |

### `orientationchange` (optional)

Signals that a device orientation has been changed during the view. On most platforms this information is not available directly to the player SDK so the customer implementation will notify the Mux SDK when the orientation is changed and Mux will fire an event based on the notification.

This specific event should be accompanied by the following metadata:

* `viewer_device_orientation`. The device's orientation after the change. The orientation is expressed as a  `(x, y, z)` coordinate system, with the most common orientations being `(0,0,90)` for portrait and `(0,0,0)` for landscape.

### `playbackmodechange` (optional)

Signals that the mode of playback has changed. You can use a defined preset or a custom value based on your playback offering.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `playbackmode`| string | Required | The name of the playback mode. Presets include `standard`, `inline`, `fullscreen`, `pip`, `miniplayer`, `background`. You can also pass a custom string.
| `playbackmodedata` | string (json) | Optional | Optional string that is valid json that contains metadata which can be used for more detailed analysis. Non json strings will be ignored.

### `networkchange` (optional)

Signals that the network infrastructure available to the player has changed.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `viewer_connection_type`          | string  | Required | The type of network infrastructure available to the player: `wifi`, `cellular`, `wired`, `other`; or `no_connection` if none are available; or `unknown` if there is infrastructure available but its type cannot be determined. |
| `viewer_connection_low_data_mode` | boolean | Optional | Whether the network infrastructure available to the player should be treated as constrained.                                                                                                                                     |

### `heartbeat`

Internal event that is used to provide periodic updates on the playback state, while the player is not paused. Each core library emits heartbeat events (`hb`) automatically, and custom integrations should not need to emit this.

### `viewend`

Internal event that is used to signal the end of a view tracked by Mux. Each core library emits the `viewend` event automatically as a result of either tearing down the SDK or changing the video (`videochange`). Custom integrations do not need to emit this manually.

## Ad Events

For players that support ad playback, the following events are expected. If you do not provide these events, playback will still be monitored, but there will not be ad-specific metrics or knowledge of ads vs content.

These events require additional data to be provided. See [Building a Custom Integration](/docs/guides/build-a-custom-data-integration).

### `adrequest` (optional)

Signals that an ad request is about to be made, or was just made but the response has not been received.

In the process of the player retrieving an ad payload, multiple `adrequest` and `adresponse` events may be fired (either due to waterfall, or for an ad break that has multiple ads). In the case that these requests are made in parallel, the player integration must send an `ad_request_id` in the data along with each `adrequest` and `adresponse` event, so that Mux can match them up correctly.

### `adresponse` (optional)

Signals that a response was received from the ad server.

In the process of the player retrieving an ad payload, multiple `adrequest` and `adresponse` events may be fired (either due to waterfall, or for an ad break that has multiple ads). In the case that these requests are made in parallel, the player integration must send a `ad_request_id` in the data object along with each `adrequest` and `adresponse` event, so that Mux can match them up correctly.

The `adresponse` event can only be fired by the player integration if the `adrequest` events are fired as well.

### `adbreakstart`

Signals that an ad break has begun. This coincides with the playback of the video being paused in order to display the ads at the current position. This event should come immediately after the `pause` event is fired due to attempting to play back an ad break, and before any `adplay`, `adplaying`, `adpause`, or `adended`.

The `adbreakstart` event may come before, during, or after the `adrequest`/`adresponse` events, depending on the player’s configuration for making ad requests.

### `adplay`

Signals that the player is beginning its attempt to play back an individual advertisement video. The ad is not yet showing on the screen (or moving forward in the case of a resume). The buffer may be empty or full depending on the pre-loading strategy.

This event is the ad-specific equivalent of `play`.

### `adplaying`

Signals that an advertisement is now actually playing. The buffer is full enough that the player has decided it can start showing frames for the ad.

This event is the ad-specific equivalent of `playing`.

### `adpause`

Signals that playback of an advertisement has been intentionally delayed, either by the viewer or by the player (e.g. user pressing pause on the ad player controls).

This event is the ad-specific equivalent of `pause`.

### `adfirstquartile` (optional)

Signals that the current advertisement has progressed past the first quartile in playback. This event should coincide with the point in time that the ad integration would fire the `firstQuartile` ad tracking beacon (in VAST terminology).

### `admidpoint` (optional)

Signals that the current advertisement has progressed past the midpoint in playback. This event should coincide with the point in time that the ad integration would fire the midpoint ad tracking beacon (in VAST terminology).

### `adthirdquartile` (optional)

Signals that the current advertisement has progressed past the third quartile in playback. This event should coincide with the point in time that the ad integration would fire the `thirdQuartile` ad tracking beacon (in VAST terminology).

### `adended`

Signals that the advertisement has played to completion.

This event is the ad-specific equivalent of `ended`.

### `adbreakend`

Signals that all ads in the ad break have completed, and playback is about to resume on the main content. This event should be come immediately after the last `adended` event in the ad break, and before the resuming `play` event signifying that playback of the main content is resuming.

There may be multiple `adplay`/`adended` combinations within a single ad break.

### `aderror`

Signals that an error has occurred that relates to the ad break currently in play or the ad request/response.

## Bandwidth throughput events

Like the Ad-specific events, these events are not required. However, if you include any of these, you must include all of them. Each of these events refers to a network request made for some component of the media playback. This includes but, depending on your exact configuration, may not be limited to:

* manifests and content segment requests for HLS playback
* manifests, init fragment, and content fragment requests for DASH playback

These events should *not* be fired for ad requests and require additional data to be sent along with them. See [Network Request Data](#network-request-data).

### `requestcompleted`

Signals that a network request for a piece of content returned successfully.

### `requestfailed`

Signals that a network request for a piece of content returned unsuccessfully.

### `requestcanceled`

Signals that a network request for a piece of content was aborted before it could return (either successfully or unsuccessfully).

## Accompanying Data

Each core SDK has its own mechanism for providing data along with each event. This data is used to provide information such as player state (e.g. paused or playhead time), and potentially to override the data that is pulled automatically from the player.

For the most part, most data is retrieved automatically, and you will not need to provide any accompanying data. The notable exceptions for this are in regards to ad information, as well as network request information.

See the following guides for each library on how to provide additional data with each event.

## Ad-Specific Data

The following data should be sent while emitting the ad-specific events, where possible.

### `ad_type`

The type of ad used during playback: `preroll`, `midroll`, `postroll`

### `ad_asset_url`

The URL for the current ad being played. For example, in a VAST response, this would correspond with the MediaFile URL that is being played.

Note: this data should only be included alongside `adplay`, `adplaying`, `adpause`, `adended`, `adfirstquartile`, `admidpoint`, `adthirdquartile` events, as they are the only events that correlate with the ad asset that is being played.

### `ad_tag_url`

The URL for the current ad tag/ad request being made. For example, this could be the URL that is expected to return a VMAP or VAST document detailing what ad(s) to play.

Note: this data should only be included alongside `adrequest` and `adresponse` events, as those are the only events that correlate with the ad tag URL being used currently.

### `ad_creative_id`

The Creative Id of the ad. This usually is the Ad-Id of the selected creative in the VAST response.

### `ad_id`

The Id of the ad. This usually is unique in the Ad Provider's system and specified in the VAST response.

### `ad_universal_id`

The Universal Id of the ad. This usually is globally unique for the ad across all Ad Providers.

Note: the above 3 metadata can be included in all ad events except for `adrequest` and `adresponse` events.

## Network Request Data

The following data should be sent along with any of the network events (`request*`).

### `request_start`

Timestamp that the request was initiated, in milliseconds since the Unix epoch.

Include alongside: `requestcompleted`, `requestfailed`, `requestcanceled`

### `request_bytes_loaded`

The total number of bytes loaded as part of this request.

Include alongside: `requestcompleted`

### `request_response_start`

Timestamp that the response to the request began (i.e. the first byte was received), in milliseconds since the Unix epoch.

Include alongside: `requestcompleted`

### `request_response_end`

Timestamp that the response was fully received (i.e. the last byte was received), in milliseconds since the Unix epoch.

Include alongside: `requestcompleted`

### `request_type` (optional but recommended)

The type of content being requested. Specifying the `video` as the `request_type` for video segements is recommended to ensure CDN tracking accuracy. One of the following:

| Type | Description |
|------|-------------|
| `manifest` | Used when the request is for a master or rendition manifest in HLS, or a DASH manifest. |
| `video` | Used when the request is for a video-only segment/fragment |
| `audio` | Used when the request is for an audio-only segment/fragment |
| `video_init` | Used when the request is for the video init fragment (DASH only) |
| `audio_init` | Used when the request is for the audio init fragment (DASH only) |
| `media` | Used when the type of content being request cannot be determined, is audio+video, or is some other type. |
| `subtitle` | Used when the request is for subtitle or caption content |
| `encryption` | Used when the request is for a DRM encryption key |

Include alongside: `requestcompleted`, `requestfailed`, `requestcanceled`

### `request_hostname`

The hostname portion of the URL that was requested.

Include alongside: `requestcompleted`, `requestfailed`, `requestcanceled`

### `request_id` (optional)

The id for identifying the individual request. CDNs often include a request id in their responses which can be used for correlating requests across the player and CDN.

Include alongside: `requestcompleted`, `requestfailed`, `requestcanceled`

### `request_url` (optional)

The URL that was requested.

Include alongside: `requestcompleted`

### `request_labeled_bitrate` (optional)

Labeled bitrate (in bps) of the video, audio, or media segment that was downloaded.

Include alongside: `requestcompleted`

### `request_response_headers` (optional)

A map of response headers and their values. You should include whatever headers are available to the client, as this information may be used to determine routing of each request. The most important header, though, is the X-CDN header as described in [CDN Configuration for Request-Level Metadata](/docs/guides/enable-automatic-cdn-detection).

Include alongside: `requestcompleted`

### `request_media_duration` (optional)

The duration of the media loaded, in seconds. Should not be included for `requestcompleted` events for manifests.

Include alongside: `requestcompleted`

### `request_video_width` (optional)

For events with `media` or `video` `request_type`, the width of the video included in the segment/fragment that was downloaded.

Include alongside: `requestcompleted`

### `request_video_height` (optional)

For events with `media` or `video` `request_type`, the height of the video included in the segment/fragment that was downloaded.

Include alongside: `requestcompleted`

### `request_error`

The name of the error event that occurred. Note this is not the status code of the request itself, but rather something along the lines of `FragLoadError`.

Include alongside: `requestfailed`

### `request_error_code`

The response code of the request that spawned the error (i.e. 401, 400, 500, etc).

Include alongside: `requestfailed`

### `request_error_text`

The message returned with the failed status code.

Include alongside: `requestfailed`

## Sample Sequence of Events

A sample sequence of events for an integration would look like the following:

| Event | Description |
|-------|-------------|
| `playerready` | |
| `viewinit` | When the video is about to be loaded in a player |
| `play` | When the user presses play to attempt playing back the video |
| `playing` | When the first frame of video is displayed |
| `timeupdate` | At least every 250 ms with progress of the playhead time |
| `pause` | When the viewer presses pause |
| `play` | When the viewer resumes playback |
| `playing` | When the first frame is displayed after resuming |
| `timeupdate` | |
| `ended` | When the video playback is complete |
| `viewend` | When the view is complete - e.g. the user is no longer attempting to watch the video |

At the end, if the viewer loads a new video into the player, a `videochange` event should be emitted instead of the `viewend` event, with the new video data.


# Export Monitoring data for integration
Understand how to export your monitoring data into your own system for processing and taking action.
<Callout type="info">
  The Monitoring Samples Stream is only available on **Mux Custom Media** plans. Learn more about [Mux Data Plans](https://data.mux.com/pricing) or [contact support](https://mux.com/support).
</Callout>

Mux provides a mechanism for customers to subscribe to a near-realtime, video view-level data stream of events and measurements related to the quality of service for customers with a Mux Data integration.

This can be used to identify service-level problems, such as widespread rebuffering or playback failures. It can also be used to integrate Mux data with platforms for multi-CDN switching platform, alerting, or constructing your own version of the Mux Data Monitoring dashboard.

## Monitoring Sample Messages

A single Monitoring Samples payload may contain multiple samples. Each sample corresponds to a single active video view, with a different view id per sample. The sample can contain multiple records, where each record contains metrics for a point in time for the video view. A record specifies a time period and metrics measured over that time period. All metrics inside a single record will apply to the time range implied by the `start` timestamp field plus the `duration_ms` field. If the duration field is zero, the record includes instantaneous metrics. A record MUST contain at least one metric.

<Image sm src="/docs/images/monitoring-stream-format.png" width={640} height={480} />

## Metrics Included

### START\_LATENCY\_MS

Also known as Time To First Frame (TTFF). This is Mux’s Video Startup Time which measures the time that the viewer waits for the video to play after the page is loaded and the player is ready.

### EXIT\_BEFORE\_VIDEO\_START

Instantaneous event metric that is sent when a playback drop is detected. This is sent when Mux has detected an intent to play but playback never begins. Inherently has a delay (up to 1 minute) while waiting to detect play start. The value field contains the playhead time of the player at the time of exit, in milliseconds, typically this value is 0 for videos starting from the beginning. This is NOT sent when the playback is halted due to a PLAYBACK\_ERROR.

### WATCH\_DURATION\_MS

Watch Duration is the amount of time in millisecond that viewers spend attempting to watch a video. This includes all time spent waiting for video to load, including rebuffering and seeking. It does not include time spent paused.

### SEEK\_LATENCY\_MS

The Seek Latency metric measures the average amount of time that viewers wait for the video to start playing again after seeking to a new time. Seeking is any time the player is asked to jump backward or forward to a new time in the video, outside of normal playback.

### REBUFFER\_DURATION\_MS

Rebuffer Duration is the amount of time in milliseconds that viewers spend rebuffering during the record window.

### REBUFFER\_COUNT

Rebuffer Count is the number of independent rebuffer events encountered over the record time window.

### PLAYBACK\_ERROR

Instantaneous event metric that is sent when playback has failed due to a fatal technical error. The value is the player playhead timestamp in milliseconds when the error occurred. Non-fatal technical errors and business errors are not included in the Monitoring stream.

## Continuously stream data

Mux Data supports streaming the Monitoring Samples to an Amazon Kinesis Data Stream in your cloud account. Monitoring data is sent to the configured destination each 30 second interval.

The samples stream data can be stored in your long-term storage for processing and aggregation. This method of access is most useful for customers who want real-time updates of the current performance that can be used for aggregations that inform real-time CDN switching, custom alerting, or internal NOC tools.

## Setting up a Monitoring Samples stream

Monitoring Samples streams are enabled by working with the Mux team; they are *not* currently configured in the **Streaming Exports** settings in your Mux dashboard. Generally, the steps for configuring realtime sample exports are as follows:

* Mux will work with the customer to generate the AWS account details.
* The customer will create the destination and security artifacts in AWS.
* Send the AWS ARNs to Mux.
* Mux enables real-time sample exports to the customer Kinesis stream in production & staging.

For more information on setting up AWS Kinesis, refer to the [Amazon Kinesis Data Streams](/docs/guides/export-amazon-kinesis-data-streams) setup guide for more information on setting up an export.

## Message format

Messages are in either JSON format or Protobuf (proto2) encoding. You can choose between the two formats when setting up the data stream with Mux support.

For Protobuf encoding, every message uses the `com.mux.realtime.Samples` message type defined in the export Protobuf spec, which is available in the [mux-protobuf repository](https://github.com/muxinc/mux-protobuf/tree/main/video_view). Use the latest Protobuf spec when creating schemas or generating code.

## Monitoring Samples message format

The protobuf definition for the Monitoring Samples stream is available in the [mux-protobuf repository](https://github.com/muxinc/mux-protobuf/tree/main/monitoring_samples). Please subscribe to this repository for updates to the protobuf definition.

The JSON format payload contains identical fields as the protobuf-encoded format.

## Versioning

## Backward compatibility

The schema provided by Mux Data is backward compatible, meaning that each schema version guarantees that it will still work upon future upgrades. Customers do not need to worry about breaking changes.

## When to upgrade the schema?

When Mux adds new fields or metrics to the Monitoring Samples stream, we will upgrade the schema version. Without taking any actions, new fields will be automatically included in the data stream. For JSON formatted data, the new fields will be included in the data objects as they are added to the stream. For proto encoded streams, the new fields will be available once you upgrade to the latest [proto definition](https://github.com/muxinc/mux-protobuf/tree/main/monitoring_samples).


# Ensure privacy compliance with Mux Data
Collect the information you need to be successful while ensuring privacy for your viewers and complying with GDPR and other privacy regulations.
## Is Mux Data GDPR/CCPA/VPPA compliant and privacy preserving?

Mux takes privacy expectations seriously; we are compliant with the Video Privacy Protection Act (VPPA), California Consumer Privacy Act (CCPA), and General Data Protection Regulation (GDPR). We also make available a [detailed Data Processing Addendum (DPA)](https://mux.com/dpa/) that details the measures we've taken to achieve compliance. Mux has attested to our data privacy protections and has been certified under the [Data Privacy Framework (DPF)](https://www.dataprivacyframework.gov/) program.

Mux works to ensure the privacy of viewers while providing development teams using Mux Data with the visibility they need to track audience engagement and their viewers' quality of experience. We don't believe that privacy and insights should be a trade-off for developers or video viewers.

We also want to ensure that the metadata that developers send to us is also properly anonymized in order to reduce the possibility of personally identifying a viewer with their activity. We strongly urge developers using Mux Data to provide an anonymized viewer id - using a non-personally identifiable id from a system only the customer has access to - that is meaningful to the developer but not to Mux as part of the view metadata.

## Do you track sensitive personally identifiable information?

No. Mux Data does not store information about the user such as email, name, or built-in device identifier (such as the Id for Analytics on iOS). For more information about the data we store - which doesn't include personal viewer information - please reference our [Data Processing Addendum](https://mux.com/dpa/).

## Does Mux have a Data Protection Addendum (DPA)?

Yes. The Mux Data Protection Addendum (DPA) is available at https://mux.com/dpa/ .

## Does Mux participate in the Data Privacy Framework?

Yes, Mux participates in the EU-U.S. [Data Privacy Framework (DPF)](https://www.dataprivacyframework.gov/), having self-certified our compliance. The DPF enables lawful transfers of personal data from the EU to the U.S. and is designed to ensure strong privacy protections. As a U.S.-based company handling data from international customers, our participation in the DPF underscores our commitment to data privacy and provides reassurance that we meet the standards required under EU law. You can find more detailed information and view Mux’s certification on the official DPF website at https://www.dataprivacyframework.gov/list .

## How do I make a GDPR or CCPA data erasure request?

Mux Data does not knowingly store personally identifiable information, but GDPR and CCPA data erasure requests can be made to the gdpr@mux.com email. This email is monitored and you will receive a response from us that the viewers' data is being removed, if any can be identified.

## Is it possible to keep my viewers' IP address data in Europe?

Yes. Mux Data has an ingest location in the European Union (EU) that can be used for processing video views. The full IP addresses will only be processed at our location in Germany and the post-processed view data, including corresponding truncated IP address with the last octet removed, will be sent to the United States for aggregation and reporting. For more information on using the EU location, please reach out to your sales contact or email sales@mux.com.

## What information does Mux Data collect?

Mux Data collects non-personally identifiable information about the viewer experience that allows you to track engagement and the quality of experience for your audience.

* IP address: We process a viewer's IP address in order to look up coarse location information and do bot detection. After processing, we pseudonymize the IP address by truncating it (to /24 for IPv4) and then we store only the pseudonymized value.
* Geographic location and Autonomous Systems Number (ASN): We generate coarse location information at the country and state-level from the IP address, but we do not collect fine grained latitude/longitude information nor do we access geo-location features of mobile devices.
* Viewer ID: We generate a unique, random identifier for a viewer that is used as a viewer id if none is provided by the developer implementing the Mux Data SDK. We do not associate these IDs with any activity other than the video views and we do not associate the id with any advertising profile data. Because we do not store identifiable information about viewers, we are not able to associate the video view history with a specific individual.
* Device information: Information about the device that is used to access video playback, including model, device type, operating system, and browser used.
* Details about video content watched: Metadata such as type of stream: live or VOD, video format, autoplay status, etc. A list of [additional metadata](/docs/guides/make-your-data-actionable-with-metadata) is available for reference and most metadata is optional, to be set by the developer implementing Mux Data.

## How long does Mux Data store viewership data?

Pseudonymized video view data is stored for up to 100 days and is then deleted from our systems.

## Is Mux Data appropriate for applications targeted to children?

Yes. Mux does not store personally identifiable data, use viewer data for advertising, or sell user identifiable data. The Mux Data and Mux Video SDKs can be used in applications and receive approval for children's apps on the app stores.

## What information is stored in Mux Data's HTTP cookies?

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 example, if your site or application is targeted towards children under 13, you should disable the use of cookies. Please refer to the documentation for the specific Mux Data SDK you are using for info on how to disable cookies.

The cookie is set as a first party cookie on the domain of the website that is embedding the player and Mux Data SDK. For example, if the video player with Mux Data integrated is located on the page: `http://example.com/demo.html` the cookie will be set on the domain `example.com`. The cookies are only available on each individual customer's domain and cannot be used to track viewers across Mux customers.

The Mux Data cookie contains the following information:

* `mux_viewer_id`: a randomly generated viewer id that is used as the default anonymous Viewer ID.
* `msn`: random value used to decide if the viewer will be sampled (tracked) or not
* `sid`: randomly generated anonymous session id
* `sst`: the time the session started
* `sex`: the time at which the session will expire

## Do I need to ask permission to track on iOS when I use a Mux Data SDK in my app?

Mux does not access the Identifier for Advertisers (IDFA) in any SDK, nor does it use viewer data for advertising or advertising efficiency measurement so the Apple AppTrackingTransparency (ATT) framework does not require a tracking permission request to use the Mux SDK.

As of version 2.4.2 of the [Mux Data for AVPlayer SDK](https://github.com/muxinc/mux-stats-sdk-avplayer), the Identifier for Vendors (IDFV) is no longer used and the Mux Data SDK generates a random unique identifier on the device for default Viewer Id. We do not sell the identifier data or attempt to track users across Mux customers.

As of version 3.6.1 of the [Mux Data for AVPlayer SDK](https://github.com/muxinc/mux-stats-sdk-avplayer) and versions 4.7.1 and 5.0.1 of the [Mux Data Objective-C Core SDK](https://github.com/muxinc/stats-sdk-objc), a privacy manifest file that satisfies [Apple’s requirements for third-party SDKs](https://developer.apple.com/support/third-party-SDK-requirements/) to outline privacy practices associated with their use. Customers who export data from Mux for additional processing may need to include additional privacy manifest entries with their application subject to their specific practices.

## Does my app need to access a hardware id on Android when I use a Mux Data SDK?

As of version 2.4.1 of the [Mux Data for ExoPlayer SDK](https://github.com/muxinc/mux-stats-sdk-exoplayer), the Mux Data SDK generates a random unique identifier on the device for the default Viewer Id. We do not sell the identifier data or attempt to track users across Mux customers.


# Integrate a Data custom domain
Learn how to integrate a Data custom domain for beacon collection.
In this guide you will learn how to configure a custom domain used for submitting Mux Data beacons from SDK clients. Video view data will be sent to the specified custom domain rather than the default Mux domain.

You might choose to do this for a couple of reasons, such as allowing analytics traffic to bypass school or other network firewall restrictions (via a known domain), [zero-rating](https://en.wikipedia.org/wiki/Zero-rating) this traffic, or to aid tracking performance when ad blockers are in place.

<Callout type="info">
  Custom Domains for Mux Data are available on select plans, such as **Mux Data Media**. [Reach out](mailto:help@mux.com) if you have any questions.
</Callout>

## 1. Point your custom domain to the Mux domain

After selecting your desired custom domain, you will need to create CNAME records with your DNS provider to alias the custom domain to a Mux-controlled one and allow Mux to issue TLS certificates for your selected domain. After providing your Customer Success Manager with the desired subdomain, Mux will provide you with the specific required DNS records to enable custom domains (including the value for `{KEY}` below). The records will have the following basic format:

```
subdomain.yourdomain.com 300 IN CNAME ${KEY}.customdomains.litix.io
_acme-challenge.subdomain.yourdomain.com 300 IN CNAME ${KEY}.validations.customdomains.litix.io
```

Notify Mux after these records have been created so we can issue TLS certificates to terminate beacon traffic sent to your selected custom domain. You will be notified by Mux when the domain has been successfully provisioned.

## 2. Configure your SDK integration to use a custom beacon domain

You can verify whether the custom domain is operational by using `curl` to query your domain:

```
$ curl https://subdomain.yourdomain.com -s -w "%{http_code}"
200%
```

<Callout type="info">
  Make sure that you have upgraded to the latest versions of each SDK to ensure Custom Domains function correctly.
</Callout>

It may take some time for DNS records to propagate before this request will work. After that is complete, configure your SDK integrations to specify your custom domain. Set the `beaconCollectionDomain` property to your custom domain.

Depending on your SDK, you can set the value for `beaconCollectionDomain` in various ways.

```brightscript

m.mux = m.top.CreateNode("mux")
m.mux.setField("video", m.video)
muxConfig = {
  env_key: "ENV_KEY",
  beaconCollectionDomain: "CUSTOM_DOMAIN"
}
m.mux.setField("config", muxConfig)
m.mux.control = "RUN"

```

```javascript

mux.monitor('#my-player', {
  debug: false,
  beaconCollectionDomain: 'CUSTOM_DOMAIN',
  data: {
    env_key: 'ENV_KEY', //required
    // ...
  }
});

```

```kotlin

val customOptions = CustomOptions().apply {
  beaconCollectionDomain = "CUSTOM_DOMAIN"
}
muxStatsExoPlayer = exoPlayer.monitorWithMuxData(
  context = requireContext(),
  envKey = "YOUR_ENV_KEY_HERE",
  playerView = playerView,
  customerData = customerData,
  customOptions = customOptions
)

```

```objc

_playerBinding = [MUXSDKStats monitorAVPlayerViewController:_avplayerController 
                                             withPlayerName:@"mainPlayer" 
                                               customerData:customerData
                                     automaticErrorTracking:YES
                                     beaconCollectionDomain:@"CUSTOM_DOMAIN"];

```

```swift

let playerBinding = MUXSDKStats.monitorAVPlayerViewController(self, withPlayerName: "mainPlayer", customerData: customerData, automaticErrorTracking: true, beaconCollectionDomain: "CUSTOM_DOMAIN");

```



# Track autoplaying videos
Use this guide to understand best practices around autoplay to make your autoplaying videos are correctly tracked.
If you are autoplaying videos with any web based players that use the video element then make sure you read this guide so that Mux can accurately track your videos' startup time. This applies to video elements with the `autoplay` attribute and anytime you are calling `play()` on a video element (this includes all HTML5 players like VideoJS, JWPlayer, Shaka player, etc.).

Browser vendors are frequently changing their policies when autoplay is allowed and not allowed, so your application should be prepared to deal with both scenarios, and we want to make sure we're tracking your views and errors accurately.

# Increase your chance of autoplay working

There's a few conditions that will increase your chance of autoplay working.

* Your video is muted with the muted attribute.
* The user has interacted with the page with a click or a tap.
* (Chrome - desktop) The user’s [Media Engagement Index](https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#mei) threshold has been crossed. Chrome keeps track of how often a user consumes media on a site and if a user has played a lot of media on this site then Chrome will probably allow autoplay.
* (Chrome - mobile) The user has added the site to their home screen.
* (Safari) Device is not in power-saving mode.

<Callout type="error" title="Autoplay will never work 100% of the time">
  Even if autoplay works when you test it out, you can never rely on it working for every one of your users. Your application must be prepared for autoplay to fail.
</Callout>

# Avoid the `autoplay` attribute

When you use the `autoplay` attribute (it looks like `<video autoplay>`, you (and Mux) have no way to know if the browser blocked or didn't block autoplay.

The issue is that when using the `autoplay` attribute the `video` element sometimes does not send the `play` event when it should, which can result in incorrect Video Startup Time measurements.

To avoid this issue, use `video.play()` instead, which returns a promise and allows you to know if playback played successfully or not. If autoplay worked, the promise will resolve, if autoplay did not work then the promise will reject with an error. The great thing about this approach is that you can choose what to do with the error.

For example: you can report the error to your own error tracking tools or update the UI to reflect this error. Note that Mux's custom error tracking is for tracking fatal errors, so you wouldn't want to report an autoplay failure to Mux because then it will be considered a fatal error.

```js
const video = document.querySelector('#my-video');
mux.monitor(
  /*
    see the web-integration-guide HTML5 to set this up
  */
);

video.play().then(function () {
  // autoplay was successful!
}).catch(function (error) {
  // do something if you want to handle or track this error
});
```

For further reading, see [the mux blog post](https://mux.com/blog/video-autoplay-considered-harmful/) about this topic.


# Mux Data FAQs
Answers to common questions about Mux Data.
## Why use Mux Data?

Mux Data uncovers four key dimensions of video quality of service: playback failures, startup time, rebuffering, and video quality. If your aim is broadcast-quality video streaming, Mux Data enables you to monitor these critical video metrics.

With each Mux Data metric, you can monitor and track what matters to your viewers. For example, [Overall Viewer Experience Score](/docs/guides/data-overall-viewer-experience-metric#overall-viewer-experience-score) is a metric that quickly summarizes your video platform's performance.

To get familiar with more of the features of Mux Data, see this [introduction to Mux Data](https://mux.com/data).

## What is the Mux Data Dashboard and what can you do with it?

The Mux Data Dashboard is an interface that lets you set filters and view graphs that monitor each specific key metric you are interested in. With each metric, you can monitor and track what matters to your viewers.

You can also immediately see what is happening before users do with [Anomaly Alerts](/docs/guides/setup-alerts#anomaly-alerts) and [Threshold Alerts](/docs/guides/setup-alerts#threshold-alerts). It is easy to set these alerts for prompt notifications. Check your dashboard to track the source or sources.

You may want to apply [Filters](/docs/guides/setup-alerts#filters) to the alert definition to track only specific data. Finally can also use the <ApiRefLink href="/docs/api-reference/data/metrics/list-insights">List Insights</ApiRefLink> feature as a way of *impact sorting* which browsers, devices, regions, CDNs, players, ads and videos are creating the most problems for your viewers.

## What is the Monitoring Dashboard and what can you do with it?

The [Mux Data Monitoring Dashboard](https://data.mux.com/real-time-monitoring/), previously called the Real-time Dashboard, allows you to monitor your critical metrics in one operational dashboard that updates in real-time. This lets you respond to major streaming issues quickly.

<Callout type="success">
  Read this blog post as a great example and resource:

  [Respond to and Resolve Incidents with the Monitoring (formerly Real-time) Dashboard](https://mux.com/blog/respond-to-and-resolve-incidents-with-the-real-time-dashboard/).

  It dives into how to use tools on the Monitoring Dashboard to investigate the incident, communicate with stakeholders, resolve the issue, and improve your resiliency.
</Callout>

## Can I access Mux Data via an API?

Yes, all Mux Data views and metrics are all available through the Data API. Raw video view data can be [exported via the API](/docs/guides/export-raw-video-view-data). Additionally, here is a detailed blog post describing how to [create graphs using the Mux API](https://mux.com/blog/use-the-mux-data-api-to-create-graphs-in-react/).

## Where do I find Mux Data Pricing? What features are included in the Pay-as-you-go, Media, and Custom Media Plans?

Choose a Mux Data pricing plan on the [Data Pricing page](https://mux.com/data/#DataPricing). Here you can view a breakdown of all features that Mux includes with each plan including Pay-as-you-go, Media, and Custom Media.

Or, [contact our Sales team](https://mux.com/sales-contact) to acquire more detailed information.

## Where do I find all supported metrics, dimensions, and devices?

You can find more [Technical Specs here](https://mux.com/data/#TechSpecs) covering all tracked video metrics, available filters, and supported players.

## Can I use Mux Data to monitor audio-only content?

Yes, Mux Data can be used to monitor audio content that uses the `<audio>` element. Mux Data will track Engagement metrics, such as the number of plays and length of playback time, as well as basic Quality of Experience metrics including Startup Time, Rebuffering Percentage, and others. Video Quality metrics are not calculated for audio content.

## Is Mux Video delivery usage API similar to watch time in Mux Data?

No, these two measurements are quite different. Mux Video's <ApiRefLink href="/docs/api-reference/video/delivery-usage">Delivery Usage API</ApiRefLink> is based on the number of minutes delivered to clients. This is a server-side (CDN) metric. Whereas Mux Data collects metrics from the client-side and calculates watch time based on the user's interaction with the player.

If a user watches a video, rewinds, and watches the video again that content was only delivered one time to the device but it was watched multiple times. In this scenario Mux Video's delivery usage would be lower than the watched time in Mux Data.

More commonly, the client will build up a buffer of downloaded video content. The user will watch some of it and then leave before watching the full length of the video. In this scenario Mux Video's minutes delivered would be higher than the watched time in Mux Data because the client downloaded more minutes of video than it watched.

Another factor to keep in mind is that because Mux Data runs as a client-side SDK, it is susceptible to being blocked by ad-blockers.

## How should I use Mux environments?

Environments allow you to separate data collected from players to more accurately analyze your video engagement and performance. A Development and Production environment are created automatically when you sign up, and this is the most common way of organizing environments. You can rename your environments or add additional environments as needed, but we recommend keeping development and production data separate.

Multiple sites or apps can use the same environment and Mux Data environment key. For example, if you have both web and mobile players, and want to view and compare metrics across them, you should use the same environment. Additionally, if you are using Mux Video, use the same environment for Mux Data. Views tracked by Mux Data for videos or live streams streamed from Mux Video are automatically populated with Mux Video identifiers when they’re within the same environment. This allows you to easily view metrics for your assets and live streams in your Mux dashboard. Learn more in our [blog post](https://www.mux.com/blog/giving-developers-more-with-mux-data-mux-video) on Data features for Mux Video.

## How are Mux Data environment keys used?

Each environment has a client-side key associated with it, which you can find on your Environments page. You’ll also see it in Get Started with Data (accessed from the Overview page) for any environment you haven’t integrated yet. When integrating a Mux Data SDK, your environment key allows us to associate the views collected with that SDK to the correct environment. Environment keys are not secret. In rare cases where you would like to change your environment key, [contact us](/support) and we can change it for you.


# Signing JWTs
JSON Web Tokens are an open, industry standard method for representing claims securely between two parties. Mux APIs leverage JWTs to authenticate requests.
## What is a JWT?

JWTs are made up of a header, a payload, and a signature. The header contains metadata useful for decrypting the rest of the token. The payload contains configuration options. And the signature is generated from a signing key-pair. More information can be found at [jwt.io](https://jwt.io/).

In order to sign the JWT you must create a signing key. Signing keys can be created from the [Signing Keys section](https://dashboard.mux.com/settings/signing-keys) of the Mux Dashboard or via the <ApiRefLink href="/docs/api-reference/system/signing-keys">Mux System API</ApiRefLink>. This key-pair will be used by a cryptographic function to sign JWTs.

## Signing JWTs during Development

While developing an app, you may want an easy way to generate JWTs locally because you're not yet ready to set up a full blown production system that signs JWTs for client-side applications. There are a few different options for generating these JWTs.

### Web Based JWT Signer

<Callout type="warning">
  Pasting credentials into a web browser is generally a bad practice. This web-based tool signs JWTs on the client which means your credentials never leave your machine. This is a tool designed by Mux, intended to be used with Mux credentials, and will always be hosted on a Mux domain. **Never use a tool like this if it is hosted on a non-Mux domain.**
</Callout>

Mux provides a web based JWT Signer at https://jwt.mux.dev. Simply input the Signing key-pair and configure the claims you wish to test your app with. Then, copy the JWT into your application code and run it.

<Image src="/docs/images/jwt-signer.gif" width={600} height={440} alt="Mux's JWT Signer" />

### Node based CLI

Mux provides a [Node.js based CLI](https://github.com/muxinc/cli) for performing common tasks including signing JWTs for [playback IDs](https://github.com/muxinc/cli#mux-sign-playback-id).

After [installing Node.js](https://nodejs.org/), the Mux CLI must be initialized with an Access Token. Follow [this guide](/docs/core/make-api-requests#http-basic-auth) to create an Access Token. With your newly created Access Token, initialize the Mux CLI.

```
npx @mux/cli init
```

Now that the Mux CLI is initialized with your credentials, you can sign a JWT for [Video Playback](https://github.com/muxinc/cli#mux-sign-playback-id).

```
npx @mux/cli sign PLAYBACK-ID
```

For more details, refer to https://github.com/muxinc/cli.

<Callout type="warning">
  You should only sign a JWT on the server, where you can keep your signing key secret. You should not put your signing key in the client itself.
</Callout>

<Callout type="success">
  Setup a REST endpoint behind your own authentication system that provides your client-side code with signed JWTs. That way, the sensitive secret from the signing key-pair stays on the server instead of being included in the client.
</Callout>

## Signing JWTs for Production

Once you're ready for customers to start using your app, you need a way to sign JWTs securely at-scale. Use the code examples below depending on which Mux product you would like to sign JWTs for.

### Sign Video Playback JWTs

```go

package main

import (
    "encoding/base64"
    "fmt"
    "log"
    "time"

    "github.com/golang-jwt/jwt/v4"
)

func main() {

    playbackId := "" // Enter your signed playback id here
    keyId      := "" // Enter your signing key id here
    key        := "" // Enter your base64 encoded private key here

    decodedKey, err := base64.StdEncoding.DecodeString(key)
    if err != nil {
        log.Fatalf("Could not base64 decode private key: %v", err)
    }

    signKey, err := jwt.ParseRSAPrivateKeyFromPEM(decodedKey)
    if err != nil {
        log.Fatalf("Could not parse RSA private key: %v", err)
    }

    token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
        "sub": playbackId,
        "aud": "v",
        "exp": time.Now().Add(time.Minute * 15).Unix(),
        "kid": keyId,
    })

    tokenString, err := token.SignedString(signKey)
    if err != nil {
        log.Fatalf("Could not generate token: %v", err)
    }

    fmt.Println(tokenString)
}

```

```node

// We've created some helper functions for Node to make your signing-life easier
const Mux = require('@mux/mux-node');
const mux = new Mux();

async function createTokens () {
  const playbackId = ''; // Enter your signed playback id here

  // Set some base options we can use for a few different signing types
  // Type can be either video, thumbnail, gif, or storyboard
  let baseOptions = {
    keyId: '', // Enter your signing key id here
    keySecret: '', // Enter your base64 encoded private key here
    expiration: '7d', // E.g 60, "2 days", "10h", "7d", numeric value interpreted as seconds
  };

  const token = await mux.jwt.signPlaybackId(playbackId, { ...baseOptions, type: 'video' });
  console.log('video token', token);

  // Now the signed playback url should look like this:
  // https://stream.mux.com/${playbackId}.m3u8?token=${token}

  // If you wanted to pass in params for something like a gif, use the
  // params key in the options object
  const gifToken = await mux.jwt.signPlaybackId(playbackId, {
    ...baseOptions,
    type: 'gif',
    params: { time: '10' },
  });
  console.log('gif token', gifToken);

  // Then, use this token in a URL like this:
  // https://image.mux.com/${playbackId}/animated.gif?token=${gifToken}

  // A final example, if you wanted to sign a thumbnail url with a playback restriction
  const thumbnailToken = await mux.jwt.signPlaybackId(playbackId, {
    ...baseOptions,
    type: 'thumbnail',
    params: { playback_restriction_id: YOUR_PLAYBACK_RESTRICTION_ID },
  });
  console.log('thumbnail token', thumbnailToken);

  // When used in a URL, it should look like this:
  // https://image.mux.com/${playbackId}/thumbnail.png?token=${thumbnailToken}
}

```

```php

<?php
  // Using Composer and https://github.com/firebase/php-jwt
  require __DIR__ . '/vendor/autoload.php';
  use \Firebase\JWT\JWT;

  $playbackId = ""; // Enter your signed playback id here
  $keyId = "";      // Enter your signing key id here
  $keySecret = "";  // Enter your base64 encoded private key here

  $payload = array(
    "sub" => $playbackId,
    "aud" => "t",          // v = video, t = thumbnail, g = gif.
    "exp" => time() + 600, // Expiry time in epoch - in this case now + 10 mins
    "kid" => $keyId,

    // Optional, include any additional manipulations
    "time"     => 10,
    "width"    => 640,
    "fit_mode" => "smartcrop"
  );

  $jwt = JWT::encode($payload, base64_decode($keySecret), 'RS256');

  print "$jwt\n";

?>

```

```python

# This example uses pyjwt / cryptography:
# pip install pyjwt
# pip install cryptography

import jwt
import base64
import time

playback_id = ''        # Enter your signed playback id here
signing_key_id = ''     # Enter your signing key id here
private_key_base64 = '' # Enter your base64 encoded private key here

private_key = base64.b64decode(private_key_base64)

token = {
    'sub': playback_id,
    'exp': int(time.time()) + 3600, # 1 hour
    'aud': 'v'
}
headers = {
    'kid': signing_key_id
}

json_web_token = jwt.encode(
    token, private_key, algorithm="RS256", headers=headers)

print(json_web_token)

```

```ruby

require 'base64'
require 'jwt'

def sign_url(playback_id, audience, expires, signing_key_id, private_key, params = {})
    rsa_private = OpenSSL::PKey::RSA.new(Base64.decode64(private_key))
    payload = {sub: playback_id, exp: expires.to_i, kid: signing_key_id, aud: audience}
    payload.merge!(params)
    JWT.encode(payload, rsa_private, 'RS256')
end

playback_id = ''        # Enter your signed playback id here
signing_key_id = ''     # Enter your signing key id here
private_key_base64 = '' # Enter your base64 encoded private key here

token = sign_url(playback_id, 'v', Time.now + 3600, signing_key_id, private_key_base64)

```



### Sign Data JWTs

```go

package main

import (
    "encoding/base64"
    "fmt"
    "log"
    "time"
    "github.com/golang-jwt/jwt/v4"
)

func main() {

    myId := ""       // Enter the id for which you would like to get counts here
    myIdType := ""   // Enter the type of ID provided in my_id; one of video_id | asset_id | playback_id | live_stream_id
    keyId := ""      // Enter your signing key id here
    key := ""        // Enter your base64 encoded private key here

    decodedKey, err := base64.StdEncoding.DecodeString(key)
    if err != nil {
        log.Fatalf("Could not base64 decode private key: %v", err)
    }

    signKey, err := jwt.ParseRSAPrivateKeyFromPEM(decodedKey)
    if err != nil {
        log.Fatalf("Could not parse RSA private key: %v", err)
    }

    token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
        "sub": myId,
        "aud": myIdType,
        "exp": time.Now().Add(time.Minute * 15).Unix(),
        "kid": keyId,
    })

    tokenString, err := token.SignedString(signKey)
    if err != nil {
        log.Fatalf("Could not generate token: %v", err)
    }

    fmt.Println(tokenString)
}

```

```node

// using @mux/mux-node@8

import Mux from '@mux/mux-node';
const mux = new Mux();
const myId = ''; // Enter the id for which you would like to get counts here
const myIdType = ''; // Enter the type of ID provided in myId; one of video_id | asset_id | playback_id | live_stream_id
const signingKeyId = ''; // Enter your Mux signing key id here
const privateKeyBase64 = ''; // Enter your Mux base64 encoded private key here

const getViewerCountsToken = async () => {
    return await mux.jwt.signViewerCounts(myId, {
        expiration: '1 day',
        type: myIdType,
        keyId: signingKeyId,
        keySecret: privateKeyBase64,
    });
};

const sign = async () => {
    const token = await getViewerCountsToken();
    console.log(token);
};

sign();

```

```php

<?php

  // Using https://github.com/firebase/php-jwt

  use \Firebase\JWT\JWT;

  $myId = "";       // Enter the id for which you would like to get counts here
  $myIdType = "";   // Enter the type of ID provided in my_id; one of video_id | asset_id | playback_id | live_stream_id
  $keyId = "";      // Enter your signing key id here
  $keySecret = "";  // Enter your base64 encoded private key here

  $payload = array(
  "sub" => $myId,
  "aud" => $myIdType,
  "exp" => time() + 600, // Expiry time in epoch - in this case now + 10 mins
  "kid" => $keyId
  );

  $jwt = JWT::encode($payload, base64_decode($keySecret), 'RS256');

  print "$jwt\n";

?>

```

```python

# This example uses pyjwt / cryptography:
# pip install pyjwt
# pip install cryptography

import jwt
import base64
import time

my_id = ''              # Enter the id for which you would like to get counts here
my_id_type = ''         # Enter the type of ID provided in my_id; one of video_id | asset_id | playback_id | live_stream_id
signing_key_id = ''     # Enter your signing key id here
private_key_base64 = '' # Enter your base64 encoded private key here

private_key = base64.b64decode(private_key_base64)

payload = {
    'sub': my_id,
    'aud': my_id_type,
    'exp': int(time.time()) + 3600, # 1 hour
}
headers = {
    'kid': signing_key_id
}

encoded = jwt.encode(payload, private_key, algorithm="RS256", headers=headers)
print(encoded)

```

```ruby

require 'base64'
require 'jwt'

def sign_url(subject, audience, expires, signing_key_id, private_key, params = {})
    rsa_private = OpenSSL::PKey::RSA.new(Base64.decode64(private_key))
    payload = {sub: subject, aud: audience, exp: expires.to_i, kid: signing_key_id}
    payload.merge!(params)
    JWT.encode(payload, rsa_private, 'RS256')
end

my_id = ''                 # Enter the id for which you would like to get counts here
my_id_type = ''            # Enter the type of ID provided in my_id; one of video_id | asset_id | playback_id | live_stream_id
signing_key_id = ''        # Enter your signing key id here
private_key_base64 = ''    # Enter your base64 encoded private key here

token = sign_url(my_id, my_id_type, Time.now + 3600, signing_key_id, private_key_base64)

```



# Limit which Environments a user has access to in the Dashboard
Learn how to restrict which Environments a user can see in the Dashboard
This feature allows Admins to limit which Environments a given user can access within the Dashboard.

## Admin use of Environment restrictions

All management of Environment access is done in the Dashboard under **User > Organization**.

Admins have the following permissions:

* Access to all Environments
* View what Environments any given user has access to
* See what users can access a specific Environment
* Apply Environment restrictions to a user invitation
* Manage Environment restrictions for all users

## Inviting new users with Environment restrictions

Upon inviting a new user to an organization, Admins must provide at least one (1) Environment that the new user can access.

## Modify Environment access for existing users

Admins can modify the Environment restrictions for users at any time. Clicking on a user under **User > Organization** will display a list of the Environments that user has access to in the Dashboard, which can be toggled on/off and applied on Save. All users must have access to at least one (1) environment.
