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