Merge "Transcoder: Add HDR -> SDR unit test using sample plugin." into sc-dev
diff --git a/media/libmediatranscoding/tests/assets/TranscodingTestAssets/video_1280x720_hevc_hdr10_static_3mbps.mp4 b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/video_1280x720_hevc_hdr10_static_3mbps.mp4
new file mode 100644
index 0000000..17150d4
--- /dev/null
+++ b/media/libmediatranscoding/tests/assets/TranscodingTestAssets/video_1280x720_hevc_hdr10_static_3mbps.mp4
Binary files differ
diff --git a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
index acf5f6c..ab08d73 100644
--- a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
@@ -145,12 +145,12 @@
         VideoTrackTranscoder::CodecWrapper* wrapper =
                 static_cast<VideoTrackTranscoder::CodecWrapper*>(userdata);
         if (auto transcoder = wrapper->getTranscoder()) {
-            const char* kCodecName = (codec == transcoder->mDecoder ? "Decoder" : "Encoder");
+            const bool isDecoder = codec == transcoder->mDecoder;
+            const char* kCodecName = (isDecoder ? "Decoder" : "Encoder");
             LOG(DEBUG) << kCodecName << " format changed: " << AMediaFormat_toString(format);
-            if (codec == transcoder->mEncoder->getCodec()) {
-                transcoder->mCodecMessageQueue.push(
-                        [transcoder, format] { transcoder->updateTrackFormat(format); });
-            }
+            transcoder->mCodecMessageQueue.push([transcoder, format, isDecoder] {
+                transcoder->updateTrackFormat(format, isDecoder);
+            });
         }
     }
 
@@ -507,7 +507,25 @@
     }
 }
 
-void VideoTrackTranscoder::updateTrackFormat(AMediaFormat* outputFormat) {
+void VideoTrackTranscoder::updateTrackFormat(AMediaFormat* outputFormat, bool fromDecoder) {
+    if (fromDecoder) {
+        static const AMediaFormatUtils::EntryCopier kValuesToCopy[] = {
+                ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_RANGE, Int32),
+                ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_STANDARD, Int32),
+                ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_TRANSFER, Int32),
+        };
+        AMediaFormat* params = AMediaFormat_new();
+        if (params != nullptr) {
+            AMediaFormatUtils::CopyFormatEntries(outputFormat, params, kValuesToCopy,
+                                                 std::size(kValuesToCopy));
+            if (AMediaCodec_setParameters(mEncoder->getCodec(), params) != AMEDIA_OK) {
+                LOG(WARNING) << "Unable to update encoder with color information";
+            }
+            AMediaFormat_delete(params);
+        }
+        return;
+    }
+
     if (mActualOutputFormat != nullptr) {
         LOG(WARNING) << "Ignoring duplicate format change.";
         return;
@@ -571,6 +589,7 @@
     // TODO: transfer other fields as required.
 
     mActualOutputFormat = std::shared_ptr<AMediaFormat>(formatCopy, &AMediaFormat_delete);
+    LOG(DEBUG) << "Actual output format: " << AMediaFormat_toString(formatCopy);
 
     notifyTrackFormatAvailable();
 }
diff --git a/media/libmediatranscoding/transcoder/include/media/VideoTrackTranscoder.h b/media/libmediatranscoding/transcoder/include/media/VideoTrackTranscoder.h
index 4413a6c..8a506a0 100644
--- a/media/libmediatranscoding/transcoder/include/media/VideoTrackTranscoder.h
+++ b/media/libmediatranscoding/transcoder/include/media/VideoTrackTranscoder.h
@@ -83,8 +83,8 @@
     // Dequeues an encoded buffer from the encoder and adds it to the output queue.
     void dequeueOutputSample(int32_t bufferIndex, AMediaCodecBufferInfo bufferInfo);
 
-    // Updates the video track's actual format based on encoder output format.
-    void updateTrackFormat(AMediaFormat* outputFormat);
+    // Updates the video track's actual format based on encoder and decoder output format.
+    void updateTrackFormat(AMediaFormat* outputFormat, bool fromDecoder);
 
     AMediaCodec* mDecoder = nullptr;
     std::shared_ptr<CodecWrapper> mEncoder;
