stagefright: pass color aspects into codecs

Bug: 25975353
Change-Id: Ie2cdb845769f5ec3561a099f96e8f4dd406299ef
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 4a7fc62..761f182 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -290,6 +290,8 @@
 
     bool mTunneled;
 
+    OMX_INDEXTYPE mDescribeColorAspectsIndex;
+
     status_t setCyclicIntraMacroblockRefresh(const sp<AMessage> &msg, int32_t mode);
     status_t allocateBuffersOnPort(OMX_U32 portIndex);
     status_t freeBuffersOnPort(OMX_U32 portIndex);
@@ -341,16 +343,22 @@
     status_t setSupportedOutputFormat(bool getLegacyFlexibleFormat);
 
     status_t setupVideoDecoder(
-            const char *mime, const sp<AMessage> &msg, bool usingNativeBuffers);
+            const char *mime, const sp<AMessage> &msg, bool usingNativeBuffers,
+            sp<AMessage> &outputformat);
 
     status_t setupVideoEncoder(
-            const char *mime, const sp<AMessage> &msg);
+            const char *mime, const sp<AMessage> &msg, sp<AMessage> &outputformat);
 
     status_t setVideoFormatOnPort(
             OMX_U32 portIndex,
             int32_t width, int32_t height,
             OMX_VIDEO_CODINGTYPE compressionFormat, float frameRate = -1.0);
 
