Implementation of Stream Measurement


Version

2.3

Date

11.11.2019

Author

Kantar Media Division

Version history

Version

Date

Author

Comments

0.1

23.05.2005

Christopher Wirtz

Initial

0.5

26.05.2005

Christopher Wirtz

Changes

1.0

02.06.2005

Christopher Wirtz

Release

1.1

12.03.2007

Christopher Wirtz

Flash added

1.2

06.07.2009

Christopher Wirtz

Review/Changes

1.3

14.07.2009

Christopher Wirtz

Added Example to set the „duration“ manually in ActionScript

1.4

29.06.2010

Christopher Wirtz

Unload Delay

1.5

02.11.2010

Frank Kammann

Add Example using a NetStreamAdapter, Streaming Segments and parallel Streams

1.6

14.03.2011
23.03.2011
24.03.2011

Frank Kammann
Ralf Cornehl
Ralf Cornehl

Description of the general Operation (API) and Information on the Security Settings for Flash
Review/Changes
Translation into English

1.7

17.09.2012

23.10.2012

Christopher Wirtz

Ralf Cornehl

Adapting the position-based Stream Measurement to event-based Measurement

Review, Adapting Explanations

1.8

04.12.2012

04.12.2012

Frank Kammann

Ralf Cornehl

Mobile Streaming added

Review, Adapting Explanations, Translation into English

1.9

13.05.2013

13.05.2013

26.09.2013

Frank Kammann

Katrin Joachimsky

Ralf Cornehl

Chapter HTML5 added

Translation into English

Adapting Stream Name Guidelines

2.0

22.01.2014

10.11.2014

19.08.2015

Hendrik Vermeylen

Ralf Cornehl

Ralf Cornehl

Offline Mode for App Streaming

Opt-Out Method

URL-scheme added for iOS

2.125.09.2015Ralf CornehliOS9 Support (URL-Scheme-White-listing, ATS, Bitcode)
2.218.10.2017Marc BornAndroid Streaming Sensor now uses Singleton template for creation
2.311.11.2019Christopher KirchImplementation Streaming for Cordova

Content


Introduction

Whether news shows, product presentations or online radio programs - all are now using audio and video streams as medium of communication.

Even more than on the static pages, not only the number of visitors but also the behavior of users within a stream is of interest. For how long the clip was actually viewed, which sections were of particular interest and which were not?

When streaming media is called from a web site that is composed by the COM model and/or Flash, it is not only possible to track the number of users, but also watching the users’ behavior.

This document describes the integration of streaming measurement for different applications.

Concept of Measurement

In order to measure any streaming content, a sensor on the client side is necessary, which measures which sequences of the stream were played by the user.

These sequences can be defined by time intervals in seconds - therefore, the player or the playing instance must be able to provide a method for delivering the actual position on the stream in seconds.

The regular reading of the current position allows the tracking of all actions on a stream. Winding operations can be identified, if there are unexpected jumps in reading out the position. Stop or pause operations are identified by the fact, that the current position will not change.

User actions and operations like stop or pause are not measured directly (not player event based) but are instead derived from measuring the current position on stream.

The following chart shows the architecture of the API:

The simplicity of the API and the concept lead to the following features:

  • Any desired streaming content, that is able to provide the current position in seconds on the stream, can be measured.
  • The API can easily be ported to other programming languages.
  • The same method is used in a cross-technology manner, which makes the same streaming content comparable even on different frameworks and technologies.

Basic Use of the API

The following discussion is presented in JavaScript syntax. The procedure is more or less the same in any programming language used.

// 1. Instantiate the API object
var sensors = new SpringStreams("site code");

// 2. Create a description object for streaming content
var desc = {
   "stream":"videos/mystream",
   "duration":600, // in seconds
   "sx":video.width,
   "sy":video.height
};

// 3. Provide a content object
var content = ...; // Any object, which is able to deliver the current position on the stream in seconds

// 4. Register the content with the description object on the sensor
var stream = sensors.track(content, desc);

Remarks:

to 1: Instantiate the API object

to 2: Create a description object for streaming content

Variable

Optional

Description

stream

No

Name of the stream, which is ideally given as a hierarchy.

