Difference between revisions of "DCR Denmark Video Android SDK"

From Engineering Client Portal

Line 1: Line 1:
{{Breadcrumb|}} {{Breadcrumb|Digital}} {{Breadcrumb|International DCR}}  {{CurrentBreadcrumb}}
+
{{Breadcrumb|}} {{Breadcrumb|Digital}} {{Breadcrumb|DCR & DTVR}}  {{CurrentBreadcrumb}}
 
[[Category:Digital]]
 
[[Category:Digital]]
  
 
== Overview ==
 
== Overview ==
 
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.
 
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.
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:
+
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  It supports a variety of Nielsen Measurement Products like Digital in TV Ratings ([[DCR & DTVR|DTVR]]), Digital Content Ratings ([[DCR & DTVR|DCR]]), 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:
 
*Application launch events and how long app was running
 
*Application launch events and how long app was running
 
*Time of viewing a sub section / page in the application.
 
*Time of viewing a sub section / page in the application.
Line 12: Line 12:
 
{| class="wikitable"
 
{| class="wikitable"
 
|-
 
|-
 +
! style="width: 30px;" |
 
! style="width: 15%;" | Item
 
! style="width: 15%;" | Item
 
! Description
 
! Description
 
! Source
 
! Source
|-
+
|-style="background-color:#d0f6f8;"
|| '''App ID (appid)''' || Unique ID assigned to the player/site and configured by product. || Provided by Nielsen
+
|| ☑ || ""App ID (apid)"" || Unique ID assigned to the player/site and configured by product. || Contact Nielsen
|-
+
|-style="background-color:#d0f6f8;"
|| '''Nielsen SDK''' || Includes SDK frameworks and '''sample implementation'''; ''See [[Android SDK Release Notes]]'' || '''[https://engineeringportal.nielsen.com/docs/Special:Downloads?region=DK Download]'''
+
|| ☑ || ""Nielsen SDK"" || Includes SDK frameworks and ""sample implementation""; "See [[Android SDK Release Notes]]" || [[Special:Downloads|Download]]
 
|}
 
|}
  
If need App ID(s) or our SDKs, feel free to reach out to us and we will be happy to help you get started.
+
== Step 1: Setting up your Android Development Environment  ==
Refer to [[Digital Measurement Onboarding]] guide for information on how to get a Nielsen App SDK and appid.
+
<br />
 
+
1) Ensure to unzip the Nielsen App SDK zip file and copy the "AppSdk.jar" into the app/libs folder on the App’s project. Add it as dependency.<br />
__TOC__
+
2) Add the following permissions on the project’s "AndroidManifest.xml" file.<br />
 
 
== Implementation ==
 
This guide covers implementation steps for Android Studio utilizing the Standard Nielsen SDK for DCR.
 
=== How to obtain the NielsenAppApi ===
 
The Nielsen AppSDK can either be downloaded directly or can be integrated directly within an application through the use of Gradle. We recommend using the Gradle-based integration whenever possible to ensure you maintain the most recent changes and enhancements to the Nielsen libraries.
 
* [[Digital_Measurement_Android_Artifactory_Guide|Select to obtain Gradle implementation guide]]
 
* [[Special:Downloads|Select to Download Directly]]
 
 
 
== Setting up your Development Environment  ==
 
 
 
=== Configuring Android Development Environment ===
 
