py: cam: Convert ctx and state to classes

Convert ctx and state dicts to classes. No functional changes.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
Tomi Valkeinen 2022-05-27 17:44:27 +03:00 committed by Laurent Pinchart
parent bb1cbd53d2
commit 6eb1143e27
5 changed files with 380 additions and 398 deletions

View file

@ -6,6 +6,7 @@
# \todo Convert ctx and state dicts to proper classes, and move relevant # \todo Convert ctx and state dicts to proper classes, and move relevant
# functions to those classes. # functions to those classes.
from typing import Any
import argparse import argparse
import binascii import binascii
import libcamera as libcam import libcamera as libcam
@ -14,64 +15,52 @@ import sys
import traceback import traceback
class CustomAction(argparse.Action): class CameraContext:
def __init__(self, option_strings, dest, **kwargs): camera: libcam.Camera
super().__init__(option_strings, dest, default={}, **kwargs) id: str
idx: int
def __call__(self, parser, namespace, values, option_string=None): opt_stream: str
if len(namespace.camera) == 0: opt_strict_formats: bool
print(f'Option {option_string} requires a --camera context') opt_crc: bool
sys.exit(-1) opt_metadata: bool
opt_save_frames: bool
opt_capture: int
if self.type == bool: stream_names: dict[libcam.Stream, str]
values = True streams: list[libcam.Stream]
allocator: libcam.FrameBufferAllocator
requests: list[libcam.Request]
reqs_queued: int
reqs_completed: int
last: int = 0
fps: float
current = namespace.camera[-1] def __init__(self, camera, idx):
self.camera = camera
self.idx = idx
self.id = 'cam' + str(idx)
self.reqs_queued = 0
self.reqs_completed = 0
data = getattr(namespace, self.dest) def do_cmd_list_props(self):
print('Properties for', self.id)
if self.nargs == '+': for name, prop in self.camera.properties.items():
if current not in data:
data[current] = []
data[current] += values
else:
data[current] = values
def do_cmd_list(cm):
print('Available cameras:')
for idx, c in enumerate(cm.cameras):
print(f'{idx + 1}: {c.id}')
def do_cmd_list_props(ctx):
camera = ctx['camera']
print('Properties for', ctx['id'])
for name, prop in camera.properties.items():
print('\t{}: {}'.format(name, prop)) print('\t{}: {}'.format(name, prop))
def do_cmd_list_controls(self):
print('Controls for', self.id)
def do_cmd_list_controls(ctx): for name, prop in self.camera.controls.items():
camera = ctx['camera']
print('Controls for', ctx['id'])
for name, prop in camera.controls.items():
print('\t{}: {}'.format(name, prop)) print('\t{}: {}'.format(name, prop))
def do_cmd_info(self):
def do_cmd_info(ctx): print('Stream info for', self.id)
camera = ctx['camera']
print('Stream info for', ctx['id'])
roles = [libcam.StreamRole.Viewfinder] roles = [libcam.StreamRole.Viewfinder]
camconfig = camera.generate_configuration(roles) camconfig = self.camera.generate_configuration(roles)
if camconfig is None: if camconfig is None:
raise Exception('Generating config failed') raise Exception('Generating config failed')
@ -85,23 +74,17 @@ def do_cmd_info(ctx):
for size in formats.sizes(fmt): for size in formats.sizes(fmt):
print('\t -', size) print('\t -', size)
def acquire(self):
self.camera.acquire()
def acquire(ctx): def release(self):
camera = ctx['camera'] self.camera.release()
camera.acquire() def __parse_streams(self):
def release(ctx):
camera = ctx['camera']
camera.release()
def parse_streams(ctx):
streams = [] streams = []
for stream_desc in ctx['opt-stream']: for stream_desc in self.opt_stream:
stream_opts: dict[str, Any]
stream_opts = {'role': libcam.StreamRole.Viewfinder} stream_opts = {'role': libcam.StreamRole.Viewfinder}
for stream_opt in stream_desc.split(','): for stream_opt in stream_desc.split(','):
@ -145,15 +128,12 @@ def parse_streams(ctx):
return streams return streams
def configure(self):
def configure(ctx): streams = self.__parse_streams()
camera = ctx['camera']
streams = parse_streams(ctx)
roles = [opts['role'] for opts in streams] roles = [opts['role'] for opts in streams]
camconfig = camera.generate_configuration(roles) camconfig = self.camera.generate_configuration(roles)
if camconfig is None: if camconfig is None:
raise Exception('Generating config failed') raise Exception('Generating config failed')
@ -175,32 +155,29 @@ def configure(ctx):
print('Camera configuration invalid') print('Camera configuration invalid')
exit(-1) exit(-1)
elif stat == libcam.CameraConfiguration.Status.Adjusted: elif stat == libcam.CameraConfiguration.Status.Adjusted:
if ctx['opt-strict-formats']: if self.opt_strict_formats:
print('Adjusting camera configuration disallowed by --strict-formats argument') print('Adjusting camera configuration disallowed by --strict-formats argument')
exit(-1) exit(-1)
print('Camera configuration adjusted') print('Camera configuration adjusted')
r = camera.configure(camconfig) r = self.camera.configure(camconfig)
if r != 0: if r != 0:
raise Exception('Configure failed') raise Exception('Configure failed')
ctx['stream-names'] = {} self.stream_names = {}
ctx['streams'] = [] self.streams = []
for idx, stream_config in enumerate(camconfig): for idx, stream_config in enumerate(camconfig):
stream = stream_config.stream stream = stream_config.stream
ctx['streams'].append(stream) self.streams.append(stream)
ctx['stream-names'][stream] = 'stream' + str(idx) self.stream_names[stream] = 'stream' + str(idx)
print('{}-{}: stream config {}'.format(ctx['id'], ctx['stream-names'][stream], stream.configuration)) print('{}-{}: stream config {}'.format(self.id, self.stream_names[stream], stream.configuration))
def alloc_buffers(self):
allocator = libcam.FrameBufferAllocator(self.camera)
def alloc_buffers(ctx): for stream in self.streams:
camera = ctx['camera']
allocator = libcam.FrameBufferAllocator(camera)
for idx, stream in enumerate(ctx['streams']):
ret = allocator.allocate(stream) ret = allocator.allocate(stream)
if ret < 0: if ret < 0:
print('Cannot allocate buffers') print('Cannot allocate buffers')
@ -208,30 +185,27 @@ def alloc_buffers(ctx):
allocated = len(allocator.buffers(stream)) allocated = len(allocator.buffers(stream))
print('{}-{}: Allocated {} buffers'.format(ctx['id'], ctx['stream-names'][stream], allocated)) print('{}-{}: Allocated {} buffers'.format(self.id, self.stream_names[stream], allocated))
ctx['allocator'] = allocator self.allocator = allocator
def create_requests(self):
def create_requests(ctx): self.requests = []
camera = ctx['camera']
ctx['requests'] = []
# Identify the stream with the least number of buffers # Identify the stream with the least number of buffers
num_bufs = min([len(ctx['allocator'].buffers(stream)) for stream in ctx['streams']]) num_bufs = min([len(self.allocator.buffers(stream)) for stream in self.streams])
requests = [] requests = []
for buf_num in range(num_bufs): for buf_num in range(num_bufs):
request = camera.create_request(ctx['idx']) request = self.camera.create_request(self.idx)
if request is None: if request is None:
print('Can not create request') print('Can not create request')
exit(-1) exit(-1)
for stream in ctx['streams']: for stream in self.streams:
buffers = ctx['allocator'].buffers(stream) buffers = self.allocator.buffers(stream)
buffer = buffers[buf_num] buffer = buffers[buf_num]
ret = request.add_buffer(stream, buffer) ret = request.add_buffer(stream, buffer)
@ -241,93 +215,67 @@ def create_requests(ctx):
requests.append(request) requests.append(request)
ctx['requests'] = requests self.requests = requests
def start(self):
self.camera.start()
def stop(self):
self.camera.stop()
def queue_requests(self):
for request in self.requests:
self.camera.queue_request(request)
self.reqs_queued += 1
del self.requests
def start(ctx): class CaptureState:
camera = ctx['camera'] cm: libcam.CameraManager
contexts: list[CameraContext]
renderer: Any
camera.start() def __init__(self, cm, contexts):
self.cm = cm
self.contexts = contexts
# Called from renderer when there is a libcamera event
def stop(ctx): def event_handler(self):
camera = ctx['camera']
camera.stop()
def queue_requests(ctx):
camera = ctx['camera']
for request in ctx['requests']:
camera.queue_request(request)
ctx['reqs-queued'] += 1
del ctx['requests']
def capture_init(contexts):
for ctx in contexts:
acquire(ctx)
for ctx in contexts:
configure(ctx)
for ctx in contexts:
alloc_buffers(ctx)
for ctx in contexts:
create_requests(ctx)
def capture_start(contexts):
for ctx in contexts:
start(ctx)
for ctx in contexts:
queue_requests(ctx)
# Called from renderer when there is a libcamera event
def event_handler(state):
try: try:
cm = state['cm'] self.cm.read_event()
contexts = state['contexts']
cm.read_event() reqs = self.cm.get_ready_requests()
reqs = cm.get_ready_requests()
for req in reqs: for req in reqs:
ctx = next(ctx for ctx in contexts if ctx['idx'] == req.cookie) ctx = next(ctx for ctx in self.contexts if ctx.idx == req.cookie)
request_handler(state, ctx, req) self.__request_handler(ctx, req)
running = any(ctx['reqs-completed'] < ctx['opt-capture'] for ctx in contexts) running = any(ctx.reqs_completed < ctx.opt_capture for ctx in self.contexts)
return running return running
except Exception: except Exception:
traceback.print_exc() traceback.print_exc()
return False return False
def __request_handler(self, ctx, req):
def request_handler(state, ctx, req):
if req.status != libcam.Request.Status.Complete: if req.status != libcam.Request.Status.Complete:
raise Exception('{}: Request failed: {}'.format(ctx['id'], req.status)) raise Exception('{}: Request failed: {}'.format(ctx.id, req.status))
buffers = req.buffers buffers = req.buffers
# Compute the frame rate. The timestamp is arbitrarily retrieved from # Compute the frame rate. The timestamp is arbitrarily retrieved from
# the first buffer, as all buffers should have matching timestamps. # the first buffer, as all buffers should have matching timestamps.
ts = buffers[next(iter(buffers))].metadata.timestamp ts = buffers[next(iter(buffers))].metadata.timestamp
last = ctx.get('last', 0) last = ctx.last
fps = 1000000000.0 / (ts - last) if (last != 0 and (ts - last) != 0) else 0 fps = 1000000000.0 / (ts - last) if (last != 0 and (ts - last) != 0) else 0
ctx['last'] = ts ctx.last = ts
ctx['fps'] = fps ctx.fps = fps
for stream, fb in buffers.items(): for stream, fb in buffers.items():
stream_name = ctx['stream-names'][stream] stream_name = ctx.stream_names[stream]
crcs = [] crcs = []
if ctx['opt-crc']: if ctx.opt_crc:
with libcamera.utils.MappedFrameBuffer(fb) as mfb: with libcamera.utils.MappedFrameBuffer(fb) as mfb:
plane_crcs = [binascii.crc32(p) for p in mfb.planes] plane_crcs = [binascii.crc32(p) for p in mfb.planes]
crcs.append(plane_crcs) crcs.append(plane_crcs)
@ -336,57 +284,102 @@ def request_handler(state, ctx, req):
print('{:.6f} ({:.2f} fps) {}-{}: seq {}, bytes {}, CRCs {}' print('{:.6f} ({:.2f} fps) {}-{}: seq {}, bytes {}, CRCs {}'
.format(ts / 1000000000, fps, .format(ts / 1000000000, fps,
ctx['id'], stream_name, ctx.id, stream_name,
meta.sequence, meta.bytesused, meta.sequence, meta.bytesused,
crcs)) crcs))
if ctx['opt-metadata']: if ctx.opt_metadata:
reqmeta = req.metadata reqmeta = req.metadata
for ctrl, val in reqmeta.items(): for ctrl, val in reqmeta.items():
print(f'\t{ctrl} = {val}') print(f'\t{ctrl} = {val}')
if ctx['opt-save-frames']: if ctx.opt_save_frames:
with libcamera.utils.MappedFrameBuffer(fb) as mfb: with libcamera.utils.MappedFrameBuffer(fb) as mfb:
filename = 'frame-{}-{}-{}.data'.format(ctx['id'], stream_name, ctx['reqs-completed']) filename = 'frame-{}-{}-{}.data'.format(ctx.id, stream_name, ctx.reqs_completed)
with open(filename, 'wb') as f: with open(filename, 'wb') as f:
for p in mfb.planes: for p in mfb.planes:
f.write(p) f.write(p)
state['renderer'].request_handler(ctx, req) self.renderer.request_handler(ctx, req)
ctx['reqs-completed'] += 1 ctx.reqs_completed += 1
# Called from renderer when it has finished with a request
# Called from renderer when it has finished with a request def request_processed(self, ctx, req):
def request_prcessed(ctx, req): if ctx.reqs_queued < ctx.opt_capture:
camera = ctx['camera']
if ctx['reqs-queued'] < ctx['opt-capture']:
req.reuse() req.reuse()
camera.queue_request(req) ctx.camera.queue_request(req)
ctx['reqs-queued'] += 1 ctx.reqs_queued += 1
def __capture_init(self):
for ctx in self.contexts:
ctx.acquire()
for ctx in self.contexts:
ctx.configure()
for ctx in self.contexts:
ctx.alloc_buffers()
for ctx in self.contexts:
ctx.create_requests()
def __capture_start(self):
for ctx in self.contexts:
ctx.start()
for ctx in self.contexts:
ctx.queue_requests()
def __capture_deinit(self):
for ctx in self.contexts:
ctx.stop()
for ctx in self.contexts:
ctx.release()
def do_cmd_capture(self):
self.__capture_init()
self.renderer.setup()
self.__capture_start()
self.renderer.run()
self.__capture_deinit()
def capture_deinit(contexts): class CustomAction(argparse.Action):
for ctx in contexts: def __init__(self, option_strings, dest, **kwargs):
stop(ctx) super().__init__(option_strings, dest, default={}, **kwargs)
for ctx in contexts: def __call__(self, parser, namespace, values, option_string=None):
release(ctx) if len(namespace.camera) == 0:
print(f'Option {option_string} requires a --camera context')
sys.exit(-1)
if self.type == bool:
values = True
current = namespace.camera[-1]
data = getattr(namespace, self.dest)
if self.nargs == '+':
if current not in data:
data[current] = []
data[current] += values
else:
data[current] = values
def do_cmd_capture(state): def do_cmd_list(cm):
capture_init(state['contexts']) print('Available cameras:')
renderer = state['renderer'] for idx, c in enumerate(cm.cameras):
print(f'{idx + 1}: {c.id}')
renderer.setup()
capture_start(state['contexts'])
renderer.run()
capture_deinit(state['contexts'])
def main(): def main():
@ -422,39 +415,28 @@ def main():
print('Unable to find camera', cam_idx) print('Unable to find camera', cam_idx)
return -1 return -1
contexts.append({ ctx = CameraContext(camera, cam_idx)
'camera': camera, ctx.opt_capture = args.capture.get(cam_idx, 0)
'idx': cam_idx, ctx.opt_crc = args.crc.get(cam_idx, False)
'id': 'cam' + str(cam_idx), ctx.opt_save_frames = args.save_frames.get(cam_idx, False)
'reqs-queued': 0, ctx.opt_metadata = args.metadata.get(cam_idx, False)
'reqs-completed': 0, ctx.opt_strict_formats = args.strict_formats.get(cam_idx, False)
'opt-capture': args.capture.get(cam_idx, False), ctx.opt_stream = args.stream.get(cam_idx, ['role=viewfinder'])
'opt-crc': args.crc.get(cam_idx, False), contexts.append(ctx)
'opt-save-frames': args.save_frames.get(cam_idx, False),
'opt-metadata': args.metadata.get(cam_idx, False),
'opt-strict-formats': args.strict_formats.get(cam_idx, False),
'opt-stream': args.stream.get(cam_idx, ['role=viewfinder']),
})
for ctx in contexts: for ctx in contexts:
print('Using camera {} as {}'.format(ctx['camera'].id, ctx['id'])) print('Using camera {} as {}'.format(ctx.camera.id, ctx.id))
for ctx in contexts: for ctx in contexts:
if args.list_properties: if args.list_properties:
do_cmd_list_props(ctx) ctx.do_cmd_list_props()
if args.list_controls: if args.list_controls:
do_cmd_list_controls(ctx) ctx.do_cmd_list_controls()
if args.info: if args.info:
do_cmd_info(ctx) ctx.do_cmd_info()
if args.capture: if args.capture:
state = CaptureState(cm, contexts)
state = {
'cm': cm,
'contexts': contexts,
'event_handler': event_handler,
'request_prcessed': request_prcessed,
}
if args.renderer == 'null': if args.renderer == 'null':
import cam_null import cam_null
@ -472,9 +454,9 @@ def main():
print('Bad renderer', args.renderer) print('Bad renderer', args.renderer)
return -1 return -1
state['renderer'] = renderer state.renderer = renderer
do_cmd_capture(state) state.do_cmd_capture()
return 0 return 0