Example:
BroadCaster_OD/Channel/Program/Season
/Episode/Streamname
BroadCaster_Live/Channel/Program/Season/Episode/Streamname

In general there is no limitation of the charset – conform to the JSON standard http://json.org – , but for having a better interpretation of stream paths in scores we advice to use the described standards below.

The allowable character set for all values in the tagging is UTF-8. It is however recommended to limit to the following characters:

a-z
A-Z
0-9
comma ","
point "."
dash "-"
underscore "_"

The slash "/" is used as a separator within the stream name for creating different hierarchy levels


dur, duration

Yes

The duration must be setup if there's no adapter implementation.
For live streams it can be omitted, or supplied with the value 0

ct

Yes

Any value, that makes a statement about the content type can be used
For example, the value "ad" can be given to the stream to mark it as advertising

cqYesAny value, that makes a statement about the content quality can be used
For example, the value "programme-ID" can be given to the stream to mark it as an unique content

sx

Yes

The width of the stream window - if it's a movie
For radio streams it can be omitted, or supplied with the value 0

sy

Yes

The height of the stream window - if it's a movie
For radio streams it can be omitted, or supplied with the value 0


to 3: Provide a content object

to 4: Register the content with the description object on the sensor

var stream = sensors.track(content, desc);

Internal Function Description of the API

After the streaming content with the accompanying description object has been passed to the track method, the current position on the streaming content is queried every 200ms. Internally, all intervals are collected, which were viewed by the user on the stream. At a constant viewing this is one interval.
Once it's determined that the current position changes by more than 1 second to the expected position a start, stop or winding event or even a buffering phase can be assumed. In this case, a new interval is created.

An interval is defined as:

[start, end, timestamp]


start

The starting second on the stream

end

The ending second on the stream

timestamp

The client-side time stamp by creating the interval


All the collected usage information of the measured streams are encoded in an HTTP request and regularly transmitted to the measuring system. The following rules apply:

Data Transport

// System Object
[{"sx":1280,
"sy":1024,
"pl":"FlashPlugin",
"plv":"WIN 10,0,45,2"
},
// Usage Information
{"stream":"spring/teststream", 
     "dur":"600", 
     "sx":"400",
     "sy":"300",
     "uid":"267fgut",
      "vt":356, 
     "pst":[[0,0,"kzog7e"],[22,378,"kzog7e"]]
}]

Two objects are sent by the data transport:

The description of the Encoding for the HTTP request is not part of this documentation. The request is designed in a manner that the variables are still clearly identifiable during debugging activities.

Implementation via Javascript for any Player (also proprietary ones)

Prior to using the springStreams-functions the Javascript-library must be loaded.
This is achieved by the following script-tag:

<script src=".../springstreams.js"></script>

The springStreams-functions are provided by the class SpringStreams. The code of a site will be passed on with the creation of an object of this class:

	
var sensors = new SpringStreams("test"); 

(warning) test = The site code in this case is “test”. You have to replace the site code "test" by the site code (unique name provided by Kantar) for the measured site.

To measure any player – even proprietary ones – an adapter to the specific player can be used to pass on data to the sensors of the measurement system. This adapter provides the following three functions:

var myAdapter = {

	"getMeta" : function(id) {
		return {
			"pl" :"own player",
			"plv" :"version1",
			"sx" : screen.width,
			"sy" : screen.height
		}
	},
	"getDuration" : function(id) {
		return streamlength in seconds;
	},
	"getPosition" : function(id) {
		return new Date().getTime() / 1000;
	}
};

1. The function getMeta is used to acquire a description of the player and the available screen area. By calling this function a return of an object with the following attributes is expected:

pl

player description

plv

player version

sx

screen width in pixel

sy

screen height in pixel

2. The function getDuration is used to acquire the total duration of the stream in seconds. If this cannot be done (because it is e.g. a live stream) 0 should be returned.

3. The function getPosition is used to acquire the current position in the stream. The function delivers the current play position in seconds. This function is also used to determine the intervals within the streams being played, skipped or stopped.

By calling the object to be measured (from the first parameter of the call to sensors.track(…)) it will be transmitted to all functions.

The self-created adapter can now be used for the measurement:

In the next step the stream content, the description object and the adapter can be transmitted to the API method track.  


sensors.track(someObject, desc, myAdapter);

