tools: replay: fix wrong timestamps for multiple device replay
authorPeter Hutterer <peter.hutterer@who-t.net>
Mon, 25 Nov 2019 04:47:42 +0000 (14:47 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Mon, 25 Nov 2019 23:06:09 +0000 (23:06 +0000)
When multiple devices are recorded, the event times are offset from a global
baseline. Each device thus has a different offset for the first event. To
replay correctly, we must figure out the offset of the first event (across all
devices) and use that for all of them.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
tools/libinput-replay

index 4f80fadfe31fab62b5aabd77772e46b610b09345..ead331ea52b939ff00ae415857976fca86963a1a 100755 (executable)
@@ -111,8 +111,10 @@ def replay(device, verbose):
         return
     uinput = device['__uinput']
 
-    offset = time.time()
-    handled_first_event = False
+    # The first event may have a nonzero offset but we want to replay
+    # immediately regardless. When replaying multiple devices, the first
+    # offset is the offset from the first event on any device.
+    offset = time.time() - device['__first_event_offset']
 
     # each 'evdev' set contains one SYN_REPORT so we only need to check for
     # the time offset once per event
@@ -123,13 +125,6 @@ def replay(device, verbose):
             continue
 
         (sec, usec, evtype, evcode, value) = evdev[0]
-
-        # The first event may have a nonzero offset but we want to replay
-        # immediately regardless.
-        if not handled_first_event:
-            offset -= sec + usec / 1.e6
-            handled_first_event = True
-
         evtime = sec + usec / 1e6 + offset
         now = time.time()
 
@@ -142,6 +137,22 @@ def replay(device, verbose):
             print_events(uinput.devnode, device['__index'], evs)
 
 
+def first_timestamp(device):
+    try:
+        events = fetch(device, 'events')
+        if events is None:
+            raise YamlException('No events from this device')
+
+        evdev = fetch(events[0], 'evdev')
+        (sec, usec, *_) = evdev[0]
+
+        return sec + usec / 1.e6
+
+    except YamlException:
+        import math
+        return math.inf
+
+
 def wrap(func, *args):
     try:
         func(*args)
@@ -152,11 +163,16 @@ def wrap(func, *args):
 def loop(args, recording):
     devices = fetch(recording, 'devices')
 
+    # All devices need to start replaying at the same time, so let's find
+    # the very first event and offset everything by that timestamp.
+    toffset = min([first_timestamp(d) for d in devices])
+
     for idx, d in enumerate(devices):
         uinput = create(d)
         print('{}: {}'.format(uinput.devnode, uinput.name))
         d['__uinput'] = uinput  # cheaper to hide it in the dict then work around it
         d['__index'] = idx
+        d['__first_event_offset'] = toffset
 
     while True:
         input('Hit enter to start replaying')