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 package android.mediav2.cts;
18 
19 import android.media.MediaCodec;
20 import android.media.MediaFormat;
21 import android.media.MediaMuxer;
22 
23 import androidx.test.filters.SmallTest;
24 
25 import org.junit.After;
26 import org.junit.Before;
27 import org.junit.Ignore;
28 import org.junit.Rule;
29 import org.junit.Test;
30 import org.junit.experimental.runners.Enclosed;
31 import org.junit.rules.TestName;
32 import org.junit.runner.RunWith;
33 
34 import java.io.File;
35 import java.io.FileDescriptor;
36 import java.io.FileInputStream;
37 import java.io.FileOutputStream;
38 import java.io.IOException;
39 import java.nio.ByteBuffer;
40 import java.nio.charset.StandardCharsets;
41 
42 import static android.system.Os.pipe;
43 import static org.junit.Assert.assertTrue;
44 import static org.junit.Assert.fail;
45 
46 /**
47  * Tests MediaMuxer API that are independent of MediaMuxer.OutputFormat. Constructors,
48  * addTrack, start, writeSampleData, stop, release are independent of OutputFormat selected.
49  * Legality of these APIs are tested in this class.
50  */
51 @RunWith(Enclosed.class)
52 public class MuxerUnitTest {
53     // duplicate definitions of hide fields of MediaMuxer.OutputFormat.
54     private static final int MUXER_OUTPUT_LAST = MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG;
55 
56     @NonMediaMainlineTest
57     @SmallTest
58     public static class TestApi {
59         @Rule
60         public TestName testName = new TestName();
61 
62         @Before
prologue()63         public void prologue() throws IOException {
64             mOutMedia = File.createTempFile(testName.getMethodName(), ".out");
65             mOutLoc = mOutMedia.getAbsolutePath();
66         }
67 
68         @After
epilogue()69         public void epilogue() {
70             new File(mOutLoc).delete();
71         }
72 
73         private File mOutMedia;
74         private String mOutLoc;
75 
76         // Insert one frame SubRip
insertPerFrameSubtitles(MediaMuxer muxer, long presentationTimeUs, int trackID)77         static private void insertPerFrameSubtitles(MediaMuxer muxer, long presentationTimeUs,
78                 int trackID) {
79             byte[] greeting = "hello world".getBytes(StandardCharsets.UTF_8);
80             ByteBuffer metaBuff = ByteBuffer.allocate(greeting.length);
81             metaBuff.put(greeting);
82             MediaCodec.BufferInfo metaInfo = new MediaCodec.BufferInfo();
83             metaInfo.offset = 0;
84             metaInfo.size = greeting.length;
85             metaInfo.presentationTimeUs = presentationTimeUs;
86             metaInfo.flags = 0;
87             muxer.writeSampleData(trackID, metaBuff, metaInfo);
88         }
89 
90         @Test
testIfNullPathIsRejected()91         public void testIfNullPathIsRejected() {
92             MediaMuxer muxer = null;
93             try {
94                 String nullPath = null;
95                 muxer = new MediaMuxer(nullPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP);
96                 fail("null destination path accepted by constructor");
97             } catch (IllegalArgumentException e) {
98                 // expected
99             } catch (Exception e) {
100                 fail(e.getMessage());
101             } finally {
102                 if (null != muxer) muxer.release();
103             }
104         }
105 
106         @Test
testIfNullFdIsRejected()107         public void testIfNullFdIsRejected() {
108             MediaMuxer muxer = null;
109             try {
110                 FileDescriptor fd = null;
111                 muxer = new MediaMuxer(fd, MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP);
112                 fail("null fd accepted by constructor");
113             } catch (IllegalArgumentException e) {
114                 // expected
115             } catch (Exception e) {
116                 fail(e.getMessage());
117             } finally {
118                 if (null != muxer) muxer.release();
119             }
120         }
121 
122         @Test
testIfInvalidFdIsRejected()123         public void testIfInvalidFdIsRejected() {
124             MediaMuxer muxer = null;
125             try {
126                 FileDescriptor fd = new FileDescriptor();
127                 muxer = new MediaMuxer(fd, MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP);
128                 fail("Invalid fd accepted by constructor");
129             } catch (IllegalArgumentException e) {
130                 // expected
131             } catch (Exception e) {
132                 fail(e.getMessage());
133             } finally {
134                 if (null != muxer) muxer.release();
135             }
136         }
137 
138         @Test
testIfReadOnlyFdIsRejected()139         public void testIfReadOnlyFdIsRejected() {
140             MediaMuxer muxer = null;
141             try (FileInputStream fInp = new FileInputStream(mOutMedia)) {
142                 muxer = new MediaMuxer(fInp.getFD(), MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP);
143                 fail("fd with read-only attribute accepted by constructor");
144             } catch (IOException e) {
145                 // expected
146             } catch (Exception e) {
147                 fail(e.getMessage());
148             } finally {
149                 if (null != muxer) muxer.release();
150             }
151         }
152 
153         @Test
154         @Ignore("TODO(b/146417874)")
testIfWriteOnlyFdIsRejected()155         public void testIfWriteOnlyFdIsRejected() {
156             MediaMuxer muxer = null;
157             try (FileOutputStream fOut = new FileOutputStream(mOutMedia)) {
158                 muxer = new MediaMuxer(fOut.getFD(), MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM);
159                 fail("fd with write only attribute accepted by constructor");
160             } catch (Exception e) {
161                 // expected
162             } finally {
163                 if (null != muxer) muxer.release();
164             }
165             assertTrue(mOutMedia.delete());
166         }
167 
168         @Test
169         @Ignore("TODO(b/146417874)")
testIfNonSeekableFdIsRejected()170         public void testIfNonSeekableFdIsRejected() {
171             MediaMuxer muxer = null;
172             try {
173                 FileDescriptor[] fd = pipe();
174                 muxer = new MediaMuxer(fd[1], MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP);
175                 fail("pipe, a non-seekable fd accepted by constructor");
176             } catch (IOException e) {
177                 // expected
178             } catch (Exception e) {
179                 fail(e.getMessage());
180             } finally {
181                 if (null != muxer) muxer.release();
182             }
183         }
184 
185         @Test
testIfInvalidOutputFormatIsRejected()186         public void testIfInvalidOutputFormatIsRejected() {
187             MediaMuxer muxer = null;
188             try {
189                 muxer = new MediaMuxer(mOutLoc, MUXER_OUTPUT_LAST + 1);
190                 fail("Invalid Media format accepted by constructor");
191             } catch (IllegalArgumentException e) {
192                 // expected
193             } catch (Exception e) {
194                 fail(e.getMessage());
195             } finally {
196                 if (null != muxer) muxer.release();
197             }
198         }
199 
200         @Test
testIfNullMediaFormatIsRejected()201         public void testIfNullMediaFormatIsRejected() throws IOException {
202             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
203             try {
204                 muxer.addTrack(null);
205                 fail("null media format accepted by addTrack");
206             } catch (IllegalArgumentException e) {
207                 // expected
208             } finally {
209                 muxer.release();
210             }
211         }
212 
213         @Test
testIfInvalidMediaFormatIsRejected()214         public void testIfInvalidMediaFormatIsRejected() throws IOException {
215             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
216             try {
217                 // Invalid media format - no mime key
218                 try {
219                     muxer.addTrack(new MediaFormat());
220                     fail("Invalid media format accepted by addTrack");
221                 } catch (IllegalArgumentException e) {
222                     // expected
223                 }
224 
225                 // metadata mime format shall start with "application/*"
226                 try {
227                     MediaFormat format = new MediaFormat();
228                     format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_CEA_608);
229                     muxer.addTrack(format);
230                     fail("Invalid media format accepted by addTrack");
231                 } catch (IllegalArgumentException | IllegalStateException e) {
232                     // Ideally check only for IllegalArgumentException.
233                     // expected
234                 }
235             } finally {
236                 muxer.release();
237             }
238         }
239 
240         @Test
241         @Ignore("TODO(b/146923138)")
testIfCorruptMediaFormatIsRejected()242         public void testIfCorruptMediaFormatIsRejected() throws IOException {
243             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
244 
245              /* TODO: Audio/Video formats, have certain keys required to be set. It is noticed
246                 that even when these keys are not set, no exceptions were raised. Do we need to
247                 add fixtures for those cases. */
248             try {
249                 MediaFormat format = new MediaFormat();
250                 format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AAC);
251                 format.setInteger(MediaFormat.KEY_SAMPLE_RATE, -1);
252                 muxer.addTrack(format);
253                 muxer.start();
254                 fail("muxer accepts media format with required key-value pairs missing");
255             } catch (Exception e) {
256                 // expected
257             } finally {
258                 muxer.release();
259             }
260         }
261 
262         @Test
testIfAddTrackSucceedsAfterStart()263         public void testIfAddTrackSucceedsAfterStart() throws IOException {
264             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
265             try {
266                 MediaFormat format = new MediaFormat();
267                 format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
268                 muxer.addTrack(format);
269                 muxer.start();
270                 muxer.addTrack(format);
271                 fail("muxer.addTrack() succeeded after muxer.start()");
272             } catch (IllegalStateException e) {
273                 // expected
274             } catch (Exception e) {
275                 fail(e.getMessage());
276             } finally {
277                 muxer.release();
278             }
279         }
280 
281         @Test
testIfAddTrackSucceedsAfterWriteSampleData()282         public void testIfAddTrackSucceedsAfterWriteSampleData() throws IOException {
283             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
284             try {
285                 MediaFormat format = new MediaFormat();
286                 format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
287                 int trackID = muxer.addTrack(format);
288                 muxer.start();
289                 insertPerFrameSubtitles(muxer, 0, trackID);
290                 muxer.addTrack(format);
291                 fail("muxer.addTrack() succeeded after muxer.writeSampleData()");
292             } catch (IllegalStateException e) {
293                 // expected
294             } catch (Exception e) {
295                 fail(e.getMessage());
296             } finally {
297                 muxer.release();
298             }
299         }
300 
301         @Test
testIfAddTrackSucceedsAfterStop()302         public void testIfAddTrackSucceedsAfterStop() throws IOException {
303             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
304             try {
305                 MediaFormat format = new MediaFormat();
306                 format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
307                 int trackID = muxer.addTrack(format);
308                 muxer.start();
309                 insertPerFrameSubtitles(muxer, 0, trackID);
310                 muxer.stop();
311                 muxer.addTrack(format);
312                 fail("muxer.addTrack() succeeded after muxer.stop()");
313             } catch (IllegalStateException e) {
314                 // expected
315             } catch (Exception e) {
316                 fail(e.getMessage());
317             } finally {
318                 muxer.release();
319             }
320         }
321 
322         @Test
testIfAddTrackSucceedsAfterRelease()323         public void testIfAddTrackSucceedsAfterRelease() throws IOException {
324             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
325             try {
326                 MediaFormat format = new MediaFormat();
327                 format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
328                 muxer.release();
329                 muxer.addTrack(format);
330                 fail("muxer.addTrack() succeeded after muxer.release()");
331             } catch (IllegalStateException e) {
332                 // expected
333             } catch (Exception e) {
334                 fail(e.getMessage());
335             } finally {
336                 muxer.release();
337             }
338         }
339 
340         @Test
testIfMuxerStartsBeforeAddTrack()341         public void testIfMuxerStartsBeforeAddTrack() throws IOException {
342             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
343             MediaFormat format = new MediaFormat();
344             format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
345 
346             try {
347                 muxer.start();
348                 fail("muxer.start() succeeded before muxer.addTrack()");
349             } catch (IllegalStateException e) {
350                 // expected
351             } catch (Exception e) {
352                 fail(e.getMessage());
353             } finally {
354                 muxer.release();
355             }
356         }
357 
358         @Test
testIdempotentStart()359         public void testIdempotentStart() throws IOException {
360             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
361             MediaFormat format = new MediaFormat();
362             format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
363 
364             try {
365                 muxer.addTrack(format);
366                 muxer.start();
367                 muxer.start();
368                 fail("muxer.start() succeeded after muxer.start()");
369             } catch (IllegalStateException e) {
370                 // expected
371             } catch (Exception e) {
372                 fail(e.getMessage());
373             } finally {
374                 muxer.release();
375             }
376         }
377 
378         @Test
testIfMuxerStartsAfterWriteSampleData()379         public void testIfMuxerStartsAfterWriteSampleData() throws IOException {
380             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
381             MediaFormat format = new MediaFormat();
382             format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
383 
384             try {
385                 int trackID = muxer.addTrack(format);
386                 muxer.start();
387                 insertPerFrameSubtitles(muxer, 0, trackID);
388                 muxer.start();
389                 fail("muxer.start() succeeded after muxer.writeSampleData()");
390             } catch (IllegalStateException e) {
391                 // expected
392             } catch (Exception e) {
393                 fail(e.getMessage());
394             } finally {
395                 muxer.release();
396             }
397         }
398 
399         @Test
testIfMuxerStartsAfterStop()400         public void testIfMuxerStartsAfterStop() throws IOException {
401             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
402             MediaFormat format = new MediaFormat();
403             format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
404 
405             try {
406                 int trackID = muxer.addTrack(format);
407                 muxer.start();
408                 insertPerFrameSubtitles(muxer, 0, trackID);
409                 muxer.stop();
410                 muxer.start();
411                 fail("muxer.start() succeeded after muxer.stop()");
412             } catch (IllegalStateException e) {
413                 // expected
414             } catch (Exception e) {
415                 fail(e.getMessage());
416             } finally {
417                 muxer.release();
418             }
419         }
420 
421         @Test
testIfMuxerStartsAfterRelease()422         public void testIfMuxerStartsAfterRelease() throws IOException {
423             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
424 
425             try {
426                 muxer.release();
427                 muxer.start();
428                 fail("muxer.start() succeeded after muxer.release()");
429             } catch (IllegalStateException e) {
430                 // expected
431             } catch (Exception e) {
432                 fail(e.getMessage());
433             } finally {
434                 muxer.release();
435             }
436         }
437 
438         @Test
testStopOnANonStartedMuxer()439         public void testStopOnANonStartedMuxer() throws IOException {
440             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
441 
442             try {
443                 muxer.stop();
444                 fail("muxer.stop() succeeded before muxer.start()");
445             } catch (IllegalStateException e) {
446                 // expected
447             } catch (Exception e) {
448                 fail(e.getMessage());
449             } finally {
450                 muxer.release();
451             }
452         }
453 
454         @Test
testIdempotentStop()455         public void testIdempotentStop() throws IOException {
456             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
457             MediaFormat format = new MediaFormat();
458             format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
459 
460             try {
461                 int trackID = muxer.addTrack(format);
462                 muxer.start();
463                 insertPerFrameSubtitles(muxer, 0, trackID);
464                 muxer.stop();
465                 muxer.stop();
466                 fail("muxer.stop() succeeded after muxer.stop()");
467             } catch (IllegalStateException e) {
468                 // expected
469             } catch (Exception e) {
470                 fail(e.getMessage());
471             } finally {
472                 muxer.release();
473             }
474         }
475 
476         @Test
testStopAfterRelease()477         public void testStopAfterRelease() throws IOException {
478             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
479             try {
480                 muxer.release();
481                 muxer.stop();
482                 fail("muxer.stop() succeeded after muxer.release()");
483             } catch (IllegalStateException e) {
484                 // expected
485             } catch (Exception e) {
486                 fail(e.getMessage());
487             } finally {
488                 muxer.release();
489             }
490         }
491 
492         @Test
testSimpleStartStopMuxer()493         public void testSimpleStartStopMuxer() throws IOException {
494             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
495             MediaFormat format = new MediaFormat();
496             format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
497 
498             try {
499                 muxer.addTrack(format);
500                 muxer.start();
501                 muxer.stop();
502             } catch (Exception e) {
503                 fail(e.getMessage());
504             } finally {
505                 muxer.release();
506             }
507         }
508 
509         @Test
testIfWriteSampleDataRejectsInvalidTrackIndex()510         public void testIfWriteSampleDataRejectsInvalidTrackIndex() throws IOException {
511             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
512             MediaFormat format = new MediaFormat();
513             format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
514 
515             int trackID = muxer.addTrack(format);
516             muxer.start();
517             insertPerFrameSubtitles(muxer, 22000, trackID);
518             try {
519                 insertPerFrameSubtitles(muxer, 0, trackID - 1);
520                 fail("muxer.writeSampleData() succeeded with bad argument, trackIndex");
521             } catch (IllegalArgumentException e) {
522                 // expected
523             } finally {
524                 muxer.release();
525             }
526         }
527 
528         @Test
testIfWriteSampleDataRejectsNullByteBuffer()529         public void testIfWriteSampleDataRejectsNullByteBuffer() throws IOException {
530             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
531             MediaFormat format = new MediaFormat();
532             format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
533 
534             int trackID = muxer.addTrack(format);
535             muxer.start();
536             insertPerFrameSubtitles(muxer, 22000, trackID);
537 
538             MediaCodec.BufferInfo metaInfo = new MediaCodec.BufferInfo();
539             metaInfo.offset = 0;
540             metaInfo.size = 24;
541             metaInfo.presentationTimeUs = 0;
542             metaInfo.flags = 0;
543             try {
544                 muxer.writeSampleData(trackID, null, metaInfo);
545                 fail("muxer.writeSampleData() succeeded with bad argument, byteBuffer = null");
546             } catch (IllegalArgumentException e) {
547                 // expected
548             } finally {
549                 muxer.release();
550             }
551         }
552 
553         @Test
testIfWriteSampleDataRejectsNullBuffInfo()554         public void testIfWriteSampleDataRejectsNullBuffInfo() throws IOException {
555             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
556             MediaFormat format = new MediaFormat();
557             format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
558 
559             int trackID = muxer.addTrack(format);
560             muxer.start();
561             insertPerFrameSubtitles(muxer, 22000, trackID);
562 
563             byte[] greeting = "hello world".getBytes(StandardCharsets.UTF_8);
564             ByteBuffer metaBuff = ByteBuffer.allocate(greeting.length);
565             metaBuff.put(greeting);
566 
567             try {
568                 muxer.writeSampleData(trackID, metaBuff, null);
569                 fail("muxer.writeSampleData() succeeded with bad argument, byteBuffer = null");
570             } catch (IllegalArgumentException e) {
571                 // expected
572             } finally {
573                 muxer.release();
574             }
575         }
576 
577         @Test
testIfWriteSampleDataRejectsInvalidBuffInfo()578         public void testIfWriteSampleDataRejectsInvalidBuffInfo() throws IOException {
579             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
580             MediaFormat format = new MediaFormat();
581             format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
582 
583             int trackID = muxer.addTrack(format);
584             muxer.start();
585             insertPerFrameSubtitles(muxer, 22000, trackID);
586 
587             byte[] greeting = "hello world".getBytes(StandardCharsets.UTF_8);
588             ByteBuffer metaBuff = ByteBuffer.allocate(greeting.length);
589             metaBuff.put(greeting);
590 
591             MediaCodec.BufferInfo metaInfo = new MediaCodec.BufferInfo();
592 
593             try {
594                 // invalid metaData : buffer offset < 0
595                 try {
596                     metaInfo.offset = -1;
597                     metaInfo.size = greeting.length;
598                     metaInfo.presentationTimeUs = 0;
599                     metaInfo.flags = 0;
600                     muxer.writeSampleData(trackID, metaBuff, metaInfo);
601                     fail("muxer.writeSampleData() succeeded with bad argument, bufInfo.offset < 0");
602                 } catch (IllegalArgumentException e) {
603                     // expected
604                 }
605 
606                 // invalid metaData : buffer size < 0
607                 try {
608                     metaInfo.offset = 0;
609                     metaInfo.size = -1;
610                     metaInfo.presentationTimeUs = 0;
611                     metaInfo.flags = 0;
612                     muxer.writeSampleData(trackID, metaBuff, metaInfo);
613                     fail("muxer.writeSampleData() succeeded with bad argument, buffInfo.size < 0");
614                 } catch (IllegalArgumentException e) {
615                     // expected
616                 }
617 
618                 // invalid metaData : buffer size > capacity
619                 try {
620                     metaInfo.offset = 0;
621                     metaInfo.size = greeting.length * 2;
622                     metaInfo.presentationTimeUs = 0;
623                     metaInfo.flags = 0;
624                     muxer.writeSampleData(trackID, metaBuff, metaInfo);
625                     fail("muxer.writeSampleData() succeeded with bad argument, buffInfo.size > " +
626                             "byteBuffer.capacity()");
627                 } catch (IllegalArgumentException e) {
628                     // expected
629                 }
630 
631                 // invalid metaData : buffer offset + size > capacity
632                 try {
633                     metaInfo.offset = 1;
634                     metaInfo.size = greeting.length;
635                     metaInfo.presentationTimeUs = 0;
636                     metaInfo.flags = 0;
637                     muxer.writeSampleData(trackID, metaBuff, metaInfo);
638                     fail("muxer.writeSampleData() succeeded with bad argument, bufferInfo.offset " +
639                             "+ bufferInfo.size > byteBuffer.capacity()");
640                 } catch (IllegalArgumentException e) {
641                     // expected
642                 }
643             } finally {
644                 muxer.release();
645             }
646         }
647 
648         @Test
649         @Ignore("TODO(b/147128377)")
testIfWriteSampleDataRejectsInvalidPts()650         public void testIfWriteSampleDataRejectsInvalidPts() throws IOException {
651             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
652             MediaFormat format = new MediaFormat();
653             format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
654 
655             int trackID = muxer.addTrack(format);
656             muxer.start();
657             insertPerFrameSubtitles(muxer, 22000, trackID);
658             try {
659                 insertPerFrameSubtitles(muxer, -33000, trackID);
660                 fail("muxer.writeSampleData() succeeded with bad argument, presentationTime");
661             } catch (IllegalArgumentException e) {
662                 // expected
663             } finally {
664                 muxer.release();
665             }
666         }
667 
668         @Test
testIfWriteSampleDataSucceedsBeforeStart()669         public void testIfWriteSampleDataSucceedsBeforeStart() throws IOException {
670             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
671             MediaFormat format = new MediaFormat();
672             format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
673 
674             try {
675                 int trackID = muxer.addTrack(format);
676                 insertPerFrameSubtitles(muxer, 0, trackID);
677                 fail("muxer.WriteSampleData() succeeds before muxer.start()");
678             } catch (IllegalStateException e) {
679                 // expected
680             } catch (Exception e) {
681                 fail(e.getMessage());
682             } finally {
683                 muxer.release();
684             }
685         }
686 
687         @Test
testIfWriteSampleDataSucceedsAfterStop()688         public void testIfWriteSampleDataSucceedsAfterStop() throws IOException {
689             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
690             MediaFormat format = new MediaFormat();
691             format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
692 
693             try {
694                 int trackID = muxer.addTrack(format);
695                 muxer.start();
696                 insertPerFrameSubtitles(muxer, 0, trackID);
697                 muxer.stop();
698                 insertPerFrameSubtitles(muxer, 0, trackID);
699                 fail("muxer.WriteSampleData() succeeds after muxer.stop()");
700             } catch (IllegalStateException e) {
701                 // expected
702             } catch (Exception e) {
703                 fail(e.getMessage());
704             } finally {
705                 muxer.release();
706             }
707         }
708 
709         @Test
testIfWriteSampleDataSucceedsAfterRelease()710         public void testIfWriteSampleDataSucceedsAfterRelease() throws IOException {
711             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
712             MediaFormat format = new MediaFormat();
713             format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP);
714 
715             try {
716                 int trackID = muxer.addTrack(format);
717                 muxer.start();
718                 insertPerFrameSubtitles(muxer, 0, trackID);
719                 muxer.release();
720                 insertPerFrameSubtitles(muxer, 0, trackID);
721                 fail("muxer.WriteSampleData() succeeds after muxer.release()");
722             } catch (IllegalStateException e) {
723                 // expected
724             } catch (Exception e) {
725                 fail(e.getMessage());
726             } finally {
727                 muxer.release();
728             }
729         }
730 
731         @Test
testIdempotentRelease()732         public void testIdempotentRelease() throws IOException {
733             MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
734             try {
735                 muxer.release();
736                 muxer.release();
737             } catch (Exception e) {
738                 fail(e.getMessage());
739             }
740         }
741     }
742 
743     @NonMediaMainlineTest
744     @SmallTest
745     public static class TestApiNative {
746         @Rule
747         public TestName testName = new TestName();
748 
749         static {
750             System.loadLibrary("ctsmediav2muxer_jni");
751         }
752 
753         @Before
prologue()754         public void prologue() throws IOException {
755             File mOutMedia = File.createTempFile(testName.getMethodName(), ".out");
756             mOutLoc = mOutMedia.getAbsolutePath();
757         }
758 
759         @After
epilogue()760         public void epilogue() {
761             new File(mOutLoc).delete();
762         }
763 
764         private String mOutLoc;
765 
nativeTestIfInvalidFdIsRejected()766         private native boolean nativeTestIfInvalidFdIsRejected();
nativeTestIfReadOnlyFdIsRejected(String outPath)767         private native boolean nativeTestIfReadOnlyFdIsRejected(String outPath);
nativeTestIfWriteOnlyFdIsRejected(String outPath)768         private native boolean nativeTestIfWriteOnlyFdIsRejected(String outPath);
nativeTestIfNonSeekableFdIsRejected(String outPath)769         private native boolean nativeTestIfNonSeekableFdIsRejected(String outPath);
nativeTestIfInvalidOutputFormatIsRejected(String outPath)770         private native boolean nativeTestIfInvalidOutputFormatIsRejected(String outPath);
771 
nativeTestIfInvalidMediaFormatIsRejected(String outPath)772         private native boolean nativeTestIfInvalidMediaFormatIsRejected(String outPath);
nativeTestIfCorruptMediaFormatIsRejected(String outPath)773         private native boolean nativeTestIfCorruptMediaFormatIsRejected(String outPath);
nativeTestIfAddTrackSucceedsAfterStart(String outPath)774         private native boolean nativeTestIfAddTrackSucceedsAfterStart(String outPath);
nativeTestIfAddTrackSucceedsAfterWriteSampleData(String outPath)775         private native boolean nativeTestIfAddTrackSucceedsAfterWriteSampleData(String outPath);
nativeTestIfAddTrackSucceedsAfterStop(String outPath)776         private native boolean nativeTestIfAddTrackSucceedsAfterStop(String outPath);
777 
nativeTestIfMuxerStartsBeforeAddTrack(String outPath)778         private native boolean nativeTestIfMuxerStartsBeforeAddTrack(String outPath);
nativeTestIdempotentStart(String outPath)779         private native boolean nativeTestIdempotentStart(String outPath);
nativeTestIfMuxerStartsAfterWriteSampleData(String outPath)780         private native boolean nativeTestIfMuxerStartsAfterWriteSampleData(String outPath);
nativeTestIfMuxerStartsAfterStop(String outPath)781         private native boolean nativeTestIfMuxerStartsAfterStop(String outPath);
782 
nativeTestStopOnANonStartedMuxer(String outPath)783         private native boolean nativeTestStopOnANonStartedMuxer(String outPath);
nativeTestIdempotentStop(String outPath)784         private native boolean nativeTestIdempotentStop(String outPath);
nativeTestSimpleStartStop(String outPath)785         private native boolean nativeTestSimpleStartStop(String outPath);
786 
nativeTestIfWriteSampleDataRejectsInvalidTrackIndex(String outPath)787         private native boolean nativeTestIfWriteSampleDataRejectsInvalidTrackIndex(String outPath);
nativeTestIfWriteSampleDataRejectsInvalidPts(String outPath)788         private native boolean nativeTestIfWriteSampleDataRejectsInvalidPts(String outPath);
nativeTestIfWriteSampleDataSucceedsBeforeStart(String outPath)789         private native boolean nativeTestIfWriteSampleDataSucceedsBeforeStart(String outPath);
nativeTestIfWriteSampleDataSucceedsAfterStop(String outPath)790         private native boolean nativeTestIfWriteSampleDataSucceedsAfterStop(String outPath);
791 
792         @Test
793         @Ignore("TODO(b/146417874)")
testIfInvalidFdIsRejected()794         public void testIfInvalidFdIsRejected() {
795             assertTrue(nativeTestIfInvalidFdIsRejected());
796         }
797 
798         @Test
799         @Ignore("TODO(b/146417874)")
testIfReadOnlyFdIsRejected()800         public void testIfReadOnlyFdIsRejected() {
801             assertTrue(nativeTestIfReadOnlyFdIsRejected(mOutLoc));
802         }
803 
804         @Test
805         @Ignore("TODO(b/146417874)")
testIfWriteOnlyFdIsRejected()806         public void testIfWriteOnlyFdIsRejected() {
807             assertTrue(nativeTestIfWriteOnlyFdIsRejected(mOutLoc));
808         }
809 
810         @Test
811         @Ignore("TODO(b/146417874)")
testIfNonSeekableFdIsRejected()812         public void testIfNonSeekableFdIsRejected() {
813             assertTrue(nativeTestIfNonSeekableFdIsRejected(mOutLoc));
814         }
815 
816         @Test
817         @Ignore("TODO(b/146417874)")
testIfInvalidOutputFormatIsRejected()818         public void testIfInvalidOutputFormatIsRejected() {
819             assertTrue(nativeTestIfInvalidOutputFormatIsRejected(mOutLoc));
820         }
821 
822         @Test
testIfInvalidMediaFormatIsRejected()823         public void testIfInvalidMediaFormatIsRejected() {
824             assertTrue(nativeTestIfInvalidMediaFormatIsRejected(mOutLoc));
825         }
826 
827         @Test
828         @Ignore("TODO(b/146923138)")
testIfCorruptMediaFormatIsRejected()829         public void testIfCorruptMediaFormatIsRejected() {
830             assertTrue(nativeTestIfCorruptMediaFormatIsRejected(mOutLoc));
831         }
832 
833         @Test
testIfAddTrackSucceedsAfterStart()834         public void testIfAddTrackSucceedsAfterStart() {
835             assertTrue(nativeTestIfAddTrackSucceedsAfterStart(mOutLoc));
836         }
837 
838         @Test
testIfAddTrackSucceedsAfterWriteSampleData()839         public void testIfAddTrackSucceedsAfterWriteSampleData() {
840             assertTrue(nativeTestIfAddTrackSucceedsAfterWriteSampleData(mOutLoc));
841         }
842 
843         @Test
testIfAddTrackSucceedsAfterStop()844         public void testIfAddTrackSucceedsAfterStop() {
845             assertTrue(nativeTestIfAddTrackSucceedsAfterStop(mOutLoc));
846         }
847 
848         @Test
testIfMuxerStartsBeforeAddTrack()849         public void testIfMuxerStartsBeforeAddTrack() {
850             assertTrue(nativeTestIfMuxerStartsBeforeAddTrack(mOutLoc));
851         }
852 
853         @Test
testIdempotentStart()854         public void testIdempotentStart() {
855             assertTrue(nativeTestIdempotentStart(mOutLoc));
856         }
857 
858         @Test
testIfMuxerStartsAfterWriteSampleData()859         public void testIfMuxerStartsAfterWriteSampleData() {
860             assertTrue(nativeTestIfMuxerStartsAfterWriteSampleData(mOutLoc));
861         }
862 
863         @Test
testIfMuxerStartsAfterStop()864         public void testIfMuxerStartsAfterStop() {
865             assertTrue(nativeTestIfMuxerStartsAfterStop(mOutLoc));
866         }
867 
868         @Test
testStopOnANonStartedMuxer()869         public void testStopOnANonStartedMuxer() {
870             assertTrue(nativeTestStopOnANonStartedMuxer(mOutLoc));
871         }
872 
873         @Test
testIdempotentStop()874         public void testIdempotentStop() {
875             assertTrue(nativeTestIdempotentStop(mOutLoc));
876         }
877 
878         @Test
testSimpleStartStopMuxer()879         public void testSimpleStartStopMuxer() {
880             assertTrue(nativeTestSimpleStartStop(mOutLoc));
881         }
882 
883         @Test
testIfWriteSampleDataRejectsInvalidTrackIndex()884         public void testIfWriteSampleDataRejectsInvalidTrackIndex() {
885             assertTrue(nativeTestIfWriteSampleDataRejectsInvalidTrackIndex(mOutLoc));
886         }
887 
888         @Test
889         @Ignore("TODO(b/147128377)")
testIfWriteSampleDataRejectsInvalidPts()890         public void testIfWriteSampleDataRejectsInvalidPts() {
891             assertTrue(nativeTestIfWriteSampleDataRejectsInvalidPts(mOutLoc));
892         }
893 
894         @Test
testIfWriteSampleDataSucceedsBeforeStart()895         public void testIfWriteSampleDataSucceedsBeforeStart() {
896             assertTrue(nativeTestIfWriteSampleDataSucceedsBeforeStart(mOutLoc));
897         }
898 
899         @Test
testIfWriteSampleDataSucceedsAfterStop()900         public void testIfWriteSampleDataSucceedsAfterStop() {
901             assertTrue(nativeTestIfWriteSampleDataSucceedsAfterStop(mOutLoc));
902         }
903     }
904 }
905