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