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