diff options
Diffstat (limited to 'src/libcamera/pipeline/rkisp1/timeline.cpp')
-rw-r--r-- | src/libcamera/pipeline/rkisp1/timeline.cpp | 227 |
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 */ |