*The Nielsen App SDK (located in the [https://engineeringportal.nielsen.com/docs/Special:Downloads Downloads section] of the website) class is the primary application interface to the Nielsen App SDK on Android.
 
*The Nielsen App SDK class is defined as the only public class belonging to the com.nielsen.app.sdk package.
 
 
 
'''Nielsen App SDK is compatible with Android OS versions 2.3+. Clients can control / configure the protocol to be used – HTTPS or HTTP to suit their needs.'''
 
 
 
The requirement for the Java ''AppSdk.jar'' library and the ''libAppSdk.so'' native library will depend on the type of host application that will make use of them.
 
* '''For Video player applications'''
 
** The Android OS hosting the App SDK should use a media player supporting HLS streaming (Android 3.0 and later will support it natively).
 
** If the player application uses a 3rd party media player implementing its own HLS, then the minimum Android version will be limited to version 2.3, since the SDK depends on Google Play support to work properly.
 
 
 
Once SDK is downloaded ensure to unzip the Nielsen SDK and copy the AppSdk.jar in your app (Android Studio) libs folder, then right click the AppSdk.jar and select '''Add As Library'''.
 
Ensure the AppSdk.jar file is added in 'build.grade (App Level) file.
 
* App SDK 1.2 provides support for x86, mips, and armeabi-7a architecture.
 
  
==== Google Play Services ====
 
Add the Google Play Services in the project,
 
Steps: Android Studio -> File -> Project Structure ->(In module selection) select App -> Dependencies (tab) -> Click "+" button and select <code>"com.google.android.gms:play-services"</code>.
 
Ensure it is added in build.gradle (App level) file
 
 
==== Manifest File ====
 
* Add the following permissions on the project’s ''AndroidManifest.xml'' file.
 
 
<syntaxhighlight lang="java">
 
<syntaxhighlight lang="java">
 
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
 
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
 
<uses-permission android:name="android.permission.INTERNET"/></syntaxhighlight>
 
<uses-permission android:name="android.permission.INTERNET"/></syntaxhighlight>
For more details to handle runtime permissions in Android versions, please visit [https://developer.android.com/training/permissions/requesting.html]. 
+
3) Add Google Play Services lib into dependencies as Nielsen AppSDK uses the following packages/classes from the Google Play service.
 
+
Libraries:
* In <code>AndroidManifest.xml </code>under <application> node add the following metadata
+
* com.google.android.gms:play-services
 
+
Requiered Google Play Service CLasses and Packages :
<syntaxhighlight lang="java"><meta-data
 
android:name="com.google.android.gms.version"
 
android:value="@integer/google_play_services_version"/></syntaxhighlight>
 
 
 
* App SDK checks to see if there is a Google service available and updated.
 
* If not available or updated, App SDK will not use this service when executing its functions and will make reference to missing imports and the app will not be compiled.
 
 
 
==== Library ====
 
Nielsen App SDK uses the following packages/classes from the Google Play service.
 
* google-play-services_lib
 
 
 
==== Classes/package ====
 
 
* com.google.android.gms.ads.identifier.AdvertisingIdClient;
 
* com.google.android.gms.ads.identifier.AdvertisingIdClient;
 
* com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
 
* com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
Line 83: Line 41:
 
* com.google.android.gms.common.GooglePlayServicesNotAvailableException;
 
* com.google.android.gms.common.GooglePlayServicesNotAvailableException;
  
== SDK Initialization ==
+
4) Once the files are in place, import com.nielsen.app.sdk to the java source code and start accessing the public interface.<br />
 +
<syntaxhighlight lang="java">import com.nielsen.app.sdk.*;</syntaxhighlight><br />
 +
<br />
 +
 
 +
Notes:
 +
*The Nielsen App SDK (located in the "com.nielsen.app.sdk" package) class is the primary application interface to the Nielsen App SDK on Android.
 +
*The Nielsen App SDK class is defined as the only public class belonging to the com.nielsen.app.sdk package.
 +
*Nielsen App SDK is compatible with Android OS versions 2.3+.
 +
*Clients can control / configure the protocol to be used – HTTPS or HTTP to suit their needs.
 +
*The Android OS hosting the App SDK should use a media player supporting HLS streaming (Android 4.0 and later will support it natively).
 +
*If the player application uses a 3rd party media player implementing its own HLS/MPEG-DASH stack, then the minimum Android version will be limited to version 2.3, since the SDK depends on Google Play support to work properly.
 +
 
 +
== Step 2: Create SDK Instance ==
 
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. (Version 4.0 for Android)
 
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. (Version 4.0 for Android)
 +
 +
* A maximum of four SDK instances per appid are supported. When a fifth SDK instance is launched, the SDK will return “nil” from [[initWithAppInfo:delegate:]]
 +
* When four SDK instances exist, you must destroy an old instance before creating a new one.
  
 
The following table contains the list of arguments that can be passed via the AppInfo JSON schema.
 
The following table contains the list of arguments that can be passed via the AppInfo JSON schema.
 
{| class="wikitable"
 
{| class="wikitable"
 
|-
 
|-
! Parameter / Argument !! Description !! Source !! Required? !! Example
+
! Parameter / Argument !! Description !! Source !! Required/Obligatory? !! Example
 
|-
 
|-
| 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
+
| appid || Unique id for the application assigned by Nielsen. It is GUID data type.|| Nielsen-specified || || PXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
 +
|-
 +
| appname || Name of the application || Client-defined || No || Nielsen Sample App
 +
|-
 +
| appversion || Current version of the app used || Client-defined || No || "1.0.2"
 
|-
 
|-
 
| nol_devDebug || Enables Nielsen console logging. Only required for testing
 
| nol_devDebug || Enables Nielsen console logging. Only required for testing
 
|| Nielsen-specified || Optional || "DEBUG"
 
|| Nielsen-specified || Optional || "DEBUG"
 
|}
 
|}
 +
<br />
  
== Debug flag for development environment ==
+
1) AppSDK() is no longer a singleton object and should be created as below.
Player application developers / integrators can use Debug flag to check whether an App SDK API call made is successful. To activate the Debug flag,
+
<syntaxhighlight lang="java">
Pass the argument <code>@"nol_devDebug":@"INFO"</code>, in the JSON string . The permitted values are:
+
try{
 
 
* '''INFO''': Displays the API calls and the input data from the application (validate player name, app ID, etc.). It can be used as certification Aid.
 
* '''WARN''': Indicates potential integration / configuration errors or SDK issues.
 
* '''ERROR''': Indicates important integration errors or non-recoverable SDK issues.
 
* '''DEBUG''': Debug logs, used by the developers to debug more complex issues.
 
 
 
Once the flag is active, it logs each API call made and the data passed. The log created by this flag is minimal.
 
<blockquote>'''Note''': DO NOT activate the Debug flag in a production environment.</blockquote>
 
 
 
==== Sample SDK Initialization Code ====
 
[[AppSDK()]] is no longer a singleton object and should be initialized as below.
 
 
 
'''Initialization of App SDK object through a JSON object'''
 
<syntaxhighlight lang="java">try
 
{
 
 
   // Prepare AppSdk configuration object (JSONObject)
 
   // Prepare AppSdk configuration object (JSONObject)
 
   JSONObject appSdkConfig = new JSONObject()
 
   JSONObject appSdkConfig = new JSONObject()
           .put("appid", "PDA7D5EE6-B1B8-XXXX-XXXX-2A788BCXXXCA")
+
           .put("appid", "P83EDEF8F-7269-429B-BAA6-A7843F8AFA03")
 +
          .put("appversion", "1.0")
 +
          .put("appname", "Sample App Name")
 
           .put("nol_devDebug", "DEBUG"); // only for debug builds
 
           .put("nol_devDebug", "DEBUG"); // only for debug builds
  
// Pass appSdkConfig to the AppSdk constructor
+
        // Pass appSdkConfig to the AppSdk constructor
mAppSdk = new AppSdk(appContext, appSdkConfig, appSdkListener);
+
        mAppSdk = new AppSdk(appContext, appSdkConfig, this);
 
}
 
}
catch (JSONException e)
+
catch (JSONException e){
{
+
        Log.e(TAG, "Couldn’t prepare JSONObject for appSdkConfig", e);
  Log.e(TAG, "Couldn’t prepare JSONObject for appSdkConfig", e);
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
Here, <code>appContext</code> is the App context object and <code>appSdkConfig</code> is JSON object for holding the parameters (<code>appid</code>) the App passes to the Nielsen App SDK via a JSON string. The appid is obtained from Nielsen operational support and is unique to the app.
+
2) implement IAppNotifier into your activity like
 +
<syntaxhighlight lang="java">public class MainActivity extends AppCompatActivity implements IAppNotifier
 +
</syntaxhighlight>
 +
3) implement callback
 +
<syntaxhighlight lang="java">@Override
 +
public void onAppSdkEvent(long timestamp, int code, String description){
 +
  Log.d(TAG, "SDK callback onAppSdkEvent " + description);
 +
}
 +
</syntaxhighlight>
 +
 
 +
So whole Activity will look like
 +
<syntaxhighlight lang="java">
 +
package com.example.josefvancura.nlsdemotmp;
 +
 
 +
import android.content.Context;
 +
import android.support.v7.app.AppCompatActivity;
 +
import android.os.Bundle;
 +
import android.util.Log;
 +
 
 +
import com.nielsen.app.sdk.*;
  
 +
import org.json.JSONException;
 +
import org.json.JSONObject;
  
