transcoder: fix rotation handling

Transfer rotation degree from source format to track format
for MediaSampleWriter, and setOrietationHint on the muxer.
During transcoding using encoder/decoder, clear the rotation
degree so that the codec is guaranted to operate non-rotated.

bug: 154734285
bug: 159649977
bug: 152091443

test: unit testing (updated test with rotation)

Change-Id: I7e829ef777b1954d894411e8591a5e31ff36e275
diff --git a/media/libmediatranscoding/tests/assets/desk_hevc_1920x1080_aac_48KHz_rot90.mp4 b/media/libmediatranscoding/tests/assets/desk_hevc_1920x1080_aac_48KHz_rot90.mp4
new file mode 100644
index 0000000..df42a15
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/desk_hevc_1920x1080_aac_48KHz_rot90.mp4
Binary files differ
diff --git a/media/libmediatranscoding/tests/assets/push_assets.sh b/media/libmediatranscoding/tests/assets/push_assets.sh
index 2fcb233..8c85e58 100755
--- a/media/libmediatranscoding/tests/assets/push_assets.sh
+++ b/media/libmediatranscoding/tests/assets/push_assets.sh
@@ -21,5 +21,5 @@
 
 #TODO(hkuang): Check if the destination folder already exists. If so, skip the copying.
 echo "Copying files to device"
-adb push $ANDROID_BUILD_TOP/frameworks/av/media/libmediatranscoding/tests/assets /data/local/tmp/TranscodingTestAssets
+adb push $ANDROID_BUILD_TOP/frameworks/av/media/libmediatranscoding/tests/assets/* /data/local/tmp/TranscodingTestAssets
 echo "Copy done"
diff --git a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
index aaa2adb..0fdb0b7 100644
--- a/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
+++ b/media/libmediatranscoding/transcoder/MediaSampleWriter.cpp
@@ -26,7 +26,19 @@
 class DefaultMuxer : public MediaSampleWriterMuxerInterface {
 public:
     // MediaSampleWriterMuxerInterface
-    ssize_t addTrack(const AMediaFormat* trackFormat) override {
+    ssize_t addTrack(AMediaFormat* trackFormat) override {
+        // If the track format has rotation, need to call AMediaMuxer_setOrientationHint
+        // to set the rotation. Muxer doesn't take rotation specified on the track.
+        const char* mime;
+        if (AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime) &&
+            strncmp(mime, "video/", 6) == 0) {
+            int32_t rotation;
+            if (AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_ROTATION, &rotation) &&
+                (rotation != 0)) {
+                AMediaMuxer_setOrientationHint(mMuxer, rotation);
+            }
+        }
+
         return AMediaMuxer_addTrack(mMuxer, trackFormat);
     }
     media_status_t start() override { return AMediaMuxer_start(mMuxer); }
@@ -213,4 +225,4 @@
 
     return AMEDIA_OK;
 }
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
index 96e2e61..4df3296 100644
--- a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
@@ -139,6 +139,10 @@
     }
     AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, kColorFormatSurface);
 
+    // Always encode without rotation. The rotation degree will be transferred directly to
+    // MediaSampleWriter track format, and MediaSampleWriter will call AMediaMuxer_setOrientationHint.
+    AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_ROTATION, 0);
+
     mDestinationFormat = std::shared_ptr<AMediaFormat>(encoderFormat, &AMediaFormat_delete);
 
     // Create and configure the encoder.
@@ -339,25 +343,35 @@
     // at container level. This is supposed to override any SAR settings in the bitstream,
     // thus should always be transferred to the container of the transcoded file.
     int32_t sarWidth, sarHeight;
-    if (AMediaFormat_getInt32(mDestinationFormat.get(), AMEDIAFORMAT_KEY_SAR_WIDTH, &sarWidth) &&
+    if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_SAR_WIDTH, &sarWidth) &&
         (sarWidth > 0) &&
-        AMediaFormat_getInt32(mDestinationFormat.get(), AMEDIAFORMAT_KEY_SAR_HEIGHT, &sarHeight) &&
+        AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_SAR_HEIGHT, &sarHeight) &&
         (sarHeight > 0)) {
         AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_WIDTH, sarWidth);
         AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_SAR_HEIGHT, sarHeight);
     }
     // Transfer DAR settings.
     int32_t displayWidth, displayHeight;
-    if (AMediaFormat_getInt32(mDestinationFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_WIDTH,
-                              &displayWidth) &&
+    if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_WIDTH, &displayWidth) &&
         (displayWidth > 0) &&
-        AMediaFormat_getInt32(mDestinationFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_HEIGHT,
+        AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_DISPLAY_HEIGHT,
                               &displayHeight) &&
         (displayHeight > 0)) {
         AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_WIDTH, displayWidth);
         AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_DISPLAY_HEIGHT, displayHeight);
     }
 
+    // Transfer rotation settings.
+    // Note that muxer itself doesn't take rotation from the track format. It requires
+    // AMediaMuxer_setOrientationHint to set the rotation. Here we pass the rotation to
+    // MediaSampleWriter using the track format. MediaSampleWriter will then call
+    // AMediaMuxer_setOrientationHint as needed.
+    int32_t rotation;
+    if (AMediaFormat_getInt32(mSourceFormat.get(), AMEDIAFORMAT_KEY_ROTATION, &rotation) &&
+        (rotation != 0)) {
+        AMediaFormat_setInt32(formatCopy, AMEDIAFORMAT_KEY_ROTATION, rotation);
+    }
+
     // TODO: transfer other fields as required.
 
     mActualOutputFormat = std::shared_ptr<AMediaFormat>(formatCopy, &AMediaFormat_delete);
diff --git a/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h b/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
index 4d0264b..da83167 100644
--- a/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
+++ b/media/libmediatranscoding/transcoder/include/media/MediaSampleWriter.h
@@ -41,7 +41,7 @@
      * @param trackFormat Format of the new track.
      * @return A non-negative track index on success, or a negative number on failure.
      */
