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