qemu-usb: adapt to webcam capture on/off changes

Issue #4287
This commit is contained in:
Christian Helmuth 2021-10-08 14:37:06 +02:00 committed by Norman Feske
parent 6f1d3862cd
commit d078f7db76
6 changed files with 52 additions and 80 deletions

View File

@ -33,28 +33,21 @@ when the timer triggers.
Usage webcam model attached to the xHCI model
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If the xHCI model is enabled and used, then the webcam device model can be
enabled by specifying a XML node in the config ROM:
If the xHCI model is enabled and used, the webcam device model can be
enabled by specifying a XML node in the config ROM.
<config ...>
...
<webcam width="640" height="480" fps="15" vertical_flip="false" screen_size="false" report="false"/>
...
...
<webcam width="640" height="480" fps="15" vertical_flip="false" screen_size="false"/>
...
</config>
The webcam attributes are optional. The values shown above, are the default ones
when a attribute is not specified. When the webcam node is specified, the model
will open and will use a Genode capture session to obtain the
frames in the rate as specified by the frame per secondes (fps) attribute. The
'vertical_flip' attribute specifies, whether the frames are shown flipped
for guests. For Windows guests the value has to be false, for Linux guests
true. The format supported by the model is YUV2 or BGR3. If the 'screen_size' is set
to true, the webcam model will try to use the screen size as provided by the
capture session. If the screen size is invalid (e.g. 0x0), the attribute
values of 'width' and 'height' will be used instead. If the 'report' attribute
is set, a report will be generated whenever the guests starts/ends capturing and
if the capturing format choosen by the guest changes,
<capture enabled="..." format="BGR3"/>
or
<capture enabled="..." format="YUV2"/>
Webcam attributes are optional, default values are depicted above. The model
supports YUV2 and BGR3 image formats reported to the guest via bFormatIndex.
It requests a Genode capture session to obtain the frames in the rate as
specified by the 'fps' (frame per seconds) attribute. The 'vertical_flip'
attribute specifies, whether the frames are shown flipped vertically for
guests. For Windows guests the value has to be false, for Linux guests true.
If the 'screen_size' attribute is set to true, the webcam model configures the
initial screen size to the size provided by the capture session. If the screen
size is invalid (e.g. 0x0), 'width' and 'height' will be used as fallback.

View File

