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