blob: 9db4afa323e489301a4081fd3caeecd07690e5a2 [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"
Andy Hung9d197e72023-02-03 19:18:33 -080021#include "media/QuaternionUtil.h"
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070022
23#include "ModeSelector.h"
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -080024#include "PoseBias.h"
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070025#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) {
Eric Laurentf8a85262023-01-23 17:49:04 +0100100 recenter(true, false, "calculate: screen movement");
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -0800101 }
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) {
Eric Laurentf8a85262023-01-23 17:49:04 +0100112 recenter(true, false, "calculate: head movement");
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
Eric Laurentf8a85262023-01-23 17:49:04 +0100142 void recenter(bool recenterHead, bool recenterScreen, std::string source) override {
Ytai Ben-Tsvi95b00c82021-08-27 15:27:08 -0700143 if (recenterHead) {
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -0800144 mHeadPoseBias.recenter();
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800145 mHeadStillnessDetector.reset();
Eric Laurentf8a85262023-01-23 17:49:04 +0100146 mLocalLog.log("recenter Head from %s", source.c_str());
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();
Eric Laurentf8a85262023-01-23 17:49:04 +0100151 mLocalLog.log("recenter Screen from %s", source.c_str());
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";
Shunkai Yao0324a392022-08-30 03:14:50 +0000167 StringAppendF(&ss, "%s maxTranslationalVelocity: %f meter/second\n", prefixSpace.c_str(),
Shunkai Yao59b27bc2022-07-22 18:42:27 +0000168 mOptions.maxTranslationalVelocity);
Shunkai Yao0324a392022-08-30 03:14:50 +0000169 StringAppendF(&ss, "%s maxRotationalVelocity: %f rad/second\n", prefixSpace.c_str(),
Shunkai Yao59b27bc2022-07-22 18:42:27 +0000170 mOptions.maxRotationalVelocity);
Shunkai Yao0324a392022-08-30 03:14:50 +0000171 StringAppendF(&ss, "%s freshnessTimeout: %0.4f ms\n", prefixSpace.c_str(),
172 media::nsToFloatMs(mOptions.freshnessTimeout));
173 StringAppendF(&ss, "%s predictionDuration: %0.4f ms\n", prefixSpace.c_str(),
174 media::nsToFloatMs(mOptions.predictionDuration));
175 StringAppendF(&ss, "%s autoRecenterWindowDuration: %0.4f ms\n", prefixSpace.c_str(),
176 media::nsToFloatMs(mOptions.autoRecenterWindowDuration));
177 StringAppendF(&ss, "%s autoRecenterTranslationalThreshold: %f meter\n", prefixSpace.c_str(),
Shunkai Yao59b27bc2022-07-22 18:42:27 +0000178 mOptions.autoRecenterTranslationalThreshold);
Shunkai Yao0324a392022-08-30 03:14:50 +0000179 StringAppendF(&ss, "%s autoRecenterRotationalThreshold: %f radians\n", prefixSpace.c_str(),
Shunkai Yao59b27bc2022-07-22 18:42:27 +0000180 mOptions.autoRecenterRotationalThreshold);
Shunkai Yao0324a392022-08-30 03:14:50 +0000181 StringAppendF(&ss, "%s screenStillnessWindowDuration: %0.4f ms\n", prefixSpace.c_str(),
182 media::nsToFloatMs(mOptions.screenStillnessWindowDuration));
183 StringAppendF(&ss, "%s screenStillnessTranslationalThreshold: %f meter\n",
184 prefixSpace.c_str(), mOptions.screenStillnessTranslationalThreshold);
185 StringAppendF(&ss, "%s screenStillnessRotationalThreshold: %f radians\n",
186 prefixSpace.c_str(), mOptions.screenStillnessRotationalThreshold);
187 ss += mModeSelector.toString(level + 1);
188 ss += mRateLimiter.toString(level + 1);
Shunkai Yao59b27bc2022-07-22 18:42:27 +0000189 ss.append(prefixSpace + "ReCenterHistory:\n");
190 ss += mLocalLog.dumpToString((prefixSpace + " ").c_str(), mMaxLocalLogLine);
Shunkai Yao59b27bc2022-07-22 18:42:27 +0000191 return ss;
192 }
193
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700194 private:
195 const Options mOptions;
196 float mPhysicalToLogicalAngle = 0;
Ytai Ben-Tsvi7901bdd2021-09-08 16:11:07 -0700197 // We store the physical to logical angle as "pending" until the next world-to-screen sample it
198 // applies to arrives.
199 float mPendingPhysicalToLogicalAngle = 0;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700200 std::optional<int64_t> mWorldToHeadTimestamp;
201 std::optional<int64_t> mWorldToScreenTimestamp;
202 Pose3f mHeadToStagePose;
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -0800203 PoseBias mHeadPoseBias;
204 PoseBias mScreenPoseBias;
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800205 StillnessDetector mHeadStillnessDetector;
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -0800206 StillnessDetector mScreenStillnessDetector;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700207 ScreenHeadFusion mScreenHeadFusion;
208 ModeSelector mModeSelector;
209 PoseRateLimiter mRateLimiter;
Shunkai Yao59b27bc2022-07-22 18:42:27 +0000210 static constexpr std::size_t mMaxLocalLogLine = 10;
211 SimpleLog mLocalLog{mMaxLocalLogLine};
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700212};
213
214} // namespace
215
Ytai Ben-Tsviedbab3d2021-08-16 11:27:52 -0700216std::unique_ptr<HeadTrackingProcessor> createHeadTrackingProcessor(
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700217 const HeadTrackingProcessor::Options& options, HeadTrackingMode initialMode) {
218 return std::make_unique<HeadTrackingProcessorImpl>(options, initialMode);
219}
220
Shunkai Yao0324a392022-08-30 03:14:50 +0000221std::string toString(HeadTrackingMode mode) {
222 switch (mode) {
223 case HeadTrackingMode::STATIC:
224 return "STATIC";
225 case HeadTrackingMode::WORLD_RELATIVE:
226 return "WORLD_RELATIVE";
227 case HeadTrackingMode::SCREEN_RELATIVE:
228 return "SCREEN_RELATIVE";
229 }
230 return "EnumNotImplemented";
231};
232
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700233} // namespace media
234} // namespace android