Transcoder: Allow setting operating rate and priority on both encoder and decoder

Plumb through operating rate and priority to both codecs when
configuring the video track transcoder.

Fixes: 169719271
Test: Unit test, manually verifying the settings and benchmarking.
Change-Id: I229eaaf0ae0d5002e2eec52eb83a6bb92694b1d2
diff --git a/media/libmediatranscoding/transcoder/MediaTranscoder.cpp b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
index e850d66..5868ffd 100644
--- a/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/MediaTranscoder.cpp
@@ -23,24 +23,13 @@
 #include <media/MediaSampleReaderNDK.h>
 #include <media/MediaSampleWriter.h>
 #include <media/MediaTranscoder.h>
+#include <media/NdkCommon.h>
 #include <media/PassthroughTrackTranscoder.h>
 #include <media/VideoTrackTranscoder.h>
 #include <unistd.h>
 
 namespace android {
 
-#define DEFINE_FORMAT_VALUE_COPY_FUNC(_type, _typeName)                                  \
-    static void copy##_typeName(const char* key, AMediaFormat* to, AMediaFormat* from) { \
-        _type value;                                                                     \
-        if (AMediaFormat_get##_typeName(from, key, &value)) {                            \
-            AMediaFormat_set##_typeName(to, key, value);                                 \
-        }                                                                                \
-    }
-
-DEFINE_FORMAT_VALUE_COPY_FUNC(const char*, String);
-DEFINE_FORMAT_VALUE_COPY_FUNC(int64_t, Int64);
-DEFINE_FORMAT_VALUE_COPY_FUNC(int32_t, Int32);
-
 static AMediaFormat* mergeMediaFormats(AMediaFormat* base, AMediaFormat* overlay) {
     if (base == nullptr || overlay == nullptr) {
         LOG(ERROR) << "Cannot merge null formats";
@@ -58,29 +47,26 @@
     // along with their value types and copy the ones that are present. A better solution would be
     // to either implement required functions in NDK or to parse the overlay format's string
     // representation and copy all existing keys.
-    static const struct {
-        const char* key;
-        void (*copyValue)(const char* key, AMediaFormat* to, AMediaFormat* from);
-    } kSupportedConfigs[] = {
-            {AMEDIAFORMAT_KEY_MIME, copyString},
-            {AMEDIAFORMAT_KEY_DURATION, copyInt64},
-            {AMEDIAFORMAT_KEY_WIDTH, copyInt32},
-            {AMEDIAFORMAT_KEY_HEIGHT, copyInt32},
-            {AMEDIAFORMAT_KEY_BIT_RATE, copyInt32},
-            {AMEDIAFORMAT_KEY_PROFILE, copyInt32},
-            {AMEDIAFORMAT_KEY_LEVEL, copyInt32},
-            {AMEDIAFORMAT_KEY_COLOR_FORMAT, copyInt32},
-            {AMEDIAFORMAT_KEY_COLOR_RANGE, copyInt32},
-            {AMEDIAFORMAT_KEY_COLOR_STANDARD, copyInt32},
-            {AMEDIAFORMAT_KEY_COLOR_TRANSFER, copyInt32},
-            {AMEDIAFORMAT_KEY_FRAME_RATE, copyInt32},
-            {AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, copyInt32},
+    static const AMediaFormatUtils::EntryCopier kSupportedFormatEntries[] = {
+            ENTRY_COPIER(AMEDIAFORMAT_KEY_MIME, String),
+            ENTRY_COPIER(AMEDIAFORMAT_KEY_DURATION, Int64),
+            ENTRY_COPIER(AMEDIAFORMAT_KEY_WIDTH, Int32),
+            ENTRY_COPIER(AMEDIAFORMAT_KEY_HEIGHT, Int32),
+            ENTRY_COPIER(AMEDIAFORMAT_KEY_BIT_RATE, Int32),
+            ENTRY_COPIER(AMEDIAFORMAT_KEY_PROFILE, Int32),
+            ENTRY_COPIER(AMEDIAFORMAT_KEY_LEVEL, Int32),
+            ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_FORMAT, Int32),
+            ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_RANGE, Int32),
+            ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_STANDARD, Int32),
+            ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_TRANSFER, Int32),
+            ENTRY_COPIER(AMEDIAFORMAT_KEY_FRAME_RATE, Int32),
+            ENTRY_COPIER(AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, Int32),
+            ENTRY_COPIER(AMEDIAFORMAT_KEY_PRIORITY, Int32),
+            ENTRY_COPIER2(AMEDIAFORMAT_KEY_OPERATING_RATE, Float, Int32),
     };
