# 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>


---

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