- Introduction to Data
- Track your video performance
- Make API requests
- Setup alerts
- Make your data actionable with metadata
- Track autoplaying videos
- Extend Data with custom metadata
- Track CDN for request metrics
- See how many people are watching
- Build a custom integration
- Understand metric definitions
- Export raw video view data
- Ensure privacy compliance
- Mux Data FAQs
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.
In this guide:
Mux has a pre-built integration with Google's ExoPlayer v2 and 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.
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. This project is a good example of how to use the Java core library in building a player integration.
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:
repositories { maven { url "https://muxinc.jfrog.io/artifactory/default-maven-release-local" } }
Next, add a dependency on Mux Core (current version is 7.0.7):
api 'com.mux:stats.muxcore:7.0.7'
Option 2: Add Maven dependency
Add the Mux repository to your Maven pom.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.7):
<dependency> <groupId>com.mux</groupId> <artifactId>stats.muxcore</artifactId> <version>7.0.7</version> </dependency>
Option 3: Download the Mux Core JAR file and include it with your application
The latest version of the Mux Core JAR file can be found here.
Import this JAR file into your Android application, as you would any standard JAR file, and include it as a dependency within your top-level build.gradle
file.
Note the version marked in the file in the libs
directory. As of v5.0.0, the IDevice
interface has changed.
// build.gradle dependencies { // other dependencies api files('libs/MuxCore.jar') }
This will include our current release of the core Java SDK. There will be no breaking updates within major versions of this library, so you should update as new versions are released.
The core Java SDK is initialized by implementing certain interfaces and providing these back to the SDK. In general, the structure used within MuxBaseExoPlayer should be followed, where you create a class that extends EventBus
and implements IPlayerListener
, and then follows the following general steps.
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:
- Initializes the
EventBus
superclass - Sets the host device to a new instance of a class that implements
IDevice
- Sets the host network API to a new instance of a class that implements
INetworkRequest
- Instantiates a new instance of
MuxStats
, passing itself (a class that implementsIPlayerListener
) along with metadata - 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.
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.
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.
INetworkRequest
The INetworkRequest
interface defines the methods that the Mux core SDK requires in order to make the necessary network requests.
package com.mux.stats.sdk.muxstats; public interface INetworkRequest { // Interface for handling network completion requests. Implemented in MuxCore interface IMuxNetworkRequestsCompletion { void onComplete(boolean result); } // When invoked, the passed `url` should be used in a standard GET request void get(URL url); // When invoked, the passed `url` should be used in a POST request, // with the provided `body` and the provided set of `headers`. void post(URL url, JSONObject body, Hashtable<String, String> headers); // When invoked, a POST request should be made to the URL: // `"https://" + envKey + ".litix.io"`, with the `body` and `headers` as // passed. Once the request completes (successful or not), the // `callback` should be called with `true` if it was successful, // and `false` if not. void postWithCompletion(String envKey, String body, Hashtable<String, String> headers, IMuxNetworkRequestsCompletion callback); }
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.
IPlayerListener
The IPlayerListener
interface defines the callbacks that MuxStats
will utilize to retrieve player state information.
package com.mux.stats.sdk.muxstats; public interface IPlayerListener { // Return the current playhead position in milliseconds 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 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 application, in pixels int getPlayerViewWidth(); // Return the height of the player in application, in pixels int getPlayerViewHeight(); // Return the current advertised bitrate of the rendition playing // in the player, in bits per second Integer getSourceAdvertisedBitrate(); // Return the current advertised framerate of the rendition playing // in the player, in frames per second Float getSourceAdvertisedFramerate(); }
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. 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).
Playback Events
For the Java core SDK, the Mux 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.
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:
playerMuxPluginName
- The name of the integration being built, as a stringplayerMuxPluginVersion
- The version of the integration being built, as a stringplayerSoftwareName
- The name of the player software (e.g.Exoplayer
, etc)playerSoftwareLanguageCode
- The language code (e.g. en-US) of the player UI localizationplayerWidth
- The width of the player, in pixelsplayerHeight
- The height of the player, in pixelsplayerIsFullscreen
- Boolean of whether the player is currently displayed in full screen or notplayerIsPaused
- 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 millisecondsplayerErrorCode
- Error code for the current fatal playback error. Omit this if the player has not encountered an error for this view.playerErrorMessage
- Error message for the current fatal playback error. Omit this if the player has not encountered an error for this view.
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 in order to make a working integration.
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.
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.
The implementation of these events for the Mux ExoPlayer integration can be found here in this file, 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. 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:
ViewData viewData = new ViewData(); viewData.setViewPrerollAdId(adId); viewData.setViewPrerollCreativeId(creativeId); AdPlayEvent adPlayEvent = new AdPlayEvent(null); adPlayEvent.setViewData(viewData); 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, 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);
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.