Patch Detail
Show a patch.
GET /api/patches/1274/?format=api
{ "id": 1274, "url": "https://patchwork.libcamera.org/api/patches/1274/?format=api", "web_url": "https://patchwork.libcamera.org/patch/1274/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/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": "<20190523151306.22007-2-niklas.soderlund@ragnatech.se>", "date": "2019-05-23T15:13:05", "name": "[libcamera-devel,v2,1/2] cam: capture: Break out capture to a new class", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "ed41263c72b5a92dc1a57e9c3bfd773e5fb339d6", "submitter": { "id": 5, "url": "https://patchwork.libcamera.org/api/people/5/?format=api", "name": "Niklas Söderlund", "email": "niklas.soderlund@ragnatech.se" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/1274/mbox/", "series": [ { "id": 323, "url": "https://patchwork.libcamera.org/api/series/323/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=323", "date": "2019-05-23T15:13:04", "name": "cam: cleanup code structure", "version": 2, "mbox": "https://patchwork.libcamera.org/series/323/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/1274/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/1274/checks/", "tags": {}, "headers": { "Return-Path": "<niklas.soderlund@ragnatech.se>", "Received": [ "from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net\n\t[195.74.38.229])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9B2C36187B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 23 May 2019 17:13:50 +0200 (CEST)", "from bismarck.berto.se (unknown [89.233.230.99])\n\tby bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA\n\tid 5c686cad-7d6d-11e9-8d05-005056917f90;\n\tThu, 23 May 2019 17:13:46 +0200 (CEST)" ], "X-Halon-ID": "5c686cad-7d6d-11e9-8d05-005056917f90", "Authorized-sender": "niklas@soderlund.pp.se", "From": "=?utf-8?q?Niklas_S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Thu, 23 May 2019 17:13:05 +0200", "Message-Id": "<20190523151306.22007-2-niklas.soderlund@ragnatech.se>", "X-Mailer": "git-send-email 2.21.0", "In-Reply-To": "<20190523151306.22007-1-niklas.soderlund@ragnatech.se>", "References": "<20190523151306.22007-1-niklas.soderlund@ragnatech.se>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=UTF-8", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v2 1/2] cam: capture: Break out capture to\n\ta new class", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.23", "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>", "X-List-Received-Date": "Thu, 23 May 2019 15:13:50 -0000" }, "content": "Reduce the complexity of main.cpp by compartmentalising the capture\nlogic into its own class. There is no functional change.\n\nSigned-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n---\n src/cam/capture.cpp | 249 ++++++++++++++++++++++++++++++++++++++++++++\n src/cam/capture.h | 43 ++++++++\n src/cam/main.cpp | 236 +----------------------------------------\n src/cam/main.h | 19 ++++\n src/cam/meson.build | 1 +\n 5 files changed, 316 insertions(+), 232 deletions(-)\n create mode 100644 src/cam/capture.cpp\n create mode 100644 src/cam/capture.h\n create mode 100644 src/cam/main.h", "diff": "diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp\nnew file mode 100644\nindex 0000000000000000..a88c00c81daba0b4\n--- /dev/null\n+++ b/src/cam/capture.cpp\n@@ -0,0 +1,249 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * capture.cpp - Cam capture\n+ */\n+\n+#include <climits>\n+#include <iomanip>\n+#include <iostream>\n+#include <sstream>\n+\n+#include \"capture.h\"\n+#include \"main.h\"\n+\n+using namespace libcamera;\n+\n+Capture::Capture()\n+\t: camera_(nullptr), writer_(nullptr), last_(0)\n+{\n+}\n+\n+int Capture::run(Camera *camera, EventLoop *loop,\n+\t\t const OptionsParser::Options &options)\n+{\n+\tint ret;\n+\n+\tif (!camera) {\n+\t\tstd::cout << \"Can't capture without a camera\" << std::endl;\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tcamera_ = camera;\n+\n+\tret = prepareConfig(options);\n+\tif (ret) {\n+\t\tstd::cout << \"Failed to prepare camera configuration\" << std::endl;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (options.isSet(OptFile)) {\n+\t\tif (!options[OptFile].toString().empty())\n+\t\t\twriter_ = new BufferWriter(options[OptFile]);\n+\t\telse\n+\t\t\twriter_ = new BufferWriter();\n+\t}\n+\n+\tret = camera_->configure(config_.get());\n+\tif (ret < 0) {\n+\t\tstd::cout << \"Failed to configure camera\" << std::endl;\n+\t\treturn ret;\n+\t}\n+\n+\tret = camera_->allocateBuffers();\n+\tif (ret) {\n+\t\tstd::cerr << \"Failed to allocate buffers\" << std::endl;\n+\t\treturn ret;\n+\t}\n+\n+\tcamera_->requestCompleted.connect(this, &Capture::requestComplete);\n+\n+\tret = capture(loop);\n+\n+\tcamera_->freeBuffers();\n+\tconfig_.reset();\n+\n+\tif (options.isSet(OptFile)) {\n+\t\tdelete writer_;\n+\t\twriter_ = nullptr;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+int Capture::prepareConfig(const OptionsParser::Options &options)\n+{\n+\tStreamRoles roles;\n+\n+\tif (options.isSet(OptStream)) {\n+\t\tconst std::vector<OptionValue> &streamOptions =\n+\t\t\toptions[OptStream].toArray();\n+\n+\t\t/* Use roles and get a default configuration. */\n+\t\tfor (auto const &value : streamOptions) {\n+\t\t\tKeyValueParser::Options opt = value.toKeyValues();\n+\n+\t\t\tif (!opt.isSet(\"role\")) {\n+\t\t\t\troles.push_back(StreamRole::VideoRecording);\n+\t\t\t} else if (opt[\"role\"].toString() == \"viewfinder\") {\n+\t\t\t\troles.push_back(StreamRole::Viewfinder);\n+\t\t\t} else if (opt[\"role\"].toString() == \"video\") {\n+\t\t\t\troles.push_back(StreamRole::VideoRecording);\n+\t\t\t} else if (opt[\"role\"].toString() == \"still\") {\n+\t\t\t\troles.push_back(StreamRole::StillCapture);\n+\t\t\t} else {\n+\t\t\t\tstd::cerr << \"Unknown stream role \"\n+\t\t\t\t\t << opt[\"role\"].toString() << std::endl;\n+\t\t\t\treturn -EINVAL;\n+\t\t\t}\n+\t\t}\n+\t} else {\n+\t\t/* If no configuration is provided assume a single video stream. */\n+\t\troles.push_back(StreamRole::VideoRecording);\n+\t}\n+\n+\tconfig_ = camera_->generateConfiguration(roles);\n+\tif (!config_ || config_->size() != roles.size()) {\n+\t\tstd::cerr << \"Failed to get default stream configuration\"\n+\t\t\t << std::endl;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* Apply configuration if explicitly requested. */\n+\tif (options.isSet(OptStream)) {\n+\t\tconst std::vector<OptionValue> &streamOptions =\n+\t\t\toptions[OptStream].toArray();\n+\n+\t\tunsigned int i = 0;\n+\t\tfor (auto const &value : streamOptions) {\n+\t\t\tKeyValueParser::Options opt = value.toKeyValues();\n+\t\t\tStreamConfiguration &cfg = config_->at(i++);\n+\n+\t\t\tif (opt.isSet(\"width\"))\n+\t\t\t\tcfg.size.width = opt[\"width\"];\n+\n+\t\t\tif (opt.isSet(\"height\"))\n+\t\t\t\tcfg.size.height = opt[\"height\"];\n+\n+\t\t\t/* TODO: Translate 4CC string to ID. */\n+\t\t\tif (opt.isSet(\"pixelformat\"))\n+\t\t\t\tcfg.pixelFormat = opt[\"pixelformat\"];\n+\t\t}\n+\t}\n+\n+\tstreamName_.clear();\n+\tfor (unsigned int index = 0; index < config_->size(); ++index) {\n+\t\tStreamConfiguration &cfg = config_->at(index);\n+\t\tstreamName_[cfg.stream()] = \"stream\" + std::to_string(index);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int Capture::capture(EventLoop *loop)\n+{\n+\tint ret;\n+\n+\t/* Identify the stream with the least number of buffers. */\n+\tunsigned int nbuffers = UINT_MAX;\n+\tfor (StreamConfiguration &cfg : *config_) {\n+\t\tStream *stream = cfg.stream();\n+\t\tnbuffers = std::min(nbuffers, stream->bufferPool().count());\n+\t}\n+\n+\t/*\n+\t * TODO: make cam tool smarter to support still capture by for\n+\t * example pushing a button. For now run all streams all the time.\n+\t */\n+\n+\tstd::vector<Request *> requests;\n+\tfor (unsigned int i = 0; i < nbuffers; i++) {\n+\t\tRequest *request = camera_->createRequest();\n+\t\tif (!request) {\n+\t\t\tstd::cerr << \"Can't create request\" << std::endl;\n+\t\t\treturn -ENOMEM;\n+\t\t}\n+\n+\t\tstd::map<Stream *, Buffer *> map;\n+\t\tfor (StreamConfiguration &cfg : *config_) {\n+\t\t\tStream *stream = cfg.stream();\n+\t\t\tmap[stream] = &stream->bufferPool().buffers()[i];\n+\t\t}\n+\n+\t\tret = request->setBuffers(map);\n+\t\tif (ret < 0) {\n+\t\t\tstd::cerr << \"Can't set buffers for request\" << std::endl;\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\trequests.push_back(request);\n+\t}\n+\n+\tret = camera_->start();\n+\tif (ret) {\n+\t\tstd::cout << \"Failed to start capture\" << std::endl;\n+\t\treturn ret;\n+\t}\n+\n+\tfor (Request *request : requests) {\n+\t\tret = camera_->queueRequest(request);\n+\t\tif (ret < 0) {\n+\t\t\tstd::cerr << \"Can't queue request\" << std::endl;\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\tstd::cout << \"Capture until user interrupts by SIGINT\" << std::endl;\n+\tret = loop->exec();\n+\n+\tret = camera_->stop();\n+\tif (ret)\n+\t\tstd::cout << \"Failed to stop capture\" << std::endl;\n+\n+\treturn ret;\n+}\n+\n+void Capture::requestComplete(Request *request, const std::map<Stream *, Buffer *> &buffers)\n+{\n+\tdouble fps = 0.0;\n+\tuint64_t now;\n+\n+\tif (request->status() == Request::RequestCancelled)\n+\t\treturn;\n+\n+\tstruct timespec time;\n+\tclock_gettime(CLOCK_MONOTONIC, &time);\n+\tnow = time.tv_sec * 1000 + time.tv_nsec / 1000000;\n+\tfps = now - last_;\n+\tfps = last_ && fps ? 1000.0 / fps : 0.0;\n+\tlast_ = now;\n+\n+\tstd::stringstream info;\n+\tinfo << \"fps: \" << std::fixed << std::setprecision(2) << fps;\n+\n+\tfor (auto it = buffers.begin(); it != buffers.end(); ++it) {\n+\t\tStream *stream = it->first;\n+\t\tBuffer *buffer = it->second;\n+\t\tconst std::string &name = streamName_[stream];\n+\n+\t\tinfo << \" \" << name\n+\t\t << \" (\" << buffer->index() << \")\"\n+\t\t << \" seq: \" << std::setw(6) << std::setfill('0') << buffer->sequence()\n+\t\t << \" bytesused: \" << buffer->bytesused();\n+\n+\t\tif (writer_)\n+\t\t\twriter_->write(buffer, name);\n+\t}\n+\n+\tstd::cout << info.str() << std::endl;\n+\n+\trequest = camera_->createRequest();\n+\tif (!request) {\n+\t\tstd::cerr << \"Can't create request\" << std::endl;\n+\t\treturn;\n+\t}\n+\n+\trequest->setBuffers(buffers);\n+\tcamera_->queueRequest(request);\n+}\ndiff --git a/src/cam/capture.h b/src/cam/capture.h\nnew file mode 100644\nindex 0000000000000000..de70f22e59f00bfd\n--- /dev/null\n+++ b/src/cam/capture.h\n@@ -0,0 +1,43 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * capture.h - Cam capture\n+ */\n+#ifndef __CAM_CAPTURE_H__\n+#define __CAM_CAPTURE_H__\n+\n+#include <memory>\n+\n+#include <libcamera/camera.h>\n+#include <libcamera/request.h>\n+#include <libcamera/stream.h>\n+\n+#include \"buffer_writer.h\"\n+#include \"event_loop.h\"\n+#include \"options.h\"\n+\n+class Capture\n+{\n+public:\n+\tCapture();\n+\n+\tint run(libcamera::Camera *camera, EventLoop *loop,\n+\t\tconst OptionsParser::Options &options);\n+private:\n+\tint prepareConfig(const OptionsParser::Options &options);\n+\n+\tint capture(EventLoop *loop);\n+\n+\tvoid requestComplete(libcamera::Request *request,\n+\t\t\t const std::map<libcamera::Stream *, libcamera::Buffer *> &buffers);\n+\n+\tlibcamera::Camera *camera_;\n+\tstd::unique_ptr<libcamera::CameraConfiguration> config_;\n+\n+\tstd::map<libcamera::Stream *, std::string> streamName_;\n+\tBufferWriter *writer_;\n+\tuint64_t last_;\n+};\n+\n+#endif /* __CAM_CAPTURE_H__ */\ndiff --git a/src/cam/main.cpp b/src/cam/main.cpp\nindex 4155889678ce1897..fe7d4f90dbf14ffd 100644\n--- a/src/cam/main.cpp\n+++ b/src/cam/main.cpp\n@@ -5,37 +5,22 @@\n * main.cpp - cam - The libcamera swiss army knife\n */\n \n-#include <algorithm>\n-#include <iomanip>\n #include <iostream>\n-#include <limits.h>\n-#include <map>\n #include <signal.h>\n-#include <sstream>\n #include <string.h>\n \n #include <libcamera/libcamera.h>\n \n-#include \"buffer_writer.h\"\n+#include \"capture.h\"\n #include \"event_loop.h\"\n+#include \"main.h\"\n #include \"options.h\"\n \n using namespace libcamera;\n \n OptionsParser::Options options;\n std::shared_ptr<Camera> camera;\n-std::map<Stream *, std::string> streamInfo;\n EventLoop *loop;\n-BufferWriter *writer;\n-\n-enum {\n-\tOptCamera = 'c',\n-\tOptCapture = 'C',\n-\tOptFile = 'F',\n-\tOptHelp = 'h',\n-\tOptList = 'l',\n-\tOptStream = 's',\n-};\n \n void signalHandler(int signal)\n {\n@@ -85,201 +70,6 @@ static int parseOptions(int argc, char *argv[])\n \treturn 0;\n }\n \n-static std::unique_ptr<CameraConfiguration> prepareCameraConfig()\n-{\n-\tStreamRoles roles;\n-\n-\t/* If no configuration is provided assume a single video stream. */\n-\tif (!options.isSet(OptStream))\n-\t\treturn camera->generateConfiguration({ StreamRole::VideoRecording });\n-\n-\tconst std::vector<OptionValue> &streamOptions =\n-\t\toptions[OptStream].toArray();\n-\n-\t/* Use roles and get a default configuration. */\n-\tfor (auto const &value : streamOptions) {\n-\t\tKeyValueParser::Options opt = value.toKeyValues();\n-\n-\t\tif (!opt.isSet(\"role\")) {\n-\t\t\troles.push_back(StreamRole::VideoRecording);\n-\t\t} else if (opt[\"role\"].toString() == \"viewfinder\") {\n-\t\t\troles.push_back(StreamRole::Viewfinder);\n-\t\t} else if (opt[\"role\"].toString() == \"video\") {\n-\t\t\troles.push_back(StreamRole::VideoRecording);\n-\t\t} else if (opt[\"role\"].toString() == \"still\") {\n-\t\t\troles.push_back(StreamRole::StillCapture);\n-\t\t} else {\n-\t\t\tstd::cerr << \"Unknown stream role \"\n-\t\t\t\t << opt[\"role\"].toString() << std::endl;\n-\t\t\treturn nullptr;\n-\t\t}\n-\t}\n-\n-\tstd::unique_ptr<CameraConfiguration> config = camera->generateConfiguration(roles);\n-\tif (!config || config->size() != roles.size()) {\n-\t\tstd::cerr << \"Failed to get default stream configuration\"\n-\t\t\t << std::endl;\n-\t\treturn nullptr;\n-\t}\n-\n-\t/* Apply configuration explicitly requested. */\n-\tunsigned int i = 0;\n-\tfor (auto const &value : streamOptions) {\n-\t\tKeyValueParser::Options opt = value.toKeyValues();\n-\t\tStreamConfiguration &cfg = config->at(i++);\n-\n-\t\tif (opt.isSet(\"width\"))\n-\t\t\tcfg.size.width = opt[\"width\"];\n-\n-\t\tif (opt.isSet(\"height\"))\n-\t\t\tcfg.size.height = opt[\"height\"];\n-\n-\t\t/* TODO: Translate 4CC string to ID. */\n-\t\tif (opt.isSet(\"pixelformat\"))\n-\t\t\tcfg.pixelFormat = opt[\"pixelformat\"];\n-\t}\n-\n-\treturn config;\n-}\n-\n-static void requestComplete(Request *request, const std::map<Stream *, Buffer *> &buffers)\n-{\n-\tstatic uint64_t now, last = 0;\n-\tdouble fps = 0.0;\n-\n-\tif (request->status() == Request::RequestCancelled)\n-\t\treturn;\n-\n-\tstruct timespec time;\n-\tclock_gettime(CLOCK_MONOTONIC, &time);\n-\tnow = time.tv_sec * 1000 + time.tv_nsec / 1000000;\n-\tfps = now - last;\n-\tfps = last && fps ? 1000.0 / fps : 0.0;\n-\tlast = now;\n-\n-\tstd::stringstream info;\n-\tinfo << \"fps: \" << std::fixed << std::setprecision(2) << fps;\n-\n-\tfor (auto it = buffers.begin(); it != buffers.end(); ++it) {\n-\t\tStream *stream = it->first;\n-\t\tBuffer *buffer = it->second;\n-\t\tconst std::string &name = streamInfo[stream];\n-\n-\t\tinfo << \" \" << name\n-\t\t << \" (\" << buffer->index() << \")\"\n-\t\t << \" seq: \" << std::setw(6) << std::setfill('0') << buffer->sequence()\n-\t\t << \" bytesused: \" << buffer->bytesused();\n-\n-\t\tif (writer)\n-\t\t\twriter->write(buffer, name);\n-\t}\n-\n-\tstd::cout << info.str() << std::endl;\n-\n-\trequest = camera->createRequest();\n-\tif (!request) {\n-\t\tstd::cerr << \"Can't create request\" << std::endl;\n-\t\treturn;\n-\t}\n-\n-\trequest->setBuffers(buffers);\n-\tcamera->queueRequest(request);\n-}\n-\n-static int capture()\n-{\n-\tint ret;\n-\n-\tstd::unique_ptr<CameraConfiguration> config = prepareCameraConfig();\n-\tif (!config) {\n-\t\tstd::cout << \"Failed to prepare camera configuration\" << std::endl;\n-\t\treturn -EINVAL;\n-\t}\n-\n-\tret = camera->configure(config.get());\n-\tif (ret < 0) {\n-\t\tstd::cout << \"Failed to configure camera\" << std::endl;\n-\t\treturn ret;\n-\t}\n-\n-\tstreamInfo.clear();\n-\n-\tfor (unsigned int index = 0; index < config->size(); ++index) {\n-\t\tStreamConfiguration &cfg = config->at(index);\n-\t\tstreamInfo[cfg.stream()] = \"stream\" + std::to_string(index);\n-\t}\n-\n-\tret = camera->allocateBuffers();\n-\tif (ret) {\n-\t\tstd::cerr << \"Failed to allocate buffers\"\n-\t\t\t << std::endl;\n-\t\treturn ret;\n-\t}\n-\n-\tcamera->requestCompleted.connect(requestComplete);\n-\n-\t/* Identify the stream with the least number of buffers. */\n-\tunsigned int nbuffers = UINT_MAX;\n-\tfor (StreamConfiguration &cfg : *config) {\n-\t\tStream *stream = cfg.stream();\n-\t\tnbuffers = std::min(nbuffers, stream->bufferPool().count());\n-\t}\n-\n-\t/*\n-\t * TODO: make cam tool smarter to support still capture by for\n-\t * example pushing a button. For now run all streams all the time.\n-\t */\n-\n-\tstd::vector<Request *> requests;\n-\tfor (unsigned int i = 0; i < nbuffers; i++) {\n-\t\tRequest *request = camera->createRequest();\n-\t\tif (!request) {\n-\t\t\tstd::cerr << \"Can't create request\" << std::endl;\n-\t\t\tret = -ENOMEM;\n-\t\t\tgoto out;\n-\t\t}\n-\n-\t\tstd::map<Stream *, Buffer *> map;\n-\t\tfor (StreamConfiguration &cfg : *config) {\n-\t\t\tStream *stream = cfg.stream();\n-\t\t\tmap[stream] = &stream->bufferPool().buffers()[i];\n-\t\t}\n-\n-\t\tret = request->setBuffers(map);\n-\t\tif (ret < 0) {\n-\t\t\tstd::cerr << \"Can't set buffers for request\" << std::endl;\n-\t\t\tgoto out;\n-\t\t}\n-\n-\t\trequests.push_back(request);\n-\t}\n-\n-\tret = camera->start();\n-\tif (ret) {\n-\t\tstd::cout << \"Failed to start capture\" << std::endl;\n-\t\tgoto out;\n-\t}\n-\n-\tfor (Request *request : requests) {\n-\t\tret = camera->queueRequest(request);\n-\t\tif (ret < 0) {\n-\t\t\tstd::cerr << \"Can't queue request\" << std::endl;\n-\t\t\tgoto out;\n-\t\t}\n-\t}\n-\n-\tstd::cout << \"Capture until user interrupts by SIGINT\" << std::endl;\n-\tret = loop->exec();\n-\n-\tret = camera->stop();\n-\tif (ret)\n-\t\tstd::cout << \"Failed to stop capture\" << std::endl;\n-out:\n-\tcamera->freeBuffers();\n-\n-\treturn ret;\n-}\n-\n int main(int argc, char **argv)\n {\n \tint ret;\n@@ -327,26 +117,8 @@ int main(int argc, char **argv)\n \t}\n \n \tif (options.isSet(OptCapture)) {\n-\t\tif (!camera) {\n-\t\t\tstd::cout << \"Can't capture without a camera\"\n-\t\t\t\t << std::endl;\n-\t\t\tret = EXIT_FAILURE;\n-\t\t\tgoto out;\n-\t\t}\n-\n-\t\tif (options.isSet(OptFile)) {\n-\t\t\tif (!options[OptFile].toString().empty())\n-\t\t\t\twriter = new BufferWriter(options[OptFile]);\n-\t\t\telse\n-\t\t\t\twriter = new BufferWriter();\n-\t\t}\n-\n-\t\tcapture();\n-\n-\t\tif (options.isSet(OptFile)) {\n-\t\t\tdelete writer;\n-\t\t\twriter = nullptr;\n-\t\t}\n+\t\tCapture capture;\n+\t\tret = capture.run(camera.get(), loop, options);\n \t}\n \n \tif (camera) {\ndiff --git a/src/cam/main.h b/src/cam/main.h\nnew file mode 100644\nindex 0000000000000000..fff81b1f6c860b57\n--- /dev/null\n+++ b/src/cam/main.h\n@@ -0,0 +1,19 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * main.h - Cam application\n+ */\n+#ifndef __CAM_MAIN_H__\n+#define __CAM_MAIN_H__\n+\n+enum {\n+\tOptCamera = 'c',\n+\tOptCapture = 'C',\n+\tOptFile = 'F',\n+\tOptHelp = 'h',\n+\tOptList = 'l',\n+\tOptStream = 's',\n+};\n+\n+#endif /* __CAM_MAIN_H__ */\ndiff --git a/src/cam/meson.build b/src/cam/meson.build\nindex 3faddc6c8d85c765..478346c59590631d 100644\n--- a/src/cam/meson.build\n+++ b/src/cam/meson.build\n@@ -1,5 +1,6 @@\n cam_sources = files([\n 'buffer_writer.cpp',\n+ 'capture.cpp',\n 'event_loop.cpp',\n 'main.cpp',\n 'options.cpp',\n", "prefixes": [ "libcamera-devel", "v2", "1/2" ] }