The integration of Nielsen App SDK will depend on type of client app.<br />
+
public class MainActivity extends AppCompatActivity implements IAppNotifier {
* Ensure that SDK files (AppSdk.jar and libAppSdk.so [App SDK 1.2 Only]) are included under the App’s project and the App SDK is linked to the App (the setting to link App SDK to the App can be found on property page of the App’s project).
 
  
<!-- == Initializing the Nielsen AppSDK to measure the Viewability ==
+
    private AppSdk mAppSdk = null;
The integrator to support the viewability metrics in the application has to provide a tag value of the player view to let Nielsen AppSDK know that there is a player that needs to be tracked. It’s called the ‘containerId’ and it should be passed in application info dictionary as string while initializing the Nielsen AppSDK.
+
    private String TAG = "MainActivity";
  
==== Android ====
+
    @Override
{| class="wikitable"
+
    protected void onCreate(Bundle savedInstanceState) {
|-
+
        super.onCreate(savedInstanceState);
! # !! Parameter Name !! Description !! Supported Values !! Example
+
        setContentView(R.layout.activity_main);
|-
+
 
| 1 || containerId ||View ID of the UI element used as player view in application. getId() method of View class can be used to get this value. ||A positive integer used to identify the view. || 2131558561
+
        Context context = getApplicationContext();
|}
+
 
 +
        try{
 +
            // Prepare AppSdk configuration object (JSONObject)
 +
            JSONObject appSdkConfig = new JSONObject()
 +
                    .put("appid", "P83EDEF8F-7269-429B-BAA6-A7843F8AFA03")
 +
                    .put("appversion", "1.0")
 +
                    .put("appname", "Sample App Name")
 +
                    .put("nol_devDebug", "DEBUG"); // only for debug builds
 +
 
 +
            // Pass appSdkConfig to the AppSdk constructor
 +
            mAppSdk = new AppSdk(context, appSdkConfig, this ); // Notifier - activity implements IAppNotifier, callback in onAppSdkEvent()
 +
 
 +
        }
 +
        catch (JSONException e){
 +
            Log.e(TAG, "Couldn’t prepare JSONObject for appSdkConfig", e);
 +
        }
  
==== iOS ====
+
    }
{| class="wikitable"
 
|-
 
! # !! Parameter Name !! Description !! Supported Values !! Example
 
|-
 
| 1 || containerId ||The tag of the UIView that represents the Player View ||The string value representing the NSInteger value with maximum value of NSIntegerMax that is related on 32- or 64-bit applications. || "100" <br> "2131558561"
 
|}
 
  
For iOS it is required to link additional frameworks that are needed for viewability engine:<br>
+
    @Override
<code>JavaScriptCore.framework</code> <br>
+
    public void onAppSdkEvent(long timestamp, int code, String description) {
<code>WebKit.framework</code>
+
        Log.d(TAG, "SDK callback onAppSdkEvent " + description);
 +
    }
  
The Nielsen AppSDK uses a tracking WebView (TWV) approach.  For more information on Viewability, please refer to [https://engineeringportal.nielsen.com/docs/Implementing_Viewability_with_AppSDK Implementing Viewability with AppSDK.]
+
}
-->
+
</syntaxhighlight>
  
== APP SDK Error & Event Codes ==
+
==== APP SDK Error & Event Codes ====
 
To view the Error and Event codes for iOS and Android, please review the [[APP SDK Event Codes|App SDK Event Code]] Reference page.
 
To view the Error and Event codes for iOS and Android, please review the [[APP SDK Event Codes|App SDK Event Code]] Reference page.
  
== Configure Payload ==
+
==== Life cycle of SDK instance ====
=== Handling JSON Metadata ===
+
Life cycle of SDK instance includes four general states:
All the SDK methods handles only two types of objects: NSString, NSDictionary. The parameters passed must be either a JSON formatted string or a NSDictionary object. The JSON passed in the SDK must be well-formed.
+
# ""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"".
 +
# ""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.
 +
# ""Processing state"" – The SDK instance is processing playing information. API calls "play" and "loadMetadata" move the SDK instance into this state. In this state, the SDK instance will be able to process the API calls (see below)
 +
# ""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.
 +
## Initialization fails
 +
## <code>appDisableApi</code> is called
 +
<syntaxhighlight lang="objective-c">
 +
@property (assign) BOOL appDisableApi;
 +
</syntaxhighlight>
 +
 
 +
== Step 3: Create Metadata Objects ==
 +
The parameters passed must be either a JSON formatted string or a NSDictionary object. The JSON passed in the SDK must be well-formed.
 
* NSDictionary object
 
* NSDictionary object
 
** If an object of unexpected type is passed to the method, the error message will be logged.
 
** If an object of unexpected type is passed to the method, the error message will be logged.
Line 171: Line 176:
 
* JSON value must be string value.
 
* JSON value must be string value.
 
** This includes boolean and numeric values. For example, a value of true should be represented with "true", number value 123 should be "123".
 
** This includes boolean and numeric values. For example, a value of true should be represented with "true", number value 123 should be "123".
** All the Variable Names like appid, appname, sfcode, dataSrc, title, type etc. are case-sensitive. Use the correct variable name as specified in the documentation.
+
** All the Variable Names like appid, appname, dataSrc, title, type etc. are case-sensitive. Use the correct variable name as specified in the documentation.
* JSON string can be prepared using either raw NSString or serialized NSDictionary.
+
* JSON string can be prepared using either raw NSString or serialized NSDictionary.<br />
<syntaxhighlight lang="java">
 
JSONObject contentMetadata = new JSONObject()
 
//SDK Metadata
 
    .put("type", "content")
 
    .put("assetid", "vid345-67483")
 
    .put("program", "Program Name")
 
    .put("title", "Program S3, EP1")
 
    .put("length", "3600")
 
    .put("isfullepisode", "yes")
 
    .put("airdate", "20200321 20:00:00")
 
    .put("adloadtype", "2")
 
    .put("segB", "CustomSegmentValueB")  //optional
 
    .put("segC", "CustomSegmentValueC")  //optional 
 
    .put("crossId1", "Standard Episode ID")  //optional 
 
