summaryrefslogtreecommitdiff
path: root/src/apps
AgeCommit message (Collapse)Author
2024-06-29apps: common: dng_writer: Rename packing functionsStefan Klug
The old names lead to confusions. Rename to better express the intent. Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2024-06-29apps: common: dng_writer: Support RAW16 formatsDaniel Scally
Add support for RAW16 formats to the DNGWriter helpers so that we can produce dng files from the mali-c55. Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2024-06-26apps: common: dng_writer: Workaround for "Unknown tag 33421" errorStefan Klug
In libtiff version 4.5.1 and later the CFA* tags were missing. This got fixed in https://gitlab.com/libtiff/libtiff/-/commit/49856998c3d82e65444b47bb4fb11b7830a0c2be Unfortunately the fix is not released yet, but the faulty libtiff is contained in current buildroot. As a local fix is pretty easy and without side effects, let's workaround that. Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2024-06-26apps: Define local functions in anonymous namespaceLaurent Pinchart
Multiple local functions are defined in the global namespace without the static keyword. This compiles fine for now, but will cause a missing declaration warning when we enable them. To prepare for that, move the function declaration to an anonymous namespace. While at it, for consistency, include an existing static function in the namespace and drop the static keyword. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2024-05-21treewide: Query list of cameras just onceBarnabás Pőcze
This is more efficient since only a single vector will be constructed, and furthermore, it prevents the TOCTOU issue that might arise when the list of cameras changes between the two queries. Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2024-05-09libcamera: Drop remaining file name from header comment blocksLaurent Pinchart
Source files in libcamera start by a comment block header, which includes the file name and a one-line description of the file contents. While the latter is useful to get a quick overview of the file contents at a glance, the former is mostly a source of inconvenience. The name in the comments can easily get out of sync with the file name when files are renamed, and copy & paste during development have often lead to incorrect names being used to start with. Readers of the source code are expected to know which file they're looking it. Drop the file name from the header comment blocks in all remaining locations that were not caught by the automated script as they are out of sync with the file name. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
2024-05-08libcamera: Drop file name from header comment blocksLaurent Pinchart
Source files in libcamera start by a comment block header, which includes the file name and a one-line description of the file contents. While the latter is useful to get a quick overview of the file contents at a glance, the former is mostly a source of inconvenience. The name in the comments can easily get out of sync with the file name when files are renamed, and copy & paste during development have often lead to incorrect names being used to start with. Readers of the source code are expected to know which file they're looking it. Drop the file name from the header comment block. The change was generated with the following script: ---------------------------------------- dirs="include/libcamera src test utils" declare -rA patterns=( ['c']=' \* ' ['cpp']=' \* ' ['h']=' \* ' ['py']='# ' ['sh']='# ' ) for ext in ${!patterns[@]} ; do files=$(for dir in $dirs ; do find $dir -name "*.${ext}" ; done) pattern=${patterns[${ext}]} for file in $files ; do name=$(basename ${file}) sed -i "s/^\(${pattern}\)${name} - /\1/" "$file" done done ---------------------------------------- This misses several files that are out of sync with the comment block header. Those will be addressed separately and manually. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
2024-05-03apps: qcam: Use standard key sequence for quit actionLaurent Pinchart
Replace the manual CTRL+Q key sequence with QKeySequence::Quit. This automatically maps to the native shortcut for the quit action, regardless of the platform. Even though we don't expect qcam to run on non-Linux platform, using a QKeySequence is still a good practice when one exists. This doesn't change qcam's behaviour, as the native quit key sequence is CTRL+Q on Linux systems. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com> Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
2024-05-03apps: cam: Fix C++20 deprecation warningBarnabás Pőcze
C++20 deprecated implicit capture of `this` via `[=]`. Fix that by explicitly capturing the necessary variables. Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2024-03-28apps: cam: Add support for PPM output formatMilan Zamazal
When file output is requested from cam app, it simply dumps the processed data and it must be converted to a readable image format manually. Let's add support for PPM output file format to make files produced by cam directly readable by image display and processing software. For now, only BGR888 output format, which is the simplest one to use, is supported but nothing prevents adding support for other output formats if needed. Nevertheless, they would typically need byte reordering or other conversions for PPM file format. It may be better to find a way to dump the image data in other output formats directly using some of the already existing file formats or raw file format converters. Signed-off-by: Milan Zamazal <mzamazal@redhat.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2024-03-20apps: lc-compliance: Re-organize source directoryJacopo Mondi
Before adding more tests and more helper classes to lc-compliance, reorganize the source tree to split test and helpers in two separate directories. While at it, rename the 'SimpleCapture' class and its derived classes to just 'Capture'. Rename the source files accordingly. Re-sort headers inclusions to please checkstyle.py too. Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> Tested-by: Stefan Klug <stefan.klug@ideasonboard.com>
2024-03-20apps: lc-compliance: Fix typo in commentJacopo Mondi
Fix a small typo in a comment. Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2024-03-20apps: lc-compliance: Check that requests complete successfullyNícolas F. R. A. Prado
When a request fails to queue it is completed but with its status set to RequestCancelled. Add a check in the requestComplete callback to make sure that the request was completed successfully. For the SimpleCaptureUnbalanced test we need to do this check only if the capture isn't over yet, otherwise the few extra requests that get cancelled at the end, which is the normal behavior, will make the test fail. Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2024-03-11cam: capture_script: Make parseRectangles work for non-arrayPaul Elder
parseRectangles currently always parses Rectangle controls as an array of Rectangles. This causes non-array Rectangle controls to not be parsed correctly, as when the ControlValue is get()ed, the non-array assertion will fail. Set the ControlValue with a single Rectangle in case a single Rectangle has been specified in the yaml capture script to fix that. Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2024-01-24apps: common: dng_writer: Add a default case for switch-case on a moduloPaul Elder
Clearly all cases in the switch are already satisfied, but some compilers fail to realize this and spit out an error: Compiler version: gcc 11.2.0 "aarch64-buildroot-linux-gnu-gcc.br_real (Buildroot 2021.11) 11.2.0" ../../src/apps/common/dng_writer.cpp: In function ‘void thumbScanlineIPU3(const FormatInfo&, void*, const void*, unsigned int, unsigned int)’: ../../src/apps/common/dng_writer.cpp:277:55: error: ‘val4’ may be used uninitialized in this function [-Werror=maybe-uninitialized] 277 | uint8_t value = (val1 + val2 + val3 + val4) >> 10; | ^~~~ ../../src/apps/common/dng_writer.cpp:277:48: error: ‘val3’ may be used uninitialized in this function [-Werror=maybe-uninitialized] 277 | uint8_t value = (val1 + val2 + val3 + val4) >> 10; | ^~~~ ../../src/apps/common/dng_writer.cpp:277:41: error: ‘val2’ may be used uninitialized in this function [-Werror=maybe-uninitialized] 277 | uint8_t value = (val1 + val2 + val3 + val4) >> 10; | ^~~~ ../../src/apps/common/dng_writer.cpp:277:34: error: ‘val1’ may be used uninitialized in this function [-Werror=maybe-uninitialized] 277 | uint8_t value = (val1 + val2 + val3 + val4) >> 10; | ^~~~ Add a default case for the switch-case on a modulo to silence this. Bug: https://bugs.libcamera.org/show_bug.cgi?id=207 Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2024-01-09apps: lc-compliance: Fix source file ordering in meson.buildNícolas F. R. A. Prado
The capture_test.cpp file was added in the source list of meson in the wrong place. Fix it so the list is alphabetically sorted. Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2024-01-09lc-compliance: Set minimum version for gtest dependencyLaurent Pinchart
lc-compliance depends on support for skipping tests in gtest (commit 00938b2b228f upstream, merged in v1.10.0). Set the corresponding minimum version for the gtest dependency. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2023-12-07meson: Tag all installed filesLaurent Pinchart
Meson uses tags to sort installed files in categories, and makes it possible to install a subset of the files using the '--tags' argument to 'meson install'. This is typically used by distributions to split the runtime, development and documentation files into separate packages. By default, meson tries to guess the correct tag for installed files, but can't always do so properly. Mark the install targets that meson can't guess with the correct install_tag. As the feature has been introduced in meson 0.60, bump the minimum meson version. The latest LTS release of all major distributions that libcamera currently targets ship a recent enough meson version. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2023-10-23apps: cam: Add option to set stream orientationJacopo Mondi
Add a '--orientation|-o' option to the cam test application to set an orientation to the image stream. Supported values are the ones obtained by applying flips to the camera sensor: - rot0: no rotation - rot180: rotate 180 degrees - flip: vertical flip - mirror: horizontal flip Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Reviewed-by: David Plowman <david.plowman@raspberrypi.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2023-10-23apps: qcam: Add support for RGB565Daniel Scally
Qt supports RGB565 natively; add support for the format by mapping the libcamera format to Qt's representation of it. Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2023-07-07meson: Fix space around colon issuesLaurent Pinchart
The meson style, which libcamera follows, recommends a space before colons in function parameters. Fix the style violations through the project. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
2023-07-05apps: Add ipa-verify applicationLaurent Pinchart
When packaging libcamera, distributions may break IPA module signatures if the packaging process strips binaries. This can be fixed by resigning the modules, but the process is error-prone. Add a command line ipa-verify utility that tests the signature on an IPA module to help packagers. The tool takes a single argument, the path to an IPA module shared object, and expects the signature file (.sign) to be in the same directory. In order to access the public key needed for signature verification, add a static function to the IPAManager class. As the class is internal to libcamera, this doesn't affect the public API. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Tested-by: Javier Martinez Canillas <javierm@redhat.com>
2023-07-04libcamera: Remove `StreamRoles` aliasBarnabás Pőcze
Now that `Camera::generateConfiguration()` takes a `libcamera::Span` of `StreamRole`, remove the `StreamRoles` type, which was an alias to `std::vector<libcamera::StreamRole>`. The removal has two reasons: - it is no longer strictly necessary, - its presence may suggest that that is the preferred (or correct) way to build/pass a list of `StreamRole`. Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> [Kieran: Fix small checkstyle report on roles initialiser] Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2023-05-04apps: qcam: Remove redundant checkBarnabás Pőcze
The switch statement checks `roles.size()` with cases for 1 and 2, so in the `default` branch, `role.size() > 2`, i.e. it is always different from 1, so the check is unnecessary. Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2023-05-01cam: file_sink: Workaround gcc-13 dangling-reference false positiveEric Curtin
A new warning has been introduced to gcc-13 that produces a false positive on the cam file sink object: src/cam/file_sink.cpp:92:45: error: possibly dangling reference to a temporary [-Werror=dangling-reference] 92 | const FrameMetadata::Plane &meta = buffer->metadata().planes()[i]; | ^~~~ src/cam/file_sink.cpp:92:81: note: the temporary was destroyed at the end of the full expression '(& buffer->libcamera::FrameBuffer::metadata())->libcamera::FrameMetadata::planes().libcamera::Span<const libcamera::FrameMetadata::Plane>::operator[](i)' 92 | const FrameMetadata::Plane &meta = buffer->metadata().planes()[i]; | ^ cc1plus: all warnings being treated as errors Workaround this issue by refactoring the code to take a local const copy of the bytesused value, rather than a local const reference to the plane. Bug: https://bugs.libcamera.org/show_bug.cgi?id=185 Bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107532 Co-developed-by: Khem Raj <raj.khem@gmail.com> Signed-off-by: Eric Curtin <ecurtin@redhat.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> [Kieran: Commit and comment reworded prior to merge] Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2023-04-26apps: cam: kms_sink: Drop unique_ptr<> from DRM::AtomicRequestUmang Jain
There is no need to wrap DRM::AtomicRequest in std::unique_ptr<> in KMSSink::start(). Remove it so that the syntax becomes similar to what we have in KMSSink::stop(). No functional changes intended. Signed-off-by: Umang Jain <umang.jain@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2023-02-15apps: Return std::optional<> from StreamKeyValueParser::parseRole()Barnabás Pőcze
Instead of having bool return type and an out parameter, use std::optional<libcamera::StreamRole> to return from StreamKeyValueParser::parseRole(). Meanwhile at it, re-word an existing comment to make it lucid. Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com> Signed-off-by: Umang Jain <umang.jain@ideasonboard.com>
2022-12-30qcam: Show string representation of pixel formatChristian Rauch
The raw pixel format in form of the fourcc integer is not easily readable. Use the string representation instead for easier debugging. Signed-off-by: Christian Rauch <Rauch.Christian@gmx.de> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2022-12-09lc-compliance: simple_capture: Free Requests properlyPaul Elder
In the simple capture tests, in the capture functions, the Requests were auto-deallocated by the function going out of scope after the test completed. However, before the end of the scope, the Framebuffers that the Requests referred to were manually freed. Thus when the Requests were deallocated, they tried to cancel their Framebuffers, which involve setting the status to cancelled, which obviously causes a segfault because the Framebuffers had already been freed. Fix this by moving the list of Requests to a member variable and deallocating them before deallocating the Framebuffers at stop() time. Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Tested-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Nícolas F. R. A. Prado <nfraprado@collabora.com> Tested-by: Nícolas F. R. A. Prado <nfraprado@collabora.com> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2022-11-25cam: capture_script: Support parsing array controlsJacopo Mondi
Add support for parsing array controls to the cam capture script. Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2022-11-25libcamera: stream: Turn StreamRole into scoped enumerationLaurent Pinchart
The StreamRole enum has enumerators such as 'Raw' that are too generic to be in the global libcamera namespace. Turn it into a scoped enum to avoid namespace clashes, and update users accordingly. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
2022-11-19apps: cam: kms: Avoid 'unused-parameter' warningsChristian Rauch
The parameter 'request' is only used in an assert. assert is only defined for debug builds and release builds will not use the parameter, resulting in warnings messages only for non-debug builds. Fix this by flagging the parameter as 'maybe_unused'. Signed-off-by: Christian Rauch <Rauch.Christian@gmx.de> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2022-10-24apps: Share common source between applicationsLaurent Pinchart
Multiple source files in the src/apps/cam/ directory are used by cam, qcam and lc-compliance. They are compiled separately for each application. Move them to a new src/apps/common/ directory and compile them in a static library to decrease the number of compilation operations. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2022-10-24apps: cam: Fix compilation error with clang when libtiff-4 is not foundLaurent Pinchart
When libtiff-4 is not found, the private camera_ member of the FileSink class is set but never used. This causes a compilation error with clang: In file included from ../../src/apps/cam/file_sink.cpp:19: ../../src/apps/cam/file_sink.h:39:27: error: private field 'camera_' is not used [-Werror,-Wunused-private-field] const libcamera::Camera *camera_; Fix by making the camera_ member field conditional on HAVE_TIFF. Fixes: 6404b163bcbb ("cam: file_sink: Add support for DNG output") Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2022-10-20apps: Move libtiff dependency to src/apps/meson.buildLaurent Pinchart
libtiff is a shared dependency between cam and qcam, move it to src/apps/. The shared dependency will be used to condition compilation of source files in an upcoming application static library. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2022-10-20apps: Move libevent dependency to src/apps/meson.buildLaurent Pinchart
libevent is a shared dependency between cam and lc-compliance, move it to src/apps/. The shared dependency will be used to condition compilation of source files in an upcoming application static library. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2022-10-20Move test applications to src/apps/Laurent Pinchart
The cam and qcam test application share code, currently through a crude hack that references the cam source files directly from the qcam meson.build file. To prepare for the introduction of hosting that code in a static library, move all applications to src/apps/. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>