> For the complete documentation index, see [llms.txt](https://docs.mediamelon.com/mediamelon/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.mediamelon.com/mediamelon/smartsight-player-sdk-integration/roku/mediamelon-roku-sdk-integration-document.md).

# MediaMelon Roku SDK Integration Document

**Step 1:** [Include MediaMelon SmartStreaming SDK](#step-1-include-mediamelon-smartstreaming-sdk)

**Step 2:** [Set up a new MediaMelon Task](#step-2-setup-new-mediamelon-task)

**Step 3:** [MediaMelon Code Integration](#step-3-integrate-mediamelon-code-to-main-application)

**Step 4:** [Request Information](#step-4-request-information)

**Step 5:** [Error Handling](#step-5-error-handling)

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

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

[Release Notes](#release-notes)

### Step 1: Include MediaMelon SmartStreaming SDK <a href="#step-1-include-mediamelon-smartstreaming-sdk" id="step-1-include-mediamelon-smartstreaming-sdk"></a>

To integrate, unzip the MediaMelon Roku library and place the `MMSmartStream` folder in the components directory.

SDK: [MediaMelon Roku SDK](https://sdks.mediamelon.com/roku/qoe/2.11.0/MMSmartStream.zip)

### Step 2: Set up a new MediaMelon Task <a href="#step-2-setup-new-mediamelon-task" id="step-2-setup-new-mediamelon-task"></a>

Create a new XML file `MMTask.xml` inside your components folder. This is used to link MediaMelon’s `brs`files into your application.

The `MMTask.xml`file should contain the following:

```xml
<component name="MMTask" extends="Task">
  <interface>
    <field id="video" type="node" alwaysNotify="true" />
    <field id="config" type="assocarray" alwaysNotify="true" />
    <field id="customConfig" type="assocarray" alwaysNotify="true" />    
    <field id="contentMetadata" type="assocarray" alwaysNotify="true" />
    <field id="customTags" type="assocarray" alwaysNotify="true" />
    <field id="error" type="assocarray" alwaysNotify="true"/>
    <field id="adError" type="assocarray" alwaysNotify="true"/>
    <field id="view" type="String" alwaysNotify="true" />
    <field id="exit" type="Boolean" alwaysNotify="true" />
    <field id="mmRafEvent" type="assocarray" alwaysNotify="true"/>
    <field id="sdk_version" type="String" alwaysNotify="true" />
    <field id="seekThreshold" type="Integer" alwaysNotify="true"/>
    <field id="codecs" type="assocarray" alwaysNotify="true" />
    <field id="isVideoLive" type="Boolean" alwaysNotify="true" />
    <field id="requestInfo" type="assocarray" alwaysNotify="true" />
    <field id="customEvent" type="assocarray" alwaysNotify="true" />
    <field id="deviceInfo" type="assocarray" alwaysNotify="true" />
  </interface>
  <script type="text/brightscript" uri="pkg:/components/MMSmartStream/MMNetworkHandler.brs" />
  <script type="text/brightscript" uri="pkg:/components/MMSmartStream/MMSmartStreamWrapper.brs" />
  <script type="text/brightscript" uri="pkg:/components/MMSmartStream/MMSmartStreamEngine.brs" />
  <script type="text/brightscript" uri="pkg:/components//MMSmartStream/MMSmartStreamRokuPlugin.brs" />
  <script type="text/brightscript" uri="pkg:/components/MMSmartStream/Utilities.brs" />
</component>

```

### Step 3: MediaMelon Code Integration <a href="#step-3-integrate-mediamelon-code-to-main-application" id="step-3-integrate-mediamelon-code-to-main-application"></a>

#### 3.1. Create MM Task Node:

In the main application, after the video instance has been created, create a MediaMelon Task node, and pass the Video node to it.

{% 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 %}

```raku
m.MM = createObject("roSGNode", "MMTask")
m.MM.setField("video", m.video)
```

#### 3.2. Set Configuration Details:

```raku
MMConfig = {
  customerID: "CUSTOMER_ID"
  subscriberId: "SUBSCRIBER_ID"
  subscriberType: "SUBSCRIBER_TYPE"
  subscriberTag: "SUBSCRIBER_TAG"
  playerName: "PLAYER_NAME"
  playerId: "PLAYER_ID"
  playerVersion: "PLAYER_VERSION"
  basePlayerName: "BASE_PLAYER_NAME"
  basePlayerVersion: "BASE_PLAYER_VERSION"
  disableManifestFetch: true
  domainName: "DOMAIN_NAME"
  appName: "APP_NAME"
  appSdkVersion: "APP_VERSION"
  viewSessionId: "VIEW_SESSION_ID"
  appSessionId: "APP_SESSION_ID"
  hashSubscriberId: false 'Important
  enableCustomErrorReporting: true 'Important
}
m.MM.setField("config", MMConfig)
```

{% hint style="info" %}

* `disableManifestFetch`: If your workflow restricts the manifest to be accessible from both the player and SDK simultaneously, you can disable manifest fetch. This is an optional step.
* `hashSubscriberId`: Set `true` to hash the subscriberId and to `false` to process the subscriberId without hashing.
* `enableCustomErrorReporting`: If set to `true` SDK will consider only client-reported errors, else if set to `false` SDK will consider errors from the video node. ([Refer Here](#step-4-error-handling))
  {% endhint %}

#### 3.3. Set Content Metadata:

```raku
contentMetadata = {
  "assetName": "ASSET_NAME",
  "assetId": "ASSET_ID",
  "videoId": "VIDEO_ID",
  "genre": "GENRE",
  "drmProtection": "DRM_PROTECTION",
  "drmLevel": "DRM_LEVEL",
  "episodeNumber": "EPISODE_NUMBER",
  "season": "SEASON",
  "seriesTitle": "SERIES_TITLE",
  "contentType": "CONTENT_TYPE"
}
m.MM.setField("contentMetadata", contentMetadata)
```

#### 3.4. Set Custom Configuration Details:

```raku
customConfig = {
  cdn: "CDN"
  experimentName: "EXPERIMENT_NAME"
  subPropertyId: "SUB_PROPERTY_ID"
  streamFormat: "STREAM_FORMAT"
  mediaType: "MEDIA_TYPE"
  drmProtection: "DRM_PROTECTION"
  drmLevel: "DRM_LEVEL"
  preload: <boolean>
  encodingService: "ENCODING_SERVICE"
}
m.MM.setField("customConfig", customConfig)
```

#### 3.5. Set Codecs:

```raku
codecs = {
  video: "VIDEO_CODEC"
  audio: "AUDIO_CODEC"
}
m.MM.setField("codecs", codecs)
```

#### 3.6. Set Custom Metadata:

```raku
customMetadata = {
  "CUSTOM_KEY_1": "VALUE_1",
  "CUSTOM_KEY_2": "VALUE_2"
}
m.MM.setField("customTags", customMetadata)
```

{% hint style="info" %}
For Custom Metadata, first, provide the mapping in the [SmartSight](https://smartsight3.mediamelon.com/settings) dashboard under the settings section, and send the values to the SDK accordingly.
{% endhint %}

#### 3.7. Set Seek Threshold:

By default seek threshold is set to `1.25 seconds` in the SDK.&#x20;

```raku
m.MM.setField("seekThreshold", 2)
```

#### 3.8. Set is Video Live:

Set `isVideoLive` to `true` for live video stream and to `false` for the VOD stream

```raku
m.MM.setField("isVideoLive", true)
```

#### 3.9. Set View:

{% hint style="info" %}
It's important to set the view field  `start` for every new view start, and to `end` when the view ends.
{% endhint %}

* **Start View:**

```raku
m.MM.setField("exit", false)
m.MM.setField("view", "start")
```

* **End View:**

```raku
m.MM.setField("view", "end")
m.MM.setField("exit", true)
```

#### 3.10. Run the SDK:

```raku
m.MM.control = "RUN"
m.player.setFocus(true)
```

#### 3.11. Device Info:

```raku
deviceInfo = {
  deviceCapabilities: "DEVICE_CAPABILITIES"
}
m.MM.setField("deviceInfo", deviceInfo)
```

### Step 4: Request Information

Report `failed` or `cancelled` requests for the following request types:

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

```raku
requestInfo = { 
    requestStatus: <request_status>,    'string (This should be "failed" or "cancelled")
    requestType: <request_type>,        'string (This can be pir, manifest, audio_chunk, video_chunk, drm, subtitle)
    id: "ID",
    error: "ERROR",
    text: "TEXT",
    hostname: "HOSTNAME",
    url: "URL"
}

m.MM.setField("requestInfo", requestInfo)
```

### Step 5: Error Handling

By default, the SDK listens to the video node for errors. To report custom errors, set `enableCustomErrorReporting` flag to `true` in `MMConfig` and report the errors in the following way;

{% hint style="info" %}
Upon setting `enableCustomErrorReporting` flag to true, SDK will stop listening to the video node errors. Only custom errors will be reported.
{% endhint %}

#### 5.1. Video Error Reporting:

```raku
m.MM.error = { 
    errorCode: "ERROR_CODE",
    errorMessage: "ERROR_MESSAGE",
    errorDetails: "ERROR_DETAILS",
    errorSeverity: <error_severity>   'string (This should be "warning" or "fatal")
}
```

{% hint style="info" %}
`error_severity`: Set this to `"warning"` for warnings and set to `"fatal"` for fatal errors.
{% endhint %}

#### 5.2. Ad Error Reporting:

```raku
m.MM.adError = { 
    errorCode: "ERROR_CODE",
    errorMessage: "ERROR_MESSAGE",
    errorDetails: "ERROR_DETAILS",
    errorSeverity: <error_severity>   'string (This should be "warning" or "fatal")
}
```

### Step 6: Ad Data & Ad Events:

```raku
m.mmTask.setField("mmRafEvent", rafEventData)
```

**Sample:** `rafEventData = { obj: rafData, eventType: "Start", ctx: ctx }`

<details>

<summary><strong>Ad Event Type and Mapping</strong></summary>

<table data-search="false"><thead><tr><th>Event Type</th><th>MediaMelon Dashboard Event</th></tr></thead><tbody><tr><td><code>adRequest</code></td><td>AD_REQUEST</td></tr><tr><td><code>adResponse</code></td><td>AD_RESPONSE</td></tr><tr><td><code>PodStart</code></td><td>AD_BREAK_STARTED</td></tr><tr><td><code>PodComplete</code></td><td>AD_BREAK_ENDED</td></tr><tr><td><code>Start</code></td><td>AD_IMPRESSION &#x26; AD_PLAY</td></tr><tr><td><code>Pause</code></td><td>AD_PAUSED</td></tr><tr><td><code>Resume</code></td><td>AD_RESUMED</td></tr><tr><td><code>Complete</code></td><td>AD_COMPLETE</td></tr><tr><td><code>Close</code></td><td>AD_ENDED</td></tr><tr><td><code>FirstQuartile</code></td><td>AD_PLAYED_FIRST_QUARTILE</td></tr><tr><td><code>Midpoint</code></td><td>AD_PLAYED_MID_QUARTILE</td></tr><tr><td><code>ThirdQuartile</code></td><td>AD_PLAYED_THIRD_QUARTILE</td></tr><tr><td><code>Skip</code></td><td>AD_SKIPPED</td></tr><tr><td><code>Click</code></td><td>AD_CLICK</td></tr></tbody></table>

</details>

<details>

<summary><strong>Ad Information (<code>ctx</code>)</strong></summary>

<table data-header-hidden data-search="false"><thead><tr><th width="197.25390625">Field</th><th>Description</th></tr></thead><tbody><tr><td>adTitle</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>A unique identifier for the ad creative, often defined by the ad server or DSP.</td></tr><tr><td>adCreativeId</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>adClient</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>The timing of the ad in relation to the main content: "pre" (before), "mid" (during), or "post" (after).</td></tr><tr><td>adSystem</td><td>The ad server or source that delivered the ad. Example: Google Ad Manager, Freewheel, etc.</td></tr><tr><td>adUrl</td><td>The URL from which the ad video is fetched. Typically a media file or stream URL.</td></tr><tr><td>adPodIndex</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>adPodPosition</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>The total number of ads in the current pod. Useful for showing “Ad 2 of 5” type of UI.</td></tr></tbody></table>

</details>

### Step 7: 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.

```raku
m.MM.customEvent = {
    eventName: "EVENT_NAME",
    eventValue: "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>

#### v2.11.0[^1]  <sup><sub>**`Jun 29, 2026`**<sub></sup>

* Enhanced internal SDK data reporting
  * Separated all data reporting logic into a dedicated `MMNetworkHandler` module.
  * Added batch-based data reporting with configurable reporting intervals.
  * Improved reliability with exponential backoff retries, jitter, request timeouts, and Dead Letter Queue (DLQ) support.
  * Enabled remote configuration for batch size, reporting interval, retry settings, and the batching feature flag.
* Added support for reporting new ad events, including quartiles, request/response, errors, warnings, skip, and click events.
* Added internal intermediate buffering duration reporting for long buffering sessions to improve real-time buffering monitoring.
* Replaced hardcoded logs with the SDK logging function.
* Added support for reporting device capabilities through `deviceInfo` interface.

</details>

***

<details>

<summary>Previous Releases</summary>

#### v2.10.0[^1]  <sup><sub>**`May 06, 2026`**<sub></sup>

* Added new metadata fields `player ID` and `DRM Level`.
* Fixed play duration offset calculation issue.

#### v2.9.0[^1]  <sup><sub>**`Mar 30, 2026`**<sub></sup>

* Added support for reporting a new field encoding service via `customConfig`.
* Introduced custom event reporting through `customEvent` interface.
* Added FPS reporting in the Rendition event.
* Fixed the `NA` Asset Name issue.

#### v2.8.0[^1]  <sup><sub>**`Feb 06, 2026`**<sub></sup>

* Added `CDN_CHANGE` event functionality.
* Added Seek Duration calculation internally.
* Added Audio and Video Codecs in Rendition Event.

#### v2.7.2[^1]  <sup><sub>**`Jan 23, 2026`**<sub></sup>

* Cleaned up the content change handler

#### v2.7.1[^1]  <sup><sub>**`Jan 06, 2026`**<sub></sup>

* Fixed a bug in HTTP port handling in the case of upnext.

#### v2.7.0  <sup><sub>**`Dec 22, 2025`**<sub></sup>

* Updated network call handling to avoid waiting after the HTTP call.
* Updated Device Resolution string.

#### v2.6.5  <sup><sub>**`Dec 12, 2025`**<sub></sup>

* Device & Network Information fixes
* Fixed Player Resolution
* Preload field addition
* App Session Id field addition
* Fix to consider Exit in Buffering

</details>

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