diff --git a/media/libmediatranscoding/transcoder/setloglevel.sh b/media/libmediatranscoding/transcoder/setloglevel.sh
index 5eb7b67..b0f0a2e 100755
--- a/media/libmediatranscoding/transcoder/setloglevel.sh
+++ b/media/libmediatranscoding/transcoder/setloglevel.sh
@@ -2,19 +2,29 @@
 
 if [ $# -ne 1 ]
 then
-    echo Usage: $0 loglevel
+    echo "Usage 1: $0 <loglevel>"
+    echo "  Set all transcoder log tags to <loglevel>"
+    echo "Usage 2: $0 -l"
+    echo "  List all transcoder log tags and exit"
     exit 1
 fi
 
-level=$1
-echo Setting transcoder log level to $level
-
 # List all log tags
 declare -a tags=(
   MediaTranscoder MediaTrackTranscoder VideoTrackTranscoder PassthroughTrackTranscoder
   MediaSampleWriter MediaSampleReader MediaSampleQueue MediaTranscoderTests
   MediaTrackTranscoderTests VideoTrackTranscoderTests PassthroughTrackTranscoderTests
-  MediaSampleWriterTests MediaSampleReaderNDKTests MediaSampleQueueTests)
+  MediaSampleWriterTests MediaSampleReaderNDKTests MediaSampleQueueTests HdrTranscodeTests)
+
+if [ "$1" == "-l" ]; then
+  echo "Transcoder log tags:"
+  for tag in "${tags[@]}"; do echo -n "$tag "; done
+  echo
+  exit 0
+fi
+
+level=$1
+echo Setting transcoder log level to $level
 
 # Set log level for all tags
 for tag in "${tags[@]}"
diff --git a/media/libmediatranscoding/transcoder/tests/Android.bp b/media/libmediatranscoding/transcoder/tests/Android.bp
index d68c967..11b19c9 100644
--- a/media/libmediatranscoding/transcoder/tests/Android.bp
+++ b/media/libmediatranscoding/transcoder/tests/Android.bp
@@ -89,6 +89,13 @@
     srcs: ["MediaSampleWriterTests.cpp"],
 }
 
