summaryrefslogtreecommitdiff
path: root/src/libcamera/pipeline/rkisp1/timeline.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcamera/pipeline/rkisp1/timeline.cpp')
-rw-r--r--src/libcamera/pipeline/rkisp1/timeline.cpp227
1 files changed, 227 insertions, 0 deletions
diff --git a/src/libcamera/pipeline/rkisp1/timeline.cpp b/src/libcamera/pipeline/rkisp1/timeline.cpp
new file mode 100644
index 00000000..b98a1668
--- /dev/null
+++ b/src/libcamera/pipeline/rkisp1/timeline.cpp
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * timeline.cpp - Timeline for per-frame control
+ */
+
+#include "timeline.h"
+
+#include "log.h"
+
+/**
+ * \file timeline.h
+ * \brief Timeline for per-frame control
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(Timeline)
+
+/**
+ * \class FrameAction
+ * \brief Action that can be schedule on a Timeline
+ *
+ * A frame action is an event schedule to be executed on a Timeline. A frame
+ * action has two primal attributes a frame number and a type.
+ *
+ * The frame number describes the frame to which the action is associated. The
+ * type is a numerical ID which identifies the action within the pipeline and
+ * IPA protocol.
+ */
+
+/**
+ * \class Timeline
+ * \brief Executor of FrameAction
+ *
+ * The timeline has three primary functions:
+ *
+ * 1. Keep track of the Start of Exposure (SOE) for every frame processed by
+ * the hardware. Using this information it shall keep an up-to-date estimate
+ * of the frame interval (time between two consecutive SOE events).
+ *
+ * The estimated frame interval together with recorded SOE events are the
+ * foundation for how the timeline schedule FrameAction at specific points
+ * in time.
+ * \todo Improve the frame interval estimation algorithm.
+ *
+ * 2. Keep track of current delays for different types of actions. The delays
+ * for different actions might differ during a capture session. Exposure time
+ * effects the over all FPS and different ISP parameters might impacts its
+ * processing time.
+ *
+ * The action type delays shall be updated by the IPA in conjunction with
+ * how it changes the capture parameters.
+ *
+ * 3. Schedule actions on the timeline. This is the process of taking a
+ * FrameAction which contains an abstract description of what frame and
+ * what type of action it contains and turning that into an time point
+ * and make sure the action is executed at that time.
+ */
+
+Timeline::Timeline()
+ : frameInterval_(0)
+{
+ timer_.timeout.connect(this, &Timeline::timeout);
+}
+
+/**
+ * \brief Reset and stop the timeline
+ *
+ * The timeline needs to be reset when the timeline should no longer execute
+ * actions. A timeline should be reset between two capture sessions to prevent
+ * the old capture session to effect the second one.
+ */
+void Timeline::reset()
+{
+ timer_.stop();
+
+ actions_.clear();
+ history_.clear();
+}
+
+/**
+ * \brief Schedule an action on the timeline
+ * \param[in] action FrameAction to schedule
+ *
+ * The act of scheduling an action to the timeline is the process of taking
+ * the properties of the action (type, frame and time offsets) and translating
+ * that to a time point using the current values for the action type timings
+ * value recorded in the timeline. If an action is scheduled too late, execute
+ * it immediately.
+ */
+void Timeline::scheduleAction(std::unique_ptr<FrameAction> action)
+{
+ unsigned int lastFrame;
+ utils::time_point lastTime;
+
+ if (history_.empty()) {
+ lastFrame = 0;
+ lastTime = std::chrono::steady_clock::now();
+ } else {
+ lastFrame = history_.back().first;
+ lastTime = history_.back().second;
+ }
+
+ /*
+ * Calculate when the action shall be schedule by first finding out how
+ * many frames in the future the action acts on and then add the actions
+ * frame offset. After the spatial frame offset is found out translate
+ * that to a time point by using the last estimated start of exposure
+ * (SOE) as the fixed offset. Lastly add the action time offset to the
+ * time point.
+ */
+ int frame = action->frame() - lastFrame + frameOffset(action->type());
+ utils::time_point deadline = lastTime + frame * frameInterval_
+ + timeOffset(action->type());
+
+ utils::time_point now = std::chrono::steady_clock::now();
+ if (deadline < now) {
+ LOG(Timeline, Warning)
+ << "Action scheduled too late "
+ << utils::time_point_to_string(deadline)
+ << ", run now " << utils::time_point_to_string(now);
+ action->run();
+ } else {
+ actions_.insert({ deadline, std::move(action) });
+ updateDeadline();
+ }
+}
+
+void Timeline::notifyStartOfExposure(unsigned int frame, utils::time_point time)
+{
+ history_.push_back(std::make_pair(frame, time));
+
+ if (history_.size() <= HISTORY_DEPTH / 2)
+ return;
+
+ while (history_.size() > HISTORY_DEPTH)
+ history_.pop_front();
+
+ /* Update esitmated time between two start of exposures. */
+ utils::duration sumExposures(0);
+ unsigned int numExposures = 0;
+
+ utils::time_point lastTime;
+ for (auto it = history_.begin(); it != history_.end(); it++) {
+ if (it != history_.begin()) {
+ sumExposures += it->second - lastTime;
+ numExposures++;
+ }
+
+ lastTime = it->second;
+ }
+
+ frameInterval_ = sumExposures;
+ if (numExposures)
+ frameInterval_ /= numExposures;
+}
+
+int Timeline::frameOffset(unsigned int type) const
+{
+ const auto it = delays_.find(type);
+ if (it == delays_.end()) {
+ LOG(Timeline, Error)
+ << "No frame offset set for action type " << type;
+ return 0;
+ }
+
+ return it->second.first;
+}
+
+utils::duration Timeline::timeOffset(unsigned int type) const
+{
+ const auto it = delays_.find(type);
+ if (it == delays_.end()) {
+ LOG(Timeline, Error)
+ << "No time offset set for action type " << type;
+ return utils::duration::zero();
+ }
+
+ return it->second.second;
+}
+
+void Timeline::setRawDelay(unsigned int type, int frame, utils::duration time)
+{
+ delays_[type] = std::make_pair(frame, time);
+}
+
+void Timeline::updateDeadline()
+{
+ if (actions_.empty())
+ return;
+
+ const utils::time_point &deadline = actions_.begin()->first;
+
+ if (timer_.isRunning() && deadline >= timer_.deadline())
+ return;
+
+ if (deadline <= std::chrono::steady_clock::now()) {
+ timeout(&timer_);
+ return;
+ }
+
+ timer_.start(deadline);
+}
+
+void Timeline::timeout(Timer *timer)
+{
+ utils::time_point now = std::chrono::steady_clock::now();
+
+ for (auto it = actions_.begin(); it != actions_.end();) {
+ const utils::time_point &sched = it->first;
+
+ if (sched > now)
+ break;
+
+ FrameAction *action = it->second.get();
+
+ action->run();
+
+ it = actions_.erase(it);
+ }
+
+ updateDeadline();
+}
+
+} /* namespace libcamera */