    .put("crossId2", "Content Originator")  //optional 
 
</syntaxhighlight>
 
  
=== Content metadata ===
 
Content metadata should remain constant throughout the entirety of an episode/clip including when ads play.
 
{{DCR Content Metadata}}
 
  
=== Ad Metadata ===
+
=== Create channelName Metadata ===
The Ad Metadata (if applicable) should be passed for each individual ad.
+
channelName should remain constant throughout the completion of an episode or live stream.
 +
<br />
 
{| class="wikitable"
 
{| class="wikitable"
 
|-
 
|-
! Keys !! Description !! Values !! Required
+
! Key !! Description !! Values !! Required
 
|-
 
|-
| type || type of Ad || <code>'preroll'</code>,  <code>'midroll'</code>,  <code>'postroll'</code> <br> <code>'ad'</code>  - If specific type can not be identified.|| ✓
+
| channelName || Any string representing the channel/stream || custom || ✓
 
|-
 
|-
| assetid || unique ID assigned to Ad || custom || ✓
 
 
|}
 
|}
  
=== Example Ad Object ===
+
=== Create Content Metadata ===
<syntaxhighlight lang="javascript">
+
Content metadata should remain constant throughout the entirety of an episode/clip including when ads play.
// create ad object
 
"ad": {
 
  "type": "preroll",
 
  "assetid": "AD-ID123"
 
}
 
</syntaxhighlight>
 
  
== Sequence of Calls ==
+
<br/>
=== play ===
+
<blockquote> program and title metadata values should be passed to SDK as UTF-8 strings. </blockquote>
Use [[DCR_Video_APP_SDK#play|play]] to pass the channel descriptor information through channelName parameter when the user taps the '''Play''' button on the player.
 
<syntaxhighlight lang="java">    public void play(JSONObject channelInfo);</syntaxhighlight>
 
  
=== loadMetadata ===
 
<syntaxhighlight lang="java">public void loadMetadata(JSONObject contentMetadata);</syntaxhighlight>
 
 
=== playheadPosition ===
 
<syntaxhighlight lang="java">
 
public void setPlayheadPosition(long position)
 
</syntaxhighlight>
 
 
=== stop ===
 
<syntaxhighlight lang="java">public void stop()</syntaxhighlight>
 
 
=== end ===
 
When content stop is initiated and content cannot be resumed from the same position (it can only be restarted from the beginning of stream).
 
<syntaxhighlight lang="java">public void end()</syntaxhighlight>
 
 
== Configure API Calls ==
 
 
=== Sample API Sequence ===
 
A Sample API sequence could follow this flow:
 
 
{| class="wikitable"
 
{| class="wikitable"
 
|-
 
|-
! Type !! Sample code !! Description
+
! Keys !! Description !! Values !! Required
|-
 
|On App Start||<code>[nielsenMeter loadMetadata: contentMetadata];</code> || // contentMetadata Object contains the JSON metadata for the impression
 
 
|-
 
|-
| rowspan="2" | Start of stream || <code>[nielsenMeter play];</code> || // call at start of each new stream
+
| type || type of asset || "content" for main content ||
 
|-
 
|-
| <code>[nielsenMeter loadMetadata: contentMetadataObject];</code> || // contentMetadataObject contains the JSON metadata for the content being played
+
| assetid || unique ID assigned to asset || custom<br>(no [[Special Characters]]) ||
 
|-
 
|-
| Content || <code>[nielsenMeter setplayheadPosition: position];</code> || // playheadPosition is position of the playhead while the content is being played
+
| program || name of program || custom ||
 
|-
 
|-
| End of Stream || <code>[nielsenMeter end];</code> || // Content playback is completed.
+
| title || episode name || custom ||
|}
 
 
 
=== SDK Events ===
 
{| class="wikitable"
 
 
|-
 
|-
! Event !! Parameter !! Description
+
| length || length of content in seconds || Length of content in seconds (86400 seconds for live stream (24/7). For Event-Live streams planned length. For VoD video length)|| ✓
 
|-
 
|-
| 'play' || || Call at start of each new stream
+
| airdate || the original date (local time with hh:mm:ss as 24h time stamp) content became available. This date does not change if the content is rebroadcasted on linear TV. || YYYYMMDD HH:MI:SS (if not known set it to eg. "19700101 00:00:00") || ✓
 
|-
 
|-
| 'loadMetadata' || content/ad metadata object || Needs to be called at the beginning of each asset
+
| isfullepisode || full episode flag ||
 +
*"y", "yes", full episode (full content for a program)
 +
*"n", "no", non full episode (only one part of the entire content for a program , i.e. preview, sub-episodes)
 +
||
 +
 
|-
 
|-
| 'setPlayheadPosition' || playhead position as integer<br/>
+
|adloadtype || type of Ad load:
VOD: || current position in seconds <br/>
+
* 1) Linear - matches TV ad load
Live: current Unix timestamp (seconds since Jan-1-1970 UTC) <br/>
+
* 2) Dynamic - Dynamic Ad Insertion (DAI)
Note: 'setPlayheadPosition' has to be called every second
 
 
||
 
||
Pass playhead position every second during playback
+
||
|-
+
| 'stop' || playhead position || Call during any interruption to content or Ad playback and at the end of each Ad.
 
|-
 
| 'end' || playhead position in seconds || Call when the current video asset completes playback and pass the playhead position. <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.
 
 
|}
 
|}
<blockquote>Note: For livestream, send the Unix timestamp; for VOD send the time in seconds as integer. The final playhead position must be sent for the current asset being played before calling <code>'''stop'''</code>, <code>'''end'''</code> or<code> '''loadmetadata'''</code>,.</blockquote>
 
  
=== Life cycle of SDK instance ===
+
=== Create Ad Metadata (optional for DR) ===
Life cycle of SDK instance includes four general states:
+
The Ad Metadata (if applicable) should be passed for each individual ad.
# '''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'''.
 
# '''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.
 
# '''Processing state''' – The SDK instance is processing playing information. The <code>'''play'''</code> and <code>'''loadMetadata''' </code> calls move the SDK instance into this state. In this state, the SDK instance will be able to process the following calls.
 
## <code>'''setplayheadPosition'''</code> – Call this API every one second when playhead position timer is fired.  If a LIVE event, use the current Unix timestamp (seconds since Jan-1-1970 UTC).
 
## <code>'''stop'''</code> – Call this API when the playback is paused, switches between content and ad (within the same content playback) or encounters interruptions.
 
## <code>'''end'''</code> – Call when content completes. When called, the SDK instance exits from Processing state.
 
# '''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.
 
## Initialization fails
 
## <code>'''appDisableApi'''</code> is set to <code>true</code>
 
  
<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.
+
Note: All metadata values should be passed as UTF-8 strings.
 
 
'''Note''': In case of any interruptions during playback due to alarm, calendar, call, flight mode, Wi-Fi toggle, channel change, etc., call [[stop]] to stop the measurement.
 
* As soon as the playback resumes, <code>'''loadMetadata''' </code> and  <code>'''playheadPosition'''</code> </blockquote>
 
 
 
=== API Call Sequence ===
 
==== Use Case 1: Content has no Advertisements ====
 
Call [[play()]] at start of stream
 
 
 
Call [[loadMetadata()]] with JSON metadata for content as below.
 
<syntaxhighlight lang="json">{
 
  "type": "content",
 
  "assetid": "vid345-67483",
 
  "program": "ProgramName",
 
  "title": "Program S3, EP1",
 
  "length": "3600",
 
  ...
 
}</syntaxhighlight>
 
Call [[setPlayheadPosition()]] every one second until a pause / stop.
 
Use the sample API sequence below as a reference to identify the specific events that need to be called during content playback without ads.
 
 
{| class="wikitable"
 
{| class="wikitable"
 
|-
 
|-
! Type !! Sample code !! Description
+
! Keys !! Description !! Values !! Required
 
|-
 
|-
| rowspan="2" | Start of stream || <code>mAppSdk.play(); </code> || // Call at start of each new stream
+
| type || type of Ad || <code>"preroll"</code>,  <code>"midroll"</code>,  <code>"postroll"</code> <br> <code>"ad"</code> - If specific type can not be identified.||
 
|-
 
|-
| <code>mAppSdk.loadMetadata(contentMetaDataObject);</code> || // contentMetadataObject contains the JSON metadata for the content being played
+
| assetid || unique ID assigned to Ad || custom<br>(no [[Special Characters]]) ||
|-
 
| Content || <code>mAppSdk.setPlayheadPosition(playheadPosition);</code> || // position is position of the playhead while the content is being played
 
|-
 
| Interruption || <code>mAppSdk.stop();</code> || // call stop when content playback is interrupted
 
|-
 
| rowspan="2" | Resume Content || <code>mAppSdk.loadMetadata(contentMetaDataObject);</code>  || // Call loadMetadata and pass content metadata object when content resumes
 
|-
 
|<code>mAppSdk.setPlayheadPosition(playheadPosition);</code> || // continue pasing playhead position every second starting from position where content is resumed
 
|-
 
| End of Stream || <code>mAppSdk.end();</code> || // Content playback is completed.
 
 
|}
 
|}
  
==== Use Case 2: Content has Advertisements ====
+
=== MetaData Example ===
Call [[play()]]
+
<syntaxhighlight lang="java">
 +
JSONObject channelInfo = new JSONObject()
 +
    .put("channelname","My Channel Name 1")
  
Call [[loadMetadata()]] with JSON metadata for ad as below.
+
JSONObject contentMetadata = new JSONObject()
<syntaxhighlight lang="json">{
+
  .put("assetid", "unique_content_id")
   "type": "preroll",
+
   .put("type", "content")
   "assetid": "ad-123"
+
  .put("program", "program name")
}</syntaxhighlight>
+
   .put("title", "episode title")
<blockquote>Note: In case the individual ad details are not available, send ad pod (presence) details through the [[loadMetadata]] and playhead position through [[playheadPosition]].</blockquote>
+
  .put("length", "length in seconds")
 +
  .put("airdate", "20200713 10:22:00")
 +
  .put("isfullepisode", "y")
 +
  .put("adloadtype", "2");
  
Call [[setPlayheadPosition()]] every one second until a pause / stop / another [[loadMetadata()]] is called. Playhead should be passed for the entire duration of ad pod, if the ad pod details are passed as part of [[loadMetadata()]].
+
JSONObject adMetadata = new JSONObject()
 +
  .put("assetid", "unique_postroll_ad_id")
 +
  .put("type", "postroll");
  
'''Ad Content'''
 
<syntaxhighlight lang="java">    long pos = mAdPlayer.videoPosition() / 1000;
 
