Skip to content

P.1203 Client Report Analysis

The P.1203 Client Report is a unique overview of an entire video streaming session. You can derive various statistics from it. In our statistic values we show various pre-calculated statistics that you can fetch from our Export API.

You can of course derive your own statistics from the P.1203 Client Report. This guide shows you how.

Example (Python)

The following example shows you how to derive a few statistics from the P.1203 Client Report. You can use this as a starting point for your own analysis. The below example only requires numpy installed.

The example works with any report – you could read the JSON in Python using json.load(), and then pass the parsed dictionary to the p1203_report_to_stats() function.

# p1203_report_to_stats.py
#
# A helper function to convert a P.1203 input report to a dictionary of statistics.
#
# Author: Werner Robitza, AVEQ GmbH
# Copyright (c) 2022 AVEQ GmbH. All rights reserved.

from collections import Counter, defaultdict
import numpy as np


def most_frequent(List):
    occurence_count = Counter(List)
    return occurence_count.most_common(1)[0][0]


def p1203_report_to_stats(p1203_report):
    audio_segment_durations = [s["duration"] for s in p1203_report["I11"]["segments"]]
    audio_bitrates = [s["bitrate"] for s in p1203_report["I11"]["segments"]]

    video_segment_durations = [s["duration"] for s in p1203_report["I13"]["segments"]]
    duration = sum(video_segment_durations)
    video_bitrates = [s["bitrate"] for s in p1203_report["I13"]["segments"]]
    fps_values = [min(max(0, s["fps"]), 60) for s in p1203_report["I13"]["segments"]]

    avg_video_framerate = (
        np.average(fps_values, weights=video_segment_durations)
        if len(video_segment_durations)
        else None
    )
    avg_video_bitrate = (
        np.average(video_bitrates, weights=video_segment_durations)
        if len(video_segment_durations)
        else None
    )
    avg_audio_bitrate = (
        np.average(audio_bitrates, weights=audio_segment_durations)
        if len(audio_segment_durations)
        else None
    )

    most_frequent_codec = None
    longest_played_resolution = None

    resolution_durations = defaultdict(float)
    for segment in p1203_report["I13"]["segments"]:
        resolution_durations[segment["resolution"]] += segment["duration"]
    # find the key of the largest entry in resolution_durations
    longest_played_resolution = max(resolution_durations, key=resolution_durations.get)
    if longest_played_resolution is not None:
        longest_played_resolution = int(longest_played_resolution.split("x")[1])
        most_frequent_codec = most_frequent(
            [s["codec"] for s in p1203_report["I13"]["segments"]]
        )

    number_of_resolution_switches_up = 0
    number_of_resolution_switches_down = 0

    curr_height = None
    for segment in p1203_report["I13"]["segments"]:
        height = int(segment["resolution"].split("x")[1])
        if curr_height is None:
            curr_height = height
            continue
        else:
            if height > curr_height:
                number_of_resolution_switches_up += 1
            elif height < curr_height:
                number_of_resolution_switches_down += 1
            curr_height = height

    if len(p1203_report["I23"]["stalling"]):
        initial_loading_delay = p1203_report["I23"]["stalling"][0][1]
    else:
        initial_loading_delay = None  # could not be determined, is not 0!
    stallings = [s for s in p1203_report["I23"]["stalling"] if s[0] != 0]
    number_of_stalling_events = len(stallings)
    avg_stalling_duration = (
        np.mean([s[1] for s in stallings]) if number_of_stalling_events else 0
    )
    total_stalling_time = (
        sum([s[1] for s in stallings]) if number_of_stalling_events else 0
    )

    display_resolution = int(p1203_report["IGen"]["displaySize"].split("x")[1])

    stats = {
        "most_frequent_codec": most_frequent_codec,
        "longest_played_resolution": longest_played_resolution,
        "display_resolution": display_resolution,
        "initial_loading_delay": initial_loading_delay,
        "number_of_stalling_events": number_of_stalling_events,
        "avg_stalling_duration": avg_stalling_duration,
        "total_stalling_time": total_stalling_time,
        "avg_video_bitrate": avg_video_bitrate,
        "avg_video_framerate": avg_video_framerate,
        "avg_audio_bitrate": avg_audio_bitrate,
        "number_of_resolution_switches_up": number_of_resolution_switches_up,
        "number_of_resolution_switches_down": number_of_resolution_switches_down,
        "duration": duration
    }

    return stats