The measurement can be monitored by using the method debug on the SpringStreams object:

sensors.debug = function(v) {
        window.status = " " + v;
}

Upon each transmission of the sensor to the measurement system this method is attached as a parameter (string) to the URL called.

SpringStreams does not intervene with the event model of the surrounding document. Therefore it is necessary to notify the sensors of any termination event of the streams by using events outside of the player. The most simple option for such a method is the insertion of an unload function by calling the method sensors.unload(), when the website is left by the user:

function unload() {
        sensors.unload();
}

Because the browser will close the web site after the unload sequence is finished, it is possible that the closing counting impulse was not committed. An improvement can be achieved by inserting a piece of code at the end of the unload sequence.

function unload() {
	sensors.unload();
        // give time for submission
	var start= new Date(); var now = null; do now = new Date();  
        while(now-start < 100);
}

To do so, this function can be added into the body tag:

<body onunload="unload();">

Single streams can also be terminated directly, for example by changing the play list:

var stream = sensors.track(someObject, desc, myAdapter);
...
stream.stop();

For other embedded players than the WMP, an adapter can be used. Find below an example for the Real Player with the id rvplayer:

sensors.track(rvplayer, description, sensors.RVStreamAdapter);

Example for a proprietary player

<html>
<head>
<title>spring sensors</title>
<script src="springstreams.js"></script>
</head>
<body onunload="unload();">
<script type="text/javascript">

var sensors = new SpringStreams("test"); 
var adapter = {
	"getMeta" : function() {
		return {
			"pl" :"own player",
			"plv" :"version1",
			"sx" : screen.width,
			"sy" : screen.height
		}
	},
	"getDuration" : function() {
		return streamlength in seconds;
	},
	"getPosition" : function() {
		return new Date().getTime() / 1000;
	}
};

var desc = {
	"stream": "videos/teststream"
}

sensors.track("someid", desc, adapter);

// uncomment for debugging
//	sensors.debug = function(v) {
//		window.status = v;
//	}

function unload() {
	sensors.unload();
        // give time for submission
	var start= new Date(); var now = null; do now = new Date();  
        while(now-start < 100);
}
</script>	
</body>

Implementation via Javascript (Windows Media player)

Prior to using the springStreams-functions the Javascript-library must be loaded.
This is achieved by the following script-tag:

<script src=".../springstreams.js"></script>

The springStreams-functions are provided by the class SpringStreams. The code of a site will be passed on with the creation of an object of this class:

var sensors = new SpringStreams("test"); 

(warning) test = The site code in this case is “test”. You have to replace the site code “test” by the site code (unique name provided by Kantar) for the measured site.

A media player embedded into the website can now be measured directly. For this, the object which is to be measured is required. In most cases this will be available through the id of the object’s tag. In the following example an object with the ID wmplayer with a reference to the Windows Media Player is being used.

sensors.track(wmplayer, {});

Beyond the ID of the object a number of descriptive information can be passed on in form of a JavaScript object:

var description = {
	"stream": "videos/teststream"
	"desc": "some additional description" 
};
sensors.track(wmplayer, description);

It is possible to include more additional attributes to the description. The declaration of stream with the name or the hierarchy of the stream is expected as a minimum.

More than one object can be measured.

Example Windows Media Player

<html>
<head>
<title>spring sensors</title>
<script src="springstreams.js"></script>
</head>
<body onunload="unload();">

<SCRIPT type="text/javascript">
      if(-1 != navigator.userAgent.indexOf("MSIE"))
      {
        document.write('<OBJECT id="wmplayer" width="435" height="326" CLASSID="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6" type="application/x-ms-wmp">');
        document.write('                <param name="URL" value="http://wstreaming.zdf.de/zdf/veryhigh/070912_iaa_mim.asx">');
        document.write('                <param name="AutoStart" VALUE="1">');
        document.write('                <param name="ShowStatusBar" VALUE="1">');
        document.write('</object>');
     }
      else  {
       document.write('<OBJECT id="wmplayer" width="435" height="326" type="application/x-ms-wmp">');
       document.write('                 <param name="URL" value="http://wstreaming.zdf.de/zdf/veryhigh/070912_iaa_mim.asx">');
       document.write('                 <param name="AutoStart" VALUE="1">');
       document.write('                 <param name="ShowStatusBar" VALUE="1">');
       document.write(' </object>');
      }         
    </SCRIPT>
		
