summaryrefslogtreecommitdiff
path: root/Documentation
diff options
context:
space:
mode:
authorNaushir Patuck <naush@raspberrypi.com>2021-01-19 15:30:46 +0000
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2021-01-20 14:13:50 +0200
commit92b8ccc42a0f3ad323c79bde3c132d6956011239 (patch)
treec0e5beff20fd126387ba78d26977bdc37b2c61cf /Documentation
parent6232ec3c164102eb59c2deec9facb6552b55cca0 (diff)
libcamera: raspberrypi: Add control of sensor vblanking
Add support for setting V4L2_CID_VBLANK appropriately when setting V4L2_CID_EXPOSURE. This will allow adaptive framerates during viewfinder use cases (e.g. when the exposure time goes above 33ms, we can reduce the framerate to lower than 30fps). The minimum and maximum frame durations are provided via libcamera controls, and will prioritise exposure time limits over any AGC request. V4L2_CID_VBLANK is controlled through the staggered writer, just like the exposure and gain controls. Signed-off-by: Naushir Patuck <naush@raspberrypi.com> Reviewed-by: David Plowman <david.plowman@raspberrypi.com> Tested-by: David Plowman <david.plowman@raspberrypi.com> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Diffstat (limited to 'Documentation')
0 files changed, 0 insertions, 0 deletions
' href='#n194'>194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * Threads test
 */

#include <chrono>
#include <iostream>
#include <memory>
#include <pthread.h>
#include <sched.h>
#include <thread>
#include <time.h>

#include <libcamera/base/object.h>
#include <libcamera/base/thread.h>

#include "test.h"

using namespace std;
using namespace libcamera;

class DelayThread : public Thread
{
public:
	DelayThread(chrono::steady_clock::duration duration)
		: duration_(duration)
	{
	}

protected:
	void run()
	{
		this_thread::sleep_for(duration_);
	}

private:
	chrono::steady_clock::duration duration_;
};

class CancelThread : public Thread
{
public:
	CancelThread(bool &cancelled)
		: cancelled_(cancelled)
	{
	}

protected:
	void run()
	{
		cancelled_ = true;

		/*
		 * Cancel the thread and call a guaranteed cancellation point
		 * (nanosleep).
		 */
		pthread_cancel(pthread_self());

		struct timespec req{ 0, 100*000*000 };
		nanosleep(&req, nullptr);

		cancelled_ = false;
	}

private:
	bool &cancelled_;
};

class CpuSetTester : public Object
{
public:
	CpuSetTester(unsigned int cpuset)
		: cpuset_(cpuset) {}

	bool testCpuSet()
	{
		int ret = sched_getcpu();
		if (static_cast<int>(cpuset_) != ret) {
			cout << "Invalid cpuset: " << ret << ", expecting: " << cpuset_ << endl;
			return false;
		}

		return true;
	}

private:
	const unsigned int cpuset_;
};

class ThreadTest : public Test
{
protected:
	int init()
	{
		return 0;
	}

	int run()
	{
		/* Test Thread() retrieval for the main thread. */
		Thread *mainThread = Thread::current();
		if (!mainThread) {
			cout << "Thread::current() failed to main thread"
			     << endl;
			return TestFail;
		}

		if (!mainThread->isRunning()) {
			cout << "Main thread is not running" << endl;
			return TestFail;
		}

		/* Test starting the main thread, the test shall not crash. */
		mainThread->start();

		/* Test the running state of a custom thread. */
		std::unique_ptr<Thread> thread = std::make_unique<Thread>();
		thread->start();

		if (!thread->isRunning()) {
			cout << "Thread is not running after being started"
			     << endl;
			return TestFail;
		}

		thread->exit(0);
		thread->wait();

		if (thread->isRunning()) {
			cout << "Thread is still running after finishing"
			     << endl;
			return TestFail;
		}

		/* Test waiting for completion with a timeout. */
		thread = std::make_unique<DelayThread>(chrono::milliseconds(500));
		thread->start();
		thread->exit(0);

		bool timeout = !thread->wait(chrono::milliseconds(100));

		if (!timeout) {
			cout << "Waiting for thread didn't time out" << endl;
			return TestFail;
		}

		timeout = !thread->wait(chrono::milliseconds(1000));

		if (timeout) {
			cout << "Waiting for thread timed out" << endl;
			return TestFail;
		}

		/* Test waiting on a thread that isn't running. */
		thread = std::make_unique<Thread>();

		timeout = !thread->wait();
		if (timeout) {
			cout << "Waiting for non-started thread timed out" << endl;
			return TestFail;
		}

		thread->start();
		thread->exit(0);
		thread->wait();

		timeout = !thread->wait();
		if (timeout) {
			cout << "Waiting for already stopped thread timed out" << endl;
			return TestFail;
		}

		/* Test thread cleanup upon abnormal termination. */
		bool cancelled = false;
		bool finished = false;

		thread = std::make_unique<CancelThread>(cancelled);
		thread->finished.connect(this, [&finished]() { finished = true; });

		thread->start();
		thread->exit(0);
		thread->wait(chrono::milliseconds(1000));

		if (!cancelled || !finished) {
			cout << "Cleanup failed upon abnormal termination" << endl;
			return TestFail;
		}

		const unsigned int numCpus = std::thread::hardware_concurrency();
		for (unsigned int i = 0; i < numCpus; ++i) {
			thread = std::make_unique<Thread>();
			const std::array<const unsigned int, 1> cpus{ i };
			thread->setThreadAffinity(cpus);
			thread->start();

			CpuSetTester tester(i);
			tester.moveToThread(thread.get());

			if (!tester.invokeMethod(&CpuSetTester::testCpuSet, ConnectionTypeBlocking))
				return TestFail;

			thread->exit(0);
			thread->wait();
		}

		return TestPass;
	}

	void cleanup()
	{
	}
};

TEST_REGISTER(ThreadTest)