From patchwork Tue May 24 11:46:04 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 16020 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 75B82C3274 for ; Tue, 24 May 2022 11:46:52 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0FF1F6569F; Tue, 24 May 2022 13:46:52 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1653392812; bh=A0jcK9k2ooiMmYrbeoitDMJX2pKECCk+9tbqDLkqz6Q=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=uszj2TpLUQgBj1tKZpiC8wqsjbzg/0PYTfIM0cHassYt5MW2q3OSbXmtdubaRN2KV 8um90WKSQBFsys2tV6JoLkrjH8R79RBbwo6EVTR6Wy12eV5ZC9tJvVxyjN5UKtXEkG 7Ai6odHLoLxUxfAkor6sKY5BTs+S1+IVARsSypmISbEtteNR2soSRFidqke2avfenL 86M/bX/XrIuTDDV9oosylnzrtpt63vGz441QKgm3hEWKCQp5IktxnUIR6rVt+QjgTu OfPSe/UieOb2ppDWUkY59gWLJZed1iTcAKuQuphQUieNWGxkSL6hEWCspl6ACNYtCh 6DuB8MJh3WTIw== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DE50E65676 for ; Tue, 24 May 2022 13:46:36 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="vRgoqxob"; dkim-atps=neutral Received: from deskari.lan (91-156-85-209.elisa-laajakaista.fi [91.156.85.209]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3CA8E59D; Tue, 24 May 2022 13:46:36 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1653392796; bh=A0jcK9k2ooiMmYrbeoitDMJX2pKECCk+9tbqDLkqz6Q=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vRgoqxob49QDInd9um1hTasJXr2zHRp8W6mQQihWbtrbN13XhhLD+IKOh7GH5MKLx b1klp4cAEczX10rCWXn1Fhl40/3uMazy1Zx3eSQaNvSiKY/M3Hh7RcxZlDO/IEKB2D CMtolC+z72e+iOgLxOZXUTmAhqfN9GemQgWsz1V0= To: libcamera-devel@lists.libcamera.org, David Plowman , Kieran Bingham , Laurent Pinchart , Jacopo Mondi Date: Tue, 24 May 2022 14:46:04 +0300 Message-Id: <20220524114610.41848-14-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220524114610.41848-1-tomi.valkeinen@ideasonboard.com> References: <20220524114610.41848-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 13/19] py: cam: Convert ctx and state to classes X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Cc: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" From: Tomi Valkeinen Convert ctx and state dicts to classes. No functional changes. Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart --- src/py/cam/cam.py | 585 +++++++++++++++++++++-------------------- src/py/cam/cam_kms.py | 12 +- src/py/cam/cam_null.py | 8 +- src/py/cam/cam_qt.py | 16 +- src/py/cam/cam_qtgl.py | 22 +- 5 files changed, 327 insertions(+), 316 deletions(-) diff --git a/src/py/cam/cam.py b/src/py/cam/cam.py index 64f67e86..f6e8232c 100755 --- a/src/py/cam/cam.py +++ b/src/py/cam/cam.py @@ -6,6 +6,7 @@ # \todo Convert ctx and state dicts to proper classes, and move relevant # functions to those classes. +from typing import Any import argparse import binascii import libcamera as libcam @@ -14,379 +15,400 @@ import sys import traceback -class CustomAction(argparse.Action): - def __init__(self, option_strings, dest, **kwargs): - super().__init__(option_strings, dest, default={}, **kwargs) +class CameraContext: + camera: libcam.Camera + id: str + idx: int - def __call__(self, parser, namespace, values, option_string=None): - if len(namespace.camera) == 0: - print(f'Option {option_string} requires a --camera context') - sys.exit(-1) + opt_stream: str + opt_strict_formats: bool + opt_crc: bool + opt_metadata: bool + opt_save_frames: bool + opt_capture: int - if self.type == bool: - values = True + stream_names: dict[libcam.Stream, str] + 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): + camera = self.camera - if self.nargs == '+': - if current not in data: - data[current] = [] + print('Properties for', self.id) - data[current] += values - else: - data[current] = values + for name, prop in camera.properties.items(): + print('\t{}: {}'.format(name, prop)) + def do_cmd_list_controls(self): + camera = self.camera -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)) - - -def do_cmd_list_controls(ctx): - camera = ctx['camera'] - - print('Controls for', ctx['id']) - - for name, prop in camera.controls.items(): - print('\t{}: {}'.format(name, prop)) + print('Controls for', self.id) + for name, prop in camera.controls.items(): + print('\t{}: {}'.format(name, prop)) -def do_cmd_info(ctx): - camera = ctx['camera'] + def do_cmd_info(self): + camera = self.camera - print('Stream info for', ctx['id']) + print('Stream info for', self.id) - roles = [libcam.StreamRole.Viewfinder] + roles = [libcam.StreamRole.Viewfinder] - camconfig = camera.generate_configuration(roles) - if camconfig is None: - raise Exception('Generating config failed') + camconfig = camera.generate_configuration(roles) + if camconfig is None: + raise Exception('Generating config failed') - for i, stream_config in enumerate(camconfig): - print('\t{}: {}'.format(i, stream_config)) + for i, stream_config in enumerate(camconfig): + print('\t{}: {}'.format(i, stream_config)) - formats = stream_config.formats - for fmt in formats.pixel_formats: - print('\t * Pixelformat:', fmt, formats.range(fmt)) + formats = stream_config.formats + for fmt in formats.pixel_formats: + print('\t * Pixelformat:', fmt, formats.range(fmt)) - for size in formats.sizes(fmt): - print('\t -', size) + for size in formats.sizes(fmt): + print('\t -', size) + def acquire(self): + camera = self.camera -def acquire(ctx): - camera = ctx['camera'] + camera.acquire() - camera.acquire() + def release(self): + camera = self.camera + camera.release() -def release(ctx): - camera = ctx['camera'] + def __parse_streams(self): + streams = [] - camera.release() + for stream_desc in self.opt_stream: + stream_opts: dict[str, Any] + stream_opts = {'role': libcam.StreamRole.Viewfinder} + for stream_opt in stream_desc.split(','): + if stream_opt == 0: + continue -def parse_streams(ctx): - streams = [] - - for stream_desc in ctx['opt-stream']: - stream_opts = {'role': libcam.StreamRole.Viewfinder} - - for stream_opt in stream_desc.split(','): - if stream_opt == 0: - continue - - arr = stream_opt.split('=') - if len(arr) != 2: - print('Bad stream option', stream_opt) - sys.exit(-1) - - key = arr[0] - value = arr[1] - - if key in ['width', 'height']: - value = int(value) - elif key == 'role': - rolemap = { - 'still': libcam.StreamRole.StillCapture, - 'raw': libcam.StreamRole.Raw, - 'video': libcam.StreamRole.VideoRecording, - 'viewfinder': libcam.StreamRole.Viewfinder, - } - - role = rolemap.get(value.lower(), None) + arr = stream_opt.split('=') + if len(arr) != 2: + print('Bad stream option', stream_opt) + sys.exit(-1) - if role is None: - print('Bad stream role', value) + key = arr[0] + value = arr[1] + + if key in ['width', 'height']: + value = int(value) + elif key == 'role': + rolemap = { + 'still': libcam.StreamRole.StillCapture, + 'raw': libcam.StreamRole.Raw, + 'video': libcam.StreamRole.VideoRecording, + 'viewfinder': libcam.StreamRole.Viewfinder, + } + + role = rolemap.get(value.lower(), None) + + if role is None: + print('Bad stream role', value) + sys.exit(-1) + + value = role + elif key == 'pixelformat': + pass + else: + print('Bad stream option key', key) sys.exit(-1) - value = role - elif key == 'pixelformat': - pass - else: - print('Bad stream option key', key) - sys.exit(-1) + stream_opts[key] = value - stream_opts[key] = value + streams.append(stream_opts) - streams.append(stream_opts) + return streams - return streams + def configure(self): + camera = self.camera + streams = self.__parse_streams() -def configure(ctx): - camera = ctx['camera'] + roles = [opts['role'] for opts in streams] - streams = parse_streams(ctx) + camconfig = camera.generate_configuration(roles) + if camconfig is None: + raise Exception('Generating config failed') - roles = [opts['role'] for opts in streams] + for idx, stream_opts in enumerate(streams): + stream_config = camconfig.at(idx) - camconfig = camera.generate_configuration(roles) - if camconfig is None: - raise Exception('Generating config failed') + if 'width' in stream_opts: + stream_config.size.width = stream_opts['width'] - for idx, stream_opts in enumerate(streams): - stream_config = camconfig.at(idx) + if 'height' in stream_opts: + stream_config.size.height = stream_opts['height'] - if 'width' in stream_opts: - stream_config.size.width = stream_opts['width'] + if 'pixelformat' in stream_opts: + stream_config.pixel_format = libcam.PixelFormat(stream_opts['pixelformat']) - if 'height' in stream_opts: - stream_config.size.height = stream_opts['height'] + stat = camconfig.validate() - if 'pixelformat' in stream_opts: - stream_config.pixel_format = libcam.PixelFormat(stream_opts['pixelformat']) + if stat == libcam.CameraConfiguration.Status.Invalid: + print('Camera configuration invalid') + exit(-1) + elif stat == libcam.CameraConfiguration.Status.Adjusted: + if self.opt_strict_formats: + print('Adjusting camera configuration disallowed by --strict-formats argument') + exit(-1) - stat = camconfig.validate() + print('Camera configuration adjusted') - if stat == libcam.CameraConfiguration.Status.Invalid: - print('Camera configuration invalid') - exit(-1) - elif stat == libcam.CameraConfiguration.Status.Adjusted: - if ctx['opt-strict-formats']: - print('Adjusting camera configuration disallowed by --strict-formats argument') - exit(-1) + r = camera.configure(camconfig) + if r != 0: + raise Exception('Configure failed') - print('Camera configuration adjusted') + self.stream_names = {} + self.streams = [] - r = camera.configure(camconfig) - if r != 0: - raise Exception('Configure failed') + for idx, stream_config in enumerate(camconfig): + stream = stream_config.stream + self.streams.append(stream) + self.stream_names[stream] = 'stream' + str(idx) + print('{}-{}: stream config {}'.format(self.id, self.stream_names[stream], stream.configuration)) - ctx['stream-names'] = {} - ctx['streams'] = [] + def alloc_buffers(self): + camera = self.camera - for idx, stream_config in enumerate(camconfig): - stream = stream_config.stream - ctx['streams'].append(stream) - ctx['stream-names'][stream] = 'stream' + str(idx) - print('{}-{}: stream config {}'.format(ctx['id'], ctx['stream-names'][stream], stream.configuration)) + allocator = libcam.FrameBufferAllocator(camera) + for stream in self.streams: + ret = allocator.allocate(stream) + if ret < 0: + print('Cannot allocate buffers') + exit(-1) -def alloc_buffers(ctx): - camera = ctx['camera'] + allocated = len(allocator.buffers(stream)) - allocator = libcam.FrameBufferAllocator(camera) + print('{}-{}: Allocated {} buffers'.format(self.id, self.stream_names[stream], allocated)) - for idx, stream in enumerate(ctx['streams']): - ret = allocator.allocate(stream) - if ret < 0: - print('Cannot allocate buffers') - exit(-1) + self.allocator = allocator - allocated = len(allocator.buffers(stream)) + def create_requests(self): + camera = self.camera - print('{}-{}: Allocated {} buffers'.format(ctx['id'], ctx['stream-names'][stream], allocated)) + self.requests = [] - ctx['allocator'] = allocator + # Identify the stream with the least number of buffers + num_bufs = min([len(self.allocator.buffers(stream)) for stream in self.streams]) + requests = [] -def create_requests(ctx): - camera = ctx['camera'] + for buf_num in range(num_bufs): + request = camera.create_request(self.idx) - ctx['requests'] = [] + if request is None: + print('Can not create request') + exit(-1) - # Identify the stream with the least number of buffers - num_bufs = min([len(ctx['allocator'].buffers(stream)) for stream in ctx['streams']]) + for stream in self.streams: + buffers = self.allocator.buffers(stream) + buffer = buffers[buf_num] - requests = [] + ret = request.add_buffer(stream, buffer) + if ret < 0: + print('Can not set buffer for request') + exit(-1) - for buf_num in range(num_bufs): - request = camera.create_request(ctx['idx']) + requests.append(request) - if request is None: - print('Can not create request') - exit(-1) + self.requests = requests - for stream in ctx['streams']: - buffers = ctx['allocator'].buffers(stream) - buffer = buffers[buf_num] + def start(self): + camera = self.camera - ret = request.add_buffer(stream, buffer) - if ret < 0: - print('Can not set buffer for request') - exit(-1) + camera.start() - requests.append(request) + def stop(self): + camera = self.camera - ctx['requests'] = requests + camera.stop() + def queue_requests(self): + camera = self.camera -def start(ctx): - camera = ctx['camera'] + for request in self.requests: + camera.queue_request(request) + self.reqs_queued += 1 - camera.start() + del self.requests -def stop(ctx): - camera = ctx['camera'] +class CaptureState: + cm: libcam.CameraManager + contexts: list[CameraContext] + renderer: Any - camera.stop() + def __init__(self, cm, contexts): + self.cm = cm + self.contexts = contexts + # Called from renderer when there is a libcamera event + def event_handler(self): + try: + cm = self.cm + contexts = self.contexts -def queue_requests(ctx): - camera = ctx['camera'] + cm.read_event() - for request in ctx['requests']: - camera.queue_request(request) - ctx['reqs-queued'] += 1 + reqs = cm.get_ready_requests() - del ctx['requests'] + for req in reqs: + ctx = next(ctx for ctx in contexts if ctx.idx == req.cookie) + self.__request_handler(ctx, req) + running = any(ctx.reqs_completed < ctx.opt_capture for ctx in contexts) + return running + except Exception: + traceback.print_exc() + return False -def capture_init(contexts): - for ctx in contexts: - acquire(ctx) + def __request_handler(self, ctx, req): + if req.status != libcam.Request.Status.Complete: + raise Exception('{}: Request failed: {}'.format(ctx.id, req.status)) - for ctx in contexts: - configure(ctx) + buffers = req.buffers - for ctx in contexts: - alloc_buffers(ctx) + # Compute the frame rate. The timestamp is arbitrarily retrieved from + # the first buffer, as all buffers should have matching timestamps. + ts = buffers[next(iter(buffers))].metadata.timestamp + last = ctx.last + fps = 1000000000.0 / (ts - last) if (last != 0 and (ts - last) != 0) else 0 + ctx.last = ts + ctx.fps = fps - for ctx in contexts: - create_requests(ctx) + for stream, fb in buffers.items(): + stream_name = ctx.stream_names[stream] + crcs = [] + if ctx.opt_crc: + with libcamera.utils.MappedFrameBuffer(fb) as mfb: + plane_crcs = [binascii.crc32(p) for p in mfb.planes] + crcs.append(plane_crcs) -def capture_start(contexts): - for ctx in contexts: - start(ctx) + meta = fb.metadata - for ctx in contexts: - queue_requests(ctx) + print('{:.6f} ({:.2f} fps) {}-{}: seq {}, bytes {}, CRCs {}' + .format(ts / 1000000000, fps, + ctx.id, stream_name, + meta.sequence, meta.bytesused, + crcs)) + if ctx.opt_metadata: + reqmeta = req.metadata + for ctrl, val in reqmeta.items(): + print(f'\t{ctrl} = {val}') -# Called from renderer when there is a libcamera event -def event_handler(state): - try: - cm = state['cm'] - contexts = state['contexts'] + if ctx.opt_save_frames: + with libcamera.utils.MappedFrameBuffer(fb) as mfb: + filename = 'frame-{}-{}-{}.data'.format(ctx.id, stream_name, ctx.reqs_completed) + with open(filename, 'wb') as f: + for p in mfb.planes: + f.write(p) - cm.read_event() + self.renderer.request_handler(ctx, req) - reqs = cm.get_ready_requests() + ctx.reqs_completed += 1 - for req in reqs: - ctx = next(ctx for ctx in contexts if ctx['idx'] == req.cookie) - request_handler(state, ctx, req) + # Called from renderer when it has finished with a request + def request_processed(self, ctx, req): + camera = ctx.camera - running = any(ctx['reqs-completed'] < ctx['opt-capture'] for ctx in contexts) - return running - except Exception: - traceback.print_exc() - return False + if ctx.reqs_queued < ctx.opt_capture: + req.reuse() + camera.queue_request(req) + ctx.reqs_queued += 1 + def __capture_init(self): + for ctx in self.contexts: + ctx.acquire() -def request_handler(state, ctx, req): - if req.status != libcam.Request.Status.Complete: - raise Exception('{}: Request failed: {}'.format(ctx['id'], req.status)) + for ctx in self.contexts: + ctx.configure() - buffers = req.buffers + for ctx in self.contexts: + ctx.alloc_buffers() - # Compute the frame rate. The timestamp is arbitrarily retrieved from - # the first buffer, as all buffers should have matching timestamps. - ts = buffers[next(iter(buffers))].metadata.timestamp - last = ctx.get('last', 0) - fps = 1000000000.0 / (ts - last) if (last != 0 and (ts - last) != 0) else 0 - ctx['last'] = ts - ctx['fps'] = fps + for ctx in self.contexts: + ctx.create_requests() - for stream, fb in buffers.items(): - stream_name = ctx['stream-names'][stream] + def __capture_start(self): + for ctx in self.contexts: + ctx.start() - crcs = [] - if ctx['opt-crc']: - with libcamera.utils.MappedFrameBuffer(fb) as mfb: - plane_crcs = [binascii.crc32(p) for p in mfb.planes] - crcs.append(plane_crcs) + for ctx in self.contexts: + ctx.queue_requests() - meta = fb.metadata + def __capture_deinit(self): + for ctx in self.contexts: + ctx.stop() - print('{:.6f} ({:.2f} fps) {}-{}: seq {}, bytes {}, CRCs {}' - .format(ts / 1000000000, fps, - ctx['id'], stream_name, - meta.sequence, meta.bytesused, - crcs)) + for ctx in self.contexts: + ctx.release() - if ctx['opt-metadata']: - reqmeta = req.metadata - for ctrl, val in reqmeta.items(): - print(f'\t{ctrl} = {val}') + def do_cmd_capture(self): + self.__capture_init() - if ctx['opt-save-frames']: - with libcamera.utils.MappedFrameBuffer(fb) as mfb: - filename = 'frame-{}-{}-{}.data'.format(ctx['id'], stream_name, ctx['reqs-completed']) - with open(filename, 'wb') as f: - for p in mfb.planes: - f.write(p) + renderer = self.renderer - state['renderer'].request_handler(ctx, req) + renderer.setup() - ctx['reqs-completed'] += 1 + self.__capture_start() + renderer.run() -# Called from renderer when it has finished with a request -def request_prcessed(ctx, req): - camera = ctx['camera'] + self.__capture_deinit() - if ctx['reqs-queued'] < ctx['opt-capture']: - req.reuse() - camera.queue_request(req) - ctx['reqs-queued'] += 1 +class CustomAction(argparse.Action): + def __init__(self, option_strings, dest, **kwargs): + super().__init__(option_strings, dest, default={}, **kwargs) -def capture_deinit(contexts): - for ctx in contexts: - stop(ctx) + def __call__(self, parser, namespace, values, option_string=None): + if len(namespace.camera) == 0: + print(f'Option {option_string} requires a --camera context') + sys.exit(-1) - for ctx in contexts: - release(ctx) + if self.type == bool: + values = True + current = namespace.camera[-1] -def do_cmd_capture(state): - capture_init(state['contexts']) + data = getattr(namespace, self.dest) - renderer = state['renderer'] + if self.nargs == '+': + if current not in data: + data[current] = [] - renderer.setup() + data[current] += values + else: + data[current] = values - capture_start(state['contexts']) - renderer.run() +def do_cmd_list(cm): + print('Available cameras:') - capture_deinit(state['contexts']) + for idx, c in enumerate(cm.cameras): + print(f'{idx + 1}: {c.id}') def main(): @@ -422,39 +444,28 @@ def main(): print('Unable to find camera', cam_idx) return -1 - contexts.append({ - 'camera': camera, - 'idx': cam_idx, - 'id': 'cam' + str(cam_idx), - 'reqs-queued': 0, - 'reqs-completed': 0, - 'opt-capture': args.capture.get(cam_idx, False), - 'opt-crc': args.crc.get(cam_idx, False), - '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']), - }) + ctx = CameraContext(camera, cam_idx) + ctx.opt_capture = args.capture.get(cam_idx, 0) + ctx.opt_crc = args.crc.get(cam_idx, False) + ctx.opt_save_frames = args.save_frames.get(cam_idx, False) + ctx.opt_metadata = args.metadata.get(cam_idx, False) + ctx.opt_strict_formats = args.strict_formats.get(cam_idx, False) + ctx.opt_stream = args.stream.get(cam_idx, ['role=viewfinder']) + contexts.append(ctx) 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: if args.list_properties: - do_cmd_list_props(ctx) + ctx.do_cmd_list_props() if args.list_controls: - do_cmd_list_controls(ctx) + ctx.do_cmd_list_controls() if args.info: - do_cmd_info(ctx) + ctx.do_cmd_info() if args.capture: - - state = { - 'cm': cm, - 'contexts': contexts, - 'event_handler': event_handler, - 'request_prcessed': request_prcessed, - } + state = CaptureState(cm, contexts) if args.renderer == 'null': import cam_null @@ -472,9 +483,9 @@ def main(): print('Bad renderer', args.renderer) return -1 - state['renderer'] = renderer + state.renderer = renderer - do_cmd_capture(state) + state.do_cmd_capture() return 0 diff --git a/src/py/cam/cam_kms.py b/src/py/cam/cam_kms.py index 74cd3b38..213e0b03 100644 --- a/src/py/cam/cam_kms.py +++ b/src/py/cam/cam_kms.py @@ -10,8 +10,8 @@ class KMSRenderer: def __init__(self, state): self.state = state - self.cm = state['cm'] - self.contexts = state['contexts'] + self.cm = state.cm + self.contexts = state.contexts self.running = False card = pykms.Card() @@ -92,7 +92,7 @@ class KMSRenderer: if old: req = old['camreq'] ctx = old['camctx'] - self.state['request_prcessed'](ctx, req) + self.state.request_processed(ctx, req) def queue(self, drmreq): if not self.next: @@ -108,7 +108,7 @@ class KMSRenderer: idx = 0 for ctx in self.contexts: - for stream in ctx['streams']: + for stream in ctx.streams: cfg = stream.configuration fmt = cfg.pixel_format @@ -125,7 +125,7 @@ class KMSRenderer: 'size': cfg.size, }) - for fb in ctx['allocator'].buffers(stream): + for fb in ctx.allocator.buffers(stream): w = cfg.size.width h = cfg.size.height fds = [] @@ -148,7 +148,7 @@ class KMSRenderer: self.handle_page_flip(ev.seq, ev.time) def readcam(self, fd): - self.running = self.state['event_handler'](self.state) + self.running = self.state.event_handler() def readkey(self, fileobj): sys.stdin.readline() diff --git a/src/py/cam/cam_null.py b/src/py/cam/cam_null.py index a6da9671..45c5f467 100644 --- a/src/py/cam/cam_null.py +++ b/src/py/cam/cam_null.py @@ -9,8 +9,8 @@ class NullRenderer: def __init__(self, state): self.state = state - self.cm = state['cm'] - self.contexts = state['contexts'] + self.cm = state.cm + self.contexts = state.contexts self.running = False @@ -37,11 +37,11 @@ class NullRenderer: print('Exiting...') def readcam(self, fd): - self.running = self.state['event_handler'](self.state) + self.running = self.state.event_handler() def readkey(self, fileobj): sys.stdin.readline() self.running = False def request_handler(self, ctx, req): - self.state['request_prcessed'](ctx, req) + self.state.request_processed(ctx, req) diff --git a/src/py/cam/cam_qt.py b/src/py/cam/cam_qt.py index 03096920..d638e9cc 100644 --- a/src/py/cam/cam_qt.py +++ b/src/py/cam/cam_qt.py @@ -176,8 +176,8 @@ class QtRenderer: def __init__(self, state): self.state = state - self.cm = state['cm'] - self.contexts = state['contexts'] + self.cm = state.cm + self.contexts = state.contexts def setup(self): self.app = QtWidgets.QApplication([]) @@ -185,7 +185,7 @@ class QtRenderer: windows = [] for ctx in self.contexts: - for stream in ctx['streams']: + for stream in ctx.streams: window = MainWindow(ctx, stream) window.show() windows.append(window) @@ -206,7 +206,7 @@ class QtRenderer: print('Exiting...') def readcam(self): - running = self.state['event_handler'](self.state) + running = self.state.event_handler() if not running: self.app.quit() @@ -223,7 +223,7 @@ class QtRenderer: wnd.handle_request(stream, fb) - self.state['request_prcessed'](ctx, req) + self.state.request_processed(ctx, req) def cleanup(self): for w in self.windows: @@ -254,7 +254,7 @@ class MainWindow(QtWidgets.QWidget): group.setLayout(groupLayout) controlsLayout.addWidget(group) - lab = QtWidgets.QLabel(ctx['id']) + lab = QtWidgets.QLabel(ctx.id) groupLayout.addWidget(lab) self.frameLabel = QtWidgets.QLabel() @@ -265,7 +265,7 @@ class MainWindow(QtWidgets.QWidget): group.setLayout(groupLayout) controlsLayout.addWidget(group) - camera = ctx['camera'] + camera = ctx.camera for k, v in camera.properties.items(): lab = QtWidgets.QLabel() @@ -308,4 +308,4 @@ class MainWindow(QtWidgets.QWidget): self.label.setPixmap(pix) 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)) diff --git a/src/py/cam/cam_qtgl.py b/src/py/cam/cam_qtgl.py index c9e367a2..5f7ccf1e 100644 --- a/src/py/cam/cam_qtgl.py +++ b/src/py/cam/cam_qtgl.py @@ -142,7 +142,7 @@ class QtRenderer: self.window = window 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()) keynotif = QtCore.QSocketNotifier(sys.stdin.fileno(), QtCore.QSocketNotifier.Read) @@ -155,7 +155,7 @@ class QtRenderer: print('Exiting...') def readcam(self): - running = self.state['event_handler'](self.state) + running = self.state.event_handler() if not running: self.app.quit() @@ -184,12 +184,12 @@ class MainWindow(QtWidgets.QWidget): self.reqqueue = {} self.current = {} - for ctx in self.state['contexts']: + for ctx in self.state.contexts: - self.reqqueue[ctx['idx']] = [] - self.current[ctx['idx']] = [] + self.reqqueue[ctx.idx] = [] + self.current[ctx.idx] = [] - for stream in ctx['streams']: + for stream in ctx.streams: self.textures[stream] = None num_tiles = len(self.textures) @@ -312,12 +312,12 @@ class MainWindow(QtWidgets.QWidget): if len(queue) == 0: 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]: old = self.current[ctx_idx] self.current[ctx_idx] = None - self.state['request_prcessed'](ctx, old) + self.state.request_processed(ctx, old) next_req = queue.pop(0) self.current[ctx_idx] = next_req @@ -336,8 +336,8 @@ class MainWindow(QtWidgets.QWidget): size = self.size() - for idx, ctx in enumerate(self.state['contexts']): - for stream in ctx['streams']: + for idx, ctx in enumerate(self.state.contexts): + for stream in ctx.streams: if self.textures[stream] is None: continue @@ -359,5 +359,5 @@ class MainWindow(QtWidgets.QWidget): assert(b) def handle_request(self, ctx, req): - self.reqqueue[ctx['idx']].append(req) + self.reqqueue[ctx.idx].append(req) self.update()