1 /*
2 * Copyright (C) 2019 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 //#define LOG_NDEBUG 0
18 #define LOG_TAG "NativeExtractorTest"
19 #include <log/log.h>
20
21 #include <NdkMediaExtractor.h>
22 #include <jni.h>
23 #include <sys/stat.h>
24 #include <zlib.h>
25
26 #include <cstdlib>
27 #include <random>
28
29 #include "NativeMediaCommon.h"
30
31 #define CHECK_KEY(hasKey, format, isPass) \
32 if (!(hasKey)) { \
33 AMediaFormat_delete((format)); \
34 (isPass) = false; \
35 break; \
36 }
37
isExtractorOKonEOS(AMediaExtractor * extractor)38 static bool isExtractorOKonEOS(AMediaExtractor* extractor) {
39 return AMediaExtractor_getSampleTrackIndex(extractor) < 0 &&
40 AMediaExtractor_getSampleSize(extractor) < 0 &&
41 (int)AMediaExtractor_getSampleFlags(extractor) < 0 &&
42 AMediaExtractor_getSampleTime(extractor) < 0;
43 }
44
isSampleInfoIdentical(AMediaCodecBufferInfo * refSample,AMediaCodecBufferInfo * testSample)45 static bool isSampleInfoIdentical(AMediaCodecBufferInfo* refSample,
46 AMediaCodecBufferInfo* testSample) {
47 return refSample->flags == testSample->flags && refSample->size == testSample->size &&
48 refSample->presentationTimeUs == testSample->presentationTimeUs;
49 }
50
isSampleInfoValidAndIdentical(AMediaCodecBufferInfo * refSample,AMediaCodecBufferInfo * testSample)51 static bool isSampleInfoValidAndIdentical(AMediaCodecBufferInfo* refSample,
52 AMediaCodecBufferInfo* testSample) {
53 return refSample->flags == testSample->flags && refSample->size == testSample->size &&
54 abs(refSample->presentationTimeUs - testSample->presentationTimeUs) <= 1 &&
55 (int)refSample->flags >= 0 && refSample->size >= 0 && refSample->presentationTimeUs >= 0;
56 }
57
setSampleInfo(AMediaExtractor * extractor,AMediaCodecBufferInfo * info)58 static void inline setSampleInfo(AMediaExtractor* extractor, AMediaCodecBufferInfo* info) {
59 info->flags = AMediaExtractor_getSampleFlags(extractor);
60 info->offset = 0;
61 info->size = AMediaExtractor_getSampleSize(extractor);
62 info->presentationTimeUs = AMediaExtractor_getSampleTime(extractor);
63 }
64
isMediaSimilar(AMediaExtractor * refExtractor,AMediaExtractor * testExtractor,const char * mime,int sampleLimit=INT32_MAX)65 static bool isMediaSimilar(AMediaExtractor* refExtractor, AMediaExtractor* testExtractor,
66 const char* mime, int sampleLimit = INT32_MAX) {
67 const int maxSampleSize = (4 * 1024 * 1024);
68 auto refBuffer = new uint8_t[maxSampleSize];
69 auto testBuffer = new uint8_t[maxSampleSize];
70 int noOfTracksMatched = 0;
71 for (size_t refTrackID = 0; refTrackID < AMediaExtractor_getTrackCount(refExtractor);
72 refTrackID++) {
73 AMediaFormat* refFormat = AMediaExtractor_getTrackFormat(refExtractor, refTrackID);
74 const char* refMime = nullptr;
75 bool hasKey = AMediaFormat_getString(refFormat, AMEDIAFORMAT_KEY_MIME, &refMime);
76 if (!hasKey || (mime != nullptr && strcmp(refMime, mime) != 0)) {
77 AMediaFormat_delete(refFormat);
78 continue;
79 }
80 for (size_t testTrackID = 0; testTrackID < AMediaExtractor_getTrackCount(testExtractor);
81 testTrackID++) {
82 AMediaFormat* testFormat = AMediaExtractor_getTrackFormat(testExtractor, testTrackID);
83 if (!isFormatSimilar(refFormat, testFormat)) {
84 AMediaFormat_delete(testFormat);
85 continue;
86 }
87 AMediaExtractor_selectTrack(refExtractor, refTrackID);
88 AMediaExtractor_selectTrack(testExtractor, testTrackID);
89
90 AMediaCodecBufferInfo refSampleInfo, testSampleInfo;
91 bool areTracksIdentical = true;
92 for (int frameCount = 0;; frameCount++) {
93 setSampleInfo(refExtractor, &refSampleInfo);
94 setSampleInfo(testExtractor, &testSampleInfo);
95 if (!isSampleInfoValidAndIdentical(&refSampleInfo, &testSampleInfo)) {
96 ALOGD(" Mime: %s mismatch for sample: %d", mime, frameCount);
97 ALOGD(" flags exp/got: %d / %d", refSampleInfo.flags, testSampleInfo.flags);
98 ALOGD(" size exp/got: %d / %d ", refSampleInfo.size, testSampleInfo.size);
99 ALOGD(" ts exp/got: %" PRId64 " / %" PRId64 "",
100 refSampleInfo.presentationTimeUs, testSampleInfo.presentationTimeUs);
101 areTracksIdentical = false;
102 break;
103 }
104 ssize_t refSz =
105 AMediaExtractor_readSampleData(refExtractor, refBuffer, maxSampleSize);
106 if (refSz != refSampleInfo.size) {
107 ALOGD("Mime: %s Size exp/got: %d / %zd ", mime, refSampleInfo.size, refSz);
108 areTracksIdentical = false;
109 break;
110 }
111 ssize_t testSz =
112 AMediaExtractor_readSampleData(testExtractor, testBuffer, maxSampleSize);
113 if (testSz != testSampleInfo.size) {
114 ALOGD("Mime: %s Size exp/got: %d / %zd ", mime, testSampleInfo.size, testSz);
115 areTracksIdentical = false;
116 break;
117 }
118 int trackIndex = AMediaExtractor_getSampleTrackIndex(refExtractor);
119 if (trackIndex != refTrackID) {
120 ALOGD("Mime: %s TrackID exp/got: %zu / %d", mime, refTrackID, trackIndex);
121 areTracksIdentical = false;
122 break;
123 }
124 trackIndex = AMediaExtractor_getSampleTrackIndex(testExtractor);
125 if (trackIndex != testTrackID) {
126 ALOGD("Mime: %s TrackID exp/got %zd / %d : ", mime, testTrackID, trackIndex);
127 areTracksIdentical = false;
128 break;
129 }
130 if (memcmp(refBuffer, testBuffer, refSz)) {
131 ALOGD("Mime: %s Mismatch in sample data", mime);
132 areTracksIdentical = false;
133 break;
134 }
135 bool haveRefSamples = AMediaExtractor_advance(refExtractor);
136 bool haveTestSamples = AMediaExtractor_advance(testExtractor);
137 if (haveRefSamples != haveTestSamples) {
138 ALOGD("Mime: %s Mismatch in sampleCount", mime);
139 areTracksIdentical = false;
140 break;
141 }
142
143 if (!haveRefSamples && !isExtractorOKonEOS(refExtractor)) {
144 ALOGD("Mime: %s calls post advance() are not OK", mime);
145 areTracksIdentical = false;
146 break;
147 }
148 if (!haveTestSamples && !isExtractorOKonEOS(testExtractor)) {
149 ALOGD("Mime: %s calls post advance() are not OK", mime);
150 areTracksIdentical = false;
151 break;
152 }
153 ALOGV("Mime: %s Sample: %d flags: %d size: %d ts: % " PRId64 "", mime,
154 frameCount, refSampleInfo.flags, refSampleInfo.size,
155 refSampleInfo.presentationTimeUs);
156 if (!haveRefSamples || frameCount >= sampleLimit) {
157 break;
158 }
159 }
160 AMediaExtractor_unselectTrack(testExtractor, testTrackID);
161 AMediaExtractor_unselectTrack(refExtractor, refTrackID);
162 AMediaFormat_delete(testFormat);
163 if (areTracksIdentical) {
164 noOfTracksMatched++;
165 break;
166 }
167 }
168 AMediaFormat_delete(refFormat);
169 if (mime != nullptr && noOfTracksMatched > 0) break;
170 }
171 delete[] refBuffer;
172 delete[] testBuffer;
173 if (mime == nullptr) {
174 return noOfTracksMatched == AMediaExtractor_getTrackCount(refExtractor);
175 } else {
176 return noOfTracksMatched > 0;
177 }
178 }
179
validateCachedDuration(AMediaExtractor * extractor,bool isNetworkSource)180 static bool validateCachedDuration(AMediaExtractor* extractor, bool isNetworkSource) {
181 if (isNetworkSource) {
182 AMediaExtractor_selectTrack(extractor, 0);
183 for (unsigned cnt = 0;; cnt++) {
184 if ((cnt & (cnt - 1)) == 0) {
185 if (AMediaExtractor_getCachedDuration(extractor) < 0) {
186 ALOGE("getCachedDuration is less than zero for network source");
187 return false;
188 }
189 }
190 if (!AMediaExtractor_advance(extractor)) break;
191 }
192 AMediaExtractor_unselectTrack(extractor, 0);
193 } else {
194 if (AMediaExtractor_getCachedDuration(extractor) != -1) {
195 ALOGE("getCachedDuration != -1 for non-network source");
196 return false;
197 }
198 }
199 return true;
200 }
201
createExtractorFromFD(FILE * fp)202 static AMediaExtractor* createExtractorFromFD(FILE* fp) {
203 AMediaExtractor* extractor = nullptr;
204 struct stat buf {};
205 if (fp && !fstat(fileno(fp), &buf)) {
206 extractor = AMediaExtractor_new();
207 media_status_t res = AMediaExtractor_setDataSourceFd(extractor, fileno(fp), 0, buf.st_size);
208 if (res != AMEDIA_OK) {
209 AMediaExtractor_delete(extractor);
210 extractor = nullptr;
211 }
212 }
213 return extractor;
214 }
215
createExtractorFromUrl(JNIEnv * env,jobjectArray jkeys,jobjectArray jvalues,AMediaExtractor ** ex,AMediaDataSource ** ds,const char * url)216 static bool createExtractorFromUrl(JNIEnv* env, jobjectArray jkeys, jobjectArray jvalues,
217 AMediaExtractor** ex, AMediaDataSource** ds, const char* url) {
218 int numkeys = jkeys ? env->GetArrayLength(jkeys) : 0;
219 int numvalues = jvalues ? env->GetArrayLength(jvalues) : 0;
220 if (numkeys != numvalues) {
221 ALOGE("Unequal number of keys and values");
222 return false;
223 }
224 const char** keyvalues = numkeys ? new const char*[numkeys * 2] : nullptr;
225 for (int i = 0; i < numkeys; i++) {
226 auto jkey = (jstring)(env->GetObjectArrayElement(jkeys, i));
227 auto jvalue = (jstring)(env->GetObjectArrayElement(jvalues, i));
228 const char* key = env->GetStringUTFChars(jkey, nullptr);
229 const char* value = env->GetStringUTFChars(jvalue, nullptr);
230 keyvalues[i * 2] = key;
231 keyvalues[i * 2 + 1] = value;
232 }
233 *ex = AMediaExtractor_new();
234 *ds = AMediaDataSource_newUri(url, numkeys, keyvalues);
235 bool isPass = *ds ? (AMEDIA_OK == AMediaExtractor_setDataSourceCustom(*ex, *ds)) : false;
236 if (!isPass) ALOGE("setDataSourceCustom failed");
237 for (int i = 0; i < numkeys; i++) {
238 auto jkey = (jstring)(env->GetObjectArrayElement(jkeys, i));
239 auto jvalue = (jstring)(env->GetObjectArrayElement(jvalues, i));
240 env->ReleaseStringUTFChars(jkey, keyvalues[i * 2]);
241 env->ReleaseStringUTFChars(jvalue, keyvalues[i * 2 + 1]);
242 }
243 delete[] keyvalues;
244 return isPass;
245 }
246
247 // content necessary for testing seek are grouped in this class
248 class SeekTestParams {
249 public:
SeekTestParams(AMediaCodecBufferInfo expected,int64_t timeStamp,SeekMode mode)250 SeekTestParams(AMediaCodecBufferInfo expected, int64_t timeStamp, SeekMode mode)
251 : mExpected{expected}, mTimeStamp{timeStamp}, mMode{mode} {}
252
253 AMediaCodecBufferInfo mExpected;
254 int64_t mTimeStamp;
255 SeekMode mMode;
256 };
257
getSeekablePoints(const char * srcFile,const char * mime)258 static std::vector<AMediaCodecBufferInfo*> getSeekablePoints(const char* srcFile,
259 const char* mime) {
260 std::vector<AMediaCodecBufferInfo*> bookmarks;
261 if (mime == nullptr) return bookmarks;
262 FILE* srcFp = fopen(srcFile, "rbe");
263 if (!srcFp) {
264 ALOGE("fopen failed for srcFile %s", srcFile);
265 return bookmarks;
266 }
267 AMediaExtractor* extractor = createExtractorFromFD(srcFp);
268 if (!extractor) {
269 if (srcFp) fclose(srcFp);
270 ALOGE("createExtractorFromFD failed");
271 return bookmarks;
272 }
273
274 for (size_t trackID = 0; trackID < AMediaExtractor_getTrackCount(extractor); trackID++) {
275 AMediaFormat* format = AMediaExtractor_getTrackFormat(extractor, trackID);
276 const char* currMime = nullptr;
277 bool hasKey = AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &currMime);
278 if (!hasKey || strcmp(currMime, mime) != 0) {
279 AMediaFormat_delete(format);
280 continue;
281 }
282 AMediaExtractor_selectTrack(extractor, trackID);
283 do {
284 uint32_t sampleFlags = AMediaExtractor_getSampleFlags(extractor);
285 if ((sampleFlags & AMEDIAEXTRACTOR_SAMPLE_FLAG_SYNC) != 0) {
286 auto sampleInfo = new AMediaCodecBufferInfo;
287 setSampleInfo(extractor, sampleInfo);
288 bookmarks.push_back(sampleInfo);
289 }
290 } while (AMediaExtractor_advance(extractor));
291 AMediaExtractor_unselectTrack(extractor, trackID);
292 AMediaFormat_delete(format);
293 break;
294 }
295 AMediaExtractor_delete(extractor);
296 if (srcFp) fclose(srcFp);
297 return bookmarks;
298 }
299
300 static constexpr unsigned kSeed = 0x7ab7;
301
generateSeekTestArgs(const char * srcFile,const char * mime,bool isRandom)302 static std::vector<SeekTestParams*> generateSeekTestArgs(const char* srcFile, const char* mime,
303 bool isRandom) {
304 std::vector<SeekTestParams*> testArgs;
305 if (mime == nullptr) return testArgs;
306 const int MAX_SEEK_POINTS = 7;
307 std::srand(kSeed);
308 if (isRandom) {
309 FILE* srcFp = fopen(srcFile, "rbe");
310 if (!srcFp) {
311 ALOGE("fopen failed for srcFile %s", srcFile);
312 return testArgs;
313 }
314 AMediaExtractor* extractor = createExtractorFromFD(srcFp);
315 if (!extractor) {
316 if (srcFp) fclose(srcFp);
317 ALOGE("createExtractorFromFD failed");
318 return testArgs;
319 }
320
321 const int64_t maxEstDuration = 4000000;
322 for (size_t trackID = 0; trackID < AMediaExtractor_getTrackCount(extractor); trackID++) {
323 AMediaFormat* format = AMediaExtractor_getTrackFormat(extractor, trackID);
324 const char* currMime = nullptr;
325 bool hasKey = AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &currMime);
326 if (!hasKey || strcmp(currMime, mime) != 0) {
327 AMediaFormat_delete(format);
328 continue;
329 }
330 AMediaExtractor_selectTrack(extractor, trackID);
331 for (int i = 0; i < MAX_SEEK_POINTS; i++) {
332 double r = ((double)rand() / (RAND_MAX));
333 long pts = (long)(r * maxEstDuration);
334
335 for (int mode = AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC;
336 mode <= AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC; mode++) {
337 AMediaExtractor_seekTo(extractor, pts, (SeekMode)mode);
338 AMediaCodecBufferInfo currInfo;
339 setSampleInfo(extractor, &currInfo);
340 testArgs.push_back((new SeekTestParams(currInfo, pts, (SeekMode)mode)));
341 }
342 }
343 AMediaExtractor_unselectTrack(extractor, trackID);
344 AMediaFormat_delete(format);
345 break;
346 }
347 AMediaExtractor_delete(extractor);
348 if (srcFp) fclose(srcFp);
349 } else {
350 std::vector<AMediaCodecBufferInfo*> bookmarks = getSeekablePoints(srcFile, mime);
351 if (bookmarks.empty()) return testArgs;
352 int size = bookmarks.size();
353 int* indices;
354 int indexSize = 0;
355 if (size > MAX_SEEK_POINTS) {
356 indices = new int[MAX_SEEK_POINTS];
357 indexSize = MAX_SEEK_POINTS;
358 indices[0] = 0;
359 indices[MAX_SEEK_POINTS - 1] = size - 1;
360 for (int i = 1; i < MAX_SEEK_POINTS - 1; i++) {
361 double r = ((double)rand() / (RAND_MAX));
362 indices[i] = (int)(r * (MAX_SEEK_POINTS - 1) + 1);
363 }
364 } else {
365 indices = new int[size];
366 indexSize = size;
367 for (int i = 0; i < size; i++) indices[i] = i;
368 }
369 for (int i = 0; i < indexSize; i++) {
370 AMediaCodecBufferInfo currInfo = *bookmarks[i];
371 int64_t pts = currInfo.presentationTimeUs;
372 testArgs.push_back(
373 (new SeekTestParams(currInfo, pts, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC)));
374 testArgs.push_back((new SeekTestParams(currInfo, pts, AMEDIAEXTRACTOR_SEEK_NEXT_SYNC)));
375 testArgs.push_back(
376 (new SeekTestParams(currInfo, pts, AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC)));
377 if (i > 0) {
378 AMediaCodecBufferInfo prevInfo = *bookmarks[i - 1];
379 int64_t ptsMinus = prevInfo.presentationTimeUs;
380 ptsMinus = pts - ((pts - ptsMinus) >> 3);
381 testArgs.push_back((
382 new SeekTestParams(currInfo, ptsMinus, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC)));
383 testArgs.push_back(
384 (new SeekTestParams(currInfo, ptsMinus, AMEDIAEXTRACTOR_SEEK_NEXT_SYNC)));
385 testArgs.push_back((new SeekTestParams(prevInfo, ptsMinus,
386 AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC)));
387 }
388 if (i < size - 1) {
389 AMediaCodecBufferInfo nextInfo = *bookmarks[i + 1];
390 int64_t ptsPlus = nextInfo.presentationTimeUs;
391 ptsPlus = pts + ((ptsPlus - pts) >> 3);
392 testArgs.push_back(
393 (new SeekTestParams(currInfo, ptsPlus, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC)));
394 testArgs.push_back(
395 (new SeekTestParams(nextInfo, ptsPlus, AMEDIAEXTRACTOR_SEEK_NEXT_SYNC)));
396 testArgs.push_back((
397 new SeekTestParams(currInfo, ptsPlus, AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC)));
398 }
399 }
400 for (auto bookmark : bookmarks) {
401 delete bookmark;
402 }
403 bookmarks.clear();
404 delete[] indices;
405 }
406 return testArgs;
407 }
408
checkSeekPoints(const char * srcFile,const char * mime,const std::vector<SeekTestParams * > & seekTestArgs)409 static int checkSeekPoints(const char* srcFile, const char* mime,
410 const std::vector<SeekTestParams*>& seekTestArgs) {
411 int errCnt = 0;
412 FILE* srcFp = fopen(srcFile, "rbe");
413 AMediaExtractor* extractor = createExtractorFromFD(srcFp);
414 if (!extractor) {
415 if (srcFp) fclose(srcFp);
416 ALOGE("createExtractorFromFD failed");
417 return -1;
418 }
419 for (size_t trackID = 0; trackID < AMediaExtractor_getTrackCount(extractor); trackID++) {
420 AMediaFormat* format = AMediaExtractor_getTrackFormat(extractor, trackID);
421 const char* currMime = nullptr;
422 bool hasKey = AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &currMime);
423 if (!hasKey || strcmp(currMime, mime) != 0) {
424 AMediaFormat_delete(format);
425 continue;
426 }
427 AMediaExtractor_selectTrack(extractor, trackID);
428 AMediaCodecBufferInfo received;
429 for (auto arg : seekTestArgs) {
430 AMediaExtractor_seekTo(extractor, arg->mTimeStamp, arg->mMode);
431 setSampleInfo(extractor, &received);
432 if (!isSampleInfoIdentical(&arg->mExpected, &received)) {
433 ALOGE(" flags exp/got: %d / %d", arg->mExpected.flags, received.flags);
434 ALOGE(" size exp/got: %d / %d ", arg->mExpected.size, received.size);
435 ALOGE(" ts exp/got: %" PRId64 " / %" PRId64 "", arg->mExpected.presentationTimeUs,
436 received.presentationTimeUs);
437 errCnt++;
438 }
439 }
440 AMediaExtractor_unselectTrack(extractor, trackID);
441 AMediaFormat_delete(format);
442 break;
443 }
444 AMediaExtractor_delete(extractor);
445 if (srcFp) fclose(srcFp);
446 return errCnt;
447 }
448
isFileFormatIdentical(AMediaExtractor * refExtractor,AMediaExtractor * testExtractor)449 static bool isFileFormatIdentical(AMediaExtractor* refExtractor, AMediaExtractor* testExtractor) {
450 bool result = false;
451 if (refExtractor && testExtractor) {
452 AMediaFormat* refFormat = AMediaExtractor_getFileFormat(refExtractor);
453 AMediaFormat* testFormat = AMediaExtractor_getFileFormat(testExtractor);
454 if (refFormat && testFormat) {
455 const char *refMime = nullptr, *testMime = nullptr;
456 bool hasRefKey = AMediaFormat_getString(refFormat, AMEDIAFORMAT_KEY_MIME, &refMime);
457 bool hasTestKey = AMediaFormat_getString(testFormat, AMEDIAFORMAT_KEY_MIME, &testMime);
458 /* TODO: Not Sure if we need to verify any other parameter of file format */
459 if (hasRefKey && hasTestKey && strcmp(refMime, testMime) == 0) {
460 result = true;
461 } else {
462 ALOGE("file format exp/got : %s/%s", refMime, testMime);
463 }
464 }
465 if (refFormat) AMediaFormat_delete(refFormat);
466 if (testFormat) AMediaFormat_delete(testFormat);
467 }
468 return result;
469 }
470
isSeekOk(AMediaExtractor * refExtractor,AMediaExtractor * testExtractor)471 static bool isSeekOk(AMediaExtractor* refExtractor, AMediaExtractor* testExtractor) {
472 const long maxEstDuration = 14000000;
473 const int MAX_SEEK_POINTS = 7;
474 std::srand(kSeed);
475 AMediaCodecBufferInfo refSampleInfo, testSampleInfo;
476 bool result = true;
477 for (size_t trackID = 0; trackID < AMediaExtractor_getTrackCount(refExtractor); trackID++) {
478 AMediaExtractor_selectTrack(refExtractor, trackID);
479 AMediaExtractor_selectTrack(testExtractor, trackID);
480 for (int i = 0; i < MAX_SEEK_POINTS && result; i++) {
481 double r = ((double)rand() / (RAND_MAX));
482 long pts = (long)(r * maxEstDuration);
483 for (int mode = AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC;
484 mode <= AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC; mode++) {
485 AMediaExtractor_seekTo(refExtractor, pts, (SeekMode)mode);
486 AMediaExtractor_seekTo(testExtractor, pts, (SeekMode)mode);
487 setSampleInfo(refExtractor, &refSampleInfo);
488 setSampleInfo(testExtractor, &testSampleInfo);
489 result = isSampleInfoIdentical(&refSampleInfo, &testSampleInfo);
490 if (!result) {
491 ALOGE(" flags exp/got: %d / %d", refSampleInfo.flags, testSampleInfo.flags);
492 ALOGE(" size exp/got: %d / %d ", refSampleInfo.size, testSampleInfo.size);
493 ALOGE(" ts exp/got: %" PRId64 " / %" PRId64 "",
494 refSampleInfo.presentationTimeUs, testSampleInfo.presentationTimeUs);
495 }
496 int refTrackIdx = AMediaExtractor_getSampleTrackIndex(refExtractor);
497 int testTrackIdx = AMediaExtractor_getSampleTrackIndex(testExtractor);
498 if (refTrackIdx != testTrackIdx) {
499 ALOGE("trackIdx exp/got: %d/%d ", refTrackIdx, testTrackIdx);
500 result = false;
501 }
502 }
503 }
504 AMediaExtractor_unselectTrack(refExtractor, trackID);
505 AMediaExtractor_unselectTrack(testExtractor, trackID);
506 }
507 return result;
508 }
509
nativeReadAllData(JNIEnv * env,jobject,jstring jsrcPath,jstring jmime,jint sampleLimit,jobjectArray jkeys,jobjectArray jvalues,jboolean isSrcUrl)510 static jlong nativeReadAllData(JNIEnv* env, jobject, jstring jsrcPath, jstring jmime,
511 jint sampleLimit, jobjectArray jkeys, jobjectArray jvalues,
512 jboolean isSrcUrl) {
513 const int maxSampleSize = (4 * 1024 * 1024);
514 bool isPass = true;
515 uLong crc32value = 0U;
516 const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
517 const char* cmime = env->GetStringUTFChars(jmime, nullptr);
518 AMediaExtractor* extractor = nullptr;
519 AMediaDataSource* dataSource = nullptr;
520 FILE* srcFp = nullptr;
521
522 if (isSrcUrl) {
523 isPass = createExtractorFromUrl(env, jkeys, jvalues, &extractor, &dataSource, csrcPath);
524 } else {
525 srcFp = fopen(csrcPath, "rbe");
526 extractor = createExtractorFromFD(srcFp);
527 if (extractor == nullptr) {
528 if (srcFp) fclose(srcFp);
529 isPass = false;
530 }
531 }
532 if (!isPass) {
533 env->ReleaseStringUTFChars(jmime, cmime);
534 env->ReleaseStringUTFChars(jsrcPath, csrcPath);
535 ALOGE("Error while creating extractor");
536 if (dataSource) AMediaDataSource_delete(dataSource);
537 if (extractor) AMediaExtractor_delete(extractor);
538 return static_cast<jlong>(-2);
539 }
540 auto buffer = new uint8_t[maxSampleSize];
541 int bufferSize = 0;
542 int tracksSelected = 0;
543 for (size_t trackID = 0; trackID < AMediaExtractor_getTrackCount(extractor) && isPass;
544 trackID++) {
545 AMediaFormat* format = AMediaExtractor_getTrackFormat(extractor, trackID);
546 const char* refMime = nullptr;
547 CHECK_KEY(AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &refMime), format, isPass);
548 if (strlen(cmime) != 0 && strcmp(refMime, cmime) != 0) {
549 AMediaFormat_delete(format);
550 continue;
551 }
552 AMediaExtractor_selectTrack(extractor, trackID);
553 tracksSelected++;
554 if (strncmp(refMime, "audio/", strlen("audio/")) == 0) {
555 int32_t refSampleRate, refNumChannels;
556 flattenField<int32_t>(buffer, &bufferSize, 0);
557 CHECK_KEY(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &refSampleRate),
558 format, isPass);
559 flattenField<int32_t>(buffer, &bufferSize, refSampleRate);
560 CHECK_KEY(
561 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &refNumChannels),
562 format, isPass);
563 flattenField<int32_t>(buffer, &bufferSize, refNumChannels);
564 } else if (strncmp(refMime, "video/", strlen("video/")) == 0) {
565 int32_t refWidth, refHeight;
566 flattenField<int32_t>(buffer, &bufferSize, 1);
567 CHECK_KEY(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &refWidth), format,
568 isPass);
569 flattenField<int32_t>(buffer, &bufferSize, refWidth);
570 CHECK_KEY(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &refHeight), format,
571 isPass);
572 flattenField<int32_t>(buffer, &bufferSize, refHeight);
573 } else {
574 flattenField<int32_t>(buffer, &bufferSize, 2);
575 }
576 int64_t keyDuration = 0;
577 CHECK_KEY(AMediaFormat_getInt64(format, AMEDIAFORMAT_KEY_DURATION, &keyDuration), format,
578 isPass);
579 flattenField<int64_t>(buffer, &bufferSize, keyDuration);
580 // csd keys
581 for (int i = 0;; i++) {
582 char csdName[16];
583 void* csdBuffer;
584 size_t csdSize;
585 snprintf(csdName, sizeof(csdName), "csd-%d", i);
586 if (AMediaFormat_getBuffer(format, csdName, &csdBuffer, &csdSize)) {
587 crc32value = crc32(crc32value, static_cast<uint8_t*>(csdBuffer), csdSize);
588 } else
589 break;
590 }
591 AMediaFormat_delete(format);
592 }
593 if (tracksSelected < 1) {
594 isPass = false;
595 ALOGE("No track selected");
596 }
597 crc32value = crc32(crc32value, buffer, bufferSize);
598
599 AMediaCodecBufferInfo sampleInfo;
600 for (int sampleCount = 0; sampleCount < sampleLimit && isPass; sampleCount++) {
601 setSampleInfo(extractor, &sampleInfo);
602 ssize_t refSz = AMediaExtractor_readSampleData(extractor, buffer, maxSampleSize);
603 crc32value = crc32(crc32value, buffer, refSz);
604 if (sampleInfo.size != refSz) {
605 isPass = false;
606 ALOGE("Buffer read size %zd mismatches extractor sample size %d", refSz,
607 sampleInfo.size);
608 }
609 if (sampleInfo.flags == -1) {
610 isPass = false;
611 ALOGE("No extractor flags");
612 }
613 if (sampleInfo.presentationTimeUs == -1) {
614 isPass = false;
615 ALOGE("Presentation times are negative");
616 }
617 bufferSize = 0;
618 flattenField<int32_t>(buffer, &bufferSize, sampleInfo.size);
619 flattenField<int32_t>(buffer, &bufferSize, sampleInfo.flags);
620 flattenField<int64_t>(buffer, &bufferSize, sampleInfo.presentationTimeUs);
621 crc32value = crc32(crc32value, buffer, bufferSize);
622 sampleCount++;
623 if (!AMediaExtractor_advance(extractor)) {
624 if (!isExtractorOKonEOS(extractor)) {
625 isPass = false;
626 ALOGE("Mime: %s calls post advance() are not OK", cmime);
627 }
628 break;
629 }
630 }
631 delete[] buffer;
632 if (extractor) AMediaExtractor_delete(extractor);
633 if (dataSource) AMediaDataSource_delete(dataSource);
634 if (srcFp) fclose(srcFp);
635 env->ReleaseStringUTFChars(jmime, cmime);
636 env->ReleaseStringUTFChars(jsrcPath, csrcPath);
637 if (!isPass) {
638 ALOGE(" Error while extracting");
639 return static_cast<jlong>(-3);
640 }
641 return static_cast<jlong>(crc32value);
642 }
643
nativeTestExtract(JNIEnv * env,jobject,jstring jsrcPath,jstring jrefPath,jstring jmime)644 static jboolean nativeTestExtract(JNIEnv* env, jobject, jstring jsrcPath, jstring jrefPath,
645 jstring jmime) {
646 bool isPass = false;
647 const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
648 const char* ctestPath = env->GetStringUTFChars(jrefPath, nullptr);
649 const char* cmime = env->GetStringUTFChars(jmime, nullptr);
650 FILE* srcFp = fopen(csrcPath, "rbe");
651 AMediaExtractor* srcExtractor = createExtractorFromFD(srcFp);
652 FILE* testFp = fopen(ctestPath, "rbe");
653 AMediaExtractor* testExtractor = createExtractorFromFD(testFp);
654 if (srcExtractor && testExtractor) {
655 isPass = isMediaSimilar(srcExtractor, testExtractor, cmime);
656 if (!isPass) {
657 ALOGE(" Src and test are different from extractor perspective");
658 }
659 AMediaExtractor_delete(srcExtractor);
660 AMediaExtractor_delete(testExtractor);
661 }
662 if (srcFp) fclose(srcFp);
663 if (testFp) fclose(testFp);
664 env->ReleaseStringUTFChars(jmime, cmime);
665 env->ReleaseStringUTFChars(jsrcPath, csrcPath);
666 env->ReleaseStringUTFChars(jrefPath, ctestPath);
667 return static_cast<jboolean>(isPass);
668 }
669
nativeTestSeek(JNIEnv * env,jobject,jstring jsrcPath,jstring jmime)670 static jboolean nativeTestSeek(JNIEnv* env, jobject, jstring jsrcPath, jstring jmime) {
671 bool isPass = false;
672 const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
673 const char* cmime = env->GetStringUTFChars(jmime, nullptr);
674 std::vector<SeekTestParams*> seekTestArgs = generateSeekTestArgs(csrcPath, cmime, false);
675 if (!seekTestArgs.empty()) {
676 std::shuffle(seekTestArgs.begin(), seekTestArgs.end(), std::default_random_engine(kSeed));
677 int seekAccErrCnt = checkSeekPoints(csrcPath, cmime, seekTestArgs);
678 if (seekAccErrCnt != 0) {
679 ALOGE("For %s seek chose inaccurate Sync point in: %d / %zu", csrcPath, seekAccErrCnt,
680 seekTestArgs.size());
681 isPass = false;
682 } else {
683 isPass = true;
684 }
685 for (auto seekTestArg : seekTestArgs) {
686 delete seekTestArg;
687 }
688 seekTestArgs.clear();
689 } else {
690 ALOGE("No sync samples found.");
691 }
692 env->ReleaseStringUTFChars(jmime, cmime);
693 env->ReleaseStringUTFChars(jsrcPath, csrcPath);
694 return static_cast<jboolean>(isPass);
695 }
696
nativeTestSeekFlakiness(JNIEnv * env,jobject,jstring jsrcPath,jstring jmime)697 static jboolean nativeTestSeekFlakiness(JNIEnv* env, jobject, jstring jsrcPath, jstring jmime) {
698 bool isPass = false;
699 const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
700 const char* cmime = env->GetStringUTFChars(jmime, nullptr);
701 std::vector<SeekTestParams*> seekTestArgs = generateSeekTestArgs(csrcPath, cmime, true);
702 if (!seekTestArgs.empty()) {
703 std::shuffle(seekTestArgs.begin(), seekTestArgs.end(), std::default_random_engine(kSeed));
704 int flakyErrCnt = checkSeekPoints(csrcPath, cmime, seekTestArgs);
705 if (flakyErrCnt != 0) {
706 ALOGE("No. of Samples where seek showed flakiness is: %d", flakyErrCnt);
707 isPass = false;
708 } else {
709 isPass = true;
710 }
711 for (auto seekTestArg : seekTestArgs) {
712 delete seekTestArg;
713 }
714 seekTestArgs.clear();
715 } else {
716 ALOGE("No sync samples found.");
717 }
718 env->ReleaseStringUTFChars(jmime, cmime);
719 env->ReleaseStringUTFChars(jsrcPath, csrcPath);
720 return static_cast<jboolean>(isPass);
721 }
722
nativeTestSeekToZero(JNIEnv * env,jobject,jstring jsrcPath,jstring jmime)723 static jboolean nativeTestSeekToZero(JNIEnv* env, jobject, jstring jsrcPath, jstring jmime) {
724 bool isPass = false;
725 const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
726 const char* cmime = env->GetStringUTFChars(jmime, nullptr);
727 FILE* srcFp = fopen(csrcPath, "rbe");
728 AMediaExtractor* extractor = createExtractorFromFD(srcFp);
729 if (extractor) {
730 AMediaCodecBufferInfo sampleInfoAtZero;
731 AMediaCodecBufferInfo currInfo;
732 static long randomPts = 1 << 20;
733 for (size_t trackID = 0; trackID < AMediaExtractor_getTrackCount(extractor); trackID++) {
734 AMediaFormat* format = AMediaExtractor_getTrackFormat(extractor, trackID);
735 if (format) {
736 const char* currMime = nullptr;
737 bool hasKey = AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &currMime);
738 if (!hasKey || strcmp(currMime, cmime) != 0) {
739 AMediaFormat_delete(format);
740 continue;
741 }
742 AMediaExtractor_selectTrack(extractor, trackID);
743 setSampleInfo(extractor, &sampleInfoAtZero);
744 AMediaExtractor_seekTo(extractor, randomPts, AMEDIAEXTRACTOR_SEEK_NEXT_SYNC);
745 AMediaExtractor_seekTo(extractor, 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC);
746 setSampleInfo(extractor, &currInfo);
747 isPass = isSampleInfoIdentical(&sampleInfoAtZero, &currInfo);
748 if (!isPass) {
749 ALOGE("seen mismatch seekTo(0, SEEK_TO_CLOSEST_SYNC)");
750 ALOGE(" flags exp/got: %d / %d", sampleInfoAtZero.flags, currInfo.flags);
751 ALOGE(" size exp/got: %d / %d ", sampleInfoAtZero.size, currInfo.size);
752 ALOGE(" ts exp/got: %" PRId64 " / %" PRId64 " ",
753 sampleInfoAtZero.presentationTimeUs, currInfo.presentationTimeUs);
754 AMediaFormat_delete(format);
755 break;
756 }
757 AMediaExtractor_seekTo(extractor, -1L, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC);
758 setSampleInfo(extractor, &currInfo);
759 isPass = isSampleInfoIdentical(&sampleInfoAtZero, &currInfo);
760 if (!isPass) {
761 ALOGE("seen mismatch seekTo(-1, SEEK_TO_CLOSEST_SYNC)");
762 ALOGE(" flags exp/got: %d / %d", sampleInfoAtZero.flags, currInfo.flags);
763 ALOGE(" size exp/got: %d / %d ", sampleInfoAtZero.size, currInfo.size);
764 ALOGE(" ts exp/got: %" PRId64 " / %" PRId64 "",
765 sampleInfoAtZero.presentationTimeUs, currInfo.presentationTimeUs);
766 AMediaFormat_delete(format);
767 break;
768 }
769 AMediaExtractor_unselectTrack(extractor, trackID);
770 AMediaFormat_delete(format);
771 }
772 }
773 AMediaExtractor_delete(extractor);
774 }
775 if (srcFp) fclose(srcFp);
776 env->ReleaseStringUTFChars(jmime, cmime);
777 env->ReleaseStringUTFChars(jsrcPath, csrcPath);
778 return static_cast<jboolean>(isPass);
779 }
780
nativeTestFileFormat(JNIEnv * env,jobject,jstring jsrcPath)781 static jboolean nativeTestFileFormat(JNIEnv* env, jobject, jstring jsrcPath) {
782 bool isPass = false;
783 const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
784 FILE* srcFp = fopen(csrcPath, "rbe");
785 AMediaExtractor* extractor = createExtractorFromFD(srcFp);
786 if (extractor) {
787 AMediaFormat* format = AMediaExtractor_getFileFormat(extractor);
788 const char* mime = nullptr;
789 bool hasKey = AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
790 /* TODO: Not Sure if we need to verify any other parameter of file format */
791 if (hasKey && mime && strlen(mime) > 0) {
792 isPass = true;
793 }
794 AMediaFormat_delete(format);
795 AMediaExtractor_delete(extractor);
796 }
797 if (srcFp) fclose(srcFp);
798 env->ReleaseStringUTFChars(jsrcPath, csrcPath);
799 return static_cast<jboolean>(isPass);
800 }
801
nativeTestDataSource(JNIEnv * env,jobject,jstring jsrcPath,jstring jsrcUrl)802 static jboolean nativeTestDataSource(JNIEnv* env, jobject, jstring jsrcPath, jstring jsrcUrl) {
803 bool isPass = true;
804 const char* csrcUrl = env->GetStringUTFChars(jsrcUrl, nullptr);
805 AMediaExtractor* refExtractor = AMediaExtractor_new();
806 media_status_t status = AMediaExtractor_setDataSource(refExtractor, csrcUrl);
807 if (status == AMEDIA_OK) {
808 isPass &= validateCachedDuration(refExtractor, true);
809 const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
810 AMediaDataSource* dataSource = nullptr;
811 AMediaExtractor* testExtractor = nullptr;
812 if (createExtractorFromUrl(env, nullptr, nullptr, &testExtractor, &dataSource, csrcUrl)) {
813 isPass &= validateCachedDuration(testExtractor, true);
814 if (!(isMediaSimilar(refExtractor, testExtractor, nullptr) &&
815 isFileFormatIdentical(refExtractor, testExtractor) &&
816 isSeekOk(refExtractor, testExtractor))) {
817 isPass = false;
818 }
819 }
820 if (testExtractor) AMediaExtractor_delete(testExtractor);
821 if (dataSource) AMediaDataSource_delete(dataSource);
822
823 FILE* testFp = fopen(csrcPath, "rbe");
824 testExtractor = createExtractorFromFD(testFp);
825 if (testExtractor == nullptr) {
826 ALOGE("createExtractorFromFD failed for test extractor");
827 isPass = false;
828 } else {
829 isPass &= validateCachedDuration(testExtractor, false);
830 if (!(isMediaSimilar(refExtractor, testExtractor, nullptr) &&
831 isFileFormatIdentical(refExtractor, testExtractor) &&
832 isSeekOk(refExtractor, testExtractor))) {
833 isPass = false;
834 }
835 }
836 if (testExtractor) AMediaExtractor_delete(testExtractor);
837 if (testFp) fclose(testFp);
838 env->ReleaseStringUTFChars(jsrcPath, csrcPath);
839 } else {
840 ALOGE("setDataSource failed");
841 isPass = false;
842 }
843 if (refExtractor) AMediaExtractor_delete(refExtractor);
844 env->ReleaseStringUTFChars(jsrcUrl, csrcUrl);
845 return static_cast<jboolean>(isPass);
846 }
847
registerAndroidMediaV2CtsExtractorTestSetDS(JNIEnv * env)848 int registerAndroidMediaV2CtsExtractorTestSetDS(JNIEnv* env) {
849 const JNINativeMethod methodTable[] = {
850 {"nativeTestDataSource", "(Ljava/lang/String;Ljava/lang/String;)Z",
851 (void*)nativeTestDataSource},
852 };
853 jclass c = env->FindClass("android/mediav2/cts/ExtractorTest$SetDataSourceTest");
854 return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
855 }
856
registerAndroidMediaV2CtsExtractorTestFunc(JNIEnv * env)857 int registerAndroidMediaV2CtsExtractorTestFunc(JNIEnv* env) {
858 const JNINativeMethod methodTable[] = {
859 {"nativeTestExtract", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
860 (void*)nativeTestExtract},
861 {"nativeTestSeek", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)nativeTestSeek},
862 {"nativeTestSeekFlakiness", "(Ljava/lang/String;Ljava/lang/String;)Z",
863 (void*)nativeTestSeekFlakiness},
864 {"nativeTestSeekToZero", "(Ljava/lang/String;Ljava/lang/String;)Z",
865 (void*)nativeTestSeekToZero},
866 {"nativeTestFileFormat", "(Ljava/lang/String;)Z", (void*)nativeTestFileFormat},
867 };
868 jclass c = env->FindClass("android/mediav2/cts/ExtractorTest$FunctionalityTest");
869 return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
870 }
871
registerAndroidMediaV2CtsExtractorTest(JNIEnv * env)872 int registerAndroidMediaV2CtsExtractorTest(JNIEnv* env) {
873 const JNINativeMethod methodTable[] = {
874 {"nativeReadAllData",
875 "(Ljava/lang/String;Ljava/lang/String;I[Ljava/lang/String;[Ljava/lang/String;Z)J",
876 (void*)nativeReadAllData},
877 };
878 jclass c = env->FindClass("android/mediav2/cts/ExtractorTest");
879 return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
880 }
881
882 extern int registerAndroidMediaV2CtsExtractorUnitTestApi(JNIEnv* env);
883
JNI_OnLoad(JavaVM * vm,void *)884 extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
885 JNIEnv* env;
886 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) return JNI_ERR;
887 if (registerAndroidMediaV2CtsExtractorTest(env) != JNI_OK) return JNI_ERR;
888 if (registerAndroidMediaV2CtsExtractorTestSetDS(env) != JNI_OK) return JNI_ERR;
889 if (registerAndroidMediaV2CtsExtractorTestFunc(env) != JNI_OK) return JNI_ERR;
890 if (registerAndroidMediaV2CtsExtractorUnitTestApi(env) != JNI_OK) return JNI_ERR;
891 return JNI_VERSION_1_6;
892 }
893