  if (mAppSdk != null)
 
  {
 
    mAppSdk.setPlayheadPosition(pos);
 
  }</syntaxhighlight>
 
<blockquote>Note: The playhead positions for ad and content should be maintained separately.</blockquote>
 
 
The sample API sequence can be used as a reference to identify the specific events that need to be called during content and ad playback.
 
{| class="wikitable"
 
|-
 
! Type !! Sample code !! Description
 
|-
 
| rowspan="2" | Start of stream || <code>mAppSdk.play(); </code> || // stream starts
 
|-
 
| <code>mAppSdk.loadMetadata(contentMetaDataObject);</code> || // contentMetadataObject contains the JSON metadata for the content being played
 
|-
 
| rowspan="3" | Preroll || <code>mAppSdk.loadMetadata(prerollMetadataObject);</code> || // prerollMetadataObject contains the JSON metadata for the preroll ad
 
|-
 
| <code>mAppSdk.setPlayheadPosition(playheadPosition);</code> || // position is position of the playhead while the preroll ad is being played
 
|-
 
| <code>mAppSdk.stop();</code> || // Call stop after preroll occurs
 
|-
 
| rowspan="2" | Content || <code>mAppSdk.loadMetadata(contentMetaDataObject);</code> || // contentMetadataObject contains the JSON metadata for the content being played
 
|-
 
| <code>mAppSdk.setPlayheadPosition(playheadPosition);</code> || // position is position of the playhead while the content is being played
 
|-
 
| rowspan="3" | Midroll || <code>mAppSdk.loadMetadata(midrollMetaDataObject);</code> || // midrollMetadataObject contains the JSON metadata for the midroll ad
 
|-
 
| <code>mAppSdk.setPlayheadPosition(playheadPosition);</code> || // position is position of the playhead while the midroll ad is being played
 
|-
 
| <code>mAppSdk.stop();</code> || // Call stop after midroll occurs
 
|-
 
| rowspan="2" | Content Resumes || <code>mAppSdk.loadMetadata(contentMetaDataObject);</code> || // contentMetadataObject contains the JSON metadata for the content being played
 
|-
 
| <code>mAppSdk.setPlayheadPosition(playheadPosition);</code> || // position is position of the playhead while the content is being played
 
|-
 
|  rowspan="1" | End of Stream || <code>mAppSdk.end();</code> || // Call end() at the end of content
 
|-
 
| rowspan="3" | Postroll || <code>mAppSdk.loadMetadata(postrollMetaDataObject);</code> || // postrollMetadataObject contains the JSON metadata for the postroll ad
 
|-
 
| <code>mAppSdk.setPlayheadPosition(playheadPosition);</code> || // position is position of the playhead while the postroll ad is being played
 
|-
 
| <code>mAppSdk.stop();</code> || // Call stop after postroll occurs
 
|}
 
 
<blockquote>Note: Each Ad playhead should reset or begin from 0 at ad start. When content has resumed following an ad break, playhead position must continue from where previous content segment was left off.</blockquote>
 
 
== Handling Foreground and Background states ==
 
There are a few approaches to managing the Foreground and Background states of an app available to use for state measurement.
 
* Utilizing the Androidx LifeCycleObserver (The recommended approach starting sdk version 7.1.0.0+)
 
* Utilizing the [[DCR_Video_Android_SDK#The_SdkBgFgDetectionUtility_class|SdkBgFgDetectionUtility]] class
 
* Adding a tag to the Manifest XML
 
* Manual Management
 
=== The LifeCycleObserver ===
 
AndroidX replaces the original support library APIs with packages in the androidx namespace, and Android Studio 3.2 and higher provides an automated migration tool. (Select '''Refactor> Migrate to AndroidX''' from the menu bar.)
 
 
Starting with version 7.1.0, with AndroidX support, an additional utility is provided in the AppSDK - application background/foreground state detection by the AppSdk leveraging the Android Architecture component "LifeCycleObserver".
 
 
The AppSdk is now capable of detecting the application UI visibility state transitions between background and foreground, without forcing the applications to register for AppSdk's AppSdkApplication class, which is responsible for handling the detection of application background/foreground state transitions at present.
 
 
<blockquote>Please note, that if you already have an app designed that utilizes the depreciated SdkBgFgDetectionUtility Class, the AppSDK will ignore any calls to these methods if it can utilize the LifeCycleObserver.  LifeCycleObserver based auto detection will take precedence. </blockquote>
 
==== Adding the AndroidX dependency ====
 
In order to make use of the app background/foreground state transition auto detection feature of AndroidX AppSdk, the app gradle file needs the androidx dependency. The AppSdk API calls - <code>appInForeground()</code> and <code>appInBackground()</code>  will still be respected by AppSdk by executing the old AppSdk flow of handling "app in foreground" and "app in background" states as is.
 
 
==== Using the LifeCycle Extension ====
 
The following androidx dependency is required in the app gradle file:
 
<syntaxhighlight lang="java">
 
implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"
 
</syntaxhighlight>
 
 
<blockquote>
 
If you would like to take advantage of this auto detection feature of AppSdk at the very initial stage (e.g. splash screen or at of app launch time), before the AppSdk is initialized, can do so by calling the following newly introduced AppSdk public api, passing the application context :
 
<syntaxhighlight lang="java">
 
public static void registerLifeCycleObserver(Context applicationContext)
 
 
</syntaxhighlight>
 
</syntaxhighlight>
</blockquote>
 
  
==== Log messages for the new auto detection ====
+
== Step 4: Start the Measurement ==
  
When the AppSdk app successfully registers for the LifeCycleObserver : <code>Registered LifeCycleObserver for App Background/Foreground auto-detection</code>
+
=== Overview of SDK API Calls ===
  
* When the app enters the foreground state :<code>App is in foreground, auto detected by AppSDK</code>
+
==== play ====
* When the app enters the background state :<code>App is in background, auto detected by AppSDK</code>
+
The play method prepares the SDK for reporting once an asset has loaded and playback has begun. Use play to pass the channel descriptor information through channelName parameter when the user taps the ""Play"" button on the player. Call play only when initially starting the video.
* If the client app doesn't have the "androidx" gradle dependency and AppSdk fails to register LifeCycleObserver :<code>AndroidX LifecycleObserver can not be observed. Please use androidx dependency to activate SDK auto-detection of app background/foreground state.</code>
+
{{ExampleCode|
* When the appInForeground() is explicitly called while LifeCycleObserver auto detection is active :<code>Ignoring the appInBackground() call, as the App Background/Foreground auto-detection is active. The current state is - foreground</code>
+
|Java = <syntaxhighlight lang="java">mAppSdk.play(JSONObject channelInfo);</syntaxhighlight>
* When the appInBackground() is explicitly called while LifeCycleObserver auto detection is active :<code>Ignoring the appInBackground() call, as the App Background/Foreground auto-detection is active. The current state is - background</code>
+
}}
  
=== The SdkBgFgDetectionUtility class ===
+
==== loadMetadata ====
Foreground/Background state measurement is a requirement of Nielsen AppSDK implementation which is especially crucial for static measurement. It may be implemented in multiple ways for Android. This includes
+
Needs to be called at the beginning of each asset, pass JSON object for relevant content or ad. Make sure to pass as 1st loadMetadata for content at the begining of playlist - see below API call sequence examples.
* Enable the Nielsen SDK to measure background/foreground state by makingthe relevant update to the AndroidManifest.
+
{{ExampleCode|
* Integrate Nielsen’s SdkBgFgDetectionUtility class within your Custom Application Class.
+
|Java = <syntaxhighlight lang="java">mAppSdk.loadMetadata(JSONObject contentMetadata);</syntaxhighlight>
* Custom implementation of the required methods within your application.
+
}}
  
==== ForeGround/Background Measurement via AndroidManifest ====
+
==== playheadPosition ====
The simplest way to measure the app background/foreground state is to add the following application tag to the Manifest XML. Integrating this into the Manifest XML will enable the SDK to measure app state directly. This approach is supported for Android 4.0 and up only; it requires that the application class is not in use for some other purpose.
+
Pass playhead position every second during playback. for VOD: pass current position in seconds. for Live: current Unix timestamp (seconds since Jan-1-1970 UTC) - if it is possible to seek back in Live content, then pass related Unix time (not current). Pass whole number that increments only by 1 like 1,2,3..
<syntaxhighlight lang="java">
+
{{ExampleCode|
<application android:name="com.nielsen.app.sdk.AppSdkApplication">
+
|Java = <syntaxhighlight lang="java">
 +
mAppSdk.setPlayheadPosition(long videoPositon);
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
}}
  
==== Using the Android SdkBgFbDetectionUtility Class ====
+
==== stop ====
For developers who are already using the application class, it is recommended that background/foreground state is implemented using the  [https://engineeringportal.nielsen.com/docs/Android_Background_Foreground SdkBgFgDetectionUtility class].  The  [https://engineeringportal.nielsen.com/docs/Android_Background_Foreground SdkBgFgDetectionUtility class] is compatible with Android 4+ and has been made available to Nielsen clients. (You will need to copy/paste the code provided into a file).
+
Call when
 
+
* content or ads complete playing
==== Manual Background/ForeGround State Management ====
+
* when a user pauses playback
In cases where the developer is not able to use the AndroidManifest.xml solution nor the Nielsen provided  [https://engineeringportal.nielsen.com/docs/Android_Background_Foreground SdkBgFgDetectionUtility class] the developer will need to manually identify the change of state through the application and call the respective API (appInForeground() or appInBackground()) to inform the SDK regarding the change of state from background to foreground or foreground to background.
+
* upon any user interruption scenario - see bellow chapter Interruption scenario
 
 
The SDK is informed about app state using the below methods.
 
<syntaxhighlight lang="java">
 
AppLaunchMeasurementManager.appInForeground(getApplicationContext());
 
AppLaunchMeasurementManager.appInBackground(getApplicationContext());
 
</syntaxhighlight>
 
Within the lifecycle of individual activities, onResume() and onPause() are best suited to providing indication of the app state.
 
  
 +
{{ExampleCode|
 +
|Java = <syntaxhighlight lang="java">mAppSdk.stop();</syntaxhighlight>
 +
}}
  
Correct measurement of the foreground/background state is crucial to Static App measurement within Nielsen Digital Content Ratings (DCR).
+
==== end ====
== Using the NielsenAppSDKJSHandler ==
+
Call when the content asset completes playbackStops measurement progress.
There could be a scenario in which a browser page, that is already tagged with the Nielsen BSDK, needs to be loaded via a webviewIn this situation it is recommended to use the '''NielsenAppSDKJSHandler''' which will allow communication between the AppSDK and the BSDK.  
+
{{ExampleCode|
<br>
+
|Java = <syntaxhighlight lang="java">mAppSdk.end();</syntaxhighlight>
 +
}}
 +
<br />
  
'''This feature is supported in versions 7.2 and above.'''
 
<br>
 
  
=== Implementation ===
+
=== Start the Measurement ===
* Make sure you have the latest AppSdk.jar from Nielsen
+
In order to start the measurement, follow the 3 first steps below for Content without Ads. When terminating the Content playback call <code>end</code> to terminate the Content Measurement for the given asset.
* Enable the javascript on webview using below lines
 
<br>
 
<syntaxhighlight lang=java>
 
WebSettings webSetting = webView.getSettings();
 
webSetting.setJavaScriptEnabled(true);
 
</syntaxhighlight>
 
<br>
 
* Add  NielsenAppSDKJSHandler instance as javascript interface to webview with name “NielsenAppSDK” like below snippet
 
<br>
 
<syntaxhighlight lang=java>
 
webView.addJavascriptInterface(new NielsenAppSDKJSHandler(getApplicationContext()), "NielsenAppSDK");
 
</syntaxhighlight>
 
<br>
 
This will enable listening to BSDK api calls within the APPSDK.  Please make sure your Technical Account Manager is aware that you wish to implement this method so a configuration file can be modified on the Nielsen servers; however, there are '''no changes required to the Browser page'''.
 
  
==== Example:====
+
{| class="wikitable"
The below is an example of opening a webview with the NielsenApp[SDKJSHandler
+
|-
<syntaxhighlight lang=java>
+
! Playlist !! Sample code !! Description
      WebSettings webSetting = webView.getSettings();
+
|-
      webView.getSettings().setDomStorageEnabled(true);
+
| rowspan="2" | 1. Start of stream || <code>play(channelName)</code> || channelName contains JSON metadata of channel/video name being played
      webSetting.setJavaScriptEnabled(true);
+
|-
      webView.addJavascriptInterface(new NielsenAppSDKJSHandler(getApplicationContext(), ""), "NielsenAppSDK");
+
| <code>loadMetadata(contentMetadataObject)</code> || contentMetadataObject contains the JSON metadata for the content being played
      String url = "https://nielsen.com.index.htm";
+
|-
      webView.loadUrl(url);
+
| 2. Content is playing || <code>playheadPosition(position)</code> || playheadPosition is position of the playhead while the content is being played
</syntaxhighlight>
+
|-
<br>
+
| 3. End of Stream || <code>end</code> || Content playback is completed.
<code>AndroidManifest.xml</code> may need the following permissions set:
+
|}
<syntaxhighlight lang=java>
 
    <uses-permission android:name="android.permission.INTERNET"/>
 
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 
</syntaxhighlight>
 
<br>
 
  
== Interruptions during playback ==
+
== Step 5: Stop/Resume the Measurement for video Playback Interruptions ==
As part of integrating Nielsen App SDK with the player application, the Video app developer needs to handle the following possible interruption scenarios:
+
As part of integrating Nielsen App SDK with the player application, the Audio / Video app developer needs to handle the following possible interruption scenarios:
 
* Pause / Play
 
* Pause / Play
 
* Network Loss (Wi-Fi / Airplane / Cellular)
 
* Network Loss (Wi-Fi / Airplane / Cellular)
Line 503: Line 326:
 
* Alarm Interrupt
 
* Alarm Interrupt
 
* Content Buffering
 
* Content Buffering
* Device Lock / Unlock  
+
* Device Lock / Unlock (Video players only, not for Audio players)
* App going in the Background/Foreground
+
* App going in the Background/Foreground (Video players only, not for Audio players)
 
* Channel / Station Change Scenario
 
* Channel / Station Change Scenario
 
* Unplugging of headphone
 
* Unplugging of headphone
 
In case of encountering one of the above interruptions, the player application needs to
 
In case of encountering one of the above interruptions, the player application needs to
* Call [[stop]] immediately (except when content is buffering) and withhold sending playhead position.
+
* Call <code>stop</code> immediately (except when content is buffering) and withhold sending playhead position.
* Start sending pings – <code>'loadMetadata'</code> and <code>'playheadPosition'</code> for the new viewing session, once the playback resumes.
+
* Start sending pings – <code>loadMetadata</code> and <code>playheadPosition</code> for the new viewing session, once the playback resumes.
Please see the [https://engineeringportal.nielsen.com/docs/Digital_Measurement_Interruption_Scenarios Interruption Scenarios Page] for more details
+
Please see the [[Digital Measurement FAQ]] for more details
 +
 
 +
== SDK Integration Architecture Diagram ==
 +
 
 +
=== For Content Playback ===
 +
 
 +
[[File:nlsn-sdk-achitecture-diagram_content.png||SDK Integration Architecture Diagram - Content]]
  
== Pre-Certification Checklists ==
+
=== For Ad Playback ===
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.
+
[[File:nlsn-sdk-achitecture-diagram_ad.png||SDK Integration Architecture Diagram - Ad]]
  
 
== Privacy ==
 
== Privacy ==
 
 
The privacy section will be added soon.
 
The privacy section will be added soon.
  
  
== Going Live ==
+
== Step 7 :  Test your player by yourself ==
Following Nielsen testing, you will need to:
+
=== Guide ===
 +
1. Connect your PC and test device (tablet or phone) via same router.<br />
 +
2. PC side: run Proxy sw (like Charles) and get local IP<br />
 +
3. Test device: modify Wifi setting to pass through Proxy IP from add 2<br />
 +
4. Test device: run your player, launch video<br />
 +
5. PC side: filter traffic by "nmr" and confirm presence of GN pings<br />
  
# '''Disable Debug Logging''': Disable logging by deleting <code>{nol_sdkDebug: 'DEBUG'}</code> from initialization call.
+
=== Example of GN ping ===
# '''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.
+
<code></code>
  
== Sample Applications ==
+
== Step 8 :  Provide your app for certification ==
The below sample applications have been designed to show the API's functionality and are broken into two distinct categories:
+
Once ready please send your application to Nielsen local staff for certification.
* '''Basic''' - To show the functionality of the Nielsen API using a standard no-frills player.
 
** [[Android Basic example|Android Studio Example]]
 
  
* '''Advanced''' - Nielsen API integrated into a custom video player is bundled with the SDK.
+
== Step 9 :  Going Live ==
 +
After the integration has been certified (but not prior that), disable debug logging by deleting {nol_sdkDebug: "DEBUG"} from initialization call - see Step 2.

Revision as of 11:56, 24 November 2020

Engineering Portal breadcrumbArrow.png Digital breadcrumbArrow.png DCR & DTVR breadcrumbArrow.png DCR Denmark Video Android SDK

Overview

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. 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 It supports a variety of Nielsen Measurement Products like Digital in TV Ratings (DTVR), Digital Content Ratings (DCR), 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:

  • Application launch events and how long app was running
  • Time of viewing a sub section / page in the application.

Prerequisites

Before you start the integration, you will need:

Item Description Source
""App ID (apid)"" Unique ID assigned to the player/site and configured by product. Contact Nielsen
""Nielsen SDK"" Includes SDK frameworks and ""sample implementation""; "See Android SDK Release Notes" Download

Step 1: Setting up your Android Development Environment


1) Ensure to unzip the Nielsen App SDK zip file and copy the "AppSdk.jar" into the app/libs folder on the App’s project. Add it as dependency.
2) Add the following permissions on the project’s "AndroidManifest.xml" file.

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>

3) Add Google Play Services lib into dependencies as Nielsen AppSDK uses the following packages/classes from the Google Play service. Libraries:

  • com.google.android.gms:play-services

Requiered Google Play Service CLasses and Packages :

  • com.google.android.gms.ads.identifier.AdvertisingIdClient;
  • com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
  • com.google.android.gms.common.ConnectionResult;
  • com.google.android.gms.common.GooglePlayServicesUtil;
  • com.google.android.gms.common.GooglePlayServicesRepairableException;
  • com.google.android.gms.common.GooglePlayServicesNotAvailableException;

4) Once the files are in place, import com.nielsen.app.sdk to the java source code and start accessing the public interface.

import com.nielsen.app.sdk.*;



Notes:

  • The Nielsen App SDK (located in the "com.nielsen.app.sdk" package) class is the primary application interface to the Nielsen App SDK on Android.
  • The Nielsen App SDK class is defined as the only public class belonging to the com.nielsen.app.sdk package.
  • Nielsen App SDK is compatible with Android OS versions 2.3+.
  • Clients can control / configure the protocol to be used – HTTPS or HTTP to suit their needs.
  • The Android OS hosting the App SDK should use a media player supporting HLS streaming (Android 4.0 and later will support it natively).
  • If the player application uses a 3rd party media player implementing its own HLS/MPEG-DASH stack, then the minimum Android version will be limited to version 2.3, since the SDK depends on Google Play support to work properly.

Step 2: Create SDK Instance

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. (Version 4.0 for Android)

  • A maximum of four SDK instances per appid are supported. When a fifth SDK instance is launched, the SDK will return “nil” from initWithAppInfo:delegate:
  • When four SDK instances exist, you must destroy an old instance before creating a new one.

The following table contains the list of arguments that can be passed via the AppInfo JSON schema.

Parameter / Argument Description Source Required/Obligatory? Example
appid Unique id for the application assigned by Nielsen. It is GUID data type. Nielsen-specified PXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
appname Name of the application Client-defined No Nielsen Sample App
appversion Current version of the app used Client-defined No "1.0.2"
nol_devDebug Enables Nielsen console logging. Only required for testing Nielsen-specified Optional "DEBUG"


1) AppSDK() is no longer a singleton object and should be created as below.

try{
  // Prepare AppSdk configuration object (JSONObject)
  JSONObject appSdkConfig = new JSONObject()
          .put("appid", "P83EDEF8F-7269-429B-BAA6-A7843F8AFA03")
          .put("appversion", "1.0")
          .put("appname", "Sample App Name")
          .put("nol_devDebug", "DEBUG"); // only for debug builds

         // Pass appSdkConfig to the AppSdk constructor
         mAppSdk = new AppSdk(appContext, appSdkConfig, this);
}
catch (JSONException e){
         Log.e(TAG, "Couldn’t prepare JSONObject for appSdkConfig", e);
}

2) implement IAppNotifier into your activity like

public class MainActivity extends AppCompatActivity implements IAppNotifier

3) implement callback