+    const size_t entryCount = sizeof(kSupportedFormatEntries) / sizeof(kSupportedFormatEntries[0]);
 
-    for (int i = 0; i < (sizeof(kSupportedConfigs) / sizeof(kSupportedConfigs[0])); ++i) {
-        kSupportedConfigs[i].copyValue(kSupportedConfigs[i].key, format, overlay);
-    }
-
+    AMediaFormatUtils::CopyFormatEntries(overlay, format, kSupportedFormatEntries, entryCount);
     return format;
 }
 
diff --git a/media/libmediatranscoding/transcoder/NdkCommon.cpp b/media/libmediatranscoding/transcoder/NdkCommon.cpp
index d8cf1c8..e58330f 100644
--- a/media/libmediatranscoding/transcoder/NdkCommon.cpp
+++ b/media/libmediatranscoding/transcoder/NdkCommon.cpp
@@ -16,7 +16,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "NdkCommon"
 
-#include <log/log.h>
+#include <android-base/logging.h>
 #include <media/NdkCommon.h>
 
 #include <cstdio>
@@ -39,3 +39,38 @@
 const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
 const char* TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate";
 const char* TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES = "max-bframes";
+
+namespace AMediaFormatUtils {
+
+#define DEFINE_FORMAT_VALUE_COPY_FUNC(_type, _typeName)                                      \
+    bool CopyFormatEntry##_typeName(const char* key, AMediaFormat* from, AMediaFormat* to) { \
+        _type value;                                                                         \
+        if (AMediaFormat_get##_typeName(from, key, &value)) {                                \
+            AMediaFormat_set##_typeName(to, key, value);                                     \
+            return true;                                                                     \
+        }                                                                                    \
+        return false;                                                                        \
+    }
+
+DEFINE_FORMAT_VALUE_COPY_FUNC(const char*, String);
+DEFINE_FORMAT_VALUE_COPY_FUNC(int64_t, Int64);
+DEFINE_FORMAT_VALUE_COPY_FUNC(int32_t, Int32);
+DEFINE_FORMAT_VALUE_COPY_FUNC(float, Float);
+
+void CopyFormatEntries(AMediaFormat* from, AMediaFormat* to, const EntryCopier* entries,
+                       size_t entryCount) {
+    if (from == nullptr || to == nullptr) {
+        LOG(ERROR) << "Cannot copy null formats";
+        return;
+    } else if (entries == nullptr || entryCount < 1) {
+        LOG(WARNING) << "No entries to copy";
+        return;
+    }
+
+    for (size_t i = 0; i < entryCount; ++i) {
+        if (!entries[i].copy(entries[i].key, from, to) && entries[i].copy2 != nullptr) {
+            entries[i].copy2(entries[i].key, from, to);
+        }
+    }
+}
+}  // namespace AMediaFormatUtils
\ No newline at end of file
diff --git a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
index 5579868..0713eb8 100644
--- a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
@@ -270,6 +270,15 @@
     // Prevent decoder from overwriting frames that the encoder has not yet consumed.
     AMediaFormat_setInt32(decoderFormat.get(), TBD_AMEDIACODEC_PARAMETER_KEY_ALLOW_FRAME_DROP, 0);
 
+    // Copy over configurations that apply to both encoder and decoder.
+    static const AMediaFormatUtils::EntryCopier kEncoderEntriesToCopy[] = {
+            ENTRY_COPIER2(AMEDIAFORMAT_KEY_OPERATING_RATE, Float, Int32),
+            ENTRY_COPIER(AMEDIAFORMAT_KEY_PRIORITY, Int32),
+    };
+    const size_t entryCount = sizeof(kEncoderEntriesToCopy) / sizeof(kEncoderEntriesToCopy[0]);
+    AMediaFormatUtils::CopyFormatEntries(mDestinationFormat.get(), decoderFormat.get(),
+                                         kEncoderEntriesToCopy, entryCount);
+
     status = AMediaCodec_configure(mDecoder, decoderFormat.get(), mSurface, NULL /* crypto */,
                                    0 /* flags */);
     if (status != AMEDIA_OK) {
diff --git a/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp b/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp
index ccd9353..f190453 100644
--- a/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp
+++ b/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp
@@ -76,11 +76,27 @@
     bool mFinished = false;
 };
 