+// HDR Transcode unit test
+cc_test {
+    name: "HdrTranscodeTests",
+    defaults: ["testdefaults"],
+    srcs: ["HdrTranscodeTests.cpp"],
+}
+
 // MediaTranscoder unit test
 cc_test {
     name: "MediaTranscoderTests",
diff --git a/media/libmediatranscoding/transcoder/tests/HdrTranscodeTests.cpp b/media/libmediatranscoding/transcoder/tests/HdrTranscodeTests.cpp
new file mode 100644
index 0000000..3a2882b
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/HdrTranscodeTests.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Unit Test for HDR to SDR transcoding.
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "HdrTranscodeTests"
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/binder_process.h>
+#include <fcntl.h>
+#include <gtest/gtest.h>
+#include <media/MediaSampleReaderNDK.h>
+#include <media/MediaTranscoder.h>
+#include <media/NdkCommon.h>
+
+#include "TranscoderTestUtils.h"
+
+namespace android {
+
+// Debug property to load the sample HDR plugin.
+static const std::string kLoadSamplePluginProperty{"debug.codec2.force-sample-plugin"};
+
+// SDR color standard, from MediaFormat.
+static constexpr int COLOR_STANDARD_BT709 = 1;
+
+class HdrTranscodeTests : public ::testing::Test {
+public:
+    HdrTranscodeTests() { LOG(DEBUG) << "HdrTranscodeTests created"; }
+    ~HdrTranscodeTests() { LOG(DEBUG) << "HdrTranscodeTests destroyed"; }
+
+    void SetUp() override {
+        LOG(DEBUG) << "HdrTranscodeTests set up";
+        mCallbacks = std::make_shared<TestTranscoderCallbacks>();
+        ABinderProcess_startThreadPool();
+    }
+
+    void TearDown() override {
+        LOG(DEBUG) << "HdrTranscodeTests tear down";
+        mCallbacks.reset();
+    }
+
+    media_status_t transcode(const char* srcFile, const char* dstFile, const char* dstMime) {
+        std::string srcPath = mSrcDir + srcFile;
+        std::string dstPath = mDstDir + dstFile;
+
+        auto transcoder = MediaTranscoder::create(mCallbacks, -1 /*heartBeatIntervalUs*/);
+        EXPECT_NE(transcoder, nullptr);
+
+        const int srcFd = open(srcPath.c_str(), O_RDONLY);
+        EXPECT_EQ(transcoder->configureSource(srcFd), AMEDIA_OK);
+        close(srcFd);
+
+        std::vector<std::shared_ptr<AMediaFormat>> trackFormats = transcoder->getTrackFormats();
+        EXPECT_GT(trackFormats.size(), 0);
+
+        for (int i = 0; i < trackFormats.size(); ++i) {
+            std::shared_ptr<AMediaFormat> format;
+            const char* mime = nullptr;
+
+            AMediaFormat_getString(trackFormats[i].get(), AMEDIAFORMAT_KEY_MIME, &mime);
+            if (strncmp(mime, "video/", 6) == 0) {
+                format = std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
+                AMediaFormat_setString(format.get(), AMEDIAFORMAT_KEY_MIME, dstMime);
+            }
+
+            media_status_t status = transcoder->configureTrackFormat(i, format.get());
+            if (status != AMEDIA_OK) {
+                return status;
+            }
+        }
+
+        const int dstFd = open(dstPath.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+        EXPECT_EQ(transcoder->configureDestination(dstFd), AMEDIA_OK);
+        close(dstFd);
+
+        media_status_t startStatus = transcoder->start();
+        EXPECT_EQ(startStatus, AMEDIA_OK);
+        if (startStatus != AMEDIA_OK) {
+            return startStatus;
+        }
+
+        mCallbacks->waitForTranscodingFinished();
+        return mCallbacks->mStatus;
+    }
+
+    media_status_t validateOutput(const char* dstFile __unused) {
+        std::string path = mDstDir + dstFile;
+
+        auto format = TranscoderTestUtils::GetVideoFormat(path);
+        EXPECT_NE(format.get(), nullptr);
+
+        int32_t value;
+        EXPECT_TRUE(AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_COLOR_STANDARD, &value));
+        EXPECT_EQ(value, COLOR_STANDARD_BT709);
+
+        EXPECT_TRUE(AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_COLOR_TRANSFER, &value));
+        EXPECT_EQ(value, COLOR_TRANSFER_SDR_VIDEO);
+
+        // TODO(lnilsson): Validate decoded pixels as well. Either by comparing similarity against a
+        //  known good "golden master" corresponding SDR video, or by looking at the histogram.
+        return AMEDIA_OK;
+    }
+
+    bool hdrToSdrConversionSupported(const char* hdrFile) {
+        std::string srcPath = mSrcDir + hdrFile;
+
+        std::string mime;
+        auto format = TranscoderTestUtils::GetVideoFormat(srcPath, &mime);
+        EXPECT_NE(format.get(), nullptr);
+
+        AMediaCodec* decoder = AMediaCodec_createDecoderByType(mime.c_str());
+        EXPECT_NE(decoder, nullptr);
+
+        AMediaFormat_setInt32(format.get(), TBD_AMEDIACODEC_PARAMETER_KEY_COLOR_TRANSFER_REQUEST,
+                              COLOR_TRANSFER_SDR_VIDEO);
+
+        EXPECT_EQ(AMediaCodec_configure(decoder, format.get(), nullptr /*surface*/,
+                                        nullptr /*crypto*/, 0 /*flags*/),
+                  AMEDIA_OK);
+
+        AMediaFormat* inputFormat = AMediaCodec_getInputFormat(decoder);
+        EXPECT_NE(inputFormat, nullptr);
+
+        int32_t transferFunc;
+        bool conversionSupported =
+                AMediaFormat_getInt32(inputFormat,
+                                      TBD_AMEDIACODEC_PARAMETER_KEY_COLOR_TRANSFER_REQUEST,
+                                      &transferFunc) &&
+                transferFunc == COLOR_TRANSFER_SDR_VIDEO;
+
+        AMediaFormat_delete(inputFormat);
+        AMediaCodec_delete(decoder);
+
+        return conversionSupported;
+    }
+
+    std::shared_ptr<TestTranscoderCallbacks> mCallbacks;
+    const std::string mSrcDir{"/data/local/tmp/TranscodingTestAssets/"};
+    const std::string mDstDir{"/data/local/tmp/"};
+};
+
+TEST_F(HdrTranscodeTests, TestHdrSamplePluginTranscode) {
+    const char* hdrFile = "video_1280x720_hevc_hdr10_static_3mbps.mp4";
+    const char* dstFile = "video_1280x720_hevc_hdr10_static_3mbps_transcoded.mp4";
+
+    EXPECT_TRUE(android::base::SetProperty(kLoadSamplePluginProperty, "true"));
+
+    if (hdrToSdrConversionSupported(hdrFile)) {
+        LOG(INFO) << "HDR -> SDR supported, validating output..";
+        EXPECT_EQ(transcode(hdrFile, dstFile, AMEDIA_MIMETYPE_VIDEO_AVC), AMEDIA_OK);
+        EXPECT_EQ(validateOutput(dstFile), AMEDIA_OK);
+    } else {
+        LOG(INFO) << "HDR -> SDR *not* supported";
+        EXPECT_EQ(transcode(hdrFile, dstFile, AMEDIA_MIMETYPE_VIDEO_AVC), AMEDIA_ERROR_UNSUPPORTED);
+    }
+
+    EXPECT_TRUE(android::base::SetProperty(kLoadSamplePluginProperty, "false"));
+}
+}  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/media/libmediatranscoding/transcoder/tests/MediaTrackTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaTrackTranscoderTests.cpp
index 21f0b86..791e983 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaTrackTranscoderTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaTrackTranscoderTests.cpp
@@ -28,7 +28,7 @@
 #include <media/PassthroughTrackTranscoder.h>
 #include <media/VideoTrackTranscoder.h>
 