@ -435,18 +435,6 @@ static void usb_webcam_handle_reset(USBDevice *dev)
usb_webcam_init_state(state);
}
static void usb_webcam_capture_state_changed(bool const on)
{
char const * format = "unknown";
if (vs_commit_state.bFormatIndex == DEVICE_VS_FORMAT_BGR)
format = "BGR3";
else if (vs_commit_state.bFormatIndex == DEVICE_VS_FORMAT_YUV)
format = "YUY2";
capture_state_changed(on, format);
}
static void usb_webcam_setup_packet(USBWebcamState * const state, USBPacket * const p)
{
unsigned packet_size = vs_commit_state.dwMaxPayLoadTransferSize;
@ -458,7 +446,7 @@ static void usb_webcam_setup_packet(USBWebcamState * const state, USBPacket * co
if (packet_size <= sizeof(header)) {
p->status = USB_RET_STALL;
if (state->capture)
usb_webcam_capture_state_changed(false);
capture_state_changed(false);
usb_webcam_init_state(state);
return;
}
@ -466,7 +454,7 @@ static void usb_webcam_setup_packet(USBWebcamState * const state, USBPacket * co
if (state->bytes_frame >= max_frame_size(active_format())) {
p->status = USB_RET_STALL;
if (state->capture)
usb_webcam_capture_state_changed(false);
capture_state_changed(false);
usb_webcam_init_state(state);
return;
}
@ -478,7 +466,7 @@ static void usb_webcam_setup_packet(USBWebcamState * const state, USBPacket * co
/* check for capture state change */
if (!state->capture) {
state->capture = true;
usb_webcam_capture_state_changed(state->capture);
capture_state_changed(state->capture);
}
if (!state->timer_active)
@ -543,7 +531,7 @@ static void webcam_timeout(void *opague)
if (state->delay_packet || (state->watchdog && state->watchdog >= fps)) {
state->capture = false;
state->delay_packet = false;
usb_webcam_capture_state_changed(state->capture);
capture_state_changed(state->capture);
} else {
state->watchdog ++;
webcam_start_timer(state);
@ -625,7 +613,7 @@ static void usb_webcam_handle_control(USBDevice * const dev,
if (state->capture) {
state->capture = false;
state->delay_packet = false;
usb_webcam_capture_state_changed(state->capture);
capture_state_changed(state->capture);
}
}
stall = false;
@ -689,7 +677,7 @@ static void usb_webcam_handle_control(USBDevice * const dev,
if (notify) {
USBWebcamState *state = USB_WEBCAM(dev);
usb_webcam_capture_state_changed(state->capture);
capture_state_changed(state->capture);
}
}
@ -719,7 +707,7 @@ static void usb_webcam_handle_data(USBDevice *dev, USBPacket *p)
if (!p->ep || p->ep->nr != DEVICE_EP_ID) {
p->status = USB_RET_STALL;
if (state->capture)
usb_webcam_capture_state_changed(false);
capture_state_changed(false);
usb_webcam_init_state(state);
return;
}
@ -727,7 +715,7 @@ static void usb_webcam_handle_data(USBDevice *dev, USBPacket *p)
default:
p->status = USB_RET_STALL;
if (state->capture)
usb_webcam_capture_state_changed(false);
capture_state_changed(false);
usb_webcam_init_state(state);
return;
}

View File

@ -20,6 +20,6 @@ struct webcam_config {
extern void webcam_backend_config(struct webcam_config *);
extern bool capture_bgr_frame(void * pixel);
extern bool capture_yuv_frame(void * pixel);
extern void capture_state_changed(bool on, char const * format);
extern void capture_state_changed(bool on);
#endif /* _WEBCAM_BACKEND_H_ */

View File

@ -30,28 +30,27 @@ using namespace Genode;
struct Capture_webcam
{
Env &_env;
Capture::Connection _capture { _env, "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 { };
Constructible<Capture::Connection> _capture;
Constructible<Attached_dataspace> _ds;
Gui::Area setup_area(Gui::Area const area_in, bool const auto_area)
Gui::Area _setup_area(Gui::Area const area_in, bool const auto_area)
{
Gui::Area area = area_in;
if (auto_area) {
area = _capture.screen_size();
Capture::Connection probe { _env, "webcam" };
area = probe.screen_size();
if (!area.valid())
area = area_in;
}
/* request setup of dataspace by server */
_capture.buffer(area);
return area;
}
@ -62,7 +61,7 @@ struct Capture_webcam
return false;
bool changed = _force_update;
_capture.capture_at(Capture::Point(0, 0)).for_each_rect([&](auto) {
_capture->capture_at(Capture::Point(0, 0)).for_each_rect([&](auto) {
changed = true; });
if (!changed)
@ -71,7 +70,7 @@ struct Capture_webcam
int const src_stride_argb = _area.w() * 4;
int const dst_stride_yuy2 = _area.w() * 2;
libyuv::ARGBToYUY2(_ds.local_addr<uint8_t>(), src_stride_argb,
libyuv::ARGBToYUY2(_ds->local_addr<uint8_t>(), src_stride_argb,
reinterpret_cast<uint8_t*>(frame), dst_stride_yuy2,
_area.w(), _area.h());
@ -89,7 +88,7 @@ struct Capture_webcam
bool changed = false;
uint8_t * const bgr = reinterpret_cast<uint8_t *>(frame);
Pixel_rgb888 const * const data = _ds.local_addr<Pixel_rgb888>();
Pixel_rgb888 const * const data = _ds->local_addr<Pixel_rgb888>();
auto const &update_fn = ([&](auto &rect) {
changed = true;
@ -110,52 +109,47 @@ struct Capture_webcam
/* update whole frame */
_force_update = false;
Rect const whole(Point(0,0), _area);
_capture.capture_at(Capture::Point(0, 0));
_capture->capture_at(Capture::Point(0, 0));
update_fn(whole);
} else
_capture.capture_at(Capture::Point(0, 0)).for_each_rect(update_fn);
_capture->capture_at(Capture::Point(0, 0)).for_each_rect(update_fn);
return changed;
}
void capture_state_changed(bool on, char const * format)
void capture_state_changed(bool on)
{
/* next time update whole frame due to format changes or on/off */
_force_update = true;
if (!_reporter.constructed())
return;
Reporter::Xml_generator xml(*_reporter, [&] () {
xml.attribute("enabled", on);
xml.attribute("format", format);
});
/* construct/destruct capture connection and dataspace */
if (on) {
_capture.construct(_env, "webcam");
_capture->buffer(_area);
_ds.construct(_env.rm(), _capture->dataspace());
} else {
_ds.destruct();
_capture.destruct();
}
}
Capture_webcam (Env &env, Gui::Area area, bool auto_area, bool flip,
uint8_t fps, bool report)
Capture_webcam(Env &env, Gui::Area area, bool auto_area, bool flip, uint8_t fps)
:
_env(env),
_area(setup_area(area, auto_area)),
_area(_setup_area(area, auto_area)),
_vflip(flip),
_fps(fps)
{
if (report) {
_reporter.construct(_env, "capture");
_reporter->enabled(true);
}
log("USB webcam ", _area, " fps=", _fps, " vertical_flip=",
_vflip ? "yes" : "no",
" report=", _reporter.constructed() ? "enabled" : "disabled");
_vflip ? "yes" : "no");
}
};
static Genode::Constructible<Capture_webcam> capture;
extern "C" void capture_state_changed(bool on, char const * format)
extern "C" void capture_state_changed(bool on)
{
capture->capture_state_changed(on, format);
capture->capture_state_changed(on);
}
extern "C" bool capture_bgr_frame(void * pixel)
@ -187,8 +181,7 @@ extern "C" void _type_init_host_webcam_register_types(Env &env,
webcam.attribute_value("height", 480u)),
webcam.attribute_value("screen_size", false),
webcam.attribute_value("vertical_flip", false),
webcam.attribute_value("fps", 15u),
webcam.attribute_value("report", false));
webcam.attribute_value("fps", 15u));
/* register webcam model, which will call webcam_backend_config() */
_type_init_usb_webcam_register_types();

View File

@ -5,7 +5,6 @@
<file_system label="shared"/>
<nic/>
<capture/>
<report label="capture"/>
<gui/>
<rom label="capslock"/>
<report label="shape"/>
@ -67,7 +66,7 @@
<fs writeable="yes"/>
</vfs>
<libc stdout="/dev/log" stderr="/dev/log" rtc="/dev/rtc"/>
<webcam vertical_flip="true" fps="30" report="true" screen_size="false" width="640" height="480"/>
<webcam vertical_flip="true" fps="30" screen_size="false" width="640" height="480"/>
</config>
<route>
<service name="Audio_out"> <parent/> </service>
@ -81,7 +80,6 @@
<service name="Report" label="shape"> <parent label="shape"/> </service>
<service name="ROM" label="clipboard"> <parent label="clipboard"/> </service>
<service name="Report" label="clipboard"> <parent label="clipboard"/> </service>
<service name="Report" label="capture"> <parent label="capture"/> </service>
<service name="Gui"> <parent label=""/> </service>
<any-service> <parent/> </any-service>
</route>

View File

@ -280,7 +280,7 @@ for { set i 1} { $i <= $use_vms } { incr i} {
}
append_if [expr $use_webcam] config_of_app {
<webcam vertical_flip="} $webcam_vflip {" fps="15" report="false" screen_size="false" width="640" height="480" />}
<webcam vertical_flip="} $webcam_vflip {" fps="15" screen_size="false" width="640" height="480"/>}
append config_of_app {
<libc stdout="/dev/log" stderr="/dev/log" rtc="/dev/rtc" pipe="/pipe"/>