/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.mediav2.cts; import static android.system.Os.pipe; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.media.MediaCodec; import android.media.MediaFormat; import android.media.MediaMuxer; import androidx.test.filters.SmallTest; import com.android.compatibility.common.util.ApiTest; import com.android.compatibility.common.util.FrameworkSpecificTest; import com.android.compatibility.common.util.NonMainlineTest; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.rules.TestName; import org.junit.runner.RunWith; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; /** * Tests MediaMuxer API that are independent of MediaMuxer.OutputFormat. Constructors, * addTrack, start, writeSampleData, stop, release are independent of OutputFormat selected. * Legality of these APIs are tested in this class. */ @RunWith(Enclosed.class) public class MuxerUnitTest { // duplicate definitions of hide fields of MediaMuxer.OutputFormat. private static final int MUXER_OUTPUT_LAST = MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG; @FrameworkSpecificTest @NonMainlineTest @SmallTest public static class TestApi { @Rule public TestName testName = new TestName(); @Before public void prologue() throws IOException { mOutMedia = File.createTempFile(testName.getMethodName(), ".out"); mOutLoc = mOutMedia.getAbsolutePath(); } @After public void epilogue() { new File(mOutLoc).delete(); } private File mOutMedia; private String mOutLoc; // Insert one frame SubRip static private void insertPerFrameSubtitles(MediaMuxer muxer, long presentationTimeUs, int trackID) { byte[] greeting = "hello world".getBytes(StandardCharsets.UTF_8); ByteBuffer metaBuff = ByteBuffer.allocate(greeting.length); metaBuff.put(greeting); MediaCodec.BufferInfo metaInfo = new MediaCodec.BufferInfo(); metaInfo.offset = 0; metaInfo.size = greeting.length; metaInfo.presentationTimeUs = presentationTimeUs; metaInfo.flags = 0; muxer.writeSampleData(trackID, metaBuff, metaInfo); } @ApiTest(apis = "android.media.MediaMuxer#MediaMuxer") @Test public void testIfNullPathIsRejected() { MediaMuxer muxer = null; try { String nullPath = null; muxer = new MediaMuxer(nullPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP); fail("null destination path accepted by constructor"); } catch (IllegalArgumentException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { if (null != muxer) muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#MediaMuxer") @Test public void testIfNullFdIsRejected() { MediaMuxer muxer = null; try { FileDescriptor fd = null; muxer = new MediaMuxer(fd, MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP); fail("null fd accepted by constructor"); } catch (IllegalArgumentException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { if (null != muxer) muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#MediaMuxer") @Test public void testIfInvalidFdIsRejected() { MediaMuxer muxer = null; try { FileDescriptor fd = new FileDescriptor(); muxer = new MediaMuxer(fd, MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP); fail("Invalid fd accepted by constructor"); } catch (IllegalArgumentException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { if (null != muxer) muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#MediaMuxer") @Test public void testIfReadOnlyFdIsRejected() { MediaMuxer muxer = null; try (FileInputStream fInp = new FileInputStream(mOutMedia)) { muxer = new MediaMuxer(fInp.getFD(), MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP); fail("fd with read-only attribute accepted by constructor"); } catch (IOException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { if (null != muxer) muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#MediaMuxer") @Test public void testIfNonSeekableFdIsRejected() { MediaMuxer muxer = null; try { FileDescriptor[] fd = pipe(); muxer = new MediaMuxer(fd[1], MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP); fail("pipe, a non-seekable fd accepted by constructor"); } catch (IllegalArgumentException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { if (null != muxer) muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#MediaMuxer") @Test public void testIfInvalidOutputFormatIsRejected() { MediaMuxer muxer = null; try { muxer = new MediaMuxer(mOutLoc, MUXER_OUTPUT_LAST + 1); fail("Invalid Media format accepted by constructor"); } catch (IllegalArgumentException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { if (null != muxer) muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#addTrack") @Test public void testIfNullMediaFormatIsRejected() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); try { muxer.addTrack(null); fail("null media format accepted by addTrack"); } catch (IllegalArgumentException e) { // expected } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#addTrack") @Test public void testIfInvalidMediaFormatIsRejected() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); try { // Invalid media format - no mediaType key try { muxer.addTrack(new MediaFormat()); fail("Invalid media format accepted by addTrack"); } catch (IllegalArgumentException e) { // expected } // metadata mediaType format shall start with "application/*" try { MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_CEA_608); muxer.addTrack(format); fail("Invalid media format accepted by addTrack"); } catch (IllegalArgumentException | IllegalStateException e) { // Ideally check only for IllegalArgumentException. // expected } } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#addTrack") @Test public void testIfCorruptMediaFormatIsRejected() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); /* TODO(b/278260304): Audio/Video formats, have certain keys required to be set. It is noticed that even when these keys are not set, no exceptions were raised. Do we need to add fixtures for those cases. */ try { MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AAC); format.setInteger(MediaFormat.KEY_SAMPLE_RATE, -1); muxer.addTrack(format); muxer.start(); fail("muxer accepts media format with required key-value pairs missing"); } catch (Exception e) { // expected } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#addTrack") @Test public void testIfAddTrackSucceedsAfterStart() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); try { MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); muxer.addTrack(format); muxer.start(); muxer.addTrack(format); fail("muxer.addTrack() succeeded after muxer.start()"); } catch (IllegalStateException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#addTrack") @Test public void testIfAddTrackSucceedsAfterWriteSampleData() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); try { MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); int trackID = muxer.addTrack(format); muxer.start(); insertPerFrameSubtitles(muxer, 0, trackID); muxer.addTrack(format); fail("muxer.addTrack() succeeded after muxer.writeSampleData()"); } catch (IllegalStateException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#addTrack") @Test public void testIfAddTrackSucceedsAfterStop() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); try { MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); int trackID = muxer.addTrack(format); muxer.start(); insertPerFrameSubtitles(muxer, 0, trackID); muxer.stop(); muxer.addTrack(format); fail("muxer.addTrack() succeeded after muxer.stop()"); } catch (IllegalStateException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#addTrack") @Test public void testIfAddTrackSucceedsAfterRelease() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); try { MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); muxer.release(); muxer.addTrack(format); fail("muxer.addTrack() succeeded after muxer.release()"); } catch (IllegalStateException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#start") @Test public void testIfMuxerStartsBeforeAddTrack() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); try { muxer.start(); fail("muxer.start() succeeded before muxer.addTrack()"); } catch (IllegalStateException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#start") @Test public void testIdempotentStart() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); try { muxer.addTrack(format); muxer.start(); muxer.start(); fail("muxer.start() succeeded after muxer.start()"); } catch (IllegalStateException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#start") @Test public void testIfMuxerStartsAfterWriteSampleData() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); try { int trackID = muxer.addTrack(format); muxer.start(); insertPerFrameSubtitles(muxer, 0, trackID); muxer.start(); fail("muxer.start() succeeded after muxer.writeSampleData()"); } catch (IllegalStateException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#start") @Test public void testIfMuxerStartsAfterStop() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); try { int trackID = muxer.addTrack(format); muxer.start(); insertPerFrameSubtitles(muxer, 0, trackID); muxer.stop(); muxer.start(); fail("muxer.start() succeeded after muxer.stop()"); } catch (IllegalStateException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#start") @Test public void testIfMuxerStartsAfterRelease() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); try { muxer.release(); muxer.start(); fail("muxer.start() succeeded after muxer.release()"); } catch (IllegalStateException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#stop") @Test public void testStopOnANonStartedMuxer() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); try { muxer.stop(); fail("muxer.stop() succeeded before muxer.start()"); } catch (IllegalStateException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#stop") @Test public void testIdempotentStop() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); try { int trackID = muxer.addTrack(format); muxer.start(); insertPerFrameSubtitles(muxer, 0, trackID); muxer.stop(); muxer.stop(); fail("muxer.stop() succeeded after muxer.stop()"); } catch (IllegalStateException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#stop") @Test public void testStopAfterRelease() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); try { muxer.release(); muxer.stop(); fail("muxer.stop() succeeded after muxer.release()"); } catch (IllegalStateException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { muxer.release(); } } @ApiTest(apis = {"android.media.MediaMuxer#start", "android.media.MediaMuxer#stop"}) @Test public void testSimpleStartStopMuxer() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); try { muxer.addTrack(format); muxer.start(); muxer.stop(); } catch (Exception e) { fail(e.getMessage()); } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#writeSampleData") @Test public void testIfWriteSampleDataRejectsInvalidTrackIndex() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); int trackID = muxer.addTrack(format); muxer.start(); insertPerFrameSubtitles(muxer, 22000, trackID); try { insertPerFrameSubtitles(muxer, 0, trackID - 1); fail("muxer.writeSampleData() succeeded with bad argument, trackIndex"); } catch (IllegalArgumentException e) { // expected } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#writeSampleData") @Test public void testIfWriteSampleDataRejectsNullByteBuffer() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); int trackID = muxer.addTrack(format); muxer.start(); insertPerFrameSubtitles(muxer, 22000, trackID); MediaCodec.BufferInfo metaInfo = new MediaCodec.BufferInfo(); metaInfo.offset = 0; metaInfo.size = 24; metaInfo.presentationTimeUs = 0; metaInfo.flags = 0; try { muxer.writeSampleData(trackID, null, metaInfo); fail("muxer.writeSampleData() succeeded with bad argument, byteBuffer = null"); } catch (IllegalArgumentException e) { // expected } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#writeSampleData") @Test public void testIfWriteSampleDataRejectsNullBuffInfo() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); int trackID = muxer.addTrack(format); muxer.start(); insertPerFrameSubtitles(muxer, 22000, trackID); byte[] greeting = "hello world".getBytes(StandardCharsets.UTF_8); ByteBuffer metaBuff = ByteBuffer.allocate(greeting.length); metaBuff.put(greeting); try { muxer.writeSampleData(trackID, metaBuff, null); fail("muxer.writeSampleData() succeeded with bad argument, byteBuffer = null"); } catch (IllegalArgumentException e) { // expected } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#writeSampleData") @Test public void testIfWriteSampleDataRejectsInvalidBuffInfo() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); int trackID = muxer.addTrack(format); muxer.start(); insertPerFrameSubtitles(muxer, 22000, trackID); byte[] greeting = "hello world".getBytes(StandardCharsets.UTF_8); ByteBuffer metaBuff = ByteBuffer.allocate(greeting.length); metaBuff.put(greeting); MediaCodec.BufferInfo metaInfo = new MediaCodec.BufferInfo(); try { // invalid metaData : buffer offset < 0 try { metaInfo.offset = -1; metaInfo.size = greeting.length; metaInfo.presentationTimeUs = 0; metaInfo.flags = 0; muxer.writeSampleData(trackID, metaBuff, metaInfo); fail("muxer.writeSampleData() succeeded with bad argument, bufInfo.offset < 0"); } catch (IllegalArgumentException e) { // expected } // invalid metaData : buffer size < 0 try { metaInfo.offset = 0; metaInfo.size = -1; metaInfo.presentationTimeUs = 0; metaInfo.flags = 0; muxer.writeSampleData(trackID, metaBuff, metaInfo); fail("muxer.writeSampleData() succeeded with bad argument, buffInfo.size < 0"); } catch (IllegalArgumentException e) { // expected } // invalid metaData : buffer size > capacity try { metaInfo.offset = 0; metaInfo.size = greeting.length * 2; metaInfo.presentationTimeUs = 0; metaInfo.flags = 0; muxer.writeSampleData(trackID, metaBuff, metaInfo); fail("muxer.writeSampleData() succeeded with bad argument, buffInfo.size > " + "byteBuffer.capacity()"); } catch (IllegalArgumentException e) { // expected } // invalid metaData : buffer offset + size > capacity try { metaInfo.offset = 1; metaInfo.size = greeting.length; metaInfo.presentationTimeUs = 0; metaInfo.flags = 0; muxer.writeSampleData(trackID, metaBuff, metaInfo); fail("muxer.writeSampleData() succeeded with bad argument, bufferInfo.offset " + "+ bufferInfo.size > byteBuffer.capacity()"); } catch (IllegalArgumentException e) { // expected } } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#writeSampleData") @Test @Ignore("TODO(b/147128377)") public void testIfWriteSampleDataRejectsInvalidPts() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); int trackID = muxer.addTrack(format); muxer.start(); insertPerFrameSubtitles(muxer, 22000, trackID); try { insertPerFrameSubtitles(muxer, -33000, trackID); fail("muxer.writeSampleData() succeeded with bad argument, presentationTime"); } catch (IllegalArgumentException e) { // expected } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#writeSampleData") @Test public void testIfWriteSampleDataSucceedsBeforeStart() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); try { int trackID = muxer.addTrack(format); insertPerFrameSubtitles(muxer, 0, trackID); fail("muxer.WriteSampleData() succeeds before muxer.start()"); } catch (IllegalStateException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#writeSampleData") @Test public void testIfWriteSampleDataSucceedsAfterStop() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); try { int trackID = muxer.addTrack(format); muxer.start(); insertPerFrameSubtitles(muxer, 0, trackID); muxer.stop(); insertPerFrameSubtitles(muxer, 0, trackID); fail("muxer.WriteSampleData() succeeds after muxer.stop()"); } catch (IllegalStateException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#writeSampleData") @Test public void testIfWriteSampleDataSucceedsAfterRelease() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_TEXT_SUBRIP); try { int trackID = muxer.addTrack(format); muxer.start(); insertPerFrameSubtitles(muxer, 0, trackID); muxer.release(); insertPerFrameSubtitles(muxer, 0, trackID); fail("muxer.WriteSampleData() succeeds after muxer.release()"); } catch (IllegalStateException e) { // expected } catch (Exception e) { fail(e.getMessage()); } finally { muxer.release(); } } @ApiTest(apis = "android.media.MediaMuxer#release") @Test public void testIdempotentRelease() throws IOException { MediaMuxer muxer = new MediaMuxer(mOutLoc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); try { muxer.release(); muxer.release(); } catch (Exception e) { fail(e.getMessage()); } } } @FrameworkSpecificTest @NonMainlineTest @SmallTest public static class TestApiNative { @Rule public TestName testName = new TestName(); static { System.loadLibrary("ctsmediav2muxer_jni"); } @Before public void prologue() throws IOException { File mOutMedia = File.createTempFile(testName.getMethodName(), ".out"); mOutLoc = mOutMedia.getAbsolutePath(); } @After public void epilogue() { new File(mOutLoc).delete(); } private String mOutLoc; private native boolean nativeTestIfInvalidFdIsRejected(); private native boolean nativeTestIfReadOnlyFdIsRejected(String outPath); private native boolean nativeTestIfNonSeekableFdIsRejected(); private native boolean nativeTestIfInvalidOutputFormatIsRejected(String outPath); private native boolean nativeTestIfInvalidMediaFormatIsRejected(String outPath); private native boolean nativeTestIfCorruptMediaFormatIsRejected(String outPath); private native boolean nativeTestIfAddTrackSucceedsAfterStart(String outPath); private native boolean nativeTestIfAddTrackSucceedsAfterWriteSampleData(String outPath); private native boolean nativeTestIfAddTrackSucceedsAfterStop(String outPath); private native boolean nativeTestIfMuxerStartsBeforeAddTrack(String outPath); private native boolean nativeTestIdempotentStart(String outPath); private native boolean nativeTestIfMuxerStartsAfterWriteSampleData(String outPath); private native boolean nativeTestIfMuxerStartsAfterStop(String outPath); private native boolean nativeTestStopOnANonStartedMuxer(String outPath); private native boolean nativeTestIdempotentStop(String outPath); private native boolean nativeTestSimpleStartStop(String outPath); private native boolean nativeTestIfWriteSampleDataRejectsInvalidTrackIndex(String outPath); private native boolean nativeTestIfWriteSampleDataRejectsInvalidPts(String outPath); private native boolean nativeTestIfWriteSampleDataSucceedsBeforeStart(String outPath); private native boolean nativeTestIfWriteSampleDataSucceedsAfterStop(String outPath); @ApiTest(apis = "AMediaMuxer_new") @Test public void testIfInvalidFdIsRejected() { assertTrue(nativeTestIfInvalidFdIsRejected()); } @ApiTest(apis = "AMediaMuxer_new") @Test public void testIfReadOnlyFdIsRejected() { assertTrue(nativeTestIfReadOnlyFdIsRejected(mOutLoc)); } @ApiTest(apis = "AMediaMuxer_new") @Test public void testIfNonSeekableFdIsRejected() { assertTrue(nativeTestIfNonSeekableFdIsRejected()); } @ApiTest(apis = "AMediaMuxer_new") @Test public void testIfInvalidOutputFormatIsRejected() { assertTrue(nativeTestIfInvalidOutputFormatIsRejected(mOutLoc)); } @ApiTest(apis = "AMediaMuxer_addTrack") @Test public void testIfInvalidMediaFormatIsRejected() { assertTrue(nativeTestIfInvalidMediaFormatIsRejected(mOutLoc)); } @ApiTest(apis = "AMediaMuxer_addTrack") @Test public void testIfCorruptMediaFormatIsRejected() { assertTrue(nativeTestIfCorruptMediaFormatIsRejected(mOutLoc)); } @ApiTest(apis = "AMediaMuxer_addTrack") @Test public void testIfAddTrackSucceedsAfterStart() { assertTrue(nativeTestIfAddTrackSucceedsAfterStart(mOutLoc)); } @ApiTest(apis = "AMediaMuxer_addTrack") @Test public void testIfAddTrackSucceedsAfterWriteSampleData() { assertTrue(nativeTestIfAddTrackSucceedsAfterWriteSampleData(mOutLoc)); } @ApiTest(apis = "AMediaMuxer_addTrack") @Test public void testIfAddTrackSucceedsAfterStop() { assertTrue(nativeTestIfAddTrackSucceedsAfterStop(mOutLoc)); } @ApiTest(apis = "AMediaMuxer_start") @Test public void testIfMuxerStartsBeforeAddTrack() { assertTrue(nativeTestIfMuxerStartsBeforeAddTrack(mOutLoc)); } @ApiTest(apis = "AMediaMuxer_start") @Test public void testIdempotentStart() { assertTrue(nativeTestIdempotentStart(mOutLoc)); } @ApiTest(apis = "AMediaMuxer_start") @Test public void testIfMuxerStartsAfterWriteSampleData() { assertTrue(nativeTestIfMuxerStartsAfterWriteSampleData(mOutLoc)); } @ApiTest(apis = "AMediaMuxer_start") @Test public void testIfMuxerStartsAfterStop() { assertTrue(nativeTestIfMuxerStartsAfterStop(mOutLoc)); } @ApiTest(apis = "AMediaMuxer_stop") @Test public void testStopOnANonStartedMuxer() { assertTrue(nativeTestStopOnANonStartedMuxer(mOutLoc)); } @ApiTest(apis = "AMediaMuxer_stop") @Test public void testIdempotentStop() { assertTrue(nativeTestIdempotentStop(mOutLoc)); } @ApiTest(apis = {"AMediaMuxer_start", "AMediaMuxer_stop"}) @Test public void testSimpleStartStopMuxer() { assertTrue(nativeTestSimpleStartStop(mOutLoc)); } @ApiTest(apis = "AMediaMuxer_writeSampleData") @Test public void testIfWriteSampleDataRejectsInvalidTrackIndex() { assertTrue(nativeTestIfWriteSampleDataRejectsInvalidTrackIndex(mOutLoc)); } @ApiTest(apis = "AMediaMuxer_writeSampleData") @Test @Ignore("TODO(b/147128377)") public void testIfWriteSampleDataRejectsInvalidPts() { assertTrue(nativeTestIfWriteSampleDataRejectsInvalidPts(mOutLoc)); } @ApiTest(apis = "AMediaMuxer_writeSampleData") @Test public void testIfWriteSampleDataSucceedsBeforeStart() { assertTrue(nativeTestIfWriteSampleDataSucceedsBeforeStart(mOutLoc)); } @ApiTest(apis = "AMediaMuxer_writeSampleData") @Test public void testIfWriteSampleDataSucceedsAfterStop() { assertTrue(nativeTestIfWriteSampleDataSucceedsAfterStop(mOutLoc)); } } }