AV Insights
If you’re moving to Echo X.X.X-avi, you’ll encounter a pretty significant breaking change in the AV reporting area. This page should guide you through implementing AV Insights. You should not switch to Echo-avi unless you have an extraordinarily good reason to do so.
Table of contents
- Set Media Player attributes
- Instantiating the Media class
- Pass the Media object to EchoClient
- Media Events
- Seeking
- Buffering
- Handling buffering after a seek event
- Buffering Example
- The Player Delegate
- Window State
- AV Ad Events
- Piano Consent Management
Set Media Player attributes
First tell Echo about your media player. Note that these can change at any time and will be updated in subsequent events. Name and version should be set initially, the others should be set as appropriate (often in response to a user interacting with the player settings).
setMediaPlayer
is called when a media player is currently playing media, then Echo will trigger an av.stop event and clear the current media. You will need to call setMedia
afterwards. var mediaPlayer = new Echo.MediaPlayer();
echo.setMediaPlayer(mediaPlayer)
.setPlayerName('MyMediaPlayer')
.setPlayerVersion('0.1.0')
.setPlayerVolume(30);
MediaPlayer mediaPlayer = new MediaPlayer();
echo.setMediaPlayer(mediaPlayer);
echo.setPlayerName('MyMediaPlayer');
echo.setPlayerVersion('0.1.0');
echo.setPlayerVolume(30);
mediaPlayer: MediaPlayer = MediaPlayer()
echo.setMediaPlayer(mediaPlayer)
echo.setPlayerName("MyMediaPlayer")
echo.setPlayerVersion("0.1.0")
echo.setPlayerVolume(30)
The full list of setters for media player attributes are as follows:
Property | Function | Example | Description |
---|---|---|---|
Player Name |
| myMediaPlayer | The name of the media player. |
Player Version |
| 1.0.0 | The version number of the media player. |
Player Volume |
| 50 | A numer from 0-100, 0 being the lowest volume and 100 being the highest. If you need to perform a calculation to match this range (for example, if your volume goes up to 11) then please use floor and not round. |
Player subtitle state |
| true/false | Whether or not the player has subtitles enabled. Defaults to false. |
Player subtitle size |
| smallest/small/medium/large/largest | If subtitles are on, the size of the subtitles displayed. |
Player subtitle type |
| closed caption / narrative | If subtitles are on, the type of subtitles displayed. |
Player auto mode |
| true/false | Whether or not the player is set to autoplay mode. Defaults to false. |
Player window state |
| FULL / NORMAL / MINIMISED / MAXIMISED / PICTUREINPICTURE | One of the allowed enumerated values describing window state. |
Player popped-out state |
| true / false | Whether or not the player is in a popped-out window (as with the Sounds audio player for example) |
Player quality level |
| low / medium / high | The quality level chosen by the user |
Player playback speed |
| 0.5 / 0.8 / 1 / 1.5 / 2 | The playback speed chosen by the user |
Player position |
| ? | The position of the player in the page |
Player launch reason |
| ? | The reason the player was launched |
Player error |
| Error message of your choice | An error message to report when the player is in an error state |
Player location |
| internal / external | Whether the content is externally embedded (i.e. on a non-BBC website) or internal |
Instantiating the Media class
Once you have set up your player, metadata about the media content must be set. This is passed to your instance of EchoClient
using an instance of the Media
class:
You should aim to provide Echo with meaningful content IDs:
- a version ID for on-demand or download
- a service ID for a live simulcast
- a version ID for a live webcast
If you don’t have these then it is possible supply a lesser ID as shown below, but only one is required and it is not necessary to provide additional identifiers (they will be discarded).
The setVpid
method should only be used when you are unable to determine if an identifier represents a service or version (eg SMP playlist item vpid).
For more information about setting media producer, including how to use a string or masterbrand string, see the Reporting to Piano page here.
You must pass the type (i.e. Video or Audio) and consumption mode (on-demand, live or download) using the enumerated values as shown in the code samples below, when you create the Media object. You can then call the setter methods to set the media attributes.
var myPieceOfMedia = new Media(
Enums.AvType.VIDEO, // Type (Video or Audio)
Enums.MediaConsumptionMode.LIVE, // On demand / Download / Live
);
// Set the media producer
myPieceOfMedia.setProducer(Enums.Producer.BBC_THREE);
// Set at least one of the required ids
myPieceOfMedia.setVersionId("some_valid_versionId");
myPieceOfMedia.setServiceId("some_valid_serviceId");
myPieceOfMedia.setEpisodeId("some_valid_episodeId");
myPieceOfMedia.setClipId("some_valid_clipId");
// only use setVpid if you do not know an explicit version or service Id
// eg an SMP playlistObject.item.vpid
myPieceOfMedia.setVpId("some_valid_vpId");
// set length (in ms) if you know it
// you do not need to set length for 'live' media
myPieceOfMedia.setLength(3600000);
// set media language if you know it
myPieceOfMedia.setLanguage("en-gb");
Media myPieceOfMedia = new Media(
MediaAvType.VIDEO,
MediaConsumptionMode.LIVE);
// Set the media producer
myPieceOfMedia.setProducer(Producer.BBC_THREE);
// Set at least one of the required ids
myPieceOfMedia.setVersionId("some_valid_versionId");
myPieceOfMedia.setServiceId("some_valid_serviceId");
myPieceOfMedia.setEpisodeId("some_valid_episodeId");
myPieceOfMedia.setClipId("some_valid_clipId");
// only use setVpid if you do not know an explicit version or service Id
// eg an SMP playlistObject.item.vpid
myPieceOfMedia.setVpId("some_valid_vpId");
// set length (in ms) if you know it
// you do not need to set length for 'live' media
myPieceOfMedia.setLength(3600000);
// set media language if you know it
myPieceOfMedia.setLanguage("en-gb");
media = Media(avType: .video, consumptionMode: .onDemand)
// Set the media producer
media.producer = .BBCThree
// Set at least one of the required ids
media.versionID = "some_valid_versionId"
media.serviceId = "some_valid_serviceId"
media.episodeId = "some_valid_episodeId"
media.clipId = "some_valid_clipId"
// only use setVpid if you do not know an explicit version or service Id
// eg an SMP playlistObject.item.vpid
media.vpId = "some_valid_vpId"
// set length (in ms) if you know it
// you do not need to set length for 'live' media
media.length = 3600000
// set media language if you know it
media.language = "en-gb"
A full list of media properties follow:
Property | Function | Example | Description |
---|---|---|---|
Name |
| six nations special | The asset title. |
Playlist |
| sounds of the 70s | The name/identifier of the playlist. For a live stream this is automatically set to the Service ID. |
World Service Partner ID |
| news.mail.ru | The partner identifier is used to define content playback for a syndication partner. |
Casting device referrer |
| cc | The device referrer value is the referring device for casting devices:
|
Language |
| en-gb / cy | The language code representing the language of the content. |
Stream supplier |
| Akamai / BIDI | The CDN/supplier of the stream |
Transfer format |
| DASH / HLS | The streaming protocol in use |
Video dimension |
| 1080x960 | The dimensions of the video on screen in pixels |
Version aspect ratio |
| 16:9 / 4:3 | The aspect ratio of the content |
Pass the Media object to EchoClient
Once you have created a Media object and set its attributes, you must pass it to the EchoClient’s setMedia
method.
Media
object after you have passed it to EchoClient.setMedia()
will be ignored by Echo. There is one exception which is media length, which you can change after playback begins by calling echo.setMediaLength()
passing in a new length in milliseconds. setMedia
is called while the current media is playing, then Echo will trigger an av.stop event automatically. You can play a new piece of media using the same media player by creating a new media object and passing it to setMedia()
again, it will override the previously set media.
echo.setMedia(myPieceOfMedia);
/**
* When you know the media length you should call the appropriate Echo Client method.
*
* Any changes made to a media object after it has been passed to
* setMedia will be ignored.
*/
// you do not need to set the length for 'live' media
// the length value will default to 0
// you can also set it to 0 if desired
echo.setMediaLength(50000); // NB in ms
echo.setMedia(myPieceOfMedia);
/**
* When you know the media length you should call the appropriate Echo Client method.
*
* Any changes made to a media object after it has been passed to
* setMedia will be ignored.
*/
// you do not need to set the length for 'live' media
// the length value will default to 0
// you can also set it to 0 if desired
echo.setMediaLength(50000);
echo.setMedia(myPieceOfMedia)
/**
* When you know the media length you should call the appropriate Echo Client method.
*
* Any changes made to a media object after it has been passed to
* setMedia will be ignored.
*/
// you do not need to set the length for 'live' media
// the length value will default to 0
// you can also set it to 0 if desired
echo.setMediaLength(50000)
Media Events
After calling echo.setMedia()
you can begin sending AV measurements. Each method requires the media playhead position to be passed in, and also accepts a properties object:
avPlayEvent
should not be called when the user activates the play button, but when the media actually begins playing. You should wait until any calls to mediaselector etc have completed. avPlayInitiatedEvent
should be called when the play is requested (either by a user or automatic playback initiated by something else). avEndEvent
should only be called when playback of a media item can no longer continue eg you are exiting a screen with a player, or a player is about to play a different item. If you have reached the end of a stream and the user is still able to rewind and continue playback you should use an avPauseEvent
. avPauseEvent
or avEndEvent
as instructed above. - do not need to specify a length
- should provide 0 as the position for avPlayEvent, avPauseEvent etc
// Start playing the media
echo.avPlayInitiatedEvent(0);
echo.avPlayEvent(0);
// Pause (passing an optional custom property for this event)
echo.avPauseEvent(5000, {my_custom_prop: 'custard'});
// Start playing again
echo.avPlayEvent(5000);
// Starts to buffer
echo.avBufferEvent(5100);
// Resumes again
echo.avPlayEvent(5100);
/**
* avSeekEvent will put Echo into a paused state.
* You must call avPlayEvent in order to resume playback.
*/
// Seek (position represents position prior to seek)
echo.avSeekEvent(12000);
// Play (position represents position after the seek is completed)
echo.avPlayEvent(10000);
/**
* Call avEndEvent when the current media item can longer be resumed.
* Once this event is called you will not be able to call
* any other av events eg avPlayEvent against the current media
* item.
*/
echo.avEndEvent(20000);
// Start playing the media
echo.avPlayEvent(0);
HashMap<String, String> pauseProps = new HashMap<String, String>();
pauseProps.put("lablab", "custard");
// Pause (passing an optional custom property for this event)
echo.avPauseEvent(5000, pauseProps);
// Start playing again
echo.avPlayEvent(5000);
// Starts to buffer
echo.avBufferEvent(5100);
// Resumes again
echo.avPlayEvent(5100);
/**
* avSeekEvent, avRewindEvent and avFastForwardEvent will put Echo
* into a paused state. You must call avPlayEvent in order to
* resume playback.
*/
// Seek (position represents position prior to seek)
echo.avSeekEvent(2000);
echo.avPlayEvent(2000);
// Rewind (position represents position prior to rewind)
echo.avRewindEvent(10000, 4);
echo.avPlayEvent(5000);
// Fastforward (position represents position prior to fast-forward)
echo.avFastForwardEvent(6000, 2);
echo.avPlayEvent(10000);
/**
* Call avEndEvent when the current media item can longer be resumed.
* Once this event is called you will not be able to call
* any other av events eg avPlayEvent against the current media
* item.
*/
echo.avEndEvent(10000);
// Start playing the media
echo.avPlayEvent(at: 0, eventLabels: nil]
// Pause
echo.avPauseEvent(at: 5000, eventLabels: nil)
// Start playing again
echo.avPlayEvent(at: 5000, eventLabels: nil)
// Starts to buffer
echo.avBufferEvent(at: 5100, eventLabels: nil)
// Resumes again
echo.avPlayEvent(at: 5100, eventLabels: nil)
/**
* avSeekEvent, avRewindEvent and avFastForwardEvent will put Echo
* into a paused state. You must call avPlayEvent in order to
* resume playback.
*/
// Seek (position represents position prior to seek)
echo.avSeekEvent(at: 5000, eventLabels: nil)
echo.avPlayEvent(at: 2000, eventLabels: nil)
// Rewind (position represents position prior to rewind)
echo.avRewindEvent(at: 10000, rate: 1, eventLabels: nil)
echo.avPlayEvent(at: 5000, eventLabels: nil)
// Fastforward (position represents position prior to fast-forward)
echo.avFastForwardEvent(at: 6000, rate: 1, eventLabels: nil)
echo.avPlayEvent(at: 10000, eventLabels: nil)
/**
* Call avEndEvent when the current media item can longer be resumed.
* Once this event is called you will not be able to call
* any other av events eg avPlayEvent against the current media
* item.
*/
echo.avEndEvent(at: 10000, eventLabels: nil)
Subsequent events
Echo tracks the previous events and enforces a rule that certain events cannot be sent subsequently.
- Sending a play event, while playing, will be ignored
- Sending a seek event, while seeking, will be ignored
- Sending a pause event, while paused, will be ignored
- However, if paused, then seeking, a subsequent pause event can be triggered to conclude the seek.
- Sending a buffer event, while buffering, will be ignored
Seeking
Due to how Piano track seeking (requiring both a start and end position, whereas Echo’s API accepts only one position), there are some considerations to be aware of.
Calling avSeekEvent
will trigger Piano to send an event (av.seek.start
), and the seek will be concluded on a call to either avPlayEvent
or avPauseEvent
at which point Piano will send another event to record the seek has taken place (av.forward
if the position of avSeekEvent
is lower than the subsequent event, and av.backward
if it is higher).
Note if you are in a paused state before seeking, and after (i.e. playback doesn’t begin immediately upon the seek concluding), you should call avPauseEvent
. This will not result in an extra pause event being sent, but it will conclude the seek properly. If you are playing as the seek ends, then of course avPlayEvent
is correct.
Note also that the position supplied to avSeekEvent
should be the position before the seek takes place. If you don’t know the exact position then the last known position, as close as possible, is acceptable.
For example:
// Media play initiated
echo.avPlayInitiatedEvent(0);
echo.avPlayEvent(0);
// Start seeking after a few seconds
echo.avSeekEvent(3000);
// Finish seeking to a position five minutes into the programme (av.forward event sent as well as av.resume for the play)
echo.avPlayEvent(300000);
// Pause and seek
echo.avPauseEvent(310000);
echo.avSeekEvent(310000);
// Finish seeking without playing (no pause event is sent, but av.forward is)
echo.avPauseEvent(600000);
Buffering
Please follow these rules when reporting buffering to Echo:
- You should notify Echo that buffering has started using the
avBufferEvent
method when you expect content to be playing but the media is no longer progressing. (note this does now include when loading media after pressing the play button for the first time.) - You should notify Echo if buffering has started before the media has started playing.
- Once buffering has started, only a play or pause event will consider the buffering ended. In other words, sending a play or pause event, while buffering, ends the buffering state.
- You may seek, while in a buffering state, but you still must play or pause in order for Echo to consider the buffering ended.
- If you enter an error state/screen following a period of buffering then notify Echo of a pause or end event. Time spent on an error screen should not count as playing or buffering time.
It is important that you inform Echo when you enter a non-playing, non-buffering state eg an error screen as this should not count as playing or buffering time. See the examples and screenshots below. Failure to correctly report changes to playing state will result in inaccurate values being reported and distorted overall playing/buffering time reporting.
Handling buffering after a seek event
Depending on how your player or application is implemented a number of outcomes may follow a seek:
- seek completes succesfully after a short time and playback continues (do not report buffering to Echo)
- seek fails so you are unable to continue playback (do not report buffering to Echo)
- enters a buffering state after a period of time has passed (you may want to report buffering to Echo)
Buffering Example
1) Content is playing
Call EchoClient.avPlayEvent(0);
2) Playback stops progressing due to buffering
Playback progress has stopped. Some sort of buffering indicator may be displayed.
Call EchoClient.avBufferEvent(120000);
3) Buffering ends and an error screen is displayed
After some time spent in a buffering state that player has decided to display an error message to the user. The player is no longer in a buffering state.
Call EchoClient.avPauseEvent(120000);
If buffering ends and you are able to continue playback you should call the avPlayEvent
method.
The Player Delegate
In addition to player position values being passed into Echo methods such as avPlayEvent
Echo must also be able to pull position or timestamp values from players. This is achieved by players implementing the Echo PlayerDelegate interface with the following methods:
Method | Consumption Mode | Value | Frequency | Notes |
---|---|---|---|---|
getPosition | on-demand or download | 0 or position in milliseconds | 200ms | Only called if BARB is enabled. |
getTimestamp | live | 0 or epoch in milliseconds | 1 second | Used for both ATI and BARB |
If you are unable to provide a value you can safely return 0 whenever either method is called.
For more detail on the workings of the BARB Spring library see the Spring library documentation.
Timestamps
Echo expects players to provide a value representing the broadcast time for the content currently being consumed as an offset since 1 January 1970 00:00:00 UTC in milliseconds eg 1464702095000 which represents Tue, 31 May 2016 13:41:35 GMT.
It is expected that this will be obtained from the metadata available through the stream being consumed (you may need to convert to milliseconds). If you know that your timestamp does not include leap seconds please correct for this before passing a value to Echo.
NSTimeInterval
type, which specifies a value in seconds (as opposed to the milliseconds used by other platforms). The above example would therefore be given as 1464702095.000
. Example
/**
* Assumes some object called player with a position and timestamp
* property exists in your application.
*
* Your implementation will vary.
*/
var playerDelegate = {
getPosition: function() {
return player.position;
},
getTimestamp: function() {
return player.timestamp;
}
};
// create an EchoClient instance
var echo = new EchoClient('MyApp', Enums.ApplicationType.WEB);
// pass in a reference to you player delegate implementation
echo.setPlayerDelegate(playerDelegate);
// create a media player
var mediaPlayer = new MediaPlayer();
echo.setMediaPlayer(mediaPlayer);
// create a piece of media
var media = new Media(Enums.AvType.VIDEO, Enums.MediaConsumptionMode.LIVE);
media.setServiceId('bbc_one_london');
echo.setMedia(media);
// start playing
echo.avPlayEvent(0);
// Echo will start calling playerDelegate.getTimestamp once a second
import uk.co.bbc.echo.interfaces.PlayerDelegate;
import uk.co.bbc.echo.EchoConfigKeys;
public class MediaDelegate implements PlayerDelegate {
private long position;
private long timestamp;
private void updatePosition() {
//....Implement update behaviour
}
private void updateTimestamp() {
//....Implement update behaviour
}
public long getPosition() {
this.updatePosition();
// Return position of
return this.position;
}
public long getTimestamp() {
this.updateTimestamp();
// Return epoch timestamp
return timestamp;
}
}
// In your app
public class TestShellApplication extends Application {
private MediaDelegate mediaDelegate;
public long initApplication() {
HashMap<String, String> config = new HashMap<String, String>();
config.put(EchoConfigKeys.USE_ESS, true);
mediaDelegate = new MediaDelegate();
EchoClient echo = new EchoClient(
"MyApp", // App Name
ApplicationType.MOBILE_APP, // App type
"echo.android.test", // App Countername
getApplicationContext(), // The Android Context of your Application
config, // config options HashMap
bbcUser, // BBCUser instance
this // Application instance of your Application
);
// Android also requires these player attributes to be set
echo.setPlayerName("Some Player");
echo.setPlayerVersion("1.0.1");
echo.setPlayerSize(400, 600);
echo.setPlayerDelegate(mediaDelegate);
}
}
import Echo
class MediaDelegate: PlayerDelegate {
var position: UInt64?
var timestamp: TimeInterval?
func updatePosition() {
//....Implement update behaviour
}
func updateTimestamp() {
//....Implement update behaviour
}
func getPosition() -> UInt64 {
updatePosition()
return this.position
}
func getTimestamp() -> TimeInterval {
this.updateTimestamp()
return timestamp
}
}
// In your app
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//Echo Config
var config:Dictionary<EchoConfigKey, String> = [
.echoTrace: "echo_demo_ios",
// other config values here
]
// BBCUser
user = BBCUser(signedIn: true,
hashedID: hashedId,
tokenRefreshTimestamp: Date(timeIntervalSince1970: 1510156737)
)
mediaDelegate = MediaDelegate()
// Initialise Echo
do {
echo = try EchoClient(appName: "EchoDemoApp",
appType: .mobileApp,
startCounterName: "bbc.start.page",
config: config,
bbcUser: user)
} catch {
// Handle Echo config error
}
// Set player attributes
echo.setPlayerName("Some Player")
echo.setPlayerVersion("1.0.1")
echo.setPlayerSize(400, 600)
echo.setPlayerDelegate(playerDelegate: mediaDelegate)
return true
}
Window State
Setting the player window state mode is done via these specific enumerated values:
Modes | JS | Android | Swift |
---|---|---|---|
Normal | NORMAL | NORMAL | .normal |
Full | FULL | FULL | .full |
Minimised | MINIMISED | MINIMISED | .minimised |
Maximised | MAXIMISED | MAXIMISED | .maximised |
Resized | - | RESIZED | .resized |
Picture in picture | PICTUREINPICTURE | PICTUREINPICTURE | .pip |
echo.setPlayerWindowState(Echo.WindowState.FULL);
echo.setPlayerWindowState(WindowState.FULL)
echo.setPlayerWindowState(.full)
AV Ad Events
If your AV content includes ads, you can report on this as follows. You may include any number of custom properties to describe the ad content. For more information on available on ad properties, see the Data Dictionary. The function, avAdStart, will automatically pause playback.
// Media play initiated
echo.avPlayInitiatedEvent(0);
// Pre-roll content
echo.avAdStart(0, {custom_property: 'ad information'});
// Ad is clicked
echo.avAdClick(0, {custom_property: 'ad information'});
// Main feature begins playing
echo.avPlayEvent(0);
// Mid-roll
echo.avAdStart(900000, {custom_property: 'ad information'});
// Ad is skipped
echo.avAdSkip(900000, {custom_property: 'ad information'});
// Main feature resumes
echo.avPlayEvent(900000);
// Post-roll (completes without click or skip)
echo.avAdStart(1800000, {custom_property: 'ad information'});
// Player unloads
echo.avEndEvent(1800000);
HashMap<String, String> props = new HashMap<>();
props.put("custom_property", "ad information");
// Media play initiated
echo.avPlayInitiatedEvent(0, null);
// Pre-roll content
echo.avAdStart(0, props);
// Ad is clicked
echo.avAdClick(0, props);
// Main feature begins playing
echo.avPlayEvent(0, null);
// Mid-roll
echo.avAdStart(900000, props);
// Ad is skipped
echo.avAdSkip(900000, props);
// Main feature resumes
echo.avPlayEvent(900000, null);
// Post-roll (completes without click or skip)
echo.avAdStart(1800000, props);
// Player unloads
echo.avEndEvent(1800000, null);
// Media play initiated
let props = ["custom_property": "ad information"]
echo.avPlayInitiatedEvent(at: 0, eventLabels: nil)
// Pre-roll content
echo.avAdStart(at: 0, eventLabels: props)
// Ad is clicked
echo.avAdClick(at: 0, eventLabels: props)
// Main feature begins playing
echo.avPlayEvent(at: 0, eventLabels: nil)
// Mid-roll
echo.avAdStart(at: 900000, eventLabels: props)
// Ad is skipped
echo.avAdSkip(at: 900000, eventLabels: props)
// Main feature resumes
echo.avPlayEvent(at: 900000, eventLabels: nil)
// Post-roll (completes without click or skip)
echo.avAdStart(at: 1800000, eventLabels: props)
// Player unloads
echo.avEndEvent(at: 1800000, eventLabels: nil)
Piano Consent Management
Echo automatically integrates with Piano’s Consent Management. Developers do not need to manually configure these cookies.
Echo checks the value of the ckns_policy
cookie, to determine if the user has accepted performance cookies. If so, consent is enabled for the additional Piano products which require user opt in. Whether ot not performance cookies have been accepted, normal tracking will occur regardless.
You can test the library by verifying that the window.pdl object is populated correctly when performance cookies are allowed using console.log(window.pdl);