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 <fcntl.h>
22 #include <jni.h>
23 #include <media/NdkMediaExtractor.h>
24 #include <media/NdkMediaFormat.h>
25 #include <media/NdkMediaMuxer.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, AMEDIAMUXER_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     if (ofp == nullptr) {
64         ALOGE("Unable to open file %s", cdstPath);
65         env->ReleaseStringUTFChars(jdstPath, cdstPath);
66         return static_cast<jboolean>(false);
67     }
68     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_THREE_GPP);
69     bool isPass = true;
70     if (muxer != nullptr) {
71         AMediaMuxer_delete(muxer);
72         ALOGE("error: muxer constructor accepts read-only file descriptor");
73         isPass = false;
74     }
75     fclose(ofp);
76     env->ReleaseStringUTFChars(jdstPath, cdstPath);
77     return static_cast<jboolean>(isPass);
78 }
79 
nativeTestIfNonSeekableFdIsRejected(JNIEnv *,jobject)80 static jboolean nativeTestIfNonSeekableFdIsRejected(JNIEnv*, jobject) {
81     int pipefd[2];
82     if (pipe(pipefd) == -1) {
83         ALOGE("unable to create pipe fd");
84         return false;
85     }
86     AMediaMuxer* muxer = AMediaMuxer_new(pipefd[1], AMEDIAMUXER_OUTPUT_FORMAT_THREE_GPP);
87     bool isPass = true;
88     if (muxer != nullptr) {
89         AMediaMuxer_delete(muxer);
90         ALOGE("error: muxer constructor accepts non-seekable file descriptor");
91         isPass = false;
92     }
93     close(pipefd[0]);
94     close(pipefd[1]);
95     return static_cast<jboolean>(isPass);
96 }
97 
nativeTestIfInvalidOutputFormatIsRejected(JNIEnv * env,jobject,jstring jdstPath)98 static jboolean nativeTestIfInvalidOutputFormatIsRejected(JNIEnv* env, jobject, jstring jdstPath) {
99     const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
100     FILE* ofp = fopen(cdstPath, "wbe+");
101     if (ofp == nullptr) {
102         ALOGE("Unable to open file %s", cdstPath);
103         env->ReleaseStringUTFChars(jdstPath, cdstPath);
104         return static_cast<jboolean>(false);
105     }
106     AMediaMuxer* muxer = nullptr;
107     bool isPass = true;
108 
109     muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)(-1));
110     if (muxer != nullptr) {
111         AMediaMuxer_delete(muxer);
112         ALOGE("error: muxer constructor accepts invalid (negative) output format");
113         isPass = false;
114         muxer = nullptr;
115     }
116     muxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)(LOCAL_AMEDIAMUXER_OUTPUT_FORMAT_LAST + 1));
117     if (muxer != nullptr) {
118         AMediaMuxer_delete(muxer);
119         ALOGE("error: muxer constructor accepts invalid (too large) output format");
120         isPass = false;
121         muxer = nullptr;
122     }
123     fclose(ofp);
124     env->ReleaseStringUTFChars(jdstPath, cdstPath);
125     return static_cast<jboolean>(isPass);
126 }
127 
nativeTestIfInvalidMediaFormatIsRejected(JNIEnv * env,jobject,jstring jdstPath)128 static jboolean nativeTestIfInvalidMediaFormatIsRejected(JNIEnv* env, jobject, jstring jdstPath) {
129     const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
130     FILE* ofp = fopen(cdstPath, "wbe+");
131     if (ofp == nullptr) {
132         ALOGE("Unable to open file %s", cdstPath);
133         env->ReleaseStringUTFChars(jdstPath, cdstPath);
134         return static_cast<jboolean>(false);
135     }
136     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
137     AMediaFormat* format = AMediaFormat_new();
138     bool isPass = true;
139     if (AMediaMuxer_addTrack(muxer, format) >= 0) {
140         ALOGE("error: muxer.addTrack succeeds with format that has no mediaType key");
141         isPass = false;
142     }
143 
144     AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "text/cea-608");
145     if (AMediaMuxer_addTrack(muxer, format) >= 0) {
146         ALOGE("error: muxer.addTrack succeeds with format whose mediaType is non-compliant");
147         isPass = false;
148     }
149     AMediaFormat_delete(format);
150     AMediaMuxer_delete(muxer);
151     fclose(ofp);
152     env->ReleaseStringUTFChars(jdstPath, cdstPath);
153     return static_cast<jboolean>(isPass);
154 }
155 
nativeTestIfCorruptMediaFormatIsRejected(JNIEnv * env,jobject,jstring jdstPath)156 static jboolean nativeTestIfCorruptMediaFormatIsRejected(JNIEnv* env, jobject, jstring jdstPath) {
157     const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
158     FILE* ofp = fopen(cdstPath, "wbe+");
159     if (ofp == nullptr) {
160         ALOGE("Unable to open file %s", cdstPath);
161         env->ReleaseStringUTFChars(jdstPath, cdstPath);
162         return static_cast<jboolean>(false);
163     }
164     bool isPass = true;
165     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
166     AMediaFormat* format = AMediaFormat_new();
167     AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_AUDIO_AAC);
168     AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, -1);
169     if (AMediaMuxer_addTrack(muxer, format) >= 0) {
170         ALOGE("error: muxer.addTrack succeeds with erroneous key-value pairs in media format");
171         isPass = false;
172     }
173     AMediaFormat_delete(format);
174     AMediaMuxer_delete(muxer);
175     fclose(ofp);
176     env->ReleaseStringUTFChars(jdstPath, cdstPath);
177     return static_cast<jboolean>(isPass);
178 }
179 
nativeTestIfAddTrackSucceedsAfterStart(JNIEnv * env,jobject,jstring jdstPath)180 static jboolean nativeTestIfAddTrackSucceedsAfterStart(JNIEnv* env, jobject, jstring jdstPath) {
181     const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
182     FILE* ofp = fopen(cdstPath, "wbe+");
183     if (ofp == nullptr) {
184         ALOGE("Unable to open file %s", cdstPath);
185         env->ReleaseStringUTFChars(jdstPath, cdstPath);
186         return static_cast<jboolean>(false);
187     }
188     bool isPass = true;
189     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
190     AMediaFormat* format = AMediaFormat_new();
191     AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
192     isPass &= AMediaMuxer_addTrack(muxer, format) >= 0;
193     isPass &= (AMediaMuxer_start(muxer) == AMEDIA_OK);
194     if (AMediaMuxer_addTrack(muxer, format) >= 0) {
195         ALOGE("error: muxer.addTrack succeeds after muxer.start");
196         isPass = false;
197     }
198     AMediaFormat_delete(format);
199     AMediaMuxer_delete(muxer);
200     fclose(ofp);
201     env->ReleaseStringUTFChars(jdstPath, cdstPath);
202     return static_cast<jboolean>(isPass);
203 }
204 
nativeTestIfAddTrackSucceedsAfterWriteSampleData(JNIEnv * env,jobject,jstring jdstPath)205 static jboolean nativeTestIfAddTrackSucceedsAfterWriteSampleData(JNIEnv* env, jobject,
206                                                                  jstring jdstPath) {
207     const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
208     FILE* ofp = fopen(cdstPath, "wbe+");
209     if (ofp == nullptr) {
210         ALOGE("Unable to open file %s", cdstPath);
211         env->ReleaseStringUTFChars(jdstPath, cdstPath);
212         return static_cast<jboolean>(false);
213     }
214     bool isPass = true;
215     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
216     AMediaFormat* format = AMediaFormat_new();
217     AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
218     ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
219     isPass &= trackID >= 0;
220     isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
221     isPass &= insertPerFrameSubtitles(muxer, 0, trackID) == AMEDIA_OK;
222     if (AMediaMuxer_addTrack(muxer, format) >= 0) {
223         ALOGE("error: muxer.addTrack succeeds after muxer.writeSampleData");
224         isPass = false;
225     }
226     AMediaFormat_delete(format);
227     AMediaMuxer_delete(muxer);
228     fclose(ofp);
229     env->ReleaseStringUTFChars(jdstPath, cdstPath);
230     return static_cast<jboolean>(isPass);
231 }
232 
nativeTestIfAddTrackSucceedsAfterStop(JNIEnv * env,jobject,jstring jdstPath)233 static jboolean nativeTestIfAddTrackSucceedsAfterStop(JNIEnv* env, jobject, jstring jdstPath) {
234     const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
235     FILE* ofp = fopen(cdstPath, "wbe+");
236     if (ofp == nullptr) {
237         ALOGE("Unable to open file %s", cdstPath);
238         env->ReleaseStringUTFChars(jdstPath, cdstPath);
239         return static_cast<jboolean>(false);
240     }
241     bool isPass = true;
242     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
243     AMediaFormat* format = AMediaFormat_new();
244     AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
245     ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
246     isPass &= trackID >= 0;
247     isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
248     isPass &= insertPerFrameSubtitles(muxer, 0, trackID) == AMEDIA_OK;
249     isPass &= AMediaMuxer_stop(muxer) == AMEDIA_OK;
250     if (AMediaMuxer_addTrack(muxer, format) >= 0) {
251         ALOGE("error: muxer.addTrack succeeds after muxer.stop");
252         isPass = false;
253     }
254     AMediaFormat_delete(format);
255     AMediaMuxer_delete(muxer);
256     fclose(ofp);
257     env->ReleaseStringUTFChars(jdstPath, cdstPath);
258     return static_cast<jboolean>(isPass);
259 }
260 
nativeTestIfMuxerStartsBeforeAddTrack(JNIEnv * env,jobject,jstring jdstPath)261 static jboolean nativeTestIfMuxerStartsBeforeAddTrack(JNIEnv* env, jobject, jstring jdstPath) {
262     const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
263     FILE* ofp = fopen(cdstPath, "wbe+");
264     if (ofp == nullptr) {
265         ALOGE("Unable to open file %s", cdstPath);
266         env->ReleaseStringUTFChars(jdstPath, cdstPath);
267         return static_cast<jboolean>(false);
268     }
269     bool isPass = true;
270     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
271     if (AMediaMuxer_start(muxer) == AMEDIA_OK) {
272         ALOGE("error: muxer.start succeeds before muxer.addTrack");
273         isPass = false;
274     }
275     AMediaMuxer_delete(muxer);
276     fclose(ofp);
277     env->ReleaseStringUTFChars(jdstPath, cdstPath);
278     return static_cast<jboolean>(isPass);
279 }
280 
nativeTestIdempotentStart(JNIEnv * env,jobject,jstring jdstPath)281 static jboolean nativeTestIdempotentStart(JNIEnv* env, jobject, jstring jdstPath) {
282     const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
283     FILE* ofp = fopen(cdstPath, "wbe+");
284     if (ofp == nullptr) {
285         ALOGE("Unable to open file %s", cdstPath);
286         env->ReleaseStringUTFChars(jdstPath, cdstPath);
287         return static_cast<jboolean>(false);
288     }
289     bool isPass = true;
290     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
291     AMediaFormat* format = AMediaFormat_new();
292     AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
293     isPass &= AMediaMuxer_addTrack(muxer, format) >= 0;
294     isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
295     if (AMediaMuxer_start(muxer) == AMEDIA_OK) {
296         ALOGE("error: double muxer.start succeeds");
297         isPass = false;
298     }
299     AMediaFormat_delete(format);
300     AMediaMuxer_delete(muxer);
301     fclose(ofp);
302     env->ReleaseStringUTFChars(jdstPath, cdstPath);
303     return static_cast<jboolean>(isPass);
304 }
305 
nativeTestIfMuxerStartsAfterWriteSampleData(JNIEnv * env,jobject,jstring jdstPath)306 static jboolean nativeTestIfMuxerStartsAfterWriteSampleData(JNIEnv* env, jobject,
307                                                             jstring jdstPath) {
308     const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
309     FILE* ofp = fopen(cdstPath, "wbe+");
310     if (ofp == nullptr) {
311         ALOGE("Unable to open file %s", cdstPath);
312         env->ReleaseStringUTFChars(jdstPath, cdstPath);
313         return static_cast<jboolean>(false);
314     }
315     bool isPass = true;
316     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
317     AMediaFormat* format = AMediaFormat_new();
318     AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
319     ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
320     isPass &= trackID >= 0;
321     isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
322     isPass &= insertPerFrameSubtitles(muxer, 0, trackID) == AMEDIA_OK;
323     if (AMediaMuxer_start(muxer) == AMEDIA_OK) {
324         ALOGE("error: muxer.start succeeds after muxer.writeSampleData");
325         isPass = false;
326     }
327     AMediaFormat_delete(format);
328     AMediaMuxer_delete(muxer);
329     fclose(ofp);
330     env->ReleaseStringUTFChars(jdstPath, cdstPath);
331     return static_cast<jboolean>(isPass);
332 }
333 
nativeTestIfMuxerStartsAfterStop(JNIEnv * env,jobject,jstring jdstPath)334 static jboolean nativeTestIfMuxerStartsAfterStop(JNIEnv* env, jobject, jstring jdstPath) {
335     const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
336     FILE* ofp = fopen(cdstPath, "wbe+");
337     if (ofp == nullptr) {
338         ALOGE("Unable to open file %s", cdstPath);
339         env->ReleaseStringUTFChars(jdstPath, cdstPath);
340         return static_cast<jboolean>(false);
341     }
342     bool isPass = true;
343     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
344     AMediaFormat* format = AMediaFormat_new();
345     AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
346     ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
347     isPass &= trackID >= 0;
348     isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
349     isPass &= insertPerFrameSubtitles(muxer, 0, trackID) == AMEDIA_OK;
350     isPass &= AMediaMuxer_stop(muxer) == AMEDIA_OK;
351     if (AMediaMuxer_start(muxer) == AMEDIA_OK) {
352         ALOGE("error: muxer.start succeeds after muxer.stop");
353         isPass = false;
354     }
355     AMediaFormat_delete(format);
356     AMediaMuxer_delete(muxer);
357     fclose(ofp);
358     env->ReleaseStringUTFChars(jdstPath, cdstPath);
359     return static_cast<jboolean>(isPass);
360 }
361 
nativeTestStopOnANonStartedMuxer(JNIEnv * env,jobject,jstring jdstPath)362 static jboolean nativeTestStopOnANonStartedMuxer(JNIEnv* env, jobject, jstring jdstPath) {
363     const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
364     FILE* ofp = fopen(cdstPath, "wbe+");
365     if (ofp == nullptr) {
366         ALOGE("Unable to open file %s", cdstPath);
367         env->ReleaseStringUTFChars(jdstPath, cdstPath);
368         return static_cast<jboolean>(false);
369     }
370     bool isPass = true;
371     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
372     AMediaFormat* format = AMediaFormat_new();
373     AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
374     isPass &= AMediaMuxer_addTrack(muxer, format) >= 0;
375     if (AMEDIA_OK == AMediaMuxer_stop(muxer)) {
376         ALOGE("error: muxer.stop succeeds before muxer.start");
377         isPass = false;
378     }
379     AMediaFormat_delete(format);
380     AMediaMuxer_delete(muxer);
381     fclose(ofp);
382     env->ReleaseStringUTFChars(jdstPath, cdstPath);
383     return static_cast<jboolean>(isPass);
384 }
385 
nativeTestIdempotentStop(JNIEnv * env,jobject,jstring jdstPath)386 static jboolean nativeTestIdempotentStop(JNIEnv* env, jobject, jstring jdstPath) {
387     const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
388     FILE* ofp = fopen(cdstPath, "wbe+");
389     if (ofp == nullptr) {
390         ALOGE("Unable to open file %s", cdstPath);
391         env->ReleaseStringUTFChars(jdstPath, cdstPath);
392         return static_cast<jboolean>(false);
393     }
394     bool isPass = true;
395     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
396     AMediaFormat* format = AMediaFormat_new();
397     AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
398     ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
399     isPass &= trackID >= 0;
400     isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
401     isPass &= insertPerFrameSubtitles(muxer, 0, trackID) == AMEDIA_OK;
402     isPass &= AMediaMuxer_stop(muxer) == AMEDIA_OK;
403     if (AMEDIA_OK == AMediaMuxer_stop(muxer)) {
404         ALOGE("error: double muxer.stop succeeds");
405         isPass = false;
406     }
407     AMediaFormat_delete(format);
408     AMediaMuxer_delete(muxer);
409     fclose(ofp);
410     env->ReleaseStringUTFChars(jdstPath, cdstPath);
411     return static_cast<jboolean>(isPass);
412 }
413 
nativeTestSimpleStartStop(JNIEnv * env,jobject,jstring jdstPath)414 static jboolean nativeTestSimpleStartStop(JNIEnv* env, jobject, jstring jdstPath) {
415     const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
416     FILE* ofp = fopen(cdstPath, "wbe+");
417     if (ofp == nullptr) {
418         ALOGE("Unable to open file %s", cdstPath);
419         env->ReleaseStringUTFChars(jdstPath, cdstPath);
420         return static_cast<jboolean>(false);
421     }
422     bool isPass = true;
423     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
424     AMediaFormat* format = AMediaFormat_new();
425     AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
426     ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
427     isPass &= trackID >= 0;
428     isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
429     isPass &= AMediaMuxer_stop(muxer) == AMEDIA_OK;
430     AMediaFormat_delete(format);
431     AMediaMuxer_delete(muxer);
432     fclose(ofp);
433     env->ReleaseStringUTFChars(jdstPath, cdstPath);
434     return static_cast<jboolean>(isPass);
435 }
436 
nativeTestIfWriteSampleDataRejectsInvalidTrackIndex(JNIEnv * env,jobject,jstring jdstPath)437 static jboolean nativeTestIfWriteSampleDataRejectsInvalidTrackIndex(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     if (ofp == nullptr) {
443         ALOGE("Unable to open file %s", cdstPath);
444         env->ReleaseStringUTFChars(jdstPath, cdstPath);
445         return static_cast<jboolean>(false);
446     }
447     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
448     AMediaFormat* format = AMediaFormat_new();
449     AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
450     ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
451     isPass &= trackID >= 0;
452     isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
453     isPass &= insertPerFrameSubtitles(muxer, 0, trackID) == AMEDIA_OK;
454     if (AMEDIA_OK == insertPerFrameSubtitles(muxer, 22000, trackID + 1)) {
455         ALOGE("error: muxer.writeSampleData succeeds for invalid track ID");
456         isPass = false;
457     }
458     AMediaFormat_delete(format);
459     AMediaMuxer_delete(muxer);
460     fclose(ofp);
461     env->ReleaseStringUTFChars(jdstPath, cdstPath);
462     return static_cast<jboolean>(isPass);
463 }
464 
nativeTestIfWriteSampleDataRejectsInvalidPts(JNIEnv * env,jobject,jstring jdstPath)465 static jboolean nativeTestIfWriteSampleDataRejectsInvalidPts(JNIEnv* env, jobject,
466                                                              jstring jdstPath) {
467     bool isPass = true;
468     const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
469     FILE* ofp = fopen(cdstPath, "wbe+");
470     if (ofp == nullptr) {
471         ALOGE("Unable to open file %s", cdstPath);
472         env->ReleaseStringUTFChars(jdstPath, cdstPath);
473         return static_cast<jboolean>(false);
474     }
475     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
476     AMediaFormat* format = AMediaFormat_new();
477     AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
478     ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
479     isPass &= trackID >= 0;
480     isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
481     isPass &= insertPerFrameSubtitles(muxer, 0, trackID) == AMEDIA_OK;
482     if (AMEDIA_OK == insertPerFrameSubtitles(muxer, -33000, trackID)) {
483         ALOGE("error: muxer.writeSampleData succeeds for invalid pts");
484         isPass = false;
485     }
486     AMediaFormat_delete(format);
487     AMediaMuxer_delete(muxer);
488     fclose(ofp);
489     env->ReleaseStringUTFChars(jdstPath, cdstPath);
490     return static_cast<jboolean>(isPass);
491 }
492 
nativeTestIfWriteSampleDataSucceedsBeforeStart(JNIEnv * env,jobject,jstring jdstPath)493 static jboolean nativeTestIfWriteSampleDataSucceedsBeforeStart(JNIEnv* env, jobject,
494                                                                jstring jdstPath) {
495     bool isPass = true;
496     const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
497     FILE* ofp = fopen(cdstPath, "wbe+");
498     if (ofp == nullptr) {
499         ALOGE("Unable to open file %s", cdstPath);
500         env->ReleaseStringUTFChars(jdstPath, cdstPath);
501         return static_cast<jboolean>(false);
502     }
503     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
504     AMediaFormat* format = AMediaFormat_new();
505     AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
506     ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
507     isPass &= trackID >= 0;
508     if (AMEDIA_OK == insertPerFrameSubtitles(muxer, 0, trackID)) {
509         ALOGE("error: muxer.writeSampleData succeeds before muxer.start");
510         isPass = false;
511     }
512     AMediaFormat_delete(format);
513     AMediaMuxer_delete(muxer);
514     fclose(ofp);
515     env->ReleaseStringUTFChars(jdstPath, cdstPath);
516     return static_cast<jboolean>(isPass);
517 }
518 
nativeTestIfWriteSampleDataSucceedsAfterStop(JNIEnv * env,jobject,jstring jdstPath)519 static jboolean nativeTestIfWriteSampleDataSucceedsAfterStop(JNIEnv* env, jobject,
520                                                              jstring jdstPath) {
521     bool isPass = true;
522     const char* cdstPath = env->GetStringUTFChars(jdstPath, nullptr);
523     FILE* ofp = fopen(cdstPath, "wbe+");
524     if (ofp == nullptr) {
525         ALOGE("Unable to open file %s", cdstPath);
526         env->ReleaseStringUTFChars(jdstPath, cdstPath);
527         return static_cast<jboolean>(false);
528     }
529     AMediaMuxer* muxer = AMediaMuxer_new(fileno(ofp), AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
530     AMediaFormat* format = AMediaFormat_new();
531     AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "application/x-subrip");
532     ssize_t trackID = AMediaMuxer_addTrack(muxer, format);
533     isPass &= trackID >= 0;
534     isPass &= AMediaMuxer_start(muxer) == AMEDIA_OK;
535     isPass &= insertPerFrameSubtitles(muxer, 0, trackID) == AMEDIA_OK;
536     isPass &= AMediaMuxer_stop(muxer) == AMEDIA_OK;
537     if (AMEDIA_OK == insertPerFrameSubtitles(muxer, 33000, trackID)) {
538         ALOGE("error: muxer.writeSampleData succeeds after muxer.stop");
539         isPass = false;
540     }
541     AMediaFormat_delete(format);
542     AMediaMuxer_delete(muxer);
543     fclose(ofp);
544     env->ReleaseStringUTFChars(jdstPath, cdstPath);
545     return static_cast<jboolean>(isPass);
546 }
547 
registerAndroidMediaV2CtsMuxerUnitTestApi(JNIEnv * env)548 int registerAndroidMediaV2CtsMuxerUnitTestApi(JNIEnv* env) {
549     const JNINativeMethod methodTable[] = {
550             {"nativeTestIfInvalidFdIsRejected", "()Z", (void*)nativeTestIfInvalidFdIsRejected},
551             {"nativeTestIfReadOnlyFdIsRejected", "(Ljava/lang/String;)Z",
552              (void*)nativeTestIfReadOnlyFdIsRejected},
553             {"nativeTestIfNonSeekableFdIsRejected", "()Z",
554              (void*)nativeTestIfNonSeekableFdIsRejected},
555             {"nativeTestIfInvalidOutputFormatIsRejected", "(Ljava/lang/String;)Z",
556              (void*)nativeTestIfInvalidOutputFormatIsRejected},
557 
558             {"nativeTestIfInvalidMediaFormatIsRejected", "(Ljava/lang/String;)Z",
559              (void*)nativeTestIfInvalidMediaFormatIsRejected},
560             {"nativeTestIfCorruptMediaFormatIsRejected", "(Ljava/lang/String;)Z",
561              (void*)nativeTestIfCorruptMediaFormatIsRejected},
562             {"nativeTestIfAddTrackSucceedsAfterStart", "(Ljava/lang/String;)Z",
563              (void*)nativeTestIfAddTrackSucceedsAfterStart},
564             {"nativeTestIfAddTrackSucceedsAfterWriteSampleData", "(Ljava/lang/String;)Z",
565              (void*)nativeTestIfAddTrackSucceedsAfterWriteSampleData},
566             {"nativeTestIfAddTrackSucceedsAfterStop", "(Ljava/lang/String;)Z",
567              (void*)nativeTestIfAddTrackSucceedsAfterStop},
568 
569             {"nativeTestIfMuxerStartsBeforeAddTrack", "(Ljava/lang/String;)Z",
570              (void*)nativeTestIfMuxerStartsBeforeAddTrack},
571             {"nativeTestIdempotentStart", "(Ljava/lang/String;)Z",
572              (void*)nativeTestIdempotentStart},
573             {"nativeTestIfMuxerStartsAfterWriteSampleData", "(Ljava/lang/String;)Z",
574              (void*)nativeTestIfMuxerStartsAfterWriteSampleData},
575             {"nativeTestIfMuxerStartsAfterStop", "(Ljava/lang/String;)Z",
576              (void*)nativeTestIfMuxerStartsAfterStop},
577 
578             {"nativeTestStopOnANonStartedMuxer", "(Ljava/lang/String;)Z",
579              (void*)nativeTestStopOnANonStartedMuxer},
580             {"nativeTestIdempotentStop", "(Ljava/lang/String;)Z", (void*)nativeTestIdempotentStop},
581             {"nativeTestSimpleStartStop", "(Ljava/lang/String;)Z",
582              (void*)nativeTestSimpleStartStop},
583 
584             {"nativeTestIfWriteSampleDataRejectsInvalidTrackIndex", "(Ljava/lang/String;)Z",
585              (void*)nativeTestIfWriteSampleDataRejectsInvalidTrackIndex},
586             {"nativeTestIfWriteSampleDataRejectsInvalidPts", "(Ljava/lang/String;)Z",
587              (void*)nativeTestIfWriteSampleDataRejectsInvalidPts},
588             {"nativeTestIfWriteSampleDataSucceedsBeforeStart", "(Ljava/lang/String;)Z",
589              (void*)nativeTestIfWriteSampleDataSucceedsBeforeStart},
590             {"nativeTestIfWriteSampleDataSucceedsAfterStop", "(Ljava/lang/String;)Z",
591              (void*)nativeTestIfWriteSampleDataSucceedsAfterStop},
592     };
593     jclass c = env->FindClass("android/mediav2/cts/MuxerUnitTest$TestApiNative");
594     return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
595 }
596