blob: 2fc9ff9db95942ec692acf2bd52b59271ffbe230 [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 Yao5a251df2022-07-22 18:42:27 +000016#include <inttypes.h>
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070017
Shunkai Yao5a251df2022-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 Yao5a251df2022-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 {
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -080091 // Handle the screen first, since it might trigger a recentering of the head.
92 if (mWorldToScreenTimestamp.has_value()) {
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -080093 const Pose3f worldToLogicalScreen = mScreenPoseBias.getOutput();
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -080094 bool screenStable = mScreenStillnessDetector.calculate(timestamp);
95 mModeSelector.setScreenStable(mWorldToScreenTimestamp.value(), screenStable);
96 // Whenever the screen is unstable, recenter the head pose.
97 if (!screenStable) {
98 recenter(true, false);
99 }
100 mScreenHeadFusion.setWorldToScreenPose(mWorldToScreenTimestamp.value(),
101 worldToLogicalScreen);
102 }
103
104 // Handle head.
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700105 if (mWorldToHeadTimestamp.has_value()) {
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -0800106 Pose3f worldToHead = mHeadPoseBias.getOutput();
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800107 // Auto-recenter.
108 if (mHeadStillnessDetector.calculate(timestamp)) {
109 recenter(true, false);
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -0800110 worldToHead = mHeadPoseBias.getOutput();
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800111 }
112
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700113 mScreenHeadFusion.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
114 mModeSelector.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
115 }
116
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700117 auto maybeScreenToHead = mScreenHeadFusion.calculate();
118 if (maybeScreenToHead.has_value()) {
119 mModeSelector.setScreenToHeadPose(maybeScreenToHead->timestamp,
120 maybeScreenToHead->pose);
121 } else {
122 mModeSelector.setScreenToHeadPose(timestamp, std::nullopt);
123 }
124
125 HeadTrackingMode prevMode = mModeSelector.getActualMode();
126 mModeSelector.calculate(timestamp);
127 if (mModeSelector.getActualMode() != prevMode) {
128 // Mode has changed, enable rate limiting.
129 mRateLimiter.enable();
130 }
131 mRateLimiter.setTarget(mModeSelector.getHeadToStagePose());
132 mHeadToStagePose = mRateLimiter.calculatePose(timestamp);
133 }
134
135 Pose3f getHeadToStagePose() const override { return mHeadToStagePose; }
136
137 HeadTrackingMode getActualMode() const override { return mModeSelector.getActualMode(); }
138
Ytai Ben-Tsvi95b00c82021-08-27 15:27:08 -0700139 void recenter(bool recenterHead, bool recenterScreen) override {
140 if (recenterHead) {
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -0800141 mHeadPoseBias.recenter();
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800142 mHeadStillnessDetector.reset();
Shunkai Yao5a251df2022-07-22 18:42:27 +0000143 mLocalLog.log("recenter Head");
Ytai Ben-Tsvi95b00c82021-08-27 15:27:08 -0700144 }
145 if (recenterScreen) {
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -0800146 mScreenPoseBias.recenter();
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -0800147 mScreenStillnessDetector.reset();
Shunkai Yao5a251df2022-07-22 18:42:27 +0000148 mLocalLog.log("recenter Screen");
Ytai Ben-Tsvi95b00c82021-08-27 15:27:08 -0700149 }
150
151 // If a sensor being recentered is included in the current mode, apply rate limiting to
152 // avoid discontinuities.
153 HeadTrackingMode mode = mModeSelector.getActualMode();
154 if ((recenterHead && (mode == HeadTrackingMode::WORLD_RELATIVE ||
155 mode == HeadTrackingMode::SCREEN_RELATIVE)) ||
156 (recenterScreen && mode == HeadTrackingMode::SCREEN_RELATIVE)) {
157 mRateLimiter.enable();
158 }
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700159 }
160
Shunkai Yao5a251df2022-07-22 18:42:27 +0000161 std::string toString_l(unsigned level) const override {
162 std::string prefixSpace(level, ' ');
163 std::string ss = prefixSpace + "HeadTrackingProcessor:\n";
Shunkai Yao51379452022-08-30 03:14:50 +0000164 StringAppendF(&ss, "%s maxTranslationalVelocity: %f meter/second\n", prefixSpace.c_str(),
Shunkai Yao5a251df2022-07-22 18:42:27 +0000165 mOptions.maxTranslationalVelocity);
Shunkai Yao51379452022-08-30 03:14:50 +0000166 StringAppendF(&ss, "%s maxRotationalVelocity: %f rad/second\n", prefixSpace.c_str(),
Shunkai Yao5a251df2022-07-22 18:42:27 +0000167 mOptions.maxRotationalVelocity);
Shunkai Yao51379452022-08-30 03:14:50 +0000168 StringAppendF(&ss, "%s freshnessTimeout: %0.4f ms\n", prefixSpace.c_str(),
169 media::nsToFloatMs(mOptions.freshnessTimeout));
170 StringAppendF(&ss, "%s predictionDuration: %0.4f ms\n", prefixSpace.c_str(),
171 media::nsToFloatMs(mOptions.predictionDuration));
172 StringAppendF(&ss, "%s autoRecenterWindowDuration: %0.4f ms\n", prefixSpace.c_str(),
173 media::nsToFloatMs(mOptions.autoRecenterWindowDuration));
174 StringAppendF(&ss, "%s autoRecenterTranslationalThreshold: %f meter\n", prefixSpace.c_str(),
Shunkai Yao5a251df2022-07-22 18:42:27 +0000175 mOptions.autoRecenterTranslationalThreshold);
Shunkai Yao51379452022-08-30 03:14:50 +0000176 StringAppendF(&ss, "%s autoRecenterRotationalThreshold: %f radians\n", prefixSpace.c_str(),
Shunkai Yao5a251df2022-07-22 18:42:27 +0000177 mOptions.autoRecenterRotationalThreshold);
Shunkai Yao51379452022-08-30 03:14:50 +0000178 StringAppendF(&ss, "%s screenStillnessWindowDuration: %0.4f ms\n", prefixSpace.c_str(),
179 media::nsToFloatMs(mOptions.screenStillnessWindowDuration));
180 StringAppendF(&ss, "%s screenStillnessTranslationalThreshold: %f meter\n",
181 prefixSpace.c_str(), mOptions.screenStillnessTranslationalThreshold);
182 StringAppendF(&ss, "%s screenStillnessRotationalThreshold: %f radians\n",
183 prefixSpace.c_str(), mOptions.screenStillnessRotationalThreshold);
184 ss += mModeSelector.toString(level + 1);
185 ss += mRateLimiter.toString(level + 1);
Shunkai Yao5a251df2022-07-22 18:42:27 +0000186 ss.append(prefixSpace + "ReCenterHistory:\n");
187 ss += mLocalLog.dumpToString((prefixSpace + " ").c_str(), mMaxLocalLogLine);
Shunkai Yao5a251df2022-07-22 18:42:27 +0000188 return ss;
189 }
190
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700191 private:
192 const Options mOptions;
193 float mPhysicalToLogicalAngle = 0;
Ytai Ben-Tsvi7901bdd2021-09-08 16:11:07 -0700194 // We store the physical to logical angle as "pending" until the next world-to-screen sample it
195 // applies to arrives.
196 float mPendingPhysicalToLogicalAngle = 0;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700197 std::optional<int64_t> mWorldToHeadTimestamp;
198 std::optional<int64_t> mWorldToScreenTimestamp;
199 Pose3f mHeadToStagePose;
Ytai Ben-Tsvi09ad8c92022-01-28 14:19:08 -0800200 PoseBias mHeadPoseBias;
201 PoseBias mScreenPoseBias;
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800202 StillnessDetector mHeadStillnessDetector;
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -0800203 StillnessDetector mScreenStillnessDetector;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700204 ScreenHeadFusion mScreenHeadFusion;
205 ModeSelector mModeSelector;
206 PoseRateLimiter mRateLimiter;
Shunkai Yao5a251df2022-07-22 18:42:27 +0000207 static constexpr std::size_t mMaxLocalLogLine = 10;
208 SimpleLog mLocalLog{mMaxLocalLogLine};
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700209};
210
211} // namespace
212
Ytai Ben-Tsviedbab3d2021-08-16 11:27:52 -0700213std::unique_ptr<HeadTrackingProcessor> createHeadTrackingProcessor(
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700214 const HeadTrackingProcessor::Options& options, HeadTrackingMode initialMode) {
215 return std::make_unique<HeadTrackingProcessorImpl>(options, initialMode);
216}
217
Shunkai Yao51379452022-08-30 03:14:50 +0000218std::string toString(HeadTrackingMode mode) {
219 switch (mode) {
220 case HeadTrackingMode::STATIC:
221 return "STATIC";
222 case HeadTrackingMode::WORLD_RELATIVE:
223 return "WORLD_RELATIVE";
224 case HeadTrackingMode::SCREEN_RELATIVE:
225 return "SCREEN_RELATIVE";
226 }
227 return "EnumNotImplemented";
228};
229
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700230} // namespace media
231} // namespace android