Head-tracking library for Immersive Audio

See README.md for details.

Bug: 188502620
Test: atest --host libheadtracking-test
Change-Id: I34201c4780e5e581cc96449bd89863bcbc250783
diff --git a/media/libheadtracking/ModeSelector.h b/media/libheadtracking/ModeSelector.h
new file mode 100644
index 0000000..17a5142
--- /dev/null
+++ b/media/libheadtracking/ModeSelector.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <optional>
+
+#include "media/HeadTrackingMode.h"
+#include "media/Pose.h"
+
+#include "PoseRateLimiter.h"
+
+namespace android {
+namespace media {
+
+/**
+ * Head-tracking mode selector.
+ *
+ * This class is responsible for production of the determining pose for audio virtualization, based
+ * on a number of available sources and a selectable mode.
+ *
+ * Typical flow is:
+ * ModeSelector selector(...);
+ * while (...) {
+ *     // Set inputs.
+ *     selector.setFoo(...);
+ *     selector.setBar(...);
+ *
+ *     // Update outputs based on inputs.
+ *     selector.calculate(...);
+ *
+ *     // Get outputs.
+ *     Pose3f pose = selector.getHeadToStagePose();
+ * }
+ *
+ * This class is not thread-safe, but thread-compatible.
+ *
+ * For details on the frames of reference involved, their composition and the definitions to the
+ * different modes, refer to:
+ * go/immersive-audio-frames
+ *
+ * The actual mode may deviate from the desired mode in the following cases:
+ * - When we cannot get a valid and fresh estimate of the screen-to-head pose, we will fall back
+ *   from screen-relative to world-relative.
+ * - When we cannot get a fresh estimate of the world-to-head pose, we will fall back from
+ *   world-relative to static.
+ *
+ * All the timestamps used here are of arbitrary units and origin. They just need to be consistent
+ * between all the calls and with the Options provided for determining freshness and rate limiting.
+ */
+class ModeSelector {
+  public:
+    struct Options {
+        int64_t freshnessTimeout = std::numeric_limits<int64_t>::max();
+    };
+
+    ModeSelector(const Options& options, HeadTrackingMode initialMode = HeadTrackingMode::STATIC);
+
+    /** Sets the desired head-tracking mode. */
+    void setDesiredMode(HeadTrackingMode mode);
+
+    /**
+     * Set the screen-to-stage pose, used in all modes.
+     */
+    void setScreenToStagePose(const Pose3f& screenToStage);
+
+    /**
+     * Set the screen-to-head pose, used in screen-relative mode.
+     * The timestamp needs to reflect how fresh the sample is (not necessarily which point in time
+     * it applies to). nullopt can be used if it is determined that the listener is not in front of
+     * the screen.
+     */
+    void setScreenToHeadPose(int64_t timestamp, const std::optional<Pose3f>& screenToHead);
+
+    /**
+     * Set the world-to-head pose, used in world-relative mode.
+     * The timestamp needs to reflect how fresh the sample is (not necessarily which point in time
+     * it applies to).
+     */
+    void setWorldToHeadPose(int64_t timestamp, const Pose3f& worldToHead);
+
+    /**
+     * Process all the previous inputs and update the outputs.
+     */
+    void calculate(int64_t timestamp);
+
+    /**
+     * Get the aggregate head-to-stage pose (primary output of this module).
+     */
+    Pose3f getHeadToStagePose() const;
+
+    /**
+     * Get the actual head-tracking mode (which may deviate from the desired one as mentioned in the
+     * class documentation above).
+     */
+    HeadTrackingMode getActualMode() const;
+
+  private:
+    const Options mOptions;
+
+    HeadTrackingMode mDesiredMode;
+    Pose3f mScreenToStage;
+    std::optional<Pose3f> mScreenToHead;
+    int64_t mScreenToHeadTimestamp;
+    std::optional<Pose3f> mWorldToHead;
+    int64_t mWorldToHeadTimestamp;
+
+    HeadTrackingMode mActualMode;
+    Pose3f mHeadToStage;
+
+    void calculateActualMode(int64_t timestamp);
+};
+
+}  // namespace media
+}  // namespace android