-    virtual ssize_t addTrack(const AMediaFormat* trackFormat) = 0;
+    virtual ssize_t addTrack(AMediaFormat* trackFormat) = 0;
 
     /** Starts the muxer. */
     virtual media_status_t start() = 0;
diff --git a/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
index 9ffb32b..98813e7 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaSampleWriterTests.cpp
@@ -58,7 +58,7 @@
 class TestMuxer : public MediaSampleWriterMuxerInterface {
 public:
     // MuxerInterface
-    ssize_t addTrack(const AMediaFormat* trackFormat) override {
+    ssize_t addTrack(AMediaFormat* trackFormat) override {
         mEventQueue.push_back(AddTrack(trackFormat));
         return mTrackCount++;
     }
diff --git a/media/libmediatranscoding/transcoder/tests/MediaTrackTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaTrackTranscoderTests.cpp
index 4d9386a..71d3a4e 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaTrackTranscoderTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaTrackTranscoderTests.cpp
@@ -67,7 +67,7 @@
 
     void initSampleReader() {
         const char* sourcePath =
-                "/data/local/tmp/TranscoderTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+                "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
 
         const int sourceFd = open(sourcePath, O_RDONLY);
         ASSERT_GT(sourceFd, 0);
diff --git a/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
index 6bf6562..67f1ca1 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
@@ -40,7 +40,7 @@
 
 struct FormatVerifierEntry {
     const char* key;
-    bool (*equal)(const char* key, AMediaFormat* src, AMediaFormat* dst);
+    std::function<bool(const char*, AMediaFormat*, AMediaFormat*)> equal;
 };
 
 static const FormatVerifierEntry kFieldsToPreserve[] = {
@@ -92,11 +92,6 @@
     bool mFinished = false;
 };
 
-static const char* SOURCE_PATH_AVC =
-        "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
-static const char* SOURCE_PATH_HEVC =
-        "/data/local/tmp/TranscodingTestAssets/jets_hevc_1280x720_20Mbps.mp4";
-
 // Write-only, create file if non-existent, don't overwrite existing file.
 static constexpr int kOpenFlags = O_WRONLY | O_CREAT | O_EXCL;
 // User R+W permission.
@@ -161,6 +156,44 @@
         return mCallbacks->mStatus;
     }
 
+    void testTranscodeVideo(const char* srcPath, const char* destPath, const char* dstMime) {
+        const int32_t kBitRate = 8 * 1000 * 1000;  // 8Mbs
+
+        EXPECT_EQ(
+                transcodeHelper(
+                        srcPath, destPath,
+                        [dstMime](AMediaFormat* sourceFormat) {
+                            AMediaFormat* format = nullptr;
+                            const char* mime = nullptr;
+                            AMediaFormat_getString(sourceFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+
+                            if (strncmp(mime, "video/", 6) == 0) {
+                                format = AMediaFormat_new();
+                                AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, kBitRate);
+
+                                if (dstMime != nullptr) {
+                                    AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, dstMime);
+                                }
+                            }
+                            return format;
+                        }),
+                AMEDIA_OK);
+
+        if (dstMime != nullptr) {
+            std::vector<FormatVerifierEntry> extraVerifiers = {
+                    {AMEDIAFORMAT_KEY_MIME,
+                     [dstMime](const char* key, AMediaFormat* src __unused, AMediaFormat* dst) {
+                         const char* mime = nullptr;
+                         AMediaFormat_getString(dst, key, &mime);
+                         return !strcmp(mime, dstMime);
+                     }},
+            };
+            verifyOutputFormat(destPath, &extraVerifiers);
+        } else {
+            verifyOutputFormat(destPath);
+        }
+    }
+
     void verifyOutputFormat(const char* destPath,
                             const std::vector<FormatVerifierEntry>* extraVerifiers = nullptr) {
         int dstFd = open(destPath, O_RDONLY);
@@ -194,7 +227,8 @@
 
         for (int i = 0; i < (sizeof(kFieldsToPreserve) / sizeof(kFieldsToPreserve[0])); ++i) {
             EXPECT_TRUE(kFieldsToPreserve[i].equal(kFieldsToPreserve[i].key,
-                                                   mSourceVideoFormat.get(), videoFormat.get()));
+                                                   mSourceVideoFormat.get(), videoFormat.get()))
+                    << "Failed at key " << kFieldsToPreserve[i].key;
         }
 
         if (extraVerifiers != nullptr) {
@@ -212,76 +246,31 @@
 };
 
 TEST_F(MediaTranscoderTests, TestPassthrough) {
+    const char* srcPath = "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
     const char* destPath = "/data/local/tmp/MediaTranscoder_Passthrough.MP4";
 
-    EXPECT_EQ(transcodeHelper(SOURCE_PATH_AVC, destPath, [](AMediaFormat*) { return nullptr; }),
-              AMEDIA_OK);
+    EXPECT_EQ(transcodeHelper(srcPath, destPath, [](AMediaFormat*) { return nullptr; }), AMEDIA_OK);
 
     verifyOutputFormat(destPath);
 }
 