-#include "TrackTranscoderTestUtils.h"
+#include "TranscoderTestUtils.h"
 
 namespace android {
 
@@ -49,7 +49,7 @@
         // (b/155663561).
         ABinderProcess_startThreadPool();
 
-        mCallback = std::make_shared<TestCallback>();
+        mCallback = std::make_shared<TestTrackTranscoderCallback>();
 
         switch (GetParam()) {
         case VIDEO:
@@ -134,7 +134,7 @@
 
 protected:
     std::shared_ptr<MediaTrackTranscoder> mTranscoder;
-    std::shared_ptr<TestCallback> mCallback;
+    std::shared_ptr<TestTrackTranscoderCallback> mCallback;
 
     std::shared_ptr<MediaSampleReader> mMediaSampleReader;
     int mTrackIndex;
diff --git a/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
index 4e33ec3..b7c7bd8 100644
--- a/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/MediaTranscoderTests.cpp
@@ -27,6 +27,8 @@
 #include <media/MediaTranscoder.h>
 #include <media/NdkCommon.h>
 
+#include "TranscoderTestUtils.h"
+
 namespace android {
 
 #define DEFINE_FORMAT_VALUE_EQUAL_FUNC(_type, _typeName)                                  \
@@ -53,66 +55,6 @@
         {AMEDIAFORMAT_KEY_SAR_HEIGHT, equalInt32},     {AMEDIAFORMAT_KEY_ROTATION, equalInt32},
 };
 
-class TestCallbacks : public MediaTranscoder::CallbackInterface {
-public:
-    virtual void onFinished(const MediaTranscoder* transcoder __unused) override {
-        std::unique_lock<std::mutex> lock(mMutex);
-        EXPECT_FALSE(mFinished);
-        mFinished = true;
-        mCondition.notify_all();
-    }
-
-    virtual void onError(const MediaTranscoder* transcoder __unused,
-                         media_status_t error) override {
-        std::unique_lock<std::mutex> lock(mMutex);
-        EXPECT_NE(error, AMEDIA_OK);
-        EXPECT_FALSE(mFinished);
-        mFinished = true;
-        mStatus = error;
-        mCondition.notify_all();
-    }
-
-    virtual void onProgressUpdate(const MediaTranscoder* transcoder __unused,
-                                  int32_t progress) override {
-        std::unique_lock<std::mutex> lock(mMutex);
-        if (progress > 0 && !mProgressMade) {
-            mProgressMade = true;
-            mCondition.notify_all();
-        }
-    }
-
-    virtual void onHeartBeat(const MediaTranscoder* transcoder __unused) override {
-        std::unique_lock<std::mutex> lock(mMutex);
-        mHeartBeatCount++;
-    }
-
-    virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
-                                     const std::shared_ptr<ndk::ScopedAParcel>& pausedState
-                                             __unused) override {}
-
-    void waitForTranscodingFinished() {
-        std::unique_lock<std::mutex> lock(mMutex);
-        while (!mFinished) {
-            mCondition.wait(lock);
-        }
-    }
-
-    void waitForProgressMade() {
-        std::unique_lock<std::mutex> lock(mMutex);
-        while (!mProgressMade && !mFinished) {
-            mCondition.wait(lock);
-        }
-    }
-    media_status_t mStatus = AMEDIA_OK;
-    bool mFinished = false;
-    int32_t mHeartBeatCount = 0;
-
-private:
-    std::mutex mMutex;
-    std::condition_variable mCondition;
-    bool mProgressMade = false;
-};
-
 // 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.
@@ -125,7 +67,7 @@
 
     void SetUp() override {
         LOG(DEBUG) << "MediaTranscoderTests set up";
-        mCallbacks = std::make_shared<TestCallbacks>();
+        mCallbacks = std::make_shared<TestTranscoderCallbacks>();
         ABinderProcess_startThreadPool();
     }
 
@@ -325,7 +267,7 @@
         close(dstFd);
     }
 
