[{"id":1686,"web_url":"https://patchwork.libcamera.org/comment/1686/","msgid":"<20190523152336.GO4745@pendragon.ideasonboard.com>","date":"2019-05-23T15:23:36","subject":"Re: [libcamera-devel] [PATCH v2 1/2] cam: capture: Break out\n\tcapture to a new class","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Niklas,\n\nThank you for the patch.\n\nOn Thu, May 23, 2019 at 05:13:05PM +0200, Niklas Söderlund wrote:\n> Reduce the complexity of main.cpp by compartmentalising the capture\n> logic into its own class. There is no functional change.\n> \n> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> Reviewed-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\n> \n> diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp\n> new file mode 100644\n> index 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\nWould it make sense to pass the camera to the constructor ?\n\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\nYou're leaking writer_ here and below.\n\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\nNo need to assign the return value to ret if you then ignore it.\n\nWith these small issues fixed,\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\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> +}\n> diff --git a/src/cam/capture.h b/src/cam/capture.h\n> new file mode 100644\n> index 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__ */\n> diff --git a/src/cam/main.cpp b/src/cam/main.cpp\n> index 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) {\n> diff --git a/src/cam/main.h b/src/cam/main.h\n> new file mode 100644\n> index 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__ */\n> diff --git a/src/cam/meson.build b/src/cam/meson.build\n> index 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',","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id AA81D60B1B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 23 May 2019 17:23:54 +0200 (CEST)","from pendragon.ideasonboard.com (81-175-216-236.bb.dnainternet.fi\n\t[81.175.216.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 1963F583;\n\tThu, 23 May 2019 17:23:54 +0200 (CEST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1558625034;\n\tbh=wgRXVgE5rVfZTTnTu4BNe6kru8GqUIe5JsOlInUVk+A=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=BGPOt9v5nW4nGAegoAlUFlZ/hZkmZhZivlM78td5eNnIIwSfbQ0CBm4Gt5UFqV0aT\n\thkDjmPaoWYIV4TLI1StQTYtk02U2jT/Akf5yDJInnlvcqxMTYXo58YNxCglE3Rdn7d\n\tPvEasRx+c/s+6yDKKmshHnXbat2E6Cda+7N8hWmQ=","Date":"Thu, 23 May 2019 18:23:36 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Niklas =?utf-8?q?S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190523152336.GO4745@pendragon.ideasonboard.com>","References":"<20190523151306.22007-1-niklas.soderlund@ragnatech.se>\n\t<20190523151306.22007-2-niklas.soderlund@ragnatech.se>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20190523151306.22007-2-niklas.soderlund@ragnatech.se>","User-Agent":"Mutt/1.10.1 (2018-07-13)","Subject":"Re: [libcamera-devel] [PATCH v2 1/2] cam: capture: Break out\n\tcapture to a 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:23:54 -0000"}}]