xref: /aosp_15_r20/frameworks/av/services/audiopolicy/service/SpatializerPoseController.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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