1 /*
2 * Copyright 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 */
16
17 #include "SpatializerPoseController.h"
18 #include <android-base/stringprintf.h>
19 #include <chrono>
20 #include <cstdint>
21 #include <string>
22
23 #define LOG_TAG "SpatializerPoseController"
24 //#define LOG_NDEBUG 0
25 #include <audio_utils/mutex.h>
26 #include <cutils/properties.h>
27 #include <sensor/Sensor.h>
28 #include <media/MediaMetricsItem.h>
29 #include <media/QuaternionUtil.h>
30 #include <utils/Log.h>
31 #include <utils/SystemClock.h>
32
33 namespace android {
34
35 using media::createHeadTrackingProcessor;
36 using media::HeadTrackingMode;
37 using media::HeadTrackingProcessor;
38 using media::Pose3f;
39 using media::SensorPoseProvider;
40 using media::Twist3f;
41
42 using namespace std::chrono_literals;
43
44 namespace {
45
46 // This is how fast, in m/s, we allow position to shift during rate-limiting.
47 constexpr float kMaxTranslationalVelocity = 2;
48
49 // This is how fast, in rad/s, we allow rotation angle to shift during rate-limiting.
50 constexpr float kMaxRotationalVelocity = 0.8f;
51
52 // This is how far into the future we predict the head pose.
53 // The prediction duration should be based on the actual latency from
54 // head-tracker to audio output, though setting the prediction duration too
55 // high may result in higher prediction errors when the head accelerates or
56 // decelerates (changes velocity).
57 //
58 // The head tracking predictor will do a best effort to achieve the requested
59 // prediction duration. If the duration is too far in the future based on
60 // current sensor variance, the predictor may internally restrict duration to what
61 // is achievable with reasonable confidence as the "best prediction".
62 constexpr auto kPredictionDuration = 120ms;
63
64 // After not getting a pose sample for this long, we would treat the measurement as stale.
65 // The max connection interval is 50ms, and HT sensor event interval can differ depending on the
66 // sampling rate, scheduling, sensor eventQ FIFO etc. 120 (2 * 50 + 20) ms seems reasonable for now.
67 constexpr auto kFreshnessTimeout = 120ms;
68
69 // Auto-recenter kicks in after the head has been still for this long.
70 constexpr auto kAutoRecenterWindowDuration = 6s;
71
72 // Auto-recenter considers head not still if translated by this much (in meters, approx).
73 constexpr float kAutoRecenterTranslationThreshold = 0.1f;
74
75 // Auto-recenter considers head not still if rotated by this much (in radians, approx).
76 constexpr float kAutoRecenterRotationThreshold = 10.5f / 180 * M_PI;
77
78 // Screen is considered to be unstable (not still) if it has moved significantly within the last
79 // time window of this duration.
80 constexpr auto kScreenStillnessWindowDuration = 750ms;
81
82 // Screen is considered to have moved significantly if translated by this much (in meter, approx).
83 constexpr float kScreenStillnessTranslationThreshold = 0.1f;
84
85 // Screen is considered to have moved significantly if rotated by this much (in radians, approx).
86 constexpr float kScreenStillnessRotationThreshold = 15.0f / 180 * M_PI;
87
88 // Time units for system clock ticks. This is what the Sensor Framework timestamps represent and
89 // what we use for pose filtering.
90 using Ticks = std::chrono::nanoseconds;
91
92 // How many ticks in a second.
93 constexpr auto kTicksPerSecond = Ticks::period::den;
94
getSensorMetricsId(int32_t sensorId)95 std::string getSensorMetricsId(int32_t sensorId) {
96 return std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_SENSOR).append(std::to_string(sensorId));
97 }
98
99 } // namespace
100
SpatializerPoseController(Listener * listener,std::chrono::microseconds sensorPeriod,std::optional<std::chrono::microseconds> maxUpdatePeriod)101 SpatializerPoseController::SpatializerPoseController(Listener* listener,
102 std::chrono::microseconds sensorPeriod,
103 std::optional<std::chrono::microseconds> maxUpdatePeriod)
104 : mListener(listener),
105 mSensorPeriod(sensorPeriod),
106 mProcessor(createHeadTrackingProcessor(HeadTrackingProcessor::Options{
107 .maxTranslationalVelocity = kMaxTranslationalVelocity / kTicksPerSecond,
108 .maxRotationalVelocity = kMaxRotationalVelocity / kTicksPerSecond,
109 .freshnessTimeout = Ticks(kFreshnessTimeout).count(),
110 .predictionDuration = []() -> float {
111 const int duration_ms =
112 property_get_int32("audio.spatializer.prediction_duration_ms", -1);
113 if (duration_ms >= 0) {
114 return duration_ms * 1'000'000LL;
115 } else {
116 return Ticks(kPredictionDuration).count();
117 }
118 }(),
119 .autoRecenterWindowDuration = Ticks(kAutoRecenterWindowDuration).count(),
120 .autoRecenterTranslationalThreshold = kAutoRecenterTranslationThreshold,
121 .autoRecenterRotationalThreshold = kAutoRecenterRotationThreshold,
122 .screenStillnessWindowDuration = Ticks(kScreenStillnessWindowDuration).count(),
123 .screenStillnessTranslationalThreshold = kScreenStillnessTranslationThreshold,
124 .screenStillnessRotationalThreshold = kScreenStillnessRotationThreshold,
125 })),
126 mPoseProvider(SensorPoseProvider::create("headtracker", this)),
__anon40b8ed630302null127 mThread([this, maxUpdatePeriod] { // It's important that mThread is initialized after
128 // everything else because it runs a member
129 // function that may use any member
130 // of this class.
131 while (true) {
132 Pose3f headToStage;
133 std::optional<HeadTrackingMode> modeIfChanged;
134 {
135 audio_utils::unique_lock ul(mMutex);
136 while (true) {
137 if (mShouldExit) {
138 ALOGV("Exiting thread");
139 return;
140 }
141 if (mShouldCalculate) {
142 std::tie(headToStage, modeIfChanged) = calculate_l();
143 break;
144 }
145 if (maxUpdatePeriod.has_value()) {
146 mCondVar.wait_for(ul, maxUpdatePeriod.value());
147 } else {
148 mCondVar.wait(ul);
149 }
150 }
151 }
152
153 // Invoke the callbacks outside the lock.
154 mListener->onHeadToStagePose(headToStage);
155 if (modeIfChanged) {
156 mListener->onActualModeChange(modeIfChanged.value());
157 }
158
159 {
160 std::lock_guard lock(mMutex);
161 if (!mCalculated) {
162 mCalculated = true;
163 mCondVar.notify_all();
164 }
165 mShouldCalculate = false;
166 }
167 }
168 }) {
169 const media::PosePredictorType posePredictorType =
170 (media::PosePredictorType)
171 property_get_int32("audio.spatializer.pose_predictor_type", -1);
172 if (isValidPosePredictorType(posePredictorType)) {
173 mProcessor->setPosePredictorType(posePredictorType);
174 }
175 }
176
~SpatializerPoseController()177 SpatializerPoseController::~SpatializerPoseController() {
178 {
179 std::lock_guard lock(mMutex);
180 mShouldExit = true;
181 mCondVar.notify_all();
182 }
183 mThread.join();
184 }
185
setHeadSensor(int32_t sensor)186 void SpatializerPoseController::setHeadSensor(int32_t sensor) {
187 std::lock_guard lock(mMutex);
188 if (sensor == mHeadSensor) return;
189 ALOGV("%s: new sensor:%d mHeadSensor:%d mScreenSensor:%d",
190 __func__, sensor, mHeadSensor, mScreenSensor);
191
192 // Stop current sensor, if valid and different from the other sensor.
193 if (mHeadSensor != INVALID_SENSOR && mHeadSensor != mScreenSensor) {
194 mPoseProvider->stopSensor(mHeadSensor);
195 mediametrics::LogItem(getSensorMetricsId(mHeadSensor))
196 .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
197 .record();
198 }
199
200 if (sensor != INVALID_SENSOR) {
201 if (sensor != mScreenSensor) {
202 // Start new sensor.
203 mHeadSensor =
204 mPoseProvider->startSensor(sensor, mSensorPeriod) ? sensor : INVALID_SENSOR;
205 if (mHeadSensor != INVALID_SENSOR) {
206 auto sensor = mPoseProvider->getSensorByHandle(mHeadSensor);
207 std::string stringType = sensor ? sensor->getStringType().c_str() : "";
208 mediametrics::LogItem(getSensorMetricsId(mHeadSensor))
209 .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START)
210 .set(AMEDIAMETRICS_PROP_MODE, AMEDIAMETRICS_PROP_MODE_VALUE_HEAD)
211 .set(AMEDIAMETRICS_PROP_TYPE, stringType)
212 .record();
213 }
214 } else {
215 // Sensor is already enabled.
216 mHeadSensor = mScreenSensor;
217 }
218 } else {
219 mHeadSensor = INVALID_SENSOR;
220 }
221
222 mProcessor->recenter(true /* recenterHead */, false /* recenterScreen */, __func__);
223 }
224
setScreenSensor(int32_t sensor)225 void SpatializerPoseController::setScreenSensor(int32_t sensor) {
226 std::lock_guard lock(mMutex);
227 if (sensor == mScreenSensor) return;
228 ALOGV("%s: new sensor:%d mHeadSensor:%d mScreenSensor:%d",
229 __func__, sensor, mHeadSensor, mScreenSensor);
230
231 // Stop current sensor, if valid and different from the other sensor.
232 if (mScreenSensor != INVALID_SENSOR && mScreenSensor != mHeadSensor) {
233 mPoseProvider->stopSensor(mScreenSensor);
234 mediametrics::LogItem(getSensorMetricsId(mScreenSensor))
235 .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
236 .record();
237 }
238
239 if (sensor != INVALID_SENSOR) {
240 if (sensor != mHeadSensor) {
241 // Start new sensor.
242 mScreenSensor =
243 mPoseProvider->startSensor(sensor, mSensorPeriod) ? sensor : INVALID_SENSOR;
244 auto sensor = mPoseProvider->getSensorByHandle(mScreenSensor);
245 std::string stringType = sensor ? sensor->getStringType().c_str() : "";
246 mediametrics::LogItem(getSensorMetricsId(mScreenSensor))
247 .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START)
248 .set(AMEDIAMETRICS_PROP_MODE, AMEDIAMETRICS_PROP_MODE_VALUE_SCREEN)
249 .set(AMEDIAMETRICS_PROP_TYPE, stringType)
250 .record();
251 } else {
252 // Sensor is already enabled.
253 mScreenSensor = mHeadSensor;
254 }
255 } else {
256 mScreenSensor = INVALID_SENSOR;
257 }
258
259 mProcessor->recenter(false /* recenterHead */, true /* recenterScreen */, __func__);
260 }
261
setDesiredMode(HeadTrackingMode mode)262 void SpatializerPoseController::setDesiredMode(HeadTrackingMode mode) {
263 std::lock_guard lock(mMutex);
264 mProcessor->setDesiredMode(mode);
265 }
266
setScreenToStagePose(const Pose3f & screenToStage)267 void SpatializerPoseController::setScreenToStagePose(const Pose3f& screenToStage) {
268 std::lock_guard lock(mMutex);
269 mProcessor->setScreenToStagePose(screenToStage);
270 }
271
setDisplayOrientation(float physicalToLogicalAngle)272 void SpatializerPoseController::setDisplayOrientation(float physicalToLogicalAngle) {
273 std::lock_guard lock(mMutex);
274 mProcessor->setDisplayOrientation(physicalToLogicalAngle);
275 }
276
calculateAsync()277 void SpatializerPoseController::calculateAsync() {
278 std::lock_guard lock(mMutex);
279 mShouldCalculate = true;
280 mCondVar.notify_all();
281 }
282
waitUntilCalculated()283 void SpatializerPoseController::waitUntilCalculated() {
284 audio_utils::unique_lock ul(mMutex);
285 while (!mCalculated) {
286 mCondVar.wait(ul);
287 }
288 }
289
290 std::tuple<media::Pose3f, std::optional<media::HeadTrackingMode>>
calculate_l()291 SpatializerPoseController::calculate_l() {
292 Pose3f headToStage;
293 HeadTrackingMode mode;
294 std::optional<media::HeadTrackingMode> modeIfChanged;
295
296 mProcessor->calculate(elapsedRealtimeNano());
297 headToStage = mProcessor->getHeadToStagePose();
298 mode = mProcessor->getActualMode();
299 if (!mActualMode.has_value() || mActualMode.value() != mode) {
300 mActualMode = mode;
301 modeIfChanged = mode;
302 }
303 return std::make_tuple(headToStage, modeIfChanged);
304 }
305
recenter()306 void SpatializerPoseController::recenter() {
307 std::lock_guard lock(mMutex);
308 mProcessor->recenter(true /* recenterHead */, true /* recenterScreen */, __func__);
309 }
310
onPose(int64_t timestamp,int32_t sensor,const Pose3f & pose,const std::optional<Twist3f> & twist,bool isNewReference)311 void SpatializerPoseController::onPose(int64_t timestamp, int32_t sensor, const Pose3f& pose,
312 const std::optional<Twist3f>& twist, bool isNewReference) {
313 std::lock_guard lock(mMutex);
314 constexpr float NANOS_TO_MILLIS = 1e-6;
315 constexpr float RAD_TO_DEGREE = 180.f / M_PI;
316
317 const float delayMs = (elapsedRealtimeNano() - timestamp) * NANOS_TO_MILLIS; // CLOCK_BOOTTIME
318
319 if (sensor == mHeadSensor) {
320 std::vector<float> pryprydt(8); // pitch, roll, yaw, d_pitch, d_roll, d_yaw,
321 // discontinuity, timestamp_delay
322 media::quaternionToAngles(pose.rotation(), &pryprydt[0], &pryprydt[1], &pryprydt[2]);
323 if (twist) {
324 const auto rotationalVelocity = twist->rotationalVelocity();
325 // The rotational velocity is an intrinsic transform (i.e. based on the head
326 // coordinate system, not the world coordinate system). It is a 3 element vector:
327 // axis (d theta / dt).
328 //
329 // We leave rotational velocity relative to the head coordinate system,
330 // as the initial head tracking sensor's world frame is arbitrary.
331 media::quaternionToAngles(media::rotationVectorToQuaternion(rotationalVelocity),
332 &pryprydt[3], &pryprydt[4], &pryprydt[5]);
333 }
334 pryprydt[6] = isNewReference;
335 pryprydt[7] = delayMs;
336 for (size_t i = 0; i < 6; ++i) {
337 // pitch, roll, yaw in degrees, referenced in degrees on the world frame.
338 // d_pitch, d_roll, d_yaw rotational velocity in degrees/s, based on the world frame.
339 pryprydt[i] *= RAD_TO_DEGREE;
340 }
341 mHeadSensorRecorder.record(pryprydt);
342 mHeadSensorDurableRecorder.record(pryprydt);
343
344 mProcessor->setWorldToHeadPose(timestamp, pose,
345 twist.value_or(Twist3f()) / kTicksPerSecond);
346 if (isNewReference) {
347 mProcessor->recenter(true, false, __func__);
348 }
349 }
350 if (sensor == mScreenSensor) {
351 std::vector<float> pryt{ 0.f, 0.f, 0.f, delayMs}; // pitch, roll, yaw, timestamp_delay
352 media::quaternionToAngles(pose.rotation(), &pryt[0], &pryt[1], &pryt[2]);
353 for (size_t i = 0; i < 3; ++i) {
354 pryt[i] *= RAD_TO_DEGREE;
355 }
356 mScreenSensorRecorder.record(pryt);
357 mScreenSensorDurableRecorder.record(pryt);
358
359 mProcessor->setWorldToScreenPose(timestamp, pose);
360 if (isNewReference) {
361 mProcessor->recenter(false, true, __func__);
362 }
363 }
364 }
365
toString(unsigned level) const366 std::string SpatializerPoseController::toString(unsigned level) const NO_THREAD_SAFETY_ANALYSIS {
367 std::string prefixSpace(level, ' ');
368 std::string ss = prefixSpace + "SpatializerPoseController:\n";
369 bool needUnlock = false;
370
371 prefixSpace += ' ';
372 auto now = std::chrono::steady_clock::now();
373 if (!audio_utils::std_mutex_timed_lock(mMutex, std::chrono::nanoseconds(
374 media::kSpatializerDumpSysTimeOutInSecond).count())) {
375 ss.append(prefixSpace).append("try_lock failed, dumpsys maybe INACCURATE!\n");
376 } else {
377 needUnlock = true;
378 }
379
380 ss += prefixSpace;
381 if (mHeadSensor == INVALID_SENSOR) {
382 ss += "HeadSensor: INVALID\n";
383 } else {
384 base::StringAppendF(&ss, "HeadSensor: 0x%08x "
385 "(active world-to-head : head-relative velocity) "
386 "[ pitch, roll, yaw : d_pitch, d_roll, d_yaw : disc : delay ] "
387 "(degrees, degrees/s, bool, ms)\n", mHeadSensor);
388 ss.append(prefixSpace)
389 .append(" PerMinuteHistory:\n")
390 .append(mHeadSensorDurableRecorder.toString(level + 3))
391 .append(prefixSpace)
392 .append(" PerSecondHistory:\n")
393 .append(mHeadSensorRecorder.toString(level + 3));
394 }
395
396 ss += prefixSpace;
397 if (mScreenSensor == INVALID_SENSOR) {
398 ss += "ScreenSensor: INVALID\n";
399 } else {
400 base::StringAppendF(&ss, "ScreenSensor: 0x%08x (active world-to-screen) "
401 "[ pitch, roll, yaw : delay ] "
402 "(degrees, ms)\n", mScreenSensor);
403 ss.append(prefixSpace)
404 .append(" PerMinuteHistory:\n")
405 .append(mScreenSensorDurableRecorder.toString(level + 3))
406 .append(prefixSpace)
407 .append(" PerSecondHistory:\n")
408 .append(mScreenSensorRecorder.toString(level + 3));
409 }
410
411 ss += prefixSpace;
412 if (mActualMode.has_value()) {
413 base::StringAppendF(&ss, "ActualMode: %s\n", media::toString(mActualMode.value()).c_str());
414 } else {
415 ss += "ActualMode NOTEXIST\n";
416 }
417
418 if (mProcessor) {
419 ss += mProcessor->toString_l(level + 1);
420 } else {
421 ss.append(prefixSpace.c_str()).append("HeadTrackingProcessor not exist\n");
422 }
423
424 if (mPoseProvider) {
425 ss += mPoseProvider->toString(level + 1);
426 } else {
427 ss.append(prefixSpace.c_str()).append("SensorPoseProvider not exist\n");
428 }
429
430 if (needUnlock) {
431 mMutex.unlock();
432 }
433 // TODO: 233092747 add history sensor info with SimpleLog.
434 return ss;
435 }
436
437 } // namespace android
438