Tutorials · 8 min read

terminal view of raw JSON data

Video event analysis using api.video sessions

Have you ever wondered *how much* of a video has been watched? IN this tutorial, we'll analyze the session events to determine if a video was fully watched or not.

Doug Sillars

June 21, 2021

How much of your video is being watched? With api.video analytics, we can tell you exactly how much of the video was watched. In this post, we'll walk through how to parse video session data in order to add up how much of the video was watched in a session.

A video Session

A video session occurs when an api.video player is opened in a browser. As long as the same window is used, each viewing of the video will be added to that one session.

You can get a list of your video sessions by using the analytics endpoint for a given videoId. In my code (using NodeJS), it looks something like this:

var params = { videoId, metadata, currentPage, pageSize};
  
const result = client.rawStatistics.listVideoSessions(params);
  result.then(function(videos) {
        var sessionList = videos.data;
        console.log("Number of sessions to test " , sessionList.length);

          for(i=0;i<sessionList.length;i++){
            //get each session ID, and figure out how long each session watched the video
            var sessionId = sessionList[i].session.sessionId;
            pageSize = '100';
            console.log(sessionId);

The Node endpoint takes my parameters (including the videoId I wish to analyze) and sends it to the client.rawStatistics.listVideoSessions endpoint. This returns an array of the sessions that can be analyzed.

The log will list a count of sessionIds, and then list each sessionId. Here is an example output from this code snippet:

Number of sessions to test  8
ps5orCkYsuaMw08btQmsW8vp
ps24iGEi011m9xKPqdTnIOvZ
ps7ODtZR04dfRBQCcarOIhv7
ps19mQmv5CTQwdeb75n8lLWr
ps1UdYeHbp6tQ21vrGUI8gXw
ps6xqBySKoSQfmG4zjcyZtWU
ps3cLLchT0Os75lzXovlCqTe
ps3MpNgw5Ax5EQ4GSAVyODRh

get Session Events

In each session, the video player records each event, and sends it back to api.video. Using the Session event API endpoint, we can retrieve each of these events.

 var sessionParams = {sessionId, currentPage, pageSize}; const sessionData = client.rawStatistics.listSessionEvents(sessionParams);
    sessionData.then( function(lastSessionData) {
      //this is the session data
      console.log("session events", lastSessionData);

The response of this endpoint looks something like this:

session events RawStatisticsListPlayerSessionEventsResponse {
  data: [
    PlayerSessionEvent {
      type: 'ready',
      emittedAt: 2021-06-17T19:00:54.000Z,
      at: 0,
      from: undefined,
      to: undefined
    },
    PlayerSessionEvent {
      type: 'play',
      emittedAt: 2021-06-17T19:00:55.000Z,
      at: 0,
      from: undefined,
      to: undefined
    },
    PlayerSessionEvent {
      type: 'end',
      emittedAt: 2021-06-17T19:02:27.000Z,
      at: 91,
      from: undefined,
      to: undefined
    },
    PlayerSessionEvent {
      type: 'pause',
      emittedAt: 2021-06-18T10:59:03.000Z,
      at: 91,
      from: undefined,
      to: undefined
    }
  ],

In this case, the video is ready at time =0, begins playing, and then reaches the end at 91s. The window is closed a day later, sending a pause event. (this is just evidence that your author has a browser tab problem).

Using this data (and with the knowledge that the video is 91 seconds long), it's pretty easy to determine that the entire video was watched in this session. from ready to the first pause is 91s, so the video must have been completed. But what about more complicated viewing patterns? Let's look at an example:

  data: [
    PlayerSessionEvent {
      type: 'ready',
      emittedAt: 2021-06-21T12:33:06.000Z,
      at: 0,
      from: undefined,
      to: undefined
    },
    PlayerSessionEvent {
      type: 'play',
      emittedAt: 2021-06-21T12:36:13.000Z,
      at: 0,
      from: undefined,
      to: undefined
    },
    PlayerSessionEvent {
      type: 'pause',
      emittedAt: 2021-06-21T12:36:18.000Z,
      at: 81,
      from: undefined,
      to: undefined
    },
    PlayerSessionEvent {
      type: 'resume',
      emittedAt: 2021-06-21T12:36:19.000Z,
      at: 81,
      from: undefined,
      to: undefined
    },
    PlayerSessionEvent {
      type: 'seek.forward',
      emittedAt: 2021-06-21T12:36:19.000Z,
      at: undefined,
      from: 5,
      to: 81
    },
    PlayerSessionEvent {
      type: 'end',
      emittedAt: 2021-06-21T12:37:19.000Z,
      at: 91,
      from: undefined,
      to: undefined
    },
    PlayerSessionEvent {
      type: 'pause',
      emittedAt: 2021-06-21T13:10:18.000Z,
      at: 91,
      from: undefined,
      to: undefined
    }
  ],

In this session, we again start playing at time = 0, but the next set of events occur at 81s (pause/resume). So the video stopped and restarted at t=81. The next event tells us how we got to t-81 - the view skipped ahead (noted as a 'seek.forward' event) at t=5, to t=81. The video paused because the video had to load, and then from t=81, it played until the end at t=91.

In this case, even though the playback started at t=0, and ended at t-91, no one would consider this video fully watched, as the viewer skipped 76s of the video.

Calculating the time watched

To calculate the time watched, we'll add up the time between every play/resume event and every pause/end event. Then we'll subtract any time in a 'seek.forward' event.

 for(j=0; j< numberOfEvents; j++){
        if(lastSessionData.data[j].type == "play" || lastSessionData.data[j].type == "resume"){
          //start time measure
          starttime = lastSessionData.data[j].at;
          counting = true;
          console.log ("start" , starttime);
        }else if(lastSessionData.data[j].type == "pause" || lastSessionData.data[j].type == "end"){
          if(counting){
            endtime = lastSessionData.data[j].at;
            counting=false;
            console.log ("stop" , endtime);
            console.log ("added time" , endtime-starttime);
            summedtime += endtime-starttime;
            
          }
        }else if(lastSessionData.data[j].type == "seek.forward"){
          console.log("seeked forward", (lastSessionData.data[j].to - lastSessionData.data[j].from));
          summedtime -= (lastSessionData.data[j].to - lastSessionData.data[j].from);
          //if there was a seekforward, the max time previously calculated previously is no longer valid and needs to be recalculated.
          maxWatchedTime = 0;
        }
        console.log("summedtime: ", summedtime);
				
				
}

We can log the math that happens as we iterate through the events:

summedtime:  0
start 0
summedtime:  0
stop 81
added time 81
summedtime:  81
start 81
summedtime:  81
seeked forward 76
summedtime:  5
stop 91
added time 10
summedtime:  15

Comparing these events - we can see that from the first start to the first pause is 81seconds, but then we encounter the seeked forward of 76 seconds - leading to a summed time of 5 seconds. After the last 10 seconds are watched, we have a total of 15 seconds of the 91 second video watched.

Playback totals

When watching required training videos, it is often mandatory that the time watched is logged and and recorded. With the api.video session data, we can track each user's viewing to know if they have completed a video, or if they must re-watch the video.

In a future post, we will combine this video watching algorithm with code to 'unlock' subsequent videos in a training series.

Try out more than 80 features for free

Access all the features for as long as you need.
No commitment or credit card required

Video API, simplified

Fully customizable API to manage everything video. From encoding to delivery, in minutes.

Built for Speed

The fastest video encoding platform. Serve your users globally with 140+ points of presence. 

Let end-users upload videos

Finally, an API that allows your end-users to upload videos and start live streams in a few clicks.

Affordable

Volume discounts and usage-based pricing to ensure you don’t exceed your budget.