This guide walks through integration with Google's Media3 to collect video performance metrics with Mux data.
Features
Install the Mux Data SDK
Integrate this SDK with Media 3 in your app
Add Metadata
Advanced Features
Migrating from the Mux Data SDK for ExoPlayer
Release notes
The Mux Data SDK for Media3 integrates Mux Data with Google's AndroidX Media3 SDK in order to integrate your video app with Mux Data. Our SDK consists of a set of open-source libraries capable of observing Media3 for events and data related to your customers' playback experience.
This guide will walk you through a basic integration with Mux Data and your Media3 app. You will add the Mux Data SDK to your project, integrate the SDK with your Media3 Player
and if necessary, learn to customize our SDK's functionality based on your specific needs
The following data can be collected by the Mux Data SDK when you use the AndroidX Media3 SDK, as described below.
Supported Features:
renditionchange
eventsRequest Latency is not available.
Players other than ExoPlayer
Most of this guide assumes you are using ExoPlayer
, specifically, as opposed to a MediaController
or a custom implementation of Player
. Our SDK does offer support for players other than ExoPlayer
, but this support is limited by the interface of Player
and Player.Listener
. You may supplement the data we are able to collect using your Player
's specific APIs by overriding BaseMedia3Binding
and supplying that object when you create your MuxStatsSdkMedia3<>
for your custom player.
Add Mux's maven repository to your gradle files. Newer projects require declaring this in settings.gradle
, and older projects require it to be set in the project-level build.gradle
.
// in a repositories {} block
maven {
url = uri("https://muxinc.jfrog.io/artifactory/default-maven-release-local")
}
Add our library to the dependencies
block for your app. Replace the string [Current Version]
with the current version of the SDK from the releases page.
implementation("com.mux.stats.sdk.muxstats:data-media3:[Current Version]")
By default, we try to support the latest minor release of media3 with our SDK. That is, 1.0, 1.1, etc. When media3 updates, we update our data-media3
library to support the newest version. If you need an update to the Mux Data SDK, but can't update your media3 integration, you can use one of our -atX_Y
variants. These variants of our Mux Data SDK receive all the same updates as the default version, but offer support for a specific version of media3.
To stay on a specific version of media3, add the appropriate version to the end of our artifactId
. For example, to always use Media3 1.0.x, use the library at com.mux.stats.sdk.muxstats:data-media3-at_1_0:[Current Version]
// Stay on media3 1.0 while getting the most-recent mux data
implementation("com.mux.stats.sdk.muxstats:data-media3-at_1_0:[Current Version]")
We try to support all production versions of media3. Currently, we support the following versions:
Get your ENV_KEY
from the Mux environments dashboard.
Env Key is different than your API token
ENV_KEY
is a client-side key used for Mux Data monitoring. These are not to be confused with API tokens which are created in the admin settings dashboard and meant to access the Mux API from a trusted server.
To monitor a Player
, you need to create your player and monitor it before setting playWhenReady
or calling prepare()
. You must initialize your Mux Data integration with a valid Environment Key.
val myCustomerData = CustomerData()
val muxStats = exoPlayer.monitorWithMuxData(
context = context,
envKey = "YOUR MUX DATA ENV KEY HERE",
customerData = myCustomerData,
playerView = playerView
)
// Do these after creating the monitor
player.playWhenReady = true
player.prepare()
// ... When you are done with your Player
muxStats.release()
player.release()
You can make your data more informative and actionable by supplementing it with data of your own. To supply this data, you can use the CustomerData
object you created in Step 2.
val customerData = CustomerData().apply {
customerVideoData = CustomerVideoData().apply {
// Data about this video
// Add or change properties here to customize video metadata such as title,
// language, etc
videoId = "My Custom Video ID"
}
customerViewData = CustomerViewData().apply {
// Data about this viewing session
viewSessionId = UUID.randomUUID().toString()
}
customerViewerData = CustomerViewerData().apply {
// Data about the Viewer and the device they are using
muxViewerDeviceCategory = "kiosk"
muxViewerDeviceManufacturer = "Example Display Systems"
muxViewerOsVersion = "1.2.3-dev"
}
customData = CustomData().apply {
// Add values for your Custom Dimensions.
// Up to 10 strings can be set to track your own data
customData1 = "Hello"
customData2 = "World"
customData3 = "From"
customData4 = "Mux"
customData5 = "Data"
}
// And now call monitorWithMuxData, like in Step 2.
Those examples contain only a few of the fields available. For more information, see the Metadata Guide.
All metadata details are optional, however you'll be able to compare and see more interesting results as you include more details. This gives you more metrics and metadata about video streaming, and allows you to search and filter on important fields like the player version, CDN, and video title.
Certain metadata can be collected automatically, such as the media title, source URL, and poster art.
There are two cases where the underlying tracking of the video view needs to be reset: first, when you load a new source URL into an existing player, and second, when the program within a single media stream changes (such as a program within a live stream, described more below).
Note: You do not need to change the video info when changing to a different source of the same video content (e.g. different resolution or video format).
When you change to a new video (in the same player) you need to update the information that Mux knows about the current video. Examples of when this is needed are:
This is done by calling muxStatsExoPlayer.videoChange(CustomerVideoData)
which will remove all previous video data and reset all metrics for the video view. See Metadata for the list of video details you can provide. You can include any metadata when changing the video but you should only need to update the values that start with video
.
It's best to change the video info immediately after telling the player which new source to play.
In some cases, you may have the program change within a stream, and you may want to track each program as a view on its own. An example of this is a live stream that streams multiple programs back to back, with no interruptions.
In this case, call muxStatsExoPlayer.programChange(CustomerVideoData)
. This will remove all previous video data and reset all metrics for the video view, creating a new video view. See Metadata for the list of video details you can provide. You can include any metadata when changing the video but you should only need to update the values that start with video
.
For most use cases, the SDK is capable of detecting whether or not a video is being played full-screen. Specifically, it can do so in the case where the player view is the same size as the device display (excepting ActionBars and other framework window decoration).
For other uses cases (non-overlaid controls, window decoration via plain View
s, etc) you may need to tell the SDK when the user switches to full-screen.
If you are using SimplePlayerView
or a similar ExoPlayer UI component, you can set the full-screen flag from the OnFullScreenModeChangedListener
.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// If you are using SimplePlayerView, StyledPlayerView, etc
playerView = findViewById(R.id.my_player_view)
playerView.setFullscreenButtonClickListener { isFullScreen ->
// Set presentation based on which mode is requested
if(isFullScreen) {
muxStats.presentationChange(MuxSDKViewPresentation.FULLSCREEN)
} else {
muxStats.presentationChange(MuxSDKViewPresentation.NORMAL)
}
// Handle moving to fullscreen playback with your code
}
}
By default, Mux's integration with ExoPlayer automatically tracks fatal errors as thrown by ExoPlayer. If a fatal error happens outside the context of ExoPlayer and you want to track it with Mux, you can call muxStats.error(MuxErrorException)
like this:
// Error code: integer value for the generic type of error that
// occurred.
// Error message: String providing more information on the error
// that occurred.
// For an example, the HTML5 video element uses the
// following: https://developer.mozilla.org/en-US/docs/Web/API/MediaError
// for codes and messages. Feel free to use your own codes and messages
val errorCode = 1
val errorMessage = "A fatal error was encountered during playback"
val errorContext = "Additional information about the error such as a stack trace"
val error = MuxErrorException(errorCode, errorMessage, errorContext)
muxStats.error(error)
Note that error(MuxErrorException e)
can be used with or without automatic error tracking. If your application has retry logic that attempts to recover from ExoPlayer errors then you may want to disable automatic error tracking like this:
muxStats.setAutomaticErrorTracking(false)
It is important that you only trigger an error when the playback has to be abandoned or aborted in an unexpected manner, as Mux tracks fatal playback errors only.
The Mux Data SDK for Media3 can observe events that occur during Ad playback. To enable this functionality, you need to attach an instance of MuxStatsSdkMedia3<ExoPlayer>
to your ImaAdsLoader
.
The Mux Data SDK must take over the AdErrorListener
and AdEventListener
of your loader, but you can supply your own listeners, as shown in the example.
Fist, add Mux's Media3 IMA Extension to your build:
// in your app's dependencies
implementation("com.mux.stats.sdk.muxstats:data-media3-ima:0.7.1")
Then, use the extension to monitor your IMA integration.
val newPlayer = ExoPlayer.Builder(this)
.setMediaSourceFactory(DefaultMediaSourceFactory(DefaultDataSource.Factory(this))
.setLocalAdInsertionComponents({ adsLoader }, view.playerView))
// ... rest of builder calls
.build()
val customerData = CustomerData()
// optionally, set properties on CustomerData
muxStats = newPlayer.monitorWithMuxData(context, DATA_ENV_KEY, customerData)
adsLoader = ImaAdsLoader.Builder(this)
// ... rest of builder calls
.monitorWith(
muxStats = muxStats!!,
customerAdErrorListener = { /*Optional parameter, your custom logic*/ },
customerAdEventListener = { /*Optional parameter, your custom logic*/ },
)
.build()
adsLoader.setPlayer(newPlayer)
The Mux SDK supports sending an event when the playback orientation changes. You can trigger this by calling muxStatsExoPlayer.orientationChange(MuxSDKViewOrientation orientation)
, passing either MuxSDKViewOrientation.LANDSCAPE
or MuxSDKViewOrientation.PORTRAIT
depending on the current orientation of the player.
If you are updating from our ExoPlayer SDK, you have to do a short migration. The migration steps below should get you building again:
implementation "com.mux.stats.sdk.muxstats:data-media3:1.0.0"
MuxStatsExoPlayer
to MuxStatsSdkMedia3<ExoPlayer>
new ExoPlayerBinding()
to the end of the parameters you set when creating your muxStats
.Improvements:
renditionchange
s during ad breaks must be deferred until after the ad breakInternal lib updates:
android
to v1.4.5muxstats.core
to v8.4.1Improvements:
Internal Library Updates:
muxstats-android
to v1.4.4Updates:
VideoPlayerAdCallback
supply it to ImaAdsLoader.monitorWith
Fixes:
Internal lib updates:
stats.java
to 8.1.2muxstats.android
to 1.4.2Fixes:
1.2.x
instead of the real versionImprovements:
Internal lib updates:
android
lib 1.4.0stats.java
lib to 8.1.0kt-utils
from the dependencies. It is no longer requiredImprovements:
Improvements:
New:
Updates:
Fixes:
Improvements:
New:
MuxErrorException
now allows you to report non-fatal and business-related errorsImprovements:
Fixes:
Fixes:
Improvements:
Updates:
Updates:
Updates:
IDevice
and INetworkRequest
for injection, as with the other player sdksUpdates:
Fixes:
Fixes:
Improvements:
New:
Fixes:
Improvements: