Transcoder: Adopt MediaCodec HDR->SDR conversion
Request MediaCodec to convert all HDR content to SDR.
Return an error if the conversion is not supported.
Bug: 178106748
Test: Unit tests
Change-Id: I2fe79170398d46ddc4fa6d4c81472a13046d5f47
diff --git a/media/libmediatranscoding/transcoder/MediaTranscoder.cpp b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
index 3d4ff15..d58d88d 100644
--- a/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
@@ -234,7 +234,6 @@
lseek(fd, 0, SEEK_SET);
mSampleReader = MediaSampleReaderNDK::createFromFd(fd, 0 /* offset */, fileSize);
-
if (mSampleReader == nullptr) {
LOG(ERROR) << "Unable to parse source fd: " << fd;
return AMEDIA_ERROR_UNSUPPORTED;
diff --git a/media/libmediatranscoding/transcoder/NdkCommon.cpp b/media/libmediatranscoding/transcoder/NdkCommon.cpp
index a7b79dc..f5c9594 100644
--- a/media/libmediatranscoding/transcoder/NdkCommon.cpp
+++ b/media/libmediatranscoding/transcoder/NdkCommon.cpp
@@ -40,6 +40,9 @@
const char* TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate";
const char* TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES = "max-bframes";
+/* TODO(lnilsson): Finalize value or adopt AMediaFormat key once available. */
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_COLOR_TRANSFER_REQUEST = "color-transfer-request";
+
namespace AMediaFormatUtils {
#define DEFINE_FORMAT_VALUE_COPY_FUNC(_type, _typeName) \
diff --git a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
index e3c0b05..b5b4a9d 100644
--- a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
@@ -51,6 +51,32 @@
// Default frame rate.
static constexpr int32_t kDefaultFrameRate = 30;
+// Determines whether a track format describes HDR video content or not. The
+// logic is based on isHdr() in libstagefright/Utils.cpp.
+static bool isHdr(AMediaFormat* format) {
+ // If VUI signals HDR content, this internal flag is set by the extractor.
+ int32_t isHdr;
+ if (AMediaFormat_getInt32(format, "android._is-hdr", &isHdr)) {
+ return isHdr;
+ }
+
+ // If container supplied HDR static info without transfer set, assume HDR.
+ const char* hdrInfo;
+ int32_t transfer;
+ if ((AMediaFormat_getString(format, AMEDIAFORMAT_KEY_HDR_STATIC_INFO, &hdrInfo) ||
+ AMediaFormat_getString(format, "hdr10-plus-info", &hdrInfo)) &&
+ !AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_TRANSFER, &transfer)) {
+ return true;
+ }
+
+ // Otherwise, check if an HDR transfer function is set.
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_TRANSFER, &transfer)) {
+ return transfer == COLOR_TRANSFER_ST2084 || transfer == COLOR_TRANSFER_HLG;
+ }
+
+ return false;
+}
+
template <typename T>
void VideoTrackTranscoder::BlockingQueue<T>::push(T const& value, bool front) {
{
@@ -260,8 +286,8 @@
return AMEDIA_ERROR_INVALID_PARAMETER;
}
- // TODO: replace __ANDROID_API_FUTURE__with 31 when it's official (b/178144708)
- #define __TRANSCODING_MIN_API__ __ANDROID_API_FUTURE__
+// TODO: replace __ANDROID_API_FUTURE__with 31 when it's official (b/178144708)
+#define __TRANSCODING_MIN_API__ __ANDROID_API_FUTURE__
AMediaCodec* encoder;
if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
@@ -315,6 +341,14 @@
return AMEDIA_ERROR_INVALID_PARAMETER;
}
+ // Request decoder to convert HDR content to SDR.
+ const bool sourceIsHdr = isHdr(mSourceFormat.get());
+ if (sourceIsHdr) {
+ AMediaFormat_setInt32(decoderFormat.get(),
+ TBD_AMEDIACODEC_PARAMETER_KEY_COLOR_TRANSFER_REQUEST,
+ COLOR_TRANSFER_SDR_VIDEO);
+ }
+
// Prevent decoder from overwriting frames that the encoder has not yet consumed.
AMediaFormat_setInt32(decoderFormat.get(), TBD_AMEDIACODEC_PARAMETER_KEY_ALLOW_FRAME_DROP, 0);
@@ -335,6 +369,25 @@
return status;
}
+ if (sourceIsHdr) {
+ bool supported = false;
+ AMediaFormat* inputFormat = AMediaCodec_getInputFormat(mDecoder);
+
+ if (inputFormat != nullptr) {
+ int32_t transferFunc;
+ supported = AMediaFormat_getInt32(inputFormat,
+ TBD_AMEDIACODEC_PARAMETER_KEY_COLOR_TRANSFER_REQUEST,
+ &transferFunc) &&
+ transferFunc == COLOR_TRANSFER_SDR_VIDEO;
+ AMediaFormat_delete(inputFormat);
+ }
+
+ if (!supported) {
+ LOG(ERROR) << "HDR to SDR conversion unsupported by the codec";
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+ }
+
// Configure codecs to run in async mode.
AMediaCodecOnAsyncNotifyCallback asyncCodecCallbacks = {
.onAsyncInputAvailable = AsyncCodecCallbackDispatch::onAsyncInputAvailable,
diff --git a/media/libmediatranscoding/transcoder/include/media/NdkCommon.h b/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
index 1a72be3..cc3399a 100644
--- a/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
+++ b/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
@@ -45,11 +45,18 @@
static constexpr int COLOR_FormatYUV420Flexible = 0x7F420888;
static constexpr int COLOR_FormatSurface = 0x7f000789;
+// Color transfer functions defined by MediaCodecConstants.h but not in NDK
+static constexpr int32_t COLOR_TRANSFER_HLG = 7;
+static constexpr int32_t COLOR_TRANSFER_LINEAR = 1;
+static constexpr int32_t COLOR_TRANSFER_SDR_VIDEO = 3;
+static constexpr int32_t COLOR_TRANSFER_ST2084 = 6;
+
// constants not defined in NDK
extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_ALLOW_FRAME_DROP;
extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME;
extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE;
extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES;
+extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_COLOR_TRANSFER_REQUEST;
static constexpr int TBD_AMEDIACODEC_BUFFER_FLAG_KEY_FRAME = 0x1;
static constexpr int kBitrateModeConstant = 2;
diff --git a/media/libmediatranscoding/transcoder/tests/build_and_run_all_unit_tests.sh b/media/libmediatranscoding/transcoder/tests/build_and_run_all_unit_tests.sh
index 792c541..bba2bc5 100755
--- a/media/libmediatranscoding/transcoder/tests/build_and_run_all_unit_tests.sh
+++ b/media/libmediatranscoding/transcoder/tests/build_and_run_all_unit_tests.sh
@@ -1,8 +1,11 @@
#!/bin/bash
+
+# Exit on compilation error.
+set -e
+
#
# Run tests in this directory.
#
-
if [ "$SYNC_FINISHED" != true ]; then
if [ -z "$ANDROID_BUILD_TOP" ]; then
echo "Android build environment not set"
@@ -24,6 +27,9 @@
echo "========================================"
+# Don't exit if a test fails.
+set +e
+
echo "testing MediaSampleReaderNDK"
adb shell ASAN_OPTIONS=detect_container_overflow=0 /data/nativetest64/MediaSampleReaderNDKTests/MediaSampleReaderNDKTests