View file

@ -10,8 +10,8 @@ class KMSRenderer:
def __init__(self, state): def __init__(self, state):
self.state = state self.state = state
self.cm = state['cm'] self.cm = state.cm
self.contexts = state['contexts'] self.contexts = state.contexts
self.running = False self.running = False
card = pykms.Card() card = pykms.Card()
@ -92,7 +92,7 @@ class KMSRenderer:
if old: if old:
req = old['camreq'] req = old['camreq']
ctx = old['camctx'] ctx = old['camctx']
self.state['request_prcessed'](ctx, req) self.state.request_processed(ctx, req)
def queue(self, drmreq): def queue(self, drmreq):
if not self.next: if not self.next:
@ -108,7 +108,7 @@ class KMSRenderer:
idx = 0 idx = 0
for ctx in self.contexts: for ctx in self.contexts:
for stream in ctx['streams']: for stream in ctx.streams:
cfg = stream.configuration cfg = stream.configuration
fmt = cfg.pixel_format fmt = cfg.pixel_format
@ -125,7 +125,7 @@ class KMSRenderer:
'size': cfg.size, 'size': cfg.size,
}) })
for fb in ctx['allocator'].buffers(stream): for fb in ctx.allocator.buffers(stream):
w = cfg.size.width w = cfg.size.width
h = cfg.size.height h = cfg.size.height
fds = [] fds = []
@ -148,7 +148,7 @@ class KMSRenderer:
self.handle_page_flip(ev.seq, ev.time) self.handle_page_flip(ev.seq, ev.time)
def readcam(self, fd): def readcam(self, fd):
self.running = self.state['event_handler'](self.state) self.running = self.state.event_handler()
def readkey(self, fileobj): def readkey(self, fileobj):
sys.stdin.readline() sys.stdin.readline()