@Override
public void onAppSdkEvent(long timestamp, int code, String description){
  Log.d(TAG, "SDK callback onAppSdkEvent " + description);
}

So whole Activity will look like

package com.example.josefvancura.nlsdemotmp;

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import com.nielsen.app.sdk.*;

import org.json.JSONException;
import org.json.JSONObject;

public class MainActivity extends AppCompatActivity implements IAppNotifier {

    private AppSdk mAppSdk = null;
    private String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Context context = getApplicationContext();

        try{
            // Prepare AppSdk configuration object (JSONObject)
            JSONObject appSdkConfig = new JSONObject()
                    .put("appid", "P83EDEF8F-7269-429B-BAA6-A7843F8AFA03")
                    .put("appversion", "1.0")
                    .put("appname", "Sample App Name")
                    .put("nol_devDebug", "DEBUG"); // only for debug builds

            // Pass appSdkConfig to the AppSdk constructor
            mAppSdk = new AppSdk(context, appSdkConfig, this ); // Notifier - activity implements IAppNotifier, callback in onAppSdkEvent()

        }
        catch (JSONException e){
            Log.e(TAG, "Couldn’t prepare JSONObject for appSdkConfig", e);
        }

    }

    @Override
    public void onAppSdkEvent(long timestamp, int code, String description) {
        Log.d(TAG, "SDK callback onAppSdkEvent " + description);
    }

}

