mirror of
https://github.com/genodelabs/genode.git
synced 2025-03-23 04:25:21 +00:00
qemu-usb: reduce cpu load by webcam model
if no new frame is available for capturing Fixes #4078 #4196
This commit is contained in:
parent
ff452619e3
commit
4aa99fd1a9
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);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user