View file

@ -9,8 +9,8 @@ class NullRenderer:
def __init__(self, state): def __init__(self, state):
self.state = state self.state = state
self.cm = state['cm'] self.cm = state.cm
self.contexts = state['contexts'] self.contexts = state.contexts
self.running = False self.running = False
@ -37,11 +37,11 @@ class NullRenderer:
print('Exiting...') print('Exiting...')
def readcam(self, fd): def readcam(self, fd):
self.running = self.state['event_handler'](self.state) self.running = self.state.event_handler()
def readkey(self, fileobj): def readkey(self, fileobj):
sys.stdin.readline() sys.stdin.readline()
self.running = False self.running = False
def request_handler(self, ctx, req): def request_handler(self, ctx, req):
self.state['request_prcessed'](ctx, req) self.state.request_processed(ctx, req)

View file

@ -176,8 +176,8 @@ class QtRenderer:
def __init__(self, state): def __init__(self, state):
self.state = state self.state = state
self.cm = state['cm'] self.cm = state.cm
self.contexts = state['contexts'] self.contexts = state.contexts
def setup(self): def setup(self):
self.app = QtWidgets.QApplication([]) self.app = QtWidgets.QApplication([])
@ -185,7 +185,7 @@ class QtRenderer:
windows = [] windows = []
for ctx in self.contexts: for ctx in self.contexts:
for stream in ctx['streams']: for stream in ctx.streams:
window = MainWindow(ctx, stream) window = MainWindow(ctx, stream)
window.show() window.show()
windows.append(window) windows.append(window)
@ -206,7 +206,7 @@ class QtRenderer:
print('Exiting...') print('Exiting...')
def readcam(self): def readcam(self):
running = self.state['event_handler'](self.state) running = self.state.event_handler()
if not running: if not running:
self.app.quit() self.app.quit()
@ -223,7 +223,7 @@ class QtRenderer:
wnd.handle_request(stream, fb) wnd.handle_request(stream, fb)
self.state['request_prcessed'](ctx, req) self.state.request_processed(ctx, req)
def cleanup(self): def cleanup(self):
for w in self.windows: for w in self.windows:
@ -254,7 +254,7 @@ class MainWindow(QtWidgets.QWidget):
group.setLayout(groupLayout) group.setLayout(groupLayout)
controlsLayout.addWidget(group) controlsLayout.addWidget(group)
lab = QtWidgets.QLabel(ctx['id']) lab = QtWidgets.QLabel(ctx.id)
groupLayout.addWidget(lab) groupLayout.addWidget(lab)
self.frameLabel = QtWidgets.QLabel() self.frameLabel = QtWidgets.QLabel()
@ -265,7 +265,7 @@ class MainWindow(QtWidgets.QWidget):
group.setLayout(groupLayout) group.setLayout(groupLayout)
controlsLayout.addWidget(group) controlsLayout.addWidget(group)
camera = ctx['camera'] camera = ctx.camera
for k, v in camera.properties.items(): for k, v in camera.properties.items():
lab = QtWidgets.QLabel() lab = QtWidgets.QLabel()
@ -308,4 +308,4 @@ class MainWindow(QtWidgets.QWidget):
self.label.setPixmap(pix) self.label.setPixmap(pix)
self.frameLabel.setText('Queued: {}\nDone: {}\nFps: {:.2f}' self.frameLabel.setText('Queued: {}\nDone: {}\nFps: {:.2f}'
.format(ctx['reqs-queued'], ctx['reqs-completed'], ctx['fps'])) .format(ctx.reqs_queued, ctx.reqs_completed, ctx.fps))

