Link Search Menu Expand Document

AV Reporting

All of Echo's public methods and properties relating to time use milliseconds as a unit.

Table of contents

  1. Set Media player attributes
  2. Media Player Volume
  3. Instantiating the Media class
  4. Setting Media Theme Information
  5. Pass the Media object to EchoClient
  6. Media Events
  7. Buffering
  8. Handling buffering after a seek event
  9. Buffering Example
  10. The Player Delegate
    1. Timestamps
    2. Example
  11. Window State

Set Media player attributes

First tell Echo about your player name, version, volume etc.

  echo.setPlayerName('MyMediaPlayer')
      .setPlayerVersion('0.1.0')
      .setPlayerVolume(30);
  
  echo.setPlayerName("MyMediaPlayer");
  echo.setPlayerVersion("0.1.0");
  echo.setPlayerVolume(30);
  
  echo.setPlayerName("MyMediaPlayer")
  echo.setPlayerVersion("0.1.0")
  echo.setPlayerVolume(30)
  

Media Player Volume

Echo’s media player volume range is from 0-100, where 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.

Instantiating the Media class

Before you can send AV events, meta-data 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.

  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);

  // COMING SOON: set media language if you know it
  myPieceOfMedia.setLanguage("en-gb"); // Not yet available on JS
  
  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"
  

Setting Media Theme Information

Additional information (see table below) about the media object can be set by the appropriate methods. This information is applied to the media theme attribute for reporting purposes.

For more information on Media Themes see this Confluence page

Property Function Example Description
Name
  media.setName(String)
  
  media.setName(String)
  
  media.name = String
  
six nations special The asset title.
Playlist
  media.setPlaylist(String)
  
  media.setPlaylist(String)
  
  media.playlist = String
  
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
  media.setWsPartnerId(String)
  
  media.setWsPartnerId(String)
  
  media.wsPartnerId = String
  
news.mail.ru The partner identifier is used to define content playback for a syndication partner.
Media Player Name
  media.setMediaPlayerName(String)
  
  media.setMediaPlayerName(String)
  
  media.mediaPlayerName = String
  
smphtml5 The name of the media player playing the content.
Media Player Version
  media.setMediaPlayerVersion(String)
  
  media.setMediaPlayerVersion(String)
  
  media.mediaPlayerVersion = String
  
2.21.23.5 The version of the media player playing the content.
Casting device referrer
  media.setCastingDeviceReferrer(String)
  
  media.setCastingDeviceReferrer(String)
  
  media.castingDeviceReferrer = String
  
cc The device referrer value is the referring device for casting devices:
  • cc = Chromecast
  • rk = Roku
  • gh = Google Home
  • aa = Amazon Alexa

Pass the Media object to EchoClient

Once you have created a Media object you must pass it to the EchoClient’s setMedia medthod.

Any changes you make to a Media object after you have passed it to EchoClient.setMedia() will be ignored by Echo. You must use methods on the EchoClient eg EchoClient.setMediaLength() to inform Echo about changes to media length. Length can only be set once, subsequent changes will be ignored by ATI.
  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:

The avPlayEvent should not be called when the user activates the play button, but when you start the media actually begins playing. You should wait until any calls to mediaselector etc have completed.
The avEndEvent should only be called when playback of a media item can no longer continue eg you are exiting a screen with a player, 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.
Echo will automatically insert a pause event when its internal position reaches the stated length of the current media item. You should not rely on this event however, and are expected to explicitly call avPauseEvent or avEndEvent as instructed above.
For live media you:
  • do not need to specify a length
  • should provide 0 as the position for avPlayEvent, avPauseEvent etc
  // Start playing the media
  echo.avPlayEvent(0);

  // Pause (passing an optional custom property for this event)
  echo.avPauseEvent(5000, {prop : 'custard'});

  // 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(12000);
  echo.avPlayEvent(10000);

  // Rewind (position represents position prior to rewind)
  echo.avRewindEvent(11000,2);
  echo.avPlayEvent(10000);

  // 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(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)
  

Buffering

"The definition of buffering should simply be: When the media stops progressing due to too small a buffer to play (this might be buffer exhaustion, or it might be a small duration, the important thing is not the empty buffer, but the media no longer progressing) increase bufferEvents by one. Start counting time until playback resumes, or the user pauses/seeks or similar in which case stop." - Jim Ley

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. (so not, for example, when loading media after pressing the play button for the first time.)
  • You should not notify Echo that buffering has started before the media has started playing. Echo will ignore it if you do.
  • 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

When you seek within an AV item some degree of buffering is expected. This should not be reported to Echo as buffering is an expected interuption to playback.

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);

Playback

2) Playback stops progressing due to buffering

Playback progress has stopped. Some sort of buffering indicator may be displayed.

Call EchoClient.avBufferEvent(120000);

Buffering

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.

Error screen

The Player Delegate

In addition to player position values being passed into Echo methods such as avPlayEvent Echo must also 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.

Note that although ATI does not report the actual position of the media, this information is still used for other purposes within Echo, for example ESS live media metadata enrichment

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.

Note: Timestamps on iOS For Echo iOS getTimestamp uses an 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 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

Set the player window state mode.

Modes JS Android Swift  
Normal NORMAL NORMAL .normal  
Full 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)