APP SDK Error & Event Codes

To view the Error and Event codes for iOS and Android, please review the App SDK Event Code Reference page.

Life cycle of SDK instance

Life cycle of SDK instance includes four general states:

  1. ""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"".
  2. ""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.
  3. ""Processing state"" – The SDK instance is processing playing information. API calls "play" and "loadMetadata" move the SDK instance into this state. In this state, the SDK instance will be able to process the API calls (see below)
  4. ""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.
    1. Initialization fails
    2. appDisableApi is called
@property (assign) BOOL appDisableApi;

Step 3: Create Metadata Objects

The parameters passed must be either a JSON formatted string or a NSDictionary object. The JSON passed in the SDK must be well-formed.

  • NSDictionary object
    • If an object of unexpected type is passed to the method, the error message will be logged.
    • If string has invalid JSON format, the error message will be logged.
  • JSON value must be string value.
    • This includes boolean and numeric values. For example, a value of true should be represented with "true", number value 123 should be "123".
    • All the Variable Names like appid, appname, dataSrc, title, type etc. are case-sensitive. Use the correct variable name as specified in the documentation.
  • JSON string can be prepared using either raw NSString or serialized NSDictionary.


Create channelName Metadata

channelName should remain constant throughout the completion of an episode or live stream.

Key Description Values Required
channelName Any string representing the channel/stream custom

