def collect_rebuffer(client_buffer_results, postgres_cursor): # process InfluxDB data x = {} for pt in client_buffer_results['client_buffer']: expt_id = int(pt['expt_id']) expt_config = retrieve_expt_config(expt_id, expt_id_cache, postgres_cursor) # index x by (abr, cc) abr_cc = (expt_config['abr'], expt_config['cc']) if abr_cc not in x: x[abr_cc] = {} session = (pt['user'], int(pt['init_id']), pt['channel'], int(pt['expt_id'])) if session not in x[abr_cc]: x[abr_cc][session] = {} x[abr_cc][session]['min_time'] = None x[abr_cc][session]['max_time'] = None x[abr_cc][session]['min_cum_rebuf'] = None x[abr_cc][session]['max_cum_rebuf'] = None # shorthand variable y = x[abr_cc][session] ts = try_parsing_time(pt['time']) cum_rebuf = float(pt['cum_rebuf']) if y['min_time'] is None or ts < y['min_time']: y['min_time'] = ts if y['max_time'] is None or ts > y['max_time']: y['max_time'] = ts if y['min_cum_rebuf'] is None or cum_rebuf < y['min_cum_rebuf']: y['min_cum_rebuf'] = cum_rebuf if y['max_cum_rebuf'] is None or cum_rebuf > y['max_cum_rebuf']: y['max_cum_rebuf'] = cum_rebuf # calculate rebuffer rate rebuffer = {} for abr_cc in x: total_play = 0 total_rebuf = 0 for session in x[abr_cc]: # shorthand variable y = x[abr_cc][session] total_play += (y['max_time'] - y['min_time']).total_seconds() total_rebuf += y['max_cum_rebuf'] - y['min_cum_rebuf'] if total_play == 0: sys.exit('Error: total play time is 0') rebuf_rate = total_rebuf / total_play rebuffer[abr_cc] = rebuf_rate * 100 return rebuffer
def calculate_trans_times(video_sent_results, video_acked_results, cc, postgres_cursor): d = {} last_video_ts = {} for pt in video_sent_results['video_sent']: expt_id = int(pt['expt_id']) session = (pt['user'], int(pt['init_id']), pt['channel'], expt_id) # filter data points by congestion control expt_config = retrieve_expt_config(expt_id, expt_id_cache, postgres_cursor) if cc is not None and expt_config['cc'] != cc: continue if session not in d: d[session] = {} last_video_ts[session] = None video_ts = int(pt['video_ts']) if last_video_ts[session] is not None: if video_ts != last_video_ts[session] + VIDEO_DURATION: sys.stderr.write('Warning in session {}: video_ts={}\n'.format( session, video_ts)) continue last_video_ts[session] = video_ts d[session][video_ts] = {} dsv = d[session][video_ts] # short name dsv['sent_ts'] = try_parsing_time(pt['time']) dsv['size'] = float(pt['size']) / PKT_BYTES # bytes -> packets # byte/second -> packet/second dsv['delivery_rate'] = float(pt['delivery_rate']) / PKT_BYTES dsv['cwnd'] = float(pt['cwnd']) dsv['in_flight'] = float(pt['in_flight']) dsv['min_rtt'] = float(pt['min_rtt']) / MILLION # us -> s dsv['rtt'] = float(pt['rtt']) / MILLION # us -> s # dsv['ssim_index'] = get_ssim_index(pt) for pt in video_acked_results['video_acked']: expt_id = int(pt['expt_id']) session = (pt['user'], int(pt['init_id']), pt['channel'], expt_id) # filter data points by congestion control expt_config = retrieve_expt_config(expt_id, expt_id_cache, postgres_cursor) if cc is not None and expt_config['cc'] != cc: continue if session not in d: sys.stderr.write('Warning: ignored session {}\n'.format(session)) continue video_ts = int(pt['video_ts']) if video_ts not in d[session]: sys.stderr.write('Warning: ignored acked video_ts {} in the ' 'session {}\n'.format(video_ts, session)) continue dsv = d[session][video_ts] # short name # calculate transmission time sent_ts = dsv['sent_ts'] acked_ts = try_parsing_time(pt['time']) dsv['acked_ts'] = acked_ts dsv['trans_time'] = (acked_ts - sent_ts).total_seconds() return d
def collect_rebuffer(client_buffer_results, postgres_cursor): # process InfluxDB data x = {} for pt in client_buffer_results['client_buffer']: expt_id = int(pt['expt_id']) expt_config = retrieve_expt_config(expt_id, expt_id_cache, postgres_cursor) # index x by (abr, cc) abr_cc = (expt_config['abr'], expt_config['cc']) if abr_cc not in x: x[abr_cc] = {} # index x[abr_cc] by session session = (pt['user'], int(pt['init_id']), pt['channel'], int(pt['expt_id'])) if session not in x[abr_cc]: x[abr_cc][session] = {} x[abr_cc][session]['min_play_time'] = None x[abr_cc][session]['max_play_time'] = None x[abr_cc][session]['min_cum_rebuf'] = None x[abr_cc][session]['max_cum_rebuf'] = None y = x[abr_cc][session] # short name ts = try_parsing_time(pt['time']) cum_rebuf = float(pt['cum_rebuf']) if pt['event'] == 'startup': y['min_play_time'] = ts y['min_cum_rebuf'] = cum_rebuf if y['max_play_time'] is None or ts > y['max_play_time']: y['max_play_time'] = ts if y['max_cum_rebuf'] is None or cum_rebuf > y['max_cum_rebuf']: y['max_cum_rebuf'] = cum_rebuf # calculate rebuffer rate rebuffer = {} total_play = {} total_rebuf = {} for abr_cc in x: abr_cc_play = 0 abr_cc_rebuf = 0 for session in x[abr_cc]: y = x[abr_cc][session] # short name if y['min_play_time'] is None or y['min_cum_rebuf'] is None: continue sess_play = (y['max_play_time'] - y['min_play_time']).total_seconds() sess_rebuf = y['max_cum_rebuf'] - y['min_cum_rebuf'] # exclude too short sessions if sess_play < 2: continue # TODO: identify and ignore outliers if sess_rebuf / sess_play > 0.5: continue abr_cc_play += sess_play abr_cc_rebuf += sess_rebuf if abr_cc_play == 0: sys.exit('Error: {}: total play time is 0'.format(abr_cc)) total_play[abr_cc] = abr_cc_play total_rebuf[abr_cc] = abr_cc_rebuf rebuf_rate = abr_cc_rebuf / abr_cc_play rebuffer[abr_cc] = rebuf_rate * 100 return rebuffer, total_play, total_rebuf
def collect_rebuffer(client_buffer_results, postgres_cursor): # process InfluxDB data last_ts = {} last_cum_rebuf = {} outlier_time = {} excluded_sessions = {} x = {} for pt in client_buffer_results['client_buffer']: expt_id = int(pt['expt_id']) expt_config = retrieve_expt_config(expt_id, expt_id_cache, postgres_cursor) # index x by (abr, cc) abr_cc = (expt_config['abr'], expt_config['cc']) if abr_cc not in x: x[abr_cc] = {} # index x[abr_cc] by session session = (pt['user'], int(pt['init_id']), pt['channel'], int(pt['expt_id'])) if session not in last_ts: last_ts[session] = None if session not in last_cum_rebuf: last_cum_rebuf[session] = None if session not in outlier_time: outlier_time[session] = None if session not in x[abr_cc]: if session in excluded_sessions: # ignore sessions that were intentionally removed from x[abr_cc] continue x[abr_cc][session] = {} x[abr_cc][session]['min_play_time'] = None x[abr_cc][session]['max_play_time'] = None x[abr_cc][session]['min_cum_rebuf'] = None x[abr_cc][session]['max_cum_rebuf'] = None y = x[abr_cc][session] # short name ts = try_parsing_time(pt['time']) buf = float(pt['buffer']) cum_rebuf = float(pt['cum_rebuf']) # verify that time is basically continuous in the same session if last_ts[session] is not None: diff = (ts - last_ts[session]).total_seconds() if diff > 60: # a new but different session should be ignored continue last_ts[session] = ts # identify outliers: exclude the session if there is one-minute long # duration when buffer is lower than 1 second if buf > 1: outlier_time[session] = None else: if outlier_time[session] is None: outlier_time[session] = ts else: diff = (ts - outlier_time[session]).total_seconds() if diff > 30: print('Outlier session', abr_cc, session) del x[abr_cc][session] excluded_sessions[session] = True continue # identify stalls caused by slow video decoding if last_cum_rebuf[session] is not None: if buf > 5 and cum_rebuf > last_cum_rebuf[session] + 0.25: # should not have stalls print('Decoding stalls', abr_cc, session) del x[abr_cc][session] excluded_sessions[session] = True continue last_cum_rebuf[session] = cum_rebuf if pt['event'] == 'startup': y['min_play_time'] = ts y['min_cum_rebuf'] = cum_rebuf if y['max_play_time'] is None or ts > y['max_play_time']: y['max_play_time'] = ts if y['max_cum_rebuf'] is None or cum_rebuf > y['max_cum_rebuf']: y['max_cum_rebuf'] = cum_rebuf # calculate rebuffer rate rebuffer = {} total_play = {} total_rebuf = {} for abr_cc in x: abr_cc_play = 0 abr_cc_rebuf = 0 for session in x[abr_cc]: y = x[abr_cc][session] # short name if y['min_play_time'] is None or y['min_cum_rebuf'] is None: continue sess_play = (y['max_play_time'] - y['min_play_time']).total_seconds() sess_rebuf = y['max_cum_rebuf'] - y['min_cum_rebuf'] # exclude short sessions if sess_play < 5: continue abr_cc_play += sess_play abr_cc_rebuf += sess_rebuf if abr_cc_play == 0: sys.exit('Error: {}: total play time is 0'.format(abr_cc)) total_play[abr_cc] = abr_cc_play total_rebuf[abr_cc] = abr_cc_rebuf rebuf_rate = abr_cc_rebuf / abr_cc_play rebuffer[abr_cc] = rebuf_rate * 100 return rebuffer, total_play, total_rebuf
def collect_buffer_data(client_buffer_results): d = {} # indexed by session excluded_sessions = {} last_ts = {} last_buf = {} last_cum_rebuf = {} last_low_buf = {} for pt in client_buffer_results['client_buffer']: session = (pt['user'], int(pt['init_id']), int(pt['expt_id'])) if session in excluded_sessions: continue if session not in d: d[session] = {} d[session]['min_play_time'] = None d[session]['max_play_time'] = None d[session]['min_cum_rebuf'] = None d[session]['max_cum_rebuf'] = None ds = d[session] # short name if session not in last_ts: last_ts[session] = None if session not in last_buf: last_buf[session] = None if session not in last_cum_rebuf: last_cum_rebuf[session] = None if session not in last_low_buf: last_low_buf[session] = None ts = try_parsing_time(pt['time']) buf = float(pt['buffer']) cum_rebuf = float(pt['cum_rebuf']) # update d[session] if pt['event'] == 'startup': ds['min_play_time'] = ts ds['min_cum_rebuf'] = cum_rebuf if ds['min_play_time'] is None or ds['min_cum_rebuf'] is None: # wait until 'startup' is found continue if ds['max_play_time'] is None or ts > ds['max_play_time']: ds['max_play_time'] = ts if ds['max_cum_rebuf'] is None or cum_rebuf > ds['max_cum_rebuf']: ds['max_cum_rebuf'] = cum_rebuf # verify that time is basically successive in the same session if last_ts[session] is not None: diff = (ts - last_ts[session]).total_seconds() if diff > 60: # ambiguous / suspicious session print('Ambiguous session', session) excluded_sessions[session] = True continue # identify outliers: exclude the sessions if there is a long rebuffer? if last_low_buf[session] is not None: diff = (ts - last_low_buf[session]).total_seconds() if diff > 30: print('Outlier session', session) excluded_sessions[session] = True continue # identify stalls caused by slow video decoding if last_buf[session] is not None and last_cum_rebuf[ session] is not None: if (buf > 5 and last_buf[session] > 5 and cum_rebuf > last_cum_rebuf[session] + 0.25): print('Decoding stalls', session) excluded_sessions[session] = True continue # update last_XXX last_ts[session] = ts last_buf[session] = buf last_cum_rebuf[session] = cum_rebuf if buf > 0.1: last_low_buf[session] = None else: if last_low_buf[session] is None: last_low_buf[session] = ts ret = {} # indexed by session # second pass to exclude short sessions for session in d: if session in excluded_sessions: continue ds = d[session] if ds['min_play_time'] is None or ds['min_cum_rebuf'] is None: # no 'startup' is found print('No startup found', session) continue sess_play = (ds['max_play_time'] - ds['min_play_time']).total_seconds() # exclude short sessions if sess_play < 5: continue sess_rebuf = ds['max_cum_rebuf'] - ds['min_cum_rebuf'] if sess_rebuf > 300: print('Warning: bad session (rebuffer > 5min)', session) if session not in ret: ret[session] = {} ret[session]['play'] = sess_play ret[session]['rebuf'] = sess_rebuf ret[session]['startup'] = ds['min_cum_rebuf'] print('Valid session count', len(ret)) return ret