> 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/roku-player.md).

# Roku with RAF SDK

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

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

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

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

**Step 5:** [Ad Tracking with RAF](#step-5-a-d-tracking-with-raf)

**Step 6:**  [Variables and Description](#step-6-variables-and-description)

### 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://mediamelon-builds.s3.us-east-1.amazonaws.com/MM-RELEASE-BUILDS/SDK_RELEASES/Roku/QoE/2.6.2/MMSmartStream.zip)

### Step 2: Setup 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="mmAdData" type="assocarray" alwaysNotify="true" />
    <field id="mmAdPlaying" type="Boolean" 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" />
  </interface>
  <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 Integrate <a href="#step-3-integrate-mediamelon-code-to-main-application" id="step-3-integrate-mediamelon-code-to-main-application"></a>

#### **3.1.** Add MMTask Child Component:&#x20;

Add the MediaMelon `MMTask` as a child component to the main video scene `custom-playback-channel-master/components/MainScene.xml`

```xml
<component name="MainScene" extends="Scene" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://devtools.web.roku.com/schema/RokuSceneGraph.xsd">
<!-- importing main handler -->
<script type="text/brightscript" uri="pkg:/components/MainScene.brs" />
<children>
    <MMTask id="MM"/>
</children>
```

#### 3.2. 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 %}

```brightscript
m.MM = m.top.FindNode("MM")
m.MM.setField("video", m.video)
```

#### 3.3. Set Configuration Details:

```brightscript
MMConfig = {
  customerID: "CUSTOMER_ID"
  subscriberId: "SUBSCRIBER_ID"
  subscriberType: "SUBSCRIBER_TYPE"
  subscriberTag: "SUBSCRIBER_TAG"
  playerName: "Roku"
  disableManifestFetch: false
  domainName: "DOMAIN_NAME"
  appName: "APP_NAME"
  appSdkVersion: "APP_VERSION"
  viewSessionId: "VIEW_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, then you can disable manifest fetch. This is an optional step.
* `hashSubscriberId`: Set to `true` to hash the subscriberId and to `false` to process 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.
  {% endhint %}

#### 3.4. Set Content Metadata:

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

#### 3.5. Set Custom Configuration Details:

```brightscript
customConfig = {
  cdn: "CDN"
  experimentName: "EXPERIMENT_NAME"
  subPropertyId: "SUB_PROPERTY_ID"
  streamFormat: "STREAM_FORMAT"
  mediaType: "MEDIA_TYPE"
  drmProtection: "DRM_PROTECTION"
}
m.MM.setField("customConfig", customConfig)
```

#### 3.6. Set Codecs:

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

#### 3.7. Set Custom Metadata:

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

#### 3.8. Set Seek Threshold:

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

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

#### 3.9. Set is Video Live:

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

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

#### 3.10. Set View:

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

* **Start View:**

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

* **End View:**

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

#### 3.11. Run the SDK:

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

### Step 4: Error Handling

By default, 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 %}

#### 4.1. Video Error Reporting:

```brightscript
m.MM.error = { 
    errorCode: "ERROR_CODE",
    errorMessage: "ERROR_MESSAGE",
    errorDetails: "ERROR_DETAILS"
}
```

#### 4.2. Ad Error Reporting:

```brightscript
m.MM.adError = { 
    errorCode: "ERROR_CODE",
    errorMessage: "ERROR_MESSAGE",
    errorDetails: "ERROR_DETAILS"
}
```

### Step 5: Ad Tracking with RAF

```brightscript
    RAF = Roku_Ads()
    RAF.setAdUrl(vastTag)

    logObj = {
        log: function(evtType = invalid as dynamic, ctx = invalid as dynamic)
            'The below function sends the Ad data to MediaMelon SDK for ad tracking
            sendAdDataToMMSDK(ctx)
    }
    logFunc = function(obj = invalid as dynamic, evtType = invalid as dynamic, ctx = invalid as dynamic)
        obj.log(evtType, ctx)
    end function
    RAF.setTrackingCallback(logFunc, logObj)
    
    adPods = RAF.getAds()
```

```brightscript
function sendAdDataToMMSDK(ctx as object)
    if m.global <> invalid and m.global.MMAnalytics <> invalid
        m.global.MMAnalytics.setField("mmAdData", ctx)
    end if
end function
```

Set the field named `mmAdPlaying` to true when an ad break started as shown below:

```brightscript
'preroll ads
if adPods <> invalid and adPods.count() > 0
        video.control = "stop"
        m.top.playingAd = true
        print "Preroll adPods > " adPods
        m.global.MMAnalytics.setField("mmAdPlaying", true) 'This line is part of MM SDK Integration
        keepPlaying = RAF.showAds(adPods, invalid, view)
 end if

'midroll ads
 if adPods <> invalid and adPods.count() > 0
        'ask the video to stop - the rest is handled in the state=stopped event below
        print "Midroll adPods > " adPods
        m.global.MMAnalytics.setField("mmAdPlaying", true) 'This line is part of MM SDK Integration
        video.control = "stop"
 end if
```

### Step 6: Variables and Description

<table data-header-hidden><thead><tr><th width="242.33333333333331">Variable</th><th width="359">Description</th><th></th></tr></thead><tbody><tr><td>Variable</td><td>Description</td><td>Optional/Mandatory</td></tr><tr><td>PLAYER_NAME</td><td>String containing the Player Name.</td><td>Mandatory</td></tr><tr><td>CUSTOMER_ID</td><td>String containing your MediaMelon-assigned Customer ID.</td><td>Mandatory</td></tr><tr><td>SUBSCRIBER_ID</td><td>String containing your Subscriber’s ID.</td><td>Mandatory</td></tr><tr><td>DOMAIN_NAME</td><td>String containing your section of your subscriber or assets.</td><td>Mandatory</td></tr><tr><td>SUBSCRIBER_TYPE</td><td>String containing the Subscriber Type (e.g. “Free”, “Paid”).</td><td>Mandatory</td></tr><tr><td>SUBSCRIBER_TAG</td><td>String containing additional subscriber-specific information. This is sent in clear (not hashed) to SmartSight and it is advised not to send sensitive information in this field.</td><td>Optional</td></tr><tr><td>ASSET_ID</td><td>String containing Asset Id.</td><td>Mandatory</td></tr><tr><td>ASSET_NAME</td><td>String containing Asset Name.</td><td>Mandatory</td></tr><tr><td>VIDEO_ID</td><td>String containing your video’s ID.</td><td>Mandatory</td></tr><tr><td>CONTENT_TYPE</td><td>String containing the type of the Content. For example - "Movie", "Special", "Clip", "Scene" or "Episode".</td><td>Mandatory</td></tr><tr><td>GENRE</td><td>String containing the Genre of the content. For example - "Comedy", "Horror".</td><td>Mandatory</td></tr><tr><td>DRM_PROTECTION</td><td>Widevine, Fairplay, Playready, etc. Unknown means content is protected, but the protection type is unknown. For clear contents, do not set this field</td><td>Mandatory</td></tr><tr><td>EPISODE_NUMBER</td><td>String containing sequence number of the Episode.</td><td>Mandatory</td></tr><tr><td>SEASON</td><td>String containing the Season. For example - "Season 1".</td><td>Mandatory</td></tr><tr><td>SERIES_TITLE</td><td>String containing Title of the Series.</td><td>Mandatory</td></tr><tr><td>VIDEO_TYPE</td><td>String containing Video Type. For example - "LIVE", and "VOD".</td><td>Optional</td></tr><tr><td>PLAYER_BRAND</td><td>String containing Player Brand (e.g. “Exo Player”).</td><td>Mandatory</td></tr><tr><td>PLAYER_MODEL</td><td>String containing Player Model. For example - This could be a variant of a player. Say the name of third third-party player used by the organisation. Or any human-readable name of the player.</td><td>Mandatory</td></tr><tr><td>PLAYER_VERSION</td><td>String containing Player Version.</td><td>Mandatory</td></tr><tr><td>CUSTOM_KEY_1</td><td>Custom metadata key can be added here if required.</td><td>Optional</td></tr><tr><td>VALUE_1</td><td>Custom metadata value can be added here if required.</td><td>Optional</td></tr><tr><td>BASE_PLAYER_NAME</td><td>String containing base player name</td><td>Optional</td></tr><tr><td>BASE_PLAYER_VERSION</td><td>String containing base player version</td><td>Optional</td></tr><tr><td>EXPERIMENT_NAME</td><td>You can use this field to categorise views into different experiments, allowing you to filter by this dimension later.</td><td>Optional</td></tr><tr><td>SUB_PROPERTY_ID</td><td>A sub-property is an optional feature that allows you to organize data within a property. For instance, a video platform could use sub-properties to group data by customer, or a media company might use them to differentiate between its various websites.</td><td>Optional</td></tr><tr><td>VIEW_SESSION_ID</td><td>An ID that can be used to link the view with upstream platform services, such as CDN or origin logs, for correlation purposes.</td><td>Optional</td></tr><tr><td><code>hashSubscriberId</code></td><td>To hash the subscriber ID set this boolean variable to <code>true</code>, else set it to <code>false</code>.</td><td>Optional</td></tr><tr><td><code>player</code></td><td>Player Object.</td><td>Mandatory</td></tr><tr><td><code>streamURL</code></td><td>Current playing content stream URL.</td><td>Mandatory</td></tr><tr><td><code>isVideoLive</code></td><td>Set this boolean variable to <code>true</code> if the content is Live else set it to <code>false</code> for VOD content.</td><td>Optional</td></tr></tbody></table>