-static void TranscodeMediaFile(benchmark::State& state, const std::string& srcFileName,
-                               const std::string& dstFileName, bool includeAudio,
-                               bool transcodeVideo = true) {
+static AMediaFormat* CreateDefaultVideoFormat() {
     // Default bitrate
     static constexpr int32_t kVideoBitRate = 20 * 1000 * 1000;  // 20Mbs
+
+    AMediaFormat* videoFormat = AMediaFormat_new();
+    AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_BIT_RATE, kVideoBitRate);
+    return videoFormat;
+}
+
+/**
+ * Callback to configure tracks for transcoding.
+ * @param mime The source track mime type.
+ * @param dstFormat The destination format if the track should be transcoded or nullptr if the track
+ * should be passed through.
+ * @return True if the track should be included in the output file.
+ */
+using TrackSelectionCallback = std::function<bool(const char* mime, AMediaFormat** dstFormat)>;
+
+static void TranscodeMediaFile(benchmark::State& state, const std::string& srcFileName,
+                               const std::string& dstFileName,
+                               TrackSelectionCallback trackSelectionCallback) {
     // Write-only, create file if non-existent.
     static constexpr int kDstOpenFlags = O_WRONLY | O_CREAT;
     // User R+W permission.
@@ -133,21 +149,17 @@
             }
 
             if (strncmp(mime, "video/", 6) == 0) {
-                if (transcodeVideo) {
-                    dstFormat = AMediaFormat_new();
-                    AMediaFormat_setInt32(dstFormat, AMEDIAFORMAT_KEY_BIT_RATE, kVideoBitRate);
-                }
-
                 int32_t frameCount;
                 if (AMediaFormat_getInt32(srcFormat, AMEDIAFORMAT_KEY_FRAME_COUNT, &frameCount)) {
                     state.counters["VideoFrameRate"] =
                             benchmark::Counter(frameCount, benchmark::Counter::kIsRate);
                 }
-            } else if (!includeAudio && strncmp(mime, "audio/", 6) == 0) {
-                continue;
             }
 
-            status = transcoder->configureTrackFormat(i, dstFormat);
+            if (trackSelectionCallback(mime, &dstFormat)) {
+                status = transcoder->configureTrackFormat(i, dstFormat);
+            }
+
             if (dstFormat != nullptr) {
                 AMediaFormat_delete(dstFormat);
             }
@@ -179,6 +191,21 @@
     if (dstFd > 0) close(dstFd);
 }
 
+static void TranscodeMediaFile(benchmark::State& state, const std::string& srcFileName,
+                               const std::string& dstFileName, bool includeAudio,
+                               bool transcodeVideo) {
+    TranscodeMediaFile(state, srcFileName, dstFileName,
+                       [=](const char* mime, AMediaFormat** dstFormatOut) -> bool {
+                           *dstFormatOut = nullptr;
+                           if (strncmp(mime, "video/", 6) == 0 && transcodeVideo) {
+                               *dstFormatOut = CreateDefaultVideoFormat();
+                           } else if (strncmp(mime, "audio/", 6) == 0 && !includeAudio) {
+                               return false;
+                           }
+                           return true;
+                       });
+}
+
 // Benchmark registration wrapper for transcoding.
 #define TRANSCODER_BENCHMARK(func) \
     BENCHMARK(func)->UseRealTime()->MeasureProcessCPUTime()->Unit(benchmark::kMillisecond)