View file

@ -142,7 +142,7 @@ class QtRenderer:
self.window = window self.window = window
def run(self): def run(self):
camnotif = QtCore.QSocketNotifier(self.state['cm'].efd, QtCore.QSocketNotifier.Read) camnotif = QtCore.QSocketNotifier(self.state.cm.efd, QtCore.QSocketNotifier.Read)
camnotif.activated.connect(lambda _: self.readcam()) camnotif.activated.connect(lambda _: self.readcam())
keynotif = QtCore.QSocketNotifier(sys.stdin.fileno(), QtCore.QSocketNotifier.Read) keynotif = QtCore.QSocketNotifier(sys.stdin.fileno(), QtCore.QSocketNotifier.Read)
@ -155,7 +155,7 @@ class QtRenderer:
print('Exiting...') print('Exiting...')
def readcam(self): def readcam(self):
running = self.state['event_handler'](self.state) running = self.state.event_handler()
if not running: if not running:
self.app.quit() self.app.quit()
@ -184,12 +184,12 @@ class MainWindow(QtWidgets.QWidget):
self.reqqueue = {} self.reqqueue = {}
self.current = {} self.current = {}
for ctx in self.state['contexts']: for ctx in self.state.contexts:
self.reqqueue[ctx['idx']] = [] self.reqqueue[ctx.idx] = []
self.current[ctx['idx']] = [] self.current[ctx.idx] = []
for stream in ctx['streams']: for stream in ctx.streams:
self.textures[stream] = None self.textures[stream] = None
num_tiles = len(self.textures) num_tiles = len(self.textures)
@ -312,12 +312,12 @@ class MainWindow(QtWidgets.QWidget):
if len(queue) == 0: if len(queue) == 0:
continue continue
ctx = next(ctx for ctx in self.state['contexts'] if ctx['idx'] == ctx_idx) ctx = next(ctx for ctx in self.state.contexts if ctx.idx == ctx_idx)
if self.current[ctx_idx]: if self.current[ctx_idx]:
old = self.current[ctx_idx] old = self.current[ctx_idx]
self.current[ctx_idx] = None self.current[ctx_idx] = None
self.state['request_prcessed'](ctx, old) self.state.request_processed(ctx, old)
next_req = queue.pop(0) next_req = queue.pop(0)
self.current[ctx_idx] = next_req self.current[ctx_idx] = next_req
@ -336,8 +336,8 @@ class MainWindow(QtWidgets.QWidget):
size = self.size() size = self.size()
for idx, ctx in enumerate(self.state['contexts']): for idx, ctx in enumerate(self.state.contexts):
for stream in ctx['streams']: for stream in ctx.streams:
if self.textures[stream] is None: if self.textures[stream] is None:
continue continue
@ -359,5 +359,5 @@ class MainWindow(QtWidgets.QWidget):
assert(b) assert(b)
def handle_request(self, ctx, req): def handle_request(self, ctx, req):
self.reqqueue[ctx['idx']].append(req) self.reqqueue[ctx.idx].append(req)
self.update() self.update()