mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-19 03:06:39 +00:00
parent
4a56171a77
commit
5135ff2dc2
26
repos/libports/src/app/usb_webcam/README
Normal file
26
repos/libports/src/app/usb_webcam/README
Normal file
@ -0,0 +1,26 @@
|
||||
A USB webcam app using libuvc
|
||||
|
||||
Behaviour
|
||||
---------
|
||||
|
||||
The app requests an USB session for accessing the webcam and a GUI session in
|
||||
order to render the camera into.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
! <config enabled="yes" width="320" height="240" format="yuv" fps="30">
|
||||
! <vfs>
|
||||
! <dir name="dev"> <log/> <inline name="rtc">2018-01-01 00:01</inline> </dir>
|
||||
! <dir name="pipe"> <pipe/> </dir>
|
||||
! </vfs>
|
||||
! <libc stdout="/dev/log" stderr="/dev/log" rtc="/dev/rtc" pipe="/pipe"/>
|
||||
! </conifg>
|
||||
|
||||
The frame format can be configured via the config attributes "width", "height",
|
||||
"format", "fps". Currently the "yuv" and "mjpeg" formats are supported. The
|
||||
"enabled" attribute allows for dynamic power on/off of the camera at runtime. In
|
||||
case there is no suitable frame format present, the app will dump the camera
|
||||
supported formats during startup.
|
||||
|
||||
|
274
repos/libports/src/app/usb_webcam/main.cc
Normal file
274
repos/libports/src/app/usb_webcam/main.cc
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
* \brief USB webcam app using libuvc
|
||||
* \author Josef Soentgen
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2021-01-25
|
||||
*
|
||||
* The test component is based on the original 'example.c'.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2021 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/log.h>
|
||||
#include <gui_session/connection.h>
|
||||
#include <libc/component.h>
|
||||
#include <os/pixel_rgb888.h>
|
||||
|
||||
/* libuv */
|
||||
#include <libyuv/convert_argb.h>
|
||||
|
||||
/* libuvc stuff */
|
||||
#include "libuvc/libuvc.h"
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
class Viewer
|
||||
{
|
||||
private:
|
||||
|
||||
Viewer(const Viewer &) = delete;
|
||||
const Viewer& operator=(const Viewer&) = delete;
|
||||
|
||||
using PT = Genode::Pixel_rgb888;
|
||||
|
||||
Genode::Env &_env;
|
||||
Gui::Connection _gui { _env, "webcam" };
|
||||
Gui::Session::View_handle _view { _gui.create_view() };
|
||||
Framebuffer::Mode const _mode;
|
||||
|
||||
Constructible<Genode::Attached_dataspace> _fb_ds { };
|
||||
uint8_t *_framebuffer { nullptr };
|
||||
|
||||
public:
|
||||
|
||||
Viewer(Genode::Env &env, Framebuffer::Mode mode)
|
||||
:
|
||||
_env { env },
|
||||
_mode { mode }
|
||||
{
|
||||
_gui.buffer(mode, false);
|
||||
|
||||
_fb_ds.construct(_env.rm(), _gui.framebuffer()->dataspace());
|
||||
_framebuffer = _fb_ds->local_addr<uint8_t>();
|
||||
|
||||
using Command = Gui::Session::Command;
|
||||
using namespace Gui;
|
||||
|
||||
_gui.enqueue<Command::Geometry>(_view, Gui::Rect(Gui::Point(0, 0), _mode.area));
|
||||
_gui.enqueue<Command::To_front>(_view, Gui::Session::View_handle());
|
||||
_gui.enqueue<Command::Title>(_view, "webcam");
|
||||
_gui.execute();
|
||||
}
|
||||
|
||||
uint8_t *framebuffer() { return _framebuffer; }
|
||||
|
||||
void refresh() {
|
||||
_gui.framebuffer()->refresh(0, 0, _mode.area.w(), _mode.area.h());
|
||||
}
|
||||
|
||||
Framebuffer::Mode const &mode() { return _mode; }
|
||||
};
|
||||
|
||||
|
||||
static void cb(uvc_frame_t *frame, void *ptr)
|
||||
{
|
||||
Viewer *viewer = ptr ? reinterpret_cast<Viewer*>(ptr) : nullptr;
|
||||
if (!viewer) return;
|
||||
|
||||
int err = 0;
|
||||
int width = viewer->mode().area.w();
|
||||
int height = viewer->mode().area.h();
|
||||
|
||||
switch (frame->frame_format) {
|
||||
case UVC_COLOR_FORMAT_MJPEG:
|
||||
err = libyuv::MJPGToARGB((uint8_t const *)frame->data,
|
||||
frame->data_bytes,
|
||||
viewer->framebuffer(),
|
||||
width * 4,
|
||||
width, height,
|
||||
width, height);
|
||||
if (err) {
|
||||
error("MJPGToARGB returned:", err);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case UVC_COLOR_FORMAT_YUYV:
|
||||
|
||||
/* skip incomplete frames */
|
||||
if (frame->data_bytes < width * height * 2ul)
|
||||
break;
|
||||
|
||||
err = libyuv::YUY2ToARGB((uint8_t const *)frame->data,
|
||||
width * 2,
|
||||
viewer->framebuffer(),
|
||||
width * 4,
|
||||
width, height);
|
||||
if (err) {
|
||||
error("YUY2ToARGB returned:", err);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
};
|
||||
|
||||
viewer->refresh();
|
||||
}
|
||||
|
||||
|
||||
class Webcam
|
||||
{
|
||||
private:
|
||||
|
||||
Webcam(const Webcam &) = delete;
|
||||
const Webcam& operator=(const Webcam&) = delete;
|
||||
|
||||
Env &_env;
|
||||
uvc_context_t *_context { nullptr };
|
||||
uvc_device_t *_device { nullptr };
|
||||
uvc_device_handle_t *_handle { nullptr };
|
||||
Viewer _viewer;
|
||||
|
||||
void _cleanup()
|
||||
{
|
||||
Libc::with_libc([&] () {
|
||||
if (_handle) uvc_stop_streaming(_handle);
|
||||
if (_device) uvc_unref_device(_device);
|
||||
if (_context) uvc_exit(_context);
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Webcam(Env &env, Framebuffer::Mode mode, uvc_frame_format format, unsigned fps)
|
||||
:
|
||||
_env(env), _viewer(env, mode)
|
||||
{
|
||||
int result = Libc::with_libc([&] () {
|
||||
|
||||
uvc_error_t res = uvc_init(&_context, NULL);
|
||||
|
||||
if (res < 0) {
|
||||
uvc_perror(res, "uvc_init failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = uvc_find_device(_context, &_device, 0, 0, NULL);
|
||||
if (res < 0) {
|
||||
uvc_perror(res, "uvc_find_device failed");
|
||||
return -2;
|
||||
}
|
||||
|
||||
res = uvc_open(_device, &_handle);
|
||||
if (res < 0) {
|
||||
uvc_perror(res, "uvc_open failed");
|
||||
return -3;
|
||||
}
|
||||
|
||||
uvc_stream_ctrl_t control;
|
||||
res = uvc_get_stream_ctrl_format_size(_handle, &control, format,
|
||||
mode.area.w(), mode.area.h(),
|
||||
fps);
|
||||
if (res < 0) {
|
||||
error("Unsupported mode: ", mode, " format: ", (unsigned)format, " fps: ", fps);
|
||||
log("Supported modes: ");
|
||||
uvc_print_diag(_handle, stderr);
|
||||
return -4;
|
||||
}
|
||||
|
||||
res = uvc_start_streaming(_handle, &control, cb, &_viewer, 0);
|
||||
if (res < 0) {
|
||||
uvc_perror(res, "Start streaming failed");
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* e.g., turn on auto exposure */
|
||||
uvc_set_ae_mode(_handle, 1);
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (result < 0) {
|
||||
_cleanup();
|
||||
throw -1;
|
||||
}
|
||||
}
|
||||
|
||||
~Webcam()
|
||||
{
|
||||
_cleanup();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Main
|
||||
{
|
||||
private:
|
||||
|
||||
Env &_env;
|
||||
Attached_rom_dataspace _config_rom { _env, "config" };
|
||||
Constructible<Webcam> _webcam { };
|
||||
Signal_handler<Main> _config_sigh { _env.ep(), *this, &Main::_config_update };
|
||||
|
||||
void _config_update()
|
||||
{
|
||||
_config_rom.update();
|
||||
|
||||
if (_config_rom.valid() == false) return;
|
||||
|
||||
Xml_node config = _config_rom.xml();
|
||||
bool enabled = config.attribute_value("enabled", false);
|
||||
unsigned width = config.attribute_value("width", 640u);
|
||||
unsigned height = config.attribute_value("height", 480u);
|
||||
unsigned fps = config.attribute_value("fps", 15u);
|
||||
String<8> format { config.attribute_value("format", String<8>("yuv")) };
|
||||
|
||||
uvc_frame_format frame_format;
|
||||
if (format == "yuv")
|
||||
frame_format = UVC_FRAME_FORMAT_YUYV;
|
||||
else if (format == "mjpeg")
|
||||
frame_format = UVC_FRAME_FORMAT_MJPEG;
|
||||
else {
|
||||
warning("Unkown format '", format, "' trying 'yuv'");
|
||||
frame_format = UVC_FRAME_FORMAT_YUYV;
|
||||
}
|
||||
|
||||
log("config: enabled: ", enabled, " ", width, "x", height, " format: ", (unsigned)frame_format);
|
||||
if (_webcam.constructed())
|
||||
_webcam.destruct();
|
||||
|
||||
if (enabled) {
|
||||
try {
|
||||
_webcam.construct(_env, Framebuffer::Mode {
|
||||
.area = { width, height } }, frame_format, fps);
|
||||
} catch (...) { }
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Main(Env &env)
|
||||
:
|
||||
_env(env)
|
||||
{
|
||||
_config_rom.sigh(_config_sigh);
|
||||
_config_update();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Libc::Component::construct(Libc::Env &env)
|
||||
{
|
||||
static Main main(env);
|
||||
}
|
3
repos/libports/src/app/usb_webcam/target.mk
Normal file
3
repos/libports/src/app/usb_webcam/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET := usb_webcam
|
||||
SRC_CC := main.cc
|
||||
LIBS := libuvc libyuv libc
|
Loading…
Reference in New Issue
Block a user