@@ -186,19 +213,51 @@
 static void BM_TranscodeAvc2AvcAudioVideo2AudioVideo(benchmark::State& state) {
     TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
                        "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_transcoded_AV.mp4",
-                       true /* includeAudio */);
+                       true /* includeAudio */, true /* transcodeVideo */);
 }
 
 static void BM_TranscodeAvc2AvcAudioVideo2Video(benchmark::State& state) {
     TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
                        "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_transcoded_V.mp4",
-                       false /* includeAudio */);
+                       false /* includeAudio */, true /* transcodeVideo */);
 }
 
 static void BM_TranscodeAvc2AvcVideo2Video(benchmark::State& state) {
     TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
                        "video_1920x1080_3648frame_h264_22Mbps_30fps_transcoded_V.mp4",
-                       false /* includeAudio */);
+                       false /* includeAudio */, true /* transcodeVideo */);
+}
+
+static void BM_TranscodeAvc2AvcAV2AVMaxOperatingRate(benchmark::State& state) {
+    TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps_aac.mp4",
+                       "video_1920x1080_3648frame_h264_22Mbps_30fps_aac_transcoded_AV.mp4",
+                       [](const char* mime, AMediaFormat** dstFormatOut) -> bool {
+                           AMediaFormat* dstFormat = nullptr;
+                           if (strncmp(mime, "video/", 6) == 0) {
+                               dstFormat = CreateDefaultVideoFormat();
+                               AMediaFormat_setFloat(dstFormat, AMEDIAFORMAT_KEY_OPERATING_RATE,
+                                                     INT32_MAX);
+                               AMediaFormat_setInt32(dstFormat, AMEDIAFORMAT_KEY_PRIORITY, 1);
+                           }
+                           *dstFormatOut = dstFormat;
+                           return true;
+                       });
+}
+
+static void BM_TranscodeAvc2AvcV2VMaxOperatingRate(benchmark::State& state) {
+    TranscodeMediaFile(state, "video_1920x1080_3648frame_h264_22Mbps_30fps.mp4",
+                       "video_1920x1080_3648frame_h264_22Mbps_30fps_transcoded_V.mp4",
+                       [](const char* mime, AMediaFormat** dstFormatOut) -> bool {
+                           if (strncmp(mime, "video/", 6) == 0) {
+                               AMediaFormat* dstFormat = CreateDefaultVideoFormat();
+                               AMediaFormat_setFloat(dstFormat, AMEDIAFORMAT_KEY_OPERATING_RATE,
+                                                     INT32_MAX);
+                               AMediaFormat_setInt32(dstFormat, AMEDIAFORMAT_KEY_PRIORITY, 1);
+                               *dstFormatOut = dstFormat;
+                               return true;
+                           }
+                           return false;
+                       });
 }
 
 static void BM_TranscodeAudioVideoPassthrough(benchmark::State& state) {
@@ -215,6 +274,8 @@
 TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAudioVideo2AudioVideo);
 TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAudioVideo2Video);
 TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcVideo2Video);
+TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcAV2AVMaxOperatingRate);
+TRANSCODER_BENCHMARK(BM_TranscodeAvc2AvcV2VMaxOperatingRate);
 TRANSCODER_BENCHMARK(BM_TranscodeAudioVideoPassthrough);
 TRANSCODER_BENCHMARK(BM_TranscodeVideoPassthrough);
 
diff --git a/media/libmediatranscoding/transcoder/include/media/NdkCommon.h b/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
index 044d024..5e24049 100644
--- a/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
+++ b/media/libmediatranscoding/transcoder/include/media/NdkCommon.h
@@ -54,4 +54,29 @@
 
 static constexpr int kBitrateModeConstant = 2;
 
+namespace AMediaFormatUtils {
+
+typedef struct {
+    const char* key;
+    bool (*copy)(const char* key, AMediaFormat* from, AMediaFormat* to);
+    bool (*copy2)(const char* key, AMediaFormat* from, AMediaFormat* to);
+} EntryCopier;
+
+#define ENTRY_COPIER(keyName, typeName) \
+    { keyName, AMediaFormatUtils::CopyFormatEntry##typeName, nullptr }
+#define ENTRY_COPIER2(keyName, typeName, typeName2)            \
+    {                                                          \
+        keyName, AMediaFormatUtils::CopyFormatEntry##typeName, \
+                AMediaFormatUtils::CopyFormatEntry##typeName2  \
+    }
+
+bool CopyFormatEntryString(const char* key, AMediaFormat* from, AMediaFormat* to);
+bool CopyFormatEntryInt64(const char* key, AMediaFormat* from, AMediaFormat* to);
+bool CopyFormatEntryInt32(const char* key, AMediaFormat* from, AMediaFormat* to);
+bool CopyFormatEntryFloat(const char* key, AMediaFormat* from, AMediaFormat* to);
+
+void CopyFormatEntries(AMediaFormat* from, AMediaFormat* to, const EntryCopier* entries,
+                       size_t entryCount);
+
+}  // namespace AMediaFormatUtils
 #endif  // ANDROID_MEDIA_TRANSCODING_NDK_COMMON_H