Create Content Metadata

Content metadata should remain constant throughout the entirety of an episode/clip including when ads play.


program and title metadata values should be passed to SDK as UTF-8 strings.

Keys Description Values Required
type type of asset "content" for main content
assetid unique ID assigned to asset custom
(no Special Characters)
program name of program custom
title episode name custom
length length of content in seconds Length of content in seconds (86400 seconds for live stream (24/7). For Event-Live streams planned length. For VoD video length)
airdate the original date (local time with hh:mm:ss as 24h time stamp) content became available. This date does not change if the content is rebroadcasted on linear TV. YYYYMMDD HH:MI:SS (if not known set it to eg. "19700101 00:00:00")
isfullepisode full episode flag
  • "y", "yes", full episode (full content for a program)
  • "n", "no", non full episode (only one part of the entire content for a program , i.e. preview, sub-episodes)

adloadtype type of Ad load:
  • 1) Linear - matches TV ad load
  • 2) Dynamic - Dynamic Ad Insertion (DAI)

Create Ad Metadata (optional for DR)

The Ad Metadata (if applicable) should be passed for each individual ad.

Note: All metadata values should be passed as UTF-8 strings.

Keys Description Values Required
type type of Ad "preroll", "midroll", "postroll"
"ad" - If specific type can not be identified.
assetid unique ID assigned to Ad custom
(no Special Characters)

MetaData Example

JSONObject channelInfo = new JSONObject()
    .put("channelname","My Channel Name 1")

JSONObject contentMetadata = new JSONObject()
   .put("assetid", "unique_content_id")
   .put("type", "content")
   .put("program", "program name")
   .put("title", "episode title")
   .put("length", "length in seconds")
   .put("airdate", "20200713 10:22:00")
   .put("isfullepisode", "y")
   .put("adloadtype", "2");

JSONObject adMetadata = new JSONObject()
   .put("assetid", "unique_postroll_ad_id")
   .put("type", "postroll");

Step 4: Start the Measurement

Overview of SDK API Calls

play

The play method prepares the SDK for reporting once an asset has loaded and playback has begun. Use play to pass the channel descriptor information through channelName parameter when the user taps the ""Play"" button on the player. Call play only when initially starting the video.



loadMetadata

Needs to be called at the beginning of each asset, pass JSON object for relevant content or ad. Make sure to pass as 1st loadMetadata for content at the begining of playlist - see below API call sequence examples.



playheadPosition

Pass playhead position every second during playback. for VOD: pass current position in seconds. for Live: current Unix timestamp (seconds since Jan-1-1970 UTC) - if it is possible to seek back in Live content, then pass related Unix time (not current). Pass whole number that increments only by 1 like 1,2,3..



stop

Call when

  • content or ads complete playing
  • when a user pauses playback
  • upon any user interruption scenario - see bellow chapter Interruption scenario



end

Call when the content asset completes playback. Stops measurement progress.





Start the Measurement

In order to start the measurement, follow the 3 first steps below for Content without Ads. When terminating the Content playback call end to terminate the Content Measurement for the given asset.

Playlist Sample code Description
1. Start of stream play(channelName) channelName contains JSON metadata of channel/video name being played
loadMetadata(contentMetadataObject) contentMetadataObject contains the JSON metadata for the content being played
2. Content is playing playheadPosition(position) playheadPosition is position of the playhead while the content is being played
3. End of Stream end Content playback is completed.

Step 5: Stop/Resume the Measurement for video Playback Interruptions

As part of integrating Nielsen App SDK with the player application, the Audio / Video app developer needs to handle the following possible interruption scenarios:

  • Pause / Play
  • Network Loss (Wi-Fi / Airplane / Cellular)
  • Call Interrupt (SIM or Third party Skype / Hangout call)
  • Alarm Interrupt
  • Content Buffering
  • Device Lock / Unlock (Video players only, not for Audio players)
  • App going in the Background/Foreground (Video players only, not for Audio players)
  • Channel / Station Change Scenario
  • Unplugging of headphone

In case of encountering one of the above interruptions, the player application needs to

  • Call stop immediately (except when content is buffering) and withhold sending playhead position.
  • Start sending pings – loadMetadata and playheadPosition for the new viewing session, once the playback resumes.

Please see the Digital Measurement FAQ for more details

SDK Integration Architecture Diagram

For Content Playback

SDK Integration Architecture Diagram - Content

For Ad Playback

SDK Integration Architecture Diagram - Ad

Privacy

The privacy section will be added soon.


Step 7 : Test your player by yourself

Guide

1. Connect your PC and test device (tablet or phone) via same router.
2. PC side: run Proxy sw (like Charles) and get local IP
3. Test device: modify Wifi setting to pass through Proxy IP from add 2
4. Test device: run your player, launch video
5. PC side: filter traffic by "nmr" and confirm presence of GN pings

Example of GN ping

Step 8 : Provide your app for certification

Once ready please send your application to Nielsen local staff for certification.

Step 9 : Going Live

After the integration has been certified (but not prior that), disable debug logging by deleting {nol_sdkDebug: "DEBUG"} from initialization call - see Step 2.