Mux

The Mux Developer Hub

Welcome to the Mux developer hub. You'll find comprehensive guides and documentation to help you start working with Mux as quickly as possible, as well as support if you get stuck. Let's jump right in!

Get Started

Objective-C - Building a Custom Integration

Note: This guide is currently under development. Reach out to [email protected] if you have any questions or see any issues.

Getting Started

Mux has a pre-built integration with Apple's AVPlayer for iOS and tvOS applications; for these players, see here: Integration Guide: iOS.

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.

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. This project is a good example of how to use the Objective-C core library in building a player integration.

Including the Library

Including via Cocoapods

To include the core Objective-C library via Cocoapods, modify your Podfile to use frameworks by including use_frameworks! and then add the following pod to your Podfile:

  • pod "Mux-Stats-Core", "~> 2.0"

This will include our current release of the core Objective-C library. There will be no breaking updates within major versions of this library, so you can safely run pod update.

Including Manually (not preferred)

If you do not you use Cocoapods and wish to include the library manually, include the correct Mux Objective-C Core SDK for your project by cloning our repository and dragging the frameworks into your Xcode project. The Frameworks folder contains two folders, one for iOS and one for tvOS. Inside these folders, there are 3 additional folders containing different architecture combinations. The fat folder contains a library with all architectures in one.

You can use the fat library when developing but this library cannot be used when compiling for submission to the App Store as it contains the simulator architectures that are not used by any Apple devices (the community believes this is a bug). You can use the framework in the release folder when building a release version of your application, or you can run a script to strip unneeded architectures.

$ git clone https://github.com/muxinc/stats-sdk-objc.git

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. Instead, for the Objective-C library, Environment and Viewer-specific data should be emitted to the SDK globally as follows.

MUXSDKEnvironmentData *environmentData = [[MUXSDKEnvironmentData alloc] init];
[environmentData setMuxViewerId:[[[UIDevice currentDevice] identifierForVendor] UUIDString]];
MUXSDKViewerData *viewerData = [[MUXSDKViewerData alloc] init];
NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier];
if (bundleId) {
  [viewerData setViewerApplicationName:bundleId];
}
// Set additional Viewer data as above
MUXSDKDataEvent *dataEvent = [[MUXSDKDataEvent alloc] init];
[dataEvent setEnvironmentData:environmentData];
[dataEvent setViewerData:viewerData];
[MUXSDKCore dispatchGlobalDataEvent:dataEvent];

The only field that should be modified within MUXSDKEnvironmentData is the muxViewerId, via setMuxViewerId, 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, via [MUXSDKCustomerViewerData setViewerUserId:].

For MUXSDKViewerData, the fields that may be provided are the following.

@property (nullable) NSString *viewerApplicationEngine;
@property (nullable) NSString *viewerApplicationName;
@property (nullable) NSString *viewerApplicationVersion;
@property (nullable) NSString *viewerDeviceCategory;
@property (nullable) NSString *viewerDeviceManufacturer;
@property (nullable) NSString *viewerDeviceName;
@property (nullable) NSString *viewerOsArchitecture;
@property (nullable) NSString *viewerOsFamily;
@property (nullable) NSString *viewerOsVersion;

See the AVPlayer integration for example values used.

Provide Callbacks

The Objective-C library does not require any callbacks to retrieve player data. Instead, the integration should send all player-, view-, and video-level data with each event.

Emit Events

For the Objective-C 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.

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
  • 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
  • Anything in MUXSDKCustomerPlayerData, as defined here: Metadata
  • Anything in MUXSDKCustomerVideoData, as defined here: Metadata
  • Anything in MUXSDKCustomerViewData, as defined here: Metadata

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:

// Prepare the data update
MUXSDKVideoData *videoData = [[MUXSDKVideoData alloc] init];
[videoData setVideoSourceWidth:[NSNumber numberWithInt:width]];
[videoData setVideoSourceHeight:[NSNumber numberWithInt:height]];
// put it within a MUXSDKDataEvent
MUXSDKDataEvent *dataEvent = [[MUXSDKDataEvent alloc] init];
[dataEvent setVideoData:videoData];
// Emit the event
[MUXSDKCore dispatchEvent:dataEvent forPlayer:_playerName];