-    std::shared_ptr<TestCallbacks> mCallbacks;
+    std::shared_ptr<TestTranscoderCallbacks> mCallbacks;
     std::shared_ptr<AMediaFormat> mSourceVideoFormat;
 };
 
@@ -380,7 +322,7 @@
     const char* destPath1 = "/data/local/tmp/MediaTranscoder_CustomBitrate_2Mbps.MP4";
     const char* destPath2 = "/data/local/tmp/MediaTranscoder_CustomBitrate_8Mbps.MP4";
     testTranscodeVideo(srcPath, destPath1, AMEDIA_MIMETYPE_VIDEO_AVC, 2 * 1000 * 1000);
-    mCallbacks = std::make_shared<TestCallbacks>();
+    mCallbacks = std::make_shared<TestTranscoderCallbacks>();
     testTranscodeVideo(srcPath, destPath2, AMEDIA_MIMETYPE_VIDEO_AVC, 8 * 1000 * 1000);
 
     // The source asset is very short and heavily compressed from the beginning so don't expect the
@@ -410,7 +352,7 @@
         EXPECT_EQ(transcodeHelper(srcPath, destPath, getAVCVideoFormat, kCancelAfterProgress),
                   AMEDIA_OK);
         EXPECT_FALSE(mCallbacks->mFinished);
-        mCallbacks = std::make_shared<TestCallbacks>();
+        mCallbacks = std::make_shared<TestTranscoderCallbacks>();
     }
 }
 
@@ -422,7 +364,7 @@
         EXPECT_EQ(transcodeHelper(srcPath, destPath, getAVCVideoFormat, kCancelAfterStart),
                   AMEDIA_OK);
         EXPECT_FALSE(mCallbacks->mFinished);
-        mCallbacks = std::make_shared<TestCallbacks>();
+        mCallbacks = std::make_shared<TestTranscoderCallbacks>();
     }
 }
 
@@ -434,7 +376,7 @@
         EXPECT_EQ(transcodeHelper(srcPath, destPath, getAVCVideoFormat, kPauseAfterProgress),
                   AMEDIA_OK);
         EXPECT_FALSE(mCallbacks->mFinished);
-        mCallbacks = std::make_shared<TestCallbacks>();
+        mCallbacks = std::make_shared<TestTranscoderCallbacks>();
     }
 }
 
@@ -446,7 +388,7 @@
         EXPECT_EQ(transcodeHelper(srcPath, destPath, getAVCVideoFormat, kPauseAfterStart),
                   AMEDIA_OK);
         EXPECT_FALSE(mCallbacks->mFinished);
-        mCallbacks = std::make_shared<TestCallbacks>();
+        mCallbacks = std::make_shared<TestTranscoderCallbacks>();
     }
 }
 
diff --git a/media/libmediatranscoding/transcoder/tests/PassthroughTrackTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/PassthroughTrackTranscoderTests.cpp
index 5071efd..fdbf535 100644
--- a/media/libmediatranscoding/transcoder/tests/PassthroughTrackTranscoderTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/PassthroughTrackTranscoderTests.cpp
@@ -29,7 +29,7 @@
 
 #include <vector>
 
-#include "TrackTranscoderTestUtils.h"
+#include "TranscoderTestUtils.h"
 
 namespace android {
 
@@ -152,7 +152,7 @@
     }
 
     // Create and start the transcoder.
-    std::shared_ptr<TestCallback> callback = std::make_shared<TestCallback>();
+    auto callback = std::make_shared<TestTrackTranscoderCallback>();
     PassthroughTrackTranscoder transcoder{callback};
 
     std::shared_ptr<MediaSampleReader> mediaSampleReader =