<script type="text/javascript">

// "test" is the name of the tracked website
var sensors = new SpringStreams("test");

// wmplayer is the id of the stream above
sensors.track(wmplayer, {"stream":"videos/teststream"});

// uncomment for debugging
//sensors.debug = function(v) {
//	window.status = v;
//}

function unload() {
	sensors.unload();
        // give time for submission
	var start= new Date(); var now = null; do now = new Date();  
        while(now-start < 100);
}
</script>	
</body>


Implementation via HTML5

Before using the springstreams-functions, the javascript library has to be uploaded. This is done via the following script-tag:


<script src=".../springstreams.js"></script>


The springstreams-functions are available via  SpringStreams class. During creation of an object in this class, the website-identification is transferred:


var sensors = new SpringStreams("test"); 


(warning) test is the identification of the website in this case. This identification has to be replaced by the own identification provided by Kantar.

A video- or audio element (see: http://www.w3schools.com/tags/ref_av_dom.asp) embedded into the website can now be measured directly. In order to perform the measurement, the object to be measured is needed. Usually this is available via the id of the object-tag. In this example, the object with the ID video5 is used with a reference to an HTML video element.


sensors.track(video5, {});


In addition to the ID of the object to be measured, descriptive information concerning the object in form of Javascript-object can be indicated:


var description = {
    "stream": "videos/teststream"
    "desc": "some additional description" 
};
sensors.track(video5, description);


There is the possibility to add additional attributes to the description. Minimum the identification of stream indicating the name/hirarchy of the stream is expected.

Example HTML5 video


<!DOCTYPE html>
<html lang="en">
<head>
  <title>HTML5 Video Example</title>
  <script src="./springstreams.js"></script>
</head>
<body onunload="unload()">
<script type="text/javascript">
    var sensors = new SpringStreams("test"); 
</script>
 
   <div align="center">
    <video id='video' controls preload='none' >
      <source id='mp4' src="./wetter.ard.20101209.mp4" type='video/mp4; codecs="avc1, mp4a"'></source>
      <p>Your user agent does not support the HTML5 Video element.</p>
    </video>
   </div>
 
    <script type="text/javascript">
    var description = {
        "stream":"videos/news/wetter.ard.20101209.mp4"
    };
 
    var handle = sensors.track(video, description, sensors.HTML5Adapter);
    function unload() {
      sensors.unload();
    }
 
</script>
</body>
</html>


Implementation in Flash

The implementation in Flash is similar to the one in Javascript. To use the springStreams functions the library springstreams.as3.swc (resp. springstreams.as2.swc for Actionscript2) is embedded into the application.

Now the springStreams-functions are available via the class SpringStreams. When an object of this class is created, the name of the website is being passed on:

var sensors:SpringStreams = new SpringStreams("test"); 

(warning) test = The site code in this case is “test”. You have to replace the site code “test” by the site code (unique name provided by Kantar) for the measured site.

The measurements are passed on via an http call to the measurement systems. In the case that this call should be done via SSL (HTTPS) this can be achieved by passing on this information to the sensor:

var sensors:SpringStreams = new SpringStreams("test", true); 

A NetStream object can now be measured directly:

sensors.track(ns,{});

Beyond the ID of the object a number of descriptive information can be passed on as a JavaScript object:

var description:Object = {
	"stream": "videos/teststream",
	"desc": "some additional infos",
	"sx": video.width,
	"sy": video.heigth,
};
sensors.track(ns, description);

It is possible to include more additional attributes to the description. The declaration of stream with the name of the stream or the hierarchy and sx respectively sy with the sizing description of the visible area is expected as a minimum.

Requests to the measurement system can be monitored via the ActionScript function trace in the debug environment.

The class SpringStreams defines a function SPRING_UNLOAD via the Flash class ExternalInterface (if available). Analogous to the Javascript version, this function can be used to signal the termination of the runtime environment. This can be implemented as follows:

<body onunload="unload()">
...
function unload() {
   FlexProject.SPRING_UNLOAD();
}

Because the browser will close the web site after the unload sequence is finished, it is possible, that the closing counting impulse was not initiated. An improvement will be reached by inserting a part of a little application at the end of the unload sequence, which will not be noticed by customers.

function unload() {
	FlexProject.SPRING_UNLOAD();
        // give time for submission
	var start= new Date(); var now = null; do now = new Date();  
        while(now-start < 100);
}

FlexProject is the ID of the flash object. If the Flash application already uses an unload mechanism, this mechanism can call the static function SpringStreams.unload().

Use as an Embedded Player and Security Settings

If the player should be used as Embedded Player on an other website, the security settings have to be respected.

The following settings must be setup (siehe: ExternalInterface#addCallback())

  1. Set in the HTML page at the object tag for the SWF file the following parameters:

    <param name="allowScriptAccess" value="always" />
    


  2. Paste in the SWF file the following ActionScript code:

    flash.system.Security.allowDomain( sourceDomain )
    


At absence of these settings, a security error appears in the API, because the trial of registering the SPRING_UNLOAD() function on the website will fail. This error is handled by the API and will be ignored. However, it isn't essential for the proper functioning of the measurement.
Furthermore, for the proper functioning the entry of the attribute onunload="..." in the body tag is necessary in order to report the leaving of the side to the API. At absence of these settings, it's possible that the last event at the end of the measurement is no longer transmitted to the measurement system.

Example integration in Flash

 var nsClient:Object = {};
 nsClient.onMetaData = ...
 nsClient.onCuePoint = ...  

 nc = new NetConnection();
 nc.connect(null);

 ns = new NetStream(nc);
 ns.play(streamlocation);
 ns.client = nsClient;

 video = new Video();
 video.attachNetStream(ns);
 
 addChild(video);
                
 var tracker:SpringStreams = new SpringStreams("test");
 tracker.track(ns,{"stream":"videos/teststream","sx":video.width,"sy":video.height});

The determination of the stream’s total duration is achieved by an intermediate stream-client, which accesses all meta-events of the stream. This stream-client transparently routes all events to the already registered client (nsClient in the above example).

A meta event (onMetaData) containing an info-object is expected. The attribute duration (info.duration) is requested from this info-object and interpreted as the length of the stream.

If this information should not be provided by your streams, the duration of the stream can be assigned externally (in seconds).

via the stream reference:

var nsClient:Object = {};
 nsClient.onMetaData = ...
 nsClient.onCuePoint = ...  

 nc = new NetConnection();
 nc.connect(null);

 ns = new NetStream(nc);
 ns.play(streamlocation);
 ns.client = nsClient;

 video = new Video();
 video.attachNetStream(ns);
 
 addChild(video);
                
 var tracker:SpringStreams = new SpringStreams("test");
 var stream:Stream = tracker.track(ns,{"stream":"videos/teststream","sx":video.width,"sy":video.height});

 stream.setDuration(600/*10 minutes*/);

Measurement for any Streaming Content in Adobe Flash

As described in the last chapter, you need a library for a flash.net.NetStream object to measure a streaming content. Inside the measurement, the actual position on the stream object will be readout.

The measurement is constantly calling the property NetStream.time.

var net:NetConnection = new NetConnection();
net.connect(null);
var ns:NetStream = new NetStream(net);
var currentPosition:Number = ns.time;

With this knowledge it is possible to measure any streaming object, where the actual position in seconds on the stream is transmitted.

For this you have to implement an own NetStream object and overwrite the call ns.time.

In the following example a NetStream adapter for the flash.media.Sound object is implemented.

package 
{
import flash.media.SoundChannel;
import flash.net.NetConnection;
import flash.net.NetStream;
	
public class SoundNetStreamAdapter extends NetStream
{
	private var channel:SoundChannel;
	
	public function SoundNetStreamAdapter(net:NetConnection, c:SoundChannel)
	{
		super(net);
		channel = c;
	}
		
	override public function get time():Number { 
            // return the position in seconds
		return channel.position/1000;
	}

}
}

This adapter is also a NetStream object and can be transmitted to the streaming library. By implementing, the property time is overwritten. This property delivers the actual position in seconds. This value is divided by 1000 because the SoundChannel object delivers milliseconds instead of seconds. This is necessary to adapt the interface description of the NetStream object.

The length of the stream is taken by the property Sound.length, transformed to seconds and is written into the description object with the name duration.

var tracker:SpringStreams = new SpringStreams("test");
			
var s:Sound = new Sound();
s.load(new URLRequest("file:///pathto/sound.mp3"));

var desc:Object = {
	"stream":"sounds/sound.mp3",
	"sx":0,"sy":0,
};
			
var nc:NetConnection = new NetConnection();
nc.connect(null);

var ns:NetStream = new SoundNetStreamAdapter(nc, s.play());
var stream:Stream = tracker.track(ns, desc);
stream.setDuration(s.length/1000); // duration in seconds

Advanced Topics

Segmentation of Streams

Possibly there is a need for a deeper/different segmentation of streams. This can be desirable in case of a live stream or recorded streams with several content parts, so that the individual segments or parts of the stream can be distinguished.

This can also be realized with the library by measuring each consignment separately. To do this, the player needs to know when and what is broadcasted. It simply needs something like a program structure or "play list".

Example for the measurement of individual segments or parts on a live stream

 var nsClient:Object = {};
 nsClient.onMetaData = ...
 nsClient.onCuePoint = ...  

 nc = new NetConnection();
 nc.connect(null);

 ns = new NetStream(nc);
 ns.play(streamlocation);
 ns.client = nsClient;

 video = new Video();
 video.attachNetStream(ns);
 
 addChild(video);
                
// 20:00 - 20:15 Tagesschau
// 20:15 - 21:00 PlusMinus
var tracker:SpringStreams = new SpringStreams("test");
// 20:00
var stream:Stream = tracker.track(ns,{"stream":"livestreams/ard/Tagesschau","sx":video.width,"sy":video.height});

// 20:15
stream.stop();
stream = tracker.track(ns,{"stream":"livestreams/ard/PlusMinus","sx":video.width,"sy":video.height});

Several Streams in parallel Measurement

The library is able to measure several streams in parallel. All the streams which have been started can be measured directly by being transmitted with the method “track” to the measurement of streaming.

Example for the measurement of two parallel streams

...
ns1 = new NetStream(...);
...
ns2 = new NetStream(...);
...
var tracker:SpringStreams = new SpringStreams("test");
var stream1:Stream = tracker.track(ns1,{"stream":"streams/stream1","sx":video.width,"sy":video.height});
var stream2:Stream = tracker.track(ns2,{"stream":"streams/stream2","sx":video.width,"sy":video.height});


Mobile App Streaming


Features on Platforms

Information (optional)

The user can be informed at some point that the application monitors the user actions and transmits them to a measuring system. Furthermore, the user must be informed that he has the possibility to switch of the tracking in the application and can contradict this way. (see: Implementation of Stream Measurement#Opt-Out)

For this purpose, you can include data privacy information in your language into an appropriate place of your app implementation:

Our app uses the "mobile app streaming sensor" of Kantar Media spring, Saarlouis, Germany, to gather statistics about the usage of our site. This data is collected anonymously.
This measurement of the mobile usage uses an anonymized device identifier for recognition purposes. To ensure that your device ID can not be clearly identified in our systems, it is encrypted and will be reduced by half. Only the encrypted and shortened device identifier is used in this measurement context.
This mobile measurement was developed under the observance of data protection laws. The aim of the measurement is to determine the intensity of use, the extent of use and the number of users of a mobile application. At no time, individual users will be identified. Your identity is always protected. You get no advertising by this system.
You can opt-out of the measurement by our app with the following activation switch.

Please note that only the measurement of our app is disabled. It may be that you will continue to be measured by other broadcasters using the "mobile app streaming sensor".


Mobile App Streaming Sensor Measurement: |On/Off|

Opt-Out

The application developer can give users the ability to stop the further tracking of the user actions. For this purpose the library offers the following methods:

/**
 * When the value <code>false</code> is specified, the sending of
 * requests to the measuring system is switched off.
 * This value is <code>true</code> by default.
 */
public void setTracking(boolean tracking) { }
/**
 * Delivers the value <code>true</code> when the tracking
 * is activated otherwise the value is <code>false</code>.
 */
public boolean isTracking() { }


A persistent saving of the opt-out decision in the library is not provided and needs to be implemented by the app developer.