f3c53dbf53
With gcc 5 and 6, insertion in a std::multimap copies the pair passed as an argument to the insert() method. As the mapped type is a non-copyable std::unique_ptr<>, this fails to compile. Compilation with newer gcc versions succeed due to support for C++-17 and the fix described in https://cplusplus.github.io/LWG/issue2354. To support gcc 5 and 6, fix the issue by using std::multimap::emplace(). Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
228 lines
5.9 KiB
C++
228 lines
5.9 KiB
C++
/* 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_.emplace(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 */
|