diff --git a/media/libmediatranscoding/transcoder/tests/TrackTranscoderTestUtils.h b/media/libmediatranscoding/transcoder/tests/TrackTranscoderTestUtils.h
deleted file mode 100644
index a782f71..0000000
--- a/media/libmediatranscoding/transcoder/tests/TrackTranscoderTestUtils.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <media/MediaTrackTranscoder.h>
-#include <media/MediaTrackTranscoderCallback.h>
-
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-
-namespace android {
-
-//
-// This file contains test utilities used by more than one track transcoder test.
-//
-
-class TrackTranscoderTestUtils {
-public:
-    static std::shared_ptr<AMediaFormat> getDefaultVideoDestinationFormat(
-            AMediaFormat* sourceFormat, bool includeBitrate = true) {
-        // Default video destination format setup.
-        static constexpr float kFrameRate = 30.0f;
-        static constexpr int32_t kBitRate = 2 * 1000 * 1000;
-
-        AMediaFormat* destinationFormat = AMediaFormat_new();
-        AMediaFormat_copy(destinationFormat, sourceFormat);
-        AMediaFormat_setFloat(destinationFormat, AMEDIAFORMAT_KEY_FRAME_RATE, kFrameRate);
-        if (includeBitrate) {
-            AMediaFormat_setInt32(destinationFormat, AMEDIAFORMAT_KEY_BIT_RATE, kBitRate);
-        }
-
-        return std::shared_ptr<AMediaFormat>(destinationFormat, &AMediaFormat_delete);
-    }
-};
-
-class TestCallback : public MediaTrackTranscoderCallback {
-public:
-    TestCallback() = default;
-    ~TestCallback() = default;
-
-    // MediaTrackTranscoderCallback
-    void onTrackFormatAvailable(const MediaTrackTranscoder* transcoder __unused) {
-        std::unique_lock<std::mutex> lock(mMutex);
-        mTrackFormatAvailable = true;
-        mTrackFormatAvailableCondition.notify_all();
-    }
-
-    void onTrackFinished(const MediaTrackTranscoder* transcoder __unused) {
-        std::unique_lock<std::mutex> lock(mMutex);
-        mTranscodingFinished = true;
-        mTranscodingFinishedCondition.notify_all();
-    }
-
-    virtual void onTrackStopped(const MediaTrackTranscoder* transcoder __unused) override {
-        std::unique_lock<std::mutex> lock(mMutex);
-        mTranscodingFinished = true;
-        mTranscodingStopped = true;
-        mTranscodingFinishedCondition.notify_all();
-    }
-
-    void onTrackError(const MediaTrackTranscoder* transcoder __unused, media_status_t status) {
-        std::unique_lock<std::mutex> lock(mMutex);
-        mTranscodingFinished = true;
-        mStatus = status;
-        mTranscodingFinishedCondition.notify_all();
-    }
-    // ~MediaTrackTranscoderCallback
-
-    media_status_t waitUntilFinished() {
-        std::unique_lock<std::mutex> lock(mMutex);
-        while (!mTranscodingFinished) {
-            mTranscodingFinishedCondition.wait(lock);
-        }
-        return mStatus;
-    }
-
-    void waitUntilTrackFormatAvailable() {
-        std::unique_lock<std::mutex> lock(mMutex);
-        while (!mTrackFormatAvailable) {
-            mTrackFormatAvailableCondition.wait(lock);
-        }
-    }
-
-    bool transcodingWasStopped() const { return mTranscodingFinished && mTranscodingStopped; }
-    bool transcodingFinished() const {
-        return mTranscodingFinished && !mTranscodingStopped && mStatus == AMEDIA_OK;
-    }
-
-private:
-    media_status_t mStatus = AMEDIA_OK;
-    std::mutex mMutex;
-    std::condition_variable mTranscodingFinishedCondition;
-    std::condition_variable mTrackFormatAvailableCondition;
-    bool mTranscodingFinished = false;
-    bool mTranscodingStopped = false;
-    bool mTrackFormatAvailable = false;
-};
-
-class OneShotSemaphore {
-public:
-    void wait() {
-        std::unique_lock<std::mutex> lock(mMutex);
-        while (!mSignaled) {
-            mCondition.wait(lock);
-        }
-    }
-
-    void signal() {
-        std::unique_lock<std::mutex> lock(mMutex);
-        mSignaled = true;
-        mCondition.notify_all();
-    }
-
-private:
-    std::mutex mMutex;
-    std::condition_variable mCondition;
-    bool mSignaled = false;
-};
-
-};  // namespace android
diff --git a/media/libmediatranscoding/transcoder/tests/TranscoderTestUtils.h b/media/libmediatranscoding/transcoder/tests/TranscoderTestUtils.h
new file mode 100644
index 0000000..35fe25b
--- /dev/null
+++ b/media/libmediatranscoding/transcoder/tests/TranscoderTestUtils.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/MediaTrackTranscoder.h>
+#include <media/MediaTrackTranscoderCallback.h>
+#include <media/MediaTranscoder.h>
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+
+namespace android {
+
+//
+// This file contains transcoding test utilities.
+//
+
+namespace TranscoderTestUtils {
+
+std::shared_ptr<AMediaFormat> GetVideoFormat(const std::string& path,
+                                             std::string* mimeOut = nullptr) {
+    int fd = open(path.c_str(), O_RDONLY);
+    EXPECT_GT(fd, 0);
+    ssize_t fileSize = lseek(fd, 0, SEEK_END);
+    lseek(fd, 0, SEEK_SET);
+
+    auto sampleReader = MediaSampleReaderNDK::createFromFd(fd, 0, fileSize);
+    EXPECT_NE(sampleReader, nullptr);
+
+    for (size_t i = 0; i < sampleReader->getTrackCount(); ++i) {
+        AMediaFormat* format = sampleReader->getTrackFormat(i);
+
+        const char* mime = nullptr;
+        AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
+        if (strncmp(mime, "video/", 6) == 0) {
+            if (mimeOut != nullptr) {
+                mimeOut->assign(mime);
+            }
+            return std::shared_ptr<AMediaFormat>(format, &AMediaFormat_delete);
+        }
+
+        AMediaFormat_delete(format);
+    }
+    return nullptr;
+}
+
+};  // namespace TranscoderTestUtils
+
+class TrackTranscoderTestUtils {
+public:
+    static std::shared_ptr<AMediaFormat> getDefaultVideoDestinationFormat(
+            AMediaFormat* sourceFormat, bool includeBitrate = true) {
+        // Default video destination format setup.
+        static constexpr float kFrameRate = 30.0f;
+        static constexpr int32_t kBitRate = 2 * 1000 * 1000;
+
+        AMediaFormat* destinationFormat = AMediaFormat_new();
+        AMediaFormat_copy(destinationFormat, sourceFormat);
+        AMediaFormat_setFloat(destinationFormat, AMEDIAFORMAT_KEY_FRAME_RATE, kFrameRate);
+        if (includeBitrate) {
+            AMediaFormat_setInt32(destinationFormat, AMEDIAFORMAT_KEY_BIT_RATE, kBitRate);
+        }
+
+        return std::shared_ptr<AMediaFormat>(destinationFormat, &AMediaFormat_delete);
+    }
+};
+
+class TestTrackTranscoderCallback : public MediaTrackTranscoderCallback {
+public:
+    TestTrackTranscoderCallback() = default;
+    ~TestTrackTranscoderCallback() = default;
+
+    // MediaTrackTranscoderCallback
+    void onTrackFormatAvailable(const MediaTrackTranscoder* transcoder __unused) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        mTrackFormatAvailable = true;
+        mTrackFormatAvailableCondition.notify_all();
+    }
+
+    void onTrackFinished(const MediaTrackTranscoder* transcoder __unused) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        mTranscodingFinished = true;
+        mTranscodingFinishedCondition.notify_all();
+    }
+
+    virtual void onTrackStopped(const MediaTrackTranscoder* transcoder __unused) override {
+        std::unique_lock<std::mutex> lock(mMutex);
+        mTranscodingFinished = true;
+        mTranscodingStopped = true;
+        mTranscodingFinishedCondition.notify_all();
+    }
+
+    void onTrackError(const MediaTrackTranscoder* transcoder __unused, media_status_t status) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        mTranscodingFinished = true;
+        mStatus = status;
+        mTranscodingFinishedCondition.notify_all();
+    }
+    // ~MediaTrackTranscoderCallback
+
+    media_status_t waitUntilFinished() {
+        std::unique_lock<std::mutex> lock(mMutex);
+        while (!mTranscodingFinished) {
+            mTranscodingFinishedCondition.wait(lock);
+        }
+        return mStatus;
+    }
+
+    void waitUntilTrackFormatAvailable() {
+        std::unique_lock<std::mutex> lock(mMutex);
+        while (!mTrackFormatAvailable) {
+            mTrackFormatAvailableCondition.wait(lock);
+        }
+    }
+
+    bool transcodingWasStopped() const { return mTranscodingFinished && mTranscodingStopped; }
+    bool transcodingFinished() const {
+        return mTranscodingFinished && !mTranscodingStopped && mStatus == AMEDIA_OK;
+    }
+
+private:
+    media_status_t mStatus = AMEDIA_OK;
+    std::mutex mMutex;
+    std::condition_variable mTranscodingFinishedCondition;
+    std::condition_variable mTrackFormatAvailableCondition;
+    bool mTranscodingFinished = false;
+    bool mTranscodingStopped = false;
+    bool mTrackFormatAvailable = false;
+};
+
+class TestTranscoderCallbacks : public MediaTranscoder::CallbackInterface {
+public:
+    virtual void onFinished(const MediaTranscoder* transcoder __unused) override {
+        std::unique_lock<std::mutex> lock(mMutex);
+        EXPECT_FALSE(mFinished);
+        mFinished = true;
+        mCondition.notify_all();
+    }
+
+    virtual void onError(const MediaTranscoder* transcoder __unused,
+                         media_status_t error) override {
+        std::unique_lock<std::mutex> lock(mMutex);
+        EXPECT_NE(error, AMEDIA_OK);
+        EXPECT_FALSE(mFinished);
+        mFinished = true;
+        mStatus = error;
+        mCondition.notify_all();
+    }
+
+    virtual void onProgressUpdate(const MediaTranscoder* transcoder __unused,
+                                  int32_t progress) override {
+        std::unique_lock<std::mutex> lock(mMutex);
+        if (progress > 0 && !mProgressMade) {
+            mProgressMade = true;
+            mCondition.notify_all();
+        }
+    }
+
+    virtual void onHeartBeat(const MediaTranscoder* transcoder __unused) override {
+        std::unique_lock<std::mutex> lock(mMutex);
+        mHeartBeatCount++;
+    }
+
+    virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
+                                     const std::shared_ptr<ndk::ScopedAParcel>& pausedState
+                                             __unused) override {}
+
+    void waitForTranscodingFinished() {
+        std::unique_lock<std::mutex> lock(mMutex);
+        while (!mFinished) {
+            mCondition.wait(lock);
+        }
+    }
+
+    void waitForProgressMade() {
+        std::unique_lock<std::mutex> lock(mMutex);
+        while (!mProgressMade && !mFinished) {
+            mCondition.wait(lock);
+        }
+    }
+    media_status_t mStatus = AMEDIA_OK;
+    bool mFinished = false;
+    int32_t mHeartBeatCount = 0;
+
+private:
+    std::mutex mMutex;
+    std::condition_variable mCondition;
+    bool mProgressMade = false;
+};
+
+class OneShotSemaphore {
+public:
+    void wait() {
+        std::unique_lock<std::mutex> lock(mMutex);
+        while (!mSignaled) {
+            mCondition.wait(lock);
+        }
+    }
+
+    void signal() {
+        std::unique_lock<std::mutex> lock(mMutex);
+        mSignaled = true;
+        mCondition.notify_all();
+    }
+
+private:
+    std::mutex mMutex;
+    std::condition_variable mCondition;
+    bool mSignaled = false;
+};
+
+};  // namespace android
diff --git a/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp b/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
index 4ede97f..1f9ec77 100644
--- a/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
+++ b/media/libmediatranscoding/transcoder/tests/VideoTrackTranscoderTests.cpp
@@ -27,7 +27,7 @@
 #include <media/VideoTrackTranscoder.h>
 #include <utils/Timers.h>
 
