Patch Detail
Show a patch.
GET /api/1.1/patches/15662/?format=api
{ "id": 15662, "url": "https://patchwork.libcamera.org/api/1.1/patches/15662/?format=api", "web_url": "https://patchwork.libcamera.org/patch/15662/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20220408160015.33612-2-ecurtin@redhat.com>", "date": "2022-04-08T16:00:15", "name": "[libcamera-devel,v7,2/2] cam: sdl_sink: Add SDL sink", "commit_ref": null, "pull_url": null, "state": "accepted", "archived": false, "hash": "a06c2e10c14eeac2033504a85461dbf697358473", "submitter": { "id": 101, "url": "https://patchwork.libcamera.org/api/1.1/people/101/?format=api", "name": "Eric Curtin", "email": "ecurtin@redhat.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/15662/mbox/", "series": [ { "id": 3047, "url": "https://patchwork.libcamera.org/api/1.1/series/3047/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3047", "date": "2022-04-08T16:00:14", "name": "[libcamera-devel,v7,1/2] cam: event_loop: Add timer events to event loop", "version": 7, "mbox": "https://patchwork.libcamera.org/series/3047/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/15662/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/15662/checks/", "tags": {}, "headers": { "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>", "X-Original-To": "parsemail@patchwork.libcamera.org", "Delivered-To": "parsemail@patchwork.libcamera.org", "Received": [ "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 93391C3256\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 8 Apr 2022 16:00:28 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5A7F765645;\n\tFri, 8 Apr 2022 18:00:28 +0200 (CEST)", "from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.129.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9E60E604BA\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 8 Apr 2022 18:00:26 +0200 (CEST)", "from mail-wm1-f70.google.com (mail-wm1-f70.google.com\n\t[209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id\n\tus-mta-491-CLuv_uplO3m883r__Kiv-g-1; Fri, 08 Apr 2022 12:00:22 -0400", "by mail-wm1-f70.google.com with SMTP id\n\tr64-20020a1c2b43000000b0038b59eb1940so4438464wmr.0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 08 Apr 2022 09:00:22 -0700 (PDT)", "from p1.Home ([2001:8a0:6724:4500:a69c:e66f:828e:b340])\n\tby smtp.gmail.com with ESMTPSA id\n\ta12-20020a5d53cc000000b00205a0ee9c74sm18913521wrw.89.2022.04.08.09.00.19\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tFri, 08 Apr 2022 09:00:20 -0700 (PDT)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1649433628;\n\tbh=Ng/S6yciohAgIdIwU/in2KfehV/qlxjNl71w3TK6kFk=;\n\th=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=20VYLWhcXruLMZKuHK+7isGVAND8fqfhPUV686XUVqcBhuKHpcWLC+gHfXWb/hmXV\n\t5rDXLqvqdKdkAlETg57IIANJweN3Y2/1e5OPeBnv5RHgHKbmakAi2nCIBKEdduNLBM\n\to4SU11W6qdQwM5emWdtJWg5SsBUWZi0C8EvofXgnKg/dOn+liQ7i/FM7uH0fFmyD8S\n\tT5/JwKYzjY3lnYjgfcWn7QPdKx6ubTU/TC83Ez3reD6ECBWmgoYNK2R+N4Wux1sHko\n\tFNgAUctusETEjdDrQKdggaapGMxvfwvZZG+yjcTn5nXo44HfCDAGxANmui4i9gr/RL\n\tphEtrvv60P1Dw==", "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1649433625;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tcontent-transfer-encoding:content-transfer-encoding:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=hmPJL3UzOocqYomaoA1bip63kjAEIMDVyW75F3umxxY=;\n\tb=VPBa3GSKvO2gHuGqCmLplirbj9kDt7U/C0oLLkygAMazVgFr0zI9RPk80X0YyijZuz9YFf\n\tbxz0Fkh1Ek+g0RQSZ414yTiUGEHtLxemAK6HW+URTjVraDZ7gHC7QKM9O9VkmtB59Gkvip\n\tp1l53ABF6h8nFGHZcgBWKl/op/6che0=" ], "Authentication-Results": [ "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=redhat.com\n\theader.i=@redhat.com header.b=\"VPBa3GSK\"; \n\tdkim-atps=neutral", "relay.mimecast.com;\n\tauth=pass smtp.auth=CUSA124A263 smtp.mailfrom=ecurtin@redhat.com" ], "X-MC-Unique": "CLuv_uplO3m883r__Kiv-g-1", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references:mime-version:content-transfer-encoding;\n\tbh=hmPJL3UzOocqYomaoA1bip63kjAEIMDVyW75F3umxxY=;\n\tb=keWO1TtXut8AV3IHHZmm5QW/MiZHeu8Bx02q4X53X0VbmV2F4xrQ4lZ7u7+ZUqVvKL\n\twTR7sCiWMeVfvbUB/6Fpt7BEb8gdhtR14FIgdET8sDylBwR6u9r6/0qc95MhfPOg9yKw\n\tgRYqGmiy/ffooyRg8CMtRSz2/yHxNE0V1t2IhnS2JpEE4gpoFLZ+X8EKniQ6iuSP5VI3\n\tq7rHPKe8VDQwAQiIaY3iKNecGAqJ/V7/ZHdts8wn4mS6QEU6Y2WAkutc2NYRn8edX/+c\n\tEwuP1I6eX01NgR3xrSxm9wolxDlg4XjGzIfhN6Ti4xNoKXvYABtFTP7nEbZH5g/AhSYv\n\ttlWg==", "X-Gm-Message-State": "AOAM530Tb5FOYgFQKWouKVA8nvx+mB+F8/k1eAJ4SJaStMNNTH9Rlk1B\n\tqADXQ4ZsGzMREoBIAqVrm/9QQ4Od8gzW2vfYUxsDGeXKRzFAdnEKTs2Vt5PwBwYma1KB/4pBxDb\n\t3Er0X0VEpqTaeDJkEDqpDDehMCyZ/zOBlkeklftom0GWVeN8+Ue4rVwWxIpd0r0VXhX+nVOeZen\n\tW27Bh8abm3", "X-Received": [ "by 2002:a7b:c041:0:b0:38c:7c21:8ade with SMTP id\n\tu1-20020a7bc041000000b0038c7c218ademr17565580wmc.163.1649433620979; \n\tFri, 08 Apr 2022 09:00:20 -0700 (PDT)", "by 2002:a7b:c041:0:b0:38c:7c21:8ade with SMTP id\n\tu1-20020a7bc041000000b0038c7c218ademr17565541wmc.163.1649433620509; \n\tFri, 08 Apr 2022 09:00:20 -0700 (PDT)" ], "X-Google-Smtp-Source": "ABdhPJwv1/xBFAv/dNTK0ILG04pKluQYYBzty1va2U5vgtptCulv258ockDgaf5qjPnuMw0+7HCbJg==", "To": "libcamera-devel@lists.libcamera.org", "Date": "Fri, 8 Apr 2022 17:00:15 +0100", "Message-Id": "<20220408160015.33612-2-ecurtin@redhat.com>", "X-Mailer": "git-send-email 2.35.1", "In-Reply-To": "<20220408160015.33612-1-ecurtin@redhat.com>", "References": "<20220408160015.33612-1-ecurtin@redhat.com>", "MIME-Version": "1.0", "X-Mimecast-Spam-Score": "0", "X-Mimecast-Originator": "redhat.com", "Content-Transfer-Encoding": "8bit", "Content-Type": "text/plain; charset=\"US-ASCII\"; x-default=true", "Subject": "[libcamera-devel] [PATCH v7 2/2] cam: sdl_sink: Add SDL sink", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "<libcamera-devel.lists.libcamera.org>", "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>", "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>", "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>", "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>", "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>", "From": "Eric Curtin via libcamera-devel <libcamera-devel@lists.libcamera.org>", "Reply-To": "Eric Curtin <ecurtin@redhat.com>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "This adds more portability to existing cam sinks. You can pass a\nYUYV camera buffer for example and SDL will handle the pixel buffer\nconversion, although SDL does not support decompression for pixelformats\nlike MJPEG. This allows cam reference implementation to display images\non VMs, Mac M1, Raspberry Pi, etc. This also enables cam reference\nimplementation, to run as a desktop application in wayland or x11.\nSDL also has support for Android and ChromeOS which has not been tested.\nAlso tested on simpledrm raspberry pi 4 framebuffer successfully where\nexisting kms sink did not work. Can also be used as kmsdrm sink. Only\nsupports one camera stream at present.\n\nSigned-off-by: Eric Curtin <ecurtin@redhat.com>\n---\n src/cam/camera_session.cpp | 8 ++\n src/cam/main.cpp | 5 +\n src/cam/main.h | 1 +\n src/cam/meson.build | 10 ++\n src/cam/sdl_sink.cpp | 198 +++++++++++++++++++++++++++++++++++++\n src/cam/sdl_sink.h | 46 +++++++++\n 6 files changed, 268 insertions(+)\n create mode 100644 src/cam/sdl_sink.cpp\n create mode 100644 src/cam/sdl_sink.h", "diff": "diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp\nindex 0428b538..30162dbd 100644\n--- a/src/cam/camera_session.cpp\n+++ b/src/cam/camera_session.cpp\n@@ -19,6 +19,9 @@\n #ifdef HAVE_KMS\n #include \"kms_sink.h\"\n #endif\n+#ifdef HAVE_SDL\n+#include \"sdl_sink.h\"\n+#endif\n #include \"main.h\"\n #include \"stream_options.h\"\n \n@@ -187,6 +190,11 @@ int CameraSession::start()\n \t\tsink_ = std::make_unique<KMSSink>(options_[OptDisplay].toString());\n #endif\n \n+#ifdef HAVE_SDL\n+\tif (options_.isSet(OptSDL))\n+\t\tsink_ = std::make_unique<SDLSink>();\n+#endif\n+\n \tif (options_.isSet(OptFile)) {\n \t\tif (!options_[OptFile].toString().empty())\n \t\t\tsink_ = std::make_unique<FileSink>(streamNames_,\ndiff --git a/src/cam/main.cpp b/src/cam/main.cpp\nindex c7f664b9..1d62a64a 100644\n--- a/src/cam/main.cpp\n+++ b/src/cam/main.cpp\n@@ -137,6 +137,11 @@ int CamApp::parseOptions(int argc, char *argv[])\n \t\t\t \"Display viewfinder through DRM/KMS on specified connector\",\n \t\t\t \"display\", ArgumentOptional, \"connector\", false,\n \t\t\t OptCamera);\n+#endif\n+#ifdef HAVE_SDL\n+\tparser.addOption(OptSDL, OptionNone,\n+\t\t\t \"Display viewfinder through SDL\",\n+\t\t\t \"sdl\", ArgumentNone, \"\", false, OptCamera);\n #endif\n \tparser.addOption(OptFile, OptionString,\n \t\t\t \"Write captured frames to disk\\n\"\ndiff --git a/src/cam/main.h b/src/cam/main.h\nindex 62f7bbc9..2b285808 100644\n--- a/src/cam/main.h\n+++ b/src/cam/main.h\n@@ -18,6 +18,7 @@ enum {\n \tOptListProperties = 'p',\n \tOptMonitor = 'm',\n \tOptStream = 's',\n+\tOptSDL = 'S',\n \tOptListControls = 256,\n \tOptStrictFormats = 257,\n \tOptMetadata = 258,\ndiff --git a/src/cam/meson.build b/src/cam/meson.build\nindex 5bab8c9e..59787741 100644\n--- a/src/cam/meson.build\n+++ b/src/cam/meson.build\n@@ -32,11 +32,21 @@ if libdrm.found()\n ])\n endif\n \n+libsdl2 = dependency('SDL2', required : false)\n+\n+if libsdl2.found()\n+ cam_cpp_args += ['-DHAVE_SDL']\n+ cam_sources += files([\n+ 'sdl_sink.cpp'\n+ ])\n+endif\n+\n cam = executable('cam', cam_sources,\n dependencies : [\n libatomic,\n libcamera_public,\n libdrm,\n+ libsdl2,\n libevent,\n ],\n cpp_args : cam_cpp_args,\ndiff --git a/src/cam/sdl_sink.cpp b/src/cam/sdl_sink.cpp\nnew file mode 100644\nindex 00000000..03511974\n--- /dev/null\n+++ b/src/cam/sdl_sink.cpp\n@@ -0,0 +1,198 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * sdl_sink.cpp - SDL Sink\n+ */\n+\n+#include \"sdl_sink.h\"\n+\n+#include <assert.h>\n+#include <fcntl.h>\n+#include <iomanip>\n+#include <iostream>\n+#include <signal.h>\n+#include <sstream>\n+#include <string.h>\n+#include <unistd.h>\n+\n+#include <libcamera/camera.h>\n+#include <libcamera/formats.h>\n+\n+#include \"event_loop.h\"\n+#include \"image.h\"\n+\n+using namespace libcamera;\n+\n+SDLSink::SDLSink()\n+\t: sdlWindow_(nullptr), sdlRenderer_(nullptr), sdlTexture_(nullptr), sdlRect_({}), sdlInit_(false)\n+{\n+}\n+\n+SDLSink::~SDLSink()\n+{\n+\tstop();\n+}\n+\n+int SDLSink::configure(const libcamera::CameraConfiguration &cfg)\n+{\n+\tint ret = FrameSink::configure(cfg);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tif (cfg.size() > 1) {\n+\t\tstd::cerr << \"SDL sink only supports one camera stream at present, streaming first camera stream\"\n+\t\t\t << std::endl;\n+\t} else if (cfg.empty()) {\n+\t\tstd::cerr << \"Require at least one camera stream to process\" << std::endl;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tconst libcamera::StreamConfiguration &sCfg = cfg.at(0);\n+\tsdlRect_.w = sCfg.size.width;\n+\tsdlRect_.h = sCfg.size.height;\n+\tswitch (sCfg.pixelFormat) {\n+\tcase libcamera::formats::YUYV:\n+\t\tpixelFormat_ = SDL_PIXELFORMAT_YUY2;\n+\t\tpitch_ = 4 * ((sdlRect_.w + 1) / 2);\n+\t\tbreak;\n+\n+\t/* From here down the fourcc values are identical between SDL, drm, libcamera */\n+\tcase libcamera::formats::NV21:\n+\t\tpixelFormat_ = SDL_PIXELFORMAT_NV21;\n+\t\tpitch_ = sdlRect_.w;\n+\t\tbreak;\n+\tcase libcamera::formats::NV12:\n+\t\tpixelFormat_ = SDL_PIXELFORMAT_NV12;\n+\t\tpitch_ = sdlRect_.w;\n+\t\tbreak;\n+\tcase libcamera::formats::YVU420:\n+\t\tpixelFormat_ = SDL_PIXELFORMAT_YV12;\n+\t\tpitch_ = sdlRect_.w;\n+\t\tbreak;\n+\tcase libcamera::formats::YVYU:\n+\t\tpixelFormat_ = SDL_PIXELFORMAT_YVYU;\n+\t\tpitch_ = 4 * ((sdlRect_.w + 1) / 2);\n+\t\tbreak;\n+\tcase libcamera::formats::UYVY:\n+\t\tpixelFormat_ = SDL_PIXELFORMAT_UYVY;\n+\t\tpitch_ = 4 * ((sdlRect_.w + 1) / 2);\n+\t\tbreak;\n+\tdefault:\n+\t\tstd::cerr << sCfg.pixelFormat.toString() << \" not present in libcamera <-> SDL map\"\n+\t\t\t << std::endl;\n+\t\treturn -EINVAL;\n+\t};\n+\n+\treturn 0;\n+}\n+\n+int SDLSink::start()\n+{\n+\tint ret = SDL_Init(SDL_INIT_VIDEO);\n+\tif (ret) {\n+\t\tstd::cerr << \"Failed to initialize SDL: \" << SDL_GetError() << std::endl;\n+\t\treturn ret;\n+\t}\n+\n+\tsdlInit_ = true;\n+\tsdlWindow_ = SDL_CreateWindow(\"\", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, sdlRect_.w, sdlRect_.h, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);\n+\tif (!sdlWindow_) {\n+\t\tstd::cerr << \"Failed to create SDL window: \" << SDL_GetError() << std::endl;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tsdlRenderer_ = SDL_CreateRenderer(sdlWindow_, -1, 0);\n+\tif (!sdlRenderer_) {\n+\t\tstd::cerr << \"Failed to create SDL renderer: \" << SDL_GetError() << std::endl;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tret = SDL_RenderSetLogicalSize(sdlRenderer_, sdlRect_.w, sdlRect_.h);\n+\tif (ret) { /* Not critical to set, don't return in this case, set for scaling purposes */\n+\t\tstd::cerr << \"Failed to set SDL render logical size: \" << SDL_GetError() << std::endl;\n+\t}\n+\n+\tsdlTexture_ = SDL_CreateTexture(sdlRenderer_, pixelFormat_, SDL_TEXTUREACCESS_STREAMING, sdlRect_.w, sdlRect_.h);\n+\tif (!sdlTexture_) {\n+\t\tstd::cerr << \"Failed to create SDL texture: \" << SDL_GetError() << std::endl;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tEventLoop::instance()->addTimerEvent(10000, std::bind(&SDLSink::processSDLEvents, this));\n+\n+\treturn 0;\n+}\n+\n+int SDLSink::stop()\n+{\n+\tif (sdlTexture_) {\n+\t\tSDL_DestroyTexture(sdlTexture_);\n+\t\tsdlTexture_ = nullptr;\n+\t}\n+\n+\tif (sdlRenderer_) {\n+\t\tSDL_DestroyRenderer(sdlRenderer_);\n+\t\tsdlRenderer_ = nullptr;\n+\t}\n+\n+\tif (sdlWindow_) {\n+\t\tSDL_DestroyWindow(sdlWindow_);\n+\t\tsdlWindow_ = nullptr;\n+\t}\n+\n+\tif (sdlInit_) {\n+\t\tSDL_Quit();\n+\t\tsdlInit_ = false;\n+\t}\n+\n+\treturn FrameSink::stop();\n+}\n+\n+void SDLSink::mapBuffer(FrameBuffer *buffer)\n+{\n+\tstd::unique_ptr<Image> image = Image::fromFrameBuffer(buffer, Image::MapMode::ReadOnly);\n+\tassert(image != nullptr);\n+\n+\tmappedBuffers_[buffer] = std::move(image);\n+}\n+\n+bool SDLSink::processRequest(Request *request)\n+{\n+\tfor (auto [stream, buffer] : request->buffers()) {\n+\t\trenderBuffer(buffer);\n+\t\tbreak; /* to be expanded to launch SDL window per buffer */\n+\t}\n+\n+\treturn true;\n+}\n+\n+/*\n+ * Process SDL events, required for things like window resize and quit button\n+ */\n+void SDLSink::processSDLEvents()\n+{\n+\tfor (SDL_Event e; SDL_PollEvent(&e);) {\n+\t\tif (e.type == SDL_QUIT) { /* click close icon then quit */\n+\t\t\tEventLoop::instance()->exit(0);\n+\t\t}\n+\t}\n+}\n+\n+void SDLSink::renderBuffer(FrameBuffer *buffer)\n+{\n+\tImage *image = mappedBuffers_[buffer].get();\n+\n+\tfor (unsigned int i = 0; i < buffer->planes().size(); ++i) {\n+\t\tconst FrameMetadata::Plane &meta = buffer->metadata().planes()[i];\n+\n+\t\tSpan<uint8_t> data = image->data(i);\n+\t\tif (meta.bytesused > data.size())\n+\t\t\tstd::cerr << \"payload size \" << meta.bytesused\n+\t\t\t\t << \" larger than plane size \" << data.size()\n+\t\t\t\t << std::endl;\n+\n+\t\tSDL_UpdateTexture(sdlTexture_, &sdlRect_, data.data(), pitch_);\n+\t\tSDL_RenderClear(sdlRenderer_);\n+\t\tSDL_RenderCopy(sdlRenderer_, sdlTexture_, nullptr, nullptr);\n+\t\tSDL_RenderPresent(sdlRenderer_);\n+\t}\n+}\ndiff --git a/src/cam/sdl_sink.h b/src/cam/sdl_sink.h\nnew file mode 100644\nindex 00000000..c9b0ab8e\n--- /dev/null\n+++ b/src/cam/sdl_sink.h\n@@ -0,0 +1,46 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * sdl_sink.h - SDL Sink\n+ */\n+\n+#pragma once\n+\n+#include <map>\n+#include <memory>\n+#include <string>\n+\n+#include <libcamera/stream.h>\n+\n+#include <SDL2/SDL.h>\n+\n+#include \"frame_sink.h\"\n+\n+class Image;\n+\n+class SDLSink : public FrameSink\n+{\n+public:\n+\tSDLSink();\n+\t~SDLSink();\n+\n+\tint configure(const libcamera::CameraConfiguration &cfg) override;\n+\tint start() override;\n+\tint stop() override;\n+\tvoid mapBuffer(libcamera::FrameBuffer *buffer) override;\n+\n+\tbool processRequest(libcamera::Request *request) override;\n+\n+private:\n+\tvoid renderBuffer(libcamera::FrameBuffer *buffer);\n+\tvoid processSDLEvents();\n+\n+\tstd::map<libcamera::FrameBuffer *, std::unique_ptr<Image>> mappedBuffers_;\n+\n+\tSDL_Window *sdlWindow_;\n+\tSDL_Renderer *sdlRenderer_;\n+\tSDL_Texture *sdlTexture_;\n+\tSDL_Rect sdlRect_;\n+\tSDL_PixelFormatEnum pixelFormat_;\n+\tbool sdlInit_;\n+\tint pitch_;\n+};\n", "prefixes": [ "libcamera-devel", "v7", "2/2" ] }