summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Fricke <sebastian.fricke@posteo.net>2021-01-26 19:48:54 +0100
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2021-02-04 21:00:38 +0200
commite09c487b97f8cd78944cc27ea28723622aed080b (patch)
tree32cf969c97dc374c50d62b6df62b3e3b7ba87b8b
parentd121f6c83f3da5ea6a58556a64d50354ea8efd35 (diff)
test: Add unit tests for the BayerFormat class
Test all of the present methods including the newly implemented `fromV4L2PixelFormat`, as well as the new operators `==/!=`. Reviewed-by: Jacopo Mondi <jacopo@jmondi.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: David Plowman <david.plowman@raspberrypi.com> Signed-off-by: Sebastian Fricke <sebastian.fricke@posteo.net> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-rw-r--r--test/bayer-format.cpp210
-rw-r--r--test/meson.build1
2 files changed, 211 insertions, 0 deletions
diff --git a/test/bayer-format.cpp b/test/bayer-format.cpp
new file mode 100644
index 00000000..047e7db3
--- /dev/null
+++ b/test/bayer-format.cpp
@@ -0,0 +1,210 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020, Sebastian Fricke
+ *
+ * bayer_format.cpp - BayerFormat class tests
+ */
+
+#include <iostream>
+
+#include <libcamera/internal/bayer_format.h>
+#include <libcamera/transform.h>
+
+#include "test.h"
+
+using namespace std;
+using namespace libcamera;
+
+class BayerFormatTest : public Test
+{
+protected:
+ int run()
+ {
+ /* An empty Bayer format has to be invalid. */
+ BayerFormat bayerFmt;
+ if (bayerFmt.isValid()) {
+ cerr << "An empty BayerFormat has to be invalid."
+ << endl;
+ return TestFail;
+ }
+
+ /* A correct Bayer format has to be valid. */
+ bayerFmt = BayerFormat(BayerFormat::BGGR, 8, BayerFormat::None);
+ if (!bayerFmt.isValid()) {
+ cerr << "A correct BayerFormat has to be valid."
+ << endl;
+ return TestFail;
+ }
+
+ /*
+ * Two bayer formats created with the same order and bit depth
+ * have to be equal.
+ */
+ bayerFmt = BayerFormat(BayerFormat::BGGR, 8, BayerFormat::None);
+ BayerFormat bayerFmtExpect = BayerFormat(BayerFormat::BGGR, 8,
+ BayerFormat::None);
+ if (bayerFmt != bayerFmtExpect) {
+ cerr << "Comparison of identical formats failed."
+ << endl;
+ return TestFail;
+ }
+
+ /*
+ * Two Bayer formats created with the same order but with a
+ * different bitDepth are not equal.
+ */
+ bayerFmt = BayerFormat(BayerFormat::BGGR, 8, BayerFormat::None);
+ bayerFmtExpect = BayerFormat(BayerFormat::BGGR, 12,
+ BayerFormat::None);
+ if (bayerFmt == bayerFmtExpect) {
+ cerr << "Comparison of different formats failed."
+ << endl;
+ return TestFail;
+ }
+
+ /*
+ * Create a Bayer format with a V4L2PixelFormat and check if we
+ * get the same format after converting back to the V4L2 Format.
+ */
+ V4L2PixelFormat v4l2FmtExpect = V4L2PixelFormat(
+ V4L2_PIX_FMT_SBGGR8);
+ bayerFmt = BayerFormat::fromV4L2PixelFormat(v4l2FmtExpect);
+ V4L2PixelFormat v4l2Fmt = bayerFmt.toV4L2PixelFormat();
+ if (v4l2Fmt != v4l2FmtExpect) {
+ cerr << "Expected: '" << v4l2FmtExpect.toString()
+ << "' got: '" << v4l2Fmt.toString() << "'" << endl;
+ return TestFail;
+ }
+
+ /*
+ * Use an empty Bayer format and verify that no matching
+ * V4L2PixelFormat is found.
+ */
+ v4l2FmtExpect = V4L2PixelFormat();
+ bayerFmt = BayerFormat();
+ v4l2Fmt = bayerFmt.toV4L2PixelFormat();
+ if (v4l2Fmt != v4l2FmtExpect) {
+ cerr << "Expected: empty V4L2PixelFormat got: '"
+ << v4l2Fmt.toString() << "'" << endl;
+ return TestFail;
+ }
+
+ /*
+ * Check if we get the expected Bayer format BGGR8
+ * when we convert the V4L2PixelFormat (V4L2_PIX_FMT_SBGGR8)
+ * to a Bayer format.
+ */
+ bayerFmtExpect = BayerFormat(BayerFormat::BGGR, 8,
+ BayerFormat::None);
+ v4l2Fmt = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8);
+ bayerFmt = BayerFormat::fromV4L2PixelFormat(v4l2Fmt);
+ if (bayerFmt != bayerFmtExpect) {
+ cerr << "Expected BayerFormat '"
+ << bayerFmtExpect.toString() << "', got: '"
+ << bayerFmt.toString() << "'" << endl;
+ return TestFail;
+ }
+
+ /*
+ * Confirm that a V4L2PixelFormat that is not found in
+ * the conversion table, doesn't yield a Bayer format.
+ */
+ V4L2PixelFormat v4l2FmtUnknown = V4L2PixelFormat(
+ V4L2_PIX_FMT_BGRA444);
+ bayerFmt = BayerFormat::fromV4L2PixelFormat(v4l2FmtUnknown);
+ if (bayerFmt.isValid()) {
+ cerr << "Expected empty BayerFormat got: '"
+ << bayerFmt.toString() << "'" << endl;
+ return TestFail;
+ }
+
+ /*
+ * Test if a valid Bayer format can be converted to a
+ * string representation.
+ */
+ bayerFmt = BayerFormat(BayerFormat::BGGR, 8, BayerFormat::None);
+ if (bayerFmt.toString() != "BGGR-8") {
+ cerr << "String representation != 'BGGR-8' (got: '"
+ << bayerFmt.toString() << "' ) " << endl;
+ return TestFail;
+ }
+
+ /*
+ * Determine if an empty Bayer format results in no
+ * string representation.
+ */
+ bayerFmt = BayerFormat();
+ if (bayerFmt.toString() != "INVALID") {
+ cerr << "String representation != 'INVALID' (got: '"
+ << bayerFmt.toString() << "' ) " << endl;
+ return TestFail;
+ }
+
+ /*
+ * Perform a horizontal Flip and make sure that the
+ * order is adjusted accordingly.
+ */
+ bayerFmt = BayerFormat(BayerFormat::BGGR, 8, BayerFormat::None);
+ bayerFmtExpect = BayerFormat(BayerFormat::GBRG, 8,
+ BayerFormat::None);
+ BayerFormat hFlipFmt = bayerFmt.transform(Transform::HFlip);
+ if (hFlipFmt != bayerFmtExpect) {
+ cerr << "Horizontal flip of 'BGGR-8' should result in '"
+ << bayerFmtExpect.toString() << "', got: '"
+ << hFlipFmt.toString() << "'" << endl;
+ return TestFail;
+ }
+
+ /*
+ * Perform a vertical Flip and make sure that
+ * the order is adjusted accordingly.
+ */
+ bayerFmt = BayerFormat(BayerFormat::BGGR, 8, BayerFormat::None);
+ bayerFmtExpect = BayerFormat(BayerFormat::GRBG, 8,
+ BayerFormat::None);
+ BayerFormat vFlipFmt = bayerFmt.transform(Transform::VFlip);
+ if (vFlipFmt != bayerFmtExpect) {
+ cerr << "Vertical flip of 'BGGR-8' should result in '"
+ << bayerFmtExpect.toString() << "', got: '"
+ << vFlipFmt.toString() << "'" << endl;
+ return TestFail;
+ }
+
+ /*
+ * Perform a transposition on a pixel order with both green
+ * pixels on the bottom left to top right diagonal and make
+ * sure, that it doesn't change.
+ */
+ bayerFmt = BayerFormat(BayerFormat::BGGR, 8, BayerFormat::None);
+ BayerFormat transposeFmt = bayerFmt.transform(
+ Transform::Transpose);
+ if (transposeFmt != bayerFmt) {
+ cerr << "Transpose with both green pixels on the "
+ << "antidiagonal should not change the order "
+ << "(got '" << transposeFmt.toString() << "')"
+ << endl;
+ return TestFail;
+ }
+
+ /*
+ * Perform a transposition on an pixel order with red and blue
+ * on the bottom left to top right diagonal and make sure
+ * that their positions are switched.
+ */
+ bayerFmt = BayerFormat(BayerFormat::GBRG, 8, BayerFormat::None);
+ bayerFmtExpect = BayerFormat(BayerFormat::GRBG, 8,
+ BayerFormat::None);
+ transposeFmt = bayerFmt.transform(Transform::Transpose);
+ if (transposeFmt != bayerFmtExpect) {
+ cerr << "Transpose with the red & blue pixels on the "
+ << "antidiagonal should switch their position "
+ << "(got '" << transposeFmt.toString() << "')"
+ << endl;
+ return TestFail;
+ }
+
+ return TestPass;
+ }
+};
+
+TEST_REGISTER(BayerFormatTest)
diff --git a/test/meson.build b/test/meson.build
index 065c5586..89e6ebff 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -27,6 +27,7 @@ public_tests = [
]
internal_tests = [
+ ['bayer-format', 'bayer-format.cpp'],
['byte-stream-buffer', 'byte-stream-buffer.cpp'],
['camera-sensor', 'camera-sensor.cpp'],
['delayed_contols', 'delayed_contols.cpp'],
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * Copyright (C) 2018, Google Inc.
 *
 * pipeline_handler.cpp - Pipeline handler infrastructure
 */

#include "pipeline_handler.h"

#include <sys/sysmacros.h>

#include <libcamera/buffer.h>
#include <libcamera/camera.h>
#include <libcamera/camera_manager.h>

#include "device_enumerator.h"
#include "log.h"
#include "media_device.h"
#include "utils.h"

/**
 * \file pipeline_handler.h
 * \brief Create pipelines and cameras from a set of media devices
 *
 * Each pipeline supported by libcamera needs to be backed by a pipeline
 * handler implementation that operate on a set of media devices. The pipeline
 * handler is responsible for matching the media devices it requires with the
 * devices present in the system, and once all those devices can be acquired,
 * create corresponding Camera instances.
 *
 * Every subclass of PipelineHandler shall be registered with libcamera using
 * the REGISTER_PIPELINE_HANDLER() macro.
 */

namespace libcamera {

LOG_DEFINE_CATEGORY(Pipeline)

/**
 * \class CameraData
 * \brief Base class for platform-specific data associated with a camera
 *
 * The CameraData base abstract class represents platform specific-data
 * a pipeline handler might want to associate with a Camera to access them
 * at a later time.
 *
 * Pipeline handlers are expected to extend this base class with platform
 * specific implementation, associate instances of the derived classes
 * using the setCameraData() method, and access them at a later time
 * with cameraData().
 */

/**
 * \fn CameraData::CameraData(PipelineHandler *pipe)
 * \brief Construct a CameraData instance for the given pipeline handler
 * \param[in] pipe The pipeline handler
 *
 * The reference to the pipeline handler is stored internally, the caller shall
 * guarantee that the pointer remains valid as long as the CameraData instance
 * exists.
 */

/**
 * \var CameraData::camera_
 * \brief The camera related to this CameraData instance
 *
 * The camera_ pointer provides access to the Camera object that this instance
 * is related to. It is set when the Camera is registered with
 * PipelineHandler::registerCamera() and remains valid until the CameraData
 * instance is destroyed.
 */

/**
 * \var CameraData::pipe_
 * \brief The pipeline handler related to this CameraData instance
 *
 * The pipe_ pointer provides access to the PipelineHandler object that this
 * instance is related to. It is set when the CameraData instance is created
 * and remains valid until the instance is destroyed.
 */

/**
 * \var CameraData::queuedRequests_
 * \brief The list of queued and not yet completed request
 *
 * The list of queued request is used to track requests queued in order to
 * ensure completion of all requests when the pipeline handler is stopped.
 *
 * \sa PipelineHandler::queueRequest(), PipelineHandler::stop(),
 * PipelineHandler::completeRequest()
 */

/**
 * \var CameraData::controlInfo_
 * \brief The set of controls supported by the camera
 *
 * The control information shall be initialised by the pipeline handler when
 * creating the camera, and shall not be modified afterwards.
 */

/**
 * \var CameraData::properties_
 * \brief The list of properties supported by the camera
 *
 * The list of camera properties shall be initialised by the pipeline handler
 * when creating the camera, and shall not be modified afterwards.
 */

/**
 * \var CameraData::ipa_
 * \brief The IPA module used by the camera
 *
 * Reference to the Image Processing Algorithms (IPA) operating on the camera's
 * stream(s). If no IPA exists for the camera, this field is set to nullptr.
 */

/**
 * \class PipelineHandler
 * \brief Create and manage cameras based on a set of media devices
 *
 * The PipelineHandler matches the media devices provided by a DeviceEnumerator
 * with the pipelines it supports and creates corresponding Camera devices.
 *
 * Pipeline handler instances are reference-counted through std::shared_ptr<>.
 * They implement std::enable_shared_from_this<> in order to create new
 * std::shared_ptr<> in code paths originating from member functions of the
 * PipelineHandler class where only the 'this' pointer is available.
 */

/**
 * \brief Construct a PipelineHandler instance
 * \param[in] manager The camera manager
 *
 * In order to honour the std::enable_shared_from_this<> contract,
 * PipelineHandler instances shall never be constructed manually, but always
 * through the PipelineHandlerFactory::create() method implemented by the
 * respective factories.
 */
PipelineHandler::PipelineHandler(CameraManager *manager)
	: manager_(manager)
{
}

PipelineHandler::~PipelineHandler()
{
	for (std::shared_ptr<MediaDevice> media : mediaDevices_)
		media->release();
}

/**
 * \fn PipelineHandler::match(DeviceEnumerator *enumerator)
 * \brief Match media devices and create camera instances
 * \param[in] enumerator The enumerator providing all media devices found in the
 * system
 *
 * This function is the main entry point of the pipeline handler. It is called
 * by the camera manager with the \a enumerator passed as an argument. It shall
 * acquire from the \a enumerator all the media devices it needs for a single
 * pipeline, create one or multiple Camera instances and register them with the
 * camera manager.
 *
 * If all media devices needed by the pipeline handler are found, they must all
 * be acquired by a call to MediaDevice::acquire(). This function shall then
 * create the corresponding Camera instances, store them internally, and return
 * true. Otherwise it shall not acquire any media device (or shall release all
 * the media devices is has acquired by calling MediaDevice::release()) and
 * return false.
 *
 * If multiple instances of a pipeline are available in the system, the
 * PipelineHandler class will be instantiated once per instance, and its match()
 * function called for every instance. Each call shall acquire media devices for
 * one pipeline instance, until all compatible media devices are exhausted.
 *
 * If this function returns true, a new instance of the pipeline handler will
 * be created and its match() function called.
 *
 * \context This function is called from the CameraManager thread.
 *
 * \return true if media devices have been acquired and camera instances
 * created, or false otherwise
 */

/**
 * \brief Search and acquire a MediDevice matching a device pattern
 * \param[in] enumerator Enumerator containing all media devices in the system
 * \param[in] dm Device match pattern
 *
 * Search the device \a enumerator for an available media device matching the
 * device match pattern \a dm. Matching media device that have previously been
 * acquired by MediaDevice::acquire() are not considered. If a match is found,
 * the media device is acquired and returned. The caller shall not release the
 * device explicitly, it will be automatically released when the pipeline
 * handler is destroyed.
 *
 * \context This function shall be called from the CameraManager thread.
 *
 * \return A pointer to the matching MediaDevice, or nullptr if no match is found
 */
MediaDevice *PipelineHandler::acquireMediaDevice(DeviceEnumerator *enumerator,
						 const DeviceMatch &dm)
{
	std::shared_ptr<MediaDevice> media = enumerator->search(dm);
	if (!media)
		return nullptr;

	if (!media->acquire())
		return nullptr;

	mediaDevices_.push_back(media);

	return media.get();
}

/**
 * \brief Lock all media devices acquired by the pipeline
 *
 * This method shall not be called from pipeline handler implementation, as the
 * Camera class handles locking directly.
 *
 * \context This function is \threadsafe.
 *
 * \return True if the devices could be locked, false otherwise
 * \sa unlock()
 * \sa MediaDevice::lock()
 */
bool PipelineHandler::lock()
{
	for (std::shared_ptr<MediaDevice> &media : mediaDevices_) {
		if (!media->lock()) {
			unlock();
			return false;
		}
	}

	return true;
}

/**
 * \brief Unlock all media devices acquired by the pipeline
 *
 * This method shall not be called from pipeline handler implementation, as the
 * Camera class handles locking directly.
 *
 * \context This function is \threadsafe.
 *
 * \sa lock()
 */
void PipelineHandler::unlock()
{
	for (std::shared_ptr<MediaDevice> &media : mediaDevices_)
		media->unlock();
}

/**
 * \brief Retrieve the list of controls for a camera
 * \param[in] camera The camera
 * \context This function is \threadsafe.
 * \return A ControlInfoMap listing the controls support by \a camera
 */
const ControlInfoMap &PipelineHandler::controls(Camera *camera)
{
	CameraData *data = cameraData(camera);
	return data->controlInfo_;
}

/**
 * \brief Retrieve the list of properties for a camera
 * \param[in] camera The camera
 * \return A ControlList of properties supported by \a camera
 */
const ControlList &PipelineHandler::properties(Camera *camera)
{
	CameraData *data = cameraData(camera);
	return data->properties_;
}

/**
 * \fn PipelineHandler::generateConfiguration()
 * \brief Generate a camera configuration for a specified camera
 * \param[in] camera The camera to generate a default configuration for
 * \param[in] roles A list of stream roles
 *
 * Generate a default configuration for the \a camera for a specified list of
 * stream roles. The caller shall populate the \a roles with the use-cases it
 * wishes to fetch the default configuration for. The returned configuration
 * can then be examined by the caller to learn about the selected streams and
 * their default parameters.
 *
 * The intended companion to this is \a configure() which can be used to change
 * the group of streams parameters.
 *
 * \context This function may be called from any thread and shall be
 * \threadsafe. It shall not modify the state of the \a camera in the pipeline
 * handler.
 *
 * \return A valid CameraConfiguration if the requested roles can be satisfied,
 * or a null pointer otherwise. The ownership of the returned configuration is
 * passed to the caller.
 */

/**
 * \fn PipelineHandler::configure()
 * \brief Configure a group of streams for capture
 * \param[in] camera The camera to configure
 * \param[in] config The camera configurations to setup
 *
 * Configure the specified group of streams for \a camera according to the
 * configuration specified in \a config. The intended caller of this interface
 * is the Camera class which will receive configuration to apply from the
 * application.
 *
 * The configuration is guaranteed to have been validated with
 * CameraConfiguration::valid(). The pipeline handler implementation shall not
 * perform further validation and may rely on any custom field stored in its
 * custom CameraConfiguration derived class.
 *
 * When configuring the camera the pipeline handler shall associate a Stream
 * instance to each StreamConfiguration entry in the CameraConfiguration using
 * the StreamConfiguration::setStream() method.
 *
 * \context This function is called from the CameraManager thread.
 *
 * \return 0 on success or a negative error code otherwise
 */

/**
 * \fn PipelineHandler::exportFrameBuffers()
 * \brief Allocate buffers for \a stream
 * \param[in] camera The camera
 * \param[in] stream The stream to allocate buffers for
 * \param[out] buffers Array of buffers successfully allocated
 *
 * This method allocates buffers for the \a stream from the devices associated
 * with the stream in the corresponding pipeline handler. Those buffers shall be
 * suitable to be added to a Request for the stream, and shall be mappable to
 * the CPU through their associated dmabufs with mmap().
 *
 * The method may only be called after the Camera has been configured and before
 * it gets started, or after it gets stopped. It shall be called only for
 * streams that are part of the active camera configuration, and at most once
 * per stream until buffers for the stream are freed with freeFrameBuffers().
 *
 * exportFrameBuffers() shall also allocate all other resources required by
 * the pipeline handler for the stream to prepare for starting the Camera. This
 * responsibility is shared with importFrameBuffers(), and one and only one of
 * those two methods shall be called for each stream until the buffers are
 * freed. The pipeline handler shall support all combinations of
 * exportFrameBuffers() and importFrameBuffers() for the streams contained in
 * any camera configuration.
 *
 * The only intended caller is Camera::exportFrameBuffers().
 *
 * \context This function is called from the CameraManager thread.
 *
 * \return The number of allocated buffers on success or a negative error code
 * otherwise
 */

/**
 * \fn PipelineHandler::importFrameBuffers()
 * \brief Prepare \a stream to use external buffers
 * \param[in] camera The camera
 * \param[in] stream The stream to prepare for import
 *
 * This method prepares the pipeline handler to use buffers provided by the
 * application for the \a stream.
 *
 * The method may only be called after the Camera has been configured and before
 * it gets started, or after it gets stopped. It shall be called only for
 * streams that are part of the active camera configuration, and at most once
 * per stream until buffers for the stream are freed with freeFrameBuffers().
 *
 * importFrameBuffers() shall also allocate all other resources required by the
 * pipeline handler for the stream to prepare for starting the Camera. This
 * responsibility is shared with exportFrameBuffers(), and one and only one of
 * those two methods shall be called for each stream until the buffers are
 * freed. The pipeline handler shall support all combinations of
 * exportFrameBuffers() and importFrameBuffers() for the streams contained in
 * any camera configuration.
 *
 * The only intended caller is Camera::start().
 *
 * \context This function is called from the CameraManager thread.
 *
 * \return 0 on success or a negative error code otherwise
 */

/**
 * \fn PipelineHandler::freeFrameBuffers()
 * \brief Free buffers allocated from the stream
 * \param[in] camera The camera
 * \param[in] stream The stream to free buffers for
 *
 * This method shall free all buffers and all other resources allocated for the
 * \a stream by exportFrameBuffers() or importFrameBuffers(). It shall be
 * called only after a successful call to either of these two methods, and only
 * once per stream.
 *
 * The only intended callers are Camera::stop() and Camera::freeFrameBuffers().
 *
 * \context This function is called from the CameraManager thread.
 */

/**
 * \fn PipelineHandler::start()
 * \brief Start capturing from a group of streams
 * \param[in] camera The camera to start
 *
 * Start the group of streams that have been configured for capture by
 * \a configure(). The intended caller of this method is the Camera class which
 * will in turn be called from the application to indicate that it has
 * configured the streams and is ready to capture.
 *
 * \context This function is called from the CameraManager thread.
 *
 * \return 0 on success or a negative error code otherwise
 */

/**
 * \fn PipelineHandler::stop()
 * \brief Stop capturing from all running streams
 * \param[in] camera The camera to stop
 *
 * This method stops capturing and processing requests immediately. All pending
 * requests are cancelled and complete immediately in an error state.
 *
 * \context This function is called from the CameraManager thread.
 */

/**
 * \fn PipelineHandler::queueRequest()
 * \brief Queue a request to the camera
 * \param[in] camera The camera to queue the request to
 * \param[in] request The request to queue
 *
 * This method queues a capture request to the pipeline handler for processing.
 * The request is first added to the internal list of queued requests, and
 * then passed to the pipeline handler with a call to queueRequestDevice().
 *
 * Keeping track of queued requests ensures automatic completion of all requests
 * when the pipeline handler is stopped with stop(). Request completion shall be
 * signalled by the pipeline handler using the completeRequest() method.
 *
 * \context This function is called from the CameraManager thread.
 *
 * \return 0 on success or a negative error code otherwise
 */
int PipelineHandler::queueRequest(Camera *camera, Request *request)
{
	CameraData *data = cameraData(camera);
	data->queuedRequests_.push_back(request);

	int ret = queueRequestDevice(camera, request);
	if (ret)
		data->queuedRequests_.remove(request);

	return ret;
}

/**
 * \fn PipelineHandler::queueRequestDevice()
 * \brief Queue a request to the device
 * \param[in] camera The camera to queue the request to
 * \param[in] request The request to queue
 *
 * This method queues a capture request to the device for processing. The
 * request contains a set of buffers associated with streams and a set of
 * parameters. The pipeline handler shall program the device to ensure that the
 * parameters will be applied to the frames captured in the buffers provided in
 * the request.
 *
 * \context This function is called from the CameraManager thread.
 *
 * \return 0 on success or a negative error code otherwise
 */

/**
 * \brief Complete a buffer for a request
 * \param[in] camera The camera the request belongs to
 * \param[in] request The request the buffer belongs to
 * \param[in] buffer The buffer that has completed
 *
 * This method shall be called by pipeline handlers to signal completion of the
 * \a buffer part of the \a request. It notifies applications of buffer
 * completion and updates the request's internal buffer tracking. The request
 * is not completed automatically when the last buffer completes to give
 * pipeline handlers a chance to perform any operation that may still be
 * needed. They shall complete requests explicitly with completeRequest().
 *
 * \context This function shall be called from the CameraManager thread.
 *
 * \return True if all buffers contained in the request have completed, false
 * otherwise
 */
bool PipelineHandler::completeBuffer(Camera *camera, Request *request,
				     FrameBuffer *buffer)
{
	camera->bufferCompleted.emit(request, buffer);
	return request->completeBuffer(buffer);
}

/**
 * \brief Signal request completion
 * \param[in] camera The camera that the request belongs to
 * \param[in] request The request that has completed
 *
 * The pipeline handler shall call this method to notify the \a camera that the
 * request has completed. The request is deleted and shall not be accessed once
 * this method returns.
 *
 * This method ensures that requests will be returned to the application in
 * submission order, the pipeline handler may call it on any complete request
 * without any ordering constraint.
 *
 * \context This function shall be called from the CameraManager thread.
 */
void PipelineHandler::completeRequest(Camera *camera, Request *request)
{
	request->complete();

	CameraData *data = cameraData(camera);

	while (!data->queuedRequests_.empty()) {
		Request *req = data->queuedRequests_.front();
		if (req->status() == Request::RequestPending)
			break;

		ASSERT(!req->hasPendingBuffers());
		data->queuedRequests_.pop_front();
		camera->requestComplete(req);
	}
}