From 4aa99fd1a904f92b80aa771b7969d97468878d0c Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Fri, 4 Jun 2021 15:22:49 +0200 Subject: [PATCH] qemu-usb: reduce cpu load by webcam model if no new frame is available for capturing Fixes #4078 #4196 --- repos/libports/src/lib/qemu-usb/dev-webcam.c | 29 +++++--- .../src/lib/qemu-usb/webcam-backend.h | 4 +- repos/libports/src/lib/qemu-usb/webcam.cc | 73 ++++++++++++++----- 3 files changed, 73 insertions(+), 33 deletions(-) diff --git a/repos/libports/src/lib/qemu-usb/dev-webcam.c b/repos/libports/src/lib/qemu-usb/dev-webcam.c index 002d2f64f1..8dc466c76c 100644 --- a/repos/libports/src/lib/qemu-usb/dev-webcam.c +++ b/repos/libports/src/lib/qemu-usb/dev-webcam.c @@ -42,6 +42,7 @@ typedef struct USBWebcamState { uint8_t frame_toggle_bit; bool delay_packet; bool capture; + bool timer_active; uint8_t watchdog; uint8_t *frame_pixel; } USBWebcamState; @@ -202,8 +203,8 @@ static struct { uint8_t bpp; uint16_t width; uint16_t height; - uint32_t interval; /* dwFrameInterval */ - void (*capture)(void *); + uint32_t interval; /* dwFrameInterval in 100ns units */ + bool (*capture)(void *); } formats [2]; static unsigned active_format() @@ -400,7 +401,8 @@ static Property webcam_properties[] = { static void webcam_start_timer(USBWebcamState * const state) { - int64_t const now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + state->timer_active = true; + uint64_t const now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); timer_mod(state->timer, now_ns + 100ull * formats[active_format()].interval); } @@ -420,6 +422,7 @@ static void usb_webcam_init_state(USBWebcamState *state) state->delay_packet = false; state->capture = false; state->watchdog = 0; + state->timer_active = false; } static void usb_webcam_handle_reset(USBDevice *dev) @@ -448,7 +451,6 @@ static void usb_webcam_setup_packet(USBWebcamState * const state, USBPacket * co { unsigned packet_size = vs_commit_state.dwMaxPayLoadTransferSize; struct payload_header header = { .length = 0, .bfh = 0 }; - bool start_timer = !state->bytes_frame; if (p->iov.size < packet_size) packet_size = p->iov.size; @@ -470,19 +472,16 @@ static void usb_webcam_setup_packet(USBWebcamState * const state, USBPacket * co } /* reset capture watchdog */ - if (state->watchdog) { + if (state->watchdog) state->watchdog = 0; - start_timer = true; - } /* check for capture state change */ if (!state->capture) { state->capture = true; - start_timer = true; usb_webcam_capture_state_changed(state->capture); } - if (start_timer) + if (!state->timer_active) webcam_start_timer(state); /* payload header */ @@ -533,6 +532,8 @@ static void webcam_timeout(void *opague) USBDevice *dev = (USBDevice *)opague; USBWebcamState *state = USB_WEBCAM(opague); + state->timer_active = false; + if (!state->capture) return; @@ -550,14 +551,18 @@ static void webcam_timeout(void *opague) return; } + /* start timer before requesting new frame to account the cpu time */ + webcam_start_timer(state); + + /* request next frame pixel buffer */ + if (!formats[active_format()].capture(state->frame_pixel)) + return; + USBPacket *p = state->delayed_packet; state->delayed_packet = 0; state->delay_packet = false; - /* request next frame pixel buffer */ - formats[active_format()].capture(state->frame_pixel); - /* continue usb transmission with new frame */ usb_webcam_setup_packet(state, p); if (p->status == USB_RET_SUCCESS) diff --git a/repos/libports/src/lib/qemu-usb/webcam-backend.h b/repos/libports/src/lib/qemu-usb/webcam-backend.h index d2b597818e..00e9f3653f 100644 --- a/repos/libports/src/lib/qemu-usb/webcam-backend.h +++ b/repos/libports/src/lib/qemu-usb/webcam-backend.h @@ -18,8 +18,8 @@ struct webcam_config { }; extern void webcam_backend_config(struct webcam_config *); -extern void capture_bgr_frame(void * pixel); -extern void capture_yuv_frame(void * pixel); +extern bool capture_bgr_frame(void * pixel); +extern bool capture_yuv_frame(void * pixel); extern void capture_state_changed(bool on, char const * format); #endif /* _WEBCAM_BACKEND_H_ */ diff --git a/repos/libports/src/lib/qemu-usb/webcam.cc b/repos/libports/src/lib/qemu-usb/webcam.cc index af926b3689..e174d11fef 100644 --- a/repos/libports/src/lib/qemu-usb/webcam.cc +++ b/repos/libports/src/lib/qemu-usb/webcam.cc @@ -34,6 +34,7 @@ struct Capture_webcam Gui::Area const _area; bool const _vflip; uint8_t const _fps; + bool _force_update { false }; Attached_dataspace _ds { _env.rm(), _capture.dataspace() }; Constructible _reporter { }; @@ -54,9 +55,18 @@ struct Capture_webcam return area; } - void update_yuv(void *frame) + + bool update_yuv(void *frame) { - _capture.capture_at(Capture::Point(0, 0)); + if (!_area.valid()) + return false; + + bool changed = _force_update; + _capture.capture_at(Capture::Point(0, 0)).for_each_rect([&](auto) { + changed = true; }); + + if (!changed) + return false; int const src_stride_argb = _area.w() * 4; int const dst_stride_yuy2 = _area.w() * 2; @@ -64,30 +74,55 @@ struct Capture_webcam libyuv::ARGBToYUY2(_ds.local_addr(), src_stride_argb, reinterpret_cast(frame), dst_stride_yuy2, _area.w(), _area.h()); + + if (_force_update) + _force_update = false; + + return true; } - void update_bgr(void *frame) + bool update_bgr(void *frame) { - _capture.capture_at(Capture::Point(0, 0)); + if (!_area.valid()) + return false; - uint8_t * const bgr = reinterpret_cast(frame); - Pixel_rgb888 * data = reinterpret_cast(_ds.local_addr()); + bool changed = false; - for (int y = 0; y < _area.h(); y++) { - unsigned const row = _vflip ? y : _area.h() - 1 - y; - unsigned const row_byte = (row * _area.w() * 3); - for (int x = 0; x < _area.w(); x++) { - bgr[row_byte + x * 3 + 0] = data->b(); - bgr[row_byte + x * 3 + 1] = data->g(); - bgr[row_byte + x * 3 + 2] = data->r(); + uint8_t * const bgr = reinterpret_cast(frame); + Pixel_rgb888 const * const data = _ds.local_addr(); - data++; + auto const &update_fn = ([&](auto &rect) { + changed = true; + for (int y = rect.y1(); y <= rect.y2(); y++) { + unsigned const row = _vflip ? y : _area.h() - 1 - y; + unsigned const row_byte = (row * _area.w() * 3); + + for (int x = rect.x1(); x < rect.x2(); x++) { + auto &pixel = data[y * _area.w() + x]; + bgr[row_byte + x * 3 + 0] = pixel.b(); + bgr[row_byte + x * 3 + 1] = pixel.g(); + bgr[row_byte + x * 3 + 2] = pixel.r(); + } } - } + }); + + if (_force_update) { + /* update whole frame */ + _force_update = false; + Rect const whole(Point(0,0), _area); + _capture.capture_at(Capture::Point(0, 0)); + update_fn(whole); + } else + _capture.capture_at(Capture::Point(0, 0)).for_each_rect(update_fn); + + return changed; } void capture_state_changed(bool on, char const * format) { + /* next time update whole frame due to format changes or on/off */ + _force_update = true; + if (!_reporter.constructed()) return; @@ -123,14 +158,14 @@ extern "C" void capture_state_changed(bool on, char const * format) capture->capture_state_changed(on, format); } -extern "C" void capture_bgr_frame(void * pixel) +extern "C" bool capture_bgr_frame(void * pixel) { - capture->update_bgr(pixel); + return capture->update_bgr(pixel); } -extern "C" void capture_yuv_frame(void * pixel) +extern "C" bool capture_yuv_frame(void * pixel) { - capture->update_yuv(pixel); + return capture->update_yuv(pixel); }