-TEST_F(MediaTranscoderTests, TestBasicVideoTranscodeAvcToAvc) {
-    const char* destPath = "/data/local/tmp/MediaTranscoder_VideoTranscodeAvcToAvc.MP4";
-    const int32_t kBitRate = 8 * 1000 * 1000;  // 8Mbs
-
-    EXPECT_EQ(transcodeHelper(
-                      SOURCE_PATH_AVC, destPath,
-                      [](AMediaFormat* sourceFormat) {
-                          AMediaFormat* format = nullptr;
-                          const char* mime = nullptr;
-                          AMediaFormat_getString(sourceFormat, AMEDIAFORMAT_KEY_MIME, &mime);
-
-                          if (strncmp(mime, "video/", 6) == 0) {
-                              format = AMediaFormat_new();
-                              AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, kBitRate);
-                          }
-                          return format;
-                      }),
-              AMEDIA_OK);
-
-    std::vector<FormatVerifierEntry> extraVerifiers = {
-            {AMEDIAFORMAT_KEY_MIME,
-             [](const char* key, AMediaFormat* src __unused, AMediaFormat* dst) {
-                 const char* mime = nullptr;
-                 AMediaFormat_getString(dst, key, &mime);
-                 return !strcmp(mime, AMEDIA_MIMETYPE_VIDEO_AVC);
-             }},
-    };
-
-    verifyOutputFormat(destPath, &extraVerifiers);
+TEST_F(MediaTranscoderTests, TestVideoTranscode_AvcToAvc_Basic) {
+    const char* srcPath = "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
+    const char* destPath = "/data/local/tmp/MediaTranscoder_VideoTranscode_AvcToAvc_Basic.MP4";
+    testTranscodeVideo(srcPath, destPath, nullptr /*dstMime*/);
 }
 
-TEST_F(MediaTranscoderTests, TestBasicVideoTranscodeHevcToAvc) {
-    const char* destPath = "/data/local/tmp/MediaTranscoder_VideoTranscodeHevcToAvc.MP4";
+TEST_F(MediaTranscoderTests, TestVideoTranscode_HevcToAvc_Basic) {
+    const char* srcPath = "/data/local/tmp/TranscodingTestAssets/jets_hevc_1280x720_20Mbps.mp4";
+    const char* destPath = "/data/local/tmp/MediaTranscoder_VideoTranscode_HevcToAvc_Basic.MP4";
+    testTranscodeVideo(srcPath, destPath, AMEDIA_MIMETYPE_VIDEO_AVC);
+}
 
-    EXPECT_EQ(transcodeHelper(
-                      SOURCE_PATH_HEVC, destPath,
-                      [](AMediaFormat* sourceFormat) {
-                          AMediaFormat* format = nullptr;
-                          const char* mime = nullptr;
-                          AMediaFormat_getString(sourceFormat, AMEDIAFORMAT_KEY_MIME, &mime);
-
-                          if (strncmp(mime, "video/", 6) == 0) {
-                              const int32_t kBitRate = 8 * 1000 * 1000;  // 8Mbs
-                              format = AMediaFormat_new();
-                              AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, kBitRate);
-                              AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME,
-                                                     AMEDIA_MIMETYPE_VIDEO_AVC);
-                          }
-                          return format;
-                      }),
-              AMEDIA_OK);
-
-    std::vector<FormatVerifierEntry> extraVerifiers = {
-            {AMEDIAFORMAT_KEY_MIME,
-             [](const char* key, AMediaFormat* src __unused, AMediaFormat* dst) {
-                 const char* mime = nullptr;
-                 AMediaFormat_getString(dst, key, &mime);
-                 return !strcmp(mime, AMEDIA_MIMETYPE_VIDEO_AVC);
-             }},
-    };
-
-    verifyOutputFormat(destPath, &extraVerifiers);
+TEST_F(MediaTranscoderTests, TestVideoTranscode_HevcToAvc_Rotation) {
+    const char* srcPath =
+            "/data/local/tmp/TranscodingTestAssets/desk_hevc_1920x1080_aac_48KHz_rot90.mp4";
+    const char* destPath = "/data/local/tmp/MediaTranscoder_VideoTranscode_HevcToAvc_Rotation.MP4";
+    testTranscodeVideo(srcPath, destPath, AMEDIA_MIMETYPE_VIDEO_AVC);
 }
 
 }  // namespace android