Merge "libstagefright: Fix HDR profile mapping" into tm-dev
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index 31840a2..6ff3dbc 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -79,6 +79,7 @@
struct C2PlatformConfig {
enum encoding_quality_level_t : uint32_t; ///< encoding quality level
+ enum tunnel_peek_mode_t: uint32_t; ///< tunnel peek mode
};
namespace {
@@ -280,6 +281,9 @@
// channel mask for decoded audio
kParamIndexAndroidChannelMask, // uint32
+
+ // allow tunnel peek behavior to be unspecified for app compatibility
+ kParamIndexTunnelPeekMode, // tunnel mode, enum
};
}
@@ -2482,6 +2486,28 @@
C2StreamTunnelStartRender;
constexpr char C2_PARAMKEY_TUNNEL_START_RENDER[] = "output.tunnel-start-render";
+/** Tunnel Peek Mode. */
+C2ENUM(C2PlatformConfig::tunnel_peek_mode_t, uint32_t,
+ UNSPECIFIED_PEEK = 0,
+ SPECIFIED_PEEK = 1
+);
+
+/**
+ * Tunnel Peek Mode Tuning parameter.
+ *
+ * If set to UNSPECIFIED_PEEK_MODE, the decoder is free to ignore the
+ * C2StreamTunnelHoldRender and C2StreamTunnelStartRender flags and associated
+ * features. Additionally, it becomes up to the decoder to display any frame
+ * before receiving synchronization information.
+ *
+ * Note: This parameter allows a decoder to ignore the video peek machinery and
+ * to revert to its preferred behavior.
+ */
+typedef C2StreamParam<C2Tuning, C2EasyEnum<C2PlatformConfig::tunnel_peek_mode_t>,
+ kParamIndexTunnelPeekMode> C2StreamTunnelPeekModeTuning;
+constexpr char C2_PARAMKEY_TUNNEL_PEEK_MODE[] =
+ "output.tunnel-peek-mode";
+
/**
* Encoding quality level signaling.
*
diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp
index c36ae94..5a652a3 100644
--- a/media/codec2/sfplugin/Android.bp
+++ b/media/codec2/sfplugin/Android.bp
@@ -43,6 +43,10 @@
"media_ndk_headers",
],
+ static_libs: [
+ "SurfaceFlingerProperties",
+ ],
+
shared_libs: [
"android.hardware.cas.native@1.0",
"android.hardware.drm@1.0",
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index 2643290..ba2b150 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -615,10 +615,30 @@
add(ConfigMapper("csd-0", C2_PARAMKEY_INIT_DATA, "value")
.limitTo(D::OUTPUT & D::READ));
- add(ConfigMapper(KEY_HDR10_PLUS_INFO, C2_PARAMKEY_INPUT_HDR10_PLUS_INFO, "value")
+ deprecated(ConfigMapper(KEY_HDR10_PLUS_INFO, C2_PARAMKEY_INPUT_HDR10_PLUS_INFO, "value")
.limitTo(D::VIDEO & D::PARAM & D::INPUT));
- add(ConfigMapper(KEY_HDR10_PLUS_INFO, C2_PARAMKEY_OUTPUT_HDR10_PLUS_INFO, "value")
+ deprecated(ConfigMapper(KEY_HDR10_PLUS_INFO, C2_PARAMKEY_OUTPUT_HDR10_PLUS_INFO, "value")
+ .limitTo(D::VIDEO & D::OUTPUT));
+
+ add(ConfigMapper(
+ std::string(C2_PARAMKEY_INPUT_HDR_DYNAMIC_INFO) + ".type",
+ C2_PARAMKEY_INPUT_HDR_DYNAMIC_INFO, "type")
+ .limitTo(D::VIDEO & D::PARAM & D::INPUT));
+
+ add(ConfigMapper(
+ std::string(C2_PARAMKEY_INPUT_HDR_DYNAMIC_INFO) + ".data",
+ C2_PARAMKEY_INPUT_HDR_DYNAMIC_INFO, "data")
+ .limitTo(D::VIDEO & D::PARAM & D::INPUT));
+
+ add(ConfigMapper(
+ std::string(C2_PARAMKEY_OUTPUT_HDR_DYNAMIC_INFO) + ".type",
+ C2_PARAMKEY_OUTPUT_HDR_DYNAMIC_INFO, "type")
+ .limitTo(D::VIDEO & D::OUTPUT));
+
+ add(ConfigMapper(
+ std::string(C2_PARAMKEY_OUTPUT_HDR_DYNAMIC_INFO) + ".data",
+ C2_PARAMKEY_OUTPUT_HDR_DYNAMIC_INFO, "data")
.limitTo(D::VIDEO & D::OUTPUT));
add(ConfigMapper(C2_PARAMKEY_TEMPORAL_LAYERING, C2_PARAMKEY_TEMPORAL_LAYERING, "")
@@ -965,6 +985,16 @@
return value == 0 ? C2_FALSE : C2_TRUE;
}));
+ add(ConfigMapper("android._tunnel-peek-set-legacy", C2_PARAMKEY_TUNNEL_PEEK_MODE, "value")
+ .limitTo(D::PARAM & D::VIDEO & D::DECODER)
+ .withMapper([](C2Value v) -> C2Value {
+ int32_t value = 0;
+ (void)v.get(&value);
+ return value == 0
+ ? C2Value(C2PlatformConfig::SPECIFIED_PEEK)
+ : C2Value(C2PlatformConfig::UNSPECIFIED_PEEK);
+ }));
+
add(ConfigMapper(KEY_VIDEO_QP_AVERAGE, C2_PARAMKEY_AVERAGE_QP, "value")
.limitTo(D::ENCODER & D::VIDEO & D::READ));
@@ -1560,6 +1590,22 @@
msg->removeEntryAt(msg->findEntryByName("cta861.max-cll"));
msg->removeEntryAt(msg->findEntryByName("cta861.max-fall"));
}
+
+ // HDR dynamic info
+ std::string keyPrefix = input ? C2_PARAMKEY_INPUT_HDR_DYNAMIC_INFO
+ : C2_PARAMKEY_OUTPUT_HDR_DYNAMIC_INFO;
+ std::string typeKey = keyPrefix + ".type";
+ std::string dataKey = keyPrefix + ".data";
+ int32_t type;
+ sp<ABuffer> data;
+ if (msg->findInt32(typeKey.c_str(), &type)
+ && msg->findBuffer(dataKey.c_str(), &data)) {
+ if (type == HDR_DYNAMIC_METADATA_TYPE_SMPTE_2094_40) {
+ msg->setBuffer(KEY_HDR10_PLUS_INFO, data);
+ msg->removeEntryAt(msg->findEntryByName(typeKey.c_str()));
+ msg->removeEntryAt(msg->findEntryByName(dataKey.c_str()));
+ }
+ }
}
ALOGV("converted to SDK values as %s", msg->debugString().c_str());
@@ -1761,6 +1807,16 @@
params->setFloat("cta861.max-fall", meta->sType1.mMaxFrameAverageLightLevel);
}
}
+
+ sp<ABuffer> hdrDynamicInfo;
+ if (params->findBuffer(KEY_HDR10_PLUS_INFO, &hdrDynamicInfo)) {
+ for (const std::string &prefix : { C2_PARAMKEY_INPUT_HDR_DYNAMIC_INFO,
+ C2_PARAMKEY_OUTPUT_HDR_DYNAMIC_INFO }) {
+ params->setInt32((prefix + ".type").c_str(),
+ HDR_DYNAMIC_METADATA_TYPE_SMPTE_2094_40);
+ params->setBuffer((prefix + ".data").c_str(), hdrDynamicInfo);
+ }
+ }
}
// this is to verify that we set proper signedness for standard parameters
diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
index 58e1c7f..3f9a40d 100644
--- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp
+++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
@@ -33,6 +33,7 @@
#include <OMX_Video.h>
#include <OMX_VideoExt.h>
#include <OMX_AsString.h>
+#include <SurfaceFlingerProperties.sysprop.h>
#include <android/hardware/media/omx/1.0/IOmx.h>
#include <android/hardware/media/omx/1.0/IOmxObserver.h>
@@ -159,6 +160,12 @@
// TODO: directly check this from the component interface
supports10Bit = (supportsHdr || supportsHdr10Plus);
+ // If the device doesn't support HDR display, then no codec on the device
+ // can advertise support for HDR profiles.
+ // Default to true to maintain backward compatibility
+ auto ret = sysprop::SurfaceFlingerProperties::has_HDR_display();
+ bool hasHDRDisplay = ret.has_value() ? *ret : true;
+
bool added = false;
for (C2Value::Primitive profile : profileQuery[0].values.values) {
@@ -184,8 +191,8 @@
if (mapper && mapper->mapProfile(pl.profile, &sdkProfile)
&& mapper->mapLevel(pl.level, &sdkLevel)) {
caps->addProfileLevel((uint32_t)sdkProfile, (uint32_t)sdkLevel);
- // also list HDR profiles if component supports HDR
- if (supportsHdr) {
+ // also list HDR profiles if component supports HDR and device has HDR display
+ if (supportsHdr && hasHDRDisplay) {
auto hdrMapper = C2Mapper::GetHdrProfileLevelMapper(trait.mediaType);
if (hdrMapper && hdrMapper->mapProfile(pl.profile, &sdkProfile)) {
caps->addProfileLevel((uint32_t)sdkProfile, (uint32_t)sdkLevel);
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 7917395..52c4c0f 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -77,6 +77,7 @@
namespace {
constexpr char TUNNEL_PEEK_KEY[] = "android._trigger-tunnel-peek";
+constexpr char TUNNEL_PEEK_SET_LEGACY_KEY[] = "android._tunnel-peek-set-legacy";
}
@@ -2483,17 +2484,39 @@
return BAD_VALUE;
}
- OMX_CONFIG_BOOLEANTYPE config;
- InitOMXParams(&config);
- config.bEnabled = (OMX_BOOL)(tunnelPeek != 0);
+ OMX_CONFIG_BOOLEANTYPE tunnelPeekConfig;
+ InitOMXParams(&tunnelPeekConfig);
+ tunnelPeekConfig.bEnabled = (OMX_BOOL)(tunnelPeek != 0);
status_t err = mOMXNode->setConfig(
(OMX_INDEXTYPE)OMX_IndexConfigAndroidTunnelPeek,
- &config, sizeof(config));
+ &tunnelPeekConfig, sizeof(tunnelPeekConfig));
if (err != OK) {
ALOGE("decoder cannot set %s to %d (err %d)",
- TUNNEL_PEEK_KEY, tunnelPeek, err);
+ TUNNEL_PEEK_KEY, tunnelPeek, err);
+ }
+ return err;
+}
+
+status_t ACodec::setTunnelPeekLegacy(int32_t isLegacy) {
+ if (mIsEncoder) {
+ ALOGE("encoder does not support %s", TUNNEL_PEEK_SET_LEGACY_KEY);
+ return BAD_VALUE;
+ }
+ if (!mTunneled) {
+ ALOGE("%s is only supported in tunnel mode", TUNNEL_PEEK_SET_LEGACY_KEY);
+ return BAD_VALUE;
}
+ OMX_CONFIG_BOOLEANTYPE tunnelPeekLegacyModeConfig;
+ InitOMXParams(&tunnelPeekLegacyModeConfig);
+ tunnelPeekLegacyModeConfig.bEnabled = (OMX_BOOL)(isLegacy != 0);
+ status_t err = mOMXNode->setConfig(
+ (OMX_INDEXTYPE)OMX_IndexConfigAndroidTunnelPeekLegacyMode,
+ &tunnelPeekLegacyModeConfig, sizeof(tunnelPeekLegacyModeConfig));
+ if (err != OK) {
+ ALOGE("decoder cannot set video peek legacy mode to %d (err %d)",
+ isLegacy, err);
+ }
return err;
}
@@ -7934,11 +7957,22 @@
}
}
- int32_t tunnelPeek = 0;
- if (params->findInt32(TUNNEL_PEEK_KEY, &tunnelPeek)) {
- status_t err = setTunnelPeek(tunnelPeek);
- if (err != OK) {
- return err;
+ {
+ int32_t tunnelPeek = 0;
+ if (params->findInt32(TUNNEL_PEEK_KEY, &tunnelPeek)) {
+ status_t err = setTunnelPeek(tunnelPeek);
+ if (err != OK) {
+ return err;
+ }
+ }
+ }
+ {
+ int32_t tunnelPeekSetLegacy = 0;
+ if (params->findInt32(TUNNEL_PEEK_SET_LEGACY_KEY, &tunnelPeekSetLegacy)) {
+ status_t err = setTunnelPeekLegacy(tunnelPeekSetLegacy);
+ if (err != OK) {
+ return err;
+ }
}
}
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 37489ea..ab1368f 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -817,7 +817,7 @@
mTunneledInputWidth(0),
mTunneledInputHeight(0),
mTunneled(false),
- mTunnelPeekState(TunnelPeekState::kEnabledNoBuffer),
+ mTunnelPeekState(TunnelPeekState::kLegacyMode),
mHaveInputSurface(false),
mHavePendingInputBuffers(false),
mCpuBoostRequested(false),
@@ -1087,6 +1087,8 @@
constexpr const char *MediaCodec::asString(TunnelPeekState state, const char *default_string){
switch(state) {
+ case TunnelPeekState::kLegacyMode:
+ return "LegacyMode";
case TunnelPeekState::kEnabledNoBuffer:
return "EnabledNoBuffer";
case TunnelPeekState::kDisabledNoBuffer:
@@ -1113,6 +1115,9 @@
TunnelPeekState previousState = mTunnelPeekState;
if(tunnelPeek == 0){
switch (mTunnelPeekState) {
+ case TunnelPeekState::kLegacyMode:
+ msg->setInt32("android._tunnel-peek-set-legacy", 0);
+ [[fallthrough]];
case TunnelPeekState::kEnabledNoBuffer:
mTunnelPeekState = TunnelPeekState::kDisabledNoBuffer;
break;
@@ -1125,6 +1130,9 @@
}
} else {
switch (mTunnelPeekState) {
+ case TunnelPeekState::kLegacyMode:
+ msg->setInt32("android._tunnel-peek-set-legacy", 0);
+ [[fallthrough]];
case TunnelPeekState::kDisabledNoBuffer:
mTunnelPeekState = TunnelPeekState::kEnabledNoBuffer;
break;
@@ -3539,10 +3547,12 @@
break;
}
TunnelPeekState previousState = mTunnelPeekState;
- mTunnelPeekState = TunnelPeekState::kBufferRendered;
- ALOGV("TunnelPeekState: %s -> %s",
- asString(previousState),
- asString(TunnelPeekState::kBufferRendered));
+ if (mTunnelPeekState != TunnelPeekState::kLegacyMode) {
+ mTunnelPeekState = TunnelPeekState::kBufferRendered;
+ ALOGV("TunnelPeekState: %s -> %s",
+ asString(previousState),
+ asString(TunnelPeekState::kBufferRendered));
+ }
updatePlaybackDuration(msg);
// check that we have a notification set
if (mOnFrameRenderedNotification != NULL) {
@@ -3959,6 +3969,14 @@
mTunneled = false;
}
+ // If mTunnelPeekState is still in kLegacyMode at this point,
+ // configure the codec in legacy mode
+ if (mTunneled && (mTunnelPeekState == TunnelPeekState::kLegacyMode)) {
+ sp<AMessage> params = new AMessage;
+ params->setInt32("android._tunnel-peek-set-legacy", 1);
+ onSetParameters(params);
+ }
+
int32_t background = 0;
if (format->findInt32("android._background-mode", &background) && background) {
androidSetThreadPriority(gettid(), ANDROID_PRIORITY_BACKGROUND);
@@ -4077,10 +4095,12 @@
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
TunnelPeekState previousState = mTunnelPeekState;
- mTunnelPeekState = TunnelPeekState::kEnabledNoBuffer;
- ALOGV("TunnelPeekState: %s -> %s",
- asString(previousState),
- asString(TunnelPeekState::kEnabledNoBuffer));
+ if (previousState != TunnelPeekState::kLegacyMode) {
+ mTunnelPeekState = TunnelPeekState::kEnabledNoBuffer;
+ ALOGV("TunnelPeekState: %s -> %s",
+ asString(previousState),
+ asString(TunnelPeekState::kEnabledNoBuffer));
+ }
mReplyID = replyID;
setState(STARTING);
@@ -4521,10 +4541,12 @@
mCodec->signalFlush();
returnBuffersToCodec();
TunnelPeekState previousState = mTunnelPeekState;
- mTunnelPeekState = TunnelPeekState::kEnabledNoBuffer;
- ALOGV("TunnelPeekState: %s -> %s",
- asString(previousState),
- asString(TunnelPeekState::kEnabledNoBuffer));
+ if (previousState != TunnelPeekState::kLegacyMode) {
+ mTunnelPeekState = TunnelPeekState::kEnabledNoBuffer;
+ ALOGV("TunnelPeekState: %s -> %s",
+ asString(previousState),
+ asString(TunnelPeekState::kEnabledNoBuffer));
+ }
break;
}
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index 6004cf8..4b4f65f 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -56,20 +56,161 @@
|| colorFormat == COLOR_Format32bitABGR2101010;
}
-bool ColorConverter::ColorSpace::isBt709() {
- return (mStandard == ColorUtils::kColorStandardBT709);
-}
-
-bool ColorConverter::ColorSpace::isBt2020() {
+bool ColorConverter::ColorSpace::isBt2020() const {
return (mStandard == ColorUtils::kColorStandardBT2020);
}
-bool ColorConverter::ColorSpace::isJpeg() {
+bool ColorConverter::ColorSpace::isH420() const {
+ return (mStandard == ColorUtils::kColorStandardBT709)
+ && (mRange == ColorUtils::kColorRangeLimited);
+}
+
+// the matrix coefficients are the same for both 601.625 and 601.525 standards
+bool ColorConverter::ColorSpace::isI420() const {
+ return ((mStandard == ColorUtils::kColorStandardBT601_625)
+ || (mStandard == ColorUtils::kColorStandardBT601_525))
+ && (mRange == ColorUtils::kColorRangeLimited);
+}
+
+bool ColorConverter::ColorSpace::isJ420() const {
return ((mStandard == ColorUtils::kColorStandardBT601_625)
|| (mStandard == ColorUtils::kColorStandardBT601_525))
&& (mRange == ColorUtils::kColorRangeFull);
}
+/**
+ * This class approximates the standard YUV to RGB conversions by factoring the matrix
+ * coefficients to 1/256th-s (as dividing by 256 is easy to do with right shift). The chosen value
+ * of 256 is somewhat arbitrary and was not dependent on the bit-depth, but it does limit the
+ * precision of the matrix coefficients (KR & KB).
+ *
+ * The maximum color error after clipping from using 256 is a distance of:
+ * 0.4 (8-bit) / 1.4 (10-bit) for greens in BT.601
+ * 0.5 (8-bit) / 1.9 (10-bit) for cyans in BT.709, and
+ * 0.3 (8-bit) / 1.3 (10-bit) for violets in BT.2020 (it is 0.4 for 10-bit BT.2020 limited)
+ *
+ * Note for reference: libyuv is using a divisor of 64 instead of 256 to ensure no overflow in
+ * 16-bit math. The maximum color error for libyuv is 3.5 / 14.
+ *
+ * The clamping is done using a lookup vector where negative indices are mapped to 0
+ * and indices > 255 are mapped to 255. (For 10-bit these are clamped to 0 to 1023)
+ *
+ * The matrices are assumed to be of the following format (note the sign on the 2nd row):
+ *
+ * [ R ] [ _y 0 _r_v ] [ Y - C16 ]
+ * [ G ] = [ _y -_g_u -_g_v ] * [ U - C128 ]
+ * [ B ] [ _y _b_u 0 ] [ V - C128 ]
+ *
+ * C16 is 1 << (bitdepth - 4) for limited range, and 0 for full range
+ * C128 is 1 << (bitdepth - 1)
+ * C255 is (1 << bitdepth) - 1
+ *
+ * The min and max values from these equations determine the clip range needed for clamping:
+ *
+ * min = - (_y * C16 + max((_g_u + _g_v) * (C255-C128), max(_r_v, _b_u) * C128)) / 256
+ * max = (_y * (C255 - C16) + max((_g_u + _g_v) * C128, max(_r_v, _b_u) * (C255-C128)) + 128) / 256
+ */
+
+struct ColorConverter::Coeffs {
+ int32_t _y;
+ int32_t _r_v;
+ int32_t _g_u;
+ int32_t _g_v;
+ int32_t _b_u;
+};
+
+/*
+
+Color conversion rules are dictated by ISO (e.g. ISO:IEC 23008:2)
+
+Limited range means Y is in [16, 235], U and V are in [16, 224] corresponding to [-0.5 to 0.5].
+
+Full range means Y is in [0, 255], U and V are in [0.5, 255.5] corresponding to [-0.5 to .5].
+
+RGB is always in full range ([0, 255])
+
+The color primaries determine the KR and KB values:
+
+
+For full range (assuming 8-bits) ISO defines:
+
+( Y ) ( KR 1-KR-KB KB )
+( ) ( ) (R)
+( ) (-KR/2 -(1-KR-KB)/2 ) ( )
+(U - 128) = (----- ------------ 0.5 ) * (G)
+( ) ((1-KB) (1-KB) ) ( )
+( ) ( ) (B)
+( ) ( -(1-KR-KB)/2 -KB/2 )
+(V - 128) ( 0.5 ------------ ----- )
+ ( (1-KR) (1-KR))
+
+(the math is rounded, 128 is (1 << (bitdepth - 1)) )
+
+From this
+
+(R) ( 1 0 2*(1-KR) ) ( Y )
+( ) ( ) ( )
+( ) ( 2*KB*(KB-1) 2*KR*(KR-1) ) ( )
+(G) = ( 1 ----------- ----------- ) * (U - 128)
+( ) ( 1-KR-KB 1-KR-KB ) ( )
+( ) ( ) ( )
+(B) ( 1 2*(1-KB) 0 ) (V - 128)
+
+For limited range, this becomes
+
+(R) ( 1 0 2*(1-KR) ) (255/219 0 0) (Y - 16)
+( ) ( ) ( ) ( )
+( ) ( 2*KB*(KB-1) 2*KR*(KR-1) ) ( ) ( )
+(G) = ( 1 ----------- ----------- ) * (0 255/224 0) * (U - 128)
+( ) ( 1-KR-KB 1-KR-KB ) ( ) ( )
+( ) ( ) ( ) ( )
+(B) ( 1 2*(1-KB) 0 ) (0 0 255/224) (V - 128)
+
+( For non-8-bit, 16 is (1 << (bitdepth - 4)), 128 is (1 << (bitdepth - 1)),
+ 255 is ((1 << bitdepth) - 1), 219 is (219 << (bitdepth - 8)) and
+ 224 is (224 << (bitdepth - 8)), so the matrix coefficients slightly change. )
+
+*/
+
+namespace {
+
+/**
+ * BT.601: K_R = 0.299; K_B = 0.114
+ *
+ * clip range 8-bit: [-277, 535], 10-bit: [-1111, 2155]
+ */
+const struct ColorConverter::Coeffs BT601_FULL = { 256, 359, 88, 183, 454 };
+const struct ColorConverter::Coeffs BT601_LIMITED = { 298, 409, 100, 208, 516 };
+const struct ColorConverter::Coeffs BT601_LTD_10BIT = { 299, 410, 101, 209, 518 };
+
+/**
+ * BT.709: K_R = 0.2126; K_B = 0.0722
+ *
+ * clip range 8-bit: [-289, 547], 10-bit: [-1159, 2202]
+ */
+const struct ColorConverter::Coeffs BT709_FULL = { 256, 403, 48, 120, 475 };
+const struct ColorConverter::Coeffs BT709_LIMITED = { 298, 459, 55, 136, 541 };
+const struct ColorConverter::Coeffs BT709_LTD_10BIT = { 290, 460, 55, 137, 542 };
+
+/**
+ * BT.2020: K_R = 0.2627; K_B = 0.0593
+ *
+ * clip range 8-bit: [-294, 552], 10-bit: [-1175, 2218]
+ *
+ * This is the largest clip range.
+ */
+const struct ColorConverter::Coeffs BT2020_FULL = { 256, 377, 42, 146, 482 };
+const struct ColorConverter::Coeffs BT2020_LIMITED = { 298, 430, 48, 167, 548 };
+const struct ColorConverter::Coeffs BT2020_LTD_10BIT = { 299, 431, 48, 167, 550 };
+
+constexpr int CLIP_RANGE_MIN_8BIT = -294;
+constexpr int CLIP_RANGE_MAX_8BIT = 552;
+
+constexpr int CLIP_RANGE_MIN_10BIT = -1175;
+constexpr int CLIP_RANGE_MAX_10BIT = 2218;
+
+}
+
ColorConverter::ColorConverter(
OMX_COLOR_FORMATTYPE from, OMX_COLOR_FORMATTYPE to)
: mSrcFormat(from),
@@ -106,7 +247,8 @@
case OMX_COLOR_FormatYUV420SemiPlanar:
#ifdef USE_LIBYUV
return mDstFormat == OMX_COLOR_Format16bitRGB565
- || mDstFormat == OMX_COLOR_Format32BitRGBA8888;
+ || mDstFormat == OMX_COLOR_Format32BitRGBA8888
+ || mDstFormat == OMX_COLOR_Format32bitBGRA8888;
#else
return mDstFormat == OMX_COLOR_Format16bitRGB565;
#endif
@@ -290,10 +432,53 @@
return err;
}
+const struct ColorConverter::Coeffs *ColorConverter::getMatrix() const {
+ const bool isFullRange = mSrcColorSpace.mRange == ColorUtils::kColorRangeFull;
+ const bool is10Bit = (mSrcFormat == COLOR_FormatYUVP010
+ || mSrcFormat == OMX_COLOR_FormatYUV420Planar16);
+
+ switch (mSrcColorSpace.mStandard) {
+ case ColorUtils::kColorStandardBT601_525:
+ case ColorUtils::kColorStandardBT601_625:
+ return (isFullRange ? &BT601_FULL :
+ is10Bit ? &BT601_LTD_10BIT : &BT601_LIMITED);
+
+ case ColorUtils::kColorStandardBT709:
+ return (isFullRange ? &BT709_FULL :
+ is10Bit ? &BT709_LTD_10BIT : &BT709_LIMITED);
+
+ case ColorUtils::kColorStandardBT2020:
+ return (isFullRange ? &BT2020_FULL :
+ is10Bit ? &BT2020_LTD_10BIT : &BT2020_LIMITED);
+
+ default:
+ // for now use the default matrices for unhandled color spaces
+ // TODO: fail?
+ // return nullptr;
+ [[fallthrough]];
+
+ case ColorUtils::kColorStandardUnspecified:
+ return is10Bit ? &BT2020_LTD_10BIT : &BT601_LIMITED;
+
+ }
+}
+
status_t ColorConverter::convertCbYCrY(
const BitmapParams &src, const BitmapParams &dst) {
// XXX Untested
+ const struct Coeffs *matrix = getMatrix();
+ if (!matrix) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ signed _b_u = matrix->_b_u;
+ signed _neg_g_u = -matrix->_g_u;
+ signed _neg_g_v = -matrix->_g_v;
+ signed _r_v = matrix->_r_v;
+ signed _y = matrix->_y;
+ signed _c16 = mSrcColorSpace.mRange == ColorUtils::kColorRangeLimited ? 16 : 0;
+
uint8_t *kAdjustedClip = initClip();
uint16_t *dst_ptr = (uint16_t *)dst.mBits
@@ -304,22 +489,22 @@
for (size_t y = 0; y < src.cropHeight(); ++y) {
for (size_t x = 0; x < src.cropWidth(); x += 2) {
- signed y1 = (signed)src_ptr[2 * x + 1] - 16;
- signed y2 = (signed)src_ptr[2 * x + 3] - 16;
+ signed y1 = (signed)src_ptr[2 * x + 1] - _c16;
+ signed y2 = (signed)src_ptr[2 * x + 3] - _c16;
signed u = (signed)src_ptr[2 * x] - 128;
signed v = (signed)src_ptr[2 * x + 2] - 128;
- signed u_b = u * 517;
- signed u_g = -u * 100;
- signed v_g = -v * 208;
- signed v_r = v * 409;
+ signed u_b = u * _b_u;
+ signed u_g = u * _neg_g_u;
+ signed v_g = v * _neg_g_v;
+ signed v_r = v * _r_v;
- signed tmp1 = y1 * 298;
+ signed tmp1 = y1 * _y + 128;
signed b1 = (tmp1 + u_b) / 256;
signed g1 = (tmp1 + v_g + u_g) / 256;
signed r1 = (tmp1 + v_r) / 256;
- signed tmp2 = y2 * 298;
+ signed tmp2 = y2 * _y + 128;
signed b2 = (tmp2 + u_b) / 256;
signed g2 = (tmp2 + v_g + u_g) / 256;
signed r2 = (tmp2 + v_r) / 256;
@@ -348,15 +533,32 @@
return OK;
}
+/*
+ libyuv supports the following color spaces:
+
+ I420: BT.601 limited range
+ J420: BT.601 full range (jpeg)
+ H420: BT.709 limited range
+
+*/
+
#define DECLARE_YUV2RGBFUNC(func, rgb) int (*func)( \
- const uint8_t*, int, const uint8_t*, int, \
- const uint8_t*, int, uint8_t*, int, int, int) \
- = mSrcColorSpace.isBt709() ? libyuv::H420To##rgb \
- : mSrcColorSpace.isJpeg() ? libyuv::J420To##rgb \
+ const uint8_t*, int, const uint8_t*, int, \
+ const uint8_t*, int, uint8_t*, int, int, int) \
+ = mSrcColorSpace.isH420() ? libyuv::H420To##rgb \
+ : mSrcColorSpace.isJ420() ? libyuv::J420To##rgb \
: libyuv::I420To##rgb
status_t ColorConverter::convertYUV420PlanarUseLibYUV(
const BitmapParams &src, const BitmapParams &dst) {
+ // Fall back to our conversion if libyuv does not support the color space.
+ // I420 (BT.601 limited) is default, so don't fall back if we end up using it anyway.
+ if (!mSrcColorSpace.isH420() && !mSrcColorSpace.isJ420()
+ // && !mSrcColorSpace.isI420() /* same as line below */
+ && getMatrix() != &BT601_LIMITED) {
+ return convertYUV420Planar(src, dst);
+ }
+
uint8_t *dst_ptr = (uint8_t *)dst.mBits
+ dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
@@ -404,6 +606,13 @@
status_t ColorConverter::convertYUV420SemiPlanarUseLibYUV(
const BitmapParams &src, const BitmapParams &dst) {
+ // Fall back to our conversion if libyuv does not support the color space.
+ // libyuv only supports BT.601 limited range NV12. Don't fall back if we end up using it anyway.
+ if (// !mSrcColorSpace.isI420() && /* same as below */
+ getMatrix() != &BT601_LIMITED) {
+ return convertYUV420SemiPlanar(src, dst);
+ }
+
uint8_t *dst_ptr = (uint8_t *)dst.mBits
+ dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp;
@@ -444,16 +653,16 @@
case OMX_COLOR_FormatYUV420Planar:
return [](void *src_y, void *src_u, void *src_v, size_t x,
signed *y1, signed *y2, signed *u, signed *v) {
- *y1 = ((uint8_t*)src_y)[x] - 16;
- *y2 = ((uint8_t*)src_y)[x + 1] - 16;
+ *y1 = ((uint8_t*)src_y)[x];
+ *y2 = ((uint8_t*)src_y)[x + 1];
*u = ((uint8_t*)src_u)[x / 2] - 128;
*v = ((uint8_t*)src_v)[x / 2] - 128;
};
case OMX_COLOR_FormatYUV420Planar16:
return [](void *src_y, void *src_u, void *src_v, size_t x,
signed *y1, signed *y2, signed *u, signed *v) {
- *y1 = (signed)(((uint16_t*)src_y)[x] >> 2) - 16;
- *y2 = (signed)(((uint16_t*)src_y)[x + 1] >> 2) - 16;
+ *y1 = (signed)(((uint16_t*)src_y)[x] >> 2);
+ *y2 = (signed)(((uint16_t*)src_y)[x + 1] >> 2);
*u = (signed)(((uint16_t*)src_u)[x / 2] >> 2) - 128;
*v = (signed)(((uint16_t*)src_v)[x / 2] >> 2) - 128;
};
@@ -463,6 +672,8 @@
return nullptr;
}
+// TRICKY: this method only supports RGBA_1010102 output for 10-bit sources, and all other outputs
+// for 8-bit sources as the type of kAdjustedClip is hardcoded based on output, not input.
std::function<void (void *, bool, signed, signed, signed, signed, signed, signed)>
getWriteToDst(OMX_COLOR_FORMATTYPE dstFormat, void *kAdjustedClip) {
switch ((int)dstFormat) {
@@ -557,6 +768,18 @@
status_t ColorConverter::convertYUV420Planar(
const BitmapParams &src, const BitmapParams &dst) {
+ const struct Coeffs *matrix = getMatrix();
+ if (!matrix) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ signed _b_u = matrix->_b_u;
+ signed _neg_g_u = -matrix->_g_u;
+ signed _neg_g_v = -matrix->_g_v;
+ signed _r_v = matrix->_r_v;
+ signed _y = matrix->_y;
+ signed _c16 = mSrcColorSpace.mRange == ColorUtils::kColorRangeLimited ? 16 : 0;
+
uint8_t *kAdjustedClip = initClip();
auto readFromSrc = getReadFromSrc(mSrcFormat);
@@ -575,38 +798,20 @@
for (size_t y = 0; y < src.cropHeight(); ++y) {
for (size_t x = 0; x < src.cropWidth(); x += 2) {
- // B = 1.164 * (Y - 16) + 2.018 * (U - 128)
- // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
- // R = 1.164 * (Y - 16) + 1.596 * (V - 128)
-
- // B = 298/256 * (Y - 16) + 517/256 * (U - 128)
- // G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128)
- // R = .................. + 409/256 * (V - 128)
-
- // min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277
- // min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172
- // min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223
-
- // max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534
- // max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432
- // max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481
-
- // clip range -278 .. 535
-
signed y1, y2, u, v;
readFromSrc(src_y, src_u, src_v, x, &y1, &y2, &u, &v);
- signed u_b = u * 517;
- signed u_g = -u * 100;
- signed v_g = -v * 208;
- signed v_r = v * 409;
+ signed u_b = u * _b_u;
+ signed u_g = u * _neg_g_u;
+ signed v_g = v * _neg_g_v;
+ signed v_r = v * _r_v;
- signed tmp1 = y1 * 298;
+ signed tmp1 = (y1 - _c16) * _y + 128;
signed b1 = (tmp1 + u_b) / 256;
signed g1 = (tmp1 + v_g + u_g) / 256;
signed r1 = (tmp1 + v_r) / 256;
- signed tmp2 = y2 * 298;
+ signed tmp2 = (y2 - _c16) * _y + 128;
signed b2 = (tmp2 + u_b) / 256;
signed g2 = (tmp2 + v_g + u_g) / 256;
signed r2 = (tmp2 + v_r) / 256;
@@ -648,6 +853,18 @@
status_t ColorConverter::convertYUVP010ToRGBA1010102(
const BitmapParams &src, const BitmapParams &dst) {
+ const struct Coeffs *matrix = getMatrix();
+ if (!matrix) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ signed _b_u = matrix->_b_u;
+ signed _neg_g_u = -matrix->_g_u;
+ signed _neg_g_v = -matrix->_g_v;
+ signed _r_v = matrix->_r_v;
+ signed _y = matrix->_y;
+ signed _c16 = mSrcColorSpace.mRange == ColorUtils::kColorRangeLimited ? 64 : 0;
+
uint16_t *kAdjustedClip10bit = initClip10Bit();
// auto readFromSrc = getReadFromSrc(mSrcFormat);
@@ -663,72 +880,28 @@
+ src.mStride * src.mHeight
+ (src.mCropTop / 2) * src.mStride + src.mCropLeft * src.mBpp);
- // BT.2020 Limited Range conversion
-
- // B = 1.168 *(Y - 64) + 2.148 *(U - 512)
- // G = 1.168 *(Y - 64) - 0.652 *(V - 512) - 0.188 *(U - 512)
- // R = 1.168 *(Y - 64) + 1.683 *(V - 512)
-
- // B = 1196/1024 *(Y - 64) + 2200/1024 *(U - 512)
- // G = .................... - 668/1024 *(V - 512) - 192/1024 *(U - 512)
- // R = .................... + 1723/1024 *(V - 512)
-
- // min_B = (1196 *(- 64) + 2200 *(- 512)) / 1024 = -1175
- // min_G = (1196 *(- 64) - 668 *(1023 - 512) - 192 *(1023 - 512)) / 1024 = -504
- // min_R = (1196 *(- 64) + 1723 *(- 512)) / 1024 = -937
-
- // max_B = (1196 *(1023 - 64) + 2200 *(1023 - 512)) / 1024 = 2218
- // max_G = (1196 *(1023 - 64) - 668 *(- 512) - 192 *(- 512)) / 1024 = 1551
- // max_R = (1196 *(1023 - 64) + 1723 *(1023 - 512)) / 1024 = 1980
-
- // clip range -1175 .. 2218
-
- // BT.709 Limited Range conversion
-
- // B = 1.164 * (Y - 64) + 2.018 * (U - 512)
- // G = 1.164 * (Y - 64) - 0.813 * (V - 512) - 0.391 * (U - 512)
- // R = 1.164 * (Y - 64) + 1.596 * (V - 512)
-
- // B = 1192/1024 * (Y - 64) + 2068/1024 * (U - 512)
- // G = .................... - 832/1024 * (V - 512) - 400/1024 * (U - 512)
- // R = .................... + 1636/1024 * (V - 512)
-
- // min_B = (1192 * (- 64) + 2068 * (- 512)) / 1024 = -1108
-
- // max_B = (1192 * (1023 - 64) + 517 * (1023 - 512)) / 1024 = 2148
-
- // clip range -1108 .. 2148
-
- signed mY = 1196, mU_B = 2200, mV_G = -668, mV_R = 1723, mU_G = -192;
- if (!mSrcColorSpace.isBt2020()) {
- mY = 1192;
- mU_B = 2068;
- mV_G = -832;
- mV_R = 1636;
- mU_G = -400;
- }
for (size_t y = 0; y < src.cropHeight(); ++y) {
for (size_t x = 0; x < src.cropWidth(); x += 2) {
signed y1, y2, u, v;
- y1 = (src_y[x] >> 6) - 64;
- y2 = (src_y[x + 1] >> 6) - 64;
+ y1 = (src_y[x] >> 6) - _c16;
+ y2 = (src_y[x + 1] >> 6) - _c16;
u = int(src_uv[x] >> 6) - 512;
v = int(src_uv[x + 1] >> 6) - 512;
- signed u_b = u * mU_B;
- signed u_g = u * mU_G;
- signed v_g = v * mV_G;
- signed v_r = v * mV_R;
+ signed u_b = u * _b_u;
+ signed u_g = u * _neg_g_u;
+ signed v_g = v * _neg_g_v;
+ signed v_r = v * _r_v;
- signed tmp1 = y1 * mY;
- signed b1 = (tmp1 + u_b) / 1024;
- signed g1 = (tmp1 + v_g + u_g) / 1024;
- signed r1 = (tmp1 + v_r) / 1024;
+ signed tmp1 = y1 * _y + 128;
+ signed b1 = (tmp1 + u_b) / 256;
+ signed g1 = (tmp1 + v_g + u_g) / 256;
+ signed r1 = (tmp1 + v_r) / 256;
- signed tmp2 = y2 * mY;
- signed b2 = (tmp2 + u_b) / 1024;
- signed g2 = (tmp2 + v_g + u_g) / 1024;
- signed r2 = (tmp2 + v_r) / 1024;
+ signed tmp2 = y2 * _y + 128;
+ signed b2 = (tmp2 + u_b) / 256;
+ signed g2 = (tmp2 + v_g + u_g) / 256;
+ signed r2 = (tmp2 + v_r) / 256;
bool uncropped = x + 1 < src.cropWidth();
@@ -949,11 +1122,6 @@
status_t ColorConverter::convertQCOMYUV420SemiPlanar(
const BitmapParams &src, const BitmapParams &dst) {
- uint8_t *kAdjustedClip = initClip();
-
- uint16_t *dst_ptr = (uint16_t *)dst.mBits
- + dst.mCropTop * dst.mWidth + dst.mCropLeft;
-
const uint8_t *src_y =
(const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
@@ -961,67 +1129,25 @@
(const uint8_t *)src_y + src.mWidth * src.mHeight
+ src.mCropTop * src.mWidth + src.mCropLeft;
- for (size_t y = 0; y < src.cropHeight(); ++y) {
- for (size_t x = 0; x < src.cropWidth(); x += 2) {
- signed y1 = (signed)src_y[x] - 16;
- signed y2 = (signed)src_y[x + 1] - 16;
+ /* QCOMYUV420SemiPlanar is NV21, while MediaCodec uses NV12 */
+ return convertYUV420SemiPlanarBase(
+ src, dst, src_y, src_u, src.mWidth /* row_inc */, true /* isNV21 */);
+}
- signed u = (signed)src_u[x & ~1] - 128;
- signed v = (signed)src_u[(x & ~1) + 1] - 128;
+status_t ColorConverter::convertTIYUV420PackedSemiPlanar(
+ const BitmapParams &src, const BitmapParams &dst) {
+ const uint8_t *src_y =
+ (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
- signed u_b = u * 517;
- signed u_g = -u * 100;
- signed v_g = -v * 208;
- signed v_r = v * 409;
+ const uint8_t *src_u =
+ (const uint8_t *)src_y + src.mWidth * (src.mHeight - src.mCropTop / 2);
- signed tmp1 = y1 * 298;
- signed b1 = (tmp1 + u_b) / 256;
- signed g1 = (tmp1 + v_g + u_g) / 256;
- signed r1 = (tmp1 + v_r) / 256;
-
- signed tmp2 = y2 * 298;
- signed b2 = (tmp2 + u_b) / 256;
- signed g2 = (tmp2 + v_g + u_g) / 256;
- signed r2 = (tmp2 + v_r) / 256;
-
- uint32_t rgb1 =
- ((kAdjustedClip[b1] >> 3) << 11)
- | ((kAdjustedClip[g1] >> 2) << 5)
- | (kAdjustedClip[r1] >> 3);
-
- uint32_t rgb2 =
- ((kAdjustedClip[b2] >> 3) << 11)
- | ((kAdjustedClip[g2] >> 2) << 5)
- | (kAdjustedClip[r2] >> 3);
-
- if (x + 1 < src.cropWidth()) {
- *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
- } else {
- dst_ptr[x] = rgb1;
- }
- }
-
- src_y += src.mWidth;
-
- if (y & 1) {
- src_u += src.mWidth;
- }
-
- dst_ptr += dst.mWidth;
- }
-
- return OK;
+ return convertYUV420SemiPlanarBase(
+ src, dst, src_y, src_u, src.mWidth /* row_inc */);
}
status_t ColorConverter::convertYUV420SemiPlanar(
const BitmapParams &src, const BitmapParams &dst) {
- // XXX Untested
-
- uint8_t *kAdjustedClip = initClip();
-
- uint16_t *dst_ptr = (uint16_t *)((uint8_t *)
- dst.mBits + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp);
-
const uint8_t *src_y =
(const uint8_t *)src.mBits + src.mCropTop * src.mStride + src.mCropLeft;
@@ -1029,90 +1155,49 @@
(const uint8_t *)src.mBits + src.mHeight * src.mStride +
(src.mCropTop / 2) * src.mStride + src.mCropLeft;
- for (size_t y = 0; y < src.cropHeight(); ++y) {
- for (size_t x = 0; x < src.cropWidth(); x += 2) {
- signed y1 = (signed)src_y[x] - 16;
- signed y2 = (signed)src_y[x + 1] - 16;
-
- signed v = (signed)src_u[x & ~1] - 128;
- signed u = (signed)src_u[(x & ~1) + 1] - 128;
-
- signed u_b = u * 517;
- signed u_g = -u * 100;
- signed v_g = -v * 208;
- signed v_r = v * 409;
-
- signed tmp1 = y1 * 298;
- signed b1 = (tmp1 + u_b) / 256;
- signed g1 = (tmp1 + v_g + u_g) / 256;
- signed r1 = (tmp1 + v_r) / 256;
-
- signed tmp2 = y2 * 298;
- signed b2 = (tmp2 + u_b) / 256;
- signed g2 = (tmp2 + v_g + u_g) / 256;
- signed r2 = (tmp2 + v_r) / 256;
-
- uint32_t rgb1 =
- ((kAdjustedClip[b1] >> 3) << 11)
- | ((kAdjustedClip[g1] >> 2) << 5)
- | (kAdjustedClip[r1] >> 3);
-
- uint32_t rgb2 =
- ((kAdjustedClip[b2] >> 3) << 11)
- | ((kAdjustedClip[g2] >> 2) << 5)
- | (kAdjustedClip[r2] >> 3);
-
- if (x + 1 < src.cropWidth()) {
- *(uint32_t *)(&dst_ptr[x]) = (rgb2 << 16) | rgb1;
- } else {
- dst_ptr[x] = rgb1;
- }
- }
-
- src_y += src.mStride;
-
- if (y & 1) {
- src_u += src.mStride;
- }
-
- dst_ptr = (uint16_t*)((uint8_t*)dst_ptr + dst.mStride);
- }
-
- return OK;
+ return convertYUV420SemiPlanarBase(
+ src, dst, src_y, src_u, src.mStride /* row_inc */);
}
-status_t ColorConverter::convertTIYUV420PackedSemiPlanar(
- const BitmapParams &src, const BitmapParams &dst) {
+status_t ColorConverter::convertYUV420SemiPlanarBase(
+ const BitmapParams &src, const BitmapParams &dst,
+ const uint8_t *src_y, const uint8_t *src_u, size_t row_inc, bool isNV21) {
+ const struct Coeffs *matrix = getMatrix();
+ if (!matrix) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ signed _b_u = matrix->_b_u;
+ signed _neg_g_u = -matrix->_g_u;
+ signed _neg_g_v = -matrix->_g_v;
+ signed _r_v = matrix->_r_v;
+ signed _y = matrix->_y;
+ signed _c16 = mSrcColorSpace.mRange == ColorUtils::kColorRangeLimited ? 16 : 0;
+
uint8_t *kAdjustedClip = initClip();
- uint16_t *dst_ptr = (uint16_t *)dst.mBits
- + dst.mCropTop * dst.mWidth + dst.mCropLeft;
-
- const uint8_t *src_y =
- (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
-
- const uint8_t *src_u =
- (const uint8_t *)src_y + src.mWidth * (src.mHeight - src.mCropTop / 2);
+ uint16_t *dst_ptr = (uint16_t *)((uint8_t *)
+ dst.mBits + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp);
for (size_t y = 0; y < src.cropHeight(); ++y) {
for (size_t x = 0; x < src.cropWidth(); x += 2) {
- signed y1 = (signed)src_y[x] - 16;
- signed y2 = (signed)src_y[x + 1] - 16;
+ signed y1 = (signed)src_y[x] - _c16;
+ signed y2 = (signed)src_y[x + 1] - _c16;
- signed u = (signed)src_u[x & ~1] - 128;
- signed v = (signed)src_u[(x & ~1) + 1] - 128;
+ signed u = (signed)src_u[(x & ~1) + isNV21] - 128;
+ signed v = (signed)src_u[(x & ~1) + !isNV21] - 128;
- signed u_b = u * 517;
- signed u_g = -u * 100;
- signed v_g = -v * 208;
- signed v_r = v * 409;
+ signed u_b = u * _b_u;
+ signed u_g = u * _neg_g_u;
+ signed v_g = v * _neg_g_v;
+ signed v_r = v * _r_v;
- signed tmp1 = y1 * 298;
+ signed tmp1 = y1 * _y + 128;
signed b1 = (tmp1 + u_b) / 256;
signed g1 = (tmp1 + v_g + u_g) / 256;
signed r1 = (tmp1 + v_r) / 256;
- signed tmp2 = y2 * 298;
+ signed tmp2 = y2 * _y + 128;
signed b2 = (tmp2 + u_b) / 256;
signed g2 = (tmp2 + v_g + u_g) / 256;
signed r2 = (tmp2 + v_r) / 256;
@@ -1134,46 +1219,40 @@
}
}
- src_y += src.mWidth;
+ src_y += row_inc;
if (y & 1) {
- src_u += src.mWidth;
+ src_u += row_inc;
}
- dst_ptr += dst.mWidth;
+ dst_ptr = (uint16_t*)((uint8_t*)dst_ptr + dst.mStride);
}
return OK;
}
uint8_t *ColorConverter::initClip() {
- static const signed kClipMin = -278;
- static const signed kClipMax = 535;
-
if (mClip == NULL) {
- mClip = new uint8_t[kClipMax - kClipMin + 1];
+ mClip = new uint8_t[CLIP_RANGE_MAX_8BIT - CLIP_RANGE_MIN_8BIT + 1];
- for (signed i = kClipMin; i <= kClipMax; ++i) {
- mClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i;
+ for (signed i = CLIP_RANGE_MIN_8BIT; i <= CLIP_RANGE_MAX_8BIT; ++i) {
+ mClip[i - CLIP_RANGE_MIN_8BIT] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i;
}
}
- return &mClip[-kClipMin];
+ return &mClip[-CLIP_RANGE_MIN_8BIT];
}
uint16_t *ColorConverter::initClip10Bit() {
- static const signed kClipMin = -1176;
- static const signed kClipMax = 2219;
-
if (mClip10Bit == NULL) {
- mClip10Bit = new uint16_t[kClipMax - kClipMin + 1];
+ mClip10Bit = new uint16_t[CLIP_RANGE_MAX_10BIT - CLIP_RANGE_MIN_10BIT + 1];
- for (signed i = kClipMin; i <= kClipMax; ++i) {
- mClip10Bit[i - kClipMin] = (i < 0) ? 0 : (i > 1023) ? 1023 : (uint16_t)i;
+ for (signed i = CLIP_RANGE_MIN_10BIT; i <= CLIP_RANGE_MAX_10BIT; ++i) {
+ mClip10Bit[i - CLIP_RANGE_MIN_10BIT] = (i < 0) ? 0 : (i > 1023) ? 1023 : (uint16_t)i;
}
}
- return &mClip10Bit[-kClipMin];
+ return &mClip10Bit[-CLIP_RANGE_MIN_10BIT];
}
} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index 632b32c..5a21755 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -522,6 +522,7 @@
status_t setLatency(uint32_t latency);
status_t getLatency(uint32_t *latency);
status_t setTunnelPeek(int32_t tunnelPeek);
+ status_t setTunnelPeekLegacy(int32_t isLegacy);
status_t setAudioPresentation(int32_t presentationId, int32_t programId);
status_t setOperatingRate(float rateFloat, bool isVideo);
status_t getIntraRefreshPeriod(uint32_t *intraRefreshPeriod);
diff --git a/media/libstagefright/include/media/stagefright/ColorConverter.h b/media/libstagefright/include/media/stagefright/ColorConverter.h
index 1d86a22..7a05f00 100644
--- a/media/libstagefright/include/media/stagefright/ColorConverter.h
+++ b/media/libstagefright/include/media/stagefright/ColorConverter.h
@@ -47,15 +47,20 @@
size_t dstCropLeft, size_t dstCropTop,
size_t dstCropRight, size_t dstCropBottom);
+ struct Coeffs; // matrix coefficients
+
private:
struct ColorSpace {
uint32_t mStandard;
uint32_t mRange;
uint32_t mTransfer;
- bool isBt709();
- bool isBt2020();
- bool isJpeg();
+ bool isBt2020() const;
+
+ // libyuv helper methods
+ bool isH420() const;
+ bool isI420() const;
+ bool isJ420() const;
};
struct BitmapParams {
@@ -84,6 +89,9 @@
uint8_t *initClip();
uint16_t *initClip10Bit();
+ // returns the YUV2RGB matrix coefficients according to the color aspects and bit depth
+ const struct Coeffs *getMatrix() const;
+
status_t convertCbYCrY(
const BitmapParams &src, const BitmapParams &dst);
@@ -111,6 +119,10 @@
status_t convertYUV420SemiPlanar(
const BitmapParams &src, const BitmapParams &dst);
+ status_t convertYUV420SemiPlanarBase(
+ const BitmapParams &src, const BitmapParams &dst,
+ const uint8_t *src_y, const uint8_t *src_u, size_t row_inc, bool isNV21 = false);
+
status_t convertTIYUV420PackedSemiPlanar(
const BitmapParams &src, const BitmapParams &dst);
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index a00a3e6..1d2d711 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -398,6 +398,7 @@
// <all states> -> EnabledNoBuffer when flush
// <all states> -> EnabledNoBuffer when stop then configure then start
enum struct TunnelPeekState {
+ kLegacyMode,
kDisabledNoBuffer,
kEnabledNoBuffer,
kDisabledQueued,
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
index 6b1a0b2..2eb2d55 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
@@ -87,7 +87,7 @@
int32_t PERF_CLASS_LEVEL =
property_get_int32("ro.odm.build.media_performance_class", 0);
-bool IS_PERF_CLASS = (PERF_CLASS_LEVEL == SDK_VERSION_S);
+bool IS_PERF_CLASS = (PERF_CLASS_LEVEL >= SDK_VERSION_S);
camera3::Size getMaxJpegResolution(const CameraMetadata &metadata,
bool ultraHighResolution) {