qemu-usb: reduce cpu load by webcam model

if no new frame is available for capturing

Fixes  
This commit is contained in:
Alexander Boettcher 2021-06-04 15:22:49 +02:00 committed by Christian Helmuth
parent ff452619e3
commit 4aa99fd1a9
3 changed files with 73 additions and 33 deletions
repos/libports/src/lib/qemu-usb

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

@ -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_ */

@ -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> _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<uint8_t>(), src_stride_argb,
reinterpret_cast<uint8_t*>(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<uint8_t *>(frame);
Pixel_rgb888 * data = reinterpret_cast<Pixel_rgb888 *>(_ds.local_addr<void>());
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<uint8_t *>(frame);
Pixel_rgb888 const * const data = _ds.local_addr<Pixel_rgb888>();
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);
}