blob: 7a968eb7a9839926384246ed206183219e15f9a2 [file] [log] [blame]
Linus Nilssoncab39d82020-05-14 16:32:21 -07001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// Unit Test for MediaTranscoder
18
19// #define LOG_NDEBUG 0
20#define LOG_TAG "MediaTranscoderTests"
21
22#include <android-base/logging.h>
Linus Nilsson93cf9132020-09-24 12:12:48 -070023#include <android/binder_process.h>
Chong Zhangb55c5452020-06-26 14:32:12 -070024#include <fcntl.h>
Linus Nilssoncab39d82020-05-14 16:32:21 -070025#include <gtest/gtest.h>
Chong Zhang5855ee52020-06-22 11:41:25 -070026#include <media/MediaSampleReaderNDK.h>
Linus Nilssoncab39d82020-05-14 16:32:21 -070027#include <media/MediaTranscoder.h>
Chong Zhang5855ee52020-06-22 11:41:25 -070028#include <media/NdkCommon.h>
Linus Nilssoncab39d82020-05-14 16:32:21 -070029
30namespace android {
31
Chong Zhang5855ee52020-06-22 11:41:25 -070032#define DEFINE_FORMAT_VALUE_EQUAL_FUNC(_type, _typeName) \
33 static bool equal##_typeName(const char* key, AMediaFormat* src, AMediaFormat* dst) { \
34 _type srcVal, dstVal; \
35 bool srcPresent = AMediaFormat_get##_typeName(src, key, &srcVal); \
36 bool dstPresent = AMediaFormat_get##_typeName(dst, key, &dstVal); \
37 return (srcPresent == dstPresent) && (!srcPresent || (srcVal == dstVal)); \
38 }
39
40DEFINE_FORMAT_VALUE_EQUAL_FUNC(int64_t, Int64);
41DEFINE_FORMAT_VALUE_EQUAL_FUNC(int32_t, Int32);
42
43struct FormatVerifierEntry {
44 const char* key;
Chong Zhangd6e4aec2020-06-22 14:13:07 -070045 std::function<bool(const char*, AMediaFormat*, AMediaFormat*)> equal;
Chong Zhang5855ee52020-06-22 11:41:25 -070046};
47
48static const FormatVerifierEntry kFieldsToPreserve[] = {
49 {AMEDIAFORMAT_KEY_DURATION, equalInt64}, {AMEDIAFORMAT_KEY_WIDTH, equalInt32},
50 {AMEDIAFORMAT_KEY_HEIGHT, equalInt32}, {AMEDIAFORMAT_KEY_FRAME_RATE, equalInt32},
51 {AMEDIAFORMAT_KEY_FRAME_COUNT, equalInt32}, {AMEDIAFORMAT_KEY_DISPLAY_WIDTH, equalInt32},
52 {AMEDIAFORMAT_KEY_DISPLAY_HEIGHT, equalInt32}, {AMEDIAFORMAT_KEY_SAR_WIDTH, equalInt32},
53 {AMEDIAFORMAT_KEY_SAR_HEIGHT, equalInt32}, {AMEDIAFORMAT_KEY_ROTATION, equalInt32},
54};
55
Linus Nilssoncab39d82020-05-14 16:32:21 -070056class TestCallbacks : public MediaTranscoder::CallbackInterface {
57public:
58 virtual void onFinished(const MediaTranscoder* transcoder __unused) override {
59 std::unique_lock<std::mutex> lock(mMutex);
60 EXPECT_FALSE(mFinished);
61 mFinished = true;
62 mCondition.notify_all();
63 }
64
65 virtual void onError(const MediaTranscoder* transcoder __unused,
66 media_status_t error) override {
67 std::unique_lock<std::mutex> lock(mMutex);
68 EXPECT_NE(error, AMEDIA_OK);
69 EXPECT_FALSE(mFinished);
70 mFinished = true;
71 mStatus = error;
72 mCondition.notify_all();
73 }
74
75 virtual void onProgressUpdate(const MediaTranscoder* transcoder __unused,
Linus Nilsson93cf9132020-09-24 12:12:48 -070076 int32_t progress) override {
77 std::unique_lock<std::mutex> lock(mMutex);
78 if (progress > 0 && !mProgressMade) {
79 mProgressMade = true;
80 mCondition.notify_all();
81 }
82 }
Linus Nilssoncab39d82020-05-14 16:32:21 -070083
84 virtual void onCodecResourceLost(const MediaTranscoder* transcoder __unused,
Chong Zhangb55c5452020-06-26 14:32:12 -070085 const std::shared_ptr<const Parcel>& pausedState
Linus Nilssoncab39d82020-05-14 16:32:21 -070086 __unused) override {}
87
88 void waitForTranscodingFinished() {
89 std::unique_lock<std::mutex> lock(mMutex);
90 while (!mFinished) {
91 mCondition.wait(lock);
92 }
93 }
94
Linus Nilsson93cf9132020-09-24 12:12:48 -070095 void waitForProgressMade() {
96 std::unique_lock<std::mutex> lock(mMutex);
97 while (!mProgressMade && !mFinished) {
98 mCondition.wait(lock);
99 }
100 }
Linus Nilssoncab39d82020-05-14 16:32:21 -0700101 media_status_t mStatus = AMEDIA_OK;
102
103private:
104 std::mutex mMutex;
105 std::condition_variable mCondition;
106 bool mFinished = false;
Linus Nilsson93cf9132020-09-24 12:12:48 -0700107 bool mProgressMade = false;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700108};
109
Chong Zhang308e91f2020-06-10 15:27:56 -0700110// Write-only, create file if non-existent, don't overwrite existing file.
111static constexpr int kOpenFlags = O_WRONLY | O_CREAT | O_EXCL;
112// User R+W permission.
113static constexpr int kFileMode = S_IRUSR | S_IWUSR;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700114
115class MediaTranscoderTests : public ::testing::Test {
116public:
117 MediaTranscoderTests() { LOG(DEBUG) << "MediaTranscoderTests created"; }
118 ~MediaTranscoderTests() { LOG(DEBUG) << "MediaTranscoderTests destroyed"; }
119
120 void SetUp() override {
121 LOG(DEBUG) << "MediaTranscoderTests set up";
122 mCallbacks = std::make_shared<TestCallbacks>();
Linus Nilsson93cf9132020-09-24 12:12:48 -0700123 ABinderProcess_startThreadPool();
Linus Nilssoncab39d82020-05-14 16:32:21 -0700124 }
125
126 void TearDown() override {
127 LOG(DEBUG) << "MediaTranscoderTests tear down";
128 mCallbacks.reset();
129 }
130
131 void deleteFile(const char* path) { unlink(path); }
132
Linus Nilsson800793f2020-07-31 16:16:38 -0700133 float getFileSizeDiffPercent(const char* path1, const char* path2, bool absolute = false) {
134 struct stat s1, s2;
135 EXPECT_EQ(stat(path1, &s1), 0);
136 EXPECT_EQ(stat(path2, &s2), 0);
137
138 int64_t diff = s2.st_size - s1.st_size;
139 if (absolute && diff < 0) diff = -diff;
140
141 return (float)diff * 100.0f / s1.st_size;
142 }
143
Linus Nilsson93cf9132020-09-24 12:12:48 -0700144 typedef enum {
145 kRunToCompletion,
146 kCancelAfterProgress,
147 kCancelAfterStart,
148 } TranscodeExecutionControl;
149
Linus Nilssoncab39d82020-05-14 16:32:21 -0700150 using FormatConfigurationCallback = std::function<AMediaFormat*(AMediaFormat*)>;
Chong Zhang5855ee52020-06-22 11:41:25 -0700151 media_status_t transcodeHelper(const char* srcPath, const char* destPath,
Linus Nilsson93cf9132020-09-24 12:12:48 -0700152 FormatConfigurationCallback formatCallback,
153 TranscodeExecutionControl executionControl = kRunToCompletion) {
Linus Nilssoncab39d82020-05-14 16:32:21 -0700154 auto transcoder = MediaTranscoder::create(mCallbacks, nullptr);
155 EXPECT_NE(transcoder, nullptr);
156
Chong Zhang5855ee52020-06-22 11:41:25 -0700157 const int srcFd = open(srcPath, O_RDONLY);
Chong Zhang308e91f2020-06-10 15:27:56 -0700158 EXPECT_EQ(transcoder->configureSource(srcFd), AMEDIA_OK);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700159
160 std::vector<std::shared_ptr<AMediaFormat>> trackFormats = transcoder->getTrackFormats();
161 EXPECT_GT(trackFormats.size(), 0);
162
163 for (int i = 0; i < trackFormats.size(); ++i) {
164 AMediaFormat* format = formatCallback(trackFormats[i].get());
165 EXPECT_EQ(transcoder->configureTrackFormat(i, format), AMEDIA_OK);
Chong Zhang5855ee52020-06-22 11:41:25 -0700166
167 // Save original video track format for verification.
168 const char* mime = nullptr;
169 AMediaFormat_getString(trackFormats[i].get(), AMEDIAFORMAT_KEY_MIME, &mime);
170 if (strncmp(mime, "video/", 6) == 0) {
171 mSourceVideoFormat = trackFormats[i];
172 }
173
Linus Nilssoncab39d82020-05-14 16:32:21 -0700174 if (format != nullptr) {
175 AMediaFormat_delete(format);
176 }
177 }
178 deleteFile(destPath);
Chong Zhang308e91f2020-06-10 15:27:56 -0700179 const int dstFd = open(destPath, kOpenFlags, kFileMode);
180 EXPECT_EQ(transcoder->configureDestination(dstFd), AMEDIA_OK);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700181
182 media_status_t startStatus = transcoder->start();
183 EXPECT_EQ(startStatus, AMEDIA_OK);
184 if (startStatus == AMEDIA_OK) {
Linus Nilsson93cf9132020-09-24 12:12:48 -0700185 switch (executionControl) {
186 case kCancelAfterProgress:
187 mCallbacks->waitForProgressMade();
188 FALLTHROUGH_INTENDED;
189 case kCancelAfterStart:
190 transcoder->cancel();
191 break;
192 case kRunToCompletion:
193 default:
194 mCallbacks->waitForTranscodingFinished();
195 break;
196 }
Linus Nilssoncab39d82020-05-14 16:32:21 -0700197 }
Chong Zhang308e91f2020-06-10 15:27:56 -0700198 close(srcFd);
199 close(dstFd);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700200
201 return mCallbacks->mStatus;
202 }
203
Linus Nilsson800793f2020-07-31 16:16:38 -0700204 void testTranscodeVideo(const char* srcPath, const char* destPath, const char* dstMime,
205 int32_t bitrate = 0) {
206 EXPECT_EQ(transcodeHelper(srcPath, destPath,
207 [dstMime, bitrate](AMediaFormat* sourceFormat) {
208 AMediaFormat* format = nullptr;
209 const char* mime = nullptr;
210 AMediaFormat_getString(sourceFormat, AMEDIAFORMAT_KEY_MIME,
211 &mime);
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700212
Linus Nilsson800793f2020-07-31 16:16:38 -0700213 if (strncmp(mime, "video/", 6) == 0 &&
214 (bitrate > 0 || dstMime != nullptr)) {
215 format = AMediaFormat_new();
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700216
Linus Nilsson800793f2020-07-31 16:16:38 -0700217 if (bitrate > 0) {
218 AMediaFormat_setInt32(
219 format, AMEDIAFORMAT_KEY_BIT_RATE, bitrate);
220 }
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700221
Linus Nilsson800793f2020-07-31 16:16:38 -0700222 if (dstMime != nullptr) {
223 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME,
224 dstMime);
225 }
226 }
227 return format;
228 }),
229 AMEDIA_OK);
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700230
231 if (dstMime != nullptr) {
232 std::vector<FormatVerifierEntry> extraVerifiers = {
233 {AMEDIAFORMAT_KEY_MIME,
234 [dstMime](const char* key, AMediaFormat* src __unused, AMediaFormat* dst) {
235 const char* mime = nullptr;
236 AMediaFormat_getString(dst, key, &mime);
237 return !strcmp(mime, dstMime);
238 }},
239 };
240 verifyOutputFormat(destPath, &extraVerifiers);
241 } else {
242 verifyOutputFormat(destPath);
243 }
244 }
245
Chong Zhang5855ee52020-06-22 11:41:25 -0700246 void verifyOutputFormat(const char* destPath,
247 const std::vector<FormatVerifierEntry>* extraVerifiers = nullptr) {
248 int dstFd = open(destPath, O_RDONLY);
249 EXPECT_GT(dstFd, 0);
250 ssize_t fileSize = lseek(dstFd, 0, SEEK_END);
251 lseek(dstFd, 0, SEEK_SET);
252
253 std::shared_ptr<MediaSampleReader> sampleReader =
254 MediaSampleReaderNDK::createFromFd(dstFd, 0, fileSize);
Linus Nilsson42a971b2020-07-01 16:41:11 -0700255 ASSERT_NE(sampleReader, nullptr);
Chong Zhang5855ee52020-06-22 11:41:25 -0700256
257 std::shared_ptr<AMediaFormat> videoFormat;
258 const size_t trackCount = sampleReader->getTrackCount();
259 for (size_t trackIndex = 0; trackIndex < trackCount; ++trackIndex) {
260 AMediaFormat* trackFormat = sampleReader->getTrackFormat(static_cast<int>(trackIndex));
261 if (trackFormat != nullptr) {
262 const char* mime = nullptr;
263 AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
Linus Nilsson42a971b2020-07-01 16:41:11 -0700264
Chong Zhang5855ee52020-06-22 11:41:25 -0700265 if (strncmp(mime, "video/", 6) == 0) {
266 LOG(INFO) << "Track # " << trackIndex << ": "
267 << AMediaFormat_toString(trackFormat);
268 videoFormat = std::shared_ptr<AMediaFormat>(trackFormat, &AMediaFormat_delete);
269 break;
270 }
271 }
272 }
273
274 EXPECT_NE(videoFormat, nullptr);
275
276 LOG(INFO) << "source video format: " << AMediaFormat_toString(mSourceVideoFormat.get());
277 LOG(INFO) << "transcoded video format: " << AMediaFormat_toString(videoFormat.get());
278
279 for (int i = 0; i < (sizeof(kFieldsToPreserve) / sizeof(kFieldsToPreserve[0])); ++i) {
280 EXPECT_TRUE(kFieldsToPreserve[i].equal(kFieldsToPreserve[i].key,
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700281 mSourceVideoFormat.get(), videoFormat.get()))
282 << "Failed at key " << kFieldsToPreserve[i].key;
Chong Zhang5855ee52020-06-22 11:41:25 -0700283 }
284
285 if (extraVerifiers != nullptr) {
286 for (int i = 0; i < extraVerifiers->size(); ++i) {
287 const FormatVerifierEntry& entry = (*extraVerifiers)[i];
288 EXPECT_TRUE(entry.equal(entry.key, mSourceVideoFormat.get(), videoFormat.get()));
289 }
290 }
291
292 close(dstFd);
293 }
294
Linus Nilssoncab39d82020-05-14 16:32:21 -0700295 std::shared_ptr<TestCallbacks> mCallbacks;
Chong Zhang5855ee52020-06-22 11:41:25 -0700296 std::shared_ptr<AMediaFormat> mSourceVideoFormat;
Linus Nilssoncab39d82020-05-14 16:32:21 -0700297};
298
299TEST_F(MediaTranscoderTests, TestPassthrough) {
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700300 const char* srcPath = "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
Linus Nilssoncab39d82020-05-14 16:32:21 -0700301 const char* destPath = "/data/local/tmp/MediaTranscoder_Passthrough.MP4";
Linus Nilsson800793f2020-07-31 16:16:38 -0700302 testTranscodeVideo(srcPath, destPath, nullptr);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700303}
304
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700305TEST_F(MediaTranscoderTests, TestVideoTranscode_AvcToAvc_Basic) {
306 const char* srcPath = "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
307 const char* destPath = "/data/local/tmp/MediaTranscoder_VideoTranscode_AvcToAvc_Basic.MP4";
Linus Nilsson800793f2020-07-31 16:16:38 -0700308 testTranscodeVideo(srcPath, destPath, AMEDIA_MIMETYPE_VIDEO_AVC);
Chong Zhang5855ee52020-06-22 11:41:25 -0700309}
310
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700311TEST_F(MediaTranscoderTests, TestVideoTranscode_HevcToAvc_Basic) {
312 const char* srcPath = "/data/local/tmp/TranscodingTestAssets/jets_hevc_1280x720_20Mbps.mp4";
313 const char* destPath = "/data/local/tmp/MediaTranscoder_VideoTranscode_HevcToAvc_Basic.MP4";
314 testTranscodeVideo(srcPath, destPath, AMEDIA_MIMETYPE_VIDEO_AVC);
315}
Chong Zhang5855ee52020-06-22 11:41:25 -0700316
Chong Zhangd6e4aec2020-06-22 14:13:07 -0700317TEST_F(MediaTranscoderTests, TestVideoTranscode_HevcToAvc_Rotation) {
318 const char* srcPath =
319 "/data/local/tmp/TranscodingTestAssets/desk_hevc_1920x1080_aac_48KHz_rot90.mp4";
320 const char* destPath = "/data/local/tmp/MediaTranscoder_VideoTranscode_HevcToAvc_Rotation.MP4";
321 testTranscodeVideo(srcPath, destPath, AMEDIA_MIMETYPE_VIDEO_AVC);
Linus Nilssoncab39d82020-05-14 16:32:21 -0700322}
323
Linus Nilsson800793f2020-07-31 16:16:38 -0700324TEST_F(MediaTranscoderTests, TestPreserveBitrate) {
325 const char* srcPath = "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
326 const char* destPath = "/data/local/tmp/MediaTranscoder_PreserveBitrate.MP4";
327 testTranscodeVideo(srcPath, destPath, AMEDIA_MIMETYPE_VIDEO_AVC);
328
329 // Require maximum of 10% difference in file size.
330 EXPECT_LT(getFileSizeDiffPercent(srcPath, destPath, true /* absolute*/), 10);
331}
332
333TEST_F(MediaTranscoderTests, TestCustomBitrate) {
334 const char* srcPath = "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
335 const char* destPath1 = "/data/local/tmp/MediaTranscoder_CustomBitrate_2Mbps.MP4";
336 const char* destPath2 = "/data/local/tmp/MediaTranscoder_CustomBitrate_8Mbps.MP4";
337 testTranscodeVideo(srcPath, destPath1, AMEDIA_MIMETYPE_VIDEO_AVC, 2 * 1000 * 1000);
338 mCallbacks = std::make_shared<TestCallbacks>();
339 testTranscodeVideo(srcPath, destPath2, AMEDIA_MIMETYPE_VIDEO_AVC, 8 * 1000 * 1000);
340
341 // The source asset is very short and heavily compressed from the beginning so don't expect the
342 // requested bitrate to be exactly matched. However 40% difference seems reasonable.
343 EXPECT_GT(getFileSizeDiffPercent(destPath1, destPath2), 40);
344}
345
Linus Nilsson93cf9132020-09-24 12:12:48 -0700346static AMediaFormat* getAVCVideoFormat(AMediaFormat* sourceFormat) {
347 AMediaFormat* format = nullptr;
348 const char* mime = nullptr;
349 AMediaFormat_getString(sourceFormat, AMEDIAFORMAT_KEY_MIME, &mime);
350
351 if (strncmp(mime, "video/", 6) == 0) {
352 format = AMediaFormat_new();
353 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_AVC);
354 }
355
356 return format;
357}
358
359TEST_F(MediaTranscoderTests, TestCancelAfterProgress) {
360 const char* srcPath = "/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4";
361 const char* destPath = "/data/local/tmp/MediaTranscoder_Cancel.MP4";
362
363 for (int i = 0; i < 32; ++i) {
364 EXPECT_EQ(transcodeHelper(srcPath, destPath, getAVCVideoFormat, kCancelAfterProgress),
365 AMEDIA_OK);
366 mCallbacks = std::make_shared<TestCallbacks>();
367 }
368}
369
370TEST_F(MediaTranscoderTests, TestCancelAfterStart) {
371 const char* srcPath = "/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4";
372 const char* destPath = "/data/local/tmp/MediaTranscoder_Cancel.MP4";
373
374 for (int i = 0; i < 32; ++i) {
375 EXPECT_EQ(transcodeHelper(srcPath, destPath, getAVCVideoFormat, kCancelAfterStart),
376 AMEDIA_OK);
377 mCallbacks = std::make_shared<TestCallbacks>();
378 }
379}
380
Linus Nilssoncab39d82020-05-14 16:32:21 -0700381} // namespace android
382
383int main(int argc, char** argv) {
384 ::testing::InitGoogleTest(&argc, argv);
385 return RUN_ALL_TESTS();
386}