[libcamera-devel] [PATCH v2 13/19] py: cam: Convert ctx and state to classes

Laurent Pinchart laurent.pinchart at ideasonboard.com
Fri May 27 08:53:52 CEST 2022


Hi Tomi,

Thank you for the patch.

On Tue, May 24, 2022 at 02:46:04PM +0300, Tomi Valkeinen wrote:
> From: Tomi Valkeinen <tomi.valkeinen at iki.fi>
> 
> Convert ctx and state dicts to classes. No functional changes.
> 
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen at ideasonboard.com>
> ---
>  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():

You can use self.camera here and drop the local variable. Same in a few
other functions below.

> +            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)

Raising an exception would be better, but that's a candidate for another
patch as it's a functional change.

>  
> -                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

Same here, these local variables could possibly be dropped.

Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>

>  
> -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()

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list