blob: ccb75af60bc26bf04f82a246cc7d17be444ffdac [file] [log] [blame]
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -07001/*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"){}
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Shunkai Yao59b27bc2022-07-22 18:42:27 +000016#include <inttypes.h>
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070017
Shunkai Yao59b27bc2022-07-22 18:42:27 +000018#include <android-base/stringprintf.h>
19#include <audio_utils/SimpleLog.h>
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070020#include "media/HeadTrackingProcessor.h"
21
22#include "ModeSelector.h"
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -080023#include "PoseBias.h"
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070024#include "QuaternionUtil.h"
25#include "ScreenHeadFusion.h"
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -080026#include "StillnessDetector.h"
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070027
28namespace android {
29namespace media {
30namespace {
31
Shunkai Yao59b27bc2022-07-22 18:42:27 +000032using android::base::StringAppendF;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070033using Eigen::Quaternionf;
34using Eigen::Vector3f;
35
36class HeadTrackingProcessorImpl : public HeadTrackingProcessor {
37 public:
38 HeadTrackingProcessorImpl(const Options& options, HeadTrackingMode initialMode)
39 : mOptions(options),
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -080040 mHeadStillnessDetector(StillnessDetector::Options{
Ytai Ben-Tsvi4cb1e482022-01-06 11:22:05 -080041 .defaultValue = false,
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -080042 .windowDuration = options.autoRecenterWindowDuration,
43 .translationalThreshold = options.autoRecenterTranslationalThreshold,
44 .rotationalThreshold = options.autoRecenterRotationalThreshold,
45 }),
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -080046 mScreenStillnessDetector(StillnessDetector::Options{
Ytai Ben-Tsvi4cb1e482022-01-06 11:22:05 -080047 .defaultValue = true,
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -080048 .windowDuration = options.screenStillnessWindowDuration,
49 .translationalThreshold = options.screenStillnessTranslationalThreshold,
50 .rotationalThreshold = options.screenStillnessRotationalThreshold,
51 }),
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070052 mModeSelector(ModeSelector::Options{.freshnessTimeout = options.freshnessTimeout},
53 initialMode),
54 mRateLimiter(PoseRateLimiter::Options{
55 .maxTranslationalVelocity = options.maxTranslationalVelocity,
56 .maxRotationalVelocity = options.maxRotationalVelocity}) {}
57
58 void setDesiredMode(HeadTrackingMode mode) override { mModeSelector.setDesiredMode(mode); }
59
60 void setWorldToHeadPose(int64_t timestamp, const Pose3f& worldToHead,
61 const Twist3f& headTwist) override {
62 Pose3f predictedWorldToHead =
63 worldToHead * integrate(headTwist, mOptions.predictionDuration);
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -080064 mHeadPoseBias.setInput(predictedWorldToHead);
Ytai Ben-Tsvi5e6c0f32022-01-19 08:20:39 -080065 mHeadStillnessDetector.setInput(timestamp, predictedWorldToHead);
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070066 mWorldToHeadTimestamp = timestamp;
67 }
68
69 void setWorldToScreenPose(int64_t timestamp, const Pose3f& worldToScreen) override {
Ytai Ben-Tsvi7901bdd2021-09-08 16:11:07 -070070 if (mPhysicalToLogicalAngle != mPendingPhysicalToLogicalAngle) {
71 // We're introducing an artificial discontinuity. Enable the rate limiter.
72 mRateLimiter.enable();
73 mPhysicalToLogicalAngle = mPendingPhysicalToLogicalAngle;
74 }
75
Ytai Ben-Tsvi5e6c0f32022-01-19 08:20:39 -080076 Pose3f worldToLogicalScreen = worldToScreen * Pose3f(rotateY(-mPhysicalToLogicalAngle));
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -080077 mScreenPoseBias.setInput(worldToLogicalScreen);
Ytai Ben-Tsvi5e6c0f32022-01-19 08:20:39 -080078 mScreenStillnessDetector.setInput(timestamp, worldToLogicalScreen);
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070079 mWorldToScreenTimestamp = timestamp;
80 }
81
82 void setScreenToStagePose(const Pose3f& screenToStage) override {
83 mModeSelector.setScreenToStagePose(screenToStage);
84 }
85
86 void setDisplayOrientation(float physicalToLogicalAngle) override {
Ytai Ben-Tsvi7901bdd2021-09-08 16:11:07 -070087 mPendingPhysicalToLogicalAngle = physicalToLogicalAngle;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070088 }
89
90 void calculate(int64_t timestamp) override {
Robert Daltonec0c3282022-08-26 10:33:34 +000091 bool screenStable = true;
92
93 // Handle the screen first, since it might: trigger a recentering of the head.
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -080094 if (mWorldToScreenTimestamp.has_value()) {
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -080095 const Pose3f worldToLogicalScreen = mScreenPoseBias.getOutput();
Robert Daltonec0c3282022-08-26 10:33:34 +000096 screenStable = mScreenStillnessDetector.calculate(timestamp);
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -080097 mModeSelector.setScreenStable(mWorldToScreenTimestamp.value(), screenStable);
98 // Whenever the screen is unstable, recenter the head pose.
99 if (!screenStable) {
100 recenter(true, false);
101 }
102 mScreenHeadFusion.setWorldToScreenPose(mWorldToScreenTimestamp.value(),
103 worldToLogicalScreen);
104 }
105
106 // Handle head.
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700107 if (mWorldToHeadTimestamp.has_value()) {
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -0800108 Pose3f worldToHead = mHeadPoseBias.getOutput();
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800109 // Auto-recenter.
Robert Daltonec0c3282022-08-26 10:33:34 +0000110 bool headStable = mHeadStillnessDetector.calculate(timestamp);
111 if (headStable || !screenStable) {
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800112 recenter(true, false);
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -0800113 worldToHead = mHeadPoseBias.getOutput();
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800114 }
115
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700116 mScreenHeadFusion.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
117 mModeSelector.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
118 }
119
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700120 auto maybeScreenToHead = mScreenHeadFusion.calculate();
121 if (maybeScreenToHead.has_value()) {
122 mModeSelector.setScreenToHeadPose(maybeScreenToHead->timestamp,
123 maybeScreenToHead->pose);
124 } else {
125 mModeSelector.setScreenToHeadPose(timestamp, std::nullopt);
126 }
127
128 HeadTrackingMode prevMode = mModeSelector.getActualMode();
129 mModeSelector.calculate(timestamp);
130 if (mModeSelector.getActualMode() != prevMode) {
131 // Mode has changed, enable rate limiting.
132 mRateLimiter.enable();
133 }
134 mRateLimiter.setTarget(mModeSelector.getHeadToStagePose());
135 mHeadToStagePose = mRateLimiter.calculatePose(timestamp);
136 }
137
138 Pose3f getHeadToStagePose() const override { return mHeadToStagePose; }
139
140 HeadTrackingMode getActualMode() const override { return mModeSelector.getActualMode(); }
141
Ytai Ben-Tsvi95b00c82021-08-27 15:27:08 -0700142 void recenter(bool recenterHead, bool recenterScreen) override {
143 if (recenterHead) {
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -0800144 mHeadPoseBias.recenter();
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800145 mHeadStillnessDetector.reset();
Shunkai Yao59b27bc2022-07-22 18:42:27 +0000146 mLocalLog.log("recenter Head");
Ytai Ben-Tsvi95b00c82021-08-27 15:27:08 -0700147 }
148 if (recenterScreen) {
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -0800149 mScreenPoseBias.recenter();
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -0800150 mScreenStillnessDetector.reset();
Shunkai Yao59b27bc2022-07-22 18:42:27 +0000151 mLocalLog.log("recenter Screen");
Ytai Ben-Tsvi95b00c82021-08-27 15:27:08 -0700152 }
153
154 // If a sensor being recentered is included in the current mode, apply rate limiting to
155 // avoid discontinuities.
156 HeadTrackingMode mode = mModeSelector.getActualMode();
157 if ((recenterHead && (mode == HeadTrackingMode::WORLD_RELATIVE ||
158 mode == HeadTrackingMode::SCREEN_RELATIVE)) ||
159 (recenterScreen && mode == HeadTrackingMode::SCREEN_RELATIVE)) {
160 mRateLimiter.enable();
161 }
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700162 }
163
Shunkai Yao59b27bc2022-07-22 18:42:27 +0000164 std::string toString_l(unsigned level) const override {
165 std::string prefixSpace(level, ' ');
166 std::string ss = prefixSpace + "HeadTrackingProcessor:\n";
167 StringAppendF(&ss, "%smaxTranslationalVelocity: %f\n", prefixSpace.c_str(),
168 mOptions.maxTranslationalVelocity);
169 StringAppendF(&ss, "%smaxRotationalVelocity: %f\n", prefixSpace.c_str(),
170 mOptions.maxRotationalVelocity);
171 StringAppendF(&ss, "%sfreshnessTimeout: %" PRId64 "\n", prefixSpace.c_str(),
172 mOptions.freshnessTimeout);
173 StringAppendF(&ss, "%spredictionDuration: %f\n", prefixSpace.c_str(),
174 mOptions.predictionDuration);
175 StringAppendF(&ss, "%sautoRecenterWindowDuration: %" PRId64 "\n", prefixSpace.c_str(),
176 mOptions.autoRecenterWindowDuration);
177 StringAppendF(&ss, "%sautoRecenterTranslationalThreshold: %f\n", prefixSpace.c_str(),
178 mOptions.autoRecenterTranslationalThreshold);
179 StringAppendF(&ss, "%sautoRecenterRotationalThreshold: %f\n", prefixSpace.c_str(),
180 mOptions.autoRecenterRotationalThreshold);
181 StringAppendF(&ss, "%sscreenStillnessWindowDuration: %" PRId64 "\n", prefixSpace.c_str(),
182 mOptions.screenStillnessWindowDuration);
183 StringAppendF(&ss, "%sscreenStillnessTranslationalThreshold: %f\n", prefixSpace.c_str(),
184 mOptions.screenStillnessTranslationalThreshold);
185 StringAppendF(&ss, "%sscreenStillnessRotationalThreshold: %f\n", prefixSpace.c_str(),
186 mOptions.screenStillnessRotationalThreshold);
187 ss.append(prefixSpace + "ReCenterHistory:\n");
188 ss += mLocalLog.dumpToString((prefixSpace + " ").c_str(), mMaxLocalLogLine);
189 // TODO: 233092747 add string from PoseRateLimiter/PoseRateLimiter etc...
190 return ss;
191 }
192
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700193 private:
194 const Options mOptions;
195 float mPhysicalToLogicalAngle = 0;
Ytai Ben-Tsvi7901bdd2021-09-08 16:11:07 -0700196 // We store the physical to logical angle as "pending" until the next world-to-screen sample it
197 // applies to arrives.
198 float mPendingPhysicalToLogicalAngle = 0;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700199 std::optional<int64_t> mWorldToHeadTimestamp;
200 std::optional<int64_t> mWorldToScreenTimestamp;
201 Pose3f mHeadToStagePose;
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -0800202 PoseBias mHeadPoseBias;
203 PoseBias mScreenPoseBias;
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800204 StillnessDetector mHeadStillnessDetector;
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -0800205 StillnessDetector mScreenStillnessDetector;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700206 ScreenHeadFusion mScreenHeadFusion;
207 ModeSelector mModeSelector;
208 PoseRateLimiter mRateLimiter;
Shunkai Yao59b27bc2022-07-22 18:42:27 +0000209 static constexpr std::size_t mMaxLocalLogLine = 10;
210 SimpleLog mLocalLog{mMaxLocalLogLine};
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700211};
212
213} // namespace
214
Ytai Ben-Tsviedbab3d2021-08-16 11:27:52 -0700215std::unique_ptr<HeadTrackingProcessor> createHeadTrackingProcessor(
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700216 const HeadTrackingProcessor::Options& options, HeadTrackingMode initialMode) {
217 return std::make_unique<HeadTrackingProcessorImpl>(options, initialMode);
218}
219
220} // namespace media
221} // namespace android