mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Change muxer instrumentation tests to robolectric
These tests were earlier using `MediaExtractor`, hence they were in androidTest. Now `MediaExtractor` has been replaced with `media3 MediaExtractorCompat` so these test can be robolectric. PiperOrigin-RevId: 715424470
This commit is contained in:
parent
fbf9be2f00
commit
1b2e391971
@ -58,12 +58,12 @@ dependencies {
|
|||||||
testImplementation project(modulePrefix + 'test-data')
|
testImplementation project(modulePrefix + 'test-data')
|
||||||
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
|
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
|
||||||
testImplementation 'com.google.truth:truth:' + truthVersion
|
testImplementation 'com.google.truth:truth:' + truthVersion
|
||||||
|
testImplementation project(modulePrefix + 'lib-exoplayer')
|
||||||
androidTestImplementation 'junit:junit:' + junitVersion
|
androidTestImplementation 'junit:junit:' + junitVersion
|
||||||
androidTestImplementation 'androidx.test:runner:' + androidxTestRunnerVersion
|
androidTestImplementation 'androidx.test:runner:' + androidxTestRunnerVersion
|
||||||
androidTestImplementation 'com.google.truth:truth:' + truthVersion
|
androidTestImplementation 'com.google.truth:truth:' + truthVersion
|
||||||
androidTestImplementation project(modulePrefix + 'test-utils')
|
androidTestImplementation project(modulePrefix + 'test-utils')
|
||||||
androidTestImplementation project(modulePrefix + 'lib-extractor')
|
androidTestImplementation project(modulePrefix + 'lib-extractor')
|
||||||
androidTestImplementation project(modulePrefix + 'lib-exoplayer')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
|
@ -1,75 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2023 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 androidx.media3.muxer;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.AssetFileDescriptor;
|
|
||||||
import android.media.MediaCodec;
|
|
||||||
import androidx.media3.common.util.MediaFormatUtil;
|
|
||||||
import androidx.media3.exoplayer.MediaExtractorCompat;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/** Utilities for muxer test cases. */
|
|
||||||
/* package */ final class AndroidMuxerTestUtil {
|
|
||||||
private static final String MP4_FILE_ASSET_DIRECTORY = "media/mp4/";
|
|
||||||
private static final String DUMP_FILE_OUTPUT_DIRECTORY = "muxerdumps";
|
|
||||||
private static final String DUMP_FILE_EXTENSION = "dump";
|
|
||||||
|
|
||||||
private AndroidMuxerTestUtil() {}
|
|
||||||
|
|
||||||
public static String getExpectedDumpFilePath(String originalFileName) {
|
|
||||||
return DUMP_FILE_OUTPUT_DIRECTORY + '/' + originalFileName + '.' + DUMP_FILE_EXTENSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void feedInputDataToMuxer(Context context, Muxer muxer, String inputFileName)
|
|
||||||
throws IOException, MuxerException {
|
|
||||||
MediaExtractorCompat extractor = new MediaExtractorCompat(context);
|
|
||||||
AssetFileDescriptor fd =
|
|
||||||
context.getResources().getAssets().openFd(MP4_FILE_ASSET_DIRECTORY + inputFileName);
|
|
||||||
extractor.setDataSource(fd);
|
|
||||||
|
|
||||||
List<Integer> addedTracks = new ArrayList<>();
|
|
||||||
for (int i = 0; i < extractor.getTrackCount(); i++) {
|
|
||||||
int trackId =
|
|
||||||
muxer.addTrack(MediaFormatUtil.createFormatFromMediaFormat(extractor.getTrackFormat(i)));
|
|
||||||
addedTracks.add(trackId);
|
|
||||||
extractor.selectTrack(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
|
||||||
bufferInfo.flags = extractor.getSampleFlags();
|
|
||||||
bufferInfo.offset = 0;
|
|
||||||
bufferInfo.presentationTimeUs = extractor.getSampleTime();
|
|
||||||
int sampleSize = (int) extractor.getSampleSize();
|
|
||||||
bufferInfo.size = sampleSize;
|
|
||||||
|
|
||||||
ByteBuffer sampleBuffer = ByteBuffer.allocateDirect(sampleSize);
|
|
||||||
extractor.readSampleData(sampleBuffer, /* offset= */ 0);
|
|
||||||
|
|
||||||
sampleBuffer.rewind();
|
|
||||||
|
|
||||||
muxer.writeSampleData(
|
|
||||||
addedTracks.get(extractor.getSampleTrackIndex()), sampleBuffer, bufferInfo);
|
|
||||||
} while (extractor.advance());
|
|
||||||
|
|
||||||
extractor.release();
|
|
||||||
fd.close();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,143 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2023 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 androidx.media3.muxer;
|
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
|
||||||
import static androidx.media3.muxer.AndroidMuxerTestUtil.feedInputDataToMuxer;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.media3.container.Mp4TimestampData;
|
|
||||||
import androidx.media3.extractor.mp4.Mp4Extractor;
|
|
||||||
import androidx.media3.extractor.text.DefaultSubtitleParserFactory;
|
|
||||||
import androidx.media3.test.utils.DumpFileAsserts;
|
|
||||||
import androidx.media3.test.utils.FakeExtractorOutput;
|
|
||||||
import androidx.media3.test.utils.TestUtil;
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.rules.TemporaryFolder;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
/** End to end instrumentation tests for {@link Mp4Muxer}. */
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class Mp4MuxerEndToEndNonParameterizedAndroidTest {
|
|
||||||
private static final String H265_HDR10_MP4 = "hdr10-720p.mp4";
|
|
||||||
|
|
||||||
@Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
|
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
|
||||||
private @MonotonicNonNull String outputPath;
|
|
||||||
private @MonotonicNonNull FileOutputStream outputStream;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
outputPath = temporaryFolder.newFile("muxeroutput.mp4").getPath();
|
|
||||||
outputStream = new FileOutputStream(outputPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() throws IOException {
|
|
||||||
checkNotNull(outputStream).close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void createMp4File_muxerNotClosed_createsPartiallyWrittenValidFile() throws Exception {
|
|
||||||
Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(checkNotNull(outputStream)).build();
|
|
||||||
mp4Muxer.addMetadataEntry(
|
|
||||||
new Mp4TimestampData(
|
|
||||||
/* creationTimestampSeconds= */ 100_000_000L,
|
|
||||||
/* modificationTimestampSeconds= */ 500_000_000L));
|
|
||||||
feedInputDataToMuxer(context, mp4Muxer, H265_HDR10_MP4);
|
|
||||||
|
|
||||||
// Muxer not closed.
|
|
||||||
|
|
||||||
// Audio sample written = 192 out of 195.
|
|
||||||
// Video sample written = 125 out of 127.
|
|
||||||
// Output is still a valid MP4 file.
|
|
||||||
FakeExtractorOutput fakeExtractorOutput =
|
|
||||||
TestUtil.extractAllSamplesFromFilePath(new Mp4Extractor(), checkNotNull(outputPath));
|
|
||||||
DumpFileAsserts.assertOutput(
|
|
||||||
context,
|
|
||||||
fakeExtractorOutput,
|
|
||||||
AndroidMuxerTestUtil.getExpectedDumpFilePath("partial_" + H265_HDR10_MP4));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void createMp4File_withSampleBatchingDisabled_matchesExpected() throws Exception {
|
|
||||||
@Nullable Mp4Muxer mp4Muxer = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
mp4Muxer =
|
|
||||||
new Mp4Muxer.Builder(checkNotNull(outputStream)).setSampleBatchingEnabled(false).build();
|
|
||||||
mp4Muxer.addMetadataEntry(
|
|
||||||
new Mp4TimestampData(
|
|
||||||
/* creationTimestampSeconds= */ 100_000_000L,
|
|
||||||
/* modificationTimestampSeconds= */ 500_000_000L));
|
|
||||||
feedInputDataToMuxer(context, mp4Muxer, checkNotNull(H265_HDR10_MP4));
|
|
||||||
} finally {
|
|
||||||
if (mp4Muxer != null) {
|
|
||||||
mp4Muxer.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FakeExtractorOutput fakeExtractorOutput =
|
|
||||||
TestUtil.extractAllSamplesFromFilePath(
|
|
||||||
new Mp4Extractor(new DefaultSubtitleParserFactory()), checkNotNull(outputPath));
|
|
||||||
DumpFileAsserts.assertOutput(
|
|
||||||
context,
|
|
||||||
fakeExtractorOutput,
|
|
||||||
AndroidMuxerTestUtil.getExpectedDumpFilePath("sample_batching_disabled_" + H265_HDR10_MP4));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void createMp4File_withSampleBatchingAndAttemptStreamableOutputDisabled_matchesExpected()
|
|
||||||
throws Exception {
|
|
||||||
@Nullable Mp4Muxer mp4Muxer = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
mp4Muxer =
|
|
||||||
new Mp4Muxer.Builder(checkNotNull(outputStream))
|
|
||||||
.setSampleBatchingEnabled(false)
|
|
||||||
.setAttemptStreamableOutputEnabled(false)
|
|
||||||
.build();
|
|
||||||
mp4Muxer.addMetadataEntry(
|
|
||||||
new Mp4TimestampData(
|
|
||||||
/* creationTimestampSeconds= */ 100_000_000L,
|
|
||||||
/* modificationTimestampSeconds= */ 500_000_000L));
|
|
||||||
feedInputDataToMuxer(context, mp4Muxer, checkNotNull(H265_HDR10_MP4));
|
|
||||||
} finally {
|
|
||||||
if (mp4Muxer != null) {
|
|
||||||
mp4Muxer.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FakeExtractorOutput fakeExtractorOutput =
|
|
||||||
TestUtil.extractAllSamplesFromFilePath(
|
|
||||||
new Mp4Extractor(new DefaultSubtitleParserFactory()), checkNotNull(outputPath));
|
|
||||||
DumpFileAsserts.assertOutput(
|
|
||||||
context,
|
|
||||||
fakeExtractorOutput,
|
|
||||||
AndroidMuxerTestUtil.getExpectedDumpFilePath(
|
|
||||||
"sample_batching_and_attempt_streamable_output_disabled_" + H265_HDR10_MP4));
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,7 +16,7 @@
|
|||||||
package androidx.media3.muxer;
|
package androidx.media3.muxer;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.muxer.AndroidMuxerTestUtil.feedInputDataToMuxer;
|
import static androidx.media3.muxer.MuxerTestUtil.feedInputDataToMuxer;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@ -38,13 +38,13 @@ import org.junit.Rule;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.robolectric.ParameterizedRobolectricTestRunner;
|
||||||
import org.junit.runners.Parameterized.Parameter;
|
import org.robolectric.ParameterizedRobolectricTestRunner.Parameter;
|
||||||
import org.junit.runners.Parameterized.Parameters;
|
import org.robolectric.ParameterizedRobolectricTestRunner.Parameters;
|
||||||
|
|
||||||
/** End to end instrumentation tests for {@link FragmentedMp4Muxer}. */
|
/** End to end instrumentation tests for {@link FragmentedMp4Muxer}. */
|
||||||
@RunWith(Parameterized.class)
|
@RunWith(ParameterizedRobolectricTestRunner.class)
|
||||||
public class FragmentedMp4MuxerEndToEndAndroidTest {
|
public class FragmentedMp4MuxerEndToEndTest {
|
||||||
private static final String H264_WITH_PYRAMID_B_FRAMES_MP4 =
|
private static final String H264_WITH_PYRAMID_B_FRAMES_MP4 =
|
||||||
"bbb_800x640_768kbps_30fps_avc_pyramid_3b.mp4";
|
"bbb_800x640_768kbps_30fps_avc_pyramid_3b.mp4";
|
||||||
private static final String H265_HDR10_MP4 = "hdr10-720p.mp4";
|
private static final String H265_HDR10_MP4 = "hdr10-720p.mp4";
|
||||||
@ -96,7 +96,7 @@ public class FragmentedMp4MuxerEndToEndAndroidTest {
|
|||||||
DumpFileAsserts.assertOutput(
|
DumpFileAsserts.assertOutput(
|
||||||
context,
|
context,
|
||||||
fakeExtractorOutput,
|
fakeExtractorOutput,
|
||||||
AndroidMuxerTestUtil.getExpectedDumpFilePath(inputFile + "_fragmented"));
|
MuxerTestUtil.getExpectedDumpFilePath(inputFile + "_fragmented"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -123,6 +123,6 @@ public class FragmentedMp4MuxerEndToEndAndroidTest {
|
|||||||
DumpFileAsserts.assertOutput(
|
DumpFileAsserts.assertOutput(
|
||||||
context,
|
context,
|
||||||
dumpableMp4Box,
|
dumpableMp4Box,
|
||||||
AndroidMuxerTestUtil.getExpectedDumpFilePath(H265_HDR10_MP4 + "_fragmented_box_structure"));
|
MuxerTestUtil.getExpectedDumpFilePath(H265_HDR10_MP4 + "_fragmented_box_structure"));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,7 +16,7 @@
|
|||||||
package androidx.media3.muxer;
|
package androidx.media3.muxer;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.muxer.AndroidMuxerTestUtil.feedInputDataToMuxer;
|
import static androidx.media3.muxer.MuxerTestUtil.feedInputDataToMuxer;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@ -36,13 +36,13 @@ import org.junit.Rule;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.robolectric.ParameterizedRobolectricTestRunner;
|
||||||
import org.junit.runners.Parameterized.Parameter;
|
import org.robolectric.ParameterizedRobolectricTestRunner.Parameter;
|
||||||
import org.junit.runners.Parameterized.Parameters;
|
import org.robolectric.ParameterizedRobolectricTestRunner.Parameters;
|
||||||
|
|
||||||
/** End to end parameterized instrumentation tests for {@link Mp4Muxer}. */
|
/** End to end parameterized tests for {@link Mp4Muxer}. */
|
||||||
@RunWith(Parameterized.class)
|
@RunWith(ParameterizedRobolectricTestRunner.class)
|
||||||
public class Mp4MuxerEndToEndParameterizedAndroidTest {
|
public class Mp4MuxerEndToEndParameterizedTest {
|
||||||
// Video Codecs
|
// Video Codecs
|
||||||
private static final String H263_3GP = "bbb_176x144_128kbps_15fps_h263.3gp";
|
private static final String H263_3GP = "bbb_176x144_128kbps_15fps_h263.3gp";
|
||||||
private static final String H264_MP4 = "sample_no_bframes.mp4";
|
private static final String H264_MP4 = "sample_no_bframes.mp4";
|
||||||
@ -126,6 +126,6 @@ public class Mp4MuxerEndToEndParameterizedAndroidTest {
|
|||||||
FakeExtractorOutput fakeExtractorOutput =
|
FakeExtractorOutput fakeExtractorOutput =
|
||||||
TestUtil.extractAllSamplesFromFilePath(new Mp4Extractor(), checkNotNull(outputPath));
|
TestUtil.extractAllSamplesFromFilePath(new Mp4Extractor(), checkNotNull(outputPath));
|
||||||
DumpFileAsserts.assertOutput(
|
DumpFileAsserts.assertOutput(
|
||||||
context, fakeExtractorOutput, AndroidMuxerTestUtil.getExpectedDumpFilePath(inputFile));
|
context, fakeExtractorOutput, MuxerTestUtil.getExpectedDumpFilePath(inputFile));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,9 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.muxer;
|
package androidx.media3.muxer;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.muxer.Mp4Muxer.LAST_SAMPLE_DURATION_BEHAVIOR_SET_FROM_END_OF_STREAM_BUFFER_OR_DUPLICATE_PREVIOUS;
|
import static androidx.media3.muxer.Mp4Muxer.LAST_SAMPLE_DURATION_BEHAVIOR_SET_FROM_END_OF_STREAM_BUFFER_OR_DUPLICATE_PREVIOUS;
|
||||||
import static androidx.media3.muxer.MuxerTestUtil.FAKE_VIDEO_FORMAT;
|
import static androidx.media3.muxer.MuxerTestUtil.FAKE_VIDEO_FORMAT;
|
||||||
import static androidx.media3.muxer.MuxerTestUtil.XMP_SAMPLE_DATA;
|
import static androidx.media3.muxer.MuxerTestUtil.XMP_SAMPLE_DATA;
|
||||||
|
import static androidx.media3.muxer.MuxerTestUtil.feedInputDataToMuxer;
|
||||||
import static androidx.media3.muxer.MuxerTestUtil.getFakeSampleAndSampleInfo;
|
import static androidx.media3.muxer.MuxerTestUtil.getFakeSampleAndSampleInfo;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
@ -53,6 +55,7 @@ import org.junit.runner.RunWith;
|
|||||||
public class Mp4MuxerEndToEndTest {
|
public class Mp4MuxerEndToEndTest {
|
||||||
@Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
|
@Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||||
|
|
||||||
|
private static final String H265_HDR10_MP4 = "hdr10-720p.mp4";
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -117,6 +120,30 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
assertThat(outputFileBytes).isEmpty();
|
assertThat(outputFileBytes).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createMp4File_muxerNotClosed_createsPartiallyWrittenValidFile() throws Exception {
|
||||||
|
String outputPath = temporaryFolder.newFile().getPath();
|
||||||
|
Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(new FileOutputStream(outputPath)).build();
|
||||||
|
mp4Muxer.addMetadataEntry(
|
||||||
|
new Mp4TimestampData(
|
||||||
|
/* creationTimestampSeconds= */ 100_000_000L,
|
||||||
|
/* modificationTimestampSeconds= */ 500_000_000L));
|
||||||
|
feedInputDataToMuxer(context, mp4Muxer, H265_HDR10_MP4);
|
||||||
|
|
||||||
|
// Muxer not closed.
|
||||||
|
|
||||||
|
// Audio sample written = 192 out of 195.
|
||||||
|
// Video sample written = 125 out of 127.
|
||||||
|
// Output is still a valid MP4 file.
|
||||||
|
FakeExtractorOutput fakeExtractorOutput =
|
||||||
|
TestUtil.extractAllSamplesFromFilePath(
|
||||||
|
new Mp4Extractor(new DefaultSubtitleParserFactory()), checkNotNull(outputPath));
|
||||||
|
DumpFileAsserts.assertOutput(
|
||||||
|
context,
|
||||||
|
fakeExtractorOutput,
|
||||||
|
MuxerTestUtil.getExpectedDumpFilePath("partial_" + H265_HDR10_MP4));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createMp4File_withSameTracksOffset_matchesExpected() throws Exception {
|
public void createMp4File_withSameTracksOffset_matchesExpected() throws Exception {
|
||||||
String outputFilePath = temporaryFolder.newFile().getPath();
|
String outputFilePath = temporaryFolder.newFile().getPath();
|
||||||
@ -813,6 +840,63 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
assertThat(fakeExtractorOutput.seekMap.getDurationUs()).isEqualTo(400L);
|
assertThat(fakeExtractorOutput.seekMap.getDurationUs()).isEqualTo(400L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createMp4File_withSampleBatchingDisabled_matchesExpected() throws Exception {
|
||||||
|
String outputPath = temporaryFolder.newFile().getPath();
|
||||||
|
|
||||||
|
Mp4Muxer mp4Muxer =
|
||||||
|
new Mp4Muxer.Builder(new FileOutputStream(outputPath))
|
||||||
|
.setSampleBatchingEnabled(false)
|
||||||
|
.build();
|
||||||
|
mp4Muxer.addMetadataEntry(
|
||||||
|
new Mp4TimestampData(
|
||||||
|
/* creationTimestampSeconds= */ 100_000_000L,
|
||||||
|
/* modificationTimestampSeconds= */ 500_000_000L));
|
||||||
|
try {
|
||||||
|
feedInputDataToMuxer(context, mp4Muxer, checkNotNull(H265_HDR10_MP4));
|
||||||
|
} finally {
|
||||||
|
mp4Muxer.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
FakeExtractorOutput fakeExtractorOutput =
|
||||||
|
TestUtil.extractAllSamplesFromFilePath(
|
||||||
|
new Mp4Extractor(new DefaultSubtitleParserFactory()), checkNotNull(outputPath));
|
||||||
|
DumpFileAsserts.assertOutput(
|
||||||
|
context,
|
||||||
|
fakeExtractorOutput,
|
||||||
|
MuxerTestUtil.getExpectedDumpFilePath("sample_batching_disabled_" + H265_HDR10_MP4));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createMp4File_withSampleBatchingAndAttemptStreamableOutputDisabled_matchesExpected()
|
||||||
|
throws Exception {
|
||||||
|
String outputPath = temporaryFolder.newFile().getPath();
|
||||||
|
|
||||||
|
Mp4Muxer mp4Muxer =
|
||||||
|
new Mp4Muxer.Builder(new FileOutputStream(outputPath))
|
||||||
|
.setSampleBatchingEnabled(false)
|
||||||
|
.setAttemptStreamableOutputEnabled(false)
|
||||||
|
.build();
|
||||||
|
mp4Muxer.addMetadataEntry(
|
||||||
|
new Mp4TimestampData(
|
||||||
|
/* creationTimestampSeconds= */ 100_000_000L,
|
||||||
|
/* modificationTimestampSeconds= */ 500_000_000L));
|
||||||
|
try {
|
||||||
|
feedInputDataToMuxer(context, mp4Muxer, checkNotNull(H265_HDR10_MP4));
|
||||||
|
} finally {
|
||||||
|
mp4Muxer.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
FakeExtractorOutput fakeExtractorOutput =
|
||||||
|
TestUtil.extractAllSamplesFromFilePath(
|
||||||
|
new Mp4Extractor(new DefaultSubtitleParserFactory()), checkNotNull(outputPath));
|
||||||
|
DumpFileAsserts.assertOutput(
|
||||||
|
context,
|
||||||
|
fakeExtractorOutput,
|
||||||
|
MuxerTestUtil.getExpectedDumpFilePath(
|
||||||
|
"sample_batching_and_attempt_streamable_output_disabled_" + H265_HDR10_MP4));
|
||||||
|
}
|
||||||
|
|
||||||
private static void writeFakeSamples(Mp4Muxer muxer, int trackId, int sampleCount)
|
private static void writeFakeSamples(Mp4Muxer muxer, int trackId, int sampleCount)
|
||||||
throws MuxerException {
|
throws MuxerException {
|
||||||
for (int i = 0; i < sampleCount; i++) {
|
for (int i = 0; i < sampleCount; i++) {
|
||||||
|
@ -18,13 +18,20 @@ package androidx.media3.muxer;
|
|||||||
import static androidx.media3.common.MimeTypes.AUDIO_AAC;
|
import static androidx.media3.common.MimeTypes.AUDIO_AAC;
|
||||||
import static androidx.media3.common.MimeTypes.VIDEO_H264;
|
import static androidx.media3.common.MimeTypes.VIDEO_H264;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaCodec.BufferInfo;
|
import android.media.MediaCodec.BufferInfo;
|
||||||
|
import android.net.Uri;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
|
import androidx.media3.common.util.MediaFormatUtil;
|
||||||
|
import androidx.media3.exoplayer.MediaExtractorCompat;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.io.BaseEncoding;
|
import com.google.common.io.BaseEncoding;
|
||||||
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/** Utilities for muxer test cases. */
|
/** Utilities for muxer test cases. */
|
||||||
/* package */ class MuxerTestUtil {
|
/* package */ class MuxerTestUtil {
|
||||||
@ -53,6 +60,7 @@ import java.nio.ByteBuffer;
|
|||||||
.decode(
|
.decode(
|
||||||
"0000000167F4000A919B2BF3CB3640000003004000000C83C48965800000000168EBE3C448000001658884002BFFFEF5DBF32CAE4A43FF");
|
"0000000167F4000A919B2BF3CB3640000003004000000C83C48965800000000168EBE3C448000001658884002BFFFEF5DBF32CAE4A43FF");
|
||||||
|
|
||||||
|
private static final String MP4_FILE_ASSET_DIRECTORY = "asset:///media/mp4/";
|
||||||
private static final String DUMP_FILE_OUTPUT_DIRECTORY = "muxerdumps";
|
private static final String DUMP_FILE_OUTPUT_DIRECTORY = "muxerdumps";
|
||||||
private static final String DUMP_FILE_EXTENSION = "dump";
|
private static final String DUMP_FILE_EXTENSION = "dump";
|
||||||
|
|
||||||
@ -73,5 +81,39 @@ import java.nio.ByteBuffer;
|
|||||||
return new Pair<>(sampleDirectBuffer, bufferInfo);
|
return new Pair<>(sampleDirectBuffer, bufferInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void feedInputDataToMuxer(Context context, Muxer muxer, String inputFileName)
|
||||||
|
throws IOException, MuxerException {
|
||||||
|
MediaExtractorCompat extractor = new MediaExtractorCompat(context);
|
||||||
|
Uri fileUri = Uri.parse(MP4_FILE_ASSET_DIRECTORY + inputFileName);
|
||||||
|
extractor.setDataSource(fileUri, /* offset= */ 0);
|
||||||
|
|
||||||
|
List<Integer> addedTracks = new ArrayList<>();
|
||||||
|
for (int i = 0; i < extractor.getTrackCount(); i++) {
|
||||||
|
int trackId =
|
||||||
|
muxer.addTrack(MediaFormatUtil.createFormatFromMediaFormat(extractor.getTrackFormat(i)));
|
||||||
|
addedTracks.add(trackId);
|
||||||
|
extractor.selectTrack(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
||||||
|
bufferInfo.flags = extractor.getSampleFlags();
|
||||||
|
bufferInfo.offset = 0;
|
||||||
|
bufferInfo.presentationTimeUs = extractor.getSampleTime();
|
||||||
|
int sampleSize = (int) extractor.getSampleSize();
|
||||||
|
bufferInfo.size = sampleSize;
|
||||||
|
|
||||||
|
ByteBuffer sampleBuffer = ByteBuffer.allocateDirect(sampleSize);
|
||||||
|
extractor.readSampleData(sampleBuffer, /* offset= */ 0);
|
||||||
|
|
||||||
|
sampleBuffer.rewind();
|
||||||
|
|
||||||
|
muxer.writeSampleData(
|
||||||
|
addedTracks.get(extractor.getSampleTrackIndex()), sampleBuffer, bufferInfo);
|
||||||
|
} while (extractor.advance());
|
||||||
|
|
||||||
|
extractor.release();
|
||||||
|
}
|
||||||
|
|
||||||
private MuxerTestUtil() {}
|
private MuxerTestUtil() {}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user