Playback Events

The Playback Events should be emitted as the events are defined in the referenced document. With regards to naming, the names should align with those in the document, with the following changes: MUXSDK is appended in front of the name, the name itself should be PascalCased, and Event is appended at the end. For instance, for playerready, the corresponding event is MUXSDKPlayerReadyEvent, as defined in MUXSDKPlayerReadyEvent.h.

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

  • 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. AVPlayer, AVPlayerLayer, etc)
  • playerSoftwareLanguageCode - The language code (e.g. en-US) of the player UI localization
  • playerWidth - The width of the player, in pixels
  • playerHeight - The height of the player, in 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
  • playerErrorCode - 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.

For instance, when emitting the MUXSDKPlayerReady event, it should look like the following:

// Get the player data
MUXSDKPlayerData *playerData = [[MUXSDKPlayerData alloc] init];
// Set the player data information
[playerData setPlayerMuxPluginName:@"Sample Custom Player"];
// ... repeat the above for all values within `MUXSDKPlayerData`
// Emit the event
MUXSDKPlayerReadyEvent *event = [[MUXSDKPlayerReadyEvent alloc] init];
[event setPlayerData: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, and follow similar naming conventions.

In particular:

  • Network throughput events should be emitted as MUXSDKRequestBandwidthEvents, with the addition of MUXSDKBandwidthMetricData set on the event via [MUXSDKRequestBandwidthEvent setBandwidthMetricData:].
  • Ad events are emitted via a special method, dispatchAdEvent, and details can be seen within Mux's IMA integration for AVPlayer

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 (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.

// 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.
MUXSDKDataEvent *dataEvent = [[MUXSDKDataEvent alloc] init];
[dataEvent setEnvironmentData:environmentData];
[dataEvent setViewerData:viewerData];
[MUXSDKCore dispatchGlobalDataEvent:_dataEvent];

// Prepare the view before you emit any other playback events
MUXSDKViewInitEvent *event = [[MUXSDKViewInitEvent alloc] init];
[event setPlayerData:playerData];
[MUXSDKCore dispatchEvent:event forPlayer:playerName];

// Dispatch data about the view itself. 
// Note: customerPlayerData must include your environment key.
MUXSDKDataEvent *dataEvent = [[MUXSDKDataEvent alloc] init];
[dataEvent setCustomerPlayerData:customerPlayerData];
[dataEvent setCustomerVideoData:customerVideoData];
[MUXSDKCore dispatchEvent:dataEvent forPlayer:_playerName];

// Emit playback events
MUXSDKPlayerReadyEvent *event = [[MUXSDKPlayerReadyEvent alloc] init];
[event setPlayerData:playerData];
[MUXSDKCore dispatchEvent:event forPlayer:_playerName];

// When the player begins to attempt playback
MUXSDKPlayEvent *event = [[MUXSDKPlayEvent alloc] init];
[event setPlayerData:playerData];
[MUXSDKCore dispatchEvent:event forPlayer:_playerName];

// When the player actually displays first moving frame
MUXSDKPlayingEvent *event = [[MUXSDKPlayingEvent alloc] init];
[event setPlayerData:playerData];
[MUXSDKCore dispatchEvent:event 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.

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 mux-stats-sdk-avplayer here. 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 YES.
[player dispatchViewEnd];
[player dispatchViewInit];
MUXSDKDataEvent *dataEvent = [MUXSDKDataEvent new];
[dataEvent setCustomerVideoData:videoData];
dataEvent.videoChange = YES;
[MUXSDKCore dispatchEvent:dataEvent forPlayer:name];

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: _name]; for the name of your player, so that the core library can clean up any monitoring and end the view session.

Updated about a month ago

Objective-C - Building a Custom Integration


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.