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