From 84d89b1659b6eb3e7707f2fe107b9cada516f053 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 14 Jun 2016 22:20:08 +0300 Subject: add ResourceManager --- kms++util/inc/kms++util/kms++util.h | 1 + kms++util/inc/kms++util/resourcemanager.h | 27 ++++++ kms++util/src/resourcemanager.cpp | 147 ++++++++++++++++++++++++++++++ py/pykmsutil.cpp | 18 ++++ 4 files changed, 193 insertions(+) create mode 100644 kms++util/inc/kms++util/resourcemanager.h create mode 100644 kms++util/src/resourcemanager.cpp diff --git a/kms++util/inc/kms++util/kms++util.h b/kms++util/inc/kms++util/kms++util.h index ca3c406..10a1f0a 100644 --- a/kms++util/inc/kms++util/kms++util.h +++ b/kms++util/inc/kms++util/kms++util.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include diff --git a/kms++util/inc/kms++util/resourcemanager.h b/kms++util/inc/kms++util/resourcemanager.h new file mode 100644 index 0000000..92e7b93 --- /dev/null +++ b/kms++util/inc/kms++util/resourcemanager.h @@ -0,0 +1,27 @@ +#include +#include +#include + +namespace kms { + +class ResourceManager +{ +public: + ResourceManager(Card& card); + + void reset(); + + Connector* reserve_connector(const std::string& name = ""); + Crtc* reserve_crtc(Connector* conn); + Plane* reserve_plane(Crtc* crtc, PlaneType type, PixelFormat format = PixelFormat::Undefined); + Plane* reserve_primary_plane(Crtc* crtc, PixelFormat format = PixelFormat::Undefined); + Plane* reserve_overlay_plane(Crtc* crtc, PixelFormat format = PixelFormat::Undefined); + +private: + Card& m_card; + std::vector m_reserved_connectors; + std::vector m_reserved_crtcs; + std::vector m_reserved_planes; +}; + +} diff --git a/kms++util/src/resourcemanager.cpp b/kms++util/src/resourcemanager.cpp new file mode 100644 index 0000000..cdd3e40 --- /dev/null +++ b/kms++util/src/resourcemanager.cpp @@ -0,0 +1,147 @@ +#include +#include +#include + +using namespace kms; +using namespace std; + +template +auto contains(const C& v, const T& x) +-> decltype(end(v), true) +{ + return end(v) != std::find(begin(v), end(v), x); +} + +ResourceManager::ResourceManager(Card& card) + : m_card(card) +{ +} + +void ResourceManager::reset() +{ + m_reserved_connectors.clear(); + m_reserved_crtcs.clear(); + m_reserved_planes.clear(); +} + +static Connector* find_connector(Card& card, const vector reserved) +{ + for (Connector* conn : card.get_connectors()) { + if (!conn->connected()) + continue; + + if (contains(reserved, conn)) + continue; + + return conn; + } + + return nullptr; +} + +static Connector* resolve_connector(Card& card, const string& name, const vector reserved) +{ + auto connectors = card.get_connectors(); + + if (name[0] == '@') { + char* endptr; + unsigned id = strtoul(name.c_str() + 1, &endptr, 10); + if (*endptr == 0) { + Connector* c = card.get_connector(id); + + if (!c || contains(reserved, c)) + return nullptr; + + return c; + } + } else { + char* endptr; + unsigned idx = strtoul(name.c_str(), &endptr, 10); + if (*endptr == 0) { + if (idx >= connectors.size()) + return nullptr; + + Connector* c = connectors[idx]; + + if (contains(reserved, c)) + return nullptr; + + return c; + } + } + + for (Connector* conn : connectors) { + if (to_lower(conn->fullname()).find(to_lower(name)) == string::npos) + continue; + + if (contains(reserved, conn)) + continue; + + return conn; + } + + return nullptr; +} + +Connector* ResourceManager::reserve_connector(const string& name) +{ + Connector* conn; + + if (name.empty()) + conn = find_connector(m_card, m_reserved_connectors); + else + conn = resolve_connector(m_card, name, m_reserved_connectors); + + if (!conn) + return nullptr; + + m_reserved_connectors.push_back(conn); + return conn; +} + +Crtc* ResourceManager::reserve_crtc(Connector* conn) +{ + if (Crtc* crtc = conn->get_current_crtc()) { + m_reserved_crtcs.push_back(crtc); + return crtc; + } + + for (Crtc* crtc : conn->get_possible_crtcs()) { + if (contains(m_reserved_crtcs, crtc)) + continue; + + m_reserved_crtcs.push_back(crtc); + return crtc; + } + + return nullptr; +} + +Plane* ResourceManager::reserve_plane(Crtc* crtc, PlaneType type, PixelFormat format) +{ + for (Plane* plane : crtc->get_possible_planes()) { + if (plane->plane_type() != type) + continue; + + if (format != PixelFormat::Undefined && !plane->supports_format(format)) + continue; + + if (contains(m_reserved_planes, plane)) + continue; + + m_reserved_planes.push_back(plane); + return plane; + } + + return nullptr; +} + +Plane* ResourceManager::reserve_primary_plane(Crtc* crtc, PixelFormat format) +{ + return reserve_plane(crtc, PlaneType::Primary, format); +} + +Plane* ResourceManager::reserve_overlay_plane(Crtc* crtc, PixelFormat format) +{ + return reserve_plane(crtc, PlaneType::Overlay, format); +} diff --git a/py/pykmsutil.cpp b/py/pykmsutil.cpp index 5ee1d4e..ab9f5a8 100644 --- a/py/pykmsutil.cpp +++ b/py/pykmsutil.cpp @@ -20,6 +20,24 @@ void init_pykmstest(py::module &m) .def_property_readonly("rgb565", &RGB::rgb565) ; + py::class_(m, "ResourceManager") + .def(py::init()) + .def("reset", &ResourceManager::reset) + .def("reserve_connector", &ResourceManager::reserve_connector, + py::arg("name") = string()) + .def("reserve_crtc", &ResourceManager::reserve_crtc) + .def("reserve_plane", &ResourceManager::reserve_plane, + py::arg("crtc"), + py::arg("type"), + py::arg("format") = PixelFormat::Undefined) + .def("reserve_primary_plane", &ResourceManager::reserve_primary_plane, + py::arg("crtc"), + py::arg("format") = PixelFormat::Undefined) + .def("reserve_overlay_plane", &ResourceManager::reserve_overlay_plane, + py::arg("crtc"), + py::arg("format") = PixelFormat::Undefined) + ; + // Use lambdas to handle IMappedFramebuffer m.def("draw_test_pattern", [](DumbFramebuffer& fb) { draw_test_pattern(fb); } ); m.def("draw_color_bar", [](DumbFramebuffer& fb, int old_xpos, int xpos, int width) { -- cgit v1.2.3 From 7881bfd6586bd8ffa9b766cdba332c7a1015c9dd Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 14 Jun 2016 22:20:50 +0300 Subject: py: use ResourceManager --- py/alpha-test.py | 6 +++--- py/db.py | 5 +++-- py/functest.py | 7 +++---- py/gamma.py | 6 +++--- py/helpers.py | 12 ------------ py/iact.py | 7 +++---- py/test.py | 7 +++---- py/trans-test.py | 6 +++--- 8 files changed, 21 insertions(+), 35 deletions(-) diff --git a/py/alpha-test.py b/py/alpha-test.py index 113fab0..c6ec8ee 100755 --- a/py/alpha-test.py +++ b/py/alpha-test.py @@ -9,10 +9,10 @@ card = pykms.Card() card = 0 card = pykms.Card() - -conn = card.get_first_connected_connector() +res = pykms.ResourceManager(card) +conn = res.reserve_connector() +crtc = res.reserve_crtc(conn) mode = conn.get_default_mode() -crtc = get_crtc_for_connector(conn) planes = [] for p in card.planes: diff --git a/py/db.py b/py/db.py index 6073765..3ffb716 100755 --- a/py/db.py +++ b/py/db.py @@ -41,9 +41,10 @@ class FlipHandler(pykms.PageFlipHandlerBase): card = pykms.Card() -conn = card.get_first_connected_connector() +res = pykms.ResourceManager(card) +conn = res.reserve_connector() +crtc = res.reserve_crtc(conn) mode = conn.get_default_mode() -crtc = get_crtc_for_connector(conn) fliphandler = FlipHandler() diff --git a/py/functest.py b/py/functest.py index c2548fa..44c29fb 100755 --- a/py/functest.py +++ b/py/functest.py @@ -4,16 +4,15 @@ import pykms from helpers import * card = pykms.Card() - -conn = card.get_first_connected_connector() +res = pykms.ResourceManager(card) +conn = res.reserve_connector() +crtc = res.reserve_crtc(conn) mode = conn.get_default_mode() fb = pykms.DumbFramebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); pykms.draw_test_pattern(fb); -crtc = get_crtc_for_connector(conn) - crtc.set_mode(conn, fb, mode) print("OK") diff --git a/py/gamma.py b/py/gamma.py index e1daa43..a6b68cc 100755 --- a/py/gamma.py +++ b/py/gamma.py @@ -8,10 +8,10 @@ card = pykms.Card() card = 0 card = pykms.Card() - -conn = card.get_first_connected_connector() +res = pykms.ResourceManager(card) +conn = res.reserve_connector() +crtc = res.reserve_crtc(conn) mode = conn.get_default_mode() -crtc = get_crtc_for_connector(conn) fb = pykms.DumbFramebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); pykms.draw_test_pattern(fb); diff --git a/py/helpers.py b/py/helpers.py index e92163c..fd67d41 100644 --- a/py/helpers.py +++ b/py/helpers.py @@ -52,15 +52,3 @@ def disable_planes(card): if areq.commit_sync() != 0: print("disabling planes failed") - -def get_crtc_for_connector(conn): - crtc = conn.get_current_crtc() - - if crtc != None: - return crtc - - for crtc in conn.get_possible_crtcs(): - if crtc.mode_valid == False: - return crtc - - raise RuntimeError("No free crtc found") diff --git a/py/iact.py b/py/iact.py index 518dbfa..fecd899 100755 --- a/py/iact.py +++ b/py/iact.py @@ -9,16 +9,15 @@ from math import cos from helpers import * card = pykms.Card() - -conn = card.get_first_connected_connector() +res = pykms.ResourceManager(card) +conn = res.reserve_connector() +crtc = res.reserve_crtc(conn) mode = conn.get_default_mode() fb = pykms.DumbFramebuffer(card, 200, 200, "XR24"); pykms.draw_test_pattern(fb); -crtc = get_crtc_for_connector(conn) - #crtc.set_mode(conn, fb, mode) i = 0 diff --git a/py/test.py b/py/test.py index 7625f10..9c23b5b 100755 --- a/py/test.py +++ b/py/test.py @@ -4,16 +4,15 @@ import pykms from helpers import * card = pykms.Card() - -conn = card.get_first_connected_connector() +res = pykms.ResourceManager(card) +conn = res.reserve_connector() +crtc = res.reserve_crtc(conn) mode = conn.get_default_mode() fb = pykms.DumbFramebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); pykms.draw_test_pattern(fb); -crtc = get_crtc_for_connector(conn) - crtc.set_mode(conn, fb, mode) input("press enter to exit\n") diff --git a/py/trans-test.py b/py/trans-test.py index e80802b..8c1f964 100755 --- a/py/trans-test.py +++ b/py/trans-test.py @@ -9,10 +9,10 @@ card = pykms.Card() card = 0 card = pykms.Card() - -conn = card.get_first_connected_connector() +res = pykms.ResourceManager(card) +conn = res.reserve_connector() +crtc = res.reserve_crtc(conn) mode = conn.get_default_mode() -crtc = get_crtc_for_connector(conn) planes = [] for p in card.planes: -- cgit v1.2.3 From 511d5934c4ae34db9c9dc4dce746aa8aef0272e1 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 14 Jun 2016 22:38:29 +0300 Subject: kmsview: use resman --- utils/kmsview.cpp | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/utils/kmsview.cpp b/utils/kmsview.cpp index b503f0a..6f236a1 100644 --- a/utils/kmsview.cpp +++ b/utils/kmsview.cpp @@ -79,27 +79,14 @@ int main(int argc, char** argv) Card card(dev_path); + ResourceManager res(card); - auto conn = card.get_first_connected_connector(); - auto crtc = conn->get_current_crtc(); - - auto fb = new DumbFramebuffer(card, w, h, pixfmt); - - Plane* plane = 0; - - for (Plane* p : crtc->get_possible_planes()) { - if (p->plane_type() != PlaneType::Overlay) - continue; - - if (!p->supports_format(pixfmt)) - continue; - - plane = p; - break; - } - + auto conn = res.reserve_connector(); + auto crtc = res.reserve_crtc(conn); + auto plane = res.reserve_overlay_plane(crtc, pixfmt); FAIL_IF(!plane, "available plane not found"); + auto fb = new DumbFramebuffer(card, w, h, pixfmt); unsigned frame_size = 0; for (unsigned i = 0; i < fb->num_planes(); ++i) -- cgit v1.2.3 From c65ffb7cd928894788411a3563aec87e22791ca8 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Sat, 11 Jun 2016 23:40:49 +0300 Subject: kmsutils: add VideoDevice --- kms++util/inc/kms++util/videodevice.h | 86 +++++++ kms++util/src/videodevice.cpp | 460 ++++++++++++++++++++++++++++++++++ 2 files changed, 546 insertions(+) create mode 100644 kms++util/inc/kms++util/videodevice.h create mode 100644 kms++util/src/videodevice.cpp diff --git a/kms++util/inc/kms++util/videodevice.h b/kms++util/inc/kms++util/videodevice.h new file mode 100644 index 0000000..68e2b01 --- /dev/null +++ b/kms++util/inc/kms++util/videodevice.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include + +class VideoStreamer; + +class VideoDevice +{ +public: + struct VideoFrameSize + { + uint32_t min_w, max_w, step_w; + uint32_t min_h, max_h, step_h; + }; + + VideoDevice(const std::string& dev); + VideoDevice(int fd); + ~VideoDevice(); + + VideoDevice(const VideoDevice& other) = delete; + VideoDevice& operator=(const VideoDevice& other) = delete; + + VideoStreamer* get_capture_streamer(); + VideoStreamer* get_output_streamer(); + + std::vector> get_discrete_frame_sizes(kms::PixelFormat fmt); + VideoFrameSize get_frame_sizes(kms::PixelFormat fmt); + + int fd() const { return m_fd; } + bool has_capture() const { return m_has_capture; } + bool has_output() const { return m_has_output; } + bool has_m2m() const { return m_has_m2m; } + + static std::vector get_capture_devices(); + static std::vector get_m2m_devices(); + +private: + int m_fd; + + bool m_has_capture; + bool m_has_mplane_capture; + + bool m_has_output; + bool m_has_mplane_output; + + bool m_has_m2m; + bool m_has_mplane_m2m; + + std::vector m_capture_fbs; + std::vector m_output_fbs; + + VideoStreamer* m_capture_streamer; + VideoStreamer* m_output_streamer; +}; + +class VideoStreamer +{ +public: + enum class StreamerType { + CaptureSingle, + CaptureMulti, + OutputSingle, + OutputMulti, + }; + + VideoStreamer(int fd, StreamerType type); + + std::vector get_ports(); + void set_port(uint32_t index); + + std::vector get_formats(); + void set_format(kms::PixelFormat fmt, uint32_t width, uint32_t height); + void set_queue_size(uint32_t queue_size); + void queue(kms::DumbFramebuffer* fb); + kms::DumbFramebuffer* dequeue(); + void stream_on(); + void stream_off(); + + int fd() const { return m_fd; } + +private: + int m_fd; + StreamerType m_type; + std::vector m_fbs; +}; diff --git a/kms++util/src/videodevice.cpp b/kms++util/src/videodevice.cpp new file mode 100644 index 0000000..8cc18dc --- /dev/null +++ b/kms++util/src/videodevice.cpp @@ -0,0 +1,460 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace kms; + +/* V4L2 helper funcs */ +static vector v4l2_get_formats(int fd, uint32_t buf_type) +{ + vector v; + + v4l2_fmtdesc desc { }; + desc.type = buf_type; + + while (ioctl(fd, VIDIOC_ENUM_FMT, &desc) == 0) { + v.push_back((PixelFormat)desc.pixelformat); + desc.index++; + } + + return v; +} + +static void v4l2_set_format(int fd, PixelFormat fmt, uint32_t width, uint32_t height, uint32_t buf_type) +{ + int r; + + v4l2_format v4lfmt { }; + + v4lfmt.type = buf_type; + r = ioctl(fd, VIDIOC_G_FMT, &v4lfmt); + ASSERT(r == 0); + + const PixelFormatInfo& pfi = get_pixel_format_info(fmt); + + bool mplane = buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + + if (mplane) { + v4l2_pix_format_mplane& mp = v4lfmt.fmt.pix_mp; + + mp.pixelformat = (uint32_t)fmt; + mp.width = width; + mp.height = height; + + mp.num_planes = pfi.num_planes; + + for (unsigned i = 0; i < pfi.num_planes; ++i) { + const PixelFormatPlaneInfo& pfpi = pfi.planes[i]; + v4l2_plane_pix_format& p = mp.plane_fmt[i]; + + p.bytesperline = width * pfpi.bitspp / 8; + p.sizeimage = p.bytesperline * height / pfpi.ysub; + } + + r = ioctl(fd, VIDIOC_S_FMT, &v4lfmt); + ASSERT(r == 0); + + ASSERT(mp.pixelformat == (uint32_t)fmt); + ASSERT(mp.width == width); + ASSERT(mp.height == height); + + ASSERT(mp.num_planes == pfi.num_planes); + + for (unsigned i = 0; i < pfi.num_planes; ++i) { + const PixelFormatPlaneInfo& pfpi = pfi.planes[i]; + v4l2_plane_pix_format& p = mp.plane_fmt[i]; + + ASSERT(p.bytesperline == width * pfpi.bitspp / 8); + ASSERT(p.sizeimage == p.bytesperline * height / pfpi.ysub); + } + } else { + ASSERT(pfi.num_planes == 1); + + v4lfmt.fmt.pix.pixelformat = (uint32_t)fmt; + v4lfmt.fmt.pix.width = width; + v4lfmt.fmt.pix.height = height; + v4lfmt.fmt.pix.bytesperline = width * pfi.planes[0].bitspp / 8; + + r = ioctl(fd, VIDIOC_S_FMT, &v4lfmt); + ASSERT(r == 0); + + ASSERT(v4lfmt.fmt.pix.pixelformat == (uint32_t)fmt); + ASSERT(v4lfmt.fmt.pix.width == width); + ASSERT(v4lfmt.fmt.pix.height == height); + ASSERT(v4lfmt.fmt.pix.bytesperline == width * pfi.planes[0].bitspp / 8); + } +} + +static void v4l2_request_bufs(int fd, uint32_t queue_size, uint32_t buf_type) +{ + v4l2_requestbuffers v4lreqbuf { }; + v4lreqbuf.type = buf_type; + v4lreqbuf.memory = V4L2_MEMORY_DMABUF; + v4lreqbuf.count = queue_size; + int r = ioctl(fd, VIDIOC_REQBUFS, &v4lreqbuf); + ASSERT(r == 0); + ASSERT(v4lreqbuf.count == queue_size); +} + +static void v4l2_queue_dmabuf(int fd, uint32_t index, DumbFramebuffer* fb, uint32_t buf_type) +{ + v4l2_buffer buf { }; + buf.type = buf_type; + buf.memory = V4L2_MEMORY_DMABUF; + buf.index = index; + + const PixelFormatInfo& pfi = get_pixel_format_info(fb->format()); + + bool mplane = buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + + if (mplane) { + buf.length = pfi.num_planes; + + v4l2_plane planes[4] { }; + buf.m.planes = planes; + + for (unsigned i = 0; i < pfi.num_planes; ++i) { + planes[i].m.fd = fb->prime_fd(i); + planes[i].bytesused = fb->size(i); + planes[i].length = fb->size(i); + } + + int r = ioctl(fd, VIDIOC_QBUF, &buf); + ASSERT(r == 0); + } else { + buf.m.fd = fb->prime_fd(0); + + int r = ioctl(fd, VIDIOC_QBUF, &buf); + ASSERT(r == 0); + } +} + +static uint32_t v4l2_dequeue(int fd, uint32_t buf_type) +{ + v4l2_buffer buf { }; + buf.type = buf_type; + buf.memory = V4L2_MEMORY_DMABUF; + + // V4L2 crashes if planes are not set + v4l2_plane planes[4] { }; + buf.m.planes = planes; + buf.length = 4; + + int r = ioctl(fd, VIDIOC_DQBUF, &buf); + if (r) + throw system_error(errno, generic_category()); + + return buf.index; +} + + + + +VideoDevice::VideoDevice(const string& dev) + :VideoDevice(::open(dev.c_str(), O_RDWR | O_NONBLOCK)) +{ +} + +VideoDevice::VideoDevice(int fd) + : m_fd(fd), m_has_capture(false), m_has_output(false), m_has_m2m(false), m_capture_streamer(0), m_output_streamer(0) +{ + FAIL_IF(fd < 0, "Bad fd"); + + struct v4l2_capability cap = { }; + int r = ioctl(fd, VIDIOC_QUERYCAP, &cap); + ASSERT(r == 0); + + if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) { + m_has_capture = true; + m_has_mplane_capture = true; + } else if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) { + m_has_capture = true; + m_has_mplane_capture = false; + } + + if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE) { + m_has_output = true; + m_has_mplane_output = true; + } else if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) { + m_has_output = true; + m_has_mplane_output = false; + } + + if (cap.capabilities & V4L2_CAP_VIDEO_M2M_MPLANE) { + m_has_m2m = true; + m_has_capture = true; + m_has_output = true; + m_has_mplane_m2m = true; + m_has_mplane_capture = true; + m_has_mplane_output = true; + } else if (cap.capabilities & V4L2_CAP_VIDEO_M2M) { + m_has_m2m = true; + m_has_capture = true; + m_has_output = true; + m_has_mplane_m2m = false; + m_has_mplane_capture = false; + m_has_mplane_output = false; + } +} + +VideoDevice::~VideoDevice() +{ + ::close(m_fd); +} + +VideoStreamer* VideoDevice::get_capture_streamer() +{ + ASSERT(m_has_capture); + + if (!m_capture_streamer) { + auto type = m_has_mplane_capture ? VideoStreamer::StreamerType::CaptureMulti : VideoStreamer::StreamerType::CaptureSingle; + m_capture_streamer = new VideoStreamer(m_fd, type); + } + + return m_capture_streamer; +} + +VideoStreamer* VideoDevice::get_output_streamer() +{ + ASSERT(m_has_output); + + if (!m_output_streamer) { + auto type = m_has_mplane_output ? VideoStreamer::StreamerType::OutputMulti : VideoStreamer::StreamerType::OutputSingle; + m_output_streamer = new VideoStreamer(m_fd, type); + } + + return m_output_streamer; +} + +vector> VideoDevice::get_discrete_frame_sizes(PixelFormat fmt) +{ + vector> v; + + v4l2_frmsizeenum v4lfrms { }; + v4lfrms.pixel_format = (uint32_t)fmt; + + int r = ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms); + ASSERT(r); + + FAIL_IF(v4lfrms.type != V4L2_FRMSIZE_TYPE_DISCRETE, "No discrete frame sizes"); + + while (ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms) == 0) { + v.emplace_back(v4lfrms.discrete.width, v4lfrms.discrete.height); + v4lfrms.index++; + }; + + return v; +} + +VideoDevice::VideoFrameSize VideoDevice::get_frame_sizes(PixelFormat fmt) +{ + v4l2_frmsizeenum v4lfrms { }; + v4lfrms.pixel_format = (uint32_t)fmt; + + int r = ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms); + ASSERT(r); + + FAIL_IF(v4lfrms.type == V4L2_FRMSIZE_TYPE_DISCRETE, "No continuous frame sizes"); + + VideoFrameSize s; + + s.min_w = v4lfrms.stepwise.min_width; + s.max_w = v4lfrms.stepwise.max_width; + s.step_w = v4lfrms.stepwise.step_width; + + s.min_h = v4lfrms.stepwise.min_height; + s.max_h = v4lfrms.stepwise.max_height; + s.step_h = v4lfrms.stepwise.step_height; + + return s; +} + +vector VideoDevice::get_capture_devices() +{ + vector v; + + for (int i = 0; i < 20; ++i) { + string name = "/dev/video" + to_string(i); + + struct stat buffer; + if (stat(name.c_str(), &buffer) != 0) + continue; + + VideoDevice vid(name); + + if (vid.has_capture() && !vid.has_m2m()) + v.push_back(name); + } + + return v; +} + +vector VideoDevice::get_m2m_devices() +{ + vector v; + + for (int i = 0; i < 20; ++i) { + string name = "/dev/video" + to_string(i); + + struct stat buffer; + if (stat(name.c_str(), &buffer) != 0) + continue; + + VideoDevice vid(name); + + if (vid.has_m2m()) + v.push_back(name); + } + + return v; +} + + +VideoStreamer::VideoStreamer(int fd, StreamerType type) + : m_fd(fd), m_type(type) +{ + +} + +std::vector VideoStreamer::get_ports() +{ + vector v; + + switch (m_type) { + case StreamerType::CaptureSingle: + case StreamerType::CaptureMulti: + { + struct v4l2_input input { }; + + while (ioctl(m_fd, VIDIOC_ENUMINPUT, &input) == 0) { + v.push_back(string((char*)&input.name)); + input.index++; + } + + break; + } + + case StreamerType::OutputSingle: + case StreamerType::OutputMulti: + { + struct v4l2_output output { }; + + while (ioctl(m_fd, VIDIOC_ENUMOUTPUT, &output) == 0) { + v.push_back(string((char*)&output.name)); + output.index++; + } + + break; + } + + default: + FAIL("Bad StreamerType"); + } + + return v; +} + +void VideoStreamer::set_port(uint32_t index) +{ + unsigned long req; + + switch (m_type) { + case StreamerType::CaptureSingle: + case StreamerType::CaptureMulti: + req = VIDIOC_S_INPUT; + break; + + case StreamerType::OutputSingle: + case StreamerType::OutputMulti: + req = VIDIOC_S_OUTPUT; + break; + + default: + FAIL("Bad StreamerType"); + } + + int r = ioctl(m_fd, req, &index); + ASSERT(r == 0); +} + +static v4l2_buf_type get_buf_type(VideoStreamer::StreamerType type) +{ + switch (type) { + case VideoStreamer::StreamerType::CaptureSingle: + return V4L2_BUF_TYPE_VIDEO_CAPTURE; + case VideoStreamer::StreamerType::CaptureMulti: + return V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + case VideoStreamer::StreamerType::OutputSingle: + return V4L2_BUF_TYPE_VIDEO_OUTPUT; + case VideoStreamer::StreamerType::OutputMulti: + return V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + default: + FAIL("Bad StreamerType"); + } +} + +std::vector VideoStreamer::get_formats() +{ + return v4l2_get_formats(m_fd, get_buf_type(m_type)); +} + +void VideoStreamer::set_format(PixelFormat fmt, uint32_t width, uint32_t height) +{ + v4l2_set_format(m_fd, fmt, width, height, get_buf_type(m_type)); +} + +void VideoStreamer::set_queue_size(uint32_t queue_size) +{ + v4l2_request_bufs(m_fd, queue_size, get_buf_type(m_type)); + m_fbs.resize(queue_size); +} + +void VideoStreamer::queue(DumbFramebuffer* fb) +{ + uint32_t idx; + + for (idx = 0; idx < m_fbs.size(); ++idx) { + if (m_fbs[idx] == nullptr) + break; + } + + FAIL_IF(idx == m_fbs.size(), "queue full"); + + m_fbs[idx] = fb; + + v4l2_queue_dmabuf(m_fd, idx, fb, get_buf_type(m_type)); +} + +DumbFramebuffer*VideoStreamer::dequeue() +{ + uint32_t idx = v4l2_dequeue(m_fd, get_buf_type(m_type)); + + auto fb = m_fbs[idx]; + m_fbs[idx] = nullptr; + + return fb; +} + +void VideoStreamer::stream_on() +{ + uint32_t buf_type = get_buf_type(m_type); + int r = ioctl(m_fd, VIDIOC_STREAMON, &buf_type); + FAIL_IF(r, "Failed to enable stream: %d", r); +} + +void VideoStreamer::stream_off() +{ + uint32_t buf_type = get_buf_type(m_type); + int r = ioctl(m_fd, VIDIOC_STREAMOFF, &buf_type); + FAIL_IF(r, "Failed to disable stream: %d", r); +} -- cgit v1.2.3 From a779ec31224ab217572a20587e89ce393e62b033 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Sat, 11 Jun 2016 23:42:06 +0300 Subject: py: add pyvid --- py/CMakeLists.txt | 2 +- py/pykms.cpp | 3 +++ py/pyvid.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 py/pyvid.cpp diff --git a/py/CMakeLists.txt b/py/CMakeLists.txt index 562a3cf..1349ea5 100644 --- a/py/CMakeLists.txt +++ b/py/CMakeLists.txt @@ -10,7 +10,7 @@ endif() include_directories(${PROJECT_SOURCE_DIR}/ext/pybind11/include) -add_library(pykms SHARED pykms.cpp pykmsbase.cpp pykmsutil.cpp) +add_library(pykms SHARED pykms.cpp pykmsbase.cpp pykmsutil.cpp pyvid.cpp) target_link_libraries(pykms kms++ kms++util ${LIBDRM_LIBRARIES}) # Don't add a 'lib' prefix to the shared library diff --git a/py/pykms.cpp b/py/pykms.cpp index 57ca363..c759d23 100644 --- a/py/pykms.cpp +++ b/py/pykms.cpp @@ -9,6 +9,7 @@ using namespace std; void init_pykmstest(py::module &m); void init_pykmsbase(py::module &m); +void init_pyvid(py::module &m); class PyPageFlipHandlerBase : PageFlipHandlerBase { @@ -39,5 +40,7 @@ PYBIND11_PLUGIN(pykms) { init_pykmstest(m); + init_pyvid(m); + return m.ptr(); } diff --git a/py/pyvid.cpp b/py/pyvid.cpp new file mode 100644 index 0000000..01177d5 --- /dev/null +++ b/py/pyvid.cpp @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include + +namespace py = pybind11; + +using namespace kms; +using namespace std; + +void init_pyvid(py::module &m) +{ + py::class_(m, "VideoDevice") + .def(py::init()) + .def_property_readonly("fd", &VideoDevice::fd) + .def_property_readonly("has_capture", &VideoDevice::has_capture) + .def_property_readonly("has_output", &VideoDevice::has_output) + .def_property_readonly("has_m2m", &VideoDevice::has_m2m) + .def_property_readonly("capture_streamer", &VideoDevice::get_capture_streamer) + .def_property_readonly("output_streamer", &VideoDevice::get_output_streamer) + .def_property_readonly("discrete_frame_sizes", &VideoDevice::get_discrete_frame_sizes) + .def_property_readonly("frame_sizes", &VideoDevice::get_frame_sizes) + .def("get_capture_devices", &VideoDevice::get_capture_devices) + ; + + py::class_(m, "VideoStreamer") + .def_property_readonly("fd", &VideoStreamer::fd) + .def_property_readonly("ports", &VideoStreamer::get_ports) + .def("set_port", &VideoStreamer::set_port) + .def_property_readonly("formats", &VideoStreamer::get_formats) + .def("set_format", &VideoStreamer::set_format) + .def("set_queue_size", &VideoStreamer::set_queue_size) + .def("queue", &VideoStreamer::queue) + .def("dequeue", &VideoStreamer::dequeue) + .def("stream_on", &VideoStreamer::stream_on) + ; +} -- cgit v1.2.3 From 0bf62a2f4f724dafb77230bf2a1ca37dd7420e94 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Sat, 11 Jun 2016 23:42:58 +0300 Subject: py: add cam.py --- py/cam.py | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100755 py/cam.py diff --git a/py/cam.py b/py/cam.py new file mode 100755 index 0000000..b44f8f9 --- /dev/null +++ b/py/cam.py @@ -0,0 +1,78 @@ +#!/usr/bin/python3 + +import sys +import selectors +import pykms +from helpers import * + + +w = 640 +h = 480 +fmt = pykms.PixelFormat.YUYV + + +# This hack makes drm initialize the fbcon, setting up the default connector +card = pykms.Card() +card = 0 + +card = pykms.Card() +res = pykms.ResourceManager(card) +conn = res.reserve_connector() +crtc = res.reserve_crtc(conn) +plane = res.reserve_overlay_plane(crtc, fmt) + +mode = conn.get_default_mode() + +NUM_BUFS = 5 + +fbs = [] +for i in range(NUM_BUFS): + fb = pykms.DumbFramebuffer(card, w, h, fmt) + fbs.append(fb) + +vidpath = pykms.VideoDevice.get_capture_devices()[0] + +vid = pykms.VideoDevice(vidpath) +cap = vid.capture_streamer +cap.set_port(0) +cap.set_format(fmt, w, h) +cap.set_queue_size(NUM_BUFS) + +for fb in fbs: + cap.queue(fb) + +cap.stream_on() + + +def readvid(conn, mask): + fb = cap.dequeue() + + if card.has_atomic: + set_props(plane, { + "FB_ID": fb.id, + "CRTC_ID": crtc.id, + "SRC_W": fb.width << 16, + "SRC_H": fb.height << 16, + "CRTC_W": fb.width, + "CRTC_H": fb.height, + }) + else: + crtc.set_plane(plane, fb, 0, 0, fb.width, fb.height, + 0, 0, fb.width, fb.height) + + cap.queue(fb) + +def readkey(conn, mask): + #print("KEY EVENT"); + sys.stdin.readline() + exit(0) + +sel = selectors.DefaultSelector() +sel.register(cap.fd, selectors.EVENT_READ, readvid) +sel.register(sys.stdin, selectors.EVENT_READ, readkey) + +while True: + events = sel.select() + for key, mask in events: + callback = key.data + callback(key.fileobj, mask) -- cgit v1.2.3 From 506fa7c682a59558333c27b79dd8a6adbfd3978a Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Sat, 11 Jun 2016 23:41:11 +0300 Subject: utils: add wbcap --- utils/CMakeLists.txt | 3 + utils/wbcap.cpp | 362 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 365 insertions(+) create mode 100644 utils/wbcap.cpp diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 27e4bec..ece8c67 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -18,3 +18,6 @@ target_link_libraries(kmscapture kms++ kms++util ${LIBDRM_LIBRARIES}) add_executable (kmsblank kmsblank.cpp) target_link_libraries(kmsblank kms++ kms++util ${LIBDRM_LIBRARIES}) + +add_executable (wbcap wbcap.cpp) +target_link_libraries(wbcap kms++ kms++util ${LIBDRM_LIBRARIES}) diff --git a/utils/wbcap.cpp b/utils/wbcap.cpp new file mode 100644 index 0000000..f4f2b71 --- /dev/null +++ b/utils/wbcap.cpp @@ -0,0 +1,362 @@ +#include +#include +#include +#include + +#include +#include +#include + +#define CAMERA_BUF_QUEUE_SIZE 5 + +using namespace std; +using namespace kms; + +static vector s_fbs; +static vector s_free_fbs; +static vector s_wb_fbs; +static vector s_ready_fbs; + +class WBStreamer +{ +public: + WBStreamer(VideoStreamer* streamer, Crtc* crtc, uint32_t width, uint32_t height, PixelFormat pixfmt) + : m_capdev(*streamer) + { + m_capdev.set_port(crtc->idx()); + m_capdev.set_format(pixfmt, width, height); + m_capdev.set_queue_size(s_fbs.size()); + + for (auto fb : s_free_fbs) { + m_capdev.queue(fb); + s_wb_fbs.push_back(fb); + } + + s_free_fbs.clear(); + } + + ~WBStreamer() + { + } + + WBStreamer(const WBStreamer& other) = delete; + WBStreamer& operator=(const WBStreamer& other) = delete; + + int fd() const { return m_capdev.fd(); } + + void start_streaming() + { + m_capdev.stream_on(); + } + + void stop_streaming() + { + m_capdev.stream_off(); + } + + void Dequeue() + { + auto fb = m_capdev.dequeue(); + + auto iter = find(s_wb_fbs.begin(), s_wb_fbs.end(), fb); + s_wb_fbs.erase(iter); + + s_ready_fbs.insert(s_ready_fbs.begin(), fb); + } + + void Queue() + { + if (s_free_fbs.size() == 0) + return; + + auto fb = s_free_fbs.back(); + s_free_fbs.pop_back(); + + m_capdev.queue(fb); + + s_wb_fbs.insert(s_wb_fbs.begin(), fb); + } + +private: + VideoStreamer& m_capdev; +}; + +class WBFlipState : private PageFlipHandlerBase +{ +public: + WBFlipState(Card& card, Crtc* crtc, Plane* plane) + : m_card(card), m_crtc(crtc), m_plane(plane) + { + } + + void setup(uint32_t x, uint32_t y, uint32_t width, uint32_t height) + { + auto fb = s_ready_fbs.back(); + s_ready_fbs.pop_back(); + + AtomicReq req(m_card); + + req.add(m_plane, "CRTC_ID", m_crtc->id()); + req.add(m_plane, "FB_ID", fb->id()); + + req.add(m_plane, "CRTC_X", x); + req.add(m_plane, "CRTC_Y", y); + req.add(m_plane, "CRTC_W", width); + req.add(m_plane, "CRTC_H", height); + + req.add(m_plane, "SRC_X", 0); + req.add(m_plane, "SRC_Y", 0); + req.add(m_plane, "SRC_W", fb->width() << 16); + req.add(m_plane, "SRC_H", fb->height() << 16); + + int r = req.commit_sync(); + FAIL_IF(r, "initial plane setup failed"); + + m_current_fb = fb; + } + + void queue_next() + { + if (m_queued_fb) + return; + + if (s_ready_fbs.size() == 0) + return; + + auto fb = s_ready_fbs.back(); + s_ready_fbs.pop_back(); + + AtomicReq req(m_card); + req.add(m_plane, "FB_ID", fb->id()); + + int r = req.commit(this); + if (r) + EXIT("Flip commit failed: %d\n", r); + + m_queued_fb = fb; + } + +private: + void handle_page_flip(uint32_t frame, double time) + { + if (m_queued_fb) { + if (m_current_fb) + s_free_fbs.insert(s_free_fbs.begin(), m_current_fb); + + m_current_fb = m_queued_fb; + m_queued_fb = nullptr; + } + + queue_next(); + } + + Card& m_card; + Crtc* m_crtc; + Plane* m_plane; + + DumbFramebuffer* m_current_fb = nullptr; + DumbFramebuffer* m_queued_fb = nullptr; +}; + +class BarFlipState : private PageFlipHandlerBase +{ +public: + BarFlipState(Card& card, Crtc* crtc) + : m_card(card), m_crtc(crtc) + { + m_plane = m_crtc->get_primary_plane(); + + uint32_t w = m_crtc->mode().hdisplay; + uint32_t h = m_crtc->mode().vdisplay; + + for (unsigned i = 0; i < s_num_buffers; ++i) + m_fbs[i] = new DumbFramebuffer(card, w, h, PixelFormat::XRGB8888); + } + + ~BarFlipState() + { + for (unsigned i = 0; i < s_num_buffers; ++i) + delete m_fbs[i]; + } + + void start_flipping() + { + m_frame_num = 0; + queue_next(); + } + +private: + void handle_page_flip(uint32_t frame, double time) + { + m_frame_num++; + queue_next(); + } + + static unsigned get_bar_pos(DumbFramebuffer* fb, unsigned frame_num) + { + return (frame_num * bar_speed) % (fb->width() - bar_width + 1); + } + + void draw_bar(DumbFramebuffer* fb, unsigned frame_num) + { + int old_xpos = frame_num < s_num_buffers ? -1 : get_bar_pos(fb, frame_num - s_num_buffers); + int new_xpos = get_bar_pos(fb, frame_num); + + draw_color_bar(*fb, old_xpos, new_xpos, bar_width); + draw_text(*fb, fb->width() / 2, 0, to_string(frame_num), RGB(255, 255, 255)); + } + + void queue_next() + { + AtomicReq req(m_card); + + unsigned cur = m_frame_num % s_num_buffers; + + auto fb = m_fbs[cur]; + + draw_bar(fb, m_frame_num); + + req.add(m_plane, { + { "FB_ID", fb->id() }, + }); + + int r = req.commit(this); + if (r) + EXIT("Flip commit failed: %d\n", r); + } + + static const unsigned s_num_buffers = 3; + + DumbFramebuffer* m_fbs[s_num_buffers]; + + Card& m_card; + Crtc* m_crtc; + Plane* m_plane; + + unsigned m_frame_num; + + static const unsigned bar_width = 20; + static const unsigned bar_speed = 8; +}; + +static const char* usage_str = + "Usage: wbcap [OPTIONS]\n\n" + "Options:\n" + " -s, --src=CONN Source connector\n" + " -d, --dst=CONN Destination connector\n" + " -f, --format=4CC Format" + " -h, --help Print this help\n" + ; + +int main(int argc, char** argv) +{ + string src_conn_name = "unknown"; + string dst_conn_name = "hdmi"; + PixelFormat pixfmt = PixelFormat::XRGB8888; + + OptionSet optionset = { + Option("s|src=", [&](string s) + { + src_conn_name = s; + }), + Option("d|dst=", [&](string s) + { + dst_conn_name = s; + }), + Option("f|format=", [&](string s) + { + pixfmt = FourCCToPixelFormat(s); + }), + Option("h|help", [&]() + { + puts(usage_str); + exit(-1); + }), + }; + + optionset.parse(argc, argv); + + if (optionset.params().size() > 0) { + puts(usage_str); + exit(-1); + } + + VideoDevice vid("/dev/video11"); + + Card card; + ResourceManager resman(card); + + auto src_conn = resman.reserve_connector(src_conn_name); + auto src_crtc = resman.reserve_crtc(src_conn); + + uint32_t src_width = src_crtc->mode().hdisplay; + uint32_t src_height = src_crtc->mode().vdisplay; + + printf("src %s, crtc %ux%u\n", src_conn->fullname().c_str(), src_width, src_height); + + auto dst_conn = resman.reserve_connector(dst_conn_name); + auto dst_crtc = resman.reserve_crtc(dst_conn); + auto dst_plane = resman.reserve_overlay_plane(dst_crtc, pixfmt); + FAIL_IF(!dst_plane, "Plane not found"); + + uint32_t dst_width = min((uint32_t)dst_crtc->mode().hdisplay, src_width); + uint32_t dst_height = min((uint32_t)dst_crtc->mode().vdisplay, src_height); + + printf("dst %s, crtc %ux%u, plane %ux%u\n", dst_conn->fullname().c_str(), + dst_crtc->mode().hdisplay, dst_crtc->mode().vdisplay, + dst_width, dst_height); + + for (int i = 0; i < CAMERA_BUF_QUEUE_SIZE; ++i) { + auto fb = new DumbFramebuffer(card, src_width, src_height, pixfmt); + s_fbs.push_back(fb); + s_free_fbs.push_back(fb); + } + + // get one fb for initial setup + s_ready_fbs.push_back(s_free_fbs.back()); + s_free_fbs.pop_back(); + + // This draws a moving bar to SRC display + BarFlipState barflipper(card, src_crtc); + barflipper.start_flipping(); + + // This shows the captures SRC frames on DST display + WBFlipState wbflipper(card, dst_crtc, dst_plane); + wbflipper.setup(0, 0, dst_width, dst_height); + + WBStreamer wb(vid.get_capture_streamer(), src_crtc, src_width, src_height, pixfmt); + wb.start_streaming(); + + vector fds(3); + + fds[0].fd = 0; + fds[0].events = POLLIN; + fds[1].fd = wb.fd(); + fds[1].events = POLLIN; + fds[2].fd = card.fd(); + fds[2].events = POLLIN; + + while (true) { + int r = poll(fds.data(), fds.size(), -1); + ASSERT(r > 0); + + if (fds[0].revents != 0) + break; + + if (fds[1].revents) { + fds[1].revents = 0; + + wb.Dequeue(); + wbflipper.queue_next(); + } + + if (fds[2].revents) { + fds[2].revents = 0; + + card.call_page_flip_handlers(); + wb.Queue(); + } + } + + printf("exiting...\n"); +} -- cgit v1.2.3 From 6f5d1817fdb305b9f63a5e8abfbcf46da41d3245 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Sun, 12 Jun 2016 09:50:16 +0300 Subject: utils: add wbm2m --- utils/CMakeLists.txt | 3 + utils/wbm2m.cpp | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 utils/wbm2m.cpp diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index ece8c67..dd95f70 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -21,3 +21,6 @@ target_link_libraries(kmsblank kms++ kms++util ${LIBDRM_LIBRARIES}) add_executable (wbcap wbcap.cpp) target_link_libraries(wbcap kms++ kms++util ${LIBDRM_LIBRARIES}) + +add_executable (wbm2m wbm2m.cpp) +target_link_libraries(wbm2m kms++ kms++util ${LIBDRM_LIBRARIES}) diff --git a/utils/wbm2m.cpp b/utils/wbm2m.cpp new file mode 100644 index 0000000..1f8dffa --- /dev/null +++ b/utils/wbm2m.cpp @@ -0,0 +1,168 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +const uint32_t NUM_SRC_BUFS=2; +const uint32_t NUM_DST_BUFS=2; + +using namespace std; +using namespace kms; + +static const char* usage_str = + "Usage: wbm2m [OPTIONS]\n\n" + "Options:\n" + " -f, --format=4CC Output format" + " -h, --help Print this help\n" + ; + +const int bar_speed = 4; +const int bar_width = 10; + +static unsigned get_bar_pos(DumbFramebuffer* fb, unsigned frame_num) +{ + return (frame_num * bar_speed) % (fb->width() - bar_width + 1); +} + +static void read_frame(DumbFramebuffer* fb, unsigned frame_num) +{ + static map s_bar_pos_map; + + int old_pos = -1; + if (s_bar_pos_map.find(fb) != s_bar_pos_map.end()) + old_pos = s_bar_pos_map[fb]; + + int pos = get_bar_pos(fb, frame_num); + draw_color_bar(*fb, old_pos, pos, bar_width); + draw_text(*fb, fb->width() / 2, 0, to_string(frame_num), RGB(255, 255, 255)); + s_bar_pos_map[fb] = pos; +} + +int main(int argc, char** argv) +{ + // XXX get from args + const uint32_t src_width = 800; + const uint32_t src_height = 480; + const auto src_fmt = PixelFormat::XRGB8888; + const uint32_t num_src_frames = 10; + + const uint32_t dst_width = 800; + const uint32_t dst_height = 480; + auto dst_fmt = PixelFormat::XRGB8888; + + const string filename = "wb-out.raw"; + + OptionSet optionset = { + Option("f|format=", [&](string s) + { + dst_fmt = FourCCToPixelFormat(s); + }), + Option("h|help", [&]() + { + puts(usage_str); + exit(-1); + }), + }; + + optionset.parse(argc, argv); + + if (optionset.params().size() > 0) { + puts(usage_str); + exit(-1); + } + + VideoDevice vid("/dev/video10"); + + Card card; + + uint32_t src_frame_num = 0; + uint32_t dst_frame_num = 0; + + VideoStreamer* out = vid.get_output_streamer(); + VideoStreamer* in = vid.get_capture_streamer(); + + out->set_format(src_fmt, src_width, src_height); + in->set_format(dst_fmt, dst_width, dst_height); + + out->set_queue_size(NUM_SRC_BUFS); + in->set_queue_size(NUM_DST_BUFS); + + + for (unsigned i = 0; i < min(NUM_SRC_BUFS, num_src_frames); ++i) { + auto fb = new DumbFramebuffer(card, src_width, src_height, src_fmt); + + read_frame(fb, src_frame_num++); + + out->queue(fb); + } + + for (unsigned i = 0; i < min(NUM_DST_BUFS, num_src_frames); ++i) { + auto fb = new DumbFramebuffer(card, dst_width, dst_height, dst_fmt); + in->queue(fb); + } + + vector fds(3); + + fds[0].fd = 0; + fds[0].events = POLLIN; + fds[1].fd = vid.fd(); + fds[1].events = POLLIN; + fds[2].fd = card.fd(); + fds[2].events = POLLIN; + + ofstream os(filename, ofstream::binary); + + out->stream_on(); + in->stream_on(); + + while (true) { + int r = poll(fds.data(), fds.size(), -1); + ASSERT(r > 0); + + if (fds[0].revents != 0) + break; + + if (fds[1].revents) { + fds[1].revents = 0; + + + try { + DumbFramebuffer *dst_fb = in->dequeue(); + printf("Writing frame %u\n", dst_frame_num); + for (unsigned i = 0; i < dst_fb->num_planes(); ++i) + os.write((char*)dst_fb->map(i), dst_fb->size(i)); + in->queue(dst_fb); + + dst_frame_num++; + + if (dst_frame_num >= num_src_frames) + break; + + } catch (system_error& se) { + if (se.code() != errc::resource_unavailable_try_again) + FAIL("dequeue failed: %s", se.what()); + + break; + } + + DumbFramebuffer *src_fb = out->dequeue(); + + if (src_frame_num < num_src_frames) { + read_frame(src_fb, src_frame_num++); + out->queue(src_fb); + } + } + + if (fds[2].revents) { + fds[2].revents = 0; + } + } + + printf("exiting...\n"); +} -- cgit v1.2.3