https://engineeringportal.nielsen.com//w/api.php?action=feedcontributions&user=DiegoOrdonez&feedformat=atomEngineering Client Portal - User contributions [en]2024-03-28T18:03:28ZUser contributionsMediaWiki 1.32.2https://engineeringportal.nielsen.com//w/index.php?title=TVOS_SDK_Release_Notes&diff=5581TVOS SDK Release Notes2022-01-25T16:38:56Z<p>DiegoOrdonez: </p>
<hr />
<div>{{Breadcrumb|}} {{Breadcrumb|Digital}} {{CurrentBreadcrumb}}<br />
[[Category:Digital]]<br />
__NOTOC__ <br />
== Release 8.0.0.0 (10-05-2020) ==<br />
* iOS 14 Changes (iOS App Transparency Framework support)<br />
* Disabled sending of server-provided cookies in measurement pings<br />
* AppSDK now includes Xamarin cross-platform framework support<br />
* AppSDK DLL for use in Xamarin C# apps<br />
<br />
<br />
<br />
== Release 7.0.0.0 (09-06-2019) ==<br />
* Support for CDN based config.<br />
* Support for Market based EMM UAID pings.<br />
* Changes required for proper DCR Static measurement in multi-instance/multiple appid's case.<br />
* Fixes for OTT synchronization issues between iOS and Android platforms.<br />
* Fixes for EV data parameters in few scenarios.<br />
* Fixes for DCR Static product behaviour in background app refresh and background fetch scenarios.<br />
* DCR Ad reporting improvements.<br />
* Fixes and improvements for the SDK console log messages.<br />
* Other enhancements and fixes.<br />
<br />
== Release 6.2.0.0 (02-04-2019) == <br />
* Introduced Dynamic framework for SDK.<br />
* Removal of Location Module from SDK Code.<br />
* Increased SDK log file size for Debug mode.<br />
* Removed OTT Airplay/mirroring detection that caused crashes in AVAudioSession class.<br />
* DCR performance improvements.<br />
* Fixed the parsing error happening when clientid/vcid provided as empty in metadata.<br />
* Align AppSDK for FW detection with BSDK for DCR measurement.<br />
* Other Bug Fixes/Enhancements.<br />
<br />
== Release 6.1.0.1 (9-13-2018) ==<br />
*Nielsen SDK support for Xcode 10 and tvOS 12<br />
<br />
== Release 6.0.0.4 (5-24-2018) ==<br />
*If the SDK build target is set to AGF then SDK will send the hello ping to “eu” and “eu-uat” for debug builds. No changes to the non AGF build the default sfcode will continue to be "sdk" and "cert" for debug build.<br />
*The C1 parameter (NUID) will now be sent as encrypted DeviceID.<br />
*New SessionID changes. The sessionID will contain 29 length random characters appended by timestamp.<br />
*Support for multiple SDK instance without any limit.<br />
*New log feature for CAT tool to retrieve the API level information from client apps. This ping will contain the eventType, parameters, SDK version, appid etc.<br />
*Removed Viewability for this release.<br />
<br />
== Release 5.1.1.17 (1-23-2017) ==<br />
*Added "seconds" place to the launch ping<br />
*Ability to opt-out using "Limit Ad Tracking" feature<br />
*Improved CPU Performance while apps are running in background<br />
*Opt-Out pages can be served based on user’s language and locale from device<br />
<br />
== Release 5.1.1.12 (12-14-2016) ==<br />
*Support for Nielsen TV Brand Effect<br />
*Ability to set CMS parameters at a more global level<br />
*Opt-out pages based on locale and country<br />
*Opt-out policy based on the "Limit Ad Tracking" flag<br />
*Collection of additional device information<br />
*Issue a warning in client developer’s console when ad is played for more than 5 minutes<br />
*Removed the requirement for “type” parameter<br />
*Limit the duration reported for App launch<br />
<br />
== Release 5.1.1.7 (9-13-2016) ==<br />
*General bug fix and performance improvements<br />
<br />
== Release 5.1.1.5 (7-12-2016) ==<br />
*Includes a sample TVOS application used to assist during integration.<br />
*Support for TVML based optout pages<br />
*General bug fix and performance improvements<br />
<br />
== Release 5.1.0.7 (6-15-2016) ==<br />
*Combined SDK for DCR US and International (Germany)<br />
*API to signal end of content (end API)<br />
*Support for Pause timeout<br />
*Updated API to support JSON object instead of string.<br />
*Reporting of media URL and bundle ID<br />
*Updated ping retry logic<br />
*Changes in OptOut process behavioral<br />
*Enhanced Debugging and SDK logging<br />
*Changes to API signature</div>DiegoOrdonezhttps://engineeringportal.nielsen.com//w/index.php?title=TVOS_SDK_Release_Notes&diff=5580TVOS SDK Release Notes2022-01-25T16:38:01Z<p>DiegoOrdonez: </p>
<hr />
<div>{{Breadcrumb|}} {{Breadcrumb|Digital}} {{CurrentBreadcrumb}}<br />
[[Category:Digital]]<br />
__NOTOC__ <br />
== Release 8.0.0.0 (10-05-2020) ==<br />
* Disabled sending of server-provided cookies in measurement pings<br />
* AppSDK now includes Xamarin cross-platform framework support<br />
* AppSDK DLL for use in Xamarin C# apps<br />
<br />
<br />
<br />
== Release 7.0.0.0 (09-06-2019) ==<br />
* Support for CDN based config.<br />
* Support for Market based EMM UAID pings.<br />
* Changes required for proper DCR Static measurement in multi-instance/multiple appid's case.<br />
* Fixes for OTT synchronization issues between iOS and Android platforms.<br />
* Fixes for EV data parameters in few scenarios.<br />
* Fixes for DCR Static product behaviour in background app refresh and background fetch scenarios.<br />
* DCR Ad reporting improvements.<br />
* Fixes and improvements for the SDK console log messages.<br />
* Other enhancements and fixes.<br />
<br />
== Release 6.2.0.0 (02-04-2019) == <br />
* Introduced Dynamic framework for SDK.<br />
* Removal of Location Module from SDK Code.<br />
* Increased SDK log file size for Debug mode.<br />
* Removed OTT Airplay/mirroring detection that caused crashes in AVAudioSession class.<br />
* DCR performance improvements.<br />
* Fixed the parsing error happening when clientid/vcid provided as empty in metadata.<br />
* Align AppSDK for FW detection with BSDK for DCR measurement.<br />
* Other Bug Fixes/Enhancements.<br />
<br />
== Release 6.1.0.1 (9-13-2018) ==<br />
*Nielsen SDK support for Xcode 10 and tvOS 12<br />
<br />
== Release 6.0.0.4 (5-24-2018) ==<br />
*If the SDK build target is set to AGF then SDK will send the hello ping to “eu” and “eu-uat” for debug builds. No changes to the non AGF build the default sfcode will continue to be "sdk" and "cert" for debug build.<br />
*The C1 parameter (NUID) will now be sent as encrypted DeviceID.<br />
*New SessionID changes. The sessionID will contain 29 length random characters appended by timestamp.<br />
*Support for multiple SDK instance without any limit.<br />
*New log feature for CAT tool to retrieve the API level information from client apps. This ping will contain the eventType, parameters, SDK version, appid etc.<br />
*Removed Viewability for this release.<br />
<br />
== Release 5.1.1.17 (1-23-2017) ==<br />
*Added "seconds" place to the launch ping<br />
*Ability to opt-out using "Limit Ad Tracking" feature<br />
*Improved CPU Performance while apps are running in background<br />
*Opt-Out pages can be served based on user’s language and locale from device<br />
<br />
== Release 5.1.1.12 (12-14-2016) ==<br />
*Support for Nielsen TV Brand Effect<br />
*Ability to set CMS parameters at a more global level<br />
*Opt-out pages based on locale and country<br />
*Opt-out policy based on the "Limit Ad Tracking" flag<br />
*Collection of additional device information<br />
*Issue a warning in client developer’s console when ad is played for more than 5 minutes<br />
*Removed the requirement for “type” parameter<br />
*Limit the duration reported for App launch<br />
<br />
== Release 5.1.1.7 (9-13-2016) ==<br />
*General bug fix and performance improvements<br />
<br />
== Release 5.1.1.5 (7-12-2016) ==<br />
*Includes a sample TVOS application used to assist during integration.<br />
*Support for TVML based optout pages<br />
*General bug fix and performance improvements<br />
<br />
== Release 5.1.0.7 (6-15-2016) ==<br />
*Combined SDK for DCR US and International (Germany)<br />
*API to signal end of content (end API)<br />
*Support for Pause timeout<br />
*Updated API to support JSON object instead of string.<br />
*Reporting of media URL and bundle ID<br />
*Updated ping retry logic<br />
*Changes in OptOut process behavioral<br />
*Enhanced Debugging and SDK logging<br />
*Changes to API signature</div>DiegoOrdonezhttps://engineeringportal.nielsen.com//w/index.php?title=TVOS_SDK_Release_Notes&diff=5579TVOS SDK Release Notes2022-01-25T16:18:55Z<p>DiegoOrdonez: </p>
<hr />
<div>{{Breadcrumb|}} {{Breadcrumb|Digital}} {{CurrentBreadcrumb}}<br />
[[Category:Digital]]<br />
__NOTOC__ <br />
== Release 8.0.0.0 (10-05-2020) ==<br />
* iOS 14 Changes (iOS App Transparency Framework support)<br />
* iPadOS device classification<br />
* Disabled sending of server-provided cookies in measurement pings<br />
* AppSDK now includes Xamarin cross-platform framework support<br />
* AppSDK DLL for use in Xamarin C# apps<br />
<br />
<br />
<br />
== Release 7.0.0.0 (09-06-2019) ==<br />
* Support for CDN based config.<br />
* Support for Market based EMM UAID pings.<br />
* Changes required for proper DCR Static measurement in multi-instance/multiple appid's case.<br />
* Fixes for OTT synchronization issues between iOS and Android platforms.<br />
* Fixes for EV data parameters in few scenarios.<br />
* Fixes for DCR Static product behaviour in background app refresh and background fetch scenarios.<br />
* DCR Ad reporting improvements.<br />
* Fixes and improvements for the SDK console log messages.<br />
* Other enhancements and fixes.<br />
<br />
== Release 6.2.0.0 (02-04-2019) == <br />
* Introduced Dynamic framework for SDK.<br />
* Removal of Location Module from SDK Code.<br />
* Increased SDK log file size for Debug mode.<br />
* Removed OTT Airplay/mirroring detection that caused crashes in AVAudioSession class.<br />
* DCR performance improvements.<br />
* Fixed the parsing error happening when clientid/vcid provided as empty in metadata.<br />
* Align AppSDK for FW detection with BSDK for DCR measurement.<br />
* Other Bug Fixes/Enhancements.<br />
<br />
== Release 6.1.0.1 (9-13-2018) ==<br />
*Nielsen SDK support for Xcode 10 and tvOS 12<br />
<br />
== Release 6.0.0.4 (5-24-2018) ==<br />
*If the SDK build target is set to AGF then SDK will send the hello ping to “eu” and “eu-uat” for debug builds. No changes to the non AGF build the default sfcode will continue to be "sdk" and "cert" for debug build.<br />
*The C1 parameter (NUID) will now be sent as encrypted DeviceID.<br />
*New SessionID changes. The sessionID will contain 29 length random characters appended by timestamp.<br />
*Support for multiple SDK instance without any limit.<br />
*New log feature for CAT tool to retrieve the API level information from client apps. This ping will contain the eventType, parameters, SDK version, appid etc.<br />
*Removed Viewability for this release.<br />
<br />
== Release 5.1.1.17 (1-23-2017) ==<br />
*Added "seconds" place to the launch ping<br />
*Ability to opt-out using "Limit Ad Tracking" feature<br />
*Improved CPU Performance while apps are running in background<br />
*Opt-Out pages can be served based on user’s language and locale from device<br />
<br />
== Release 5.1.1.12 (12-14-2016) ==<br />
*Support for Nielsen TV Brand Effect<br />
*Ability to set CMS parameters at a more global level<br />
*Opt-out pages based on locale and country<br />
*Opt-out policy based on the "Limit Ad Tracking" flag<br />
*Collection of additional device information<br />
*Issue a warning in client developer’s console when ad is played for more than 5 minutes<br />
*Removed the requirement for “type” parameter<br />
*Limit the duration reported for App launch<br />
<br />
== Release 5.1.1.7 (9-13-2016) ==<br />
*General bug fix and performance improvements<br />
<br />
== Release 5.1.1.5 (7-12-2016) ==<br />
*Includes a sample TVOS application used to assist during integration.<br />
*Support for TVML based optout pages<br />
*General bug fix and performance improvements<br />
<br />
== Release 5.1.0.7 (6-15-2016) ==<br />
*Combined SDK for DCR US and International (Germany)<br />
*API to signal end of content (end API)<br />
*Support for Pause timeout<br />
*Updated API to support JSON object instead of string.<br />
*Reporting of media URL and bundle ID<br />
*Updated ping retry logic<br />
*Changes in OptOut process behavioral<br />
*Enhanced Debugging and SDK logging<br />
*Changes to API signature</div>DiegoOrdonezhttps://engineeringportal.nielsen.com//w/index.php?title=Template:DCR_Content_Metadata&diff=5077Template:DCR Content Metadata2021-06-17T20:16:23Z<p>DiegoOrdonez: </p>
<hr />
<div><br />
{| class="wikitable" <br />
|-<br />
! '''Keys''' !! '''Description''' !! '''Values''' !! '''Required'''<br />
|-<br />
| type || type of asset || <code>"content"</code> || Yes<br />
|-style="background-color:#e9f9fa;"<br />
| assetid || unique ID assigned to asset || custom || Yes<br />
|-<br />
| program ||name of program (25 character limit.) <br> (no abbreviations or shorthand) || The Big Bang Theory || Yes<br />
|-style="background-color:#e9f9fa;"<br />
| title ||episode title with season and episode number (40 character limit) || The Pants Alternative S03E18|| Yes<br />
|-<br />
| length || length of content in seconds || If '''continuous''' (24 x 7) stream, pass the value of 86400.<br> If '''live stream''' of a discrete program (Live Event/Sporting Event), <br>pass length of content. If unknown, pass a value of 0.|| Yes<br />
|-style="background-color:#e9f9fa;"<br />
| airdate || original broadcast or release date for the program || iso date format <br> (YYYY-MM-DDThh:mm:ssTZD)<br> <code>2021-03-21T12:00:00Z</code> <br> <code>2021-03-21T12:00:00-5000</code> || Yes<br />
|-<br />
| isfullepisode || full episode flag || <code>"y"</code>- full episode, <code>"n"</code>- non full episode || Yes<br />
|-style="background-color:#e9f9fa;"<br />
| adloadtype || type of ad load:<br />
<code>"1"</code> Linear – matches TV ad load<br />
<br />
<code>"2"</code> Dynamic – Dynamic Ad Insertion (DAI)<br />
|| <code>"2"</code> - DCR measures content with dynamic ads || Yes<br />
|-<br />
| segB || custom segment B || custom || <br />
|-style="background-color:#e9f9fa;"<br />
| segC || custom segment C || custom || <br />
|-<br />
| crossId1 || TMS ID provided by gracenote || custom || <br />
|-style="background-color:#e9f9fa;"<br />
| crossId2 || content originator (only required for distributors) || custom || <br />
|}<br />
Custom segments (segB and segC) can be used to aggregate video and/or static content within a single Brand to receive more granular reports within a brand.<br />
<br />
Examples regarding usage of segments within SDK:<br />
* All comedy clips and stories for a Brand rolled into a "Comedy" segment<br />
* genre grouping content by Comedy vs. Drama<br />
* group related Text + Video content - i.e. for a show that has a lot of - static pages associated with it<br />
* packaging based on how clients sell inventory<br />
* grouping related types of content either by genre, category or platform.<br />
<br /></div>DiegoOrdonezhttps://engineeringportal.nielsen.com//w/index.php?title=Template:DCR_Content_Metadata&diff=5076Template:DCR Content Metadata2021-06-17T20:15:21Z<p>DiegoOrdonez: </p>
<hr />
<div><br />
{| class="wikitable" <br />
|-<br />
! '''Keys''' !! '''Description''' !! '''Values''' !! '''Required'''<br />
|-<br />
| type || type of asset || <code>"content"</code> || Yes<br />
|-style="background-color:#e9f9fa;"<br />
| assetid || unique ID assigned to asset || custom || Yes<br />
|-<br />
| program ||name of program (25 character limit.) <br> (no abbreviations or shorthand) || The Big Bang Theory || Yes<br />
|-style="background-color:#e9f9fa;"<br />
| title ||episode title with season and episode number (40 character limit) || The Pants Alternative S03E18|| Yes<br />
|-<br />
| length || length of content in seconds || If '''continuous''' (24 x 7) stream, pass the value of 86400.<br> If '''live stream''' of a discrete program (Live Event/Sporting Event), <br>pass length of content. If unknown, pass a value of 0.|| Yes<br />
|-style="background-color:#e9f9fa;"<br />
| airdate || original broadcast or release date for the program || iso date format <br> (YYYY-MM-DDThh:mm:ssTZD)<br> <code>2021-03-21T12:00:00Z</code> <br> <code>2021-03-21T12:00:00-5000</code> || Yes<br />
|-<br />
| isfullepisode || full episode flag || <code>"y"</code>- full episode, <code>"n"</code>- non full episode || Yes<br />
|-style="background-color:#e9f9fa;"<br />
| adloadtype || type of ad load:<br />
<code>"1"</code> Linear – matches TV ad load<br />
<br />
<code>"2"</code> Dynamic – Dynamic Ad Insertion (DAI)<br />
|| <code>"2"</code> - DCR measures content with dynamic ads || Yes<br />
|-<br />
| segB || custom segment B || custom || <br />
|-style="background-color:#e9f9fa;"<br />
| segC || custom segment C || custom || <br />
|-<br />
| crossId1 || standard episode ID || custom || <br />
|-style="background-color:#e9f9fa;"<br />
| crossId2 || content originator (only required for distributors) || custom || <br />
|}<br />
Custom segments (segB and segC) can be used to aggregate video and/or static content within a single Brand to receive more granular reports within a brand.<br />
<br />
Examples regarding usage of segments within SDK:<br />
* All comedy clips and stories for a Brand rolled into a "Comedy" segment<br />
* genre grouping content by Comedy vs. Drama<br />
* group related Text + Video content - i.e. for a show that has a lot of - static pages associated with it<br />
* packaging based on how clients sell inventory<br />
* grouping related types of content either by genre, category or platform.<br />
<br /></div>DiegoOrdonezhttps://engineeringportal.nielsen.com//w/index.php?title=Template:DCR_Content_Metadata&diff=5075Template:DCR Content Metadata2021-06-17T20:11:03Z<p>DiegoOrdonez: </p>
<hr />
<div><br />
{| class="wikitable" <br />
|-<br />
! '''Keys''' !! '''Description''' !! '''Values''' !! '''Required'''<br />
|-<br />
| type || type of asset || <code>"content"</code> || Yes<br />
|-style="background-color:#e9f9fa;"<br />
| assetid || unique ID assigned to asset || custom || Yes<br />
|-<br />
| program ||name of program (25 character limit.) <br> (no abbreviations or shorthand) || The Big Bang Theory || Yes<br />
|-style="background-color:#e9f9fa;"<br />
| title ||episode title with season and episode number (40 character limit) || The Pants Alternative S03E18|| Yes<br />
|-<br />
| length || length of content in seconds || If '''continuous''' (24 x 7) stream, pass the value of 86400.<br> If '''live stream''' of a discrete program (Live Event/Sporting Event), <br>pass length of content. If unknown, pass a value of 0.|| Yes<br />
|-style="background-color:#e9f9fa;"<br />
| airdate || the airdate in the linear TV || iso date format <br> (YYYY-MM-DDThh:mm:ssTZD)<br> <code>2021-03-21T12:00:00Z</code> <br> <code>2021-03-21T12:00:00-5000</code> || Yes<br />
|-<br />
| isfullepisode || full episode flag || <code>"y"</code>- full episode, <code>"n"</code>- non full episode || Yes<br />
|-style="background-color:#e9f9fa;"<br />
| adloadtype || type of ad load:<br />
<code>"1"</code> Linear – matches TV ad load<br />
<br />
<code>"2"</code> Dynamic – Dynamic Ad Insertion (DAI)<br />
|| <code>"2"</code> - DCR measures content with dynamic ads || Yes<br />
|-<br />
| segB || custom segment B || custom || <br />
|-style="background-color:#e9f9fa;"<br />
| segC || custom segment C || custom || <br />
|-<br />
| crossId1 || standard episode ID || custom || <br />
|-style="background-color:#e9f9fa;"<br />
| crossId2 || content originator (only required for distributors) || custom || <br />
|}<br />
Custom segments (segB and segC) can be used to aggregate video and/or static content within a single Brand to receive more granular reports within a brand.<br />
<br />
Examples regarding usage of segments within SDK:<br />
* All comedy clips and stories for a Brand rolled into a "Comedy" segment<br />
* genre grouping content by Comedy vs. Drama<br />
* group related Text + Video content - i.e. for a show that has a lot of - static pages associated with it<br />
* packaging based on how clients sell inventory<br />
* grouping related types of content either by genre, category or platform.<br />
<br /></div>DiegoOrdonezhttps://engineeringportal.nielsen.com//w/index.php?title=Template:DCR_Content_Metadata&diff=5074Template:DCR Content Metadata2021-06-17T20:07:59Z<p>DiegoOrdonez: </p>
<hr />
<div><br />
{| class="wikitable" <br />
|-<br />
! '''Keys''' !! '''Description''' !! '''Values''' !! '''Required'''<br />
|-<br />
| type || type of asset || <code>"content"</code> || Yes<br />
|-style="background-color:#e9f9fa;"<br />
| assetid || unique ID assigned to asset || custom || Yes<br />
|-<br />
| program ||name of program (25 character limit.) <br> (no abbreviations or shorthand) || The Big Bang Theory || Yes<br />
|-style="background-color:#e9f9fa;"<br />
| title ||‘episode title with season and episode number (40 character limit) || The Pants Alternative S03E18|| Yes<br />
|-<br />
| length || length of content in seconds || If '''continuous''' (24 x 7) stream, pass the value of 86400.<br> If '''live stream''' of a discrete program (Live Event/Sporting Event), <br>pass length of content. If unknown, pass a value of 0.|| Yes<br />
|-style="background-color:#e9f9fa;"<br />
| airdate || the airdate in the linear TV || iso date format <br> (YYYY-MM-DDThh:mm:ssTZD)<br> <code>2021-03-21T12:00:00Z</code> <br> <code>2021-03-21T12:00:00-5000</code> || Yes<br />
|-<br />
| isfullepisode || full episode flag || <code>"y"</code>- full episode, <code>"n"</code>- non full episode || Yes<br />
|-style="background-color:#e9f9fa;"<br />
| adloadtype || type of ad load:<br />
<code>"1"</code> Linear – matches TV ad load<br />
<br />
<code>"2"</code> Dynamic – Dynamic Ad Insertion (DAI)<br />
|| <code>"2"</code> - DCR measures content with dynamic ads || Yes<br />
|-<br />
| segB || custom segment B || custom || <br />
|-style="background-color:#e9f9fa;"<br />
| segC || custom segment C || custom || <br />
|-<br />
| crossId1 || standard episode ID || custom || <br />
|-style="background-color:#e9f9fa;"<br />
| crossId2 || content originator (only required for distributors) || custom || <br />
|}<br />
Custom segments (segB and segC) can be used to aggregate video and/or static content within a single Brand to receive more granular reports within a brand.<br />
<br />
Examples regarding usage of segments within SDK:<br />
* All comedy clips and stories for a Brand rolled into a "Comedy" segment<br />
* genre grouping content by Comedy vs. Drama<br />
* group related Text + Video content - i.e. for a show that has a lot of - static pages associated with it<br />
* packaging based on how clients sell inventory<br />
* grouping related types of content either by genre, category or platform.<br />
<br /></div>DiegoOrdonezhttps://engineeringportal.nielsen.com//w/index.php?title=Template:DCR_Content_Metadata&diff=5073Template:DCR Content Metadata2021-06-17T20:07:42Z<p>DiegoOrdonez: </p>
<hr />
<div><br />
{| class="wikitable" <br />
|-<br />
! '''Keys''' !! '''Description''' !! '''Values''' !! '''Required'''<br />
|-<br />
| type || type of asset || <code>"content"</code> || Yes<br />
|-style="background-color:#e9f9fa;"<br />
| assetid || unique ID assigned to asset || custom || Yes<br />
|-<br />
| program ||name of program (25 character limit.) <br> (no abbreviations or shorthand) || The Big Bang Theory || Yes<br />
|-style="background-color:#e9f9fa;"<br />
| title ||‘Episode title with season and episode number (40 character limit) || The Pants Alternative S03E18|| Yes<br />
|-<br />
| length || length of content in seconds || If '''continuous''' (24 x 7) stream, pass the value of 86400.<br> If '''live stream''' of a discrete program (Live Event/Sporting Event), <br>pass length of content. If unknown, pass a value of 0.|| Yes<br />
|-style="background-color:#e9f9fa;"<br />
| airdate || the airdate in the linear TV || iso date format <br> (YYYY-MM-DDThh:mm:ssTZD)<br> <code>2021-03-21T12:00:00Z</code> <br> <code>2021-03-21T12:00:00-5000</code> || Yes<br />
|-<br />
| isfullepisode || full episode flag || <code>"y"</code>- full episode, <code>"n"</code>- non full episode || Yes<br />
|-style="background-color:#e9f9fa;"<br />
| adloadtype || type of ad load:<br />
<code>"1"</code> Linear – matches TV ad load<br />
<br />
<code>"2"</code> Dynamic – Dynamic Ad Insertion (DAI)<br />
|| <code>"2"</code> - DCR measures content with dynamic ads || Yes<br />
|-<br />
| segB || custom segment B || custom || <br />
|-style="background-color:#e9f9fa;"<br />
| segC || custom segment C || custom || <br />
|-<br />
| crossId1 || standard episode ID || custom || <br />
|-style="background-color:#e9f9fa;"<br />
| crossId2 || content originator (only required for distributors) || custom || <br />
|}<br />
Custom segments (segB and segC) can be used to aggregate video and/or static content within a single Brand to receive more granular reports within a brand.<br />
<br />
Examples regarding usage of segments within SDK:<br />
* All comedy clips and stories for a Brand rolled into a "Comedy" segment<br />
* genre grouping content by Comedy vs. Drama<br />
* group related Text + Video content - i.e. for a show that has a lot of - static pages associated with it<br />
* packaging based on how clients sell inventory<br />
* grouping related types of content either by genre, category or platform.<br />
<br /></div>DiegoOrdonezhttps://engineeringportal.nielsen.com//w/index.php?title=DCR_Video_Browser_SDK&diff=5072DCR Video Browser SDK2021-06-17T20:01:27Z<p>DiegoOrdonez: </p>
<hr />
<div>{{Breadcrumb|}} {{Breadcrumb|Digital}} {{Breadcrumb|US DCR & DTVR}} {{CurrentBreadcrumb}}<br />
[[Category:Digital]]<br />
<br />
== Prerequisites ==<br />
Before you start the integration, you will need:<br />
{| class="wikitable"<br />
|-<br />
! style="width: 30px;" |<br />
! style="width: 15%;" | Item<br />
! Description<br />
! Source<br />
|-style="background-color:#d0f6f8;"<br />
|| ☑ || '''App ID (appid)''' || Unique ID assigned to the player/site and configured by product. || Contact Nielsen<br />
|}<br />
<br />
== Configure SDK ==<br />
There are two steps required for configuring the SDK:<br />
*Add Static Queue Snippet<br />
*Create SDK Instance<br />
<br />
=== Static Queue Snippet ===<br />
Add the following script tag to the website:<br />
<syntaxhighlight lang="javascript"><br />
<script><br />
!function(t,n)<br />
{<br />
t[n]=t[n]||<br />
{<br />
nlsQ:function(e,o,c,r,s,i)<br />
{<br />
return s=t.document,<br />
r=s.createElement("script"),<br />
r.async=1,<br />
r.src=("http:"===t.location.protocol?"http:":"https:")+"//cdn-gl.imrworldwide.com/conf/"+e+".js#name="+o+"&ns="+n,<br />
i=s.getElementsByTagName("script")[0],<br />
i.parentNode.insertBefore(r,i),<br />
t[n][o]=t[n][o]||{g:c||{},<br />
ggPM:function(e,c,r,s,i){(t[n][o].q=t[n][o].q||[]).push([e,c,r,s,i])}},t[n][o]<br />
}<br />
}<br />
}<br />
<br />
(window,"NOLBUNDLE");<br />
<br />
var nSdkInstance = NOLBUNDLE.nlsQ("XXXXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX", "nlsnInstance", {<br />
nol_sdkDebug: "debug",<br />
optout: "false"<br />
});<br />
<br />
</script><br />
</syntaxhighlight><br />
<br />
The static queue snippet allows the SDK APIs to be called while the actual SDK and configuration file are still being downloaded. As the queue can capture all API calls before the download completes, there is no wait time. Once the SDK is available, the API calls will transition from directing to the queue to the SDK seamlessly.<br />
<br />
<blockquote>For DCR static, BSDK cannot be run in an iframe because the blur/focus events of the parent page may not propagate to the iframe. The iframe events typically trigger when the iframe itself is clicked. The BSDK is dependent on the blur/focus events of the browser to detect active viewing of the page. Because the root page events do not propagate to the iframe, this would impact incorrect DCR static crediting.</blockquote><br />
<br />
===Create SDK Instance===<br />
To initialize the SDK, create an SDK instance by making the initialization call:<br />
<br />
==== Initialization API Call ====<br />
<syntaxhighlight lang="javascript"><br />
NOLBUNDLE.nlsQ("<apid>", "<instanceName>",{nol_sdkDebug: "debug"});<br />
</syntaxhighlight><br />
<br><br />
When creating an instance, pass the following values: (<code>nol_sdkDebug</code> and <code>optout</code> are optional)<br />
{| class="wikitable"<br />
|-<br />
! Parameter !! Description !! Values<br />
|-<br />
| apid || Unique ID assigned to player/site || 'PXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'<br />
|-<br />
|instanceName || Name of SDK instance || "any string value"<br />
|-<br />
| nol_sdkDebug || Enables Nielsen console logging if desired. || <syntaxhighlight lang="javascript"><br />
{nol_sdkDebug: "debug"} <br />
{nol_sdkDebug: "info"}<br />
{nol_sdkDebug: "warn"}<br />
{nol_sdkDebug: "true"}<br />
</syntaxhighlight><br />
|-<br />
|optout || Optional: OptOut global parameter. || <code>1/0</code> or <code>true/false</code><br />
|}<br />
<br />
==== Example SDK Initialization ====<br />
When the initialization call is made, a unique static configuration file, <apid>.js, will be downloaded based on the apid and will be cached on the user’s browser.<br />
<br />
Once the configuration is downloaded, the SDK itself will be downloaded and initialized. All SDK modules are included in one file: “nlsSDK600.bundle.min.js”.<br />
<br />
More information on OptOut Parameter under [[DCR Video Browser SDK#Privacy_and_Opt-Out|Privacy and Opt-Out.]]<br />
<!--<br />
==== Example SDK Initialization with Viewability ====<br />
If you decide to implement the Nielsen Viewability Module, the SDK will track the viewability of the targeted player/element, log the viewable percentage and record in seconds the time the player/element was in that viewable state. The only additional information required is the HTML element ID of the element being tracked (which is passed during SDK Initialization), and the volume level (passed as an event).<br />
<br />
<syntaxhighlight lang="javascript"><br />
var nSdkInstance = NOLBUNDLE.nlsQ("XXXXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX", "nlsnInstance", {<br />
nol_sdkDebug: "debug",<br />
optout: "false"<br />
});<br />
</syntaxhighlight><br />
<br />
<br />
If no "volume" event is passed, a default value of '1' will be used. (IE: 100%). If the volume level is adjusted, the modified value can be passed to the SDK using the standard ggPM() API along with the new "volume" event.<br />
<br /><br />
<syntaxhighlight lang="javascript"><br />
nSdkInstance.ggPM("volume", <volume level>);<br />
</syntaxhighlight><br />
<br /><br />
{| class="wikitable"<br />
|-<br />
! Event Name !! Event # !! Purpose !! Possible Values !!Example<br />
|-<br />
| volume || 61 ||ggPM event that will allow clients to pass in the volume levels. ||Supports decimal values in range from 0 - 1||instance.ggPM("61", 0); => muted<br />
instance.ggPM("61", .5) => not muted<br />
intance.ggPM("61", 1) => not muted<br />
|}<br />
Sample:<br />
<syntaxhighlight lang="javascript"><br />
nSdkInstance.ggPM("volume", 0); // muted<br />
</syntaxhighlight><br />
--><br />
<br />
=== Example SDK Configuration ===<br />
<br />
The configuration should include the Static Queue Snippet and an SDK Instance for an unique App ID as shown in the example:<br />
<syntaxhighlight lang="html"><script type="text/javascript"><br />
// Add Static Queue Snippet<br />
!function(t,n)<br />
{<br />
t[n]=t[n]||<br />
{<br />
nlsQ:function(e,o,c,r,s,i)<br />
{<br />
return s=t.document,<br />
r=s.createElement("script"),<br />
r.async=1,<br />
r.src=("http:"===t.location.protocol?"http:":"https:")+"//cdn-gl.imrworldwide.com/conf/"+e+".js#name="+o+"&ns="+n,<br />
i=s.getElementsByTagName("script")[0],<br />
i.parentNode.insertBefore(r,i),<br />
t[n][o]=t[n][o]||{g:c||{},<br />
ggPM:function(e,c,r,s,i){(t[n][o].q=t[n][o].q||[]).push([e,c,r,s,i])}},t[n][o]<br />
}<br />
}<br />
}<br />
(window,"NOLBUNDLE");<br />
<br />
// Created SDK Instance<br />
var nSdkInstance = NOLBUNDLE.nlsQ("XXXXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX", "nlsnInstance", {nol_sdkDebug: "debug"});<br />
</script></syntaxhighlight><br />
<br />
=== Create Metadata Objects ===<br />
There are two types of asset metadata:<br />
*content: identify video<br />
*ad: identify each ad<br />
<br />
The metadata received for each asset is used for classification and reporting.<br />
<br />
Metadata can be passed through key-values using the Nielsen reserved keys. User will need to set up content and ad objects with the required Nielsen keys as shown in the sample code below.<br />
<br />
==== Content Metadata ====<br />
Content metadata should remain constant throughout the entirety of an episode/clip including when ads play.<br />
{{DCR Content Metadata}}<br />
<br />
==== Example Content Object ====<br />
<syntaxhighlight lang="javascript"><br />
var contentMetadataObject =<br />
{ <br />
type: 'content',<br />
assetid: 'VID-123456',<br />
program: 'program name',<br />
title: 'episode title with season and episode number',<br />
length: 'length in seconds',<br />
airdate: '20210321 09:00:00',<br />
isfullepisode: 'y',<br />
adloadtype: '2',<br />
segB: 'custom segment B', // optional<br />
segC: 'custom segment C', // optional<br />
crossId1: 'Standard Episode ID', // optional<br />
crossId2: 'Content Originator' //optional<br />
};<br />
</syntaxhighlight><br />
<br />
=== Ad Metadata ===<br />
The Ad Metadata (if applicable) should be passed for each individual ad.<br />
{| class="wikitable"<br />
|-<br />
! Keys !! Description !! Values !! Required<br />
|-<br />
| type || type of Ad || <code>'preroll'</code>, <code>'midroll'</code>, <code>'postroll'</code> <br> <code>'ad'</code> - If specific type can not be identified.|| ✓<br />
|-<br />
| assetid || unique ID assigned to Ad || custom <br>(no [[Special Characters]]) || ✓<br />
|}<br />
<br />
==== Example Ad Object ====<br />
<syntaxhighlight lang="javascript"><br />
var adMetadataObject = <br />
{ <br />
type: 'preroll',<br />
assetid: 'AD-1'<br />
};<br />
</syntaxhighlight><br />
<br/><br />
<blockquote> URL Character Limit: There is a URL character limit of 2K characters due to browser limitations. Exceeding this value could impair data delivery on particular browsers. </blockquote><br />
<br />
==== Call Nielsen APIs ====<br />
The method for calling events is ggPM().<br />
<br />
<syntaxhighlight lang="javascript"><br />
nSdkInstance.ggPM('event', parameter, ...);<br />
</syntaxhighlight><br />
<br />
== Interrupt Scenarios ==<br />
<br />
=== Pause Event ===<br />
The setPlayheadPostion event is used for handling pause. To indicate pause, stop passing the playhead position to the SDK. Once the content resumes, begin sending the playhead again with the correct playhead value.<br />
<br />
=== Other Interrupt Scenarios ===<br />
The following possible browser interruption scenarios must be handled:<br />
<br />
* Browser/Tab close<br />
* Leaving the page to another destination<br />
* Pressing the stop button<br />
* Network Loss<br />
<br />
There are many cases where the player itself has the ability to detect such situations. If not, these interruption scenarios can be handled through JavaScript. The events that are called will depend on the asset being played (e.g. midroll vs. content).<br />
<br />
<syntaxhighlight lang="javascript"><br />
window.addEventListener('beforeunload', function(event) <br />
{<br />
// Only inside a midroll indicate <stop> for the ad<br />
nSdkInstance.ggPM('stop', playheadPosition);<br />
<br />
// Indicate <end> and <stop> for the content<br />
nSdkInstance.ggPM('end', playheadPosition);<br />
nSdkInstance.ggPM('stop', playheadPosition);<br />
});<br />
</syntaxhighlight><br />
<br />
<blockquote>'''Note:''' User may need to add code to support specific browser versions (e.g. older versions of Internet Explorer).</blockquote><br />
<br />
=== SDK Events ===<br />
{| class="wikitable"<br />
|-<br />
! Event !! Parameter !! Description<br />
|-<br />
| 'loadMetadata' || content/ad metadata object || Needs to be called at the beginning of each asset<br />
|-<br />
| 'setPlayheadPosition' || playhead position as integer<br/><br />
VOD: || current position in seconds <br/><br />
Live: current Unix timestamp (seconds since Jan-1-1970 UTC) <br/><br />
Note: 'setPlayheadPosition' has to be called every second<br />
||<br />
Pass playhead position every second during playback<br />
|-<br />
| 'stop' || playhead position in seconds || Call when content or ads complete playing and pass playhead position<br />
|-<br />
| 'end' || playhead position in seconds || Call when the current video asset completes playback and pass the playhead position. <br/><br />
Example: At the end of the content stream, if the user switches to another piece of content, when the browser is refreshed or closed.<br />
|}<br />
<br />
== SDK Playhead Event Sequence ==<br />
The sample event lifecycles can be used as a reference for identifying the order for calling events.<br />
<br />
=== Content Playback ===<br />
<syntaxhighlight lang="javascript"><br />
// START OF STREAM<br />
nSdkInstance.ggPM('loadMetadata', contentMetadataObject); <br />
<br />
// CONTENT PLAYS<br />
nSdkInstance.ggPM('setPlayheadPosition', playheadPosition);<br />
// <br />
// pass playhead every second<br />
// <br />
nSdkInstance.ggPM('setPlayheadPosition', playheadPosition);<br />
<br />
// END OF STREAM<br />
nSdkInstance.ggPM('end', playheadPosition);<br />
</syntaxhighlight><br />
<br />
<br />
=== Content Playback with Ads ===<br />
<syntaxhighlight lang="javascript"><br />
// START OF STREAM<br />
nSdkInstance.ggPM('loadMetadata', contentMetadataObject); <br />
<br />
// PREROLL<br />
nSdkInstance.ggPM('loadMetadata', prerollMetadataObject);<br />
nSdkInstance.ggPM('setPlayheadPosition', playheadPosition);<br />
// <br />
// pass playhead every second<br />
// <br />
nSdkInstance.ggPM('setPlayheadPosition', playheadPosition);<br />
nSdkInstance.ggPM('stop', playheadPosition);<br />
<br />
// CONTENT<br />
nSdkInstance.ggPM('loadMetadata', contentMetadataObject);<br />
nSdkInstance.ggPM('setPlayheadPosition', playheadPosition);<br />
// <br />
// pass playhead every second<br />
// <br />
nSdkInstance.ggPM('setPlayheadPosition', playheadPosition);<br />
nSdkInstance.ggPM('stop', playheadPosition);<br />
<br />
// MIDROLL<br />
nSdkInstance.ggPM('loadMetadata', midrollMetadataObject);<br />
nSdkInstance.ggPM('setPlayheadPosition', playheadPosition);<br />
// <br />
// pass playhead every second<br />
// <br />
nSdkInstance.ggPM('setPlayheadPosition', playheadPosition);<br />
nSdkInstance.ggPM('stop', playheadPosition);<br />
<br />
// CONTENT RESUMES<br />
nSdkInstance.ggPM('loadMetadata', contentMetadataObject);<br />
nSdkInstance.ggPM('setPlayheadPosition', playheadPosition);<br />
// <br />
// pass playhead every second<br />
// <br />
nSdkInstance.ggPM('setPlayheadPosition', playheadPosition);<br />
<br />
// END OF STREAM<br />
nSdkInstance.ggPM('setPlayheadPosition', playheadPosition);<br />
nSdkInstance.ggPM('end', playheadPosition);<br />
<br />
// POSTROLL<br />
nSdkInstance.ggPM('loadmetadata', postrollMetadataObject);<br />
nSdkInstance.ggPM('setPlayheadPosition', playheadPosition);<br />
// <br />
// pass playhead every second<br />
// <br />
nSdkInstance.ggPM('setPlayheadPosition', playheadPosition);<br />
nSdkInstance.ggPM('stop', playheadPosition);<br />
</syntaxhighlight><br />
<blockquote><br />
* 'setPlayheadPosition' is used for calculating duration and must be passed every second. The final playhead position must be sent for the current asset being played before calling 'stop', 'end', or 'loadmetadata'.<br />
<br />
* For Ad Pods, events must be called for each individual Ad. Each Ad playhead position should begin at ‘0’ when ad starts.<br />
<br />
* When content has resumed following an ad break, the playhead position update must continue where previous content segment left off. The playhead position should be passed as a rounded number with no decimals.<br />
</blockquote><br />
<br />
{{Template:Browser_Privacy_and_Opt-Out}}<br />
<br />
== Going Live ==<br />
After the integration has been certified, users will need to make a couple of updates to the initialization call to ensure that player will be measured properly.<br />
Disable debug logging by deleting {nol_sdkDebug: 'DEBUG'} from initialization call.<br />
<br />
'''Example Production Initialization Call'''<br />
<br />
<syntaxhighlight lang="javascript"><br />
var nSdkInstance = NOLBUNDLE.nlsQ("XXXXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX", "nlsnInstance", { optout: "false"});<br />
</syntaxhighlight></div>DiegoOrdonezhttps://engineeringportal.nielsen.com//w/index.php?title=DTVR_iOS_SDK&diff=4895DTVR iOS SDK2021-05-20T17:02:58Z<p>DiegoOrdonez: removed mentioning of Simplified API</p>
<hr />
<div>{{Breadcrumb|}} {{Breadcrumb|Digital}} {{Breadcrumb|US DCR & DTVR}} {{CurrentBreadcrumb}}<br />
[[Category:Digital]]<br />
<br />
== Overview ==<br />
The Nielsen SDK is one of multiple framework SDKs that Nielsen provides to enable measuring linear (live) and on-demand TV viewing using TVs, mobile devices, etc.<br />
The App SDK is the framework for mobile application developers to integrate Nielsen Measurement into their media player applications. It supports a variety of Nielsen Measurement Products like Digital in TV Ratings, Digital Content Ratings ([[DCR & DTVR]]), and [[Digital Ad Ratings]] (DAR). Nielsen SDKs are also equipped to measure static content and can track key life cycle events of an application like:<br />
*Application launch events and how long app was running<br />
*Time of viewing a sub section / page in the application.<br />
<br />
If the content being played contains ID3 tags, when played on a mobile device or within a browser, these tags can be sent to Nielsen for collection/processing via the Nielsen SDK. <br />
<blockquote><br />
VOD in TV Ratings (formally knows as Recently Telecast VOD) support is now available; however, you must notify us to ensure accurate reporting.<br />
</blockquote><br />
<br />
== Prerequisites ==<br />
Before you start the integration, you will need:<br />
{| class="wikitable"<br />
|-<br />
! style="width: 15%;" | Item<br />
! Description<br />
! Source<br />
|-<br />
|| '''App ID (appid)''' || Unique ID assigned to the player/site and configured by product. || Provided by Nielsen<br />
|-<br />
|| '''Nielsen SDK''' || Includes SDK frameworks and '''sample implementation'''; ''See [[iOS SDK Release Notes]]'' || [[Special:Downloads|Download]]<br />
|}<br />
<br />
== Implementation ==<br />
This guide covers implementation steps for iOS using Xcode utilizing the Standard Nielsen SDK for DTVR.<br />
<br />
== Setting up your Development Environment ==<br />
Prior to SDK Version 6.2.0.0 the IOS framework has been distributed as a static library packaged into framework bundle format. Apple recommends to use dynamic framework, it has some benefits over static libraries like less executable file size of an app, faster startup time and native support in xCode IDE. Nielsen AppSDK has been transformed into dynamic framework in this release ([[iOS_Static_Framework_Setup|static framework]] is still available).<br />
<br />
If migrating from the static library to this new dynamic framework, once implemented, unless your specific application requires, you can remove the following Frameworks that were once required:<code> [AdSupport, JavascriptCore, SystemConfiguration, Security, AVFoundation, libc++] </code><br />
<br><br />
<br />
The Dynamic framework is created as a fat framework. It means that it contains slices required for devices (armv7, arm64) as well as slices required for simulators (i386, x86_64). Simulator slices are needed to let clients build and debug their app on the simulators, but they should be removed before sending the app to the AppStore. The example of the shell script that should be added as a Run Script phase in the application can be [[DCR_Video_iOS_SDK#Removing_Simulators|found below]].<br />
<br />
=== How to obtain the NielsenAppApi.Framework ===<br />
The Nielsen AppSDK can either be downloaded directly or can be integrated directly within an application through the use of CocoaPods. We recommend using the CocoaPods-based integration whenever possible to ensure you maintain the most recent changes and enhancements to the Nielsen libraries.<br />
* [[Digital_Measurement_iOS_Artifactory_Guide|Select to obtain CocoaPods implementation guide]]<br />
* [[Special:Downloads|Select to Download Directly]]<br />
<br />
=== Configuring Xcode Development Environment ===<br />
Starting with SDK version 6.0.0.0, the Nielsen App SDK is compatible with Apple iOS versions 8.0 and above. In addition, when using the dynamic framework, all the required frameworks are linked automatically as the are needed. More details can be found here: https://stackoverflow.com/questions/24902787/dont-we-need-to-link-framework-to-xcode-project-anymore<br />
<br />
<blockquote>'''Note''': All communications between the SDK and the Census (Collection Facility) use HTTPS.</blockquote><br />
<br />
=== Download Framework ===<br />
The first step is to download and copy the [[Special:Downloads|NielsenAppApi.framework]] bundle to the app project directory. (''Not required if using CocaPods'')<br />
<br />
=== Add Framework ===<br />
In the General tab for app configuration add NielsenAppApi.framework in the list of Embedded Binaries. (''Not required if using CocaPods'')<br />
<br />
=== Add Path ===<br />
Add path to the NielsenAppApi.framework in the Framework Search Paths build setting. (''Not required if using CocaPods'')<br />
<br />
=== Import Framework ===<br />
Add NielsenAppApi.framework module in the source file of your app:<br />
<br />
==== Using Swift ====<br />
Add the following:<br />
<syntaxhighlight lang="swift"><br />
import NielsenAppApi<br />
</syntaxhighlight><br />
<br />
==== Using Objective-C ====<br />
<syntaxhighlight lang ="objective-c"><br />
@import NielsenAppApi;<br />
</syntaxhighlight><br />
<br />
== SDK Initialization ==<br />
The latest version of the Nielsen App SDK allows instantiating multiple instances of the SDK object, which can be used simultaneously without any issue. The sharedInstance API that creates a singleton object was deprecated prior to version 5.1.1. [[Dual_Instances_of_SDK|(Click here for an example of multiple instances)]]<br />
<br />
The following table contains the list of arguments that can be passed via the AppInfo JSON schema.<br />
{| class="wikitable"<br />
|-<br />
! Parameter / Argument !! Description !! Source !! Required? !! Example<br />
|-<br />
| appid || Unique Nielsen ID for the application. The ID is a GUID data type. If you did not receive your App ID, let us know and we will provide you. || Nielsen-specified || Yes || PXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX<br />
|-<br />
| nol_devDebug || Enables Nielsen console logging. Only required for testing<br />
|| Nielsen-specified || Optional || "DEBUG"<br />
|}<br />
<br />
== APP SDK Error & Event Codes ==<br />
To view the Error and Event codes for iOS, please review the [[APP SDK Event Codes|App SDK Event Code]] Reference page.<br />
<br />
== Content Metadata and SDK Events ==<br />
=== Content Metadata ===<br />
Content metadata should remain constant throughout the completion of an episode or live stream.<br />
{| class="wikitable"<br />
|-<br />
! Key !! Description !! Values !! Required<br />
|-<br />
| type || type of asset || "content" || <br />
|-<br />
| adModel || linear vs dynamic ad model || 1 = Linear matches TV ad load || ✓<br />
|-<br />
|}<br />
<!--<br />
- (NSDictionary *)loadChannelInfo<br />
{<br />
//Loading Channel Info. <br />
NSString *strUrl = self.url.absoluteString; <br />
NSDictionary *channel = @{ @"channelName" : @"TheGreatBigMovie": strUrl }; <br />
return channel;<br />
}<br />
--><br />
<syntaxhighlight lang="objectivec"> <br />
- (NSDictionary *)loadDtvr{<br />
<br />
//Loading DTVR data <br />
NSDictionary *dtvr = @{ @"adModel":@"1" , <br />
@"type":@"content",}; <br />
return dtvr;<br />
}<br />
</syntaxhighlight><br />
<br />
=== SDK Events ===<br />
<!--[[File:appsdkTimeline-DTVR-V2.png|icon]]--><br />
{| class="wikitable"<br />
|-<br />
! Event !! Parameter !! Description<br />
|-<br />
| 'loadMetadata' || content/ad metadata object || Needs to be called at the beginning of each asset to pass type and adModel.<br />
|-<br />
|'play'||||Call when starting or resuming a streaming session. <br />
'''Note:'''no longer required for SDK version 7.2 or higher.<br />
|-<br />
| 'stop' || playhead position || Call when content or ads complete playing and pass playhead position<br />
'''Note:'''no longer required for SDK version 7.2 or higher.<br />
|-<br />
| 'sendID3' || Used to send the ID3 tag payload retrieved from the stream || Needs to be called at the beginning of playback<br />
|}<br />
<br />
=== Configure API calls - play ===<br />
Call at start of new stream:<br />
<br />
{{ExampleCode|<br />
|Objective C = <syntaxhighlight lang="objective-c"> [nielsenAppApi play:()];</syntaxhighlight><br />
|Swift = <syntaxhighlight lang="swift">nielsenAppApi?.play();</syntaxhighlight><br />
}}<br />
<br />
'''Note:''' no longer required for SDK version 7.2 or higher.<br />
<br />
=== Configure API calls - loadMetadata ===<br />
Use <code>loadMetadata</code> to pass ‘content’ and ‘ad’ <code>Digital Measurement Metadata</code>. The CMS data must be passed as a JSON object.<br />
{{ExampleCode|<br />
|Objective C = <syntaxhighlight lang="objective-c">[nielsenApi loadMetadata:(loadDtvr)];</syntaxhighlight><br />
|Swift = <syntaxhighlight lang="swift">self.nielsenAppApi?.loadMetadata(loadDtvr)</syntaxhighlight><br />
}}<br />
<br />
=== Configure API calls - sendID3 ===<br />
[[sendID3]] API is a receiver for timed metadata events (ID3 tags) provided through iOS’s NSNotificationCenter notification system. This API filters out Nielsen-specific ID3 tags from the system and buffers the data for transfer to Nielsen’s collection facility.<br />
{{ExampleCode|<br />
|Objective C = <syntaxhighlight lang="objective-c">[nielsenApi sendID3:extraString];</syntaxhighlight><br />
|Swift = <syntaxhighlight lang="swift"> [nielsenApi sendID3:extraString];</syntaxhighlight>}}<br />
'''Sample ID3 tags''' <br />
* <code>www.nielsen.com/X100zdCIGeIlgZnkYj6UvQ==/X100zdCIGeIlgZnkYj6UvQ==/AAAB2Jz2_k74GXSzx4npHuI_<wbr />JwJd3QSUpW30rDkGTcbHEzIMWleCzM-uvNOP9fzJcQMWQLJqzXMCAxParOb5sGijSV9dNM3QiBniJYGZ5GI-lL1fXTTN0IgZ4iWBmeRiPpS9AAAAAAAAAAAAAAAAAAAAAFJWFM5SVhTONNU=/00000/00000/00</code><br />
* <code>www.nielsen.com/X100zdCIGeIlgZnkYj6UvQ==/R8WHe7pEBeqBhu8jTeXydg==/AAICoyitYqlxT7n6aZ0oMCGhe<wbr />Fi4CXFp46AMUPZz1lMr_M9tr3_cjee1SHqxrOiVerMDLeyn9xzocZSKwi746Re8vNOtpNCAZjYABs_J0R25IHpvOc1HS8<wbr />QHGgD5TgOJeS6gX100zdCIGeIlgZnkYj6UvVJWFNhSVhTiPE0=/00000/46016/00</code><br />
Refer to [[iOS SDK API Reference#Retrieving ID3 Tags|Retrieving ID3 Tags]] section to know more details.<br />
<br />
=== Configure API calls - stop ===<br />
Call <code>stop</code> in case of interruptions during playback like flight mode, Wi-Fi toggle, etc. <br />
{{ExampleCode|<br />
|Objective C = <syntaxhighlight lang="objective-c">[nielsenApi stop];</syntaxhighlight><br />
|Swift = <syntaxhighlight lang="swift">nielsenApi.stop()</syntaxhighlight><br />
}}<br />
<br />
'''Note:''' no longer required for SDK version 7.2 or higher.<br />
<br />
== Retrieving ID3 Tags ==<br />
ID3 tags have a payload of about 249 characters and start with "www.nielsen.com".<br />
<br />
ID3 tags are extracted by observing a property called timedMetadata on the iOS player item. Now this is done via a concept called KVO (Key Value Observing), where you register interest in a property, and the runtime will let you know when it has changed.<br />
<br />
Both the iOS native players have the ability to extract ID3 tags, If any other player apart from iOS native players (AVPlayer, MPMoviePlayer) is used, check and ensure that the player has the capability to extract ID3 tags.<br />
<br />
=== Examples of extracting ID3 tags from the iOS Native Player ===<br />
{{ExampleCode|<br />
|Objective C = <syntaxhighlight lang="objective-c"><br />
//Adding observer to player to track play,pause and reverse<br />
[player addObserver:self<br />
forKeyPath:@"rate"<br />
options:(NSKeyValueObservingOptionNew)<br />
context:nil];<br />
</syntaxhighlight><br />
------------------------------<br />
<syntaxhighlight lang="objective-c"> <br />
//Setting observer to track timedMetadata<br />
[player addObserver:self<br />
forKeyPath: timedMetadataKey<br />
options: (NSKeyValueObservingOptionNew)<br />
context: &TimedMetadataObserverContext];<br />
</syntaxhighlight><br />
------------------------------<br />
<syntaxhighlight lang="objective-c"> <br />
- (void)observeValueForKeyPath:(NSString *)keyPath<br />
ofObject:(id)object<br />
change:(NSDictionary *)change<br />
context:(void *)context<br />
{<br />
if(keyPath == timedMetadataKey){<br />
if(context == &TimedMetadataObserverContext){<br />
<br />
id newMetadataArray = [change objectForKey:NSKeyValueChangeNewKey];<br />
if (newMetadataArray != [NSNull null])<br />
{<br />
array = newMetadataArray;<br />
for (AVMetadataItem *metadataItem in array)<br />
{<br />
//Handling TimedMetadata<br />
[self handleTimedMetadata: metadataItem];<br />
}<br />
}<br />
<br />
}<br />
}<br />
</syntaxhighlight><br />
------------------------------<br />
<syntaxhighlight lang="objective-c"> <br />
- (void)handleTimedMetadata:(AVMetadataItem *)timedMetadata<br />
{<br />
// We expect the content to contain plists encoded as timed metadata<br />
// AVPlayer turns these into NSDictionaries<br />
<br />
id extraAttributeType = [timedMetadata extraAttributes];<br />
NSString *extraString = nil;<br />
if ([extraAttributeType isKindOfClass:[NSDictionary class]])<br />
{<br />
extraString = [extraAttributeType valueForKey:@"info"];<br />
}<br />
else if ([extraAttributeType isKindOfClass:[NSString class]])<br />
{<br />
extraString = extraAttributeType;<br />
}<br />
<br />
NSString *key = [NSString stringWithFormat:@"%@", [timedMetadata key]];<br />
<br />
//If tag starts with "www.nielsen.com", then only sending to SDK<br />
if ([key isEqualToString:@"PRIV"] && [extraString rangeOfString:@"www.nielsen.com"].length > 0)<br />
{<br />
<br />
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{<br />
[nielsenApi sendID3:extraString];<br />
});<br />
}<br />
}<br />
</syntaxhighlight><br />
|Swift = <syntaxhighlight lang="swift"> <br />
//Setting observer to track timedMetadata<br />
player.addObserver(self, forKeyPath: timedMetadataKey, options: NSKeyValueObservingOptions.new, context: &TimedMetadataObserverContext)</syntaxhighlight><br />
------------------------------------------------------<br />
<syntaxhighlight lang="swift"> <br />
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {<br />
<br />
if keyPath == timedMetadataKey {<br />
if(context == &TimedMetadataObserverContext){<br />
if change != nil {<br />
let timedMetadataArray = change![.newKey]<br />
if timedMetadataArray != nil && (timedMetadataArray! as AnyObject) is Array<Any> {<br />
for item in timedMetadataArray as! [AVMetadataItem] {<br />
//Handling TimedMetadata<br />
self.handleTimedMetadata(metadataItem: item)<br />
}<br />
}<br />
}<br />
}<br />
}<br />
</syntaxhighlight><br />
------------------------------------------------------<br />
<syntaxhighlight lang="swift"> <br />
func handleTimedMetadata(metadataItem: AVMetadataItem) {<br />
guard let extraAttributeType = metadataItem.extraAttributes else {<br />
return<br />
}<br />
let info : AVMetadataExtraAttributeKey = AVMetadataExtraAttributeKey(rawValue: "info")<br />
let extraString = extraAttributeType[info] as AnyObject<br />
let key = metadataItem.key as! String<br />
<br />
//If tag starts with "www.nielsen.com", then only sending to SDK<br />
if key == "PRIV" && extraString.range(of: "www.nielsen.com").length > 0 {<br />
<br />
DispatchQueue.global(qos: .default).async { () -> Void in<br />
self.nielsenApi?.sendID3(extraString as! String)<br />
}<br />
}<br />
}<br />
<br />
</syntaxhighlight><br />
}}<br />
<br />
== Life cycle of SDK instance ==<br />
Life cycle of SDK instance includes four general states:<br />
# '''Initial state''' – The SDK is not initialized and hence, not ready to process playing information. Once the SDK is moved out of this state, it needs instantiation of the new SDK instance in order to get the instance in the '''Initial state'''.<br />
# '''Idle state''' – The SDK is initialized and is ready to process playing information. Once Initialized, the SDK instance is not processing any data, but is listening for the play event to occur.<br />
# '''Processing state''' – The SDK instance is processing playing information. For DTVR, the <code>'''loadMetadata''' </code> call moves the SDK instance into this state. In this state, the SDK instance will be able to process the following calls.<br />
## <code>'''stop'''</code> – Call this API when the playback is paused, switches between content and ad (within the same content playback) or encounters interruptions.<br />
# '''Disabled state''' – The SDK instance is disabled and is not processing playing information. SDK instance moves into this state in one of the following scenarios.<br />
## Initialization fails<br />
## <code>'''appDisableApi'''</code> is set to <code>true</code> ''(This is testing purposes only. Not for User Opt-Out.)''<br />
<br />
<blockquote>'''Note:''' For API Version 5.1 and above, App SDK will fire data pings and continue measurement even after the user has opted out from Nielsen measurement on a device. The data ping will be marked as opted-out ping.</blockquote><br />
<br />
'''Note''': In case of any interruptions during playback due to alarm, calendar, call, flight mode, Wi-Fi toggle, channel change, etc., call <code>stop</code> to stop the measurement.<br />
<br />
== Handling Foreground and Background states ==<br />
For iOS, background/foreground detection is handled by the app lifecylce APIs which are provided by [https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html Apple:]<br />
<br />
Foreground/Background state measurement is a requirement of Nielsen AppSDK implementation which is especially crucial for static measurement.<br />
<br />
== Interruptions during playback ==<br />
As part of integrating Nielsen App SDK with the player application, the Audio / Video app developer needs to handle the following possible interruption scenarios:<br />
* Pause / Play<br />
* Network Loss (Wi-Fi / Airplane / Cellular)<br />
* Call Interrupt (SIM or Third party Skype / Hangout call)<br />
* Alarm Interrupt<br />
* Content Buffering<br />
* Device Lock / Unlock (Video players only, not for Audio players)<br />
* App going in the Background/Foreground (Video players only, not for Audio players)<br />
* Channel / Station Change Scenario<br />
* Unplugging of headphone<br />
In case of encountering one of the above interruptions, the player application needs to<br />
* Call <code>stop</code> immediately (except when content is buffering) and withhold sending playhead position.<br />
* Call <code>play</code> once the playback resumes.<br />
<br />
== Pre-Certification Checklists ==<br />
After the application is ready to be sent for Nielsen Certification, please go through the [[Digital Pre-Certification Checklist App SDK]] and ensure the app behaves as expected, before submitting to Nielsen.<br />
<br />
{{Template:iOS_Privacy_and_Opt-Out}}<br />
<br />
== Going Live ==<br />
Following Nielsen testing, you will need to:<br />
<br />
# '''Disable Debug Logging''': Disable logging by deleting <code>{nol_devDebug: 'DEBUG'}</code> from initialization call.<br />
# '''Notify Nielsen''': Once you are ready to go live, let us know so we can enable you for reporting. We will not be able to collect or report data prior to receiving notification from you.<br />
<br />
== Sample Applications ==<br />
The below sample applications have been designed to show the API's functionality and are broken into two distinct categories:<br />
* '''Basic''' - To show the functionality of the Nielsen API using a standard no-frills player.<br />
** [[Swift Basic Sample|Swift 4.0 Sample]]<br />
** [[Objective-c Basic example|Objective-C Sample]]<br />
<br />
<br />
== Testing an Implementation - App ==<br />
See [[Digital Measurement Testing]].</div>DiegoOrdonezhttps://engineeringportal.nielsen.com//w/index.php?title=DCR_Video_%26_Static_Mobile_Cloud_API&diff=4263DCR Video & Static Mobile Cloud API2020-08-31T18:09:34Z<p>DiegoOrdonez: /* Example Request */</p>
<hr />
<div>{{Breadcrumb|}} {{Breadcrumb|Digital}} {{Breadcrumb|DCR & DTVR}} {{CurrentBreadcrumb}}<br />
[[Category:Digital]]<br />
<br />
This guide shows you how to integrate the Nielsen Cloud API to enable Digital Content Ratings (DCR) measurement on your mobile apps.<br />
*For Roku Apps, please see [[DCR Video & Static Roku Cloud API]]<br />
*For OTT Devices, please see [[DCR Video & Static Cloud API]]<br />
<br />
==Prerequisites==<br />
To get started, you will need a Nielsen App ID. The App ID is a unique ID assigned to your app. This will be provided to you upon starting the integration.<br />
<br />
<syntaxhighlight lang="javascript">XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</syntaxhighlight><br />
<br />
==Integration==<br />
We will cover the steps for constructing the Cloud API Calls.<br />
<br />
===Request Overview===<br />
<br />
====URL Structure====<br />
<br />
The Cloud API Calls are HTTP GET Requests with the URL structure:<br />
<br />
<syntaxhighlight lang="javascript">[endpoint]/[appid]/[sessionID]/a?b=[payload]</syntaxhighlight><br />
<br />
The URL includes the following components:<br />
<br />
*<code>[endpoint]</code>: location of data collection environment<br />
*<code>[appid]</code>: provided App ID<br />
*<code>[sessionID]</code>: unique value for each user session<br />
*<code>[payload]</code>: metadata and events<br />
<br />
====Endpoint====<br />
<br />
There are endpoints for testing and production:<br />
<br />
*Testing: <code>https://sandbox-cloudapi.imrworldwide.com/nmapi/v2/</code><br />
*Production: <code>https://cloudapi.imrworldwide.com/nmapi/v2/</code><br />
<br />
During testing, all calls should be pointed to the testing endpoint. We will review the update to the production endpoint during the Go Live section of this guide.<br />
<br />
====URL Example====<br />
As you move through the integration steps, you can reference the below URL structure with the expanded payload:<br />
<br />
<syntaxhighlight lang="javascript"><br />
https://sandbox-cloudapi.imrworldwide.com/nmapi/v2/[appid]/[sessionID]/a?b=<br />
{<br />
"devInfo": [deviceInfo],<br />
"metadata": {<br />
"static": [static_metadata],<br />
"content": [content_metadata],<br />
"ad": [ad metadata]<br />
},<br />
"event": [event],<br />
"position": [playhead_position],<br />
"type": [asset type],<br />
"utc": [UTC]<br />
}<br />
</syntaxhighlight><br />
<br />
===Create Session ID===<br />
A unique Session ID must be created upon app launch and provided in the URL. This will allow measurement to occur for the entire duration that a user is within the app.<br />
<br />
A Session ID needs to be completely unique so it is recommended to use a version 4 UUID or another method of your choosing to guarantee there are no repeats.<br />
<br />
Upon exiting the app, the session will need to be terminated using the delete event. Sessions will automatically expire after 30 minutes of cloud inactivity.<br />
<br />
===Define URL Structure===<br />
Define the URL structure using your provided <code>[appid]</code> and a unique <code>[sessionID]</code>.<br />
<br />
<syntaxhighlight lang="javascript">https://sandbox-cloudapi.imrworldwide.com/nmapi/v2/[appid]/[sessionID]/a?b=[payload]</syntaxhighlight><br />
<br />
===Configure Payload===<br />
<br />
All Cloud API requests must contain the following payload data:<br />
<br />
*''devInfo'': device and app info<br />
*''metadata'': asset metadata<br />
*''event metadata'': type of event<br />
<br />
The payload can be passed through key-values using the Nielsen reserved keys. The specific keys and descriptions are highlighted in the tables included in this section.<br />
<br />
'''Payload Example'''<br />
<br />
The example below should be referenced when following the steps for configuring the request payload.<br />
<br />
<syntaxhighlight lang="javascript"><br />
// 3.1 Configure Payload: devInfo <br />
payload = {<br />
"devInfo": {<br />
"devId": "AD-ID", <br />
"apn": "AppName",<br />
"apv": "1.0",<br />
"uoo": "false"<br />
},<br />
<br />
// 3.2 Configure Payload: metadata<br />
"metadata": {<br />
"static": {}, // object for measuring static content<br />
"content": { // object for measuring video content<br />
"type": "content", // "content" for video<br />
"assetid": "VIDEO-ID123", // unique ID for video<br />
"isfullepisode": "y", // full episode flag<br />
"program": "Program Name", // program name<br />
"title": "Episode Title S3 - EP1", // episode name<br />
"length": "1800", // content duration in seconds<br />
"segB": "Custom Segment B", // custom segment<br />
"segC": "Custom Segment C", // custom segment<br />
"crossId1": "Standard Episode ID", // episode ID<br />
"crossId2": "Content Originator ID", // content orginator (required for distributors)<br />
"airdate": "20161013 20:00:00", // airdate<br />
"adloadtype": "2" //ad load flag<br />
"hasAds": "1", // content contains ads = 1 / no ads = 0<br />
"progen": "CV" // program genre abbreviation<br />
},<br />
"ad": {<br />
"type": "preroll", // type of ad<br />
"assetid": "AD-ID123" // unique ID for ad<br />
}<br />
},<br />
<br />
// 3.3 Configure Payload: events<br />
"event": "playhead", //event name<br />
"position": "300", // position in seconds<br />
"type": "content", //"content" or "ad"<br />
"utc": "1456448742000" //unix timestamp in milliseconds <br />
}<br />
</syntaxhighlight><br />
<br />
=====Configure Payload: devInfo=====<br />
An object <code>"devInfo"</code> will need to be created to capture App and Device information.<br />
<br />
{| class="wikitable"<br />
|-<br />
! Keys !! Description !! Values !! Required<br />
|-<br />
| devId || unique ID to identify user (e.g. Advertising ID, Roku Device ID) || custom || Yes<br />
|-<br />
| apn || app name || custom || Yes<br />
|-<br />
| apv || app build version || custom || Yes<br />
|-<br />
| uoo || device opt-out status || <code>"true"</code> or <code>"false"</code> || Yes<br />
|-<br />
|}<br />
<br />
On mobile devices you will need to pass the IDFA/Ad ID as part of the devId parameter. Please see the section below regarding Opt Out and the IDFA/Ad ID.<br />
<br />
===== Accessing the IDFA on iOS =====<br />
<br />
Accessing the ID For Advertisers (IDFA) uses OS level API calls. In order to do this, you must first import the following header file:<br />
<syntaxhighlight lang="objective-c">#import <AdSupport/ASIdentifierManager.h></syntaxhighlight><br />
<br />
Then to receive the IDFA as a NSString, use similar code to the following:<br />
<syntaxhighlight lang="objective-c">NSString *idfaString = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];</syntaxhighlight><br />
You can read more about ASIdentifierManager on Apple's [https://developer.apple.com/reference/adsupport/asidentifiermanager| developer website].<br />
<br />
===== Accessing the Ad ID on Android =====<br />
<br />
Accessing the Google Ad ID uses OS level API calls. In order to do this, you must first import Google Play Services. Then to receive the Ad ID, use the AdvertisingIdClient class as such:<br />
<syntaxhighlight lang="java">public static AdvertisingIdClient.Info getAdvertisingIdInfo (Context context)</syntaxhighlight><br />
<br />
You can read more about the AdvertisingIdClient class on Google's [https://developers.google.com/android/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient| developer website].<br />
<br />
'''Example devInfo Object'''<br />
<syntaxhighlight lang="javascript"><br />
// create devInfo object<br />
"devInfo": {<br />
"devId": "AD-ID",<br />
"apn": "AppName",<br />
"apv": "1.0",<br />
"uoo": "false"<br />
},<br />
</syntaxhighlight><br />
<br />
==== 3.2 Configure Payload: metadata ====<br />
Asset metadata can be passed through <code>"metadata"</code>. There are two asset types: <code>"content"</code> for video and <code>"ad"</code> for ads. The metadata received for each asset is used for classification and reporting.<br />
<br />
You will need to set up <code>"metadata"</code> objects for <code>"content"</code> and <code>"ad"</code> with the required Nielsen keys as shown in the sample code below.<br />
<br />
===== Content Metadata =====<br />
Content metadata should remain constant throughout the entirety of an episode/clip including when ads play.<br />
<br />
{| class="wikitable"<br />
|-<br />
! Keys !! Description !! Values !! Required<br />
|-<br />
| type || type of asset || <code>"content"</code> || Yes<br />
|-<br />
| assetid || unique ID assigned to asset || custom <br>(no [[Special Characters]]) || Yes<br />
|-<br />
| program ||name of program (25 character limit) || custom || Yes<br />
|-<br />
| title ||name of program (25 character limit) || custom || Yes<br />
|-<br />
| length || length of content in seconds || <code>seconds</code> (0 for live stream) || Yes<br />
|-<br />
| segB || custom segment B || custom || No<br />
|-<br />
| segC || custom segment C || custom || No<br />
|-<br />
| airdate || the airdate in the linear TV || YYYYMMDD HH24:MI:SS || Yes<br />
|-<br />
| isfullepisode || full episode flag || <code>"y"</code>- full episode, <code>"n"</code>- non full episode || Yes<br />
|-<br />
| crossId1 || standard episode ID || custom || Yes<br />
|-<br />
| crossId2 || content originator (only required for distributors) || Nielsen || No<br />
|-<br />
| adloadtype || type of ad load:<br />
<code>"1"</code> Linear – matches TV ad load<br />
<br />
<code>"2"</code> Dynamic – Dynamic Ad Insertion (DAI)<br />
|| <code>"2"</code> - DCR measures content with dynamic ads || Yes<br />
|-<br />
| hasAds || ads indicator<br />
<code>"1"</code>: ads included<br />
<br />
<code>"0"</code>: ads not included<br />
|| <code>"1"</code> or <code>"0"</code> || Yes<br />
|-<br />
| subbrand || sub brand override || Nielsen || No<br />
|-<br />
| progen || program genre abbreviation - see [[DCR OTT Genre List]] for accepted values || <code>"CV"</code> for Comedy Variety || Yes<br />
|}<br />
<br />
<br />
'''Example Content Object'''<br />
<syntaxhighlight lang='json'>// create content object<br />
"content": {<br />
"type": "content",<br />
"assetid": "VIDEO-ID123",<br />
"isfullepisode": "y",<br />
"program": "Program Name",<br />
"title": "Episode Title S3 - EP1",<br />
"length": "1800",<br />
"segB": "Custom Segment B",<br />
"segC": "Custom Segment C",<br />
"crossId1": "Standard Episode ID",<br />
"crossId2": "Content Originator ID",<br />
"airdate": "20161013 20:00:00",<br />
"adloadtype": "2",<br />
"hasAds": "1", <br />
"subbrand": "c05",<br />
"progen": "CV"<br />
}</syntaxhighlight><br />
<br />
===== Ad Metadata =====<br />
The ad metadata should be passed for each individual ad.<br />
<br />
{| class="wikitable"<br />
|-<br />
! Keys !! Description !! Values !! Required<br />
|-<br />
| type || type of ad || <code>"preroll"</code>, <code>"midroll"</code>, or <code>"postroll"</code> || Yes<br />
|-<br />
| assetid || unique ID assigned to ad || custom || Yes<br />
|}<br />
<br />
===== Example Ad Object =====<br />
<syntaxhighlight lang="javascript"><br />
// create ad object<br />
"ad": {<br />
"type": "preroll",<br />
"assetid": "AD-ID123"<br />
}<br />
</syntaxhighlight><br />
<br />
=== Configure Payload: Events ===<br />
<br />
The last part of the payload is for enabling events so content is measured correctly when viewed. The events and required parameters are included below.<br />
<br />
==== Event Types ====<br />
<br />
The available events are:<br />
{| class="wikitable"<br />
|-<br />
! Event !! Description<br />
|-<br />
| <code>"playhead"</code> || Playhead position in seconds, must be passed as a whole number every 10 seconds. The final playhead position should be sent before an asset has changed to properly capture full duration. When content is paused, stop passing playhead position until content is resumed. On playhead scrubbing, send current playhead position, followed by the playhead position the user scrubs to. For Live streams, you may use Unix Time (in seconds) as the playhead position. Note that ad playheads must also use Unix Time if Unix Time is used for content playheads.<br />
|-<br />
| <code>"complete"</code> || The complete event must be sent when the content has completed full playback. Before calling the complete event, a final playhead update with the final position is required to be sent to receive full duration credit. For Live streams, a complete event must be sent at program boundaries.<br />
|-<br />
| <code>"delete"</code> || The delete event is optional and can be sent when the viewing session is terminated (typically on App close). A new session ID must be generated after sending a delete event. Delete should not be sent on app interruptions or foreground/background events. All creditable duration will be summarized for all asset types when delete occurs (content and ads).<br />
|}<br />
<br />
===== Event Parameters =====<br />
<br />
The following parameters need to be passed when calling events:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Parameter !! Description !! Value !! Required<br />
|-<br />
| <code>"event"</code> || event type || <code>"playhead"</code>, <code>"complete"</code>, or <code>"delete"</code> || Yes<br />
|-<br />
| <code>"position"</code> || creditable position || playhead position in seconds or Unix timestamp (seconds since Jan-1-1970 UTC) for livestream || Yes<br />
|-<br />
| <code>"type"</code> || asset type || <code>"content"</code>, <code>"ad"</code> || Yes<br />
|-<br />
| <code>"utc"</code> || Unix timestamp in milliseconds. Must be passed every 10 seconds. || <code>"1472760000000"</code> || Yes<br />
|}<br />
<br />
===== Example Event =====<br />
You can call events by passing values in the required parameters:<br />
<br />
<syntaxhighlight lang="javascript"><br />
"devInfo": [deviceInfo],<br />
"metadata": {<br />
"static": [static metadata],<br />
"content": [content metadata],<br />
"ad": [ad metadata]<br />
},<br />
// Event Parameters<br />
"event": [event], // event name<br />
"position": [playheadPosition], //position in seconds<br />
"type": [asset type], // values are "content" or "ad"<br />
"utc": "1472760000000" //unix timestamp in milliseconds<br />
}<br />
</syntaxhighlight><br />
<br />
'''Note:''' The full payload including "devInfo" and "metadata" must be populated in each event request.<br />
<br />
===== Sample Event Lifecycle =====<br />
The sample event lifecycle can be used as a reference for identifying the order for calling events and values to pass.<br />
<br />
<syntaxhighlight lang="javascript"><br />
// Start of Session: session ID created when App is opened<br />
<br />
// Preroll<br />
Ad Playhead {"event": "playhead", "position": "0", "type": "ad", "utc": "1472760000000"} <br />
<br />
// Content<br />
Content Playhead {"event": "playhead", "position": "0", "type": "content", "utc": "1472760000000"} <br />
<br />
// Midroll<br />
Midroll Playhead {"event": "playhead", "position": "0", "type": "ad", "utc": "1472760000000"} <br />
<br />
// Content resumes at 15 minutes<br />
Content Playhead {"event": "playhead", "position": "900", "type": "content", "utc": "1472760000000"} <br />
<br />
// Content completes at 30 minutes<br />
Complete {"event": "complete", "position": "1800", "type": "content", "utc": "1472760000000"} <br />
<br />
// Postroll<br />
Ad Playhead {"event": "playhead", "position": "0", "type": "ad", "utc": "1472760000000"} <br />
<br />
//End of Session: The delete event should be called when the App is exited. The values for position and type not required to be passed.<br />
Delete { "event": "delete", "position": "", "type": "", "utc": "1472760000000"} <br />
</syntaxhighlight><br />
<br />
<br />
'''Sample Event Lifecycle - Detailed Storyline'''<br />
This detailed event sequence provides additional insight for the correct events to call when handling certain playback scenarios.<br />
<syntaxhighlight lang='javascript'>// SESSION STARTS<br />
// Start of Session: session ID created when App is opened<br />
<br />
// PREROLL<br />
// Preroll Start - Start each Ad with a position of "0", resetting to '0' for each Ad, and Ad break.<br />
Ad Playhead {"event": "playhead", "position": "0", "type": "ad", "utc": "1472760000000"} <br />
<br />
// Preroll Stop - End each Ad with the final position of the Ad.<br />
Ad Playhead {"event": "playhead", "position": "15", "type": "ad", "utc": "1472761500000"}<br />
<br />
// CONTENT <br />
// Content Start - Start new content streams with a position of "0" incrementing the position every 10 seconds.<br />
Content Playhead {"event": "playhead", "position": "0", "type": "content", "utc": "1472761500000"} <br />
<br />
// Content Stop Before Ad Break - Send a playhead update including the current content positon before an Ad break.<br />
Content Playhead {"event": "playhead", "position": "299", "type": "content", "utc": "1472787400000"}<br />
<br />
// MIDROLL<br />
// Midroll Start - Start each Ad with a position of "0", resetting to '0' for each Ad, and Ad break.<br />
Midroll Playhead {"event": "playhead", "position": "0", "type": "ad", "utc": "1472787500000"} <br />
<br />
// Midroll Stop - End each Ad with the final position of the Ad.<br />
Ad Playhead {"event": "playhead", "position": "60", "type": "ad", "utc": "1472793500000"}<br />
<br />
// CONTENT<br />
// Content resumes at 5 minutes - Send playhead update with the current resumed position, and begin incrimenting the positon every 10 seconds.<br />
Content Playhead {"event": "playhead", "position": "300", "type": "content", "utc": "1472799500000"} <br />
<br />
// Content completes at 10:12 - Make sure to send in the playhead event with the final content position before sending the complete event.<br />
Final Content Playhead {"event": "playhead", "position": "612", "type": "content", "utc": "1472830700000"} <br />
<br />
Complete {"event": "complete", "position": "612", "type": "content", "utc": "1472830800000"} <br />
<br />
// POSTROLL<br />
// Postroll Start - Start each Ad with a position of "0", resetting to '0' for each Ad, and Ad break.<br />
Ad Playhead {"event": "playhead", "position": "0", "type": "ad", "utc": "1472830900000"} <br />
<br />
// Postroll Stop - End each Ad with the final position of the Ad.<br />
Ad Playhead {"event": "playhead", "position": "45", "type": "ad", "utc": "1472835300000"}<br />
<br />
// SESSION ENDS<br />
<br />
//End of Session: The delete event should be called when the App is exited. The values for position and type not required to be passed.<br />
Delete { "event": "delete", "position": "", "type": "", "utc": "1472835400000"} </syntaxhighlight><br />
<br />
<br />
=====Handling Playhead=====<br />
Calling <code>"playhead"</code> is critical for accurate duration crediting. You can reference the below guidance to determine the correct playhead position to pass depending on the playback scenario.<br />
<br />
'''Playhead: General'''<br />
* Playhead position must start at 0 for each new asset, and be passed at least every 10 seconds.<br />
* Final postion must be sent at the end of content or an ad<br />
<br />
'''Playhead: Ads'''<br />
* The final position must be sent when switching from content to ad, or ad to content.<br />
* Each ad playhead position should be 0 at ad start.<br />
* For Ad Pods, playhead must be called, and reset to 0 for each individual ad. <br />
* The last content position before an Ad should be sent before switching to Ads.<br />
* When content has resumed following an ad break, the playhead position update must continue where the previous content segment left off.<br />
<br />
'''Playhead: User Actions'''<br />
* Upon user scrubbing, the current position must be sent before a user scrubs, and the new position should be sent where the user lands, and begin sending in the 10 second updates thereafter.<br />
* On pause, send the current position and then discontinue sending playhead event updates.<br />
* If a user exits a stream early, the last current position must be sent in a playhead update to receive accurate duration.<br />
<br />
===== Interruption Scenarios =====<br />
<br />
As part of configuring events, you will need to handle all possible interruption scenarios such as:<br />
<br />
*Pause / Play<br />
*Network Loss (Wi-Fi / Airplane Mode / Cellular)<br />
*Wi-Fi OFF / ON<br />
*Call Interrupt (SIM or Third party Skype / Hangout call)<br />
*Alarm Interrupt<br />
*Content Buffering<br />
*Device Lock / Unlock (Video players only, not for Audio players)<br />
*App going Background / Foreground (Video players only, not for Audio players)<br />
*App Crash or Exit<br />
*Channel / Station Change Scenario<br />
*Unplugging of headphone<br />
<br />
When playback is temporarily interrupted (e.g. pause, content buffering), the app needs to send the last known playhead position.<br />
<br />
*If an app is sent to background for more than 5 minutes, please create a new session ID. Otherwise, use the same session ID as before.<br />
*If loss of Internet occurs, please queue the API calls that would have been made. Once Internet connectivity is regained, please spool off the API calls in order of first generated (Note: if doing so, please use the UTC time in milliseconds)<br />
<br />
When playback is permanently interrupted, the app needs to send delete immediately.<br />
*If an app crashes, please create a new session ID. No delete call will be necessary as the previous session will timeout.<br />
<br />
Once playback resumes after delete occurs, a new session will need to be created with a unique session ID. All of the required metadata and events will need to be sent.<br />
<br />
'''Note:''' The session will automatically timeout after 30 minutes of inactivity.<br />
<br />
=== Example Request ===<br />
<br />
Now that we walked through the Cloud API integration steps, your requests should have the following components: Session ID, App ID, and Payload. You can reference the example below when your reviewing your integration.<br />
<br />
<syntaxhighlight lang="javascript"><br />
// 1. Create Session ID<br />
sessionID = dfc7dc6a-66a7-4705-9fba-adaaf7e3d5e0 // Example sessionID created using a UUID Generator<br />
<br />
// 2. Define URL Structure with App ID and Session ID<br />
sessionURL = https://sandbox-cloudapi.imrworldwide.com/nmapi/v2/[appid]/[sessionID]/a?b=<br />
<br />
// 3. Configure Payload<br />
// 3.1 Configure Payload: devInfo <br />
payload = {<br />
"devInfo": {<br />
"devId": "AD-ID", <br />
"apn": "AppName",<br />
"apv": "1.0",<br />
"uoo": "false"<br />
},<br />
<br />
// 3.2 Configure Payload: metadata<br />
"metadata": {<br />
"static": {}, // object for measuring static content<br />
"content": { // object for measuring video content<br />
"type": "content", // "content" for video<br />
"assetid": "VIDEO-ID123", // unique ID for video<br />
"isfullepisode": "y", // full episode flag<br />
"program": "Program Name", // program name<br />
"title": "Episode Title S3 - EP1", // episode name<br />
"length": "1800", // content duration in seconds<br />
"segB": "Custom Segment B", // custom segment<br />
"segC": "Custom Segment C", // custom segment<br />
"crossId1": "Standard Episode ID", // episode ID<br />
"crossId2": "Content Originator ID", // content orginator (required for distributors)<br />
"airdate": "20161013 20:00:00", // airdate<br />
"adloadtype": "2", // ad load flag<br />
"hasAds": "1", // content contains ads = 1 / no ads = 0<br />
"progen": "CV" // program genre abbreviation<br />
},<br />
"ad": {<br />
"type": "preroll", // type of ad<br />
"assetid": "AD-ID123" // unique ID for ad<br />
}<br />
},<br />
<br />
// 3.3 Configure Payload: events<br />
"event": "playhead", //event name<br />
"position": "300", // position in seconds<br />
"type": "content", //"content" or "ad"<br />
"utc": "1456448742000" //unix timestamp in milliseconds <br />
}<br />
<br />
// Append payload to URL<br />
var image = new Image()<br />
image.onerror = function() {<br />
// wait and send again<br />
}<br />
(new Image).src = sessionURL+encodeURIComponent(JSON.stringify(payload));<br />
</syntaxhighlight><br />
<br />
==== Enable Debug Logging ====<br />
<br />
Now that you have set up the Cloud API requests, you can enable debug logging to validate your integration. Enabling debug logging is required for Nielsen certification.<br />
<br />
==== GET Request ====<br />
<br />
Display GET Request to console using a name to identify each event (e.g. playhead).<br />
<br />
<syntaxhighlight lang="javascript"><br />
console.log("Event", image); <br />
</syntaxhighlight><br />
<br />
==== Payload ====<br />
<br />
Output payload to identify required metadata and events.<br />
<br />
<syntaxhighlight lang="javascript"><br />
console.log("Event Payload", payload); <br />
</syntaxhighlight><br />
<br />
==== HTTP Response Code ====<br />
<br />
Confirm request was completed by viewing HTTP response code.<br />
<br />
<syntaxhighlight lang="javascript"><br />
code = msg.GetResponseCode();<br />
console.log("Response Code", code); <br />
</syntaxhighlight><br />
<br />
You can reference the HTTP Response Code table when reviewing your requests:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Status Code !! Status Text !! Description<br />
|-<br />
| <code>200</code> || OK || request received<br />
|-<br />
| <code>403</code> || Forbidden || invalid App ID<br />
|-<br />
| <code>404</code> || Not Found || JSON issue<br />
|}<br />
<br />
== Opt-Out ==<br />
Your app must provide a means for the user to Opt-Out, or Opt-In to Nielsen Measurement. The Opt-Out requirement can be fulfilled by creating an Opt-Out/Opt-In button, toggle switch, or slider within the app "Settings", or "About" section to allow the user selection. If you have the ability to render text within the Opt-Out screen, it is recommended to add the Privacy Information included below.<br />
<br />
[[File:Nielsen Opt-Out.png|link=]]<br />
<br />
You will need to store the User Opt-Out (uoo) status, so that it can be retrieved and populated in the <code>"devInfo"</code> metadata which will be sent in every Cloud API event (playhead, complete, & delete). <br />
<br />
*In addition, you will need to ensure that the devId field is a blank value if a user has elected to Opt-Out<br />
<br />
*If the device offers "Limit Ad Tracking" or "Opt-Out" device settings, you must set uoo=true, and also ensure that the devId is set to a blank value if the user elects to enable this setting.<br />
<br />
{| class="wikitable"<br />
|-<br />
! uoo Key !! Description !! Values<br />
|-<br />
| uoo || Device is Opted-In to Nielsen Measurement (Recommended Default Setting) || <code>"false"</code><br />
|-<br />
| uoo || Device is Opted-Out of Nielsen Measurement || <code>"true"</code><br />
|}<br />
<br />
===== devInfo Opt-In JSON Payload Example =====<br />
<br />
<syntaxhighlight lang="javascript"><br />
"devInfo": {<br />
"apn": "Roku Sample App",<br />
"apv": "1",<br />
"devId": "7be25cf9-8c40-5cc2-871e-19bf41940288",<br />
"uoo": "false"<br />
},<br />
</syntaxhighlight><br />
<br />
===== devInfo Opt-Out JSON Payload Example =====<br />
<br />
<syntaxhighlight lang="javascript"><br />
"devInfo": {<br />
"apn": "Roku Sample App",<br />
"apv": "1",<br />
"devId": "", //devId must be blank when a user elects to Opt-Out.<br />
"uoo": "true"<br />
},<br />
</syntaxhighlight><br />
<br />
===== Privacy Information Template To Include In Opt-Out Screen =====<br />
<br />
<blockquote><br />
'''''ABOUT NIELSEN MEASUREMENT'''''<br />
<br />
Television and the way we watch it have come a long way since Nielsen began measuring TV audiences in 1950. Today, the ability to watch our favorite shows at any time and on multiple devices amplifies the need for exceptionally adept and flexible audience measurement capabilities.<br />
<br />
Consumers are changing with the times, and the same goes for us. As technology continues to evolve and media companies try new ways to attract viewers, understanding what consumers are watching — and what they're watching on — is more important than ever. Today, viewing video is a personal and mobile experience — anytime and anywhere. Our capabilities provide relevant metrics that are necessary to inform successful marketing and programming and drive continued growth. As a global information and measurement leader, we are committed to protecting the privacy and security of the data we collect, process and use. While our digital measurement products are not used to identify you in any way, they help us and our clients measure and analyze how consumers engage with media across online, mobile and emerging technologies, and offer insights into consumer behavior.<br />
<br />
'''''YOUR CHOICES'''''<br />
<br />
Nielsen believes that you should have a choice about whether to contribute to our research and insights. To opt out, or opt into Nielsen measurement please make the appropriate selection within the app. If you have this app on more than one device, you will need to opt out of this app on each device. To learn more about our digital measurement products and your choices in regard to them, please visit http://www.nielsen.com/digitalprivacy.<br />
</blockquote><br />
'''''Disclosure Template'''''<br />
<br />
Additionally, you must update the App description information from the App Distribution Store with the Nielsen Measurement disclosure below:<br />
<blockquote><br />
This app features Nielsen's proprietary measurement software which will allow you to contribute to market research, like Nielsen's TV Ratings. Please see http://www.nielsen.com/digitalprivacy for more information.<br />
</blockquote><br />
<br />
== Testing ==<br />
Before providing an app build to Nielsen for testing, it is important to run validation checks once you have enabled debug logging.<br />
<br />
=== Payload Validation ===<br />
<br />
Ensure that all of the required payload data is populating while testing several videos. The following areas are critical to measurement:<br />
*devInfo<br />
*Asset metadata for both content, and ads<br />
*Events<br />
*Opt-Out status<br />
<br />
=== Player Events ===<br />
Review event calls:<br />
<br />
==== playhead ====<br />
*Playhead position updates every 10 seconds starting at position '0' for each new asset.<br />
*Final playhead position is sent on content, or ad before switching between assets.<br />
*Content metadata remains constant throughout an episode, or clip play.<br />
*Ad metadata is populated appropriately for each individual ad.<br />
*Playhead position update resumes for content after an ad break, and resets to 0 for each individual ad.<br />
*For scrubbing, last current position should be sent while scrubbing occurs, and the new position should also be sent where the user scrubs to.<br />
*Exiting a stream early should execute the last current position in a playhead update to receive accurate duration.<br />
*Upon pause, the current position should be sent, and playhead updates should stop incrementing until resume play occurs.<br />
<br />
==== complete ====<br />
*Check that the complete event executes upon content complete after the final playhead update is sent<br />
*Do not execute the complete event for ads<br />
<br />
==== delete ====<br />
*Check to see that the delete event occurs upon app exit<br />
<br />
==== GET Request Format ====<br />
*Ensure that the event payloads are formatted in JSON<br />
*Check to see that each of the Cloud API GET requests are properly encoded<br />
<br />
==== HTTP Response ====<br />
*Make sure that each of the Cloud API Get requests are received by the Nielsen Cloud API properly through use of the HTTP Response Code outputs enabled in console.<br />
<br />
==== Opt-Out ====<br />
*Test the "uoo" key gets populated accurately for both Opt-In and Opt-Out selections by validating the Cloud API events called after the user Opt-Out/Opt-In selection.<br />
*Test that the devId field is populated with a blank value if a user has elected to Opt-Out. For example: "devId": "",<br />
*If the device supports "Limit Ad Tracking" or has device "Opt-Out" settings, test that uoo=true, and that devId is set to a blank value if enabled in the device settings.<br />
<br />
== Go Live ==<br />
After your integration has been certified, you will need to: Change Endpoint and Disable Logging.<br />
<br />
'''Change Endpoint:''' You will need to update to the production endpoint:<br />
<br />
*Testing: <code>https://sandbox-cloudapi.imrworldwide.com/nmapi/v2/</code><br />
*Production: <code>https://cloudapi.imrworldwide.com/nmapi/v2/</code><br />
<br />
Your production URL structure should now be:<br />
<code>https://cloudapi.imrworldwide.com/nmapi/v2/[appid]/[sessionID]/a?b=[payload]</code><br />
<br />
'''Disable Logging:''' You can now disable debug logging</div>DiegoOrdonezhttps://engineeringportal.nielsen.com//w/index.php?title=DCR_Video_%26_Static_Cloud_API&diff=4262DCR Video & Static Cloud API2020-08-31T18:08:40Z<p>DiegoOrdonez: /* Example Request */</p>
<hr />
<div>{{Breadcrumb|}} {{Breadcrumb|Digital}} {{Breadcrumb|DCR & DTVR}} {{CurrentBreadcrumb}}<br />
[[Category:Digital]]<br />
<br />
This guide shows you how to integrate the Nielsen Cloud API to enable Digital Content Ratings (DCR) measurement on your over-the-top (OTT) Apps.<br />
*For Roku Apps, please see [[DCR Video & Static Roku Cloud API]]<br />
*For Mobile Apps, please see [[DCR Video & Static Mobile Cloud API]]<br />
<br />
==Prerequisites==<br />
To get started, you will need a Nielsen App ID. The App ID is a unique ID assigned to your app. This will be provided to you upon starting the integration.<br />
<br />
<syntaxhighlight lang="javascript">XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</syntaxhighlight><br />
<br />
==Integration==<br />
We will cover the steps for constructing the Cloud API Calls.<br />
<br />
===Request Overview===<br />
<br />
====URL Structure====<br />
<br />
The Cloud API Calls are HTTP GET Requests with the URL structure:<br />
<br />
<syntaxhighlight lang="javascript">[endpoint]/[appid]/[sessionID]/a?b=[payload]</syntaxhighlight><br />
<br />
The URL includes the following components:<br />
<br />
*<code>[endpoint]</code>: location of data collection environment<br />
*<code>[appid]</code>: provided App ID<br />
*<code>[sessionID]</code>: unique value for each user session<br />
*<code>[payload]</code>: metadata and events<br />
<br />
====Endpoint====<br />
<br />
There are endpoints for testing and production:<br />
<br />
*Testing: <code>https://sandbox-cloudapi.imrworldwide.com/nmapi/v2/</code><br />
*Production: <code>https://cloudapi.imrworldwide.com/nmapi/v2/</code><br />
<br />
During testing, all calls should be pointed to the testing endpoint. We will review the update to the production endpoint during the Go Live section of this guide.<br />
<br />
====URL Example====<br />
As you move through the integration steps, you can reference the below URL structure with the expanded payload:<br />
<br />
<syntaxhighlight lang="javascript"><br />
https://sandbox-cloudapi.imrworldwide.com/nmapi/v2/[appid]/[sessionID]/a?b=<br />
{<br />
"devInfo": [deviceInfo],<br />
"metadata": {<br />
"static": [static_metadata],<br />
"content": [content_metadata],<br />
"ad": [ad metadata]<br />
},<br />
"event": [event],<br />
"position": [playhead_position],<br />
"type": [asset type],<br />
"utc": [Unix time in ms]<br />
}<br />
</syntaxhighlight><br />
<br />
===Create Session ID===<br />
A unique Session ID must be created upon app launch and provided in the URL. This will allow measurement to occur for the entire duration that a user is within the app.<br />
<br />
A Session ID needs to be completely unique so it is recommended to use a version 4 UUID or another method of your choosing to guarantee there are no repeats.<br />
<br />
Upon exiting the app, the session will need to be terminated using the delete event. Sessions will automatically expire after 30 minutes of cloud inactivity.<br />
<br />
===Define URL Structure===<br />
Define the URL structure using your provided <code>[appid]</code> and a unique <code>[sessionID]</code>.<br />
<br />
<syntaxhighlight lang="javascript">https://sandbox-cloudapi.imrworldwide.com/nmapi/v2/[appid]/[sessionID]/a?b=[payload]</syntaxhighlight><br />
<br />
===Configure Payload===<br />
<br />
All Cloud API requests must contain the following payload data:<br />
<br />
*''devInfo'': device and app info<br />
*''metadata'': asset metadata<br />
*''event metadata'': type of event<br />
<br />
The payload can be passed through key-values using the Nielsen reserved keys. The specific keys and descriptions are highlighted in the tables included in this section.<br />
<br />
'''Payload Example'''<br />
<br />
The example below should be referenced when following the steps for configuring the request payload.<br />
<br />
<syntaxhighlight lang="javascript"><br />
// 3.1 Configure Payload: devInfo <br />
payload = {<br />
"devInfo": {<br />
"devId": "AD-ID", <br />
"apn": "AppName",<br />
"apv": "1.0",<br />
"uoo": "false"<br />
},<br />
<br />
// 3.2 Configure Payload: metadata<br />
"metadata": {<br />
"static": {}, // object for measuring static content<br />
"content": { // object for measuring video content<br />
"type": "content", // "content" for video<br />
"assetid": "VIDEO-ID123", // unique ID for video<br />
"isfullepisode": "y", // full episode flag<br />
"program": "Program Name", // program name<br />
"title": "Episode Title S3 - EP1", // episode name<br />
"length": "1800", // content duration in seconds<br />
"segB": "Custom Segment B", // custom segment<br />
"segC": "Custom Segment C", // custom segment<br />
"crossId1": "Standard Episode ID", // episode ID<br />
"crossId2": "Content Originator ID", // content orginator (required for distributors)<br />
"airdate": "20161013 20:00:00", // airdate<br />
"adloadtype": "2" //ad load flag<br />
"hasAds": "1", // content contains ads = 1 / no ads = 0<br />
"progen": "CV" // program genre abbreviation<br />
},<br />
"ad": {<br />
"type": "preroll", // type of ad<br />
"assetid": "AD-ID123" // unique ID for ad<br />
}<br />
},<br />
<br />
// 3.3 Configure Payload: events<br />
"event": "playhead", //event name<br />
"position": "300", // position in seconds<br />
"type": "content", //"content" or "ad"<br />
"utc": "1456448742000" //unix timestamp in milliseconds <br />
}<br />
</syntaxhighlight><br />
<br />
=====Configure Payload: devInfo=====<br />
An object <code>"devInfo"</code> will need to be created to capture App and Device information.<br />
<br />
{| class="wikitable"<br />
|-<br />
! Keys !! Description !! Values !! Required<br />
|-<br />
| devId || unique ID to identify user (e.g. Advertising ID, Roku Device ID) || custom || Yes<br />
|-<br />
| apn || app name || custom || Yes<br />
|-<br />
| apv || app build version || custom || Yes<br />
|-<br />
| uoo || device opt-out status || <code>"true"</code> or <code>"false"</code> || Yes<br />
|-<br />
|}<br />
<br />
'''Example devInfo Object'''<br />
<syntaxhighlight lang="javascript"><br />
// create devInfo object<br />
"devInfo": {<br />
"devId": "AD-ID",<br />
"apn": "AppName",<br />
"apv": "1.0",<br />
"uoo": "false"<br />
},<br />
</syntaxhighlight><br />
<br />
==== 3.2 Configure Payload: metadata ====<br />
Asset metadata can be passed through <code>"metadata"</code>. There are two asset types: <code>"content"</code> for video and <code>"ad"</code> for ads. The metadata received for each asset is used for classification and reporting.<br />
<br />
You will need to set up <code>"metadata"</code> objects for <code>"content"</code> and <code>"ad"</code> with the required Nielsen keys as shown in the sample code below.<br />
<br />
===== Content Metadata =====<br />
Content metadata should remain constant throughout the entirety of an episode/clip including when ads play.<br />
<br />
{| class="wikitable"<br />
|-<br />
! Keys !! Description !! Values !! Required<br />
|-<br />
| type || type of asset || <code>"content"</code> || Yes<br />
|-<br />
| assetid || unique ID assigned to asset || custom <br>(no [[Special Characters]]) || Yes<br />
|-<br />
| program ||name of program (25 character limit) || custom || Yes<br />
|-<br />
| title ||name of program (25 character limit) || custom || Yes<br />
|-<br />
| length || length of content in seconds || <code>seconds</code> (0 for live stream) || Yes<br />
|-<br />
| segB || custom segment B || custom || No<br />
|-<br />
| segC || custom segment C || custom || No<br />
|-<br />
| airdate || the airdate in the linear TV || YYYYMMDD HH24:MI:SS || Yes<br />
|-<br />
| isfullepisode || full episode flag || <code>"y"</code>- full episode, <code>"n"</code>- non full episode || Yes<br />
|-<br />
| crossId1 || standard episode ID || custom || Yes<br />
|-<br />
| crossId2 || content originator (only required for distributors) || Nielsen || No<br />
|-<br />
| adloadtype || type of ad load:<br />
<code>"1"</code> Linear – matches TV ad load<br />
<br />
<code>"2"</code> Dynamic – Dynamic Ad Insertion (DAI)<br />
|| <code>"2"</code> - DCR measures content with dynamic ads || Yes<br />
|-<br />
| hasAds || ads indicator<br />
<code>"1"</code>: ads included<br />
<br />
<code>"0"</code>: ads not included<br />
|| <code>"1"</code> or <code>"0"</code> || Yes<br />
|-<br />
| subbrand || sub brand override || Nielsen || No<br />
|-<br />
| progen || program genre abbreviation - see [[DCR OTT Genre List]] for accepted values || <code>"CV"</code> for Comedy Variety || Yes<br />
|}<br />
<br />
<br />
'''Example Content Object'''<br />
<syntaxhighlight lang='json'>// create content object<br />
"content": {<br />
"type": "content",<br />
"assetid": "VIDEO-ID123",<br />
"isfullepisode": "y",<br />
"program": "Program Name",<br />
"title": "Episode Title S3 - EP1",<br />
"length": "1800",<br />
"segB": "Custom Segment B",<br />
"segC": "Custom Segment C",<br />
"crossId1": "Standard Episode ID",<br />
"crossId2": "Content Originator ID",<br />
"airdate": "20161013 20:00:00",<br />
"adloadtype": "2",<br />
"hasAds": "1", <br />
"subbrand": "c05",<br />
"progen": "CV"<br />
}</syntaxhighlight><br />
<br />
===== Ad Metadata =====<br />
The ad metadata should be passed for each individual ad.<br />
<br />
{| class="wikitable"<br />
|-<br />
! Keys !! Description !! Values !! Required<br />
|-<br />
| type || type of ad || <code>"preroll"</code>, <code>"midroll"</code>, or <code>"postroll"</code> || Yes<br />
|-<br />
| assetid || unique ID assigned to ad || custom || Yes<br />
|}<br />
<br />
===== Example Ad Object =====<br />
<syntaxhighlight lang="javascript"><br />
// create ad object<br />
"ad": {<br />
"type": "preroll",<br />
"assetid": "AD-ID123"<br />
}<br />
</syntaxhighlight><br />
<br />
=== Configure Payload: Events ===<br />
<br />
The last part of the payload is for enabling events so content is measured correctly when viewed. The events and required parameters are included below.<br />
<br />
==== Event Types ====<br />
<br />
The available events are:<br />
{| class="wikitable"<br />
|-<br />
! Event !! Description<br />
|-<br />
| <code>"playhead"</code> || Playhead position in seconds, must be passed as a whole number every 10 seconds. The final playhead position should be sent before an asset has changed to properly capture full duration. When content is paused, stop passing playhead position until content is resumed. On playhead scrubbing, send current playhead position, followed by the playhead position the user scrubs to. For Live streams, you may use Unix Time (in seconds) as the playhead position. Note that ad playheads must also use Unix Time if Unix Time is used for content playheads.<br />
|-<br />
| <code>"complete"</code> || The complete event must be sent when the content has completed full playback. Before calling the complete event, a final playhead update with the final position is required to be sent to receive full duration credit. For Live streams, a complete event must be sent at program boundaries.<br />
|-<br />
| <code>"delete"</code> || The delete event is optional and can be sent when the viewing session is terminated (typically on App close). A new session ID must be generated after sending a delete event. Delete should not be sent on app interruptions or foreground/background events. All creditable duration will be summarized for all asset types when delete occurs (content and ads).<br />
|}<br />
<br />
===== Event Parameters =====<br />
<br />
The following parameters need to be passed when calling events:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Parameter !! Description !! Value !! Required<br />
|-<br />
| <code>"event"</code> || event type || <code>"playhead"</code>, <code>"complete"</code>, or <code>"delete"</code> || Yes<br />
|-<br />
| <code>"position"</code> || playhead position in seconds or Unix time in seconds || <code>"300"</code> || Yes<br />
|-<br />
| <code>"type"</code> || asset type || <code>"content"</code>, <code>"ad"</code> || Yes<br />
|-<br />
| <code>"utc"</code> || Unix timestamp in milliseconds. Must be passed every 10 seconds. || <code>"1472760000000"</code> || Yes<br />
|}<br />
<br />
===== Example Event =====<br />
You can call events by passing values in the required parameters:<br />
<br />
<syntaxhighlight lang="javascript"><br />
"devInfo": [deviceInfo],<br />
"metadata": {<br />
"static": [static metadata],<br />
"content": [content metadata],<br />
"ad": [ad metadata]<br />
},<br />
// Event Parameters<br />
"event": [event], // event name<br />
"position": [playheadPosition], //position in seconds<br />
"type": [asset type], // values are "content" or "ad"<br />
"utc": "1472760000000" //unix timestamp in milliseconds<br />
}<br />
</syntaxhighlight><br />
<br />
'''Note:''' The full payload including "devInfo" and "metadata" must be populated in each event request.<br />
<br />
===== Sample Event Lifecycle =====<br />
The sample event lifecycle can be used as a reference for identifying the order for calling events and values to pass.<br />
<br />
<syntaxhighlight lang="javascript"><br />
// Start of Session: session ID created when App is opened<br />
<br />
// Preroll<br />
Ad Playhead {"event": "playhead", "position": "0", "type": "ad", "utc": "1472760000000"} <br />
<br />
// Content<br />
Content Playhead {"event": "playhead", "position": "0", "type": "content", "utc": "1472760000000"} <br />
<br />
// Midroll<br />
Midroll Playhead {"event": "playhead", "position": "0", "type": "ad", "utc": "1472760000000"} <br />
<br />
// Content resumes at 15 minutes<br />
Content Playhead {"event": "playhead", "position": "900", "type": "content", "utc": "1472760000000"} <br />
<br />
// Content completes at 30 minutes<br />
Complete {"event": "complete", "position": "1800", "type": "content", "utc": "1472760000000"} <br />
<br />
// Postroll<br />
Ad Playhead {"event": "playhead", "position": "0", "type": "ad", "utc": "1472760000000"} <br />
<br />
//End of Session: The delete event should be called when the App is exited. The values for position and type not required to be passed.<br />
Delete { "event": "delete", "position": "", "type": "", "utc": "1472760000000"} <br />
</syntaxhighlight><br />
<br />
<br />
'''Sample Event Lifecycle - Detailed Storyline'''<br />
This detailed event sequence provides additional insight for the correct events to call when handling certain playback scenarios.<br />
<syntaxhighlight lang='javascript'>// SESSION STARTS<br />
// Start of Session: session ID created when App is opened<br />
<br />
// PREROLL<br />
// Preroll Start - Start each Ad with a position of "0", resetting to '0' for each Ad, and Ad break.<br />
Ad Playhead {"event": "playhead", "position": "0", "type": "ad", "utc": "1472760000000"} <br />
<br />
// Preroll Stop - End each Ad with the final position of the Ad.<br />
Ad Playhead {"event": "playhead", "position": "15", "type": "ad", "utc": "1472761500000"}<br />
<br />
// CONTENT <br />
// Content Start - Start new content streams with a position of "0" incrementing the position every 10 seconds.<br />
Content Playhead {"event": "playhead", "position": "0", "type": "content", "utc": "1472761500000"} <br />
<br />
// Content Stop Before Ad Break - Send a playhead update including the current content positon before an Ad break.<br />
Content Playhead {"event": "playhead", "position": "299", "type": "content", "utc": "1472787400000"}<br />
<br />
// MIDROLL<br />
// Midroll Start - Start each Ad with a position of "0", resetting to '0' for each Ad, and Ad break.<br />
Midroll Playhead {"event": "playhead", "position": "0", "type": "ad", "utc": "1472787500000"} <br />
<br />
// Midroll Stop - End each Ad with the final position of the Ad.<br />
Ad Playhead {"event": "playhead", "position": "60", "type": "ad", "utc": "1472793500000"}<br />
<br />
// CONTENT<br />
// Content resumes at 5 minutes - Send playhead update with the current resumed position, and begin incrimenting the positon every 10 seconds.<br />
Content Playhead {"event": "playhead", "position": "300", "type": "content", "utc": "1472799500000"} <br />
<br />
// Content completes at 10:12 - Make sure to send in the playhead event with the final content position before sending the complete event.<br />
Final Content Playhead {"event": "playhead", "position": "612", "type": "content", "utc": "1472830700000"} <br />
<br />
Complete {"event": "complete", "position": "612", "type": "content", "utc": "1472830800000"} <br />
<br />
// POSTROLL<br />
// Postroll Start - Start each Ad with a position of "0", resetting to '0' for each Ad, and Ad break.<br />
Ad Playhead {"event": "playhead", "position": "0", "type": "ad", "utc": "1472830900000"} <br />
<br />
// Postroll Stop - End each Ad with the final position of the Ad.<br />
Ad Playhead {"event": "playhead", "position": "45", "type": "ad", "utc": "1472835300000"}<br />
<br />
// SESSION ENDS<br />
<br />
//End of Session: The delete event should be called when the App is exited. The values for position and type not required to be passed.<br />
Delete { "event": "delete", "position": "", "type": "", "utc": "1472835400000"} </syntaxhighlight><br />
<br />
<br />
=====Handling Playhead=====<br />
Calling <code>"playhead"</code> is critical for accurate duration crediting. You can reference the below guidance to determine the correct playhead position to pass depending on the playback scenario.<br />
<br />
'''Playhead: General'''<br />
* Playhead position must start at 0 for each new asset, and be passed at least every 10 seconds.<br />
* Final postion must be sent at the end of content or an ad<br />
* Playheads should be sent in seconds only, not milliseconds<br />
<br />
'''Playhead: Ads'''<br />
* The final position must be sent when switching from content to ad, or ad to content.<br />
* Each ad playhead position should be 0 at ad start.<br />
* For Ad Pods, playhead must be called, and reset to 0 for each individual ad. <br />
* The last content position before an Ad should be sent before switching to Ads.<br />
* When content has resumed following an ad break, the playhead position update must continue where the previous content segment left off.<br />
<br />
'''Playhead: User Actions'''<br />
* Upon user scrubbing, the current position must be sent before a user scrubs, and the new position should be sent where the user lands, and begin sending in the 10 second updates thereafter.<br />
* On pause, send the current position and then discontinue sending playhead event updates.<br />
* If a user exits a stream early, the last current position must be sent in a playhead update to receive accurate duration.<br />
<br />
===== Interruption Scenarios =====<br />
<br />
As part of configuring events, you will need to handle all possible interruption scenarios such as:<br />
<br />
*Wi-Fi OFF / ON<br />
*App going Background / Foreground (Video players only, not for Audio players)<br />
*App Crash or Exit<br />
<br />
When playback is interrupted, the app needs to send delete immediately.<br />
<br />
Once playback resumes, a new session will need to be created with a unique session ID. All of the required metadata and events will need to be sent.<br />
<br />
'''Note:''' The session will automatically timeout after 30 minutes of inactivity.<br />
<br />
=== Example Request ===<br />
<br />
Now that we walked through the Cloud API integration steps, your requests should have the following components: Session ID, App ID, and Payload. You can reference the example below when your reviewing your integration.<br />
<br />
<syntaxhighlight lang="javascript"><br />
// 1. Create Session ID<br />
sessionID = dfc7dc6a-66a7-4705-9fba-adaaf7e3d5e0 // Example sessionID created using a UUID Generator<br />
<br />
// 2. Define URL Structure with App ID and Session ID<br />
sessionURL = https://sandbox-cloudapi.imrworldwide.com/nmapi/v2/[appid]/[sessionID]/a?b=<br />
<br />
// 3. Configure Payload<br />
// 3.1 Configure Payload: devInfo <br />
payload = {<br />
"devInfo": {<br />
"devId": "AD-ID", <br />
"apn": "AppName",<br />
"apv": "1.0",<br />
"uoo": "false"<br />
},<br />
<br />
// 3.2 Configure Payload: metadata<br />
"metadata": {<br />
"static": {}, // object for measuring static content<br />
"content": { // object for measuring video content<br />
"type": "content", // "content" for video<br />
"assetid": "VIDEO-ID123", // unique ID for video<br />
"isfullepisode": "y", // full episode flag<br />
"program": "Program Name", // program name<br />
"title": "Episode Title S3 - EP1", // episode name<br />
"length": "1800", // content duration in seconds<br />
"segB": "Custom Segment B", // custom segment<br />
"segC": "Custom Segment C", // custom segment<br />
"crossId1": "Standard Episode ID", // episode ID<br />
"crossId2": "Content Originator ID", // content orginator (required for distributors)<br />
"airdate": "20161013 20:00:00", // airdate<br />
"adloadtype": "2", // ad load flag<br />
"hasAds": "1", // content contains ads = 1 / no ads = 0<br />
"progen": "CV" // program genre abbreviation<br />
},<br />
"ad": {<br />
"type": "preroll", // type of ad<br />
"assetid": "AD-ID123" // unique ID for ad<br />
}<br />
},<br />
<br />
// 3.3 Configure Payload: events<br />
"event": "playhead", //event name<br />
"position": "300", // position in seconds<br />
"type": "content", //"content" or "ad"<br />
"utc": "1456448742000" //unix timestamp in milliseconds <br />
}<br />
<br />
// Append payload to URL<br />
var image = new Image()<br />
image.onerror = function() {<br />
// wait and send again<br />
}<br />
(new Image).src = sessionURL+encodeURIComponent(JSON.stringify(payload));<br />
</syntaxhighlight><br />
<br />
==== Enable Debug Logging ====<br />
<br />
Now that you have set up the Cloud API requests, you can enable debug logging to validate your integration. Enabling debug logging is required for Nielsen certification.<br />
<br />
==== GET Request ====<br />
<br />
Display GET Request to console using a name to identify each event (e.g. playhead).<br />
<br />
<syntaxhighlight lang="javascript"><br />
console.log("Event", image); <br />
</syntaxhighlight><br />
<br />
==== Payload ====<br />
<br />
Output payload to identify required metadata and events.<br />
<br />
<syntaxhighlight lang="javascript"><br />
console.log("Event Payload", payload); <br />
</syntaxhighlight><br />
<br />
==== HTTP Response Code ====<br />
<br />
Confirm request was completed by viewing HTTP response code.<br />
<br />
<syntaxhighlight lang="javascript"><br />
code = msg.GetResponseCode();<br />
console.log("Response Code", code); <br />
</syntaxhighlight><br />
<br />
You can reference the HTTP Response Code table when reviewing your requests:<br />
<br />
{| class="wikitable"<br />
|-<br />
! Status Code !! Status Text !! Description<br />
|-<br />
| <code>200</code> || OK || request received<br />
|-<br />
| <code>403</code> || Forbidden || invalid App ID<br />
|-<br />
| <code>404</code> || Not Found || JSON issue<br />
|}<br />
<br />
== Opt-Out ==<br />
Your app must provide a means for the user to Opt-Out, or Opt-In to Nielsen Measurement. This requirement can be fulfilled by checking the device OS for the user's setting of "Limit Ad Tracking" or similar option. If the device offers "Limit Ad Tracking" settings, you should set <code>uoo=true</code> or <code>uoo=false</code> depending on the user's privacy setting. Also, ensure that the <code>devId</code> is set to a blank value if the user elects to opt-out.<br />
<br />
If the device does not have OS-level "Do Not Track" settings, you can implement opt-out by creating an Opt-Out/Opt-In button, toggle switch, or slider within the app "Settings", or "About" section to allow the user selection.<br />
<br />
[[File:Nielsen Opt-Out.png|link=]]<br />
<br />
You will need to store the User Opt-Out (uoo) status, so that it can be retrieved and populated in the <code>"devInfo"</code> metadata which will be sent in every Cloud API event (playhead, complete, & delete). <br />
<br />
{| class="wikitable"<br />
|-<br />
! uoo Key !! Description !! Values<br />
|-<br />
| uoo || Device is Opted-In to Nielsen Measurement (Recommended Default Setting) || <code>"false"</code><br />
|-<br />
| uoo || Device is Opted-Out of Nielsen Measurement || <code>"true"</code><br />
|}<br />
<br />
===== devInfo Opt-In JSON Payload Example =====<br />
<br />
<syntaxhighlight lang="javascript"><br />
"devInfo": {<br />
"apn": "Cloud API Sample App",<br />
"apv": "1",<br />
"devId": "7be25cf9-8c40-5cc2-871e-19bf41940288",<br />
"uoo": "false"<br />
},<br />
</syntaxhighlight><br />
<br />
===== devInfo Opt-Out JSON Payload Example =====<br />
<br />
<syntaxhighlight lang="javascript"><br />
"devInfo": {<br />
"apn": "Cloud API Sample App",<br />
"apv": "1",<br />
"devId": "", //devId must be blank when a user elects to Opt-Out.<br />
"uoo": "true"<br />
},<br />
</syntaxhighlight><br />
<br />
===== Privacy Information Template To Include In Opt-Out Screen =====<br />
<br />
<br />
Additionally, all applications must display the Nielsen privacy policy within their application, typically in the Settings/About screen of your application. The Nielsen privacy policy text is listed below.<br />
<br />
<blockquote><br />
'''''ABOUT NIELSEN MEASUREMENT'''''<br />
<br />
Television and the way we watch it have come a long way since Nielsen began measuring TV audiences in 1950. Today, the ability to watch our favorite shows at any time and on multiple devices amplifies the need for exceptionally adept and flexible audience measurement capabilities.<br />
<br />
Consumers are changing with the times, and the same goes for us. As technology continues to evolve and media companies try new ways to attract viewers, understanding what consumers are watching — and what they're watching on — is more important than ever. Today, viewing video is a personal and mobile experience — anytime and anywhere. Our capabilities provide relevant metrics that are necessary to inform successful marketing and programming and drive continued growth. As a global information and measurement leader, we are committed to protecting the privacy and security of the data we collect, process and use. While our digital measurement products are not used to identify you in any way, they help us and our clients measure and analyze how consumers engage with media across online, mobile and emerging technologies, and offer insights into consumer behavior.<br />
<br />
'''''YOUR CHOICES'''''<br />
<br />
Nielsen believes that you should have a choice about whether to contribute to our research and insights. To opt out or opt in to Nielsen measurement, please toggle your "Limit Ad Tracking" (or similar setting) on your device, or make the appropriate selection within the application's settings. If you have this app on more than one device, you will need to opt out of this app on each device. To learn more about our digital measurement products and your choices in regard to them, please visit http://www.nielsen.com/digitalprivacy.<br />
<br />
</blockquote><br />
'''''Disclosure Template'''''<br />
<br />
Additionally, you must update the App description information from the App Distribution Store with the Nielsen Measurement disclosure below:<br />
<br />
This app features Nielsen's proprietary measurement software which will allow you to contribute to market research, like Nielsen's TV Ratings. Please see http://www.nielsen.com/digitalprivacy for more information.<br />
<br />
== Testing ==<br />
Before providing an app build to Nielsen for testing, it is important to run validation checks once you have enabled debug logging.<br />
<br />
=== Payload Validation ===<br />
<br />
Ensure that all of the required payload data is populating while testing several videos. The following areas are critical to measurement:<br />
*devInfo<br />
*Asset metadata for both content, and ads<br />
*Events<br />
*Opt-Out status<br />
<br />
=== Player Events ===<br />
Review event calls:<br />
<br />
==== playhead ====<br />
*Playhead position updates every 10 seconds starting at position '0' for each new asset.<br />
*Final playhead position is sent on content, or ad before switching between assets.<br />
*Content metadata remains constant throughout an episode, or clip play.<br />
*Ad metadata is populated appropriately for each individual ad.<br />
*Playhead position update resumes for content after an ad break, and resets to 0 for each individual ad.<br />
*For scrubbing, last current position should be sent while scrubbing occurs, and the new position should also be sent where the user scrubs to.<br />
*Exiting a stream early should execute the last current position in a playhead update to receive accurate duration.<br />
*Upon pause, the current position should be sent, and playhead updates should stop incrementing until resume play occurs.<br />
<br />
==== complete ====<br />
*Check that the complete event executes upon content complete after the final playhead update is sent<br />
*Do not execute the complete event for ads<br />
<br />
==== delete ====<br />
*Check to see that the delete event occurs upon app exit, if the platform has the necessary exit callback events.<br />
<br />
==== GET Request Format ====<br />
*Ensure that the event payloads are formatted in JSON<br />
*Check to see that each of the Cloud API GET requests are properly encoded<br />
<br />
==== HTTP Response ====<br />
*Make sure that each of the Cloud API Get requests are received by the Nielsen Cloud API properly through use of the HTTP Response Code outputs enabled in console.<br />
<br />
==== Opt-Out ====<br />
*Test the "uoo" key gets populated accurately for both Opt-In and Opt-Out selections by validating the Cloud API events called after the user Opt-Out/Opt-In selection.<br />
*Test that the devId field is populated with a blank value if a user has elected to Opt-Out. For example: "devId": "",<br />
*If the device supports "Limit Ad Tracking" or has device "Opt-Out" settings, test that uoo=true, and that devId is set to a blank value if enabled in the device settings.<br />
<br />
== Go Live ==<br />
After your integration has been certified, you will need to: Change Endpoint and Disable Logging.<br />
<br />
'''Change Endpoint:''' You will need to update to the production endpoint:<br />
<br />
*Testing: <code>https://sandbox-cloudapi.imrworldwide.com/nmapi/v2/</code><br />
*Production: <code>https://cloudapi.imrworldwide.com/nmapi/v2/</code><br />
<br />
Your production URL structure should now be:<br />
<code>https://cloudapi.imrworldwide.com/nmapi/v2/[appid]/[sessionID]/a?b=[payload]</code><br />
<br />
'''Disable Logging:''' You can now disable debug logging</div>DiegoOrdonez