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 "NativeMuxerUnitTest"
19 #include <log/log.h>
20
21 #include <NdkMediaExtractor.h>
22 #include <NdkMediaFormat.h>
23 #include <NdkMediaMuxer.h>
24 #include <fcntl.h>
25 #include <jni.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28
29 #include <cmath>
30 #include <cstring>
31 #include <fstream>
32 #include <map>
33 #include <vector>
34
35 #include "NativeMediaCommon.h"
36
insertPerFrameSubtitles(AMediaMuxer * muxer,long pts,size_t trackID)37 static media_status_t insertPerFrameSubtitles(AMediaMuxer* muxer, long pts, size_t trackID) {
38 const char* greeting = "hello world";
39 auto* info = new AMediaCodecBufferInfo;
40 info->offset = 0;
41 info->size = strlen(greeting);
42 info->presentationTimeUs = pts;
43 info->flags = 0;
44 media_status_t status = AMediaMuxer_writeSampleData(muxer, trackID, (uint8_t*)greeting, info);
45 delete info;
46 return status;
47 }
48
nativeTestIfInvalidFdIsRejected(JNIEnv *,jobject)49 static jboolean nativeTestIfInvalidFdIsRejected(JNIEnv*, jobject) {
50 AMediaMuxer* muxer = AMediaMuxer_new(-1, (OutputFormat)OUTPUT_FORMAT_THREE_GPP);
51 bool isPass = true;
52 if (muxer != nullptr) {
53 AMediaMuxer_delete(muxer);
54 ALOGE("error: muxer constructor accepts invalid file descriptor");
55 isPass = false;
56 }
57 return static_cast<jboolean>(isPass);
58 }
59
nativeTestIfReadOnlyFdIsRejected(JNIEnv * env,jobject,jstring jdstPath)60 static jboolean nativeTestIfReadOnlyFdIsRejected(JNIEnv* env, jobject, jstring jdstPath) {
61 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
62 FILE* ofp = fopen(cdstPath, "rbe");
63 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_THREE_GPP);
64 bool isPass = true;
65 if (muxer != nullptr) {
66 AMediaMuxer_delete(muxer);
67 ALOGE("error: muxer constructor accepts read-only file descriptor");
68 isPass = false;
69 }
70 fclose(ofp);
71 env->ReleaseStringUTFChars(jdstPath, cdstPath);
72 return static_cast<jboolean>(isPass);
73 }
74
nativeTestIfWriteOnlyFdIsRejected(JNIEnv * env,jobject,jstring jdstPath)75 static jboolean nativeTestIfWriteOnlyFdIsRejected(JNIEnv* env, jobject, jstring jdstPath) {
76 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
77 FILE* ofp = fopen(cdstPath, "wbe");
78 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_WEBM);
79 bool isPass = true;
80 if (muxer != nullptr) {
81 AMediaMuxer_delete(muxer);
82 ALOGE("error: muxer constructor accepts write-only file descriptor");
83 isPass = false;
84 }
85 fclose(ofp);
86 env->ReleaseStringUTFChars(jdstPath, cdstPath);
87 return static_cast<jboolean>(isPass);
88 }
89
nativeTestIfNonSeekableFdIsRejected(JNIEnv * env,jobject,jstring jdstPath)90 static jboolean nativeTestIfNonSeekableFdIsRejected(JNIEnv* env, jobject, jstring jdstPath) {
91 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
92 mkfifo(cdstPath, 0666);
93 int fd = open(cdstPath, O_WRONLY);
94 AMediaMuxer* muxer = AMediaMuxer_new(fd, (OutputFormat)OUTPUT_FORMAT_THREE_GPP);
95 bool isPass = true;
96 if (muxer != nullptr) {
97 AMediaMuxer_delete(muxer);
98 ALOGE("error: muxer constructor accepts non-seekable file descriptor");
99 isPass = false;
100 }
101 close(fd);
102 env->ReleaseStringUTFChars(jdstPath, cdstPath);
103 return static_cast<jboolean>(isPass);
104 }
105
nativeTestIfInvalidOutputFormatIsRejected(JNIEnv * env,jobject,jstring jdstPath)106 static jboolean nativeTestIfInvalidOutputFormatIsRejected(JNIEnv* env, jobject, jstring jdstPath) {
107 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
108 FILE* ofp = fopen(cdstPath, "wbe+");
109 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)(OUTPUT_FORMAT_LIST_END + 1));
110 bool isPass = true;
111 if (muxer != nullptr) {
112 AMediaMuxer_delete(muxer);
113 ALOGE("error: muxer constructor accepts invalid output format");
114 isPass = false;
115 }
116 fclose(ofp);
117 env->ReleaseStringUTFChars(jdstPath, cdstPath);
118 return static_cast<jboolean>(isPass);
119 }
120
nativeTestIfInvalidMediaFormatIsRejected(JNIEnv * env,jobject,jstring jdstPath)121 static jboolean nativeTestIfInvalidMediaFormatIsRejected(JNIEnv* env, jobject, jstring jdstPath) {
122 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
123 FILE* ofp = fopen(cdstPath, "wbe+");
124 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_MPEG_4);
125 AMediaFormat* format = AMediaFormat_new();
126 bool isPass = true;
127 if (AMediaMuxer_addTrack(muxer, format) >= 0) {
128 ALOGE("error: muxer.addTrack succeeds with format that has no mime key");
129 isPass = false;
130 }
131
132 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "text/cea-608");
133 if (AMediaMuxer_addTrack(muxer, format) >= 0) {
134 ALOGE("error: muxer.addTrack succeeds with format whose mime is non-compliant");
135 isPass = false;
136 }
137 AMediaFormat_delete(format);
138 AMediaMuxer_delete(muxer);
139 fclose(ofp);
140 env->ReleaseStringUTFChars(jdstPath, cdstPath);
141 return static_cast<jboolean>(isPass);
142 }
143
nativeTestIfCorruptMediaFormatIsRejected(JNIEnv * env,jobject,jstring jdstPath)144 static jboolean nativeTestIfCorruptMediaFormatIsRejected(JNIEnv* env, jobject, jstring jdstPath) {
145 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
146 FILE* ofp = fopen(cdstPath, "wbe+");
147 bool isPass = true;
148 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_MPEG_4);
149 AMediaFormat* format = AMediaFormat_new();
150 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_AUDIO_AAC);
151 AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, -1);
152 if (AMediaMuxer_addTrack(muxer, format) >= 0) {
153 ALOGE("error: muxer.addTrack succeeds with erroneous key-value pairs in media format");
154 isPass = false;
155 }
156 AMediaFormat_delete(format);
157 AMediaMuxer_delete(muxer);
158 fclose(ofp);
159 env->ReleaseStringUTFChars(jdstPath, cdstPath);
160 return static_cast<jboolean>(isPass);
161 }
162
nativeTestIfAddTrackSucceedsAfterStart(JNIEnv * env,jobject,jstring jdstPath)163 static jboolean nativeTestIfAddTrackSucceedsAfterStart(JNIEnv* env, jobject, jstring jdstPath) {
164 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
165 FILE* ofp = fopen(cdstPath, "wbe+");
166 bool isPass = true;
167 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_MPEG_4);
168 AMediaFormat* format = AMediaFormat_new();
169 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
170 isPass &= AMediaMuxer_addTrack(muxer, format) >= 0;
171 isPass &= (AMediaMuxer_start(muxer) == AMEDIA_OK);
172 if (AMediaMuxer_addTrack(muxer, format) >= 0) {
173 ALOGE("error: muxer.addTrack succeeds after muxer.start");
174 isPass = false;
175 }
176 AMediaFormat_delete(format);
177 AMediaMuxer_delete(muxer);
178 fclose(ofp);
179 env->ReleaseStringUTFChars(jdstPath, cdstPath);
180 return static_cast<jboolean>(isPass);
181 }
182
nativeTestIfAddTrackSucceedsAfterWriteSampleData(JNIEnv * env,jobject,jstring jdstPath)183 static jboolean nativeTestIfAddTrackSucceedsAfterWriteSampleData(JNIEnv* env, jobject,
184 jstring jdstPath) {
185 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
186 FILE* ofp = fopen(cdstPath, "wbe+");
187 bool isPass = true;
188 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_MPEG_4);
189 AMediaFormat* format = AMediaFormat_new();
190 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
191 ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
192 isPass &= trackID >= 0;
193 isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
194 isPass &= insertPerFrameSubtitles(muxer, 0, trackID) == AMEDIA_OK;
195 if (AMediaMuxer_addTrack(muxer, format) >= 0) {
196 ALOGE("error: muxer.addTrack succeeds after muxer.writeSampleData");
197 isPass = false;
198 }
199 AMediaFormat_delete(format);
200 AMediaMuxer_delete(muxer);
201 fclose(ofp);
202 env->ReleaseStringUTFChars(jdstPath, cdstPath);
203 return static_cast<jboolean>(isPass);
204 }
205
nativeTestIfAddTrackSucceedsAfterStop(JNIEnv * env,jobject,jstring jdstPath)206 static jboolean nativeTestIfAddTrackSucceedsAfterStop(JNIEnv* env, jobject, jstring jdstPath) {
207 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
208 FILE* ofp = fopen(cdstPath, "wbe+");
209 bool isPass = true;
210 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_MPEG_4);
211 AMediaFormat* format = AMediaFormat_new();
212 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
213 ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
214 isPass &= trackID >= 0;
215 isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
216 isPass &= insertPerFrameSubtitles(muxer, 0, trackID) == AMEDIA_OK;
217 isPass &= AMediaMuxer_stop(muxer) == AMEDIA_OK;
218 if (AMediaMuxer_addTrack(muxer, format) >= 0) {
219 ALOGE("error: muxer.addTrack succeeds after muxer.stop");
220 isPass = false;
221 }
222 AMediaFormat_delete(format);
223 AMediaMuxer_delete(muxer);
224 fclose(ofp);
225 env->ReleaseStringUTFChars(jdstPath, cdstPath);
226 return static_cast<jboolean>(isPass);
227 }
228
nativeTestIfMuxerStartsBeforeAddTrack(JNIEnv * env,jobject,jstring jdstPath)229 static jboolean nativeTestIfMuxerStartsBeforeAddTrack(JNIEnv* env, jobject, jstring jdstPath) {
230 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
231 FILE* ofp = fopen(cdstPath, "wbe+");
232 bool isPass = true;
233 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_MPEG_4);
234 if (AMediaMuxer_start(muxer) == AMEDIA_OK) {
235 ALOGE("error: muxer.start succeeds before muxer.addTrack");
236 isPass = false;
237 }
238 AMediaMuxer_delete(muxer);
239 fclose(ofp);
240 env->ReleaseStringUTFChars(jdstPath, cdstPath);
241 return static_cast<jboolean>(isPass);
242 }
243
nativeTestIdempotentStart(JNIEnv * env,jobject,jstring jdstPath)244 static jboolean nativeTestIdempotentStart(JNIEnv* env, jobject, jstring jdstPath) {
245 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
246 FILE* ofp = fopen(cdstPath, "wbe+");
247 bool isPass = true;
248 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_MPEG_4);
249 AMediaFormat* format = AMediaFormat_new();
250 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
251 isPass &= AMediaMuxer_addTrack(muxer, format) >= 0;
252 isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
253 if (AMediaMuxer_start(muxer) == AMEDIA_OK) {
254 ALOGE("error: double muxer.start succeeds");
255 isPass = false;
256 }
257 AMediaFormat_delete(format);
258 AMediaMuxer_delete(muxer);
259 fclose(ofp);
260 env->ReleaseStringUTFChars(jdstPath, cdstPath);
261 return static_cast<jboolean>(isPass);
262 }
263
nativeTestIfMuxerStartsAfterWriteSampleData(JNIEnv * env,jobject,jstring jdstPath)264 static jboolean nativeTestIfMuxerStartsAfterWriteSampleData(JNIEnv* env, jobject,
265 jstring jdstPath) {
266 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
267 FILE* ofp = fopen(cdstPath, "wbe+");
268 bool isPass = true;
269 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_MPEG_4);
270 AMediaFormat* format = AMediaFormat_new();
271 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
272 ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
273 isPass &= trackID >= 0;
274 isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
275 isPass &= insertPerFrameSubtitles(muxer, 0, trackID) == AMEDIA_OK;
276 if (AMediaMuxer_start(muxer) == AMEDIA_OK) {
277 ALOGE("error: muxer.start succeeds after muxer.writeSampleData");
278 isPass = false;
279 }
280 AMediaFormat_delete(format);
281 AMediaMuxer_delete(muxer);
282 fclose(ofp);
283 env->ReleaseStringUTFChars(jdstPath, cdstPath);
284 return static_cast<jboolean>(isPass);
285 }
286
nativeTestIfMuxerStartsAfterStop(JNIEnv * env,jobject,jstring jdstPath)287 static jboolean nativeTestIfMuxerStartsAfterStop(JNIEnv* env, jobject, jstring jdstPath) {
288 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
289 FILE* ofp = fopen(cdstPath, "wbe+");
290 bool isPass = true;
291 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_MPEG_4);
292 AMediaFormat* format = AMediaFormat_new();
293 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
294 ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
295 isPass &= trackID >= 0;
296 isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
297 isPass &= insertPerFrameSubtitles(muxer, 0, trackID) == AMEDIA_OK;
298 isPass &= AMediaMuxer_stop(muxer) == AMEDIA_OK;
299 if (AMediaMuxer_start(muxer) == AMEDIA_OK) {
300 ALOGE("error: muxer.start succeeds after muxer.stop");
301 isPass = false;
302 }
303 AMediaFormat_delete(format);
304 AMediaMuxer_delete(muxer);
305 fclose(ofp);
306 env->ReleaseStringUTFChars(jdstPath, cdstPath);
307 return static_cast<jboolean>(isPass);
308 }
309
nativeTestStopOnANonStartedMuxer(JNIEnv * env,jobject,jstring jdstPath)310 static jboolean nativeTestStopOnANonStartedMuxer(JNIEnv* env, jobject, jstring jdstPath) {
311 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
312 FILE* ofp = fopen(cdstPath, "wbe+");
313 bool isPass = true;
314 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_MPEG_4);
315 AMediaFormat* format = AMediaFormat_new();
316 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
317 isPass &= AMediaMuxer_addTrack(muxer, format) >= 0;
318 if (AMEDIA_OK == AMediaMuxer_stop(muxer)) {
319 ALOGE("error: muxer.stop succeeds before muxer.start");
320 isPass = false;
321 }
322 AMediaFormat_delete(format);
323 AMediaMuxer_delete(muxer);
324 fclose(ofp);
325 env->ReleaseStringUTFChars(jdstPath, cdstPath);
326 return static_cast<jboolean>(isPass);
327 }
328
nativeTestIdempotentStop(JNIEnv * env,jobject,jstring jdstPath)329 static jboolean nativeTestIdempotentStop(JNIEnv* env, jobject, jstring jdstPath) {
330 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
331 FILE* ofp = fopen(cdstPath, "wbe+");
332 bool isPass = true;
333 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_MPEG_4);
334 AMediaFormat* format = AMediaFormat_new();
335 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
336 ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
337 isPass &= trackID >= 0;
338 isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
339 isPass &= insertPerFrameSubtitles(muxer, 0, trackID) == AMEDIA_OK;
340 isPass &= AMediaMuxer_stop(muxer) == AMEDIA_OK;
341 if (AMEDIA_OK == AMediaMuxer_stop(muxer)) {
342 ALOGE("error: double muxer.stop succeeds");
343 isPass = false;
344 }
345 AMediaFormat_delete(format);
346 AMediaMuxer_delete(muxer);
347 fclose(ofp);
348 env->ReleaseStringUTFChars(jdstPath, cdstPath);
349 return static_cast<jboolean>(isPass);
350 }
351
nativeTestSimpleStartStop(JNIEnv * env,jobject,jstring jdstPath)352 static jboolean nativeTestSimpleStartStop(JNIEnv* env, jobject, jstring jdstPath) {
353 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
354 FILE* ofp = fopen(cdstPath, "wbe+");
355 bool isPass = true;
356 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_MPEG_4);
357 AMediaFormat* format = AMediaFormat_new();
358 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
359 ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
360 isPass &= trackID >= 0;
361 isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
362 isPass &= AMediaMuxer_stop(muxer) == AMEDIA_OK;
363 AMediaFormat_delete(format);
364 AMediaMuxer_delete(muxer);
365 fclose(ofp);
366 env->ReleaseStringUTFChars(jdstPath, cdstPath);
367 return static_cast<jboolean>(isPass);
368 }
369
nativeTestIfWriteSampleDataRejectsInvalidTrackIndex(JNIEnv * env,jobject,jstring jdstPath)370 static jboolean nativeTestIfWriteSampleDataRejectsInvalidTrackIndex(JNIEnv* env, jobject,
371 jstring jdstPath) {
372 bool isPass = true;
373 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
374 FILE* ofp = fopen(cdstPath, "wbe+");
375 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_MPEG_4);
376 AMediaFormat* format = AMediaFormat_new();
377 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
378 ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
379 isPass &= trackID >= 0;
380 isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
381 isPass &= insertPerFrameSubtitles(muxer, 0, trackID) == AMEDIA_OK;
382 if (AMEDIA_OK == insertPerFrameSubtitles(muxer, 22000, trackID + 1)) {
383 ALOGE("error: muxer.writeSampleData succeeds for invalid track ID");
384 isPass = false;
385 }
386 AMediaFormat_delete(format);
387 AMediaMuxer_delete(muxer);
388 fclose(ofp);
389 env->ReleaseStringUTFChars(jdstPath, cdstPath);
390 return static_cast<jboolean>(isPass);
391 }
392
nativeTestIfWriteSampleDataRejectsInvalidPts(JNIEnv * env,jobject,jstring jdstPath)393 static jboolean nativeTestIfWriteSampleDataRejectsInvalidPts(JNIEnv* env, jobject,
394 jstring jdstPath) {
395 bool isPass = true;
396 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
397 FILE* ofp = fopen(cdstPath, "wbe+");
398 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_MPEG_4);
399 AMediaFormat* format = AMediaFormat_new();
400 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
401 ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
402 isPass &= trackID >= 0;
403 isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
404 isPass &= insertPerFrameSubtitles(muxer, 0, trackID) == AMEDIA_OK;
405 if (AMEDIA_OK == insertPerFrameSubtitles(muxer, -33000, trackID)) {
406 ALOGE("error: muxer.writeSampleData succeeds for invalid pts");
407 isPass = false;
408 }
409 AMediaFormat_delete(format);
410 AMediaMuxer_delete(muxer);
411 fclose(ofp);
412 env->ReleaseStringUTFChars(jdstPath, cdstPath);
413 return static_cast<jboolean>(isPass);
414 }
415
nativeTestIfWriteSampleDataSucceedsBeforeStart(JNIEnv * env,jobject,jstring jdstPath)416 static jboolean nativeTestIfWriteSampleDataSucceedsBeforeStart(JNIEnv* env, jobject,
417 jstring jdstPath) {
418 bool isPass = true;
419 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
420 FILE* ofp = fopen(cdstPath, "wbe+");
421 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_MPEG_4);
422 AMediaFormat* format = AMediaFormat_new();
423 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
424 ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
425 isPass &= trackID >= 0;
426 if (AMEDIA_OK == insertPerFrameSubtitles(muxer, 0, trackID)) {
427 ALOGE("error: muxer.writeSampleData succeeds before muxer.start");
428 isPass = false;
429 }
430 AMediaFormat_delete(format);
431 AMediaMuxer_delete(muxer);
432 fclose(ofp);
433 env->ReleaseStringUTFChars(jdstPath, cdstPath);
434 return static_cast<jboolean>(isPass);
435 }
436
nativeTestIfWriteSampleDataSucceedsAfterStop(JNIEnv * env,jobject,jstring jdstPath)437 static jboolean nativeTestIfWriteSampleDataSucceedsAfterStop(JNIEnv* env, jobject,
438 jstring jdstPath) {
439 bool isPass = true;
440 const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
441 FILE* ofp = fopen(cdstPath, "wbe+");
442 AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)OUTPUT_FORMAT_MPEG_4);
443 AMediaFormat* format = AMediaFormat_new();
444 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
445 ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
446 isPass &= trackID >= 0;
447 isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
448 isPass &= insertPerFrameSubtitles(muxer, 0, trackID) == AMEDIA_OK;
449 isPass &= AMediaMuxer_stop(muxer) == AMEDIA_OK;
450 if (AMEDIA_OK == insertPerFrameSubtitles(muxer, 33000, trackID)) {
451 ALOGE("error: muxer.writeSampleData succeeds after muxer.stop");
452 isPass = false;
453 }
454 AMediaFormat_delete(format);
455 AMediaMuxer_delete(muxer);
456 fclose(ofp);
457 env->ReleaseStringUTFChars(jdstPath, cdstPath);
458 return static_cast<jboolean>(isPass);
459 }
460
registerAndroidMediaV2CtsMuxerUnitTestApi(JNIEnv * env)461 int registerAndroidMediaV2CtsMuxerUnitTestApi(JNIEnv* env) {
462 const JNINativeMethod methodTable[] = {
463 {"nativeTestIfInvalidFdIsRejected", "()Z", (void*)nativeTestIfInvalidFdIsRejected},
464 {"nativeTestIfReadOnlyFdIsRejected", "(Ljava/lang/String;)Z",
465 (void*)nativeTestIfReadOnlyFdIsRejected},
466 {"nativeTestIfWriteOnlyFdIsRejected", "(Ljava/lang/String;)Z",
467 (void*)nativeTestIfWriteOnlyFdIsRejected},
468 {"nativeTestIfNonSeekableFdIsRejected", "(Ljava/lang/String;)Z",
469 (void*)nativeTestIfNonSeekableFdIsRejected},
470 {"nativeTestIfInvalidOutputFormatIsRejected", "(Ljava/lang/String;)Z",
471 (void*)nativeTestIfInvalidOutputFormatIsRejected},
472
473 {"nativeTestIfInvalidMediaFormatIsRejected", "(Ljava/lang/String;)Z",
474 (void*)nativeTestIfInvalidMediaFormatIsRejected},
475 {"nativeTestIfCorruptMediaFormatIsRejected", "(Ljava/lang/String;)Z",
476 (void*)nativeTestIfCorruptMediaFormatIsRejected},
477 {"nativeTestIfAddTrackSucceedsAfterStart", "(Ljava/lang/String;)Z",
478 (void*)nativeTestIfAddTrackSucceedsAfterStart},
479 {"nativeTestIfAddTrackSucceedsAfterWriteSampleData", "(Ljava/lang/String;)Z",
480 (void*)nativeTestIfAddTrackSucceedsAfterWriteSampleData},
481 {"nativeTestIfAddTrackSucceedsAfterStop", "(Ljava/lang/String;)Z",
482 (void*)nativeTestIfAddTrackSucceedsAfterStop},
483
484 {"nativeTestIfMuxerStartsBeforeAddTrack", "(Ljava/lang/String;)Z",
485 (void*)nativeTestIfMuxerStartsBeforeAddTrack},
486 {"nativeTestIdempotentStart", "(Ljava/lang/String;)Z",
487 (void*)nativeTestIdempotentStart},
488 {"nativeTestIfMuxerStartsAfterWriteSampleData", "(Ljava/lang/String;)Z",
489 (void*)nativeTestIfMuxerStartsAfterWriteSampleData},
490 {"nativeTestIfMuxerStartsAfterStop", "(Ljava/lang/String;)Z",
491 (void*)nativeTestIfMuxerStartsAfterStop},
492
493 {"nativeTestStopOnANonStartedMuxer", "(Ljava/lang/String;)Z",
494 (void*)nativeTestStopOnANonStartedMuxer},
495 {"nativeTestIdempotentStop", "(Ljava/lang/String;)Z", (void*)nativeTestIdempotentStop},
496 {"nativeTestSimpleStartStop", "(Ljava/lang/String;)Z",
497 (void*)nativeTestSimpleStartStop},
498
499 {"nativeTestIfWriteSampleDataRejectsInvalidTrackIndex", "(Ljava/lang/String;)Z",
500 (void*)nativeTestIfWriteSampleDataRejectsInvalidTrackIndex},
501 {"nativeTestIfWriteSampleDataRejectsInvalidPts", "(Ljava/lang/String;)Z",
502 (void*)nativeTestIfWriteSampleDataRejectsInvalidPts},
503 {"nativeTestIfWriteSampleDataSucceedsBeforeStart", "(Ljava/lang/String;)Z",
504 (void*)nativeTestIfWriteSampleDataSucceedsBeforeStart},
505 {"nativeTestIfWriteSampleDataSucceedsAfterStop", "(Ljava/lang/String;)Z",
506 (void*)nativeTestIfWriteSampleDataSucceedsAfterStop},
507 };
508 jclass c = env->FindClass("android/mediav2/cts/MuxerUnitTest$TestApiNative");
509 return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
510 }
511