+    status_t setColorAspects(
+        OMX_U32 portIndex, int32_t width, int32_t height, const sp<AMessage> &msg,
+        sp<AMessage> &format);
+    status_t getColorAspects(OMX_U32 portIndex, sp<AMessage> &format);
+
     typedef struct drcParams {
         int32_t drcCut;
         int32_t drcBoost;
diff --git a/include/media/stagefright/CodecBase.h b/include/media/stagefright/CodecBase.h
index 3131090..3de0d21 100644
--- a/include/media/stagefright/CodecBase.h
+++ b/include/media/stagefright/CodecBase.h
@@ -19,12 +19,16 @@
 #define CODEC_BASE_H_
 
 #include <stdint.h>
-#include <media/IOMX.h>
 
+#include <media/IOMX.h>
 #include <media/MediaCodecInfo.h>
 #include <media/stagefright/foundation/AHandler.h>
+#include <media/hardware/HardwareAPI.h>
+
 #include <utils/NativeHandle.h>
 
+#include <system/graphics.h>
+
 namespace android {
 
 struct ABuffer;
@@ -89,6 +93,117 @@
         DISALLOW_EVIL_CONSTRUCTORS(PortDescription);
     };
 
+    /*
+     * Codec-related defines
+     */
+
+    /**********************************************************************************************/
+
+    /*
+     * Media-platform color constants. MediaCodec uses (an extended version of) platform-defined
+     * constants that are derived from HAL_DATASPACE, since these are directly exposed to the user.
+     * We extend the values to maintain the richer set of information defined inside media
+     * containers and bitstreams that are not supported by the platform. We also expect vendors
+     * to extend some of these values with vendor-specific values. These are separated into a
+     * vendor-extension section so they won't collide with future platform values.
+     */
+
+    enum ColorStandard : uint32_t {
+        kColorStandardUnspecified =
+                HAL_DATASPACE_STANDARD_UNSPECIFIED >> HAL_DATASPACE_STANDARD_SHIFT,
+        kColorStandardBT709 =     HAL_DATASPACE_STANDARD_BT709 >> HAL_DATASPACE_STANDARD_SHIFT,
+        kColorStandardBT601_625 = HAL_DATASPACE_STANDARD_BT601_625 >> HAL_DATASPACE_STANDARD_SHIFT,
+        kColorStandardBT601_625_Unadjusted =
+                HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED >> HAL_DATASPACE_STANDARD_SHIFT,
+        kColorStandardBT601_525 = HAL_DATASPACE_STANDARD_BT601_525 >> HAL_DATASPACE_STANDARD_SHIFT,
+        kColorStandardBT601_525_Unadjusted =
+                HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED >> HAL_DATASPACE_STANDARD_SHIFT,
+        kColorStandardBT2020 =    HAL_DATASPACE_STANDARD_BT2020 >> HAL_DATASPACE_STANDARD_SHIFT,
+        kColorStandardBT2020Constant =
+                HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE >> HAL_DATASPACE_STANDARD_SHIFT,
+        kColorStandardBT470M =    HAL_DATASPACE_STANDARD_BT470M >> HAL_DATASPACE_STANDARD_SHIFT,
+        kColorStandardFilm =      HAL_DATASPACE_STANDARD_FILM >> HAL_DATASPACE_STANDARD_SHIFT,
+        kColorStandardMax =       HAL_DATASPACE_STANDARD_MASK >> HAL_DATASPACE_STANDARD_SHIFT,
+
+        /* This marks a section of color-standard values that are not supported by graphics HAL,
+           but track defined color primaries-matrix coefficient combinations in media.
+           These are stable for a given release. */
+        kColorStandardExtendedStart = kColorStandardMax + 1,
+
+        /* This marks a section of color-standard values that are not supported by graphics HAL
+           nor using media defined color primaries or matrix coefficients. These may differ per
+           device. */
+        kColorStandardVendorStart = 0x10000,
+    };
+
+    enum ColorTransfer : uint32_t  {
+        kColorTransferUnspecified =
+                HAL_DATASPACE_TRANSFER_UNSPECIFIED >> HAL_DATASPACE_TRANSFER_SHIFT,
+        kColorTransferLinear =      HAL_DATASPACE_TRANSFER_LINEAR >> HAL_DATASPACE_TRANSFER_SHIFT,
+        kColorTransferSRGB =        HAL_DATASPACE_TRANSFER_SRGB >> HAL_DATASPACE_TRANSFER_SHIFT,
+        kColorTransferSMPTE_170M =
+                HAL_DATASPACE_TRANSFER_SMPTE_170M >> HAL_DATASPACE_TRANSFER_SHIFT,
+        kColorTransferGamma22 =     HAL_DATASPACE_TRANSFER_GAMMA2_2 >> HAL_DATASPACE_TRANSFER_SHIFT,
+        kColorTransferGamma28 =     HAL_DATASPACE_TRANSFER_GAMMA2_8 >> HAL_DATASPACE_TRANSFER_SHIFT,
+        kColorTransferST2084 =      HAL_DATASPACE_TRANSFER_ST2084 >> HAL_DATASPACE_TRANSFER_SHIFT,
+        kColorTransferHLG =         HAL_DATASPACE_TRANSFER_HLG >> HAL_DATASPACE_TRANSFER_SHIFT,
+        kColorTransferMax =         HAL_DATASPACE_TRANSFER_MASK >> HAL_DATASPACE_TRANSFER_SHIFT,
+
+        /* This marks a section of color-transfer values that are not supported by graphics HAL,
+           but track media-defined color-transfer. These are stable for a given release. */
+        kColorTransferExtendedStart = kColorTransferMax + 1,
+
+        /* This marks a section of color-transfer values that are not supported by graphics HAL
+           nor defined by media. These may differ per device. */
+        kColorTransferVendorStart = 0x10000,
+    };
+
+    enum ColorRange : uint32_t  {
+        kColorRangeUnspecified = HAL_DATASPACE_RANGE_UNSPECIFIED >> HAL_DATASPACE_RANGE_SHIFT,
+        kColorRangeFull =        HAL_DATASPACE_RANGE_FULL >> HAL_DATASPACE_RANGE_SHIFT,
+        kColorRangeLimited =     HAL_DATASPACE_RANGE_LIMITED >> HAL_DATASPACE_RANGE_SHIFT,
+        kColorRangeMax =         HAL_DATASPACE_RANGE_MASK >> HAL_DATASPACE_RANGE_SHIFT,
+
+        /* This marks a section of color-transfer values that are not supported by graphics HAL,
+           but track media-defined color-transfer. These are stable for a given release. */
+        kColorRangeExtendedStart = kColorRangeMax + 1,
+
+        /* This marks a section of color-transfer values that are not supported by graphics HAL
+           nor defined by media. These may differ per device. */
+        kColorRangeVendorStart = 0x10000,
+    };
+
+    /*
+     * Static utilities for codec support
+     */
+
+    // using int32_t for media range/standard/transfers to denote extended ranges
+    static int32_t wrapColorAspectsIntoColorStandard(
+            ColorAspects::Primaries primaries, ColorAspects::MatrixCoeffs coeffs);
+    static int32_t wrapColorAspectsIntoColorRange(ColorAspects::Range range);
+    static int32_t wrapColorAspectsIntoColorTransfer(ColorAspects::Transfer transfer);
+
+    static status_t unwrapColorAspectsFromColorRange(
+            int32_t range, ColorAspects::Range *aspect);
+    static status_t unwrapColorAspectsFromColorTransfer(
+            int32_t transfer, ColorAspects::Transfer *aspect);
+    static status_t unwrapColorAspectsFromColorStandard(
+            int32_t standard,
+            ColorAspects::Primaries *primaries, ColorAspects::MatrixCoeffs *coeffs);
+
+    static status_t convertPlatformColorAspectsToCodecAspects(
+            int32_t range, int32_t standard, int32_t transfer, ColorAspects &aspects);
+    static status_t convertCodecColorAspectsToPlatformAspects(
+            const ColorAspects &aspects,
+            int32_t *range, int32_t *standard, int32_t *transfer);
+
+    // updates unspecified range, standard and transfer values to their defaults
+    static void setDefaultPlatformColorAspectsIfNeeded(
+            int32_t &range, int32_t &standard, int32_t &transfer,
+            int32_t width, int32_t height);
+    static void setDefaultCodecColorAspectsIfNeeded(
+            ColorAspects &aspects, int32_t width, int32_t height);
+
 protected:
     CodecBase();
     virtual ~CodecBase();
diff --git a/include/media/stagefright/foundation/ALookup.h b/include/media/stagefright/foundation/ALookup.h
new file mode 100644
index 0000000..571eda2
--- /dev/null
+++ b/include/media/stagefright/foundation/ALookup.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016 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.
+ */
+
+#ifndef A_LOOKUP_H_
+
+#define A_LOOKUP_H_
+
+#include <utility>
+#include <vector>
+
+namespace android {
+
+template<typename T, typename U>
+struct ALookup {
+    ALookup(std::initializer_list<std::pair<T, U>> list);
+
+    bool lookup(const T& from, U *to);
+    bool rlookup(const U& from, T *to);
+    inline bool map(const T& from, U *to) { return lookup(from, to); }
+    inline bool map(const U& from, T *to) { return rlookup(from, to); }
+
+private:
+    std::vector<std::pair<T, U>> mTable;
+};
+
+template<typename T, typename U>
+ALookup<T, U>::ALookup(std::initializer_list<std::pair<T, U>> list)
+    : mTable(list) {
+}
+
+template<typename T, typename U>
+bool ALookup<T, U>::lookup(const T& from, U *to) {
+    for (auto elem : mTable) {
+        if (elem.first == from) {
+            *to = elem.second;
+            return true;
+        }
+    }
+    return false;
+}
+
+template<typename T, typename U>
+bool ALookup<T, U>::rlookup(const U& from, T *to) {
+    for (auto elem : mTable) {
+        if (elem.second == from) {
+            *to = elem.first;
+            return true;
+        }
+    }
+    return false;
+}
+
+} // namespace android
+
+#endif  // A_UTILS_H_
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index d0651f3..def9e25 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -511,7 +511,8 @@
       mTimePerFrameUs(-1ll),
       mTimePerCaptureUs(-1ll),
       mCreateInputBuffersSuspended(false),
-      mTunneled(false) {
+      mTunneled(false),
+      mDescribeColorAspectsIndex((OMX_INDEXTYPE)0) {
     mUninitializedState = new UninitializedState(this);
     mLoadedState = new LoadedState(this);
     mLoadedToIdleState = new LoadedToIdleState(this);
@@ -1940,9 +1941,9 @@
         }
 
         if (encoder) {
-            err = setupVideoEncoder(mime, msg);
+            err = setupVideoEncoder(mime, msg, outputFormat);
         } else {
-            err = setupVideoDecoder(mime, msg, haveNativeWindow);
+            err = setupVideoDecoder(mime, msg, haveNativeWindow, outputFormat);
         }
 
         if (err != OK) {
@@ -2003,7 +2004,7 @@
                         // fallback is not supported for protected playback
                         err = PERMISSION_DENIED;
                     } else if (err == OK) {
-                        err = setupVideoDecoder(mime, msg, false);
+                        err = setupVideoDecoder(mime, msg, false, outputFormat);
                     }
                 }
             }
