# MediaMelon iOS Custom Multi-Player SDK Integration Document

**Step 1:** [Add MediaMelon SDK](#step-1-add-mediamelon-smartstreaming-sdk-hardbreak)

**Step 2:** [Instantiate and Register SDK](#step-2-register-and-initialize-mediamelon-sdk)

**Step 3:** [Initialize Playback Session](#step-3-initialize-playback-session)

**Step 4:** [Additional Custom Metadata](#step-4-additional-custom-metadata)

**Step 5:** [Network and Stream Information](#step-5-network-and-stream-information)

**Step 6:** [Chunk/Segment Information](#step-6-chunk-segment-information)

**Step 7:** [Player Events](#step-7-player-events)

**Step 8:** [Fallback & Request Status](#step-8-fallback-and-request-status)

**Step 9:** [Ad Data & Ad Events](#step-9-a-d-data-and-ad-events)

**Step 10:** [Custom Events](#step-10-custom-events)

[Release Notes](#release-notes)

### **Step 1: Add** MediaMelon SDK <a href="#step-1-add-mediamelon-smartstreaming-sdk-hardbreak" id="step-1-add-mediamelon-smartstreaming-sdk-hardbreak"></a>

* **SPM:**

```swift
.package(
      url: "https://github.com/MediamelonSDK/mm-ios-qoe-sdk",
      .upToNextMajor(from: "3.1.1")
 ),
```

### **Step 2:** Instantiate and Register SDK <a href="#step-2-register-and-initialize-mediamelon-sdk" id="step-2-register-and-initialize-mediamelon-sdk"></a>

#### Step 2.1: SDK Instance Creation:

```swift
let mmCustomAdapter = MMCustomAdapter()
```

#### Step 2.2: Report Registration Information:

{% hint style="info" %}
`CUSTOMER_ID` is your MediaMelon assigned Customer ID. If you do not know your Customer ID, contact MediaMelon at <customer-support@mediamelon.com>.
{% endhint %}

```swift
let registrationInfo = MMRegistrationInformation(customerID: "CUSTOMER_ID", playerName: "PLAYER_NAME")
registrationInfo.setDomain("DOMAIN_NAME")
registrationInfo.setSubscriberInformation(subscriberID: "SUBSCRIBER", subscriberType: "SUBSCRIBER_TYPE", subscriberTag: "SUBSCRIBER_TAG", hashSubscriberID: false)
registrationInfo.setPlayerId(playerId: "PLAYER_ID")
registrationInfo.setPlayerInformation(brand: "PLAYER_BRAND", model: "PLAYER_MODEL", version: "PLAYER_VERSION")
registrationInfo.setBasePlayerInformation(basePlayerName: "BASE_PLAYER_NAME", basePlayerVersion: "BASE_PLAYER_VERSION")

mmCustomAdapter.reportRegistrationInformation(registrationInfo)
```

{% hint style="info" %}
`hashSubscriberID`: Set it to `true` to hash the subscriber ID, and to `false` to process the subscriber ID without hashing.
{% endhint %}

#### Step 2.3: Report Device Information:

```swift
let deviceInfo = MMDeviceInformation()
deviceInfo.setDeviceTpe(type: "DEVICE_TYPE")
deviceInfo.setBrandName(brandName: "BRAND_NAME")    // Device Brand or Manufacturer
deviceInfo.setDeviceModel(deviceModel: "DEVICE_MODEL")
deviceInfo.setOSName(osName: "OS_NAME")
deviceInfo.setOSVersion(osVersion: "OS_VERSION")
deviceInfo.setTelecomOperator(telecomOperator: "TELECOM_OPERATOR")
deviceInfo.setScreenResolution(width: <screen_width>, height: <screen_height>)  //Integer Value

mmCustomAdapter.reportDeviceInformation(deviceInfo)
```

#### Step 2.4: Report Application Information:

```swift
mmCustomAdapter.reportAppData(appName: "APP_NAME", appVersion: "APP_VERSION")
mmCustomAdapter.reportAppSessionID(appSessionId: "APP_SESSION_ID")
```

#### Step 2.5: Report Sub Property ID:

```swift
mmCustomAdapter.reportSubPropertyID(subPropertyId: "SUBPROPERTY_ID")
```

### Step 3: Initialize Playback Session

{% hint style="danger" %}
`initializeAssetForPlayer( assetInfo:, registrationInfo:, deviceInfo:)` is **deprecated**. Use separate APIs to report `registrationInfo` and `deviceInfo` as given in [Step 2.2](#step-2.2-report-registration-information) and [Step 2.3](#step-2.3-report-device-information).
{% endhint %}

#### Step 3.1: Initialize Session with Content Metadata:

```swift
let assetInfo = MMAssetInformation(assetURL: "STREAM_URL", assetID: "ASSET_ID", assetName: "ASSET_NAME", videoId: "VIDEO_ID")
assetInfo.setEpisodeNumber("EPISODE_NUMBER")
assetInfo.setContentType("CONTENT_TYPE")
assetInfo.setSeriesTitle("SERIES_TITLE")
assetInfo.setGenre("GENRE")
assetInfo.setSeason("SEASON")
assetInfo.setDrmProtection("DRM_PROTECTION")
assetInfo.setDrmLevel("DRM_LEVEL")
assetInfo.addCustomKVP("CUSTOM_1", "VALUE_1")
assetInfo.addCustomKVP("CUSTOM_2", "VALUE_2")

mmCustomAdapter.initializeAssetForPlayer(assetInfo: assetInfo)
```

{% hint style="warning" %}
All metadata fields set from **Step 3** will be reset when `initializeAssetForPlayer` is called. Therefore, ensure that all session-specific metadata, such as `view session ID`, `preload`, and similar fields, are reported after `initializeAssetForPlayer` is called for every playback session.
{% endhint %}

#### Step 3.2: Report View Session ID:

```swift
mmCustomAdapter.reportViewSessionID(viewSessionId: "VIEW_SESSION_ID")
```

#### Step 3.3: Report Experiment Name:

```swift
mmCustomAdapter.reportExperimentName(experimentName: "EXPERIMENT_NAME")
```

#### Step 3.4: Report Preload:

```swift
mmCustomAdapter.reportPreload(<boolean>)
```

#### Step 3.5: Report Player Resolution:

```swift
mmCustomAdapter.reportPlayerResolution(width: <width>, height: <height>)
```

#### Step 3.6: Report User-Initiated Playback:

{% hint style="info" %}
This function indicates the intent for playback and must be invoked after `initializeAssetForPlayer`. Any metadata fields reported before this API call will be included in the first payload sent to the dashboard. To avoid `Null` values in metadata fields, it is recommended to report all available metadata before calling this API.
{% endhint %}

```swift
mmCustomAdapter.reportUserInitiatedPlayback()
```

### Step 4: Additional Custom Metadata

Check the custom tags configuration in your [dashboard](https://smartsight3.mediamelon.com/settings) and report accordingly. If the custom tags are not configured, please configure and use them accordingly.

```swift
mmCustomAdapter.reportCustomMetadata(key: "CUSTOM_3", value: "VALUE_3")
```

### Step 5: Network and Stream Information

#### Step 5.1: Report Network Information:

```swift
mmCustomAdapter.reportNetworkInfo(cdn: "CDN", 
                                  asn: <asn>,                     // Integer Value
                                  hostName: "SOURCE_HOST_NAME", 
                                  networkType: "NETWORK_TYPE",    // Network Connection Type
                                  networkOperator: "NETWORK_OPERATOR")
```

{% hint style="info" %}
Use the `reportNetworkInfo` API to report the CDN along with other network information.\
If additional network information is not available, use `reportCDN` instead.
{% endhint %}

#### Step 5.2: Report CDN Information:

```swift
mmCustomAdapter.reportCDN(cdn: "CDN")
```

#### Step 5.3: Report Stream Information:

```swift
mmCustomAdapter.reportStreamInfo(streamURL: "STREAM_URL", 
                                 streamFormat: "STREAM_FORMAT", 
                                 mediaType: "MEDIA_TYPE", 
                                 sourceType: "SOURCE_TYPE")
```

#### Step 5.4: Report Encoding Service:

```swift
mmCustomAdapter.reportEncodingService(encodingService: "ENCODING_SERVICE");
```

#### Step 5.5: Report Video Presentation Information:

```swift
mmCustomAdapter.reportPresentationInfo(isLive: <is_live>, videoDurationInMS: <video_duration>)
```

{% hint style="info" %}

* `is_live`: Set to `true` for live video stream and to `false` for the VOD stream.
* `video_duration`: Video duration in milliseconds.
  {% endhint %}

#### Step 5.6: Report Track Information:

Call this API with initial values, and every time there is a change in the track info. Call this API when the user enables or disables subtitles or changes the audio track.

```swift
mmCustomAdapter.reportMediaTrackInfo(isSubtitleActive: <is_subtitle_active>, subtitleTrack: "SUBTITLE_TRACK", audioTrack: "AUDIO_TRACK", isVDSActive: <is_vds_active>);
```

{% hint style="info" %}
`is_subtitle_active`: Set it to `true` if the subtitles are active; otherwise, set it to `false`.

`is_vds_active`: Set it to `true` if the type of audio is Virtual Dialogue Sound, otherwise, set it to `false`.
{% endhint %}

#### Step 5.7: Report Rendition:

At the start of the video, create a ReditionInfo object, assign initial rendition values to the object, and report it to the SDK. For any subsequent rendition change, update only the fields that changed in the same RenditionInfo object. Leave the unchanged fields as-is, and report the updated object to the SDK.

```swift
var renditionInfo = MMRenditionInformation()

renditionInfo.bitrate = <bitrate>            //Integr Value in bps
renditionInfo.width = <width>                //Integer Value
renditionInfo.height = <height>              //Integer Value
renditionInfo.frameRate = <frame_rate>       //Double Value in fps
renditionInfo.aCodec = "AUDIO_CODEC"
renditionInfo.vCodec = "VIDEO_CODEC"

mmCustomAdapter.reportRendition(renditionInfo: renditionInfo)
```

#### Step 5.8: Update DRM:

{% hint style="danger" %}
`updateDrmType(drmType:)` is **deprecated**. A new API called `updateDRM(drmProtection:, drmLevel:)` is introduced.
{% endhint %}

```swift
mmCustomAdapter.updateDRM(drmProtection: "DRM_PROTECTION", drmLevel: "DRM_LEVEL");
```

### Step 6: Chunk/Segment Information

#### Step 6.1: Report Download Rate:

Report the latest chunk download rate using this method. Trigger this method for every chunk.

```swift
mmCustomAdapter.reportDownloadRate(downloadRate: <downloadRate>)  //Integer Value in bps
```

### Step 7: Player Events

#### Step 7.1: Report Player State:

```swift
mmCustomAdapter.reportPlayerState(playerState: .PLAYING)

Enum: MMPlayerState
- PLAYING
- PAUSED
- STOPPED
```

#### Step 7.2: Report Buffering:

```swift
mmCustomAdapter.reportBufferingStarted()
mmCustomAdapter.reportBufferingCompleted()
```

#### Step 7.3: Report Seek:

```swift
mmCustomAdapter.reportPlayerSeekStarted() 
mmCustomAdapter.reportPlayerSeekCompleted(seekEndPositionInMS: <seek_end_position>) //Integer Value in Milli Seconds
```

#### Step 7.4: Report Error:

```swift
mmCustomAdapter.reportError(errorCode: "ERROR_CODE",
                            errorMessage: "ERROR_MESSAGE",
                            errorDetails: "ERROR_DETAILS")
```

#### Step 7.5: Report Warning:

```swift
mmCustomAdapter.reportWarning(warningCode: "WARNING_CODE",
                              warningMessage: "WARNING_MESSAGE",
                              warningDetails: "WARNING_DETAILS")
```

#### Step 7.6: Report Playback Position:

Call this every 0.5 sec or 1 sec to report the playback position from the player

```swift
mmCustomAdapter.reportPlaybackPosition(currentPlayerPosInMS: <playback_position>) //Integer Value in Milli Seconds
```

### Step 8: Fallback & Request Status

#### Step 8.1: Report Fallback Event:

```swift
mmCustomAdapter.reportFallbackEvent(fallbackManifestURL: "NEW_STREAM_URL", description: "DESCRIPTION")
```

#### Step 8.2: Report Request Status:

Report `FAILED` or `CANCELLED` requests for the following request types:

* PIR
* MANIFEST
* AUDIO\_CHUNK
* VIDEO\_CHUNK
* DRM
* SUBTITLE

```swift
var requestInfo : [String: Any] = [
    "id": "ID",
    "url": "URL",
    "error": "ERROR",
    "text": "TEXT",
    "hostname": "HOSTNAME",
    "url": "URL",
    ...
]

mmCustomAdapter.reportRequestStatus(status: .CANCELLED, type: "REQUEST_TYPE", requestInfo: requestInfo)

Enum: RequestStatus
- FAILED
- CANCELLED
```

### Step 9: Ad Data & Ad Events

#### Step 9.1: Report Ad Break Start & End:

```swift
mmCustomAdapter.reportAdBreakStart()
mmCustomAdapter.reportAdBreakEnd()
```

#### Step 9.2: Report Ad Data, Ad Start & End:

```swift
let adInfo = MMAdInformation()

adInfo.setAdTitle("AD_TITLE")
adInfo.setAdId("AD_ID")
adInfo.setAdUniversalId("AD_UNIVERSAL_ID")
adInfo.setAdCreativeId("AD_CREATIVE_ID")
adInfo.setAdCreativeType("AD_CREATIVE_TYPE")
adInfo.setAdClient("AD_CLIENT")
adInfo.setAdPosition("AD_POSITION")
adInfo.setAdServer("AD_SERVER")
adInfo.setAdResolution("AD_RESOLUTION")
adInfo.setAdUrl("AD_URL")
adInfo.setAdDuration(<ad_duration>)                    //Integer Value in Milli Seconds
adInfo.setAdPodIndex(<pod_index>)                      //Integer Value
adInfo.setAdPositionInPod(<ad_position_in_pod>)        //Integer Value
adInfo.setAdPodLendth(<pod_length>)                    //Integer Value
adInfo.setIsBumper(<is_bumper>)                        //Boolean Value

mmCustomAdapter.reportAdStart(adInfo: adInfo)
mmCustomAdapter.reportAdEnd()
```

<details>

<summary>Ad Info Fields &#x26; Description</summary>

<table data-header-hidden><thead><tr><th width="197.25390625">Field</th><th width="114.51953125">Data Type</th><th>Description</th></tr></thead><tbody><tr><td>adTitle</td><td>String</td><td>The title or name of the ad, usually provided in the VAST metadata or by the ad server.</td></tr><tr><td>adId</td><td>String</td><td>A unique identifier for the ad creative, often defined by the ad server or DSP.</td></tr><tr><td>adUniversalId</td><td>String</td><td>A globally consistent identifier for an ad creative that remains the same across all platforms and systems, enabling unified tracking and reporting.</td></tr><tr><td>adCreativeId</td><td>String</td><td>The creative ID associated with the specific ad asset (video, image, etc.). Helps in tracking and reporting creative-level performance.</td></tr><tr><td>adCreativeType</td><td>String</td><td>The format or type of the ad creative. For example, video/mp4, image/jpeg, etc., or linear, non-linear.</td></tr><tr><td>adClient</td><td>String</td><td>The SDK or client library responsible for requesting and playing the ad. Example: Google IMA, Freewheel, SpotX.</td></tr><tr><td>adPosition</td><td>String</td><td>The timing of the ad in relation to the main content: "pre" (before), "mid" (during), or "post" (after).</td></tr><tr><td>adServer</td><td>String</td><td>The ad server or source that delivered the ad. Example: Google Ad Manager, Freewheel, etc.</td></tr><tr><td>adResolution</td><td>String</td><td>The resolution of the ad video (e.g., 1920x1080), useful for reporting and quality monitoring.</td></tr><tr><td>adUrl</td><td>String</td><td>The URL from which the ad video is fetched. Typically a media file or stream URL.</td></tr><tr><td>adDuration</td><td>Integer</td><td>The total duration of the ad, in milliseconds. Example: 30000 = 30 seconds.</td></tr><tr><td>adPodIndex</td><td>Integer</td><td>The index of the ad pod within the stream. Ad pods are groups of ads played together (like a commercial break).</td></tr><tr><td>adPositionInPod</td><td>Integer</td><td>The position of the ad within its pod (e.g., 1st ad, 2nd ad in the group).</td></tr><tr><td>adPodLendth</td><td>Integer</td><td>The total number of ads in the current pod. Useful for showing “Ad 2 of 5” type of UI.</td></tr><tr><td>isBumper</td><td>Boolean</td><td>A boolean (true/false) indicating whether the ad is a bumper ad (short ad, usually &#x3C;6s, played at the start or end of ad breaks).</td></tr></tbody></table>

</details>

#### Step 9.3: Report Ad Buffering:

Use Ad Buffering APIs to report any buffering that occurs during ad playback. For any buffering event, either content buffering or ad buffering should be reported, but not both.

```swift
mmCustomAdapter.reportAdBufferingStrated();
mmCustomAdapter.reportAdBufferingCompleted();
```

### Step 10: Custom Events

The Custom Events API can be used to report events that are not covered by the default MediaMelon-supported events. Custom events must be reported within the SDK activity lifecycle. Events reported before SDK initialization or after session end will be ignored. Please refer to the guidelines and constraints below.

```swift
mmCustomAdapter.reportCustomEvent("EVENT_NAME", "EVENT_VALUE");
```

{% hint style="info" %}
**Guidelines & Constraints**

* **Event Name**
  * Maximum length: 35 characters
  * Allowed characters: Alphanumeric and underscore (\_) only
  * Special characters are not supported
  * Recommended format: UPPERCASE with underscores (e.g., DRM\_CHANGE)
* **Event Limits**
  * A maximum of 35 custom events per session is allowed.

Custom events that violate these constraints (invalid event name or exceeding the per-session limit) will be dropped. Event values exceeding 1000 characters will be truncated to 1000 characters.
{% endhint %}

#### Release Notes

<details>

<summary>Current Release</summary>

#### v3.1.1[^1]  <sup><sub>**`May 04, 2026`**<sub></sup>

* Fixed the `MMPlayerState` missing issue.

</details>

***

<details>

<summary>Previous Releases</summary>

#### v3.1.0[^1]  <sup><sub>**`Apr 21, 2026`**<sub></sup>

* Added support for multiple player instances within a single application.
* Added the following new metadata fields:
  * **Player ID** - report it using `setPlayerId` as part of Registration Information.
  * **DRM Level** - report it using `setDrmLevel` as part of the Asset Information.
  * **Ad Universal ID** - report it using `setAduniversalId` as part of the Ad Information.
* Added new APIs to report `Registration Information` and `Device Information` before initialization (refer to [Step 2](#step-2-register-and-initialize-mediamelon-sdk)).
* `MMAdInfo` replaced with `AdInformation` class (refer to [Step 9.2](#step-9.2-report-a-d-data-ad-start-and-end)).
* **Deprecations:**
  * `updateDRMType(drmType)` → **Deprecated**
    * Use `updateDRM(drmProtection, drmLevel)` (refer to [Step 5.8](#step-5.8-update-drm))
  * `initializeAssetForPlayer(assetInfo:, registrationInfo:, deviceInfo:)` → **Deprecated**
    * Use `initializeAssetForPlayer(assetInfo:)` (refer to [Step 3.1](#step-3.1-initialize-session-with-content-metadata))
    * Report `RegistrationInfo` and `DeviceInfo` separately before initialization.
* Buffering events exceeding the stats interval are now included in the stats payload.
* Updated **metadata lifecycle handling**:
  * [Step 2](#step-2-register-and-initialize-mediamelon-sdk) metadata fields persist across the SDK instance lifecycle.
  * All Metadata fields from [Step 3](#step-3-initialize-playback-session) are reset for every playback session.

***

#### [Non-Multiplayer Releases](/mediamelon/smartsight-player-sdk-integration/ios/mediamelon-ios-custom-sdk-integration-document.md#release-notes)

</details>

[^1]: **Performance Report:** [here](https://docs.mediamelon.com/mediamelon/sdk-performance-metrics/apple/apple-custom-multi-player-sdk-performance-metrics)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.mediamelon.com/mediamelon/smartsight-player-sdk-integration/ios/mediamelon-ios-custom-multi-player-sdk-integration-document.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
