mirror of https://github.com/mpv-player/mpv
167 lines
4.9 KiB
Python
Executable File
167 lines
4.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
from pyqtgraph.Qt import QtGui, QtCore
|
|
import pyqtgraph as pg
|
|
import sys
|
|
import re
|
|
|
|
filename = sys.argv[1]
|
|
|
|
events = ".*"
|
|
if len(sys.argv) > 2:
|
|
events = sys.argv[2]
|
|
event_regex = re.compile(events)
|
|
|
|
"""
|
|
This script is meant to display stats written by mpv --dump-stats=filename.
|
|
In general, each line in that file is an event of the form:
|
|
|
|
<timestamp in microseconds> <text> '#' <comment>
|
|
|
|
e.g.:
|
|
|
|
10474959 start flip #cplayer
|
|
|
|
<text> is what MP_STATS(log, "...") writes. The rest is added by msg.c.
|
|
|
|
Currently, the following event types are supported:
|
|
|
|
'signal' <name> singular event
|
|
'start' <name> start of the named event
|
|
'end' <name> end of the named event
|
|
'value' <float> <name> a normal value (as opposed to event)
|
|
'event-timed' <ts> <name> singular event at the given timestamp
|
|
'value-timed' <ts> <float> <name> a value for an event at the given timestamp
|
|
'range-timed' <ts1> <ts2> <name> like start/end, but explicit times
|
|
<name> singular event (same as 'signal')
|
|
|
|
"""
|
|
|
|
class G:
|
|
events = {}
|
|
start = None
|
|
markers = ["o", "s", "t", "d"]
|
|
curveno = {}
|
|
|
|
def find_marker():
|
|
if len(G.markers) == 0:
|
|
return "o"
|
|
m = G.markers[0]
|
|
G.markers = G.markers[1:]
|
|
return m
|
|
|
|
class Event:
|
|
pass
|
|
|
|
def get_event(event, evtype):
|
|
if event not in G.events:
|
|
e = Event()
|
|
e.name = event
|
|
e.vals = []
|
|
e.type = evtype
|
|
e.marker = "o"
|
|
if e.type == "event-signal":
|
|
e.marker = find_marker()
|
|
if not event_regex.match(e.name):
|
|
return e
|
|
G.events[event] = e
|
|
return G.events[event]
|
|
|
|
colors = [(0.0, 0.5, 0.0), (0.0, 0.0, 1.0), (0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.75, 0.75, 0), (0.0, 0.75, 0.75), (0.75, 0, 0.75)]
|
|
def mkColor(t):
|
|
return pg.mkColor(int(t[0] * 255), int(t[1] * 255), int(t[2] * 255))
|
|
|
|
SCALE = 1e6 # microseconds to seconds
|
|
|
|
for line in [line.split("#")[0].strip() for line in open(filename, "r")]:
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
ts, event = line.split(" ", 1)
|
|
ts = int(ts) / SCALE
|
|
if G.start is None:
|
|
G.start = ts
|
|
ts = ts - G.start
|
|
if event.startswith("start "):
|
|
e = get_event(event[6:], "event")
|
|
e.vals.append((ts, 0))
|
|
e.vals.append((ts, 1))
|
|
elif event.startswith("end "):
|
|
e = get_event(event[4:], "event")
|
|
e.vals.append((ts, 1))
|
|
e.vals.append((ts, 0))
|
|
elif event.startswith("value "):
|
|
_, val, name = event.split(" ", 2)
|
|
val = float(val)
|
|
e = get_event(name, "value")
|
|
e.vals.append((ts, val))
|
|
elif event.startswith("event-timed "):
|
|
_, val, name = event.split(" ", 2)
|
|
val = int(val) / SCALE - G.start
|
|
e = get_event(name, "event-signal")
|
|
e.vals.append((val, 1))
|
|
elif event.startswith("range-timed "):
|
|
_, ts1, ts2, name = event.split(" ", 3)
|
|
ts1 = int(ts1) / SCALE - G.start
|
|
ts2 = int(ts2) / SCALE - G.start
|
|
e = get_event(name, "event")
|
|
e.vals.append((ts1, 0))
|
|
e.vals.append((ts1, 1))
|
|
e.vals.append((ts2, 1))
|
|
e.vals.append((ts2, 0))
|
|
elif event.startswith("value-timed "):
|
|
_, tsval, val, name = event.split(" ", 3)
|
|
tsval = int(tsval) / SCALE - G.start
|
|
val = float(val)
|
|
e = get_event(name, "value")
|
|
e.vals.append((tsval, val))
|
|
elif event.startswith("signal "):
|
|
name = event.split(" ", 2)[1]
|
|
e = get_event(name, "event-signal")
|
|
e.vals.append((ts, 1))
|
|
else:
|
|
e = get_event(event, "event-signal")
|
|
e.vals.append((ts, 1))
|
|
|
|
# deterministically sort them; make sure the legend is sorted too
|
|
G.sevents = list(G.events.values())
|
|
G.sevents.sort(key=lambda x: x.name)
|
|
hasval = False
|
|
for e, index in zip(G.sevents, range(len(G.sevents))):
|
|
m = len(G.sevents)
|
|
if e.type == "value":
|
|
hasval = True
|
|
else:
|
|
e.vals = [(x, y * (m - index) / m) for (x, y) in e.vals]
|
|
|
|
pg.setConfigOption('background', 'w')
|
|
pg.setConfigOption('foreground', 'k')
|
|
app = QtGui.QApplication([])
|
|
win = pg.GraphicsWindow()
|
|
#win.resize(1500, 900)
|
|
|
|
ax = [None, None]
|
|
plots = 2 if hasval else 1
|
|
ax[0] = win.addPlot()
|
|
if hasval:
|
|
win.nextRow()
|
|
ax[1] = win.addPlot()
|
|
ax[1].setXLink(ax[0])
|
|
for cur in ax:
|
|
if cur is not None:
|
|
cur.addLegend(offset = (-1, 1))
|
|
for e in G.sevents:
|
|
cur = ax[1 if e.type == "value" else 0]
|
|
if not cur in G.curveno:
|
|
G.curveno[cur] = 0
|
|
args = {'name': e.name,'antialias':True}
|
|
color = mkColor(colors[G.curveno[cur] % len(colors)])
|
|
if e.type == "event-signal":
|
|
args['symbol'] = e.marker
|
|
args['symbolBrush'] = pg.mkBrush(color, width=0)
|
|
else:
|
|
args['pen'] = pg.mkPen(color, width=0)
|
|
G.curveno[cur] += 1
|
|
n = cur.plot([x for x,y in e.vals], [y for x,y in e.vals], **args)
|
|
|
|
QtGui.QApplication.instance().exec_()
|