@@ -3010,7 +3011,8 @@
 }
 
 status_t ACodec::setupVideoDecoder(
-        const char *mime, const sp<AMessage> &msg, bool haveNativeWindow) {
+        const char *mime, const sp<AMessage> &msg, bool haveNativeWindow,
+        sp<AMessage> &outputFormat) {
     int32_t width, height;
     if (!msg->findInt32("width", &width)
             || !msg->findInt32("height", &height)) {
@@ -3073,10 +3075,113 @@
         return err;
     }
 
+    err = setColorAspects(
+            kPortIndexOutput, width, height, msg, outputFormat);
+    if (err != OK) {
+        ALOGI("Falling back to presets as component does not describe color aspects.");
+        err = OK;
+    }
+
+    return err;
+}
+
+status_t ACodec::setColorAspects(
+        OMX_U32 portIndex, int32_t width, int32_t height, const sp<AMessage> &msg,
+        sp<AMessage> &format) {
+    DescribeColorAspectsParams params;
+    InitOMXParams(&params);
+    params.nPortIndex = portIndex;
+
+    // 0 values are unspecified
+    int32_t range = 0, standard = 0, transfer = 0;
+    if (portIndex == kPortIndexInput) {
+        // Encoders allow overriding default aspects with 0 if specified by format. Decoders do not.
+        setDefaultPlatformColorAspectsIfNeeded(range, standard, transfer, width, height);
+    }
+    (void)msg->findInt32("color-range", &range);
+    (void)msg->findInt32("color-standard", &standard);
+    (void)msg->findInt32("color-transfer", &transfer);
+
+    if (convertPlatformColorAspectsToCodecAspects(
+            range, standard, transfer, params.sAspects) != OK) {
+        ALOGW("[%s] Ignoring illegal color aspects(range=%d, standard=%d, transfer=%d)",
+                mComponentName.c_str(), range, standard, transfer);
+        // Invalid values were converted to unspecified !params!, but otherwise were not changed
+        // For encoders, we leave these as is. For decoders, we will use default values.
+    }
+
+    // set defaults for decoders.
+    if (portIndex != kPortIndexInput) {
+        setDefaultCodecColorAspectsIfNeeded(params.sAspects, width, height);
+        convertCodecColorAspectsToPlatformAspects(params.sAspects, &range, &standard, &transfer);
+    }
+
+    // save updated values to base output format (encoder input format will read back actually
+    // supported values by the codec)
+    if (range != 0) {
+        format->setInt32("color-range", range);
+    }
+    if (standard != 0) {
+        format->setInt32("color-standard", standard);
+    }
+    if (transfer != 0) {
+        format->setInt32("color-transfer", transfer);
+    }
+
+    // communicate color aspects to codec
+    status_t err = mOMX->getExtensionIndex(
+            mNode, "OMX.google.android.index.describeColorAspects", &mDescribeColorAspectsIndex);
+    if (err != OK) {
+        mDescribeColorAspectsIndex = (OMX_INDEXTYPE)0;
+        return err;
+    }
+
+    return mOMX->setConfig(mNode, mDescribeColorAspectsIndex, &params, sizeof(params));
+}
+
+status_t ACodec::getColorAspects(OMX_U32 portIndex, sp<AMessage> &format) {
+    if (!mDescribeColorAspectsIndex) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    DescribeColorAspectsParams params;
+    InitOMXParams(&params);
+    params.nPortIndex = portIndex;
+    ColorAspects &aspects = params.sAspects;
+    aspects.mRange = ColorAspects::RangeUnspecified;
+    aspects.mPrimaries = ColorAspects::PrimariesUnspecified;
+    aspects.mMatrixCoeffs = ColorAspects::MatrixUnspecified;
+    aspects.mTransfer = ColorAspects::TransferUnspecified;
+
+    status_t err = mOMX->getConfig(mNode, mDescribeColorAspectsIndex, &params, sizeof(params));
+    if (err != OK) {
+        return err;
+    }
+
+    // keep non-standard codec values in extension ranges
+    int32_t range, standard, transfer;
+    if (convertCodecColorAspectsToPlatformAspects(
+            params.sAspects, &range, &standard, &transfer) != OK) {
+        ALOGW("[%s] Ignoring invalid color aspects(range=%u, primaries=%u, coeffs=%u, transfer=%u)",
+                mComponentName.c_str(),
+                aspects.mRange, aspects.mPrimaries, aspects.mMatrixCoeffs, aspects.mTransfer);
+    }
+
+    // save specified values to format
+    if (range != 0) {
+        format->setInt32("color-range", range);
+    }
+    if (standard != 0) {
+        format->setInt32("color-standard", standard);
+    }
+    if (transfer != 0) {
+        format->setInt32("color-transfer", transfer);
+    }
     return OK;
 }
 