-#include "TrackTranscoderTestUtils.h"
+#include "TranscoderTestUtils.h"
 
 namespace android {
 
@@ -94,7 +94,7 @@
 
 TEST_F(VideoTrackTranscoderTests, SampleSoundness) {
     LOG(DEBUG) << "Testing SampleSoundness";
-    std::shared_ptr<TestCallback> callback = std::make_shared<TestCallback>();
+    auto callback = std::make_shared<TestTrackTranscoderCallback>();
     auto transcoder = VideoTrackTranscoder::create(callback);
 
     EXPECT_EQ(mMediaSampleReader->selectTrack(mTrackIndex), AMEDIA_OK);
@@ -139,7 +139,7 @@
 
 TEST_F(VideoTrackTranscoderTests, PreserveBitrate) {
     LOG(DEBUG) << "Testing PreserveBitrate";
-    std::shared_ptr<TestCallback> callback = std::make_shared<TestCallback>();
+    auto callback = std::make_shared<TestTrackTranscoderCallback>();
     std::shared_ptr<MediaTrackTranscoder> transcoder = VideoTrackTranscoder::create(callback);
 
     auto destFormat = TrackTranscoderTestUtils::getDefaultVideoDestinationFormat(
@@ -171,7 +171,7 @@
 // VideoTrackTranscoder needs a valid destination format.
 TEST_F(VideoTrackTranscoderTests, NullDestinationFormat) {
     LOG(DEBUG) << "Testing NullDestinationFormat";
-    std::shared_ptr<TestCallback> callback = std::make_shared<TestCallback>();
+    auto callback = std::make_shared<TestTrackTranscoderCallback>();
     std::shared_ptr<AMediaFormat> nullFormat;
 
     auto transcoder = VideoTrackTranscoder::create(callback);
@@ -181,7 +181,7 @@
 
 TEST_F(VideoTrackTranscoderTests, LingeringEncoder) {
     OneShotSemaphore semaphore;
-    auto callback = std::make_shared<TestCallback>();
+    auto callback = std::make_shared<TestTrackTranscoderCallback>();
     auto transcoder = VideoTrackTranscoder::create(callback);
 
     EXPECT_EQ(mMediaSampleReader->selectTrack(mTrackIndex), AMEDIA_OK);