-status_t ACodec::setupVideoEncoder(const char *mime, const sp<AMessage> &msg) {
+status_t ACodec::setupVideoEncoder(
+        const char *mime, const sp<AMessage> &msg, sp<AMessage> &outputFormat) {
     int32_t tmp;
     if (!msg->findInt32("color-format", &tmp)) {
         return INVALID_OPERATION;
@@ -3247,6 +3352,15 @@
             break;
     }
 
+    // Set up color aspects on input, but propagate them to the output format, as they will
+    // not be read back from encoder.
+    err = setColorAspects(
+            kPortIndexInput, width, height, msg, outputFormat);
+    if (err != OK) {
+        ALOGI("[%s] cannot encode color aspects. Ignoring.", mComponentName.c_str());
+        err = OK;
+    }
+
     if (err == OK) {
         ALOGI("setupVideoEncoder succeeded");
     }
@@ -4220,6 +4334,8 @@
                         break;
                     }
 
+                    (void)getColorAspects(portIndex, notify);
+
                     OMX_CONFIG_RECTTYPE rect;
                     InitOMXParams(&rect);
                     rect.nPortIndex = portIndex;
diff --git a/media/libstagefright/CodecBase.cpp b/media/libstagefright/CodecBase.cpp
index f729d4d..c9de2b0 100644
--- a/media/libstagefright/CodecBase.cpp
+++ b/media/libstagefright/CodecBase.cpp
@@ -20,6 +20,8 @@
 #include <inttypes.h>
 
 #include <media/stagefright/CodecBase.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALookup.h>
 
 namespace android {
 
@@ -35,4 +37,277 @@
 CodecBase::PortDescription::~PortDescription() {
 }
 
+/***************************************** COLOR SUPPORT *****************************************/
+
+// shortcut names for brevity in the following tables
+typedef ColorAspects CA;
+typedef CodecBase CB;
+
+ALookup<CB::ColorRange, CA::Range> sRanges{
+    {
+        { CB::kColorRangeLimited, CA::RangeLimited },
+        { CB::kColorRangeFull, CA::RangeFull },
+        { CB::kColorRangeUnspecified, CA::RangeUnspecified },
+    }
+};
+
+ALookup<CB::ColorStandard, std::pair<CA::Primaries, CA::MatrixCoeffs>> sStandards {
+    {
+        { CB::kColorStandardUnspecified,    { CA::PrimariesUnspecified, CA::MatrixUnspecified } },
+        { CB::kColorStandardBT709,          { CA::PrimariesBT709_5, CA::MatrixBT709_5 } },
+        { CB::kColorStandardBT601_625,      { CA::PrimariesBT601_6_625, CA::MatrixBT601_6 } },
+        { CB::kColorStandardBT601_625_Unadjusted,
+                                            // this is a really close match
+                                            { CA::PrimariesBT601_6_625, CA::MatrixBT709_5 } },
+        { CB::kColorStandardBT601_525,      { CA::PrimariesBT601_6_525, CA::MatrixBT601_6 } },
+        { CB::kColorStandardBT601_525_Unadjusted,
+                                            { CA::PrimariesBT601_6_525, CA::MatrixSMPTE240M } },
+        { CB::kColorStandardBT2020,         { CA::PrimariesBT2020, CA::MatrixBT2020 } },
+        { CB::kColorStandardBT2020Constant, { CA::PrimariesBT2020, CA::MatrixBT2020Constant } },
+        { CB::kColorStandardBT470M,         { CA::PrimariesBT470_6M, CA::MatrixBT470_6M } },
+        // NOTE: there is no close match to the matrix used by standard film, chose closest
+        { CB::kColorStandardFilm,           { CA::PrimariesGenericFilm, CA::MatrixBT2020 } },
+    }
+};
+
+ALookup<CB::ColorTransfer, CA::Transfer> sTransfers{
+    {
+        { CB::kColorTransferUnspecified,    CA::TransferUnspecified },
+        { CB::kColorTransferLinear,         CA::TransferLinear },
+        { CB::kColorTransferSRGB,           CA::TransferSRGB },
+        { CB::kColorTransferSMPTE_170M,     CA::TransferSMPTE170M },
+        { CB::kColorTransferGamma22,        CA::TransferGamma22 },
+        { CB::kColorTransferGamma28,        CA::TransferGamma28 },
+        { CB::kColorTransferST2084,         CA::TransferST2084 },
+        { CB::kColorTransferHLG,            CA::TransferHLG },
+    }
+};
+
+static bool isValid(ColorAspects::Primaries p) {
+    return p <= ColorAspects::PrimariesOther;
+}
+
+static bool isDefined(ColorAspects::Primaries p) {
+    return p <= ColorAspects::PrimariesBT2020;
+}
+
+static bool isValid(ColorAspects::MatrixCoeffs c) {
+    return c <= ColorAspects::MatrixOther;
+}
+
+static bool isDefined(ColorAspects::MatrixCoeffs c) {
+    return c <= ColorAspects::MatrixBT2020Constant;
+}
+
+//static
+int32_t CodecBase::wrapColorAspectsIntoColorStandard(
+        ColorAspects::Primaries primaries, ColorAspects::MatrixCoeffs coeffs) {
+    ColorStandard res;
+    if (sStandards.map(std::make_pair(primaries, coeffs), &res)) {
+        return res;
+    } else if (!isValid(primaries) || !isValid(coeffs)) {
+        return kColorStandardUnspecified;
+    }
+
+    // check platform media limits
+    uint32_t numPrimaries = ColorAspects::PrimariesBT2020 + 1;
+    if (isDefined(primaries) && isDefined(coeffs)) {
+        return kColorStandardExtendedStart + primaries + coeffs * numPrimaries;
+    } else {
+        return kColorStandardVendorStart + primaries + coeffs * 0x100;
+    }
+}
+
+//static
+status_t CodecBase::unwrapColorAspectsFromColorStandard(
+        int32_t standard,
+        ColorAspects::Primaries *primaries, ColorAspects::MatrixCoeffs *coeffs) {
+    std::pair<ColorAspects::Primaries, ColorAspects::MatrixCoeffs> res;
+    if (sStandards.map((ColorStandard)standard, &res)) {
+        *primaries = res.first;
+        *coeffs = res.second;
+        return OK;
+    }
+
+    int32_t start = kColorStandardExtendedStart;
+    int32_t numPrimaries = ColorAspects::PrimariesBT2020 + 1;
+    int32_t numCoeffs = ColorAspects::MatrixBT2020Constant + 1;
+    if (standard >= (int32_t)kColorStandardVendorStart) {
+        start = kColorStandardVendorStart;
+        numPrimaries = ColorAspects::PrimariesOther + 1; // 0x100
+        numCoeffs = ColorAspects::MatrixOther + 1; // 0x100;
+    }
+    if (standard >= start && standard < start + numPrimaries * numCoeffs) {
+        int32_t product = standard - start;
+        *primaries = (ColorAspects::Primaries)(product % numPrimaries);
+        *coeffs = (ColorAspects::MatrixCoeffs)(product / numPrimaries);
+        return OK;
+    }
+    *primaries = ColorAspects::PrimariesOther;
+    *coeffs = ColorAspects::MatrixOther;
+    return BAD_VALUE;
+}
+
+static bool isValid(ColorAspects::Range r) {
+    return r <= ColorAspects::RangeOther;
+}
+
+static bool isDefined(ColorAspects::Range r) {
+    return r <= ColorAspects::RangeLimited;
+}
+
+//  static
+int32_t CodecBase::wrapColorAspectsIntoColorRange(ColorAspects::Range range) {
+    ColorRange res;
+    if (sRanges.map(range, &res)) {
+        return res;
+    } else if (!isValid(range)) {
+        return kColorRangeUnspecified;
+    } else {
+        CHECK(!isDefined(range));
+        // all platform values are in sRanges
+        return kColorRangeVendorStart + range;
+    }
+}
+
+//static
+status_t CodecBase::unwrapColorAspectsFromColorRange(
+        int32_t range, ColorAspects::Range *aspect) {
+    if (sRanges.map((ColorRange)range, aspect)) {
+        return OK;
+    }
+
+    int32_t start = kColorRangeVendorStart;
+    int32_t numRanges = ColorAspects::RangeOther + 1; // 0x100
+    if (range >= start && range < start + numRanges) {
+        *aspect = (ColorAspects::Range)(range - start);
+        return OK;
+    }
+    *aspect = ColorAspects::RangeOther;
+    return BAD_VALUE;
+}
+
+static bool isValid(ColorAspects::Transfer t) {
+    return t <= ColorAspects::TransferOther;
+}
+
+static bool isDefined(ColorAspects::Transfer t) {
+    return t <= ColorAspects::TransferHLG
+            || (t >= ColorAspects::TransferSMPTE240M && t <= ColorAspects::TransferST428);
+}
+
+//  static
+int32_t CodecBase::wrapColorAspectsIntoColorTransfer(
+        ColorAspects::Transfer transfer) {
+    ColorTransfer res;
+    if (sTransfers.map(transfer, &res)) {
+        return res;
+    } else if (!isValid(transfer)) {
+        return kColorTransferUnspecified;
+    } else if (isDefined(transfer)) {
+        return kColorTransferExtendedStart + transfer;
+    } else {
+        // all platform values are in sRanges
+        return kColorTransferVendorStart + transfer;
+    }
+}
+
+//static
+status_t CodecBase::unwrapColorAspectsFromColorTransfer(
+        int32_t transfer, ColorAspects::Transfer *aspect) {
+    if (sTransfers.map((ColorTransfer)transfer, aspect)) {
+        return OK;
+    }
+
+    int32_t start = kColorTransferExtendedStart;
+    int32_t numTransfers = ColorAspects::TransferST428 + 1;
+    if (transfer >= (int32_t)kColorTransferVendorStart) {
+        start = kColorTransferVendorStart;
+        numTransfers = ColorAspects::TransferOther + 1; // 0x100
+    }
+    if (transfer >= start && transfer < start + numTransfers) {
+        *aspect = (ColorAspects::Transfer)(transfer - start);
+        return OK;
+    }
+    *aspect = ColorAspects::TransferOther;
+    return BAD_VALUE;
+}
+
+// static
+status_t CodecBase::convertPlatformColorAspectsToCodecAspects(
+    int32_t range, int32_t standard, int32_t transfer, ColorAspects &aspects) {
+    status_t res1 = unwrapColorAspectsFromColorRange(range, &aspects.mRange);
+    status_t res2 = unwrapColorAspectsFromColorStandard(
+            standard, &aspects.mPrimaries, &aspects.mMatrixCoeffs);
+    status_t res3 = unwrapColorAspectsFromColorTransfer(transfer, &aspects.mTransfer);
+    return res1 != OK ? res1 : (res2 != OK ? res2 : res3);
+}
+
+// static
+status_t CodecBase::convertCodecColorAspectsToPlatformAspects(
+    const ColorAspects &aspects, int32_t *range, int32_t *standard, int32_t *transfer) {
+    *range = wrapColorAspectsIntoColorRange(aspects.mRange);
+    *standard = wrapColorAspectsIntoColorStandard(aspects.mPrimaries, aspects.mMatrixCoeffs);
+    *transfer = wrapColorAspectsIntoColorTransfer(aspects.mTransfer);
+    if (isValid(aspects.mRange) && isValid(aspects.mPrimaries)
+            && isValid(aspects.mMatrixCoeffs) && isValid(aspects.mTransfer)) {
+        return OK;
+    } else {
+        return BAD_VALUE;
+    }
+}
+
+// static
+void CodecBase::setDefaultPlatformColorAspectsIfNeeded(
+        int32_t &range, int32_t &standard, int32_t &transfer,
+        int32_t width, int32_t height) {
+    if (range == CodecBase::kColorRangeUnspecified) {
+        range = CodecBase::kColorRangeLimited;
+    }
+
+    if (standard == CodecBase::kColorStandardUnspecified) {
+        // Default to BT2020, BT709 or BT601 based on size. Allow 2.35:1 aspect ratio. Limit BT601
+        // to PAL or smaller, BT2020 to 4K or larger, leaving BT709 for all resolutions in between.
+        if (width >= 3840 || height >= 3840 || width * (int64_t)height >= 3840 * 1634) {
+            standard = CodecBase::kColorStandardBT2020;
+        } else if ((width <= 720 && height > 480) || (height <= 720 && width > 480)) {
+            standard = CodecBase::kColorStandardBT601_625;
+        } else if ((width <= 720 && height <= 480) || (height <= 720 && width <= 480)) {
+            standard = CodecBase::kColorStandardBT601_525;
+        } else {
+            standard = CodecBase::kColorStandardBT709;
+        }
+    }
+
+    if (transfer == CodecBase::kColorTransferUnspecified) {
+        transfer = CodecBase::kColorTransferSMPTE_170M;
+    }
+}
+
+// static
+void CodecBase::setDefaultCodecColorAspectsIfNeeded(
+        ColorAspects &aspects, int32_t width, int32_t height) {
+    // reuse other method to get default aspects
+    int32_t range = 0, standard = 0, transfer = 0;
+    setDefaultPlatformColorAspectsIfNeeded(range, standard, transfer, width, height);
+    ColorAspects defaultAspects;
+    memset(&defaultAspects, 0, sizeof(defaultAspects));
+    convertPlatformColorAspectsToCodecAspects(range, standard, transfer, defaultAspects);
+
+    if (aspects.mRange == ColorAspects::RangeUnspecified) {
+        aspects.mRange = defaultAspects.mRange;
+    }
+    if (aspects.mPrimaries == ColorAspects::PrimariesUnspecified) {
+        aspects.mPrimaries = defaultAspects.mPrimaries;
+    }
+    if (aspects.mMatrixCoeffs == ColorAspects::MatrixUnspecified) {
+        aspects.mMatrixCoeffs = defaultAspects.mMatrixCoeffs;
+    }
+    if (aspects.mTransfer == ColorAspects::TransferUnspecified) {
+        aspects.mTransfer = defaultAspects.mTransfer;
+    }
+}
+
+/**************************************************************************************************/
+
 }  // namespace android